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