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