2008-03-25 Sven Herzberg <sven@imendio.com>
[hildon] / src / hildon-bread-crumb-trail.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2007 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
7  * Author: Xan Lopez <xan.lopez@nokia.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; version 2.1 of
12  * the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22  * 02110-1301 USA
23  *
24  */
25
26
27 /**
28  * SECTION:hildon-bread-crumb-trail
29  * @short_description: Widget used to represent a specific path in a hierarchical tree.
30  * Stability: Unstable
31  *
32  * HildonBreadCrumbTrail is a GTK widget used to represent the currently active path in
33  * some kind of hierarchical structure (file system, media library, structured document, etc).
34  *
35  * It has built-in support for text and icon bread crumbs, but the trail only requires a very
36  * simple interface to be implemented for its children and thus new types of items can be
37  * implemented if needed. See #HildonBreadCrumb for more details.
38  */
39
40 #include <gdk/gdkkeysyms.h>
41 #include "hildon-marshalers.h"
42 #include "hildon-bread-crumb-trail.h"
43 #include "hildon-bread-crumb-widget.h"
44
45 #define HILDON_BREAD_CRUMB_TRAIL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_BREAD_CRUMB_TRAIL, HildonBreadCrumbTrailPrivate))
46
47 struct _HildonBreadCrumbTrailPrivate
48 {
49   GtkWidget *back_button;
50   GList *item_list;
51   GtkWidget *arrow;
52 };
53
54 /* Signals */
55
56 enum {
57   CRUMB_CLICKED,
58   MOVE_PARENT,
59   LAST_SIGNAL
60 };
61
62 /* Properties */
63
64 enum {
65   PROP_0
66 };
67
68 static void hildon_bread_crumb_trail_size_request (GtkWidget *widget,
69                                                    GtkRequisition *requisition);
70 static void hildon_bread_crumb_trail_size_allocate (GtkWidget *widget,
71                                                     GtkAllocation *allocation);
72 static void hildon_bread_crumb_trail_add (GtkContainer *container,
73                                           GtkWidget *widget);
74 static void hildon_bread_crumb_trail_forall (GtkContainer *container,
75                                              gboolean include_internals,
76                                              GtkCallback callback,
77                                              gpointer callback_data);
78 static void hildon_bread_crumb_trail_remove (GtkContainer *container,
79                                              GtkWidget *widget);
80 static void hildon_bread_crumb_trail_finalize (GObject *object);
81 static void hildon_bread_crumb_trail_scroll_back (GtkWidget *button,
82                                                   HildonBreadCrumbTrail *bct);
83 static void hildon_bread_crumb_trail_update_back_button_sensitivity (HildonBreadCrumbTrail *bct);
84 static void hildon_bread_crumb_trail_move_parent (HildonBreadCrumbTrail *bct);
85
86 static gpointer get_bread_crumb_id (HildonBreadCrumb *item);
87
88 static guint bread_crumb_trail_signals[LAST_SIGNAL] = { 0 };
89
90 /* GType methods */
91
92 G_DEFINE_TYPE (HildonBreadCrumbTrail, hildon_bread_crumb_trail, GTK_TYPE_CONTAINER)
93
94 static void
95 hildon_bread_crumb_trail_class_init (HildonBreadCrumbTrailClass *klass)
96 {
97   GObjectClass *gobject_class = (GObjectClass*)klass;
98   GtkObjectClass *object_class = (GtkObjectClass*)klass;
99   GtkWidgetClass *widget_class = (GtkWidgetClass*)klass;
100   GtkContainerClass *container_class = (GtkContainerClass*)klass;
101   GtkBindingSet *binding_set;
102
103   /* GObject signals */
104   gobject_class->finalize = hildon_bread_crumb_trail_finalize;
105
106   /* GtkWidget signals */
107   widget_class->size_request = hildon_bread_crumb_trail_size_request;
108   widget_class->size_allocate = hildon_bread_crumb_trail_size_allocate;
109
110   /* GtkContainer signals */
111   container_class->add = hildon_bread_crumb_trail_add;
112   container_class->forall = hildon_bread_crumb_trail_forall;
113   container_class->remove = hildon_bread_crumb_trail_remove;
114
115   /* HildonBreadCrumbTrail signals */
116   klass->move_parent = hildon_bread_crumb_trail_move_parent;
117
118   /* Style properties */
119
120 #define _BREAD_CRUMB_TRAIL_MINIMUM_WIDTH 10
121
122   /* FIXME: is this the best way to do it? */
123   gtk_widget_class_install_style_property (widget_class,
124                                            g_param_spec_int ("minimum-width",
125                                                              "Minimum width",
126                                                              "The minimum width in characters the children widgets will request",
127                                                              0,
128                                                              G_MAXINT,
129                                                              _BREAD_CRUMB_TRAIL_MINIMUM_WIDTH,
130                                                              G_PARAM_READABLE));
131
132 #define _BREAD_CRUMB_TRAIL_ARROW_SIZE 34
133
134   gtk_widget_class_install_style_property (widget_class,
135                                            g_param_spec_int ("arrow-size",
136                                                              "Arrow size",
137                                                              "Size of the back button arrow",
138                                                              0,
139                                                              G_MAXINT,
140                                                              _BREAD_CRUMB_TRAIL_ARROW_SIZE,
141                                                              G_PARAM_READABLE));
142   /* Signals */
143   bread_crumb_trail_signals[CRUMB_CLICKED] =
144     g_signal_new ("crumb-clicked",
145                   G_OBJECT_CLASS_TYPE (object_class),
146                   G_SIGNAL_RUN_LAST,
147                   G_STRUCT_OFFSET (HildonBreadCrumbTrailClass, crumb_clicked),
148                   g_signal_accumulator_true_handled, NULL,
149                   _hildon_marshal_BOOLEAN__POINTER,
150                   G_TYPE_BOOLEAN, 1,
151                   G_TYPE_POINTER);
152
153   bread_crumb_trail_signals[MOVE_PARENT] =
154     g_signal_new ("move-parent",
155                   G_OBJECT_CLASS_TYPE (object_class),
156                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
157                   G_STRUCT_OFFSET (HildonBreadCrumbTrailClass, move_parent),
158                   NULL, NULL,
159                   g_cclosure_marshal_VOID__VOID,
160                   G_TYPE_NONE,
161                   0);
162                   
163
164   /* Binding set */
165   binding_set = gtk_binding_set_by_class (widget_class);
166
167   gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
168                                 "move-parent", 0);
169   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
170                                 "move-parent", 0);
171                                 
172   /* Private data */
173   g_type_class_add_private (gobject_class, sizeof (HildonBreadCrumbTrailPrivate));
174 }
175
176 static void
177 hildon_bread_crumb_trail_finalize (GObject *object)
178 {
179   HildonBreadCrumbTrailPrivate *priv = HILDON_BREAD_CRUMB_TRAIL (object)->priv;
180
181   g_list_free (priv->item_list);
182
183   G_OBJECT_CLASS (hildon_bread_crumb_trail_parent_class)->finalize (object);
184 }
185
186 static void
187 hildon_bread_crumb_trail_move_parent (HildonBreadCrumbTrail *bct)
188 {
189   if (g_list_length (bct->priv->item_list) > 1)
190     {
191       g_signal_emit_by_name (HILDON_BREAD_CRUMB (bct->priv->item_list->next->data),
192                              "crumb-activated");
193     }
194 }
195
196 static void
197 hildon_bread_crumb_trail_size_request (GtkWidget *widget,
198                                        GtkRequisition *requisition)
199 {
200   GList *p;
201   GtkRequisition child_requisition;
202   HildonBreadCrumbTrail *bct;
203   HildonBreadCrumbTrailPrivate *priv;
204   gint minimum_width, width = 0;
205   PangoLayout *layout;
206   gchar *tmp = NULL;
207
208   bct= HILDON_BREAD_CRUMB_TRAIL (widget);
209   priv = bct->priv;
210
211   requisition->height = 0;
212   requisition->width = 0;
213
214   gtk_widget_size_request (priv->back_button, &child_requisition);
215   requisition->width = child_requisition.width;
216   requisition->height = child_requisition.height;
217
218   if (priv->item_list)
219     {
220       /* Add minimum width for one item */
221       /* TODO: this can be probably cached */
222       gtk_widget_style_get (widget,
223                             "minimum-width", &minimum_width,
224                             NULL);
225
226       tmp = g_strnfill ((gsize)minimum_width, 'm');
227       layout = gtk_widget_create_pango_layout (widget, tmp);
228       g_free (tmp);
229       pango_layout_get_size (layout, &width, NULL);
230       requisition->width += PANGO_PIXELS (width);
231       g_object_unref (layout);
232     }
233
234   /* Button requisitions */
235   for (p = priv->item_list; p; p = p->next)
236     {
237       GtkWidget *child = GTK_WIDGET (p->data);
238
239       if (GTK_WIDGET_VISIBLE (child))
240         gtk_widget_size_request (child, &child_requisition);
241     }
242
243   /* Border width */
244   requisition->width += GTK_CONTAINER (widget)->border_width * 2;
245   requisition->height += GTK_CONTAINER (widget)->border_width * 2;
246
247   widget->requisition = *requisition;
248 }
249
250 /* Document me please */
251
252 static void
253 hildon_bread_crumb_trail_size_allocate (GtkWidget *widget,
254                                         GtkAllocation *allocation)
255 {
256   GtkRequisition req;
257   gint natural_width, natural_height;
258   HildonBreadCrumb *item;
259   GtkAllocation child_allocation;
260   GtkRequisition child_requisition;
261   GtkWidget *child;
262   gint allocation_width;
263   gint border_width, width;
264   gint extra_space;
265   GList *p, *first_show, *first_hide;
266   gint back_button_size;
267   HildonBreadCrumbTrailPrivate *priv = HILDON_BREAD_CRUMB_TRAIL (widget)->priv;
268
269   widget->allocation = *allocation;
270
271   border_width = (gint) GTK_CONTAINER (widget)->border_width;
272   allocation_width = allocation->width - 2 * border_width;
273
274   /* Allocate the back button */
275   child_allocation.x = allocation->x + border_width;
276   child_allocation.y = allocation->y + border_width;
277   gtk_widget_get_child_requisition (priv->back_button, &child_requisition);
278   /* We want the back button to be a square */
279   back_button_size = MAX (child_requisition.width, child_requisition.height);
280   child_allocation.width = child_allocation.height = back_button_size;
281   gtk_widget_size_allocate (priv->back_button, &child_allocation);
282   child_allocation.x += back_button_size;
283
284   /* If there are no buttons there's nothing else to do */
285   if (priv->item_list == NULL)
286     return;
287
288   /* We find out how many buttons can we show, starting from the
289      the last one in the logical path (the first item in the list) */
290
291   width = back_button_size;
292   p = priv->item_list;
293   first_show = NULL;
294   first_hide = NULL; 
295   extra_space = 0;
296
297   for (p = priv->item_list; p; p = p->next)
298     {
299       item = HILDON_BREAD_CRUMB (p->data);
300       child = GTK_WIDGET (item);
301
302       /* Does the widget fit with its natural size? */
303       hildon_bread_crumb_get_natural_size (item,
304                                            &natural_width,
305                                            &natural_height);
306
307       if (width + natural_width <= allocation_width)
308         {
309           /* Yes, it does */
310           first_show = p;
311           first_hide = p->next;
312           width += natural_width;
313         }
314       else
315         {
316           /* No, it doesn't. Allocate as much as possible
317              and stop */
318           child_allocation.width = allocation_width - width;
319
320           gtk_widget_size_request (child, &req);
321
322           if (child_allocation.width > req.width)
323             {
324               first_hide = p->next;
325               gtk_widget_set_child_visible (child, TRUE);
326               gtk_widget_size_allocate (child, &child_allocation);
327               child_allocation.x += child_allocation.width;
328             }
329           else
330             {
331               extra_space = child_allocation.width;
332             }
333
334           break;
335         }
336     }
337
338   /* Not enough items to fill the breadcrumb? */
339   if (p == NULL && width < allocation_width)
340     {
341       extra_space = allocation_width - width;
342     }
343
344   /* Allocate the other buttons */
345   for (p = first_show; p; p = p->prev)
346     {
347       item = HILDON_BREAD_CRUMB (p->data);
348       child = GTK_WIDGET (item);
349
350       /* Does the widget fit with its natural size? */
351       hildon_bread_crumb_get_natural_size (item,
352                                            &natural_width,
353                                            &natural_height);
354
355       /* If I'm the last and there's extra space, use it */
356       if (p->prev == NULL && extra_space != 0)
357         {
358           natural_width += extra_space;
359         }
360
361       child_allocation.width = natural_width;
362       gtk_widget_set_child_visible (child, TRUE);
363       gtk_widget_size_allocate (child, &child_allocation);
364       child_allocation.x += child_allocation.width;
365     }
366
367   for (p = first_hide; p; p = p->next)
368     {
369       item = HILDON_BREAD_CRUMB (p->data);
370       child = GTK_WIDGET (item);
371
372       gtk_widget_set_child_visible (GTK_WIDGET (item), FALSE);
373     }
374 }
375
376 static gpointer
377 get_bread_crumb_id (HildonBreadCrumb *item)
378 {
379   return g_object_get_data (G_OBJECT (item), "bread-crumb-id");
380 }
381
382 static void
383 crumb_activated_cb (GtkWidget *button,
384                     HildonBreadCrumbTrail *bct)
385 {
386   gboolean signal_handled = FALSE;
387
388   g_signal_emit (bct, bread_crumb_trail_signals[CRUMB_CLICKED], 0,
389                  get_bread_crumb_id (HILDON_BREAD_CRUMB (button)),
390                  &signal_handled);
391
392   if (signal_handled == FALSE)
393     {
394       GtkWidget *child;
395       gboolean focus_last_item = FALSE;
396       HildonBreadCrumbTrailPrivate *priv;
397
398       priv = bct->priv;
399
400       child = GTK_WIDGET (priv->item_list->data);
401
402       /* We remove the tip of the list until we hit the clicked button */
403       while (child && child != button)
404         {
405           if (GTK_WIDGET_HAS_FOCUS (child))
406             focus_last_item = TRUE;
407
408           gtk_container_remove (GTK_CONTAINER (bct), child);
409
410           if (priv->item_list == NULL)
411               return;
412
413           child = GTK_WIDGET (priv->item_list->data);
414         }
415
416       if (focus_last_item)
417         gtk_widget_grab_focus (GTK_WIDGET (bct->priv->item_list->data));
418     }
419 }
420
421 static void
422 hildon_bread_crumb_trail_add (GtkContainer *container,
423                               GtkWidget *widget)
424 {
425   gtk_widget_set_parent (widget, GTK_WIDGET (container));
426
427   if (HILDON_IS_BREAD_CRUMB (widget))
428     {
429       HildonBreadCrumbTrail *bct = HILDON_BREAD_CRUMB_TRAIL (container);
430
431       g_signal_connect (G_OBJECT (widget), "crumb-activated",
432                         G_CALLBACK (crumb_activated_cb), container);
433
434       bct->priv->item_list = g_list_prepend (bct->priv->item_list, widget);
435
436       hildon_bread_crumb_trail_update_back_button_sensitivity (bct);
437     }
438 }
439
440 static void
441 hildon_bread_crumb_trail_forall (GtkContainer *container,
442                                  gboolean include_internals,
443                                  GtkCallback callback,
444                                  gpointer callback_data)
445 {
446   g_return_if_fail (callback != NULL);
447   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (container));
448
449   GList *children;
450   HildonBreadCrumbTrailPrivate *priv = HILDON_BREAD_CRUMB_TRAIL (container)->priv;
451
452   children = priv->item_list;
453
454   while (children)
455     {
456       GtkWidget *child;
457       child = GTK_WIDGET (children->data);
458       children = children->next;
459
460       (*callback) (child, callback_data);
461     }
462
463   if (include_internals && priv->back_button)
464     {
465       (*callback) (priv->back_button, callback_data);
466     }
467 }
468
469 static void
470 hildon_bread_crumb_trail_remove (GtkContainer *container,
471                                  GtkWidget *widget)
472 {
473   GList *p, *next;
474   HildonBreadCrumbTrailPrivate *priv;
475   gboolean was_visible = GTK_WIDGET_VISIBLE (widget);
476
477   priv = HILDON_BREAD_CRUMB_TRAIL (container)->priv;
478
479   p = priv->item_list;
480
481   while (p)
482     {
483       next = p->next;
484
485       if (widget == GTK_WIDGET (p->data))
486         {
487           g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (crumb_activated_cb),
488                                                 HILDON_BREAD_CRUMB_TRAIL (container));
489           gtk_widget_unparent (widget);
490
491           priv->item_list = g_list_delete_link (priv->item_list, p);
492
493           hildon_bread_crumb_trail_update_back_button_sensitivity (HILDON_BREAD_CRUMB_TRAIL (container));
494
495           if (was_visible)
496             {
497               gtk_widget_queue_resize (GTK_WIDGET (container));
498             }
499         }
500
501       p = next;
502     }
503 }
504
505 static void
506 hildon_bread_crumb_trail_update_back_button_sensitivity (HildonBreadCrumbTrail *bct)
507 {
508   guint list_length;
509   HildonBreadCrumbTrailPrivate *priv = bct->priv;
510
511   list_length = g_list_length (priv->item_list);
512
513   if (list_length <= 1)
514     {
515       gtk_widget_set_sensitive (priv->back_button, FALSE);
516     }
517   else
518     {
519       gtk_widget_set_sensitive (priv->back_button, TRUE);
520     }
521 }
522
523 static GtkWidget*
524 create_back_button (HildonBreadCrumbTrail *bct)
525 {
526   GtkWidget *button;
527   GtkWidget *arrow;
528   gint arrow_size;
529
530   gtk_widget_push_composite_child ();
531
532   button = gtk_button_new ();
533   gtk_widget_set_name (button, "hildon-bread-crumb-back-button");
534   gtk_widget_set_sensitive (button, FALSE);
535
536   arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
537   bct->priv->arrow = arrow;
538   gtk_widget_style_get (GTK_WIDGET (bct),
539                         "arrow-size", &arrow_size,
540                         NULL);
541   gtk_widget_set_size_request (arrow, arrow_size, arrow_size);
542
543   gtk_container_add (GTK_CONTAINER (button), arrow);
544   gtk_container_add (GTK_CONTAINER (bct), button);
545   gtk_widget_show_all (button);
546
547   gtk_widget_pop_composite_child ();
548
549   return button;
550 }
551
552 static void
553 hildon_bread_crumb_trail_init (HildonBreadCrumbTrail *bct)
554 {
555   HildonBreadCrumbTrailPrivate *priv = HILDON_BREAD_CRUMB_TRAIL_GET_PRIVATE (bct);
556
557   GTK_WIDGET_SET_FLAGS (bct, GTK_NO_WINDOW);
558   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (bct), FALSE);
559
560   bct->priv = priv;
561   priv->item_list = NULL;
562
563   priv->back_button = create_back_button (bct);
564   g_signal_connect (priv->back_button, "clicked",
565                     G_CALLBACK (hildon_bread_crumb_trail_scroll_back),
566                     bct);
567 }
568
569 static void
570 hildon_bread_crumb_trail_scroll_back (GtkWidget *button,
571                                       HildonBreadCrumbTrail *bct)
572 {
573   hildon_bread_crumb_trail_move_parent (bct);
574 }
575
576 static void
577 attach_bread_crumb (HildonBreadCrumbTrail *bct,
578                     GtkWidget *bread_crumb,
579                     gpointer id,
580                     GDestroyNotify destroy)
581 {
582   g_object_set_data_full (G_OBJECT (bread_crumb), "bread-crumb-id", id, destroy);
583
584   gtk_container_add (GTK_CONTAINER (bct), bread_crumb);
585
586   gtk_widget_show (bread_crumb);
587 }
588
589 /* PUBLIC API */
590
591 /**
592  * hildon_bread_crumb_trail_new:
593  * 
594  * Creates a new #HildonBreadCrumbTrail widget.
595  *
596  * Returns: a #GtkWidget pointer of newly created bread crumb trail
597  * widget
598  *
599  * Stability: Unstable
600  */
601
602 GtkWidget*
603 hildon_bread_crumb_trail_new (void)
604 {
605   return GTK_WIDGET (g_object_new (HILDON_TYPE_BREAD_CRUMB_TRAIL, NULL));
606 }
607
608 /**
609  * hildon_bread_crumb_trail_push:
610  * @bct: pointer to #HildonBreadCrumbTrail
611  * @item: the #HildonBreadCrumb to be added to the trail
612  * @id: optional id for the bread crumb
613  * @destroy: GDestroyNotify callback to be called when the bread crumb is destroyed
614  *
615  * Adds a new bread crumb to the end of the trail.
616  *
617  * Stability: Unstable
618  */
619
620 void
621 hildon_bread_crumb_trail_push (HildonBreadCrumbTrail *bct,
622                                HildonBreadCrumb *item,
623                                gpointer id,
624                                GDestroyNotify destroy)
625 {
626   GtkWidget *widget;
627
628   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
629   g_return_if_fail (HILDON_IS_BREAD_CRUMB (item));
630
631   widget = GTK_WIDGET (item);
632
633   attach_bread_crumb (bct, widget, id, destroy);
634 }
635
636 /**
637  * hildon_bread_crumb_trail_push_text:
638  * @bct: pointer to #HildonBreadCrumbTrail
639  * @text: content of the new bread crumb
640  * @id: optional id for the bread crumb
641  * @destroy: GDestroyNotify callback to be called when the bread crumb is destroyed
642  *
643  * Adds a new bread crumb to the end of the trail containing the specified text.
644  *
645  * Stability: Unstable
646  */
647
648 void
649 hildon_bread_crumb_trail_push_text (HildonBreadCrumbTrail *bct,
650                                     const gchar *text,
651                                     gpointer id,
652                                     GDestroyNotify destroy)
653 {
654   GtkWidget *widget;
655
656   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
657   g_return_if_fail (text != NULL);
658
659   widget = _hildon_bread_crumb_widget_new_with_text (text);
660   if (bct->priv->item_list == NULL)
661     {
662       g_object_set (G_OBJECT (widget), "show-separator", FALSE, NULL);
663     }
664   attach_bread_crumb (bct, widget, id, destroy);
665 }
666
667 /**
668  * hildon_bread_crumb_trail_push_icon:
669  * @bct: pointer to #HildonBreadCrumbTrail
670  * @text: content of the new bread crumb
671  * @icon: a widget to set as the icon in the bread crumb
672  * @id: optional id for the bread crumb
673  * @destroy: GDestroyNotify callback to be called when the bread crumb is destroyed
674  *
675  * Adds a new bread crumb to the end of the trail containing the specified text and
676  * icon.
677  *
678  * Stability: Unstable
679  */
680
681 void
682 hildon_bread_crumb_trail_push_icon (HildonBreadCrumbTrail *bct,
683                                     const gchar *text,
684                                     GtkWidget *icon,
685                                     gpointer id,
686                                     GDestroyNotify destroy)
687 {
688   GtkWidget *widget;
689
690   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
691   g_return_if_fail (text != NULL);
692   g_return_if_fail (GTK_IS_WIDGET (icon));
693
694   widget = _hildon_bread_crumb_widget_new_with_icon (icon, text);
695   if (bct->priv->item_list == NULL)
696     {
697       g_object_set (G_OBJECT (widget), "show-separator", FALSE, NULL);
698     }
699   attach_bread_crumb (bct, widget, id, destroy);
700 }
701
702 /**
703  * hildon_bread_crumb_trail_pop:
704  * @bct: pointer to #HildonBreadCrumbTrail
705  *
706  * Removes the last bread crumb from the trail.
707  *
708  * Stability: Unstable
709  */
710
711 void
712 hildon_bread_crumb_trail_pop (HildonBreadCrumbTrail *bct)
713 {
714   GtkWidget *child;
715   HildonBreadCrumbTrailPrivate *priv;
716
717   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
718
719   priv = bct->priv;
720
721   if (priv->item_list == NULL)
722     return;
723
724   if (priv->item_list)
725     {
726       child = GTK_WIDGET (priv->item_list->data);
727       gtk_container_remove (GTK_CONTAINER (bct), child);
728     }
729
730   hildon_bread_crumb_trail_update_back_button_sensitivity (bct);
731 }
732
733 /**
734  * hildon_bread_crumb_trail_clear:
735  * @bct: pointer to #HildonBreadCrumbTrail
736  *
737  * Removes all the bread crumbs from the bread crumb trail.
738  *
739  * Stability: Unstable
740  */
741
742 void
743 hildon_bread_crumb_trail_clear (HildonBreadCrumbTrail *bct)
744 {
745   HildonBreadCrumbTrailPrivate *priv;
746
747   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
748
749   priv = bct->priv;
750
751   while (priv->item_list)
752     {
753       hildon_bread_crumb_trail_pop (bct);
754     }
755
756   /*
757     Sensitivity hack from hell. We need to do this while
758      http://bugzilla.gnome.org/show_bug.cgi?id=56070 is not
759      fixed to allow repeated clicking on the back button if
760      someone clears and rebuilds the trail when it's clicked
761   */
762   gtk_widget_hide (priv->back_button);
763   gtk_widget_show (priv->back_button);
764 }