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