1 /* nautilus-pathbar.c
2 * Copyright (C) 2004 Red Hat, Inc., Jonathan Blandford <jrb@gnome.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18
19 #include <config.h>
20 #include <string.h>
21 #include <gtk/gtk.h>
22 #include <glib/gi18n.h>
23 #include <gio/gio.h>
24
25 #include "nautilus-pathbar.h"
26 #include "nautilus-properties-window.h"
27
28 #include "nautilus-file.h"
29 #include "nautilus-file-utilities.h"
30 #include "nautilus-global-preferences.h"
31 #include "nautilus-icon-names.h"
32 #include "nautilus-trash-monitor.h"
33 #include "nautilus-ui-utilities.h"
34
35 #include "nautilus-window-slot-dnd.h"
36
37 enum
38 {
39 OPEN_LOCATION,
40 PATH_CLICKED,
41 LAST_SIGNAL
42 };
43
44 typedef enum
45 {
46 NORMAL_BUTTON,
47 OTHER_LOCATIONS_BUTTON,
48 ROOT_BUTTON,
49 ADMIN_ROOT_BUTTON,
50 HOME_BUTTON,
51 STARRED_BUTTON,
52 RECENT_BUTTON,
53 MOUNT_BUTTON,
54 TRASH_BUTTON,
55 } ButtonType;
56
57 #define BUTTON_DATA(x) ((ButtonData *) (x))
58
59 static guint path_bar_signals[LAST_SIGNAL] = { 0 };
60
61 #define NAUTILUS_PATH_BAR_BUTTON_MAX_WIDTH 175
62
63 typedef struct
64 {
65 GtkWidget *button;
66 ButtonType type;
67 char *dir_name;
68 GFile *path;
69 NautilusFile *file;
70 unsigned int file_changed_signal_id;
71
72 GtkWidget *image;
73 GtkWidget *label;
74 GtkWidget *separator;
75 GtkWidget *disclosure_arrow;
76 GtkWidget *container;
77
78 NautilusPathBar *path_bar;
79
80 GtkGesture *multi_press_gesture;
81
82 guint ignore_changes : 1;
83 guint is_root : 1;
84 } ButtonData;
85
86 struct _NautilusPathBar
87 {
88 GtkContainer parent_instance;
89
90 GdkWindow *event_window;
91
92 GFile *current_path;
93 gpointer current_button_data;
94
95 GList *button_list;
96 gulong settings_signal_id;
97
98 GActionGroup *action_group;
99
100 NautilusFile *context_menu_file;
101 GtkPopover *current_view_menu_popover;
102 GtkPopover *button_menu_popover;
103 GMenu *current_view_menu;
104 GMenu *extensions_section;
105 GMenu *templates_submenu;
106 GMenu *button_menu;
107 };
108
109 G_DEFINE_TYPE (NautilusPathBar, nautilus_path_bar, GTK_TYPE_CONTAINER);
110
111 static void nautilus_path_bar_check_icon_theme (NautilusPathBar *self);
112 static void nautilus_path_bar_update_button_appearance (ButtonData *button_data,
113 gboolean current_dir);
114 static void nautilus_path_bar_update_button_state (ButtonData *button_data,
115 gboolean current_dir);
116 static void nautilus_path_bar_update_path (NautilusPathBar *self,
117 GFile *file_path);
118
119 static void unschedule_pop_up_context_menu (NautilusPathBar *self);
120 static void action_pathbar_open_item_new_window (GSimpleAction *action,
121 GVariant *state,
122 gpointer user_data);
123 static void action_pathbar_open_item_new_tab (GSimpleAction *action,
124 GVariant *state,
125 gpointer user_data);
126 static void action_pathbar_properties (GSimpleAction *action,
127 GVariant *state,
128 gpointer user_data);
129 static void pop_up_pathbar_context_menu (NautilusPathBar *self,
130 NautilusFile *file);
131
132 const GActionEntry path_bar_actions[] =
133 {
134 { "open-item-new-tab", action_pathbar_open_item_new_tab },
135 { "open-item-new-window", action_pathbar_open_item_new_window },
136 { "properties", action_pathbar_properties}
137 };
138
139
140 static void
action_pathbar_open_item_new_tab(GSimpleAction * action,GVariant * state,gpointer user_data)141 action_pathbar_open_item_new_tab (GSimpleAction *action,
142 GVariant *state,
143 gpointer user_data)
144 {
145 NautilusPathBar *self;
146 GFile *location;
147
148 self = NAUTILUS_PATH_BAR (user_data);
149
150 if (self->context_menu_file == NULL)
151 {
152 return;
153 }
154
155 location = nautilus_file_get_location (self->context_menu_file);
156
157 if (location)
158 {
159 g_signal_emit (user_data, path_bar_signals[OPEN_LOCATION], 0, location, GTK_PLACES_OPEN_NEW_TAB);
160 g_object_unref (location);
161 }
162 }
163
164 static void
action_pathbar_open_item_new_window(GSimpleAction * action,GVariant * state,gpointer user_data)165 action_pathbar_open_item_new_window (GSimpleAction *action,
166 GVariant *state,
167 gpointer user_data)
168 {
169 NautilusPathBar *self;
170 GFile *location;
171
172 self = NAUTILUS_PATH_BAR (user_data);
173
174 if (self->context_menu_file == NULL)
175 {
176 return;
177 }
178
179 location = nautilus_file_get_location (self->context_menu_file);
180
181 if (location)
182 {
183 g_signal_emit (user_data, path_bar_signals[OPEN_LOCATION], 0, location, GTK_PLACES_OPEN_NEW_WINDOW);
184 g_object_unref (location);
185 }
186 }
187
188 static void
action_pathbar_properties(GSimpleAction * action,GVariant * state,gpointer user_data)189 action_pathbar_properties (GSimpleAction *action,
190 GVariant *state,
191 gpointer user_data)
192 {
193 NautilusPathBar *self;
194 GList *files;
195
196 self = NAUTILUS_PATH_BAR (user_data);
197
198 g_return_if_fail (NAUTILUS_IS_FILE (self->context_menu_file));
199
200 files = g_list_append (NULL, nautilus_file_ref (self->context_menu_file));
201
202 nautilus_properties_window_present (files, GTK_WIDGET (self), NULL, NULL,
203 NULL);
204
205 nautilus_file_list_free (files);
206 }
207
208 static void
nautilus_path_bar_init(NautilusPathBar * self)209 nautilus_path_bar_init (NautilusPathBar *self)
210 {
211 GtkBuilder *builder;
212 g_autoptr (GError) error = NULL;
213
214 builder = gtk_builder_new ();
215
216 /* Add context menu for pathbar buttons */
217 gtk_builder_add_from_resource (builder,
218 "/org/gnome/nautilus/ui/nautilus-pathbar-context-menu.ui",
219 &error);
220 if (error != NULL)
221 {
222 g_error ("Failed to add pathbar-context-menu.ui: %s", error->message);
223 }
224 self->button_menu = g_object_ref_sink (G_MENU (gtk_builder_get_object (builder, "button-menu")));
225 self->button_menu_popover = g_object_ref_sink (GTK_POPOVER (gtk_popover_new_from_model (NULL,
226 G_MENU_MODEL (self->button_menu))));
227
228 /* Add current location menu, which matches the view's background context menu */
229 gtk_builder_add_from_resource (builder,
230 "/org/gnome/nautilus/ui/nautilus-files-view-context-menus.ui",
231 &error);
232 if (error != NULL)
233 {
234 g_error ("failed to add files-view-context-menus.ui: %s", error->message);
235 }
236 self->current_view_menu = g_object_ref_sink (G_MENU (gtk_builder_get_object (builder, "background-menu")));
237 self->extensions_section = g_object_ref (G_MENU (gtk_builder_get_object (builder, "background-extensions-section")));
238 self->templates_submenu = g_object_ref (G_MENU (gtk_builder_get_object (builder, "templates-submenu")));
239 self->current_view_menu_popover = g_object_ref_sink (GTK_POPOVER (gtk_popover_new_from_model (NULL,
240 G_MENU_MODEL (self->current_view_menu))));
241
242 g_object_unref (builder);
243
244 gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
245 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (self), FALSE);
246
247 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self)),
248 GTK_STYLE_CLASS_LINKED);
249 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self)),
250 "nautilus-path-bar");
251
252 /* Action group */
253 self->action_group = G_ACTION_GROUP (g_simple_action_group_new ());
254 g_action_map_add_action_entries (G_ACTION_MAP (self->action_group),
255 path_bar_actions,
256 G_N_ELEMENTS (path_bar_actions),
257 self);
258 gtk_widget_insert_action_group (GTK_WIDGET (self),
259 "pathbar",
260 G_ACTION_GROUP (self->action_group));
261 }
262
263 static void
nautilus_path_bar_finalize(GObject * object)264 nautilus_path_bar_finalize (GObject *object)
265 {
266 NautilusPathBar *self;
267
268 self = NAUTILUS_PATH_BAR (object);
269
270 g_list_free (self->button_list);
271 g_clear_object (&self->current_view_menu);
272 g_clear_object (&self->extensions_section);
273 g_clear_object (&self->templates_submenu);
274 g_clear_object (&self->button_menu);
275 g_clear_object (&self->button_menu_popover);
276 g_clear_object (&self->current_view_menu_popover);
277
278 unschedule_pop_up_context_menu (NAUTILUS_PATH_BAR (object));
279
280 G_OBJECT_CLASS (nautilus_path_bar_parent_class)->finalize (object);
281 }
282
283 /* Removes the settings signal handler. It's safe to call multiple times */
284 static void
remove_settings_signal(NautilusPathBar * self,GdkScreen * screen)285 remove_settings_signal (NautilusPathBar *self,
286 GdkScreen *screen)
287 {
288 GtkSettings *settings;
289
290 settings = gtk_settings_get_for_screen (screen);
291
292 g_clear_signal_handler (&self->settings_signal_id, settings);
293 }
294
295 static void
nautilus_path_bar_dispose(GObject * object)296 nautilus_path_bar_dispose (GObject *object)
297 {
298 NautilusPathBar *self;
299
300 self = NAUTILUS_PATH_BAR (object);
301
302 remove_settings_signal (self, gtk_widget_get_screen (GTK_WIDGET (object)));
303
304 G_OBJECT_CLASS (nautilus_path_bar_parent_class)->dispose (object);
305 }
306
307 static const char *
get_dir_name(ButtonData * button_data)308 get_dir_name (ButtonData *button_data)
309 {
310 switch (button_data->type)
311 {
312 case ROOT_BUTTON:
313 {
314 /* Translators: This is the label used in the pathbar when seeing
315 * the root directory (also known as /) */
316 return _("Computer");
317 }
318
319 case ADMIN_ROOT_BUTTON:
320 {
321 /* Translators: This is the filesystem root directory (also known
322 * as /) when seen as administrator */
323 return _("Administrator Root");
324 }
325
326 case HOME_BUTTON:
327 {
328 return _("Home");
329 }
330
331 case OTHER_LOCATIONS_BUTTON:
332 {
333 return _("Other Locations");
334 }
335
336 case STARRED_BUTTON:
337 {
338 return _("Starred");
339 }
340
341 default:
342 {
343 return button_data->dir_name;
344 }
345 }
346 }
347
348 /* We always want to request the same size for the label, whether
349 * or not the contents are bold
350 */
351 static void
set_label_size_request(ButtonData * button_data)352 set_label_size_request (ButtonData *button_data)
353 {
354 gint width;
355 GtkRequisition nat_req;
356
357 if (button_data->label == NULL)
358 {
359 return;
360 }
361
362 gtk_widget_get_preferred_size (button_data->label, NULL, &nat_req);
363
364 width = MIN (nat_req.width, NAUTILUS_PATH_BAR_BUTTON_MAX_WIDTH);
365
366 gtk_widget_set_size_request (button_data->label, width, nat_req.height);
367 }
368
369 /* Size requisition:
370 *
371 * Ideally, our size is determined by another widget, and we are just filling
372 * available space.
373 */
374 static void
nautilus_path_bar_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)375 nautilus_path_bar_get_preferred_width (GtkWidget *widget,
376 gint *minimum,
377 gint *natural)
378 {
379 ButtonData *button_data;
380 NautilusPathBar *self;
381 GList *list;
382 gint child_height;
383 gint height;
384 gint child_min, child_nat;
385
386 self = NAUTILUS_PATH_BAR (widget);
387
388 *minimum = *natural = 0;
389 height = 0;
390
391 for (list = self->button_list; list; list = list->next)
392 {
393 button_data = BUTTON_DATA (list->data);
394 set_label_size_request (button_data);
395
396 gtk_widget_get_preferred_width (button_data->button, &child_min, &child_nat);
397 gtk_widget_get_preferred_height (button_data->button, &child_height, NULL);
398 height = MAX (height, child_height);
399
400 if (button_data->type == NORMAL_BUTTON)
401 {
402 /* Use 2*Height as button width because of ellipsized label. */
403 child_min = MAX (child_min, child_height * 2);
404 child_nat = MAX (child_min, child_height * 2);
405 }
406
407 *minimum = MAX (*minimum, child_min);
408 *natural = *natural + child_nat;
409 }
410 }
411
412 static void
nautilus_path_bar_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)413 nautilus_path_bar_get_preferred_height (GtkWidget *widget,
414 gint *minimum,
415 gint *natural)
416 {
417 ButtonData *button_data;
418 NautilusPathBar *self;
419 GList *list;
420 gint child_min, child_nat;
421
422 self = NAUTILUS_PATH_BAR (widget);
423
424 *minimum = *natural = 0;
425
426 for (list = self->button_list; list; list = list->next)
427 {
428 button_data = BUTTON_DATA (list->data);
429 set_label_size_request (button_data);
430
431 gtk_widget_get_preferred_height (button_data->button, &child_min, &child_nat);
432
433 *minimum = MAX (*minimum, child_min);
434 *natural = MAX (*natural, child_nat);
435 }
436 }
437
438 static void
nautilus_path_bar_unmap(GtkWidget * widget)439 nautilus_path_bar_unmap (GtkWidget *widget)
440 {
441 NautilusPathBar *self;
442
443 self = NAUTILUS_PATH_BAR (widget);
444
445 gdk_window_hide (self->event_window);
446
447 GTK_WIDGET_CLASS (nautilus_path_bar_parent_class)->unmap (widget);
448 }
449
450 static void
nautilus_path_bar_map(GtkWidget * widget)451 nautilus_path_bar_map (GtkWidget *widget)
452 {
453 NautilusPathBar *self;
454
455 self = NAUTILUS_PATH_BAR (widget);
456
457 gdk_window_show (self->event_window);
458
459 GTK_WIDGET_CLASS (nautilus_path_bar_parent_class)->map (widget);
460 }
461
462 #define BUTTON_BOTTOM_SHADOW 1
463
464 static void
union_with_clip(GtkWidget * widget,gpointer clip)465 union_with_clip (GtkWidget *widget,
466 gpointer clip)
467 {
468 GtkAllocation widget_clip;
469
470 if (!gtk_widget_is_drawable (widget))
471 {
472 return;
473 }
474
475 gtk_widget_get_clip (widget, &widget_clip);
476
477 gdk_rectangle_union (&widget_clip, clip, clip);
478 }
479
480 static void
_set_simple_bottom_clip(GtkWidget * widget,gint pixels)481 _set_simple_bottom_clip (GtkWidget *widget,
482 gint pixels)
483 {
484 GtkAllocation clip;
485
486 gtk_widget_get_allocation (widget, &clip);
487 clip.height += pixels;
488
489 gtk_container_forall (GTK_CONTAINER (widget), union_with_clip, &clip);
490 gtk_widget_set_clip (widget, &clip);
491 }
492
493 /* This is a tad complicated */
494 static void
nautilus_path_bar_size_allocate(GtkWidget * widget,GtkAllocation * allocation)495 nautilus_path_bar_size_allocate (GtkWidget *widget,
496 GtkAllocation *allocation)
497 {
498 NautilusPathBar *self;
499 GtkWidget *child;
500 GtkTextDirection direction;
501 GtkAllocation child_allocation;
502 GList *list, *first_button;
503 gint width;
504 gint largest_width;
505 GtkRequisition child_requisition;
506
507 self = NAUTILUS_PATH_BAR (widget);
508
509 gtk_widget_set_allocation (widget, allocation);
510
511 if (gtk_widget_get_realized (widget))
512 {
513 gdk_window_move_resize (self->event_window,
514 allocation->x, allocation->y,
515 allocation->width, allocation->height);
516 }
517
518 /* No path is set so we don't have to allocate anything. */
519 if (self->button_list == NULL)
520 {
521 _set_simple_bottom_clip (widget, BUTTON_BOTTOM_SHADOW);
522 return;
523 }
524 direction = gtk_widget_get_direction (widget);
525
526 width = 0;
527
528 gtk_widget_get_preferred_size (BUTTON_DATA (self->button_list->data)->button,
529 &child_requisition, NULL);
530 width += child_requisition.width;
531
532 for (list = self->button_list->next; list; list = list->next)
533 {
534 child = BUTTON_DATA (list->data)->button;
535 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
536 width += child_requisition.width;
537 }
538
539 if (width <= allocation->width)
540 {
541 first_button = g_list_last (self->button_list);
542 }
543 else
544 {
545 gboolean reached_end;
546 reached_end = FALSE;
547
548 first_button = self->button_list;
549
550 /* To see how much space we have, and how many buttons we can display.
551 * We start at the first button, count forward until hit the new
552 * button, then count backwards.
553 */
554 /* Count down the path chain towards the end. */
555 gtk_widget_get_preferred_size (BUTTON_DATA (first_button->data)->button,
556 &child_requisition, NULL);
557 width = child_requisition.width;
558 list = first_button->prev;
559 while (list && !reached_end)
560 {
561 child = BUTTON_DATA (list->data)->button;
562 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
563
564 if (width + child_requisition.width > allocation->width)
565 {
566 reached_end = TRUE;
567 }
568 else
569 {
570 width += child_requisition.width;
571 }
572
573 list = list->prev;
574 }
575
576 /* Finally, we walk up, seeing how many of the previous buttons we can add*/
577
578 while (first_button->next && !reached_end)
579 {
580 child = BUTTON_DATA (first_button->next->data)->button;
581 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
582
583 if (width + child_requisition.width > allocation->width)
584 {
585 reached_end = TRUE;
586 }
587 else
588 {
589 width += child_requisition.width;
590 first_button = first_button->next;
591 }
592 }
593 }
594
595 /* Now, we allocate space to the buttons */
596 child_allocation.y = allocation->y;
597 child_allocation.height = allocation->height;
598
599 if (direction == GTK_TEXT_DIR_RTL)
600 {
601 child_allocation.x = allocation->x + allocation->width;
602 }
603 else
604 {
605 child_allocation.x = allocation->x;
606 }
607
608 /* Determine the largest possible allocation size */
609 largest_width = allocation->width;
610 for (list = first_button; list; list = list->prev)
611 {
612 child = BUTTON_DATA (list->data)->button;
613 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
614
615 child_allocation.width = MIN (child_requisition.width, largest_width);
616 if (direction == GTK_TEXT_DIR_RTL)
617 {
618 child_allocation.x -= child_allocation.width;
619 }
620 /* Check to see if we've don't have any more space to allocate buttons */
621
622 gtk_widget_set_child_visible (child, TRUE);
623 gtk_widget_size_allocate (child, &child_allocation);
624
625 if (direction == GTK_TEXT_DIR_LTR)
626 {
627 child_allocation.x += child_allocation.width;
628 }
629 }
630 /* Now we go hide all the widgets that don't fit */
631 while (list)
632 {
633 child = BUTTON_DATA (list->data)->button;
634 gtk_widget_set_child_visible (child, FALSE);
635 list = list->prev;
636 }
637 for (list = first_button->next; list; list = list->next)
638 {
639 child = BUTTON_DATA (list->data)->button;
640 gtk_widget_set_child_visible (child, FALSE);
641 }
642
643 _set_simple_bottom_clip (widget, BUTTON_BOTTOM_SHADOW);
644 }
645
646 static void
nautilus_path_bar_style_updated(GtkWidget * widget)647 nautilus_path_bar_style_updated (GtkWidget *widget)
648 {
649 GTK_WIDGET_CLASS (nautilus_path_bar_parent_class)->style_updated (widget);
650
651 nautilus_path_bar_check_icon_theme (NAUTILUS_PATH_BAR (widget));
652 }
653
654 static void
nautilus_path_bar_screen_changed(GtkWidget * widget,GdkScreen * previous_screen)655 nautilus_path_bar_screen_changed (GtkWidget *widget,
656 GdkScreen *previous_screen)
657 {
658 if (GTK_WIDGET_CLASS (nautilus_path_bar_parent_class)->screen_changed)
659 {
660 GTK_WIDGET_CLASS (nautilus_path_bar_parent_class)->screen_changed (widget, previous_screen);
661 }
662 /* We might nave a new settings, so we remove the old one */
663 if (previous_screen)
664 {
665 remove_settings_signal (NAUTILUS_PATH_BAR (widget), previous_screen);
666 }
667 nautilus_path_bar_check_icon_theme (NAUTILUS_PATH_BAR (widget));
668 }
669
670 static void
nautilus_path_bar_realize(GtkWidget * widget)671 nautilus_path_bar_realize (GtkWidget *widget)
672 {
673 NautilusPathBar *self;
674 GtkAllocation allocation;
675 GdkWindow *window;
676 GdkWindowAttr attributes;
677 gint attributes_mask;
678
679 gtk_widget_set_realized (widget, TRUE);
680
681 self = NAUTILUS_PATH_BAR (widget);
682
683 window = gtk_widget_get_parent_window (widget);
684 gtk_widget_set_window (widget, window);
685 g_object_ref (window);
686
687 gtk_widget_get_allocation (widget, &allocation);
688
689 attributes.window_type = GDK_WINDOW_CHILD;
690 attributes.x = allocation.x;
691 attributes.y = allocation.y;
692 attributes.width = allocation.width;
693 attributes.height = allocation.height;
694 attributes.wclass = GDK_INPUT_ONLY;
695 attributes.event_mask = gtk_widget_get_events (widget);
696 attributes.event_mask |=
697 GDK_BUTTON_PRESS_MASK |
698 GDK_BUTTON_RELEASE_MASK |
699 GDK_POINTER_MOTION_MASK;
700 attributes_mask = GDK_WA_X | GDK_WA_Y;
701
702 self->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
703 &attributes, attributes_mask);
704 gdk_window_set_user_data (self->event_window, widget);
705 }
706
707 static void
nautilus_path_bar_unrealize(GtkWidget * widget)708 nautilus_path_bar_unrealize (GtkWidget *widget)
709 {
710 NautilusPathBar *self;
711
712 self = NAUTILUS_PATH_BAR (widget);
713
714 gdk_window_set_user_data (self->event_window, NULL);
715 gdk_window_destroy (self->event_window);
716 self->event_window = NULL;
717
718 GTK_WIDGET_CLASS (nautilus_path_bar_parent_class)->unrealize (widget);
719 }
720
721 static void
nautilus_path_bar_add(GtkContainer * container,GtkWidget * widget)722 nautilus_path_bar_add (GtkContainer *container,
723 GtkWidget *widget)
724 {
725 gtk_widget_set_parent (widget, GTK_WIDGET (container));
726 }
727
728 static void
nautilus_path_bar_remove_1(GtkContainer * container,GtkWidget * widget)729 nautilus_path_bar_remove_1 (GtkContainer *container,
730 GtkWidget *widget)
731 {
732 gboolean was_visible = gtk_widget_get_visible (widget);
733 gtk_widget_unparent (widget);
734 if (was_visible)
735 {
736 gtk_widget_queue_resize (GTK_WIDGET (container));
737 }
738 }
739
740 static void
button_data_free(ButtonData * button_data)741 button_data_free (ButtonData *button_data)
742 {
743 g_object_unref (button_data->path);
744 g_free (button_data->dir_name);
745 if (button_data->file != NULL)
746 {
747 g_signal_handler_disconnect (button_data->file,
748 button_data->file_changed_signal_id);
749 nautilus_file_monitor_remove (button_data->file, button_data);
750 nautilus_file_unref (button_data->file);
751 }
752
753 g_clear_object (&button_data->multi_press_gesture);
754
755 g_free (button_data);
756 }
757
758 static void
nautilus_path_bar_remove(GtkContainer * container,GtkWidget * widget)759 nautilus_path_bar_remove (GtkContainer *container,
760 GtkWidget *widget)
761 {
762 NautilusPathBar *self;
763 GList *children;
764
765 self = NAUTILUS_PATH_BAR (container);
766
767 children = self->button_list;
768 while (children != NULL)
769 {
770 if (widget == BUTTON_DATA (children->data)->button)
771 {
772 nautilus_path_bar_remove_1 (container, widget);
773 self->button_list = g_list_remove_link (self->button_list, children);
774 button_data_free (children->data);
775 g_list_free_1 (children);
776 return;
777 }
778 children = children->next;
779 }
780 }
781
782 static void
nautilus_path_bar_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)783 nautilus_path_bar_forall (GtkContainer *container,
784 gboolean include_internals,
785 GtkCallback callback,
786 gpointer callback_data)
787 {
788 NautilusPathBar *self;
789 GList *children;
790
791 g_return_if_fail (callback != NULL);
792 self = NAUTILUS_PATH_BAR (container);
793
794 children = self->button_list;
795 while (children != NULL)
796 {
797 GtkWidget *child;
798 child = BUTTON_DATA (children->data)->button;
799 children = children->next;
800 (*callback)(child, callback_data);
801 }
802 }
803
804 static GtkWidgetPath *
nautilus_path_bar_get_path_for_child(GtkContainer * container,GtkWidget * child)805 nautilus_path_bar_get_path_for_child (GtkContainer *container,
806 GtkWidget *child)
807 {
808 NautilusPathBar *self;
809 GtkWidgetPath *path;
810
811 self = NAUTILUS_PATH_BAR (container);
812 path = gtk_widget_path_copy (gtk_widget_get_path (GTK_WIDGET (self)));
813
814 if (gtk_widget_get_visible (child) &&
815 gtk_widget_get_child_visible (child))
816 {
817 GtkWidgetPath *sibling_path;
818 GList *visible_children;
819 GList *l;
820 int pos;
821
822 /* 1. Build the list of visible children, in visually left-to-right order
823 * (i.e. independently of the widget's direction). Note that our
824 * button_list is stored in innermost-to-outermost path order!
825 */
826
827 visible_children = NULL;
828
829 for (l = self->button_list; l; l = l->next)
830 {
831 ButtonData *data = l->data;
832
833 if (gtk_widget_get_visible (data->button) &&
834 gtk_widget_get_child_visible (data->button))
835 {
836 visible_children = g_list_prepend (visible_children, data->button);
837 }
838 }
839
840 if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL)
841 {
842 visible_children = g_list_reverse (visible_children);
843 }
844
845 /* 2. Find the index of the child within that list */
846
847 pos = 0;
848
849 for (l = visible_children; l; l = l->next)
850 {
851 GtkWidget *button = l->data;
852
853 if (button == child)
854 {
855 break;
856 }
857
858 pos++;
859 }
860
861 /* 3. Build the path */
862
863 sibling_path = gtk_widget_path_new ();
864
865 for (l = visible_children; l; l = l->next)
866 {
867 gtk_widget_path_append_for_widget (sibling_path, l->data);
868 }
869
870 gtk_widget_path_append_with_siblings (path, sibling_path, pos);
871
872 g_list_free (visible_children);
873 gtk_widget_path_unref (sibling_path);
874 }
875 else
876 {
877 gtk_widget_path_append_for_widget (path, child);
878 }
879
880 return path;
881 }
882
883 static void
nautilus_path_bar_class_init(NautilusPathBarClass * path_bar_class)884 nautilus_path_bar_class_init (NautilusPathBarClass *path_bar_class)
885 {
886 GObjectClass *gobject_class;
887 GtkWidgetClass *widget_class;
888 GtkContainerClass *container_class;
889
890 gobject_class = (GObjectClass *) path_bar_class;
891 widget_class = (GtkWidgetClass *) path_bar_class;
892 container_class = (GtkContainerClass *) path_bar_class;
893
894 gobject_class->finalize = nautilus_path_bar_finalize;
895 gobject_class->dispose = nautilus_path_bar_dispose;
896
897 widget_class->get_preferred_height = nautilus_path_bar_get_preferred_height;
898 widget_class->get_preferred_width = nautilus_path_bar_get_preferred_width;
899 widget_class->realize = nautilus_path_bar_realize;
900 widget_class->unrealize = nautilus_path_bar_unrealize;
901 widget_class->unmap = nautilus_path_bar_unmap;
902 widget_class->map = nautilus_path_bar_map;
903 widget_class->size_allocate = nautilus_path_bar_size_allocate;
904 widget_class->style_updated = nautilus_path_bar_style_updated;
905 widget_class->screen_changed = nautilus_path_bar_screen_changed;
906
907 container_class->add = nautilus_path_bar_add;
908 container_class->forall = nautilus_path_bar_forall;
909 container_class->remove = nautilus_path_bar_remove;
910 container_class->get_path_for_child = nautilus_path_bar_get_path_for_child;
911
912 path_bar_signals [OPEN_LOCATION] =
913 g_signal_new ("open-location",
914 G_OBJECT_CLASS_TYPE (path_bar_class),
915 G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
916 0,
917 NULL, NULL, NULL,
918 G_TYPE_NONE, 2,
919 G_TYPE_FILE,
920 GTK_TYPE_PLACES_OPEN_FLAGS);
921 path_bar_signals [PATH_CLICKED] =
922 g_signal_new ("path-clicked",
923 G_OBJECT_CLASS_TYPE (path_bar_class),
924 G_SIGNAL_RUN_FIRST,
925 0,
926 NULL, NULL,
927 g_cclosure_marshal_VOID__OBJECT,
928 G_TYPE_NONE, 1,
929 G_TYPE_FILE);
930
931 gtk_container_class_handle_border_width (container_class);
932 }
933
934 void
nautilus_path_bar_set_extensions_background_menu(NautilusPathBar * self,GMenuModel * menu)935 nautilus_path_bar_set_extensions_background_menu (NautilusPathBar *self,
936 GMenuModel *menu)
937 {
938 g_return_if_fail (NAUTILUS_IS_PATH_BAR (self));
939
940 nautilus_gmenu_set_from_model (self->extensions_section, menu);
941 }
942
943 void
nautilus_path_bar_set_templates_menu(NautilusPathBar * self,GMenuModel * menu)944 nautilus_path_bar_set_templates_menu (NautilusPathBar *self,
945 GMenuModel *menu)
946 {
947 g_return_if_fail (NAUTILUS_IS_PATH_BAR (self));
948
949 nautilus_gmenu_set_from_model (self->templates_submenu, menu);
950 }
951
952 /* Changes the icons wherever it is needed */
953 static void
reload_icons(NautilusPathBar * self)954 reload_icons (NautilusPathBar *self)
955 {
956 GList *list;
957
958 for (list = self->button_list; list; list = list->next)
959 {
960 ButtonData *button_data;
961
962 button_data = BUTTON_DATA (list->data);
963 if (button_data->type != NORMAL_BUTTON || button_data->is_root)
964 {
965 nautilus_path_bar_update_button_appearance (button_data,
966 list->next == NULL);
967 }
968 }
969 }
970
971 /* Callback used when a GtkSettings value changes */
972 static void
settings_notify_cb(GObject * object,GParamSpec * pspec,NautilusPathBar * self)973 settings_notify_cb (GObject *object,
974 GParamSpec *pspec,
975 NautilusPathBar *self)
976 {
977 const char *name;
978
979 name = g_param_spec_get_name (pspec);
980
981 if (!strcmp (name, "gtk-icon-theme-name") || !strcmp (name, "gtk-icon-sizes"))
982 {
983 reload_icons (self);
984 }
985 }
986
987 static void
nautilus_path_bar_check_icon_theme(NautilusPathBar * self)988 nautilus_path_bar_check_icon_theme (NautilusPathBar *self)
989 {
990 GtkSettings *settings;
991
992 if (self->settings_signal_id)
993 {
994 return;
995 }
996
997 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (self)));
998 self->settings_signal_id = g_signal_connect (settings, "notify", G_CALLBACK (settings_notify_cb), self);
999
1000 reload_icons (self);
1001 }
1002
1003 /* Public functions and their helpers */
1004 static void
nautilus_path_bar_clear_buttons(NautilusPathBar * self)1005 nautilus_path_bar_clear_buttons (NautilusPathBar *self)
1006 {
1007 while (self->button_list != NULL)
1008 {
1009 ButtonData *button_data;
1010
1011 button_data = BUTTON_DATA (self->button_list->data);
1012
1013 gtk_container_remove (GTK_CONTAINER (self), button_data->button);
1014 }
1015 }
1016
1017 static void
button_clicked_cb(GtkButton * button,gpointer data)1018 button_clicked_cb (GtkButton *button,
1019 gpointer data)
1020 {
1021 ButtonData *button_data;
1022 NautilusPathBar *self;
1023 GdkModifierType state;
1024
1025 button_data = BUTTON_DATA (data);
1026 if (button_data->ignore_changes)
1027 {
1028 return;
1029 }
1030
1031 self = button_data->path_bar;
1032
1033 gtk_get_current_event_state (&state);
1034
1035 if ((state & GDK_CONTROL_MASK) != 0)
1036 {
1037 g_signal_emit (button_data->path_bar, path_bar_signals[OPEN_LOCATION], 0,
1038 button_data->path,
1039 GTK_PLACES_OPEN_NEW_WINDOW);
1040 }
1041 else
1042 {
1043 if (g_file_equal (button_data->path, self->current_path))
1044 {
1045 gtk_popover_popup (self->current_view_menu_popover);
1046 }
1047 else
1048 {
1049 g_signal_emit (self, path_bar_signals[OPEN_LOCATION], 0,
1050 button_data->path,
1051 0);
1052 }
1053 }
1054 }
1055
1056 static void
real_pop_up_pathbar_context_menu(NautilusPathBar * self)1057 real_pop_up_pathbar_context_menu (NautilusPathBar *self)
1058 {
1059 gtk_popover_popup (self->button_menu_popover);
1060 }
1061
1062 static void
pathbar_popup_file_attributes_ready(NautilusFile * file,gpointer data)1063 pathbar_popup_file_attributes_ready (NautilusFile *file,
1064 gpointer data)
1065 {
1066 NautilusPathBar *self;
1067
1068 g_return_if_fail (NAUTILUS_IS_PATH_BAR (data));
1069
1070 self = NAUTILUS_PATH_BAR (data);
1071
1072 g_return_if_fail (file == self->context_menu_file);
1073
1074 real_pop_up_pathbar_context_menu (self);
1075 }
1076
1077 static void
unschedule_pop_up_context_menu(NautilusPathBar * self)1078 unschedule_pop_up_context_menu (NautilusPathBar *self)
1079 {
1080 if (self->context_menu_file != NULL)
1081 {
1082 g_return_if_fail (NAUTILUS_IS_FILE (self->context_menu_file));
1083 nautilus_file_cancel_call_when_ready (self->context_menu_file,
1084 pathbar_popup_file_attributes_ready,
1085 self);
1086 g_clear_pointer (&self->context_menu_file, nautilus_file_unref);
1087 }
1088 }
1089
1090 static void
schedule_pop_up_context_menu(NautilusPathBar * self,NautilusFile * file)1091 schedule_pop_up_context_menu (NautilusPathBar *self,
1092 NautilusFile *file)
1093 {
1094 g_return_if_fail (NAUTILUS_IS_FILE (file));
1095
1096 if (file == self->context_menu_file)
1097 {
1098 if (nautilus_file_check_if_ready (file,
1099 NAUTILUS_FILE_ATTRIBUTE_INFO |
1100 NAUTILUS_FILE_ATTRIBUTE_MOUNT |
1101 NAUTILUS_FILE_ATTRIBUTE_FILESYSTEM_INFO))
1102 {
1103 real_pop_up_pathbar_context_menu (self);
1104 }
1105 }
1106 else
1107 {
1108 unschedule_pop_up_context_menu (self);
1109
1110 self->context_menu_file = nautilus_file_ref (file);
1111 nautilus_file_call_when_ready (self->context_menu_file,
1112 NAUTILUS_FILE_ATTRIBUTE_INFO |
1113 NAUTILUS_FILE_ATTRIBUTE_MOUNT |
1114 NAUTILUS_FILE_ATTRIBUTE_FILESYSTEM_INFO,
1115 pathbar_popup_file_attributes_ready,
1116 self);
1117 }
1118 }
1119
1120 static void
pop_up_pathbar_context_menu(NautilusPathBar * self,NautilusFile * file)1121 pop_up_pathbar_context_menu (NautilusPathBar *self,
1122 NautilusFile *file)
1123 {
1124 if (file != NULL)
1125 {
1126 schedule_pop_up_context_menu (self, file);
1127 }
1128 }
1129
1130
1131 static void
on_multi_press_gesture_pressed(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,gpointer user_data)1132 on_multi_press_gesture_pressed (GtkGestureMultiPress *gesture,
1133 gint n_press,
1134 gdouble x,
1135 gdouble y,
1136 gpointer user_data)
1137 {
1138 ButtonData *button_data;
1139 NautilusPathBar *self;
1140 guint current_button;
1141
1142 if (n_press != 1)
1143 {
1144 return;
1145 }
1146
1147 button_data = BUTTON_DATA (user_data);
1148 self = button_data->path_bar;
1149 current_button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
1150
1151 switch (current_button)
1152 {
1153 case GDK_BUTTON_MIDDLE:
1154 {
1155 GdkModifierType state;
1156
1157 gtk_get_current_event_state (&state);
1158 state &= gtk_accelerator_get_default_mod_mask ();
1159 if (state == 0)
1160 {
1161 g_signal_emit (self, path_bar_signals[OPEN_LOCATION], 0,
1162 button_data->path,
1163 GTK_PLACES_OPEN_NEW_TAB);
1164 }
1165 }
1166 break;
1167
1168 case GDK_BUTTON_SECONDARY:
1169 {
1170 if (g_file_equal (button_data->path, self->current_path))
1171 {
1172 gtk_popover_popup (self->current_view_menu_popover);
1173 }
1174 else
1175 {
1176 gtk_popover_set_relative_to (self->button_menu_popover,
1177 button_data->button);
1178 pop_up_pathbar_context_menu (self, button_data->file);
1179 }
1180 }
1181 break;
1182
1183 default:
1184 {
1185 /* Ignore other buttons in this gesture. GtkButton will claim the
1186 * primary button presses and emit the "clicked" signal.
1187 */
1188 return;
1189 }
1190 break;
1191 }
1192
1193 /* Both middle- and secondary-clicking the title bar can have interesting
1194 * effects (minimizing the window, popping up a window manager menu, etc.),
1195 * and this avoids all that.
1196 */
1197 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
1198 }
1199
1200 static GIcon *
get_gicon_for_mount(ButtonData * button_data)1201 get_gicon_for_mount (ButtonData *button_data)
1202 {
1203 GIcon *icon;
1204 GMount *mount;
1205
1206 icon = NULL;
1207 mount = nautilus_get_mounted_mount_for_root (button_data->path);
1208
1209 if (mount != NULL)
1210 {
1211 icon = g_mount_get_symbolic_icon (mount);
1212 g_object_unref (mount);
1213 }
1214
1215 return icon;
1216 }
1217
1218 static GIcon *
get_gicon(ButtonData * button_data)1219 get_gicon (ButtonData *button_data)
1220 {
1221 switch (button_data->type)
1222 {
1223 case ROOT_BUTTON:
1224 case ADMIN_ROOT_BUTTON:
1225 {
1226 return g_themed_icon_new (NAUTILUS_ICON_FILESYSTEM);
1227 }
1228
1229 case HOME_BUTTON:
1230 {
1231 return g_themed_icon_new (NAUTILUS_ICON_HOME);
1232 }
1233
1234 case MOUNT_BUTTON:
1235 {
1236 return get_gicon_for_mount (button_data);
1237 }
1238
1239 case STARRED_BUTTON:
1240 {
1241 return g_themed_icon_new ("starred-symbolic");
1242 }
1243
1244 case RECENT_BUTTON:
1245 {
1246 return g_themed_icon_new ("document-open-recent-symbolic");
1247 }
1248
1249 case OTHER_LOCATIONS_BUTTON:
1250 {
1251 return g_themed_icon_new ("list-add-symbolic");
1252 }
1253
1254 case TRASH_BUTTON:
1255 {
1256 return nautilus_trash_monitor_get_symbolic_icon ();
1257 }
1258
1259 default:
1260 {
1261 return NULL;
1262 }
1263 }
1264
1265 return NULL;
1266 }
1267
1268 static void
nautilus_path_bar_update_button_appearance(ButtonData * button_data,gboolean current_dir)1269 nautilus_path_bar_update_button_appearance (ButtonData *button_data,
1270 gboolean current_dir)
1271 {
1272 const gchar *dir_name = get_dir_name (button_data);
1273 GIcon *icon;
1274
1275 if (button_data->label != NULL)
1276 {
1277 gtk_label_set_text (GTK_LABEL (button_data->label), dir_name);
1278 }
1279
1280 icon = get_gicon (button_data);
1281 if (icon != NULL)
1282 {
1283 gtk_image_set_from_gicon (GTK_IMAGE (button_data->image), icon, GTK_ICON_SIZE_MENU);
1284 gtk_style_context_add_class (gtk_widget_get_style_context (button_data->button),
1285 "image-button");
1286 gtk_widget_show (GTK_WIDGET (button_data->image));
1287 g_object_unref (icon);
1288 }
1289 else
1290 {
1291 gtk_widget_hide (GTK_WIDGET (button_data->image));
1292 if (!current_dir)
1293 {
1294 gtk_style_context_remove_class (gtk_widget_get_style_context (button_data->button),
1295 "image-button");
1296 }
1297 }
1298 }
1299
1300 static void
nautilus_path_bar_update_button_state(ButtonData * button_data,gboolean current_dir)1301 nautilus_path_bar_update_button_state (ButtonData *button_data,
1302 gboolean current_dir)
1303 {
1304 if (button_data->label != NULL)
1305 {
1306 gtk_label_set_label (GTK_LABEL (button_data->label), NULL);
1307 gtk_label_set_use_markup (GTK_LABEL (button_data->label), current_dir);
1308 }
1309
1310 nautilus_path_bar_update_button_appearance (button_data, current_dir);
1311 }
1312
1313 static void
setup_button_type(ButtonData * button_data,NautilusPathBar * self,GFile * location)1314 setup_button_type (ButtonData *button_data,
1315 NautilusPathBar *self,
1316 GFile *location)
1317 {
1318 g_autoptr (GMount) mount = NULL;
1319 g_autofree gchar *uri = NULL;
1320
1321 if (nautilus_is_root_directory (location))
1322 {
1323 button_data->type = ROOT_BUTTON;
1324 }
1325 else if (nautilus_is_home_directory (location))
1326 {
1327 button_data->type = HOME_BUTTON;
1328 button_data->is_root = TRUE;
1329 }
1330 else if (nautilus_is_recent_directory (location))
1331 {
1332 button_data->type = RECENT_BUTTON;
1333 button_data->is_root = TRUE;
1334 }
1335 else if (nautilus_is_starred_directory (location))
1336 {
1337 button_data->type = STARRED_BUTTON;
1338 button_data->is_root = TRUE;
1339 }
1340 else if ((mount = nautilus_get_mounted_mount_for_root (location)) != NULL)
1341 {
1342 button_data->dir_name = g_mount_get_name (mount);
1343 button_data->type = MOUNT_BUTTON;
1344 button_data->is_root = TRUE;
1345 }
1346 else if (nautilus_is_other_locations_directory (location))
1347 {
1348 button_data->type = OTHER_LOCATIONS_BUTTON;
1349 button_data->is_root = TRUE;
1350 }
1351 else if (strcmp ((uri = g_file_get_uri (location)), "admin:///") == 0)
1352 {
1353 button_data->type = ADMIN_ROOT_BUTTON;
1354 button_data->is_root = TRUE;
1355 }
1356 else if (strcmp (uri, "trash:///") == 0)
1357 {
1358 button_data->type = TRASH_BUTTON;
1359 button_data->is_root = TRUE;
1360 }
1361 else
1362 {
1363 button_data->type = NORMAL_BUTTON;
1364 }
1365 }
1366
1367 static void
button_data_file_changed(NautilusFile * file,ButtonData * button_data)1368 button_data_file_changed (NautilusFile *file,
1369 ButtonData *button_data)
1370 {
1371 GtkWidget *ancestor;
1372 GFile *location;
1373 GFile *current_location;
1374 GFile *parent;
1375 GFile *button_parent;
1376 ButtonData *current_button_data;
1377 char *display_name;
1378 NautilusPathBar *self;
1379 gboolean renamed;
1380 gboolean child;
1381 gboolean current_dir;
1382
1383 ancestor = gtk_widget_get_ancestor (button_data->button, NAUTILUS_TYPE_PATH_BAR);
1384 if (ancestor == NULL)
1385 {
1386 return;
1387 }
1388 self = NAUTILUS_PATH_BAR (ancestor);
1389
1390 g_return_if_fail (self->current_path != NULL);
1391 g_return_if_fail (self->current_button_data != NULL);
1392
1393 current_button_data = self->current_button_data;
1394
1395 location = nautilus_file_get_location (file);
1396 if (!g_file_equal (button_data->path, location))
1397 {
1398 parent = g_file_get_parent (location);
1399 button_parent = g_file_get_parent (button_data->path);
1400
1401 renamed = (parent != NULL && button_parent != NULL) &&
1402 g_file_equal (parent, button_parent);
1403
1404 if (parent != NULL)
1405 {
1406 g_object_unref (parent);
1407 }
1408 if (button_parent != NULL)
1409 {
1410 g_object_unref (button_parent);
1411 }
1412
1413 if (renamed)
1414 {
1415 button_data->path = g_object_ref (location);
1416 }
1417 else
1418 {
1419 /* the file has been moved.
1420 * If it was below the currently displayed location, remove it.
1421 * If it was not below the currently displayed location, update the path bar
1422 */
1423 child = g_file_has_prefix (button_data->path,
1424 self->current_path);
1425
1426 if (child)
1427 {
1428 /* moved file inside current path hierarchy */
1429 g_object_unref (location);
1430 location = g_file_get_parent (button_data->path);
1431 current_location = g_object_ref (self->current_path);
1432 }
1433 else
1434 {
1435 /* moved current path, or file outside current path hierarchy.
1436 * Update path bar to new locations.
1437 */
1438 current_location = nautilus_file_get_location (current_button_data->file);
1439 }
1440
1441 nautilus_path_bar_update_path (self, location);
1442 nautilus_path_bar_set_path (self, current_location);
1443 g_object_unref (location);
1444 g_object_unref (current_location);
1445 return;
1446 }
1447 }
1448 else if (nautilus_file_is_gone (file))
1449 {
1450 gint idx, position;
1451
1452 /* if the current or a parent location are gone, clear all the buttons,
1453 * the view will set the new path.
1454 */
1455 current_location = nautilus_file_get_location (current_button_data->file);
1456
1457 if (g_file_has_prefix (current_location, location) ||
1458 g_file_equal (current_location, location))
1459 {
1460 nautilus_path_bar_clear_buttons (self);
1461 }
1462 else if (g_file_has_prefix (location, current_location))
1463 {
1464 /* remove this and the following buttons */
1465 position = g_list_position (self->button_list,
1466 g_list_find (self->button_list, button_data));
1467
1468 if (position != -1)
1469 {
1470 for (idx = 0; idx <= position; idx++)
1471 {
1472 ButtonData *data;
1473
1474 data = BUTTON_DATA (self->button_list->data);
1475
1476 gtk_container_remove (GTK_CONTAINER (self), data->button);
1477 }
1478 }
1479 }
1480
1481 g_object_unref (current_location);
1482 g_object_unref (location);
1483 return;
1484 }
1485 g_object_unref (location);
1486
1487 /* MOUNTs use the GMount as the name, so don't update for those */
1488 if (button_data->type != MOUNT_BUTTON)
1489 {
1490 display_name = nautilus_file_get_display_name (file);
1491 if (g_strcmp0 (display_name, button_data->dir_name) != 0)
1492 {
1493 g_free (button_data->dir_name);
1494 button_data->dir_name = g_strdup (display_name);
1495 }
1496
1497 g_free (display_name);
1498 }
1499 current_dir = g_file_equal (self->current_path, button_data->path);
1500 nautilus_path_bar_update_button_appearance (button_data, current_dir);
1501 }
1502
1503 static ButtonData *
make_button_data(NautilusPathBar * self,NautilusFile * file,gboolean current_dir)1504 make_button_data (NautilusPathBar *self,
1505 NautilusFile *file,
1506 gboolean current_dir)
1507 {
1508 GFile *path;
1509 ButtonData *button_data;
1510 GtkStyleContext *style_context;
1511
1512 path = nautilus_file_get_location (file);
1513
1514 /* Is it a special button? */
1515 button_data = g_new0 (ButtonData, 1);
1516
1517 setup_button_type (button_data, self, path);
1518 button_data->button = gtk_button_new ();
1519 gtk_widget_set_focus_on_click (button_data->button, FALSE);
1520
1521 style_context = gtk_widget_get_style_context (button_data->button);
1522 gtk_style_context_add_class (style_context, "text-button");
1523 /* TODO update button type when xdg directories change */
1524
1525 button_data->image = gtk_image_new ();
1526
1527 switch (button_data->type)
1528 {
1529 case ROOT_BUTTON:
1530 case ADMIN_ROOT_BUTTON:
1531 case HOME_BUTTON:
1532 case MOUNT_BUTTON:
1533 case TRASH_BUTTON:
1534 case RECENT_BUTTON:
1535 case STARRED_BUTTON:
1536 case OTHER_LOCATIONS_BUTTON:
1537 {
1538 button_data->label = gtk_label_new (NULL);
1539 button_data->disclosure_arrow = gtk_image_new_from_icon_name ("pan-down-symbolic",
1540 GTK_ICON_SIZE_MENU);
1541 gtk_widget_set_margin_start (button_data->disclosure_arrow, 0);
1542 button_data->container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1543 gtk_container_add (GTK_CONTAINER (button_data->button), button_data->container);
1544
1545 gtk_box_pack_start (GTK_BOX (button_data->container), button_data->image, FALSE, FALSE, 0);
1546 gtk_box_pack_start (GTK_BOX (button_data->container), button_data->label, FALSE, FALSE, 0);
1547 gtk_box_pack_start (GTK_BOX (button_data->container), button_data->disclosure_arrow, FALSE, FALSE, 0);
1548 }
1549 break;
1550
1551 case NORMAL_BUTTON:
1552 /* Fall through */
1553 default:
1554 {
1555 button_data->label = gtk_label_new (NULL);
1556 button_data->disclosure_arrow = gtk_image_new_from_icon_name ("pan-down-symbolic",
1557 GTK_ICON_SIZE_MENU);
1558 gtk_widget_set_margin_start (button_data->disclosure_arrow, 0);
1559 button_data->container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1560 gtk_container_add (GTK_CONTAINER (button_data->button), button_data->container);
1561
1562 gtk_box_pack_start (GTK_BOX (button_data->container), button_data->label, FALSE, FALSE, 0);
1563 gtk_box_pack_start (GTK_BOX (button_data->container), button_data->disclosure_arrow, FALSE, FALSE, 0);
1564 }
1565 break;
1566 }
1567
1568 gtk_widget_set_no_show_all (button_data->disclosure_arrow, TRUE);
1569 if (current_dir)
1570 {
1571 gtk_widget_show (button_data->disclosure_arrow);
1572 gtk_popover_set_relative_to (self->current_view_menu_popover, button_data->button);
1573 gtk_style_context_add_class (gtk_widget_get_style_context (button_data->button),
1574 "image-button");
1575 }
1576
1577 if (button_data->label != NULL)
1578 {
1579 gtk_label_set_ellipsize (GTK_LABEL (button_data->label), PANGO_ELLIPSIZE_MIDDLE);
1580 gtk_label_set_single_line_mode (GTK_LABEL (button_data->label), TRUE);
1581 }
1582
1583 if (button_data->path == NULL)
1584 {
1585 button_data->path = g_object_ref (path);
1586 }
1587 if (button_data->dir_name == NULL)
1588 {
1589 button_data->dir_name = nautilus_file_get_display_name (file);
1590 }
1591 if (button_data->file == NULL)
1592 {
1593 button_data->file = nautilus_file_ref (file);
1594 nautilus_file_monitor_add (button_data->file, button_data,
1595 NAUTILUS_FILE_ATTRIBUTES_FOR_ICON);
1596 button_data->file_changed_signal_id =
1597 g_signal_connect (button_data->file, "changed",
1598 G_CALLBACK (button_data_file_changed),
1599 button_data);
1600 }
1601
1602 gtk_widget_show_all (button_data->button);
1603
1604 nautilus_path_bar_update_button_state (button_data, current_dir);
1605
1606 button_data->path_bar = self;
1607
1608 g_signal_connect (button_data->button, "clicked", G_CALLBACK (button_clicked_cb), button_data);
1609
1610 /* A gesture is needed here, because GtkButton doesn’t react to middle- or
1611 * secondary-clicking.
1612 */
1613 button_data->multi_press_gesture = gtk_gesture_multi_press_new (button_data->button);
1614
1615 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (button_data->multi_press_gesture), 0);
1616
1617 g_signal_connect (button_data->multi_press_gesture, "pressed",
1618 G_CALLBACK (on_multi_press_gesture_pressed), button_data);
1619
1620 nautilus_drag_slot_proxy_init (button_data->button, button_data->file, NULL);
1621
1622 g_object_unref (path);
1623
1624 return button_data;
1625 }
1626
1627 static void
nautilus_path_bar_update_path(NautilusPathBar * self,GFile * file_path)1628 nautilus_path_bar_update_path (NautilusPathBar *self,
1629 GFile *file_path)
1630 {
1631 NautilusFile *file;
1632 gboolean first_directory;
1633 GList *new_buttons, *l;
1634 ButtonData *button_data;
1635
1636 g_return_if_fail (NAUTILUS_IS_PATH_BAR (self));
1637 g_return_if_fail (file_path != NULL);
1638
1639 first_directory = TRUE;
1640 new_buttons = NULL;
1641
1642 file = nautilus_file_get (file_path);
1643
1644 while (file != NULL)
1645 {
1646 NautilusFile *parent_file;
1647
1648 parent_file = nautilus_file_get_parent (file);
1649 button_data = make_button_data (self, file, first_directory);
1650 nautilus_file_unref (file);
1651
1652 if (first_directory)
1653 {
1654 first_directory = FALSE;
1655 }
1656
1657 new_buttons = g_list_prepend (new_buttons, button_data);
1658
1659 if (parent_file != NULL &&
1660 button_data->is_root)
1661 {
1662 nautilus_file_unref (parent_file);
1663 break;
1664 }
1665
1666 file = parent_file;
1667 }
1668
1669 nautilus_path_bar_clear_buttons (self);
1670 self->button_list = g_list_reverse (new_buttons);
1671
1672 for (l = self->button_list; l; l = l->next)
1673 {
1674 GtkWidget *button;
1675 button = BUTTON_DATA (l->data)->button;
1676 gtk_container_add (GTK_CONTAINER (self), button);
1677 }
1678 }
1679
1680 void
nautilus_path_bar_set_path(NautilusPathBar * self,GFile * file_path)1681 nautilus_path_bar_set_path (NautilusPathBar *self,
1682 GFile *file_path)
1683 {
1684 ButtonData *button_data;
1685
1686 g_return_if_fail (NAUTILUS_IS_PATH_BAR (self));
1687 g_return_if_fail (file_path != NULL);
1688
1689 /* Check whether the new path is already present in the pathbar as buttons.
1690 * This could be a parent directory or a previous selected subdirectory. */
1691 nautilus_path_bar_update_path (self, file_path);
1692 button_data = g_list_nth_data (self->button_list, 0);
1693
1694 if (self->current_path != NULL)
1695 {
1696 g_object_unref (self->current_path);
1697 }
1698
1699 self->current_path = g_object_ref (file_path);
1700 self->current_button_data = button_data;
1701 }
1702