diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:39:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:39:48 +0000 |
commit | 3ade071f273aaa973e44bf95d6b1d4913a18f03b (patch) | |
tree | e2f99d267ae18427645404f215b984afbe73098d /src/nautilus-toolbar.c | |
parent | Initial commit. (diff) | |
download | nautilus-3ade071f273aaa973e44bf95d6b1d4913a18f03b.tar.xz nautilus-3ade071f273aaa973e44bf95d6b1d4913a18f03b.zip |
Adding upstream version 43.2.upstream/43.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/nautilus-toolbar.c | 644 |
1 files changed, 644 insertions, 0 deletions
diff --git a/src/nautilus-toolbar.c b/src/nautilus-toolbar.c new file mode 100644 index 0000000..aad0fd3 --- /dev/null +++ b/src/nautilus-toolbar.c @@ -0,0 +1,644 @@ +/* + * 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 "nautilus-application.h" +#include "nautilus-bookmark.h" +#include "nautilus-file-undo-manager.h" +#include "nautilus-global-preferences.h" +#include "nautilus-history-controls.h" +#include "nautilus-location-entry.h" +#include "nautilus-pathbar.h" +#include "nautilus-progress-indicator.h" +#include "nautilus-view-controls.h" +#include "nautilus-ui-utilities.h" +#include "nautilus-window.h" + +struct _NautilusToolbar +{ + AdwBin parent_instance; + + NautilusWindow *window; + + GtkWidget *path_bar_container; + GtkWidget *location_entry_container; + GtkWidget *search_container; + GtkWidget *toolbar_switcher; + GtkWidget *path_bar; + GtkWidget *location_entry; + + gboolean show_location_entry; + + GtkWidget *app_button; + GMenuModel *undo_redo_section; + + GtkWidget *sidebar_button; + gboolean show_sidebar_button; + gboolean sidebar_button_active; + + gboolean show_toolbar_children; + + GtkWidget *search_button; + + GtkWidget *location_entry_close_button; + + /* active slot & bindings */ + NautilusWindowSlot *window_slot; + GBinding *search_binding; +}; + +enum +{ + PROP_0, + PROP_SHOW_LOCATION_ENTRY, + PROP_WINDOW_SLOT, + PROP_SEARCHING, + PROP_SHOW_SIDEBAR_BUTTON, + PROP_SIDEBAR_BUTTON_ACTIVE, + PROP_SHOW_TOOLBAR_CHILDREN, + NUM_PROPERTIES +}; + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; + +G_DEFINE_TYPE (NautilusToolbar, nautilus_toolbar, ADW_TYPE_BIN); + +static void nautilus_toolbar_set_window_slot_real (NautilusToolbar *self, + NautilusWindowSlot *slot); +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 +update_action (NautilusToolbar *self, + const char *action_name, + gboolean enabled) +{ + GtkWidget *window; + GAction *action; + + window = gtk_widget_get_ancestor (GTK_WIDGET (self), NAUTILUS_TYPE_WINDOW); + + /* Activate/deactivate */ + action = g_action_map_lookup_action (G_ACTION_MAP (window), action_name); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled); +} + +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; + g_autoptr (GMenu) updated_section = g_menu_new (); + g_autoptr (GMenuItem) undo_menu_item = NULL; + g_autoptr (GMenuItem) redo_menu_item = NULL; + + /* 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")); + } + undo_menu_item = g_menu_item_new (undo_label, "win.undo"); + g_menu_append_item (updated_section, undo_menu_item); + update_action (self, "undo", undo_active); + + if (!redo_active || redo_label == NULL) + { + g_free (redo_label); + redo_label = g_strdup (_("_Redo")); + } + redo_menu_item = g_menu_item_new (redo_label, "win.redo"); + g_menu_append_item (updated_section, redo_menu_item); + update_action (self, "redo", redo_active); + + nautilus_gmenu_set_from_model (G_MENU (self->undo_redo_section), + G_MENU_MODEL (updated_section)); +} + +static void +on_location_entry_close (GtkWidget *close_button, + NautilusToolbar *self) +{ + nautilus_toolbar_set_show_location_entry (self, FALSE); +} + +static void +on_location_entry_focus_leave (GtkEventControllerFocus *controller, + gpointer user_data) +{ + NautilusToolbar *toolbar; + GtkWidget *focus_widget; + + toolbar = NAUTILUS_TOOLBAR (user_data); + + /* The location entry is a transient: it should hide when it loses focus. + * + * However, if we lose focus because the window itself lost focus, then the + * location entry should persist, because this may happen due to the user + * switching keyboard layout/input method; or they may want to copy/drop + * an path from another window/app. We detect this case by looking at the + * focus widget of the window (GtkRoot). + */ + + focus_widget = gtk_root_get_focus (gtk_widget_get_root (GTK_WIDGET (toolbar))); + if (focus_widget != NULL && + gtk_widget_is_ancestor (focus_widget, GTK_WIDGET (toolbar->location_entry))) + { + return; + } + + nautilus_toolbar_set_show_location_entry (toolbar, FALSE); +} + +static void +nautilus_toolbar_constructed (GObject *object) +{ + NautilusToolbar *self = NAUTILUS_TOOLBAR (object); + GtkEventController *controller; + + self->path_bar = GTK_WIDGET (g_object_new (NAUTILUS_TYPE_PATH_BAR, NULL)); + gtk_box_append (GTK_BOX (self->path_bar_container), + self->path_bar); + + self->location_entry = nautilus_location_entry_new (); + gtk_box_append (GTK_BOX (self->location_entry_container), + self->location_entry); + self->location_entry_close_button = gtk_button_new_from_icon_name ("window-close-symbolic"); + gtk_box_append (GTK_BOX (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); + + controller = gtk_event_controller_focus_new (); + gtk_widget_add_controller (self->location_entry, controller); + g_signal_connect (controller, "leave", + G_CALLBACK (on_location_entry_focus_leave), self); + + /* Setting a max width on one entry to effectively set a max expansion for + * the whole title widget. */ + gtk_editable_set_max_width_chars (GTK_EDITABLE (self->location_entry), 88); + + gtk_widget_show (GTK_WIDGET (self)); + toolbar_update_appearance (self); +} + +static void +nautilus_toolbar_init (NautilusToolbar *self) +{ + g_type_ensure (NAUTILUS_TYPE_HISTORY_CONTROLS); + g_type_ensure (NAUTILUS_TYPE_PROGRESS_INDICATOR); + g_type_ensure (NAUTILUS_TYPE_VIEW_CONTROLS); + + 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_WINDOW_SLOT: + { + g_value_set_object (value, self->window_slot); + } + break; + + case PROP_SEARCHING: + { + g_value_set_boolean (value, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->search_button))); + } + break; + + case PROP_SHOW_SIDEBAR_BUTTON: + { + g_value_set_boolean (value, self->show_sidebar_button); + } + break; + + case PROP_SIDEBAR_BUTTON_ACTIVE: + { + g_value_set_boolean (value, self->sidebar_button_active); + } + break; + + case PROP_SHOW_TOOLBAR_CHILDREN: + { + g_value_set_boolean (value, self->show_toolbar_children); + } + 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->search_binding = NULL; + + nautilus_toolbar_set_window_slot_real (self, NULL); +} + +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_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; + + case PROP_SHOW_SIDEBAR_BUTTON: + { + self->show_sidebar_button = g_value_get_boolean (value); + } + break; + + case PROP_SIDEBAR_BUTTON_ACTIVE: + { + self->sidebar_button_active = g_value_get_boolean (value); + } + break; + + case PROP_SHOW_TOOLBAR_CHILDREN: + { + self->show_toolbar_children = 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_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; + } + + 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_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); + + properties[PROP_SHOW_SIDEBAR_BUTTON] = + g_param_spec_boolean ("show-sidebar-button", NULL, NULL, FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + properties[PROP_SIDEBAR_BUTTON_ACTIVE] = + g_param_spec_boolean ("sidebar-button-active", NULL, NULL, FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + properties[PROP_SHOW_TOOLBAR_CHILDREN] = + g_param_spec_boolean ("show-toolbar-children", NULL, NULL, TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + 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, app_button); + gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, undo_redo_section); + gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, toolbar_switcher); + gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, search_container); + gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, path_bar_container); + gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, location_entry_container); + + gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, search_button); + + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_TOOLBAR); +} + +GtkWidget * +nautilus_toolbar_new (void) +{ + 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 +box_remove_all_children (GtkBox *box) +{ + GtkWidget *child; + while ((child = gtk_widget_get_first_child (GTK_WIDGET (box))) != NULL) + { + gtk_box_remove (GTK_BOX (box), child); + } +} + +static void +slot_on_extensions_background_menu_changed (NautilusToolbar *self, + GParamSpec *param, + NautilusWindowSlot *slot) +{ + g_autoptr (GMenuModel) menu = NULL; + + menu = nautilus_window_slot_get_extensions_background_menu (slot); + nautilus_path_bar_set_extensions_background_menu (NAUTILUS_PATH_BAR (self->path_bar), + menu); +} + +static void +slot_on_templates_menu_changed (NautilusToolbar *self, + GParamSpec *param, + NautilusWindowSlot *slot) +{ + g_autoptr (GMenuModel) menu = NULL; + + menu = nautilus_window_slot_get_templates_menu (slot); + nautilus_path_bar_set_templates_menu (NAUTILUS_PATH_BAR (self->path_bar), + menu); +} + +/* 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->search_binding = g_object_bind_property (self->window_slot, "searching", + self, "searching", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + + 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); + } + + box_remove_all_children (GTK_BOX (self->search_container)); + + if (self->window_slot != NULL) + { + gtk_box_append (GTK_BOX (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->search_binding, g_binding_unbind); + + 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) +{ + GtkWidget *menu; + + g_return_val_if_fail (NAUTILUS_IS_TOOLBAR (self), FALSE); + + menu = GTK_WIDGET (gtk_menu_button_get_popover (GTK_MENU_BUTTON (self->app_button))); + g_return_val_if_fail (menu != NULL, FALSE); + + return gtk_widget_is_visible (menu); +} |