2007-09-24 Xan Lopez <xan.lopez@nokia.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;
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       if (widget == GTK_WIDGET (p->data))
484         {
485           g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (crumb_activated_cb),
486                                                 HILDON_BREAD_CRUMB_TRAIL (container));
487           gtk_widget_unparent (widget);
488
489           priv->item_list = g_list_remove_link (priv->item_list, p);
490           g_list_free (p);
491
492           hildon_bread_crumb_trail_update_back_button_sensitivity (HILDON_BREAD_CRUMB_TRAIL (container));
493
494           if (was_visible)
495             {
496               gtk_widget_queue_resize (GTK_WIDGET (container));
497             }
498         }
499
500       p = p->next;
501     }
502 }
503
504 static void
505 hildon_bread_crumb_trail_update_back_button_sensitivity (HildonBreadCrumbTrail *bct)
506 {
507   guint list_length;
508   HildonBreadCrumbTrailPrivate *priv = bct->priv;
509
510   list_length = g_list_length (priv->item_list);
511
512   if (list_length <= 1)
513     {
514       gtk_widget_set_sensitive (priv->back_button, FALSE);
515     }
516   else
517     {
518       gtk_widget_set_sensitive (priv->back_button, TRUE);
519     }
520 }
521
522 static GtkWidget*
523 create_back_button (HildonBreadCrumbTrail *bct)
524 {
525   GtkWidget *button;
526   GtkWidget *arrow;
527   gint arrow_size;
528
529   gtk_widget_push_composite_child ();
530
531   button = gtk_button_new ();
532   gtk_widget_set_name (button, "hildon-bread-crumb-back-button");
533   gtk_widget_set_sensitive (button, FALSE);
534
535   arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
536   bct->priv->arrow = arrow;
537   gtk_widget_style_get (GTK_WIDGET (bct),
538                         "arrow-size", &arrow_size,
539                         NULL);
540   gtk_widget_set_size_request (arrow, arrow_size, arrow_size);
541
542   gtk_container_add (GTK_CONTAINER (button), arrow);
543   gtk_container_add (GTK_CONTAINER (bct), button);
544   gtk_widget_show_all (button);
545
546   gtk_widget_pop_composite_child ();
547
548   return button;
549 }
550
551 static void
552 hildon_bread_crumb_trail_init (HildonBreadCrumbTrail *bct)
553 {
554   HildonBreadCrumbTrailPrivate *priv = HILDON_BREAD_CRUMB_TRAIL_GET_PRIVATE (bct);
555
556   GTK_WIDGET_SET_FLAGS (bct, GTK_NO_WINDOW);
557   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (bct), FALSE);
558
559   bct->priv = priv;
560   priv->item_list = NULL;
561
562   priv->back_button = create_back_button (bct);
563   g_signal_connect (priv->back_button, "clicked",
564                     G_CALLBACK (hildon_bread_crumb_trail_scroll_back),
565                     bct);
566 }
567
568 static void
569 hildon_bread_crumb_trail_scroll_back (GtkWidget *button,
570                                       HildonBreadCrumbTrail *bct)
571 {
572   hildon_bread_crumb_trail_move_parent (bct);
573 }
574
575 static void
576 attach_bread_crumb (HildonBreadCrumbTrail *bct,
577                     GtkWidget *bread_crumb,
578                     gpointer id,
579                     GDestroyNotify destroy)
580 {
581   g_object_set_data_full (G_OBJECT (bread_crumb), "bread-crumb-id", id, destroy);
582
583   gtk_container_add (GTK_CONTAINER (bct), bread_crumb);
584
585   gtk_widget_show (bread_crumb);
586 }
587
588 /* PUBLIC API */
589
590 /**
591  * hildon_bread_crumb_trail_new:
592  * 
593  * Creates a new #HildonBreadCrumbTrail widget.
594  *
595  * Returns: a #GtkWidget pointer of newly created bread crumb trail
596  * widget
597  *
598  * Stability: Unstable
599  */
600
601 GtkWidget*
602 hildon_bread_crumb_trail_new (void)
603 {
604   return GTK_WIDGET (g_object_new (HILDON_TYPE_BREAD_CRUMB_TRAIL, NULL));
605 }
606
607 /**
608  * hildon_bread_crumb_trail_push:
609  * @bct: pointer to #HildonBreadCrumbTrail
610  * @item: the #HildonBreadCrumb to be added to the trail
611  * @id: optional id for the bread crumb
612  * @destroy: GDestroyNotify callback to be called when the bread crumb is destroyed
613  *
614  * Adds a new bread crumb to the end of the trail.
615  *
616  * Stability: Unstable
617  */
618
619 void
620 hildon_bread_crumb_trail_push (HildonBreadCrumbTrail *bct,
621                                HildonBreadCrumb *item,
622                                gpointer id,
623                                GDestroyNotify destroy)
624 {
625   GtkWidget *widget;
626
627   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
628   g_return_if_fail (HILDON_IS_BREAD_CRUMB (item));
629
630   widget = GTK_WIDGET (item);
631
632   attach_bread_crumb (bct, widget, id, destroy);
633 }
634
635 /**
636  * hildon_bread_crumb_trail_push_text:
637  * @bct: pointer to #HildonBreadCrumbTrail
638  * @text: content of the new bread crumb
639  * @id: optional id for the bread crumb
640  * @destroy: GDestroyNotify callback to be called when the bread crumb is destroyed
641  *
642  * Adds a new bread crumb to the end of the trail containing the specified text.
643  *
644  * Stability: Unstable
645  */
646
647 void
648 hildon_bread_crumb_trail_push_text (HildonBreadCrumbTrail *bct,
649                                     const gchar *text,
650                                     gpointer id,
651                                     GDestroyNotify destroy)
652 {
653   GtkWidget *widget;
654
655   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
656   g_return_if_fail (text != NULL);
657
658   widget = _hildon_bread_crumb_widget_new_with_text (text);
659   attach_bread_crumb (bct, widget, id, destroy);
660 }
661
662 /**
663  * hildon_bread_crumb_trail_push_icon:
664  * @bct: pointer to #HildonBreadCrumbTrail
665  * @text: content of the new bread crumb
666  * @icon: a widget to set as the icon in the bread crumb
667  * @id: optional id for the bread crumb
668  * @destroy: GDestroyNotify callback to be called when the bread crumb is destroyed
669  *
670  * Adds a new bread crumb to the end of the trail containing the specified text and
671  * icon.
672  *
673  * Stability: Unstable
674  */
675
676 void
677 hildon_bread_crumb_trail_push_icon (HildonBreadCrumbTrail *bct,
678                                     const gchar *text,
679                                     GtkWidget *icon,
680                                     gpointer id,
681                                     GDestroyNotify destroy)
682 {
683   GtkWidget *widget;
684
685   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
686   g_return_if_fail (text != NULL);
687   g_return_if_fail (GTK_IS_WIDGET (icon));
688
689   widget = _hildon_bread_crumb_widget_new_with_icon (icon, text);
690   attach_bread_crumb (bct, widget, id, destroy);
691 }
692
693 /**
694  * hildon_bread_crumb_trail_pop:
695  * @bct: pointer to #HildonBreadCrumbTrail
696  *
697  * Removes the last bread crumb from the trail.
698  *
699  * Stability: Unstable
700  */
701
702 void
703 hildon_bread_crumb_trail_pop (HildonBreadCrumbTrail *bct)
704 {
705   GtkWidget *child;
706   HildonBreadCrumbTrailPrivate *priv;
707
708   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
709
710   priv = bct->priv;
711
712   if (priv->item_list == NULL)
713     return;
714
715   if (priv->item_list)
716     {
717       child = GTK_WIDGET (priv->item_list->data);
718       gtk_container_remove (GTK_CONTAINER (bct), child);
719     }
720
721   hildon_bread_crumb_trail_update_back_button_sensitivity (bct);
722 }
723
724 /**
725  * hildon_bread_crumb_trail_clear:
726  * @bct: pointer to #HildonBreadCrumbTrail
727  *
728  * Removes all the bread crumbs from the bread crumb trail.
729  *
730  * Stability: Unstable
731  */
732
733 void
734 hildon_bread_crumb_trail_clear (HildonBreadCrumbTrail *bct)
735 {
736   HildonBreadCrumbTrailPrivate *priv;
737
738   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
739
740   priv = bct->priv;
741
742   while (priv->item_list)
743     {
744       hildon_bread_crumb_trail_pop (bct);
745     }
746
747   /*
748     Sensitivity hack from hell. We need to do this while
749      http://bugzilla.gnome.org/show_bug.cgi?id=56070 is not
750      fixed to allow repeated clicking on the back button if
751      someone clears and rebuilds the trail when it's clicked
752   */
753   gtk_widget_hide (priv->back_button);
754   gtk_widget_show (priv->back_button);
755 }