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