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 abc2814d4..8603b1e08 100644 --- a/libnemo-private/org.nemo.gschema.xml +++ b/libnemo-private/org.nemo.gschema.xml @@ -717,12 +717,22 @@ [] Saved tab URIs for the left pane - Internal setting used to restore the last closed window's tabs 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 tab URIs for the right pane - Internal setting used to restore the last closed window's tabs 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-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); diff --git a/src/nemo-notebook.c b/src/nemo-notebook.c index a3595f4be..055ae5d9d 100644 --- a/src/nemo-notebook.c +++ b/src/nemo-notebook.c @@ -316,6 +316,39 @@ nemo_notebook_sync_tab_label (NemoNotebook *notebook, } } +void +nemo_notebook_sync_lock (NemoNotebook *notebook, + NemoWindowSlot *slot) +{ + 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)); + + tab_label = gtk_notebook_get_tab_label (GTK_NOTEBOOK (notebook), + GTK_WIDGET (slot)); + if (tab_label == NULL) { + return; + } + + 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 (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 (lock_icon, locked); + gtk_widget_set_visible (close_button, !locked); +} + static void close_button_clicked_cb (GtkWidget *widget, NemoWindowSlot *slot) @@ -331,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; + 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 */ @@ -347,6 +380,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 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); + /* setup label */ label = gtk_label_new (NULL); gtk_label_set_width_chars (GTK_LABEL (label), 8); @@ -383,6 +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), "lock-icon", lock_icon); return hbox; } diff --git a/src/nemo-notebook.h b/src/nemo-notebook.h index ec6bcc4d5..494364dec 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_lock (NemoNotebook *nb, + NemoWindowSlot *slot); void nemo_notebook_reorder_child_relative (NemoNotebook *notebook, int page_num, 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 46f10b8e6..0c9641704 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_is_locked (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..88769d85a 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_is_locked (slot)) { + return; + } + nemo_window_pane_close_slot (pane, slot); } @@ -496,6 +500,62 @@ notebook_popup_menu_close_cb (GtkMenuItem *menuitem, notebook, NEMO_WINDOW_SLOT (page), pane); } +static NemoWindowSlot * +notebook_popup_menu_target_slot (NemoWindowPane *pane) +{ + int num_target_tab; + GtkWidget *page; + + 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; + } + + /* 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 notebook_popup_menu_show (NemoWindowPane *pane, GdkEventButton *event, @@ -507,6 +567,9 @@ notebook_popup_menu_show (NemoWindowPane *pane, int button, event_time; gboolean can_move_left, can_move_right; NemoNotebook *notebook; + GtkWidget *page; + NemoTabLockMode target_lock_mode; + gboolean target_is_locked; notebook = NEMO_NOTEBOOK (pane->notebook); @@ -550,6 +613,36 @@ 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_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_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_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 ()); @@ -560,6 +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_locked); gtk_widget_show_all (popup); @@ -655,6 +749,9 @@ notebook_switch_page_cb (GtkNotebook *notebook, slot = NEMO_WINDOW_SLOT (widget); g_assert (slot != NULL); + /* 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); diff --git a/src/nemo-window-slot.c b/src/nemo-window-slot.c index 4ff9257a4..09a5b0a37 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" @@ -232,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) { @@ -261,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 @@ -519,6 +550,9 @@ nemo_window_slot_dispose (GObject *object) g_free (slot->status_text); slot->status_text = NULL; + g_free (slot->locked_uri); + slot->locked_uri = NULL; + G_OBJECT_CLASS (nemo_window_slot_parent_class)->dispose (object); } @@ -593,6 +627,94 @@ nemo_window_slot_get_location_uri (NemoWindowSlot *slot) return NULL; } +void +nemo_window_slot_set_lock_mode (NemoWindowSlot *slot, + NemoTabLockMode mode, + const char *locked_uri) +{ + char *new_locked_uri = NULL; + + g_return_if_fail (NEMO_IS_WINDOW_SLOT (slot)); + + /* 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) { + new_locked_uri = g_file_get_uri (slot->pending_location); + } else { + new_locked_uri = nemo_window_slot_get_location_uri (slot); + } + } + + 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_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_is_locked (NemoWindowSlot *slot) +{ + g_return_val_if_fail (NEMO_IS_WINDOW_SLOT (slot), FALSE); + + 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 becomes the slot in use again. + */ +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 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..d0b035cb9 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 reselected */ + NEMO_TAB_LOCK_NEW_TAB = 2 /* never navigate, divert changes to a new tab */ +} NemoTabLockMode; + struct NemoWindowSlotClass { GtkBoxClass parent_class; @@ -121,8 +130,14 @@ struct NemoWindowSlot { gboolean visible; - /* Back/Forward chain, and history list. - * The data in these lists are NemoBookmark pointers. + /* 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. + */ + NemoTabLockMode lock_mode; + char *locked_uri; + + /* Back/Forward chain, and history list. + * The data in these lists are NemoBookmark pointers. */ GList *back_list, *forward_list; }; @@ -139,6 +154,15 @@ 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); +/* 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); void nemo_window_slot_force_reload (NemoWindowSlot *slot); diff --git a/src/nemo-window.c b/src/nemo-window.c index 51304a1f9..462d3b1ba 100644 --- a/src/nemo-window.c +++ b/src/nemo-window.c @@ -2050,61 +2050,94 @@ uri_is_native_session_uri (const char *uri) return is_native; } -static char ** -collect_pane_saved_tab_uris (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 tabs_builder; + GArray *locks; 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); + 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; + } - for (i = 0; i < n_pages; i++) { - GtkWidget *page; - NemoWindowSlot *slot; - char *uri; + 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; + } - page = gtk_notebook_get_nth_page (notebook, i); - if (page == NULL) { - continue; - } + if (uri_is_native_session_uri (uri)) { + gint32 mode = (gint32) lock_mode; - slot = NEMO_WINDOW_SLOT (page); - uri = nemo_window_slot_get_location_uri (slot); + 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_ptr_array_add (arr, uri); - saved_index++; - } else { + g_free (uri); } } - g_ptr_array_add (arr, NULL); + /* 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 (char **) g_ptr_array_free (arr, FALSE); + *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 @@ -2112,8 +2145,10 @@ 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; + GVariant *left_locks; + GVariant *right_locks; gint left_active = 0; gint right_active = 0; gboolean split_view; @@ -2135,18 +2170,17 @@ 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); + 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_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_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); - - g_strfreev (left_uris); - g_strfreev (right_uris); } static void @@ -2184,28 +2218,42 @@ 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, GVariant *locks) { - int i; + gsize i, n, n_locks; + 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); + n_locks = locks != NULL ? g_variant_n_children (locks) : 0; - for (i = 0; uris[i] != NULL; i++) { + for (i = 0; i < n; i++) { + const gchar *uri; + gint32 lock_mode = NEMO_TAB_LOCK_NONE; NemoWindowSlot *slot; GFile *location; - if (!uri_is_native_session_uri (uris[i])) { + 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; } - if (i == 0) { + 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; if (slot == NULL && pane->notebook != NULL) { @@ -2222,9 +2270,18 @@ 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); + + /* 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++; } } @@ -2233,8 +2290,10 @@ 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; + GVariant *left_locks; + GVariant *right_locks; gint left_active; gint right_active; gboolean want_split; @@ -2247,21 +2306,25 @@ 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_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); - 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); + g_variant_unref (left_locks); + g_variant_unref (right_locks); 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 +2346,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, left_locks); } - open_uri_list_in_pane (right_pane, right_uris); + open_saved_tabs_in_pane (right_pane, right_tabs, right_locks); /* Restore active tabs (clamp indices) */ if (left_pane != NULL && left_pane->notebook != NULL) { @@ -2317,8 +2380,10 @@ 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); + g_variant_unref (left_locks); + g_variant_unref (right_locks); return TRUE; }