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