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