summaryrefslogtreecommitdiffstats
path: root/src/nautilus-toolbar.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 15:59:36 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 15:59:36 +0000
commitec52555862913a23417735f9f7f5402f5230da13 (patch)
tree5e43a30d289a3daa69dddfbb060216ff6332f197 /src/nautilus-toolbar.c
parentInitial commit. (diff)
downloadnautilus-upstream/3.38.2.tar.xz
nautilus-upstream/3.38.2.zip
Adding upstream version 3.38.2.upstream/3.38.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/nautilus-toolbar.c')
-rw-r--r--src/nautilus-toolbar.c1512
1 files changed, 1512 insertions, 0 deletions
diff --git a/src/nautilus-toolbar.c b/src/nautilus-toolbar.c
new file mode 100644
index 0000000..eb86054
--- /dev/null
+++ b/src/nautilus-toolbar.c
@@ -0,0 +1,1512 @@
+/*
+ * Nautilus
+ *
+ * Copyright (C) 2011, Red Hat, Inc.
+ *
+ * Nautilus is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Nautilus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Cosimo Cecchi <cosimoc@redhat.com>
+ *
+ */
+
+#include "nautilus-toolbar.h"
+
+#include <glib/gi18n.h>
+#include <math.h>
+
+#include "animation/ide-box-theatric.h"
+#include "animation/egg-animation.h"
+
+#include "nautilus-application.h"
+#include "nautilus-bookmark.h"
+#include "nautilus-file-operations.h"
+#include "nautilus-file-undo-manager.h"
+#include "nautilus-global-preferences.h"
+#include "nautilus-location-entry.h"
+#include "nautilus-pathbar.h"
+#include "nautilus-progress-info-manager.h"
+#include "nautilus-progress-info-widget.h"
+#include "nautilus-toolbar-menu-sections.h"
+#include "nautilus-ui-utilities.h"
+#include "nautilus-window.h"
+#include "nautilus-container-max-width.h"
+
+#define OPERATION_MINIMUM_TIME 2 /*s */
+#define NEEDS_ATTENTION_ANIMATION_TIMEOUT 2000 /*ms */
+#define REMOVE_FINISHED_OPERATIONS_TIEMOUT 3 /*s */
+
+#define ANIMATION_X_GROW 30
+#define ANIMATION_Y_GROW 30
+
+/* Just design, context at https://gitlab.gnome.org/GNOME/nautilus/issues/548#note_274131 */
+#define SWITCHER_MAX_WIDTH 840
+
+typedef enum
+{
+ NAUTILUS_NAVIGATION_DIRECTION_NONE,
+ NAUTILUS_NAVIGATION_DIRECTION_BACK,
+ NAUTILUS_NAVIGATION_DIRECTION_FORWARD
+} NautilusNavigationDirection;
+
+struct _NautilusToolbar
+{
+ GtkHeaderBar parent_instance;
+
+ NautilusWindow *window;
+
+ GtkWidget *path_bar_container;
+ GtkWidget *location_entry_container;
+ GtkWidget *search_container;
+ GtkWidget *toolbar_switcher;
+ GtkWidget *toolbar_switcher_container;
+ NautilusContainerMaxWidth *toolbar_switcher_container_max_width;
+ GtkWidget *path_bar;
+ GtkWidget *location_entry;
+
+ gboolean show_location_entry;
+ gboolean location_entry_should_auto_hide;
+
+ guint start_operations_timeout_id;
+ guint remove_finished_operations_timeout_id;
+ guint operations_button_attention_timeout_id;
+
+ GtkWidget *operations_button;
+ GtkWidget *view_button;
+ GtkWidget *view_menu_zoom_section;
+ GtkWidget *view_menu_undo_redo_section;
+ GtkWidget *view_menu_extended_section;
+ GtkWidget *undo_button;
+ GtkWidget *redo_button;
+ GtkWidget *view_toggle_button;
+ GtkWidget *view_toggle_icon;
+
+ GtkWidget *operations_popover;
+ GtkWidget *operations_container;
+ GtkWidget *operations_revealer;
+ GtkWidget *operations_icon;
+
+ GtkWidget *forward_button;
+ GtkGesture *forward_button_longpress_gesture;
+ GtkGesture *forward_button_multi_press_gesture;
+
+ GtkWidget *back_button;
+ GtkGesture *back_button_longpress_gesture;
+ GtkGesture *back_button_multi_press_gesture;
+
+ GtkWidget *search_button;
+
+ GtkWidget *location_entry_close_button;
+
+ NautilusProgressInfoManager *progress_manager;
+
+ /* active slot & bindings */
+ NautilusWindowSlot *window_slot;
+ GBinding *icon_binding;
+ GBinding *search_binding;
+ GBinding *tooltip_binding;
+};
+
+enum
+{
+ PROP_WINDOW = 1,
+ PROP_SHOW_LOCATION_ENTRY,
+ PROP_WINDOW_SLOT,
+ PROP_SEARCHING,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+G_DEFINE_TYPE (NautilusToolbar, nautilus_toolbar, GTK_TYPE_HEADER_BAR);
+
+static void nautilus_toolbar_set_window_slot_real (NautilusToolbar *self,
+ NautilusWindowSlot *slot);
+static void update_operations (NautilusToolbar *self);
+
+static void
+toolbar_update_appearance (NautilusToolbar *self)
+{
+ gboolean show_location_entry;
+
+ show_location_entry = self->show_location_entry ||
+ g_settings_get_boolean (nautilus_preferences,
+ NAUTILUS_PREFERENCES_ALWAYS_USE_LOCATION_ENTRY);
+
+ if (self->window_slot != NULL &&
+ nautilus_window_slot_get_searching (self->window_slot))
+ {
+ gtk_stack_set_visible_child_name (GTK_STACK (self->toolbar_switcher), "search");
+ }
+ else if (show_location_entry)
+ {
+ gtk_stack_set_visible_child_name (GTK_STACK (self->toolbar_switcher), "location");
+ }
+ else
+ {
+ gtk_stack_set_visible_child_name (GTK_STACK (self->toolbar_switcher), "pathbar");
+ }
+}
+
+static void
+activate_back_or_forward_menu_item (GtkMenuItem *menu_item,
+ NautilusWindow *window,
+ gboolean back)
+{
+ int index;
+
+ g_assert (GTK_IS_MENU_ITEM (menu_item));
+
+ index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item), "user_data"));
+
+ nautilus_window_back_or_forward (window, back, index, nautilus_event_get_window_open_flags ());
+}
+
+static void
+activate_back_menu_item_callback (GtkMenuItem *menu_item,
+ NautilusToolbar *self)
+{
+ activate_back_or_forward_menu_item (menu_item, self->window, TRUE);
+}
+
+static void
+activate_forward_menu_item_callback (GtkMenuItem *menu_item,
+ NautilusToolbar *self)
+{
+ activate_back_or_forward_menu_item (menu_item, self->window, FALSE);
+}
+
+static void
+fill_menu (NautilusToolbar *self,
+ GtkWidget *menu,
+ gboolean back)
+{
+ GtkWidget *menu_item;
+ int index;
+ GList *list;
+
+ list = back ? nautilus_window_slot_get_back_history (self->window_slot) :
+ nautilus_window_slot_get_forward_history (self->window_slot);
+
+ index = 0;
+ while (list != NULL)
+ {
+ menu_item = nautilus_bookmark_menu_item_new (NAUTILUS_BOOKMARK (list->data));
+ g_object_set_data (G_OBJECT (menu_item), "user_data", GINT_TO_POINTER (index));
+ gtk_widget_show (GTK_WIDGET (menu_item));
+ g_signal_connect_object (menu_item, "activate",
+ back
+ ? G_CALLBACK (activate_back_menu_item_callback)
+ : G_CALLBACK (activate_forward_menu_item_callback),
+ self, 0);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+ list = g_list_next (list);
+ ++index;
+ }
+}
+
+static void
+show_menu (NautilusToolbar *self,
+ GtkWidget *widget,
+ const GdkEvent *event)
+{
+ GtkWidget *menu;
+ NautilusNavigationDirection direction;
+
+ menu = gtk_menu_new ();
+
+ direction = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget),
+ "nav-direction"));
+
+ switch (direction)
+ {
+ case NAUTILUS_NAVIGATION_DIRECTION_FORWARD:
+ {
+ fill_menu (self, menu, FALSE);
+ }
+ break;
+
+ case NAUTILUS_NAVIGATION_DIRECTION_BACK:
+ {
+ fill_menu (self, menu, TRUE);
+ }
+ break;
+
+ default:
+ {
+ g_assert_not_reached ();
+ }
+ break;
+ }
+
+ gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (self->window), NULL);
+ gtk_menu_popup_at_widget (GTK_MENU (menu), widget,
+ GDK_GRAVITY_SOUTH_WEST,
+ GDK_GRAVITY_NORTH_WEST,
+ event);
+}
+
+static void
+navigation_button_press_cb (GtkGestureMultiPress *gesture,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ gpointer user_data)
+{
+ NautilusToolbar *self;
+ GtkWidget *widget;
+ GdkEventSequence *sequence;
+ const GdkEvent *event;
+
+ self = NAUTILUS_TOOLBAR (user_data);
+ widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+ event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
+
+ show_menu (self, widget, event);
+}
+
+static void
+back_button_longpress_cb (GtkGestureLongPress *gesture,
+ double x,
+ double y,
+ gpointer user_data)
+{
+ NautilusToolbar *self = user_data;
+ GdkEventSequence *sequence;
+ const GdkEvent *event;
+
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+ event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
+
+ show_menu (self, self->back_button, event);
+}
+
+static void
+forward_button_longpress_cb (GtkGestureLongPress *gesture,
+ double x,
+ double y,
+ gpointer user_data)
+{
+ NautilusToolbar *self = user_data;
+ GdkEventSequence *sequence;
+ const GdkEvent *event;
+
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+ event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
+
+ show_menu (self, self->forward_button, event);
+}
+
+static gboolean
+should_show_progress_info (NautilusProgressInfo *info)
+{
+ return nautilus_progress_info_get_total_elapsed_time (info) +
+ nautilus_progress_info_get_remaining_time (info) > OPERATION_MINIMUM_TIME;
+}
+
+static GList *
+get_filtered_progress_infos (NautilusToolbar *self)
+{
+ GList *l;
+ GList *filtered_progress_infos;
+ GList *progress_infos;
+
+ progress_infos = nautilus_progress_info_manager_get_all_infos (self->progress_manager);
+ filtered_progress_infos = NULL;
+
+ for (l = progress_infos; l != NULL; l = l->next)
+ {
+ if (should_show_progress_info (l->data))
+ {
+ filtered_progress_infos = g_list_append (filtered_progress_infos, l->data);
+ }
+ }
+
+ return filtered_progress_infos;
+}
+
+static gboolean
+should_hide_operations_button (NautilusToolbar *self)
+{
+ GList *progress_infos;
+ GList *l;
+
+ progress_infos = get_filtered_progress_infos (self);
+
+ for (l = progress_infos; l != NULL; l = l->next)
+ {
+ if (nautilus_progress_info_get_total_elapsed_time (l->data) +
+ nautilus_progress_info_get_remaining_time (l->data) > OPERATION_MINIMUM_TIME &&
+ !nautilus_progress_info_get_is_cancelled (l->data) &&
+ !nautilus_progress_info_get_is_finished (l->data))
+ {
+ return FALSE;
+ }
+ }
+
+ g_list_free (progress_infos);
+
+ return TRUE;
+}
+
+static gboolean
+on_remove_finished_operations_timeout (NautilusToolbar *self)
+{
+ nautilus_progress_info_manager_remove_finished_or_cancelled_infos (self->progress_manager);
+ if (should_hide_operations_button (self))
+ {
+ gtk_revealer_set_reveal_child (GTK_REVEALER (self->operations_revealer),
+ FALSE);
+ }
+ else
+ {
+ update_operations (self);
+ }
+
+ self->remove_finished_operations_timeout_id = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+unschedule_remove_finished_operations (NautilusToolbar *self)
+{
+ if (self->remove_finished_operations_timeout_id != 0)
+ {
+ g_source_remove (self->remove_finished_operations_timeout_id);
+ self->remove_finished_operations_timeout_id = 0;
+ }
+}
+
+static void
+schedule_remove_finished_operations (NautilusToolbar *self)
+{
+ if (self->remove_finished_operations_timeout_id == 0)
+ {
+ self->remove_finished_operations_timeout_id =
+ g_timeout_add_seconds (REMOVE_FINISHED_OPERATIONS_TIEMOUT,
+ (GSourceFunc) on_remove_finished_operations_timeout,
+ self);
+ }
+}
+
+static void
+remove_operations_button_attention_style (NautilusToolbar *self)
+{
+ GtkStyleContext *style_context;
+
+ style_context = gtk_widget_get_style_context (self->operations_button);
+ gtk_style_context_remove_class (style_context,
+ "nautilus-operations-button-needs-attention");
+}
+
+static gboolean
+on_remove_operations_button_attention_style_timeout (NautilusToolbar *self)
+{
+ remove_operations_button_attention_style (self);
+ self->operations_button_attention_timeout_id = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+unschedule_operations_button_attention_style (NautilusToolbar *self)
+{
+ if (self->operations_button_attention_timeout_id != 0)
+ {
+ g_source_remove (self->operations_button_attention_timeout_id);
+ self->operations_button_attention_timeout_id = 0;
+ }
+}
+
+static void
+add_operations_button_attention_style (NautilusToolbar *self)
+{
+ GtkStyleContext *style_context;
+
+ style_context = gtk_widget_get_style_context (self->operations_button);
+
+ unschedule_operations_button_attention_style (self);
+ remove_operations_button_attention_style (self);
+
+ gtk_style_context_add_class (style_context,
+ "nautilus-operations-button-needs-attention");
+ self->operations_button_attention_timeout_id = g_timeout_add (NEEDS_ATTENTION_ANIMATION_TIMEOUT,
+ (GSourceFunc) on_remove_operations_button_attention_style_timeout,
+ self);
+}
+
+static void
+on_progress_info_cancelled (NautilusToolbar *self)
+{
+ /* Update the pie chart progress */
+ gtk_widget_queue_draw (self->operations_icon);
+
+ if (!nautilus_progress_manager_has_viewers (self->progress_manager))
+ {
+ schedule_remove_finished_operations (self);
+ }
+}
+
+static void
+on_progress_info_progress_changed (NautilusToolbar *self)
+{
+ /* Update the pie chart progress */
+ gtk_widget_queue_draw (self->operations_icon);
+}
+
+static void
+on_progress_info_finished (NautilusToolbar *self,
+ NautilusProgressInfo *info)
+{
+ gchar *main_label;
+ GFile *folder_to_open;
+
+ /* Update the pie chart progress */
+ gtk_widget_queue_draw (self->operations_icon);
+
+ if (!nautilus_progress_manager_has_viewers (self->progress_manager))
+ {
+ schedule_remove_finished_operations (self);
+ }
+
+ folder_to_open = nautilus_progress_info_get_destination (info);
+ /* If destination is null, don't show a notification. This happens when the
+ * operation is a trash operation, which we already show a diferent kind of
+ * notification */
+ if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->operations_button)) &&
+ folder_to_open != NULL)
+ {
+ add_operations_button_attention_style (self);
+ main_label = nautilus_progress_info_get_status (info);
+ nautilus_window_show_operation_notification (self->window,
+ main_label,
+ folder_to_open);
+ g_free (main_label);
+ }
+
+ g_clear_object (&folder_to_open);
+}
+
+static void
+disconnect_progress_infos (NautilusToolbar *self)
+{
+ GList *progress_infos;
+ GList *l;
+
+ progress_infos = nautilus_progress_info_manager_get_all_infos (self->progress_manager);
+ for (l = progress_infos; l != NULL; l = l->next)
+ {
+ g_signal_handlers_disconnect_by_data (l->data, self);
+ }
+}
+
+static void
+update_operations (NautilusToolbar *self)
+{
+ GList *progress_infos;
+ GList *l;
+ GtkWidget *progress;
+ gboolean should_show_progress_button = FALSE;
+
+ gtk_container_foreach (GTK_CONTAINER (self->operations_container),
+ (GtkCallback) gtk_widget_destroy,
+ NULL);
+
+ disconnect_progress_infos (self);
+
+ progress_infos = get_filtered_progress_infos (self);
+ for (l = progress_infos; l != NULL; l = l->next)
+ {
+ should_show_progress_button = should_show_progress_button ||
+ should_show_progress_info (l->data);
+
+ g_signal_connect_swapped (l->data, "finished",
+ G_CALLBACK (on_progress_info_finished), self);
+ g_signal_connect_swapped (l->data, "cancelled",
+ G_CALLBACK (on_progress_info_cancelled), self);
+ g_signal_connect_swapped (l->data, "progress-changed",
+ G_CALLBACK (on_progress_info_progress_changed), self);
+ progress = nautilus_progress_info_widget_new (l->data);
+ gtk_box_pack_start (GTK_BOX (self->operations_container),
+ progress,
+ FALSE, FALSE, 0);
+ }
+
+ g_list_free (progress_infos);
+
+ if (should_show_progress_button &&
+ !gtk_revealer_get_reveal_child (GTK_REVEALER (self->operations_revealer)))
+ {
+ add_operations_button_attention_style (self);
+ gtk_revealer_set_reveal_child (GTK_REVEALER (self->operations_revealer),
+ TRUE);
+ gtk_widget_queue_draw (self->operations_icon);
+
+ /* Show the popover at start to increase visibility.
+ * Check whether the toolbar is visible or not before showing the
+ * popover. This can happens if the window has the disables-chrome
+ * property set. */
+ if (gtk_widget_is_visible (GTK_WIDGET (self)))
+ {
+ GtkAllocation rect;
+ IdeBoxTheatric *theatric;
+
+ gtk_widget_get_allocation (GTK_WIDGET (self->operations_button), &rect);
+ theatric = g_object_new (IDE_TYPE_BOX_THEATRIC,
+ "alpha", 0.9,
+ "background", "#fdfdfd",
+ "target", self->operations_button,
+ "height", rect.height,
+ "width", rect.width,
+ "x", rect.x,
+ "y", rect.y,
+ NULL);
+
+ egg_object_animate_full (theatric,
+ EGG_ANIMATION_EASE_IN_CUBIC,
+ 250,
+ gtk_widget_get_frame_clock (GTK_WIDGET (self->operations_button)),
+ g_object_unref,
+ theatric,
+ "x", rect.x - ANIMATION_X_GROW,
+ "width", rect.width + (ANIMATION_X_GROW * 2),
+ "y", rect.y - ANIMATION_Y_GROW,
+ "height", rect.height + (ANIMATION_Y_GROW * 2),
+ "alpha", 0.0,
+ NULL);
+ }
+ }
+
+ /* Since we removed the info widgets, we need to restore the focus */
+ if (gtk_widget_get_visible (self->operations_popover))
+ {
+ gtk_widget_grab_focus (self->operations_popover);
+ }
+}
+
+static gboolean
+on_progress_info_started_timeout (NautilusToolbar *self)
+{
+ GList *progress_infos;
+ GList *filtered_progress_infos;
+
+ update_operations (self);
+
+ /* In case we didn't show the operations button because the operation total
+ * time stimation is not good enough, update again to make sure we don't miss
+ * a long time operation because of that */
+
+ progress_infos = nautilus_progress_info_manager_get_all_infos (self->progress_manager);
+ filtered_progress_infos = get_filtered_progress_infos (self);
+ if (!nautilus_progress_manager_are_all_infos_finished_or_cancelled (self->progress_manager) &&
+ g_list_length (progress_infos) != g_list_length (filtered_progress_infos))
+ {
+ g_list_free (filtered_progress_infos);
+ return G_SOURCE_CONTINUE;
+ }
+ else
+ {
+ g_list_free (filtered_progress_infos);
+ self->start_operations_timeout_id = 0;
+ return G_SOURCE_REMOVE;
+ }
+}
+
+static void
+schedule_operations_start (NautilusToolbar *self)
+{
+ if (self->start_operations_timeout_id == 0)
+ {
+ /* Timeout is a little more than what we require for a stimated operation
+ * total time, to make sure the stimated total time is correct */
+ self->start_operations_timeout_id =
+ g_timeout_add (SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE * 1000 + 500,
+ (GSourceFunc) on_progress_info_started_timeout,
+ self);
+ }
+}
+
+static void
+unschedule_operations_start (NautilusToolbar *self)
+{
+ if (self->start_operations_timeout_id != 0)
+ {
+ g_source_remove (self->start_operations_timeout_id);
+ self->start_operations_timeout_id = 0;
+ }
+}
+
+static void
+on_progress_info_started (NautilusProgressInfo *info,
+ NautilusToolbar *self)
+{
+ g_signal_handlers_disconnect_by_data (info, self);
+ schedule_operations_start (self);
+}
+
+static void
+on_new_progress_info (NautilusProgressInfoManager *manager,
+ NautilusProgressInfo *info,
+ NautilusToolbar *self)
+{
+ g_signal_connect (info, "started",
+ G_CALLBACK (on_progress_info_started), self);
+}
+
+static void
+on_operations_icon_draw (GtkWidget *widget,
+ cairo_t *cr,
+ NautilusToolbar *self)
+{
+ gfloat elapsed_progress = 0;
+ gint remaining_progress = 0;
+ gint total_progress;
+ gdouble ratio;
+ GList *progress_infos;
+ GList *l;
+ guint width;
+ guint height;
+ gboolean all_cancelled;
+ GdkRGBA background;
+ GdkRGBA foreground;
+ GtkStyleContext *style_context;
+
+ style_context = gtk_widget_get_style_context (widget);
+ gtk_style_context_get_color (style_context, gtk_widget_get_state_flags (widget), &foreground);
+ background = foreground;
+ background.alpha *= 0.3;
+
+ all_cancelled = TRUE;
+ progress_infos = get_filtered_progress_infos (self);
+ for (l = progress_infos; l != NULL; l = l->next)
+ {
+ if (!nautilus_progress_info_get_is_cancelled (l->data))
+ {
+ all_cancelled = FALSE;
+ remaining_progress += nautilus_progress_info_get_remaining_time (l->data);
+ elapsed_progress += nautilus_progress_info_get_elapsed_time (l->data);
+ }
+ }
+
+ g_list_free (progress_infos);
+
+ total_progress = remaining_progress + elapsed_progress;
+
+ if (all_cancelled)
+ {
+ ratio = 1.0;
+ }
+ else
+ {
+ if (total_progress > 0)
+ {
+ ratio = MAX (0.05, elapsed_progress / total_progress);
+ }
+ else
+ {
+ ratio = 0.05;
+ }
+ }
+
+
+ width = gtk_widget_get_allocated_width (widget);
+ height = gtk_widget_get_allocated_height (widget);
+
+ gdk_cairo_set_source_rgba (cr, &background);
+ cairo_arc (cr,
+ width / 2.0, height / 2.0,
+ MIN (width, height) / 2.0,
+ 0, 2 * G_PI);
+ cairo_fill (cr);
+ cairo_move_to (cr, width / 2.0, height / 2.0);
+ gdk_cairo_set_source_rgba (cr, &foreground);
+ cairo_arc (cr,
+ width / 2.0, height / 2.0,
+ MIN (width, height) / 2.0,
+ -G_PI / 2.0, ratio * 2 * G_PI - G_PI / 2.0);
+
+ cairo_fill (cr);
+}
+
+static void
+on_operations_button_toggled (NautilusToolbar *self,
+ GtkToggleButton *button)
+{
+ if (gtk_toggle_button_get_active (button))
+ {
+ unschedule_remove_finished_operations (self);
+ nautilus_progress_manager_add_viewer (self->progress_manager,
+ G_OBJECT (self));
+ }
+ else
+ {
+ nautilus_progress_manager_remove_viewer (self->progress_manager,
+ G_OBJECT (self));
+ }
+}
+
+static void
+on_progress_has_viewers_changed (NautilusProgressInfoManager *manager,
+ NautilusToolbar *self)
+{
+ if (nautilus_progress_manager_has_viewers (manager))
+ {
+ unschedule_remove_finished_operations (self);
+ return;
+ }
+
+ if (nautilus_progress_manager_are_all_infos_finished_or_cancelled (manager))
+ {
+ unschedule_remove_finished_operations (self);
+ schedule_remove_finished_operations (self);
+ }
+}
+
+static void
+update_menu_item (GtkWidget *menu_item,
+ NautilusToolbar *self,
+ const char *action_name,
+ gboolean enabled,
+ char *label)
+{
+ GAction *action;
+ GValue val = G_VALUE_INIT;
+
+ /* Activate/deactivate */
+ action = g_action_map_lookup_action (G_ACTION_MAP (self->window), action_name);
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled);
+
+ /* Set the text of the menu item. Can't use gtk_button_set_label here as
+ * we need to set the text property, not the label. There's no equivalent
+ * gtk_model_button_set_text function (refer to #766083 for discussion
+ * on adding a set_text function)
+ */
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, label);
+ g_object_set_property (G_OBJECT (menu_item), "text", &val);
+ g_value_unset (&val);
+}
+
+static void
+undo_manager_changed (NautilusToolbar *self)
+{
+ NautilusFileUndoInfo *info;
+ NautilusFileUndoManagerState undo_state;
+ gboolean undo_active;
+ gboolean redo_active;
+ g_autofree gchar *undo_label = NULL;
+ g_autofree gchar *redo_label = NULL;
+ g_autofree gchar *undo_description = NULL;
+ g_autofree gchar *redo_description = NULL;
+ gboolean is_undo;
+
+ /* Look up the last action from the undo manager, and get the text that
+ * describes it, e.g. "Undo Create Folder"/"Redo Create Folder"
+ */
+ info = nautilus_file_undo_manager_get_action ();
+ undo_state = nautilus_file_undo_manager_get_state ();
+ undo_active = redo_active = FALSE;
+ if (info != NULL && undo_state > NAUTILUS_FILE_UNDO_MANAGER_STATE_NONE)
+ {
+ is_undo = undo_state == NAUTILUS_FILE_UNDO_MANAGER_STATE_UNDO;
+
+ /* The last action can either be undone/redone. Activate the corresponding
+ * menu item and deactivate the other
+ */
+ undo_active = is_undo;
+ redo_active = !is_undo;
+ nautilus_file_undo_info_get_strings (info, &undo_label, &undo_description,
+ &redo_label, &redo_description);
+ }
+
+ /* Set the label of the undo and redo menu items, and activate them appropriately
+ */
+ if (!undo_active || undo_label == NULL)
+ {
+ g_free (undo_label);
+ undo_label = g_strdup (_("_Undo"));
+ }
+ update_menu_item (self->undo_button, self, "undo", undo_active, undo_label);
+
+ if (!redo_active || redo_label == NULL)
+ {
+ g_free (redo_label);
+ redo_label = g_strdup (_("_Redo"));
+ }
+ update_menu_item (self->redo_button, self, "redo", redo_active, redo_label);
+}
+
+static void
+on_location_entry_close (GtkWidget *close_button,
+ NautilusToolbar *self)
+{
+ nautilus_toolbar_set_show_location_entry (self, FALSE);
+}
+
+static gboolean
+on_location_entry_populate_popup (GtkEntry *entry,
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ NautilusToolbar *toolbar;
+
+ toolbar = user_data;
+
+ toolbar->location_entry_should_auto_hide = FALSE;
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static void
+on_location_entry_focus_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NautilusToolbar *toolbar;
+
+ toolbar = NAUTILUS_TOOLBAR (user_data);
+
+ if (gtk_widget_has_focus (GTK_WIDGET (object)))
+ {
+ toolbar->location_entry_should_auto_hide = TRUE;
+ }
+ else if (toolbar->location_entry_should_auto_hide)
+ {
+ nautilus_toolbar_set_show_location_entry (toolbar, FALSE);
+ }
+}
+
+static void
+nautilus_toolbar_constructed (GObject *object)
+{
+ GtkBuilder *builder;
+ NautilusToolbar *self = NAUTILUS_TOOLBAR (object);
+
+ builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/ui/nautilus-toolbar-switcher.ui");
+ self->toolbar_switcher = GTK_WIDGET (gtk_builder_get_object (builder, "toolbar_switcher"));
+ self->search_container = GTK_WIDGET (gtk_builder_get_object (builder, "search_container"));
+ self->path_bar_container = GTK_WIDGET (gtk_builder_get_object (builder, "path_bar_container"));
+ self->location_entry_container = GTK_WIDGET (gtk_builder_get_object (builder, "location_entry_container"));
+
+ self->toolbar_switcher_container_max_width = nautilus_container_max_width_new ();
+ nautilus_container_max_width_set_max_width (self->toolbar_switcher_container_max_width,
+ SWITCHER_MAX_WIDTH);
+ gtk_container_add (GTK_CONTAINER (self->toolbar_switcher_container_max_width),
+ self->toolbar_switcher);
+ gtk_container_add (GTK_CONTAINER (self->toolbar_switcher_container),
+ GTK_WIDGET (self->toolbar_switcher_container_max_width));
+
+ self->path_bar = g_object_new (NAUTILUS_TYPE_PATH_BAR, NULL);
+ gtk_container_add (GTK_CONTAINER (self->path_bar_container),
+ self->path_bar);
+
+ self->location_entry = nautilus_location_entry_new ();
+ gtk_container_add (GTK_CONTAINER (self->location_entry_container),
+ self->location_entry);
+ self->location_entry_close_button = gtk_button_new_from_icon_name ("window-close-symbolic",
+ GTK_ICON_SIZE_BUTTON);
+ gtk_container_add (GTK_CONTAINER (self->location_entry_container),
+ self->location_entry_close_button);
+ g_signal_connect (self->location_entry_close_button, "clicked",
+ G_CALLBACK (on_location_entry_close), self);
+
+ self->progress_manager = nautilus_progress_info_manager_dup_singleton ();
+ g_signal_connect (self->progress_manager, "new-progress-info",
+ G_CALLBACK (on_new_progress_info), self);
+ g_signal_connect (self->progress_manager, "has-viewers-changed",
+ G_CALLBACK (on_progress_has_viewers_changed), self);
+
+ update_operations (self);
+
+ self->back_button_longpress_gesture = gtk_gesture_long_press_new (self->back_button);
+ g_signal_connect (self->back_button_longpress_gesture, "pressed",
+ G_CALLBACK (back_button_longpress_cb), self);
+
+ self->forward_button_longpress_gesture = gtk_gesture_long_press_new (self->forward_button);
+ g_signal_connect (self->forward_button_longpress_gesture, "pressed",
+ G_CALLBACK (forward_button_longpress_cb), self);
+
+ g_object_set_data (G_OBJECT (self->back_button), "nav-direction",
+ GUINT_TO_POINTER (NAUTILUS_NAVIGATION_DIRECTION_BACK));
+ g_object_set_data (G_OBJECT (self->forward_button), "nav-direction",
+ GUINT_TO_POINTER (NAUTILUS_NAVIGATION_DIRECTION_FORWARD));
+
+
+ self->back_button_multi_press_gesture = gtk_gesture_multi_press_new (self->back_button);
+ gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->back_button_multi_press_gesture),
+ GDK_BUTTON_SECONDARY);
+ g_signal_connect (self->back_button_multi_press_gesture, "pressed",
+ G_CALLBACK (navigation_button_press_cb), self);
+
+ self->forward_button_multi_press_gesture = gtk_gesture_multi_press_new (self->forward_button);
+ gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->forward_button_multi_press_gesture),
+ GDK_BUTTON_SECONDARY);
+ g_signal_connect (self->forward_button_multi_press_gesture, "pressed",
+ G_CALLBACK (navigation_button_press_cb), self);
+
+ g_signal_connect (self->operations_popover, "show",
+ (GCallback) gtk_widget_grab_focus, NULL);
+ g_signal_connect_swapped (self->operations_popover, "closed",
+ (GCallback) gtk_widget_grab_focus, self);
+ g_signal_connect (self->location_entry, "populate-popup",
+ G_CALLBACK (on_location_entry_populate_popup), self);
+ g_signal_connect (self->location_entry, "notify::has-focus",
+ G_CALLBACK (on_location_entry_focus_changed), self);
+
+ gtk_widget_show_all (GTK_WIDGET (self));
+ toolbar_update_appearance (self);
+}
+
+static void
+nautilus_toolbar_init (NautilusToolbar *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+void
+nautilus_toolbar_on_window_constructed (NautilusToolbar *self)
+{
+ /* undo_manager_changed manipulates the window actions, so set it up
+ * after the window and it's actions have been constructed
+ */
+ g_signal_connect_object (nautilus_file_undo_manager_get (),
+ "undo-changed",
+ G_CALLBACK (undo_manager_changed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ undo_manager_changed (self);
+}
+
+static void
+nautilus_toolbar_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NautilusToolbar *self = NAUTILUS_TOOLBAR (object);
+
+ switch (property_id)
+ {
+ case PROP_SHOW_LOCATION_ENTRY:
+ {
+ g_value_set_boolean (value, self->show_location_entry);
+ }
+ break;
+
+ case PROP_SEARCHING:
+ {
+ g_value_set_boolean (value, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->search_button)));
+ }
+ break;
+
+ default:
+ {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+ break;
+ }
+}
+
+static void
+on_window_slot_destroyed (gpointer data,
+ GObject *where_the_object_was)
+{
+ NautilusToolbar *self;
+
+ self = NAUTILUS_TOOLBAR (data);
+
+ /* The window slot was finalized, and the binding has already been removed.
+ * Null it here, so that dispose() does not trip over itself when removing it.
+ */
+ self->icon_binding = NULL;
+ self->search_binding = NULL;
+
+ nautilus_toolbar_set_window_slot_real (self, NULL);
+}
+
+static void
+on_window_focus_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GtkWidget *widget;
+ NautilusToolbar *toolbar;
+
+ widget = GTK_WIDGET (object);
+ toolbar = NAUTILUS_TOOLBAR (user_data);
+
+ if (g_settings_get_boolean (nautilus_preferences,
+ NAUTILUS_PREFERENCES_ALWAYS_USE_LOCATION_ENTRY))
+ {
+ return;
+ }
+
+ /* The working assumption being made here is, if the location entry is visible,
+ * the user must have switched windows while having keyboard focus on the entry
+ * (because otherwise it would be invisible),
+ * so we focus the entry explicitly to reset the “should auto-hide” flag.
+ */
+ if (gtk_widget_has_focus (widget) && toolbar->show_location_entry)
+ {
+ gtk_widget_grab_focus (toolbar->location_entry);
+ }
+ /* The location entry in general is hidden when it loses focus,
+ * but hiding it when switching windows could be undesirable, as the user
+ * might want to copy a path from somewhere. This here prevents that from happening.
+ */
+ else
+ {
+ toolbar->location_entry_should_auto_hide = FALSE;
+ }
+}
+
+static void
+nautilus_toolbar_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NautilusToolbar *self = NAUTILUS_TOOLBAR (object);
+
+ switch (property_id)
+ {
+ case PROP_WINDOW:
+ {
+ if (self->window != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (self->window,
+ on_window_focus_changed, self);
+ }
+ self->window = g_value_get_object (value);
+ if (self->window != NULL)
+ {
+ g_signal_connect (self->window, "notify::has-focus",
+ G_CALLBACK (on_window_focus_changed), self);
+ }
+ }
+ break;
+
+ case PROP_SHOW_LOCATION_ENTRY:
+ {
+ nautilus_toolbar_set_show_location_entry (self, g_value_get_boolean (value));
+ }
+ break;
+
+ case PROP_WINDOW_SLOT:
+ {
+ nautilus_toolbar_set_window_slot (self, g_value_get_object (value));
+ }
+ break;
+
+ case PROP_SEARCHING:
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->search_button),
+ g_value_get_boolean (value));
+ }
+ break;
+
+ default:
+ {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+ break;
+ }
+}
+
+static void
+nautilus_toolbar_dispose (GObject *object)
+{
+ NautilusToolbar *self;
+
+ self = NAUTILUS_TOOLBAR (object);
+
+ g_clear_object (&self->forward_button_multi_press_gesture);
+ g_clear_object (&self->back_button_multi_press_gesture);
+ g_clear_pointer (&self->icon_binding, g_binding_unbind);
+ g_clear_pointer (&self->search_binding, g_binding_unbind);
+
+ G_OBJECT_CLASS (nautilus_toolbar_parent_class)->dispose (object);
+}
+
+static void
+nautilus_toolbar_finalize (GObject *obj)
+{
+ NautilusToolbar *self = NAUTILUS_TOOLBAR (obj);
+
+ g_signal_handlers_disconnect_by_func (nautilus_preferences,
+ toolbar_update_appearance, self);
+
+ if (self->window_slot != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (self->window_slot, self);
+ g_object_weak_unref (G_OBJECT (self->window_slot),
+ on_window_slot_destroyed, self);
+ self->window_slot = NULL;
+ }
+ disconnect_progress_infos (self);
+ unschedule_remove_finished_operations (self);
+ unschedule_operations_start (self);
+ unschedule_operations_button_attention_style (self);
+
+ g_signal_handlers_disconnect_by_data (self->progress_manager, self);
+ g_clear_object (&self->progress_manager);
+
+ g_signal_handlers_disconnect_by_func (self->window,
+ on_window_focus_changed, self);
+
+ g_clear_object (&self->back_button_longpress_gesture);
+ g_clear_object (&self->forward_button_longpress_gesture);
+
+ G_OBJECT_CLASS (nautilus_toolbar_parent_class)->finalize (obj);
+}
+
+static void
+nautilus_toolbar_class_init (NautilusToolbarClass *klass)
+{
+ GObjectClass *oclass;
+ GtkWidgetClass *widget_class;
+
+ widget_class = GTK_WIDGET_CLASS (klass);
+ oclass = G_OBJECT_CLASS (klass);
+ oclass->get_property = nautilus_toolbar_get_property;
+ oclass->set_property = nautilus_toolbar_set_property;
+ oclass->dispose = nautilus_toolbar_dispose;
+ oclass->finalize = nautilus_toolbar_finalize;
+ oclass->constructed = nautilus_toolbar_constructed;
+
+ properties[PROP_WINDOW] =
+ g_param_spec_object ("window",
+ "The NautilusWindow",
+ "The NautilusWindow this toolbar is part of",
+ NAUTILUS_TYPE_WINDOW,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+ properties[PROP_SHOW_LOCATION_ENTRY] =
+ g_param_spec_boolean ("show-location-entry",
+ "Whether to show the location entry",
+ "Whether to show the location entry instead of the pathbar",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties [PROP_WINDOW_SLOT] =
+ g_param_spec_object ("window-slot",
+ "Window slot currently active",
+ "Window slot currently acive",
+ NAUTILUS_TYPE_WINDOW_SLOT,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SEARCHING] =
+ g_param_spec_boolean ("searching",
+ "Current view is searching",
+ "Whether the current view is searching or not",
+ FALSE,
+ G_PARAM_READWRITE);
+
+ g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/nautilus/ui/nautilus-toolbar.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_button);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_icon);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_popover);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_container);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_revealer);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_button);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_toggle_button);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_toggle_icon);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, back_button);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, forward_button);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, toolbar_switcher_container);
+
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_menu_zoom_section);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_menu_undo_redo_section);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_menu_extended_section);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, undo_button);
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, redo_button);
+
+ gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, search_button);
+
+ gtk_widget_class_bind_template_callback (widget_class, on_operations_icon_draw);
+ gtk_widget_class_bind_template_callback (widget_class, on_operations_button_toggled);
+}
+
+GtkWidget *
+nautilus_toolbar_new ()
+{
+ return g_object_new (NAUTILUS_TYPE_TOOLBAR,
+ NULL);
+}
+
+GtkWidget *
+nautilus_toolbar_get_path_bar (NautilusToolbar *self)
+{
+ return self->path_bar;
+}
+
+GtkWidget *
+nautilus_toolbar_get_location_entry (NautilusToolbar *self)
+{
+ return self->location_entry;
+}
+
+void
+nautilus_toolbar_set_show_location_entry (NautilusToolbar *self,
+ gboolean show_location_entry)
+{
+ if (show_location_entry != self->show_location_entry)
+ {
+ self->show_location_entry = show_location_entry;
+ toolbar_update_appearance (self);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_LOCATION_ENTRY]);
+ }
+}
+
+static void
+container_remove_all_children (GtkContainer *container)
+{
+ GList *children;
+ GList *child;
+
+ children = gtk_container_get_children (container);
+ for (child = children; child != NULL; child = g_list_next (child))
+ {
+ gtk_container_remove (container, GTK_WIDGET (child->data));
+ }
+ g_list_free (children);
+}
+
+static void
+slot_on_extensions_background_menu_changed (NautilusToolbar *self,
+ GParamSpec *param,
+ NautilusWindowSlot *slot)
+{
+ nautilus_path_bar_set_extensions_background_menu (NAUTILUS_PATH_BAR (self->path_bar),
+ nautilus_window_slot_get_extensions_background_menu (slot));
+}
+
+static void
+slot_on_templates_menu_changed (NautilusToolbar *self,
+ GParamSpec *param,
+ NautilusWindowSlot *slot)
+{
+ nautilus_path_bar_set_templates_menu (NAUTILUS_PATH_BAR (self->path_bar),
+ nautilus_window_slot_get_templates_menu (slot));
+}
+
+static void
+on_slot_toolbar_menu_sections_changed (NautilusToolbar *self,
+ GParamSpec *param,
+ NautilusWindowSlot *slot)
+{
+ NautilusToolbarMenuSections *new_sections;
+
+ container_remove_all_children (GTK_CONTAINER (self->view_menu_zoom_section));
+ container_remove_all_children (GTK_CONTAINER (self->view_menu_extended_section));
+
+ new_sections = nautilus_window_slot_get_toolbar_menu_sections (slot);
+ if (new_sections == NULL)
+ {
+ return;
+ }
+
+ gtk_widget_set_visible (self->view_menu_undo_redo_section,
+ new_sections->supports_undo_redo);
+
+ if (new_sections->zoom_section != NULL)
+ {
+ gtk_box_pack_start (GTK_BOX (self->view_menu_zoom_section),
+ new_sections->zoom_section, FALSE, FALSE, 0);
+ }
+
+ if (new_sections->extended_section != NULL)
+ {
+ gtk_box_pack_start (GTK_BOX (self->view_menu_extended_section),
+ new_sections->extended_section, FALSE, FALSE, 0);
+ }
+
+ gtk_widget_set_sensitive (self->view_button, (new_sections->extended_section != NULL ||
+ new_sections->zoom_section != NULL ||
+ new_sections->supports_undo_redo));
+}
+
+
+static void
+disconnect_toolbar_menu_sections_change_handler (NautilusToolbar *self)
+{
+ if (self->window_slot == NULL)
+ {
+ return;
+ }
+
+ g_signal_handlers_disconnect_by_func (self->window_slot,
+ G_CALLBACK (on_slot_toolbar_menu_sections_changed),
+ self);
+}
+
+static gboolean
+nautilus_toolbar_view_toggle_icon_transform_to (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ GIcon *icon;
+
+ icon = g_value_get_object (from_value);
+
+ /* As per design decision, we let the previous used icon if no
+ * view menu is available */
+ if (icon)
+ {
+ g_value_set_object (to_value, icon);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+nautilus_toolbar_view_toggle_tooltip_transform_to (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ const gchar *tooltip;
+
+ tooltip = g_value_get_string (from_value);
+
+ /* As per design decision, we let the previous used tooltip if no
+ * view menu is available */
+ if (tooltip)
+ {
+ g_value_set_string (to_value, tooltip);
+ }
+
+ return TRUE;
+}
+
+/* Called from on_window_slot_destroyed(), since bindings and signal handlers
+ * are automatically removed once the slot goes away.
+ */
+static void
+nautilus_toolbar_set_window_slot_real (NautilusToolbar *self,
+ NautilusWindowSlot *slot)
+{
+ g_autoptr (GList) children = NULL;
+
+ self->window_slot = slot;
+
+ if (self->window_slot != NULL)
+ {
+ g_object_weak_ref (G_OBJECT (self->window_slot),
+ on_window_slot_destroyed,
+ self);
+
+ self->icon_binding = g_object_bind_property_full (self->window_slot, "icon",
+ self->view_toggle_icon, "gicon",
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
+ (GBindingTransformFunc) nautilus_toolbar_view_toggle_icon_transform_to,
+ NULL,
+ self,
+ NULL);
+
+ self->tooltip_binding = g_object_bind_property_full (self->window_slot, "tooltip",
+ self->view_toggle_button, "tooltip-text",
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
+ (GBindingTransformFunc) nautilus_toolbar_view_toggle_tooltip_transform_to,
+ NULL,
+ self,
+ NULL);
+
+ self->search_binding = g_object_bind_property (self->window_slot, "searching",
+ self, "searching",
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+
+ on_slot_toolbar_menu_sections_changed (self, NULL, self->window_slot);
+ g_signal_connect_swapped (self->window_slot, "notify::toolbar-menu-sections",
+ G_CALLBACK (on_slot_toolbar_menu_sections_changed), self);
+ g_signal_connect_swapped (self->window_slot, "notify::extensions-background-menu",
+ G_CALLBACK (slot_on_extensions_background_menu_changed), self);
+ g_signal_connect_swapped (self->window_slot, "notify::templates-menu",
+ G_CALLBACK (slot_on_templates_menu_changed), self);
+ g_signal_connect_swapped (self->window_slot, "notify::searching",
+ G_CALLBACK (toolbar_update_appearance), self);
+ }
+
+ children = gtk_container_get_children (GTK_CONTAINER (self->search_container));
+ if (children != NULL)
+ {
+ gtk_container_remove (GTK_CONTAINER (self->search_container),
+ children->data);
+ }
+
+ if (self->window_slot != NULL)
+ {
+ gtk_container_add (GTK_CONTAINER (self->search_container),
+ GTK_WIDGET (nautilus_window_slot_get_query_editor (self->window_slot)));
+ }
+
+ toolbar_update_appearance (self);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WINDOW_SLOT]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCHING]);
+}
+
+void
+nautilus_toolbar_set_window_slot (NautilusToolbar *self,
+ NautilusWindowSlot *window_slot)
+{
+ g_return_if_fail (NAUTILUS_IS_TOOLBAR (self));
+ g_return_if_fail (window_slot == NULL || NAUTILUS_IS_WINDOW_SLOT (window_slot));
+
+ if (self->window_slot == window_slot)
+ {
+ return;
+ }
+
+ g_clear_pointer (&self->icon_binding, g_binding_unbind);
+ g_clear_pointer (&self->search_binding, g_binding_unbind);
+
+ disconnect_toolbar_menu_sections_change_handler (self);
+ if (self->window_slot != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (self->window_slot, self);
+ g_object_weak_unref (G_OBJECT (self->window_slot),
+ on_window_slot_destroyed, self);
+ }
+
+ nautilus_toolbar_set_window_slot_real (self, window_slot);
+}
+
+gboolean
+nautilus_toolbar_is_menu_visible (NautilusToolbar *self)
+{
+ GtkPopover *popover;
+
+ g_return_val_if_fail (NAUTILUS_IS_TOOLBAR (self), FALSE);
+
+ popover = GTK_POPOVER (gtk_menu_button_get_popover (GTK_MENU_BUTTON (self->view_button)));
+
+ return gtk_widget_is_visible (GTK_WIDGET (popover));
+}
+
+gboolean
+nautilus_toolbar_is_operations_button_active (NautilusToolbar *self)
+{
+ return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->operations_button));
+}