]> Pileus Git - ~andy/gtk/blob - gtk/gtkpathbar.c
[GtkPathBar] Remove unused error argument
[~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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include "gtkpathbar.h"
24
25 #include <string.h>
26
27 #include "gtktogglebutton.h"
28 #include "gtkarrow.h"
29 #include "gtkdnd.h"
30 #include "gtkimage.h"
31 #include "gtkintl.h"
32 #include "gtkicontheme.h"
33 #include "gtkiconfactory.h"
34 #include "gtklabel.h"
35 #include "gtkbox.h"
36 #include "gtkmain.h"
37 #include "gtkmarshalers.h"
38
39
40 enum {
41   PATH_CLICKED,
42   LAST_SIGNAL
43 };
44
45 typedef enum {
46   NORMAL_BUTTON,
47   ROOT_BUTTON,
48   HOME_BUTTON,
49   DESKTOP_BUTTON
50 } ButtonType;
51
52 #define BUTTON_DATA(x) ((ButtonData *)(x))
53
54 #define SCROLL_DELAY_FACTOR 5
55
56 static guint path_bar_signals [LAST_SIGNAL] = { 0 };
57
58 /* Icon size for if we can't get it from the theme */
59 #define FALLBACK_ICON_SIZE 16
60
61 typedef struct _ButtonData ButtonData;
62
63 struct _ButtonData
64 {
65   GtkWidget *button;
66   ButtonType type;
67   char *dir_name;
68   GFile *file;
69   GtkWidget *image;
70   GtkWidget *label;
71   GCancellable *cancellable;
72   guint ignore_changes : 1;
73   guint file_is_hidden : 1;
74 };
75 /* This macro is used to check if a button can be used as a fake root.
76  * All buttons in front of a fake root are automatically hidden when in a
77  * directory below a fake root and replaced with the "<" arrow button.
78  */
79 #define BUTTON_IS_FAKE_ROOT(button) ((button)->type == HOME_BUTTON)
80
81 G_DEFINE_TYPE (GtkPathBar, gtk_path_bar, GTK_TYPE_CONTAINER)
82
83 static void gtk_path_bar_finalize                 (GObject          *object);
84 static void gtk_path_bar_dispose                  (GObject          *object);
85 static void gtk_path_bar_realize                  (GtkWidget        *widget);
86 static void gtk_path_bar_unrealize                (GtkWidget        *widget);
87 static void gtk_path_bar_get_preferred_width      (GtkWidget        *widget,
88                                                    gint             *minimum,
89                                                    gint             *natural);
90 static void gtk_path_bar_get_preferred_height     (GtkWidget        *widget,
91                                                    gint             *minimum,
92                                                    gint             *natural);
93 static void gtk_path_bar_map                      (GtkWidget        *widget);
94 static void gtk_path_bar_unmap                    (GtkWidget        *widget);
95 static void gtk_path_bar_size_allocate            (GtkWidget        *widget,
96                                                    GtkAllocation    *allocation);
97 static void gtk_path_bar_add                      (GtkContainer     *container,
98                                                    GtkWidget        *widget);
99 static void gtk_path_bar_remove                   (GtkContainer     *container,
100                                                    GtkWidget        *widget);
101 static void gtk_path_bar_forall                   (GtkContainer     *container,
102                                                    gboolean          include_internals,
103                                                    GtkCallback       callback,
104                                                    gpointer          callback_data);
105 static GtkWidgetPath *gtk_path_bar_get_path_for_child (GtkContainer *container,
106                                                        GtkWidget    *child);
107 static gboolean gtk_path_bar_scroll               (GtkWidget        *widget,
108                                                    GdkEventScroll   *event);
109 static void gtk_path_bar_scroll_up                (GtkPathBar       *path_bar);
110 static void gtk_path_bar_scroll_down              (GtkPathBar       *path_bar);
111 static void gtk_path_bar_stop_scrolling           (GtkPathBar       *path_bar);
112 static gboolean gtk_path_bar_slider_up_defocus    (GtkWidget        *widget,
113                                                    GdkEventButton   *event,
114                                                    GtkPathBar       *path_bar);
115 static gboolean gtk_path_bar_slider_down_defocus  (GtkWidget        *widget,
116                                                    GdkEventButton   *event,
117                                                    GtkPathBar       *path_bar);
118 static gboolean gtk_path_bar_slider_button_press  (GtkWidget        *widget,
119                                                    GdkEventButton   *event,
120                                                    GtkPathBar       *path_bar);
121 static gboolean gtk_path_bar_slider_button_release(GtkWidget        *widget,
122                                                    GdkEventButton   *event,
123                                                    GtkPathBar       *path_bar);
124 static void gtk_path_bar_grab_notify              (GtkWidget        *widget,
125                                                    gboolean          was_grabbed);
126 static void gtk_path_bar_state_changed            (GtkWidget        *widget,
127                                                    GtkStateType      previous_state);
128 static void gtk_path_bar_style_updated            (GtkWidget        *widget);
129 static void gtk_path_bar_screen_changed           (GtkWidget        *widget,
130                                                    GdkScreen        *previous_screen);
131 static void gtk_path_bar_check_icon_theme         (GtkPathBar       *path_bar);
132 static void gtk_path_bar_update_button_appearance (GtkPathBar       *path_bar,
133                                                    ButtonData       *button_data,
134                                                    gboolean          current_dir);
135
136 static void
137 on_slider_unmap (GtkWidget  *widget,
138                  GtkPathBar *path_bar)
139 {
140   if (path_bar->timer &&
141       ((widget == path_bar->up_slider_button && path_bar->scrolling_up) ||
142        (widget == path_bar->down_slider_button && path_bar->scrolling_down)))
143     gtk_path_bar_stop_scrolling (path_bar);
144 }
145
146 static GtkWidget *
147 get_slider_button (GtkPathBar  *path_bar,
148                    GtkArrowType arrow_type)
149 {
150   GtkWidget *button;
151   AtkObject *atk_obj;
152
153   gtk_widget_push_composite_child ();
154
155   button = gtk_button_new ();
156   atk_obj = gtk_widget_get_accessible (button);
157   if (arrow_type == GTK_ARROW_LEFT)
158     atk_object_set_name (atk_obj, _("Up Path"));
159   else
160     atk_object_set_name (atk_obj, _("Down Path"));
161
162   gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
163   gtk_container_add (GTK_CONTAINER (button),
164                      gtk_arrow_new (arrow_type, GTK_SHADOW_OUT));
165   gtk_container_add (GTK_CONTAINER (path_bar), button);
166   gtk_widget_show_all (button);
167
168   g_signal_connect (G_OBJECT (button), "unmap",
169                     G_CALLBACK (on_slider_unmap), path_bar);
170
171   gtk_widget_pop_composite_child ();
172
173   return button;
174 }
175
176 static void
177 gtk_path_bar_init (GtkPathBar *path_bar)
178 {
179   GtkStyleContext *context;
180
181   gtk_widget_set_has_window (GTK_WIDGET (path_bar), FALSE);
182   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (path_bar), FALSE);
183
184   path_bar->get_info_cancellable = NULL;
185
186   path_bar->spacing = 0;
187   path_bar->up_slider_button = get_slider_button (path_bar, GTK_ARROW_LEFT);
188   path_bar->down_slider_button = get_slider_button (path_bar, GTK_ARROW_RIGHT);
189   path_bar->icon_size = FALLBACK_ICON_SIZE;
190   
191   g_signal_connect_swapped (path_bar->up_slider_button, "clicked",
192                             G_CALLBACK (gtk_path_bar_scroll_up), path_bar);
193   g_signal_connect_swapped (path_bar->down_slider_button, "clicked",
194                             G_CALLBACK (gtk_path_bar_scroll_down), path_bar);
195
196   g_signal_connect (path_bar->up_slider_button, "focus-out-event",
197                     G_CALLBACK (gtk_path_bar_slider_up_defocus), path_bar);
198   g_signal_connect (path_bar->down_slider_button, "focus-out-event",
199                     G_CALLBACK (gtk_path_bar_slider_down_defocus), path_bar);
200
201   g_signal_connect (path_bar->up_slider_button, "button-press-event",
202                     G_CALLBACK (gtk_path_bar_slider_button_press), path_bar);
203   g_signal_connect (path_bar->up_slider_button, "button-release-event",
204                     G_CALLBACK (gtk_path_bar_slider_button_release), path_bar);
205   g_signal_connect (path_bar->down_slider_button, "button-press-event",
206                     G_CALLBACK (gtk_path_bar_slider_button_press), path_bar);
207   g_signal_connect (path_bar->down_slider_button, "button-release-event",
208                     G_CALLBACK (gtk_path_bar_slider_button_release), path_bar);
209
210   context = gtk_widget_get_style_context (GTK_WIDGET (path_bar));
211   gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED);
212 }
213
214 static void
215 gtk_path_bar_class_init (GtkPathBarClass *path_bar_class)
216 {
217   GObjectClass *gobject_class;
218   GtkWidgetClass *widget_class;
219   GtkContainerClass *container_class;
220
221   gobject_class = (GObjectClass *) path_bar_class;
222   widget_class = (GtkWidgetClass *) path_bar_class;
223   container_class = (GtkContainerClass *) path_bar_class;
224
225   gobject_class->finalize = gtk_path_bar_finalize;
226   gobject_class->dispose = gtk_path_bar_dispose;
227
228   widget_class->get_preferred_width = gtk_path_bar_get_preferred_width;
229   widget_class->get_preferred_height = gtk_path_bar_get_preferred_height;
230   widget_class->realize = gtk_path_bar_realize;
231   widget_class->unrealize = gtk_path_bar_unrealize;
232   widget_class->map = gtk_path_bar_map;
233   widget_class->unmap = gtk_path_bar_unmap;
234   widget_class->size_allocate = gtk_path_bar_size_allocate;
235   widget_class->style_updated = gtk_path_bar_style_updated;
236   widget_class->screen_changed = gtk_path_bar_screen_changed;
237   widget_class->grab_notify = gtk_path_bar_grab_notify;
238   widget_class->state_changed = gtk_path_bar_state_changed;
239   widget_class->scroll_event = gtk_path_bar_scroll;
240
241   container_class->add = gtk_path_bar_add;
242   container_class->forall = gtk_path_bar_forall;
243   container_class->remove = gtk_path_bar_remove;
244   container_class->get_path_for_child = gtk_path_bar_get_path_for_child;
245   gtk_container_class_handle_border_width (container_class);
246   /* FIXME: */
247   /*  container_class->child_type = gtk_path_bar_child_type;*/
248
249   path_bar_signals [PATH_CLICKED] =
250     g_signal_new (I_("path-clicked"),
251                   G_OBJECT_CLASS_TYPE (gobject_class),
252                   G_SIGNAL_RUN_FIRST,
253                   G_STRUCT_OFFSET (GtkPathBarClass, path_clicked),
254                   NULL, NULL,
255                   _gtk_marshal_VOID__POINTER_POINTER_BOOLEAN,
256                   G_TYPE_NONE, 3,
257                   G_TYPE_POINTER,
258                   G_TYPE_POINTER,
259                   G_TYPE_BOOLEAN);
260 }
261
262
263 static void
264 gtk_path_bar_finalize (GObject *object)
265 {
266   GtkPathBar *path_bar;
267
268   path_bar = GTK_PATH_BAR (object);
269
270   gtk_path_bar_stop_scrolling (path_bar);
271
272   g_list_free (path_bar->button_list);
273   if (path_bar->root_file)
274     g_object_unref (path_bar->root_file);
275   if (path_bar->home_file)
276     g_object_unref (path_bar->home_file);
277   if (path_bar->desktop_file)
278     g_object_unref (path_bar->desktop_file);
279
280   if (path_bar->root_icon)
281     g_object_unref (path_bar->root_icon);
282   if (path_bar->home_icon)
283     g_object_unref (path_bar->home_icon);
284   if (path_bar->desktop_icon)
285     g_object_unref (path_bar->desktop_icon);
286
287   if (path_bar->file_system)
288     g_object_unref (path_bar->file_system);
289
290   G_OBJECT_CLASS (gtk_path_bar_parent_class)->finalize (object);
291 }
292
293 /* Removes the settings signal handler.  It's safe to call multiple times */
294 static void
295 remove_settings_signal (GtkPathBar *path_bar,
296                         GdkScreen  *screen)
297 {
298   if (path_bar->settings_signal_id)
299     {
300       GtkSettings *settings;
301
302       settings = gtk_settings_get_for_screen (screen);
303       g_signal_handler_disconnect (settings,
304                                    path_bar->settings_signal_id);
305       path_bar->settings_signal_id = 0;
306     }
307 }
308
309 static void
310 gtk_path_bar_dispose (GObject *object)
311 {
312   GtkPathBar *path_bar = GTK_PATH_BAR (object);
313
314   remove_settings_signal (path_bar, gtk_widget_get_screen (GTK_WIDGET (object)));
315
316   if (path_bar->get_info_cancellable)
317     g_cancellable_cancel (path_bar->get_info_cancellable);
318   path_bar->get_info_cancellable = NULL;
319
320   G_OBJECT_CLASS (gtk_path_bar_parent_class)->dispose (object);
321 }
322
323 /* Size requisition:
324  * 
325  * Ideally, our size is determined by another widget, and we are just filling
326  * available space.
327  */
328 static void
329 gtk_path_bar_get_preferred_width (GtkWidget *widget,
330                                   gint      *minimum,
331                                   gint      *natural)
332 {
333   ButtonData *button_data;
334   GtkPathBar *path_bar;
335   GList *list;
336   gint child_height;
337   gint height;
338   gint child_min, child_nat;
339
340   path_bar = GTK_PATH_BAR (widget);
341
342   *minimum = *natural = 0;
343   height = 0;
344
345   for (list = path_bar->button_list; list; list = list->next)
346     {
347       button_data = BUTTON_DATA (list->data);
348       gtk_widget_get_preferred_width (button_data->button, &child_min, &child_nat);
349       gtk_widget_get_preferred_height (button_data->button, &child_height, NULL);
350       height = MAX (height, child_height);
351
352       if (button_data->type == NORMAL_BUTTON)
353         {
354           /* Use 2*Height as button width because of ellipsized label.  */
355           child_min = MAX (child_min, child_height * 2);
356           child_nat = MAX (child_min, child_height * 2);
357         }
358
359       *minimum = MAX (*minimum, child_min);
360       *natural = MAX (*natural, child_nat);
361     }
362
363   /* Add space for slider, if we have more than one path */
364   /* Theoretically, the slider could be bigger than the other button.  But we're
365    * not going to worry about that now.
366    */
367   path_bar->slider_width = MIN (height * 2 / 3 + 5, height);
368   if (path_bar->button_list && path_bar->button_list->next != NULL)
369     {
370       *minimum += (path_bar->spacing + path_bar->slider_width) * 2;
371       *natural += (path_bar->spacing + path_bar->slider_width) * 2;
372     }
373 }
374
375 static void
376 gtk_path_bar_get_preferred_height (GtkWidget *widget,
377                                    gint      *minimum,
378                                    gint      *natural)
379 {
380   ButtonData *button_data;
381   GtkPathBar *path_bar;
382   GList *list;
383   gint child_min, child_nat;
384
385   path_bar = GTK_PATH_BAR (widget);
386
387   *minimum = *natural = 0;
388
389   for (list = path_bar->button_list; list; list = list->next)
390     {
391       button_data = BUTTON_DATA (list->data);
392       gtk_widget_get_preferred_height (button_data->button, &child_min, &child_nat);
393
394       *minimum = MAX (*minimum, child_min);
395       *natural = MAX (*natural, child_nat);
396     }
397 }
398
399 static void
400 gtk_path_bar_update_slider_buttons (GtkPathBar *path_bar)
401 {
402   if (path_bar->button_list)
403     {
404       GtkWidget *button;
405
406       button = BUTTON_DATA (path_bar->button_list->data)->button;
407       if (gtk_widget_get_child_visible (button))
408         {
409           gtk_path_bar_stop_scrolling (path_bar);
410           gtk_widget_set_sensitive (path_bar->down_slider_button, FALSE);
411         }
412       else
413         gtk_widget_set_sensitive (path_bar->down_slider_button, TRUE);
414
415       button = BUTTON_DATA (g_list_last (path_bar->button_list)->data)->button;
416       if (gtk_widget_get_child_visible (button))
417         {
418           gtk_path_bar_stop_scrolling (path_bar);
419           gtk_widget_set_sensitive (path_bar->up_slider_button, FALSE);
420         }
421       else
422         gtk_widget_set_sensitive (path_bar->up_slider_button, TRUE);
423     }
424 }
425
426 static void
427 gtk_path_bar_map (GtkWidget *widget)
428 {
429   gdk_window_show (GTK_PATH_BAR (widget)->event_window);
430
431   GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->map (widget);
432 }
433
434 static void
435 gtk_path_bar_unmap (GtkWidget *widget)
436 {
437   gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
438   gdk_window_hide (GTK_PATH_BAR (widget)->event_window);
439
440   GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->unmap (widget);
441 }
442
443 static void
444 gtk_path_bar_realize (GtkWidget *widget)
445 {
446   GtkPathBar *path_bar;
447   GtkAllocation allocation;
448   GdkWindow *window;
449   GdkWindowAttr attributes;
450   gint attributes_mask;
451
452   gtk_widget_set_realized (widget, TRUE);
453
454   path_bar = GTK_PATH_BAR (widget);
455   window = gtk_widget_get_parent_window (widget);
456   gtk_widget_set_window (widget, window);
457   g_object_ref (window);
458
459   gtk_widget_get_allocation (widget, &allocation);
460
461   attributes.window_type = GDK_WINDOW_CHILD;
462   attributes.x = allocation.x;
463   attributes.y = allocation.y;
464   attributes.width = allocation.width;
465   attributes.height = allocation.height;
466   attributes.wclass = GDK_INPUT_ONLY;
467   attributes.event_mask = gtk_widget_get_events (widget);
468   attributes.event_mask |= GDK_SCROLL_MASK;
469   attributes_mask = GDK_WA_X | GDK_WA_Y;
470
471   path_bar->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
472                                            &attributes, attributes_mask);
473   gdk_window_set_user_data (path_bar->event_window, widget);
474 }
475
476 static void
477 gtk_path_bar_unrealize (GtkWidget *widget)
478 {
479   GtkPathBar *path_bar;
480
481   path_bar = GTK_PATH_BAR (widget);
482
483   gdk_window_set_user_data (path_bar->event_window, NULL);
484   gdk_window_destroy (path_bar->event_window);
485   path_bar->event_window = NULL;
486
487   GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->unrealize (widget);
488 }
489
490 static void
491 child_ordering_changed (GtkPathBar *path_bar)
492 {
493   GList *l;
494
495   if (path_bar->up_slider_button)
496     gtk_widget_reset_style (path_bar->up_slider_button);
497   if (path_bar->down_slider_button)
498     gtk_widget_reset_style (path_bar->down_slider_button);
499
500   for (l = path_bar->button_list; l; l = l->next)
501     {
502       ButtonData *data = l->data;
503
504       gtk_widget_reset_style (data->button);
505     }
506 }
507
508 /* This is a tad complicated
509  */
510 static void
511 gtk_path_bar_size_allocate (GtkWidget     *widget,
512                             GtkAllocation *allocation)
513 {
514   GtkWidget *child;
515   GtkPathBar *path_bar = GTK_PATH_BAR (widget);
516   GtkTextDirection direction;
517   GtkAllocation child_allocation;
518   GList *list, *first_button;
519   gint width;
520   gint allocation_width;
521   gboolean need_sliders = FALSE;
522   gint up_slider_offset = 0;
523   GtkRequisition child_requisition;
524
525   gtk_widget_set_allocation (widget, allocation);
526
527   if (gtk_widget_get_realized (widget))
528     gdk_window_move_resize (path_bar->event_window,
529                             allocation->x, allocation->y,
530                             allocation->width, allocation->height);
531
532   /* No path is set; we don't have to allocate anything. */
533   if (path_bar->button_list == NULL)
534     return;
535
536   direction = gtk_widget_get_direction (widget);
537   allocation_width = allocation->width;
538
539   /* First, we check to see if we need the scrollbars. */
540   if (path_bar->fake_root)
541     width = path_bar->spacing + path_bar->slider_width;
542   else
543     width = 0;
544
545   for (list = path_bar->button_list; list; list = list->next)
546     {
547       child = BUTTON_DATA (list->data)->button;
548
549       gtk_widget_get_preferred_size (child, &child_requisition, NULL);
550
551       width += child_requisition.width + path_bar->spacing;
552       if (list == path_bar->fake_root)
553         break;
554     }
555
556   if (width <= allocation_width)
557     {
558       if (path_bar->fake_root)
559         first_button = path_bar->fake_root;
560       else
561         first_button = g_list_last (path_bar->button_list);
562     }
563   else
564     {
565       gboolean reached_end = FALSE;
566       gint slider_space = 2 * (path_bar->spacing + path_bar->slider_width);
567
568       if (path_bar->first_scrolled_button)
569         first_button = path_bar->first_scrolled_button;
570       else
571         first_button = path_bar->button_list;
572       need_sliders = TRUE;
573       
574       /* To see how much space we have, and how many buttons we can display.
575        * We start at the first button, count forward until hit the new
576        * button, then count backwards.
577        */
578       /* Count down the path chain towards the end. */
579       gtk_widget_get_preferred_size (BUTTON_DATA (first_button->data)->button,
580                                      &child_requisition, NULL);
581
582       width = child_requisition.width;
583       list = first_button->prev;
584       while (list && !reached_end)
585         {
586           child = BUTTON_DATA (list->data)->button;
587
588           gtk_widget_get_preferred_size (child, &child_requisition, NULL);
589
590           if (width + child_requisition.width +
591               path_bar->spacing + slider_space > allocation_width)
592             reached_end = TRUE;
593           else if (list == path_bar->fake_root)
594             break;
595           else
596             width += child_requisition.width + path_bar->spacing;
597
598           list = list->prev;
599         }
600
601       /* Finally, we walk up, seeing how many of the previous buttons we can
602        * add */
603       while (first_button->next && !reached_end)
604         {
605           child = BUTTON_DATA (first_button->next->data)->button;
606
607           gtk_widget_get_preferred_size (child, &child_requisition, NULL);
608
609           if (width + child_requisition.width + path_bar->spacing + slider_space > allocation_width)
610             {
611               reached_end = TRUE;
612             }
613           else
614             {
615               width += child_requisition.width + path_bar->spacing;
616               if (first_button == path_bar->fake_root)
617                 break;
618               first_button = first_button->next;
619             }
620         }
621     }
622
623   /* Now, we allocate space to the buttons */
624   child_allocation.y = allocation->y;
625   child_allocation.height = allocation->height;
626
627   if (direction == GTK_TEXT_DIR_RTL)
628     {
629       child_allocation.x = allocation->x + allocation->width;
630       if (need_sliders || path_bar->fake_root)
631         {
632           child_allocation.x -= (path_bar->spacing + path_bar->slider_width);
633           up_slider_offset = allocation->width - path_bar->slider_width;
634         }
635     }
636   else
637     {
638       child_allocation.x = allocation->x;
639       if (need_sliders || path_bar->fake_root)
640         {
641           up_slider_offset = 0;
642           child_allocation.x += (path_bar->spacing + path_bar->slider_width);
643         }
644     }
645
646   for (list = first_button; list; list = list->prev)
647     {
648       GtkAllocation widget_allocation;
649       ButtonData *button_data;
650
651       button_data = BUTTON_DATA (list->data);
652       child = button_data->button;
653
654       gtk_widget_get_preferred_size (child, &child_requisition, NULL);
655
656       child_allocation.width = MIN (child_requisition.width,
657                                     allocation_width - (path_bar->spacing + path_bar->slider_width) * 2);
658
659       if (direction == GTK_TEXT_DIR_RTL)
660         child_allocation.x -= child_allocation.width;
661
662       /* Check to see if we've don't have any more space to allocate buttons */
663       if (need_sliders && direction == GTK_TEXT_DIR_RTL)
664         {
665           gtk_widget_get_allocation (widget, &widget_allocation);
666           if (child_allocation.x - path_bar->spacing - path_bar->slider_width < widget_allocation.x)
667             break;
668         }
669       else if (need_sliders && direction == GTK_TEXT_DIR_LTR)
670         {
671           gtk_widget_get_allocation (widget, &widget_allocation);
672           if (child_allocation.x + child_allocation.width + path_bar->spacing + path_bar->slider_width >
673               widget_allocation.x + allocation_width)
674             break;
675         }
676
677       if (child_allocation.width < child_requisition.width)
678         {
679           if (!gtk_widget_get_has_tooltip (child))
680             gtk_widget_set_tooltip_text (child, button_data->dir_name);
681         }
682       else if (gtk_widget_get_has_tooltip (child))
683         gtk_widget_set_tooltip_text (child, NULL);
684       
685       gtk_widget_set_child_visible (child, TRUE);
686       gtk_widget_size_allocate (child, &child_allocation);
687
688       if (direction == GTK_TEXT_DIR_RTL)
689         child_allocation.x -= path_bar->spacing;
690       else
691         child_allocation.x += child_allocation.width + path_bar->spacing;
692     }
693   /* Now we go hide all the widgets that don't fit */
694   while (list)
695     {
696       gtk_widget_set_child_visible (BUTTON_DATA (list->data)->button, FALSE);
697       list = list->prev;
698     }
699   for (list = first_button->next; list; list = list->next)
700     {
701       gtk_widget_set_child_visible (BUTTON_DATA (list->data)->button, FALSE);
702     }
703
704   if (need_sliders || path_bar->fake_root)
705     {
706       child_allocation.width = path_bar->slider_width;
707       child_allocation.x = up_slider_offset + allocation->x;
708       gtk_widget_size_allocate (path_bar->up_slider_button, &child_allocation);
709
710       gtk_widget_set_child_visible (path_bar->up_slider_button, TRUE);
711       gtk_widget_show_all (path_bar->up_slider_button);
712     }
713   else
714     gtk_widget_set_child_visible (path_bar->up_slider_button, FALSE);
715       
716   if (need_sliders)
717     {
718       child_allocation.width = path_bar->slider_width;
719
720       if (direction == GTK_TEXT_DIR_RTL)
721         child_allocation.x = 0;
722       else
723         child_allocation.x = allocation->width - path_bar->slider_width;
724
725       child_allocation.x += allocation->x;
726       
727       gtk_widget_size_allocate (path_bar->down_slider_button, &child_allocation);
728
729       gtk_widget_set_child_visible (path_bar->down_slider_button, TRUE);
730       gtk_widget_show_all (path_bar->down_slider_button);
731       gtk_path_bar_update_slider_buttons (path_bar);
732     }
733   else
734     gtk_widget_set_child_visible (path_bar->down_slider_button, FALSE);
735
736   child_ordering_changed (path_bar);
737 }
738
739 static void
740 gtk_path_bar_style_updated (GtkWidget *widget)
741 {
742   GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->style_updated (widget);
743
744   gtk_path_bar_check_icon_theme (GTK_PATH_BAR (widget));
745 }
746
747 static void
748 gtk_path_bar_screen_changed (GtkWidget *widget,
749                              GdkScreen *previous_screen)
750 {
751   if (GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->screen_changed)
752     GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->screen_changed (widget, previous_screen);
753
754   /* We might nave a new settings, so we remove the old one */
755   if (previous_screen)
756     remove_settings_signal (GTK_PATH_BAR (widget), previous_screen);
757
758   gtk_path_bar_check_icon_theme (GTK_PATH_BAR (widget));
759 }
760
761 static gboolean
762 gtk_path_bar_scroll (GtkWidget      *widget,
763                      GdkEventScroll *event)
764 {
765   switch (event->direction)
766     {
767     case GDK_SCROLL_RIGHT:
768     case GDK_SCROLL_DOWN:
769       gtk_path_bar_scroll_down (GTK_PATH_BAR (widget));
770       break;
771     case GDK_SCROLL_LEFT:
772     case GDK_SCROLL_UP:
773       gtk_path_bar_scroll_up (GTK_PATH_BAR (widget));
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 != 1)
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 }