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