]> Pileus Git - ~andy/gtk/blob - gtk/gtkpathbar.c
Replace a lot of idle and timeout calls by the new gdk_threads api.
[~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   if (path_bar->timer)
759     {
760       if (path_bar->scrolling_up)
761         gtk_path_bar_scroll_up (path_bar->up_slider_button, path_bar);
762       else if (path_bar->scrolling_down)
763         gtk_path_bar_scroll_down (path_bar->down_slider_button, path_bar);
764
765       if (path_bar->need_timer) 
766         {
767           GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (path_bar));
768           guint        timeout;
769
770           g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
771
772           path_bar->need_timer = FALSE;
773
774           path_bar->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
775                                            (GSourceFunc)gtk_path_bar_scroll_timeout,
776                                            path_bar);
777         }
778       else
779         retval = TRUE;
780     }
781
782   return retval;
783 }
784
785 static void 
786 gtk_path_bar_stop_scrolling (GtkPathBar *path_bar)
787 {
788   if (path_bar->timer)
789     {
790       g_source_remove (path_bar->timer);
791       path_bar->timer = 0;
792       path_bar->need_timer = FALSE;
793     }
794 }
795
796 static gboolean
797 gtk_path_bar_slider_button_press (GtkWidget      *widget, 
798                                   GdkEventButton *event,
799                                   GtkPathBar     *path_bar)
800 {
801   if (event->type != GDK_BUTTON_PRESS || event->button != 1)
802     return FALSE;
803
804   path_bar->ignore_click = FALSE;
805
806   if (widget == path_bar->up_slider_button)
807     {
808       path_bar->scrolling_down = FALSE;
809       path_bar->scrolling_up = TRUE;
810       gtk_path_bar_scroll_up (path_bar->up_slider_button, path_bar);
811     }
812   else if (widget == path_bar->down_slider_button)
813     {
814       path_bar->scrolling_up = FALSE;
815       path_bar->scrolling_down = TRUE;
816       gtk_path_bar_scroll_down (path_bar->down_slider_button, path_bar);
817     }
818
819   if (!path_bar->timer)
820     {
821       GtkSettings *settings = gtk_widget_get_settings (widget);
822       guint        timeout;
823
824       g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
825
826       path_bar->need_timer = TRUE;
827       path_bar->timer = gdk_threads_add_timeout (timeout,
828                                        (GSourceFunc)gtk_path_bar_scroll_timeout,
829                                        path_bar);
830     }
831
832   return FALSE;
833 }
834
835 static gboolean
836 gtk_path_bar_slider_button_release (GtkWidget      *widget, 
837                                     GdkEventButton *event,
838                                     GtkPathBar     *path_bar)
839 {
840   if (event->type != GDK_BUTTON_RELEASE)
841     return FALSE;
842
843   path_bar->ignore_click = TRUE;
844   gtk_path_bar_stop_scrolling (path_bar);
845
846   return FALSE;
847 }
848
849 static void
850 gtk_path_bar_grab_notify (GtkWidget *widget,
851                           gboolean   was_grabbed)
852 {
853   if (!was_grabbed)
854     gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
855 }
856
857 static void
858 gtk_path_bar_state_changed (GtkWidget    *widget,
859                             GtkStateType  previous_state)
860 {
861   if (!GTK_WIDGET_IS_SENSITIVE (widget)) 
862     gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
863 }
864
865
866 /* Changes the icons wherever it is needed */
867 static void
868 reload_icons (GtkPathBar *path_bar)
869 {
870   GList *list;
871
872   if (path_bar->root_icon)
873     {
874       g_object_unref (path_bar->root_icon);
875       path_bar->root_icon = NULL;
876     }
877   if (path_bar->home_icon)
878     {
879       g_object_unref (path_bar->home_icon);
880       path_bar->home_icon = NULL;
881     }
882   if (path_bar->desktop_icon)
883     {
884       g_object_unref (path_bar->desktop_icon);
885       path_bar->desktop_icon = NULL;
886     }
887
888   for (list = path_bar->button_list; list; list = list->next)
889     {
890       ButtonData *button_data;
891       gboolean current_dir;
892
893       button_data = BUTTON_DATA (list->data);
894       if (button_data->type != NORMAL_BUTTON)
895         {
896           current_dir = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_data->button));
897           gtk_path_bar_update_button_appearance (path_bar, button_data, current_dir);
898         }
899     }
900   
901 }
902
903 static void
904 change_icon_theme (GtkPathBar *path_bar)
905 {
906   GtkSettings *settings;
907   gint width, height;
908
909   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (path_bar)));
910
911   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
912     path_bar->icon_size = MAX (width, height);
913   else
914     path_bar->icon_size = FALLBACK_ICON_SIZE;
915
916   reload_icons (path_bar);
917 }
918 /* Callback used when a GtkSettings value changes */
919 static void
920 settings_notify_cb (GObject    *object,
921                     GParamSpec *pspec,
922                     GtkPathBar *path_bar)
923 {
924   const char *name;
925
926   name = g_param_spec_get_name (pspec);
927
928   if (! strcmp (name, "gtk-icon-theme-name") ||
929       ! strcmp (name, "gtk-icon-sizes"))
930     change_icon_theme (path_bar);
931 }
932
933 static void
934 gtk_path_bar_check_icon_theme (GtkPathBar *path_bar)
935 {
936   GtkSettings *settings;
937
938   if (path_bar->settings_signal_id)
939     return;
940
941   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (path_bar)));
942   path_bar->settings_signal_id = g_signal_connect (settings, "notify", G_CALLBACK (settings_notify_cb), path_bar);
943
944   change_icon_theme (path_bar);
945 }
946
947 /* Public functions and their helpers */
948 static void
949 gtk_path_bar_clear_buttons (GtkPathBar *path_bar)
950 {
951   while (path_bar->button_list != NULL)
952     {
953       gtk_container_remove (GTK_CONTAINER (path_bar), BUTTON_DATA (path_bar->button_list->data)->button);
954     }
955   path_bar->first_scrolled_button = NULL;
956   path_bar->fake_root = NULL;
957 }
958
959 static void
960 button_clicked_cb (GtkWidget *button,
961                    gpointer   data)
962 {
963   ButtonData *button_data;
964   GtkPathBar *path_bar;
965   GList *button_list;
966   gboolean child_is_hidden;
967   GtkFilePath *child_path;
968
969   button_data = BUTTON_DATA (data);
970   if (button_data->ignore_changes)
971     return;
972
973   path_bar = GTK_PATH_BAR (button->parent);
974
975   button_list = g_list_find (path_bar->button_list, button_data);
976   g_assert (button_list != NULL);
977
978   g_signal_handlers_block_by_func (button,
979                                    G_CALLBACK (button_clicked_cb), data);
980   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
981   g_signal_handlers_unblock_by_func (button,
982                                      G_CALLBACK (button_clicked_cb), data);
983
984   if (button_list->prev)
985     {
986       ButtonData *child_data;
987
988       child_data = BUTTON_DATA (button_list->prev->data);
989       child_path = child_data->path;
990       child_is_hidden = child_data->file_is_hidden;
991     }
992   else
993     {
994       child_path = NULL;
995       child_is_hidden = FALSE;
996     }
997
998   g_signal_emit (path_bar, path_bar_signals [PATH_CLICKED], 0,
999                  button_data->path, child_path, child_is_hidden);
1000 }
1001
1002 struct SetButtonImageData
1003 {
1004   GtkPathBar *path_bar;
1005   ButtonData *button_data;
1006 };
1007
1008 static void
1009 set_button_image_get_info_cb (GtkFileSystemHandle *handle,
1010                               const GtkFileInfo   *info,
1011                               const GError        *error,
1012                               gpointer             user_data)
1013 {
1014   gboolean cancelled = handle->cancelled;
1015   GdkPixbuf *pixbuf;
1016   struct SetButtonImageData *data = user_data;
1017
1018   if (handle != data->button_data->handle)
1019     goto out;
1020
1021   data->button_data->handle = NULL;
1022
1023   if (!data->button_data->button)
1024     {
1025       g_free (data->button_data);
1026       goto out;
1027     }
1028
1029   if (cancelled || error)
1030     goto out;
1031
1032   pixbuf = gtk_file_info_render_icon (info, GTK_WIDGET (data->path_bar),
1033                                       data->path_bar->icon_size, NULL);
1034   gtk_image_set_from_pixbuf (GTK_IMAGE (data->button_data->image), pixbuf);
1035
1036   switch (data->button_data->type)
1037     {
1038       case HOME_BUTTON:
1039         if (data->path_bar->home_icon)
1040           g_object_unref (pixbuf);
1041         else
1042           data->path_bar->home_icon = pixbuf;
1043         break;
1044
1045       case DESKTOP_BUTTON:
1046         if (data->path_bar->desktop_icon)
1047           g_object_unref (pixbuf);
1048         else
1049           data->path_bar->desktop_icon = pixbuf;
1050         break;
1051
1052       default:
1053         break;
1054     };
1055
1056 out:
1057   g_free (data);
1058   g_object_unref (handle);
1059 }
1060
1061 static void
1062 set_button_image (GtkPathBar *path_bar,
1063                   ButtonData *button_data)
1064 {
1065   GtkFileSystemVolume *volume;
1066   struct SetButtonImageData *data;
1067
1068   switch (button_data->type)
1069     {
1070     case ROOT_BUTTON:
1071
1072       if (path_bar->root_icon != NULL)
1073         {
1074           gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
1075           break;
1076         }
1077       
1078       volume = gtk_file_system_get_volume_for_path (path_bar->file_system, path_bar->root_path);
1079       if (volume == NULL)
1080         return;
1081
1082       path_bar->root_icon = gtk_file_system_volume_render_icon (path_bar->file_system,
1083                                                                 volume,
1084                                                                 GTK_WIDGET (path_bar),
1085                                                                 path_bar->icon_size,
1086                                                                 NULL);
1087       gtk_file_system_volume_free (path_bar->file_system, volume);
1088
1089       gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
1090       break;
1091
1092     case HOME_BUTTON:
1093       if (path_bar->home_icon != NULL)
1094         {
1095           gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->home_icon);
1096           break;
1097         }
1098
1099       data = g_new0 (struct SetButtonImageData, 1);
1100       data->path_bar = path_bar;
1101       data->button_data = button_data;
1102
1103       if (button_data->handle)
1104         gtk_file_system_cancel_operation (button_data->handle);
1105
1106       button_data->handle =
1107         gtk_file_system_get_info (path_bar->file_system,
1108                                   path_bar->home_path,
1109                                   GTK_FILE_INFO_ICON,
1110                                   set_button_image_get_info_cb,
1111                                   data);
1112       break;
1113
1114     case DESKTOP_BUTTON:
1115       if (path_bar->desktop_icon != NULL)
1116         {
1117           gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->desktop_icon);
1118           break;
1119         }
1120
1121       data = g_new0 (struct SetButtonImageData, 1);
1122       data->path_bar = path_bar;
1123       data->button_data = button_data;
1124
1125       if (button_data->handle)
1126         gtk_file_system_cancel_operation (button_data->handle);
1127
1128       button_data->handle =
1129         gtk_file_system_get_info (path_bar->file_system,
1130                                   path_bar->desktop_path,
1131                                   GTK_FILE_INFO_ICON,
1132                                   set_button_image_get_info_cb,
1133                                   data);
1134       break;
1135     default:
1136       break;
1137     }
1138 }
1139
1140 static void
1141 button_data_free (ButtonData *button_data)
1142 {
1143   if (button_data->path)
1144     gtk_file_path_free (button_data->path);
1145   button_data->path = NULL;
1146
1147   if (button_data->dir_name)
1148     g_free (button_data->dir_name);
1149   button_data->dir_name = NULL;
1150
1151   button_data->button = NULL;
1152
1153   if (button_data->handle)
1154     gtk_file_system_cancel_operation (button_data->handle);
1155   else
1156     g_free (button_data);
1157 }
1158
1159 static const char *
1160 get_dir_name (ButtonData *button_data)
1161 {
1162   return button_data->dir_name;
1163 }
1164
1165 /* We always want to request the same size for the label, whether
1166  * or not the contents are bold
1167  */
1168 static void
1169 label_size_request_cb (GtkWidget      *widget,
1170                        GtkRequisition *requisition,
1171                        ButtonData     *button_data)
1172 {
1173   const gchar *dir_name = get_dir_name (button_data);
1174   PangoLayout *layout = gtk_widget_create_pango_layout (button_data->label, dir_name);
1175   gint bold_width, bold_height;
1176   gchar *markup;
1177
1178   pango_layout_get_pixel_size (layout, &requisition->width, &requisition->height);
1179   
1180   markup = g_markup_printf_escaped ("<b>%s</b>", dir_name);
1181   pango_layout_set_markup (layout, markup, -1);
1182   g_free (markup);
1183
1184   pango_layout_get_pixel_size (layout, &bold_width, &bold_height);
1185   requisition->width = MAX (requisition->width, bold_width);
1186   requisition->height = MAX (requisition->height, bold_height);
1187   
1188   g_object_unref (layout);
1189 }
1190
1191 static void
1192 gtk_path_bar_update_button_appearance (GtkPathBar *path_bar,
1193                                        ButtonData *button_data,
1194                                        gboolean    current_dir)
1195 {
1196   const gchar *dir_name = get_dir_name (button_data);
1197
1198   if (button_data->label != NULL)
1199     {
1200       if (current_dir)
1201         {
1202           char *markup;
1203
1204           markup = g_markup_printf_escaped ("<b>%s</b>", dir_name);
1205           gtk_label_set_markup (GTK_LABEL (button_data->label), markup);
1206           g_free (markup);
1207         }
1208       else
1209         {
1210           gtk_label_set_text (GTK_LABEL (button_data->label), dir_name);
1211         }
1212     }
1213
1214   if (button_data->image != NULL)
1215     {
1216       set_button_image (path_bar, button_data);
1217     }
1218
1219   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_data->button)) != current_dir)
1220     {
1221       button_data->ignore_changes = TRUE;
1222       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button_data->button), current_dir);
1223       button_data->ignore_changes = FALSE;
1224     }
1225 }
1226
1227 static ButtonType
1228 find_button_type (GtkPathBar  *path_bar,
1229                   GtkFilePath *path)
1230 {
1231   if (path_bar->root_path != NULL &&
1232       ! gtk_file_path_compare (path, path_bar->root_path))
1233     return ROOT_BUTTON;
1234   if (path_bar->home_path != NULL &&
1235       ! gtk_file_path_compare (path, path_bar->home_path))
1236     return HOME_BUTTON;
1237   if (path_bar->desktop_path != NULL &&
1238       ! gtk_file_path_compare (path, path_bar->desktop_path))
1239     return DESKTOP_BUTTON;
1240
1241  return NORMAL_BUTTON;
1242 }
1243
1244 static void
1245 button_drag_data_get_cb (GtkWidget          *widget,
1246                          GdkDragContext     *context,
1247                          GtkSelectionData   *selection_data,
1248                          guint               info,
1249                          guint               time_,
1250                          gpointer            data)
1251 {
1252   ButtonData *button_data;
1253   GtkPathBar *path_bar;
1254   char *uri;
1255   char *uri_list;
1256
1257   button_data = data;
1258   path_bar = GTK_PATH_BAR (widget->parent); /* the button's parent *is* the path bar */
1259
1260   uri = gtk_file_system_path_to_uri (path_bar->file_system, button_data->path);
1261   uri_list = g_strconcat (uri, "\r\n", NULL);
1262   g_free (uri);
1263
1264   gtk_selection_data_set (selection_data,
1265                           selection_data->target,
1266                           8,
1267                           (guchar *)uri_list,
1268                           strlen (uri_list));
1269   g_free (uri_list);
1270 }
1271
1272 static ButtonData *
1273 make_directory_button (GtkPathBar  *path_bar,
1274                        const char  *dir_name,
1275                        GtkFilePath *path,
1276                        gboolean     current_dir,
1277                        gboolean     file_is_hidden)
1278 {
1279   const GtkTargetEntry targets[] = {
1280     { "text/uri-list", 0, 0 }
1281   };
1282
1283   GtkWidget *child = NULL;
1284   GtkWidget *label_alignment = NULL;
1285   ButtonData *button_data;
1286
1287   file_is_hidden = !! file_is_hidden;
1288   /* Is it a special button? */
1289   button_data = g_new0 (ButtonData, 1);
1290
1291   button_data->type = find_button_type (path_bar, path);
1292   button_data->button = gtk_toggle_button_new ();
1293   gtk_button_set_focus_on_click (GTK_BUTTON (button_data->button), FALSE);
1294
1295   switch (button_data->type)
1296     {
1297     case ROOT_BUTTON:
1298       button_data->image = gtk_image_new ();
1299       child = button_data->image;
1300       button_data->label = NULL;
1301       break;
1302     case HOME_BUTTON:
1303     case DESKTOP_BUTTON:
1304       button_data->image = gtk_image_new ();
1305       button_data->label = gtk_label_new (NULL);
1306       label_alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
1307       gtk_container_add (GTK_CONTAINER (label_alignment), button_data->label);
1308       child = gtk_hbox_new (FALSE, 2);
1309       gtk_box_pack_start (GTK_BOX (child), button_data->image, FALSE, FALSE, 0);
1310       gtk_box_pack_start (GTK_BOX (child), label_alignment, FALSE, FALSE, 0);
1311       break;
1312     case NORMAL_BUTTON:
1313     default:
1314       button_data->label = gtk_label_new (NULL);
1315       label_alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
1316       gtk_container_add (GTK_CONTAINER (label_alignment), button_data->label);
1317       child = label_alignment;
1318       button_data->image = NULL;
1319     }
1320
1321   /* label_alignment is created because we can't override size-request
1322    * on label itself and still have the contents of the label centered
1323    * properly in the label's requisition
1324    */
1325   if (label_alignment)
1326     g_signal_connect (label_alignment, "size-request",
1327                       G_CALLBACK (label_size_request_cb), button_data);
1328
1329   button_data->dir_name = g_strdup (dir_name);
1330   button_data->path = gtk_file_path_new_dup (gtk_file_path_get_string (path));
1331   button_data->file_is_hidden = file_is_hidden;
1332                           
1333   gtk_container_add (GTK_CONTAINER (button_data->button), child);
1334   gtk_widget_show_all (button_data->button);
1335
1336   gtk_path_bar_update_button_appearance (path_bar, button_data, current_dir);
1337
1338   g_signal_connect (button_data->button, "clicked",
1339                     G_CALLBACK (button_clicked_cb),
1340                     button_data);
1341   g_object_weak_ref (G_OBJECT (button_data->button),
1342                      (GWeakNotify) button_data_free, button_data);
1343
1344   gtk_drag_source_set (button_data->button,
1345                        GDK_BUTTON1_MASK,
1346                        targets,
1347                        G_N_ELEMENTS (targets),
1348                        GDK_ACTION_COPY);
1349   g_signal_connect (button_data->button, "drag_data_get",
1350                     G_CALLBACK (button_drag_data_get_cb), button_data);
1351
1352   return button_data;
1353 }
1354
1355 static gboolean
1356 gtk_path_bar_check_parent_path (GtkPathBar         *path_bar,
1357                                 const GtkFilePath  *file_path,
1358                                 GtkFileSystem      *file_system)
1359 {
1360   GList *list;
1361   GList *current_path = NULL;
1362   gboolean need_new_fake_root = FALSE;
1363
1364   for (list = path_bar->button_list; list; list = list->next)
1365     {
1366       ButtonData *button_data;
1367
1368       button_data = list->data;
1369       if (! gtk_file_path_compare (file_path, button_data->path))
1370         {
1371           current_path = list;
1372           break;
1373         }
1374       if (list == path_bar->fake_root)
1375         need_new_fake_root = TRUE;
1376     }
1377
1378   if (current_path)
1379     {
1380       if (need_new_fake_root)
1381         {
1382           path_bar->fake_root = NULL;
1383           for (list = current_path; list; list = list->next)
1384             {
1385               ButtonData *button_data;
1386
1387               button_data = list->data;
1388               if (BUTTON_IS_FAKE_ROOT (button_data))
1389                 {
1390                   path_bar->fake_root = list;
1391                   break;
1392                 }
1393             }
1394         }
1395
1396       for (list = path_bar->button_list; list; list = list->next)
1397         {
1398           gtk_path_bar_update_button_appearance (path_bar,
1399                                                  BUTTON_DATA (list->data),
1400                                                  (list == current_path) ? TRUE : FALSE);
1401         }
1402
1403       if (!gtk_widget_get_child_visible (BUTTON_DATA (current_path->data)->button))
1404         {
1405           path_bar->first_scrolled_button = current_path;
1406           gtk_widget_queue_resize (GTK_WIDGET (path_bar));
1407         }
1408
1409       return TRUE;
1410     }
1411   return FALSE;
1412 }
1413
1414
1415 struct SetPathInfo
1416 {
1417   GtkFilePath *path;
1418   GtkFilePath *parent_path;
1419   GtkPathBar *path_bar;
1420   GList *new_buttons;
1421   GList *fake_root;
1422   gboolean first_directory;
1423 };
1424
1425 static void
1426 gtk_path_bar_set_path_finish (struct SetPathInfo *info,
1427                               gboolean result)
1428 {
1429   if (result)
1430     {
1431       GList *l;
1432
1433       gtk_path_bar_clear_buttons (info->path_bar);
1434       info->path_bar->button_list = g_list_reverse (info->new_buttons);
1435       info->path_bar->fake_root = info->fake_root;
1436
1437       for (l = info->path_bar->button_list; l; l = l->next)
1438         {
1439           GtkWidget *button = BUTTON_DATA (l->data)->button;
1440           gtk_container_add (GTK_CONTAINER (info->path_bar), button);
1441         }
1442     }
1443   else
1444     {
1445       GList *l;
1446
1447       for (l = info->new_buttons; l; l = l->next)
1448         {
1449           ButtonData *button_data;
1450
1451           button_data = BUTTON_DATA (l->data);
1452           gtk_widget_destroy (button_data->button);
1453         }
1454
1455       g_list_free (info->new_buttons);
1456     }
1457
1458   if (info->path)
1459     gtk_file_path_free (info->path);
1460   if (info->parent_path)
1461     gtk_file_path_free (info->parent_path);
1462   g_free (info);
1463 }
1464
1465 static void
1466 gtk_path_bar_get_info_callback (GtkFileSystemHandle *handle,
1467                                 const GtkFileInfo   *file_info,
1468                                 const GError        *error,
1469                                 gpointer             data)
1470 {
1471   gboolean cancelled = handle->cancelled;
1472   struct SetPathInfo *path_info = data;
1473   ButtonData *button_data;
1474   const gchar *display_name;
1475   gboolean is_hidden;
1476   gboolean valid;
1477
1478   if (handle != path_info->path_bar->set_path_handle)
1479     {
1480       gtk_path_bar_set_path_finish (path_info, FALSE);
1481       g_object_unref (handle);
1482       return;
1483     }
1484
1485   g_object_unref (handle);
1486   path_info->path_bar->set_path_handle = NULL;
1487
1488   if (cancelled || !file_info)
1489     {
1490       gtk_path_bar_set_path_finish (path_info, FALSE);
1491       return;
1492     }
1493
1494   display_name = gtk_file_info_get_display_name (file_info);
1495   is_hidden = gtk_file_info_get_is_hidden (file_info);
1496
1497   gtk_widget_push_composite_child ();
1498   button_data = make_directory_button (path_info->path_bar, display_name,
1499                                        path_info->path,
1500                                        path_info->first_directory, is_hidden);
1501   gtk_widget_pop_composite_child ();
1502   gtk_file_path_free (path_info->path);
1503
1504   path_info->new_buttons = g_list_prepend (path_info->new_buttons, button_data);
1505
1506   if (BUTTON_IS_FAKE_ROOT (button_data))
1507     path_info->fake_root = path_info->new_buttons;
1508
1509   path_info->path = path_info->parent_path;
1510   path_info->first_directory = FALSE;
1511
1512   if (!path_info->path)
1513     {
1514       gtk_path_bar_set_path_finish (path_info, TRUE);
1515       return;
1516     }
1517
1518   valid = gtk_file_system_get_parent (path_info->path_bar->file_system,
1519                                       path_info->path,
1520                                       &path_info->parent_path,
1521                                       NULL);
1522   if (!valid)
1523     {
1524       gtk_path_bar_set_path_finish (path_info, FALSE);
1525       return;
1526     }
1527
1528   path_info->path_bar->set_path_handle =
1529     gtk_file_system_get_info (handle->file_system,
1530                               path_info->path,
1531                               GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_HIDDEN,
1532                               gtk_path_bar_get_info_callback,
1533                               path_info);
1534 }
1535
1536 gboolean
1537 _gtk_path_bar_set_path (GtkPathBar         *path_bar,
1538                         const GtkFilePath  *file_path,
1539                         const gboolean      keep_trail,     
1540                         GError            **error)
1541 {
1542   struct SetPathInfo *info;
1543   gboolean result;
1544
1545   g_return_val_if_fail (GTK_IS_PATH_BAR (path_bar), FALSE);
1546   g_return_val_if_fail (file_path != NULL, FALSE);
1547
1548   result = TRUE;
1549
1550   /* Check whether the new path is already present in the pathbar as buttons.
1551    * This could be a parent directory or a previous selected subdirectory.
1552    */
1553   if (keep_trail &&
1554       gtk_path_bar_check_parent_path (path_bar, file_path, path_bar->file_system))
1555     return TRUE;
1556
1557   info = g_new0 (struct SetPathInfo, 1);
1558   info->path = gtk_file_path_copy (file_path);
1559   info->path_bar = path_bar;
1560   info->first_directory = TRUE;
1561
1562   result = gtk_file_system_get_parent (path_bar->file_system,
1563                                        info->path, &info->parent_path, error);
1564   if (!result)
1565     {
1566       gtk_file_path_free (info->path);
1567       g_free (info);
1568       return result;
1569     }
1570
1571   if (path_bar->set_path_handle)
1572     gtk_file_system_cancel_operation (path_bar->set_path_handle);
1573
1574   path_bar->set_path_handle =
1575     gtk_file_system_get_info (path_bar->file_system,
1576                               info->path,
1577                               GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_HIDDEN,
1578                               gtk_path_bar_get_info_callback,
1579                               info);
1580
1581   return TRUE;
1582 }
1583
1584 /* FIXME: This should be a construct-only property */
1585 void
1586 _gtk_path_bar_set_file_system (GtkPathBar    *path_bar,
1587                                GtkFileSystem *file_system)
1588 {
1589   const char *home;
1590   char *desktop;
1591
1592   g_return_if_fail (GTK_IS_PATH_BAR (path_bar));
1593
1594   g_assert (path_bar->file_system == NULL);
1595
1596   path_bar->file_system = g_object_ref (file_system);
1597
1598   home = g_get_home_dir ();
1599   if (home != NULL)
1600     {
1601       path_bar->home_path = gtk_file_system_filename_to_path (path_bar->file_system, home);
1602       /* FIXME: Need file system backend specific way of getting the
1603        * Desktop path.
1604        */
1605       desktop = g_build_filename (home, "Desktop", NULL);
1606       path_bar->desktop_path = gtk_file_system_filename_to_path (path_bar->file_system, desktop);
1607       g_free (desktop);
1608     }
1609   else
1610     {
1611       path_bar->home_path = NULL;
1612       path_bar->desktop_path = NULL;
1613     }
1614   path_bar->root_path = gtk_file_system_filename_to_path (path_bar->file_system, "/");
1615 }
1616
1617 /**
1618  * _gtk_path_bar_up:
1619  * @path_bar: a #GtkPathBar
1620  * 
1621  * If the selected button in the pathbar is not the furthest button "up" (in the
1622  * root direction), act as if the user clicked on the next button up.
1623  **/
1624 void
1625 _gtk_path_bar_up (GtkPathBar *path_bar)
1626 {
1627   GList *l;
1628
1629   for (l = path_bar->button_list; l; l = l->next)
1630     {
1631       GtkWidget *button = BUTTON_DATA (l->data)->button;
1632       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1633         {
1634           if (l->next)
1635             {
1636               GtkWidget *next_button = BUTTON_DATA (l->next->data)->button;
1637               button_clicked_cb (next_button, l->next->data);
1638             }
1639           break;
1640         }
1641     }
1642 }
1643
1644 /**
1645  * _gtk_path_bar_down:
1646  * @path_bar: a #GtkPathBar
1647  * 
1648  * If the selected button in the pathbar is not the furthest button "down" (in the
1649  * leaf direction), act as if the user clicked on the next button down.
1650  **/
1651 void
1652 _gtk_path_bar_down (GtkPathBar *path_bar)
1653 {
1654   GList *l;
1655
1656   for (l = path_bar->button_list; l; l = l->next)
1657     {
1658       GtkWidget *button = BUTTON_DATA (l->data)->button;
1659       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1660         {
1661           if (l->prev)
1662             {
1663               GtkWidget *prev_button = BUTTON_DATA (l->prev->data)->button;
1664               button_clicked_cb (prev_button, l->prev->data);
1665             }
1666           break;
1667         }
1668     }
1669 }
1670
1671 #define __GTK_PATH_BAR_C__
1672 #include "gtkaliasdef.c"