]> Pileus Git - ~andy/gtk/blob - gtk/gtkpathbar.c
pathbar: Fix gcc warning
[~andy/gtk] / gtk / gtkpathbar.c
1 /* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
2 /* gtkpathbar.c
3  * Copyright (C) 2004  Red Hat, Inc.,  Jonathan Blandford <jrb@gnome.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "config.h"
20
21 #include "gtkpathbar.h"
22
23 #include <string.h>
24
25 #include "gtktogglebutton.h"
26 #include "gtkarrow.h"
27 #include "gtkdnd.h"
28 #include "gtkimage.h"
29 #include "gtkintl.h"
30 #include "gtkicontheme.h"
31 #include "gtkiconfactory.h"
32 #include "gtklabel.h"
33 #include "gtkbox.h"
34 #include "gtkmain.h"
35 #include "gtkmarshalers.h"
36
37
38 enum {
39   PATH_CLICKED,
40   LAST_SIGNAL
41 };
42
43 typedef enum {
44   NORMAL_BUTTON,
45   ROOT_BUTTON,
46   HOME_BUTTON,
47   DESKTOP_BUTTON
48 } ButtonType;
49
50 #define BUTTON_DATA(x) ((ButtonData *)(x))
51
52 #define SCROLL_DELAY_FACTOR 5
53
54 static guint path_bar_signals [LAST_SIGNAL] = { 0 };
55
56 /* Icon size for if we can't get it from the theme */
57 #define FALLBACK_ICON_SIZE 16
58
59 typedef struct _ButtonData ButtonData;
60
61 struct _ButtonData
62 {
63   GtkWidget *button;
64   ButtonType type;
65   char *dir_name;
66   GFile *file;
67   GtkWidget *image;
68   GtkWidget *label;
69   GCancellable *cancellable;
70   guint ignore_changes : 1;
71   guint file_is_hidden : 1;
72 };
73 /* This macro is used to check if a button can be used as a fake root.
74  * All buttons in front of a fake root are automatically hidden when in a
75  * directory below a fake root and replaced with the "<" arrow button.
76  */
77 #define BUTTON_IS_FAKE_ROOT(button) ((button)->type == HOME_BUTTON)
78
79 G_DEFINE_TYPE (GtkPathBar, gtk_path_bar, GTK_TYPE_CONTAINER)
80
81 static void gtk_path_bar_finalize                 (GObject          *object);
82 static void gtk_path_bar_dispose                  (GObject          *object);
83 static void gtk_path_bar_realize                  (GtkWidget        *widget);
84 static void gtk_path_bar_unrealize                (GtkWidget        *widget);
85 static void gtk_path_bar_get_preferred_width      (GtkWidget        *widget,
86                                                    gint             *minimum,
87                                                    gint             *natural);
88 static void gtk_path_bar_get_preferred_height     (GtkWidget        *widget,
89                                                    gint             *minimum,
90                                                    gint             *natural);
91 static void gtk_path_bar_map                      (GtkWidget        *widget);
92 static void gtk_path_bar_unmap                    (GtkWidget        *widget);
93 static void gtk_path_bar_size_allocate            (GtkWidget        *widget,
94                                                    GtkAllocation    *allocation);
95 static void gtk_path_bar_add                      (GtkContainer     *container,
96                                                    GtkWidget        *widget);
97 static void gtk_path_bar_remove                   (GtkContainer     *container,
98                                                    GtkWidget        *widget);
99 static void gtk_path_bar_forall                   (GtkContainer     *container,
100                                                    gboolean          include_internals,
101                                                    GtkCallback       callback,
102                                                    gpointer          callback_data);
103 static GtkWidgetPath *gtk_path_bar_get_path_for_child (GtkContainer *container,
104                                                        GtkWidget    *child);
105 static gboolean gtk_path_bar_scroll               (GtkWidget        *widget,
106                                                    GdkEventScroll   *event);
107 static void gtk_path_bar_scroll_up                (GtkPathBar       *path_bar);
108 static void gtk_path_bar_scroll_down              (GtkPathBar       *path_bar);
109 static void gtk_path_bar_stop_scrolling           (GtkPathBar       *path_bar);
110 static gboolean gtk_path_bar_slider_up_defocus    (GtkWidget        *widget,
111                                                    GdkEventButton   *event,
112                                                    GtkPathBar       *path_bar);
113 static gboolean gtk_path_bar_slider_down_defocus  (GtkWidget        *widget,
114                                                    GdkEventButton   *event,
115                                                    GtkPathBar       *path_bar);
116 static gboolean gtk_path_bar_slider_button_press  (GtkWidget        *widget,
117                                                    GdkEventButton   *event,
118                                                    GtkPathBar       *path_bar);
119 static gboolean gtk_path_bar_slider_button_release(GtkWidget        *widget,
120                                                    GdkEventButton   *event,
121                                                    GtkPathBar       *path_bar);
122 static void gtk_path_bar_grab_notify              (GtkWidget        *widget,
123                                                    gboolean          was_grabbed);
124 static void gtk_path_bar_state_changed            (GtkWidget        *widget,
125                                                    GtkStateType      previous_state);
126 static void gtk_path_bar_style_updated            (GtkWidget        *widget);
127 static void gtk_path_bar_screen_changed           (GtkWidget        *widget,
128                                                    GdkScreen        *previous_screen);
129 static void gtk_path_bar_check_icon_theme         (GtkPathBar       *path_bar);
130 static void gtk_path_bar_update_button_appearance (GtkPathBar       *path_bar,
131                                                    ButtonData       *button_data,
132                                                    gboolean          current_dir);
133
134 static void
135 on_slider_unmap (GtkWidget  *widget,
136                  GtkPathBar *path_bar)
137 {
138   if (path_bar->timer &&
139       ((widget == path_bar->up_slider_button && path_bar->scrolling_up) ||
140        (widget == path_bar->down_slider_button && path_bar->scrolling_down)))
141     gtk_path_bar_stop_scrolling (path_bar);
142 }
143
144 static GtkWidget *
145 get_slider_button (GtkPathBar  *path_bar,
146                    GtkArrowType arrow_type)
147 {
148   GtkWidget *button;
149   AtkObject *atk_obj;
150
151   gtk_widget_push_composite_child ();
152
153   button = gtk_button_new ();
154   atk_obj = gtk_widget_get_accessible (button);
155   if (arrow_type == GTK_ARROW_LEFT)
156     atk_object_set_name (atk_obj, _("Up Path"));
157   else
158     atk_object_set_name (atk_obj, _("Down Path"));
159
160   gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
161   gtk_container_add (GTK_CONTAINER (button),
162                      gtk_arrow_new (arrow_type, GTK_SHADOW_OUT));
163   gtk_container_add (GTK_CONTAINER (path_bar), button);
164   gtk_widget_show_all (button);
165
166   g_signal_connect (G_OBJECT (button), "unmap",
167                     G_CALLBACK (on_slider_unmap), path_bar);
168
169   gtk_widget_pop_composite_child ();
170
171   return button;
172 }
173
174 static void
175 gtk_path_bar_init (GtkPathBar *path_bar)
176 {
177   GtkStyleContext *context;
178
179   gtk_widget_set_has_window (GTK_WIDGET (path_bar), FALSE);
180   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (path_bar), FALSE);
181
182   path_bar->get_info_cancellable = NULL;
183
184   path_bar->spacing = 0;
185   path_bar->up_slider_button = get_slider_button (path_bar, GTK_ARROW_LEFT);
186   path_bar->down_slider_button = get_slider_button (path_bar, GTK_ARROW_RIGHT);
187   path_bar->icon_size = FALLBACK_ICON_SIZE;
188   
189   g_signal_connect_swapped (path_bar->up_slider_button, "clicked",
190                             G_CALLBACK (gtk_path_bar_scroll_up), path_bar);
191   g_signal_connect_swapped (path_bar->down_slider_button, "clicked",
192                             G_CALLBACK (gtk_path_bar_scroll_down), path_bar);
193
194   g_signal_connect (path_bar->up_slider_button, "focus-out-event",
195                     G_CALLBACK (gtk_path_bar_slider_up_defocus), path_bar);
196   g_signal_connect (path_bar->down_slider_button, "focus-out-event",
197                     G_CALLBACK (gtk_path_bar_slider_down_defocus), path_bar);
198
199   g_signal_connect (path_bar->up_slider_button, "button-press-event",
200                     G_CALLBACK (gtk_path_bar_slider_button_press), path_bar);
201   g_signal_connect (path_bar->up_slider_button, "button-release-event",
202                     G_CALLBACK (gtk_path_bar_slider_button_release), path_bar);
203   g_signal_connect (path_bar->down_slider_button, "button-press-event",
204                     G_CALLBACK (gtk_path_bar_slider_button_press), path_bar);
205   g_signal_connect (path_bar->down_slider_button, "button-release-event",
206                     G_CALLBACK (gtk_path_bar_slider_button_release), path_bar);
207
208   context = gtk_widget_get_style_context (GTK_WIDGET (path_bar));
209   gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED);
210 }
211
212 static void
213 gtk_path_bar_class_init (GtkPathBarClass *path_bar_class)
214 {
215   GObjectClass *gobject_class;
216   GtkWidgetClass *widget_class;
217   GtkContainerClass *container_class;
218
219   gobject_class = (GObjectClass *) path_bar_class;
220   widget_class = (GtkWidgetClass *) path_bar_class;
221   container_class = (GtkContainerClass *) path_bar_class;
222
223   gobject_class->finalize = gtk_path_bar_finalize;
224   gobject_class->dispose = gtk_path_bar_dispose;
225
226   widget_class->get_preferred_width = gtk_path_bar_get_preferred_width;
227   widget_class->get_preferred_height = gtk_path_bar_get_preferred_height;
228   widget_class->realize = gtk_path_bar_realize;
229   widget_class->unrealize = gtk_path_bar_unrealize;
230   widget_class->map = gtk_path_bar_map;
231   widget_class->unmap = gtk_path_bar_unmap;
232   widget_class->size_allocate = gtk_path_bar_size_allocate;
233   widget_class->style_updated = gtk_path_bar_style_updated;
234   widget_class->screen_changed = gtk_path_bar_screen_changed;
235   widget_class->grab_notify = gtk_path_bar_grab_notify;
236   widget_class->state_changed = gtk_path_bar_state_changed;
237   widget_class->scroll_event = gtk_path_bar_scroll;
238
239   container_class->add = gtk_path_bar_add;
240   container_class->forall = gtk_path_bar_forall;
241   container_class->remove = gtk_path_bar_remove;
242   container_class->get_path_for_child = gtk_path_bar_get_path_for_child;
243   gtk_container_class_handle_border_width (container_class);
244   /* FIXME: */
245   /*  container_class->child_type = gtk_path_bar_child_type;*/
246
247   path_bar_signals [PATH_CLICKED] =
248     g_signal_new (I_("path-clicked"),
249                   G_OBJECT_CLASS_TYPE (gobject_class),
250                   G_SIGNAL_RUN_FIRST,
251                   G_STRUCT_OFFSET (GtkPathBarClass, path_clicked),
252                   NULL, NULL,
253                   _gtk_marshal_VOID__POINTER_POINTER_BOOLEAN,
254                   G_TYPE_NONE, 3,
255                   G_TYPE_POINTER,
256                   G_TYPE_POINTER,
257                   G_TYPE_BOOLEAN);
258 }
259
260
261 static void
262 gtk_path_bar_finalize (GObject *object)
263 {
264   GtkPathBar *path_bar;
265
266   path_bar = GTK_PATH_BAR (object);
267
268   gtk_path_bar_stop_scrolling (path_bar);
269
270   g_list_free (path_bar->button_list);
271   if (path_bar->root_file)
272     g_object_unref (path_bar->root_file);
273   if (path_bar->home_file)
274     g_object_unref (path_bar->home_file);
275   if (path_bar->desktop_file)
276     g_object_unref (path_bar->desktop_file);
277
278   if (path_bar->root_icon)
279     g_object_unref (path_bar->root_icon);
280   if (path_bar->home_icon)
281     g_object_unref (path_bar->home_icon);
282   if (path_bar->desktop_icon)
283     g_object_unref (path_bar->desktop_icon);
284
285   if (path_bar->file_system)
286     g_object_unref (path_bar->file_system);
287
288   G_OBJECT_CLASS (gtk_path_bar_parent_class)->finalize (object);
289 }
290
291 /* Removes the settings signal handler.  It's safe to call multiple times */
292 static void
293 remove_settings_signal (GtkPathBar *path_bar,
294                         GdkScreen  *screen)
295 {
296   if (path_bar->settings_signal_id)
297     {
298       GtkSettings *settings;
299
300       settings = gtk_settings_get_for_screen (screen);
301       g_signal_handler_disconnect (settings,
302                                    path_bar->settings_signal_id);
303       path_bar->settings_signal_id = 0;
304     }
305 }
306
307 static void
308 gtk_path_bar_dispose (GObject *object)
309 {
310   GtkPathBar *path_bar = GTK_PATH_BAR (object);
311
312   remove_settings_signal (path_bar, gtk_widget_get_screen (GTK_WIDGET (object)));
313
314   if (path_bar->get_info_cancellable)
315     g_cancellable_cancel (path_bar->get_info_cancellable);
316   path_bar->get_info_cancellable = NULL;
317
318   G_OBJECT_CLASS (gtk_path_bar_parent_class)->dispose (object);
319 }
320
321 /* Size requisition:
322  * 
323  * Ideally, our size is determined by another widget, and we are just filling
324  * available space.
325  */
326 static void
327 gtk_path_bar_get_preferred_width (GtkWidget *widget,
328                                   gint      *minimum,
329                                   gint      *natural)
330 {
331   ButtonData *button_data;
332   GtkPathBar *path_bar;
333   GList *list;
334   gint child_height;
335   gint height;
336   gint child_min, child_nat;
337
338   path_bar = GTK_PATH_BAR (widget);
339
340   *minimum = *natural = 0;
341   height = 0;
342
343   for (list = path_bar->button_list; list; list = list->next)
344     {
345       button_data = BUTTON_DATA (list->data);
346       gtk_widget_get_preferred_width (button_data->button, &child_min, &child_nat);
347       gtk_widget_get_preferred_height (button_data->button, &child_height, NULL);
348       height = MAX (height, child_height);
349
350       if (button_data->type == NORMAL_BUTTON)
351         {
352           /* Use 2*Height as button width because of ellipsized label.  */
353           child_min = MAX (child_min, child_height * 2);
354           child_nat = MAX (child_min, child_height * 2);
355         }
356
357       *minimum = MAX (*minimum, child_min);
358       *natural = MAX (*natural, child_nat);
359     }
360
361   /* Add space for slider, if we have more than one path */
362   /* Theoretically, the slider could be bigger than the other button.  But we're
363    * not going to worry about that now.
364    */
365   path_bar->slider_width = MIN (height * 2 / 3 + 5, height);
366   if (path_bar->button_list && path_bar->button_list->next != NULL)
367     {
368       *minimum += (path_bar->spacing + path_bar->slider_width) * 2;
369       *natural += (path_bar->spacing + path_bar->slider_width) * 2;
370     }
371 }
372
373 static void
374 gtk_path_bar_get_preferred_height (GtkWidget *widget,
375                                    gint      *minimum,
376                                    gint      *natural)
377 {
378   ButtonData *button_data;
379   GtkPathBar *path_bar;
380   GList *list;
381   gint child_min, child_nat;
382
383   path_bar = GTK_PATH_BAR (widget);
384
385   *minimum = *natural = 0;
386
387   for (list = path_bar->button_list; list; list = list->next)
388     {
389       button_data = BUTTON_DATA (list->data);
390       gtk_widget_get_preferred_height (button_data->button, &child_min, &child_nat);
391
392       *minimum = MAX (*minimum, child_min);
393       *natural = MAX (*natural, child_nat);
394     }
395 }
396
397 static void
398 gtk_path_bar_update_slider_buttons (GtkPathBar *path_bar)
399 {
400   if (path_bar->button_list)
401     {
402       GtkWidget *button;
403
404       button = BUTTON_DATA (path_bar->button_list->data)->button;
405       if (gtk_widget_get_child_visible (button))
406         {
407           gtk_path_bar_stop_scrolling (path_bar);
408           gtk_widget_set_sensitive (path_bar->down_slider_button, FALSE);
409         }
410       else
411         gtk_widget_set_sensitive (path_bar->down_slider_button, TRUE);
412
413       button = BUTTON_DATA (g_list_last (path_bar->button_list)->data)->button;
414       if (gtk_widget_get_child_visible (button))
415         {
416           gtk_path_bar_stop_scrolling (path_bar);
417           gtk_widget_set_sensitive (path_bar->up_slider_button, FALSE);
418         }
419       else
420         gtk_widget_set_sensitive (path_bar->up_slider_button, TRUE);
421     }
422 }
423
424 static void
425 gtk_path_bar_map (GtkWidget *widget)
426 {
427   gdk_window_show (GTK_PATH_BAR (widget)->event_window);
428
429   GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->map (widget);
430 }
431
432 static void
433 gtk_path_bar_unmap (GtkWidget *widget)
434 {
435   gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
436   gdk_window_hide (GTK_PATH_BAR (widget)->event_window);
437
438   GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->unmap (widget);
439 }
440
441 static void
442 gtk_path_bar_realize (GtkWidget *widget)
443 {
444   GtkPathBar *path_bar;
445   GtkAllocation allocation;
446   GdkWindow *window;
447   GdkWindowAttr attributes;
448   gint attributes_mask;
449
450   gtk_widget_set_realized (widget, TRUE);
451
452   path_bar = GTK_PATH_BAR (widget);
453   window = gtk_widget_get_parent_window (widget);
454   gtk_widget_set_window (widget, window);
455   g_object_ref (window);
456
457   gtk_widget_get_allocation (widget, &allocation);
458
459   attributes.window_type = GDK_WINDOW_CHILD;
460   attributes.x = allocation.x;
461   attributes.y = allocation.y;
462   attributes.width = allocation.width;
463   attributes.height = allocation.height;
464   attributes.wclass = GDK_INPUT_ONLY;
465   attributes.event_mask = gtk_widget_get_events (widget);
466   attributes.event_mask |= GDK_SCROLL_MASK;
467   attributes_mask = GDK_WA_X | GDK_WA_Y;
468
469   path_bar->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
470                                            &attributes, attributes_mask);
471   gdk_window_set_user_data (path_bar->event_window, widget);
472 }
473
474 static void
475 gtk_path_bar_unrealize (GtkWidget *widget)
476 {
477   GtkPathBar *path_bar;
478
479   path_bar = GTK_PATH_BAR (widget);
480
481   gdk_window_set_user_data (path_bar->event_window, NULL);
482   gdk_window_destroy (path_bar->event_window);
483   path_bar->event_window = NULL;
484
485   GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->unrealize (widget);
486 }
487
488 static void
489 child_ordering_changed (GtkPathBar *path_bar)
490 {
491   GList *l;
492
493   if (path_bar->up_slider_button)
494     gtk_widget_reset_style (path_bar->up_slider_button);
495   if (path_bar->down_slider_button)
496     gtk_widget_reset_style (path_bar->down_slider_button);
497
498   for (l = path_bar->button_list; l; l = l->next)
499     {
500       ButtonData *data = l->data;
501
502       gtk_widget_reset_style (data->button);
503     }
504 }
505
506 /* This is a tad complicated
507  */
508 static void
509 gtk_path_bar_size_allocate (GtkWidget     *widget,
510                             GtkAllocation *allocation)
511 {
512   GtkWidget *child;
513   GtkPathBar *path_bar = GTK_PATH_BAR (widget);
514   GtkTextDirection direction;
515   GtkAllocation child_allocation;
516   GList *list, *first_button;
517   gint width;
518   gint allocation_width;
519   gboolean need_sliders = FALSE;
520   gint up_slider_offset = 0;
521   GtkRequisition child_requisition;
522
523   gtk_widget_set_allocation (widget, allocation);
524
525   if (gtk_widget_get_realized (widget))
526     gdk_window_move_resize (path_bar->event_window,
527                             allocation->x, allocation->y,
528                             allocation->width, allocation->height);
529
530   /* No path is set; we don't have to allocate anything. */
531   if (path_bar->button_list == NULL)
532     return;
533
534   direction = gtk_widget_get_direction (widget);
535   allocation_width = allocation->width;
536
537   /* First, we check to see if we need the scrollbars. */
538   if (path_bar->fake_root)
539     width = path_bar->spacing + path_bar->slider_width;
540   else
541     width = 0;
542
543   for (list = path_bar->button_list; list; list = list->next)
544     {
545       child = BUTTON_DATA (list->data)->button;
546
547       gtk_widget_get_preferred_size (child, &child_requisition, NULL);
548
549       width += child_requisition.width + path_bar->spacing;
550       if (list == path_bar->fake_root)
551         break;
552     }
553
554   if (width <= allocation_width)
555     {
556       if (path_bar->fake_root)
557         first_button = path_bar->fake_root;
558       else
559         first_button = g_list_last (path_bar->button_list);
560     }
561   else
562     {
563       gboolean reached_end = FALSE;
564       gint slider_space = 2 * (path_bar->spacing + path_bar->slider_width);
565
566       if (path_bar->first_scrolled_button)
567         first_button = path_bar->first_scrolled_button;
568       else
569         first_button = path_bar->button_list;
570       need_sliders = TRUE;
571       
572       /* To see how much space we have, and how many buttons we can display.
573        * We start at the first button, count forward until hit the new
574        * button, then count backwards.
575        */
576       /* Count down the path chain towards the end. */
577       gtk_widget_get_preferred_size (BUTTON_DATA (first_button->data)->button,
578                                      &child_requisition, NULL);
579
580       width = child_requisition.width;
581       list = first_button->prev;
582       while (list && !reached_end)
583         {
584           child = BUTTON_DATA (list->data)->button;
585
586           gtk_widget_get_preferred_size (child, &child_requisition, NULL);
587
588           if (width + child_requisition.width +
589               path_bar->spacing + slider_space > allocation_width)
590             reached_end = TRUE;
591           else if (list == path_bar->fake_root)
592             break;
593           else
594             width += child_requisition.width + path_bar->spacing;
595
596           list = list->prev;
597         }
598
599       /* Finally, we walk up, seeing how many of the previous buttons we can
600        * add */
601       while (first_button->next && !reached_end)
602         {
603           child = BUTTON_DATA (first_button->next->data)->button;
604
605           gtk_widget_get_preferred_size (child, &child_requisition, NULL);
606
607           if (width + child_requisition.width + path_bar->spacing + slider_space > allocation_width)
608             {
609               reached_end = TRUE;
610             }
611           else
612             {
613               width += child_requisition.width + path_bar->spacing;
614               if (first_button == path_bar->fake_root)
615                 break;
616               first_button = first_button->next;
617             }
618         }
619     }
620
621   /* Now, we allocate space to the buttons */
622   child_allocation.y = allocation->y;
623   child_allocation.height = allocation->height;
624
625   if (direction == GTK_TEXT_DIR_RTL)
626     {
627       child_allocation.x = allocation->x + allocation->width;
628       if (need_sliders || path_bar->fake_root)
629         {
630           child_allocation.x -= (path_bar->spacing + path_bar->slider_width);
631           up_slider_offset = allocation->width - path_bar->slider_width;
632         }
633     }
634   else
635     {
636       child_allocation.x = allocation->x;
637       if (need_sliders || path_bar->fake_root)
638         {
639           up_slider_offset = 0;
640           child_allocation.x += (path_bar->spacing + path_bar->slider_width);
641         }
642     }
643
644   for (list = first_button; list; list = list->prev)
645     {
646       GtkAllocation widget_allocation;
647       ButtonData *button_data;
648
649       button_data = BUTTON_DATA (list->data);
650       child = button_data->button;
651
652       gtk_widget_get_preferred_size (child, &child_requisition, NULL);
653
654       child_allocation.width = MIN (child_requisition.width,
655                                     allocation_width - (path_bar->spacing + path_bar->slider_width) * 2);
656
657       if (direction == GTK_TEXT_DIR_RTL)
658         child_allocation.x -= child_allocation.width;
659
660       /* Check to see if we've don't have any more space to allocate buttons */
661       if (need_sliders && direction == GTK_TEXT_DIR_RTL)
662         {
663           gtk_widget_get_allocation (widget, &widget_allocation);
664           if (child_allocation.x - path_bar->spacing - path_bar->slider_width < widget_allocation.x)
665             break;
666         }
667       else if (need_sliders && direction == GTK_TEXT_DIR_LTR)
668         {
669           gtk_widget_get_allocation (widget, &widget_allocation);
670           if (child_allocation.x + child_allocation.width + path_bar->spacing + path_bar->slider_width >
671               widget_allocation.x + allocation_width)
672             break;
673         }
674
675       if (child_allocation.width < child_requisition.width)
676         {
677           if (!gtk_widget_get_has_tooltip (child))
678             gtk_widget_set_tooltip_text (child, button_data->dir_name);
679         }
680       else if (gtk_widget_get_has_tooltip (child))
681         gtk_widget_set_tooltip_text (child, NULL);
682       
683       gtk_widget_set_child_visible (child, TRUE);
684       gtk_widget_size_allocate (child, &child_allocation);
685
686       if (direction == GTK_TEXT_DIR_RTL)
687         child_allocation.x -= path_bar->spacing;
688       else
689         child_allocation.x += child_allocation.width + path_bar->spacing;
690     }
691   /* Now we go hide all the widgets that don't fit */
692   while (list)
693     {
694       gtk_widget_set_child_visible (BUTTON_DATA (list->data)->button, FALSE);
695       list = list->prev;
696     }
697   for (list = first_button->next; list; list = list->next)
698     {
699       gtk_widget_set_child_visible (BUTTON_DATA (list->data)->button, FALSE);
700     }
701
702   if (need_sliders || path_bar->fake_root)
703     {
704       child_allocation.width = path_bar->slider_width;
705       child_allocation.x = up_slider_offset + allocation->x;
706       gtk_widget_size_allocate (path_bar->up_slider_button, &child_allocation);
707
708       gtk_widget_set_child_visible (path_bar->up_slider_button, TRUE);
709       gtk_widget_show_all (path_bar->up_slider_button);
710     }
711   else
712     gtk_widget_set_child_visible (path_bar->up_slider_button, FALSE);
713       
714   if (need_sliders)
715     {
716       child_allocation.width = path_bar->slider_width;
717
718       if (direction == GTK_TEXT_DIR_RTL)
719         child_allocation.x = 0;
720       else
721         child_allocation.x = allocation->width - path_bar->slider_width;
722
723       child_allocation.x += allocation->x;
724       
725       gtk_widget_size_allocate (path_bar->down_slider_button, &child_allocation);
726
727       gtk_widget_set_child_visible (path_bar->down_slider_button, TRUE);
728       gtk_widget_show_all (path_bar->down_slider_button);
729       gtk_path_bar_update_slider_buttons (path_bar);
730     }
731   else
732     gtk_widget_set_child_visible (path_bar->down_slider_button, FALSE);
733
734   child_ordering_changed (path_bar);
735 }
736
737 static void
738 gtk_path_bar_style_updated (GtkWidget *widget)
739 {
740   GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->style_updated (widget);
741
742   gtk_path_bar_check_icon_theme (GTK_PATH_BAR (widget));
743 }
744
745 static void
746 gtk_path_bar_screen_changed (GtkWidget *widget,
747                              GdkScreen *previous_screen)
748 {
749   if (GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->screen_changed)
750     GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->screen_changed (widget, previous_screen);
751
752   /* We might nave a new settings, so we remove the old one */
753   if (previous_screen)
754     remove_settings_signal (GTK_PATH_BAR (widget), previous_screen);
755
756   gtk_path_bar_check_icon_theme (GTK_PATH_BAR (widget));
757 }
758
759 static gboolean
760 gtk_path_bar_scroll (GtkWidget      *widget,
761                      GdkEventScroll *event)
762 {
763   switch (event->direction)
764     {
765     case GDK_SCROLL_RIGHT:
766     case GDK_SCROLL_DOWN:
767       gtk_path_bar_scroll_down (GTK_PATH_BAR (widget));
768       break;
769     case GDK_SCROLL_LEFT:
770     case GDK_SCROLL_UP:
771       gtk_path_bar_scroll_up (GTK_PATH_BAR (widget));
772       break;
773     case GDK_SCROLL_SMOOTH:
774       break;
775     }
776
777   return TRUE;
778 }
779
780 static void
781 gtk_path_bar_add (GtkContainer *container,
782                   GtkWidget    *widget)
783
784 {
785   gtk_widget_set_parent (widget, GTK_WIDGET (container));
786 }
787
788 static void
789 gtk_path_bar_remove_1 (GtkContainer *container,
790                        GtkWidget    *widget)
791 {
792   gboolean was_visible = gtk_widget_get_visible (widget);
793   gtk_widget_unparent (widget);
794   if (was_visible)
795     gtk_widget_queue_resize (GTK_WIDGET (container));
796 }
797
798 static void
799 gtk_path_bar_remove (GtkContainer *container,
800                      GtkWidget    *widget)
801 {
802   GtkPathBar *path_bar;
803   GList *children;
804
805   path_bar = GTK_PATH_BAR (container);
806
807   if (widget == path_bar->up_slider_button)
808     {
809       gtk_path_bar_remove_1 (container, widget);
810       path_bar->up_slider_button = NULL;
811       return;
812     }
813
814   if (widget == path_bar->down_slider_button)
815     {
816       gtk_path_bar_remove_1 (container, widget);
817       path_bar->down_slider_button = NULL;
818       return;
819     }
820
821   children = path_bar->button_list;
822   while (children)
823     {
824       if (widget == BUTTON_DATA (children->data)->button)
825         {
826           gtk_path_bar_remove_1 (container, widget);
827           path_bar->button_list = g_list_remove_link (path_bar->button_list, children);
828           g_list_free (children);
829           return;
830         }
831       
832       children = children->next;
833     }
834 }
835
836 static void
837 gtk_path_bar_forall (GtkContainer *container,
838                      gboolean      include_internals,
839                      GtkCallback   callback,
840                      gpointer      callback_data)
841 {
842   GtkPathBar *path_bar;
843   GList *children;
844
845   g_return_if_fail (callback != NULL);
846   path_bar = GTK_PATH_BAR (container);
847
848   children = path_bar->button_list;
849   while (children)
850     {
851       GtkWidget *child;
852       child = BUTTON_DATA (children->data)->button;
853       children = children->next;
854
855       (* callback) (child, callback_data);
856     }
857
858   if (path_bar->up_slider_button)
859     (* callback) (path_bar->up_slider_button, callback_data);
860
861   if (path_bar->down_slider_button)
862     (* callback) (path_bar->down_slider_button, callback_data);
863 }
864
865 static GtkWidgetPath *
866 gtk_path_bar_get_path_for_child (GtkContainer *container,
867                                  GtkWidget    *child)
868 {
869   GtkPathBar *path_bar = GTK_PATH_BAR (container);
870   GtkWidgetPath *path;
871
872   path = gtk_widget_path_copy (gtk_widget_get_path (GTK_WIDGET (path_bar)));
873
874   if (gtk_widget_get_visible (child) &&
875       gtk_widget_get_child_visible (child))
876     {
877       GtkWidgetPath *sibling_path;
878       GList *visible_children;
879       GList *l;
880       int pos;
881
882       /* 1. Build the list of visible children, in visually left-to-right order
883        * (i.e. independently of the widget's direction).  Note that our
884        * button_list is stored in innermost-to-outermost path order!
885        */
886
887       visible_children = NULL;
888
889       if (gtk_widget_get_visible (path_bar->down_slider_button) &&
890           gtk_widget_get_child_visible (path_bar->down_slider_button))
891         visible_children = g_list_prepend (visible_children, path_bar->down_slider_button);
892
893       for (l = path_bar->button_list; l; l = l->next)
894         {
895           ButtonData *data = l->data;
896
897           if (gtk_widget_get_visible (data->button) &&
898               gtk_widget_get_child_visible (data->button))
899             visible_children = g_list_prepend (visible_children, data->button);
900         }
901
902       if (gtk_widget_get_visible (path_bar->up_slider_button) &&
903           gtk_widget_get_child_visible (path_bar->up_slider_button))
904         visible_children = g_list_prepend (visible_children, path_bar->up_slider_button);
905
906       if (gtk_widget_get_direction (GTK_WIDGET (path_bar)) == GTK_TEXT_DIR_RTL)
907         visible_children = g_list_reverse (visible_children);
908
909       /* 2. Find the index of the child within that list */
910
911       pos = 0;
912
913       for (l = visible_children; l; l = l->next)
914         {
915           GtkWidget *button = l->data;
916
917           if (button == child)
918             break;
919
920           pos++;
921         }
922
923       /* 3. Build the path */
924
925       sibling_path = gtk_widget_path_new ();
926
927       for (l = visible_children; l; l = l->next)
928         gtk_widget_path_append_for_widget (sibling_path, l->data);
929
930       gtk_widget_path_append_with_siblings (path, sibling_path, pos);
931
932       g_list_free (visible_children);
933       gtk_widget_path_unref (sibling_path);
934     }
935   else
936     gtk_widget_path_append_for_widget (path, child);
937
938   return path;
939 }
940
941 static void
942 gtk_path_bar_scroll_down (GtkPathBar *path_bar)
943 {
944   GtkAllocation allocation, button_allocation;
945   GList *list;
946   GList *down_button = NULL;
947   gint space_available;
948
949   if (path_bar->ignore_click)
950     {
951       path_bar->ignore_click = FALSE;
952       return;   
953     }
954
955   if (gtk_widget_get_child_visible (BUTTON_DATA (path_bar->button_list->data)->button))
956     {
957       /* Return if the last button is already visible */
958       return;
959     }
960
961   gtk_widget_queue_resize (GTK_WIDGET (path_bar));
962
963   /* We find the button at the 'down' end that we have to make
964    * visible */
965   for (list = path_bar->button_list; list; list = list->next)
966     {
967       if (list->next && gtk_widget_get_child_visible (BUTTON_DATA (list->next->data)->button))
968         {
969           down_button = list;
970           break;
971         }
972     }
973
974   gtk_widget_get_allocation (GTK_WIDGET (path_bar), &allocation);
975   gtk_widget_get_allocation (BUTTON_DATA (down_button->data)->button, &button_allocation);
976
977   space_available = (allocation.width
978                      - 2 * path_bar->spacing - 2 * path_bar->slider_width
979                      - button_allocation.width);
980   path_bar->first_scrolled_button = down_button;
981   
982   /* We have space_available free space that's not being used.  
983    * So we walk down from the end, adding buttons until we use all free space.
984    */
985   while (space_available > 0)
986     {
987       path_bar->first_scrolled_button = down_button;
988       down_button = down_button->next;
989       if (!down_button)
990         break;
991       space_available -= (button_allocation.width
992                           + path_bar->spacing);
993     }
994 }
995
996 static void
997 gtk_path_bar_scroll_up (GtkPathBar *path_bar)
998 {
999   GList *list;
1000
1001   if (path_bar->ignore_click)
1002     {
1003       path_bar->ignore_click = FALSE;
1004       return;   
1005     }
1006
1007   list = g_list_last (path_bar->button_list);
1008
1009   if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button))
1010     {
1011       /* Return if the first button is already visible */
1012       return;
1013     }
1014
1015   gtk_widget_queue_resize (GTK_WIDGET (path_bar));
1016
1017   for ( ; list; list = list->prev)
1018     {
1019       if (list->prev && gtk_widget_get_child_visible (BUTTON_DATA (list->prev->data)->button))
1020         {
1021           if (list->prev == path_bar->fake_root)
1022             path_bar->fake_root = NULL;
1023           path_bar->first_scrolled_button = list;
1024           return;
1025         }
1026     }
1027 }
1028
1029 static gboolean
1030 gtk_path_bar_scroll_timeout (GtkPathBar *path_bar)
1031 {
1032   gboolean retval = FALSE;
1033
1034   if (path_bar->timer)
1035     {
1036       if (path_bar->scrolling_up)
1037         gtk_path_bar_scroll_up (path_bar);
1038       else if (path_bar->scrolling_down)
1039         gtk_path_bar_scroll_down (path_bar);
1040
1041       if (path_bar->need_timer) 
1042         {
1043           GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (path_bar));
1044           guint        timeout;
1045
1046           g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
1047
1048           path_bar->need_timer = FALSE;
1049
1050           path_bar->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
1051                                            (GSourceFunc)gtk_path_bar_scroll_timeout,
1052                                            path_bar);
1053         }
1054       else
1055         retval = TRUE;
1056     }
1057
1058   return retval;
1059 }
1060
1061 static void 
1062 gtk_path_bar_stop_scrolling (GtkPathBar *path_bar)
1063 {
1064   if (path_bar->timer)
1065     {
1066       g_source_remove (path_bar->timer);
1067       path_bar->timer = 0;
1068       path_bar->need_timer = FALSE;
1069     }
1070 }
1071
1072 static gboolean
1073 gtk_path_bar_slider_up_defocus (GtkWidget      *widget,
1074                                     GdkEventButton *event,
1075                                     GtkPathBar     *path_bar)
1076 {
1077   GList *list;
1078   GList *up_button = NULL;
1079
1080   if (event->type != GDK_FOCUS_CHANGE)
1081     return FALSE;
1082
1083   for (list = g_list_last (path_bar->button_list); list; list = list->prev)
1084     {
1085       if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button))
1086         {
1087           up_button = list;
1088           break;
1089         }
1090     }
1091
1092   /* don't let the focus vanish */
1093   if ((!gtk_widget_is_sensitive (path_bar->up_slider_button)) ||
1094       (!gtk_widget_get_child_visible (path_bar->up_slider_button)))
1095     gtk_widget_grab_focus (BUTTON_DATA (up_button->data)->button);
1096
1097   return FALSE;
1098 }
1099
1100 static gboolean
1101 gtk_path_bar_slider_down_defocus (GtkWidget      *widget,
1102                                     GdkEventButton *event,
1103                                     GtkPathBar     *path_bar)
1104 {
1105   GList *list;
1106   GList *down_button = NULL;
1107
1108   if (event->type != GDK_FOCUS_CHANGE)
1109     return FALSE;
1110
1111   for (list = path_bar->button_list; list; list = list->next)
1112     {
1113       if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button))
1114         {
1115           down_button = list;
1116           break;
1117         }
1118     }
1119
1120   /* don't let the focus vanish */
1121   if ((!gtk_widget_is_sensitive (path_bar->down_slider_button)) ||
1122       (!gtk_widget_get_child_visible (path_bar->down_slider_button)))
1123     gtk_widget_grab_focus (BUTTON_DATA (down_button->data)->button);
1124
1125   return FALSE;
1126 }
1127
1128 static gboolean
1129 gtk_path_bar_slider_button_press (GtkWidget      *widget, 
1130                                   GdkEventButton *event,
1131                                   GtkPathBar     *path_bar)
1132 {
1133   if (event->type != GDK_BUTTON_PRESS || event->button != GDK_BUTTON_PRIMARY)
1134     return FALSE;
1135
1136   path_bar->ignore_click = FALSE;
1137
1138   if (widget == path_bar->up_slider_button)
1139     {
1140       path_bar->scrolling_down = FALSE;
1141       path_bar->scrolling_up = TRUE;
1142       gtk_path_bar_scroll_up (path_bar);
1143     }
1144   else if (widget == path_bar->down_slider_button)
1145     {
1146       path_bar->scrolling_up = FALSE;
1147       path_bar->scrolling_down = TRUE;
1148       gtk_path_bar_scroll_down (path_bar);
1149     }
1150
1151   if (!path_bar->timer)
1152     {
1153       GtkSettings *settings = gtk_widget_get_settings (widget);
1154       guint        timeout;
1155
1156       g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
1157
1158       path_bar->need_timer = TRUE;
1159       path_bar->timer = gdk_threads_add_timeout (timeout,
1160                                        (GSourceFunc)gtk_path_bar_scroll_timeout,
1161                                        path_bar);
1162     }
1163
1164   return FALSE;
1165 }
1166
1167 static gboolean
1168 gtk_path_bar_slider_button_release (GtkWidget      *widget, 
1169                                     GdkEventButton *event,
1170                                     GtkPathBar     *path_bar)
1171 {
1172   if (event->type != GDK_BUTTON_RELEASE)
1173     return FALSE;
1174
1175   path_bar->ignore_click = TRUE;
1176   gtk_path_bar_stop_scrolling (path_bar);
1177
1178   return FALSE;
1179 }
1180
1181 static void
1182 gtk_path_bar_grab_notify (GtkWidget *widget,
1183                           gboolean   was_grabbed)
1184 {
1185   if (!was_grabbed)
1186     gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
1187 }
1188
1189 static void
1190 gtk_path_bar_state_changed (GtkWidget    *widget,
1191                             GtkStateType  previous_state)
1192 {
1193   if (!gtk_widget_is_sensitive (widget))
1194     gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
1195 }
1196
1197
1198 /* Changes the icons wherever it is needed */
1199 static void
1200 reload_icons (GtkPathBar *path_bar)
1201 {
1202   GList *list;
1203
1204   if (path_bar->root_icon)
1205     {
1206       g_object_unref (path_bar->root_icon);
1207       path_bar->root_icon = NULL;
1208     }
1209   if (path_bar->home_icon)
1210     {
1211       g_object_unref (path_bar->home_icon);
1212       path_bar->home_icon = NULL;
1213     }
1214   if (path_bar->desktop_icon)
1215     {
1216       g_object_unref (path_bar->desktop_icon);
1217       path_bar->desktop_icon = NULL;
1218     }
1219
1220   for (list = path_bar->button_list; list; list = list->next)
1221     {
1222       ButtonData *button_data;
1223       gboolean current_dir;
1224
1225       button_data = BUTTON_DATA (list->data);
1226       if (button_data->type != NORMAL_BUTTON)
1227         {
1228           current_dir = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_data->button));
1229           gtk_path_bar_update_button_appearance (path_bar, button_data, current_dir);
1230         }
1231     }
1232   
1233 }
1234
1235 static void
1236 change_icon_theme (GtkPathBar *path_bar)
1237 {
1238   GtkSettings *settings;
1239   gint width, height;
1240
1241   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (path_bar)));
1242
1243   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
1244     path_bar->icon_size = MAX (width, height);
1245   else
1246     path_bar->icon_size = FALLBACK_ICON_SIZE;
1247
1248   reload_icons (path_bar);
1249 }
1250 /* Callback used when a GtkSettings value changes */
1251 static void
1252 settings_notify_cb (GObject    *object,
1253                     GParamSpec *pspec,
1254                     GtkPathBar *path_bar)
1255 {
1256   const char *name;
1257
1258   name = g_param_spec_get_name (pspec);
1259
1260   if (! strcmp (name, "gtk-icon-theme-name") ||
1261       ! strcmp (name, "gtk-icon-sizes"))
1262     change_icon_theme (path_bar);
1263 }
1264
1265 static void
1266 gtk_path_bar_check_icon_theme (GtkPathBar *path_bar)
1267 {
1268   GtkSettings *settings;
1269
1270   if (path_bar->settings_signal_id)
1271     return;
1272
1273   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (path_bar)));
1274   path_bar->settings_signal_id = g_signal_connect (settings, "notify", G_CALLBACK (settings_notify_cb), path_bar);
1275
1276   change_icon_theme (path_bar);
1277 }
1278
1279 /* Public functions and their helpers */
1280 static void
1281 gtk_path_bar_clear_buttons (GtkPathBar *path_bar)
1282 {
1283   while (path_bar->button_list != NULL)
1284     {
1285       gtk_container_remove (GTK_CONTAINER (path_bar), BUTTON_DATA (path_bar->button_list->data)->button);
1286     }
1287   path_bar->first_scrolled_button = NULL;
1288   path_bar->fake_root = NULL;
1289 }
1290
1291 static void
1292 button_clicked_cb (GtkWidget *button,
1293                    gpointer   data)
1294 {
1295   ButtonData *button_data;
1296   GtkPathBar *path_bar;
1297   GList *button_list;
1298   gboolean child_is_hidden;
1299   GFile *child_file;
1300
1301   button_data = BUTTON_DATA (data);
1302   if (button_data->ignore_changes)
1303     return;
1304
1305   path_bar = GTK_PATH_BAR (gtk_widget_get_parent (button));
1306
1307   button_list = g_list_find (path_bar->button_list, button_data);
1308   g_assert (button_list != NULL);
1309
1310   g_signal_handlers_block_by_func (button,
1311                                    G_CALLBACK (button_clicked_cb), data);
1312   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
1313   g_signal_handlers_unblock_by_func (button,
1314                                      G_CALLBACK (button_clicked_cb), data);
1315
1316   if (button_list->prev)
1317     {
1318       ButtonData *child_data;
1319
1320       child_data = BUTTON_DATA (button_list->prev->data);
1321       child_file = child_data->file;
1322       child_is_hidden = child_data->file_is_hidden;
1323     }
1324   else
1325     {
1326       child_file = NULL;
1327       child_is_hidden = FALSE;
1328     }
1329
1330   g_signal_emit (path_bar, path_bar_signals [PATH_CLICKED], 0,
1331                  button_data->file, child_file, child_is_hidden);
1332 }
1333
1334 struct SetButtonImageData
1335 {
1336   GtkPathBar *path_bar;
1337   ButtonData *button_data;
1338 };
1339
1340 static void
1341 set_button_image_get_info_cb (GCancellable *cancellable,
1342                               GFileInfo    *info,
1343                               const GError *error,
1344                               gpointer      user_data)
1345 {
1346   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1347   GdkPixbuf *pixbuf;
1348   struct SetButtonImageData *data = user_data;
1349
1350   if (cancellable != data->button_data->cancellable)
1351     goto out;
1352
1353   data->button_data->cancellable = NULL;
1354
1355   if (!data->button_data->button)
1356     {
1357       g_free (data->button_data);
1358       goto out;
1359     }
1360
1361   if (cancelled || error)
1362     goto out;
1363
1364   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->path_bar),
1365                                        data->path_bar->icon_size);
1366   gtk_image_set_from_pixbuf (GTK_IMAGE (data->button_data->image), pixbuf);
1367
1368   switch (data->button_data->type)
1369     {
1370       case HOME_BUTTON:
1371         if (data->path_bar->home_icon)
1372           g_object_unref (pixbuf);
1373         else
1374           data->path_bar->home_icon = pixbuf;
1375         break;
1376
1377       case DESKTOP_BUTTON:
1378         if (data->path_bar->desktop_icon)
1379           g_object_unref (pixbuf);
1380         else
1381           data->path_bar->desktop_icon = pixbuf;
1382         break;
1383
1384       default:
1385         break;
1386     };
1387
1388 out:
1389   g_free (data);
1390   g_object_unref (cancellable);
1391 }
1392
1393 static void
1394 set_button_image (GtkPathBar *path_bar,
1395                   ButtonData *button_data)
1396 {
1397   GtkFileSystemVolume *volume;
1398   struct SetButtonImageData *data;
1399
1400   switch (button_data->type)
1401     {
1402     case ROOT_BUTTON:
1403
1404       if (path_bar->root_icon != NULL)
1405         {
1406           gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
1407           break;
1408         }
1409
1410       volume = _gtk_file_system_get_volume_for_file (path_bar->file_system, path_bar->root_file);
1411       if (volume == NULL)
1412         return;
1413
1414       path_bar->root_icon = _gtk_file_system_volume_render_icon (volume,
1415                                                                  GTK_WIDGET (path_bar),
1416                                                                  path_bar->icon_size,
1417                                                                  NULL);
1418       _gtk_file_system_volume_unref (volume);
1419
1420       gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
1421       break;
1422
1423     case HOME_BUTTON:
1424       if (path_bar->home_icon != NULL)
1425         {
1426           gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->home_icon);
1427           break;
1428         }
1429
1430       data = g_new0 (struct SetButtonImageData, 1);
1431       data->path_bar = path_bar;
1432       data->button_data = button_data;
1433
1434       if (button_data->cancellable)
1435         g_cancellable_cancel (button_data->cancellable);
1436
1437       button_data->cancellable =
1438         _gtk_file_system_get_info (path_bar->file_system,
1439                                    path_bar->home_file,
1440                                    "standard::icon",
1441                                    set_button_image_get_info_cb,
1442                                    data);
1443       break;
1444
1445     case DESKTOP_BUTTON:
1446       if (path_bar->desktop_icon != NULL)
1447         {
1448           gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->desktop_icon);
1449           break;
1450         }
1451
1452       data = g_new0 (struct SetButtonImageData, 1);
1453       data->path_bar = path_bar;
1454       data->button_data = button_data;
1455
1456       if (button_data->cancellable)
1457         g_cancellable_cancel (button_data->cancellable);
1458
1459       button_data->cancellable =
1460         _gtk_file_system_get_info (path_bar->file_system,
1461                                    path_bar->desktop_file,
1462                                    "standard::icon",
1463                                    set_button_image_get_info_cb,
1464                                    data);
1465       break;
1466     default:
1467       break;
1468     }
1469 }
1470
1471 static void
1472 button_data_free (ButtonData *button_data)
1473 {
1474   if (button_data->file)
1475     g_object_unref (button_data->file);
1476   button_data->file = NULL;
1477
1478   g_free (button_data->dir_name);
1479   button_data->dir_name = NULL;
1480
1481   button_data->button = NULL;
1482
1483   if (button_data->cancellable)
1484     g_cancellable_cancel (button_data->cancellable);
1485   else
1486     g_free (button_data);
1487 }
1488
1489 static const char *
1490 get_dir_name (ButtonData *button_data)
1491 {
1492   return button_data->dir_name;
1493 }
1494
1495 /* We always want to request the same size for the label, whether
1496  * or not the contents are bold
1497  */
1498 static void
1499 set_label_size_request (GtkWidget  *widget,
1500                         ButtonData *button_data)
1501 {
1502   const gchar *dir_name = get_dir_name (button_data);
1503   PangoLayout *layout = gtk_widget_create_pango_layout (button_data->label, dir_name);
1504   gint width, height, bold_width, bold_height;
1505   gchar *markup;
1506   
1507   pango_layout_get_pixel_size (layout, &width, &height);
1508   
1509   markup = g_markup_printf_escaped ("<b>%s</b>", dir_name);
1510   pango_layout_set_markup (layout, markup, -1);
1511   g_free (markup);
1512
1513   pango_layout_get_pixel_size (layout, &bold_width, &bold_height);
1514
1515   gtk_widget_set_size_request (widget,
1516                                MAX (width, bold_width),
1517                                MAX (height, bold_height));
1518   g_object_unref (layout);
1519 }
1520
1521 static void
1522 gtk_path_bar_update_button_appearance (GtkPathBar *path_bar,
1523                                        ButtonData *button_data,
1524                                        gboolean    current_dir)
1525 {
1526   const gchar *dir_name = get_dir_name (button_data);
1527
1528   if (button_data->label != NULL)
1529     {
1530       if (current_dir)
1531         {
1532           char *markup;
1533
1534           markup = g_markup_printf_escaped ("<b>%s</b>", dir_name);
1535           gtk_label_set_markup (GTK_LABEL (button_data->label), markup);
1536           g_free (markup);
1537         }
1538       else
1539         {
1540           gtk_label_set_text (GTK_LABEL (button_data->label), dir_name);
1541         }
1542     }
1543
1544   if (button_data->image != NULL)
1545     {
1546       set_button_image (path_bar, button_data);
1547     }
1548
1549   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_data->button)) != current_dir)
1550     {
1551       button_data->ignore_changes = TRUE;
1552       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button_data->button), current_dir);
1553       button_data->ignore_changes = FALSE;
1554     }
1555 }
1556
1557 static ButtonType
1558 find_button_type (GtkPathBar  *path_bar,
1559                   GFile       *file)
1560 {
1561   if (path_bar->root_file != NULL &&
1562       g_file_equal (file, path_bar->root_file))
1563     return ROOT_BUTTON;
1564   if (path_bar->home_file != NULL &&
1565       g_file_equal (file, path_bar->home_file))
1566     return HOME_BUTTON;
1567   if (path_bar->desktop_file != NULL &&
1568       g_file_equal (file, path_bar->desktop_file))
1569     return DESKTOP_BUTTON;
1570
1571  return NORMAL_BUTTON;
1572 }
1573
1574 static void
1575 button_drag_data_get_cb (GtkWidget        *widget,
1576                          GdkDragContext   *context,
1577                          GtkSelectionData *selection_data,
1578                          guint             info,
1579                          guint             time_,
1580                          gpointer          data)
1581 {
1582   ButtonData *button_data;
1583   char *uris[2];
1584
1585   button_data = data;
1586
1587   uris[0] = g_file_get_uri (button_data->file);
1588   uris[1] = NULL;
1589
1590   gtk_selection_data_set_uris (selection_data, uris);
1591
1592   g_free (uris[0]);
1593 }
1594
1595 static ButtonData *
1596 make_directory_button (GtkPathBar  *path_bar,
1597                        const char  *dir_name,
1598                        GFile       *file,
1599                        gboolean     current_dir,
1600                        gboolean     file_is_hidden)
1601 {
1602   AtkObject *atk_obj;
1603   GtkWidget *child = NULL;
1604   ButtonData *button_data;
1605
1606   file_is_hidden = !! file_is_hidden;
1607   /* Is it a special button? */
1608   button_data = g_new0 (ButtonData, 1);
1609
1610   button_data->type = find_button_type (path_bar, file);
1611   button_data->button = gtk_toggle_button_new ();
1612   atk_obj = gtk_widget_get_accessible (button_data->button);
1613   gtk_button_set_focus_on_click (GTK_BUTTON (button_data->button), FALSE);
1614
1615   switch (button_data->type)
1616     {
1617     case ROOT_BUTTON:
1618       button_data->image = gtk_image_new ();
1619       child = button_data->image;
1620       button_data->label = NULL;
1621       atk_object_set_name (atk_obj, _("File System Root"));
1622       break;
1623     case HOME_BUTTON:
1624     case DESKTOP_BUTTON:
1625       button_data->image = gtk_image_new ();
1626       button_data->label = gtk_label_new (NULL);
1627       child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
1628       gtk_box_pack_start (GTK_BOX (child), button_data->image, FALSE, FALSE, 0);
1629       gtk_box_pack_start (GTK_BOX (child), button_data->label, FALSE, FALSE, 0);
1630       break;
1631     case NORMAL_BUTTON:
1632     default:
1633       button_data->label = gtk_label_new (NULL);
1634       gtk_label_set_ellipsize (GTK_LABEL (button_data->label), PANGO_ELLIPSIZE_END);
1635       child = button_data->label;
1636       button_data->image = NULL;
1637     }
1638
1639   button_data->dir_name = g_strdup (dir_name);
1640   button_data->file = g_object_ref (file);
1641   button_data->file_is_hidden = file_is_hidden;
1642
1643   /*
1644    * The following function ensures that the alignment will always
1645    * request the same size whether the button's text is bold or not.
1646    */
1647   if (button_data->label)
1648     set_label_size_request (button_data->label, button_data);
1649
1650   gtk_container_add (GTK_CONTAINER (button_data->button), child);
1651   gtk_widget_show_all (button_data->button);
1652
1653   gtk_path_bar_update_button_appearance (path_bar, button_data, current_dir);
1654
1655   g_signal_connect (button_data->button, "clicked",
1656                     G_CALLBACK (button_clicked_cb),
1657                     button_data);
1658   g_object_weak_ref (G_OBJECT (button_data->button),
1659                      (GWeakNotify) button_data_free, button_data);
1660
1661   gtk_drag_source_set (button_data->button,
1662                        GDK_BUTTON1_MASK,
1663                        NULL, 0,
1664                        GDK_ACTION_COPY);
1665   gtk_drag_source_add_uri_targets (button_data->button);
1666   g_signal_connect (button_data->button, "drag-data-get",
1667                     G_CALLBACK (button_drag_data_get_cb), button_data);
1668
1669   return button_data;
1670 }
1671
1672 static gboolean
1673 gtk_path_bar_check_parent_path (GtkPathBar         *path_bar,
1674                                 GFile              *file)
1675 {
1676   GList *list;
1677   GList *current_path = NULL;
1678   gboolean need_new_fake_root = FALSE;
1679
1680   for (list = path_bar->button_list; list; list = list->next)
1681     {
1682       ButtonData *button_data;
1683
1684       button_data = list->data;
1685       if (g_file_equal (file, button_data->file))
1686         {
1687           current_path = list;
1688           break;
1689         }
1690       if (list == path_bar->fake_root)
1691         need_new_fake_root = TRUE;
1692     }
1693
1694   if (current_path)
1695     {
1696       if (need_new_fake_root)
1697         {
1698           path_bar->fake_root = NULL;
1699           for (list = current_path; list; list = list->next)
1700             {
1701               ButtonData *button_data;
1702
1703               button_data = list->data;
1704               if (BUTTON_IS_FAKE_ROOT (button_data))
1705                 {
1706                   path_bar->fake_root = list;
1707                   break;
1708                 }
1709             }
1710         }
1711
1712       for (list = path_bar->button_list; list; list = list->next)
1713         {
1714           gtk_path_bar_update_button_appearance (path_bar,
1715                                                  BUTTON_DATA (list->data),
1716                                                  (list == current_path) ? TRUE : FALSE);
1717         }
1718
1719       if (!gtk_widget_get_child_visible (BUTTON_DATA (current_path->data)->button))
1720         {
1721           path_bar->first_scrolled_button = current_path;
1722           gtk_widget_queue_resize (GTK_WIDGET (path_bar));
1723         }
1724
1725       return TRUE;
1726     }
1727   return FALSE;
1728 }
1729
1730
1731 struct SetFileInfo
1732 {
1733   GFile *file;
1734   GFile *parent_file;
1735   GtkPathBar *path_bar;
1736   GList *new_buttons;
1737   GList *fake_root;
1738   gboolean first_directory;
1739 };
1740
1741 static void
1742 gtk_path_bar_set_file_finish (struct SetFileInfo *info,
1743                               gboolean            result)
1744 {
1745   if (result)
1746     {
1747       GList *l;
1748
1749       gtk_path_bar_clear_buttons (info->path_bar);
1750       info->path_bar->button_list = g_list_reverse (info->new_buttons);
1751       info->path_bar->fake_root = info->fake_root;
1752
1753       for (l = info->path_bar->button_list; l; l = l->next)
1754         {
1755           GtkWidget *button = BUTTON_DATA (l->data)->button;
1756           gtk_container_add (GTK_CONTAINER (info->path_bar), button);
1757         }
1758
1759       child_ordering_changed (info->path_bar);
1760     }
1761   else
1762     {
1763       GList *l;
1764
1765       for (l = info->new_buttons; l; l = l->next)
1766         {
1767           ButtonData *button_data;
1768
1769           button_data = BUTTON_DATA (l->data);
1770           gtk_widget_destroy (button_data->button);
1771         }
1772
1773       g_list_free (info->new_buttons);
1774     }
1775
1776   if (info->file)
1777     g_object_unref (info->file);
1778   if (info->parent_file)
1779     g_object_unref (info->parent_file);
1780
1781   g_free (info);
1782 }
1783
1784 static void
1785 gtk_path_bar_get_info_callback (GCancellable *cancellable,
1786                                 GFileInfo    *info,
1787                                 const GError *error,
1788                                 gpointer      data)
1789 {
1790   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1791   struct SetFileInfo *file_info = data;
1792   ButtonData *button_data;
1793   const gchar *display_name;
1794   gboolean is_hidden;
1795
1796   if (cancellable != file_info->path_bar->get_info_cancellable)
1797     {
1798       gtk_path_bar_set_file_finish (file_info, FALSE);
1799       g_object_unref (cancellable);
1800       return;
1801     }
1802
1803   g_object_unref (cancellable);
1804   file_info->path_bar->get_info_cancellable = NULL;
1805
1806   if (cancelled || !info)
1807     {
1808       gtk_path_bar_set_file_finish (file_info, FALSE);
1809       return;
1810     }
1811
1812   display_name = g_file_info_get_display_name (info);
1813   is_hidden = g_file_info_get_is_hidden (info) || g_file_info_get_is_backup (info);
1814
1815   gtk_widget_push_composite_child ();
1816   button_data = make_directory_button (file_info->path_bar, display_name,
1817                                        file_info->file,
1818                                        file_info->first_directory, is_hidden);
1819   gtk_widget_pop_composite_child ();
1820   g_object_unref (file_info->file);
1821
1822   file_info->new_buttons = g_list_prepend (file_info->new_buttons, button_data);
1823
1824   if (BUTTON_IS_FAKE_ROOT (button_data))
1825     file_info->fake_root = file_info->new_buttons;
1826
1827   /* We have assigned the info for the innermost button, i.e. the deepest directory.
1828    * Now, go on to fetch the info for this directory's parent.
1829    */
1830
1831   file_info->file = file_info->parent_file;
1832   file_info->first_directory = FALSE;
1833
1834   if (!file_info->file)
1835     {
1836       /* No parent?  Okay, we are done. */
1837       gtk_path_bar_set_file_finish (file_info, TRUE);
1838       return;
1839     }
1840
1841   file_info->parent_file = g_file_get_parent (file_info->file);
1842
1843   /* Recurse asynchronously */
1844   file_info->path_bar->get_info_cancellable =
1845     _gtk_file_system_get_info (file_info->path_bar->file_system,
1846                                file_info->file,
1847                                "standard::display-name,standard::is-hidden,standard::is-backup",
1848                                gtk_path_bar_get_info_callback,
1849                                file_info);
1850 }
1851
1852 void
1853 _gtk_path_bar_set_file (GtkPathBar      *path_bar,
1854                         GFile           *file,
1855                         const gboolean   keep_trail)
1856 {
1857   struct SetFileInfo *info;
1858
1859   g_return_if_fail (GTK_IS_PATH_BAR (path_bar));
1860   g_return_if_fail (G_IS_FILE (file));
1861
1862   /* Check whether the new path is already present in the pathbar as buttons.
1863    * This could be a parent directory or a previous selected subdirectory.
1864    */
1865   if (keep_trail && gtk_path_bar_check_parent_path (path_bar, file))
1866     return;
1867
1868   info = g_new0 (struct SetFileInfo, 1);
1869   info->file = g_object_ref (file);
1870   info->path_bar = path_bar;
1871   info->first_directory = TRUE;
1872   info->parent_file = g_file_get_parent (info->file);
1873
1874   if (path_bar->get_info_cancellable)
1875     g_cancellable_cancel (path_bar->get_info_cancellable);
1876
1877   path_bar->get_info_cancellable =
1878     _gtk_file_system_get_info (path_bar->file_system,
1879                                info->file,
1880                                "standard::display-name,standard::is-hidden,standard::is-backup",
1881                                gtk_path_bar_get_info_callback,
1882                                info);
1883 }
1884
1885 /* FIXME: This should be a construct-only property */
1886 void
1887 _gtk_path_bar_set_file_system (GtkPathBar    *path_bar,
1888                                GtkFileSystem *file_system)
1889 {
1890   const char *home;
1891
1892   g_return_if_fail (GTK_IS_PATH_BAR (path_bar));
1893
1894   g_assert (path_bar->file_system == NULL);
1895
1896   path_bar->file_system = g_object_ref (file_system);
1897
1898   home = g_get_home_dir ();
1899   if (home != NULL)
1900     {
1901       const gchar *desktop;
1902
1903       path_bar->home_file = g_file_new_for_path (home);
1904       /* FIXME: Need file system backend specific way of getting the
1905        * Desktop path.
1906        */
1907       desktop = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1908       if (desktop != NULL)
1909         path_bar->desktop_file = g_file_new_for_path (desktop);
1910       else 
1911         path_bar->desktop_file = NULL;
1912     }
1913   else
1914     {
1915       path_bar->home_file = NULL;
1916       path_bar->desktop_file = NULL;
1917     }
1918   path_bar->root_file = g_file_new_for_path ("/");
1919 }
1920
1921 /**
1922  * _gtk_path_bar_up:
1923  * @path_bar: a #GtkPathBar
1924  * 
1925  * If the selected button in the pathbar is not the furthest button "up" (in the
1926  * root direction), act as if the user clicked on the next button up.
1927  **/
1928 void
1929 _gtk_path_bar_up (GtkPathBar *path_bar)
1930 {
1931   GList *l;
1932
1933   for (l = path_bar->button_list; l; l = l->next)
1934     {
1935       GtkWidget *button = BUTTON_DATA (l->data)->button;
1936       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1937         {
1938           if (l->next)
1939             {
1940               GtkWidget *next_button = BUTTON_DATA (l->next->data)->button;
1941               button_clicked_cb (next_button, l->next->data);
1942             }
1943           break;
1944         }
1945     }
1946 }
1947
1948 /**
1949  * _gtk_path_bar_down:
1950  * @path_bar: a #GtkPathBar
1951  * 
1952  * If the selected button in the pathbar is not the furthest button "down" (in the
1953  * leaf direction), act as if the user clicked on the next button down.
1954  **/
1955 void
1956 _gtk_path_bar_down (GtkPathBar *path_bar)
1957 {
1958   GList *l;
1959
1960   for (l = path_bar->button_list; l; l = l->next)
1961     {
1962       GtkWidget *button = BUTTON_DATA (l->data)->button;
1963       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1964         {
1965           if (l->prev)
1966             {
1967               GtkWidget *prev_button = BUTTON_DATA (l->prev->data)->button;
1968               button_clicked_cb (prev_button, l->prev->data);
1969             }
1970           break;
1971         }
1972     }
1973 }