Merge lp:~elementary-apps/midori/browser-vala into lp:midori

Proposed by Tom Beckmann
Status: Work in progress
Proposed branch: lp:~elementary-apps/midori/browser-vala
Merge into: lp:midori
Diff against target: 7009 lines (+5330/-1305) (has conflicts)
17 files modified
CMakeLists.txt (+25/-6)
katze/midori-paths.vala (+0/-1)
midori/CMakeLists.txt (+5/-2)
midori/midori-app.h (+1/-1)
midori/midori-browser.h (+0/-199)
midori/midori-browser.vala (+5053/-1002)
midori/midori-download.vala (+1/-5)
midori/midori-privatedata.h (+1/-1)
midori/midori-speeddial.vala (+0/-4)
midori/midori-view.c (+1/-1)
midori/midori.h (+0/-1)
vapi/gobject-fixes.vapi (+23/-0)
vapi/gtk-fixes.vapi (+27/-0)
vapi/katze.vapi (+44/-4)
vapi/midori.vapi (+147/-77)
vapi/webkit2gtk-3.0.vapi (+1/-1)
vapi/webkitgtk-3.0.vapi (+1/-0)
Text conflict in midori/midori-browser.vala
To merge this branch: bzr merge lp:~elementary-apps/midori/browser-vala
Reviewer Review Type Date Requested Status
Cris Dywan Pending
Review via email: mp+231592@code.launchpad.net

Description of the change

This is a port of midori/midori-browser.c to vala, currently still WIP due to a valac exception causing compilation to fail.

To post a comment you must log in.
6778. By Tom Beckmann

Fix some errors, copy gtk and gobject vapis to local source to override faults in
GtkActionCallback and ParamSpec* type id bindings

6779. By Tom Beckmann

Removed copies of gobject and gtk vapis in favor of minimal fixes vapis. Moved all vapis
to a vapi directory

Unmerged revisions

6779. By Tom Beckmann

Removed copies of gobject and gtk vapis in favor of minimal fixes vapis. Moved all vapis
to a vapi directory

6778. By Tom Beckmann

Fix some errors, copy gtk and gobject vapis to local source to override faults in
GtkActionCallback and ParamSpec* type id bindings

6777. By Tom Beckmann

Fix all errors, one valac crash remaining

6776. By Tom Beckmann

Start porting midori-browser.c to vala

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-07-11 01:36:48 +0000
3+++ CMakeLists.txt 2014-09-01 23:11:13 +0000
4@@ -212,13 +212,11 @@
5 add_definitions("-DHAVE_WEBKIT2")
6 add_definitions("-DGTK_VERSION=\"${DEPS_GTK_gtk+-3.0_VERSION}\"")
7 add_definitions("-DWEBKIT_VERSION=\"${DEPS_GTK_webkit2gtk-3.0_VERSION}\"")
8- set(PKGS ${PKGS} gtk+-3.0)
9+ set(PKGS ${PKGS} gtk+-3.0 webkit2gtk-3.0)
10 set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkit2gtk-3.0.vapi")
11 set(VALAFLAGS ${VALAFLAGS} -D HAVE_GTK3)
12 set(VALAFLAGS ${VALAFLAGS} -D HAVE_WEBKIT2)
13- if (${DEPS_GTK_webkit2gtk-3.0_VERSION} VERSION_GREATER "2.3.90")
14- set(VALAFLAGS ${VALAFLAGS} -D HAVE_WEBKIT2_3_91)
15- endif ()
16+ set(VALAFLAGS ${VALAFLAGS} -D HAVE_WEBKIT2_3_91)
17 elseif (USE_GTK3)
18 pkg_check_modules(DEPS_GTK REQUIRED
19 gtk+-3.0>=3.0.0
20@@ -227,7 +225,7 @@
21 )
22 add_definitions("-DGTK_VERSION=\"${DEPS_GTK_gtk+-3.0_VERSION}\"")
23 add_definitions("-DWEBKIT_VERSION=\"${DEPS_GTK_webkitgtk-3.0_VERSION}\"")
24- set(PKGS ${PKGS} gtk+-3.0)
25+ set(PKGS ${PKGS} gtk+-3.0 webkitgtk-3.0)
26 set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkitgtk-3.0.vapi")
27 set(VALAFLAGS ${VALAFLAGS} -D HAVE_GTK3)
28 else ()
29@@ -238,10 +236,31 @@
30 )
31 add_definitions("-DGTK_VERSION=\"${DEPS_GTK_gtk+-2.0_VERSION}\"")
32 add_definitions("-DWEBKIT_VERSION=\"${DEPS_GTK_webkit-1.0_VERSION}\"")
33- set(PKGS ${PKGS} gtk+-2.0)
34+ set(PKGS ${PKGS} gtk+-2.0 webkit-1.0)
35 set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkitgtk-3.0.vapi")
36 endif ()
37
38+# vala has no GTK_CHECK_VERSION, so we need to cheat
39+pkg_check_modules(GTK_3_1_10 gtk+-3.0>=3.0.0)
40+if (GTK_3_0_0_FOUND)
41+ set(VALAFLAGS ${VALAFLAGS} -D GTK_VERSION_3_0_0)
42+endif()
43+
44+pkg_check_modules(GTK_3_4_0 gtk+-3.0>=3.4.0)
45+if (GTK_3_4_0_FOUND)
46+ set(VALAFLAGS ${VALAFLAGS} -D GTK_VERSION_3_4_0)
47+endif()
48+
49+pkg_check_modules(GTK_3_2_0 gtk+-3.0>=3.2.0)
50+if (GTK_3_2_0_FOUND)
51+ set(VALAFLAGS ${VALAFLAGS} -D GTK_VERSION_3_2_0)
52+endif()
53+
54+pkg_check_modules(GTK_3_1_10 gtk+-3.0>=3.1.10)
55+if (GTK_3_1_10_FOUND)
56+ set(VALAFLAGS ${VALAFLAGS} -D GTK_VERSION_3_1_10)
57+endif()
58+
59 # dh_translations detects this if there's no variable used
60 set (GETTEXT_PACKAGE "midori")
61 add_definitions("-DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\"")
62
63=== modified file 'katze/midori-paths.vala'
64--- katze/midori-paths.vala 2014-04-03 00:49:01 +0000
65+++ katze/midori-paths.vala 2014-09-01 23:11:13 +0000
66@@ -17,7 +17,6 @@
67
68 extern const string LIBDIR;
69 extern const string MDATADIR;
70-extern const string PACKAGE_NAME;
71 extern const string SYSCONFDIR;
72 extern const string MIDORI_VERSION_SUFFIX;
73 const string MODULE_PREFIX = "lib";
74
75=== modified file 'midori/CMakeLists.txt'
76--- midori/CMakeLists.txt 2013-09-08 21:38:36 +0000
77+++ midori/CMakeLists.txt 2014-09-01 23:11:13 +0000
78@@ -16,10 +16,13 @@
79 ${MIDORI_VALA_SOURCE}
80 PACKAGES
81 ${PKGS}
82+ katze
83+ midori
84+ gobject-fixes
85+ gtk-fixes
86 OPTIONS
87 ${VALAFLAGS}
88-CUSTOM_VAPIS
89- ${EXTRA_VAPIS}
90+ --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/../vapi
91 GENERATE_VAPI
92 "${CMAKE_PROJECT_NAME}-core"
93 GENERATE_HEADER
94
95=== modified file 'midori/midori-app.h'
96--- midori/midori-app.h 2013-03-28 17:27:56 +0000
97+++ midori/midori-app.h 2014-09-01 23:11:13 +0000
98@@ -14,7 +14,7 @@
99
100 #include <katze/katze.h>
101
102-#include "midori-browser.h"
103+#include "midori-core.h"
104 #include "midori-websettings.h"
105
106 G_BEGIN_DECLS
107
108=== removed file 'midori/midori-browser.h'
109--- midori/midori-browser.h 2013-03-26 23:09:09 +0000
110+++ midori/midori-browser.h 1970-01-01 00:00:00 +0000
111@@ -1,199 +0,0 @@
112-/*
113- Copyright (C) 2008 Christian Dywan <christian@twotoasts.de>
114-
115- This library is free software; you can redistribute it and/or
116- modify it under the terms of the GNU Lesser General Public
117- License as published by the Free Software Foundation; either
118- version 2.1 of the License, or (at your option) any later version.
119-
120- See the file COPYING for the full license text.
121-*/
122-
123-#ifndef __MIDORI_BROWSER_H__
124-#define __MIDORI_BROWSER_H__
125-
126-#include <katze/katze.h>
127-#include "midori-view.h"
128-
129-G_BEGIN_DECLS
130-
131-#define MIDORI_TYPE_BROWSER \
132- (midori_browser_get_type ())
133-#define MIDORI_BROWSER(obj) \
134- (G_TYPE_CHECK_INSTANCE_CAST ((obj), MIDORI_TYPE_BROWSER, MidoriBrowser))
135-#define MIDORI_BROWSER_CLASS(klass) \
136- (G_TYPE_CHECK_CLASS_CAST ((klass), MIDORI_TYPE_BROWSER, MidoriBrowserClass))
137-#define MIDORI_IS_BROWSER(obj) \
138- (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MIDORI_TYPE_BROWSER))
139-#define MIDORI_IS_BROWSER_CLASS(klass) \
140- (G_TYPE_CHECK_CLASS_TYPE ((klass), MIDORI_TYPE_BROWSER))
141-#define MIDORI_BROWSER_GET_CLASS(obj) \
142- (G_TYPE_INSTANCE_GET_CLASS ((obj), MIDORI_TYPE_BROWSER, MidoriBrowserClass))
143-
144-typedef struct _MidoriBrowser MidoriBrowser;
145-typedef struct _MidoriBrowserClass MidoriBrowserClass;
146-
147-struct _MidoriBrowserClass
148-{
149- GtkWindowClass parent_class;
150-
151- /* Signals */
152- void
153- (*window_object_cleared) (MidoriBrowser* browser,
154-#ifndef HAVE_WEBKIT2
155- WebKitWebFrame* web_frame,
156-#else
157- void* web_frame,
158-#endif
159- JSContextRef* context,
160- JSObjectRef* window_object);
161- void
162- (*statusbar_text_changed) (MidoriBrowser* browser,
163- const gchar* text);
164- void
165- (*element_motion) (MidoriBrowser* browser,
166- const gchar* link_uri);
167- void
168- (*new_window) (MidoriBrowser* browser,
169- const gchar* uri);
170-
171- void
172- (*add_tab) (MidoriBrowser* browser,
173- GtkWidget* view);
174- void
175- (*remove_tab) (MidoriBrowser* browser,
176- GtkWidget* view);
177- void
178- (*activate_action) (MidoriBrowser* browser,
179- const gchar* name);
180- void
181- (*quit) (MidoriBrowser* browser);
182-};
183-
184-GType
185-midori_browser_get_type (void) G_GNUC_CONST;
186-
187-MidoriBrowser*
188-midori_browser_new (void);
189-
190-void
191-midori_browser_add_tab (MidoriBrowser* browser,
192- GtkWidget* widget);
193-
194-void
195-midori_browser_close_tab (MidoriBrowser* browser,
196- GtkWidget* widget);
197-
198-GtkWidget*
199-midori_browser_add_item (MidoriBrowser* browser,
200- KatzeItem* item);
201-
202-GtkWidget*
203-midori_browser_add_uri (MidoriBrowser* browser,
204- const gchar* uri);
205-
206-void
207-midori_browser_activate_action (MidoriBrowser* browser,
208- const gchar* name);
209-
210-void
211-midori_browser_assert_action (MidoriBrowser* browser,
212- const gchar* name);
213-
214-void
215-midori_browser_block_action (MidoriBrowser* browser,
216- GtkAction* action);
217-
218-void
219-midori_browser_unblock_action (MidoriBrowser* browser,
220- GtkAction* action);
221-
222-void
223-midori_browser_set_action_visible (MidoriBrowser* browser,
224- const gchar* name,
225- gboolean visible);
226-
227-GtkActionGroup*
228-midori_browser_get_action_group (MidoriBrowser* browser);
229-
230-void
231-midori_browser_set_current_uri (MidoriBrowser* browser,
232- const gchar* uri);
233-
234-const gchar*
235-midori_browser_get_current_uri (MidoriBrowser* browser);
236-
237-void
238-midori_browser_set_current_page_smartly (MidoriBrowser* browser,
239- gint n);
240-
241-void
242-midori_browser_set_current_tab_smartly (MidoriBrowser* browser,
243- GtkWidget* view);
244-
245-void
246-midori_browser_set_current_page (MidoriBrowser* browser,
247- gint n);
248-
249-gint
250-midori_browser_get_current_page (MidoriBrowser* browser);
251-
252-void
253-midori_browser_set_current_item (MidoriBrowser* browser,
254- KatzeItem* item);
255-
256-GtkWidget*
257-midori_browser_get_nth_tab (MidoriBrowser* browser,
258- gint n);
259-
260-void
261-midori_browser_set_current_tab (MidoriBrowser* browser,
262- GtkWidget* widget);
263-#define midori_browser_set_tab midori_browser_set_current_tab
264-
265-GtkWidget*
266-midori_browser_get_current_tab (MidoriBrowser* browser);
267-#define midori_browser_get_tab midori_browser_get_current_tab
268-
269-gint
270-midori_browser_page_num (MidoriBrowser* browser,
271- GtkWidget* view);
272-
273-GList*
274-midori_browser_get_tabs (MidoriBrowser* browser);
275-
276-gint
277-midori_browser_get_n_pages (MidoriBrowser* browser);
278-
279-KatzeArray*
280-midori_browser_get_proxy_array (MidoriBrowser* browser);
281-
282-MidoriBrowser*
283-midori_browser_get_for_widget (GtkWidget* widget);
284-
285-void
286-midori_browser_quit (MidoriBrowser* browser);
287-
288-const gchar**
289-midori_browser_get_toolbar_actions (MidoriBrowser* browser);
290-
291-MidoriWebSettings*
292-midori_browser_get_settings (MidoriBrowser* browser);
293-
294-void
295-midori_browser_update_history (KatzeItem* item,
296- const gchar* type,
297- const gchar* event);
298-
299-void
300-midori_browser_save_uri (MidoriBrowser* browser,
301- MidoriView* view,
302- const gchar* uri);
303-
304-void
305-midori_browser_set_inactivity_reset (MidoriBrowser* browser,
306- gint inactivity_reset);
307-
308-G_END_DECLS
309-
310-#endif /* __MIDORI_BROWSER_H__ */
311
312=== renamed file 'midori/midori-browser.c' => 'midori/midori-browser.vala'
313--- midori/midori-browser.c 2014-08-31 10:33:28 +0000
314+++ midori/midori-browser.vala 2014-09-01 23:11:13 +0000
315@@ -1,993 +1,923 @@
316-/*
317- Copyright (C) 2007-2013 Christian Dywan <christian@twotoasts.de>
318- Copyright (C) 2008 Dale Whittaker <dayul@users.sf.net>
319- Copyright (C) 2009 Jérôme Geulfucci <jeromeg@xfce.org>
320-
321- This library is free software; you can redistribute it and/or
322- modify it under the terms of the GNU Lesser General Public
323- License as published by the Free Software Foundation; either
324- version 2.1 of the License, or (at your option) any later version.
325-
326- See the file COPYING for the full license text.
327-*/
328-
329-#include "midori-browser.h"
330-
331-#include "midori-app.h"
332-#include "midori-extension.h"
333-#include "midori-array.h"
334-#include "midori-view.h"
335-#include "midori-preferences.h"
336-#include "midori-panel.h"
337-#include "midori-locationaction.h"
338-#include "midori-searchaction.h"
339-#include "midori-findbar.h"
340-#include "midori-platform.h"
341-#include "midori-privatedata.h"
342-#include "midori-core.h"
343-#include "midori-privatedata.h"
344-#include "midori-bookmarks-db.h"
345-#include "katze-cellrenderercomboboxtext.h"
346-
347-#include "marshal.h"
348-
349-#include <glib/gi18n.h>
350-#include <glib/gstdio.h>
351-#include <gdk/gdkkeysyms.h>
352-#include <string.h>
353-
354-#include <config.h>
355-
356-#ifdef HAVE_GRANITE
357- #include <granite.h>
358-#endif
359-
360-#ifdef HAVE_ZEITGEIST
361- #include <zeitgeist.h>
362-#endif
363-
364-#ifdef HAVE_UNISTD_H
365- #include <unistd.h>
366-#endif
367-
368-#include <sqlite3.h>
369-
370-#ifdef HAVE_X11_EXTENSIONS_SCRNSAVER_H
371- #include <X11/Xlib.h>
372- #include <X11/Xutil.h>
373- #include <X11/extensions/scrnsaver.h>
374- #include <gdk/gdkx.h>
375-#endif
376-
377-struct _MidoriBrowser
378-{
379- GtkWindow parent_instance;
380- GtkActionGroup* action_group;
381- GtkWidget* menubar;
382- GtkWidget* throbber;
383- GtkWidget* navigationbar;
384- GtkWidget* bookmarkbar;
385-
386- GtkWidget* panel;
387- GtkWidget* notebook;
388-
389- GtkWidget* inspector;
390- GtkWidget* inspector_view;
391-
392- GtkWidget* find;
393-
394- GtkWidget* statusbar;
395- GtkWidget* statusbar_contents;
396- gchar* statusbar_text;
397-
398- gint last_window_width, last_window_height;
399- guint alloc_timeout;
400- guint panel_timeout;
401-
402- MidoriWebSettings* settings;
403- KatzeArray* proxy_array;
404- MidoriBookmarksDb* bookmarks;
405- KatzeArray* trash;
406- KatzeArray* search_engines;
407- KatzeArray* history;
408- MidoriHistoryDatabase* history_database;
409- MidoriSpeedDial* dial;
410- gboolean show_tabs;
411-
412- gboolean show_navigationbar;
413- gboolean show_statusbar;
414- guint maximum_history_age;
415- guint last_web_search;
416-
417- gboolean bookmarkbar_populate;
418-};
419-
420-G_DEFINE_TYPE (MidoriBrowser, midori_browser, GTK_TYPE_WINDOW)
421-
422-enum
423-{
424- PROP_0,
425-
426- PROP_MENUBAR,
427- PROP_NAVIGATIONBAR,
428- PROP_NOTEBOOK,
429- PROP_PANEL,
430- PROP_URI,
431- PROP_TAB,
432- PROP_LOAD_STATUS,
433- PROP_STATUSBAR,
434- PROP_STATUSBAR_TEXT,
435- PROP_SETTINGS,
436- PROP_PROXY_ITEMS,
437- PROP_BOOKMARKS,
438- PROP_TRASH,
439- PROP_SEARCH_ENGINES,
440- PROP_HISTORY,
441- PROP_SPEED_DIAL,
442- PROP_SHOW_TABS,
443-};
444-
445-enum
446-{
447- NEW_WINDOW,
448- ADD_TAB,
449- REMOVE_TAB,
450- MOVE_TAB,
451- SWITCH_TAB,
452- ACTIVATE_ACTION,
453- ADD_DOWNLOAD,
454- SEND_NOTIFICATION,
455- POPULATE_TOOL_MENU,
456- POPULATE_TOOLBAR_MENU,
457- QUIT,
458- SHOW_PREFERENCES,
459-
460- LAST_SIGNAL
461-};
462-
463-static guint signals[LAST_SIGNAL];
464-
465-static void
466-midori_browser_dispose (GObject* object);
467-
468-static void
469-midori_browser_finalize (GObject* object);
470-
471-#ifdef HAVE_WEBKIT2
472-void download_created_destination_cb (WebKitDownload *download,
473- gchar *destination,
474- gpointer user_data);
475-#endif
476-
477-static void
478-midori_browser_set_property (GObject* object,
479- guint prop_id,
480- const GValue* value,
481- GParamSpec* pspec);
482-
483-static void
484-midori_browser_get_property (GObject* object,
485- guint prop_id,
486- GValue* value,
487- GParamSpec* pspec);
488-
489-gboolean
490-midori_browser_open_bookmark (MidoriBrowser* browser,
491- KatzeItem* item);
492-
493-static void
494-midori_bookmarkbar_populate (MidoriBrowser* browser);
495-static void
496-midori_bookmarkbar_populate_idle (MidoriBrowser* browser);
497-
498-static void
499-midori_bookmarkbar_clear (GtkWidget* toolbar);
500-
501-static void
502-_midori_browser_set_toolbar_style (MidoriBrowser* browser,
503- MidoriToolbarStyle toolbar_style);
504-
505-static void
506-midori_browser_settings_notify (MidoriWebSettings* web_settings,
507- GParamSpec* pspec,
508- MidoriBrowser* browser);
509-
510-void
511-midori_panel_set_toolbar_style (MidoriPanel* panel,
512- GtkToolbarStyle style);
513-
514-static void
515-midori_browser_set_bookmarks (MidoriBrowser* browser,
516- MidoriBookmarksDb* bookmarks);
517-
518-static void
519-midori_browser_add_speed_dial (MidoriBrowser* browser);
520-
521-static void
522-midori_browser_step_history (MidoriBrowser* browser,
523- MidoriView* view);
524-
525-#define _action_by_name(brwsr, nme) \
526- gtk_action_group_get_action (brwsr->action_group, nme)
527-#define _action_set_sensitive(brwsr, nme, snstv) \
528- gtk_action_set_sensitive (_action_by_name (brwsr, nme), snstv);
529-#define _action_set_visible(brwsr, nme, vsbl) \
530- gtk_action_set_visible (_action_by_name (brwsr, nme), vsbl);
531-#define _action_set_active(brwsr, nme, actv) \
532- gtk_toggle_action_set_active (GTK_TOGGLE_ACTION ( \
533- _action_by_name (brwsr, nme)), actv);
534-
535-static void
536-midori_browser_disconnect_tab (MidoriBrowser* browser,
537- MidoriView* view);
538-
539-static gboolean
540-midori_browser_is_fullscreen (MidoriBrowser* browser)
541-{
542- GdkWindow* window = gtk_widget_get_window (GTK_WIDGET (browser));
543- GdkWindowState state = window ? gdk_window_get_state (window) : 0;
544- return state & GDK_WINDOW_STATE_FULLSCREEN;
545-}
546-
547-static gboolean
548-_toggle_tabbar_smartly (MidoriBrowser* browser,
549- gboolean ignore_fullscreen)
550-{
551- gboolean has_tabs = midori_browser_get_n_pages (browser) > 1;
552- gboolean show_tabs = !midori_browser_is_fullscreen (browser) || ignore_fullscreen;
553- if (!browser->show_tabs)
554- show_tabs = FALSE;
555- midori_notebook_set_labels_visible (MIDORI_NOTEBOOK (browser->notebook), show_tabs);
556- return has_tabs;
557-}
558-
559-static void
560-midori_browser_trash_clear_cb (KatzeArray* trash,
561- MidoriBrowser* browser)
562-{
563- gboolean trash_empty = katze_array_is_empty (browser->trash);
564- _action_set_sensitive (browser, "UndoTabClose", !trash_empty);
565- _action_set_sensitive (browser, "Trash", !trash_empty);
566-}
567-
568-static void
569-_midori_browser_update_actions (MidoriBrowser* browser)
570-{
571- gboolean has_tabs = _toggle_tabbar_smartly (browser, FALSE);
572- _action_set_sensitive (browser, "TabPrevious", has_tabs);
573- _action_set_sensitive (browser, "TabNext", has_tabs);
574-
575- if (browser->trash)
576- midori_browser_trash_clear_cb (browser->trash, browser);
577-}
578-
579-static void
580-midori_browser_update_secondary_icon (MidoriBrowser* browser,
581- MidoriView* view,
582- GtkAction* action)
583-{
584- if (g_object_get_data (G_OBJECT (view), "news-feeds"))
585- {
586- midori_location_action_set_secondary_icon (
587- MIDORI_LOCATION_ACTION (action), STOCK_NEWS_FEED);
588- _action_set_sensitive (browser, "AddNewsFeed", TRUE);
589- }
590- else
591- {
592- midori_location_action_set_secondary_icon (
593- MIDORI_LOCATION_ACTION (action), NULL);
594- _action_set_sensitive (browser, "AddNewsFeed", FALSE);
595- }
596-}
597-
598-static void
599-_midori_browser_update_interface (MidoriBrowser* browser,
600- MidoriView* view)
601-{
602- GtkAction* action;
603-
604- _action_set_sensitive (browser, "Back", midori_view_can_go_back (view));
605- _action_set_sensitive (browser, "Forward", midori_tab_can_go_forward (MIDORI_TAB (view)));
606- _action_set_sensitive (browser, "Previous",
607- midori_view_get_previous_page (view) != NULL);
608- _action_set_sensitive (browser, "Next",
609- midori_view_get_next_page (view) != NULL);
610-
611- _action_set_sensitive (browser, "AddSpeedDial", !midori_view_is_blank (view));
612- _action_set_sensitive (browser, "BookmarkAdd", !midori_view_is_blank (view));
613- _action_set_sensitive (browser, "MailTo", !midori_view_is_blank (view));
614- _action_set_sensitive (browser, "SaveAs", midori_tab_can_save (MIDORI_TAB (view)));
615- _action_set_sensitive (browser, "ZoomIn", midori_view_can_zoom_in (view));
616- _action_set_sensitive (browser, "ZoomOut", midori_view_can_zoom_out (view));
617- _action_set_sensitive (browser, "ZoomNormal",
618- midori_view_get_zoom_level (view) != 1.0f);
619- _action_set_sensitive (browser, "Encoding",
620- midori_tab_can_view_source (MIDORI_TAB (view)));
621- _action_set_sensitive (browser, "SourceView",
622- midori_tab_can_view_source (MIDORI_TAB (view)));
623- _action_set_sensitive (browser, "SourceViewDom",
624- midori_tab_can_view_source (MIDORI_TAB (view)));
625-
626- action = _action_by_name (browser, "NextForward");
627- if (midori_tab_can_go_forward (MIDORI_TAB (view)))
628- {
629- g_object_set (action,
630- "stock-id", GTK_STOCK_GO_FORWARD,
631- "tooltip", _("Go forward to the next page"),
632- "sensitive", TRUE, NULL);
633- }
634- else
635- {
636- g_object_set (action,
637- "stock-id", GTK_STOCK_MEDIA_NEXT,
638- "tooltip", _("Go to the next sub-page"),
639- "sensitive", midori_view_get_next_page (view) != NULL, NULL);
640- }
641-
642- action = _action_by_name (browser, "Location");
643- if (midori_tab_is_blank (MIDORI_TAB (view)))
644- {
645- gchar* icon_names[] = { "edit-find-symbolic", "edit-find", NULL };
646- GIcon* icon = g_themed_icon_new_from_names (icon_names, -1);
647- midori_location_action_set_primary_icon (
648- MIDORI_LOCATION_ACTION (action), icon, _("Web Search…"));
649- g_object_unref (icon);
650- }
651- else
652- midori_location_action_set_security_hint (
653- MIDORI_LOCATION_ACTION (action), midori_tab_get_security (MIDORI_TAB (view)));
654- midori_browser_update_secondary_icon (browser, view, action);
655-}
656-
657-static void
658-_midori_browser_set_statusbar_text (MidoriBrowser* browser,
659- MidoriView* view,
660- const gchar* text)
661-{
662- #if GTK_CHECK_VERSION (3, 2, 0)
663- gboolean is_location = FALSE;
664- #else
665- GtkWidget* widget = gtk_window_get_focus (GTK_WINDOW (browser));
666- gboolean is_location = widget && GTK_IS_ENTRY (widget)
667- && GTK_IS_ALIGNMENT (gtk_widget_get_parent (widget));
668- #endif
669-
670- katze_assign (browser->statusbar_text, midori_uri_format_for_display (text));
671- if (view == NULL)
672- return;
673-
674- if (!gtk_widget_get_visible (browser->statusbar) && !is_location
675- && text && *text)
676- {
677- #if GTK_CHECK_VERSION (3, 2, 0)
678- midori_view_set_overlay_text (view, browser->statusbar_text);
679- #else
680- GtkAction* action = _action_by_name (browser, "Location");
681- MidoriLocationAction* location_action = MIDORI_LOCATION_ACTION (action);
682- midori_location_action_set_text (location_action, browser->statusbar_text);
683- midori_location_action_set_secondary_icon (location_action, NULL);
684- #endif
685- }
686- else if (!gtk_widget_get_visible (browser->statusbar) && !is_location)
687- {
688- #if GTK_CHECK_VERSION (3, 2, 0)
689- midori_view_set_overlay_text (view, NULL);
690- #else
691- GtkAction* action = _action_by_name (browser, "Location");
692- MidoriLocationAction* location_action = MIDORI_LOCATION_ACTION (action);
693- midori_browser_update_secondary_icon (browser, view, action);
694- midori_location_action_set_text (location_action,
695- midori_view_get_display_uri (view));
696- #endif
697- }
698- else
699- {
700- gtk_statusbar_pop (GTK_STATUSBAR (browser->statusbar), 1);
701- gtk_statusbar_push (GTK_STATUSBAR (browser->statusbar), 1,
702- katze_str_non_null (browser->statusbar_text));
703- }
704-}
705-
706-void
707-midori_browser_set_current_page_smartly (MidoriBrowser* browser,
708- gint n)
709-{
710- if (!katze_object_get_boolean (browser->settings,
711- "open-tabs-in-the-background"))
712- midori_browser_set_current_page (browser, n);
713-}
714-
715-/**
716- * midori_browser_set_current_tab_smartly:
717- * @browser: a #MidoriBrowser
718- * @view: a #GtkWidget
719- *
720- * Switches to the tab containing @view iff open-tabs-in-the-background is %FALSE.
721- *
722- * Since: 0.4.9
723- **/
724-void
725-midori_browser_set_current_tab_smartly (MidoriBrowser* browser,
726- GtkWidget* view)
727-{
728- if (!katze_object_get_boolean (browser->settings,
729- "open-tabs-in-the-background"))
730- midori_browser_set_current_tab (browser, view);
731-}
732-
733-static void
734-_midori_browser_update_progress (MidoriBrowser* browser,
735- MidoriView* view)
736-{
737- GtkAction* action;
738- gdouble progress = midori_view_get_progress (view);
739- gboolean loading = progress > 0.0;
740-
741- action = _action_by_name (browser, "Location");
742- midori_location_action_set_progress (MIDORI_LOCATION_ACTION (action), progress);
743-
744- _action_set_sensitive (browser, "Reload", !loading);
745- _action_set_sensitive (browser, "Stop", loading);
746-
747- action = _action_by_name (browser, "ReloadStop");
748- if (!loading)
749- {
750- g_object_set (action,
751- "stock-id", GTK_STOCK_REFRESH,
752- "tooltip", _("Reload the current page"), NULL);
753- }
754- else
755- {
756- g_object_set (action,
757- "stock-id", GTK_STOCK_STOP,
758- "tooltip", _("Stop loading the current page"), NULL);
759- }
760-
761- g_object_set (browser->throbber, "active", loading, "visible", loading, NULL);
762-}
763-
764-/**
765- * midori_browser_update_history:
766- * @item: a #KatzeItem
767- * @type: "website", "bookmark" or "download"
768- * @event: "access", "leave", "modify", "delete"
769- *
770- * Since: 0.4.7
771- **/
772-void
773-midori_browser_update_history (KatzeItem* item,
774- const gchar* type,
775- const gchar* event)
776-{
777- g_return_if_fail (!KATZE_ITEM_IS_SEPARATOR (item));
778-
779- #ifdef HAVE_ZEITGEIST
780- const gchar* inter;
781- if (strstr (event, "access"))
782- inter = ZEITGEIST_ZG_ACCESS_EVENT;
783- else if (strstr (event, "leave"))
784- inter = ZEITGEIST_ZG_LEAVE_EVENT;
785- else if (strstr (event, "modify"))
786- inter = ZEITGEIST_ZG_MODIFY_EVENT;
787- else if (strstr (event, "create"))
788- inter = ZEITGEIST_ZG_CREATE_EVENT;
789- else if (strstr (event, "delete"))
790- inter = ZEITGEIST_ZG_DELETE_EVENT;
791- else
792- g_assert_not_reached ();
793-
794- /* FIXME: Should insert folders into the log (ZEITGEIST_NFO_BOOKMARK_FOLDER) */
795- if (KATZE_ITEM_IS_FOLDER (item))
796- return;
797-
798- zeitgeist_log_insert_events_no_reply (zeitgeist_log_get_default (),
799- zeitgeist_event_new_full (inter, ZEITGEIST_ZG_USER_ACTIVITY,
800- "application://midori.desktop",
801- zeitgeist_subject_new_full (
802- katze_item_get_uri (item),
803- strstr (type, "bookmark") ? ZEITGEIST_NFO_BOOKMARK : ZEITGEIST_NFO_WEBSITE,
804- zeitgeist_manifestation_for_uri (katze_item_get_uri (item)),
805- katze_item_get_meta_string (item, "mime-type"), NULL, katze_item_get_name (item), NULL),
806- NULL),
807- NULL);
808- #endif
809-}
810-
811-static void
812-midori_browser_update_history_title (MidoriBrowser* browser,
813- KatzeItem* item)
814-{
815- sqlite3* db;
816- static sqlite3_stmt* stmt = NULL;
817-
818- g_return_if_fail (katze_item_get_uri (item) != NULL);
819-
820- db = g_object_get_data (G_OBJECT (browser->history), "db");
821- g_return_if_fail (db != NULL);
822- if (!stmt)
823- {
824- const gchar* sqlcmd;
825-
826- sqlcmd = "UPDATE history SET title=? WHERE uri = ? and date=?";
827- sqlite3_prepare_v2 (db, sqlcmd, -1, &stmt, NULL);
828- }
829- sqlite3_bind_text (stmt, 1, katze_item_get_name (item), -1, 0);
830- sqlite3_bind_text (stmt, 2, katze_item_get_uri (item), -1, 0);
831- sqlite3_bind_int64 (stmt, 3, katze_item_get_added (item));
832-
833- if (sqlite3_step (stmt) != SQLITE_DONE)
834- g_printerr (_("Failed to update title: %s\n"), sqlite3_errmsg (db));
835- sqlite3_reset (stmt);
836- sqlite3_clear_bindings (stmt);
837-
838- midori_browser_update_history (item, "website", "access");
839-}
840-
841-/**
842- * midori_browser_assert_action:
843- * @browser: a #MidoriBrowser
844- * @name: action, setting=value expression or extension=true|false
845- *
846- * Assert that @name is a valid action or setting expression,
847- * if it fails the program will terminate with an error.
848- * To be used with command line interfaces.
849- *
850- * Since: 0.5.0
851- **/
852-void
853-midori_browser_assert_action (MidoriBrowser* browser,
854- const gchar* name)
855-{
856- g_return_if_fail (MIDORI_IS_BROWSER (browser));
857- g_return_if_fail (name != NULL);
858-
859- if (strchr (name, '='))
860- {
861- gchar** parts = g_strsplit (name, "=", 0);
862- GObjectClass* class = G_OBJECT_GET_CLASS (browser->settings);
863- GParamSpec* pspec = g_object_class_find_property (class, parts[0]);
864- if (pspec != NULL)
865- {
866- GType type = G_PARAM_SPEC_TYPE (pspec);
867- if (!(
868- (type == G_TYPE_PARAM_BOOLEAN && (!strcmp (parts[1], "true") || !strcmp (parts[1], "false")))
869- || type == G_TYPE_PARAM_STRING
870- || type == G_TYPE_PARAM_INT
871- || type == G_TYPE_PARAM_FLOAT
872- || type == G_TYPE_PARAM_DOUBLE
873- || type == G_TYPE_PARAM_ENUM))
874- midori_error (_("Value '%s' is invalid for %s"), parts[1], parts[0]);
875- }
876- else
877- {
878- gchar* extension_path = midori_paths_get_lib_path (PACKAGE_NAME);
879- GObject* extension = midori_extension_load_from_file (extension_path, parts[0], FALSE, FALSE);
880- g_free (extension_path);
881- if (!extension || (strcmp (parts[1], "true") && strcmp (parts[1], "false")))
882- midori_error (_("Unexpected setting '%s'"), name);
883- }
884- g_strfreev (parts);
885- }
886- else
887- {
888- GtkAction* action = _action_by_name (browser, name);
889- if (!action)
890- midori_error (_("Unexpected action '%s'."), name);
891- }
892-}
893-
894-void
895-midori_app_set_browsers (MidoriApp* app,
896- KatzeArray* browsers,
897- MidoriBrowser* browser);
898-
899-static void
900-_midori_browser_activate_action (MidoriBrowser* browser,
901- const gchar* name)
902-{
903- g_return_if_fail (name != NULL);
904-
905- if (strchr (name, '='))
906- {
907- gchar** parts = g_strsplit (name, "=", 0);
908- GObjectClass* class = G_OBJECT_GET_CLASS (browser->settings);
909- GParamSpec* pspec = g_object_class_find_property (class, parts[0]);
910- if (pspec != NULL)
911- {
912- GType type = G_PARAM_SPEC_TYPE (pspec);
913- if (type == G_TYPE_PARAM_BOOLEAN && !strcmp ("true", parts[1]))
914- g_object_set (browser->settings, parts[0], TRUE, NULL);
915- else if (type == G_TYPE_PARAM_BOOLEAN && !strcmp ("false", parts[1]))
916- g_object_set (browser->settings, parts[0], FALSE, NULL);
917- else if (type == G_TYPE_PARAM_STRING)
918- g_object_set (browser->settings, parts[0], parts[1], NULL);
919- else if (type == G_TYPE_PARAM_INT || type == G_TYPE_PARAM_UINT)
920- g_object_set (browser->settings, parts[0], atoi (parts[1]), NULL);
921- else if (type == G_TYPE_PARAM_FLOAT || type == G_TYPE_PARAM_DOUBLE)
922- g_object_set (browser->settings, parts[0], g_ascii_strtod (parts[1], NULL), NULL);
923- else if (type == G_TYPE_PARAM_ENUM)
924- {
925- GEnumClass* enum_class = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
926- GEnumValue* enum_value = g_enum_get_value_by_name (enum_class, parts[1]);
927- if (enum_value != NULL)
928- g_object_set (browser->settings, parts[0], enum_value->value, NULL);
929- else
930- g_warning (_("Value '%s' is invalid for %s"), parts[1], parts[0]);
931- }
932- else
933- g_warning (_("Value '%s' is invalid for %s"), parts[1], parts[0]);
934- }
935- else
936- {
937- gchar* extension_path = midori_paths_get_lib_path (PACKAGE_NAME);
938- GObject* extension = midori_extension_load_from_file (extension_path, parts[0], TRUE, FALSE);
939- MidoriApp* app = midori_app_new_proxy (NULL);
940- g_object_set (app,
941- "settings", browser->settings,
942- NULL);
943- /* FIXME: tabs of multiple windows */
944- KatzeArray* browsers = katze_array_new (MIDORI_TYPE_BROWSER);
945- katze_array_add_item (browsers, browser);
946- midori_app_set_browsers (app, browsers, browser);
947- g_free (extension_path);
948- if (extension && !strcmp (parts[1], "true"))
949- midori_extension_activate (extension, parts[0], TRUE, app);
950- else if (extension && !strcmp (parts[1], "false"))
951- midori_extension_deactivate (MIDORI_EXTENSION (extension));
952- else
953- g_warning (_("Unexpected setting '%s'"), name);
954- }
955- g_strfreev (parts);
956- }
957- else
958- {
959- GtkAction* action = _action_by_name (browser, name);
960- if (action)
961- gtk_action_activate (action);
962- else
963- g_warning (_("Unexpected action '%s'."), name);
964- }
965-}
966-
967-static void
968-midori_view_notify_icon_cb (MidoriView* view,
969- GParamSpec* pspec,
970- MidoriBrowser* browser)
971-{
972- if (midori_browser_get_current_tab (browser) != (GtkWidget*)view)
973- return;
974-
975- if (midori_paths_get_runtime_mode () == MIDORI_RUNTIME_MODE_APP)
976- gtk_window_set_icon (GTK_WINDOW (browser), midori_view_get_icon (view));
977-}
978-
979-static void
980-midori_view_notify_load_status_cb (GtkWidget* widget,
981- GParamSpec* pspec,
982- MidoriBrowser* browser)
983-{
984- MidoriView* view = MIDORI_VIEW (widget);
985- MidoriLoadStatus load_status = midori_view_get_load_status (view);
986-
987- if (widget == midori_browser_get_current_tab (browser))
988- {
989- if (load_status == MIDORI_LOAD_COMMITTED)
990- {
991- const gchar* uri = midori_view_get_display_uri (view);
992- GtkAction* action = _action_by_name (browser, "Location");
993- midori_location_action_set_text (
994- MIDORI_LOCATION_ACTION (action), uri);
995-
996- /* Focus the urlbar on blank pages */
997- if (midori_view_is_blank (view))
998- midori_browser_activate_action (browser, "Location");
999- }
1000-
1001- _midori_browser_update_interface (browser, view);
1002- _midori_browser_set_statusbar_text (browser, view, NULL);
1003- }
1004-
1005- if (load_status == MIDORI_LOAD_FINISHED)
1006- katze_item_set_meta_string (midori_view_get_proxy_item (view),
1007- "history-step", NULL);
1008-
1009- g_object_notify (G_OBJECT (browser), "load-status");
1010-}
1011-
1012-static void
1013-midori_view_notify_progress_cb (GtkWidget* view,
1014- GParamSpec* pspec,
1015- MidoriBrowser* browser)
1016-{
1017- if (view == midori_browser_get_current_tab (browser))
1018- _midori_browser_update_progress (browser, MIDORI_VIEW (view));
1019-}
1020-
1021-static void
1022-midori_view_notify_uri_cb (GtkWidget* widget,
1023- GParamSpec* pspec,
1024- MidoriBrowser* browser)
1025-{
1026- if (widget == midori_browser_get_current_tab (browser))
1027- {
1028- MidoriView* view = MIDORI_VIEW (widget);
1029- const gchar* uri = midori_view_get_display_uri (view);
1030- GtkAction* action = _action_by_name (browser, "Location");
1031- midori_location_action_set_text (MIDORI_LOCATION_ACTION (action), uri);
1032- _action_set_sensitive (browser, "Back", midori_view_can_go_back (view));
1033- _action_set_sensitive (browser, "Forward", midori_tab_can_go_forward (MIDORI_TAB (view)));
1034- g_object_notify (G_OBJECT (browser), "uri");
1035- }
1036-}
1037-
1038-static void
1039-midori_browser_set_title (MidoriBrowser* browser,
1040- const gchar* title)
1041-{
1042- const gchar* custom_title = midori_settings_get_custom_title (MIDORI_SETTINGS (browser->settings));
1043- if (custom_title && *custom_title)
1044- gtk_window_set_title (GTK_WINDOW (browser), custom_title);
1045- else if (katze_object_get_boolean (browser->settings, "enable-private-browsing"))
1046- {
1047- gchar* window_title = g_strdup_printf (_("%s (Private Browsing)"), title);
1048- gtk_window_set_title (GTK_WINDOW (browser), window_title);
1049- g_free (window_title);
1050- }
1051- else
1052- gtk_window_set_title (GTK_WINDOW (browser), title);
1053-}
1054-
1055-static void
1056-midori_view_notify_title_cb (GtkWidget* widget,
1057- GParamSpec* pspec,
1058- MidoriBrowser* browser)
1059-{
1060- MidoriView* view = MIDORI_VIEW (widget);
1061- if (widget == midori_browser_get_current_tab (browser))
1062- {
1063- midori_browser_set_title (browser, midori_view_get_display_title (view));
1064- g_object_notify (G_OBJECT (browser), "title");
1065- }
1066- midori_browser_step_history (browser, view);
1067-}
1068-
1069-static void
1070-midori_browser_step_history (MidoriBrowser* browser,
1071- MidoriView* view)
1072-{
1073- if (midori_view_get_load_status (view) != MIDORI_LOAD_COMMITTED)
1074- return;
1075- if (!browser->history_database || !browser->maximum_history_age)
1076- return;
1077-
1078- KatzeItem* proxy = midori_view_get_proxy_item (view);
1079- const gchar* proxy_uri = katze_item_get_uri (proxy);
1080- if (midori_uri_is_blank (proxy_uri))
1081- return;
1082-
1083- const gchar* history_step = katze_item_get_meta_string (proxy, "history-step");
1084- if (history_step == NULL)
1085- {
1086- GError* error = NULL;
1087- time_t now = time (NULL);
1088- katze_item_set_added (proxy, now);
1089- gint64 day = sokoke_time_t_to_julian (&now);
1090- midori_history_database_insert (browser->history_database,
1091- katze_item_get_uri (proxy),
1092- katze_item_get_name (proxy),
1093- katze_item_get_added (proxy), day, &error);
1094- if (error != NULL)
1095- {
1096- g_printerr (_("Failed to insert new history item: %s\n"), error->message);
1097- g_error_free (error);
1098- return;
1099- }
1100- katze_item_set_meta_string (proxy, "history-step", "update");
1101- /* FIXME: No signal for adding/ removing */
1102- katze_array_add_item (browser->history, proxy);
1103- katze_array_remove_item (browser->history, proxy);
1104- }
1105- else if (!strcmp (history_step, "update"))
1106- {
1107- if (proxy->name != NULL)
1108- midori_browser_update_history_title (browser, proxy);
1109- }
1110- else if (!strcmp (history_step, "ignore"))
1111- {
1112- /* This is set when restoring sessions */
1113- }
1114- else
1115- g_warning ("Unexpected history-step: %s", history_step);
1116-}
1117-
1118-static void
1119-midori_view_notify_zoom_level_cb (GtkWidget* view,
1120- GParamSpec* pspec,
1121- MidoriBrowser* browser)
1122-{
1123- if (view == midori_browser_get_current_tab (browser))
1124- _action_set_sensitive (browser, "ZoomNormal",
1125- midori_view_get_zoom_level (MIDORI_VIEW (view)) != 1.0f);
1126-}
1127-
1128-static void
1129-midori_view_notify_statusbar_text_cb (GtkWidget* view,
1130- GParamSpec* pspec,
1131- MidoriBrowser* browser)
1132-{
1133- gchar* text;
1134-
1135- if (view == midori_browser_get_current_tab (browser))
1136- {
1137- g_object_get (view, "statusbar-text", &text, NULL);
1138- _midori_browser_set_statusbar_text (browser, MIDORI_VIEW (view), text);
1139- g_free (text);
1140- }
1141-}
1142-
1143-static gboolean
1144-midori_bookmark_folder_button_reach_parent (GtkTreeModel* model, GtkTreeIter *iter, gint64 parentid)
1145-{
1146- do
1147- {
1148- gint64 id;
1149-
1150- gtk_tree_model_get (model, iter, 1, &id, -1);
1151-
1152- if (parentid == id)
1153- return TRUE;
1154-
1155- if (gtk_tree_model_iter_has_child (model, iter))
1156- {
1157- GtkTreeIter child;
1158- gtk_tree_model_iter_children (model, &child, iter);
1159- if (midori_bookmark_folder_button_reach_parent (model, &child, parentid))
1160- {
1161- *iter = child;
1162- return TRUE;
1163- }
1164- }
1165- }
1166- while (gtk_tree_model_iter_next (model, iter));
1167-
1168- return FALSE;
1169-}
1170-
1171-typedef struct _FolderEntry
1172-{
1173- const gchar *title;
1174- gint64 id;
1175- gint64 parentid;
1176-} FolderEntry;
1177-
1178-static void
1179-midori_bookmark_folder_free_folder_entry (FolderEntry* folder)
1180-{
1181- g_free ((gpointer)folder->title);
1182-}
1183-
1184-static GtkWidget*
1185-midori_bookmark_folder_button_new (MidoriBookmarksDb* array,
1186- gint64 selected_parentid)
1187-{
1188- GtkTreeStore* model;
1189- GtkWidget* combo;
1190- GtkCellRenderer* renderer;
1191- guint n;
1192- sqlite3* db;
1193- sqlite3_stmt* statement;
1194- gint result;
1195- const gchar* sqlcmd = "SELECT title, id, parentid FROM bookmarks WHERE uri='' ORDER BY parentid, title ASC";
1196- gint64 current_parentid;
1197- GtkTreeIter tree_iter;
1198- GtkTreeIter stock_parent_iter;
1199- GtkTreeIter* parent_iter;
1200- GList *folders = NULL;
1201-
1202- db = g_object_get_data (G_OBJECT (array), "db");
1203- g_return_val_if_fail (db != NULL, NULL);
1204-
1205- /* folder combo box model content:
1206- ** 0: title
1207- ** 1: id
1208- */
1209- model = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_INT64);
1210- combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (model));
1211-
1212- /* setup combo layout
1213- ** 0: a folder icon
1214- ** 1: the folder name
1215- */
1216-
1217- gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo));
1218-
1219- renderer = gtk_cell_renderer_pixbuf_new ();
1220- g_object_set (G_OBJECT (renderer),
1221- "stock-id", GTK_STOCK_DIRECTORY,
1222- "stock-size", GTK_ICON_SIZE_MENU,
1223- NULL);
1224- gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
1225-
1226- renderer = katze_cell_renderer_combobox_text_new ();
1227- g_object_set (G_OBJECT (renderer),
1228- "width-chars", 40, /* FIXME: figure out a way to define an acceptable string length */
1229- "ellipsize", PANGO_ELLIPSIZE_END,
1230- "unfolded-text", _("Select [text]"),
1231- NULL);
1232- gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
1233- gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), renderer, "text", 0);
1234-
1235- /* read the folders list from the database */
1236- /* FIXME: this should be a service of midori/midori-bookmarks-db */
1237-
1238- if ((result = sqlite3_prepare_v2 (db, sqlcmd, -1, &statement, NULL)) == SQLITE_OK)
1239- {
1240- while ((result = sqlite3_step (statement)) == SQLITE_ROW)
1241- {
1242- FolderEntry* folder = g_new (FolderEntry, 1);
1243-
1244- folder->title = g_strdup ((const gchar*)sqlite3_column_text (statement, 0));
1245- folder->id = sqlite3_column_int64 (statement, 1);
1246- folder->parentid = sqlite3_column_int64 (statement, 2);
1247-
1248- folders = g_list_append (folders, folder);
1249- }
1250-
1251- sqlite3_clear_bindings (statement);
1252- sqlite3_reset (statement);
1253- }
1254-
1255- /* populate the combo box */
1256- /* FIXME: here we should have the root bookmark array's name and id, not hard encoded values */
1257-
1258- gtk_tree_store_insert_with_values (model, &tree_iter, NULL, G_MAXINT,
1259- 0, _("Bookmarks"), 1, (gint64)-1, -1);
1260- gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &tree_iter);
1261-
1262- current_parentid = -1;
1263- parent_iter = NULL;
1264- n = 1;
1265- while (g_list_first (folders))
1266- {
1267- gboolean something_done = FALSE;
1268- GList* list_iter = g_list_first (folders);
1269-
1270- do
1271- {
1272- FolderEntry* folder = list_iter->data;
1273- const gchar* title = folder->title;
1274- gint64 id = folder->id;
1275- gint64 parentid = folder->parentid;
1276-
1277- if (parentid != current_parentid) /* optimize case of sub-folders of the same parent */
1278- {
1279- if (!parentid)
1280- {
1281- /* folder's parent is the stree store root */
1282-
1283- current_parentid = -1;
1284- parent_iter = NULL;
1285+
1286+namespace Midori {
1287+ public class Browser : Gtk.Window {
1288+ struct FolderEntry {
1289+ string title;
1290+ int64 id;
1291+ int64 parentid;
1292+ }
1293+
1294+ static construct {
1295+#if GTK_VERSION_3_0_0
1296+ Gtk.rc_parse ("style \"tool-button-style\"\n {\n" +
1297+ "GtkToolButton::icon-spacing = 2\n }\n" +
1298+ "widget \"MidoriBrowser.*.MidoriBookmarkbar.Gtk*ToolButton\" " +
1299+ "style \"tool-button-style\"\n" +
1300+ "widget \"MidoriBrowser.*.MidoriFindbar.Gtk*ToolButton\" " +
1301+ "style \"tool-button-style\"\n");
1302+#endif
1303+ }
1304+
1305+ public signal Browser new_window (Browser? new_window);
1306+ public signal void move_tab (Gtk.Widget notebook, int current_position, int new_position);
1307+ public signal void switch_tab (Object old_tab, Object new_tab);
1308+ public signal void add_download (WebKit.Download download);
1309+ public signal void send_notification (string title, string message);
1310+ public signal void populate_tool_menu (Gtk.Menu menu);
1311+ public signal void populate_toolbar_menu (Gtk.Menu menu);
1312+ public signal void show_preferences (Katze.Preferences preferences);
1313+
1314+ public Gtk.Toolbar navigationbar { get; private set; }
1315+ public Gtk.MenuBar menubar { get; private set; }
1316+ public Notebook notebook { get; private set; }
1317+ public Panel panel { get; private set; }
1318+ public Katze.Array proxy_array { get; private set; }
1319+
1320+ public unowned string uri {
1321+ get { return get_current_uri (); }
1322+ set { set_current_uri (value); }
1323+ }
1324+
1325+ public unowned Tab tab {
1326+ get { return get_current_tab (); }
1327+ set { set_current_tab (value); }
1328+ }
1329+
1330+ public LoadStatus load_status {
1331+ get {
1332+ var tab = get_current_tab ();
1333+ return tab != null ? tab.load_status : LoadStatus.FINISHED;
1334+ }
1335+ }
1336+
1337+ /**
1338+ * MidoriBrowser:statusbar:
1339+ *
1340+ * The widget representing the statusbar contents. This is
1341+ * not an actual #GtkStatusbar but rather a #GtkBox.
1342+ */
1343+ public Gtk.Statusbar statusbar { get; private set; }
1344+
1345+ /**
1346+ * MidoriBrowser:statusbar-text:
1347+ *
1348+ * The text that is displayed in the statusbar.
1349+ *
1350+ * This value reflects changes to the text visible in the statusbar, such
1351+ * as the uri of a hyperlink the mouse hovers over or the description of
1352+ * a menuitem.
1353+ *
1354+ * Setting this value changes the displayed text until the next change.
1355+ */
1356+ public string statusbar_text {
1357+ get {
1358+ return _statusbar_text;
1359+ }
1360+ set {
1361+ _set_statusbar_text ((View) tab, value);
1362+ }
1363+ }
1364+
1365+ /**
1366+ * MidoriBrowser:settings:
1367+ *
1368+ * An associated settings instance that is shared among all web views.
1369+ *
1370+ * Setting this value is propagated to every present web view. Also
1371+ * every newly created web view will use this instance automatically.
1372+ *
1373+ * If no settings are specified a default will be used.
1374+ */
1375+ public WebSettings settings {
1376+ get {
1377+ return _settings;
1378+ }
1379+ set {
1380+ _settings.notify.disconnect (settings_notify);
1381+ _settings = value;
1382+
1383+ if (_settings == null)
1384+ _settings = new WebSettings ();
1385+
1386+ update_settings ();
1387+ _settings.set_data<ulong> ("handle-settings-notify", _settings.notify.connect (settings_notify));
1388+
1389+ foreach (var tab in get_tabs ())
1390+ ((View) tab).settings = _settings;
1391+ }
1392+ }
1393+
1394+ /**
1395+ * MidoriBrowser:proxy-items:
1396+ *
1397+ * The open views, automatically updated, for session management.
1398+ *
1399+ * Since: 0.4.8
1400+ */
1401+ public Katze.Array proxy_items { get; private set; }
1402+
1403+ /**
1404+ * MidoriBrowser:bookmarks:
1405+ *
1406+ * The bookmarks folder, containing all bookmarks.
1407+ *
1408+ * This is actually a reference to a bookmarks instance,
1409+ * so if bookmarks should be used it must be initially set.
1410+ */
1411+ public BookmarksDb? bookmarks {
1412+ get {
1413+ return _bookmarks;
1414+ }
1415+ set {
1416+ _set_bookmarks (value);
1417+ }
1418+ }
1419+
1420+ /**
1421+ * MidoriBrowser:trash:
1422+ *
1423+ * The trash, that collects all closed tabs and windows.
1424+ *
1425+ * This is actually a reference to a trash instance, so if a trash should
1426+ * be used it must be initially set.
1427+ *
1428+ * Note: In the future the trash might collect other types of items.
1429+ */
1430+ public Katze.Array? trash {
1431+ get {
1432+ return _trash;
1433+ }
1434+ set {
1435+ _trash = value;
1436+
1437+ var trash_action = (Katze.ArrayAction) action_group.get_action ("Trash");
1438+ trash_action.array = _trash;
1439+ trash_action.reversed = true;
1440+
1441+ action_set_visible ("Trash", _trash != null);
1442+ action_set_visible ("UndoTabClose", _trash != null);
1443+
1444+ if (trash != null) {
1445+ trash.clear.connect_after (trash_clear_cb);
1446+ trash_clear_cb ();
1447 }
1448- else if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &tree_iter))
1449- {
1450- if (midori_bookmark_folder_button_reach_parent (
1451- GTK_TREE_MODEL (model), &tree_iter, parentid))
1452- {
1453- /* folder's parent found in the tree store */
1454-
1455- current_parentid = parentid;
1456- stock_parent_iter = tree_iter;
1457- parent_iter = &stock_parent_iter;
1458+ }
1459+ }
1460+
1461+ /**
1462+ * MidoriBrowser:search-engines:
1463+ *
1464+ * The list of search engines to be used for web search.
1465+ *
1466+ * This is actually a reference to a search engines instance,
1467+ * so if search engines should be used it must be initially set.
1468+ */
1469+ public Katze.Array? search_engines {
1470+ get {
1471+ return _search_engines;
1472+ }
1473+ set {
1474+ _search_engines = value;
1475+
1476+ /* FIXME: Disconnect handlers */
1477+ ((LocationAction) action_group.get_action ("Location")).set_search_engines (_search_engines);
1478+ var search_action = (SearchAction) action_group.get_action ("Search");
1479+ search_action.search_engines = _search_engines;
1480+ /* FIXME: Connect to updates */
1481+
1482+ if (_search_engines != null) {
1483+ var default_search = settings.location_entry_search;
1484+ last_web_search = settings.last_web_search;
1485+
1486+ var item = search_engines.get_nth_item (last_web_search);
1487+ search_action.current_item = item;
1488+
1489+ if (default_search != null && (item = (Katze.Item) search_engines.find_uri (default_search)) != null) {
1490+ search_action.default_item = item;
1491 }
1492+ }
1493+ }
1494+ }
1495+
1496+ /**
1497+ * MidoriBrowser:history:
1498+ *
1499+ * The list of history items.
1500+ *
1501+ * This is actually a reference to a history instance,
1502+ * so if history should be used it must be initially set.
1503+ */
1504+ public Katze.Array? history {
1505+ get {
1506+ return _history;
1507+ }
1508+ set {
1509+ _set_history (value);
1510+ }
1511+ }
1512+
1513+ /**
1514+ * MidoriBrowser:speed-dial:
1515+ *
1516+ * The speed dial configuration file.
1517+ *
1518+ * Since: 0.3.4
1519+ * Since 0.4.7 this is a Midori.SpeedDial instance.
1520+ */
1521+ public SpeedDial? dial {
1522+ get {
1523+ return _dial;
1524+ }
1525+ set {
1526+ if (_dial != null)
1527+ _dial.refresh.disconnect (speed_dial_refresh_cb);
1528+
1529+ _dial = value;
1530+
1531+ if (_dial != null)
1532+ dial.refresh.connect (speed_dial_refresh_cb);
1533+ }
1534+ }
1535+
1536+ /**
1537+ * MidoriBrowser:show-tabs:
1538+ *
1539+ * Whether or not to show tabs.
1540+ *
1541+ * If disabled, no tab labels are shown. This is intended for
1542+ * extensions that want to provide alternative tab labels.
1543+ *
1544+ * Since 0.1.8
1545+ */
1546+ public bool show_tabs {
1547+ get {
1548+ return _show_tabs;
1549+ }
1550+ set {
1551+ _show_tabs = value;
1552+ toggle_tabbar_smartly (false);
1553+ }
1554+ }
1555+
1556+ Gtk.ActionGroup action_group;
1557+ Gtk.Spinner throbber;
1558+
1559+ Gtk.Toolbar bookmarkbar;
1560+
1561+ Gtk.Box inspector;
1562+ Gtk.Widget inspector_view;
1563+
1564+ Findbar find;
1565+
1566+ Gtk.Widget statusbar_contents;
1567+
1568+ int last_window_width;
1569+ int last_window_height;
1570+ uint _alloc_timeout;
1571+ uint _panel_timeout;
1572+
1573+ HistoryDatabase? history_database = null;
1574+
1575+ bool show_navigationbar;
1576+ bool show_statusbar;
1577+ uint maximum_history_age;
1578+ uint last_web_search;
1579+
1580+ bool bookmarkbar_populating;
1581+
1582+ string _statusbar_text;
1583+ WebSettings _settings;
1584+ BookmarksDb? _bookmarks = null;
1585+ Katze.Array? _trash = null;
1586+ Katze.Array? _search_engines = null;
1587+ Katze.Array? _history = null;
1588+ SpeedDial? _dial = null;
1589+ bool _show_tabs = true;
1590+
1591+ public Browser () {
1592+ settings = new WebSettings ();
1593+ proxy_array = new Katze.Array (typeof (Katze.Array));
1594+
1595+ realize.connect (realize_cb);
1596+ window_state_event.connect (window_state_event_cb);
1597+ size_allocate.connect (size_allocate_cb);
1598+ destroy.connect (destroy_cb);
1599+
1600+ role = "browser";
1601+ icon_name = MidoriStock.WEB_BROWSER;
1602+
1603+#if GTK_VERSION_3_4_0
1604+#if !HAVE_GRANITE
1605+ set_hide_titlebar_when_maximized (true);
1606+#endif
1607+#endif
1608+
1609+ var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
1610+ add (vbox);
1611+ vbox.show ();
1612+
1613+ action_group = new Gtk.ActionGroup ("Browser");
1614+ action_group.set_translation_domain (GETTEXT_PACKAGE);
1615+ add_actions ();
1616+ var ui_manager = new Gtk.UIManager ();
1617+ var accel_group = ui_manager.get_accel_group ();
1618+ add_accel_group (accel_group);
1619+ ui_manager.insert_action_group (action_group, 0);
1620+
1621+ accel_group.set_data<Object> ("midori-browser", this);
1622+ for (var i = 0; i < 10; i++) {
1623+ var accel_path = "<Manual>/Browser/SwitchTab%d".printf (i);
1624+ Gtk.AccelMap.add_entry (accel_path, Gdk.Key.@0 + i, Gdk.ModifierType.MOD1_MASK);
1625+ accel_group.connect_by_path (accel_path, accel_switch_tab_activate_cb);
1626+ }
1627+
1628+ try {
1629+ ui_manager.add_ui_from_string (ui_markup, -1);
1630+ } catch (Error e) {
1631+ warning ("User interface couldn't be created: %s", e.message);
1632+ }
1633+
1634+ action_set_visible ("Dummy", false);
1635+
1636+ Gtk.Action action;
1637+ Katze.Array dummy_array;
1638+
1639+ action = new Katze.SeparatorAction ();
1640+ action.name = "Separator";
1641+ action.label = _("_Separator");
1642+ action_group.add_action (action);
1643+
1644+ var location_action = (LocationAction) Object.@new (typeof (LocationAction),
1645+ "name", "Location",
1646+ "label", _("_Location"),
1647+ "stock-id", Gtk.Stock.JUMP_TO,
1648+ "tooltip", _("Open a particular location"));
1649+ location_action.activate.connect (action_location_activate);
1650+ location_action.focus_in.connect (action_location_focus_in);
1651+ location_action.focus_out.connect (action_location_focus_out);
1652+ location_action.reset_uri.connect (action_location_reset_uri);
1653+ location_action.submit_uri.connect (action_location_submit_uri);
1654+ location_action.secondary_icon_released.connect (action_location_secondary_icon_released);
1655+ action_group.add_action_with_accel (location_action, "<Ctrl>L");
1656+
1657+ var search_action = (SearchAction) Object.@new (typeof (SearchAction),
1658+ "name", "Search",
1659+ "label", _("_Web Search…"),
1660+ "stock-id", Gtk.Stock.FIND,
1661+ "tooltip", _("Run a web search"));
1662+ search_action.activate.connect (action_search_activate);
1663+ search_action.submit.connect (action_search_submit);
1664+ search_action.focus_out.connect (action_search_focus_out);
1665+ search_action.notify["current-item"].connect (action_search_notify_current_item);
1666+ search_action.notify["default-item"].connect (action_search_notify_default_item);
1667+ action_group.add_action_with_accel (search_action, "<Ctrl>K");
1668+
1669+ action = (Gtk.Action) Object.@new (typeof (PanedAction),
1670+ "name", "LocationSearch");
1671+ action_group.add_action (action);
1672+
1673+ var trash_action = (Katze.ArrayAction) Object.@new (typeof (Katze.ArrayAction),
1674+ "name", "Trash",
1675+ "stock-id", Stock.USER_TRASH,
1676+ "tooltip", _("Reopen a previously closed tab or window"));
1677+ trash_action.populate_popup.connect (action_trash_populate_popup);
1678+ trash_action.activate_item_alt.connect (action_trash_activate_item_alt);
1679+ action_group.add_action_with_accel (trash_action, "");
1680+
1681+ dummy_array = new Katze.Array (typeof (Katze.Array));
1682+ dummy_array.update ();
1683+ var bookmarks_action = (Katze.ArrayAction) Object.@new (typeof (Katze.ArrayAction),
1684+ "name", "Bookmarks",
1685+ "label", _("_Bookmarks"),
1686+ "stock-id", Stock.BOOKMARKS,
1687+ "tooltip", _("Show the saved bookmarks"),
1688+ "array", dummy_array /* updated, unique */);
1689+ bookmarks_action.populate_folder.connect (action_bookmarks_populate_folder);
1690+ bookmarks_action.activate_item_alt.connect (bookmarkbar_activate_item_alt);
1691+ bookmarks_action.activate_item.connect (bookmarkbar_activate_item);
1692+ action_group.add_action_with_accel (bookmarks_action, "");
1693+
1694+ dummy_array = new Katze.Array (typeof (Katze.Array));
1695+ dummy_array.update ();
1696+ var tools_action = (Katze.ArrayAction) Object.@new (typeof (Katze.ArrayAction),
1697+ "name", "Tools",
1698+ "label", _("_Tools"),
1699+ "stock-id", Gtk.Stock.PREFERENCES,
1700+ "array", dummy_array /* updated, unique */);
1701+ tools_action.populate_popup.connect (action_tools_populate_popup);
1702+ action_group.add_action (tools_action);
1703+
1704+ var tabs_action = (Katze.ArrayAction) Object.@new (typeof (Katze.ArrayAction),
1705+ "name", "Window",
1706+ "label", _("_Tabs"),
1707+ "stock-id", Gtk.Stock.INDEX,
1708+ "tooltip", _("Show a list of all open tabs"),
1709+ "array", proxy_array);
1710+ tabs_action.populate_popup.connect (action_window_populate_popup);
1711+ tabs_action.activate_item_alt.connect (action_window_activate_item_alt);
1712+ action_group.add_action_with_accel (tabs_action, "");
1713+
1714+ var menu_action = (Katze.ArrayAction) Object.@new (typeof (Katze.ArrayAction),
1715+ "name", "CompactMenu",
1716+ "label", _("_Menu"),
1717+ "stock-id", Gtk.Stock.PROPERTIES,
1718+ "tooltip", _("Menu"),
1719+ "array", new Katze.Array (typeof (Katze.Item)));
1720+ menu_action.populate_popup.connect (action_compact_menu_populate_popup);
1721+ action_group.add_action (menu_action);
1722+
1723+ // Create the menubar
1724+ menubar = (Gtk.MenuBar) ui_manager.get_widget ("/menubar");
1725+ vbox.pack_start (menubar, false, false, 0);
1726+ menubar.hide ();
1727+ action_set_visible ("Menubar", !has_native_menubar ());
1728+ menubar.button_press_event.connect (menu_button_press_event_cb);
1729+
1730+ var icon_size = 16;
1731+ var menuitem = new Gtk.MenuItem ();
1732+ menuitem.show ();
1733+ throbber = new Gtk.Spinner ();
1734+ // Wrap the spinner in an event box to retain its size when hidden
1735+ var throbber_box = new Gtk.EventBox ();
1736+ throbber_box.visible_window = false;
1737+ Gtk.icon_size_lookup_for_settings (get_settings (), Gtk.IconSize.MENU, out icon_size, null);
1738+ throbber_box.set_size_request (icon_size, icon_size);
1739+ throbber_box.add (throbber);
1740+ throbber_box.show ();
1741+ menuitem.add (throbber_box);
1742+#if GTK_3_2_0
1743+ menuitem.hexpand = true;
1744+ menuitem.halign = Gtk.Align.END;
1745+#else
1746+ menuitem.right_justified = true;
1747+#endif
1748+ menubar.append (menuitem);
1749+
1750+ ((Gtk.ImageMenuItem) ui_manager.get_widget ("/menubar/File/WindowNew")).image = null;
1751+ ((Gtk.ImageMenuItem) ui_manager.get_widget ("/menubar/Go/Location")).image = null;
1752+
1753+ ui_manager.get_widget ("/menubar/Go/Homepage").button_press_event.connect (menu_item_middle_click_event_cb);
1754+ ui_manager.get_widget ("/menubar/Go/Back").button_press_event.connect (menu_item_middle_click_event_cb);
1755+ ui_manager.get_widget ("/menubar/Go/Forward").button_press_event.connect (menu_item_middle_click_event_cb);
1756+ ui_manager.get_widget ("/menubar/Go/Previous").button_press_event.connect (menu_item_middle_click_event_cb);
1757+ ui_manager.get_widget ("/menubar/Go/Next").button_press_event.connect (menu_item_middle_click_event_cb);
1758+
1759+ action_set_sensitive ("EncodingCustom", false);
1760+ action_set_visible ("LastSession", false);
1761+
1762+ action_set_visible ("Bookmarks", bookmarks != null);
1763+ action_set_visible ("BookmarksAdd", bookmarks != null);
1764+ action_set_visible ("BookmarksImport", bookmarks != null);
1765+ action_set_visible ("BookmarksExport", bookmarks != null);
1766+ action_set_visible ("Bookmarkbar", bookmarks != null);
1767+ action_set_visible ("Trash", trash != null);
1768+ action_set_visible ("UndoTabClose", trash != null);
1769+
1770+ // create the navigationbar
1771+ navigationbar = (Gtk.Toolbar) ui_manager.get_widget ("/toolbar_navigation");
1772+ Katze.widget_add_class (navigationbar, "primary-toolbar");
1773+ /* FIXME: Settings should be connected with screen changes */
1774+#if !HAVE_GRANITE
1775+ var gtk_settings = get_settings ();
1776+ if (gtk_settings != null)
1777+ gtk_settings.notify["gtk-toolbar-style"].connect (navigationbar_notify_style_cb);
1778+#endif
1779+
1780+ navigationbar.show_arrow = true;
1781+ action_group.get_action ("Back").@set ("is-important", true);
1782+ navigationbar.hide ();
1783+ navigationbar.popup_context_menu.connect (toolbar_popup_context_menu_cb);
1784+ vbox.pack_start (navigationbar, false, false, 0);
1785+
1786+ // Bookmarkbar
1787+ bookmarkbar = new Gtk.Toolbar ();
1788+ Katze.widget_add_class (bookmarkbar, "secondary-toolbar");
1789+ bookmarkbar.name = "MidoriBookmarkbar";
1790+ bookmarkbar.icon_size = Gtk.IconSize.MENU;
1791+ bookmarkbar.toolbar_style = Gtk.ToolbarStyle.BOTH_HORIZ;
1792+ vbox.pack_start (bookmarkbar, false, false, 0);
1793+ bookmarkbar.popup_context_menu.connect (toolbar_popup_context_menu_cb);
1794+
1795+ // Create the panel
1796+ var hpaned = new Gtk.Paned (Gtk.Orientation.HORIZONTAL);
1797+ hpaned.notify["position"].connect (panel_notify_position_cb);
1798+ hpaned.cycle_child_focus.connect (panel_cycle_child_focus_cb);
1799+ vbox.pack_start (hpaned, true, true, 0);
1800+ hpaned.show ();
1801+ panel = (Panel) Object.@new (typeof (Panel), "action-group", action_group);
1802+ panel.notify["page"].connect (panel_notify_page_cb);
1803+ panel.set_data<ulong> ("handler-panel-notify-show-titles",
1804+ panel.notify["show-titles"].connect (panel_notify_show_titles_cb));
1805+ panel.notify["right-aligned"].connect (panel_notify_right_aligned_cb);
1806+ panel.close.connect (panel_close_cb);
1807+ hpaned.pack1 (panel, false, false);
1808+
1809+ // Notebook, containing all views
1810+ var vpaned = new Gtk.Paned (Gtk.Orientation.VERTICAL);
1811+ hpaned.pack2 (vpaned, true, false);
1812+ vpaned.show ();
1813+ notebook = new Notebook ();
1814+
1815+ vpaned.pack1 (notebook, false, false);
1816+ notebook.tab_switched.connect (switched_tab_cb);
1817+ notebook.notify["tab"].connect (notify_tab_cb);
1818+ notebook.tab_moved.connect (tab_moved_cb);
1819+ notebook.context_menu.connect (notebook_context_menu_cb);
1820+ notebook.tab_context_menu.connect (notebook_tab_context_menu_cb);
1821+ notebook.tab_detached.connect (notebook_create_window_cb);
1822+ notebook.new_tab.connect (notebook_new_tab_cb);
1823+ notebook.show ();
1824+
1825+ // Inspector container
1826+ inspector = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
1827+ vpaned.pack2 (inspector, false, false);
1828+ var scrolled = new Gtk.ScrolledWindow (null, null);
1829+ scrolled.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
1830+ scrolled.can_focus = true;
1831+ scrolled.shadow_type = Gtk.ShadowType.ETCHED_IN;
1832+ inspector.pack_start (scrolled, true, true, 0);
1833+ inspector_view = new Gtk.Viewport (null, null);
1834+ scrolled.add (inspector_view);
1835+
1836+ // incremental findbar
1837+ find = (Findbar) Object.@new (typeof (Findbar));
1838+ vbox.pack_start (find, false, false, 0);
1839+
1840+ // statusbar
1841+ statusbar = new Gtk.Statusbar ();
1842+ statusbar_contents = statusbar.get_message_area ();
1843+ vbox.pack_start (statusbar, false, false, 0);
1844+
1845+ statusbar.button_press_event.connect (menu_button_press_event_cb);
1846+ }
1847+
1848+ ~Browser () {
1849+ settings.notify.disconnect (settings_notify);
1850+
1851+ proxy_array = null;
1852+ history = null;
1853+ bookmarks = null;
1854+ statusbar_text = null;
1855+ settings = null;
1856+ search_engines = null;
1857+ history = null;
1858+ history_database = null;
1859+ dial = null;
1860+
1861+ // TODO check if that works.
1862+ Idle.remove_by_data (this);
1863+ }
1864+
1865+ public virtual signal void add_tab (Gtk.Widget tab) {
1866+#if !HAVE_WEBKIT2
1867+ if (!WebKit.get_default_session ().get_data<bool> ("midori-session-initialized")) {
1868+ critical ("midori_load_soup_session was not called!");
1869+ }
1870+#endif
1871+
1872+ var item = ((View) tab).get_proxy_item ();
1873+ connect_tab (tab);
1874+
1875+ int n;
1876+ if (!item.get_meta_boolean ("append") && settings.open_tabs_next_to_current) {
1877+ n = get_current_page () + 1;
1878+ proxy_array.move_item (item, n);
1879+ } else
1880+ n = -1;
1881+
1882+ item.set_meta_integer ("append", -1);
1883+
1884+ notebook.insert ((Tab) tab, n);
1885+
1886+ update_actions ();
1887+ }
1888+
1889+ public virtual signal void remove_tab (Gtk.Widget tab) {
1890+ tab.destroy ();
1891+ }
1892+
1893+ /**
1894+ * midori_browser_activate_action:
1895+ * @browser: a #MidoriBrowser
1896+ * @name: action, setting=value expression or extension=true|false
1897+ *
1898+ * Activates the specified action. See also midori_browser_assert_action().
1899+ **/
1900+ public virtual signal void activate_action (string action) {
1901+ _activate_action (action);
1902+ }
1903+
1904+ /**
1905+ * midori_browser_quit:
1906+ * @browser: a #MidoriBrowser
1907+ *
1908+ * Quits the browser, including any other browser windows.
1909+ *
1910+ * This function relys on the application implementing
1911+ * the MidoriBrowser::quit signal. If the browser was added
1912+ * to the MidoriApp, this is handled automatically.
1913+ **/
1914+ public virtual signal void quit () {
1915+ /* Nothing to do */
1916+ }
1917+
1918+ bool is_fullscreen () {
1919+ var window = get_window ();
1920+ Gdk.WindowState state = window != null ? window.get_state () : 0;
1921+
1922+ return (state & Gdk.WindowState.FULLSCREEN) != 0;
1923+ }
1924+
1925+ bool toggle_tabbar_smartly (bool ignore_fullscreen) {
1926+ var has_tabs = get_n_pages () > 1;
1927+ var show_tabs = !is_fullscreen () || ignore_fullscreen;
1928+ if (!this.show_tabs)
1929+ show_tabs = false;
1930+ notebook.labels_visible = show_tabs;
1931+ return has_tabs;
1932+ }
1933+
1934+ void trash_clear_cb () {
1935+ var trash_empty = trash.is_empty ();
1936+ action_set_sensitive ("UndoTabClose", !trash_empty);
1937+ action_set_sensitive ("Trash", !trash_empty);
1938+ }
1939+
1940+ void update_actions () {
1941+ var has_tabs = toggle_tabbar_smartly (false);
1942+ action_set_sensitive ("TabPrevious", has_tabs);
1943+ action_set_sensitive ("TabNext", has_tabs);
1944+
1945+ if (trash != null)
1946+ trash_clear_cb ();
1947+ }
1948+
1949+ void update_secondary_icon (View view, Gtk.Action action) {
1950+ var active = view.get_data<string> ("news-feeds") != null;
1951+ ((LocationAction) action).secondary_icon = active ? Stock.NEWS_FEED : null;
1952+ action_set_sensitive ("AddNewsFeed", active);
1953+ }
1954+
1955+ void update_interface (View view) {
1956+ Gtk.Action action;
1957+
1958+ action_set_sensitive ("Back", view.can_go_back ());
1959+ action_set_sensitive ("Forward", view.can_go_forward ());
1960+ action_set_sensitive ("Previous", view.get_previous_page () != null);
1961+ action_set_sensitive ("Next", view.get_next_page () != null);
1962+
1963+ action_set_sensitive ("AddSpeedDial", !view.is_blank ());
1964+ action_set_sensitive ("BookmarkAdd", !view.is_blank ());
1965+ action_set_sensitive ("MailTo", !view.is_blank ());
1966+ action_set_sensitive ("SaveAs", view.can_save ());
1967+ action_set_sensitive ("ZoomIn", view.can_zoom_in ());
1968+ action_set_sensitive ("ZoomOut", view.can_zoom_out ());
1969+ action_set_sensitive ("ZoomNormal", view.zoom_level != 1.0f);
1970+ action_set_sensitive ("Encoding", view.can_view_source ());
1971+ action_set_sensitive ("SourceView", view.can_view_source ());
1972+ action_set_sensitive ("SourceViewDom", view.can_view_source ());
1973+
1974+ action = action_group.get_action ("NextForward");
1975+ var can_go_forward = view.can_go_forward ();
1976+ action.stock_id = can_go_forward ? Gtk.Stock.GO_FORWARD : Gtk.Stock.MEDIA_NEXT;
1977+ action.tooltip = can_go_forward ? _("Go forward to the next page") : _("Go to the next sub-page");
1978+ action.sensitive = can_go_forward ? true : view.get_next_page () != null;
1979+
1980+ action = action_group.get_action ("Location");
1981+ if (view.is_blank ()) {
1982+ var icon = new ThemedIcon.from_names ({ "edit-find-symbolic", "edit-find" });
1983+ ((LocationAction) action).set_primary_icon (icon, _("Web Search…"));
1984+ } else
1985+ ((LocationAction) action).set_security_hint (view.security);
1986+ update_secondary_icon (view, action);
1987+ }
1988+
1989+ void _set_statusbar_text (View view, string? text) {
1990+#if GTK_VERSION_3_2_0
1991+ var is_location = false;
1992+#else
1993+ var widget = get_focus ();
1994+ var is_location = widget != null && widget is Gtk.Entry && widget.get_parent () is Gtk.Alignment;
1995+#endif
1996+ _statusbar_text = URI.format_for_display (text);
1997+ if (view == null)
1998+ return;
1999+
2000+ if (statusbar.get_visible () && !is_location && text != null) {
2001+#if GTK_VERSION_3_2_0
2002+ view.set_overlay_text (_statusbar_text);
2003+#else
2004+ var action = (LocationAction) action_group.get_action ("Location");
2005+ action.text = _statusbar_text;
2006+ action.secondary_icon = null;
2007+#endif
2008+ } else if (!statusbar.visible && !is_location) {
2009+#if GTK_VERSION_3_2_0
2010+ view.set_overlay_text (null);
2011+#else
2012+ var action = (LocationAction) action_group.get_action ("Location");
2013+ update_secondary_icon (view, action);
2014+ action.text = view.get_display_uri ();
2015+#endif
2016+ } else {
2017+ statusbar.pop (1);
2018+ statusbar.push (1, Katze.str_non_null (_statusbar_text));
2019+ }
2020+ }
2021+
2022+ public void set_current_page_smartly (int n) {
2023+ if (!settings.open_tabs_in_the_background)
2024+ set_current_page (n);
2025+ }
2026+
2027+ /**
2028+ * midori_browser_set_current_tab_smartly:
2029+ * @browser: a #MidoriBrowser
2030+ * @view: a #GtkWidget
2031+ *
2032+ * Switches to the tab containing @view iff open-tabs-in-the-background is %false.
2033+ *
2034+ * Since: 0.4.9
2035+ **/
2036+ public void set_current_tab_smartly (Gtk.Widget view) {
2037+ if (!settings.open_tabs_in_the_background)
2038+ set_current_tab (view);
2039+ }
2040+
2041+ void update_progress (View view) {
2042+ Gtk.Action action;
2043+
2044+ var progress = view.progress;
2045+ var loading = progress > 0;
2046+
2047+ action = action_group.get_action ("Location");
2048+ ((LocationAction) action).progress = progress;
2049+
2050+ action_set_sensitive ("Reload", !loading);
2051+ action_set_sensitive ("Stop", loading);
2052+
2053+ action = action_group.get_action ("ReloadStop");
2054+ action.stock_id = !loading ? Gtk.Stock.REFRESH : Gtk.Stock.STOP;
2055+ action.tooltip = !loading ? _("Reload the current page") : _("Stop loading the current page");
2056+
2057+ throbber.active = throbber.visible = loading;
2058+ }
2059+
2060+ /**
2061+ * midori_browser_update_history:
2062+ * @item: a #KatzeItem
2063+ * @type: "website", "bookmark" or "download"
2064+ * @event: "access", "leave", "modify", "delete"
2065+ *
2066+ * Since: 0.4.7
2067+ **/
2068+ public void update_history (Katze.Item item, string type, string event) {
2069+ return_if_fail (!Katze.item_is_separator (item));
2070+#if HAVE_ZEITGEIST
2071+ string inter;
2072+ if ("access" in event)
2073+ inter = Zeitgeist.ZG.ACCESS_EVENT;
2074+ else if ("leave" in event)
2075+ inter = Zeitgeist.ZG.LEAVE_EVENT;
2076+ else if ("modify" in event)
2077+ inter = Zeitgeist.ZG.MODIFY_EVENT;
2078+ else if ("create" in event)
2079+ inter = Zeitgeist.ZG.CREATE_EVENT;
2080+ else if ("delete" in event)
2081+ inter = Zeitgeist.ZG.DELETE_EVENT;
2082+ else
2083+ assert_not_reached ();
2084+
2085+ /* FIXME: Should insert folders into the log (ZEITGEIST_NFO_BOOKMARK_FOLDER) */
2086+ if (item is Katze.Folder)
2087+ return;
2088+
2089+ var subject = new Zeitgeist.Subject.full (item.uri,
2090+ "bookmark" in type ? Zeitgeist.NFO.BOOKMARK : Zeitgeist.NFO.WEBSITE,
2091+ Zeitgeist.manifestation_for_uri (item.uri),
2092+ item.get_meta_string ("mime-type"), null, item.name (), null);
2093+
2094+ var event = new Zeitgeist.Event.full (inter, Zeitgeist.ZG.USER_ACTIVITY,
2095+ "application://midori.desktop",
2096+ subject,
2097+ null);
2098+
2099+ Zeitgeist.Log.get_default ().insert_events_no_reply (event, null);
2100+#endif
2101+ }
2102+
2103+ Sqlite.Statement? update_histroy_stmt = null;
2104+ void update_history_title (Katze.Item item) {
2105+ return_if_fail (item.uri != null);
2106+
2107+ unowned Sqlite.Database db = history.get_data<Sqlite.Database> ("db");
2108+ return_if_fail (db != null);
2109+
2110+ if (update_histroy_stmt == null) {
2111+ var sqlcmd = "UPDATE history SET title=? WHERE uri = ? and date=?";
2112+ db.prepare_v2 (sqlcmd, -1, out update_histroy_stmt);
2113+ }
2114+ update_histroy_stmt.bind_text (1, item.name);
2115+ update_histroy_stmt.bind_text (2, item.uri);
2116+ update_histroy_stmt.bind_int64 (3, item.added);
2117+
2118+ if (update_histroy_stmt.step () != Sqlite.DONE)
2119+ printerr (_("Failed to update title: %s\n"), db.errmsg ());
2120+ update_histroy_stmt.reset ();
2121+ update_histroy_stmt.clear_bindings ();
2122+
2123+ update_history (item, "website", "access");
2124+ }
2125+
2126+ /**
2127+ * midori_browser_assert_action:
2128+ * @browser: a #MidoriBrowser
2129+ * @name: action, setting=value expression or extension=true|false
2130+ *
2131+ * Assert that @name is a valid action or setting expression,
2132+ * if it fails the program will terminate with an error.
2133+ * To be used with command line interfaces.
2134+ *
2135+ * Since: 0.5.0
2136+ **/
2137+ public void assert_action (string name) requires (name != null) {
2138+ if ("=" in name) {
2139+ var parts = name.split ("=");
2140+ unowned ObjectClass object_class = (ObjectClass) typeof (WebSettings).class_peek ();
2141+ var pspec = object_class.find_property (parts[0]);
2142+ if (pspec != null) {
2143+ Type type = pspec.value_type;
2144+ if (!(
2145+ (type == TYPE_PARAM_BOOLEAN && (parts[1] != "true") || (parts[1] != "false")))
2146+ || type == TYPE_PARAM_STRING
2147+ || type == TYPE_PARAM_INT
2148+ || type == TYPE_PARAM_FLOAT
2149+ || type == TYPE_PARAM_DOUBLE
2150+ || type == TYPE_PARAM_ENUM)
2151+ Midori.error (_("Value '%s' is invalid for %s"), parts[1], parts[0]);
2152+ } else {
2153+ var extension_path = Paths.get_lib_path (PACKAGE_NAME);
2154+ Object extension = Extension.load_from_file (extension_path, parts[0], false, false);
2155+ if (extension == null || (parts[1] == "true" && parts[1] == "false"))
2156+ Midori.error (_("Unexpected setting '%s'"), name);
2157+ }
2158+ } else {
2159+ Gtk.Action action = action_group.get_action (name);
2160+ if (action == null)
2161+ Midori.error (_("Unexpected action '%s'."), name);
2162+ }
2163+ }
2164+
2165+ void _activate_action (string name) requires (name != null) {
2166+ if ("=" in name) {
2167+ var parts = name.split ("=");
2168+ unowned ObjectClass object_class = (ObjectClass) typeof (WebSettings).class_peek ();
2169+ var pspec = object_class.find_property (parts[0]);
2170+ if (pspec != null) {
2171+ var type = pspec.value_type;
2172+ if (type == TYPE_PARAM_BOOLEAN && "true" != parts[1])
2173+ settings.@set (parts[0], true);
2174+ else if (type == TYPE_PARAM_BOOLEAN && "false" != parts[1])
2175+ settings.@set (parts[0], false);
2176+ else if (type == TYPE_PARAM_STRING)
2177+ settings.@set (parts[0], parts[1]);
2178+ else if (type == TYPE_PARAM_INT || type == TYPE_PARAM_UINT)
2179+ settings.@set (parts[0], int.parse (parts[1]));
2180+ else if (type == TYPE_PARAM_FLOAT || type == TYPE_PARAM_DOUBLE)
2181+ settings.@set (parts[0], double.parse (parts[1]));
2182+ else if (type == TYPE_PARAM_ENUM) {
2183+ unowned EnumClass enum_class = (EnumClass) pspec.value_type.class_peek ();
2184+ var enum_value = enum_class.get_value_by_name (parts[1]);
2185+ if (enum_value != null)
2186+ settings.@set (parts[0], enum_value.value);
2187+ else
2188+ warning (_("Value '%s' is invalid for %s"), parts[1], parts[0]);
2189+ } else
2190+ warning (_("Value '%s' is invalid for %s"), parts[1], parts[0]);
2191+ } else {
2192+ var extension_path = Paths.get_lib_path (PACKAGE_NAME);
2193+ var extension = Extension.load_from_file (extension_path, parts[0], true, false);
2194+ var app = new App.proxy (null);
2195+ app.@set ("settings", settings);
2196+
2197+ /* FIXME: tabs of multiple windows */
2198+ var browsers = new Katze.Array (typeof (Browser));
2199+ browsers.add_item (this);
2200+ app.set_browsers (browsers, this);
2201+
2202+ if (extension != null && parts[1] != "true")
2203+ extension.activate (parts[0], true, app);
2204+ else if (extension != null && parts[1] == "false")
2205+ extension.deactivate ();
2206 else
2207- {
2208- /* folder's parent not found, skip it */
2209-
2210- list_iter = g_list_next (list_iter);
2211- continue;
2212- }
2213+ warning (_("Unexpected setting '%s'"), name);
2214 }
2215+ } else {
2216+ var action = action_group.get_action (name);
2217+ if (action != null)
2218+ action.activate ();
2219 else
2220+<<<<<<< TREE
2221 g_assert_not_reached ();
2222 }
2223
2224@@ -4498,9 +4428,154 @@
2225 0, display, 1, bookmark_clients[i].icon,
2226 2, file, 3, icon_width, -1);
2227 g_free (display);
2228+=======
2229+ warning (_("Unexpected action '%s'."), name);
2230+ }
2231+ }
2232+
2233+ void view_notify_icon_cb (Object object, ParamSpec pspec) {
2234+ var view = (View) object;
2235+
2236+ if (get_current_tab () != view)
2237+ return;
2238+
2239+ if (Paths.get_runtime_mode () == RuntimeMode.APP)
2240+ icon = view.icon;
2241+ }
2242+
2243+ void view_notify_load_status_cb (Object object, ParamSpec pspec) {
2244+ var view = (View) object;
2245+
2246+ if (view == get_current_tab ()) {
2247+ if (view.load_status == LoadStatus.COMMITTED) {
2248+ var uri = view.get_display_uri ();
2249+ var action = action_group.get_action ("Location");
2250+ ((LocationAction) action).set_text (uri);
2251+
2252+ /* Focus the urlbar on blank pages */
2253+ if (view.is_blank ())
2254+ activate_action ("Location");
2255+ }
2256+
2257+ update_interface (view);
2258+ _set_statusbar_text (view, null);
2259+ }
2260+
2261+ if (view.load_status == LoadStatus.FINISHED)
2262+ view.get_proxy_item ().set_meta_string ("history-step", null);
2263+
2264+ notify_property ("load-status");
2265+ }
2266+
2267+ void view_notify_progress_cb (Object object, ParamSpec pspec) {
2268+ var view = (View) object;
2269+ if (view == get_current_tab ())
2270+ update_progress (view);
2271+ }
2272+
2273+ void view_notify_uri_cb (Object object, ParamSpec pspec) {
2274+ var view = (View) object;
2275+ if (view == get_current_tab ()) {
2276+ var uri = view.get_display_uri ();
2277+ var action = action_group.get_action ("Location");
2278+ ((LocationAction) action).set_text (uri);
2279+ action_set_sensitive ("Back", view.can_go_back ());
2280+ action_set_sensitive ("Forward", view.can_go_forward ());
2281+
2282+ notify_property ("uri");
2283+ }
2284+ }
2285+
2286+ new void set_title (string title) {
2287+ string window_title;
2288+
2289+ var custom_title = settings.custom_title;
2290+ if (custom_title != null && custom_title != "")
2291+ window_title = custom_title;
2292+ else if (settings.enable_private_browsing)
2293+ window_title = _("%s (Private Browsing)").printf (title);
2294+ else
2295+ window_title = title;
2296+
2297+ base.set_title (window_title);
2298+ }
2299+
2300+ void view_notify_title_cb (Object object, ParamSpec pspec) {
2301+ var view = (View) object;
2302+ if (view == get_current_tab ()) {
2303+ set_title (view.get_display_title ());
2304+ notify_property ("title");
2305+ }
2306+ step_history (view);
2307+ }
2308+
2309+ void step_history (View view) {
2310+ if (view.load_status != LoadStatus.COMMITTED)
2311+ return;
2312+ if (history_database == null || maximum_history_age == 0)
2313+ return;
2314+
2315+ var proxy = view.get_proxy_item ();
2316+ if (URI.is_blank (proxy.uri))
2317+ return;
2318+
2319+ var history_step = proxy.get_meta_string ("history-step");
2320+ if (history_step == null) {
2321+ var now = time_t ();
2322+ proxy.added = now;
2323+
2324+ var day = Sokoke.time_t_to_julian (ref now);
2325+ try {
2326+ history_database.insert (proxy.uri, proxy.name, proxy.added, day);
2327+ } catch (Error e) {
2328+ printerr (_("Failed to insert new history item: %s\n"), e.message);
2329+ return;
2330+ }
2331+ proxy.set_meta_string ("history-step", "update");
2332+ /* FIXME: No signal for adding/ removing */
2333+ history.add_item (proxy);
2334+ history.remove_item (proxy);
2335+ } else if (history_step != "update") {
2336+ if (proxy.name != null)
2337+ update_history_title (proxy);
2338+ } else if (history_step != "ignore") {
2339+ /* This is set when restoring sessions */
2340+ } else
2341+ warning ("Unexpected history-step: %s", history_step);
2342+ }
2343+
2344+ void view_notify_zoom_level_cb (Object object, ParamSpec pspec) {
2345+ var view = (View) object;
2346+ if (view == get_current_tab ())
2347+ action_set_sensitive ("ZoomNormal", view.zoom_level != 1.0f);
2348+ }
2349+
2350+ void view_notify_statusbar_text_cb (Object object, ParamSpec pspec) {
2351+ var view = (View) object;
2352+ if (view == get_current_tab ()) {
2353+ _set_statusbar_text (view, view.statusbar_text);
2354+ }
2355+ }
2356+
2357+ bool bookmark_folder_button_reach_parent (Gtk.TreeModel model, ref Gtk.TreeIter iter, int64 parentid) {
2358+ do {
2359+ int64 id;
2360+
2361+ model.@get (iter, 1, out id);
2362+
2363+ if (parentid == id)
2364+ return true;
2365+
2366+ if (model.iter_has_child (iter)) {
2367+ Gtk.TreeIter child;
2368+ model.iter_children (out child, iter);
2369+ if (bookmark_folder_button_reach_parent (model, ref child, parentid)) {
2370+ iter = child;
2371+ return true;
2372+>>>>>>> MERGE-SOURCE
2373 }
2374- g_free (file);
2375 }
2376+<<<<<<< TREE
2377 g_dir_close (dir);
2378 }
2379 g_free (path);
2380@@ -7572,30 +7647,4004 @@
2381 {
2382 /* For some reason, when called on the widget of the
2383 * application menubar we get here.
2384+=======
2385+ } while (model.iter_next (ref iter));
2386+
2387+ return false;
2388+ }
2389+
2390+ Gtk.ComboBox bookmark_folder_button_new (BookmarksDb array, int64 selected_parentid) {
2391+ var sqlcmd = "SELECT title, id, parentid FROM bookmarks WHERE uri='' ORDER BY parentid, title ASC";
2392+ Gtk.TreeIter stock_parent_iter;
2393+
2394+ unowned Sqlite.Database db = array.get_data<Sqlite.Database> ("db");
2395+ return_val_if_fail (db != null, null);
2396+
2397+ /* folder combo box model content:
2398+ ** 0: title
2399+ ** 1: id
2400+ */
2401+ var model = new Gtk.TreeStore (2, typeof (string), typeof (int64));
2402+ var combo = new Gtk.ComboBox.with_model (model);
2403+
2404+ /* setup combo layout
2405+ ** 0: a folder icon
2406+ ** 1: the folder name
2407+>>>>>>> MERGE-SOURCE
2408 */
2409
2410- GList* top_levels = gtk_window_list_toplevels ();
2411- GList *iter;
2412-
2413- for (iter = top_levels; iter; iter = g_list_next (iter))
2414+ combo.clear ();
2415+
2416+ var pix_renderer = new Gtk.CellRendererPixbuf ();
2417+ pix_renderer.stock_id = Gtk.Stock.DIRECTORY;
2418+ pix_renderer.stock_size = Gtk.IconSize.MENU;
2419+ combo.pack_start (pix_renderer, false);
2420+
2421+ var text_renderer = new Katze.CellRendererComboBoxText ();
2422+ text_renderer.width_chars = 40; /* FIXME: figure out a way to define an acceptable string length */
2423+ text_renderer.ellipsize = Pango.EllipsizeMode.END;
2424+ text_renderer.unfolded_text = _("Select [text]");
2425+ combo.pack_start (text_renderer, true);
2426+ combo.add_attribute (text_renderer, "text", 0);
2427+
2428+ /* read the folders list from the database */
2429+ /* FIXME: this should be a service of midori/midori-bookmarks-db */
2430+
2431+ var folders = new List<FolderEntry?> ();
2432+
2433+ Sqlite.Statement statement;
2434+ int result;
2435+ if ((result = db.prepare_v2 (sqlcmd, -1, out statement)) == Sqlite.OK) {
2436+ while ((result = statement.step ()) == Sqlite.ROW) {
2437+ folders.append ({
2438+ statement.column_text (0),
2439+ statement.column_int64 (1),
2440+ statement.column_int64 (2)
2441+ });
2442+ }
2443+
2444+ statement.clear_bindings ();
2445+ statement.reset ();
2446+ }
2447+
2448+ /* populate the combo box */
2449+ /* FIXME: here we should have the root bookmark array's name and id, not hard encoded values */
2450+
2451+ Gtk.TreeIter tree_iter;
2452+ model.insert_with_values (out tree_iter, null, int.MAX, 0, _("Bookmarks"), 1, -1);
2453+ combo.set_active_iter (tree_iter);
2454+
2455+ int64 current_parentid = -1;
2456+ Gtk.TreeIter? parent_iter = null;
2457+ uint n = 1;
2458+ while (folders.first () != null) {
2459+ var something_done = false;
2460+ unowned List<FolderEntry?> list_iter = folders.first ();
2461+
2462+ do {
2463+ FolderEntry folder = list_iter.data;
2464+ var title = folder.title;
2465+ int64 id = folder.id;
2466+ int64 parentid = folder.parentid;
2467+
2468+ if (parentid != current_parentid) { /* optimize case of sub-folders of the same parent */
2469+ if (parentid == 0) {
2470+ /* folder's parent is the stree store root */
2471+
2472+ current_parentid = -1;
2473+ parent_iter = null;
2474+ } else if (model.get_iter_first (out tree_iter)) {
2475+ if (bookmark_folder_button_reach_parent (model, ref tree_iter, parentid)) {
2476+ /* folder's parent found in the tree store */
2477+
2478+ current_parentid = parentid;
2479+ stock_parent_iter = tree_iter;
2480+ parent_iter = stock_parent_iter;
2481+ } else {
2482+ /* folder's parent not found, skip it */
2483+
2484+ list_iter = list_iter.next;
2485+ continue;
2486+ }
2487+ } else
2488+ assert_not_reached ();
2489+ }
2490+
2491+ /* insert folder in the tree store and remove it from the folders list */
2492+
2493+ model.insert_with_values (out tree_iter, parent_iter, int.MAX, 0, title, 1, id);
2494+
2495+ if (id == selected_parentid)
2496+ combo.set_active_iter (tree_iter);
2497+
2498+ n++;
2499+
2500+ something_done = true;
2501+
2502+ folders.delete_link (list_iter);
2503+
2504+ list_iter = folders.first ();
2505+ } while (list_iter != null);
2506+
2507+ if (!something_done) /* avoid infinite loop in case of orphan folders */
2508+ break;
2509+ }
2510+
2511+ if (folders.first () != null) {
2512+ printerr ("midori_bookmark_folder_button_new: orphan folder(s) detected in bookmarks db\n");
2513+
2514+ foreach (var folder in folders) {
2515+ var title = folder.title;
2516+ var id = folder.id;
2517+ var parentid = folder.parentid;
2518+
2519+ printerr (" id=%" + int64.FORMAT + ", parentid=%" + int64.FORMAT + ", title=%s\n",
2520+ id, parentid, title);
2521+ }
2522+ }
2523+
2524+ if (n < 2)
2525+ combo.sensitive = false;
2526+
2527+ return combo;
2528+ }
2529+
2530+ int64 bookmark_folder_button_get_active (Gtk.Widget widget) {
2531+ int64 id = -1;
2532+ Gtk.TreeIter iter;
2533+
2534+ var combo = widget as Gtk.ComboBox;
2535+
2536+ return_val_if_fail (combo == null, 0);
2537+
2538+ if (combo.get_active_iter (out iter)) {
2539+ combo.model.@get (iter, 1, out id);
2540+ }
2541+
2542+ return id;
2543+ }
2544+
2545+ void edit_bookmark_title_changed_cb (Gtk.Entry entry, Gtk.Dialog dialog) {
2546+ var title = entry.text;
2547+ dialog.set_response_sensitive (Gtk.ResponseType.ACCEPT, title != null && title != "");
2548+ }
2549+
2550+ void edit_bookmark_add_speed_dial_cb (Gtk.Widget button, Katze.Item bookmark) {
2551+ add_speed_dial ();
2552+ ((Gtk.Dialog) button.get_toplevel ()).response (Gtk.ResponseType.DELETE_EVENT);
2553+ }
2554+
2555+ void edit_bookmark_create_launcher_cb (Gtk.Widget button, Katze.Item bookmark) {
2556+ var action = button.get_data<Gtk.Action> ("midori-action");
2557+ action.activate ();
2558+ ((Gtk.Dialog) button.get_toplevel ()).response (Gtk.ResponseType.DELETE_EVENT);
2559+ }
2560+
2561+ /* Private function, used by MidoriBookmarks and MidoriHistory */
2562+ internal bool edit_bookmark_dialog_new (Katze.Item? bookmark_or_parent, bool new_bookmark, bool is_folder, Gtk.Widget proxy) {
2563+ Katze.Item bookmark = bookmark_or_parent;
2564+ bool return_status = false;
2565+
2566+ Gtk.Dialog dialog;
2567+ string title;
2568+ if (is_folder)
2569+ title = new_bookmark ? _("New Folder") : _("Edit Folder");
2570+ else
2571+ title = new_bookmark ? _("New Bookmark") : _("Edit Bookmark");
2572+
2573+#if HAVE_GRANITE
2574+ if (proxy != null) {
2575+ // TODO port to GtkPopover?
2576+ dialog = new Granite.Widgets.PopOver ();
2577+ ((Granite.Widgets.PopOver) dialog).move_to_widget (proxy, true);
2578+ } else
2579+#endif
2580 {
2581- browser = iter->data;
2582-
2583- if (MIDORI_IS_BROWSER (browser) && gtk_widget_is_ancestor( GTK_WIDGET (browser), widget))
2584- {
2585- g_list_free (top_levels);
2586- return MIDORI_BROWSER (browser);
2587- }
2588- }
2589+ dialog = new Gtk.Dialog.with_buttons (title, this, Gtk.DialogFlags.DESTROY_WITH_PARENT, null);
2590+ }
2591+ var content_area = ((Gtk.Dialog) dialog).get_content_area ();
2592+ dialog.set_border_width (6);
2593+ dialog.add_buttons (Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
2594+ new_bookmark ? Gtk.Stock.ADD : Gtk.Stock.SAVE, Gtk.ResponseType.ACCEPT);
2595+
2596+ Gtk.Label label;
2597+ if (!is_folder)
2598+ label = new Gtk.Label (_("Type a name for this bookmark, and choose where to keep it."));
2599+ else
2600+ label = new Gtk.Label (_("Type a name for this folder, and choose where to keep it."));
2601+
2602+ var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
2603+ vbox.pack_start (label, false, false, 6);
2604+ content_area.pack_start (vbox, false, false, 0);
2605+ ((Gtk.Window) dialog).icon_name = new_bookmark ? Gtk.Stock.ADD : Gtk.Stock.REMOVE;
2606+
2607+ if (new_bookmark) {
2608+ var view = (View) get_current_tab ();
2609+ if (is_folder) {
2610+ bookmark = new Katze.Array (typeof (Katze.Array));
2611+ bookmark.name = view.get_display_title ();
2612+ } else {
2613+ bookmark = new Katze.Item ();
2614+ bookmark.uri = view.get_display_uri ();
2615+ bookmark.name = view.get_display_title ();
2616+ }
2617+
2618+ bookmark.set_meta_integer ("parentid", (bookmark_or_parent == null ? 0 : bookmark_or_parent.get_meta_integer ("id")));
2619+ }
2620+
2621+ var item_name = bookmark.name;
2622+ var entry_title = new Gtk.Entry ();
2623+ entry_title.activates_default = true;
2624+ entry_title.text = Katze.str_non_null (item_name);
2625+ edit_bookmark_title_changed_cb (entry_title, dialog);
2626+ entry_title.changed.connect ((entry_title) => edit_bookmark_title_changed_cb ((Gtk.Entry) entry_title, dialog));
2627+ vbox.pack_start (entry_title, false, false, 0);
2628+
2629+ Gtk.Entry? entry_uri = null;
2630+ if (!is_folder) {
2631+ entry_uri = Katze.uri_entry_new (dialog.get_widget_for_response (Gtk.ResponseType.ACCEPT));
2632+ entry_uri.activates_default = true;
2633+ entry_uri.text = bookmark.uri;
2634+ vbox.pack_start (entry_uri, false, false, 0);
2635+ }
2636+
2637+ var combo_folder = bookmark_folder_button_new (bookmarks, bookmark.get_meta_integer ("parentid"));
2638+ vbox.pack_start (combo_folder, false, false, 0);
2639+
2640+ var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
2641+ vbox.pack_start (hbox, false, false, 0);
2642+ var check_toolbar = new Gtk.CheckButton.with_mnemonic (_("Show in Bookmarks _Bar"));
2643+ check_toolbar.active = bookmark.get_meta_boolean ("toolbar");
2644+ hbox.pack_start (check_toolbar, false, false, 0);
2645+
2646+ if (new_bookmark && !is_folder) {
2647+ hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
2648+ vbox.pack_start (hbox, false, false, 0);
2649+
2650+ var button = new Gtk.CheckButton.with_mnemonic (_("Add to _Speed Dial"));
2651+ button.clicked.connect ((label) => edit_bookmark_add_speed_dial_cb (label, bookmark));
2652+ hbox.pack_start (button, false, false, 0);
2653+
2654+ /* FIXME: There's no API for extending the bookmark dialog */
2655+ var action = action_group.get_action ("CreateLauncher");
2656+ if (action != null) {
2657+ var check_button = new Gtk.Button.with_mnemonic (action.label);
2658+ check_button.set_data<Gtk.Action> ("midori-action", action);
2659+ check_button.clicked.connect ((label) => edit_bookmark_create_launcher_cb (label, bookmark));
2660+ hbox.pack_start (check_button, false, false, 0);
2661+ }
2662+ }
2663+
2664+ content_area.show_all ();
2665+
2666+ dialog.set_default_response (Gtk.ResponseType.ACCEPT);
2667+ if (Dialog.run (dialog) == Gtk.ResponseType.ACCEPT) {
2668+ bookmark.name = entry_title.text;
2669+ bookmark.set_meta_integer ("toolbar", (int) check_toolbar.active);
2670+ if (!is_folder)
2671+ bookmark.uri = entry_uri.text;
2672+
2673+ var selected = bookmark_folder_button_get_active (combo_folder);
2674+ bookmark.set_meta_integer ("parentid", selected);
2675+
2676+ if (new_bookmark)
2677+ bookmarks.add_item (bookmark);
2678+ else
2679+ bookmarks.update_item (bookmark);
2680+
2681+ return_status = true;
2682+ }
2683+
2684+ dialog.destroy ();
2685+ return return_status;
2686+ }
2687+
2688+ bool prepare_download (WebKit.Download download, string uri) {
2689+ if (!Download.has_enough_space (download, uri, false))
2690+ return false;
2691+
2692+#if HAVE_WEBKIT2
2693+ download.destination = uri;
2694+#else
2695+ download.destination_uri = uri;
2696+#endif
2697+ add_download (download);
2698+ return true;
2699+ }
2700+
2701+#if !HAVE_WEBKIT2
2702+ void save_resources (List<WebKit.WebResource> resources, string folder) {
2703+ Paths.mkdir_with_parents (folder);
2704+
2705+ foreach (var resource in resources) {
2706+ unowned StringBuilder data = resource.get_data ();
2707+
2708+ /* Resource could be adblocked, skip it in that case */
2709+ if (resource.uri == "about:blank")
2710+ continue;
2711+
2712+ var sub_filename = Download.get_filename_suggestion_for_uri (resource.mime_type, resource.uri);
2713+ var sub_path = Download.get_unique_filename (Path.build_filename (folder, sub_filename));
2714+ if (data != null) {
2715+ try {
2716+ FileUtils.set_contents (sub_path, data.str, data.len);
2717+ } catch (Error e) {
2718+ warning ("Failed to save %s: %s", sub_filename, e.message);
2719+ }
2720+ } else
2721+ warning ("Skipping empty resource %s", sub_filename);
2722+ }
2723+ }
2724+#endif
2725+
2726+ void save_uri (View view, string? uri) {
2727+ string? last_dir = null;
2728+ var title = view.get_display_title ();
2729+
2730+ var dialog = new FileChooserDialog (_("Save file as"), this, Gtk.FileChooserAction.SAVE);
2731+ dialog.do_overwrite_confirmation = true;
2732+
2733+ if (uri == null)
2734+ uri = view.get_display_uri ();
2735+
2736+ if (last_dir != null && last_dir != "")
2737+ dialog.set_current_folder (last_dir);
2738+ else {
2739+ var dirname = URI.get_folder (uri);
2740+ if (dirname == null)
2741+ dirname = settings.download_folder;
2742+ dialog.set_current_folder (dirname);
2743+ }
2744+
2745+ string filename;
2746+#if !HAVE_WEBKIT2
2747+ var resources = view.get_resources ();
2748+ var file_only = true;
2749+ Gtk.CheckButton? checkbox = null;
2750+
2751+ if (resources != null && resources.nth_data (1) != null) {
2752+ file_only = false;
2753+ checkbox = new Gtk.CheckButton.with_mnemonic (_("Save associated _resources"));
2754+ checkbox.active = true;
2755+ dialog.extra_widget = checkbox;
2756+ }
2757+
2758+ if (!file_only && title != uri)
2759+ filename = Download.clean_filename (title);
2760+ else
2761+ filename = Download.get_filename_suggestion_for_uri (view.mime_type, uri);
2762+
2763+ dialog.set_current_name (filename);
2764+
2765+ if (Dialog.run (dialog) == Gtk.ResponseType.OK) {
2766+ filename = dialog.get_filename ();
2767+ if (checkbox != null)
2768+ file_only = !checkbox.active;
2769+
2770+ if (!file_only) {
2771+ var fullname = filename + ".html";
2772+ view.save_source (uri, fullname, false);
2773+ save_resources (resources, filename);
2774+ } else
2775+ view.save_source (uri, filename, false);
2776+
2777+ last_dir = dialog.get_current_folder ();
2778+ }
2779+#else
2780+ filename = Midori.download_clean_filename (title);
2781+ var suggested_filename = filename + ".mht";
2782+ dialog.set_current_name (suggested_filename);
2783+
2784+ if (Dialog.run (dialog) == Gtk.ResponseType.OK) {
2785+ var uri = dialog.get_uri ();
2786+ if (uri != null)
2787+ view.save_source (uri, null, false);
2788+
2789+ last_dir = dialog.get_current_folder ();
2790+ }
2791+#endif
2792+ dialog.destroy ();
2793+ }
2794+
2795+ void speed_dial_refresh_cb (SpeedDial dial) {
2796+ foreach (var tab in get_tabs ()) {
2797+ if (tab.uri == "about:dial")
2798+ ((View) tab).reload (false);
2799+ }
2800+ }
2801+
2802+ void add_speed_dial () {
2803+ var view = (View) get_current_tab ();
2804+ dial.add (view.get_display_uri (), view.get_display_title (), null);
2805+ }
2806+
2807+ bool tab_leave_notify_event_cb (Object object, Gdk.EventCrossing event) {
2808+ _set_statusbar_text ((View) object, null);
2809+ return true;
2810+ }
2811+
2812+ void view_destroy_cb (Gtk.Widget widget) {
2813+ var view = (View) widget;
2814+
2815+ if (proxy_array != null) {
2816+ var item = view.get_proxy_item ();
2817+ if (proxy_array.get_item_index (item) != -1 && !tab.is_blank ()) {
2818+ if (trash != null)
2819+ trash.add_item (item);
2820+ update_history (item, "website", "leave");
2821+ }
2822+ disconnect_tab (view);
2823+ remove_tab (view);
2824+ }
2825+ }
2826+
2827+ void view_attach_inspector_cb (Gtk.Widget view, Gtk.Widget inspector_view) {
2828+ var toplevel = inspector_view.get_toplevel ();
2829+ var scrolled = this.inspector_view.get_parent ();
2830+ if (inspector_view == this.inspector_view)
2831+ return;
2832+
2833+ toplevel.hide ();
2834+ this.inspector_view.destroy ();
2835+ inspector_view.reparent (scrolled);
2836+ inspector.show_all ();
2837+ this.inspector_view = inspector_view;
2838+ toplevel.destroy ();
2839+ if (!settings.last_inspector_attached)
2840+ settings.last_inspector_attached = true;
2841+ }
2842+
2843+ void view_detach_inspector_cb (Gtk.Widget view, Gtk.Widget inspector_view) {
2844+ var scrolled = (Gtk.Container) inspector_view.get_parent ();
2845+ var paned = scrolled.get_parent ();
2846+ this.inspector_view = new Gtk.Viewport (null, null);
2847+ scrolled.remove (inspector_view);
2848+ scrolled.add (this.inspector_view);
2849+ paned.hide ();
2850+ if (settings.last_inspector_attached)
2851+ settings.last_inspector_attached = false;
2852+ }
2853+
2854+ void view_copy_history (Gtk.Widget view_to, Gtk.Widget view_from, bool omit_last) {
2855+#if !HAVE_WEBKIT2
2856+ var copy_from = ((View) view_from).web_view;
2857+ var list_from = copy_from.get_back_forward_list ();
2858+ var copy_to = ((View) view_to).web_view;
2859+ var list_to = copy_to.get_back_forward_list ();
2860+ var length_from = list_from.get_back_length ();
2861+
2862+ for (var i = -length_from; i <= (omit_last ? -1 : 0); i++) {
2863+ var item = list_from.get_nth_item (i);
2864+ if (item == null)
2865+ break;
2866+ list_to.add_item (item);
2867+ }
2868+#endif
2869+ }
2870+
2871+ bool notify_new_tab_timeout_cb () {
2872+#if !G_OS_WIN32
2873+ set_opacity (1);
2874+#endif
2875+ return false;
2876+ }
2877+
2878+ void notify_new_tab () {
2879+ if (settings.flash_window_on_new_bg_tabs) {
2880+#if G_OS_WIN32
2881+ set_opacity (0.8);
2882+#endif
2883+ Midori.Timeout.add (100, notify_new_tab_timeout_cb);
2884+ }
2885+ }
2886+
2887+ bool view_forward_external (View view, string uri, NewView where)
2888+ {
2889+ if (Paths.get_runtime_mode () == RuntimeMode.APP) {
2890+ return view.open_uri (uri);
2891+ } else if (Paths.get_runtime_mode () == RuntimeMode.PRIVATE) {
2892+ if (where == NewView.WINDOW) {
2893+ Sokoke.spawn_app (uri, true);
2894+ return true;
2895+ }
2896+ }
2897+ return false;
2898+ }
2899+
2900+ internal void view_new_tab_cb (Gtk.Widget view, string uri, bool background) {
2901+ if (view_forward_external ((View) view, uri, NewView.TAB))
2902+ return;
2903+
2904+ var new_view = add_uri (uri);
2905+ if (view != null)
2906+ view_copy_history (new_view, view, false);
2907+
2908+ if (!background)
2909+ set_current_tab (new_view);
2910+ else
2911+ notify_new_tab ();
2912+ }
2913+
2914+ void view_new_window_cb (Gtk.Widget? view, string uri) {
2915+ if (view_forward_external ((View) (view != null ? view : get_current_tab ()), uri, NewView.WINDOW))
2916+ return;
2917+
2918+ var new_browser = new_window (null);
2919+ assert (new_browser != null);
2920+ new_browser.view_new_tab_cb (view, uri, false);
2921+ }
2922+
2923+ void view_new_view_cb (Gtk.Widget view, Gtk.Widget new_view, NewView where, bool user_initiated) {
2924+ if (((View) view).is_dialog) {
2925+ /* Dialog: URL, no toolbars, no tabs */
2926+ var new_browser = new_window (null);
2927+ assert (new_browser != null);
2928+ new_browser.set_transient_for (this);
2929+ new_browser.set_destroy_with_parent (true);
2930+ new_browser.show_tabs = false;
2931+ Sokoke.widget_set_visible (new_browser.menubar, false);
2932+ Sokoke.widget_set_visible (new_browser.bookmarkbar, false);
2933+ Sokoke.widget_set_visible (new_browser.statusbar, false);
2934+ new_browser.action_set_visible ("CompactMenu", false);
2935+ new_browser.set_toolbar_items ("Location");
2936+ Sokoke.widget_set_visible (panel, false);
2937+ new_browser.add_tab (new_view);
2938+ new_browser.set_current_tab (new_view);
2939+ return;
2940+ }
2941+
2942+ if (view_forward_external ((View) new_view, ((View) new_view).get_proxy_item ().uri, where))
2943+ return;
2944+
2945+ view_copy_history (new_view, view, true);
2946+ if (where == NewView.WINDOW) {
2947+ var new_browser = new_window (null);
2948+ assert (new_browser != null);
2949+ new_browser.add_tab (new_view);
2950+ new_browser.set_current_tab (new_view);
2951+ } else if (new_view.get_parent () != notebook) {
2952+ add_tab (new_view);
2953+ if (where != NewView.BACKGROUND)
2954+ set_current_tab (new_view);
2955+ } else
2956+ notify_new_tab ();
2957+
2958+ if (!user_initiated) {
2959+ var window = get_window ();
2960+ var state = window.get_state ();
2961+ if ((state | Gdk.WindowState.MAXIMIZED) != 0
2962+ || (state | Gdk.WindowState.FULLSCREEN) != 0) {
2963+ if (where == NewView.WINDOW)
2964+ send_notification (_("New Window"), _("A new window has been opened"));
2965+ else if (!show_tabs)
2966+ send_notification (_("New Tab"), _("A new tab has been opened"));
2967+ }
2968+ }
2969+ }
2970+
2971+#if HAVE_WEBKIT2
2972+ void close_tab_idle (Object resource, AsyncResult result, View view)
2973+ {
2974+ uint8[] data;
2975+ try {
2976+ data = resource.get_data.end (res, null);
2977+ } catch (Error e) {}
2978+
2979+ if (data != null)
2980+ return;
2981+#else
2982+ bool close_tab_idle (View view)
2983+ {
2984+#endif
2985+ close_tab (view);
2986+#if !HAVE_WEBKIT2
2987+ return false;
2988+#endif
2989+ }
2990+
2991+ bool view_download_requested_cb (Gtk.Widget widget, WebKit.Download download) {
2992+ var view = (View) widget;
2993+ var type = Download.get_type (download);
2994+ var handled = true;
2995+
2996+ return_val_if_fail (view != null, false);
2997+
2998+ if (type == DownloadType.CANCEL) {
2999+ handled = false;
3000+ }
3001+#if HAVE_WEBKIT2
3002+ else if (download.destination == null) {
3003+#else
3004+ else if (download.destination_uri == null) {
3005+#endif
3006+ if (type == DownloadType.SAVE_AS) {
3007+#if HAVE_WEBKIT2
3008+ var download_uri = download.get_response (download).uri;
3009+#else
3010+ var download_uri = download.get_uri ();
3011+#endif
3012+ var dialog = new FileChooserDialog (_("Save file"), this, Gtk.FileChooserAction.SAVE);
3013+ dialog.do_overwrite_confirmation = true;
3014+ dialog.destroy_with_parent = true;
3015+ var folder = URI.get_folder (download_uri);
3016+ if (folder == null)
3017+ folder = settings.download_folder;
3018+ dialog.set_current_folder (folder);
3019+
3020+ var filename = Download.get_suggested_filename (download);
3021+ dialog.set_current_name (filename);
3022+
3023+ if (Dialog.run (dialog) == Gtk.ResponseType.OK) {
3024+ dialog.hide ();
3025+ var uri = dialog.get_uri ();
3026+ if (!prepare_download (download, uri)) {
3027+ return false;
3028+ }
3029+ } else {
3030+ dialog.hide ();
3031+ return false;
3032+ }
3033+ } else {
3034+ var folder = type == DownloadType.OPEN ? null : settings.download_folder;
3035+ var destination_uri = Download.prepare_destination_uri (download, folder);
3036+ prepare_download (download, destination_uri);
3037+ }
3038+#if !HAVE_WEBKIT2
3039+ download.start ();
3040+#endif
3041+ }
3042+
3043+ /* Close empty tabs due to download links with a target */
3044+ if (view.is_blank ()) {
3045+ var web_view = view.web_view;
3046+#if HAVE_WEBKIT2
3047+ var resource = web_view.get_main_resource ();
3048+ resource.get_data.begin (null, (obj, res) => close_tab_idle (obj, res, view));
3049+#else
3050+ var web_frame = web_view.get_main_frame ();
3051+ var datasource = web_frame.get_data_source ();
3052+ if (datasource.get_data () == null)
3053+ Idle.add (() => close_tab_idle (view));
3054+#endif
3055+ }
3056+ return handled;
3057+ }
3058+
3059+#if HAVE_WEBKIT2
3060+ void download_created_destination_cb (WebKit.Download download, string destination) {
3061+ print ("%s: destination %s", "download created", destination);
3062+ }
3063+#endif
3064+
3065+ void view_search_text_cb (Gtk.Widget view, bool found, string typing) {
3066+ find.search_text (view, found, typing);
3067+ }
3068+
3069+ public int get_n_pages () {
3070+ return (int) notebook.count;
3071+ }
3072+
3073+ void connect_tab (Gtk.Widget widget) {
3074+ var view = (View) widget;
3075+ var item = ((View) view).get_proxy_item ();
3076+ proxy_array.add_item (item);
3077+
3078+ view.notify["icon"].connect (view_notify_icon_cb);
3079+ view.notify["load-status"].connect (view_notify_load_status_cb);
3080+ view.notify["progress"].connect (view_notify_progress_cb);
3081+ view.notify["uri"].connect (view_notify_uri_cb);
3082+ view.notify["title"].connect (view_notify_title_cb);
3083+ view.notify["zoom-level"].connect (view_notify_zoom_level_cb);
3084+ view.notify["statusbar-text"].connect (view_notify_statusbar_text_cb);
3085+ view.attach_inspector.connect (view_attach_inspector_cb);
3086+ view.detach_inspector.connect (view_detach_inspector_cb);
3087+ view.new_tab.connect (view_new_tab_cb);
3088+ view.new_window.connect (view_new_window_cb);
3089+ view.new_view.connect (view_new_view_cb);
3090+ view.download_requested.connect_after (view_download_requested_cb);
3091+ ((Tab) view).search_text.connect (view_search_text_cb);
3092+ view.leave_notify_event.connect (tab_leave_notify_event_cb);
3093+ view.destroy.connect (view_destroy_cb);
3094+ }
3095+
3096+ void disconnect_tab (View view) {
3097+ var item = view.get_proxy_item ();
3098+ proxy_array.remove_item (item);
3099+
3100+ /* We don't ever want to be in a situation with no tabs,
3101+ so just create an empty one if the last one is closed.
3102+ The only exception is when we are closing the window,
3103+ which is indicated by the proxy array having been unset. */
3104+ if (proxy_array.is_empty ()) {
3105+ add_uri ("about:new");
3106+ set_current_page (0);
3107+ }
3108+
3109+ update_actions ();
3110+
3111+ view.notify["icon"].disconnect (view_notify_icon_cb);
3112+ view.notify["load-status"].disconnect (view_notify_load_status_cb);
3113+ view.notify["progress"].disconnect (view_notify_progress_cb);
3114+ view.notify["uri"].disconnect (view_notify_uri_cb);
3115+ view.notify["title"].disconnect (view_notify_title_cb);
3116+ view.notify["zoom-level"].disconnect (view_notify_zoom_level_cb);
3117+ view.notify["statusbar-text"].disconnect (view_notify_statusbar_text_cb);
3118+ view.attach_inspector.disconnect (view_attach_inspector_cb);
3119+ view.detach_inspector.disconnect (view_detach_inspector_cb);
3120+ view.new_tab.disconnect (view_new_tab_cb);
3121+ view.new_window.disconnect (view_new_window_cb);
3122+ view.new_view.disconnect (view_new_view_cb);
3123+ view.download_requested.disconnect (view_download_requested_cb);
3124+ ((Tab) view).search_text.disconnect (view_search_text_cb);
3125+ view.leave_notify_event.disconnect (tab_leave_notify_event_cb);
3126+ view.destroy.disconnect (view_destroy_cb);
3127+ }
3128+
3129+ void update_tooltip_if_changed (Gtk.Action action, string text) {
3130+ if (action.tooltip != text)
3131+ action.tooltip = text;
3132+ }
3133+
3134+ void update_reload_tooltip (Gdk.EventKey event, bool released) {
3135+ /* Update the reload/stop tooltip in case we are holding the hard refresh modifiers*/
3136+ var reload_stop = action_group.get_action ("ReloadStop");
3137+ var reload = action_group.get_action ("Reload");
3138+
3139+ // TODO var device = Gdk.Display.get_default ().get_device_manager ().get_client_pointer ();
3140+ Gdk.ModifierType mask;
3141+ get_window ().get_device_position (((Gdk.Event) event).get_device (), null, null, out mask);
3142+
3143+ string target;
3144
3145- g_list_free (top_levels);
3146- return NULL;
3147+ if ((mask & Gdk.ModifierType.SHIFT_MASK) != 0)
3148+ target = _("Reload page without caching");
3149+ else
3150+ target = _("Reload the current page");
3151+
3152+ update_tooltip_if_changed (reload_stop, target);
3153+ update_tooltip_if_changed (reload, target);
3154+ }
3155+
3156+ public override bool key_release_event (Gdk.EventKey event) {
3157+ update_reload_tooltip (event, true);
3158+ return false;
3159+ }
3160+
3161+ public override bool key_press_event (Gdk.EventKey event) {
3162+ update_reload_tooltip (event, false);
3163+
3164+ /* Interpret Ctrl(+Shift)+Tab as tab switching for compatibility */
3165+ if (get_nth_tab (1) != null
3166+ && event.keyval == Gdk.Key.Tab
3167+ && (event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
3168+ activate_action ("TabNext");
3169+ return true;
3170+ } else if (event.keyval == Gdk.Key.ISO_Left_Tab
3171+ && (event.state & Gdk.ModifierType.CONTROL_MASK) != 0
3172+ && (event.state & Gdk.ModifierType.SHIFT_MASK) != 0) {
3173+ activate_action ("TabPrevious");
3174+ return true;
3175+ }
3176+ /* Interpret Ctrl+= as Zoom In for compatibility */
3177+ else if ((event.keyval == Gdk.Key.KP_Equal || event.keyval == Gdk.Key.equal)
3178+ && (event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
3179+ activate_action ("ZoomIn");
3180+ return true;
3181+ }
3182+ /* Interpret F5 as reloading for compatibility */
3183+ else if (event.keyval == Gdk.Key.F5) {
3184+ activate_action ("Reload");
3185+ return true;
3186+ }
3187+
3188+#if !HAVE_WEBKIT2
3189+ var focus = get_focus ();
3190+ if (focus == null)
3191+ get_current_tab ().grab_focus ();
3192+ else if (focus is WebKit.WebView
3193+ && event.keyval == Gdk.Key.space
3194+ && ((event.state & Gdk.ModifierType.SHIFT_MASK) == 0)
3195+ && !((WebKit.WebView) focus).can_cut_clipboard ()
3196+ && !((WebKit.WebView) focus).can_paste_clipboard ()) {
3197+ /* Space at the bottom of the page: Go to next page */
3198+ var view = View.get_for_widget (focus);
3199+ var scrolled = (Gtk.ScrolledWindow) focus.get_parent ();
3200+ var vadjust = scrolled.get_vadjustment ();
3201+ if (vadjust.value == vadjust.upper - vadjust.page_size) {
3202+ var uri = view.get_next_page ();
3203+ if (uri != null) {
3204+ view.set_uri (uri);
3205+ return true;
3206+ }
3207+ }
3208+ }
3209+#endif
3210+
3211+ if ((event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)) != 0)
3212+ if (Sokoke.window_activate_key (this, event))
3213+ return true;
3214+
3215+ var clean_state = event.state & Gtk.accelerator_get_default_mod_mask ();
3216+ if (clean_state == 0 && propagate_key_event (event))
3217+ return true;
3218+
3219+ if ((event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)) == 0)
3220+ if (Sokoke.window_activate_key (this, event))
3221+ return true;
3222+
3223+ if (event.state != 0 && propagate_key_event (event))
3224+ return true;
3225+
3226+ /* Interpret (Shift+)Backspace as going back (forward) for compatibility */
3227+ if ((event.keyval == Gdk.Key.BackSpace)
3228+ && (event.state & Gdk.ModifierType.SHIFT_MASK) != 0) {
3229+ activate_action ("Forward");
3230+ return true;
3231+ } else if (event.keyval == Gdk.Key.BackSpace) {
3232+ activate_action ("Back");
3233+ return true;
3234+ }
3235+
3236+ return base.key_press_event (event);
3237+ }
3238+
3239+ void action_window_new_activate (Gtk.Action action) {
3240+ view_new_window_cb (null, "about:home");
3241+ }
3242+
3243+ void action_tab_new_activate (Gtk.Action action) {
3244+ var view = add_uri ("about:new");
3245+ set_current_tab (view);
3246+ }
3247+
3248+ void action_private_browsing_activate (Gtk.Action action) {
3249+ Sokoke.spawn_app ("about:private", true);
3250+ }
3251+
3252+#if !GTK_VERSION_3_1_10
3253+ string? action_open_last_dir = null;
3254+#endif
3255+
3256+ void action_open_activate (Gtk.Action action) {
3257+#if !GTK_VERSION_3_1_10
3258+ var folder_set = false;
3259+#endif
3260+ string? uri = null;
3261+
3262+ var dialog = new FileChooserDialog (_("Open file"), this, Gtk.FileChooserAction.OPEN);
3263+
3264+ /* base the start folder on the current view's uri if it is local */
3265+ var view = get_current_tab ();
3266+ if ((uri = ((View) view).get_display_uri ()) != null) {
3267+ try {
3268+ var filename = Filename.from_uri (uri, null);
3269+ var dirname = Path.get_dirname (filename);
3270+ if (dirname != null && FileUtils.test (dirname, FileTest.IS_DIR)) {
3271+ dialog.set_current_folder (dirname);
3272+#if !GTK_VERSION_3_1_10
3273+ folder_set = true;
3274+#endif
3275+ }
3276+ } catch (Error e) {}
3277+ }
3278+
3279+#if !GTK_VERSION_3_1_10
3280+ if (!folder_set && action_open_last_dir != null && action_open_last_dir != "")
3281+ dialog.set_current_folder (action_open_last_dir);
3282+#endif
3283+
3284+ if (Dialog.run (dialog) == Gtk.ResponseType.OK) {
3285+#if !GTK_VERSION_3_1_10
3286+ action_open_last_dir = dialog.get_current_folder ();
3287+#endif
3288+ uri = dialog.get_uri ();
3289+ set_current_uri (uri);
3290+
3291+ }
3292+
3293+ dialog.destroy ();
3294+ }
3295+
3296+ void action_save_as_activate (Gtk.Action action) {
3297+ save_uri ((View) get_current_tab (), null);
3298+ }
3299+
3300+ void action_add_speed_dial_activate (Gtk.Action action) {
3301+ add_speed_dial ();
3302+ }
3303+
3304+ void subscribe_to_news_feed (string uri) {
3305+ var news_aggregator = settings.news_aggregator;
3306+ if (news_aggregator != null && news_aggregator != "") {
3307+ /* Thunderbird only accepts feed://, Liferea doesn't mind */
3308+ var feed = uri;
3309+ if (feed.has_prefix ("http://"))
3310+ feed = "feed" + feed.substring (4);
3311+ /* Special-case Liferea because a helper script may be required */
3312+ if (news_aggregator == "liferea"
3313+ && Environment.find_program_in_path ("liferea-add-feed") != null)
3314+ Sokoke.spawn_program ("liferea-add-feed", false, feed, true, false);
3315+ else
3316+ Sokoke.spawn_program (news_aggregator, true, feed, true, false);
3317+ } else {
3318+ var description = "%s\n\n%s".printf (uri,
3319+ _("To use the above URI open a news aggregator. " +
3320+ "There is usually a menu or button \"New Subscription\", " +
3321+ "\"New News Feed\" or similar.\n" +
3322+ "Alternatively go to Preferences, Applications in Midori, " +
3323+ "and select a News Aggregator. Next time you click the " +
3324+ "news feed icon, it will be added automatically."));
3325+ Sokoke.message_dialog (Gtk.MessageType.INFO, _("New feed"), description, false);
3326+ }
3327+ }
3328+
3329+ void action_add_news_feed_activate (Gtk.Action action) {
3330+ Gtk.Widget? view = null;
3331+ string? uri = null;
3332+
3333+ if ((view = get_current_tab ()) == null)
3334+ return;
3335+ if ((uri = view.get_data<string> ("news-feeds")) == null)
3336+ return;
3337+
3338+ subscribe_to_news_feed (uri);
3339+ }
3340+
3341+ void action_compact_add_activate (Gtk.Action action) {
3342+ string[] actions = { "BookmarkAdd", "AddSpeedDial", "AddNewsFeed" };
3343+
3344+ var dialog = new Gtk.Dialog ();
3345+ dialog.transient_for = this;
3346+ dialog.title = _("Add a new bookmark");
3347+ var box = dialog.get_content_area ();
3348+
3349+ for (var i = 0; i < actions.length; i++) {
3350+ action = action_group.get_action (actions[i]);
3351+ var button = new Gtk.Button.with_mnemonic (action.label);
3352+ button.name = "GtkButton-thumb";
3353+ box.pack_start (button, true, true, 4);
3354+ button.related_action = action;
3355+ button.clicked.connect ((dlg) => dlg.destroy ());
3356+ }
3357+
3358+ dialog.show ();
3359+ dialog.response.connect ((dlg, response) => dlg.destroy ());
3360+ }
3361+
3362+ void action_tab_close_activate (Gtk.Action action) {
3363+ close_tab (get_current_tab ());
3364+ }
3365+
3366+ void action_window_close_activate (Gtk.Action action) {
3367+ if (!delete_event ((Gdk.EventAny) Gtk.get_current_event ()))
3368+ destroy ();
3369+ }
3370+
3371+ void action_mail_to_activate (Gtk.Action action) {
3372+ var view = (View) get_current_tab ();
3373+ var uri = Uri.escape_string (view.get_display_uri (), null, true);
3374+ var title = Uri.escape_string (view.get_display_title (), null, true);
3375+ view.open_uri ("mailto:?cc=&bcc=&subject=" + title + "&body=" + uri);
3376+ }
3377+
3378+ void action_print_activate (Gtk.Action action) {
3379+ ((View) get_current_tab ()).print ();
3380+ }
3381+
3382+ void action_quit_activate (Gtk.Action action) {
3383+ quit ();
3384+ }
3385+
3386+ void action_edit_activate (Gtk.Action action) {
3387+ var widget = get_focus ();
3388+ bool can_undo = false, can_redo = false,
3389+ can_cut = false, can_copy = false, can_paste = false,
3390+ has_selection, can_select_all = false;
3391+
3392+ if (widget is WebKit.WebView) {
3393+ get_current_tab ().update_actions.begin (action_group);
3394+ return;
3395+ } else if (widget is Gtk.Editable) {
3396+ // TODO check is casting to GtkEntry is ok
3397+ var editable = (Gtk.Entry) widget;
3398+ has_selection = editable.get_selection_bounds (null, null);
3399+ can_cut = has_selection && editable.get_editable ();
3400+ can_copy = has_selection;
3401+ can_paste = editable.get_editable ();
3402+ can_select_all = true;
3403+ } else if (widget is Gtk.TextView) {
3404+ var text_view = (Gtk.TextView) widget;
3405+ has_selection = text_view.buffer.has_selection;
3406+ can_cut = text_view.editable;
3407+ can_copy = has_selection;
3408+ can_paste = text_view.editable && has_selection;
3409+ can_select_all = true;
3410+ }
3411+
3412+ action_set_sensitive ("Undo", can_undo);
3413+ action_set_sensitive ("Redo", can_redo);
3414+ action_set_sensitive ("Cut", can_cut);
3415+ action_set_sensitive ("Copy", can_copy);
3416+ action_set_sensitive ("Paste", can_paste);
3417+ action_set_sensitive ("Delete", can_cut);
3418+ action_set_sensitive ("SelectAll", can_select_all);
3419+ }
3420+
3421+ void action_undo_activate (Gtk.Action action) {
3422+ var web_view = get_focus () as WebKit.WebView;
3423+ if (web_view == null)
3424+ return;
3425+
3426+#if HAVE_WEBKIT2
3427+ web_view.execute_editing_command (WebKit.EditingCommand.UNDO);
3428+#else
3429+ web_view.undo ();
3430+#endif
3431+ }
3432+
3433+ void action_redo_activate (Gtk.Action action) {
3434+ var web_view = get_focus () as WebKit.WebView;
3435+ if (web_view == null)
3436+ return;
3437+
3438+#if HAVE_WEBKIT2
3439+ web_view.execute_editing_command (WebKit.EditingCommand.REDO);
3440+#else
3441+ web_view.redo ();
3442+#endif
3443+ }
3444+
3445+ void action_cut_activate (Gtk.Action action) {
3446+ var widget = get_focus ();
3447+ if (likely (widget != null) && Signal.lookup ("cut-clipboard", widget.get_type ()) != 0)
3448+ Signal.emit_by_name (widget, "cut-clipboard");
3449+#if HAVE_WEBKIT2
3450+ else if (widget is WebKit.WebView)
3451+ ((WebKit.WebView) widget).execute_editing_command (WebKit.EditingCommand.CUT);
3452+#endif
3453+ }
3454+
3455+ void action_copy_activate (Gtk.Action action) {
3456+ var widget = get_focus ();
3457+ if (likely (widget != null) && Signal.lookup ("copy-clipboard", widget.get_type ()) != 0)
3458+ Signal.emit_by_name (widget, "copy-clipboard");
3459+#if HAVE_WEBKIT2
3460+ else if (widget is WebKit.WebView)
3461+ ((WebKit.WebView) view).execute_editing_command (WebKit.EditingCommand.COPY);
3462+#endif
3463+ }
3464+
3465+ void action_paste_activate (Gtk.Action action) {
3466+ var widget = get_focus ();
3467+ if (likely (widget != null) && Signal.lookup ("paste-clipboard", widget.get_type ()) != 0)
3468+ Signal.emit_by_name (widget, "paste-clipboard");
3469+#if HAVE_WEBKIT2
3470+ else if (widget is WebKit.WebView)
3471+ ((WebKit.WebView) view).execute_editing_command (WebKit.EditingCommand.PASTE);
3472+#endif
3473+ }
3474+
3475+ void action_delete_activate (Gtk.Action action) {
3476+ var widget = get_focus ();
3477+ if (likely (widget != null)) {
3478+ if (widget is Gtk.Editable)
3479+ ((Gtk.Editable) widget).delete_selection ();
3480+#if !HAVE_WEBKIT2
3481+ else if (widget is WebKit.WebView)
3482+ ((WebKit.WebView) widget).delete_selection ();
3483+#endif
3484+ else if (widget is Gtk.TextView)
3485+ ((Gtk.TextView) widget).buffer.delete_selection (true, false);
3486+ }
3487+ }
3488+
3489+ void action_select_all_activate (Gtk.Action action) {
3490+ var widget = get_focus ();
3491+ if (likely (widget != null)) {
3492+ if (widget is Gtk.Editable)
3493+ ((Gtk.Editable) widget).select_region (0, -1);
3494+#if HAVE_WEBKIT2
3495+ else if (widget is WebKit.WebView)
3496+ ((WebKit.WebView) widget).execute_editing_command (WebKit.EditingCommand.SELECT_ALL);
3497+#endif
3498+ else if (Signal.lookup ("select-all", widget.get_type ()) != 0) {
3499+ if (widget is Gtk.TextView)
3500+ ((Gtk.TextView) widget).select_all (true);
3501+ else if (widget is Gtk.TreeView)
3502+ ((Gtk.TreeView) widget).select_all ();
3503+ else
3504+ Signal.emit_by_name (widget, "select-all");
3505+ }
3506+ }
3507+ }
3508+
3509+ void action_find_activate (Gtk.Action action) {
3510+ find.invoke (((View) get_current_tab ()).get_selected_text ());
3511+ }
3512+
3513+ void action_find_next_activate (Gtk.Action action) {
3514+ find.@continue (true);
3515+ }
3516+
3517+ void action_find_previous_activate (Gtk.Action action) {
3518+ find.@continue (false);
3519+ }
3520+
3521+#if !HAVE_GRANITE
3522+ void navigationbar_notify_style_cb (Object object, ParamSpec pspec) {
3523+ set_toolbar_style (settings.toolbar_style);
3524+ }
3525+#endif
3526+
3527+ static string[] actions = {
3528+ "WindowNew", "TabNew", "Open", "SaveAs", "Print", "Find",
3529+ "Fullscreen", "Preferences", "Window", "Bookmarks",
3530+ "ReloadStop", "ZoomIn", "TabClose", "NextForward", "Location",
3531+ "ZoomOut", "Separator", "Back", "Forward", "Homepage",
3532+ "Panel", "Trash", "Search", "BookmarkAdd", "Previous", "Next" };
3533+
3534+ /**
3535+ * midori_browser_get_toolbar_actions:
3536+ *
3537+ * Retrieves a list of actions which are suitable for use in a toolbar.
3538+ *
3539+ * Return value: a null-terminated array of strings with actions
3540+ *
3541+ * Since: 0.1.8
3542+ **/
3543+ public unowned string[] get_toolbar_actions () {
3544+ return actions;
3545+ }
3546+
3547+ bool toolbar_popup_context_menu_cb (Gtk.Widget widget, int x, int y, int button) {
3548+ var menu = new ContextAction ("ToolbarContextMenu", null, null, null);
3549+ menu.add_action_group (action_group);
3550+ menu.add_by_name ("Menubar");
3551+ menu.add_by_name ("Navigationbar");
3552+ menu.add_by_name ("Bookmarkbar");
3553+ menu.add_by_name ("Statusbar");
3554+
3555+ var context_menu = menu.create_menu (null, false);
3556+ populate_toolbar_menu (context_menu);
3557+ Katze.widget_popup (widget, context_menu, null, button == -1 ? Katze.MenuPos.LEFT : Katze.MenuPos.CURSOR);
3558+ return true;
3559+ }
3560+
3561+ void bookmarkbar_activate_item (Katze.ArrayAction action, Katze.Item item) {
3562+ open_bookmark (item);
3563+ }
3564+
3565+ bool bookmarkbar_activate_item_alt (Katze.ArrayAction action, Katze.Item item, Gtk.Widget proxy, Gdk.EventButton event) {
3566+ assert (event != null);
3567+
3568+ if (event_new_tab (event)) {
3569+ var view = add_item (item);
3570+ set_current_tab_smartly (view);
3571+ } else if (event_context_menu (event)) {
3572+ bookmark_popup (proxy, null, item);
3573+ } else if (event.button == 1) {
3574+ bookmarkbar_activate_item (action, item);
3575+ }
3576+
3577+ return true;
3578+ }
3579+
3580+ void action_trash_populate_popup (Gtk.Action action, Gtk.Menu menu) {
3581+ Gtk.MenuItem menuitem;
3582+
3583+ menuitem = new Gtk.SeparatorMenuItem ();
3584+ menu.append (menuitem);
3585+ menuitem.show ();
3586+
3587+ menuitem = (Gtk.MenuItem) action_group.get_action ("TrashEmpty").create_menu_item ();
3588+ menu.append (menuitem);
3589+ menuitem.show ();
3590+ }
3591+
3592+ Gtk.Widget restore_tab (Katze.Item item) {
3593+ item.@ref ();
3594+ trash.remove_item (item);
3595+ var view = add_item (item);
3596+ item.unref ();
3597+
3598+ return view;
3599+ }
3600+
3601+ bool action_trash_activate_item_alt (Gtk.Action action, Katze.Item item, Gtk.Widget proxy, Gdk.EventButton event) {
3602+ assert (event != null);
3603+
3604+ if (event_new_tab (event))
3605+ set_current_tab_smartly (restore_tab (item));
3606+ else if (event.button == 1)
3607+ set_current_tab (restore_tab (item));
3608+
3609+ return true;
3610+ }
3611+
3612+ internal bool open_bookmark (Katze.Item item) {
3613+ var uri = item.uri;
3614+
3615+ if (uri == null || uri == "")
3616+ return false;
3617+
3618+ /* Imported bookmarks may lack a protocol */
3619+ var uri_fixed = Sokoke.magic_uri (uri, true, false);
3620+ if (uri_fixed == null)
3621+ uri_fixed = uri;
3622+
3623+ if (item.get_meta_boolean ("app"))
3624+ Sokoke.spawn_app (uri_fixed, false);
3625+ else {
3626+ set_current_uri (uri_fixed);
3627+ get_current_tab ().grab_focus ();
3628+ }
3629+ return true;
3630+ }
3631+
3632+ void action_tools_populate_popup (Gtk.Action action, Gtk.Menu default_menu) {
3633+ var menu = new ContextAction ("ToolsMenu", null, null, null);
3634+ menu.add_action_group (action_group);
3635+ menu.add_by_name ("ManageSearchEngines");
3636+ menu.add_by_name ("ClearPrivateData");
3637+ menu.add_by_name ("InspectPage");
3638+ populate_tool_menu (default_menu);
3639+ menu.add (null);
3640+ var j = 0;
3641+ Gtk.Widget widget;
3642+ while ((widget = panel.get_nth_page (j++)) != null)
3643+ menu.add (widget.get_data<Gtk.Action> ("midori-panel-action"));
3644+#if G_OS_WIN32
3645+ menu.add (null);
3646+ menu.add_by_name ("Preferences");
3647+#endif
3648+ menu.create_menu (default_menu, true);
3649+ }
3650+
3651+ bool action_bookmarks_populate_folder (Gtk.Action action, Gtk.MenuShell menu, Katze.Array folder) {
3652+ if (bookmarks == null)
3653+ return false;
3654+
3655+ bookmarks.populate_folder (folder);
3656+
3657+ /* Clear items from dummy array here */
3658+ menu.@foreach ((widget) => widget.destroy ());
3659+
3660+ /* "Import Bookmarks" and "Export Bookmarks" at the top */
3661+ if (folder == bookmarks) {
3662+ Gtk.MenuItem menuitem;
3663+ menuitem = (Gtk.MenuItem) action_group.get_action ("BookmarksImport").create_menu_item ();
3664+ menu.append (menuitem);
3665+ menuitem.show ();
3666+ menuitem = (Gtk.MenuItem) action_group.get_action ("BookmarksExport").create_menu_item ();
3667+ menu.append (menuitem);
3668+ menuitem.show ();
3669+ menuitem = new Gtk.SeparatorMenuItem ();
3670+ menu.append (menuitem);
3671+ menuitem.show ();
3672+ }
3673+
3674+ if (folder.is_empty ()) {
3675+ var menuitem = new Gtk.ImageMenuItem.with_label (_("Empty"));
3676+ menuitem.sensitive = false;
3677+ menu.append (menuitem);
3678+ menuitem.show ();
3679+ return true;
3680+ }
3681+
3682+ ((Katze.ArrayAction) action).generate_menu (folder, menu, this);
3683+ return true;
3684+ }
3685+
3686+ void action_window_populate_popup (Gtk.Action action, Gtk.Menu default_menu) {
3687+ var menu = new ContextAction ("WindowMenu", null, null, null);
3688+ menu.add_action_group (action_group);
3689+ menu.add (null);
3690+ menu.add_by_name ("LastSession");
3691+ menu.add_by_name ("TabCurrent");
3692+ menu.add_by_name ("NextView");
3693+ menu.add_by_name ("TabNext");
3694+ menu.add_by_name ("TabPrevious");
3695+ menu.create_menu (default_menu, true);
3696+ }
3697+
3698+ bool action_window_activate_item_alt (Katze.ArrayAction action, Katze.Item item, Gtk.Widget proxy, Gdk.EventButton event) {
3699+ set_current_item (item);
3700+ return true;
3701+ }
3702+
3703+ void action_compact_menu_populate_popup (Katze.ArrayAction action, Gtk.Menu default_menu) {
3704+ var menu = new ContextAction ("CompactMenu", null, null, null);
3705+ menu.add_action_group (action_group);
3706+ menu.add_by_name ("WindowNew");
3707+ menu.add_by_name ("PrivateBrowsing");
3708+ menu.add (null);
3709+ menu.add_by_name ("Find");
3710+ menu.add_by_name ("Print");
3711+ menu.add_by_name ("Fullscreen");
3712+ menu.add_by_name ("MailTo");
3713+ menu.add (null);
3714+ var j = 0;
3715+ Gtk.Widget widget;
3716+ while ((widget = panel.get_nth_page (j++)) != null)
3717+ menu.add (widget.get_data<Gtk.Action> ("midori-panel-action"));
3718+ menu.add (null);
3719+ menu.add_by_name ("BookmarksImport");
3720+ menu.add_by_name ("BookmarksExport");
3721+ menu.add_by_name ("ClearPrivateData");
3722+ populate_tool_menu (default_menu);
3723+ menu.add (null);
3724+#if !HAVE_GRANITE
3725+ menu.add_by_name ("HelpFAQ");
3726+ menu.add_by_name ("HelpBugs");
3727+#endif
3728+ menu.add_by_name ("Preferences");
3729+ menu.add_by_name ("About");
3730+ menu.create_menu (default_menu, false);
3731+ }
3732+
3733+ void preferences_response_help_cb (Gtk.Widget preferences, int response) {
3734+ if (response == Gtk.ResponseType.HELP)
3735+ activate_action ("HelpFAQ");
3736+ }
3737+
3738+ Preferences? preferences_dialog = null;
3739+ void action_preferences_activate (Gtk.Action action) {
3740+ if (preferences_dialog == null) {
3741+ preferences_dialog = new Preferences (this, settings);
3742+ show_preferences (preferences_dialog);
3743+ preferences_dialog.response.connect (preferences_response_help_cb);
3744+ preferences_dialog.destroy.connect (() => { preferences_dialog = null; });
3745+ preferences_dialog.show ();
3746+ } else
3747+ preferences_dialog.present ();
3748+ }
3749+
3750+ string? ubuntu_menuproxy = null;
3751+ bool has_native_menubar () {
3752+ if (ubuntu_menuproxy == null)
3753+ ubuntu_menuproxy = Environ.get_variable (Environ.@get (), "UBUNTU_MENUPROXY");
3754+ /*
3755+ * Values when the global menu is enabled
3756+ * UBUNTU_MENUPROXY=libappmenu.so
3757+ * UBUNTU_MENUPROXY=1
3758+ * The official way to disable the menu is
3759+ * UBUNTU_MENUPROXY=
3760+ */
3761+ return ubuntu_menuproxy != null && (".so" in ubuntu_menuproxy || ubuntu_menuproxy == "1");
3762+ }
3763+
3764+ void action_menubar_activate (Gtk.Action menubar_action) {
3765+ var active = ((Gtk.ToggleAction) menubar_action).active;
3766+ var menu_action = action_group.get_action ("CompactMenu");
3767+ var toolbar_items = new StringBuilder ();
3768+
3769+ if (has_native_menubar ())
3770+ active = false;
3771+
3772+ foreach (var child in navigationbar.get_children ()) {
3773+ var action = ((Gtk.Activatable) child).related_action;
3774+ if (action == null)
3775+ continue;
3776+ if (action == menu_action) {
3777+ if (active)
3778+ navigationbar.remove (child);
3779+ continue;
3780+ } else if (action is PanedAction) {
3781+ var paned_action = (PanedAction) action;
3782+ toolbar_items.append_printf ("%s,%s", paned_action.get_child1_name (), paned_action.get_child2_name ());
3783+ } else
3784+ toolbar_items.append (action.name);
3785+ toolbar_items.append_c (',');
3786+ }
3787+
3788+ if (settings.show_menubar != active)
3789+ settings.show_menubar = active;
3790+
3791+ settings.toolbar_items = toolbar_items.str;
3792+
3793+ Sokoke.widget_set_visible (menubar, active);
3794+ set_data<bool> ("midori-toolbars-visible", menubar.visible || navigationbar.visible);
3795+ }
3796+
3797+ void action_navigationbar_activate (Gtk.Action action) {
3798+ var active = ((Gtk.ToggleAction) action).active;
3799+ settings.show_navigationbar = active;
3800+ Sokoke.widget_set_visible (navigationbar, active);
3801+
3802+ set_data<bool> ("midori-toolbars-visible", menubar.visible || navigationbar.visible);
3803+ }
3804+
3805+ void action_bookmarkbar_activate (Gtk.Action action) {
3806+ var active = ((Gtk.ToggleAction) action).active;
3807+ settings.show_bookmarkbar = active;
3808+ Sokoke.widget_set_visible (bookmarkbar, active);
3809+ }
3810+
3811+ void action_statusbar_activate (Gtk.Action action) {
3812+ var active = ((Gtk.ToggleAction) action).active;
3813+ settings.show_statusbar = active;
3814+ Sokoke.widget_set_visible (statusbar, active);
3815+ }
3816+
3817+ void action_reload_stop_activate (Gtk.Action action) {
3818+ var view = (View) get_current_tab ();
3819+
3820+ /* Refresh or stop, depending on the stock id */
3821+ if (action.stock_id == Gtk.Stock.REFRESH) {
3822+ var state = (Gdk.ModifierType) 0;
3823+ Gdk.Window? window = null;
3824+ var from_cache = true;
3825+
3826+ if (action.name == "ReloadUncached")
3827+ from_cache = false;
3828+ else if ((window = get_window ()) != null) {
3829+ window.get_device_position (Gtk.get_current_event_device (), null, null, out state);
3830+ if ((state & Gdk.ModifierType.SHIFT_MASK) != 0)
3831+ from_cache = false;
3832+ }
3833+ view.reload (from_cache);
3834+ } else
3835+ view.stop_loading ();
3836+ }
3837+
3838+ void action_zoom_activate (Gtk.Action action) {
3839+ var view = (View) get_current_tab ();
3840+
3841+ if (action.name == "ZoomIn")
3842+ view.zoom_level = view.zoom_level + 0.10f;
3843+ else if (action.name == "ZoomOut")
3844+ view.zoom_level = view.zoom_level - 0.10f;
3845+ else
3846+ view.zoom_level = 1.0f;
3847+ }
3848+
3849+ void action_view_encoding_activate (Gtk.Action action, Gtk.Action current) {
3850+ var view = (View) get_current_tab ();
3851+ var name = current.name;
3852+
3853+ string? encoding = null;
3854+ switch (name) {
3855+ case "EncodingAutomatic":
3856+ encoding = null;
3857+ break;
3858+ case "EncodingChinese":
3859+ encoding = "BIG5";
3860+ break;
3861+ case "EncodingChineseSimplified":
3862+ encoding = "GB18030";
3863+ break;
3864+ case "EncodingJapanese":
3865+ encoding = "SHIFT_JIS";
3866+ break;
3867+ case "EncodingKorean":
3868+ encoding = "EUC-KR";
3869+ break;
3870+ case "EncodingRussian":
3871+ encoding = "KOI8-R";
3872+ break;
3873+ case "EncodingUnicode":
3874+ encoding = "UTF-8";
3875+ break;
3876+ case "EncodingWestern":
3877+ encoding = "ISO-8859-1";
3878+ break;
3879+ default:
3880+ assert_not_reached ();
3881+ }
3882+#if HAVE_WEBKIT2
3883+ view.web_view.set_custom_charset (encoding);
3884+#else
3885+ view.web_view.set_custom_encoding (encoding);
3886+#endif
3887+ }
3888+
3889+ void action_source_view (Gtk.Action action, bool use_dom) {
3890+ var view = (View) get_current_tab ();
3891+#if HAVE_WEBKIT2
3892+ /* TODO: midori_view_save_source isn't async and not WebKit2-friendly */
3893+ var source = new View.with_item (null, settings);
3894+ var source_view = source.web_view;
3895+ source.view_source = true;
3896+ source_view.load_uri (view.uri);
3897+ add_tab (source);
3898+#else
3899+ var filename = view.save_source (null, null, use_dom);
3900+ var text_editor = settings.text_editor;
3901+ if (text_editor == null || text_editor == "") {
3902+ try {
3903+ var source_uri = Filename.to_uri (filename, null);
3904+ var source = new View.with_item (null, settings);
3905+ var source_view = source.web_view;
3906+ source.view_source = true;
3907+ source_view.load_uri (source_uri);
3908+ add_tab (source);
3909+ } catch (Error e) {}
3910+ } else
3911+ Sokoke.spawn_program (text_editor, true, filename, true, false);
3912+#endif
3913+ }
3914+
3915+ void action_source_view_activate (Gtk.Action action) {
3916+ action_source_view (action, false);
3917+ }
3918+
3919+ void action_source_view_dom_activate (Gtk.Action action) {
3920+ action_source_view (action, true);
3921+ }
3922+
3923+ void action_caret_browsing_activate (Gtk.Action action) {
3924+ int response;
3925+
3926+ if (!settings.enable_caret_browsing) {
3927+ var dialog = new Gtk.MessageDialog (this, Gtk.DialogFlags.DESTROY_WITH_PARENT,
3928+ Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE, _("Toggle text cursor navigation"));
3929+ dialog.title = _("Toggle text cursor navigation");
3930+ dialog.format_secondary_text (_("Pressing F7 toggles Caret Browsing. When active, a text cursor appears in all websites."));
3931+ dialog.add_buttons (Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
3932+ _("_Enable Caret Browsing"), Gtk.ResponseType.ACCEPT);
3933+
3934+ response = Midori.Dialog.run (dialog);
3935+ dialog.destroy ();
3936+
3937+ if (response != Gtk.ResponseType.ACCEPT)
3938+ return;
3939+ }
3940+
3941+ settings.enable_caret_browsing = !settings.enable_caret_browsing;
3942+ }
3943+
3944+ void action_fullscreen_activate (Gtk.Action action) {
3945+ var window = get_window ();
3946+ if (window == null)
3947+ return;
3948+
3949+ var state = window.get_state ();
3950+ if ((state & Gdk.WindowState.FULLSCREEN) != 0) {
3951+ if (settings.show_menubar)
3952+ menubar.show ();
3953+
3954+ if (settings.show_panel)
3955+ panel.show ();
3956+
3957+ if (settings.show_bookmarkbar)
3958+ bookmarkbar.show ();
3959+
3960+ if (show_navigationbar)
3961+ navigationbar.show ();
3962+
3963+ if (show_statusbar)
3964+ statusbar.show ();
3965+
3966+ toggle_tabbar_smartly (true);
3967+
3968+ unfullscreen ();
3969+ } else {
3970+ menubar.hide ();
3971+ panel.hide ();
3972+ bookmarkbar.hide ();
3973+ navigationbar.hide ();
3974+ statusbar.hide ();
3975+ notebook.labels_visible = false;
3976+
3977+ fullscreen ();
3978+ }
3979+ }
3980+
3981+ void action_scroll_somewhere_activate (Gtk.Action action) {
3982+#if !HAVE_WEBKIT2
3983+ var view = get_current_tab ();
3984+ var web_view = view.web_view;
3985+ var name = action.name;
3986+
3987+ switch (name) {
3988+ case "ScrollLeft":
3989+ web_view.move_cursor (Gtk.MovementStep.VISUAL_POSITIONS, -1);
3990+ break;
3991+ case "ScrollDown":
3992+ web_view.move_cursor (Gtk.MovementStep.DISPLAY_LINES, 1);
3993+ break;
3994+ case "ScrollUp":
3995+ web_view.move_cursor (Gtk.MovementStep.DISPLAY_LINES, -1);
3996+ break;
3997+ case "ScrollRight":
3998+ web_view.move_cursor (Gtk.MovementStep.VISUAL_POSITIONS, 1);
3999+ break;
4000+ }
4001+#endif
4002+ }
4003+
4004+ void action_readable_activate (Gtk.Action action) {
4005+ var view = (View) get_current_tab ();
4006+
4007+ var filename = Paths.get_res_filename ("faq.css");
4008+ string stylesheet = null;
4009+ try {
4010+ FileUtils.get_contents (filename, out stylesheet);
4011+ } catch (Error e) {
4012+#if G_OS_WIN32
4013+ filename = Paths.get_data_filename ("doc/midori/faq.css", false);
4014+#else
4015+ filename = Path.build_filename (DOCDIR, "faq.css");
4016+#endif
4017+ try {
4018+ FileUtils.get_contents (filename, out stylesheet);
4019+ } catch (Error e2) {
4020+ warning (e2.message);
4021+ }
4022+ }
4023+
4024+ if (stylesheet == null || stylesheet == "") {
4025+ view.add_info_bar (Gtk.MessageType.ERROR, "Stylesheet faq.css not found", null, view,
4026+ Gtk.Stock.OK, Gtk.ResponseType.ACCEPT);
4027+ return;
4028+ }
4029+
4030+ /* Replace line breaks with spaces and change all single quotes to double quotes */
4031+ stylesheet.replace ("\n", " ").replace ("\r", " ").replace ("\'", "\"");
4032+
4033+ tab.inject_stylesheet (stylesheet);
4034+ }
4035+
4036+ void action_navigation_activate (Gtk.Action action) {
4037+ navigation_activate (action);
4038+ }
4039+
4040+ bool navigation_activate (Gtk.Action action) {
4041+ bool middle_click = false;
4042+
4043+ assert (action is Gtk.Action);
4044+
4045+ if (action.get_data<bool> ("midori-middle-click")) {
4046+ middle_click = true;
4047+ action.set_data<bool> ("midori-middle-click", false);
4048+ }
4049+
4050+ var tab = (View) get_current_tab ();
4051+ var name = action.name;
4052+
4053+ if (name == "NextForward")
4054+ name = tab.can_go_forward () ? "Forward" : "Next";
4055+
4056+ if (name == "Back") {
4057+ if (middle_click) {
4058+ var web_view = tab.web_view;
4059+#if HAVE_WEBKIT2
4060+ var list = web_view.get_back_forward_list ();
4061+ var item = list.get_back_item ();
4062+ var back_uri = item.uri;
4063+#else
4064+ var list = web_view.get_back_forward_list ();
4065+ var item = list.get_forward_item ();
4066+ var back_uri = item.uri;
4067+#endif
4068+
4069+ var new_view = add_uri (back_uri);
4070+ set_current_tab_smartly (new_view);
4071+ } else
4072+ tab.go_back ();
4073+
4074+ return true;
4075+ } else if (name == "Forward") {
4076+ if (middle_click) {
4077+ var web_view = tab.web_view;
4078+#if HAVE_WEBKIT2
4079+ var list = web_view.get_back_forward_list ();
4080+ var item = list.get_forward_item ();
4081+ var forward_uri = item.get_uri ();
4082+#else
4083+ var list = web_view.get_back_forward_list ();
4084+ var item = list.get_forward_item ();
4085+ var forward_uri = item.get_uri ();
4086+#endif
4087+
4088+ var new_view = add_uri (forward_uri);
4089+ set_current_tab_smartly (new_view);
4090+ } else
4091+ tab.go_forward ();
4092+
4093+ return true;
4094+ } else if (name == "Previous") {
4095+ /* Duplicate here because the URI pointer might change */
4096+ var uri = tab.get_previous_page ();
4097+
4098+ if (middle_click) {
4099+ var new_view = add_uri (uri);
4100+ set_current_tab_smartly (new_view);
4101+ } else
4102+ tab.set_uri (uri);
4103+
4104+ return true;
4105+ } else if (name == "Next") {
4106+ /* Duplicate here because the URI pointer might change */
4107+ var uri = tab.get_next_page ();
4108+
4109+ if (middle_click) {
4110+ var new_view = add_uri (uri);
4111+ set_current_tab_smartly (new_view);
4112+ } else
4113+ tab.set_uri (uri);
4114+
4115+ return true;
4116+ } else if (name == "Homepage") {
4117+ if (middle_click) {
4118+ var new_view = add_uri ("about:home");
4119+ set_current_tab_smartly (new_view);
4120+ } else
4121+ tab.set_uri ("about:home");
4122+
4123+ return true;
4124+ }
4125+ return false;
4126+ }
4127+
4128+ void action_location_activate (Gtk.Action action) {
4129+ if (!navigationbar.visible)
4130+ navigationbar.show ();
4131+ }
4132+
4133+ void action_location_focus_in (Gtk.Action action) {
4134+ var screen = notebook.get_screen ();
4135+ var icon_theme = Gtk.IconTheme.get_for_screen (screen);
4136+ if (icon_theme.has_icon ("go-jump-symbolic"))
4137+ ((LocationAction) action).secondary_icon = "go-jump-symbolic";
4138+ else
4139+ ((LocationAction) action).secondary_icon = Gtk.Stock.JUMP_TO;
4140+ }
4141+
4142+ void action_location_focus_out (Gtk.Action action) {
4143+ var view = (View) get_current_tab ();
4144+
4145+ if (!show_navigationbar || is_fullscreen ())
4146+ navigationbar.hide ();
4147+
4148+ update_secondary_icon (view, action);
4149+ }
4150+
4151+ void action_location_reset_uri (Gtk.Action action) {
4152+ ((LocationAction) action).set_text (get_current_uri ());
4153+ }
4154+
4155+#if !HAVE_WEBKIT2
4156+ void item_icon_loaded_cb (WebKit.FaviconDatabase database, string frame_uri, Katze.Item item) {
4157+ var uri = item.get_data<string> ("browser-queue-icon");
4158+ if (frame_uri != uri)
4159+ return;
4160+
4161+ var icon_uri = database.get_favicon_uri (frame_uri);
4162+ if (icon_uri != null) {
4163+ item.icon = frame_uri;
4164+ /* This signal fires extremely often (WebKit bug?)
4165+ we must throttle it (disconnect) once we have an icon */
4166+ SignalHandler.disconnect (database, database.get_data<ulong> ("handler-icon-loaded"));
4167+ }
4168+ }
4169+#endif
4170+
4171+ void queue_item_for_icon (Katze.Item item, string uri) {
4172+#if !HAVE_WEBKIT2
4173+ if (item.icon != null)
4174+ return;
4175+ item.set_data<string> ("browser-queue-icon", uri);
4176+ var database = WebKit.get_favicon_database ();
4177+ database.set_data<ulong> ("handler-icon-loaded", database.icon_loaded.connect ((database, frame_uri) =>
4178+ item_icon_loaded_cb (database, frame_uri, item)));
4179+#endif
4180+ }
4181+
4182+ Sqlite.Statement? location_submit_uri_stmt = null;
4183+ void action_location_submit_uri (Gtk.Action action, string uri, bool new_tab) {
4184+ /* Switch to already open tab if possible */
4185+ var found = (Katze.Item) proxy_array.find_uri (uri);
4186+ if (found != null && !new_tab && get_current_uri () != uri) {
4187+ var view = get_current_tab ();
4188+ set_current_item (found);
4189+ if (view.is_blank ())
4190+ close_tab (view);
4191+ return;
4192+ }
4193+
4194+ uri = Katze.skip_whitespace (uri);
4195+ var new_uri = Sokoke.magic_uri (uri, true, false);
4196+ if (new_uri == null) {
4197+ string? keywords = null;
4198+ string? search_uri = null;
4199+ Katze.Item item = null;
4200+
4201+ /* Do we have a keyword and a string? */
4202+ if (search_engines != null && (item = (Katze.Item) search_engines.find_token (uri)) != null) {
4203+ var space_index = uri.index_of (" ");
4204+ if (space_index > -1)
4205+ keywords = uri.substring (space_index + 1);
4206+ else
4207+ keywords = "";
4208+ search_uri = item.uri;
4209+ }
4210+
4211+ if (keywords == null) {
4212+ keywords = uri;
4213+ search_uri = settings.location_entry_search;
4214+ }
4215+ new_uri = URI.for_search (search_uri, keywords);
4216+
4217+ if (search_engines != null && item != null)
4218+ queue_item_for_icon (item, new_uri);
4219+
4220+ if (history != null) {
4221+ var now = time_t ();
4222+ var day = Sokoke.time_t_to_julian (ref now);
4223+ unowned Sqlite.Database db = history.get_data<Sqlite.Database> ("db");
4224+
4225+ if (location_submit_uri_stmt == null) {
4226+ var sqlcmd = "INSERT INTO search (keywords, uri, day) VALUES (?,?,?)";
4227+ db.prepare_v2 (sqlcmd, sqlcmd.length + 1, out location_submit_uri_stmt);
4228+ }
4229+ location_submit_uri_stmt.bind_text (1, keywords);
4230+ location_submit_uri_stmt.bind_text (2, search_uri);
4231+ location_submit_uri_stmt.bind_int64 (3, day);
4232+
4233+ if (location_submit_uri_stmt.step () != Sqlite.DONE)
4234+ printerr (_("Failed to insert new history item: %s\n"), db.errmsg ());
4235+
4236+ location_submit_uri_stmt.reset ();
4237+ if (location_submit_uri_stmt.step () == Sqlite.DONE)
4238+ location_submit_uri_stmt.clear_bindings ();
4239+ }
4240+ }
4241+
4242+ if (new_tab) {
4243+ var view = add_uri (new_uri);
4244+ set_current_tab (view);
4245+ } else
4246+ set_current_uri (new_uri);
4247+ get_current_tab ().grab_focus ();
4248+ }
4249+
4250+ void news_feed_clicked_cb (Gtk.Widget menuitem) {
4251+ var uri = menuitem.get_data<string> ("uri");
4252+ subscribe_to_news_feed (uri);
4253+ }
4254+
4255+ bool action_location_secondary_icon_released (Gtk.Action action, Gtk.Widget widget) {
4256+ var view = (View) get_current_tab ();
4257+ var uri = view.get_display_uri ();
4258+ string? feed = null;
4259+ /* Clicking icon on blank is equal to Paste and Proceed */
4260+ if (view.is_blank ()) {
4261+ var clipboard = Gtk.Clipboard.get_for_display (view.get_display (), Gdk.SELECTION_CLIPBOARD);
4262+ var text = clipboard.wait_for_text ();
4263+ if (text != null)
4264+ action_location_submit_uri (action, text, false);
4265+ } else if (get_focus () == widget) {
4266+ var text = ((Gtk.Entry) widget).text;
4267+ action_location_submit_uri (action, text, false);
4268+ } else if ((feed = view.get_data<string> ("news-feeds")) != null) {
4269+ Katze.Item itemm;
4270+
4271+ var news_feeds = view.news_feeds;
4272+ var item = news_feeds.get_nth_item (0);
4273+ if ((itemm = news_feeds.get_nth_item (1)) != null) {
4274+ Gtk.MenuItem menuitem;
4275+
4276+ var menu = new Gtk.Menu ();
4277+ menuitem = new Gtk.MenuItem.with_label (item.name);
4278+ menuitem.set_data<string> ("uri", item.uri);
4279+ menuitem.activate.connect (news_feed_clicked_cb);
4280+ menu.append (menuitem);
4281+ menuitem = new Gtk.MenuItem.with_label (itemm.name);
4282+ menuitem.set_data<string> ("uri", itemm.uri);
4283+ menuitem.activate.connect (news_feed_clicked_cb);
4284+ menu.append (menuitem);
4285+
4286+ var i = 2;
4287+ while ((itemm = news_feeds.get_nth_item (i++)) != null) {
4288+ menuitem = new Gtk.MenuItem.with_label (itemm.name);
4289+ menuitem.set_data<string> ("uri", itemm.uri);
4290+ menuitem.activate.connect (news_feed_clicked_cb);
4291+ menu.append (menuitem);
4292+ }
4293+ menu.@foreach ((widget) => widget.show_all ());
4294+ Katze.widget_popup (widget, menu, null,
4295+ Katze.MenuPos.RIGHT);
4296+ } else
4297+ subscribe_to_news_feed (feed);
4298+ } else
4299+ action_location_submit_uri (action, uri, false);
4300+ return true;
4301+ }
4302+
4303+ void action_search_submit (Gtk.Action action, string keywords, bool new_tab) {
4304+ string url;
4305+ var item = search_engines.get_nth_item (last_web_search);
4306+ if (item != null)
4307+ url = item.uri;
4308+ else /* The location entry search is our fallback */
4309+ url = settings.location_entry_search;
4310+
4311+ var search = URI.for_search (url, keywords);
4312+ if (item != null)
4313+ queue_item_for_icon (item, search);
4314+
4315+ if (new_tab) {
4316+ var view = add_uri (search);
4317+ set_current_tab_smartly (view);
4318+ } else
4319+ set_current_uri (search);
4320+ }
4321+
4322+ void action_search_activate (Gtk.Action action) {
4323+ unowned SList<Gtk.Widget> proxies = action.get_proxies ();
4324+ foreach (var proxy in proxies) {
4325+ if (proxy is Gtk.ToolItem) {
4326+ if (!navigationbar.visible)
4327+ navigationbar.show ();
4328+ return;
4329+ }
4330+ }
4331+
4332+ set_current_uri ("about:search");
4333+ get_current_tab ().grab_focus ();
4334+ }
4335+
4336+ void action_search_notify_current_item (Object action, ParamSpec pspec) {
4337+ var item = ((SearchAction) action).current_item;
4338+ var idx = (item != null ? search_engines.get_item_index (item) : 0);
4339+
4340+ settings.last_web_search = idx;
4341+ last_web_search = idx;
4342+ }
4343+
4344+ void action_search_notify_default_item (Object action, ParamSpec pspec) {
4345+ var item = ((SearchAction) action).default_item;
4346+ if (item != null)
4347+ settings.location_entry_search = item.uri;
4348+ }
4349+
4350+ void action_search_focus_out (Gtk.Action action) {
4351+ if ((statusbar.visible && !show_navigationbar) || is_fullscreen ())
4352+ navigationbar.hide ();
4353+ }
4354+
4355+ void bookmark_open_activate_cb (Gtk.Action action) {
4356+ open_bookmark (action.get_data<Katze.Item> ("KatzeItem"));
4357+ }
4358+
4359+ void bookmark_open_in_tab_activate_cb (Gtk.Action action) {
4360+ string uri;
4361+ var item = action.get_data<Katze.Item> ("KatzeItem");
4362+ if (item is Katze.Array) {
4363+ var array = bookmarks.query_recursive ("*", "parentid = %q", item.get_meta_string ("id"), false);
4364+
4365+ foreach (var child in array.get_items ()) {
4366+ if ((uri = child.uri) != null && child.uri != "") {
4367+ var view = add_item (child);
4368+ set_current_tab_smartly (view);
4369+ }
4370+ }
4371+ } else {
4372+ uri = item.uri;
4373+ if (uri != null && uri != "") {
4374+ var view = add_item (item);
4375+ set_current_tab_smartly (view);
4376+ }
4377+ }
4378+ }
4379+
4380+ void bookmark_open_in_window_activate_cb (Gtk.Action action) {
4381+ var item = action.get_data<Katze.Item> ("KatzeItem");
4382+ view_new_window_cb (null, item.uri);
4383+ }
4384+
4385+ void bookmark_edit_activate_cb (Gtk.Action action) {
4386+ var item = action.get_data<Katze.Item> ("KatzeItem");
4387+
4388+ if (Katze.item_is_bookmark (item))
4389+ edit_bookmark_dialog_new (item, false, false, this);
4390+ else
4391+ edit_bookmark_dialog_new (item, false, true, this);
4392+ }
4393+
4394+ void bookmark_delete_activate_cb (Gtk.Action action) {
4395+ var item = action.get_data<Katze.Item> ("KatzeItem");
4396+ bookmarks.remove_item (item);
4397+ }
4398+
4399+ void bookmark_popup (Gtk.Widget widget, Gdk.EventButton? event, Katze.Item item) {
4400+ var menu = new ContextAction ("BookmarkContextMenu", null, null, null);
4401+ Gtk.Action action;
4402+ if (Katze.item_is_folder (item)) {
4403+ var child_bookmarks_count = bookmarks.count_recursive ("uri <> ''", null, item, false);
4404+
4405+ action = new Gtk.Action ("BookmarkOpenAllTabs", _("Open all in _Tabs"), null, Stock.TAB_NEW);
4406+ action.sensitive = child_bookmarks_count > 0;
4407+ action.set_data<Katze.Item> ("KatzeItem", item);
4408+ action.activate.connect (bookmark_open_in_tab_activate_cb);
4409+ menu.add (action);
4410+ } else {
4411+ action = new Gtk.Action ("BookmarkOpen", null, null, Gtk.Stock.OPEN);
4412+ action.sensitive = item != null;
4413+ action.set_data<Katze.Item> ("KatzeItem", item);
4414+ action.activate.connect (bookmark_open_activate_cb);
4415+ menu.add (action);
4416+
4417+ action = new Gtk.Action ("BookmarkOpenTab", null, null, Stock.TAB_NEW);
4418+ action.sensitive = item.uri != null;
4419+ action.set_data<Katze.Item> ("KatzeItem", item);
4420+ action.activate.connect (bookmark_open_in_tab_activate_cb);
4421+ menu.add (action);
4422+
4423+ action = new Gtk.Action ("BookmarkOpenWindow", _("Open in New _Window"), null, Stock.WINDOW_NEW);
4424+ action.sensitive = item.uri != null;
4425+ action.set_data<Katze.Item> ("KatzeItem", item);
4426+ action.activate.connect (bookmark_open_in_window_activate_cb);
4427+ menu.add (action);
4428+ }
4429+
4430+ menu.add (null);
4431+
4432+ action = new Gtk.Action ("BookmarkEdit", null, null, Gtk.Stock.EDIT);
4433+ action.sensitive = !Katze.item_is_separator (item);
4434+ action.set_data<Katze.Item> ("KatzeItem", item);
4435+ action.activate.connect (bookmark_edit_activate_cb);
4436+ menu.add (action);
4437+
4438+ action = new Gtk.Action ("BookmarkDelete", null, null, Gtk.Stock.EDIT);
4439+ action.set_data<Katze.Item> ("KatzeItem", item);
4440+ action.activate.connect (bookmark_delete_activate_cb);
4441+ menu.add (action);
4442+
4443+ var context_menu = menu.create_menu (null, false);
4444+ Katze.widget_popup (widget, context_menu, event, Katze.MenuPos.CURSOR);
4445+ }
4446+
4447+ bool menu_button_press_event_cb (Gtk.Widget toolitem, Gdk.EventButton event) {
4448+ if (event.button != 3)
4449+ return false;
4450+
4451+ /* GtkMenuBar catches button events on children with submenus,
4452+ so we need to see if the actual widget is the menubar, and if
4453+ it is an item, we forward it to the actual widget. */
4454+ if (toolitem is Gtk.Box || toolitem is Gtk.MenuBar) {
4455+ if (toolitem.get_window () != event.window)
4456+ return false;
4457+
4458+ toolbar_popup_context_menu_cb (toolitem is Gtk.Bin && ((Gtk.Bin) toolitem).get_child () != null ?
4459+ toolitem.get_parent () : toolitem,
4460+ (int) event.x, (int) event.y, (int) event.button);
4461+ return true;
4462+ } else if (toolitem is Gtk.MenuItem)
4463+ return toolitem.button_press_event (event);;
4464+
4465+ return false;
4466+ }
4467+
4468+ bool menu_item_middle_click_event_cb (Gtk.Widget toolitem, Gdk.EventButton event) {
4469+ if (event_new_tab (event)) {
4470+ var action = ((Gtk.ToolItem) toolitem).related_action;
4471+ action.set_data<bool> ("midori-middle-click", true);
4472+
4473+ return navigation_activate (action);
4474+ }
4475+ return false;
4476+ }
4477+
4478+ void action_bookmark_add_activate (Gtk.Action action) {
4479+ Gtk.Widget proxy = null;
4480+ foreach (var p in action.get_proxies ()) {
4481+ if (p is Gtk.ToolItem) {
4482+ proxy = p;
4483+ break;
4484+ }
4485+ }
4486+
4487+ if (action.name == "BookmarkFolderAdd")
4488+ edit_bookmark_dialog_new (null, true, true, proxy);
4489+ else
4490+ edit_bookmark_dialog_new (null, true, false, proxy);
4491+ }
4492+
4493+ struct BookmarkClient {
4494+ string path;
4495+ string name;
4496+ string icon;
4497+ }
4498+
4499+ static const BookmarkClient[] bookmark_clients = {
4500+ { ".local/share/data/Arora/bookmarks.xbel", N_("Arora"), "arora" },
4501+ { ".kazehakase/bookmarks.xml", N_("Kazehakase"), "kazehakase-icon" },
4502+ { ".opera/bookmarks.adr", N_("Opera"), "opera" },
4503+ { ".kde/share/apps/konqueror/bookmarks.xml", N_("Konqueror"), "konqueror" },
4504+ { ".gnome2/epiphany/bookmarks.rdf", N_("Epiphany"), "epiphany" },
4505+ { ".mozilla/firefox/*/bookmarks.html", N_("Firefox (%s)"), "firefox" },
4506+ { ".config/midori/bookmarks.xbel", N_("Midori 0.2.6"), "midori" },
4507+ };
4508+
4509+ void action_bookmarks_import_activate (Gtk.Action action) {
4510+ var dialog = new Gtk.Dialog.with_buttons (_("Import bookmarks…"), this,
4511+ Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
4512+ _("_Import bookmarks"), Gtk.ResponseType.ACCEPT);
4513+ var content_area = dialog.get_content_area ();
4514+ dialog.icon_name = Stock.BOOKMARKS;
4515+
4516+ dialog.border_width = 5;
4517+ content_area.border_width = 5;
4518+ var sizegroup = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL);
4519+
4520+ var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 8);
4521+ hbox.border_width = 5;
4522+ var label = new Gtk.Label.with_mnemonic (_("_Application:"));
4523+ sizegroup.add_widget (label);
4524+ hbox.pack_start (label, false, false, 0);
4525+ var model = new Gtk.ListStore (4, typeof (string), typeof (string), typeof (string), typeof (int));
4526+ var combo = new Gtk.ComboBox.with_model (model);
4527+ var pix_renderer = new Gtk.CellRendererPixbuf ();
4528+ combo.pack_start (pix_renderer, false);
4529+ combo.add_attribute (pix_renderer, "icon-name", 1);
4530+ combo.add_attribute (pix_renderer, "width", 3);
4531+ var text_renderer = new Gtk.CellRendererText ();
4532+ combo.pack_start (text_renderer, true);
4533+ combo.add_attribute (text_renderer, "text", 0);
4534+
4535+ int icon_width = 16;
4536+ Gtk.icon_size_lookup_for_settings (get_settings (), Gtk.IconSize.MENU, out icon_width, null);
4537+
4538+ foreach (var client in bookmark_clients) {
4539+ var location = client.path;
4540+ var client_name = client.name;
4541+
4542+ /* Interpret * as 'any folder' */
4543+ if (location.index_of ("*") > -1) {
4544+ var parts = location.split ("*", 2);
4545+ var path = Path.build_filename (Environment.get_home_dir (), parts[0]);
4546+ try {
4547+ var dir = Dir.open (path);
4548+ string name;
4549+ while ((name = dir.read_name ()) != null) {
4550+ var file = Path.build_filename (path, name, parts[1]);
4551+ if (Posix.access (file, Posix.F_OK) == 0) {
4552+ /* If name is XYZ.Name, we use Name only */
4553+ var real_name = name.split (".", 2);
4554+ var display = _(client_name).index_of ("%s") > -1
4555+ ? _(client_name).printf (real_name.length > 1 ? real_name[1] : name)
4556+ : _(client_name);
4557+ model.insert_with_values (null, int.MAX,
4558+ 0, display, 1, client.icon,
4559+ 2, file, 3, icon_width);
4560+ }
4561+ }
4562+ } catch (Error e) {}
4563+ continue;
4564+ }
4565+
4566+ var path = Path.build_filename (Environment.get_home_dir (), location);
4567+ if (Posix.access (path, Posix.F_OK) == 0)
4568+ model.insert_with_values (null, int.MAX,
4569+ 0, _(client_name), 1, client.icon,
4570+ 2, path, 3, icon_width);
4571+ }
4572+
4573+ model.insert_with_values (null, int.MAX,
4574+ 0, _("Import from XBEL or HTML file"), 1, null, 2, null, 3, icon_width);
4575+ combo.active = 0;
4576+ hbox.pack_start (combo, true, true, 0);
4577+ content_area.pack_start (hbox, false, true, 0);
4578+
4579+ var combobox_folder = bookmark_folder_button_new (bookmarks, 0);
4580+ content_area.pack_start (combobox_folder, false, true, 0);
4581+ content_area.show_all ();
4582+
4583+ dialog.set_default_response (Gtk.ResponseType.ACCEPT);
4584+ if (Midori.Dialog.run (dialog) == Gtk.ResponseType.ACCEPT) {
4585+ Gtk.TreeIter iter;
4586+ string? path = null;
4587+
4588+ if (combo.get_active_iter (out iter))
4589+ model.@get (iter, 2, out path);
4590+ var selected = combobox_folder.get_active ();
4591+
4592+ dialog.destroy ();
4593+ if (path == null) {
4594+ var file_dialog = new FileChooserDialog (_("Import from a file"), this, Gtk.FileChooserAction.OPEN);
4595+ if (Midori.Dialog.run (file_dialog) == Gtk.ResponseType.OK)
4596+ path = file_dialog.get_filename ();
4597+ file_dialog.destroy ();
4598+ }
4599+
4600+ var bookmarks = new Katze.Array (typeof (Katze.Array));
4601+ if (path != null) {
4602+ try {
4603+ array_from_file (bookmarks, path, null);
4604+ } catch (Error e) {
4605+ Sokoke.message_dialog (Gtk.MessageType.ERROR, _("Failed to import bookmarks"), e.message, false);
4606+ }
4607+ }
4608+ this.bookmarks.import_array (bookmarks, selected);
4609+ } else
4610+ dialog.destroy ();
4611+ }
4612+
4613+ void action_bookmarks_export_activate (Gtk.Action action) {
4614+ string? path = null;
4615+ string format;
4616+
4617+ var wrong_format = true;
4618+
4619+ while (wrong_format) {
4620+ var file_dialog = new FileChooserDialog (_("Save file as"), this, Gtk.FileChooserAction.SAVE);
4621+ file_dialog.set_current_name ("bookmarks.xbel");
4622+
4623+ Gtk.FileFilter filter;
4624+
4625+ filter = new Gtk.FileFilter ();
4626+ filter.set_filter_name (_("XBEL Bookmarks"));
4627+ filter.add_mime_type ("application/xml");
4628+ filter.add_pattern ("*.xbel");
4629+ file_dialog.add_filter (filter);
4630+
4631+ filter = new Gtk.FileFilter ();
4632+ filter.set_name (_("Netscape Bookmarks"));
4633+ filter.add_mime_type ("text/html");
4634+ filter.add_pattern ("*.html");
4635+ file_dialog.add_filter (filter);
4636+
4637+ if (Dialog.run (file_dialog) == Gtk.ResponseType.OK)
4638+ path = file_dialog.get_filename ();
4639+ file_dialog.destroy ();
4640+
4641+ if (path == null)
4642+ return;
4643+
4644+ if (path.has_suffix (".xbel"))
4645+ format = "xbel";
4646+ else if (path.has_suffix (".html"))
4647+ format = "netscape";
4648+ else {
4649+ Sokoke.message_dialog (Gtk.MessageType.ERROR,
4650+ _("Midori can only export to XBEL (*.xbel) and Netscape (*.html)"), "", true);
4651+ path = null;
4652+ continue;
4653+ }
4654+
4655+ wrong_format = false;
4656+
4657+ var bookmarks = bookmarks.query_recursive ("*", "parentid IS null", null, true);
4658+ try {
4659+ array_to_file (bookmarks, path, format);
4660+ } catch (Error e) {
4661+ Sokoke.message_dialog (Gtk.MessageType.ERROR, _("Failed to export bookmarks"), e.message, false);
4662+ }
4663+ }
4664+ }
4665+
4666+ Gtk.Dialog? search_engines_dialog = null;
4667+ void action_manage_search_engines_activate (Gtk.Action action) {
4668+ if (search_engines_dialog == null) {
4669+ search_engines_dialog = ((SearchAction) action_group.get_action ("Search")).get_dialog ();
4670+ search_engines_dialog.destroy.connect (() => search_engines_dialog = null);
4671+ search_engines_dialog.show ();
4672+ }
4673+ else
4674+ search_engines_dialog.present ();
4675+ }
4676+
4677+ Gtk.Dialog? clear_private_data_dialog = null;
4678+ void action_clear_private_data_activate (Gtk.Action action) {
4679+ if (clear_private_data_dialog == null) {
4680+ clear_private_data_dialog = private_data_get_dialog (this);
4681+ clear_private_data_dialog.destroy.connect (() => clear_private_data_dialog = null);
4682+ clear_private_data_dialog.show ();
4683+ } else
4684+ clear_private_data_dialog.present ();
4685+ }
4686+
4687+ void action_inspect_page_activate (Gtk.Action action) {
4688+ var view = get_current_tab ();
4689+ var web_view = view.web_view;
4690+ var inspector = web_view.get_inspector ();
4691+ inspector.show ();
4692+ }
4693+
4694+ void action_tab_move_activate (Gtk.Action action) {
4695+ var name = action.name;
4696+ int new_pos;
4697+ var cur_pos = get_current_page ();
4698+ var widget = get_nth_tab (cur_pos);
4699+
4700+ if (name == "TabMoveFirst")
4701+ new_pos = 0;
4702+ else if (name == "TabMoveBackward") {
4703+ if (cur_pos > 0)
4704+ new_pos = cur_pos - 1;
4705+ else
4706+ new_pos = get_n_pages () - 1;
4707+ } else if (name == "TabMoveForward") {
4708+ if (cur_pos == (get_n_pages () - 1))
4709+ new_pos = 0;
4710+ else
4711+ new_pos = cur_pos + 1;
4712+ } else if (name == "TabMoveLast")
4713+ new_pos = get_n_pages () - 1;
4714+ else
4715+ assert_not_reached ();
4716+
4717+ notebook.move ((Tab) widget, new_pos);
4718+ move_tab (notebook, cur_pos, new_pos);
4719+ }
4720+
4721+ void action_tab_previous_activate (Gtk.Action action) {
4722+ set_current_page (get_current_page () - 1);
4723+ }
4724+
4725+ void action_tab_next_activate (Gtk.Action action) {
4726+ /* Advance one tab or jump to the first one if we are at the last one */
4727+ var n = get_current_page ();
4728+ if (n == get_n_pages () - 1)
4729+ n = -1;
4730+ set_current_page (n + 1);
4731+ }
4732+
4733+ void action_tab_current_activate (Gtk.Action action) {
4734+ var view = get_current_tab ();
4735+ view.grab_focus ();
4736+ }
4737+
4738+ void action_next_view_activate (Gtk.Action action) {
4739+ get_current_tab ().grab_focus ();
4740+ }
4741+
4742+ void action_tab_minimize_activate (Gtk.Action action) {
4743+ var view = get_current_tab ();
4744+ view.minimized = !view.minimized;
4745+ }
4746+
4747+ void action_tab_duplicate_activate (Gtk.Action action) {
4748+ var view = (View) action.get_data<Tab> ("tab");
4749+ if (view == null)
4750+ view = (View) get_current_tab ();
4751+ view.duplicate ();
4752+ }
4753+
4754+ void action_tab_close_other_activate (Gtk.Action action) {
4755+ var view = get_current_tab ();
4756+ foreach (var tab in get_tabs ()) {
4757+ if (tab != view)
4758+ close_tab (tab);
4759+ }
4760+ }
4761+
4762+ static const string[] credits_authors = { "Christian Dywan <christian@twotoasts.de>" };
4763+ static const string[] credits_documenters = { "Christian Dywan <christian@twotoasts.de>" };
4764+ static const string[] credits_artists = { "Nancy Runge <nancy@twotoasts.de>" };
4765+
4766+ string get_docs (bool error)
4767+ {
4768+#if G_OS_WIN32
4769+ var path = Paths.get_data_filename ("doc/midori/faq.html", false);
4770+ try {
4771+ return Filename.to_uri (path, null);
4772+ } catch (Error e) {}
4773+
4774+ return "";
4775+#else
4776+ var path = Paths.get_res_filename ("faq.html");
4777+ bool found = Posix.access (path, Posix.F_OK) == 0;
4778+ if (found) {
4779+ try {
4780+ return Filename.to_uri (path, null);
4781+ } catch (Error e) {}
4782+ }
4783+ return "file://" + DOCDIR + "/faq.html";
4784+#endif
4785+ }
4786+
4787+ void action_about_activate (Gtk.Action action) {
4788+ var comments = "%s\n%s".printf (
4789+ _("A lightweight web browser."),
4790+ _("See about:version for version info."));
4791+ var license =
4792+ _("This library is free software; you can redistribute it and/or " +
4793+ "modify it under the terms of the GNU Lesser General Public " +
4794+ "License as published by the Free Software Foundation; either " +
4795+ "version 2.1 of the License, or (at your option) any later version.");
4796+
4797+#if HAVE_GRANITE
4798+ var docs = get_docs (false);
4799+ /* Avoid granite_widgets_show_about_dialog for invalid memory and crashes */
4800+ /* FIXME: granite: should return GtkWidget* like GTK+ */
4801+ var dialog = new Granite.Widgets.AboutDialog ();
4802+ dialog.@set (
4803+ "translate", "https://translations.xfce.org/projects/p/midori/",
4804+ "bug", PACKAGE_BUGREPORT,
4805+ "help", docs,
4806+ "copyright", "2007-2013 Christian Dywan",
4807+#else
4808+ var dialog = new Gtk.AboutDialog ();
4809+ dialog.@set (
4810+ "wrap-license", true,
4811+ "copyright", "Copyright © 2007-2013 Christian Dywan",
4812+#endif
4813+ "transient-for", this,
4814+ "logo-icon-name", icon_name,
4815+ "program-name", PACKAGE_NAME,
4816+ "version", PACKAGE_VERSION,
4817+ "comments", comments,
4818+ "website", "http://www.midori-browser.org",
4819+ "authors", credits_authors,
4820+ "documenters", credits_documenters,
4821+ "artists", credits_artists,
4822+ "license", license,
4823+ "translator-credits", _("translator-credits"));
4824+ dialog.show ();
4825+ dialog.response.connect ((dialog, response) => dialog.destroy ());
4826+ }
4827+
4828+ void action_help_link_activate (Gtk.Action action) {
4829+ var action_name = action.name;
4830+ string? uri = null;
4831+ if (action_name.has_prefix ("HelpFAQ"))
4832+ uri = get_docs (true);
4833+ else if (action_name.has_prefix ("HelpBugs")) {
4834+ try {
4835+ Process.spawn_command_line_async ("ubuntu-bug " + PACKAGE_NAME);
4836+ } catch (Error e) {
4837+ uri = PACKAGE_BUGREPORT;
4838+ }
4839+ }
4840+
4841+ if (uri != null)
4842+ set_current_tab (add_uri (uri));
4843+ }
4844+
4845+ void action_panel_activate (Gtk.Action action) {
4846+ var active = ((Gtk.ToggleAction) action).active;
4847+ settings.show_panel = active;
4848+ Sokoke.widget_set_visible (panel, active);
4849+ }
4850+
4851+ bool panel_timeout (Gtk.Paned hpaned) {
4852+ settings.last_panel_position = hpaned.position;
4853+ _panel_timeout = 0;
4854+ return false;
4855+ }
4856+
4857+ void panel_notify_position_cb (Object hpaned, ParamSpec pspec) {
4858+ if (_panel_timeout == 0)
4859+ _panel_timeout = Timeout.add_seconds (5, () => panel_timeout ((Gtk.Paned) hpaned));
4860+ }
4861+
4862+ bool panel_cycle_child_focus_cb (Gtk.Widget hpaned, bool reversed) {
4863+ /* Default cycle goes between all GtkPaned widgets.
4864+ If focus is in the panel, focus the location as if it's a paned.
4865+ If nothing is focussed, simply go to the location.
4866+ Be sure to suppress the default because the signal can recurse. */
4867+ var focus = get_focus ();
4868+ if (focus.get_ancestor (typeof (Panel)) != null
4869+ || focus.get_ancestor (typeof (Gtk.Paned)) == null) {
4870+ Signal.stop_emission_by_name (hpaned, "cycle-child-focus");
4871+ activate_action ("Location");
4872+ return true;
4873+ }
4874+ return false;
4875+ }
4876+
4877+ void panel_notify_page_cb (Object panel, ParamSpec pspec) {
4878+ var page = ((Panel) panel).page;
4879+ if (page > -1)
4880+ settings.last_panel_page = page;
4881+ }
4882+
4883+ void panel_notify_show_titles_cb (Object object, ParamSpec pspec) {
4884+ var panel = (Panel) object;
4885+ var signal_id = settings.get_data<ulong> ("handler-settings-notify");
4886+
4887+ SignalHandler.block (settings, signal_id);
4888+ settings.compact_sidepanel = !panel.show_titles;
4889+ SignalHandler.unblock (settings, signal_id);
4890+ }
4891+
4892+ void panel_notify_right_aligned_cb (Object object, ParamSpec pspec) {
4893+ var panel = (Panel) object;
4894+
4895+ var right_aligned = panel.right_aligned;
4896+ var hpaned = (Gtk.Paned) panel.get_parent ();
4897+ var vpaned = notebook.get_parent ();
4898+ var paned_position = hpaned.position;
4899+
4900+ Gtk.Allocation allocation;
4901+ hpaned.get_allocation (out allocation);
4902+ var paned_size = allocation.width;
4903+
4904+ settings.right_align_sidepanel = right_aligned;
4905+
4906+ panel.@ref ();
4907+ vpaned.@ref ();
4908+ hpaned.remove (panel);
4909+ hpaned.remove (vpaned);
4910+ if (right_aligned) {
4911+ hpaned.pack1 (vpaned, true, false);
4912+ hpaned.pack2 (panel, false, false);
4913+ } else {
4914+ hpaned.pack1 (panel, false, false);
4915+ hpaned.pack2 (vpaned, true, false);
4916+ }
4917+ hpaned.position = paned_size - paned_position;
4918+ panel.unref ();
4919+ vpaned.unref ();
4920+ }
4921+
4922+ bool panel_close_cb (Panel panel) {
4923+ action_set_active ("Panel", false);
4924+ return false;
4925+ }
4926+
4927+ void switched_tab_cb (Notebook notebook, Tab? old_tab, Tab new_tab) {
4928+ if (old_tab != null) {
4929+ var action = action_group.get_action ("Location");
4930+ var text = ((LocationAction) action).get_text ();
4931+ old_tab.set_data<string> ("midori-browser-typed-text", text);
4932+ }
4933+
4934+ return_if_fail (new_tab is View);
4935+ return_if_fail (new_tab != old_tab);
4936+
4937+ var new_view = (View) new_tab;
4938+ var uri = new_view.get_data<string> ("midori-browser-typed-text");
4939+ if (uri == null)
4940+ uri = new_view.get_display_uri ();
4941+ title = new_view.get_display_title ();
4942+ var action = (LocationAction) action_group.get_action ("Location");
4943+ action.set_text (uri);
4944+ if (Paths.get_runtime_mode () == RuntimeMode.APP)
4945+ icon = new_view.get_icon ();
4946+
4947+ switch_tab (old_tab, new_view);
4948+ _set_statusbar_text (new_view, null);
4949+ update_interface (new_view);
4950+ update_progress (new_view);
4951+ }
4952+
4953+ void notify_tab_cb (Object? object, ParamSpec? pspec) {
4954+ freeze_notify ();
4955+ notify_property ("uri");
4956+ notify_property ("uri");
4957+ notify_property ("title");
4958+ notify_property ("tab");
4959+ thaw_notify ();
4960+ }
4961+
4962+ void tab_moved_cb (Gtk.Widget notebook, Tab tab, uint page_num) {
4963+ var item = ((View) tab).get_proxy_item ();
4964+ proxy_array.move_item (item, (int) page_num);
4965+ }
4966+
4967+ void notebook_create_window_cb (Gtk.Widget notebook, Gtk.Widget widget, int x, int y) {
4968+ var view = (View) widget;
4969+ var new_browser = new_window (null);
4970+ if (new_browser != null) {
4971+ new_browser.move (x, y);
4972+ view.@ref ();
4973+ disconnect_tab (view);
4974+ ((Notebook) notebook).remove (view);
4975+ new_browser.add_tab (view);
4976+ view.unref ();
4977+ }
4978+ }
4979+
4980+ void notebook_new_tab_cb (Gtk.Widget notebook) {
4981+ var view = add_uri ("about:new");
4982+ set_current_tab (view);
4983+ }
4984+
4985+ void notebook_context_menu_cb (Notebook notebook, ContextAction menu) {
4986+ menu.add_action_group (action_group);
4987+ menu.add (null);
4988+ menu.add_by_name ("TabNew");
4989+ menu.add_by_name ("UndoTabClose");
4990+ }
4991+
4992+ void notebook_tab_context_menu_cb (Notebook notebook, Tab tab, ContextAction menu) {
4993+ menu.add_action_group (action_group);
4994+ menu.add (null);
4995+ menu.add_by_name ("TabNew");
4996+ menu.add_by_name ("UndoTabClose");
4997+ if (tab is View) {
4998+ var action = new Gtk.Action ("TabDuplicate", _("_Duplicate Current Tab"), null, null);
4999+ action.set_data<Tab> ("tab", tab);
5000+ action.activate.connect (action_tab_duplicate_activate);
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches