]> Pileus Git - ~andy/gtk/blob - gtk/gtkpathbar.c
types: Clean up gtkwidget.h includes
[~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 "gtkarrow.h"
26 #include "gtkbox.h"
27 #include "gtkdnd.h"
28 #include "gtkiconfactory.h"
29 #include "gtkicontheme.h"
30 #include "gtkimage.h"
31 #include "gtkintl.h"
32 #include "gtklabel.h"
33 #include "gtkmain.h"
34 #include "gtkmarshalers.h"
35 #include "gtksettings.h"
36 #include "gtktogglebutton.h"
37
38
39 enum {
40   PATH_CLICKED,
41   LAST_SIGNAL
42 };
43
44 typedef enum {
45   NORMAL_BUTTON,
46   ROOT_BUTTON,
47   HOME_BUTTON,
48   DESKTOP_BUTTON
49 } ButtonType;
50
51 #define BUTTON_DATA(x) ((ButtonData *)(x))
52
53 #define SCROLL_DELAY_FACTOR 5
54
55 static guint path_bar_signals [LAST_SIGNAL] = { 0 };
56
57 /* Icon size for if we can't get it from the theme */
58 #define FALLBACK_ICON_SIZE 16
59
60 typedef struct _ButtonData ButtonData;
61
62 struct _ButtonData
63 {
64   GtkWidget *button;
65   ButtonType type;
66   char *dir_name;
67   GFile *file;
68   GtkWidget *image;
69   GtkWidget *label;
70   GCancellable *cancellable;
71   guint ignore_changes : 1;
72   guint file_is_hidden : 1;
73 };
74 /* This macro is used to check if a button can be used as a fake root.
75  * All buttons in front of a fake root are automatically hidden when in a
76  * directory below a fake root and replaced with the "<" arrow button.
77  */
78 #define BUTTON_IS_FAKE_ROOT(button) ((button)->type == HOME_BUTTON)
79
80 G_DEFINE_TYPE (GtkPathBar, gtk_path_bar, GTK_TYPE_CONTAINER)
81
82 static void gtk_path_bar_finalize                 (GObject          *object);
83 static void gtk_path_bar_dispose                  (GObject          *object);
84 static void gtk_path_bar_realize                  (GtkWidget        *widget);
85 static void gtk_path_bar_unrealize                (GtkWidget        *widget);
86 static void gtk_path_bar_get_preferred_width      (GtkWidget        *widget,
87                                                    gint             *minimum,
88                                                    gint             *natural);
89 static void gtk_path_bar_get_preferred_height     (GtkWidget        *widget,
90                                                    gint             *minimum,
91                                                    gint             *natural);
92 static void gtk_path_bar_map                      (GtkWidget        *widget);
93 static void gtk_path_bar_unmap                    (GtkWidget        *widget);
94 static void gtk_path_bar_size_allocate            (GtkWidget        *widget,
95                                                    GtkAllocation    *allocation);
96 static void gtk_path_bar_add                      (GtkContainer     *container,
97                                                    GtkWidget        *widget);
98 static void gtk_path_bar_remove                   (GtkContainer     *container,
99                                                    GtkWidget        *widget);
100 static void gtk_path_bar_forall                   (GtkContainer     *container,
101                                                    gboolean          include_internals,
102                                                    GtkCallback       callback,
103                                                    gpointer          callback_data);
104 static GtkWidgetPath *gtk_path_bar_get_path_for_child (GtkContainer *container,
105                                                        GtkWidget    *child);
106 static gboolean gtk_path_bar_scroll               (GtkWidget        *widget,
107                                                    GdkEventScroll   *event);
108 static void gtk_path_bar_scroll_up                (GtkPathBar       *path_bar);
109 static void gtk_path_bar_scroll_down              (GtkPathBar       *path_bar);
110 static void gtk_path_bar_stop_scrolling           (GtkPathBar       *path_bar);
111 static gboolean gtk_path_bar_slider_up_defocus    (GtkWidget        *widget,
112                                                    GdkEventButton   *event,
113                                                    GtkPathBar       *path_bar);
114 static gboolean gtk_path_bar_slider_down_defocus  (GtkWidget        *widget,
115                                                    GdkEventButton   *event,
116                                                    GtkPathBar       *path_bar);
117 static gboolean gtk_path_bar_slider_button_press  (GtkWidget        *widget,
118                                                    GdkEventButton   *event,
119                                                    GtkPathBar       *path_bar);
120 static gboolean gtk_path_bar_slider_button_release(GtkWidget        *widget,
121                                                    GdkEventButton   *event,
122                                                    GtkPathBar       *path_bar);
123 static void gtk_path_bar_grab_notify              (GtkWidget        *widget,
124                                                    gboolean          was_grabbed);
125 static void gtk_path_bar_state_changed            (GtkWidget        *widget,
126                                                    GtkStateType      previous_state);
127 static void gtk_path_bar_style_updated            (GtkWidget        *widget);
128 static void gtk_path_bar_screen_changed           (GtkWidget        *widget,
129                                                    GdkScreen        *previous_screen);
130 static void gtk_path_bar_check_icon_theme         (GtkPathBar       *path_bar);
131 static void gtk_path_bar_update_button_appearance (GtkPathBar       *path_bar,
132                                                    ButtonData       *button_data,
133                                                    gboolean          current_dir);
134
135 static void
136 on_slider_unmap (GtkWidget  *widget,
137                  GtkPathBar *path_bar)
138 {
139   if (path_bar->timer &&
140       ((widget == path_bar->up_slider_button && path_bar->scrolling_up) ||
141        (widget == path_bar->down_slider_button && path_bar->scrolling_down)))
142     gtk_path_bar_stop_scrolling (path_bar);
143 }
144
145 static GtkWidget *
146 get_slider_button (GtkPathBar  *path_bar,
147                    GtkArrowType arrow_type)
148 {
149   GtkWidget *button;
150   AtkObject *atk_obj;
151
152   gtk_widget_push_composite_child ();
153
154   button = gtk_button_new ();
155   atk_obj = gtk_widget_get_accessible (button);
156   if (arrow_type == GTK_ARROW_LEFT)
157     atk_object_set_name (atk_obj, _("Up Path"));
158   else
159     atk_object_set_name (atk_obj, _("Down Path"));
160
161   gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
162   gtk_widget_add_events (button, GDK_SCROLL_MASK);
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     case GDK_SCROLL_SMOOTH:
776       break;
777     }
778
779   return TRUE;
780 }
781
782 static void
783 gtk_path_bar_add (GtkContainer *container,
784                   GtkWidget    *widget)
785
786 {
787   gtk_widget_set_parent (widget, GTK_WIDGET (container));
788 }
789
790 static void
791 gtk_path_bar_remove_1 (GtkContainer *container,
792                        GtkWidget    *widget)
793 {
794   gboolean was_visible = gtk_widget_get_visible (widget);
795   gtk_widget_unparent (widget);
796   if (was_visible)
797     gtk_widget_queue_resize (GTK_WIDGET (container));
798 }
799
800 static void
801 gtk_path_bar_remove (GtkContainer *container,
802                      GtkWidget    *widget)
803 {
804   GtkPathBar *path_bar;
805   GList *children;
806
807   path_bar = GTK_PATH_BAR (container);
808
809   if (widget == path_bar->up_slider_button)
810     {
811       gtk_path_bar_remove_1 (container, widget);
812       path_bar->up_slider_button = NULL;
813       return;
814     }
815
816   if (widget == path_bar->down_slider_button)
817     {
818       gtk_path_bar_remove_1 (container, widget);
819       path_bar->down_slider_button = NULL;
820       return;
821     }
822
823   children = path_bar->button_list;
824   while (children)
825     {
826       if (widget == BUTTON_DATA (children->data)->button)
827         {
828           gtk_path_bar_remove_1 (container, widget);
829           path_bar->button_list = g_list_remove_link (path_bar->button_list, children);
830           g_list_free (children);
831           return;
832         }
833       
834       children = children->next;
835     }
836 }
837
838 static void
839 gtk_path_bar_forall (GtkContainer *container,
840                      gboolean      include_internals,
841                      GtkCallback   callback,
842                      gpointer      callback_data)
843 {
844   GtkPathBar *path_bar;
845   GList *children;
846
847   g_return_if_fail (callback != NULL);
848   path_bar = GTK_PATH_BAR (container);
849
850   children = path_bar->button_list;
851   while (children)
852     {
853       GtkWidget *child;
854       child = BUTTON_DATA (children->data)->button;
855       children = children->next;
856
857       (* callback) (child, callback_data);
858     }
859
860   if (path_bar->up_slider_button)
861     (* callback) (path_bar->up_slider_button, callback_data);
862
863   if (path_bar->down_slider_button)
864     (* callback) (path_bar->down_slider_button, callback_data);
865 }
866
867 static GtkWidgetPath *
868 gtk_path_bar_get_path_for_child (GtkContainer *container,
869                                  GtkWidget    *child)
870 {
871   GtkPathBar *path_bar = GTK_PATH_BAR (container);
872   GtkWidgetPath *path;
873
874   path = gtk_widget_path_copy (gtk_widget_get_path (GTK_WIDGET (path_bar)));
875
876   if (gtk_widget_get_visible (child) &&
877       gtk_widget_get_child_visible (child))
878     {
879       GtkWidgetPath *sibling_path;
880       GList *visible_children;
881       GList *l;
882       int pos;
883
884       /* 1. Build the list of visible children, in visually left-to-right order
885        * (i.e. independently of the widget's direction).  Note that our
886        * button_list is stored in innermost-to-outermost path order!
887        */
888
889       visible_children = NULL;
890
891       if (gtk_widget_get_visible (path_bar->down_slider_button) &&
892           gtk_widget_get_child_visible (path_bar->down_slider_button))
893         visible_children = g_list_prepend (visible_children, path_bar->down_slider_button);
894
895       for (l = path_bar->button_list; l; l = l->next)
896         {
897           ButtonData *data = l->data;
898
899           if (gtk_widget_get_visible (data->button) &&
900               gtk_widget_get_child_visible (data->button))
901             visible_children = g_list_prepend (visible_children, data->button);
902         }
903
904       if (gtk_widget_get_visible (path_bar->up_slider_button) &&
905           gtk_widget_get_child_visible (path_bar->up_slider_button))
906         visible_children = g_list_prepend (visible_children, path_bar->up_slider_button);
907
908       if (gtk_widget_get_direction (GTK_WIDGET (path_bar)) == GTK_TEXT_DIR_RTL)
909         visible_children = g_list_reverse (visible_children);
910
911       /* 2. Find the index of the child within that list */
912
913       pos = 0;
914
915       for (l = visible_children; l; l = l->next)
916         {
917           GtkWidget *button = l->data;
918
919           if (button == child)
920             break;
921
922           pos++;
923         }
924
925       /* 3. Build the path */
926
927       sibling_path = gtk_widget_path_new ();
928
929       for (l = visible_children; l; l = l->next)
930         gtk_widget_path_append_for_widget (sibling_path, l->data);
931
932       gtk_widget_path_append_with_siblings (path, sibling_path, pos);
933
934       g_list_free (visible_children);
935       gtk_widget_path_unref (sibling_path);
936     }
937   else
938     gtk_widget_path_append_for_widget (path, child);
939
940   return path;
941 }
942
943 static void
944 gtk_path_bar_scroll_down (GtkPathBar *path_bar)
945 {
946   GtkAllocation allocation, button_allocation;
947   GList *list;
948   GList *down_button = NULL;
949   gint space_available;
950
951   if (path_bar->ignore_click)
952     {
953       path_bar->ignore_click = FALSE;
954       return;   
955     }
956
957   if (gtk_widget_get_child_visible (BUTTON_DATA (path_bar->button_list->data)->button))
958     {
959       /* Return if the last button is already visible */
960       return;
961     }
962
963   gtk_widget_queue_resize (GTK_WIDGET (path_bar));
964
965   /* We find the button at the 'down' end that we have to make
966    * visible */
967   for (list = path_bar->button_list; list; list = list->next)
968     {
969       if (list->next && gtk_widget_get_child_visible (BUTTON_DATA (list->next->data)->button))
970         {
971           down_button = list;
972           break;
973         }
974     }
975
976   gtk_widget_get_allocation (GTK_WIDGET (path_bar), &allocation);
977   gtk_widget_get_allocation (BUTTON_DATA (down_button->data)->button, &button_allocation);
978
979   space_available = (allocation.width
980                      - 2 * path_bar->spacing - 2 * path_bar->slider_width
981                      - button_allocation.width);
982   path_bar->first_scrolled_button = down_button;
983   
984   /* We have space_available free space that's not being used.  
985    * So we walk down from the end, adding buttons until we use all free space.
986    */
987   while (space_available > 0)
988     {
989       path_bar->first_scrolled_button = down_button;
990       down_button = down_button->next;
991       if (!down_button)
992         break;
993       space_available -= (button_allocation.width
994                           + path_bar->spacing);
995     }
996 }
997
998 static void
999 gtk_path_bar_scroll_up (GtkPathBar *path_bar)
1000 {
1001   GList *list;
1002
1003   if (path_bar->ignore_click)
1004     {
1005       path_bar->ignore_click = FALSE;
1006       return;   
1007     }
1008
1009   list = g_list_last (path_bar->button_list);
1010
1011   if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button))
1012     {
1013       /* Return if the first button is already visible */
1014       return;
1015     }
1016
1017   gtk_widget_queue_resize (GTK_WIDGET (path_bar));
1018
1019   for ( ; list; list = list->prev)
1020     {
1021       if (list->prev && gtk_widget_get_child_visible (BUTTON_DATA (list->prev->data)->button))
1022         {
1023           if (list->prev == path_bar->fake_root)
1024             path_bar->fake_root = NULL;
1025           path_bar->first_scrolled_button = list;
1026           return;
1027         }
1028     }
1029 }
1030
1031 static gboolean
1032 gtk_path_bar_scroll_timeout (GtkPathBar *path_bar)
1033 {
1034   gboolean retval = FALSE;
1035
1036   if (path_bar->timer)
1037     {
1038       if (path_bar->scrolling_up)
1039         gtk_path_bar_scroll_up (path_bar);
1040       else if (path_bar->scrolling_down)
1041         gtk_path_bar_scroll_down (path_bar);
1042
1043       if (path_bar->need_timer) 
1044         {
1045           GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (path_bar));
1046           guint        timeout;
1047
1048           g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
1049
1050           path_bar->need_timer = FALSE;
1051
1052           path_bar->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
1053                                            (GSourceFunc)gtk_path_bar_scroll_timeout,
1054                                            path_bar);
1055         }
1056       else
1057         retval = TRUE;
1058     }
1059
1060   return retval;
1061 }
1062
1063 static void 
1064 gtk_path_bar_stop_scrolling (GtkPathBar *path_bar)
1065 {
1066   if (path_bar->timer)
1067     {
1068       g_source_remove (path_bar->timer);
1069       path_bar->timer = 0;
1070       path_bar->need_timer = FALSE;
1071     }
1072 }
1073
1074 static gboolean
1075 gtk_path_bar_slider_up_defocus (GtkWidget      *widget,
1076                                     GdkEventButton *event,
1077                                     GtkPathBar     *path_bar)
1078 {
1079   GList *list;
1080   GList *up_button = NULL;
1081
1082   if (event->type != GDK_FOCUS_CHANGE)
1083     return FALSE;
1084
1085   for (list = g_list_last (path_bar->button_list); list; list = list->prev)
1086     {
1087       if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button))
1088         {
1089           up_button = list;
1090           break;
1091         }
1092     }
1093
1094   /* don't let the focus vanish */
1095   if ((!gtk_widget_is_sensitive (path_bar->up_slider_button)) ||
1096       (!gtk_widget_get_child_visible (path_bar->up_slider_button)))
1097     gtk_widget_grab_focus (BUTTON_DATA (up_button->data)->button);
1098
1099   return FALSE;
1100 }
1101
1102 static gboolean
1103 gtk_path_bar_slider_down_defocus (GtkWidget      *widget,
1104                                     GdkEventButton *event,
1105                                     GtkPathBar     *path_bar)
1106 {
1107   GList *list;
1108   GList *down_button = NULL;
1109
1110   if (event->type != GDK_FOCUS_CHANGE)
1111     return FALSE;
1112
1113   for (list = path_bar->button_list; list; list = list->next)
1114     {
1115       if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button))
1116         {
1117           down_button = list;
1118           break;
1119         }
1120     }
1121
1122   /* don't let the focus vanish */
1123   if ((!gtk_widget_is_sensitive (path_bar->down_slider_button)) ||
1124       (!gtk_widget_get_child_visible (path_bar->down_slider_button)))
1125     gtk_widget_grab_focus (BUTTON_DATA (down_button->data)->button);
1126
1127   return FALSE;
1128 }
1129
1130 static gboolean
1131 gtk_path_bar_slider_button_press (GtkWidget      *widget, 
1132                                   GdkEventButton *event,
1133                                   GtkPathBar     *path_bar)
1134 {
1135   if (event->type != GDK_BUTTON_PRESS || event->button != GDK_BUTTON_PRIMARY)
1136     return FALSE;
1137
1138   path_bar->ignore_click = FALSE;
1139
1140   if (widget == path_bar->up_slider_button)
1141     {
1142       path_bar->scrolling_down = FALSE;
1143       path_bar->scrolling_up = TRUE;
1144       gtk_path_bar_scroll_up (path_bar);
1145     }
1146   else if (widget == path_bar->down_slider_button)
1147     {
1148       path_bar->scrolling_up = FALSE;
1149       path_bar->scrolling_down = TRUE;
1150       gtk_path_bar_scroll_down (path_bar);
1151     }
1152
1153   if (!path_bar->timer)
1154     {
1155       GtkSettings *settings = gtk_widget_get_settings (widget);
1156       guint        timeout;
1157
1158       g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
1159
1160       path_bar->need_timer = TRUE;
1161       path_bar->timer = gdk_threads_add_timeout (timeout,
1162                                        (GSourceFunc)gtk_path_bar_scroll_timeout,
1163                                        path_bar);
1164     }
1165
1166   return FALSE;
1167 }
1168
1169 static gboolean
1170 gtk_path_bar_slider_button_release (GtkWidget      *widget, 
1171                                     GdkEventButton *event,
1172                                     GtkPathBar     *path_bar)
1173 {
1174   if (event->type != GDK_BUTTON_RELEASE)
1175     return FALSE;
1176
1177   path_bar->ignore_click = TRUE;
1178   gtk_path_bar_stop_scrolling (path_bar);
1179
1180   return FALSE;
1181 }
1182
1183 static void
1184 gtk_path_bar_grab_notify (GtkWidget *widget,
1185                           gboolean   was_grabbed)
1186 {
1187   if (!was_grabbed)
1188     gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
1189 }
1190
1191 static void
1192 gtk_path_bar_state_changed (GtkWidget    *widget,
1193                             GtkStateType  previous_state)
1194 {
1195   if (!gtk_widget_is_sensitive (widget))
1196     gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
1197 }
1198
1199
1200 /* Changes the icons wherever it is needed */
1201 static void
1202 reload_icons (GtkPathBar *path_bar)
1203 {
1204   GList *list;
1205
1206   if (path_bar->root_icon)
1207     {
1208       g_object_unref (path_bar->root_icon);
1209       path_bar->root_icon = NULL;
1210     }
1211   if (path_bar->home_icon)
1212     {
1213       g_object_unref (path_bar->home_icon);
1214       path_bar->home_icon = NULL;
1215     }
1216   if (path_bar->desktop_icon)
1217     {
1218       g_object_unref (path_bar->desktop_icon);
1219       path_bar->desktop_icon = NULL;
1220     }
1221
1222   for (list = path_bar->button_list; list; list = list->next)
1223     {
1224       ButtonData *button_data;
1225       gboolean current_dir;
1226
1227       button_data = BUTTON_DATA (list->data);
1228       if (button_data->type != NORMAL_BUTTON)
1229         {
1230           current_dir = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_data->button));
1231           gtk_path_bar_update_button_appearance (path_bar, button_data, current_dir);
1232         }
1233     }
1234   
1235 }
1236
1237 static void
1238 change_icon_theme (GtkPathBar *path_bar)
1239 {
1240   GtkSettings *settings;
1241   gint width, height;
1242
1243   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (path_bar)));
1244
1245   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
1246     path_bar->icon_size = MAX (width, height);
1247   else
1248     path_bar->icon_size = FALLBACK_ICON_SIZE;
1249
1250   reload_icons (path_bar);
1251 }
1252 /* Callback used when a GtkSettings value changes */
1253 static void
1254 settings_notify_cb (GObject    *object,
1255                     GParamSpec *pspec,
1256                     GtkPathBar *path_bar)
1257 {
1258   const char *name;
1259
1260   name = g_param_spec_get_name (pspec);
1261
1262   if (! strcmp (name, "gtk-icon-theme-name") ||
1263       ! strcmp (name, "gtk-icon-sizes"))
1264     change_icon_theme (path_bar);
1265 }
1266
1267 static void
1268 gtk_path_bar_check_icon_theme (GtkPathBar *path_bar)
1269 {
1270   GtkSettings *settings;
1271
1272   if (path_bar->settings_signal_id)
1273     return;
1274
1275   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (path_bar)));
1276   path_bar->settings_signal_id = g_signal_connect (settings, "notify", G_CALLBACK (settings_notify_cb), path_bar);
1277
1278   change_icon_theme (path_bar);
1279 }
1280
1281 /* Public functions and their helpers */
1282 static void
1283 gtk_path_bar_clear_buttons (GtkPathBar *path_bar)
1284 {
1285   while (path_bar->button_list != NULL)
1286     {
1287       gtk_container_remove (GTK_CONTAINER (path_bar), BUTTON_DATA (path_bar->button_list->data)->button);
1288     }
1289   path_bar->first_scrolled_button = NULL;
1290   path_bar->fake_root = NULL;
1291 }
1292
1293 static void
1294 button_clicked_cb (GtkWidget *button,
1295                    gpointer   data)
1296 {
1297   ButtonData *button_data;
1298   GtkPathBar *path_bar;
1299   GList *button_list;
1300   gboolean child_is_hidden;
1301   GFile *child_file;
1302
1303   button_data = BUTTON_DATA (data);
1304   if (button_data->ignore_changes)
1305     return;
1306
1307   path_bar = GTK_PATH_BAR (gtk_widget_get_parent (button));
1308
1309   button_list = g_list_find (path_bar->button_list, button_data);
1310   g_assert (button_list != NULL);
1311
1312   g_signal_handlers_block_by_func (button,
1313                                    G_CALLBACK (button_clicked_cb), data);
1314   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
1315   g_signal_handlers_unblock_by_func (button,
1316                                      G_CALLBACK (button_clicked_cb), data);
1317
1318   if (button_list->prev)
1319     {
1320       ButtonData *child_data;
1321
1322       child_data = BUTTON_DATA (button_list->prev->data);
1323       child_file = child_data->file;
1324       child_is_hidden = child_data->file_is_hidden;
1325     }
1326   else
1327     {
1328       child_file = NULL;
1329       child_is_hidden = FALSE;
1330     }
1331
1332   g_signal_emit (path_bar, path_bar_signals [PATH_CLICKED], 0,
1333                  button_data->file, child_file, child_is_hidden);
1334 }
1335
1336 struct SetButtonImageData
1337 {
1338   GtkPathBar *path_bar;
1339   ButtonData *button_data;
1340 };
1341
1342 static void
1343 set_button_image_get_info_cb (GCancellable *cancellable,
1344                               GFileInfo    *info,
1345                               const GError *error,
1346                               gpointer      user_data)
1347 {
1348   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1349   GdkPixbuf *pixbuf;
1350   struct SetButtonImageData *data = user_data;
1351
1352   if (cancellable != data->button_data->cancellable)
1353     goto out;
1354
1355   data->button_data->cancellable = NULL;
1356
1357   if (!data->button_data->button)
1358     {
1359       g_free (data->button_data);
1360       goto out;
1361     }
1362
1363   if (cancelled || error)
1364     goto out;
1365
1366   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->path_bar),
1367                                        data->path_bar->icon_size);
1368   gtk_image_set_from_pixbuf (GTK_IMAGE (data->button_data->image), pixbuf);
1369
1370   switch (data->button_data->type)
1371     {
1372       case HOME_BUTTON:
1373         if (data->path_bar->home_icon)
1374           g_object_unref (pixbuf);
1375         else
1376           data->path_bar->home_icon = pixbuf;
1377         break;
1378
1379       case DESKTOP_BUTTON:
1380         if (data->path_bar->desktop_icon)
1381           g_object_unref (pixbuf);
1382         else
1383           data->path_bar->desktop_icon = pixbuf;
1384         break;
1385
1386       default:
1387         break;
1388     };
1389
1390 out:
1391   g_free (data);
1392   g_object_unref (cancellable);
1393 }
1394
1395 static void
1396 set_button_image (GtkPathBar *path_bar,
1397                   ButtonData *button_data)
1398 {
1399   GtkFileSystemVolume *volume;
1400   struct SetButtonImageData *data;
1401
1402   switch (button_data->type)
1403     {
1404     case ROOT_BUTTON:
1405
1406       if (path_bar->root_icon != NULL)
1407         {
1408           gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
1409           break;
1410         }
1411
1412       volume = _gtk_file_system_get_volume_for_file (path_bar->file_system, path_bar->root_file);
1413       if (volume == NULL)
1414         return;
1415
1416       path_bar->root_icon = _gtk_file_system_volume_render_icon (volume,
1417                                                                  GTK_WIDGET (path_bar),
1418                                                                  path_bar->icon_size,
1419                                                                  NULL);
1420       _gtk_file_system_volume_unref (volume);
1421
1422       gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
1423       break;
1424
1425     case HOME_BUTTON:
1426       if (path_bar->home_icon != NULL)
1427         {
1428           gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->home_icon);
1429           break;
1430         }
1431
1432       data = g_new0 (struct SetButtonImageData, 1);
1433       data->path_bar = path_bar;
1434       data->button_data = button_data;
1435
1436       if (button_data->cancellable)
1437         g_cancellable_cancel (button_data->cancellable);
1438
1439       button_data->cancellable =
1440         _gtk_file_system_get_info (path_bar->file_system,
1441                                    path_bar->home_file,
1442                                    "standard::icon",
1443                                    set_button_image_get_info_cb,
1444                                    data);
1445       break;
1446
1447     case DESKTOP_BUTTON:
1448       if (path_bar->desktop_icon != NULL)
1449         {
1450           gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->desktop_icon);
1451           break;
1452         }
1453
1454       data = g_new0 (struct SetButtonImageData, 1);
1455       data->path_bar = path_bar;
1456       data->button_data = button_data;
1457
1458       if (button_data->cancellable)
1459         g_cancellable_cancel (button_data->cancellable);
1460
1461       button_data->cancellable =
1462         _gtk_file_system_get_info (path_bar->file_system,
1463                                    path_bar->desktop_file,
1464                                    "standard::icon",
1465                                    set_button_image_get_info_cb,
1466                                    data);
1467       break;
1468     default:
1469       break;
1470     }
1471 }
1472
1473 static void
1474 button_data_free (ButtonData *button_data)
1475 {
1476   if (button_data->file)
1477     g_object_unref (button_data->file);
1478   button_data->file = NULL;
1479
1480   g_free (button_data->dir_name);
1481   button_data->dir_name = NULL;
1482
1483   button_data->button = NULL;
1484
1485   if (button_data->cancellable)
1486     g_cancellable_cancel (button_data->cancellable);
1487   else
1488     g_free (button_data);
1489 }
1490
1491 static const char *
1492 get_dir_name (ButtonData *button_data)
1493 {
1494   return button_data->dir_name;
1495 }
1496
1497 /* We always want to request the same size for the label, whether
1498  * or not the contents are bold
1499  */
1500 static void
1501 set_label_size_request (GtkWidget  *widget,
1502                         ButtonData *button_data)
1503 {
1504   const gchar *dir_name = get_dir_name (button_data);
1505   PangoLayout *layout = gtk_widget_create_pango_layout (button_data->label, dir_name);
1506   gint width, height, bold_width, bold_height;
1507   gchar *markup;
1508   
1509   pango_layout_get_pixel_size (layout, &width, &height);
1510   
1511   markup = g_markup_printf_escaped ("<b>%s</b>", dir_name);
1512   pango_layout_set_markup (layout, markup, -1);
1513   g_free (markup);
1514
1515   pango_layout_get_pixel_size (layout, &bold_width, &bold_height);
1516
1517   gtk_widget_set_size_request (widget,
1518                                MAX (width, bold_width),
1519                                MAX (height, bold_height));
1520   g_object_unref (layout);
1521 }
1522
1523 static void
1524 gtk_path_bar_update_button_appearance (GtkPathBar *path_bar,
1525                                        ButtonData *button_data,
1526                                        gboolean    current_dir)
1527 {
1528   const gchar *dir_name = get_dir_name (button_data);
1529
1530   if (button_data->label != NULL)
1531     {
1532       if (current_dir)
1533         {
1534           char *markup;
1535
1536           markup = g_markup_printf_escaped ("<b>%s</b>", dir_name);
1537           gtk_label_set_markup (GTK_LABEL (button_data->label), markup);
1538           g_free (markup);
1539         }
1540       else
1541         {
1542           gtk_label_set_text (GTK_LABEL (button_data->label), dir_name);
1543         }
1544     }
1545
1546   if (button_data->image != NULL)
1547     {
1548       set_button_image (path_bar, button_data);
1549     }
1550
1551   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_data->button)) != current_dir)
1552     {
1553       button_data->ignore_changes = TRUE;
1554       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button_data->button), current_dir);
1555       button_data->ignore_changes = FALSE;
1556     }
1557 }
1558
1559 static ButtonType
1560 find_button_type (GtkPathBar  *path_bar,
1561                   GFile       *file)
1562 {
1563   if (path_bar->root_file != NULL &&
1564       g_file_equal (file, path_bar->root_file))
1565     return ROOT_BUTTON;
1566   if (path_bar->home_file != NULL &&
1567       g_file_equal (file, path_bar->home_file))
1568     return HOME_BUTTON;
1569   if (path_bar->desktop_file != NULL &&
1570       g_file_equal (file, path_bar->desktop_file))
1571     return DESKTOP_BUTTON;
1572
1573  return NORMAL_BUTTON;
1574 }
1575
1576 static void
1577 button_drag_data_get_cb (GtkWidget        *widget,
1578                          GdkDragContext   *context,
1579                          GtkSelectionData *selection_data,
1580                          guint             info,
1581                          guint             time_,
1582                          gpointer          data)
1583 {
1584   ButtonData *button_data;
1585   char *uris[2];
1586
1587   button_data = data;
1588
1589   uris[0] = g_file_get_uri (button_data->file);
1590   uris[1] = NULL;
1591
1592   gtk_selection_data_set_uris (selection_data, uris);
1593
1594   g_free (uris[0]);
1595 }
1596
1597 static ButtonData *
1598 make_directory_button (GtkPathBar  *path_bar,
1599                        const char  *dir_name,
1600                        GFile       *file,
1601                        gboolean     current_dir,
1602                        gboolean     file_is_hidden)
1603 {
1604   AtkObject *atk_obj;
1605   GtkWidget *child = NULL;
1606   ButtonData *button_data;
1607
1608   file_is_hidden = !! file_is_hidden;
1609   /* Is it a special button? */
1610   button_data = g_new0 (ButtonData, 1);
1611
1612   button_data->type = find_button_type (path_bar, file);
1613   button_data->button = gtk_toggle_button_new ();
1614   atk_obj = gtk_widget_get_accessible (button_data->button);
1615   gtk_button_set_focus_on_click (GTK_BUTTON (button_data->button), FALSE);
1616   gtk_widget_add_events (button_data->button, GDK_SCROLL_MASK);
1617
1618   switch (button_data->type)
1619     {
1620     case ROOT_BUTTON:
1621       button_data->image = gtk_image_new ();
1622       child = button_data->image;
1623       button_data->label = NULL;
1624       atk_object_set_name (atk_obj, _("File System Root"));
1625       break;
1626     case HOME_BUTTON:
1627     case DESKTOP_BUTTON:
1628       button_data->image = gtk_image_new ();
1629       button_data->label = gtk_label_new (NULL);
1630       child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
1631       gtk_box_pack_start (GTK_BOX (child), button_data->image, FALSE, FALSE, 0);
1632       gtk_box_pack_start (GTK_BOX (child), button_data->label, FALSE, FALSE, 0);
1633       break;
1634     case NORMAL_BUTTON:
1635     default:
1636       button_data->label = gtk_label_new (NULL);
1637       gtk_label_set_ellipsize (GTK_LABEL (button_data->label), PANGO_ELLIPSIZE_END);
1638       child = button_data->label;
1639       button_data->image = NULL;
1640     }
1641
1642   button_data->dir_name = g_strdup (dir_name);
1643   button_data->file = g_object_ref (file);
1644   button_data->file_is_hidden = file_is_hidden;
1645
1646   /*
1647    * The following function ensures that the alignment will always
1648    * request the same size whether the button's text is bold or not.
1649    */
1650   if (button_data->label)
1651     set_label_size_request (button_data->label, button_data);
1652
1653   gtk_container_add (GTK_CONTAINER (button_data->button), child);
1654   gtk_widget_show_all (button_data->button);
1655
1656   gtk_path_bar_update_button_appearance (path_bar, button_data, current_dir);
1657
1658   g_signal_connect (button_data->button, "clicked",
1659                     G_CALLBACK (button_clicked_cb),
1660                     button_data);
1661   g_object_weak_ref (G_OBJECT (button_data->button),
1662                      (GWeakNotify) button_data_free, button_data);
1663
1664   gtk_drag_source_set (button_data->button,
1665                        GDK_BUTTON1_MASK,
1666                        NULL, 0,
1667                        GDK_ACTION_COPY);
1668   gtk_drag_source_add_uri_targets (button_data->button);
1669   g_signal_connect (button_data->button, "drag-data-get",
1670                     G_CALLBACK (button_drag_data_get_cb), button_data);
1671
1672   return button_data;
1673 }
1674
1675 static gboolean
1676 gtk_path_bar_check_parent_path (GtkPathBar         *path_bar,
1677                                 GFile              *file)
1678 {
1679   GList *list;
1680   GList *current_path = NULL;
1681   gboolean need_new_fake_root = FALSE;
1682
1683   for (list = path_bar->button_list; list; list = list->next)
1684     {
1685       ButtonData *button_data;
1686
1687       button_data = list->data;
1688       if (g_file_equal (file, button_data->file))
1689         {
1690           current_path = list;
1691           break;
1692         }
1693       if (list == path_bar->fake_root)
1694         need_new_fake_root = TRUE;
1695     }
1696
1697   if (current_path)
1698     {
1699       if (need_new_fake_root)
1700         {
1701           path_bar->fake_root = NULL;
1702           for (list = current_path; list; list = list->next)
1703             {
1704               ButtonData *button_data;
1705
1706               button_data = list->data;
1707               if (BUTTON_IS_FAKE_ROOT (button_data))
1708                 {
1709                   path_bar->fake_root = list;
1710                   break;
1711                 }
1712             }
1713         }
1714
1715       for (list = path_bar->button_list; list; list = list->next)
1716         {
1717           gtk_path_bar_update_button_appearance (path_bar,
1718                                                  BUTTON_DATA (list->data),
1719                                                  (list == current_path) ? TRUE : FALSE);
1720         }
1721
1722       if (!gtk_widget_get_child_visible (BUTTON_DATA (current_path->data)->button))
1723         {
1724           path_bar->first_scrolled_button = current_path;
1725           gtk_widget_queue_resize (GTK_WIDGET (path_bar));
1726         }
1727
1728       return TRUE;
1729     }
1730   return FALSE;
1731 }
1732
1733
1734 struct SetFileInfo
1735 {
1736   GFile *file;
1737   GFile *parent_file;
1738   GtkPathBar *path_bar;
1739   GList *new_buttons;
1740   GList *fake_root;
1741   gboolean first_directory;
1742 };
1743
1744 static void
1745 gtk_path_bar_set_file_finish (struct SetFileInfo *info,
1746                               gboolean            result)
1747 {
1748   if (result)
1749     {
1750       GList *l;
1751
1752       gtk_path_bar_clear_buttons (info->path_bar);
1753       info->path_bar->button_list = g_list_reverse (info->new_buttons);
1754       info->path_bar->fake_root = info->fake_root;
1755
1756       for (l = info->path_bar->button_list; l; l = l->next)
1757         {
1758           GtkWidget *button = BUTTON_DATA (l->data)->button;
1759           gtk_container_add (GTK_CONTAINER (info->path_bar), button);
1760         }
1761
1762       child_ordering_changed (info->path_bar);
1763     }
1764   else
1765     {
1766       GList *l;
1767
1768       for (l = info->new_buttons; l; l = l->next)
1769         {
1770           ButtonData *button_data;
1771
1772           button_data = BUTTON_DATA (l->data);
1773           gtk_widget_destroy (button_data->button);
1774         }
1775
1776       g_list_free (info->new_buttons);
1777     }
1778
1779   if (info->file)
1780     g_object_unref (info->file);
1781   if (info->parent_file)
1782     g_object_unref (info->parent_file);
1783
1784   g_free (info);
1785 }
1786
1787 static void
1788 gtk_path_bar_get_info_callback (GCancellable *cancellable,
1789                                 GFileInfo    *info,
1790                                 const GError *error,
1791                                 gpointer      data)
1792 {
1793   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1794   struct SetFileInfo *file_info = data;
1795   ButtonData *button_data;
1796   const gchar *display_name;
1797   gboolean is_hidden;
1798
1799   if (cancellable != file_info->path_bar->get_info_cancellable)
1800     {
1801       gtk_path_bar_set_file_finish (file_info, FALSE);
1802       g_object_unref (cancellable);
1803       return;
1804     }
1805
1806   g_object_unref (cancellable);
1807   file_info->path_bar->get_info_cancellable = NULL;
1808
1809   if (cancelled || !info)
1810     {
1811       gtk_path_bar_set_file_finish (file_info, FALSE);
1812       return;
1813     }
1814
1815   display_name = g_file_info_get_display_name (info);
1816   is_hidden = g_file_info_get_is_hidden (info) || g_file_info_get_is_backup (info);
1817
1818   gtk_widget_push_composite_child ();
1819   button_data = make_directory_button (file_info->path_bar, display_name,
1820                                        file_info->file,
1821                                        file_info->first_directory, is_hidden);
1822   gtk_widget_pop_composite_child ();
1823   g_object_unref (file_info->file);
1824
1825   file_info->new_buttons = g_list_prepend (file_info->new_buttons, button_data);
1826
1827   if (BUTTON_IS_FAKE_ROOT (button_data))
1828     file_info->fake_root = file_info->new_buttons;
1829
1830   /* We have assigned the info for the innermost button, i.e. the deepest directory.
1831    * Now, go on to fetch the info for this directory's parent.
1832    */
1833
1834   file_info->file = file_info->parent_file;
1835   file_info->first_directory = FALSE;
1836
1837   if (!file_info->file)
1838     {
1839       /* No parent?  Okay, we are done. */
1840       gtk_path_bar_set_file_finish (file_info, TRUE);
1841       return;
1842     }
1843
1844   file_info->parent_file = g_file_get_parent (file_info->file);
1845
1846   /* Recurse asynchronously */
1847   file_info->path_bar->get_info_cancellable =
1848     _gtk_file_system_get_info (file_info->path_bar->file_system,
1849                                file_info->file,
1850                                "standard::display-name,standard::is-hidden,standard::is-backup",
1851                                gtk_path_bar_get_info_callback,
1852                                file_info);
1853 }
1854
1855 void
1856 _gtk_path_bar_set_file (GtkPathBar      *path_bar,
1857                         GFile           *file,
1858                         const gboolean   keep_trail)
1859 {
1860   struct SetFileInfo *info;
1861
1862   g_return_if_fail (GTK_IS_PATH_BAR (path_bar));
1863   g_return_if_fail (G_IS_FILE (file));
1864
1865   /* Check whether the new path is already present in the pathbar as buttons.
1866    * This could be a parent directory or a previous selected subdirectory.
1867    */
1868   if (keep_trail && gtk_path_bar_check_parent_path (path_bar, file))
1869     return;
1870
1871   info = g_new0 (struct SetFileInfo, 1);
1872   info->file = g_object_ref (file);
1873   info->path_bar = path_bar;
1874   info->first_directory = TRUE;
1875   info->parent_file = g_file_get_parent (info->file);
1876
1877   if (path_bar->get_info_cancellable)
1878     g_cancellable_cancel (path_bar->get_info_cancellable);
1879
1880   path_bar->get_info_cancellable =
1881     _gtk_file_system_get_info (path_bar->file_system,
1882                                info->file,
1883                                "standard::display-name,standard::is-hidden,standard::is-backup",
1884                                gtk_path_bar_get_info_callback,
1885                                info);
1886 }
1887
1888 /* FIXME: This should be a construct-only property */
1889 void
1890 _gtk_path_bar_set_file_system (GtkPathBar    *path_bar,
1891                                GtkFileSystem *file_system)
1892 {
1893   const char *home;
1894
1895   g_return_if_fail (GTK_IS_PATH_BAR (path_bar));
1896
1897   g_assert (path_bar->file_system == NULL);
1898
1899   path_bar->file_system = g_object_ref (file_system);
1900
1901   home = g_get_home_dir ();
1902   if (home != NULL)
1903     {
1904       const gchar *desktop;
1905
1906       path_bar->home_file = g_file_new_for_path (home);
1907       /* FIXME: Need file system backend specific way of getting the
1908        * Desktop path.
1909        */
1910       desktop = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1911       if (desktop != NULL)
1912         path_bar->desktop_file = g_file_new_for_path (desktop);
1913       else 
1914         path_bar->desktop_file = NULL;
1915     }
1916   else
1917     {
1918       path_bar->home_file = NULL;
1919       path_bar->desktop_file = NULL;
1920     }
1921   path_bar->root_file = g_file_new_for_path ("/");
1922 }
1923
1924 /**
1925  * _gtk_path_bar_up:
1926  * @path_bar: a #GtkPathBar
1927  * 
1928  * If the selected button in the pathbar is not the furthest button "up" (in the
1929  * root direction), act as if the user clicked on the next button up.
1930  **/
1931 void
1932 _gtk_path_bar_up (GtkPathBar *path_bar)
1933 {
1934   GList *l;
1935
1936   for (l = path_bar->button_list; l; l = l->next)
1937     {
1938       GtkWidget *button = BUTTON_DATA (l->data)->button;
1939       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1940         {
1941           if (l->next)
1942             {
1943               GtkWidget *next_button = BUTTON_DATA (l->next->data)->button;
1944               button_clicked_cb (next_button, l->next->data);
1945             }
1946           break;
1947         }
1948     }
1949 }
1950
1951 /**
1952  * _gtk_path_bar_down:
1953  * @path_bar: a #GtkPathBar
1954  * 
1955  * If the selected button in the pathbar is not the furthest button "down" (in the
1956  * leaf direction), act as if the user clicked on the next button down.
1957  **/
1958 void
1959 _gtk_path_bar_down (GtkPathBar *path_bar)
1960 {
1961   GList *l;
1962
1963   for (l = path_bar->button_list; l; l = l->next)
1964     {
1965       GtkWidget *button = BUTTON_DATA (l->data)->button;
1966       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1967         {
1968           if (l->prev)
1969             {
1970               GtkWidget *prev_button = BUTTON_DATA (l->prev->data)->button;
1971               button_clicked_cb (prev_button, l->prev->data);
1972             }
1973           break;
1974         }
1975     }
1976 }