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;
}