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