Merge lp:~tuxator/midori/tabs2one into lp:midori

Proposed by Paweł Forysiuk
Status: Merged
Approved by: Cris Dywan
Approved revision: 6194
Merged at revision: 6900
Proposed branch: lp:~tuxator/midori/tabs2one
Merge into: lp:midori
Diff against target: 414 lines (+410/-0)
1 file modified
extensions/tabs2one.c (+410/-0)
To merge this branch: bzr merge lp:~tuxator/midori/tabs2one
Reviewer Review Type Date Requested Status
Cris Dywan Approve
André Stösel Pending
Review via email: mp+166593@code.launchpad.net

Commit message

Implement Tabs2One extension

To post a comment you must log in.
Revision history for this message
Cris Dywan (kalikiana) wrote :

Let's get this in.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'extensions/tabs2one.c'
2--- extensions/tabs2one.c 1970-01-01 00:00:00 +0000
3+++ extensions/tabs2one.c 2014-08-16 12:58:37 +0000
4@@ -0,0 +1,410 @@
5+/*
6+ Copyright (C) 2013 Eder Sosa <eder.sohe@gmail.com>
7+
8+ This library is free software; you can redistribute it and/or
9+ modify it under the terms of the GNU Lesser General Public
10+ License as published by the Free Software Foundation; either
11+ version 2.1 of the License, or (at your option) any later version.
12+
13+ See the file COPYING for the full license text.
14+*/
15+
16+#include <midori/midori.h>
17+
18+typedef struct
19+{
20+ gchar* folder;
21+ gchar* filename;
22+ gchar* uri;
23+} tabs2onePriv;
24+
25+static tabs2onePriv* priv;
26+
27+tabs2onePriv*
28+tabs2one_private_new ()
29+{
30+ tabs2onePriv* priv = g_slice_new (tabs2onePriv);
31+ priv->folder = NULL;
32+ priv->filename = NULL;
33+ priv->uri = NULL;
34+
35+ return priv;
36+}
37+
38+void tabs2one_private_destroy (tabs2onePriv* priv)
39+{
40+ katze_assign (priv->folder, NULL);
41+ katze_assign (priv->filename, NULL);
42+ katze_assign (priv->uri, NULL);
43+ g_slice_free (tabs2onePriv, priv);
44+}
45+
46+static void
47+tabs2one_dom_click_restore_item_cb (WebKitDOMNode *element,
48+ WebKitDOMEvent *dom_event,
49+ WebKitWebView *webview);
50+
51+static void
52+tabs2one_dom_click_remove_item_cb (WebKitDOMNode *element,
53+ WebKitDOMEvent *dom_event,
54+ WebKitWebView *webview);
55+
56+static gchar*
57+tabs2one_id_generator (void)
58+{
59+ GString* id = g_string_new("");
60+ GRand* generator = g_rand_new();
61+ gint32 id1 = g_rand_int_range (generator, 1000, 9999);
62+ gint32 id2 = g_rand_int_range (generator, 1000, 9999);
63+ g_rand_free(generator);
64+ g_string_printf(id, "%i-%i", id1, id2);
65+ return g_string_free(id, FALSE);
66+}
67+
68+static void
69+tabs2one_dom_create_item (WebKitDOMDocument* doc,
70+ const gchar* icon,
71+ const gchar* uri,
72+ const gchar* title)
73+{
74+ WebKitDOMElement* body = webkit_dom_document_query_selector(doc, "body", NULL);
75+ WebKitDOMElement* item = webkit_dom_document_create_element(doc, "div", NULL);
76+ WebKitDOMElement* favicon = webkit_dom_document_create_element(doc, "img", NULL);
77+ WebKitDOMElement* link = webkit_dom_document_create_element(doc, "a", NULL);
78+ WebKitDOMElement* close_link = webkit_dom_document_create_element(doc, "a", NULL);
79+ WebKitDOMElement* close_button = webkit_dom_document_create_element (doc, "img", NULL);
80+ WebKitDOMElement* br = webkit_dom_document_create_element(doc, "br", NULL);
81+ WebKitDOMText* content = webkit_dom_document_create_text_node(doc, title);
82+
83+ webkit_dom_element_set_attribute(item, "id", tabs2one_id_generator(), NULL);
84+ webkit_dom_element_set_attribute(item, "class", "item", NULL);
85+ webkit_dom_element_set_attribute(item, "style", "padding: 5px;", NULL);
86+
87+ webkit_dom_element_set_attribute(close_link, "class", "close-link", NULL);
88+ webkit_dom_element_set_attribute(close_link, "target", "_blank", NULL);
89+ webkit_dom_element_set_attribute(close_link, "href", "about:blank", NULL);
90+ webkit_dom_element_set_attribute(close_button, "src", "res://close.png", NULL);
91+ webkit_dom_element_set_attribute(close_button, "width", "16px", NULL);
92+ webkit_dom_element_set_attribute(close_button, "height", "16px", NULL);
93+
94+ webkit_dom_element_set_attribute(favicon, "src", icon, NULL);
95+ webkit_dom_element_set_attribute(favicon, "width", "16px", NULL);
96+ webkit_dom_element_set_attribute(favicon, "height", "16px", NULL);
97+ webkit_dom_element_set_attribute(favicon, "style", "padding-left: 5px;", NULL);
98+
99+ webkit_dom_element_set_attribute(link, "class", "restore-link",NULL);
100+ webkit_dom_element_set_attribute(link, "href", uri, NULL);
101+ webkit_dom_element_set_attribute(link, "style", "padding-left: 5px;", NULL);
102+ webkit_dom_element_set_attribute(link, "target", "_blank", NULL);
103+
104+ webkit_dom_node_append_child(WEBKIT_DOM_NODE(link), WEBKIT_DOM_NODE(content), NULL);
105+ webkit_dom_node_append_child(WEBKIT_DOM_NODE(item), WEBKIT_DOM_NODE(close_link), NULL);
106+ webkit_dom_node_append_child(WEBKIT_DOM_NODE(close_link), WEBKIT_DOM_NODE(close_button), NULL);
107+ webkit_dom_node_append_child(WEBKIT_DOM_NODE(item), WEBKIT_DOM_NODE(favicon), NULL);
108+ webkit_dom_node_append_child(WEBKIT_DOM_NODE(item), WEBKIT_DOM_NODE(link), NULL);
109+ webkit_dom_node_append_child(WEBKIT_DOM_NODE(item), WEBKIT_DOM_NODE(br), NULL);
110+ webkit_dom_node_append_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(item), NULL);
111+}
112+
113+static void
114+tabs2one_cache_create_dir (void)
115+{
116+ midori_paths_mkdir_with_parents (priv->folder, 0700);
117+}
118+
119+static gchar*
120+tabs2one_cache_get_filename (void)
121+{
122+ return priv->filename;
123+}
124+
125+static gchar*
126+tabs2one_cache_get_uri (void)
127+{
128+ return priv->uri;
129+}
130+
131+static bool
132+tabs2one_cache_exist (void)
133+{
134+ return g_file_test (priv->filename, G_FILE_TEST_EXISTS);
135+}
136+
137+static gboolean
138+tabs2one_is_uri_tabs2one_tab (const gchar* uri)
139+{
140+ return g_str_equal (uri, tabs2one_cache_get_uri ());
141+}
142+
143+static void
144+tabs2one_dom_add_click_listeners (WebKitDOMDocument* doc,
145+ WebKitWebView* webview)
146+{
147+ WebKitDOMNodeList *elements = webkit_dom_document_query_selector_all(doc, ".restore-link", NULL);
148+
149+ int i;
150+
151+ for (i = 0; i < webkit_dom_node_list_get_length(elements); i++)
152+ {
153+ WebKitDOMNode *element = webkit_dom_node_list_item(elements, i);
154+ webkit_dom_event_target_add_event_listener(
155+ WEBKIT_DOM_EVENT_TARGET(element), "click",
156+ G_CALLBACK (tabs2one_dom_click_restore_item_cb), TRUE, webview);
157+ }
158+
159+ elements = webkit_dom_document_query_selector_all(doc, ".close-link", NULL);
160+ for (i = 0; i < webkit_dom_node_list_get_length(elements); i++)
161+ {
162+ WebKitDOMNode *element = webkit_dom_node_list_item(elements, i);
163+ webkit_dom_event_target_add_event_listener(
164+ WEBKIT_DOM_EVENT_TARGET(element), "click",
165+ G_CALLBACK (tabs2one_dom_click_remove_item_cb), TRUE, webview);
166+ }
167+}
168+
169+static bool
170+tabs2one_cache_write_file (WebKitWebView* webview)
171+{
172+ WebKitDOMDocument* doc = webkit_web_view_get_dom_document(webview);
173+ WebKitDOMHTMLDocument* dochtml = (WebKitDOMHTMLDocument*)doc;
174+ WebKitDOMHTMLElement* elementhtml = (WebKitDOMHTMLElement*)dochtml;
175+ const gchar* content = webkit_dom_html_element_get_inner_html(elementhtml);
176+ if (content == NULL)
177+ return FALSE;
178+
179+ tabs2one_dom_add_click_listeners (doc, webview);
180+
181+ return g_file_set_contents(tabs2one_cache_get_filename (), content, -1, NULL);
182+}
183+
184+static void
185+tabs2one_onload_create_items_cb(WebKitWebView* webview,
186+ WebKitWebFrame* webframe,
187+ MidoriBrowser* browser)
188+{
189+ WebKitDOMDocument* doc = webkit_web_view_get_dom_document(webview);
190+
191+ const gchar* icon;
192+ const gchar* title;
193+ const gchar* uri;
194+
195+ GList* tabs = midori_browser_get_tabs (browser);
196+ for (; tabs; tabs = g_list_next (tabs))
197+ {
198+ icon = midori_view_get_icon_uri (tabs->data);
199+ if (icon == NULL) icon = "";
200+ title = midori_view_get_display_title (tabs->data);
201+ uri = midori_view_get_display_uri (tabs->data);
202+
203+ if (!tabs2one_is_uri_tabs2one_tab (uri))
204+ {
205+ if (!midori_uri_is_blank (uri))
206+ tabs2one_dom_create_item(doc, icon, uri, title);
207+ }
208+ }
209+
210+
211+ if (tabs2one_cache_write_file (webview))
212+ {
213+ tabs = midori_browser_get_tabs (browser);
214+ while ((tabs = g_list_next (tabs)))
215+ midori_browser_close_tab (browser, tabs->data);
216+
217+ tabs2one_dom_add_click_listeners (doc, webview);
218+ midori_browser_set_current_uri (browser, tabs2one_cache_get_uri ());
219+ }
220+
221+ g_list_free(tabs);
222+}
223+
224+static void
225+tabs2one_reload_connected_events_cb(WebKitWebView* webview,
226+ WebKitWebFrame* frame,
227+ MidoriView* view)
228+{
229+ const gchar* uri = midori_view_get_display_uri(view);
230+
231+ if (tabs2one_is_uri_tabs2one_tab (uri))
232+ {
233+ WebKitDOMDocument* doc = webkit_web_view_get_dom_document(webview);
234+ tabs2one_dom_add_click_listeners (doc, webview);
235+ }
236+}
237+
238+
239+static void
240+tabs2one_add_tab_cb (MidoriBrowser* browser,
241+ MidoriView* view,
242+ MidoriExtension* extension)
243+{
244+ WebKitWebView* webview = WEBKIT_WEB_VIEW (midori_view_get_web_view(view));
245+ g_signal_connect (webview, "document-load-finished",
246+ G_CALLBACK (tabs2one_reload_connected_events_cb), view);
247+}
248+
249+static void
250+tabs2one_dom_click_restore_item_cb (WebKitDOMNode *element,
251+ WebKitDOMEvent *dom_event,
252+ WebKitWebView *webview)
253+{
254+ webkit_dom_event_prevent_default (dom_event);
255+ MidoriView* view = midori_view_get_for_widget (GTK_WIDGET (webview));
256+ MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (webview));
257+ WebKitDOMNode* item = webkit_dom_node_get_parent_node (element);
258+ WebKitDOMNode* body = webkit_dom_node_get_parent_node (item);
259+ const gchar* uri = webkit_dom_element_get_attribute(WEBKIT_DOM_ELEMENT(element), "href");
260+ midori_browser_add_uri (browser, uri);
261+
262+ WebKitDOMDocument* doc = webkit_web_view_get_dom_document (webview);
263+ webkit_dom_node_remove_child(body, item, NULL);
264+ tabs2one_cache_write_file (webview);
265+
266+ WebKitDOMNodeList *elements = webkit_dom_document_query_selector_all(doc, ".restore-link", NULL);
267+ if (webkit_dom_node_list_get_length(elements) <= 0){
268+ midori_browser_close_tab(browser, GTK_WIDGET(view));
269+ }
270+}
271+
272+static void
273+tabs2one_dom_click_remove_item_cb (WebKitDOMNode *element,
274+ WebKitDOMEvent *dom_event,
275+ WebKitWebView *webview)
276+{
277+ webkit_dom_event_prevent_default (dom_event);
278+ MidoriView* view = midori_view_get_for_widget (GTK_WIDGET (webview));
279+ MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (webview));
280+ WebKitDOMNode* item = webkit_dom_node_get_parent_node (element);
281+ WebKitDOMNode* body = webkit_dom_node_get_parent_node (item);
282+
283+ WebKitDOMDocument* doc = webkit_web_view_get_dom_document (webview);
284+ webkit_dom_node_remove_child(body, item, NULL);
285+ tabs2one_cache_write_file (webview);
286+
287+ WebKitDOMNodeList *elements = webkit_dom_document_query_selector_all(doc, ".close-link", NULL);
288+ if (webkit_dom_node_list_get_length(elements) <= 0){
289+ midori_browser_close_tab(browser, GTK_WIDGET(view));
290+ }
291+}
292+
293+static void
294+tabs2one_apply_cb (GtkWidget* menuitem,
295+ MidoriBrowser* browser)
296+{
297+ GtkWidget* tab = NULL;
298+
299+ if (!tabs2one_cache_exist ())
300+ {
301+ GString* tpl = g_string_new ("<html>\n<title>");
302+
303+ g_string_append_printf (tpl, "%s", _("Tabs to One"));
304+ g_string_append (tpl, "</title>\n<head><meta charset=\"utf-8\"></head><body>\n");
305+ g_string_append_printf (tpl, "<h2>%s</h2>\n", _("Tabs you collected so far"));
306+ g_string_append_printf (tpl, "<div><span>%s</span></div>\n", _("Clicking an item restores a tab."));
307+ g_string_append (tpl, "</body>\n</html>\n");
308+
309+ g_file_set_contents(tabs2one_cache_get_filename (), g_string_free (tpl, FALSE), -1, NULL);
310+ }
311+
312+ tab = midori_browser_add_uri (browser, tabs2one_cache_get_uri ());
313+
314+ WebKitWebView* webview = WEBKIT_WEB_VIEW (midori_view_get_web_view(MIDORI_VIEW (tab)));
315+ midori_browser_set_current_tab (browser, tab);
316+
317+ g_signal_connect (webview, "document-load-finished",
318+ G_CALLBACK (tabs2one_onload_create_items_cb), browser);
319+}
320+
321+static void
322+tabs2one_app_add_browser_cb (MidoriApp* app,
323+ MidoriBrowser* browser,
324+ MidoriExtension* extension);
325+
326+static void
327+tabs2one_browser_populate_tool_menu_cb (MidoriBrowser* browser,
328+ GtkWidget* menu,
329+ MidoriExtension* extension)
330+{
331+ GtkWidget* menuitem = gtk_menu_item_new_with_mnemonic (_("Tabs to _One"));
332+
333+ g_signal_connect (menuitem, "activate",
334+ G_CALLBACK (tabs2one_apply_cb), browser);
335+ gtk_widget_show (menuitem);
336+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
337+}
338+
339+static void
340+tabs2one_deactivate_cb (MidoriExtension* extension,
341+ MidoriBrowser* browser)
342+{
343+ MidoriApp* app = midori_extension_get_app (extension);
344+
345+ g_signal_handlers_disconnect_by_func (
346+ browser, tabs2one_browser_populate_tool_menu_cb, extension);
347+ g_signal_handlers_disconnect_by_func (
348+ extension, tabs2one_deactivate_cb, browser);
349+ g_signal_handlers_disconnect_by_func (
350+ app, tabs2one_app_add_browser_cb, extension);
351+ g_signal_handlers_disconnect_by_func (
352+ browser, tabs2one_add_tab_cb, extension);
353+
354+ tabs2one_private_destroy (priv);
355+}
356+
357+static void
358+tabs2one_app_add_browser_cb (MidoriApp* app,
359+ MidoriBrowser* browser,
360+ MidoriExtension* extension)
361+{
362+ g_signal_connect (browser, "populate-tool-menu",
363+ G_CALLBACK (tabs2one_browser_populate_tool_menu_cb), extension);
364+ g_signal_connect (extension, "deactivate",
365+ G_CALLBACK (tabs2one_deactivate_cb), browser);
366+ g_signal_connect_after (browser, "add-tab",
367+ G_CALLBACK (tabs2one_add_tab_cb), extension);
368+
369+ tabs2one_cache_create_dir();
370+}
371+
372+static void
373+tabs2one_activate_cb (MidoriExtension* extension,
374+ MidoriApp* app)
375+{
376+ const gchar* config_dir = midori_extension_get_config_dir (extension);
377+ priv = tabs2one_private_new ();
378+ KatzeArray* browsers;
379+ MidoriBrowser* browser;
380+ gchar* filename, *uri;
381+
382+ filename = g_build_filename (config_dir, "tabs2one.html", NULL);
383+ uri = g_filename_to_uri (filename, NULL, NULL);
384+
385+ priv->filename = g_strdup (filename);
386+ priv->uri = g_strdup (uri);
387+ priv->folder = g_strdup (config_dir);
388+
389+ browsers = katze_object_get_object (app, "browsers");
390+ KATZE_ARRAY_FOREACH_ITEM (browser, browsers)
391+ tabs2one_app_add_browser_cb (app, browser, extension);
392+ g_object_unref (browsers);
393+ g_signal_connect (app, "add-browser",
394+ G_CALLBACK (tabs2one_app_add_browser_cb), extension);
395+
396+ g_free (filename);
397+ g_free (uri);
398+}
399+
400+MidoriExtension*
401+extension_init (void)
402+{
403+ MidoriExtension* extension = g_object_new (MIDORI_TYPE_EXTENSION,
404+ "name", _("Tabs to One"),
405+ "description", _("Closes all open tabs and creates new tab with tabs links"),
406+ "version", "0.1" MIDORI_VERSION_SUFFIX,
407+ "authors", "Eder Sosa <eder.sohe@gmail.com>",
408+ NULL);
409+
410+ g_signal_connect (extension, "activate",
411+ G_CALLBACK (tabs2one_activate_cb), NULL);
412+
413+ return extension;
414+}

Subscribers

People subscribed via source and target branches

to all changes: