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