[epiphany] ephy-session: add API to restore closed tabs



commit f0907d8fe648b6b8d345ca6434ddbe2cf90f1fc5
Author: Claudio Saavedra <csaavedra igalia com>
Date:   Mon Jan 28 12:55:31 2013 +0200

    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 <diegoe igalia com>
    
    https://bugzilla.gnome.org/show_bug.cgi?id=128184

 src/ephy-session.c |  249 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ephy-session.h |    2 +
 2 files changed, 251 insertions(+), 0 deletions(-)
---
diff --git a/src/ephy-session.c b/src/ephy-session.c
index b9d9c4d..fecfee2 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 6f3b36c..e6d0ab2 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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]