From f0907d8fe648b6b8d345ca6434ddbe2cf90f1fc5 Mon Sep 17 00:00:00 2001 From: Claudio Saavedra Date: Mon, 28 Jan 2013 12:55:31 +0200 Subject: ephy-session: add API to restore closed tabs We add a queue of closed tabs to EphySession, which is later used to restore them through ephy_session_undo_close_tab(). Based on a patch by Diego Escalante Urrelo https://bugzilla.gnome.org/show_bug.cgi?id=128184 --- src/ephy-session.c | 249 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ephy-session.h | 2 + 2 files changed, 251 insertions(+) (limited to 'src') diff --git a/src/ephy-session.c b/src/ephy-session.c index b9d9c4d50..fecfee236 100644 --- a/src/ephy-session.c +++ b/src/ephy-session.c @@ -30,6 +30,7 @@ #include "ephy-embed.h" #include "ephy-file-helpers.h" #include "ephy-gui.h" +#include "ephy-notebook.h" #include "ephy-prefs.h" #include "ephy-settings.h" #include "ephy-shell.h" @@ -43,13 +44,23 @@ #define EPHY_SESSION_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_SESSION, EphySessionPrivate)) +typedef struct +{ + gpointer* parent_location; + int position; + char *url; + GList *bflist; +} ClosedTab; + struct _EphySessionPrivate { + GQueue *closed_tabs; GCancellable *save_cancellable; guint dont_save : 1; }; #define SESSION_STATE "type:session_state" +#define MAX_CLOSED_TABS 10 G_DEFINE_TYPE (EphySession, ephy_session, G_TYPE_OBJECT) @@ -121,6 +132,242 @@ load_status_notify_cb (EphyWebView *view, } #endif +static gpointer * +parent_location_new (EphyNotebook *notebook) +{ + gpointer *location = g_slice_new (gpointer); + *location = notebook; + g_object_add_weak_pointer (G_OBJECT (notebook), location); + + return location; +} + +static void +parent_location_free (gpointer *location, gboolean last_reference) +{ + if (!location) + return; + + if (*location && last_reference) + { + g_object_remove_weak_pointer (G_OBJECT (*location), location); + } + + g_slice_free (gpointer, location); +} + +static void +closed_tab_free (ClosedTab *tab) +{ + if (tab->bflist) + { + g_list_free_full (tab->bflist, g_object_unref); + tab->bflist = NULL; + } + + if (tab->url) + { + g_free (tab->url); + tab->url = NULL; + } + + g_slice_free (ClosedTab, tab); +} + +static int +compare_func (ClosedTab *iter, EphyNotebook *notebook) +{ + return (EphyNotebook *)*iter->parent_location - notebook; +} + +static ClosedTab * +find_tab_with_notebook (GQueue *queue, EphyNotebook *notebook) +{ + GList *item = g_queue_find_custom (queue, notebook, (GCompareFunc)compare_func); + return item ? (ClosedTab*)item->data : NULL; +} + +static ClosedTab * +closed_tab_new (GQueue *closed_tabs, + const char *address, + GList *bflist, + int position, + EphyNotebook *parent_notebook) +{ + ClosedTab *tab = g_slice_new0 (ClosedTab); + ClosedTab *sibling_tab; + + tab->url = g_strdup (address); +#ifndef HAVE_WEBKIT2 + tab->bflist = g_list_copy_deep (bflist, (GCopyFunc)webkit_web_history_item_copy, NULL); +#endif + tab->position = position; + + sibling_tab = find_tab_with_notebook (closed_tabs, parent_notebook); + if (sibling_tab) + tab->parent_location = sibling_tab->parent_location; + else + tab->parent_location = parent_location_new (parent_notebook); + + return tab; +} + +static void +post_restore_cleanup (GQueue *closed_tabs, ClosedTab *restored_tab, gboolean notebook_is_new) +{ + + if (find_tab_with_notebook (closed_tabs, *restored_tab->parent_location)) + { + if (notebook_is_new == TRUE) + { + /* If this is a newly opened notebook and + there are other tabs that must be restored + here, add a weak poiner to keep track of + the lifetime of it. */ + g_object_add_weak_pointer (G_OBJECT (*restored_tab->parent_location), + restored_tab->parent_location); + } + } + else + { + /* If there are no other tabs that must be restored to this notebook, + we can remove the pointer keeping track of its location. + If this is a new window, we don't need to remove any weak + pointer, as no one has been added yet. */ + parent_location_free (restored_tab->parent_location, !notebook_is_new); + } +} + +void +ephy_session_undo_close_tab (EphySession *session) +{ + EphySessionPrivate *priv; + EphyEmbed *embed, *new_tab; + ClosedTab *tab; +#ifndef HAVE_WEBKIT2 + WebKitWebBackForwardList *dest; + GList *i; +#endif + EphyNewTabFlags flags = EPHY_NEW_TAB_OPEN_PAGE + | EPHY_NEW_TAB_PRESENT_WINDOW + | EPHY_NEW_TAB_JUMP + | EPHY_NEW_TAB_DONT_COPY_HISTORY; + + g_return_if_fail (EPHY_IS_SESSION (session)); + + priv = session->priv; + + tab = g_queue_pop_head (priv->closed_tabs); + if (tab == NULL) + return; + + LOG ("UNDO CLOSE TAB: %s", tab->url); + if (*tab->parent_location != NULL) + { + GtkWidget *window; + + flags |= EPHY_NEW_TAB_IN_EXISTING_WINDOW; + + if (tab->position > 0) + { + /* Append in the n-th position. */ + embed = EPHY_EMBED (gtk_notebook_get_nth_page (GTK_NOTEBOOK (*tab->parent_location), + tab->position - 1)); + flags |= EPHY_NEW_TAB_APPEND_AFTER; + } + else + { + /* Just prepend in the first position. */ + embed = NULL; + flags |= EPHY_NEW_TAB_FIRST; + } + + window = gtk_widget_get_toplevel (GTK_WIDGET (*tab->parent_location)); + new_tab = ephy_shell_new_tab (ephy_shell_get_default (), + EPHY_WINDOW (window), embed, tab->url, + flags); + post_restore_cleanup (priv->closed_tabs, tab, FALSE); + } + else + { + EphyNotebook *notebook; + flags |= EPHY_NEW_TAB_IN_NEW_WINDOW; + new_tab = ephy_shell_new_tab (ephy_shell_get_default (), + NULL, NULL, tab->url, flags); + + /* FIXME: This makes the assumption that the notebook + is the parent of the returned EphyEmbed. */ + notebook = EPHY_NOTEBOOK (gtk_widget_get_parent (GTK_WIDGET (new_tab))); + *tab->parent_location = notebook; + post_restore_cleanup (priv->closed_tabs, tab, TRUE); + } + + /* This is deficient: we need to recreate the whole + * BackForward list. Also, WebKit2 doesn't have this API. */ +#ifndef HAVE_WEBKIT2 + dest = webkit_web_view_get_back_forward_list (EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (new_tab)); + for (i = tab->bflist; i; i = i->next) + { + LOG ("ADDING TO BF: %s", + webkit_web_history_item_get_title ((WebKitWebHistoryItem*) i->data)); + webkit_web_back_forward_list_add_item (dest, + webkit_web_history_item_copy ((WebKitWebHistoryItem*) i->data)); + } +#endif + closed_tab_free (tab); +} + +static void +ephy_session_tab_closed (EphySession *session, + EphyNotebook *notebook, + EphyEmbed *embed, + gint position) +{ + EphySessionPrivate *priv = session->priv; + EphyWebView *view; + const char *address; +#ifdef HAVE_WEBKIT2 + WebKitBackForwardList *source; +#else + WebKitWebBackForwardList *source; +#endif + ClosedTab *tab; + GList *items = NULL; + + view = ephy_embed_get_web_view (embed); + address = ephy_web_view_get_address (view); + + source = webkit_web_view_get_back_forward_list (WEBKIT_WEB_VIEW (view)); +#ifdef HAVE_WEBKIT2 + items = webkit_back_forward_list_get_back_list_with_limit (source, EPHY_WEBKIT_BACK_FORWARD_LIMIT); +#else + items = webkit_web_back_forward_list_get_back_list_with_limit (source, EPHY_WEBKIT_BACK_FORWARD_LIMIT); +#endif + if (items == NULL && g_strcmp0 (address, "ephy-about:overview") == 0) + return; + + if (g_queue_get_length (priv->closed_tabs) == MAX_CLOSED_TABS) + { + tab = g_queue_pop_tail (priv->closed_tabs); + if (tab->parent_location && !find_tab_with_notebook (priv->closed_tabs, *tab->parent_location)) + { + parent_location_free (tab->parent_location, TRUE); + } + + closed_tab_free (tab); + tab = NULL; + } + + items = g_list_reverse (items); + tab = closed_tab_new (priv->closed_tabs, address, items, position, notebook); + g_list_free (items); + + g_queue_push_head (priv->closed_tabs, tab); + + LOG ("Added: %s to the list (%d elements)", + address, g_queue_get_legth (priv->closed_tabs)); +} + static void notebook_page_added_cb (GtkWidget *notebook, EphyEmbed *embed, @@ -153,6 +400,7 @@ notebook_page_removed_cb (GtkWidget *notebook, (ephy_embed_get_web_view (embed), G_CALLBACK (load_status_notify_cb), session); #endif + ephy_session_tab_closed (session, EPHY_NOTEBOOK (notebook), embed, position); } static void @@ -247,6 +495,7 @@ ephy_session_init (EphySession *session) session->priv = EPHY_SESSION_GET_PRIVATE (session); + session->priv->closed_tabs = g_queue_new (); shell = ephy_shell_get_default (); g_signal_connect (shell, "window-added", G_CALLBACK (window_added_cb), session); diff --git a/src/ephy-session.h b/src/ephy-session.h index 6f3b36c4e..e6d0ab253 100644 --- a/src/ephy-session.h +++ b/src/ephy-session.h @@ -95,6 +95,8 @@ gboolean ephy_session_resume_finish (EphySession *session, void ephy_session_close (EphySession *session); +void ephy_session_undo_close_tab (EphySession *session); + G_END_DECLS #endif -- cgit v1.2.3