From 30a535beccab0c20d2c92ffd194b1299b70e827c Mon Sep 17 00:00:00 2001 From: kavehtehrani Date: Fri, 19 Dec 2025 16:07:55 -0300 Subject: [PATCH 01/10] Restore last closed window tabs Save split view and tab URIs on window close and restore them when starting Nemo with no explicit locations. --- libnemo-private/nemo-global-preferences.h | 7 + libnemo-private/org.nemo.gschema.xml | 27 ++ src/nemo-main-application.c | 27 +- src/nemo-window-private.h | 2 + src/nemo-window.c | 293 ++++++++++++++++++++++ 5 files changed, 354 insertions(+), 2 deletions(-) diff --git a/libnemo-private/nemo-global-preferences.h b/libnemo-private/nemo-global-preferences.h index 576a4616d..1a842fa90 100644 --- a/libnemo-private/nemo-global-preferences.h +++ b/libnemo-private/nemo-global-preferences.h @@ -122,6 +122,13 @@ typedef enum #define NEMO_WINDOW_STATE_DEVICES_EXPANDED "devices-expanded" #define NEMO_WINDOW_STATE_NETWORK_EXPANDED "network-expanded" +/* Saved session (last closed window) */ +#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_ACTIVE_TAB_LEFT "saved-active-tab-left" +#define NEMO_WINDOW_STATE_SAVED_ACTIVE_TAB_RIGHT "saved-active-tab-right" + /* Sorting order */ #define NEMO_PREFERENCES_SORT_DIRECTORIES_FIRST "sort-directories-first" #define NEMO_PREFERENCES_SORT_FAVORITES_FIRST "sort-favorites-first" diff --git a/libnemo-private/org.nemo.gschema.xml b/libnemo-private/org.nemo.gschema.xml index a43065d35..59b6a1066 100644 --- a/libnemo-private/org.nemo.gschema.xml +++ b/libnemo-private/org.nemo.gschema.xml @@ -702,6 +702,33 @@ Side pane view The side pane view to show in newly opened windows. + + + + false + 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 tab URIs for the right pane + Internal setting used to restore the last closed window's tabs for the right pane. + + + 0 + Index of the active tab in the left pane + Internal setting used to restore which tab was active in the left pane. + + + 0 + Index of the active tab in the right pane + Internal setting used to restore which tab was active in the right pane. + diff --git a/src/nemo-main-application.c b/src/nemo-main-application.c index b0f42581a..abaf8a04d 100644 --- a/src/nemo-main-application.c +++ b/src/nemo-main-application.c @@ -474,8 +474,31 @@ open_windows (NemoMainApplication *application, gint i; if (files == NULL || files[0] == NULL) { - /* Open a window pointing at the default location. */ - open_window (application, NULL, screen, geometry); + /* No explicit locations requested: try restoring the last session. */ + NemoWindow *window; + gboolean have_geometry; + + window = nemo_main_application_create_window (NEMO_APPLICATION (application), screen); + + have_geometry = geometry != NULL && strcmp (geometry, "") != 0; + if (have_geometry && !gtk_widget_get_visible (GTK_WIDGET (window))) { + /* never maximize windows opened from shell if a + * custom geometry has been requested. + */ + gtk_window_unmaximize (GTK_WINDOW (window)); + eel_gtk_window_set_initial_geometry_from_string (GTK_WINDOW (window), + geometry, + APPLICATION_WINDOW_MIN_WIDTH, + APPLICATION_WINDOW_MIN_HEIGHT, + FALSE); + } + + if (!nemo_window_restore_saved_tabs (window)) { + /* Fall back to a safe default location */ + GFile *home = g_file_new_for_path (g_get_home_dir ()); + nemo_window_go_to (window, home); + g_object_unref (home); + } } else { if (open_in_existing_window) { /* Open one tab at each requested location in an existing window */ diff --git a/src/nemo-window-private.h b/src/nemo-window-private.h index b8f8e0444..e79f9d5ca 100644 --- a/src/nemo-window-private.h +++ b/src/nemo-window-private.h @@ -154,6 +154,8 @@ void nemo_window_set_active_pane (NemoWindow NemoWindowPane *new_pane); NemoWindowPane * nemo_window_get_active_pane (NemoWindow *window); +gboolean nemo_window_restore_saved_tabs (NemoWindow *window); + /* sync window GUI with current slot. Used when changing slots, * and when updating the slot state. diff --git a/src/nemo-window.c b/src/nemo-window.c index a9e502d72..0bbaa380a 100644 --- a/src/nemo-window.c +++ b/src/nemo-window.c @@ -2025,11 +2025,304 @@ real_get_icon (NemoWindow *window, NEMO_FILE_ICON_FLAGS_USE_MOUNT_ICON); } +static gboolean +uri_is_native_session_uri (const char *uri) +{ + GFile *file; + gboolean is_native; + + if (uri == NULL || uri[0] == '\0') { + return FALSE; + } + + file = g_file_new_for_uri (uri); + + /* skip searches and non-native locations for this simple session restore */ + if (g_file_has_uri_scheme (file, "x-nemo-search")) { + g_object_unref (file); + return FALSE; + } + + is_native = g_file_is_native (file); + g_object_unref (file); + + return is_native; +} + +static char ** +collect_pane_saved_tab_uris (NemoWindowPane *pane, gint *active_index_out) +{ + GtkNotebook *notebook; + int n_pages, i; + int current_page; + int saved_index = 0; + int saved_active_index = 0; + GPtrArray *arr; + + if (active_index_out != NULL) { + *active_index_out = 0; + } + + if (pane == NULL || pane->notebook == NULL) { + return g_new0 (char *, 1); + } + + 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; + + page = gtk_notebook_get_nth_page (notebook, i); + if (page == NULL) { + continue; + } + + slot = NEMO_WINDOW_SLOT (page); + uri = nemo_window_slot_get_location_uri (slot); + + if (uri_is_native_session_uri (uri)) { + if (i == current_page) { + saved_active_index = saved_index; + } + g_ptr_array_add (arr, uri); + saved_index++; + } else { + g_free (uri); + } + } + + g_ptr_array_add (arr, NULL); + + if (active_index_out != NULL) { + *active_index_out = saved_active_index; + } + + return (char **) g_ptr_array_free (arr, FALSE); +} + +static void +nemo_window_save_session_state (NemoWindow *window) +{ + NemoWindowPane *left_pane; + NemoWindowPane *right_pane; + char **left_uris; + char **right_uris; + gint left_active = 0; + gint right_active = 0; + gboolean split_view; + + g_return_if_fail (NEMO_IS_WINDOW (window)); + + /* Do not store session state for the desktop window */ + if (nemo_window_is_desktop (window)) { + return; + } + + left_pane = g_list_nth_data (window->details->panes, 0); + right_pane = g_list_nth_data (window->details->panes, 1); + + left_uris = collect_pane_saved_tab_uris (left_pane, &left_active); + right_uris = collect_pane_saved_tab_uris (right_pane, &right_active); + split_view = nemo_window_split_view_showing (window); + + 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_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 +clear_pane_to_single_slot (NemoWindowPane *pane) +{ + GtkNotebook *notebook; + int n_pages; + + if (pane == NULL || pane->notebook == NULL) { + return; + } + + notebook = GTK_NOTEBOOK (pane->notebook); + n_pages = gtk_notebook_get_n_pages (notebook); + + /* Ensure there is a predictable active tab */ + if (n_pages > 0) { + gtk_notebook_set_current_page (notebook, 0); + } + + /* Close all tabs except the first one */ + while (gtk_notebook_get_n_pages (notebook) > 1) { + GtkWidget *page; + NemoWindowSlot *slot; + int last = gtk_notebook_get_n_pages (notebook) - 1; + + page = gtk_notebook_get_nth_page (notebook, last); + if (page == NULL) { + break; + } + + slot = NEMO_WINDOW_SLOT (page); + nemo_window_pane_close_slot (pane, slot); + } +} + +static void +open_uri_list_in_pane (NemoWindowPane *pane, char **uris) +{ + NemoWindow *window; + int i; + + if (pane == NULL) { + return; + } + + window = pane->window; + + /* If no URIs were saved for this pane, leave its first tab alone */ + if (uris == NULL || uris[0] == NULL) { + return; + } + + for (i = 0; uris[i] != NULL; i++) { + NemoWindowSlot *slot; + GFile *location; + + if (!uri_is_native_session_uri (uris[i])) { + continue; + } + + if (i == 0) { + /* Reuse the existing first tab */ + slot = pane->active_slot; + if (slot == NULL && pane->notebook != NULL) { + GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (pane->notebook), 0); + if (page != NULL) { + slot = NEMO_WINDOW_SLOT (page); + } + } + } else { + slot = nemo_window_pane_open_slot (pane, NEMO_WINDOW_OPEN_SLOT_APPEND); + } + + if (slot == NULL) { + continue; + } + + location = g_file_new_for_uri (uris[i]); + nemo_window_slot_open_location (slot, location, 0); + g_object_unref (location); + + /* Avoid leaving the window's active slot on the last tab we opened */ + if (window != NULL && i == 0) { + nemo_window_set_active_slot (window, slot); + } + } +} + +gboolean +nemo_window_restore_saved_tabs (NemoWindow *window) +{ + NemoWindowPane *left_pane; + NemoWindowPane *right_pane; + char **left_uris; + char **right_uris; + gint left_active; + gint right_active; + gboolean want_split; + gboolean saved_split; + + g_return_val_if_fail (NEMO_IS_WINDOW (window), FALSE); + + /* Never restore tabs for the desktop window */ + if (nemo_window_is_desktop (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_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); + 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); + + if (want_split && !nemo_window_split_view_showing (window)) { + nemo_window_split_view_on (window); + } else if (!want_split && nemo_window_split_view_showing (window)) { + nemo_window_split_view_off (window); + } + + left_pane = g_list_nth_data (window->details->panes, 0); + right_pane = want_split ? g_list_nth_data (window->details->panes, 1) : NULL; + + /* Reset panes to one tab each, then rebuild tabs in saved order */ + clear_pane_to_single_slot (left_pane); + 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) { + 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_uri_list_in_pane (right_pane, right_uris); + + /* Restore active tabs (clamp indices) */ + if (left_pane != NULL && left_pane->notebook != NULL) { + GtkNotebook *nb = GTK_NOTEBOOK (left_pane->notebook); + int n = gtk_notebook_get_n_pages (nb); + if (n > 0) { + gtk_notebook_set_current_page (nb, CLAMP (left_active, 0, n - 1)); + } + } + + if (right_pane != NULL && right_pane->notebook != NULL) { + GtkNotebook *nb = GTK_NOTEBOOK (right_pane->notebook); + int n = gtk_notebook_get_n_pages (nb); + if (n > 0) { + gtk_notebook_set_current_page (nb, CLAMP (right_active, 0, n - 1)); + } + } + + /* Make the left pane active for a predictable starting point */ + if (left_pane != NULL) { + nemo_window_set_active_pane (window, left_pane); + } + + g_strfreev (left_uris); + g_strfreev (right_uris); + + return TRUE; +} + static void real_window_close (NemoWindow *window) { g_return_if_fail (NEMO_IS_WINDOW (window)); + nemo_window_save_session_state (window); nemo_window_save_geometry (window); gtk_widget_destroy (GTK_WIDGET (window)); From 63f99c4b1260a1b457d6570dbcddebf569689684 Mon Sep 17 00:00:00 2001 From: kavehtehrani Date: Fri, 19 Dec 2025 16:49:02 -0300 Subject: [PATCH 02/10] adding tabs --- src/nemo-window.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/nemo-window.c b/src/nemo-window.c index 0bbaa380a..ef0d46d41 100644 --- a/src/nemo-window.c +++ b/src/nemo-window.c @@ -837,6 +837,10 @@ nemo_window_destroy (GtkWidget *object) DEBUG ("Destroying window"); + /* Ensure session state is saved even when the application quits by + * destroying windows directly (bypassing delete-event/close). */ + nemo_window_save_session_state (window); + /* close the sidebar first */ nemo_window_tear_down_sidebar (window); From 0c8f7b82beb0a26dfa20c947df1b9f45240cba85 Mon Sep 17 00:00:00 2001 From: kavehtehrani Date: Fri, 19 Dec 2025 16:59:44 -0300 Subject: [PATCH 03/10] adding tabs --- src/nemo-application.c | 11 +++++++++++ src/nemo-window-private.h | 1 + src/nemo-window.c | 10 ++++++---- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/nemo-application.c b/src/nemo-application.c index 5366afb2e..a90ab3c90 100644 --- a/src/nemo-application.c +++ b/src/nemo-application.c @@ -534,6 +534,17 @@ nemo_application_quit (NemoApplication *self) GList *windows; windows = gtk_application_get_windows (GTK_APPLICATION (app)); + + /* Save session state once, based on the active Nemo window, before we + * destroy all windows. Destroying multiple windows can otherwise overwrite + * the stored state with an arbitrary last-destroyed window. */ + { + GtkWindow *active = gtk_application_get_active_window (GTK_APPLICATION (app)); + if (active != NULL && NEMO_IS_WINDOW (active)) { + nemo_window_save_session_state_for_quit (NEMO_WINDOW (active)); + } + } + g_list_foreach (windows, (GFunc) gtk_widget_destroy, NULL); /* we have been asked to force quit */ diff --git a/src/nemo-window-private.h b/src/nemo-window-private.h index e79f9d5ca..898a2426b 100644 --- a/src/nemo-window-private.h +++ b/src/nemo-window-private.h @@ -155,6 +155,7 @@ void nemo_window_set_active_pane (NemoWindow NemoWindowPane * nemo_window_get_active_pane (NemoWindow *window); gboolean nemo_window_restore_saved_tabs (NemoWindow *window); +void nemo_window_save_session_state_for_quit (NemoWindow *window); /* sync window GUI with current slot. Used when changing slots, diff --git a/src/nemo-window.c b/src/nemo-window.c index ef0d46d41..17a811372 100644 --- a/src/nemo-window.c +++ b/src/nemo-window.c @@ -837,10 +837,6 @@ nemo_window_destroy (GtkWidget *object) DEBUG ("Destroying window"); - /* Ensure session state is saved even when the application quits by - * destroying windows directly (bypassing delete-event/close). */ - nemo_window_save_session_state (window); - /* close the sidebar first */ nemo_window_tear_down_sidebar (window); @@ -2145,6 +2141,12 @@ nemo_window_save_session_state (NemoWindow *window) g_strfreev (right_uris); } +void +nemo_window_save_session_state_for_quit (NemoWindow *window) +{ + nemo_window_save_session_state (window); +} + static void clear_pane_to_single_slot (NemoWindowPane *pane) { From e86bb447dea90b9efc80a91d94a3b2d19e3090bf Mon Sep 17 00:00:00 2001 From: kavehtehrani Date: Fri, 19 Dec 2025 17:04:45 -0300 Subject: [PATCH 04/10] adding tabs --- src/nemo-application.c | 51 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/src/nemo-application.c b/src/nemo-application.c index a90ab3c90..bd930ca72 100644 --- a/src/nemo-application.c +++ b/src/nemo-application.c @@ -535,13 +535,52 @@ nemo_application_quit (NemoApplication *self) windows = gtk_application_get_windows (GTK_APPLICATION (app)); - /* Save session state once, based on the active Nemo window, before we - * destroy all windows. Destroying multiple windows can otherwise overwrite - * the stored state with an arbitrary last-destroyed window. */ + /* Save session state once, before we destroy all windows. + * + * We can't rely on gtk_application_get_active_window() here; depending on + * timing/focus it can be NULL or point to non-browser dialogs. + * + * Instead, pick the "best" Nemo window to snapshot: + * - prefer non-desktop windows + * - prefer split-view windows + * - prefer the window with the most tabs across panes + * + * Destroying multiple windows can otherwise overwrite stored state with an + * arbitrary last-destroyed window. */ { - GtkWindow *active = gtk_application_get_active_window (GTK_APPLICATION (app)); - if (active != NULL && NEMO_IS_WINDOW (active)) { - nemo_window_save_session_state_for_quit (NEMO_WINDOW (active)); + NemoWindow *best = NULL; + gint best_pane_count = -1; + gint best_tab_count = -1; + + for (GList *l = windows; l != NULL; l = l->next) { + GtkWindow *w = GTK_WINDOW (l->data); + + if (!NEMO_IS_WINDOW (w) || NEMO_IS_DESKTOP_WINDOW (w)) { + continue; + } + + NemoWindow *nw = NEMO_WINDOW (w); + gint pane_count = g_list_length (nw->details->panes); + gint tab_count = 0; + + for (GList *p = nw->details->panes; p != NULL; p = p->next) { + NemoWindowPane *pane = p->data; + if (pane != NULL && pane->notebook != NULL) { + tab_count += gtk_notebook_get_n_pages (GTK_NOTEBOOK (pane->notebook)); + } + } + + /* Prefer more panes first (split view), then more tabs */ + if (pane_count > best_pane_count || + (pane_count == best_pane_count && tab_count > best_tab_count)) { + best = nw; + best_pane_count = pane_count; + best_tab_count = tab_count; + } + } + + if (best != NULL) { + nemo_window_save_session_state_for_quit (best); } } From 01d1e86f0efc63b89587007570b602d21b1833ce Mon Sep 17 00:00:00 2001 From: kavehtehrani Date: Fri, 19 Dec 2025 17:08:31 -0300 Subject: [PATCH 05/10] adding tabs --- src/nemo-application.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/nemo-application.c b/src/nemo-application.c index bd930ca72..2c22bee61 100644 --- a/src/nemo-application.c +++ b/src/nemo-application.c @@ -555,11 +555,17 @@ nemo_application_quit (NemoApplication *self) for (GList *l = windows; l != NULL; l = l->next) { GtkWindow *w = GTK_WINDOW (l->data); - if (!NEMO_IS_WINDOW (w) || NEMO_IS_DESKTOP_WINDOW (w)) { + if (!NEMO_IS_WINDOW (w)) { continue; } NemoWindow *nw = NEMO_WINDOW (w); + + /* Avoid saving the desktop window */ + if (nw->details != NULL && nw->details->disable_chrome) { + continue; + } + gint pane_count = g_list_length (nw->details->panes); gint tab_count = 0; From 1e848b246d91f20ec77f5253e17dbd44dbc4ab00 Mon Sep 17 00:00:00 2001 From: kavehtehrani Date: Fri, 19 Dec 2025 17:12:51 -0300 Subject: [PATCH 06/10] adding tabs --- src/nemo-window.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/nemo-window.c b/src/nemo-window.c index 17a811372..42dd9ce18 100644 --- a/src/nemo-window.c +++ b/src/nemo-window.c @@ -2116,6 +2116,9 @@ nemo_window_save_session_state (NemoWindow *window) gint left_active = 0; gint right_active = 0; gboolean split_view; + GtkPaned *paned; + GtkWidget *child1; + GtkWidget *child2; g_return_if_fail (NEMO_IS_WINDOW (window)); @@ -2124,12 +2127,16 @@ nemo_window_save_session_state (NemoWindow *window) return; } - left_pane = g_list_nth_data (window->details->panes, 0); - right_pane = g_list_nth_data (window->details->panes, 1); + paned = GTK_PANED (window->details->split_view_hpane); + child1 = gtk_paned_get_child1 (paned); + child2 = gtk_paned_get_child2 (paned); + + 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); - split_view = nemo_window_split_view_showing (window); + 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); @@ -2275,8 +2282,14 @@ nemo_window_restore_saved_tabs (NemoWindow *window) nemo_window_split_view_off (window); } - left_pane = g_list_nth_data (window->details->panes, 0); - right_pane = want_split ? g_list_nth_data (window->details->panes, 1) : NULL; + { + GtkPaned *paned = GTK_PANED (window->details->split_view_hpane); + GtkWidget *child1 = gtk_paned_get_child1 (paned); + GtkWidget *child2 = gtk_paned_get_child2 (paned); + + left_pane = child1 != NULL ? NEMO_WINDOW_PANE (child1) : NULL; + right_pane = (want_split && child2 != NULL) ? NEMO_WINDOW_PANE (child2) : NULL; + } /* Reset panes to one tab each, then rebuild tabs in saved order */ clear_pane_to_single_slot (left_pane); From 25d0a1d296a2979c38618e8fbd43975dc7ee8a58 Mon Sep 17 00:00:00 2001 From: kavehtehrani Date: Fri, 19 Dec 2025 17:20:18 -0300 Subject: [PATCH 07/10] adding tabs --- src/nemo-main-application.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/nemo-main-application.c b/src/nemo-main-application.c index abaf8a04d..fa1339d9d 100644 --- a/src/nemo-main-application.c +++ b/src/nemo-main-application.c @@ -844,15 +844,13 @@ nemo_main_application_local_command_line (GApplication *application, g_strfreev (remaining); } - if (files == NULL && !no_default_window) { - files = g_malloc0 (2 * sizeof (GFile *)); - len = 1; - - files[0] = g_file_new_for_path (g_get_home_dir ()); - files[1] = NULL; - } - /* Invoke "Open" to open in existing window or create new windows */ - if (len > 0) { + /* Invoke "Open" to open in existing window or create new windows. + * + * IMPORTANT: When no URIs are specified and a default window is desired, + * call open() with 0 files instead of synthesizing the home directory. + * This allows features (like session restore) to distinguish between + * "no args" and an explicit request to open a location. */ + if (len > 0 || (files == NULL && !no_default_window)) { gchar* concatOptions = g_malloc0(64); if (open_in_existing_window) { g_stpcpy (concatOptions, "EXISTING_WINDOW"); @@ -867,10 +865,12 @@ nemo_main_application_local_command_line (GApplication *application, g_free (concatOptions); } - for (idx = 0; idx < len; idx++) { - g_object_unref (files[idx]); + if (files != NULL) { + for (idx = 0; idx < len; idx++) { + g_object_unref (files[idx]); + } + g_free (files); } - g_free (files); out: g_option_context_free (context); From a7705f61780b472d9153e8c4d7701c8c98ec48b5 Mon Sep 17 00:00:00 2001 From: kavehtehrani Date: Fri, 19 Dec 2025 21:49:15 -0300 Subject: [PATCH 08/10] adding tabs --- src/nemo-application.c | 2 +- src/nemo-main-application.c | 52 ++++++++++++++++++++++++++++++------- src/nemo-window-private.h | 2 +- src/nemo-window.c | 8 +----- 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/nemo-application.c b/src/nemo-application.c index 2c22bee61..a5ff1a7fd 100644 --- a/src/nemo-application.c +++ b/src/nemo-application.c @@ -586,7 +586,7 @@ nemo_application_quit (NemoApplication *self) } if (best != NULL) { - nemo_window_save_session_state_for_quit (best); + nemo_window_save_session_state (best); } } diff --git a/src/nemo-main-application.c b/src/nemo-main-application.c index fa1339d9d..9cd86ea77 100644 --- a/src/nemo-main-application.c +++ b/src/nemo-main-application.c @@ -556,6 +556,7 @@ nemo_main_application_open (GApplication *app, gboolean open_in_tabs = FALSE; gchar *geometry = NULL; gboolean open_in_existing_window = strcmp (options, "EXISTING_WINDOW") == 0; + gboolean default_no_args = FALSE; const char splitter = '='; g_debug ("Open called on the GApplication instance; %d files", n_files); @@ -564,7 +565,12 @@ nemo_main_application_open (GApplication *app, /* Check if local command line passed --geometry or --tabs */ if (strlen (options) > 0) { gchar** split_options = g_strsplit (options, &splitter, 2); - if (strcmp (split_options[0], "NULL") != 0) { + if (g_str_has_prefix (split_options[0], "DEFAULT")) { + default_no_args = TRUE; + if (g_str_has_prefix (split_options[0], "DEFAULT+")) { + geometry = g_strdup (split_options[0] + strlen ("DEFAULT+")); + } + } else if (strcmp (split_options[0], "NULL") != 0) { geometry = g_strdup (split_options[0]); } sscanf (split_options[1], "%d", &open_in_tabs); @@ -579,7 +585,13 @@ nemo_main_application_open (GApplication *app, geometry ? geometry : "none", open_in_existing_window ? "yes" : "no"); - open_windows (self, files, n_files, gdk_screen_get_default (), geometry, open_in_tabs, open_in_existing_window); + if (default_no_args) { + /* Treat this as a no-arg launch; open_windows() will attempt session restore + * and fall back to Home if restore isn't possible. */ + open_windows (self, NULL, 0, gdk_screen_get_default (), geometry, open_in_tabs, open_in_existing_window); + } else { + open_windows (self, files, n_files, gdk_screen_get_default (), geometry, open_in_tabs, open_in_existing_window); + } g_clear_pointer (&geometry, g_free); } @@ -821,9 +833,11 @@ nemo_main_application_local_command_line (GApplication *application, GFile **files; gint idx, len; + gboolean used_default_location; len = 0; files = NULL; + used_default_location = FALSE; /* Convert args to GFiles */ if (remaining != NULL) { @@ -844,21 +858,39 @@ nemo_main_application_local_command_line (GApplication *application, g_strfreev (remaining); } + if (files == NULL && !no_default_window) { + /* Original behavior: default to Home when no URIs are provided. */ + files = g_malloc0 (2 * sizeof (GFile *)); + len = 1; + + files[0] = g_file_new_for_path (g_get_home_dir ()); + files[1] = NULL; + + /* Mark that this was a no-arg launch, not an explicit URI. */ + used_default_location = TRUE; + } + /* Invoke "Open" to open in existing window or create new windows. - * - * IMPORTANT: When no URIs are specified and a default window is desired, - * call open() with 0 files instead of synthesizing the home directory. - * This allows features (like session restore) to distinguish between - * "no args" and an explicit request to open a location. */ - if (len > 0 || (files == NULL && !no_default_window)) { + */ + if (len > 0) { gchar* concatOptions = g_malloc0(64); if (open_in_existing_window) { g_stpcpy (concatOptions, "EXISTING_WINDOW"); } else { if (self->priv->geometry == NULL) { - g_snprintf (concatOptions, 64, "NULL=%d", open_in_tabs); + /* If Home was synthesized because no URIs were passed, signal that + * to the primary instance so it can attempt session restore. */ + if (used_default_location) { + g_snprintf (concatOptions, 64, "DEFAULT=%d", open_in_tabs); + } else { + g_snprintf (concatOptions, 64, "NULL=%d", open_in_tabs); + } } else { - g_snprintf (concatOptions, 64, "%s=%d", self->priv->geometry, open_in_tabs); + if (used_default_location) { + g_snprintf (concatOptions, 64, "DEFAULT+%s=%d", self->priv->geometry, open_in_tabs); + } else { + g_snprintf (concatOptions, 64, "%s=%d", self->priv->geometry, open_in_tabs); + } } } g_application_open (application, files, len, concatOptions); diff --git a/src/nemo-window-private.h b/src/nemo-window-private.h index 898a2426b..1f43e5f40 100644 --- a/src/nemo-window-private.h +++ b/src/nemo-window-private.h @@ -155,7 +155,7 @@ void nemo_window_set_active_pane (NemoWindow NemoWindowPane * nemo_window_get_active_pane (NemoWindow *window); gboolean nemo_window_restore_saved_tabs (NemoWindow *window); -void nemo_window_save_session_state_for_quit (NemoWindow *window); +void nemo_window_save_session_state (NemoWindow *window); /* sync window GUI with current slot. Used when changing slots, diff --git a/src/nemo-window.c b/src/nemo-window.c index 42dd9ce18..45d88ab41 100644 --- a/src/nemo-window.c +++ b/src/nemo-window.c @@ -2106,7 +2106,7 @@ collect_pane_saved_tab_uris (NemoWindowPane *pane, gint *active_index_out) return (char **) g_ptr_array_free (arr, FALSE); } -static void +void nemo_window_save_session_state (NemoWindow *window) { NemoWindowPane *left_pane; @@ -2148,12 +2148,6 @@ nemo_window_save_session_state (NemoWindow *window) g_strfreev (right_uris); } -void -nemo_window_save_session_state_for_quit (NemoWindow *window) -{ - nemo_window_save_session_state (window); -} - static void clear_pane_to_single_slot (NemoWindowPane *pane) { From 7b632020637079fdbecae665b0ee24d06547b51d Mon Sep 17 00:00:00 2001 From: kavehtehrani Date: Sat, 20 Dec 2025 00:23:26 -0300 Subject: [PATCH 09/10] option under 'behaviour' in preferences to toggle on restoring tabs --- .../nemo-file-management-properties.glade | 19 +++++++++++++++- libnemo-private/nemo-global-preferences.h | 1 + libnemo-private/org.nemo.gschema.xml | 5 +++++ src/nemo-application.c | 22 +++++++++++++++---- src/nemo-file-management-properties.c | 5 +++++ src/nemo-main-application.c | 5 ++++- src/nemo-window.c | 8 ------- 7 files changed, 51 insertions(+), 14 deletions(-) diff --git a/gresources/nemo-file-management-properties.glade b/gresources/nemo-file-management-properties.glade index 02b8e4cb4..7bb4fffbe 100644 --- a/gresources/nemo-file-management-properties.glade +++ b/gresources/nemo-file-management-properties.glade @@ -1256,6 +1256,23 @@ along with . If not, see . 3 + + + Restore last window tabs on startup + True + True + False + True + 0 + True + + + False + False + 3 + 4 + + Automatically expand rows during drag-and-drop @@ -1269,7 +1286,7 @@ along with . If not, see . False False - 4 + 5 diff --git a/libnemo-private/nemo-global-preferences.h b/libnemo-private/nemo-global-preferences.h index 1a842fa90..91f404854 100644 --- a/libnemo-private/nemo-global-preferences.h +++ b/libnemo-private/nemo-global-preferences.h @@ -144,6 +144,7 @@ typedef enum #define NEMO_PREFERENCES_CLOSE_DEVICE_VIEW_ON_EJECT "close-device-view-on-device-eject" #define NEMO_PREFERENCES_START_WITH_DUAL_PANE "start-with-dual-pane" +#define NEMO_PREFERENCES_RESTORE_TABS_ON_STARTUP "restore-tabs-on-startup" #define NEMO_PREFERENCES_IGNORE_VIEW_METADATA "ignore-view-metadata" #define NEMO_PREFERENCES_SHOW_BOOKMARKS_IN_TO_MENUS "show-bookmarks-in-to-menus" #define NEMO_PREFERENCES_SHOW_PLACES_IN_TO_MENUS "show-places-in-to-menus" diff --git a/libnemo-private/org.nemo.gschema.xml b/libnemo-private/org.nemo.gschema.xml index 59b6a1066..abc2814d4 100644 --- a/libnemo-private/org.nemo.gschema.xml +++ b/libnemo-private/org.nemo.gschema.xml @@ -346,6 +346,11 @@ Whether to default to showing dual-pane view when a new window is opened If set to true, new Nemo windows will default to showing two panes + + false + Restore the previous window tabs on startup + If set to true, Nemo will restore the last saved window tab state when launched without explicit locations. + false Whether to ignore folder metadata for view zoom levels and layouts diff --git a/src/nemo-application.c b/src/nemo-application.c index a5ff1a7fd..422d955ec 100644 --- a/src/nemo-application.c +++ b/src/nemo-application.c @@ -566,12 +566,26 @@ nemo_application_quit (NemoApplication *self) continue; } - gint pane_count = g_list_length (nw->details->panes); + /* Prefer windows that are actually in split view and have more tabs. */ + GtkPaned *paned = GTK_PANED (nw->details->split_view_hpane); + GtkWidget *child1 = gtk_paned_get_child1 (paned); + GtkWidget *child2 = gtk_paned_get_child2 (paned); + + gint pane_count = 0; gint tab_count = 0; - for (GList *p = nw->details->panes; p != NULL; p = p->next) { - NemoWindowPane *pane = p->data; - if (pane != NULL && pane->notebook != NULL) { + if (child1 != NULL) { + NemoWindowPane *pane = NEMO_WINDOW_PANE (child1); + pane_count++; + if (pane->notebook != NULL) { + tab_count += gtk_notebook_get_n_pages (GTK_NOTEBOOK (pane->notebook)); + } + } + + if (child2 != NULL) { + NemoWindowPane *pane = NEMO_WINDOW_PANE (child2); + pane_count++; + if (pane->notebook != NULL) { tab_count += gtk_notebook_get_n_pages (GTK_NOTEBOOK (pane->notebook)); } } diff --git a/src/nemo-file-management-properties.c b/src/nemo-file-management-properties.c index d0a1958e1..1566a467f 100644 --- a/src/nemo-file-management-properties.c +++ b/src/nemo-file-management-properties.c @@ -99,6 +99,7 @@ #define NEMO_FILE_MANAGEMENT_PROPERTIES_DETECT_CONTENT_MEDIA_WIDGET "media_detect_content_checkbutton" #define NEMO_FILE_MANAGEMENT_PROPERTIES_SHOW_ADVANCED_PERMISSIONS_WIDGET "show_advanced_permissions_checkbutton" #define NEMO_FILE_MANAGEMENT_PROPERTIES_START_WITH_DUAL_PANE_WIDGET "start_with_dual_pane_checkbutton" +#define NEMO_FILE_MANAGEMENT_PROPERTIES_RESTORE_TABS_ON_STARTUP_WIDGET "restore_tabs_on_startup_checkbutton" #define NEMO_FILE_MANAGEMENT_PROPERTIES_IGNORE_VIEW_METADATA_WIDGET "ignore_view_metadata_checkbutton" #define NEMO_FILE_MANAGEMENT_PROPERTIES_BOOKMARKS_IN_TO_MENUS_WIDGET "bookmarks_in_to_checkbutton" #define NEMO_FILE_MANAGEMENT_PROPERTIES_PLACES_IN_TO_MENUS_WIDGET "places_in_to_checkbutton" @@ -1067,6 +1068,10 @@ nemo_file_management_properties_dialog_setup (GtkBuilder *builder, NEMO_FILE_MANAGEMENT_PROPERTIES_START_WITH_DUAL_PANE_WIDGET, NEMO_PREFERENCES_START_WITH_DUAL_PANE); + bind_builder_bool (builder, nemo_preferences, + NEMO_FILE_MANAGEMENT_PROPERTIES_RESTORE_TABS_ON_STARTUP_WIDGET, + NEMO_PREFERENCES_RESTORE_TABS_ON_STARTUP); + bind_builder_bool (builder, nemo_preferences, NEMO_FILE_MANAGEMENT_PROPERTIES_IGNORE_VIEW_METADATA_WIDGET, NEMO_PREFERENCES_IGNORE_VIEW_METADATA); diff --git a/src/nemo-main-application.c b/src/nemo-main-application.c index 9cd86ea77..4efe081bd 100644 --- a/src/nemo-main-application.c +++ b/src/nemo-main-application.c @@ -477,6 +477,7 @@ open_windows (NemoMainApplication *application, /* No explicit locations requested: try restoring the last session. */ NemoWindow *window; gboolean have_geometry; + gboolean do_restore; window = nemo_main_application_create_window (NEMO_APPLICATION (application), screen); @@ -493,7 +494,9 @@ open_windows (NemoMainApplication *application, FALSE); } - if (!nemo_window_restore_saved_tabs (window)) { + do_restore = g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_RESTORE_TABS_ON_STARTUP); + + if (!do_restore || !nemo_window_restore_saved_tabs (window)) { /* Fall back to a safe default location */ GFile *home = g_file_new_for_path (g_get_home_dir ()); nemo_window_go_to (window, home); diff --git a/src/nemo-window.c b/src/nemo-window.c index 45d88ab41..c7956eb10 100644 --- a/src/nemo-window.c +++ b/src/nemo-window.c @@ -2185,15 +2185,12 @@ clear_pane_to_single_slot (NemoWindowPane *pane) static void open_uri_list_in_pane (NemoWindowPane *pane, char **uris) { - NemoWindow *window; int i; if (pane == NULL) { return; } - window = pane->window; - /* If no URIs were saved for this pane, leave its first tab alone */ if (uris == NULL || uris[0] == NULL) { return; @@ -2227,11 +2224,6 @@ open_uri_list_in_pane (NemoWindowPane *pane, char **uris) location = g_file_new_for_uri (uris[i]); nemo_window_slot_open_location (slot, location, 0); g_object_unref (location); - - /* Avoid leaving the window's active slot on the last tab we opened */ - if (window != NULL && i == 0) { - nemo_window_set_active_slot (window, slot); - } } } From 3698616da21a36e6d136fe9c240509a3de4c10e5 Mon Sep 17 00:00:00 2001 From: kavehtehrani Date: Mon, 22 Dec 2025 18:51:39 -0300 Subject: [PATCH 10/10] simplified sessions save logic to only save the window that exits --- src/nemo-application.c | 52 ++++-------------------------------------- 1 file changed, 5 insertions(+), 47 deletions(-) diff --git a/src/nemo-application.c b/src/nemo-application.c index 422d955ec..38a8db767 100644 --- a/src/nemo-application.c +++ b/src/nemo-application.c @@ -536,21 +536,9 @@ nemo_application_quit (NemoApplication *self) windows = gtk_application_get_windows (GTK_APPLICATION (app)); /* Save session state once, before we destroy all windows. - * - * We can't rely on gtk_application_get_active_window() here; depending on - * timing/focus it can be NULL or point to non-browser dialogs. - * - * Instead, pick the "best" Nemo window to snapshot: - * - prefer non-desktop windows - * - prefer split-view windows - * - prefer the window with the most tabs across panes - * - * Destroying multiple windows can otherwise overwrite stored state with an - * arbitrary last-destroyed window. */ + * Save the last non-desktop window we find. */ { - NemoWindow *best = NULL; - gint best_pane_count = -1; - gint best_tab_count = -1; + NemoWindow *last_window = NULL; for (GList *l = windows; l != NULL; l = l->next) { GtkWindow *w = GTK_WINDOW (l->data); @@ -566,41 +554,11 @@ nemo_application_quit (NemoApplication *self) continue; } - /* Prefer windows that are actually in split view and have more tabs. */ - GtkPaned *paned = GTK_PANED (nw->details->split_view_hpane); - GtkWidget *child1 = gtk_paned_get_child1 (paned); - GtkWidget *child2 = gtk_paned_get_child2 (paned); - - gint pane_count = 0; - gint tab_count = 0; - - if (child1 != NULL) { - NemoWindowPane *pane = NEMO_WINDOW_PANE (child1); - pane_count++; - if (pane->notebook != NULL) { - tab_count += gtk_notebook_get_n_pages (GTK_NOTEBOOK (pane->notebook)); - } - } - - if (child2 != NULL) { - NemoWindowPane *pane = NEMO_WINDOW_PANE (child2); - pane_count++; - if (pane->notebook != NULL) { - tab_count += gtk_notebook_get_n_pages (GTK_NOTEBOOK (pane->notebook)); - } - } - - /* Prefer more panes first (split view), then more tabs */ - if (pane_count > best_pane_count || - (pane_count == best_pane_count && tab_count > best_tab_count)) { - best = nw; - best_pane_count = pane_count; - best_tab_count = tab_count; - } + last_window = nw; } - if (best != NULL) { - nemo_window_save_session_state (best); + if (last_window != NULL) { + nemo_window_save_session_state (last_window); } }