2008-07-29 Claudio Saavedra <csaavedra@igalia.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   gboolean rtl;
269
270   /* Get the rtl status */
271   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
272
273   widget->allocation = *allocation;
274
275   border_width = (gint) GTK_CONTAINER (widget)->border_width;
276   allocation_width = allocation->width - 2 * border_width;
277
278   /* Allocate the back button */
279   if (rtl)
280     child_allocation.x = allocation->width - border_width;
281   else
282     child_allocation.x = allocation->x + border_width;
283
284   child_allocation.y = allocation->y + border_width;
285   gtk_widget_get_child_requisition (priv->back_button, &child_requisition);
286   /* We want the back button to be a square */
287   back_button_size = MAX (child_requisition.width, child_requisition.height);
288   child_allocation.width = child_allocation.height = back_button_size;
289
290   if (rtl)
291     child_allocation.x -= back_button_size;
292
293   gtk_widget_size_allocate (priv->back_button, &child_allocation);
294
295   if (!rtl)
296     child_allocation.x += back_button_size;
297
298   /* If there are no buttons there's nothing else to do */
299   if (priv->item_list == NULL)
300     return;
301
302   /* We find out how many buttons can we show, starting from the
303      the last one in the logical path (the first item in the list) */
304
305   width = back_button_size;
306   p = priv->item_list;
307   first_show = NULL;
308   first_hide = NULL; 
309   extra_space = 0;
310
311   for (p = priv->item_list; p; p = p->next)
312     {
313       item = HILDON_BREAD_CRUMB (p->data);
314       child = GTK_WIDGET (item);
315
316       /* Does the widget fit with its natural size? */
317       hildon_bread_crumb_get_natural_size (item,
318                                            &natural_width,
319                                            &natural_height);
320
321       if (width + natural_width <= allocation_width)
322         {
323           /* Yes, it does */
324           first_show = p;
325           first_hide = p->next;
326           width += natural_width;
327         }
328       else
329         {
330           /* No, it doesn't. Allocate as much as possible
331              and stop */
332           child_allocation.width = allocation_width - width;
333
334           gtk_widget_size_request (child, &req);
335
336           if (child_allocation.width > req.width)
337             {
338               first_hide = p->next;
339               gtk_widget_set_child_visible (child, TRUE);
340
341               if (rtl)
342                 child_allocation.x -= child_allocation.width;
343
344               gtk_widget_size_allocate (child, &child_allocation);
345
346               if (!rtl)
347                 child_allocation.x += child_allocation.width;
348             }
349           else
350             {
351               extra_space = child_allocation.width;
352             }
353
354           break;
355         }
356     }
357
358   /* Not enough items to fill the breadcrumb? */
359   if (p == NULL && width < allocation_width)
360     {
361       extra_space = allocation_width - width;
362     }
363
364   /* Allocate the other buttons */
365   for (p = first_show; p; p = p->prev)
366     {
367       item = HILDON_BREAD_CRUMB (p->data);
368       child = GTK_WIDGET (item);
369
370       /* Does the widget fit with its natural size? */
371       hildon_bread_crumb_get_natural_size (item,
372                                            &natural_width,
373                                            &natural_height);
374
375       /* If I'm the last and there's extra space, use it */
376       if (p->prev == NULL && extra_space != 0)
377         {
378           natural_width += extra_space;
379         }
380
381       child_allocation.width = natural_width;
382       gtk_widget_set_child_visible (child, TRUE);
383
384       if (rtl)
385         child_allocation.x -= child_allocation.width;
386
387       gtk_widget_size_allocate (child, &child_allocation);
388
389       if (!rtl)
390         child_allocation.x += child_allocation.width;
391     }
392
393   for (p = first_hide; p; p = p->next)
394     {
395       item = HILDON_BREAD_CRUMB (p->data);
396       child = GTK_WIDGET (item);
397
398       gtk_widget_set_child_visible (GTK_WIDGET (item), FALSE);
399     }
400 }
401
402 static gpointer
403 get_bread_crumb_id (HildonBreadCrumb *item)
404 {
405   return g_object_get_data (G_OBJECT (item), "bread-crumb-id");
406 }
407
408 static void
409 crumb_activated_cb (GtkWidget *button,
410                     HildonBreadCrumbTrail *bct)
411 {
412   gboolean signal_handled = FALSE;
413
414   g_signal_emit (bct, bread_crumb_trail_signals[CRUMB_CLICKED], 0,
415                  get_bread_crumb_id (HILDON_BREAD_CRUMB (button)),
416                  &signal_handled);
417
418   if (signal_handled == FALSE)
419     {
420       GtkWidget *child;
421       gboolean focus_last_item = FALSE;
422       HildonBreadCrumbTrailPrivate *priv;
423
424       priv = bct->priv;
425
426       child = GTK_WIDGET (priv->item_list->data);
427
428       /* We remove the tip of the list until we hit the clicked button */
429       while (child && child != button)
430         {
431           if (GTK_WIDGET_HAS_FOCUS (child))
432             focus_last_item = TRUE;
433
434           gtk_container_remove (GTK_CONTAINER (bct), child);
435
436           if (priv->item_list == NULL)
437               return;
438
439           child = GTK_WIDGET (priv->item_list->data);
440         }
441
442       if (focus_last_item)
443         gtk_widget_grab_focus (GTK_WIDGET (bct->priv->item_list->data));
444     }
445 }
446
447 static void
448 hildon_bread_crumb_trail_add (GtkContainer *container,
449                               GtkWidget *widget)
450 {
451   gtk_widget_set_parent (widget, GTK_WIDGET (container));
452
453   if (HILDON_IS_BREAD_CRUMB (widget))
454     {
455       HildonBreadCrumbTrail *bct = HILDON_BREAD_CRUMB_TRAIL (container);
456
457       g_signal_connect (G_OBJECT (widget), "crumb-activated",
458                         G_CALLBACK (crumb_activated_cb), container);
459
460       bct->priv->item_list = g_list_prepend (bct->priv->item_list, widget);
461
462       hildon_bread_crumb_trail_update_back_button_sensitivity (bct);
463     }
464 }
465
466 static void
467 hildon_bread_crumb_trail_forall (GtkContainer *container,
468                                  gboolean include_internals,
469                                  GtkCallback callback,
470                                  gpointer callback_data)
471 {
472   g_return_if_fail (callback != NULL);
473   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (container));
474
475   GList *children;
476   HildonBreadCrumbTrailPrivate *priv = HILDON_BREAD_CRUMB_TRAIL (container)->priv;
477
478   children = priv->item_list;
479
480   while (children)
481     {
482       GtkWidget *child;
483       child = GTK_WIDGET (children->data);
484       children = children->next;
485
486       (*callback) (child, callback_data);
487     }
488
489   if (include_internals && priv->back_button)
490     {
491       (*callback) (priv->back_button, callback_data);
492     }
493 }
494
495 static void
496 hildon_bread_crumb_trail_remove (GtkContainer *container,
497                                  GtkWidget *widget)
498 {
499   GList *p, *next;
500   HildonBreadCrumbTrailPrivate *priv;
501   gboolean was_visible = GTK_WIDGET_VISIBLE (widget);
502
503   priv = HILDON_BREAD_CRUMB_TRAIL (container)->priv;
504
505   p = priv->item_list;
506
507   while (p)
508     {
509       next = p->next;
510
511       if (widget == GTK_WIDGET (p->data))
512         {
513           g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (crumb_activated_cb),
514                                                 HILDON_BREAD_CRUMB_TRAIL (container));
515           gtk_widget_unparent (widget);
516
517           priv->item_list = g_list_delete_link (priv->item_list, p);
518
519           hildon_bread_crumb_trail_update_back_button_sensitivity (HILDON_BREAD_CRUMB_TRAIL (container));
520
521           if (was_visible)
522             {
523               gtk_widget_queue_resize (GTK_WIDGET (container));
524             }
525         }
526
527       p = next;
528     }
529 }
530
531 static void
532 hildon_bread_crumb_trail_update_back_button_sensitivity (HildonBreadCrumbTrail *bct)
533 {
534   guint list_length;
535   HildonBreadCrumbTrailPrivate *priv = bct->priv;
536
537   list_length = g_list_length (priv->item_list);
538
539   if (list_length <= 1)
540     {
541       gtk_widget_set_sensitive (priv->back_button, FALSE);
542     }
543   else
544     {
545       gtk_widget_set_sensitive (priv->back_button, TRUE);
546     }
547 }
548
549 static GtkWidget*
550 create_back_button (HildonBreadCrumbTrail *bct)
551 {
552   GtkWidget *button;
553   GtkWidget *arrow;
554   gint arrow_size;
555
556   gtk_widget_push_composite_child ();
557
558   button = gtk_button_new ();
559   gtk_widget_set_name (button, "hildon-bread-crumb-back-button");
560   gtk_widget_set_sensitive (button, FALSE);
561
562   arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
563   bct->priv->arrow = arrow;
564   gtk_widget_style_get (GTK_WIDGET (bct),
565                         "arrow-size", &arrow_size,
566                         NULL);
567   gtk_widget_set_size_request (arrow, arrow_size, arrow_size);
568
569   gtk_container_add (GTK_CONTAINER (button), arrow);
570   gtk_container_add (GTK_CONTAINER (bct), button);
571   gtk_widget_show_all (button);
572
573   gtk_widget_pop_composite_child ();
574
575   return button;
576 }
577
578 static void
579 hildon_bread_crumb_trail_init (HildonBreadCrumbTrail *bct)
580 {
581   HildonBreadCrumbTrailPrivate *priv = HILDON_BREAD_CRUMB_TRAIL_GET_PRIVATE (bct);
582
583   GTK_WIDGET_SET_FLAGS (bct, GTK_NO_WINDOW);
584   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (bct), FALSE);
585
586   bct->priv = priv;
587   priv->item_list = NULL;
588
589   priv->back_button = create_back_button (bct);
590   g_signal_connect (priv->back_button, "clicked",
591                     G_CALLBACK (hildon_bread_crumb_trail_scroll_back),
592                     bct);
593 }
594
595 static void
596 hildon_bread_crumb_trail_scroll_back (GtkWidget *button,
597                                       HildonBreadCrumbTrail *bct)
598 {
599   hildon_bread_crumb_trail_move_parent (bct);
600 }
601
602 static void
603 attach_bread_crumb (HildonBreadCrumbTrail *bct,
604                     GtkWidget *bread_crumb,
605                     gpointer id,
606                     GDestroyNotify destroy)
607 {
608   g_object_set_data_full (G_OBJECT (bread_crumb), "bread-crumb-id", id, destroy);
609
610   gtk_container_add (GTK_CONTAINER (bct), bread_crumb);
611
612   gtk_widget_show (bread_crumb);
613 }
614
615 /* PUBLIC API */
616
617 /**
618  * hildon_bread_crumb_trail_new:
619  * 
620  * Creates a new #HildonBreadCrumbTrail widget.
621  *
622  * Returns: a #GtkWidget pointer of newly created bread crumb trail
623  * widget
624  *
625  * Stability: Unstable
626  */
627
628 GtkWidget*
629 hildon_bread_crumb_trail_new (void)
630 {
631   return GTK_WIDGET (g_object_new (HILDON_TYPE_BREAD_CRUMB_TRAIL, NULL));
632 }
633
634 /**
635  * hildon_bread_crumb_trail_push:
636  * @bct: pointer to #HildonBreadCrumbTrail
637  * @item: the #HildonBreadCrumb to be added to the trail
638  * @id: optional id for the bread crumb
639  * @destroy: GDestroyNotify callback to be called when the bread crumb is destroyed
640  *
641  * Adds a new bread crumb to the end of the trail.
642  *
643  * Stability: Unstable
644  */
645
646 void
647 hildon_bread_crumb_trail_push (HildonBreadCrumbTrail *bct,
648                                HildonBreadCrumb *item,
649                                gpointer id,
650                                GDestroyNotify destroy)
651 {
652   GtkWidget *widget;
653
654   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
655   g_return_if_fail (HILDON_IS_BREAD_CRUMB (item));
656
657   widget = GTK_WIDGET (item);
658
659   attach_bread_crumb (bct, widget, id, destroy);
660 }
661
662 /**
663  * hildon_bread_crumb_trail_push_text:
664  * @bct: pointer to #HildonBreadCrumbTrail
665  * @text: content of the new bread crumb
666  * @id: optional id for the bread crumb
667  * @destroy: GDestroyNotify callback to be called when the bread crumb is destroyed
668  *
669  * Adds a new bread crumb to the end of the trail containing the specified text.
670  *
671  * Stability: Unstable
672  */
673
674 void
675 hildon_bread_crumb_trail_push_text (HildonBreadCrumbTrail *bct,
676                                     const gchar *text,
677                                     gpointer id,
678                                     GDestroyNotify destroy)
679 {
680   GtkWidget *widget;
681
682   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
683   g_return_if_fail (text != NULL);
684
685   widget = _hildon_bread_crumb_widget_new_with_text (text);
686   if (bct->priv->item_list == NULL)
687     {
688       g_object_set (G_OBJECT (widget), "show-separator", FALSE, NULL);
689     }
690   attach_bread_crumb (bct, widget, id, destroy);
691 }
692
693 /**
694  * hildon_bread_crumb_trail_push_icon:
695  * @bct: pointer to #HildonBreadCrumbTrail
696  * @text: content of the new bread crumb
697  * @icon: a widget to set as the icon in the bread crumb
698  * @id: optional id for the bread crumb
699  * @destroy: GDestroyNotify callback to be called when the bread crumb is destroyed
700  *
701  * Adds a new bread crumb to the end of the trail containing the specified text and
702  * icon.
703  *
704  * Stability: Unstable
705  */
706
707 void
708 hildon_bread_crumb_trail_push_icon (HildonBreadCrumbTrail *bct,
709                                     const gchar *text,
710                                     GtkWidget *icon,
711                                     gpointer id,
712                                     GDestroyNotify destroy)
713 {
714   GtkWidget *widget;
715
716   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
717   g_return_if_fail (text != NULL);
718   g_return_if_fail (GTK_IS_WIDGET (icon));
719
720   widget = _hildon_bread_crumb_widget_new_with_icon (icon, text);
721   if (bct->priv->item_list == NULL)
722     {
723       g_object_set (G_OBJECT (widget), "show-separator", FALSE, NULL);
724     }
725   attach_bread_crumb (bct, widget, id, destroy);
726 }
727
728 /**
729  * hildon_bread_crumb_trail_pop:
730  * @bct: pointer to #HildonBreadCrumbTrail
731  *
732  * Removes the last bread crumb from the trail.
733  *
734  * Stability: Unstable
735  */
736
737 void
738 hildon_bread_crumb_trail_pop (HildonBreadCrumbTrail *bct)
739 {
740   GtkWidget *child;
741   HildonBreadCrumbTrailPrivate *priv;
742
743   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
744
745   priv = bct->priv;
746
747   if (priv->item_list == NULL)
748     return;
749
750   if (priv->item_list)
751     {
752       child = GTK_WIDGET (priv->item_list->data);
753       gtk_container_remove (GTK_CONTAINER (bct), child);
754     }
755
756   hildon_bread_crumb_trail_update_back_button_sensitivity (bct);
757 }
758
759 /**
760  * hildon_bread_crumb_trail_clear:
761  * @bct: pointer to #HildonBreadCrumbTrail
762  *
763  * Removes all the bread crumbs from the bread crumb trail.
764  *
765  * Stability: Unstable
766  */
767
768 void
769 hildon_bread_crumb_trail_clear (HildonBreadCrumbTrail *bct)
770 {
771   HildonBreadCrumbTrailPrivate *priv;
772
773   g_return_if_fail (HILDON_IS_BREAD_CRUMB_TRAIL (bct));
774
775   priv = bct->priv;
776
777   while (priv->item_list)
778     {
779       hildon_bread_crumb_trail_pop (bct);
780     }
781
782   /*
783     Sensitivity hack from hell. We need to do this while
784      http://bugzilla.gnome.org/show_bug.cgi?id=56070 is not
785      fixed to allow repeated clicking on the back button if
786      someone clears and rebuilds the trail when it's clicked
787   */
788   gtk_widget_hide (priv->back_button);
789   gtk_widget_show (priv->back_button);
790 }