Merge lp:~tuxator/midori/tabs2one into lp:midori
- tabs2one
- Merge into trunk
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 |
Related bugs: |
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
Description of the change
To post a comment you must log in.
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 | +} |
Let's get this in.