1 /* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
3 * Copyright (C) 2004 Red Hat, Inc., Jonathan Blandford <jrb@gnome.org>
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.
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.
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/>.
21 #include "gtkpathbar.h"
28 #include "gtkiconfactory.h"
29 #include "gtkicontheme.h"
34 #include "gtkmarshalers.h"
35 #include "gtksettings.h"
36 #include "gtktogglebutton.h"
37 #include "gtkwidgetpath.h"
38 #include "gtkwidgetprivate.h"
53 #define BUTTON_DATA(x) ((ButtonData *)(x))
55 #define SCROLL_DELAY_FACTOR 5
57 static guint path_bar_signals [LAST_SIGNAL] = { 0 };
59 /* Icon size for if we can't get it from the theme */
60 #define FALLBACK_ICON_SIZE 16
62 typedef struct _ButtonData ButtonData;
72 GCancellable *cancellable;
73 guint ignore_changes : 1;
74 guint file_is_hidden : 1;
76 /* This macro is used to check if a button can be used as a fake root.
77 * All buttons in front of a fake root are automatically hidden when in a
78 * directory below a fake root and replaced with the "<" arrow button.
80 #define BUTTON_IS_FAKE_ROOT(button) ((button)->type == HOME_BUTTON)
82 G_DEFINE_TYPE (GtkPathBar, gtk_path_bar, GTK_TYPE_CONTAINER)
84 static void gtk_path_bar_finalize (GObject *object);
85 static void gtk_path_bar_dispose (GObject *object);
86 static void gtk_path_bar_realize (GtkWidget *widget);
87 static void gtk_path_bar_unrealize (GtkWidget *widget);
88 static void gtk_path_bar_get_preferred_width (GtkWidget *widget,
91 static void gtk_path_bar_get_preferred_height (GtkWidget *widget,
94 static void gtk_path_bar_map (GtkWidget *widget);
95 static void gtk_path_bar_unmap (GtkWidget *widget);
96 static void gtk_path_bar_size_allocate (GtkWidget *widget,
97 GtkAllocation *allocation);
98 static void gtk_path_bar_add (GtkContainer *container,
100 static void gtk_path_bar_remove (GtkContainer *container,
102 static void gtk_path_bar_forall (GtkContainer *container,
103 gboolean include_internals,
104 GtkCallback callback,
105 gpointer callback_data);
106 static GtkWidgetPath *gtk_path_bar_get_path_for_child (GtkContainer *container,
108 static gboolean gtk_path_bar_scroll (GtkWidget *widget,
109 GdkEventScroll *event);
110 static void gtk_path_bar_scroll_up (GtkPathBar *path_bar);
111 static void gtk_path_bar_scroll_down (GtkPathBar *path_bar);
112 static void gtk_path_bar_stop_scrolling (GtkPathBar *path_bar);
113 static gboolean gtk_path_bar_slider_up_defocus (GtkWidget *widget,
114 GdkEventButton *event,
115 GtkPathBar *path_bar);
116 static gboolean gtk_path_bar_slider_down_defocus (GtkWidget *widget,
117 GdkEventButton *event,
118 GtkPathBar *path_bar);
119 static gboolean gtk_path_bar_slider_button_press (GtkWidget *widget,
120 GdkEventButton *event,
121 GtkPathBar *path_bar);
122 static gboolean gtk_path_bar_slider_button_release(GtkWidget *widget,
123 GdkEventButton *event,
124 GtkPathBar *path_bar);
125 static void gtk_path_bar_grab_notify (GtkWidget *widget,
126 gboolean was_grabbed);
127 static void gtk_path_bar_state_changed (GtkWidget *widget,
128 GtkStateType previous_state);
129 static void gtk_path_bar_style_updated (GtkWidget *widget);
130 static void gtk_path_bar_screen_changed (GtkWidget *widget,
131 GdkScreen *previous_screen);
132 static void gtk_path_bar_check_icon_theme (GtkPathBar *path_bar);
133 static void gtk_path_bar_update_button_appearance (GtkPathBar *path_bar,
134 ButtonData *button_data,
135 gboolean current_dir);
138 on_slider_unmap (GtkWidget *widget,
139 GtkPathBar *path_bar)
141 if (path_bar->timer &&
142 ((widget == path_bar->up_slider_button && path_bar->scrolling_up) ||
143 (widget == path_bar->down_slider_button && path_bar->scrolling_down)))
144 gtk_path_bar_stop_scrolling (path_bar);
148 get_slider_button (GtkPathBar *path_bar,
149 GtkArrowType arrow_type)
154 gtk_widget_push_composite_child ();
156 button = gtk_button_new ();
157 atk_obj = gtk_widget_get_accessible (button);
158 if (arrow_type == GTK_ARROW_LEFT)
159 atk_object_set_name (atk_obj, _("Up Path"));
161 atk_object_set_name (atk_obj, _("Down Path"));
163 gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
164 gtk_widget_add_events (button, GDK_SCROLL_MASK);
165 gtk_container_add (GTK_CONTAINER (button),
166 gtk_arrow_new (arrow_type, GTK_SHADOW_OUT));
167 gtk_container_add (GTK_CONTAINER (path_bar), button);
168 gtk_widget_show_all (button);
170 g_signal_connect (G_OBJECT (button), "unmap",
171 G_CALLBACK (on_slider_unmap), path_bar);
173 gtk_widget_pop_composite_child ();
179 gtk_path_bar_init (GtkPathBar *path_bar)
181 GtkStyleContext *context;
183 gtk_widget_set_has_window (GTK_WIDGET (path_bar), FALSE);
184 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (path_bar), FALSE);
186 path_bar->get_info_cancellable = NULL;
188 path_bar->spacing = 0;
189 path_bar->up_slider_button = get_slider_button (path_bar, GTK_ARROW_LEFT);
190 path_bar->down_slider_button = get_slider_button (path_bar, GTK_ARROW_RIGHT);
191 path_bar->icon_size = FALLBACK_ICON_SIZE;
193 g_signal_connect_swapped (path_bar->up_slider_button, "clicked",
194 G_CALLBACK (gtk_path_bar_scroll_up), path_bar);
195 g_signal_connect_swapped (path_bar->down_slider_button, "clicked",
196 G_CALLBACK (gtk_path_bar_scroll_down), path_bar);
198 g_signal_connect (path_bar->up_slider_button, "focus-out-event",
199 G_CALLBACK (gtk_path_bar_slider_up_defocus), path_bar);
200 g_signal_connect (path_bar->down_slider_button, "focus-out-event",
201 G_CALLBACK (gtk_path_bar_slider_down_defocus), path_bar);
203 g_signal_connect (path_bar->up_slider_button, "button-press-event",
204 G_CALLBACK (gtk_path_bar_slider_button_press), path_bar);
205 g_signal_connect (path_bar->up_slider_button, "button-release-event",
206 G_CALLBACK (gtk_path_bar_slider_button_release), path_bar);
207 g_signal_connect (path_bar->down_slider_button, "button-press-event",
208 G_CALLBACK (gtk_path_bar_slider_button_press), path_bar);
209 g_signal_connect (path_bar->down_slider_button, "button-release-event",
210 G_CALLBACK (gtk_path_bar_slider_button_release), path_bar);
212 context = gtk_widget_get_style_context (GTK_WIDGET (path_bar));
213 gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED);
217 gtk_path_bar_class_init (GtkPathBarClass *path_bar_class)
219 GObjectClass *gobject_class;
220 GtkWidgetClass *widget_class;
221 GtkContainerClass *container_class;
223 gobject_class = (GObjectClass *) path_bar_class;
224 widget_class = (GtkWidgetClass *) path_bar_class;
225 container_class = (GtkContainerClass *) path_bar_class;
227 gobject_class->finalize = gtk_path_bar_finalize;
228 gobject_class->dispose = gtk_path_bar_dispose;
230 widget_class->get_preferred_width = gtk_path_bar_get_preferred_width;
231 widget_class->get_preferred_height = gtk_path_bar_get_preferred_height;
232 widget_class->realize = gtk_path_bar_realize;
233 widget_class->unrealize = gtk_path_bar_unrealize;
234 widget_class->map = gtk_path_bar_map;
235 widget_class->unmap = gtk_path_bar_unmap;
236 widget_class->size_allocate = gtk_path_bar_size_allocate;
237 widget_class->style_updated = gtk_path_bar_style_updated;
238 widget_class->screen_changed = gtk_path_bar_screen_changed;
239 widget_class->grab_notify = gtk_path_bar_grab_notify;
240 widget_class->state_changed = gtk_path_bar_state_changed;
241 widget_class->scroll_event = gtk_path_bar_scroll;
243 container_class->add = gtk_path_bar_add;
244 container_class->forall = gtk_path_bar_forall;
245 container_class->remove = gtk_path_bar_remove;
246 container_class->get_path_for_child = gtk_path_bar_get_path_for_child;
247 gtk_container_class_handle_border_width (container_class);
249 /* container_class->child_type = gtk_path_bar_child_type;*/
251 path_bar_signals [PATH_CLICKED] =
252 g_signal_new (I_("path-clicked"),
253 G_OBJECT_CLASS_TYPE (gobject_class),
255 G_STRUCT_OFFSET (GtkPathBarClass, path_clicked),
257 _gtk_marshal_VOID__POINTER_POINTER_BOOLEAN,
266 gtk_path_bar_finalize (GObject *object)
268 GtkPathBar *path_bar;
270 path_bar = GTK_PATH_BAR (object);
272 gtk_path_bar_stop_scrolling (path_bar);
274 g_list_free (path_bar->button_list);
275 if (path_bar->root_file)
276 g_object_unref (path_bar->root_file);
277 if (path_bar->home_file)
278 g_object_unref (path_bar->home_file);
279 if (path_bar->desktop_file)
280 g_object_unref (path_bar->desktop_file);
282 if (path_bar->root_icon)
283 g_object_unref (path_bar->root_icon);
284 if (path_bar->home_icon)
285 g_object_unref (path_bar->home_icon);
286 if (path_bar->desktop_icon)
287 g_object_unref (path_bar->desktop_icon);
289 if (path_bar->file_system)
290 g_object_unref (path_bar->file_system);
292 G_OBJECT_CLASS (gtk_path_bar_parent_class)->finalize (object);
295 /* Removes the settings signal handler. It's safe to call multiple times */
297 remove_settings_signal (GtkPathBar *path_bar,
300 if (path_bar->settings_signal_id)
302 GtkSettings *settings;
304 settings = gtk_settings_get_for_screen (screen);
305 g_signal_handler_disconnect (settings,
306 path_bar->settings_signal_id);
307 path_bar->settings_signal_id = 0;
312 gtk_path_bar_dispose (GObject *object)
314 GtkPathBar *path_bar = GTK_PATH_BAR (object);
316 remove_settings_signal (path_bar, gtk_widget_get_screen (GTK_WIDGET (object)));
318 if (path_bar->get_info_cancellable)
319 g_cancellable_cancel (path_bar->get_info_cancellable);
320 path_bar->get_info_cancellable = NULL;
322 G_OBJECT_CLASS (gtk_path_bar_parent_class)->dispose (object);
327 * Ideally, our size is determined by another widget, and we are just filling
331 gtk_path_bar_get_preferred_width (GtkWidget *widget,
335 ButtonData *button_data;
336 GtkPathBar *path_bar;
340 gint child_min, child_nat;
342 path_bar = GTK_PATH_BAR (widget);
344 *minimum = *natural = 0;
347 for (list = path_bar->button_list; list; list = list->next)
349 button_data = BUTTON_DATA (list->data);
350 gtk_widget_get_preferred_width (button_data->button, &child_min, &child_nat);
351 gtk_widget_get_preferred_height (button_data->button, &child_height, NULL);
352 height = MAX (height, child_height);
354 if (button_data->type == NORMAL_BUTTON)
356 /* Use 2*Height as button width because of ellipsized label. */
357 child_min = MAX (child_min, child_height * 2);
358 child_nat = MAX (child_min, child_height * 2);
361 *minimum = MAX (*minimum, child_min);
362 *natural = MAX (*natural, child_nat);
365 /* Add space for slider, if we have more than one path */
366 /* Theoretically, the slider could be bigger than the other button. But we're
367 * not going to worry about that now.
369 path_bar->slider_width = MIN (height * 2 / 3 + 5, height);
370 if (path_bar->button_list && path_bar->button_list->next != NULL)
372 *minimum += (path_bar->spacing + path_bar->slider_width) * 2;
373 *natural += (path_bar->spacing + path_bar->slider_width) * 2;
378 gtk_path_bar_get_preferred_height (GtkWidget *widget,
382 ButtonData *button_data;
383 GtkPathBar *path_bar;
385 gint child_min, child_nat;
387 path_bar = GTK_PATH_BAR (widget);
389 *minimum = *natural = 0;
391 for (list = path_bar->button_list; list; list = list->next)
393 button_data = BUTTON_DATA (list->data);
394 gtk_widget_get_preferred_height (button_data->button, &child_min, &child_nat);
396 *minimum = MAX (*minimum, child_min);
397 *natural = MAX (*natural, child_nat);
402 gtk_path_bar_update_slider_buttons (GtkPathBar *path_bar)
404 if (path_bar->button_list)
408 button = BUTTON_DATA (path_bar->button_list->data)->button;
409 if (gtk_widget_get_child_visible (button))
411 gtk_path_bar_stop_scrolling (path_bar);
412 gtk_widget_set_sensitive (path_bar->down_slider_button, FALSE);
415 gtk_widget_set_sensitive (path_bar->down_slider_button, TRUE);
417 button = BUTTON_DATA (g_list_last (path_bar->button_list)->data)->button;
418 if (gtk_widget_get_child_visible (button))
420 gtk_path_bar_stop_scrolling (path_bar);
421 gtk_widget_set_sensitive (path_bar->up_slider_button, FALSE);
424 gtk_widget_set_sensitive (path_bar->up_slider_button, TRUE);
429 gtk_path_bar_map (GtkWidget *widget)
431 gdk_window_show (GTK_PATH_BAR (widget)->event_window);
433 GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->map (widget);
437 gtk_path_bar_unmap (GtkWidget *widget)
439 gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
440 gdk_window_hide (GTK_PATH_BAR (widget)->event_window);
442 GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->unmap (widget);
446 gtk_path_bar_realize (GtkWidget *widget)
448 GtkPathBar *path_bar;
449 GtkAllocation allocation;
451 GdkWindowAttr attributes;
452 gint attributes_mask;
454 gtk_widget_set_realized (widget, TRUE);
456 path_bar = GTK_PATH_BAR (widget);
457 window = gtk_widget_get_parent_window (widget);
458 gtk_widget_set_window (widget, window);
459 g_object_ref (window);
461 gtk_widget_get_allocation (widget, &allocation);
463 attributes.window_type = GDK_WINDOW_CHILD;
464 attributes.x = allocation.x;
465 attributes.y = allocation.y;
466 attributes.width = allocation.width;
467 attributes.height = allocation.height;
468 attributes.wclass = GDK_INPUT_ONLY;
469 attributes.event_mask = gtk_widget_get_events (widget);
470 attributes.event_mask |= GDK_SCROLL_MASK;
471 attributes_mask = GDK_WA_X | GDK_WA_Y;
473 path_bar->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
474 &attributes, attributes_mask);
475 gdk_window_set_user_data (path_bar->event_window, widget);
479 gtk_path_bar_unrealize (GtkWidget *widget)
481 GtkPathBar *path_bar;
483 path_bar = GTK_PATH_BAR (widget);
485 gdk_window_set_user_data (path_bar->event_window, NULL);
486 gdk_window_destroy (path_bar->event_window);
487 path_bar->event_window = NULL;
489 GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->unrealize (widget);
493 child_ordering_changed (GtkPathBar *path_bar)
497 if (path_bar->up_slider_button)
498 _gtk_widget_invalidate_style_context (path_bar->up_slider_button,
499 GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
500 if (path_bar->down_slider_button)
501 _gtk_widget_invalidate_style_context (path_bar->down_slider_button,
502 GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
504 for (l = path_bar->button_list; l; l = l->next)
506 ButtonData *data = l->data;
508 _gtk_widget_invalidate_style_context (data->button,
509 GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
513 /* This is a tad complicated
516 gtk_path_bar_size_allocate (GtkWidget *widget,
517 GtkAllocation *allocation)
520 GtkPathBar *path_bar = GTK_PATH_BAR (widget);
521 GtkTextDirection direction;
522 GtkAllocation child_allocation;
523 GList *list, *first_button;
525 gint allocation_width;
526 gboolean need_sliders = FALSE;
527 gint up_slider_offset = 0;
528 GtkRequisition child_requisition;
529 gboolean needs_reorder = FALSE;
531 gtk_widget_set_allocation (widget, allocation);
533 if (gtk_widget_get_realized (widget))
534 gdk_window_move_resize (path_bar->event_window,
535 allocation->x, allocation->y,
536 allocation->width, allocation->height);
538 /* No path is set; we don't have to allocate anything. */
539 if (path_bar->button_list == NULL)
542 direction = gtk_widget_get_direction (widget);
543 allocation_width = allocation->width;
545 /* First, we check to see if we need the scrollbars. */
546 if (path_bar->fake_root)
547 width = path_bar->spacing + path_bar->slider_width;
551 for (list = path_bar->button_list; list; list = list->next)
553 child = BUTTON_DATA (list->data)->button;
555 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
557 width += child_requisition.width + path_bar->spacing;
558 if (list == path_bar->fake_root)
562 if (width <= allocation_width)
564 if (path_bar->fake_root)
565 first_button = path_bar->fake_root;
567 first_button = g_list_last (path_bar->button_list);
571 gboolean reached_end = FALSE;
572 gint slider_space = 2 * (path_bar->spacing + path_bar->slider_width);
574 if (path_bar->first_scrolled_button)
575 first_button = path_bar->first_scrolled_button;
577 first_button = path_bar->button_list;
580 /* To see how much space we have, and how many buttons we can display.
581 * We start at the first button, count forward until hit the new
582 * button, then count backwards.
584 /* Count down the path chain towards the end. */
585 gtk_widget_get_preferred_size (BUTTON_DATA (first_button->data)->button,
586 &child_requisition, NULL);
588 width = child_requisition.width;
589 list = first_button->prev;
590 while (list && !reached_end)
592 child = BUTTON_DATA (list->data)->button;
594 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
596 if (width + child_requisition.width +
597 path_bar->spacing + slider_space > allocation_width)
599 else if (list == path_bar->fake_root)
602 width += child_requisition.width + path_bar->spacing;
607 /* Finally, we walk up, seeing how many of the previous buttons we can
609 while (first_button->next && !reached_end)
611 child = BUTTON_DATA (first_button->next->data)->button;
613 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
615 if (width + child_requisition.width + path_bar->spacing + slider_space > allocation_width)
621 width += child_requisition.width + path_bar->spacing;
622 if (first_button == path_bar->fake_root)
624 first_button = first_button->next;
629 /* Now, we allocate space to the buttons */
630 child_allocation.y = allocation->y;
631 child_allocation.height = allocation->height;
633 if (direction == GTK_TEXT_DIR_RTL)
635 child_allocation.x = allocation->x + allocation->width;
636 if (need_sliders || path_bar->fake_root)
638 child_allocation.x -= (path_bar->spacing + path_bar->slider_width);
639 up_slider_offset = allocation->width - path_bar->slider_width;
644 child_allocation.x = allocation->x;
645 if (need_sliders || path_bar->fake_root)
647 up_slider_offset = 0;
648 child_allocation.x += (path_bar->spacing + path_bar->slider_width);
652 for (list = first_button; list; list = list->prev)
654 GtkAllocation widget_allocation;
655 ButtonData *button_data;
657 button_data = BUTTON_DATA (list->data);
658 child = button_data->button;
660 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
662 child_allocation.width = MIN (child_requisition.width,
663 allocation_width - (path_bar->spacing + path_bar->slider_width) * 2);
665 if (direction == GTK_TEXT_DIR_RTL)
666 child_allocation.x -= child_allocation.width;
668 /* Check to see if we've don't have any more space to allocate buttons */
669 if (need_sliders && direction == GTK_TEXT_DIR_RTL)
671 gtk_widget_get_allocation (widget, &widget_allocation);
672 if (child_allocation.x - path_bar->spacing - path_bar->slider_width < widget_allocation.x)
675 else if (need_sliders && direction == GTK_TEXT_DIR_LTR)
677 gtk_widget_get_allocation (widget, &widget_allocation);
678 if (child_allocation.x + child_allocation.width + path_bar->spacing + path_bar->slider_width >
679 widget_allocation.x + allocation_width)
683 if (child_allocation.width < child_requisition.width)
685 if (!gtk_widget_get_has_tooltip (child))
686 gtk_widget_set_tooltip_text (child, button_data->dir_name);
688 else if (gtk_widget_get_has_tooltip (child))
689 gtk_widget_set_tooltip_text (child, NULL);
691 needs_reorder |= gtk_widget_get_child_visible (child) == FALSE;
692 gtk_widget_set_child_visible (child, TRUE);
693 gtk_widget_size_allocate (child, &child_allocation);
695 if (direction == GTK_TEXT_DIR_RTL)
696 child_allocation.x -= path_bar->spacing;
698 child_allocation.x += child_allocation.width + path_bar->spacing;
700 /* Now we go hide all the widgets that don't fit */
703 needs_reorder |= gtk_widget_get_child_visible (child) == TRUE;
704 gtk_widget_set_child_visible (BUTTON_DATA (list->data)->button, FALSE);
707 for (list = first_button->next; list; list = list->next)
709 needs_reorder |= gtk_widget_get_child_visible (child) == TRUE;
710 gtk_widget_set_child_visible (BUTTON_DATA (list->data)->button, FALSE);
713 if (need_sliders || path_bar->fake_root)
715 child_allocation.width = path_bar->slider_width;
716 child_allocation.x = up_slider_offset + allocation->x;
717 gtk_widget_size_allocate (path_bar->up_slider_button, &child_allocation);
719 needs_reorder |= gtk_widget_get_child_visible (path_bar->up_slider_button) == FALSE;
720 gtk_widget_set_child_visible (path_bar->up_slider_button, TRUE);
721 gtk_widget_show_all (path_bar->up_slider_button);
725 needs_reorder |= gtk_widget_get_child_visible (path_bar->up_slider_button) == TRUE;
726 gtk_widget_set_child_visible (path_bar->up_slider_button, FALSE);
731 child_allocation.width = path_bar->slider_width;
733 if (direction == GTK_TEXT_DIR_RTL)
734 child_allocation.x = 0;
736 child_allocation.x = allocation->width - path_bar->slider_width;
738 child_allocation.x += allocation->x;
740 gtk_widget_size_allocate (path_bar->down_slider_button, &child_allocation);
742 needs_reorder |= gtk_widget_get_child_visible (path_bar->down_slider_button) == FALSE;
743 gtk_widget_set_child_visible (path_bar->down_slider_button, TRUE);
744 gtk_widget_show_all (path_bar->down_slider_button);
745 gtk_path_bar_update_slider_buttons (path_bar);
749 needs_reorder |= gtk_widget_get_child_visible (path_bar->down_slider_button) == TRUE;
750 gtk_widget_set_child_visible (path_bar->down_slider_button, FALSE);
754 child_ordering_changed (path_bar);
758 gtk_path_bar_style_updated (GtkWidget *widget)
760 GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->style_updated (widget);
762 gtk_path_bar_check_icon_theme (GTK_PATH_BAR (widget));
766 gtk_path_bar_screen_changed (GtkWidget *widget,
767 GdkScreen *previous_screen)
769 if (GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->screen_changed)
770 GTK_WIDGET_CLASS (gtk_path_bar_parent_class)->screen_changed (widget, previous_screen);
772 /* We might nave a new settings, so we remove the old one */
774 remove_settings_signal (GTK_PATH_BAR (widget), previous_screen);
776 gtk_path_bar_check_icon_theme (GTK_PATH_BAR (widget));
780 gtk_path_bar_scroll (GtkWidget *widget,
781 GdkEventScroll *event)
783 switch (event->direction)
785 case GDK_SCROLL_RIGHT:
786 case GDK_SCROLL_DOWN:
787 gtk_path_bar_scroll_down (GTK_PATH_BAR (widget));
789 case GDK_SCROLL_LEFT:
791 gtk_path_bar_scroll_up (GTK_PATH_BAR (widget));
793 case GDK_SCROLL_SMOOTH:
801 gtk_path_bar_add (GtkContainer *container,
805 gtk_widget_set_parent (widget, GTK_WIDGET (container));
809 gtk_path_bar_remove_1 (GtkContainer *container,
812 gboolean was_visible = gtk_widget_get_visible (widget);
813 gtk_widget_unparent (widget);
815 gtk_widget_queue_resize (GTK_WIDGET (container));
819 gtk_path_bar_remove (GtkContainer *container,
822 GtkPathBar *path_bar;
825 path_bar = GTK_PATH_BAR (container);
827 if (widget == path_bar->up_slider_button)
829 gtk_path_bar_remove_1 (container, widget);
830 path_bar->up_slider_button = NULL;
834 if (widget == path_bar->down_slider_button)
836 gtk_path_bar_remove_1 (container, widget);
837 path_bar->down_slider_button = NULL;
841 children = path_bar->button_list;
844 if (widget == BUTTON_DATA (children->data)->button)
846 gtk_path_bar_remove_1 (container, widget);
847 path_bar->button_list = g_list_remove_link (path_bar->button_list, children);
848 g_list_free (children);
852 children = children->next;
857 gtk_path_bar_forall (GtkContainer *container,
858 gboolean include_internals,
859 GtkCallback callback,
860 gpointer callback_data)
862 GtkPathBar *path_bar;
865 g_return_if_fail (callback != NULL);
866 path_bar = GTK_PATH_BAR (container);
868 children = path_bar->button_list;
872 child = BUTTON_DATA (children->data)->button;
873 children = children->next;
875 (* callback) (child, callback_data);
878 if (path_bar->up_slider_button)
879 (* callback) (path_bar->up_slider_button, callback_data);
881 if (path_bar->down_slider_button)
882 (* callback) (path_bar->down_slider_button, callback_data);
885 static GtkWidgetPath *
886 gtk_path_bar_get_path_for_child (GtkContainer *container,
889 GtkPathBar *path_bar = GTK_PATH_BAR (container);
892 path = _gtk_widget_create_path (GTK_WIDGET (path_bar));
894 if (gtk_widget_get_visible (child) &&
895 gtk_widget_get_child_visible (child))
897 GtkWidgetPath *sibling_path;
898 GList *visible_children;
902 /* 1. Build the list of visible children, in visually left-to-right order
903 * (i.e. independently of the widget's direction). Note that our
904 * button_list is stored in innermost-to-outermost path order!
907 visible_children = NULL;
909 if (gtk_widget_get_visible (path_bar->down_slider_button) &&
910 gtk_widget_get_child_visible (path_bar->down_slider_button))
911 visible_children = g_list_prepend (visible_children, path_bar->down_slider_button);
913 for (l = path_bar->button_list; l; l = l->next)
915 ButtonData *data = l->data;
917 if (gtk_widget_get_visible (data->button) &&
918 gtk_widget_get_child_visible (data->button))
919 visible_children = g_list_prepend (visible_children, data->button);
922 if (gtk_widget_get_visible (path_bar->up_slider_button) &&
923 gtk_widget_get_child_visible (path_bar->up_slider_button))
924 visible_children = g_list_prepend (visible_children, path_bar->up_slider_button);
926 if (gtk_widget_get_direction (GTK_WIDGET (path_bar)) == GTK_TEXT_DIR_RTL)
927 visible_children = g_list_reverse (visible_children);
929 /* 2. Find the index of the child within that list */
933 for (l = visible_children; l; l = l->next)
935 GtkWidget *button = l->data;
943 /* 3. Build the path */
945 sibling_path = gtk_widget_path_new ();
947 for (l = visible_children; l; l = l->next)
948 gtk_widget_path_append_for_widget (sibling_path, l->data);
950 gtk_widget_path_append_with_siblings (path, sibling_path, pos);
952 g_list_free (visible_children);
953 gtk_widget_path_unref (sibling_path);
956 gtk_widget_path_append_for_widget (path, child);
962 gtk_path_bar_scroll_down (GtkPathBar *path_bar)
964 GtkAllocation allocation, button_allocation;
966 GList *down_button = NULL;
967 gint space_available;
969 if (path_bar->ignore_click)
971 path_bar->ignore_click = FALSE;
975 if (gtk_widget_get_child_visible (BUTTON_DATA (path_bar->button_list->data)->button))
977 /* Return if the last button is already visible */
981 gtk_widget_queue_resize (GTK_WIDGET (path_bar));
983 /* We find the button at the 'down' end that we have to make
985 for (list = path_bar->button_list; list; list = list->next)
987 if (list->next && gtk_widget_get_child_visible (BUTTON_DATA (list->next->data)->button))
994 gtk_widget_get_allocation (GTK_WIDGET (path_bar), &allocation);
995 gtk_widget_get_allocation (BUTTON_DATA (down_button->data)->button, &button_allocation);
997 space_available = (allocation.width
998 - 2 * path_bar->spacing - 2 * path_bar->slider_width
999 - button_allocation.width);
1000 path_bar->first_scrolled_button = down_button;
1002 /* We have space_available free space that's not being used.
1003 * So we walk down from the end, adding buttons until we use all free space.
1005 while (space_available > 0)
1007 path_bar->first_scrolled_button = down_button;
1008 down_button = down_button->next;
1011 space_available -= (button_allocation.width
1012 + path_bar->spacing);
1017 gtk_path_bar_scroll_up (GtkPathBar *path_bar)
1021 if (path_bar->ignore_click)
1023 path_bar->ignore_click = FALSE;
1027 list = g_list_last (path_bar->button_list);
1029 if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button))
1031 /* Return if the first button is already visible */
1035 gtk_widget_queue_resize (GTK_WIDGET (path_bar));
1037 for ( ; list; list = list->prev)
1039 if (list->prev && gtk_widget_get_child_visible (BUTTON_DATA (list->prev->data)->button))
1041 if (list->prev == path_bar->fake_root)
1042 path_bar->fake_root = NULL;
1043 path_bar->first_scrolled_button = list;
1050 gtk_path_bar_scroll_timeout (GtkPathBar *path_bar)
1052 gboolean retval = FALSE;
1054 if (path_bar->timer)
1056 if (path_bar->scrolling_up)
1057 gtk_path_bar_scroll_up (path_bar);
1058 else if (path_bar->scrolling_down)
1059 gtk_path_bar_scroll_down (path_bar);
1061 if (path_bar->need_timer)
1063 GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (path_bar));
1066 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
1068 path_bar->need_timer = FALSE;
1070 path_bar->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
1071 (GSourceFunc)gtk_path_bar_scroll_timeout,
1082 gtk_path_bar_stop_scrolling (GtkPathBar *path_bar)
1084 if (path_bar->timer)
1086 g_source_remove (path_bar->timer);
1087 path_bar->timer = 0;
1088 path_bar->need_timer = FALSE;
1093 gtk_path_bar_slider_up_defocus (GtkWidget *widget,
1094 GdkEventButton *event,
1095 GtkPathBar *path_bar)
1098 GList *up_button = NULL;
1100 if (event->type != GDK_FOCUS_CHANGE)
1103 for (list = g_list_last (path_bar->button_list); list; list = list->prev)
1105 if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button))
1112 /* don't let the focus vanish */
1113 if ((!gtk_widget_is_sensitive (path_bar->up_slider_button)) ||
1114 (!gtk_widget_get_child_visible (path_bar->up_slider_button)))
1115 gtk_widget_grab_focus (BUTTON_DATA (up_button->data)->button);
1121 gtk_path_bar_slider_down_defocus (GtkWidget *widget,
1122 GdkEventButton *event,
1123 GtkPathBar *path_bar)
1126 GList *down_button = NULL;
1128 if (event->type != GDK_FOCUS_CHANGE)
1131 for (list = path_bar->button_list; list; list = list->next)
1133 if (gtk_widget_get_child_visible (BUTTON_DATA (list->data)->button))
1140 /* don't let the focus vanish */
1141 if ((!gtk_widget_is_sensitive (path_bar->down_slider_button)) ||
1142 (!gtk_widget_get_child_visible (path_bar->down_slider_button)))
1143 gtk_widget_grab_focus (BUTTON_DATA (down_button->data)->button);
1149 gtk_path_bar_slider_button_press (GtkWidget *widget,
1150 GdkEventButton *event,
1151 GtkPathBar *path_bar)
1153 if (event->type != GDK_BUTTON_PRESS || event->button != GDK_BUTTON_PRIMARY)
1156 path_bar->ignore_click = FALSE;
1158 if (widget == path_bar->up_slider_button)
1160 path_bar->scrolling_down = FALSE;
1161 path_bar->scrolling_up = TRUE;
1162 gtk_path_bar_scroll_up (path_bar);
1164 else if (widget == path_bar->down_slider_button)
1166 path_bar->scrolling_up = FALSE;
1167 path_bar->scrolling_down = TRUE;
1168 gtk_path_bar_scroll_down (path_bar);
1171 if (!path_bar->timer)
1173 GtkSettings *settings = gtk_widget_get_settings (widget);
1176 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
1178 path_bar->need_timer = TRUE;
1179 path_bar->timer = gdk_threads_add_timeout (timeout,
1180 (GSourceFunc)gtk_path_bar_scroll_timeout,
1188 gtk_path_bar_slider_button_release (GtkWidget *widget,
1189 GdkEventButton *event,
1190 GtkPathBar *path_bar)
1192 if (event->type != GDK_BUTTON_RELEASE)
1195 path_bar->ignore_click = TRUE;
1196 gtk_path_bar_stop_scrolling (path_bar);
1202 gtk_path_bar_grab_notify (GtkWidget *widget,
1203 gboolean was_grabbed)
1206 gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
1210 gtk_path_bar_state_changed (GtkWidget *widget,
1211 GtkStateType previous_state)
1213 if (!gtk_widget_is_sensitive (widget))
1214 gtk_path_bar_stop_scrolling (GTK_PATH_BAR (widget));
1218 /* Changes the icons wherever it is needed */
1220 reload_icons (GtkPathBar *path_bar)
1224 if (path_bar->root_icon)
1226 g_object_unref (path_bar->root_icon);
1227 path_bar->root_icon = NULL;
1229 if (path_bar->home_icon)
1231 g_object_unref (path_bar->home_icon);
1232 path_bar->home_icon = NULL;
1234 if (path_bar->desktop_icon)
1236 g_object_unref (path_bar->desktop_icon);
1237 path_bar->desktop_icon = NULL;
1240 for (list = path_bar->button_list; list; list = list->next)
1242 ButtonData *button_data;
1243 gboolean current_dir;
1245 button_data = BUTTON_DATA (list->data);
1246 if (button_data->type != NORMAL_BUTTON)
1248 current_dir = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_data->button));
1249 gtk_path_bar_update_button_appearance (path_bar, button_data, current_dir);
1256 change_icon_theme (GtkPathBar *path_bar)
1258 GtkSettings *settings;
1261 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (path_bar)));
1263 if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
1264 path_bar->icon_size = MAX (width, height);
1266 path_bar->icon_size = FALLBACK_ICON_SIZE;
1268 reload_icons (path_bar);
1270 /* Callback used when a GtkSettings value changes */
1272 settings_notify_cb (GObject *object,
1274 GtkPathBar *path_bar)
1278 name = g_param_spec_get_name (pspec);
1280 if (! strcmp (name, "gtk-icon-theme-name") ||
1281 ! strcmp (name, "gtk-icon-sizes"))
1282 change_icon_theme (path_bar);
1286 gtk_path_bar_check_icon_theme (GtkPathBar *path_bar)
1288 GtkSettings *settings;
1290 if (path_bar->settings_signal_id)
1293 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (path_bar)));
1294 path_bar->settings_signal_id = g_signal_connect (settings, "notify", G_CALLBACK (settings_notify_cb), path_bar);
1296 change_icon_theme (path_bar);
1299 /* Public functions and their helpers */
1301 gtk_path_bar_clear_buttons (GtkPathBar *path_bar)
1303 while (path_bar->button_list != NULL)
1305 gtk_container_remove (GTK_CONTAINER (path_bar), BUTTON_DATA (path_bar->button_list->data)->button);
1307 path_bar->first_scrolled_button = NULL;
1308 path_bar->fake_root = NULL;
1312 button_clicked_cb (GtkWidget *button,
1315 ButtonData *button_data;
1316 GtkPathBar *path_bar;
1318 gboolean child_is_hidden;
1321 button_data = BUTTON_DATA (data);
1322 if (button_data->ignore_changes)
1325 path_bar = GTK_PATH_BAR (gtk_widget_get_parent (button));
1327 button_list = g_list_find (path_bar->button_list, button_data);
1328 g_assert (button_list != NULL);
1330 g_signal_handlers_block_by_func (button,
1331 G_CALLBACK (button_clicked_cb), data);
1332 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
1333 g_signal_handlers_unblock_by_func (button,
1334 G_CALLBACK (button_clicked_cb), data);
1336 if (button_list->prev)
1338 ButtonData *child_data;
1340 child_data = BUTTON_DATA (button_list->prev->data);
1341 child_file = child_data->file;
1342 child_is_hidden = child_data->file_is_hidden;
1347 child_is_hidden = FALSE;
1350 g_signal_emit (path_bar, path_bar_signals [PATH_CLICKED], 0,
1351 button_data->file, child_file, child_is_hidden);
1354 struct SetButtonImageData
1356 GtkPathBar *path_bar;
1357 ButtonData *button_data;
1361 set_button_image_get_info_cb (GCancellable *cancellable,
1363 const GError *error,
1366 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1368 struct SetButtonImageData *data = user_data;
1370 if (cancellable != data->button_data->cancellable)
1373 data->button_data->cancellable = NULL;
1375 if (!data->button_data->button)
1377 g_free (data->button_data);
1381 if (cancelled || error)
1384 pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->path_bar),
1385 data->path_bar->icon_size);
1386 gtk_image_set_from_pixbuf (GTK_IMAGE (data->button_data->image), pixbuf);
1388 switch (data->button_data->type)
1391 if (data->path_bar->home_icon)
1392 g_object_unref (pixbuf);
1394 data->path_bar->home_icon = pixbuf;
1397 case DESKTOP_BUTTON:
1398 if (data->path_bar->desktop_icon)
1399 g_object_unref (pixbuf);
1401 data->path_bar->desktop_icon = pixbuf;
1410 g_object_unref (cancellable);
1414 set_button_image (GtkPathBar *path_bar,
1415 ButtonData *button_data)
1417 GtkFileSystemVolume *volume;
1418 struct SetButtonImageData *data;
1420 switch (button_data->type)
1424 if (path_bar->root_icon != NULL)
1426 gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
1430 volume = _gtk_file_system_get_volume_for_file (path_bar->file_system, path_bar->root_file);
1434 path_bar->root_icon = _gtk_file_system_volume_render_icon (volume,
1435 GTK_WIDGET (path_bar),
1436 path_bar->icon_size,
1438 _gtk_file_system_volume_unref (volume);
1440 gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
1444 if (path_bar->home_icon != NULL)
1446 gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->home_icon);
1450 data = g_new0 (struct SetButtonImageData, 1);
1451 data->path_bar = path_bar;
1452 data->button_data = button_data;
1454 if (button_data->cancellable)
1455 g_cancellable_cancel (button_data->cancellable);
1457 button_data->cancellable =
1458 _gtk_file_system_get_info (path_bar->file_system,
1459 path_bar->home_file,
1461 set_button_image_get_info_cb,
1465 case DESKTOP_BUTTON:
1466 if (path_bar->desktop_icon != NULL)
1468 gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->desktop_icon);
1472 data = g_new0 (struct SetButtonImageData, 1);
1473 data->path_bar = path_bar;
1474 data->button_data = button_data;
1476 if (button_data->cancellable)
1477 g_cancellable_cancel (button_data->cancellable);
1479 button_data->cancellable =
1480 _gtk_file_system_get_info (path_bar->file_system,
1481 path_bar->desktop_file,
1483 set_button_image_get_info_cb,
1492 button_data_free (ButtonData *button_data)
1494 if (button_data->file)
1495 g_object_unref (button_data->file);
1496 button_data->file = NULL;
1498 g_free (button_data->dir_name);
1499 button_data->dir_name = NULL;
1501 button_data->button = NULL;
1503 if (button_data->cancellable)
1504 g_cancellable_cancel (button_data->cancellable);
1506 g_free (button_data);
1510 get_dir_name (ButtonData *button_data)
1512 return button_data->dir_name;
1515 /* We always want to request the same size for the label, whether
1516 * or not the contents are bold
1519 set_label_size_request (GtkWidget *widget,
1520 ButtonData *button_data)
1522 const gchar *dir_name = get_dir_name (button_data);
1523 PangoLayout *layout = gtk_widget_create_pango_layout (button_data->label, dir_name);
1524 gint width, height, bold_width, bold_height;
1527 pango_layout_get_pixel_size (layout, &width, &height);
1529 markup = g_markup_printf_escaped ("<b>%s</b>", dir_name);
1530 pango_layout_set_markup (layout, markup, -1);
1533 pango_layout_get_pixel_size (layout, &bold_width, &bold_height);
1535 gtk_widget_set_size_request (widget,
1536 MAX (width, bold_width),
1537 MAX (height, bold_height));
1538 g_object_unref (layout);
1542 gtk_path_bar_update_button_appearance (GtkPathBar *path_bar,
1543 ButtonData *button_data,
1544 gboolean current_dir)
1546 const gchar *dir_name = get_dir_name (button_data);
1548 if (button_data->label != NULL)
1554 markup = g_markup_printf_escaped ("<b>%s</b>", dir_name);
1555 gtk_label_set_markup (GTK_LABEL (button_data->label), markup);
1560 gtk_label_set_text (GTK_LABEL (button_data->label), dir_name);
1564 if (button_data->image != NULL)
1566 set_button_image (path_bar, button_data);
1569 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_data->button)) != current_dir)
1571 button_data->ignore_changes = TRUE;
1572 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button_data->button), current_dir);
1573 button_data->ignore_changes = FALSE;
1578 find_button_type (GtkPathBar *path_bar,
1581 if (path_bar->root_file != NULL &&
1582 g_file_equal (file, path_bar->root_file))
1584 if (path_bar->home_file != NULL &&
1585 g_file_equal (file, path_bar->home_file))
1587 if (path_bar->desktop_file != NULL &&
1588 g_file_equal (file, path_bar->desktop_file))
1589 return DESKTOP_BUTTON;
1591 return NORMAL_BUTTON;
1595 button_drag_data_get_cb (GtkWidget *widget,
1596 GdkDragContext *context,
1597 GtkSelectionData *selection_data,
1602 ButtonData *button_data;
1607 uris[0] = g_file_get_uri (button_data->file);
1610 gtk_selection_data_set_uris (selection_data, uris);
1616 make_directory_button (GtkPathBar *path_bar,
1617 const char *dir_name,
1619 gboolean current_dir,
1620 gboolean file_is_hidden)
1623 GtkWidget *child = NULL;
1624 ButtonData *button_data;
1626 file_is_hidden = !! file_is_hidden;
1627 /* Is it a special button? */
1628 button_data = g_new0 (ButtonData, 1);
1630 button_data->type = find_button_type (path_bar, file);
1631 button_data->button = gtk_toggle_button_new ();
1632 atk_obj = gtk_widget_get_accessible (button_data->button);
1633 gtk_button_set_focus_on_click (GTK_BUTTON (button_data->button), FALSE);
1634 gtk_widget_add_events (button_data->button, GDK_SCROLL_MASK);
1636 switch (button_data->type)
1639 button_data->image = gtk_image_new ();
1640 child = button_data->image;
1641 button_data->label = NULL;
1642 atk_object_set_name (atk_obj, _("File System Root"));
1645 case DESKTOP_BUTTON:
1646 button_data->image = gtk_image_new ();
1647 button_data->label = gtk_label_new (NULL);
1648 child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
1649 gtk_box_pack_start (GTK_BOX (child), button_data->image, FALSE, FALSE, 0);
1650 gtk_box_pack_start (GTK_BOX (child), button_data->label, FALSE, FALSE, 0);
1654 button_data->label = gtk_label_new (NULL);
1655 gtk_label_set_ellipsize (GTK_LABEL (button_data->label), PANGO_ELLIPSIZE_END);
1656 child = button_data->label;
1657 button_data->image = NULL;
1660 button_data->dir_name = g_strdup (dir_name);
1661 button_data->file = g_object_ref (file);
1662 button_data->file_is_hidden = file_is_hidden;
1665 * The following function ensures that the alignment will always
1666 * request the same size whether the button's text is bold or not.
1668 if (button_data->label)
1669 set_label_size_request (button_data->label, button_data);
1671 gtk_container_add (GTK_CONTAINER (button_data->button), child);
1672 gtk_widget_show_all (button_data->button);
1674 gtk_path_bar_update_button_appearance (path_bar, button_data, current_dir);
1676 g_signal_connect (button_data->button, "clicked",
1677 G_CALLBACK (button_clicked_cb),
1679 g_object_weak_ref (G_OBJECT (button_data->button),
1680 (GWeakNotify) button_data_free, button_data);
1682 gtk_drag_source_set (button_data->button,
1686 gtk_drag_source_add_uri_targets (button_data->button);
1687 g_signal_connect (button_data->button, "drag-data-get",
1688 G_CALLBACK (button_drag_data_get_cb), button_data);
1694 gtk_path_bar_check_parent_path (GtkPathBar *path_bar,
1698 GList *current_path = NULL;
1699 gboolean need_new_fake_root = FALSE;
1701 for (list = path_bar->button_list; list; list = list->next)
1703 ButtonData *button_data;
1705 button_data = list->data;
1706 if (g_file_equal (file, button_data->file))
1708 current_path = list;
1711 if (list == path_bar->fake_root)
1712 need_new_fake_root = TRUE;
1717 if (need_new_fake_root)
1719 path_bar->fake_root = NULL;
1720 for (list = current_path; list; list = list->next)
1722 ButtonData *button_data;
1724 button_data = list->data;
1725 if (BUTTON_IS_FAKE_ROOT (button_data))
1727 path_bar->fake_root = list;
1733 for (list = path_bar->button_list; list; list = list->next)
1735 gtk_path_bar_update_button_appearance (path_bar,
1736 BUTTON_DATA (list->data),
1737 (list == current_path) ? TRUE : FALSE);
1740 if (!gtk_widget_get_child_visible (BUTTON_DATA (current_path->data)->button))
1742 path_bar->first_scrolled_button = current_path;
1743 gtk_widget_queue_resize (GTK_WIDGET (path_bar));
1756 GtkPathBar *path_bar;
1759 gboolean first_directory;
1763 gtk_path_bar_set_file_finish (struct SetFileInfo *info,
1770 gtk_path_bar_clear_buttons (info->path_bar);
1771 info->path_bar->button_list = g_list_reverse (info->new_buttons);
1772 info->path_bar->fake_root = info->fake_root;
1774 for (l = info->path_bar->button_list; l; l = l->next)
1776 GtkWidget *button = BUTTON_DATA (l->data)->button;
1777 gtk_container_add (GTK_CONTAINER (info->path_bar), button);
1780 child_ordering_changed (info->path_bar);
1786 for (l = info->new_buttons; l; l = l->next)
1788 ButtonData *button_data;
1790 button_data = BUTTON_DATA (l->data);
1791 gtk_widget_destroy (button_data->button);
1794 g_list_free (info->new_buttons);
1798 g_object_unref (info->file);
1799 if (info->parent_file)
1800 g_object_unref (info->parent_file);
1806 gtk_path_bar_get_info_callback (GCancellable *cancellable,
1808 const GError *error,
1811 gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1812 struct SetFileInfo *file_info = data;
1813 ButtonData *button_data;
1814 const gchar *display_name;
1817 if (cancellable != file_info->path_bar->get_info_cancellable)
1819 gtk_path_bar_set_file_finish (file_info, FALSE);
1820 g_object_unref (cancellable);
1824 g_object_unref (cancellable);
1825 file_info->path_bar->get_info_cancellable = NULL;
1827 if (cancelled || !info)
1829 gtk_path_bar_set_file_finish (file_info, FALSE);
1833 display_name = g_file_info_get_display_name (info);
1834 is_hidden = g_file_info_get_is_hidden (info) || g_file_info_get_is_backup (info);
1836 gtk_widget_push_composite_child ();
1837 button_data = make_directory_button (file_info->path_bar, display_name,
1839 file_info->first_directory, is_hidden);
1840 gtk_widget_pop_composite_child ();
1841 g_object_unref (file_info->file);
1843 file_info->new_buttons = g_list_prepend (file_info->new_buttons, button_data);
1845 if (BUTTON_IS_FAKE_ROOT (button_data))
1846 file_info->fake_root = file_info->new_buttons;
1848 /* We have assigned the info for the innermost button, i.e. the deepest directory.
1849 * Now, go on to fetch the info for this directory's parent.
1852 file_info->file = file_info->parent_file;
1853 file_info->first_directory = FALSE;
1855 if (!file_info->file)
1857 /* No parent? Okay, we are done. */
1858 gtk_path_bar_set_file_finish (file_info, TRUE);
1862 file_info->parent_file = g_file_get_parent (file_info->file);
1864 /* Recurse asynchronously */
1865 file_info->path_bar->get_info_cancellable =
1866 _gtk_file_system_get_info (file_info->path_bar->file_system,
1868 "standard::display-name,standard::is-hidden,standard::is-backup",
1869 gtk_path_bar_get_info_callback,
1874 _gtk_path_bar_set_file (GtkPathBar *path_bar,
1876 const gboolean keep_trail)
1878 struct SetFileInfo *info;
1880 g_return_if_fail (GTK_IS_PATH_BAR (path_bar));
1881 g_return_if_fail (G_IS_FILE (file));
1883 /* Check whether the new path is already present in the pathbar as buttons.
1884 * This could be a parent directory or a previous selected subdirectory.
1886 if (keep_trail && gtk_path_bar_check_parent_path (path_bar, file))
1889 info = g_new0 (struct SetFileInfo, 1);
1890 info->file = g_object_ref (file);
1891 info->path_bar = path_bar;
1892 info->first_directory = TRUE;
1893 info->parent_file = g_file_get_parent (info->file);
1895 if (path_bar->get_info_cancellable)
1896 g_cancellable_cancel (path_bar->get_info_cancellable);
1898 path_bar->get_info_cancellable =
1899 _gtk_file_system_get_info (path_bar->file_system,
1901 "standard::display-name,standard::is-hidden,standard::is-backup",
1902 gtk_path_bar_get_info_callback,
1906 /* FIXME: This should be a construct-only property */
1908 _gtk_path_bar_set_file_system (GtkPathBar *path_bar,
1909 GtkFileSystem *file_system)
1913 g_return_if_fail (GTK_IS_PATH_BAR (path_bar));
1915 g_assert (path_bar->file_system == NULL);
1917 path_bar->file_system = g_object_ref (file_system);
1919 home = g_get_home_dir ();
1922 const gchar *desktop;
1924 path_bar->home_file = g_file_new_for_path (home);
1925 /* FIXME: Need file system backend specific way of getting the
1928 desktop = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1929 if (desktop != NULL)
1930 path_bar->desktop_file = g_file_new_for_path (desktop);
1932 path_bar->desktop_file = NULL;
1936 path_bar->home_file = NULL;
1937 path_bar->desktop_file = NULL;
1939 path_bar->root_file = g_file_new_for_path ("/");
1944 * @path_bar: a #GtkPathBar
1946 * If the selected button in the pathbar is not the furthest button "up" (in the
1947 * root direction), act as if the user clicked on the next button up.
1950 _gtk_path_bar_up (GtkPathBar *path_bar)
1954 for (l = path_bar->button_list; l; l = l->next)
1956 GtkWidget *button = BUTTON_DATA (l->data)->button;
1957 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1961 GtkWidget *next_button = BUTTON_DATA (l->next->data)->button;
1962 button_clicked_cb (next_button, l->next->data);
1970 * _gtk_path_bar_down:
1971 * @path_bar: a #GtkPathBar
1973 * If the selected button in the pathbar is not the furthest button "down" (in the
1974 * leaf direction), act as if the user clicked on the next button down.
1977 _gtk_path_bar_down (GtkPathBar *path_bar)
1981 for (l = path_bar->button_list; l; l = l->next)
1983 GtkWidget *button = BUTTON_DATA (l->data)->button;
1984 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1988 GtkWidget *prev_button = BUTTON_DATA (l->prev->data)->button;
1989 button_clicked_cb (prev_button, l->prev->data);