From 574281c59434b02407132771c7e23d6fa0220252 Mon Sep 17 00:00:00 2001 From: Dawid Zyla Date: Mon, 6 Jul 2026 17:53:00 -0600 Subject: [PATCH 1/9] window-slot: Add pinned (locked tab) state and API --- src/nemo-window-slot.c | 34 ++++++++++++++++++++++++++++++++++ src/nemo-window-slot.h | 15 +++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/nemo-window-slot.c b/src/nemo-window-slot.c index 4ff9257a4..232f31713 100644 --- a/src/nemo-window-slot.c +++ b/src/nemo-window-slot.c @@ -519,6 +519,9 @@ nemo_window_slot_dispose (GObject *object) g_free (slot->status_text); slot->status_text = NULL; + g_free (slot->pinned_uri); + slot->pinned_uri = NULL; + G_OBJECT_CLASS (nemo_window_slot_parent_class)->dispose (object); } @@ -593,6 +596,37 @@ nemo_window_slot_get_location_uri (NemoWindowSlot *slot) return NULL; } +void +nemo_window_slot_set_pinned (NemoWindowSlot *slot, + gboolean pinned, + const char *pinned_uri) +{ + g_return_if_fail (NEMO_IS_WINDOW_SLOT (slot)); + + g_free (slot->pinned_uri); + slot->pinned_uri = NULL; + + if (pinned) { + if (pinned_uri != NULL) { + slot->pinned_uri = g_strdup (pinned_uri); + } else if (slot->pending_location != NULL) { + slot->pinned_uri = g_file_get_uri (slot->pending_location); + } else { + slot->pinned_uri = nemo_window_slot_get_location_uri (slot); + } + } + + slot->pinned = pinned && slot->pinned_uri != NULL; +} + +gboolean +nemo_window_slot_get_pinned (NemoWindowSlot *slot) +{ + g_return_val_if_fail (NEMO_IS_WINDOW_SLOT (slot), FALSE); + + return slot->pinned; +} + void nemo_window_slot_make_hosting_pane_active (NemoWindowSlot *slot) { diff --git a/src/nemo-window-slot.h b/src/nemo-window-slot.h index 5020b1d5c..16ebcfe3e 100644 --- a/src/nemo-window-slot.h +++ b/src/nemo-window-slot.h @@ -121,8 +121,14 @@ struct NemoWindowSlot { gboolean visible; - /* Back/Forward chain, and history list. - * The data in these lists are NemoBookmark pointers. + /* Tab locking (Total Commander style): a pinned tab cannot be closed + * from the UI and is restored at pinned_uri on startup. + */ + gboolean pinned; + char *pinned_uri; + + /* Back/Forward chain, and history list. + * The data in these lists are NemoBookmark pointers. */ GList *back_list, *forward_list; }; @@ -139,6 +145,11 @@ void nemo_window_slot_set_query_editor_visible (NemoWindowSlot *slot, GFile * nemo_window_slot_get_location (NemoWindowSlot *slot); char * nemo_window_slot_get_location_uri (NemoWindowSlot *slot); +void nemo_window_slot_set_pinned (NemoWindowSlot *slot, + gboolean pinned, + const char *pinned_uri); +gboolean nemo_window_slot_get_pinned (NemoWindowSlot *slot); + void nemo_window_slot_queue_reload (NemoWindowSlot *slot, gboolean clear_thumbs); void nemo_window_slot_force_reload (NemoWindowSlot *slot); From 9675a362f9fc7cf9520bd08556e3e641c8aef6e0 Mon Sep 17 00:00:00 2001 From: Dawid Zyla Date: Mon, 6 Jul 2026 17:56:08 -0600 Subject: [PATCH 2/9] notebook: Show padlock and hide close button on pinned tabs --- src/nemo-notebook.c | 30 +++++++++++++++++++++++++++++- src/nemo-notebook.h | 2 ++ src/nemo-window-slot.c | 5 +++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/nemo-notebook.c b/src/nemo-notebook.c index a3595f4be..3e6d40d31 100644 --- a/src/nemo-notebook.c +++ b/src/nemo-notebook.c @@ -316,6 +316,29 @@ nemo_notebook_sync_tab_label (NemoNotebook *notebook, } } +void +nemo_notebook_sync_pinned (NemoNotebook *notebook, + NemoWindowSlot *slot) +{ + GtkWidget *tab_label, *pinned_icon, *close_button; + + g_return_if_fail (NEMO_IS_NOTEBOOK (notebook)); + g_return_if_fail (NEMO_IS_WINDOW_SLOT (slot)); + + tab_label = gtk_notebook_get_tab_label (GTK_NOTEBOOK (notebook), + GTK_WIDGET (slot)); + if (tab_label == NULL) { + return; + } + + pinned_icon = GTK_WIDGET (g_object_get_data (G_OBJECT (tab_label), "pinned-icon")); + close_button = GTK_WIDGET (g_object_get_data (G_OBJECT (tab_label), "close-button")); + g_return_if_fail (pinned_icon != NULL && close_button != NULL); + + gtk_widget_set_visible (pinned_icon, slot->pinned); + gtk_widget_set_visible (close_button, !slot->pinned); +} + static void close_button_clicked_cb (GtkWidget *widget, NemoWindowSlot *slot) @@ -331,7 +354,7 @@ close_button_clicked_cb (GtkWidget *widget, static GtkWidget * build_tab_label (NemoNotebook *nb, NemoWindowSlot *slot) { - GtkWidget *hbox, *label, *close_button, *image, *spinner, *icon; + GtkWidget *hbox, *label, *close_button, *image, *spinner, *icon, *pinned_icon; /* set hbox spacing and label padding (see below) so that there's an * equal amount of space around the label */ @@ -347,6 +370,10 @@ build_tab_label (NemoNotebook *nb, NemoWindowSlot *slot) gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0); /* don't show the icon */ + /* setup padlock icon for pinned (locked) tabs, hidden by default */ + pinned_icon = gtk_image_new_from_icon_name ("changes-prevent-symbolic", GTK_ICON_SIZE_MENU); + gtk_box_pack_start (GTK_BOX (hbox), pinned_icon, FALSE, FALSE, 0); + /* setup label */ label = gtk_label_new (NULL); gtk_label_set_width_chars (GTK_LABEL (label), 8); @@ -383,6 +410,7 @@ build_tab_label (NemoNotebook *nb, NemoWindowSlot *slot) g_object_set_data (G_OBJECT (hbox), "spinner", spinner); g_object_set_data (G_OBJECT (hbox), "icon", icon); g_object_set_data (G_OBJECT (hbox), "close-button", close_button); + g_object_set_data (G_OBJECT (hbox), "pinned-icon", pinned_icon); return hbox; } diff --git a/src/nemo-notebook.h b/src/nemo-notebook.h index ec6bcc4d5..8749cff6e 100644 --- a/src/nemo-notebook.h +++ b/src/nemo-notebook.h @@ -76,6 +76,8 @@ void nemo_notebook_sync_tab_label (NemoNotebook *nb, NemoWindowSlot *slot); void nemo_notebook_sync_loading (NemoNotebook *nb, NemoWindowSlot *slot); +void nemo_notebook_sync_pinned (NemoNotebook *nb, + NemoWindowSlot *slot); void nemo_notebook_reorder_child_relative (NemoNotebook *notebook, int page_num, diff --git a/src/nemo-window-slot.c b/src/nemo-window-slot.c index 232f31713..78acd7a5e 100644 --- a/src/nemo-window-slot.c +++ b/src/nemo-window-slot.c @@ -30,6 +30,7 @@ #include "nemo-desktop-window.h" #include "nemo-filter-bar.h" #include "nemo-floating-bar.h" +#include "nemo-notebook.h" #include "nemo-window-private.h" #include "nemo-window-manage-views.h" #include "nemo-window-types.h" @@ -617,6 +618,10 @@ nemo_window_slot_set_pinned (NemoWindowSlot *slot, } slot->pinned = pinned && slot->pinned_uri != NULL; + + if (slot->pane != NULL && slot->pane->notebook != NULL) { + nemo_notebook_sync_pinned (NEMO_NOTEBOOK (slot->pane->notebook), slot); + } } gboolean From 30e62208283f59eddf71dfe285abe853fea7a498 Mon Sep 17 00:00:00 2001 From: Dawid Zyla Date: Mon, 6 Jul 2026 17:59:53 -0600 Subject: [PATCH 3/9] window-pane: Add Lock Tab menu item and guard close paths for pinned tabs --- src/nemo-window-menus.c | 4 ++++ src/nemo-window-pane.c | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/nemo-window-menus.c b/src/nemo-window-menus.c index 46f10b8e6..5ff99c645 100644 --- a/src/nemo-window-menus.c +++ b/src/nemo-window-menus.c @@ -98,6 +98,10 @@ action_close_window_slot_callback (GtkAction *action, window = NEMO_WINDOW (user_data); slot = nemo_window_get_active_slot (window); + if (nemo_window_slot_get_pinned (slot)) { + return; + } + nemo_window_pane_close_slot (slot->pane, slot); } diff --git a/src/nemo-window-pane.c b/src/nemo-window-pane.c index a7841b002..78bd024ef 100644 --- a/src/nemo-window-pane.c +++ b/src/nemo-window-pane.c @@ -473,6 +473,10 @@ notebook_tab_close_requested (NemoNotebook *notebook, NemoWindowSlot *slot, NemoWindowPane *pane) { + if (nemo_window_slot_get_pinned (slot)) { + return; + } + nemo_window_pane_close_slot (pane, slot); } @@ -496,6 +500,28 @@ notebook_popup_menu_close_cb (GtkMenuItem *menuitem, notebook, NEMO_WINDOW_SLOT (page), pane); } +static void +notebook_popup_menu_pin_cb (GtkCheckMenuItem *menuitem, + gpointer user_data) +{ + NemoWindowPane *pane; + int num_target_tab; + GtkWidget *page; + NemoWindowSlot *slot; + + pane = NEMO_WINDOW_PANE (user_data); + num_target_tab = GPOINTER_TO_INT ( + g_object_get_data (G_OBJECT (pane), "num_target_tab")); + page = gtk_notebook_get_nth_page ( + GTK_NOTEBOOK (pane->notebook), num_target_tab); + if (page == NULL) { + return; + } + + slot = NEMO_WINDOW_SLOT (page); + nemo_window_slot_set_pinned (slot, !nemo_window_slot_get_pinned (slot), NULL); +} + static void notebook_popup_menu_show (NemoWindowPane *pane, GdkEventButton *event, @@ -507,6 +533,8 @@ notebook_popup_menu_show (NemoWindowPane *pane, int button, event_time; gboolean can_move_left, can_move_right; NemoNotebook *notebook; + GtkWidget *page; + gboolean target_is_pinned; notebook = NEMO_NOTEBOOK (pane->notebook); @@ -550,6 +578,21 @@ notebook_popup_menu_show (NemoWindowPane *pane, item); gtk_widget_set_sensitive (item, can_move_right); + page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), num_target_tab); + target_is_pinned = page != NULL && + nemo_window_slot_get_pinned (NEMO_WINDOW_SLOT (page)); + + gtk_menu_shell_append (GTK_MENU_SHELL (popup), + gtk_separator_menu_item_new ()); + + item = gtk_check_menu_item_new_with_mnemonic (_("Loc_k Tab")); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), target_is_pinned); + g_signal_connect (item, "toggled", + G_CALLBACK (notebook_popup_menu_pin_cb), + pane); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), + item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), gtk_separator_menu_item_new ()); @@ -560,6 +603,7 @@ notebook_popup_menu_show (NemoWindowPane *pane, G_CALLBACK (notebook_popup_menu_close_cb), pane); gtk_menu_shell_append (GTK_MENU_SHELL (popup), item); + gtk_widget_set_sensitive (item, !target_is_pinned); gtk_widget_show_all (popup); From 901f12bfd492621233212395ffcff7923fac27f2 Mon Sep 17 00:00:00 2001 From: Dawid Zyla Date: Mon, 6 Jul 2026 18:04:26 -0600 Subject: [PATCH 4/9] window: Persist tab lock state in saved session (a(sb) tabs keys) Co-Authored-By: Claude Fable 5 --- libnemo-private/org.nemo.gschema.xml | 12 +-- src/nemo-window.c | 108 +++++++++++++++------------ 2 files changed, 67 insertions(+), 53 deletions(-) diff --git a/libnemo-private/org.nemo.gschema.xml b/libnemo-private/org.nemo.gschema.xml index abc2814d4..d58c491fe 100644 --- a/libnemo-private/org.nemo.gschema.xml +++ b/libnemo-private/org.nemo.gschema.xml @@ -714,15 +714,15 @@ Whether split view was enabled when the last window was closed Internal setting used to restore the last closed window's split view and tabs. - + [] - Saved tab URIs for the left pane - Internal setting used to restore the last closed window's tabs for the left pane. + Saved tabs for the left pane + Internal setting used to restore the last closed window's tabs for the left pane. Each entry is a (uri, locked) pair; locked tabs are restored pinned at the saved location. - + [] - Saved tab URIs for the right pane - Internal setting used to restore the last closed window's tabs for the right pane. + Saved tabs for the right pane + Internal setting used to restore the last closed window's tabs for the right pane. Each entry is a (uri, locked) pair; locked tabs are restored pinned at the saved location. 0 diff --git a/src/nemo-window.c b/src/nemo-window.c index 51304a1f9..9b038c103 100644 --- a/src/nemo-window.c +++ b/src/nemo-window.c @@ -2050,34 +2050,35 @@ uri_is_native_session_uri (const char *uri) return is_native; } -static char ** -collect_pane_saved_tab_uris (NemoWindowPane *pane, gint *active_index_out) +static GVariant * +collect_pane_saved_tabs (NemoWindowPane *pane, gint *active_index_out) { GtkNotebook *notebook; + GVariantBuilder builder; int n_pages, i; int current_page; int saved_index = 0; int saved_active_index = 0; - GPtrArray *arr; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sb)")); if (active_index_out != NULL) { *active_index_out = 0; } if (pane == NULL || pane->notebook == NULL) { - return g_new0 (char *, 1); + return g_variant_builder_end (&builder); } notebook = GTK_NOTEBOOK (pane->notebook); n_pages = gtk_notebook_get_n_pages (notebook); current_page = gtk_notebook_get_current_page (notebook); - arr = g_ptr_array_new_with_free_func (g_free); - for (i = 0; i < n_pages; i++) { GtkWidget *page; NemoWindowSlot *slot; char *uri; + gboolean pinned; page = gtk_notebook_get_nth_page (notebook, i); if (page == NULL) { @@ -2085,26 +2086,34 @@ collect_pane_saved_tab_uris (NemoWindowPane *pane, gint *active_index_out) } slot = NEMO_WINDOW_SLOT (page); - uri = nemo_window_slot_get_location_uri (slot); + pinned = nemo_window_slot_get_pinned (slot); + + /* Pinned tabs are restored at their pinned home, not at the + * last visited location. + */ + if (pinned && slot->pinned_uri != NULL) { + uri = g_strdup (slot->pinned_uri); + } else { + uri = nemo_window_slot_get_location_uri (slot); + pinned = FALSE; + } if (uri_is_native_session_uri (uri)) { if (i == current_page) { saved_active_index = saved_index; } - g_ptr_array_add (arr, uri); + g_variant_builder_add (&builder, "(sb)", uri, pinned); saved_index++; - } else { - g_free (uri); } - } - g_ptr_array_add (arr, NULL); + g_free (uri); + } if (active_index_out != NULL) { *active_index_out = saved_active_index; } - return (char **) g_ptr_array_free (arr, FALSE); + return g_variant_builder_end (&builder); } void @@ -2112,8 +2121,8 @@ nemo_window_save_session_state (NemoWindow *window) { NemoWindowPane *left_pane; NemoWindowPane *right_pane; - char **left_uris; - char **right_uris; + GVariant *left_tabs; + GVariant *right_tabs; gint left_active = 0; gint right_active = 0; gboolean split_view; @@ -2135,18 +2144,15 @@ nemo_window_save_session_state (NemoWindow *window) left_pane = child1 != NULL ? NEMO_WINDOW_PANE (child1) : NULL; right_pane = child2 != NULL ? NEMO_WINDOW_PANE (child2) : NULL; - left_uris = collect_pane_saved_tab_uris (left_pane, &left_active); - right_uris = collect_pane_saved_tab_uris (right_pane, &right_active); + left_tabs = collect_pane_saved_tabs (left_pane, &left_active); + right_tabs = collect_pane_saved_tabs (right_pane, &right_active); split_view = (right_pane != NULL); g_settings_set_boolean (nemo_window_state, NEMO_WINDOW_STATE_SAVED_SPLIT_VIEW, split_view); - g_settings_set_strv (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TABS_LEFT, (const gchar * const *) left_uris); - g_settings_set_strv (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TABS_RIGHT, (const gchar * const *) right_uris); + g_settings_set_value (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TABS_LEFT, left_tabs); + g_settings_set_value (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TABS_RIGHT, right_tabs); g_settings_set_int (nemo_window_state, NEMO_WINDOW_STATE_SAVED_ACTIVE_TAB_LEFT, left_active); g_settings_set_int (nemo_window_state, NEMO_WINDOW_STATE_SAVED_ACTIVE_TAB_RIGHT, right_active); - - g_strfreev (left_uris); - g_strfreev (right_uris); } static void @@ -2184,28 +2190,30 @@ clear_pane_to_single_slot (NemoWindowPane *pane) } static void -open_uri_list_in_pane (NemoWindowPane *pane, char **uris) +open_saved_tabs_in_pane (NemoWindowPane *pane, GVariant *tabs) { - int i; + gsize i, n; + gsize opened = 0; - if (pane == NULL) { + if (pane == NULL || tabs == NULL) { return; } - /* If no URIs were saved for this pane, leave its first tab alone */ - if (uris == NULL || uris[0] == NULL) { - return; - } + n = g_variant_n_children (tabs); - for (i = 0; uris[i] != NULL; i++) { + for (i = 0; i < n; i++) { + const gchar *uri; + gboolean pinned; NemoWindowSlot *slot; GFile *location; - if (!uri_is_native_session_uri (uris[i])) { + g_variant_get_child (tabs, i, "(&sb)", &uri, &pinned); + + if (!uri_is_native_session_uri (uri)) { continue; } - if (i == 0) { + if (opened == 0) { /* Reuse the existing first tab */ slot = pane->active_slot; if (slot == NULL && pane->notebook != NULL) { @@ -2222,9 +2230,15 @@ open_uri_list_in_pane (NemoWindowPane *pane, char **uris) continue; } - location = g_file_new_for_uri (uris[i]); + location = g_file_new_for_uri (uri); nemo_window_slot_open_location (slot, location, 0); g_object_unref (location); + + if (pinned) { + nemo_window_slot_set_pinned (slot, TRUE, uri); + } + + opened++; } } @@ -2233,8 +2247,8 @@ nemo_window_restore_saved_tabs (NemoWindow *window) { NemoWindowPane *left_pane; NemoWindowPane *right_pane; - char **left_uris; - char **right_uris; + GVariant *left_tabs; + GVariant *right_tabs; gint left_active; gint right_active; gboolean want_split; @@ -2247,21 +2261,21 @@ nemo_window_restore_saved_tabs (NemoWindow *window) return FALSE; } - left_uris = g_settings_get_strv (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TABS_LEFT); - right_uris = g_settings_get_strv (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TABS_RIGHT); + left_tabs = g_settings_get_value (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TABS_LEFT); + right_tabs = g_settings_get_value (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TABS_RIGHT); left_active = g_settings_get_int (nemo_window_state, NEMO_WINDOW_STATE_SAVED_ACTIVE_TAB_LEFT); right_active = g_settings_get_int (nemo_window_state, NEMO_WINDOW_STATE_SAVED_ACTIVE_TAB_RIGHT); saved_split = g_settings_get_boolean (nemo_window_state, NEMO_WINDOW_STATE_SAVED_SPLIT_VIEW); - if ((left_uris == NULL || left_uris[0] == NULL) && - (right_uris == NULL || right_uris[0] == NULL)) { - g_strfreev (left_uris); - g_strfreev (right_uris); + if (g_variant_n_children (left_tabs) == 0 && + g_variant_n_children (right_tabs) == 0) { + g_variant_unref (left_tabs); + g_variant_unref (right_tabs); return FALSE; } /* Only create the extra pane if we actually have tabs to restore there */ - want_split = saved_split && (right_uris != NULL && right_uris[0] != NULL); + want_split = saved_split && g_variant_n_children (right_tabs) > 0; if (want_split && !nemo_window_split_view_showing (window)) { nemo_window_split_view_on (window); @@ -2283,17 +2297,17 @@ nemo_window_restore_saved_tabs (NemoWindow *window) clear_pane_to_single_slot (right_pane); /* If nothing saved for the left pane, open Home as a minimal fallback */ - if (left_uris == NULL || left_uris[0] == NULL) { + if (g_variant_n_children (left_tabs) == 0) { GFile *home = g_file_new_for_path (g_get_home_dir ()); if (left_pane != NULL && left_pane->active_slot != NULL) { nemo_window_slot_open_location (left_pane->active_slot, home, 0); } g_object_unref (home); } else { - open_uri_list_in_pane (left_pane, left_uris); + open_saved_tabs_in_pane (left_pane, left_tabs); } - open_uri_list_in_pane (right_pane, right_uris); + open_saved_tabs_in_pane (right_pane, right_tabs); /* Restore active tabs (clamp indices) */ if (left_pane != NULL && left_pane->notebook != NULL) { @@ -2317,8 +2331,8 @@ nemo_window_restore_saved_tabs (NemoWindow *window) nemo_window_set_active_pane (window, left_pane); } - g_strfreev (left_uris); - g_strfreev (right_uris); + g_variant_unref (left_tabs); + g_variant_unref (right_tabs); return TRUE; } From 6ed2207098f90fca7d84e68daaee9723e135e5c4 Mon Sep 17 00:00:00 2001 From: Dawid Zyla Date: Wed, 8 Jul 2026 19:47:30 -0600 Subject: [PATCH 5/9] window-slot: Replace pinned flag with three-valued tab lock mode A locked tab previously only refused to close and reopened at its locked folder on restart; nothing returned it there at runtime, so a locked tab that had been navigated away stayed away. Replace the pinned boolean with NemoTabLockMode: NONE navigate freely, keep the last folder (default) RETURN navigate freely, return to the locked folder when reselected NEW_TAB never navigate, divert folder changes into a new unlocked tab Both locked modes keep the existing close protection. Enforcement sits in nemo_window_slot_open_location_full(), which every navigation call site routes through, plus nemo_window_back_or_forward(), which reaches begin_location_change() directly when flags are 0. The RETURN snap-back hangs off ::switch-page, deferred to an idle so it does not navigate while the notebook is still switching. Search is exempt from the NEW_TAB diversion: sync_search_location_cb() asserts the originating slot holds a search directory, so sending the search to another tab would abort. The tab context menu grows an "Open Changes in New Tab" check item, sensitive only while the tab is locked. Saved sessions change from a(sb) to a(si), each entry being (uri, lock mode). All tabs are saved; locked ones save their locked folder. Sessions written by the earlier a(sb) build are dropped once on type mismatch. Co-Authored-By: Claude Opus 4.8 --- libnemo-private/org.nemo.gschema.xml | 8 +-- src/nemo-notebook.c | 32 ++++++--- src/nemo-notebook.h | 2 +- src/nemo-window-manage-views.c | 23 +++++- src/nemo-window-menus.c | 2 +- src/nemo-window-pane.c | 104 +++++++++++++++++++++++---- src/nemo-window-slot.c | 87 +++++++++++++++++----- src/nemo-window-slot.h | 29 +++++--- src/nemo-window.c | 33 +++++---- 9 files changed, 250 insertions(+), 70 deletions(-) diff --git a/libnemo-private/org.nemo.gschema.xml b/libnemo-private/org.nemo.gschema.xml index d58c491fe..600af2769 100644 --- a/libnemo-private/org.nemo.gschema.xml +++ b/libnemo-private/org.nemo.gschema.xml @@ -714,15 +714,15 @@ Whether split view was enabled when the last window was closed Internal setting used to restore the last closed window's split view and tabs. - + [] Saved tabs for the left pane - Internal setting used to restore the last closed window's tabs for the left pane. Each entry is a (uri, locked) pair; locked tabs are restored pinned at the saved location. + Internal setting used to restore the last closed window's tabs for the left pane. Each entry is a (uri, lock mode) pair, where the lock mode is 0 for an unlocked tab, 1 for a tab that returns to its locked folder when reselected, and 2 for a tab whose folder changes open in a new tab. Locked tabs are restored at their locked folder. - + [] Saved tabs for the right pane - Internal setting used to restore the last closed window's tabs for the right pane. Each entry is a (uri, locked) pair; locked tabs are restored pinned at the saved location. + Internal setting used to restore the last closed window's tabs for the right pane. Each entry is a (uri, lock mode) pair, where the lock mode is 0 for an unlocked tab, 1 for a tab that returns to its locked folder when reselected, and 2 for a tab whose folder changes open in a new tab. Locked tabs are restored at their locked folder. 0 diff --git a/src/nemo-notebook.c b/src/nemo-notebook.c index 3e6d40d31..09098ab0c 100644 --- a/src/nemo-notebook.c +++ b/src/nemo-notebook.c @@ -317,10 +317,11 @@ nemo_notebook_sync_tab_label (NemoNotebook *notebook, } void -nemo_notebook_sync_pinned (NemoNotebook *notebook, - NemoWindowSlot *slot) +nemo_notebook_sync_lock (NemoNotebook *notebook, + NemoWindowSlot *slot) { - GtkWidget *tab_label, *pinned_icon, *close_button; + GtkWidget *tab_label, *lock_icon, *close_button; + gboolean locked; g_return_if_fail (NEMO_IS_NOTEBOOK (notebook)); g_return_if_fail (NEMO_IS_WINDOW_SLOT (slot)); @@ -331,12 +332,21 @@ nemo_notebook_sync_pinned (NemoNotebook *notebook, return; } - pinned_icon = GTK_WIDGET (g_object_get_data (G_OBJECT (tab_label), "pinned-icon")); + lock_icon = GTK_WIDGET (g_object_get_data (G_OBJECT (tab_label), "lock-icon")); close_button = GTK_WIDGET (g_object_get_data (G_OBJECT (tab_label), "close-button")); - g_return_if_fail (pinned_icon != NULL && close_button != NULL); + g_return_if_fail (lock_icon != NULL && close_button != NULL); + + locked = nemo_window_slot_is_locked (slot); + + if (locked) { + gtk_widget_set_tooltip_text (lock_icon, + slot->lock_mode == NEMO_TAB_LOCK_NEW_TAB ? + _("Locked: folder changes open in a new tab") : + _("Locked: returns to this folder when reselected")); + } - gtk_widget_set_visible (pinned_icon, slot->pinned); - gtk_widget_set_visible (close_button, !slot->pinned); + gtk_widget_set_visible (lock_icon, locked); + gtk_widget_set_visible (close_button, !locked); } static void @@ -354,7 +364,7 @@ close_button_clicked_cb (GtkWidget *widget, static GtkWidget * build_tab_label (NemoNotebook *nb, NemoWindowSlot *slot) { - GtkWidget *hbox, *label, *close_button, *image, *spinner, *icon, *pinned_icon; + GtkWidget *hbox, *label, *close_button, *image, *spinner, *icon, *lock_icon; /* set hbox spacing and label padding (see below) so that there's an * equal amount of space around the label */ @@ -371,8 +381,8 @@ build_tab_label (NemoNotebook *nb, NemoWindowSlot *slot) /* don't show the icon */ /* setup padlock icon for pinned (locked) tabs, hidden by default */ - pinned_icon = gtk_image_new_from_icon_name ("changes-prevent-symbolic", GTK_ICON_SIZE_MENU); - gtk_box_pack_start (GTK_BOX (hbox), pinned_icon, FALSE, FALSE, 0); + lock_icon = gtk_image_new_from_icon_name ("changes-prevent-symbolic", GTK_ICON_SIZE_MENU); + gtk_box_pack_start (GTK_BOX (hbox), lock_icon, FALSE, FALSE, 0); /* setup label */ label = gtk_label_new (NULL); @@ -410,7 +420,7 @@ build_tab_label (NemoNotebook *nb, NemoWindowSlot *slot) g_object_set_data (G_OBJECT (hbox), "spinner", spinner); g_object_set_data (G_OBJECT (hbox), "icon", icon); g_object_set_data (G_OBJECT (hbox), "close-button", close_button); - g_object_set_data (G_OBJECT (hbox), "pinned-icon", pinned_icon); + g_object_set_data (G_OBJECT (hbox), "lock-icon", lock_icon); return hbox; } diff --git a/src/nemo-notebook.h b/src/nemo-notebook.h index 8749cff6e..494364dec 100644 --- a/src/nemo-notebook.h +++ b/src/nemo-notebook.h @@ -76,7 +76,7 @@ void nemo_notebook_sync_tab_label (NemoNotebook *nb, NemoWindowSlot *slot); void nemo_notebook_sync_loading (NemoNotebook *nb, NemoWindowSlot *slot); -void nemo_notebook_sync_pinned (NemoNotebook *nb, +void nemo_notebook_sync_lock (NemoNotebook *nb, NemoWindowSlot *slot); void nemo_notebook_reorder_child_relative (NemoNotebook *notebook, diff --git a/src/nemo-window-manage-views.c b/src/nemo-window-manage-views.c index 96c2e575a..0d3cc53fd 100644 --- a/src/nemo-window-manage-views.c +++ b/src/nemo-window-manage-views.c @@ -476,6 +476,23 @@ nemo_window_slot_open_location_full (NemoWindowSlot *slot, } } else { use_same |= g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_ALWAYS_USE_BROWSER); + + /* A tab locked in "new tab" mode never leaves its folder: divert the + * new location into a fresh, unlocked tab and leave this one alone. + * + * Search is exempt. It is a view of the locked folder rather than a + * move away from it, and its completion callback assumes the search + * landed in the slot that started it. + */ + if (nemo_window_slot_get_lock_mode (slot) == NEMO_TAB_LOCK_NEW_TAB && + (flags & (NEMO_WINDOW_OPEN_FLAG_NEW_TAB | + NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW | + NEMO_WINDOW_OPEN_FLAG_SEARCH)) == 0 && + slot->location != NULL && + !g_file_equal (slot->location, location)) { + flags |= NEMO_WINDOW_OPEN_FLAG_NEW_TAB; + use_same = TRUE; + } } g_assert (!((flags & NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW) != 0 && @@ -1904,7 +1921,11 @@ nemo_window_back_or_forward (NemoWindow *window, bookmark = g_list_nth_data (list, distance); location = nemo_bookmark_get_location (bookmark); - if (flags != 0) { + /* Route a "new tab" locked slot through open_location, whose guard turns + * the history step into a new tab rather than moving the locked one. + */ + if (flags != 0 || + nemo_window_slot_get_lock_mode (slot) == NEMO_TAB_LOCK_NEW_TAB) { nemo_window_slot_open_location (slot, location, flags); } else { char *scroll_pos; diff --git a/src/nemo-window-menus.c b/src/nemo-window-menus.c index 5ff99c645..0c9641704 100644 --- a/src/nemo-window-menus.c +++ b/src/nemo-window-menus.c @@ -98,7 +98,7 @@ action_close_window_slot_callback (GtkAction *action, window = NEMO_WINDOW (user_data); slot = nemo_window_get_active_slot (window); - if (nemo_window_slot_get_pinned (slot)) { + if (nemo_window_slot_is_locked (slot)) { return; } diff --git a/src/nemo-window-pane.c b/src/nemo-window-pane.c index 78bd024ef..aa7a801de 100644 --- a/src/nemo-window-pane.c +++ b/src/nemo-window-pane.c @@ -473,7 +473,7 @@ notebook_tab_close_requested (NemoNotebook *notebook, NemoWindowSlot *slot, NemoWindowPane *pane) { - if (nemo_window_slot_get_pinned (slot)) { + if (nemo_window_slot_is_locked (slot)) { return; } @@ -500,26 +500,60 @@ notebook_popup_menu_close_cb (GtkMenuItem *menuitem, notebook, NEMO_WINDOW_SLOT (page), pane); } -static void -notebook_popup_menu_pin_cb (GtkCheckMenuItem *menuitem, - gpointer user_data) +static NemoWindowSlot * +notebook_popup_menu_target_slot (NemoWindowPane *pane) { - NemoWindowPane *pane; int num_target_tab; GtkWidget *page; - NemoWindowSlot *slot; - pane = NEMO_WINDOW_PANE (user_data); num_target_tab = GPOINTER_TO_INT ( g_object_get_data (G_OBJECT (pane), "num_target_tab")); page = gtk_notebook_get_nth_page ( GTK_NOTEBOOK (pane->notebook), num_target_tab); if (page == NULL) { + return NULL; + } + + return NEMO_WINDOW_SLOT (page); +} + +static void +notebook_popup_menu_lock_cb (GtkCheckMenuItem *menuitem, + gpointer user_data) +{ + NemoWindowSlot *slot; + + slot = notebook_popup_menu_target_slot (NEMO_WINDOW_PANE (user_data)); + if (slot == NULL) { return; } - slot = NEMO_WINDOW_SLOT (page); - nemo_window_slot_set_pinned (slot, !nemo_window_slot_get_pinned (slot), NULL); + /* Locking anchors the tab at the folder it is showing now. */ + if (nemo_window_slot_is_locked (slot)) { + nemo_window_slot_set_lock_mode (slot, NEMO_TAB_LOCK_NONE, NULL); + } else { + nemo_window_slot_set_lock_mode (slot, NEMO_TAB_LOCK_RETURN, NULL); + } +} + +static void +notebook_popup_menu_lock_new_tab_cb (GtkCheckMenuItem *menuitem, + gpointer user_data) +{ + NemoWindowSlot *slot; + NemoTabLockMode mode; + + slot = notebook_popup_menu_target_slot (NEMO_WINDOW_PANE (user_data)); + if (slot == NULL || !nemo_window_slot_is_locked (slot)) { + return; + } + + mode = gtk_check_menu_item_get_active (menuitem) ? + NEMO_TAB_LOCK_NEW_TAB : NEMO_TAB_LOCK_RETURN; + + /* Keep the existing locked folder; only the mode changes. */ + nemo_window_slot_set_lock_mode (slot, mode, + nemo_window_slot_get_locked_uri (slot)); } static void @@ -534,7 +568,8 @@ notebook_popup_menu_show (NemoWindowPane *pane, gboolean can_move_left, can_move_right; NemoNotebook *notebook; GtkWidget *page; - gboolean target_is_pinned; + NemoTabLockMode target_lock_mode; + gboolean target_is_locked; notebook = NEMO_NOTEBOOK (pane->notebook); @@ -579,19 +614,34 @@ notebook_popup_menu_show (NemoWindowPane *pane, gtk_widget_set_sensitive (item, can_move_right); page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), num_target_tab); - target_is_pinned = page != NULL && - nemo_window_slot_get_pinned (NEMO_WINDOW_SLOT (page)); + target_lock_mode = page != NULL ? + nemo_window_slot_get_lock_mode (NEMO_WINDOW_SLOT (page)) : + NEMO_TAB_LOCK_NONE; + target_is_locked = target_lock_mode != NEMO_TAB_LOCK_NONE; gtk_menu_shell_append (GTK_MENU_SHELL (popup), gtk_separator_menu_item_new ()); + /* Set the state before connecting, so priming the check items does not + * fire "toggled" and flip the slot we are describing. + */ item = gtk_check_menu_item_new_with_mnemonic (_("Loc_k Tab")); - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), target_is_pinned); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), target_is_locked); + g_signal_connect (item, "toggled", + G_CALLBACK (notebook_popup_menu_lock_cb), + pane); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), + item); + + item = gtk_check_menu_item_new_with_mnemonic (_("Open Changes in New _Tab")); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), + target_lock_mode == NEMO_TAB_LOCK_NEW_TAB); g_signal_connect (item, "toggled", - G_CALLBACK (notebook_popup_menu_pin_cb), + G_CALLBACK (notebook_popup_menu_lock_new_tab_cb), pane); gtk_menu_shell_append (GTK_MENU_SHELL (popup), item); + gtk_widget_set_sensitive (item, target_is_locked); gtk_menu_shell_append (GTK_MENU_SHELL (popup), gtk_separator_menu_item_new ()); @@ -603,7 +653,7 @@ notebook_popup_menu_show (NemoWindowPane *pane, G_CALLBACK (notebook_popup_menu_close_cb), pane); gtk_menu_shell_append (GTK_MENU_SHELL (popup), item); - gtk_widget_set_sensitive (item, !target_is_pinned); + gtk_widget_set_sensitive (item, !target_is_locked); gtk_widget_show_all (popup); @@ -683,6 +733,19 @@ notebook_popup_menu_cb (GtkWidget *widget, return TRUE; } +static gboolean +return_locked_slot_idle_cb (gpointer user_data) +{ + NemoWindowSlot *slot = NEMO_WINDOW_SLOT (user_data); + + /* The tab may have been destroyed between the switch and this idle. */ + if (slot->pane != NULL) { + nemo_window_slot_return_to_locked_uri (slot); + } + + return G_SOURCE_REMOVE; +} + static gboolean notebook_switch_page_cb (GtkNotebook *notebook, GtkWidget *page, @@ -702,6 +765,17 @@ notebook_switch_page_cb (GtkNotebook *notebook, nemo_window_set_active_slot (nemo_window_slot_get_window (slot), slot); + /* A tab locked in "return" mode goes back to its locked folder whenever + * it is reselected. Deferred, because navigating from inside + * ::switch-page re-enters the notebook while it is still switching. + */ + if (nemo_window_slot_get_lock_mode (slot) == NEMO_TAB_LOCK_RETURN) { + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + return_locked_slot_idle_cb, + g_object_ref (slot), + g_object_unref); + } + return FALSE; } diff --git a/src/nemo-window-slot.c b/src/nemo-window-slot.c index 78acd7a5e..90bfc523f 100644 --- a/src/nemo-window-slot.c +++ b/src/nemo-window-slot.c @@ -520,8 +520,8 @@ nemo_window_slot_dispose (GObject *object) g_free (slot->status_text); slot->status_text = NULL; - g_free (slot->pinned_uri); - slot->pinned_uri = NULL; + g_free (slot->locked_uri); + slot->locked_uri = NULL; G_OBJECT_CLASS (nemo_window_slot_parent_class)->dispose (object); } @@ -598,38 +598,91 @@ nemo_window_slot_get_location_uri (NemoWindowSlot *slot) } void -nemo_window_slot_set_pinned (NemoWindowSlot *slot, - gboolean pinned, - const char *pinned_uri) +nemo_window_slot_set_lock_mode (NemoWindowSlot *slot, + NemoTabLockMode mode, + const char *locked_uri) { - g_return_if_fail (NEMO_IS_WINDOW_SLOT (slot)); + char *new_locked_uri = NULL; - g_free (slot->pinned_uri); - slot->pinned_uri = NULL; + g_return_if_fail (NEMO_IS_WINDOW_SLOT (slot)); - if (pinned) { - if (pinned_uri != NULL) { - slot->pinned_uri = g_strdup (pinned_uri); + /* Resolve before freeing: callers switching between lock modes pass the + * slot's own locked_uri back in to preserve it. + */ + if (mode != NEMO_TAB_LOCK_NONE) { + if (locked_uri != NULL) { + new_locked_uri = g_strdup (locked_uri); } else if (slot->pending_location != NULL) { - slot->pinned_uri = g_file_get_uri (slot->pending_location); + new_locked_uri = g_file_get_uri (slot->pending_location); } else { - slot->pinned_uri = nemo_window_slot_get_location_uri (slot); + new_locked_uri = nemo_window_slot_get_location_uri (slot); } } - slot->pinned = pinned && slot->pinned_uri != NULL; + g_free (slot->locked_uri); + slot->locked_uri = new_locked_uri; + + /* A tab with no folder to return to cannot meaningfully be locked. */ + slot->lock_mode = (slot->locked_uri != NULL) ? mode : NEMO_TAB_LOCK_NONE; if (slot->pane != NULL && slot->pane->notebook != NULL) { - nemo_notebook_sync_pinned (NEMO_NOTEBOOK (slot->pane->notebook), slot); + nemo_notebook_sync_lock (NEMO_NOTEBOOK (slot->pane->notebook), slot); } } +NemoTabLockMode +nemo_window_slot_get_lock_mode (NemoWindowSlot *slot) +{ + g_return_val_if_fail (NEMO_IS_WINDOW_SLOT (slot), NEMO_TAB_LOCK_NONE); + + return slot->lock_mode; +} + +const char * +nemo_window_slot_get_locked_uri (NemoWindowSlot *slot) +{ + g_return_val_if_fail (NEMO_IS_WINDOW_SLOT (slot), NULL); + + return slot->locked_uri; +} + gboolean -nemo_window_slot_get_pinned (NemoWindowSlot *slot) +nemo_window_slot_is_locked (NemoWindowSlot *slot) { g_return_val_if_fail (NEMO_IS_WINDOW_SLOT (slot), FALSE); - return slot->pinned; + return slot->lock_mode != NEMO_TAB_LOCK_NONE; +} + +/* Send a NEMO_TAB_LOCK_RETURN tab back to the folder it was locked at, if it + * has wandered. Called when the tab is reactivated. + */ +void +nemo_window_slot_return_to_locked_uri (NemoWindowSlot *slot) +{ + GFile *locked_location; + + g_return_if_fail (NEMO_IS_WINDOW_SLOT (slot)); + + if (slot->lock_mode != NEMO_TAB_LOCK_RETURN || slot->locked_uri == NULL) { + return; + } + + locked_location = g_file_new_for_uri (slot->locked_uri); + + /* pending_location is the authoritative target while a load is in + * flight; without this check a snap-back races an in-progress change. + */ + if (slot->pending_location != NULL) { + if (!g_file_equal (slot->pending_location, locked_location)) { + nemo_window_slot_open_location (slot, locked_location, 0); + } + } else if (slot->location == NULL || + !g_file_equal (slot->location, locked_location)) { + nemo_window_slot_open_location (slot, locked_location, 0); + } + + g_object_unref (locked_location); } void diff --git a/src/nemo-window-slot.h b/src/nemo-window-slot.h index 16ebcfe3e..44befcb6a 100644 --- a/src/nemo-window-slot.h +++ b/src/nemo-window-slot.h @@ -43,6 +43,15 @@ typedef enum { NEMO_LOCATION_CHANGE_RELOAD } NemoLocationChangeType; +/* How a tab is tied to its locked folder. A tab in any mode other than + * NEMO_TAB_LOCK_NONE cannot be closed from the UI. + */ +typedef enum { + NEMO_TAB_LOCK_NONE = 0, /* navigate freely, keep the last folder */ + NEMO_TAB_LOCK_RETURN = 1, /* navigate freely, return home when reactivated */ + NEMO_TAB_LOCK_NEW_TAB = 2 /* never navigate, divert changes to a new tab */ +} NemoTabLockMode; + struct NemoWindowSlotClass { GtkBoxClass parent_class; @@ -121,11 +130,11 @@ struct NemoWindowSlot { gboolean visible; - /* Tab locking (Total Commander style): a pinned tab cannot be closed - * from the UI and is restored at pinned_uri on startup. + /* Tab locking (Total Commander style). locked_uri is the folder the tab + * was locked at, and is NULL exactly when lock_mode is NEMO_TAB_LOCK_NONE. */ - gboolean pinned; - char *pinned_uri; + NemoTabLockMode lock_mode; + char *locked_uri; /* Back/Forward chain, and history list. * The data in these lists are NemoBookmark pointers. @@ -145,10 +154,14 @@ void nemo_window_slot_set_query_editor_visible (NemoWindowSlot *slot, GFile * nemo_window_slot_get_location (NemoWindowSlot *slot); char * nemo_window_slot_get_location_uri (NemoWindowSlot *slot); -void nemo_window_slot_set_pinned (NemoWindowSlot *slot, - gboolean pinned, - const char *pinned_uri); -gboolean nemo_window_slot_get_pinned (NemoWindowSlot *slot); +/* Passing NULL for locked_uri locks the tab at its current folder. */ +void nemo_window_slot_set_lock_mode (NemoWindowSlot *slot, + NemoTabLockMode mode, + const char *locked_uri); +NemoTabLockMode nemo_window_slot_get_lock_mode (NemoWindowSlot *slot); +const char * nemo_window_slot_get_locked_uri (NemoWindowSlot *slot); +gboolean nemo_window_slot_is_locked (NemoWindowSlot *slot); +void nemo_window_slot_return_to_locked_uri (NemoWindowSlot *slot); void nemo_window_slot_queue_reload (NemoWindowSlot *slot, gboolean clear_thumbs); diff --git a/src/nemo-window.c b/src/nemo-window.c index 9b038c103..0df80c96e 100644 --- a/src/nemo-window.c +++ b/src/nemo-window.c @@ -2060,7 +2060,7 @@ collect_pane_saved_tabs (NemoWindowPane *pane, gint *active_index_out) int saved_index = 0; int saved_active_index = 0; - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sb)")); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(si)")); if (active_index_out != NULL) { *active_index_out = 0; @@ -2078,7 +2078,8 @@ collect_pane_saved_tabs (NemoWindowPane *pane, gint *active_index_out) GtkWidget *page; NemoWindowSlot *slot; char *uri; - gboolean pinned; + NemoTabLockMode lock_mode; + const char *locked_uri; page = gtk_notebook_get_nth_page (notebook, i); if (page == NULL) { @@ -2086,23 +2087,24 @@ collect_pane_saved_tabs (NemoWindowPane *pane, gint *active_index_out) } slot = NEMO_WINDOW_SLOT (page); - pinned = nemo_window_slot_get_pinned (slot); + lock_mode = nemo_window_slot_get_lock_mode (slot); + locked_uri = nemo_window_slot_get_locked_uri (slot); - /* Pinned tabs are restored at their pinned home, not at the + /* Locked tabs are restored at their locked folder, not at the * last visited location. */ - if (pinned && slot->pinned_uri != NULL) { - uri = g_strdup (slot->pinned_uri); + if (lock_mode != NEMO_TAB_LOCK_NONE && locked_uri != NULL) { + uri = g_strdup (locked_uri); } else { uri = nemo_window_slot_get_location_uri (slot); - pinned = FALSE; + lock_mode = NEMO_TAB_LOCK_NONE; } if (uri_is_native_session_uri (uri)) { if (i == current_page) { saved_active_index = saved_index; } - g_variant_builder_add (&builder, "(sb)", uri, pinned); + g_variant_builder_add (&builder, "(si)", uri, (gint32) lock_mode); saved_index++; } @@ -2203,16 +2205,20 @@ open_saved_tabs_in_pane (NemoWindowPane *pane, GVariant *tabs) for (i = 0; i < n; i++) { const gchar *uri; - gboolean pinned; + gint32 lock_mode; NemoWindowSlot *slot; GFile *location; - g_variant_get_child (tabs, i, "(&sb)", &uri, &pinned); + g_variant_get_child (tabs, i, "(&si)", &uri, &lock_mode); if (!uri_is_native_session_uri (uri)) { continue; } + if (lock_mode < NEMO_TAB_LOCK_NONE || lock_mode > NEMO_TAB_LOCK_NEW_TAB) { + lock_mode = NEMO_TAB_LOCK_NONE; + } + if (opened == 0) { /* Reuse the existing first tab */ slot = pane->active_slot; @@ -2234,8 +2240,11 @@ open_saved_tabs_in_pane (NemoWindowPane *pane, GVariant *tabs) nemo_window_slot_open_location (slot, location, 0); g_object_unref (location); - if (pinned) { - nemo_window_slot_set_pinned (slot, TRUE, uri); + /* After open_location, so a NEMO_TAB_LOCK_NEW_TAB slot does not + * divert its own restore into a second tab. + */ + if (lock_mode != NEMO_TAB_LOCK_NONE) { + nemo_window_slot_set_lock_mode (slot, lock_mode, uri); } opened++; From d1a39ceb614c99cee8fd62ee15e93041cbab2679 Mon Sep 17 00:00:00 2001 From: Dawid Zyla Date: Wed, 8 Jul 2026 21:09:48 -0600 Subject: [PATCH 6/9] window-slot: Return locked tabs home on slot activation, not tab switch The snap-back for NEMO_TAB_LOCK_RETURN hung off the notebook's ::switch-page, which only fires when a notebook's current page changes. A locked tab can stop being the one in use without that happening: switching to the other pane in split view, or being the only tab in its pane. In those cases the tab never went home, so a tab locked at ~ and navigated to ~/Downloads still showed Downloads on return. Move the hook to the slot's own "active" handler. nemo_window_set_active_slot() emits it for every way a slot becomes current, covering tab switches and pane switches alike. Hooking "inactive" instead would park the tab the moment it is left, but navigating a background slot makes its fresh view take focus - the icon and list views call nemo_window_slot_make_hosting_pane_active() from their focus-in handlers - which drags the user back to the tab they just left. Activation-side navigation touches only the slot already in focus. Co-Authored-By: Claude Opus 4.8 --- src/nemo-window-pane.c | 27 +++------------------------ src/nemo-window-slot.c | 32 +++++++++++++++++++++++++++++++- src/nemo-window-slot.h | 2 +- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/nemo-window-pane.c b/src/nemo-window-pane.c index aa7a801de..33ee3756e 100644 --- a/src/nemo-window-pane.c +++ b/src/nemo-window-pane.c @@ -733,19 +733,6 @@ notebook_popup_menu_cb (GtkWidget *widget, return TRUE; } -static gboolean -return_locked_slot_idle_cb (gpointer user_data) -{ - NemoWindowSlot *slot = NEMO_WINDOW_SLOT (user_data); - - /* The tab may have been destroyed between the switch and this idle. */ - if (slot->pane != NULL) { - nemo_window_slot_return_to_locked_uri (slot); - } - - return G_SOURCE_REMOVE; -} - static gboolean notebook_switch_page_cb (GtkNotebook *notebook, GtkWidget *page, @@ -762,20 +749,12 @@ notebook_switch_page_cb (GtkNotebook *notebook, slot = NEMO_WINDOW_SLOT (widget); g_assert (slot != NULL); + /* Sending a locked tab home is the slot's "inactive" handler's job; it + * also covers the tab losing focus to the other pane. + */ nemo_window_set_active_slot (nemo_window_slot_get_window (slot), slot); - /* A tab locked in "return" mode goes back to its locked folder whenever - * it is reselected. Deferred, because navigating from inside - * ::switch-page re-enters the notebook while it is still switching. - */ - if (nemo_window_slot_get_lock_mode (slot) == NEMO_TAB_LOCK_RETURN) { - g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, - return_locked_slot_idle_cb, - g_object_ref (slot), - g_object_unref); - } - return FALSE; } diff --git a/src/nemo-window-slot.c b/src/nemo-window-slot.c index 90bfc523f..09a5b0a37 100644 --- a/src/nemo-window-slot.c +++ b/src/nemo-window-slot.c @@ -233,6 +233,19 @@ nemo_window_slot_set_query_editor_visible (NemoWindowSlot *slot, } } +static gboolean +return_to_locked_uri_idle_cb (gpointer user_data) +{ + NemoWindowSlot *slot = NEMO_WINDOW_SLOT (user_data); + + /* The tab may have been destroyed between activation and this idle. */ + if (slot->pane != NULL) { + nemo_window_slot_return_to_locked_uri (slot); + } + + return G_SOURCE_REMOVE; +} + static void real_active (NemoWindowSlot *slot) { @@ -262,6 +275,23 @@ real_active (NemoWindowSlot *slot) nemo_window_sync_view_type (window); nemo_window_load_extension_menus (window); } + + /* A tab locked in "return" mode goes back to its locked folder whenever it + * becomes the slot in use again, whether that is a tab switch or a switch + * back to this pane. Deferred, because the caller is still part way through + * making this slot active, and ::switch-page is still in flight. + * + * This runs on activation rather than deactivation on purpose: navigating a + * background slot makes its fresh view take focus (focus_in_event_callback + * calls nemo_window_slot_make_hosting_pane_active), which would drag the + * user back to the tab they just left. + */ + if (slot->lock_mode == NEMO_TAB_LOCK_RETURN) { + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + return_to_locked_uri_idle_cb, + g_object_ref (slot), + g_object_unref); + } } static void @@ -655,7 +685,7 @@ nemo_window_slot_is_locked (NemoWindowSlot *slot) } /* Send a NEMO_TAB_LOCK_RETURN tab back to the folder it was locked at, if it - * has wandered. Called when the tab is reactivated. + * has wandered. Called when the tab becomes the slot in use again. */ void nemo_window_slot_return_to_locked_uri (NemoWindowSlot *slot) diff --git a/src/nemo-window-slot.h b/src/nemo-window-slot.h index 44befcb6a..d0b035cb9 100644 --- a/src/nemo-window-slot.h +++ b/src/nemo-window-slot.h @@ -48,7 +48,7 @@ typedef enum { */ typedef enum { NEMO_TAB_LOCK_NONE = 0, /* navigate freely, keep the last folder */ - NEMO_TAB_LOCK_RETURN = 1, /* navigate freely, return home when reactivated */ + NEMO_TAB_LOCK_RETURN = 1, /* navigate freely, return home when reselected */ NEMO_TAB_LOCK_NEW_TAB = 2 /* never navigate, divert changes to a new tab */ } NemoTabLockMode; From 292ae0fa6c60a8951ae2cd438790e02dc98e37a3 Mon Sep 17 00:00:00 2001 From: Dawid Zyla Date: Wed, 8 Jul 2026 21:40:06 -0600 Subject: [PATCH 7/9] window: Store tab lock modes beside the saved tabs, not inside them Commit 901f12b changed saved-tabs-left/right from "as" to "a(sb)", and 6ed22070 changed them again to "a(si)". Both retyped a key that already ships in master, and GSettings answers a type mismatch by discarding the stored value and handing back the default. Every tab a user had saved went silently missing on upgrade -- twice. The plan justified this as safe because the keys were "internal and unreleased", which was wrong on the second count: they landed upstream in PR #3661. Leave saved-tabs-left/right at "as" and carry the lock modes in new position-aligned "ai" keys, saved-tab-locks-left/right. A lock list may be shorter than its tab list -- anything it does not cover is unlocked -- so tabs written by a Nemo without tab locking restore unchanged, and a pane with nothing locked writes [] exactly as before. Trailing unlocked entries are trimmed on save to keep that the common case. Co-Authored-By: Claude Opus 4.8 --- libnemo-private/nemo-global-preferences.h | 2 + libnemo-private/org.nemo.gschema.xml | 22 +++- src/nemo-window.c | 154 ++++++++++++++-------- 3 files changed, 116 insertions(+), 62 deletions(-) diff --git a/libnemo-private/nemo-global-preferences.h b/libnemo-private/nemo-global-preferences.h index 91f404854..96b194978 100644 --- a/libnemo-private/nemo-global-preferences.h +++ b/libnemo-private/nemo-global-preferences.h @@ -126,6 +126,8 @@ typedef enum #define NEMO_WINDOW_STATE_SAVED_SPLIT_VIEW "saved-split-view" #define NEMO_WINDOW_STATE_SAVED_TABS_LEFT "saved-tabs-left" #define NEMO_WINDOW_STATE_SAVED_TABS_RIGHT "saved-tabs-right" +#define NEMO_WINDOW_STATE_SAVED_TAB_LOCKS_LEFT "saved-tab-locks-left" +#define NEMO_WINDOW_STATE_SAVED_TAB_LOCKS_RIGHT "saved-tab-locks-right" #define NEMO_WINDOW_STATE_SAVED_ACTIVE_TAB_LEFT "saved-active-tab-left" #define NEMO_WINDOW_STATE_SAVED_ACTIVE_TAB_RIGHT "saved-active-tab-right" diff --git a/libnemo-private/org.nemo.gschema.xml b/libnemo-private/org.nemo.gschema.xml index 600af2769..8603b1e08 100644 --- a/libnemo-private/org.nemo.gschema.xml +++ b/libnemo-private/org.nemo.gschema.xml @@ -714,15 +714,25 @@ Whether split view was enabled when the last window was closed Internal setting used to restore the last closed window's split view and tabs. - + [] - Saved tabs for the left pane - Internal setting used to restore the last closed window's tabs for the left pane. Each entry is a (uri, lock mode) pair, where the lock mode is 0 for an unlocked tab, 1 for a tab that returns to its locked folder when reselected, and 2 for a tab whose folder changes open in a new tab. Locked tabs are restored at their locked folder. + Saved tab URIs for the left pane + Internal setting used to restore the last closed window's tabs for the left pane. A locked tab is saved at its locked folder rather than at the last folder it visited. - + [] - Saved tabs for the right pane - Internal setting used to restore the last closed window's tabs for the right pane. Each entry is a (uri, lock mode) pair, where the lock mode is 0 for an unlocked tab, 1 for a tab that returns to its locked folder when reselected, and 2 for a tab whose folder changes open in a new tab. Locked tabs are restored at their locked folder. + Saved tab URIs for the right pane + Internal setting used to restore the last closed window's tabs for the right pane. A locked tab is saved at its locked folder rather than at the last folder it visited. + + + [] + Saved tab lock modes for the left pane + Internal setting used to restore the lock mode of each tab in saved-tabs-left, position by position: 0 for an unlocked tab, 1 for a tab that returns to its locked folder when reselected, and 2 for a tab whose folder changes open in a new tab. Entries missing from the end of this list default to 0, so a saved-tabs-left written by a Nemo without tab locking restores as all-unlocked. + + + [] + Saved tab lock modes for the right pane + Internal setting used to restore the lock mode of each tab in saved-tabs-right, position by position: 0 for an unlocked tab, 1 for a tab that returns to its locked folder when reselected, and 2 for a tab whose folder changes open in a new tab. Entries missing from the end of this list default to 0, so a saved-tabs-right written by a Nemo without tab locking restores as all-unlocked. 0 diff --git a/src/nemo-window.c b/src/nemo-window.c index 0df80c96e..462d3b1ba 100644 --- a/src/nemo-window.c +++ b/src/nemo-window.c @@ -2050,72 +2050,94 @@ uri_is_native_session_uri (const char *uri) return is_native; } -static GVariant * -collect_pane_saved_tabs (NemoWindowPane *pane, gint *active_index_out) +/* The tab URIs and their lock modes are stored in two position-aligned keys + * rather than one list of pairs, so that saved-tabs-left/right keep the plain + * "as" type they have always had. A Nemo without tab locking still restores + * the tabs and simply ignores the lock modes. + */ +static void +collect_pane_saved_tabs (NemoWindowPane *pane, + GVariant **tabs_out, + GVariant **locks_out, + gint *active_index_out) { GtkNotebook *notebook; - GVariantBuilder builder; + GVariantBuilder tabs_builder; + GArray *locks; int n_pages, i; int current_page; int saved_index = 0; int saved_active_index = 0; + guint locks_len = 0; + + g_variant_builder_init (&tabs_builder, G_VARIANT_TYPE ("as")); + locks = g_array_new (FALSE, FALSE, sizeof (gint32)); + + if (pane != NULL && pane->notebook != NULL) { + notebook = GTK_NOTEBOOK (pane->notebook); + n_pages = gtk_notebook_get_n_pages (notebook); + current_page = gtk_notebook_get_current_page (notebook); + + for (i = 0; i < n_pages; i++) { + GtkWidget *page; + NemoWindowSlot *slot; + char *uri; + NemoTabLockMode lock_mode; + const char *locked_uri; + + page = gtk_notebook_get_nth_page (notebook, i); + if (page == NULL) { + continue; + } - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(si)")); - - if (active_index_out != NULL) { - *active_index_out = 0; - } - - if (pane == NULL || pane->notebook == NULL) { - return g_variant_builder_end (&builder); - } - - notebook = GTK_NOTEBOOK (pane->notebook); - n_pages = gtk_notebook_get_n_pages (notebook); - current_page = gtk_notebook_get_current_page (notebook); - - for (i = 0; i < n_pages; i++) { - GtkWidget *page; - NemoWindowSlot *slot; - char *uri; - NemoTabLockMode lock_mode; - const char *locked_uri; - - page = gtk_notebook_get_nth_page (notebook, i); - if (page == NULL) { - continue; - } + slot = NEMO_WINDOW_SLOT (page); + lock_mode = nemo_window_slot_get_lock_mode (slot); + locked_uri = nemo_window_slot_get_locked_uri (slot); + + /* Locked tabs are restored at their locked folder, not at the + * last visited location. + */ + if (lock_mode != NEMO_TAB_LOCK_NONE && locked_uri != NULL) { + uri = g_strdup (locked_uri); + } else { + uri = nemo_window_slot_get_location_uri (slot); + lock_mode = NEMO_TAB_LOCK_NONE; + } - slot = NEMO_WINDOW_SLOT (page); - lock_mode = nemo_window_slot_get_lock_mode (slot); - locked_uri = nemo_window_slot_get_locked_uri (slot); + if (uri_is_native_session_uri (uri)) { + gint32 mode = (gint32) lock_mode; - /* Locked tabs are restored at their locked folder, not at the - * last visited location. - */ - if (lock_mode != NEMO_TAB_LOCK_NONE && locked_uri != NULL) { - uri = g_strdup (locked_uri); - } else { - uri = nemo_window_slot_get_location_uri (slot); - lock_mode = NEMO_TAB_LOCK_NONE; - } + if (i == current_page) { + saved_active_index = saved_index; + } - if (uri_is_native_session_uri (uri)) { - if (i == current_page) { - saved_active_index = saved_index; + g_variant_builder_add (&tabs_builder, "s", uri); + g_array_append_val (locks, mode); + if (lock_mode != NEMO_TAB_LOCK_NONE) { + locks_len = locks->len; + } + saved_index++; } - g_variant_builder_add (&builder, "(si)", uri, (gint32) lock_mode); - saved_index++; - } - g_free (uri); + g_free (uri); + } } + /* Drop trailing unlocked entries: a short lock list restores as unlocked, + * so a pane with no locked tabs writes [] just as an older Nemo would. + */ + g_array_set_size (locks, locks_len); + if (active_index_out != NULL) { *active_index_out = saved_active_index; } - return g_variant_builder_end (&builder); + *tabs_out = g_variant_builder_end (&tabs_builder); + *locks_out = g_variant_new_fixed_array (G_VARIANT_TYPE_INT32, + locks->data, locks->len, + sizeof (gint32)); + + g_array_free (locks, TRUE); } void @@ -2125,6 +2147,8 @@ nemo_window_save_session_state (NemoWindow *window) NemoWindowPane *right_pane; GVariant *left_tabs; GVariant *right_tabs; + GVariant *left_locks; + GVariant *right_locks; gint left_active = 0; gint right_active = 0; gboolean split_view; @@ -2146,13 +2170,15 @@ nemo_window_save_session_state (NemoWindow *window) left_pane = child1 != NULL ? NEMO_WINDOW_PANE (child1) : NULL; right_pane = child2 != NULL ? NEMO_WINDOW_PANE (child2) : NULL; - left_tabs = collect_pane_saved_tabs (left_pane, &left_active); - right_tabs = collect_pane_saved_tabs (right_pane, &right_active); + collect_pane_saved_tabs (left_pane, &left_tabs, &left_locks, &left_active); + collect_pane_saved_tabs (right_pane, &right_tabs, &right_locks, &right_active); split_view = (right_pane != NULL); g_settings_set_boolean (nemo_window_state, NEMO_WINDOW_STATE_SAVED_SPLIT_VIEW, split_view); g_settings_set_value (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TABS_LEFT, left_tabs); g_settings_set_value (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TABS_RIGHT, right_tabs); + g_settings_set_value (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TAB_LOCKS_LEFT, left_locks); + g_settings_set_value (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TAB_LOCKS_RIGHT, right_locks); g_settings_set_int (nemo_window_state, NEMO_WINDOW_STATE_SAVED_ACTIVE_TAB_LEFT, left_active); g_settings_set_int (nemo_window_state, NEMO_WINDOW_STATE_SAVED_ACTIVE_TAB_RIGHT, right_active); } @@ -2192,9 +2218,9 @@ clear_pane_to_single_slot (NemoWindowPane *pane) } static void -open_saved_tabs_in_pane (NemoWindowPane *pane, GVariant *tabs) +open_saved_tabs_in_pane (NemoWindowPane *pane, GVariant *tabs, GVariant *locks) { - gsize i, n; + gsize i, n, n_locks; gsize opened = 0; if (pane == NULL || tabs == NULL) { @@ -2202,14 +2228,22 @@ open_saved_tabs_in_pane (NemoWindowPane *pane, GVariant *tabs) } n = g_variant_n_children (tabs); + n_locks = locks != NULL ? g_variant_n_children (locks) : 0; for (i = 0; i < n; i++) { const gchar *uri; - gint32 lock_mode; + gint32 lock_mode = NEMO_TAB_LOCK_NONE; NemoWindowSlot *slot; GFile *location; - g_variant_get_child (tabs, i, "(&si)", &uri, &lock_mode); + g_variant_get_child (tabs, i, "&s", &uri); + + /* The lock list is position-aligned with the tab list and may be + * shorter; anything it does not cover is an unlocked tab. + */ + if (i < n_locks) { + g_variant_get_child (locks, i, "i", &lock_mode); + } if (!uri_is_native_session_uri (uri)) { continue; @@ -2258,6 +2292,8 @@ nemo_window_restore_saved_tabs (NemoWindow *window) NemoWindowPane *right_pane; GVariant *left_tabs; GVariant *right_tabs; + GVariant *left_locks; + GVariant *right_locks; gint left_active; gint right_active; gboolean want_split; @@ -2272,6 +2308,8 @@ nemo_window_restore_saved_tabs (NemoWindow *window) left_tabs = g_settings_get_value (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TABS_LEFT); right_tabs = g_settings_get_value (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TABS_RIGHT); + left_locks = g_settings_get_value (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TAB_LOCKS_LEFT); + right_locks = g_settings_get_value (nemo_window_state, NEMO_WINDOW_STATE_SAVED_TAB_LOCKS_RIGHT); left_active = g_settings_get_int (nemo_window_state, NEMO_WINDOW_STATE_SAVED_ACTIVE_TAB_LEFT); right_active = g_settings_get_int (nemo_window_state, NEMO_WINDOW_STATE_SAVED_ACTIVE_TAB_RIGHT); saved_split = g_settings_get_boolean (nemo_window_state, NEMO_WINDOW_STATE_SAVED_SPLIT_VIEW); @@ -2280,6 +2318,8 @@ nemo_window_restore_saved_tabs (NemoWindow *window) g_variant_n_children (right_tabs) == 0) { g_variant_unref (left_tabs); g_variant_unref (right_tabs); + g_variant_unref (left_locks); + g_variant_unref (right_locks); return FALSE; } @@ -2313,10 +2353,10 @@ nemo_window_restore_saved_tabs (NemoWindow *window) } g_object_unref (home); } else { - open_saved_tabs_in_pane (left_pane, left_tabs); + open_saved_tabs_in_pane (left_pane, left_tabs, left_locks); } - open_saved_tabs_in_pane (right_pane, right_tabs); + open_saved_tabs_in_pane (right_pane, right_tabs, right_locks); /* Restore active tabs (clamp indices) */ if (left_pane != NULL && left_pane->notebook != NULL) { @@ -2342,6 +2382,8 @@ nemo_window_restore_saved_tabs (NemoWindow *window) g_variant_unref (left_tabs); g_variant_unref (right_tabs); + g_variant_unref (left_locks); + g_variant_unref (right_locks); return TRUE; } From b1e3cb980806536cf71f71285ce12f7095d10cf4 Mon Sep 17 00:00:00 2001 From: Dawid Zyla Date: Wed, 8 Jul 2026 21:40:18 -0600 Subject: [PATCH 8/9] application: Save the session when the session manager ends it Session state was only written from real_window_close and the Quit action, so logging out with a window open saved nothing: the session manager sends SIGTERM, and with no handler installed the process died where it stood. Tabs only ever persisted if you closed the window by hand first, which defeats the point of restoring them. Handle SIGTERM and SIGHUP, saving through the same path Quit uses. Flush with g_settings_sync() once the state is written, since both callers are on their way out and an unflushed GSettings write dies with the process. Co-Authored-By: Claude Opus 4.8 --- src/nemo-application.c | 89 +++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 27 deletions(-) diff --git a/src/nemo-application.c b/src/nemo-application.c index 3e1e847ad..ffce0f43d 100644 --- a/src/nemo-application.c +++ b/src/nemo-application.c @@ -73,6 +73,7 @@ #include #include #include +#include #include #include #include @@ -534,6 +535,60 @@ nemo_application_init (NemoApplication *application) g_object_unref (action); } +/* Save session state once, from the last non-desktop window we find, and flush + * it: both callers are on their way out of the process, and an unflushed + * GSettings write dies with us. + */ +static void +save_last_window_session_state (NemoApplication *self) +{ + GList *windows; + NemoWindow *last_window = NULL; + + windows = gtk_application_get_windows (GTK_APPLICATION (self)); + + for (GList *l = windows; l != NULL; l = l->next) { + GtkWindow *w = GTK_WINDOW (l->data); + + if (!NEMO_IS_WINDOW (w)) { + continue; + } + + /* Avoid saving the desktop window */ + if (NEMO_IS_DESKTOP_WINDOW (w)) { + continue; + } + + last_window = NEMO_WINDOW (w); + break; + } + + if (last_window != NULL) { + nemo_window_save_session_state (last_window); + g_settings_sync (); + } +} + +/* Logging out terminates us with SIGTERM (SIGHUP if the session's terminal + * goes away first). Without this the session is never saved, because the + * window close and Quit paths never run. + */ +static gboolean +session_end_signal_cb (gpointer user_data) +{ + static gboolean ending = FALSE; + + /* Both signals can arrive; tear down once. */ + if (ending) { + return G_SOURCE_REMOVE; + } + ending = TRUE; + + nemo_application_quit (NEMO_APPLICATION (user_data)); + + return G_SOURCE_REMOVE; +} + void nemo_application_quit (NemoApplication *self) { @@ -547,34 +602,10 @@ nemo_application_quit (NemoApplication *self) GList *windows; - windows = gtk_application_get_windows (GTK_APPLICATION (app)); - - /* Save session state once, before we destroy all windows. - * Save the last non-desktop window we find. */ - { - NemoWindow *last_window = NULL; - - for (GList *l = windows; l != NULL; l = l->next) { - GtkWindow *w = GTK_WINDOW (l->data); - - if (!NEMO_IS_WINDOW (w)) { - continue; - } - - /* Avoid saving the desktop window */ - if (NEMO_IS_DESKTOP_WINDOW (w)) { - continue; - } - - last_window = NEMO_WINDOW (w); - break; - } - - if (last_window != NULL) { - nemo_window_save_session_state (last_window); - } - } + /* Before we destroy all windows. */ + save_last_window_session_state (self); + windows = gtk_application_get_windows (GTK_APPLICATION (app)); g_list_foreach (windows, (GFunc) gtk_widget_destroy, NULL); /* we have been asked to force quit */ @@ -596,6 +627,10 @@ nemo_application_startup (GApplication *app) /* initialize preferences and create the global GSettings objects */ nemo_global_preferences_init (); + /* save the session when the session manager ends it */ + g_unix_signal_add (SIGTERM, session_end_signal_cb, self); + g_unix_signal_add (SIGHUP, session_end_signal_cb, self); + /* Run desktop- or main- specific things */ NEMO_APPLICATION_CLASS (G_OBJECT_GET_CLASS (self))->continue_startup (self); From 4e672c36b8c87de1334c615a0bbe9ff0bf2d6afc Mon Sep 17 00:00:00 2001 From: Dawid Zyla Date: Wed, 8 Jul 2026 21:40:18 -0600 Subject: [PATCH 9/9] window-pane: Fix comments left behind by the tab lock rework notebook_switch_page_cb credited the slot's "inactive" handler for sending a locked tab home, but d1a39ce deliberately moved that to the "active" handler; the comment said the opposite of what the code does. build_tab_label still called locked tabs "pinned", the name 6ed22070 replaced. Co-Authored-By: Claude Opus 4.8 --- src/nemo-notebook.c | 2 +- src/nemo-window-pane.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nemo-notebook.c b/src/nemo-notebook.c index 09098ab0c..055ae5d9d 100644 --- a/src/nemo-notebook.c +++ b/src/nemo-notebook.c @@ -380,7 +380,7 @@ build_tab_label (NemoNotebook *nb, NemoWindowSlot *slot) gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0); /* don't show the icon */ - /* setup padlock icon for pinned (locked) tabs, hidden by default */ + /* setup padlock icon for locked tabs, hidden by default */ lock_icon = gtk_image_new_from_icon_name ("changes-prevent-symbolic", GTK_ICON_SIZE_MENU); gtk_box_pack_start (GTK_BOX (hbox), lock_icon, FALSE, FALSE, 0); diff --git a/src/nemo-window-pane.c b/src/nemo-window-pane.c index 33ee3756e..88769d85a 100644 --- a/src/nemo-window-pane.c +++ b/src/nemo-window-pane.c @@ -749,8 +749,8 @@ notebook_switch_page_cb (GtkNotebook *notebook, slot = NEMO_WINDOW_SLOT (widget); g_assert (slot != NULL); - /* Sending a locked tab home is the slot's "inactive" handler's job; it - * also covers the tab losing focus to the other pane. + /* Sending a locked tab home is the slot's "active" handler's job; it also + * covers the tab becoming current again by way of the other pane. */ nemo_window_set_active_slot (nemo_window_slot_get_window (slot), slot);