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
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2014-07-11 01:36:48 +0000
+++ CMakeLists.txt 2014-09-01 23:11:13 +0000
@@ -212,13 +212,11 @@
212 add_definitions("-DHAVE_WEBKIT2")212 add_definitions("-DHAVE_WEBKIT2")
213 add_definitions("-DGTK_VERSION=\"${DEPS_GTK_gtk+-3.0_VERSION}\"")213 add_definitions("-DGTK_VERSION=\"${DEPS_GTK_gtk+-3.0_VERSION}\"")
214 add_definitions("-DWEBKIT_VERSION=\"${DEPS_GTK_webkit2gtk-3.0_VERSION}\"")214 add_definitions("-DWEBKIT_VERSION=\"${DEPS_GTK_webkit2gtk-3.0_VERSION}\"")
215 set(PKGS ${PKGS} gtk+-3.0)215 set(PKGS ${PKGS} gtk+-3.0 webkit2gtk-3.0)
216 set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkit2gtk-3.0.vapi")216 set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkit2gtk-3.0.vapi")
217 set(VALAFLAGS ${VALAFLAGS} -D HAVE_GTK3)217 set(VALAFLAGS ${VALAFLAGS} -D HAVE_GTK3)
218 set(VALAFLAGS ${VALAFLAGS} -D HAVE_WEBKIT2)218 set(VALAFLAGS ${VALAFLAGS} -D HAVE_WEBKIT2)
219 if (${DEPS_GTK_webkit2gtk-3.0_VERSION} VERSION_GREATER "2.3.90")219 set(VALAFLAGS ${VALAFLAGS} -D HAVE_WEBKIT2_3_91)
220 set(VALAFLAGS ${VALAFLAGS} -D HAVE_WEBKIT2_3_91)
221 endif ()
222elseif (USE_GTK3)220elseif (USE_GTK3)
223 pkg_check_modules(DEPS_GTK REQUIRED221 pkg_check_modules(DEPS_GTK REQUIRED
224 gtk+-3.0>=3.0.0222 gtk+-3.0>=3.0.0
@@ -227,7 +225,7 @@
227 )225 )
228 add_definitions("-DGTK_VERSION=\"${DEPS_GTK_gtk+-3.0_VERSION}\"")226 add_definitions("-DGTK_VERSION=\"${DEPS_GTK_gtk+-3.0_VERSION}\"")
229 add_definitions("-DWEBKIT_VERSION=\"${DEPS_GTK_webkitgtk-3.0_VERSION}\"")227 add_definitions("-DWEBKIT_VERSION=\"${DEPS_GTK_webkitgtk-3.0_VERSION}\"")
230 set(PKGS ${PKGS} gtk+-3.0)228 set(PKGS ${PKGS} gtk+-3.0 webkitgtk-3.0)
231 set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkitgtk-3.0.vapi")229 set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkitgtk-3.0.vapi")
232 set(VALAFLAGS ${VALAFLAGS} -D HAVE_GTK3)230 set(VALAFLAGS ${VALAFLAGS} -D HAVE_GTK3)
233else ()231else ()
@@ -238,10 +236,31 @@
238 )236 )
239 add_definitions("-DGTK_VERSION=\"${DEPS_GTK_gtk+-2.0_VERSION}\"")237 add_definitions("-DGTK_VERSION=\"${DEPS_GTK_gtk+-2.0_VERSION}\"")
240 add_definitions("-DWEBKIT_VERSION=\"${DEPS_GTK_webkit-1.0_VERSION}\"")238 add_definitions("-DWEBKIT_VERSION=\"${DEPS_GTK_webkit-1.0_VERSION}\"")
241 set(PKGS ${PKGS} gtk+-2.0)239 set(PKGS ${PKGS} gtk+-2.0 webkit-1.0)
242 set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkitgtk-3.0.vapi")240 set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkitgtk-3.0.vapi")
243endif ()241endif ()
244242
243# vala has no GTK_CHECK_VERSION, so we need to cheat
244pkg_check_modules(GTK_3_1_10 gtk+-3.0>=3.0.0)
245if (GTK_3_0_0_FOUND)
246 set(VALAFLAGS ${VALAFLAGS} -D GTK_VERSION_3_0_0)
247endif()
248
249pkg_check_modules(GTK_3_4_0 gtk+-3.0>=3.4.0)
250if (GTK_3_4_0_FOUND)
251 set(VALAFLAGS ${VALAFLAGS} -D GTK_VERSION_3_4_0)
252endif()
253
254pkg_check_modules(GTK_3_2_0 gtk+-3.0>=3.2.0)
255if (GTK_3_2_0_FOUND)
256 set(VALAFLAGS ${VALAFLAGS} -D GTK_VERSION_3_2_0)
257endif()
258
259pkg_check_modules(GTK_3_1_10 gtk+-3.0>=3.1.10)
260if (GTK_3_1_10_FOUND)
261 set(VALAFLAGS ${VALAFLAGS} -D GTK_VERSION_3_1_10)
262endif()
263
245# dh_translations detects this if there's no variable used264# dh_translations detects this if there's no variable used
246set (GETTEXT_PACKAGE "midori")265set (GETTEXT_PACKAGE "midori")
247add_definitions("-DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\"")266add_definitions("-DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\"")
248267
=== modified file 'katze/midori-paths.vala'
--- katze/midori-paths.vala 2014-04-03 00:49:01 +0000
+++ katze/midori-paths.vala 2014-09-01 23:11:13 +0000
@@ -17,7 +17,6 @@
1717
18extern const string LIBDIR;18extern const string LIBDIR;
19extern const string MDATADIR;19extern const string MDATADIR;
20extern const string PACKAGE_NAME;
21extern const string SYSCONFDIR;20extern const string SYSCONFDIR;
22extern const string MIDORI_VERSION_SUFFIX;21extern const string MIDORI_VERSION_SUFFIX;
23const string MODULE_PREFIX = "lib";22const string MODULE_PREFIX = "lib";
2423
=== modified file 'midori/CMakeLists.txt'
--- midori/CMakeLists.txt 2013-09-08 21:38:36 +0000
+++ midori/CMakeLists.txt 2014-09-01 23:11:13 +0000
@@ -16,10 +16,13 @@
16 ${MIDORI_VALA_SOURCE}16 ${MIDORI_VALA_SOURCE}
17PACKAGES17PACKAGES
18 ${PKGS}18 ${PKGS}
19 katze
20 midori
21 gobject-fixes
22 gtk-fixes
19OPTIONS23OPTIONS
20 ${VALAFLAGS}24 ${VALAFLAGS}
21CUSTOM_VAPIS25 --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/../vapi
22 ${EXTRA_VAPIS}
23GENERATE_VAPI26GENERATE_VAPI
24 "${CMAKE_PROJECT_NAME}-core"27 "${CMAKE_PROJECT_NAME}-core"
25GENERATE_HEADER28GENERATE_HEADER
2629
=== modified file 'midori/midori-app.h'
--- midori/midori-app.h 2013-03-28 17:27:56 +0000
+++ midori/midori-app.h 2014-09-01 23:11:13 +0000
@@ -14,7 +14,7 @@
1414
15#include <katze/katze.h>15#include <katze/katze.h>
1616
17#include "midori-browser.h"17#include "midori-core.h"
18#include "midori-websettings.h"18#include "midori-websettings.h"
1919
20G_BEGIN_DECLS20G_BEGIN_DECLS
2121
=== removed file 'midori/midori-browser.h'
--- midori/midori-browser.h 2013-03-26 23:09:09 +0000
+++ midori/midori-browser.h 1970-01-01 00:00:00 +0000
@@ -1,199 +0,0 @@
1/*
2 Copyright (C) 2008 Christian Dywan <christian@twotoasts.de>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 See the file COPYING for the full license text.
10*/
11
12#ifndef __MIDORI_BROWSER_H__
13#define __MIDORI_BROWSER_H__
14
15#include <katze/katze.h>
16#include "midori-view.h"
17
18G_BEGIN_DECLS
19
20#define MIDORI_TYPE_BROWSER \
21 (midori_browser_get_type ())
22#define MIDORI_BROWSER(obj) \
23 (G_TYPE_CHECK_INSTANCE_CAST ((obj), MIDORI_TYPE_BROWSER, MidoriBrowser))
24#define MIDORI_BROWSER_CLASS(klass) \
25 (G_TYPE_CHECK_CLASS_CAST ((klass), MIDORI_TYPE_BROWSER, MidoriBrowserClass))
26#define MIDORI_IS_BROWSER(obj) \
27 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MIDORI_TYPE_BROWSER))
28#define MIDORI_IS_BROWSER_CLASS(klass) \
29 (G_TYPE_CHECK_CLASS_TYPE ((klass), MIDORI_TYPE_BROWSER))
30#define MIDORI_BROWSER_GET_CLASS(obj) \
31 (G_TYPE_INSTANCE_GET_CLASS ((obj), MIDORI_TYPE_BROWSER, MidoriBrowserClass))
32
33typedef struct _MidoriBrowser MidoriBrowser;
34typedef struct _MidoriBrowserClass MidoriBrowserClass;
35
36struct _MidoriBrowserClass
37{
38 GtkWindowClass parent_class;
39
40 /* Signals */
41 void
42 (*window_object_cleared) (MidoriBrowser* browser,
43#ifndef HAVE_WEBKIT2
44 WebKitWebFrame* web_frame,
45#else
46 void* web_frame,
47#endif
48 JSContextRef* context,
49 JSObjectRef* window_object);
50 void
51 (*statusbar_text_changed) (MidoriBrowser* browser,
52 const gchar* text);
53 void
54 (*element_motion) (MidoriBrowser* browser,
55 const gchar* link_uri);
56 void
57 (*new_window) (MidoriBrowser* browser,
58 const gchar* uri);
59
60 void
61 (*add_tab) (MidoriBrowser* browser,
62 GtkWidget* view);
63 void
64 (*remove_tab) (MidoriBrowser* browser,
65 GtkWidget* view);
66 void
67 (*activate_action) (MidoriBrowser* browser,
68 const gchar* name);
69 void
70 (*quit) (MidoriBrowser* browser);
71};
72
73GType
74midori_browser_get_type (void) G_GNUC_CONST;
75
76MidoriBrowser*
77midori_browser_new (void);
78
79void
80midori_browser_add_tab (MidoriBrowser* browser,
81 GtkWidget* widget);
82
83void
84midori_browser_close_tab (MidoriBrowser* browser,
85 GtkWidget* widget);
86
87GtkWidget*
88midori_browser_add_item (MidoriBrowser* browser,
89 KatzeItem* item);
90
91GtkWidget*
92midori_browser_add_uri (MidoriBrowser* browser,
93 const gchar* uri);
94
95void
96midori_browser_activate_action (MidoriBrowser* browser,
97 const gchar* name);
98
99void
100midori_browser_assert_action (MidoriBrowser* browser,
101 const gchar* name);
102
103void
104midori_browser_block_action (MidoriBrowser* browser,
105 GtkAction* action);
106
107void
108midori_browser_unblock_action (MidoriBrowser* browser,
109 GtkAction* action);
110
111void
112midori_browser_set_action_visible (MidoriBrowser* browser,
113 const gchar* name,
114 gboolean visible);
115
116GtkActionGroup*
117midori_browser_get_action_group (MidoriBrowser* browser);
118
119void
120midori_browser_set_current_uri (MidoriBrowser* browser,
121 const gchar* uri);
122
123const gchar*
124midori_browser_get_current_uri (MidoriBrowser* browser);
125
126void
127midori_browser_set_current_page_smartly (MidoriBrowser* browser,
128 gint n);
129
130void
131midori_browser_set_current_tab_smartly (MidoriBrowser* browser,
132 GtkWidget* view);
133
134void
135midori_browser_set_current_page (MidoriBrowser* browser,
136 gint n);
137
138gint
139midori_browser_get_current_page (MidoriBrowser* browser);
140
141void
142midori_browser_set_current_item (MidoriBrowser* browser,
143 KatzeItem* item);
144
145GtkWidget*
146midori_browser_get_nth_tab (MidoriBrowser* browser,
147 gint n);
148
149void
150midori_browser_set_current_tab (MidoriBrowser* browser,
151 GtkWidget* widget);
152#define midori_browser_set_tab midori_browser_set_current_tab
153
154GtkWidget*
155midori_browser_get_current_tab (MidoriBrowser* browser);
156#define midori_browser_get_tab midori_browser_get_current_tab
157
158gint
159midori_browser_page_num (MidoriBrowser* browser,
160 GtkWidget* view);
161
162GList*
163midori_browser_get_tabs (MidoriBrowser* browser);
164
165gint
166midori_browser_get_n_pages (MidoriBrowser* browser);
167
168KatzeArray*
169midori_browser_get_proxy_array (MidoriBrowser* browser);
170
171MidoriBrowser*
172midori_browser_get_for_widget (GtkWidget* widget);
173
174void
175midori_browser_quit (MidoriBrowser* browser);
176
177const gchar**
178midori_browser_get_toolbar_actions (MidoriBrowser* browser);
179
180MidoriWebSettings*
181midori_browser_get_settings (MidoriBrowser* browser);
182
183void
184midori_browser_update_history (KatzeItem* item,
185 const gchar* type,
186 const gchar* event);
187
188void
189midori_browser_save_uri (MidoriBrowser* browser,
190 MidoriView* view,
191 const gchar* uri);
192
193void
194midori_browser_set_inactivity_reset (MidoriBrowser* browser,
195 gint inactivity_reset);
196
197G_END_DECLS
198
199#endif /* __MIDORI_BROWSER_H__ */
2000
=== renamed file 'midori/midori-browser.c' => 'midori/midori-browser.vala'
--- midori/midori-browser.c 2014-08-31 10:33:28 +0000
+++ midori/midori-browser.vala 2014-09-01 23:11:13 +0000
@@ -1,993 +1,923 @@
1/*1
2 Copyright (C) 2007-2013 Christian Dywan <christian@twotoasts.de>2namespace Midori {
3 Copyright (C) 2008 Dale Whittaker <dayul@users.sf.net>3 public class Browser : Gtk.Window {
4 Copyright (C) 2009 Jérôme Geulfucci <jeromeg@xfce.org>4 struct FolderEntry {
55 string title;
6 This library is free software; you can redistribute it and/or6 int64 id;
7 modify it under the terms of the GNU Lesser General Public7 int64 parentid;
8 License as published by the Free Software Foundation; either8 }
9 version 2.1 of the License, or (at your option) any later version.9
1010 static construct {
11 See the file COPYING for the full license text.11#if GTK_VERSION_3_0_0
12*/12 Gtk.rc_parse ("style \"tool-button-style\"\n {\n" +
1313 "GtkToolButton::icon-spacing = 2\n }\n" +
14#include "midori-browser.h"14 "widget \"MidoriBrowser.*.MidoriBookmarkbar.Gtk*ToolButton\" " +
1515 "style \"tool-button-style\"\n" +
16#include "midori-app.h"16 "widget \"MidoriBrowser.*.MidoriFindbar.Gtk*ToolButton\" " +
17#include "midori-extension.h"17 "style \"tool-button-style\"\n");
18#include "midori-array.h"18#endif
19#include "midori-view.h"19 }
20#include "midori-preferences.h"20
21#include "midori-panel.h"21 public signal Browser new_window (Browser? new_window);
22#include "midori-locationaction.h"22 public signal void move_tab (Gtk.Widget notebook, int current_position, int new_position);
23#include "midori-searchaction.h"23 public signal void switch_tab (Object old_tab, Object new_tab);
24#include "midori-findbar.h"24 public signal void add_download (WebKit.Download download);
25#include "midori-platform.h"25 public signal void send_notification (string title, string message);
26#include "midori-privatedata.h"26 public signal void populate_tool_menu (Gtk.Menu menu);
27#include "midori-core.h"27 public signal void populate_toolbar_menu (Gtk.Menu menu);
28#include "midori-privatedata.h"28 public signal void show_preferences (Katze.Preferences preferences);
29#include "midori-bookmarks-db.h"29
30#include "katze-cellrenderercomboboxtext.h"30 public Gtk.Toolbar navigationbar { get; private set; }
3131 public Gtk.MenuBar menubar { get; private set; }
32#include "marshal.h"32 public Notebook notebook { get; private set; }
3333 public Panel panel { get; private set; }
34#include <glib/gi18n.h>34 public Katze.Array proxy_array { get; private set; }
35#include <glib/gstdio.h>35
36#include <gdk/gdkkeysyms.h>36 public unowned string uri {
37#include <string.h>37 get { return get_current_uri (); }
3838 set { set_current_uri (value); }
39#include <config.h>39 }
4040
41#ifdef HAVE_GRANITE41 public unowned Tab tab {
42 #include <granite.h>42 get { return get_current_tab (); }
43#endif43 set { set_current_tab (value); }
4444 }
45#ifdef HAVE_ZEITGEIST45
46 #include <zeitgeist.h>46 public LoadStatus load_status {
47#endif47 get {
4848 var tab = get_current_tab ();
49#ifdef HAVE_UNISTD_H49 return tab != null ? tab.load_status : LoadStatus.FINISHED;
50 #include <unistd.h>50 }
51#endif51 }
5252
53#include <sqlite3.h>53 /**
5454 * MidoriBrowser:statusbar:
55#ifdef HAVE_X11_EXTENSIONS_SCRNSAVER_H55 *
56 #include <X11/Xlib.h>56 * The widget representing the statusbar contents. This is
57 #include <X11/Xutil.h>57 * not an actual #GtkStatusbar but rather a #GtkBox.
58 #include <X11/extensions/scrnsaver.h>58 */
59 #include <gdk/gdkx.h>59 public Gtk.Statusbar statusbar { get; private set; }
60#endif60
6161 /**
62struct _MidoriBrowser62 * MidoriBrowser:statusbar-text:
63{63 *
64 GtkWindow parent_instance;64 * The text that is displayed in the statusbar.
65 GtkActionGroup* action_group;65 *
66 GtkWidget* menubar;66 * This value reflects changes to the text visible in the statusbar, such
67 GtkWidget* throbber;67 * as the uri of a hyperlink the mouse hovers over or the description of
68 GtkWidget* navigationbar;68 * a menuitem.
69 GtkWidget* bookmarkbar;69 *
7070 * Setting this value changes the displayed text until the next change.
71 GtkWidget* panel;71 */
72 GtkWidget* notebook;72 public string statusbar_text {
7373 get {
74 GtkWidget* inspector;74 return _statusbar_text;
75 GtkWidget* inspector_view;75 }
7676 set {
77 GtkWidget* find;77 _set_statusbar_text ((View) tab, value);
7878 }
79 GtkWidget* statusbar;79 }
80 GtkWidget* statusbar_contents;80
81 gchar* statusbar_text;81 /**
8282 * MidoriBrowser:settings:
83 gint last_window_width, last_window_height;83 *
84 guint alloc_timeout;84 * An associated settings instance that is shared among all web views.
85 guint panel_timeout;85 *
8686 * Setting this value is propagated to every present web view. Also
87 MidoriWebSettings* settings;87 * every newly created web view will use this instance automatically.
88 KatzeArray* proxy_array;88 *
89 MidoriBookmarksDb* bookmarks;89 * If no settings are specified a default will be used.
90 KatzeArray* trash;90 */
91 KatzeArray* search_engines;91 public WebSettings settings {
92 KatzeArray* history;92 get {
93 MidoriHistoryDatabase* history_database;93 return _settings;
94 MidoriSpeedDial* dial;94 }
95 gboolean show_tabs;95 set {
9696 _settings.notify.disconnect (settings_notify);
97 gboolean show_navigationbar;97 _settings = value;
98 gboolean show_statusbar;98
99 guint maximum_history_age;99 if (_settings == null)
100 guint last_web_search;100 _settings = new WebSettings ();
101101
102 gboolean bookmarkbar_populate;102 update_settings ();
103};103 _settings.set_data<ulong> ("handle-settings-notify", _settings.notify.connect (settings_notify));
104104
105G_DEFINE_TYPE (MidoriBrowser, midori_browser, GTK_TYPE_WINDOW)105 foreach (var tab in get_tabs ())
106106 ((View) tab).settings = _settings;
107enum107 }
108{108 }
109 PROP_0,109
110110 /**
111 PROP_MENUBAR,111 * MidoriBrowser:proxy-items:
112 PROP_NAVIGATIONBAR,112 *
113 PROP_NOTEBOOK,113 * The open views, automatically updated, for session management.
114 PROP_PANEL,114 *
115 PROP_URI,115 * Since: 0.4.8
116 PROP_TAB,116 */
117 PROP_LOAD_STATUS,117 public Katze.Array proxy_items { get; private set; }
118 PROP_STATUSBAR,118
119 PROP_STATUSBAR_TEXT,119 /**
120 PROP_SETTINGS,120 * MidoriBrowser:bookmarks:
121 PROP_PROXY_ITEMS,121 *
122 PROP_BOOKMARKS,122 * The bookmarks folder, containing all bookmarks.
123 PROP_TRASH,123 *
124 PROP_SEARCH_ENGINES,124 * This is actually a reference to a bookmarks instance,
125 PROP_HISTORY,125 * so if bookmarks should be used it must be initially set.
126 PROP_SPEED_DIAL,126 */
127 PROP_SHOW_TABS,127 public BookmarksDb? bookmarks {
128};128 get {
129129 return _bookmarks;
130enum130 }
131{131 set {
132 NEW_WINDOW,132 _set_bookmarks (value);
133 ADD_TAB,133 }
134 REMOVE_TAB,134 }
135 MOVE_TAB,135
136 SWITCH_TAB,136 /**
137 ACTIVATE_ACTION,137 * MidoriBrowser:trash:
138 ADD_DOWNLOAD,138 *
139 SEND_NOTIFICATION,139 * The trash, that collects all closed tabs and windows.
140 POPULATE_TOOL_MENU,140 *
141 POPULATE_TOOLBAR_MENU,141 * This is actually a reference to a trash instance, so if a trash should
142 QUIT,142 * be used it must be initially set.
143 SHOW_PREFERENCES,143 *
144144 * Note: In the future the trash might collect other types of items.
145 LAST_SIGNAL145 */
146};146 public Katze.Array? trash {
147147 get {
148static guint signals[LAST_SIGNAL];148 return _trash;
149149 }
150static void150 set {
151midori_browser_dispose (GObject* object);151 _trash = value;
152152
153static void153 var trash_action = (Katze.ArrayAction) action_group.get_action ("Trash");
154midori_browser_finalize (GObject* object);154 trash_action.array = _trash;
155155 trash_action.reversed = true;
156#ifdef HAVE_WEBKIT2156
157void download_created_destination_cb (WebKitDownload *download,157 action_set_visible ("Trash", _trash != null);
158 gchar *destination,158 action_set_visible ("UndoTabClose", _trash != null);
159 gpointer user_data);159
160#endif160 if (trash != null) {
161161 trash.clear.connect_after (trash_clear_cb);
162static void162 trash_clear_cb ();
163midori_browser_set_property (GObject* object,
164 guint prop_id,
165 const GValue* value,
166 GParamSpec* pspec);
167
168static void
169midori_browser_get_property (GObject* object,
170 guint prop_id,
171 GValue* value,
172 GParamSpec* pspec);
173
174gboolean
175midori_browser_open_bookmark (MidoriBrowser* browser,
176 KatzeItem* item);
177
178static void
179midori_bookmarkbar_populate (MidoriBrowser* browser);
180static void
181midori_bookmarkbar_populate_idle (MidoriBrowser* browser);
182
183static void
184midori_bookmarkbar_clear (GtkWidget* toolbar);
185
186static void
187_midori_browser_set_toolbar_style (MidoriBrowser* browser,
188 MidoriToolbarStyle toolbar_style);
189
190static void
191midori_browser_settings_notify (MidoriWebSettings* web_settings,
192 GParamSpec* pspec,
193 MidoriBrowser* browser);
194
195void
196midori_panel_set_toolbar_style (MidoriPanel* panel,
197 GtkToolbarStyle style);
198
199static void
200midori_browser_set_bookmarks (MidoriBrowser* browser,
201 MidoriBookmarksDb* bookmarks);
202
203static void
204midori_browser_add_speed_dial (MidoriBrowser* browser);
205
206static void
207midori_browser_step_history (MidoriBrowser* browser,
208 MidoriView* view);
209
210#define _action_by_name(brwsr, nme) \
211 gtk_action_group_get_action (brwsr->action_group, nme)
212#define _action_set_sensitive(brwsr, nme, snstv) \
213 gtk_action_set_sensitive (_action_by_name (brwsr, nme), snstv);
214#define _action_set_visible(brwsr, nme, vsbl) \
215 gtk_action_set_visible (_action_by_name (brwsr, nme), vsbl);
216#define _action_set_active(brwsr, nme, actv) \
217 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION ( \
218 _action_by_name (brwsr, nme)), actv);
219
220static void
221midori_browser_disconnect_tab (MidoriBrowser* browser,
222 MidoriView* view);
223
224static gboolean
225midori_browser_is_fullscreen (MidoriBrowser* browser)
226{
227 GdkWindow* window = gtk_widget_get_window (GTK_WIDGET (browser));
228 GdkWindowState state = window ? gdk_window_get_state (window) : 0;
229 return state & GDK_WINDOW_STATE_FULLSCREEN;
230}
231
232static gboolean
233_toggle_tabbar_smartly (MidoriBrowser* browser,
234 gboolean ignore_fullscreen)
235{
236 gboolean has_tabs = midori_browser_get_n_pages (browser) > 1;
237 gboolean show_tabs = !midori_browser_is_fullscreen (browser) || ignore_fullscreen;
238 if (!browser->show_tabs)
239 show_tabs = FALSE;
240 midori_notebook_set_labels_visible (MIDORI_NOTEBOOK (browser->notebook), show_tabs);
241 return has_tabs;
242}
243
244static void
245midori_browser_trash_clear_cb (KatzeArray* trash,
246 MidoriBrowser* browser)
247{
248 gboolean trash_empty = katze_array_is_empty (browser->trash);
249 _action_set_sensitive (browser, "UndoTabClose", !trash_empty);
250 _action_set_sensitive (browser, "Trash", !trash_empty);
251}
252
253static void
254_midori_browser_update_actions (MidoriBrowser* browser)
255{
256 gboolean has_tabs = _toggle_tabbar_smartly (browser, FALSE);
257 _action_set_sensitive (browser, "TabPrevious", has_tabs);
258 _action_set_sensitive (browser, "TabNext", has_tabs);
259
260 if (browser->trash)
261 midori_browser_trash_clear_cb (browser->trash, browser);
262}
263
264static void
265midori_browser_update_secondary_icon (MidoriBrowser* browser,
266 MidoriView* view,
267 GtkAction* action)
268{
269 if (g_object_get_data (G_OBJECT (view), "news-feeds"))
270 {
271 midori_location_action_set_secondary_icon (
272 MIDORI_LOCATION_ACTION (action), STOCK_NEWS_FEED);
273 _action_set_sensitive (browser, "AddNewsFeed", TRUE);
274 }
275 else
276 {
277 midori_location_action_set_secondary_icon (
278 MIDORI_LOCATION_ACTION (action), NULL);
279 _action_set_sensitive (browser, "AddNewsFeed", FALSE);
280 }
281}
282
283static void
284_midori_browser_update_interface (MidoriBrowser* browser,
285 MidoriView* view)
286{
287 GtkAction* action;
288
289 _action_set_sensitive (browser, "Back", midori_view_can_go_back (view));
290 _action_set_sensitive (browser, "Forward", midori_tab_can_go_forward (MIDORI_TAB (view)));
291 _action_set_sensitive (browser, "Previous",
292 midori_view_get_previous_page (view) != NULL);
293 _action_set_sensitive (browser, "Next",
294 midori_view_get_next_page (view) != NULL);
295
296 _action_set_sensitive (browser, "AddSpeedDial", !midori_view_is_blank (view));
297 _action_set_sensitive (browser, "BookmarkAdd", !midori_view_is_blank (view));
298 _action_set_sensitive (browser, "MailTo", !midori_view_is_blank (view));
299 _action_set_sensitive (browser, "SaveAs", midori_tab_can_save (MIDORI_TAB (view)));
300 _action_set_sensitive (browser, "ZoomIn", midori_view_can_zoom_in (view));
301 _action_set_sensitive (browser, "ZoomOut", midori_view_can_zoom_out (view));
302 _action_set_sensitive (browser, "ZoomNormal",
303 midori_view_get_zoom_level (view) != 1.0f);
304 _action_set_sensitive (browser, "Encoding",
305 midori_tab_can_view_source (MIDORI_TAB (view)));
306 _action_set_sensitive (browser, "SourceView",
307 midori_tab_can_view_source (MIDORI_TAB (view)));
308 _action_set_sensitive (browser, "SourceViewDom",
309 midori_tab_can_view_source (MIDORI_TAB (view)));
310
311 action = _action_by_name (browser, "NextForward");
312 if (midori_tab_can_go_forward (MIDORI_TAB (view)))
313 {
314 g_object_set (action,
315 "stock-id", GTK_STOCK_GO_FORWARD,
316 "tooltip", _("Go forward to the next page"),
317 "sensitive", TRUE, NULL);
318 }
319 else
320 {
321 g_object_set (action,
322 "stock-id", GTK_STOCK_MEDIA_NEXT,
323 "tooltip", _("Go to the next sub-page"),
324 "sensitive", midori_view_get_next_page (view) != NULL, NULL);
325 }
326
327 action = _action_by_name (browser, "Location");
328 if (midori_tab_is_blank (MIDORI_TAB (view)))
329 {
330 gchar* icon_names[] = { "edit-find-symbolic", "edit-find", NULL };
331 GIcon* icon = g_themed_icon_new_from_names (icon_names, -1);
332 midori_location_action_set_primary_icon (
333 MIDORI_LOCATION_ACTION (action), icon, _("Web Search…"));
334 g_object_unref (icon);
335 }
336 else
337 midori_location_action_set_security_hint (
338 MIDORI_LOCATION_ACTION (action), midori_tab_get_security (MIDORI_TAB (view)));
339 midori_browser_update_secondary_icon (browser, view, action);
340}
341
342static void
343_midori_browser_set_statusbar_text (MidoriBrowser* browser,
344 MidoriView* view,
345 const gchar* text)
346{
347 #if GTK_CHECK_VERSION (3, 2, 0)
348 gboolean is_location = FALSE;
349 #else
350 GtkWidget* widget = gtk_window_get_focus (GTK_WINDOW (browser));
351 gboolean is_location = widget && GTK_IS_ENTRY (widget)
352 && GTK_IS_ALIGNMENT (gtk_widget_get_parent (widget));
353 #endif
354
355 katze_assign (browser->statusbar_text, midori_uri_format_for_display (text));
356 if (view == NULL)
357 return;
358
359 if (!gtk_widget_get_visible (browser->statusbar) && !is_location
360 && text && *text)
361 {
362 #if GTK_CHECK_VERSION (3, 2, 0)
363 midori_view_set_overlay_text (view, browser->statusbar_text);
364 #else
365 GtkAction* action = _action_by_name (browser, "Location");
366 MidoriLocationAction* location_action = MIDORI_LOCATION_ACTION (action);
367 midori_location_action_set_text (location_action, browser->statusbar_text);
368 midori_location_action_set_secondary_icon (location_action, NULL);
369 #endif
370 }
371 else if (!gtk_widget_get_visible (browser->statusbar) && !is_location)
372 {
373 #if GTK_CHECK_VERSION (3, 2, 0)
374 midori_view_set_overlay_text (view, NULL);
375 #else
376 GtkAction* action = _action_by_name (browser, "Location");
377 MidoriLocationAction* location_action = MIDORI_LOCATION_ACTION (action);
378 midori_browser_update_secondary_icon (browser, view, action);
379 midori_location_action_set_text (location_action,
380 midori_view_get_display_uri (view));
381 #endif
382 }
383 else
384 {
385 gtk_statusbar_pop (GTK_STATUSBAR (browser->statusbar), 1);
386 gtk_statusbar_push (GTK_STATUSBAR (browser->statusbar), 1,
387 katze_str_non_null (browser->statusbar_text));
388 }
389}
390
391void
392midori_browser_set_current_page_smartly (MidoriBrowser* browser,
393 gint n)
394{
395 if (!katze_object_get_boolean (browser->settings,
396 "open-tabs-in-the-background"))
397 midori_browser_set_current_page (browser, n);
398}
399
400/**
401 * midori_browser_set_current_tab_smartly:
402 * @browser: a #MidoriBrowser
403 * @view: a #GtkWidget
404 *
405 * Switches to the tab containing @view iff open-tabs-in-the-background is %FALSE.
406 *
407 * Since: 0.4.9
408 **/
409void
410midori_browser_set_current_tab_smartly (MidoriBrowser* browser,
411 GtkWidget* view)
412{
413 if (!katze_object_get_boolean (browser->settings,
414 "open-tabs-in-the-background"))
415 midori_browser_set_current_tab (browser, view);
416}
417
418static void
419_midori_browser_update_progress (MidoriBrowser* browser,
420 MidoriView* view)
421{
422 GtkAction* action;
423 gdouble progress = midori_view_get_progress (view);
424 gboolean loading = progress > 0.0;
425
426 action = _action_by_name (browser, "Location");
427 midori_location_action_set_progress (MIDORI_LOCATION_ACTION (action), progress);
428
429 _action_set_sensitive (browser, "Reload", !loading);
430 _action_set_sensitive (browser, "Stop", loading);
431
432 action = _action_by_name (browser, "ReloadStop");
433 if (!loading)
434 {
435 g_object_set (action,
436 "stock-id", GTK_STOCK_REFRESH,
437 "tooltip", _("Reload the current page"), NULL);
438 }
439 else
440 {
441 g_object_set (action,
442 "stock-id", GTK_STOCK_STOP,
443 "tooltip", _("Stop loading the current page"), NULL);
444 }
445
446 g_object_set (browser->throbber, "active", loading, "visible", loading, NULL);
447}
448
449/**
450 * midori_browser_update_history:
451 * @item: a #KatzeItem
452 * @type: "website", "bookmark" or "download"
453 * @event: "access", "leave", "modify", "delete"
454 *
455 * Since: 0.4.7
456 **/
457void
458midori_browser_update_history (KatzeItem* item,
459 const gchar* type,
460 const gchar* event)
461{
462 g_return_if_fail (!KATZE_ITEM_IS_SEPARATOR (item));
463
464 #ifdef HAVE_ZEITGEIST
465 const gchar* inter;
466 if (strstr (event, "access"))
467 inter = ZEITGEIST_ZG_ACCESS_EVENT;
468 else if (strstr (event, "leave"))
469 inter = ZEITGEIST_ZG_LEAVE_EVENT;
470 else if (strstr (event, "modify"))
471 inter = ZEITGEIST_ZG_MODIFY_EVENT;
472 else if (strstr (event, "create"))
473 inter = ZEITGEIST_ZG_CREATE_EVENT;
474 else if (strstr (event, "delete"))
475 inter = ZEITGEIST_ZG_DELETE_EVENT;
476 else
477 g_assert_not_reached ();
478
479 /* FIXME: Should insert folders into the log (ZEITGEIST_NFO_BOOKMARK_FOLDER) */
480 if (KATZE_ITEM_IS_FOLDER (item))
481 return;
482
483 zeitgeist_log_insert_events_no_reply (zeitgeist_log_get_default (),
484 zeitgeist_event_new_full (inter, ZEITGEIST_ZG_USER_ACTIVITY,
485 "application://midori.desktop",
486 zeitgeist_subject_new_full (
487 katze_item_get_uri (item),
488 strstr (type, "bookmark") ? ZEITGEIST_NFO_BOOKMARK : ZEITGEIST_NFO_WEBSITE,
489 zeitgeist_manifestation_for_uri (katze_item_get_uri (item)),
490 katze_item_get_meta_string (item, "mime-type"), NULL, katze_item_get_name (item), NULL),
491 NULL),
492 NULL);
493 #endif
494}
495
496static void
497midori_browser_update_history_title (MidoriBrowser* browser,
498 KatzeItem* item)
499{
500 sqlite3* db;
501 static sqlite3_stmt* stmt = NULL;
502
503 g_return_if_fail (katze_item_get_uri (item) != NULL);
504
505 db = g_object_get_data (G_OBJECT (browser->history), "db");
506 g_return_if_fail (db != NULL);
507 if (!stmt)
508 {
509 const gchar* sqlcmd;
510
511 sqlcmd = "UPDATE history SET title=? WHERE uri = ? and date=?";
512 sqlite3_prepare_v2 (db, sqlcmd, -1, &stmt, NULL);
513 }
514 sqlite3_bind_text (stmt, 1, katze_item_get_name (item), -1, 0);
515 sqlite3_bind_text (stmt, 2, katze_item_get_uri (item), -1, 0);
516 sqlite3_bind_int64 (stmt, 3, katze_item_get_added (item));
517
518 if (sqlite3_step (stmt) != SQLITE_DONE)
519 g_printerr (_("Failed to update title: %s\n"), sqlite3_errmsg (db));
520 sqlite3_reset (stmt);
521 sqlite3_clear_bindings (stmt);
522
523 midori_browser_update_history (item, "website", "access");
524}
525
526/**
527 * midori_browser_assert_action:
528 * @browser: a #MidoriBrowser
529 * @name: action, setting=value expression or extension=true|false
530 *
531 * Assert that @name is a valid action or setting expression,
532 * if it fails the program will terminate with an error.
533 * To be used with command line interfaces.
534 *
535 * Since: 0.5.0
536 **/
537void
538midori_browser_assert_action (MidoriBrowser* browser,
539 const gchar* name)
540{
541 g_return_if_fail (MIDORI_IS_BROWSER (browser));
542 g_return_if_fail (name != NULL);
543
544 if (strchr (name, '='))
545 {
546 gchar** parts = g_strsplit (name, "=", 0);
547 GObjectClass* class = G_OBJECT_GET_CLASS (browser->settings);
548 GParamSpec* pspec = g_object_class_find_property (class, parts[0]);
549 if (pspec != NULL)
550 {
551 GType type = G_PARAM_SPEC_TYPE (pspec);
552 if (!(
553 (type == G_TYPE_PARAM_BOOLEAN && (!strcmp (parts[1], "true") || !strcmp (parts[1], "false")))
554 || type == G_TYPE_PARAM_STRING
555 || type == G_TYPE_PARAM_INT
556 || type == G_TYPE_PARAM_FLOAT
557 || type == G_TYPE_PARAM_DOUBLE
558 || type == G_TYPE_PARAM_ENUM))
559 midori_error (_("Value '%s' is invalid for %s"), parts[1], parts[0]);
560 }
561 else
562 {
563 gchar* extension_path = midori_paths_get_lib_path (PACKAGE_NAME);
564 GObject* extension = midori_extension_load_from_file (extension_path, parts[0], FALSE, FALSE);
565 g_free (extension_path);
566 if (!extension || (strcmp (parts[1], "true") && strcmp (parts[1], "false")))
567 midori_error (_("Unexpected setting '%s'"), name);
568 }
569 g_strfreev (parts);
570 }
571 else
572 {
573 GtkAction* action = _action_by_name (browser, name);
574 if (!action)
575 midori_error (_("Unexpected action '%s'."), name);
576 }
577}
578
579void
580midori_app_set_browsers (MidoriApp* app,
581 KatzeArray* browsers,
582 MidoriBrowser* browser);
583
584static void
585_midori_browser_activate_action (MidoriBrowser* browser,
586 const gchar* name)
587{
588 g_return_if_fail (name != NULL);
589
590 if (strchr (name, '='))
591 {
592 gchar** parts = g_strsplit (name, "=", 0);
593 GObjectClass* class = G_OBJECT_GET_CLASS (browser->settings);
594 GParamSpec* pspec = g_object_class_find_property (class, parts[0]);
595 if (pspec != NULL)
596 {
597 GType type = G_PARAM_SPEC_TYPE (pspec);
598 if (type == G_TYPE_PARAM_BOOLEAN && !strcmp ("true", parts[1]))
599 g_object_set (browser->settings, parts[0], TRUE, NULL);
600 else if (type == G_TYPE_PARAM_BOOLEAN && !strcmp ("false", parts[1]))
601 g_object_set (browser->settings, parts[0], FALSE, NULL);
602 else if (type == G_TYPE_PARAM_STRING)
603 g_object_set (browser->settings, parts[0], parts[1], NULL);
604 else if (type == G_TYPE_PARAM_INT || type == G_TYPE_PARAM_UINT)
605 g_object_set (browser->settings, parts[0], atoi (parts[1]), NULL);
606 else if (type == G_TYPE_PARAM_FLOAT || type == G_TYPE_PARAM_DOUBLE)
607 g_object_set (browser->settings, parts[0], g_ascii_strtod (parts[1], NULL), NULL);
608 else if (type == G_TYPE_PARAM_ENUM)
609 {
610 GEnumClass* enum_class = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
611 GEnumValue* enum_value = g_enum_get_value_by_name (enum_class, parts[1]);
612 if (enum_value != NULL)
613 g_object_set (browser->settings, parts[0], enum_value->value, NULL);
614 else
615 g_warning (_("Value '%s' is invalid for %s"), parts[1], parts[0]);
616 }
617 else
618 g_warning (_("Value '%s' is invalid for %s"), parts[1], parts[0]);
619 }
620 else
621 {
622 gchar* extension_path = midori_paths_get_lib_path (PACKAGE_NAME);
623 GObject* extension = midori_extension_load_from_file (extension_path, parts[0], TRUE, FALSE);
624 MidoriApp* app = midori_app_new_proxy (NULL);
625 g_object_set (app,
626 "settings", browser->settings,
627 NULL);
628 /* FIXME: tabs of multiple windows */
629 KatzeArray* browsers = katze_array_new (MIDORI_TYPE_BROWSER);
630 katze_array_add_item (browsers, browser);
631 midori_app_set_browsers (app, browsers, browser);
632 g_free (extension_path);
633 if (extension && !strcmp (parts[1], "true"))
634 midori_extension_activate (extension, parts[0], TRUE, app);
635 else if (extension && !strcmp (parts[1], "false"))
636 midori_extension_deactivate (MIDORI_EXTENSION (extension));
637 else
638 g_warning (_("Unexpected setting '%s'"), name);
639 }
640 g_strfreev (parts);
641 }
642 else
643 {
644 GtkAction* action = _action_by_name (browser, name);
645 if (action)
646 gtk_action_activate (action);
647 else
648 g_warning (_("Unexpected action '%s'."), name);
649 }
650}
651
652static void
653midori_view_notify_icon_cb (MidoriView* view,
654 GParamSpec* pspec,
655 MidoriBrowser* browser)
656{
657 if (midori_browser_get_current_tab (browser) != (GtkWidget*)view)
658 return;
659
660 if (midori_paths_get_runtime_mode () == MIDORI_RUNTIME_MODE_APP)
661 gtk_window_set_icon (GTK_WINDOW (browser), midori_view_get_icon (view));
662}
663
664static void
665midori_view_notify_load_status_cb (GtkWidget* widget,
666 GParamSpec* pspec,
667 MidoriBrowser* browser)
668{
669 MidoriView* view = MIDORI_VIEW (widget);
670 MidoriLoadStatus load_status = midori_view_get_load_status (view);
671
672 if (widget == midori_browser_get_current_tab (browser))
673 {
674 if (load_status == MIDORI_LOAD_COMMITTED)
675 {
676 const gchar* uri = midori_view_get_display_uri (view);
677 GtkAction* action = _action_by_name (browser, "Location");
678 midori_location_action_set_text (
679 MIDORI_LOCATION_ACTION (action), uri);
680
681 /* Focus the urlbar on blank pages */
682 if (midori_view_is_blank (view))
683 midori_browser_activate_action (browser, "Location");
684 }
685
686 _midori_browser_update_interface (browser, view);
687 _midori_browser_set_statusbar_text (browser, view, NULL);
688 }
689
690 if (load_status == MIDORI_LOAD_FINISHED)
691 katze_item_set_meta_string (midori_view_get_proxy_item (view),
692 "history-step", NULL);
693
694 g_object_notify (G_OBJECT (browser), "load-status");
695}
696
697static void
698midori_view_notify_progress_cb (GtkWidget* view,
699 GParamSpec* pspec,
700 MidoriBrowser* browser)
701{
702 if (view == midori_browser_get_current_tab (browser))
703 _midori_browser_update_progress (browser, MIDORI_VIEW (view));
704}
705
706static void
707midori_view_notify_uri_cb (GtkWidget* widget,
708 GParamSpec* pspec,
709 MidoriBrowser* browser)
710{
711 if (widget == midori_browser_get_current_tab (browser))
712 {
713 MidoriView* view = MIDORI_VIEW (widget);
714 const gchar* uri = midori_view_get_display_uri (view);
715 GtkAction* action = _action_by_name (browser, "Location");
716 midori_location_action_set_text (MIDORI_LOCATION_ACTION (action), uri);
717 _action_set_sensitive (browser, "Back", midori_view_can_go_back (view));
718 _action_set_sensitive (browser, "Forward", midori_tab_can_go_forward (MIDORI_TAB (view)));
719 g_object_notify (G_OBJECT (browser), "uri");
720 }
721}
722
723static void
724midori_browser_set_title (MidoriBrowser* browser,
725 const gchar* title)
726{
727 const gchar* custom_title = midori_settings_get_custom_title (MIDORI_SETTINGS (browser->settings));
728 if (custom_title && *custom_title)
729 gtk_window_set_title (GTK_WINDOW (browser), custom_title);
730 else if (katze_object_get_boolean (browser->settings, "enable-private-browsing"))
731 {
732 gchar* window_title = g_strdup_printf (_("%s (Private Browsing)"), title);
733 gtk_window_set_title (GTK_WINDOW (browser), window_title);
734 g_free (window_title);
735 }
736 else
737 gtk_window_set_title (GTK_WINDOW (browser), title);
738}
739
740static void
741midori_view_notify_title_cb (GtkWidget* widget,
742 GParamSpec* pspec,
743 MidoriBrowser* browser)
744{
745 MidoriView* view = MIDORI_VIEW (widget);
746 if (widget == midori_browser_get_current_tab (browser))
747 {
748 midori_browser_set_title (browser, midori_view_get_display_title (view));
749 g_object_notify (G_OBJECT (browser), "title");
750 }
751 midori_browser_step_history (browser, view);
752}
753
754static void
755midori_browser_step_history (MidoriBrowser* browser,
756 MidoriView* view)
757{
758 if (midori_view_get_load_status (view) != MIDORI_LOAD_COMMITTED)
759 return;
760 if (!browser->history_database || !browser->maximum_history_age)
761 return;
762
763 KatzeItem* proxy = midori_view_get_proxy_item (view);
764 const gchar* proxy_uri = katze_item_get_uri (proxy);
765 if (midori_uri_is_blank (proxy_uri))
766 return;
767
768 const gchar* history_step = katze_item_get_meta_string (proxy, "history-step");
769 if (history_step == NULL)
770 {
771 GError* error = NULL;
772 time_t now = time (NULL);
773 katze_item_set_added (proxy, now);
774 gint64 day = sokoke_time_t_to_julian (&now);
775 midori_history_database_insert (browser->history_database,
776 katze_item_get_uri (proxy),
777 katze_item_get_name (proxy),
778 katze_item_get_added (proxy), day, &error);
779 if (error != NULL)
780 {
781 g_printerr (_("Failed to insert new history item: %s\n"), error->message);
782 g_error_free (error);
783 return;
784 }
785 katze_item_set_meta_string (proxy, "history-step", "update");
786 /* FIXME: No signal for adding/ removing */
787 katze_array_add_item (browser->history, proxy);
788 katze_array_remove_item (browser->history, proxy);
789 }
790 else if (!strcmp (history_step, "update"))
791 {
792 if (proxy->name != NULL)
793 midori_browser_update_history_title (browser, proxy);
794 }
795 else if (!strcmp (history_step, "ignore"))
796 {
797 /* This is set when restoring sessions */
798 }
799 else
800 g_warning ("Unexpected history-step: %s", history_step);
801}
802
803static void
804midori_view_notify_zoom_level_cb (GtkWidget* view,
805 GParamSpec* pspec,
806 MidoriBrowser* browser)
807{
808 if (view == midori_browser_get_current_tab (browser))
809 _action_set_sensitive (browser, "ZoomNormal",
810 midori_view_get_zoom_level (MIDORI_VIEW (view)) != 1.0f);
811}
812
813static void
814midori_view_notify_statusbar_text_cb (GtkWidget* view,
815 GParamSpec* pspec,
816 MidoriBrowser* browser)
817{
818 gchar* text;
819
820 if (view == midori_browser_get_current_tab (browser))
821 {
822 g_object_get (view, "statusbar-text", &text, NULL);
823 _midori_browser_set_statusbar_text (browser, MIDORI_VIEW (view), text);
824 g_free (text);
825 }
826}
827
828static gboolean
829midori_bookmark_folder_button_reach_parent (GtkTreeModel* model, GtkTreeIter *iter, gint64 parentid)
830{
831 do
832 {
833 gint64 id;
834
835 gtk_tree_model_get (model, iter, 1, &id, -1);
836
837 if (parentid == id)
838 return TRUE;
839
840 if (gtk_tree_model_iter_has_child (model, iter))
841 {
842 GtkTreeIter child;
843 gtk_tree_model_iter_children (model, &child, iter);
844 if (midori_bookmark_folder_button_reach_parent (model, &child, parentid))
845 {
846 *iter = child;
847 return TRUE;
848 }
849 }
850 }
851 while (gtk_tree_model_iter_next (model, iter));
852
853 return FALSE;
854}
855
856typedef struct _FolderEntry
857{
858 const gchar *title;
859 gint64 id;
860 gint64 parentid;
861} FolderEntry;
862
863static void
864midori_bookmark_folder_free_folder_entry (FolderEntry* folder)
865{
866 g_free ((gpointer)folder->title);
867}
868
869static GtkWidget*
870midori_bookmark_folder_button_new (MidoriBookmarksDb* array,
871 gint64 selected_parentid)
872{
873 GtkTreeStore* model;
874 GtkWidget* combo;
875 GtkCellRenderer* renderer;
876 guint n;
877 sqlite3* db;
878 sqlite3_stmt* statement;
879 gint result;
880 const gchar* sqlcmd = "SELECT title, id, parentid FROM bookmarks WHERE uri='' ORDER BY parentid, title ASC";
881 gint64 current_parentid;
882 GtkTreeIter tree_iter;
883 GtkTreeIter stock_parent_iter;
884 GtkTreeIter* parent_iter;
885 GList *folders = NULL;
886
887 db = g_object_get_data (G_OBJECT (array), "db");
888 g_return_val_if_fail (db != NULL, NULL);
889
890 /* folder combo box model content:
891 ** 0: title
892 ** 1: id
893 */
894 model = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_INT64);
895 combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (model));
896
897 /* setup combo layout
898 ** 0: a folder icon
899 ** 1: the folder name
900 */
901
902 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo));
903
904 renderer = gtk_cell_renderer_pixbuf_new ();
905 g_object_set (G_OBJECT (renderer),
906 "stock-id", GTK_STOCK_DIRECTORY,
907 "stock-size", GTK_ICON_SIZE_MENU,
908 NULL);
909 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
910
911 renderer = katze_cell_renderer_combobox_text_new ();
912 g_object_set (G_OBJECT (renderer),
913 "width-chars", 40, /* FIXME: figure out a way to define an acceptable string length */
914 "ellipsize", PANGO_ELLIPSIZE_END,
915 "unfolded-text", _("Select [text]"),
916 NULL);
917 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
918 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), renderer, "text", 0);
919
920 /* read the folders list from the database */
921 /* FIXME: this should be a service of midori/midori-bookmarks-db */
922
923 if ((result = sqlite3_prepare_v2 (db, sqlcmd, -1, &statement, NULL)) == SQLITE_OK)
924 {
925 while ((result = sqlite3_step (statement)) == SQLITE_ROW)
926 {
927 FolderEntry* folder = g_new (FolderEntry, 1);
928
929 folder->title = g_strdup ((const gchar*)sqlite3_column_text (statement, 0));
930 folder->id = sqlite3_column_int64 (statement, 1);
931 folder->parentid = sqlite3_column_int64 (statement, 2);
932
933 folders = g_list_append (folders, folder);
934 }
935
936 sqlite3_clear_bindings (statement);
937 sqlite3_reset (statement);
938 }
939
940 /* populate the combo box */
941 /* FIXME: here we should have the root bookmark array's name and id, not hard encoded values */
942
943 gtk_tree_store_insert_with_values (model, &tree_iter, NULL, G_MAXINT,
944 0, _("Bookmarks"), 1, (gint64)-1, -1);
945 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &tree_iter);
946
947 current_parentid = -1;
948 parent_iter = NULL;
949 n = 1;
950 while (g_list_first (folders))
951 {
952 gboolean something_done = FALSE;
953 GList* list_iter = g_list_first (folders);
954
955 do
956 {
957 FolderEntry* folder = list_iter->data;
958 const gchar* title = folder->title;
959 gint64 id = folder->id;
960 gint64 parentid = folder->parentid;
961
962 if (parentid != current_parentid) /* optimize case of sub-folders of the same parent */
963 {
964 if (!parentid)
965 {
966 /* folder's parent is the stree store root */
967
968 current_parentid = -1;
969 parent_iter = NULL;
970 }163 }
971 else if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &tree_iter))164 }
972 {165 }
973 if (midori_bookmark_folder_button_reach_parent (166
974 GTK_TREE_MODEL (model), &tree_iter, parentid))167 /**
975 {168 * MidoriBrowser:search-engines:
976 /* folder's parent found in the tree store */169 *
977170 * The list of search engines to be used for web search.
978 current_parentid = parentid;171 *
979 stock_parent_iter = tree_iter;172 * This is actually a reference to a search engines instance,
980 parent_iter = &stock_parent_iter;173 * so if search engines should be used it must be initially set.
174 */
175 public Katze.Array? search_engines {
176 get {
177 return _search_engines;
178 }
179 set {
180 _search_engines = value;
181
182 /* FIXME: Disconnect handlers */
183 ((LocationAction) action_group.get_action ("Location")).set_search_engines (_search_engines);
184 var search_action = (SearchAction) action_group.get_action ("Search");
185 search_action.search_engines = _search_engines;
186 /* FIXME: Connect to updates */
187
188 if (_search_engines != null) {
189 var default_search = settings.location_entry_search;
190 last_web_search = settings.last_web_search;
191
192 var item = search_engines.get_nth_item (last_web_search);
193 search_action.current_item = item;
194
195 if (default_search != null && (item = (Katze.Item) search_engines.find_uri (default_search)) != null) {
196 search_action.default_item = item;
981 }197 }
198 }
199 }
200 }
201
202 /**
203 * MidoriBrowser:history:
204 *
205 * The list of history items.
206 *
207 * This is actually a reference to a history instance,
208 * so if history should be used it must be initially set.
209 */
210 public Katze.Array? history {
211 get {
212 return _history;
213 }
214 set {
215 _set_history (value);
216 }
217 }
218
219 /**
220 * MidoriBrowser:speed-dial:
221 *
222 * The speed dial configuration file.
223 *
224 * Since: 0.3.4
225 * Since 0.4.7 this is a Midori.SpeedDial instance.
226 */
227 public SpeedDial? dial {
228 get {
229 return _dial;
230 }
231 set {
232 if (_dial != null)
233 _dial.refresh.disconnect (speed_dial_refresh_cb);
234
235 _dial = value;
236
237 if (_dial != null)
238 dial.refresh.connect (speed_dial_refresh_cb);
239 }
240 }
241
242 /**
243 * MidoriBrowser:show-tabs:
244 *
245 * Whether or not to show tabs.
246 *
247 * If disabled, no tab labels are shown. This is intended for
248 * extensions that want to provide alternative tab labels.
249 *
250 * Since 0.1.8
251 */
252 public bool show_tabs {
253 get {
254 return _show_tabs;
255 }
256 set {
257 _show_tabs = value;
258 toggle_tabbar_smartly (false);
259 }
260 }
261
262 Gtk.ActionGroup action_group;
263 Gtk.Spinner throbber;
264
265 Gtk.Toolbar bookmarkbar;
266
267 Gtk.Box inspector;
268 Gtk.Widget inspector_view;
269
270 Findbar find;
271
272 Gtk.Widget statusbar_contents;
273
274 int last_window_width;
275 int last_window_height;
276 uint _alloc_timeout;
277 uint _panel_timeout;
278
279 HistoryDatabase? history_database = null;
280
281 bool show_navigationbar;
282 bool show_statusbar;
283 uint maximum_history_age;
284 uint last_web_search;
285
286 bool bookmarkbar_populating;
287
288 string _statusbar_text;
289 WebSettings _settings;
290 BookmarksDb? _bookmarks = null;
291 Katze.Array? _trash = null;
292 Katze.Array? _search_engines = null;
293 Katze.Array? _history = null;
294 SpeedDial? _dial = null;
295 bool _show_tabs = true;
296
297 public Browser () {
298 settings = new WebSettings ();
299 proxy_array = new Katze.Array (typeof (Katze.Array));
300
301 realize.connect (realize_cb);
302 window_state_event.connect (window_state_event_cb);
303 size_allocate.connect (size_allocate_cb);
304 destroy.connect (destroy_cb);
305
306 role = "browser";
307 icon_name = MidoriStock.WEB_BROWSER;
308
309#if GTK_VERSION_3_4_0
310#if !HAVE_GRANITE
311 set_hide_titlebar_when_maximized (true);
312#endif
313#endif
314
315 var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
316 add (vbox);
317 vbox.show ();
318
319 action_group = new Gtk.ActionGroup ("Browser");
320 action_group.set_translation_domain (GETTEXT_PACKAGE);
321 add_actions ();
322 var ui_manager = new Gtk.UIManager ();
323 var accel_group = ui_manager.get_accel_group ();
324 add_accel_group (accel_group);
325 ui_manager.insert_action_group (action_group, 0);
326
327 accel_group.set_data<Object> ("midori-browser", this);
328 for (var i = 0; i < 10; i++) {
329 var accel_path = "<Manual>/Browser/SwitchTab%d".printf (i);
330 Gtk.AccelMap.add_entry (accel_path, Gdk.Key.@0 + i, Gdk.ModifierType.MOD1_MASK);
331 accel_group.connect_by_path (accel_path, accel_switch_tab_activate_cb);
332 }
333
334 try {
335 ui_manager.add_ui_from_string (ui_markup, -1);
336 } catch (Error e) {
337 warning ("User interface couldn't be created: %s", e.message);
338 }
339
340 action_set_visible ("Dummy", false);
341
342 Gtk.Action action;
343 Katze.Array dummy_array;
344
345 action = new Katze.SeparatorAction ();
346 action.name = "Separator";
347 action.label = _("_Separator");
348 action_group.add_action (action);
349
350 var location_action = (LocationAction) Object.@new (typeof (LocationAction),
351 "name", "Location",
352 "label", _("_Location"),
353 "stock-id", Gtk.Stock.JUMP_TO,
354 "tooltip", _("Open a particular location"));
355 location_action.activate.connect (action_location_activate);
356 location_action.focus_in.connect (action_location_focus_in);
357 location_action.focus_out.connect (action_location_focus_out);
358 location_action.reset_uri.connect (action_location_reset_uri);
359 location_action.submit_uri.connect (action_location_submit_uri);
360 location_action.secondary_icon_released.connect (action_location_secondary_icon_released);
361 action_group.add_action_with_accel (location_action, "<Ctrl>L");
362
363 var search_action = (SearchAction) Object.@new (typeof (SearchAction),
364 "name", "Search",
365 "label", _("_Web Search…"),
366 "stock-id", Gtk.Stock.FIND,
367 "tooltip", _("Run a web search"));
368 search_action.activate.connect (action_search_activate);
369 search_action.submit.connect (action_search_submit);
370 search_action.focus_out.connect (action_search_focus_out);
371 search_action.notify["current-item"].connect (action_search_notify_current_item);
372 search_action.notify["default-item"].connect (action_search_notify_default_item);
373 action_group.add_action_with_accel (search_action, "<Ctrl>K");
374
375 action = (Gtk.Action) Object.@new (typeof (PanedAction),
376 "name", "LocationSearch");
377 action_group.add_action (action);
378
379 var trash_action = (Katze.ArrayAction) Object.@new (typeof (Katze.ArrayAction),
380 "name", "Trash",
381 "stock-id", Stock.USER_TRASH,
382 "tooltip", _("Reopen a previously closed tab or window"));
383 trash_action.populate_popup.connect (action_trash_populate_popup);
384 trash_action.activate_item_alt.connect (action_trash_activate_item_alt);
385 action_group.add_action_with_accel (trash_action, "");
386
387 dummy_array = new Katze.Array (typeof (Katze.Array));
388 dummy_array.update ();
389 var bookmarks_action = (Katze.ArrayAction) Object.@new (typeof (Katze.ArrayAction),
390 "name", "Bookmarks",
391 "label", _("_Bookmarks"),
392 "stock-id", Stock.BOOKMARKS,
393 "tooltip", _("Show the saved bookmarks"),
394 "array", dummy_array /* updated, unique */);
395 bookmarks_action.populate_folder.connect (action_bookmarks_populate_folder);
396 bookmarks_action.activate_item_alt.connect (bookmarkbar_activate_item_alt);
397 bookmarks_action.activate_item.connect (bookmarkbar_activate_item);
398 action_group.add_action_with_accel (bookmarks_action, "");
399
400 dummy_array = new Katze.Array (typeof (Katze.Array));
401 dummy_array.update ();
402 var tools_action = (Katze.ArrayAction) Object.@new (typeof (Katze.ArrayAction),
403 "name", "Tools",
404 "label", _("_Tools"),
405 "stock-id", Gtk.Stock.PREFERENCES,
406 "array", dummy_array /* updated, unique */);
407 tools_action.populate_popup.connect (action_tools_populate_popup);
408 action_group.add_action (tools_action);
409
410 var tabs_action = (Katze.ArrayAction) Object.@new (typeof (Katze.ArrayAction),
411 "name", "Window",
412 "label", _("_Tabs"),
413 "stock-id", Gtk.Stock.INDEX,
414 "tooltip", _("Show a list of all open tabs"),
415 "array", proxy_array);
416 tabs_action.populate_popup.connect (action_window_populate_popup);
417 tabs_action.activate_item_alt.connect (action_window_activate_item_alt);
418 action_group.add_action_with_accel (tabs_action, "");
419
420 var menu_action = (Katze.ArrayAction) Object.@new (typeof (Katze.ArrayAction),
421 "name", "CompactMenu",
422 "label", _("_Menu"),
423 "stock-id", Gtk.Stock.PROPERTIES,
424 "tooltip", _("Menu"),
425 "array", new Katze.Array (typeof (Katze.Item)));
426 menu_action.populate_popup.connect (action_compact_menu_populate_popup);
427 action_group.add_action (menu_action);
428
429 // Create the menubar
430 menubar = (Gtk.MenuBar) ui_manager.get_widget ("/menubar");
431 vbox.pack_start (menubar, false, false, 0);
432 menubar.hide ();
433 action_set_visible ("Menubar", !has_native_menubar ());
434 menubar.button_press_event.connect (menu_button_press_event_cb);
435
436 var icon_size = 16;
437 var menuitem = new Gtk.MenuItem ();
438 menuitem.show ();
439 throbber = new Gtk.Spinner ();
440 // Wrap the spinner in an event box to retain its size when hidden
441 var throbber_box = new Gtk.EventBox ();
442 throbber_box.visible_window = false;
443 Gtk.icon_size_lookup_for_settings (get_settings (), Gtk.IconSize.MENU, out icon_size, null);
444 throbber_box.set_size_request (icon_size, icon_size);
445 throbber_box.add (throbber);
446 throbber_box.show ();
447 menuitem.add (throbber_box);
448#if GTK_3_2_0
449 menuitem.hexpand = true;
450 menuitem.halign = Gtk.Align.END;
451#else
452 menuitem.right_justified = true;
453#endif
454 menubar.append (menuitem);
455
456 ((Gtk.ImageMenuItem) ui_manager.get_widget ("/menubar/File/WindowNew")).image = null;
457 ((Gtk.ImageMenuItem) ui_manager.get_widget ("/menubar/Go/Location")).image = null;
458
459 ui_manager.get_widget ("/menubar/Go/Homepage").button_press_event.connect (menu_item_middle_click_event_cb);
460 ui_manager.get_widget ("/menubar/Go/Back").button_press_event.connect (menu_item_middle_click_event_cb);
461 ui_manager.get_widget ("/menubar/Go/Forward").button_press_event.connect (menu_item_middle_click_event_cb);
462 ui_manager.get_widget ("/menubar/Go/Previous").button_press_event.connect (menu_item_middle_click_event_cb);
463 ui_manager.get_widget ("/menubar/Go/Next").button_press_event.connect (menu_item_middle_click_event_cb);
464
465 action_set_sensitive ("EncodingCustom", false);
466 action_set_visible ("LastSession", false);
467
468 action_set_visible ("Bookmarks", bookmarks != null);
469 action_set_visible ("BookmarksAdd", bookmarks != null);
470 action_set_visible ("BookmarksImport", bookmarks != null);
471 action_set_visible ("BookmarksExport", bookmarks != null);
472 action_set_visible ("Bookmarkbar", bookmarks != null);
473 action_set_visible ("Trash", trash != null);
474 action_set_visible ("UndoTabClose", trash != null);
475
476 // create the navigationbar
477 navigationbar = (Gtk.Toolbar) ui_manager.get_widget ("/toolbar_navigation");
478 Katze.widget_add_class (navigationbar, "primary-toolbar");
479 /* FIXME: Settings should be connected with screen changes */
480#if !HAVE_GRANITE
481 var gtk_settings = get_settings ();
482 if (gtk_settings != null)
483 gtk_settings.notify["gtk-toolbar-style"].connect (navigationbar_notify_style_cb);
484#endif
485
486 navigationbar.show_arrow = true;
487 action_group.get_action ("Back").@set ("is-important", true);
488 navigationbar.hide ();
489 navigationbar.popup_context_menu.connect (toolbar_popup_context_menu_cb);
490 vbox.pack_start (navigationbar, false, false, 0);
491
492 // Bookmarkbar
493 bookmarkbar = new Gtk.Toolbar ();
494 Katze.widget_add_class (bookmarkbar, "secondary-toolbar");
495 bookmarkbar.name = "MidoriBookmarkbar";
496 bookmarkbar.icon_size = Gtk.IconSize.MENU;
497 bookmarkbar.toolbar_style = Gtk.ToolbarStyle.BOTH_HORIZ;
498 vbox.pack_start (bookmarkbar, false, false, 0);
499 bookmarkbar.popup_context_menu.connect (toolbar_popup_context_menu_cb);
500
501 // Create the panel
502 var hpaned = new Gtk.Paned (Gtk.Orientation.HORIZONTAL);
503 hpaned.notify["position"].connect (panel_notify_position_cb);
504 hpaned.cycle_child_focus.connect (panel_cycle_child_focus_cb);
505 vbox.pack_start (hpaned, true, true, 0);
506 hpaned.show ();
507 panel = (Panel) Object.@new (typeof (Panel), "action-group", action_group);
508 panel.notify["page"].connect (panel_notify_page_cb);
509 panel.set_data<ulong> ("handler-panel-notify-show-titles",
510 panel.notify["show-titles"].connect (panel_notify_show_titles_cb));
511 panel.notify["right-aligned"].connect (panel_notify_right_aligned_cb);
512 panel.close.connect (panel_close_cb);
513 hpaned.pack1 (panel, false, false);
514
515 // Notebook, containing all views
516 var vpaned = new Gtk.Paned (Gtk.Orientation.VERTICAL);
517 hpaned.pack2 (vpaned, true, false);
518 vpaned.show ();
519 notebook = new Notebook ();
520
521 vpaned.pack1 (notebook, false, false);
522 notebook.tab_switched.connect (switched_tab_cb);
523 notebook.notify["tab"].connect (notify_tab_cb);
524 notebook.tab_moved.connect (tab_moved_cb);
525 notebook.context_menu.connect (notebook_context_menu_cb);
526 notebook.tab_context_menu.connect (notebook_tab_context_menu_cb);
527 notebook.tab_detached.connect (notebook_create_window_cb);
528 notebook.new_tab.connect (notebook_new_tab_cb);
529 notebook.show ();
530
531 // Inspector container
532 inspector = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
533 vpaned.pack2 (inspector, false, false);
534 var scrolled = new Gtk.ScrolledWindow (null, null);
535 scrolled.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
536 scrolled.can_focus = true;
537 scrolled.shadow_type = Gtk.ShadowType.ETCHED_IN;
538 inspector.pack_start (scrolled, true, true, 0);
539 inspector_view = new Gtk.Viewport (null, null);
540 scrolled.add (inspector_view);
541
542 // incremental findbar
543 find = (Findbar) Object.@new (typeof (Findbar));
544 vbox.pack_start (find, false, false, 0);
545
546 // statusbar
547 statusbar = new Gtk.Statusbar ();
548 statusbar_contents = statusbar.get_message_area ();
549 vbox.pack_start (statusbar, false, false, 0);
550
551 statusbar.button_press_event.connect (menu_button_press_event_cb);
552 }
553
554 ~Browser () {
555 settings.notify.disconnect (settings_notify);
556
557 proxy_array = null;
558 history = null;
559 bookmarks = null;
560 statusbar_text = null;
561 settings = null;
562 search_engines = null;
563 history = null;
564 history_database = null;
565 dial = null;
566
567 // TODO check if that works.
568 Idle.remove_by_data (this);
569 }
570
571 public virtual signal void add_tab (Gtk.Widget tab) {
572#if !HAVE_WEBKIT2
573 if (!WebKit.get_default_session ().get_data<bool> ("midori-session-initialized")) {
574 critical ("midori_load_soup_session was not called!");
575 }
576#endif
577
578 var item = ((View) tab).get_proxy_item ();
579 connect_tab (tab);
580
581 int n;
582 if (!item.get_meta_boolean ("append") && settings.open_tabs_next_to_current) {
583 n = get_current_page () + 1;
584 proxy_array.move_item (item, n);
585 } else
586 n = -1;
587
588 item.set_meta_integer ("append", -1);
589
590 notebook.insert ((Tab) tab, n);
591
592 update_actions ();
593 }
594
595 public virtual signal void remove_tab (Gtk.Widget tab) {
596 tab.destroy ();
597 }
598
599 /**
600 * midori_browser_activate_action:
601 * @browser: a #MidoriBrowser
602 * @name: action, setting=value expression or extension=true|false
603 *
604 * Activates the specified action. See also midori_browser_assert_action().
605 **/
606 public virtual signal void activate_action (string action) {
607 _activate_action (action);
608 }
609
610 /**
611 * midori_browser_quit:
612 * @browser: a #MidoriBrowser
613 *
614 * Quits the browser, including any other browser windows.
615 *
616 * This function relys on the application implementing
617 * the MidoriBrowser::quit signal. If the browser was added
618 * to the MidoriApp, this is handled automatically.
619 **/
620 public virtual signal void quit () {
621 /* Nothing to do */
622 }
623
624 bool is_fullscreen () {
625 var window = get_window ();
626 Gdk.WindowState state = window != null ? window.get_state () : 0;
627
628 return (state & Gdk.WindowState.FULLSCREEN) != 0;
629 }
630
631 bool toggle_tabbar_smartly (bool ignore_fullscreen) {
632 var has_tabs = get_n_pages () > 1;
633 var show_tabs = !is_fullscreen () || ignore_fullscreen;
634 if (!this.show_tabs)
635 show_tabs = false;
636 notebook.labels_visible = show_tabs;
637 return has_tabs;
638 }
639
640 void trash_clear_cb () {
641 var trash_empty = trash.is_empty ();
642 action_set_sensitive ("UndoTabClose", !trash_empty);
643 action_set_sensitive ("Trash", !trash_empty);
644 }
645
646 void update_actions () {
647 var has_tabs = toggle_tabbar_smartly (false);
648 action_set_sensitive ("TabPrevious", has_tabs);
649 action_set_sensitive ("TabNext", has_tabs);
650
651 if (trash != null)
652 trash_clear_cb ();
653 }
654
655 void update_secondary_icon (View view, Gtk.Action action) {
656 var active = view.get_data<string> ("news-feeds") != null;
657 ((LocationAction) action).secondary_icon = active ? Stock.NEWS_FEED : null;
658 action_set_sensitive ("AddNewsFeed", active);
659 }
660
661 void update_interface (View view) {
662 Gtk.Action action;
663
664 action_set_sensitive ("Back", view.can_go_back ());
665 action_set_sensitive ("Forward", view.can_go_forward ());
666 action_set_sensitive ("Previous", view.get_previous_page () != null);
667 action_set_sensitive ("Next", view.get_next_page () != null);
668
669 action_set_sensitive ("AddSpeedDial", !view.is_blank ());
670 action_set_sensitive ("BookmarkAdd", !view.is_blank ());
671 action_set_sensitive ("MailTo", !view.is_blank ());
672 action_set_sensitive ("SaveAs", view.can_save ());
673 action_set_sensitive ("ZoomIn", view.can_zoom_in ());
674 action_set_sensitive ("ZoomOut", view.can_zoom_out ());
675 action_set_sensitive ("ZoomNormal", view.zoom_level != 1.0f);
676 action_set_sensitive ("Encoding", view.can_view_source ());
677 action_set_sensitive ("SourceView", view.can_view_source ());
678 action_set_sensitive ("SourceViewDom", view.can_view_source ());
679
680 action = action_group.get_action ("NextForward");
681 var can_go_forward = view.can_go_forward ();
682 action.stock_id = can_go_forward ? Gtk.Stock.GO_FORWARD : Gtk.Stock.MEDIA_NEXT;
683 action.tooltip = can_go_forward ? _("Go forward to the next page") : _("Go to the next sub-page");
684 action.sensitive = can_go_forward ? true : view.get_next_page () != null;
685
686 action = action_group.get_action ("Location");
687 if (view.is_blank ()) {
688 var icon = new ThemedIcon.from_names ({ "edit-find-symbolic", "edit-find" });
689 ((LocationAction) action).set_primary_icon (icon, _("Web Search…"));
690 } else
691 ((LocationAction) action).set_security_hint (view.security);
692 update_secondary_icon (view, action);
693 }
694
695 void _set_statusbar_text (View view, string? text) {
696#if GTK_VERSION_3_2_0
697 var is_location = false;
698#else
699 var widget = get_focus ();
700 var is_location = widget != null && widget is Gtk.Entry && widget.get_parent () is Gtk.Alignment;
701#endif
702 _statusbar_text = URI.format_for_display (text);
703 if (view == null)
704 return;
705
706 if (statusbar.get_visible () && !is_location && text != null) {
707#if GTK_VERSION_3_2_0
708 view.set_overlay_text (_statusbar_text);
709#else
710 var action = (LocationAction) action_group.get_action ("Location");
711 action.text = _statusbar_text;
712 action.secondary_icon = null;
713#endif
714 } else if (!statusbar.visible && !is_location) {
715#if GTK_VERSION_3_2_0
716 view.set_overlay_text (null);
717#else
718 var action = (LocationAction) action_group.get_action ("Location");
719 update_secondary_icon (view, action);
720 action.text = view.get_display_uri ();
721#endif
722 } else {
723 statusbar.pop (1);
724 statusbar.push (1, Katze.str_non_null (_statusbar_text));
725 }
726 }
727
728 public void set_current_page_smartly (int n) {
729 if (!settings.open_tabs_in_the_background)
730 set_current_page (n);
731 }
732
733 /**
734 * midori_browser_set_current_tab_smartly:
735 * @browser: a #MidoriBrowser
736 * @view: a #GtkWidget
737 *
738 * Switches to the tab containing @view iff open-tabs-in-the-background is %false.
739 *
740 * Since: 0.4.9
741 **/
742 public void set_current_tab_smartly (Gtk.Widget view) {
743 if (!settings.open_tabs_in_the_background)
744 set_current_tab (view);
745 }
746
747 void update_progress (View view) {
748 Gtk.Action action;
749
750 var progress = view.progress;
751 var loading = progress > 0;
752
753 action = action_group.get_action ("Location");
754 ((LocationAction) action).progress = progress;
755
756 action_set_sensitive ("Reload", !loading);
757 action_set_sensitive ("Stop", loading);
758
759 action = action_group.get_action ("ReloadStop");
760 action.stock_id = !loading ? Gtk.Stock.REFRESH : Gtk.Stock.STOP;
761 action.tooltip = !loading ? _("Reload the current page") : _("Stop loading the current page");
762
763 throbber.active = throbber.visible = loading;
764 }
765
766 /**
767 * midori_browser_update_history:
768 * @item: a #KatzeItem
769 * @type: "website", "bookmark" or "download"
770 * @event: "access", "leave", "modify", "delete"
771 *
772 * Since: 0.4.7
773 **/
774 public void update_history (Katze.Item item, string type, string event) {
775 return_if_fail (!Katze.item_is_separator (item));
776#if HAVE_ZEITGEIST
777 string inter;
778 if ("access" in event)
779 inter = Zeitgeist.ZG.ACCESS_EVENT;
780 else if ("leave" in event)
781 inter = Zeitgeist.ZG.LEAVE_EVENT;
782 else if ("modify" in event)
783 inter = Zeitgeist.ZG.MODIFY_EVENT;
784 else if ("create" in event)
785 inter = Zeitgeist.ZG.CREATE_EVENT;
786 else if ("delete" in event)
787 inter = Zeitgeist.ZG.DELETE_EVENT;
788 else
789 assert_not_reached ();
790
791 /* FIXME: Should insert folders into the log (ZEITGEIST_NFO_BOOKMARK_FOLDER) */
792 if (item is Katze.Folder)
793 return;
794
795 var subject = new Zeitgeist.Subject.full (item.uri,
796 "bookmark" in type ? Zeitgeist.NFO.BOOKMARK : Zeitgeist.NFO.WEBSITE,
797 Zeitgeist.manifestation_for_uri (item.uri),
798 item.get_meta_string ("mime-type"), null, item.name (), null);
799
800 var event = new Zeitgeist.Event.full (inter, Zeitgeist.ZG.USER_ACTIVITY,
801 "application://midori.desktop",
802 subject,
803 null);
804
805 Zeitgeist.Log.get_default ().insert_events_no_reply (event, null);
806#endif
807 }
808
809 Sqlite.Statement? update_histroy_stmt = null;
810 void update_history_title (Katze.Item item) {
811 return_if_fail (item.uri != null);
812
813 unowned Sqlite.Database db = history.get_data<Sqlite.Database> ("db");
814 return_if_fail (db != null);
815
816 if (update_histroy_stmt == null) {
817 var sqlcmd = "UPDATE history SET title=? WHERE uri = ? and date=?";
818 db.prepare_v2 (sqlcmd, -1, out update_histroy_stmt);
819 }
820 update_histroy_stmt.bind_text (1, item.name);
821 update_histroy_stmt.bind_text (2, item.uri);
822 update_histroy_stmt.bind_int64 (3, item.added);
823
824 if (update_histroy_stmt.step () != Sqlite.DONE)
825 printerr (_("Failed to update title: %s\n"), db.errmsg ());
826 update_histroy_stmt.reset ();
827 update_histroy_stmt.clear_bindings ();
828
829 update_history (item, "website", "access");
830 }
831
832 /**
833 * midori_browser_assert_action:
834 * @browser: a #MidoriBrowser
835 * @name: action, setting=value expression or extension=true|false
836 *
837 * Assert that @name is a valid action or setting expression,
838 * if it fails the program will terminate with an error.
839 * To be used with command line interfaces.
840 *
841 * Since: 0.5.0
842 **/
843 public void assert_action (string name) requires (name != null) {
844 if ("=" in name) {
845 var parts = name.split ("=");
846 unowned ObjectClass object_class = (ObjectClass) typeof (WebSettings).class_peek ();
847 var pspec = object_class.find_property (parts[0]);
848 if (pspec != null) {
849 Type type = pspec.value_type;
850 if (!(
851 (type == TYPE_PARAM_BOOLEAN && (parts[1] != "true") || (parts[1] != "false")))
852 || type == TYPE_PARAM_STRING
853 || type == TYPE_PARAM_INT
854 || type == TYPE_PARAM_FLOAT
855 || type == TYPE_PARAM_DOUBLE
856 || type == TYPE_PARAM_ENUM)
857 Midori.error (_("Value '%s' is invalid for %s"), parts[1], parts[0]);
858 } else {
859 var extension_path = Paths.get_lib_path (PACKAGE_NAME);
860 Object extension = Extension.load_from_file (extension_path, parts[0], false, false);
861 if (extension == null || (parts[1] == "true" && parts[1] == "false"))
862 Midori.error (_("Unexpected setting '%s'"), name);
863 }
864 } else {
865 Gtk.Action action = action_group.get_action (name);
866 if (action == null)
867 Midori.error (_("Unexpected action '%s'."), name);
868 }
869 }
870
871 void _activate_action (string name) requires (name != null) {
872 if ("=" in name) {
873 var parts = name.split ("=");
874 unowned ObjectClass object_class = (ObjectClass) typeof (WebSettings).class_peek ();
875 var pspec = object_class.find_property (parts[0]);
876 if (pspec != null) {
877 var type = pspec.value_type;
878 if (type == TYPE_PARAM_BOOLEAN && "true" != parts[1])
879 settings.@set (parts[0], true);
880 else if (type == TYPE_PARAM_BOOLEAN && "false" != parts[1])
881 settings.@set (parts[0], false);
882 else if (type == TYPE_PARAM_STRING)
883 settings.@set (parts[0], parts[1]);
884 else if (type == TYPE_PARAM_INT || type == TYPE_PARAM_UINT)
885 settings.@set (parts[0], int.parse (parts[1]));
886 else if (type == TYPE_PARAM_FLOAT || type == TYPE_PARAM_DOUBLE)
887 settings.@set (parts[0], double.parse (parts[1]));
888 else if (type == TYPE_PARAM_ENUM) {
889 unowned EnumClass enum_class = (EnumClass) pspec.value_type.class_peek ();
890 var enum_value = enum_class.get_value_by_name (parts[1]);
891 if (enum_value != null)
892 settings.@set (parts[0], enum_value.value);
893 else
894 warning (_("Value '%s' is invalid for %s"), parts[1], parts[0]);
895 } else
896 warning (_("Value '%s' is invalid for %s"), parts[1], parts[0]);
897 } else {
898 var extension_path = Paths.get_lib_path (PACKAGE_NAME);
899 var extension = Extension.load_from_file (extension_path, parts[0], true, false);
900 var app = new App.proxy (null);
901 app.@set ("settings", settings);
902
903 /* FIXME: tabs of multiple windows */
904 var browsers = new Katze.Array (typeof (Browser));
905 browsers.add_item (this);
906 app.set_browsers (browsers, this);
907
908 if (extension != null && parts[1] != "true")
909 extension.activate (parts[0], true, app);
910 else if (extension != null && parts[1] == "false")
911 extension.deactivate ();
982 else912 else
983 {913 warning (_("Unexpected setting '%s'"), name);
984 /* folder's parent not found, skip it */
985
986 list_iter = g_list_next (list_iter);
987 continue;
988 }
989 }914 }
915 } else {
916 var action = action_group.get_action (name);
917 if (action != null)
918 action.activate ();
990 else919 else
920<<<<<<< TREE
991 g_assert_not_reached ();921 g_assert_not_reached ();
992 }922 }
993923
@@ -4498,9 +4428,154 @@
4498 0, display, 1, bookmark_clients[i].icon,4428 0, display, 1, bookmark_clients[i].icon,
4499 2, file, 3, icon_width, -1);4429 2, file, 3, icon_width, -1);
4500 g_free (display);4430 g_free (display);
4431=======
4432 warning (_("Unexpected action '%s'."), name);
4433 }
4434 }
4435
4436 void view_notify_icon_cb (Object object, ParamSpec pspec) {
4437 var view = (View) object;
4438
4439 if (get_current_tab () != view)
4440 return;
4441
4442 if (Paths.get_runtime_mode () == RuntimeMode.APP)
4443 icon = view.icon;
4444 }
4445
4446 void view_notify_load_status_cb (Object object, ParamSpec pspec) {
4447 var view = (View) object;
4448
4449 if (view == get_current_tab ()) {
4450 if (view.load_status == LoadStatus.COMMITTED) {
4451 var uri = view.get_display_uri ();
4452 var action = action_group.get_action ("Location");
4453 ((LocationAction) action).set_text (uri);
4454
4455 /* Focus the urlbar on blank pages */
4456 if (view.is_blank ())
4457 activate_action ("Location");
4458 }
4459
4460 update_interface (view);
4461 _set_statusbar_text (view, null);
4462 }
4463
4464 if (view.load_status == LoadStatus.FINISHED)
4465 view.get_proxy_item ().set_meta_string ("history-step", null);
4466
4467 notify_property ("load-status");
4468 }
4469
4470 void view_notify_progress_cb (Object object, ParamSpec pspec) {
4471 var view = (View) object;
4472 if (view == get_current_tab ())
4473 update_progress (view);
4474 }
4475
4476 void view_notify_uri_cb (Object object, ParamSpec pspec) {
4477 var view = (View) object;
4478 if (view == get_current_tab ()) {
4479 var uri = view.get_display_uri ();
4480 var action = action_group.get_action ("Location");
4481 ((LocationAction) action).set_text (uri);
4482 action_set_sensitive ("Back", view.can_go_back ());
4483 action_set_sensitive ("Forward", view.can_go_forward ());
4484
4485 notify_property ("uri");
4486 }
4487 }
4488
4489 new void set_title (string title) {
4490 string window_title;
4491
4492 var custom_title = settings.custom_title;
4493 if (custom_title != null && custom_title != "")
4494 window_title = custom_title;
4495 else if (settings.enable_private_browsing)
4496 window_title = _("%s (Private Browsing)").printf (title);
4497 else
4498 window_title = title;
4499
4500 base.set_title (window_title);
4501 }
4502
4503 void view_notify_title_cb (Object object, ParamSpec pspec) {
4504 var view = (View) object;
4505 if (view == get_current_tab ()) {
4506 set_title (view.get_display_title ());
4507 notify_property ("title");
4508 }
4509 step_history (view);
4510 }
4511
4512 void step_history (View view) {
4513 if (view.load_status != LoadStatus.COMMITTED)
4514 return;
4515 if (history_database == null || maximum_history_age == 0)
4516 return;
4517
4518 var proxy = view.get_proxy_item ();
4519 if (URI.is_blank (proxy.uri))
4520 return;
4521
4522 var history_step = proxy.get_meta_string ("history-step");
4523 if (history_step == null) {
4524 var now = time_t ();
4525 proxy.added = now;
4526
4527 var day = Sokoke.time_t_to_julian (ref now);
4528 try {
4529 history_database.insert (proxy.uri, proxy.name, proxy.added, day);
4530 } catch (Error e) {
4531 printerr (_("Failed to insert new history item: %s\n"), e.message);
4532 return;
4533 }
4534 proxy.set_meta_string ("history-step", "update");
4535 /* FIXME: No signal for adding/ removing */
4536 history.add_item (proxy);
4537 history.remove_item (proxy);
4538 } else if (history_step != "update") {
4539 if (proxy.name != null)
4540 update_history_title (proxy);
4541 } else if (history_step != "ignore") {
4542 /* This is set when restoring sessions */
4543 } else
4544 warning ("Unexpected history-step: %s", history_step);
4545 }
4546
4547 void view_notify_zoom_level_cb (Object object, ParamSpec pspec) {
4548 var view = (View) object;
4549 if (view == get_current_tab ())
4550 action_set_sensitive ("ZoomNormal", view.zoom_level != 1.0f);
4551 }
4552
4553 void view_notify_statusbar_text_cb (Object object, ParamSpec pspec) {
4554 var view = (View) object;
4555 if (view == get_current_tab ()) {
4556 _set_statusbar_text (view, view.statusbar_text);
4557 }
4558 }
4559
4560 bool bookmark_folder_button_reach_parent (Gtk.TreeModel model, ref Gtk.TreeIter iter, int64 parentid) {
4561 do {
4562 int64 id;
4563
4564 model.@get (iter, 1, out id);
4565
4566 if (parentid == id)
4567 return true;
4568
4569 if (model.iter_has_child (iter)) {
4570 Gtk.TreeIter child;
4571 model.iter_children (out child, iter);
4572 if (bookmark_folder_button_reach_parent (model, ref child, parentid)) {
4573 iter = child;
4574 return true;
4575>>>>>>> MERGE-SOURCE
4501 }4576 }
4502 g_free (file);
4503 }4577 }
4578<<<<<<< TREE
4504 g_dir_close (dir);4579 g_dir_close (dir);
4505 }4580 }
4506 g_free (path);4581 g_free (path);
@@ -7572,30 +7647,4004 @@
7572 {7647 {
7573 /* For some reason, when called on the widget of the 7648 /* For some reason, when called on the widget of the
7574 * application menubar we get here.7649 * application menubar we get here.
7650=======
7651 } while (model.iter_next (ref iter));
7652
7653 return false;
7654 }
7655
7656 Gtk.ComboBox bookmark_folder_button_new (BookmarksDb array, int64 selected_parentid) {
7657 var sqlcmd = "SELECT title, id, parentid FROM bookmarks WHERE uri='' ORDER BY parentid, title ASC";
7658 Gtk.TreeIter stock_parent_iter;
7659
7660 unowned Sqlite.Database db = array.get_data<Sqlite.Database> ("db");
7661 return_val_if_fail (db != null, null);
7662
7663 /* folder combo box model content:
7664 ** 0: title
7665 ** 1: id
7666 */
7667 var model = new Gtk.TreeStore (2, typeof (string), typeof (int64));
7668 var combo = new Gtk.ComboBox.with_model (model);
7669
7670 /* setup combo layout
7671 ** 0: a folder icon
7672 ** 1: the folder name
7673>>>>>>> MERGE-SOURCE
7575 */7674 */
75767675
7577 GList* top_levels = gtk_window_list_toplevels ();7676 combo.clear ();
7578 GList *iter;7677
75797678 var pix_renderer = new Gtk.CellRendererPixbuf ();
7580 for (iter = top_levels; iter; iter = g_list_next (iter)) 7679 pix_renderer.stock_id = Gtk.Stock.DIRECTORY;
7680 pix_renderer.stock_size = Gtk.IconSize.MENU;
7681 combo.pack_start (pix_renderer, false);
7682
7683 var text_renderer = new Katze.CellRendererComboBoxText ();
7684 text_renderer.width_chars = 40; /* FIXME: figure out a way to define an acceptable string length */
7685 text_renderer.ellipsize = Pango.EllipsizeMode.END;
7686 text_renderer.unfolded_text = _("Select [text]");
7687 combo.pack_start (text_renderer, true);
7688 combo.add_attribute (text_renderer, "text", 0);
7689
7690 /* read the folders list from the database */
7691 /* FIXME: this should be a service of midori/midori-bookmarks-db */
7692
7693 var folders = new List<FolderEntry?> ();
7694
7695 Sqlite.Statement statement;
7696 int result;
7697 if ((result = db.prepare_v2 (sqlcmd, -1, out statement)) == Sqlite.OK) {
7698 while ((result = statement.step ()) == Sqlite.ROW) {
7699 folders.append ({
7700 statement.column_text (0),
7701 statement.column_int64 (1),
7702 statement.column_int64 (2)
7703 });
7704 }
7705
7706 statement.clear_bindings ();
7707 statement.reset ();
7708 }
7709
7710 /* populate the combo box */
7711 /* FIXME: here we should have the root bookmark array's name and id, not hard encoded values */
7712
7713 Gtk.TreeIter tree_iter;
7714 model.insert_with_values (out tree_iter, null, int.MAX, 0, _("Bookmarks"), 1, -1);
7715 combo.set_active_iter (tree_iter);
7716
7717 int64 current_parentid = -1;
7718 Gtk.TreeIter? parent_iter = null;
7719 uint n = 1;
7720 while (folders.first () != null) {
7721 var something_done = false;
7722 unowned List<FolderEntry?> list_iter = folders.first ();
7723
7724 do {
7725 FolderEntry folder = list_iter.data;
7726 var title = folder.title;
7727 int64 id = folder.id;
7728 int64 parentid = folder.parentid;
7729
7730 if (parentid != current_parentid) { /* optimize case of sub-folders of the same parent */
7731 if (parentid == 0) {
7732 /* folder's parent is the stree store root */
7733
7734 current_parentid = -1;
7735 parent_iter = null;
7736 } else if (model.get_iter_first (out tree_iter)) {
7737 if (bookmark_folder_button_reach_parent (model, ref tree_iter, parentid)) {
7738 /* folder's parent found in the tree store */
7739
7740 current_parentid = parentid;
7741 stock_parent_iter = tree_iter;
7742 parent_iter = stock_parent_iter;
7743 } else {
7744 /* folder's parent not found, skip it */
7745
7746 list_iter = list_iter.next;
7747 continue;
7748 }
7749 } else
7750 assert_not_reached ();
7751 }
7752
7753 /* insert folder in the tree store and remove it from the folders list */
7754
7755 model.insert_with_values (out tree_iter, parent_iter, int.MAX, 0, title, 1, id);
7756
7757 if (id == selected_parentid)
7758 combo.set_active_iter (tree_iter);
7759
7760 n++;
7761
7762 something_done = true;
7763
7764 folders.delete_link (list_iter);
7765
7766 list_iter = folders.first ();
7767 } while (list_iter != null);
7768
7769 if (!something_done) /* avoid infinite loop in case of orphan folders */
7770 break;
7771 }
7772
7773 if (folders.first () != null) {
7774 printerr ("midori_bookmark_folder_button_new: orphan folder(s) detected in bookmarks db\n");
7775
7776 foreach (var folder in folders) {
7777 var title = folder.title;
7778 var id = folder.id;
7779 var parentid = folder.parentid;
7780
7781 printerr (" id=%" + int64.FORMAT + ", parentid=%" + int64.FORMAT + ", title=%s\n",
7782 id, parentid, title);
7783 }
7784 }
7785
7786 if (n < 2)
7787 combo.sensitive = false;
7788
7789 return combo;
7790 }
7791
7792 int64 bookmark_folder_button_get_active (Gtk.Widget widget) {
7793 int64 id = -1;
7794 Gtk.TreeIter iter;
7795
7796 var combo = widget as Gtk.ComboBox;
7797
7798 return_val_if_fail (combo == null, 0);
7799
7800 if (combo.get_active_iter (out iter)) {
7801 combo.model.@get (iter, 1, out id);
7802 }
7803
7804 return id;
7805 }
7806
7807 void edit_bookmark_title_changed_cb (Gtk.Entry entry, Gtk.Dialog dialog) {
7808 var title = entry.text;
7809 dialog.set_response_sensitive (Gtk.ResponseType.ACCEPT, title != null && title != "");
7810 }
7811
7812 void edit_bookmark_add_speed_dial_cb (Gtk.Widget button, Katze.Item bookmark) {
7813 add_speed_dial ();
7814 ((Gtk.Dialog) button.get_toplevel ()).response (Gtk.ResponseType.DELETE_EVENT);
7815 }
7816
7817 void edit_bookmark_create_launcher_cb (Gtk.Widget button, Katze.Item bookmark) {
7818 var action = button.get_data<Gtk.Action> ("midori-action");
7819 action.activate ();
7820 ((Gtk.Dialog) button.get_toplevel ()).response (Gtk.ResponseType.DELETE_EVENT);
7821 }
7822
7823 /* Private function, used by MidoriBookmarks and MidoriHistory */
7824 internal bool edit_bookmark_dialog_new (Katze.Item? bookmark_or_parent, bool new_bookmark, bool is_folder, Gtk.Widget proxy) {
7825 Katze.Item bookmark = bookmark_or_parent;
7826 bool return_status = false;
7827
7828 Gtk.Dialog dialog;
7829 string title;
7830 if (is_folder)
7831 title = new_bookmark ? _("New Folder") : _("Edit Folder");
7832 else
7833 title = new_bookmark ? _("New Bookmark") : _("Edit Bookmark");
7834
7835#if HAVE_GRANITE
7836 if (proxy != null) {
7837 // TODO port to GtkPopover?
7838 dialog = new Granite.Widgets.PopOver ();
7839 ((Granite.Widgets.PopOver) dialog).move_to_widget (proxy, true);
7840 } else
7841#endif
7581 {7842 {
7582 browser = iter->data;7843 dialog = new Gtk.Dialog.with_buttons (title, this, Gtk.DialogFlags.DESTROY_WITH_PARENT, null);
75837844 }
7584 if (MIDORI_IS_BROWSER (browser) && gtk_widget_is_ancestor( GTK_WIDGET (browser), widget))7845 var content_area = ((Gtk.Dialog) dialog).get_content_area ();
7585 {7846 dialog.set_border_width (6);
7586 g_list_free (top_levels);7847 dialog.add_buttons (Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
7587 return MIDORI_BROWSER (browser);7848 new_bookmark ? Gtk.Stock.ADD : Gtk.Stock.SAVE, Gtk.ResponseType.ACCEPT);
7588 }7849
7589 }7850 Gtk.Label label;
7851 if (!is_folder)
7852 label = new Gtk.Label (_("Type a name for this bookmark, and choose where to keep it."));
7853 else
7854 label = new Gtk.Label (_("Type a name for this folder, and choose where to keep it."));
7855
7856 var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
7857 vbox.pack_start (label, false, false, 6);
7858 content_area.pack_start (vbox, false, false, 0);
7859 ((Gtk.Window) dialog).icon_name = new_bookmark ? Gtk.Stock.ADD : Gtk.Stock.REMOVE;
7860
7861 if (new_bookmark) {
7862 var view = (View) get_current_tab ();
7863 if (is_folder) {
7864 bookmark = new Katze.Array (typeof (Katze.Array));
7865 bookmark.name = view.get_display_title ();
7866 } else {
7867 bookmark = new Katze.Item ();
7868 bookmark.uri = view.get_display_uri ();
7869 bookmark.name = view.get_display_title ();
7870 }
7871
7872 bookmark.set_meta_integer ("parentid", (bookmark_or_parent == null ? 0 : bookmark_or_parent.get_meta_integer ("id")));
7873 }
7874
7875 var item_name = bookmark.name;
7876 var entry_title = new Gtk.Entry ();
7877 entry_title.activates_default = true;
7878 entry_title.text = Katze.str_non_null (item_name);
7879 edit_bookmark_title_changed_cb (entry_title, dialog);
7880 entry_title.changed.connect ((entry_title) => edit_bookmark_title_changed_cb ((Gtk.Entry) entry_title, dialog));
7881 vbox.pack_start (entry_title, false, false, 0);
7882
7883 Gtk.Entry? entry_uri = null;
7884 if (!is_folder) {
7885 entry_uri = Katze.uri_entry_new (dialog.get_widget_for_response (Gtk.ResponseType.ACCEPT));
7886 entry_uri.activates_default = true;
7887 entry_uri.text = bookmark.uri;
7888 vbox.pack_start (entry_uri, false, false, 0);
7889 }
7890
7891 var combo_folder = bookmark_folder_button_new (bookmarks, bookmark.get_meta_integer ("parentid"));
7892 vbox.pack_start (combo_folder, false, false, 0);
7893
7894 var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
7895 vbox.pack_start (hbox, false, false, 0);
7896 var check_toolbar = new Gtk.CheckButton.with_mnemonic (_("Show in Bookmarks _Bar"));
7897 check_toolbar.active = bookmark.get_meta_boolean ("toolbar");
7898 hbox.pack_start (check_toolbar, false, false, 0);
7899
7900 if (new_bookmark && !is_folder) {
7901 hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
7902 vbox.pack_start (hbox, false, false, 0);
7903
7904 var button = new Gtk.CheckButton.with_mnemonic (_("Add to _Speed Dial"));
7905 button.clicked.connect ((label) => edit_bookmark_add_speed_dial_cb (label, bookmark));
7906 hbox.pack_start (button, false, false, 0);
7907
7908 /* FIXME: There's no API for extending the bookmark dialog */
7909 var action = action_group.get_action ("CreateLauncher");
7910 if (action != null) {
7911 var check_button = new Gtk.Button.with_mnemonic (action.label);
7912 check_button.set_data<Gtk.Action> ("midori-action", action);
7913 check_button.clicked.connect ((label) => edit_bookmark_create_launcher_cb (label, bookmark));
7914 hbox.pack_start (check_button, false, false, 0);
7915 }
7916 }
7917
7918 content_area.show_all ();
7919
7920 dialog.set_default_response (Gtk.ResponseType.ACCEPT);
7921 if (Dialog.run (dialog) == Gtk.ResponseType.ACCEPT) {
7922 bookmark.name = entry_title.text;
7923 bookmark.set_meta_integer ("toolbar", (int) check_toolbar.active);
7924 if (!is_folder)
7925 bookmark.uri = entry_uri.text;
7926
7927 var selected = bookmark_folder_button_get_active (combo_folder);
7928 bookmark.set_meta_integer ("parentid", selected);
7929
7930 if (new_bookmark)
7931 bookmarks.add_item (bookmark);
7932 else
7933 bookmarks.update_item (bookmark);
7934
7935 return_status = true;
7936 }
7937
7938 dialog.destroy ();
7939 return return_status;
7940 }
7941
7942 bool prepare_download (WebKit.Download download, string uri) {
7943 if (!Download.has_enough_space (download, uri, false))
7944 return false;
7945
7946#if HAVE_WEBKIT2
7947 download.destination = uri;
7948#else
7949 download.destination_uri = uri;
7950#endif
7951 add_download (download);
7952 return true;
7953 }
7954
7955#if !HAVE_WEBKIT2
7956 void save_resources (List<WebKit.WebResource> resources, string folder) {
7957 Paths.mkdir_with_parents (folder);
7958
7959 foreach (var resource in resources) {
7960 unowned StringBuilder data = resource.get_data ();
7961
7962 /* Resource could be adblocked, skip it in that case */
7963 if (resource.uri == "about:blank")
7964 continue;
7965
7966 var sub_filename = Download.get_filename_suggestion_for_uri (resource.mime_type, resource.uri);
7967 var sub_path = Download.get_unique_filename (Path.build_filename (folder, sub_filename));
7968 if (data != null) {
7969 try {
7970 FileUtils.set_contents (sub_path, data.str, data.len);
7971 } catch (Error e) {
7972 warning ("Failed to save %s: %s", sub_filename, e.message);
7973 }
7974 } else
7975 warning ("Skipping empty resource %s", sub_filename);
7976 }
7977 }
7978#endif
7979
7980 void save_uri (View view, string? uri) {
7981 string? last_dir = null;
7982 var title = view.get_display_title ();
7983
7984 var dialog = new FileChooserDialog (_("Save file as"), this, Gtk.FileChooserAction.SAVE);
7985 dialog.do_overwrite_confirmation = true;
7986
7987 if (uri == null)
7988 uri = view.get_display_uri ();
7989
7990 if (last_dir != null && last_dir != "")
7991 dialog.set_current_folder (last_dir);
7992 else {
7993 var dirname = URI.get_folder (uri);
7994 if (dirname == null)
7995 dirname = settings.download_folder;
7996 dialog.set_current_folder (dirname);
7997 }
7998
7999 string filename;
8000#if !HAVE_WEBKIT2
8001 var resources = view.get_resources ();
8002 var file_only = true;
8003 Gtk.CheckButton? checkbox = null;
8004
8005 if (resources != null && resources.nth_data (1) != null) {
8006 file_only = false;
8007 checkbox = new Gtk.CheckButton.with_mnemonic (_("Save associated _resources"));
8008 checkbox.active = true;
8009 dialog.extra_widget = checkbox;
8010 }
8011
8012 if (!file_only && title != uri)
8013 filename = Download.clean_filename (title);
8014 else
8015 filename = Download.get_filename_suggestion_for_uri (view.mime_type, uri);
8016
8017 dialog.set_current_name (filename);
8018
8019 if (Dialog.run (dialog) == Gtk.ResponseType.OK) {
8020 filename = dialog.get_filename ();
8021 if (checkbox != null)
8022 file_only = !checkbox.active;
8023
8024 if (!file_only) {
8025 var fullname = filename + ".html";
8026 view.save_source (uri, fullname, false);
8027 save_resources (resources, filename);
8028 } else
8029 view.save_source (uri, filename, false);
8030
8031 last_dir = dialog.get_current_folder ();
8032 }
8033#else
8034 filename = Midori.download_clean_filename (title);
8035 var suggested_filename = filename + ".mht";
8036 dialog.set_current_name (suggested_filename);
8037
8038 if (Dialog.run (dialog) == Gtk.ResponseType.OK) {
8039 var uri = dialog.get_uri ();
8040 if (uri != null)
8041 view.save_source (uri, null, false);
8042
8043 last_dir = dialog.get_current_folder ();
8044 }
8045#endif
8046 dialog.destroy ();
8047 }
8048
8049 void speed_dial_refresh_cb (SpeedDial dial) {
8050 foreach (var tab in get_tabs ()) {
8051 if (tab.uri == "about:dial")
8052 ((View) tab).reload (false);
8053 }
8054 }
8055
8056 void add_speed_dial () {
8057 var view = (View) get_current_tab ();
8058 dial.add (view.get_display_uri (), view.get_display_title (), null);
8059 }
8060
8061 bool tab_leave_notify_event_cb (Object object, Gdk.EventCrossing event) {
8062 _set_statusbar_text ((View) object, null);
8063 return true;
8064 }
8065
8066 void view_destroy_cb (Gtk.Widget widget) {
8067 var view = (View) widget;
8068
8069 if (proxy_array != null) {
8070 var item = view.get_proxy_item ();
8071 if (proxy_array.get_item_index (item) != -1 && !tab.is_blank ()) {
8072 if (trash != null)
8073 trash.add_item (item);
8074 update_history (item, "website", "leave");
8075 }
8076 disconnect_tab (view);
8077 remove_tab (view);
8078 }
8079 }
8080
8081 void view_attach_inspector_cb (Gtk.Widget view, Gtk.Widget inspector_view) {
8082 var toplevel = inspector_view.get_toplevel ();
8083 var scrolled = this.inspector_view.get_parent ();
8084 if (inspector_view == this.inspector_view)
8085 return;
8086
8087 toplevel.hide ();
8088 this.inspector_view.destroy ();
8089 inspector_view.reparent (scrolled);
8090 inspector.show_all ();
8091 this.inspector_view = inspector_view;
8092 toplevel.destroy ();
8093 if (!settings.last_inspector_attached)
8094 settings.last_inspector_attached = true;
8095 }
8096
8097 void view_detach_inspector_cb (Gtk.Widget view, Gtk.Widget inspector_view) {
8098 var scrolled = (Gtk.Container) inspector_view.get_parent ();
8099 var paned = scrolled.get_parent ();
8100 this.inspector_view = new Gtk.Viewport (null, null);
8101 scrolled.remove (inspector_view);
8102 scrolled.add (this.inspector_view);
8103 paned.hide ();
8104 if (settings.last_inspector_attached)
8105 settings.last_inspector_attached = false;
8106 }
8107
8108 void view_copy_history (Gtk.Widget view_to, Gtk.Widget view_from, bool omit_last) {
8109#if !HAVE_WEBKIT2
8110 var copy_from = ((View) view_from).web_view;
8111 var list_from = copy_from.get_back_forward_list ();
8112 var copy_to = ((View) view_to).web_view;
8113 var list_to = copy_to.get_back_forward_list ();
8114 var length_from = list_from.get_back_length ();
8115
8116 for (var i = -length_from; i <= (omit_last ? -1 : 0); i++) {
8117 var item = list_from.get_nth_item (i);
8118 if (item == null)
8119 break;
8120 list_to.add_item (item);
8121 }
8122#endif
8123 }
8124
8125 bool notify_new_tab_timeout_cb () {
8126#if !G_OS_WIN32
8127 set_opacity (1);
8128#endif
8129 return false;
8130 }
8131
8132 void notify_new_tab () {
8133 if (settings.flash_window_on_new_bg_tabs) {
8134#if G_OS_WIN32
8135 set_opacity (0.8);
8136#endif
8137 Midori.Timeout.add (100, notify_new_tab_timeout_cb);
8138 }
8139 }
8140
8141 bool view_forward_external (View view, string uri, NewView where)
8142 {
8143 if (Paths.get_runtime_mode () == RuntimeMode.APP) {
8144 return view.open_uri (uri);
8145 } else if (Paths.get_runtime_mode () == RuntimeMode.PRIVATE) {
8146 if (where == NewView.WINDOW) {
8147 Sokoke.spawn_app (uri, true);
8148 return true;
8149 }
8150 }
8151 return false;
8152 }
8153
8154 internal void view_new_tab_cb (Gtk.Widget view, string uri, bool background) {
8155 if (view_forward_external ((View) view, uri, NewView.TAB))
8156 return;
8157
8158 var new_view = add_uri (uri);
8159 if (view != null)
8160 view_copy_history (new_view, view, false);
8161
8162 if (!background)
8163 set_current_tab (new_view);
8164 else
8165 notify_new_tab ();
8166 }
8167
8168 void view_new_window_cb (Gtk.Widget? view, string uri) {
8169 if (view_forward_external ((View) (view != null ? view : get_current_tab ()), uri, NewView.WINDOW))
8170 return;
8171
8172 var new_browser = new_window (null);
8173 assert (new_browser != null);
8174 new_browser.view_new_tab_cb (view, uri, false);
8175 }
8176
8177 void view_new_view_cb (Gtk.Widget view, Gtk.Widget new_view, NewView where, bool user_initiated) {
8178 if (((View) view).is_dialog) {
8179 /* Dialog: URL, no toolbars, no tabs */
8180 var new_browser = new_window (null);
8181 assert (new_browser != null);
8182 new_browser.set_transient_for (this);
8183 new_browser.set_destroy_with_parent (true);
8184 new_browser.show_tabs = false;
8185 Sokoke.widget_set_visible (new_browser.menubar, false);
8186 Sokoke.widget_set_visible (new_browser.bookmarkbar, false);
8187 Sokoke.widget_set_visible (new_browser.statusbar, false);
8188 new_browser.action_set_visible ("CompactMenu", false);
8189 new_browser.set_toolbar_items ("Location");
8190 Sokoke.widget_set_visible (panel, false);
8191 new_browser.add_tab (new_view);
8192 new_browser.set_current_tab (new_view);
8193 return;
8194 }
8195
8196 if (view_forward_external ((View) new_view, ((View) new_view).get_proxy_item ().uri, where))
8197 return;
8198
8199 view_copy_history (new_view, view, true);
8200 if (where == NewView.WINDOW) {
8201 var new_browser = new_window (null);
8202 assert (new_browser != null);
8203 new_browser.add_tab (new_view);
8204 new_browser.set_current_tab (new_view);
8205 } else if (new_view.get_parent () != notebook) {
8206 add_tab (new_view);
8207 if (where != NewView.BACKGROUND)
8208 set_current_tab (new_view);
8209 } else
8210 notify_new_tab ();
8211
8212 if (!user_initiated) {
8213 var window = get_window ();
8214 var state = window.get_state ();
8215 if ((state | Gdk.WindowState.MAXIMIZED) != 0
8216 || (state | Gdk.WindowState.FULLSCREEN) != 0) {
8217 if (where == NewView.WINDOW)
8218 send_notification (_("New Window"), _("A new window has been opened"));
8219 else if (!show_tabs)
8220 send_notification (_("New Tab"), _("A new tab has been opened"));
8221 }
8222 }
8223 }
8224
8225#if HAVE_WEBKIT2
8226 void close_tab_idle (Object resource, AsyncResult result, View view)
8227 {
8228 uint8[] data;
8229 try {
8230 data = resource.get_data.end (res, null);
8231 } catch (Error e) {}
8232
8233 if (data != null)
8234 return;
8235#else
8236 bool close_tab_idle (View view)
8237 {
8238#endif
8239 close_tab (view);
8240#if !HAVE_WEBKIT2
8241 return false;
8242#endif
8243 }
8244
8245 bool view_download_requested_cb (Gtk.Widget widget, WebKit.Download download) {
8246 var view = (View) widget;
8247 var type = Download.get_type (download);
8248 var handled = true;
8249
8250 return_val_if_fail (view != null, false);
8251
8252 if (type == DownloadType.CANCEL) {
8253 handled = false;
8254 }
8255#if HAVE_WEBKIT2
8256 else if (download.destination == null) {
8257#else
8258 else if (download.destination_uri == null) {
8259#endif
8260 if (type == DownloadType.SAVE_AS) {
8261#if HAVE_WEBKIT2
8262 var download_uri = download.get_response (download).uri;
8263#else
8264 var download_uri = download.get_uri ();
8265#endif
8266 var dialog = new FileChooserDialog (_("Save file"), this, Gtk.FileChooserAction.SAVE);
8267 dialog.do_overwrite_confirmation = true;
8268 dialog.destroy_with_parent = true;
8269 var folder = URI.get_folder (download_uri);
8270 if (folder == null)
8271 folder = settings.download_folder;
8272 dialog.set_current_folder (folder);
8273
8274 var filename = Download.get_suggested_filename (download);
8275 dialog.set_current_name (filename);
8276
8277 if (Dialog.run (dialog) == Gtk.ResponseType.OK) {
8278 dialog.hide ();
8279 var uri = dialog.get_uri ();
8280 if (!prepare_download (download, uri)) {
8281 return false;
8282 }
8283 } else {
8284 dialog.hide ();
8285 return false;
8286 }
8287 } else {
8288 var folder = type == DownloadType.OPEN ? null : settings.download_folder;
8289 var destination_uri = Download.prepare_destination_uri (download, folder);
8290 prepare_download (download, destination_uri);
8291 }
8292#if !HAVE_WEBKIT2
8293 download.start ();
8294#endif
8295 }
8296
8297 /* Close empty tabs due to download links with a target */
8298 if (view.is_blank ()) {
8299 var web_view = view.web_view;
8300#if HAVE_WEBKIT2
8301 var resource = web_view.get_main_resource ();
8302 resource.get_data.begin (null, (obj, res) => close_tab_idle (obj, res, view));
8303#else
8304 var web_frame = web_view.get_main_frame ();
8305 var datasource = web_frame.get_data_source ();
8306 if (datasource.get_data () == null)
8307 Idle.add (() => close_tab_idle (view));
8308#endif
8309 }
8310 return handled;
8311 }
8312
8313#if HAVE_WEBKIT2
8314 void download_created_destination_cb (WebKit.Download download, string destination) {
8315 print ("%s: destination %s", "download created", destination);
8316 }
8317#endif
8318
8319 void view_search_text_cb (Gtk.Widget view, bool found, string typing) {
8320 find.search_text (view, found, typing);
8321 }
8322
8323 public int get_n_pages () {
8324 return (int) notebook.count;
8325 }
8326
8327 void connect_tab (Gtk.Widget widget) {
8328 var view = (View) widget;
8329 var item = ((View) view).get_proxy_item ();
8330 proxy_array.add_item (item);
8331
8332 view.notify["icon"].connect (view_notify_icon_cb);
8333 view.notify["load-status"].connect (view_notify_load_status_cb);
8334 view.notify["progress"].connect (view_notify_progress_cb);
8335 view.notify["uri"].connect (view_notify_uri_cb);
8336 view.notify["title"].connect (view_notify_title_cb);
8337 view.notify["zoom-level"].connect (view_notify_zoom_level_cb);
8338 view.notify["statusbar-text"].connect (view_notify_statusbar_text_cb);
8339 view.attach_inspector.connect (view_attach_inspector_cb);
8340 view.detach_inspector.connect (view_detach_inspector_cb);
8341 view.new_tab.connect (view_new_tab_cb);
8342 view.new_window.connect (view_new_window_cb);
8343 view.new_view.connect (view_new_view_cb);
8344 view.download_requested.connect_after (view_download_requested_cb);
8345 ((Tab) view).search_text.connect (view_search_text_cb);
8346 view.leave_notify_event.connect (tab_leave_notify_event_cb);
8347 view.destroy.connect (view_destroy_cb);
8348 }
8349
8350 void disconnect_tab (View view) {
8351 var item = view.get_proxy_item ();
8352 proxy_array.remove_item (item);
8353
8354 /* We don't ever want to be in a situation with no tabs,
8355 so just create an empty one if the last one is closed.
8356 The only exception is when we are closing the window,
8357 which is indicated by the proxy array having been unset. */
8358 if (proxy_array.is_empty ()) {
8359 add_uri ("about:new");
8360 set_current_page (0);
8361 }
8362
8363 update_actions ();
8364
8365 view.notify["icon"].disconnect (view_notify_icon_cb);
8366 view.notify["load-status"].disconnect (view_notify_load_status_cb);
8367 view.notify["progress"].disconnect (view_notify_progress_cb);
8368 view.notify["uri"].disconnect (view_notify_uri_cb);
8369 view.notify["title"].disconnect (view_notify_title_cb);
8370 view.notify["zoom-level"].disconnect (view_notify_zoom_level_cb);
8371 view.notify["statusbar-text"].disconnect (view_notify_statusbar_text_cb);
8372 view.attach_inspector.disconnect (view_attach_inspector_cb);
8373 view.detach_inspector.disconnect (view_detach_inspector_cb);
8374 view.new_tab.disconnect (view_new_tab_cb);
8375 view.new_window.disconnect (view_new_window_cb);
8376 view.new_view.disconnect (view_new_view_cb);
8377 view.download_requested.disconnect (view_download_requested_cb);
8378 ((Tab) view).search_text.disconnect (view_search_text_cb);
8379 view.leave_notify_event.disconnect (tab_leave_notify_event_cb);
8380 view.destroy.disconnect (view_destroy_cb);
8381 }
8382
8383 void update_tooltip_if_changed (Gtk.Action action, string text) {
8384 if (action.tooltip != text)
8385 action.tooltip = text;
8386 }
8387
8388 void update_reload_tooltip (Gdk.EventKey event, bool released) {
8389 /* Update the reload/stop tooltip in case we are holding the hard refresh modifiers*/
8390 var reload_stop = action_group.get_action ("ReloadStop");
8391 var reload = action_group.get_action ("Reload");
8392
8393 // TODO var device = Gdk.Display.get_default ().get_device_manager ().get_client_pointer ();
8394 Gdk.ModifierType mask;
8395 get_window ().get_device_position (((Gdk.Event) event).get_device (), null, null, out mask);
8396
8397 string target;
7590 8398
7591 g_list_free (top_levels);8399 if ((mask & Gdk.ModifierType.SHIFT_MASK) != 0)
7592 return NULL;8400 target = _("Reload page without caching");
8401 else
8402 target = _("Reload the current page");
8403
8404 update_tooltip_if_changed (reload_stop, target);
8405 update_tooltip_if_changed (reload, target);
8406 }
8407
8408 public override bool key_release_event (Gdk.EventKey event) {
8409 update_reload_tooltip (event, true);
8410 return false;
8411 }
8412
8413 public override bool key_press_event (Gdk.EventKey event) {
8414 update_reload_tooltip (event, false);
8415
8416 /* Interpret Ctrl(+Shift)+Tab as tab switching for compatibility */
8417 if (get_nth_tab (1) != null
8418 && event.keyval == Gdk.Key.Tab
8419 && (event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
8420 activate_action ("TabNext");
8421 return true;
8422 } else if (event.keyval == Gdk.Key.ISO_Left_Tab
8423 && (event.state & Gdk.ModifierType.CONTROL_MASK) != 0
8424 && (event.state & Gdk.ModifierType.SHIFT_MASK) != 0) {
8425 activate_action ("TabPrevious");
8426 return true;
8427 }
8428 /* Interpret Ctrl+= as Zoom In for compatibility */
8429 else if ((event.keyval == Gdk.Key.KP_Equal || event.keyval == Gdk.Key.equal)
8430 && (event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
8431 activate_action ("ZoomIn");
8432 return true;
8433 }
8434 /* Interpret F5 as reloading for compatibility */
8435 else if (event.keyval == Gdk.Key.F5) {
8436 activate_action ("Reload");
8437 return true;
8438 }
8439
8440#if !HAVE_WEBKIT2
8441 var focus = get_focus ();
8442 if (focus == null)
8443 get_current_tab ().grab_focus ();
8444 else if (focus is WebKit.WebView
8445 && event.keyval == Gdk.Key.space
8446 && ((event.state & Gdk.ModifierType.SHIFT_MASK) == 0)
8447 && !((WebKit.WebView) focus).can_cut_clipboard ()
8448 && !((WebKit.WebView) focus).can_paste_clipboard ()) {
8449 /* Space at the bottom of the page: Go to next page */
8450 var view = View.get_for_widget (focus);
8451 var scrolled = (Gtk.ScrolledWindow) focus.get_parent ();
8452 var vadjust = scrolled.get_vadjustment ();
8453 if (vadjust.value == vadjust.upper - vadjust.page_size) {
8454 var uri = view.get_next_page ();
8455 if (uri != null) {
8456 view.set_uri (uri);
8457 return true;
8458 }
8459 }
8460 }
8461#endif
8462
8463 if ((event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)) != 0)
8464 if (Sokoke.window_activate_key (this, event))
8465 return true;
8466
8467 var clean_state = event.state & Gtk.accelerator_get_default_mod_mask ();
8468 if (clean_state == 0 && propagate_key_event (event))
8469 return true;
8470
8471 if ((event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)) == 0)
8472 if (Sokoke.window_activate_key (this, event))
8473 return true;
8474
8475 if (event.state != 0 && propagate_key_event (event))
8476 return true;
8477
8478 /* Interpret (Shift+)Backspace as going back (forward) for compatibility */
8479 if ((event.keyval == Gdk.Key.BackSpace)
8480 && (event.state & Gdk.ModifierType.SHIFT_MASK) != 0) {
8481 activate_action ("Forward");
8482 return true;
8483 } else if (event.keyval == Gdk.Key.BackSpace) {
8484 activate_action ("Back");
8485 return true;
8486 }
8487
8488 return base.key_press_event (event);
8489 }
8490
8491 void action_window_new_activate (Gtk.Action action) {
8492 view_new_window_cb (null, "about:home");
8493 }
8494
8495 void action_tab_new_activate (Gtk.Action action) {
8496 var view = add_uri ("about:new");
8497 set_current_tab (view);
8498 }
8499
8500 void action_private_browsing_activate (Gtk.Action action) {
8501 Sokoke.spawn_app ("about:private", true);
8502 }
8503
8504#if !GTK_VERSION_3_1_10
8505 string? action_open_last_dir = null;
8506#endif
8507
8508 void action_open_activate (Gtk.Action action) {
8509#if !GTK_VERSION_3_1_10
8510 var folder_set = false;
8511#endif
8512 string? uri = null;
8513
8514 var dialog = new FileChooserDialog (_("Open file"), this, Gtk.FileChooserAction.OPEN);
8515
8516 /* base the start folder on the current view's uri if it is local */
8517 var view = get_current_tab ();
8518 if ((uri = ((View) view).get_display_uri ()) != null) {
8519 try {
8520 var filename = Filename.from_uri (uri, null);
8521 var dirname = Path.get_dirname (filename);
8522 if (dirname != null && FileUtils.test (dirname, FileTest.IS_DIR)) {
8523 dialog.set_current_folder (dirname);
8524#if !GTK_VERSION_3_1_10
8525 folder_set = true;
8526#endif
8527 }
8528 } catch (Error e) {}
8529 }
8530
8531#if !GTK_VERSION_3_1_10
8532 if (!folder_set && action_open_last_dir != null && action_open_last_dir != "")
8533 dialog.set_current_folder (action_open_last_dir);
8534#endif
8535
8536 if (Dialog.run (dialog) == Gtk.ResponseType.OK) {
8537#if !GTK_VERSION_3_1_10
8538 action_open_last_dir = dialog.get_current_folder ();
8539#endif
8540 uri = dialog.get_uri ();
8541 set_current_uri (uri);
8542
8543 }
8544
8545 dialog.destroy ();
8546 }
8547
8548 void action_save_as_activate (Gtk.Action action) {
8549 save_uri ((View) get_current_tab (), null);
8550 }
8551
8552 void action_add_speed_dial_activate (Gtk.Action action) {
8553 add_speed_dial ();
8554 }
8555
8556 void subscribe_to_news_feed (string uri) {
8557 var news_aggregator = settings.news_aggregator;
8558 if (news_aggregator != null && news_aggregator != "") {
8559 /* Thunderbird only accepts feed://, Liferea doesn't mind */
8560 var feed = uri;
8561 if (feed.has_prefix ("http://"))
8562 feed = "feed" + feed.substring (4);
8563 /* Special-case Liferea because a helper script may be required */
8564 if (news_aggregator == "liferea"
8565 && Environment.find_program_in_path ("liferea-add-feed") != null)
8566 Sokoke.spawn_program ("liferea-add-feed", false, feed, true, false);
8567 else
8568 Sokoke.spawn_program (news_aggregator, true, feed, true, false);
8569 } else {
8570 var description = "%s\n\n%s".printf (uri,
8571 _("To use the above URI open a news aggregator. " +
8572 "There is usually a menu or button \"New Subscription\", " +
8573 "\"New News Feed\" or similar.\n" +
8574 "Alternatively go to Preferences, Applications in Midori, " +
8575 "and select a News Aggregator. Next time you click the " +
8576 "news feed icon, it will be added automatically."));
8577 Sokoke.message_dialog (Gtk.MessageType.INFO, _("New feed"), description, false);
8578 }
8579 }
8580
8581 void action_add_news_feed_activate (Gtk.Action action) {
8582 Gtk.Widget? view = null;
8583 string? uri = null;
8584
8585 if ((view = get_current_tab ()) == null)
8586 return;
8587 if ((uri = view.get_data<string> ("news-feeds")) == null)
8588 return;
8589
8590 subscribe_to_news_feed (uri);
8591 }
8592
8593 void action_compact_add_activate (Gtk.Action action) {
8594 string[] actions = { "BookmarkAdd", "AddSpeedDial", "AddNewsFeed" };
8595
8596 var dialog = new Gtk.Dialog ();
8597 dialog.transient_for = this;
8598 dialog.title = _("Add a new bookmark");
8599 var box = dialog.get_content_area ();
8600
8601 for (var i = 0; i < actions.length; i++) {
8602 action = action_group.get_action (actions[i]);
8603 var button = new Gtk.Button.with_mnemonic (action.label);
8604 button.name = "GtkButton-thumb";
8605 box.pack_start (button, true, true, 4);
8606 button.related_action = action;
8607 button.clicked.connect ((dlg) => dlg.destroy ());
8608 }
8609
8610 dialog.show ();
8611 dialog.response.connect ((dlg, response) => dlg.destroy ());
8612 }
8613
8614 void action_tab_close_activate (Gtk.Action action) {
8615 close_tab (get_current_tab ());
8616 }
8617
8618 void action_window_close_activate (Gtk.Action action) {
8619 if (!delete_event ((Gdk.EventAny) Gtk.get_current_event ()))
8620 destroy ();
8621 }
8622
8623 void action_mail_to_activate (Gtk.Action action) {
8624 var view = (View) get_current_tab ();
8625 var uri = Uri.escape_string (view.get_display_uri (), null, true);
8626 var title = Uri.escape_string (view.get_display_title (), null, true);
8627 view.open_uri ("mailto:?cc=&bcc=&subject=" + title + "&body=" + uri);
8628 }
8629
8630 void action_print_activate (Gtk.Action action) {
8631 ((View) get_current_tab ()).print ();
8632 }
8633
8634 void action_quit_activate (Gtk.Action action) {
8635 quit ();
8636 }
8637
8638 void action_edit_activate (Gtk.Action action) {
8639 var widget = get_focus ();
8640 bool can_undo = false, can_redo = false,
8641 can_cut = false, can_copy = false, can_paste = false,
8642 has_selection, can_select_all = false;
8643
8644 if (widget is WebKit.WebView) {
8645 get_current_tab ().update_actions.begin (action_group);
8646 return;
8647 } else if (widget is Gtk.Editable) {
8648 // TODO check is casting to GtkEntry is ok
8649 var editable = (Gtk.Entry) widget;
8650 has_selection = editable.get_selection_bounds (null, null);
8651 can_cut = has_selection && editable.get_editable ();
8652 can_copy = has_selection;
8653 can_paste = editable.get_editable ();
8654 can_select_all = true;
8655 } else if (widget is Gtk.TextView) {
8656 var text_view = (Gtk.TextView) widget;
8657 has_selection = text_view.buffer.has_selection;
8658 can_cut = text_view.editable;
8659 can_copy = has_selection;
8660 can_paste = text_view.editable && has_selection;
8661 can_select_all = true;
8662 }
8663
8664 action_set_sensitive ("Undo", can_undo);
8665 action_set_sensitive ("Redo", can_redo);
8666 action_set_sensitive ("Cut", can_cut);
8667 action_set_sensitive ("Copy", can_copy);
8668 action_set_sensitive ("Paste", can_paste);
8669 action_set_sensitive ("Delete", can_cut);
8670 action_set_sensitive ("SelectAll", can_select_all);
8671 }
8672
8673 void action_undo_activate (Gtk.Action action) {
8674 var web_view = get_focus () as WebKit.WebView;
8675 if (web_view == null)
8676 return;
8677
8678#if HAVE_WEBKIT2
8679 web_view.execute_editing_command (WebKit.EditingCommand.UNDO);
8680#else
8681 web_view.undo ();
8682#endif
8683 }
8684
8685 void action_redo_activate (Gtk.Action action) {
8686 var web_view = get_focus () as WebKit.WebView;
8687 if (web_view == null)
8688 return;
8689
8690#if HAVE_WEBKIT2
8691 web_view.execute_editing_command (WebKit.EditingCommand.REDO);
8692#else
8693 web_view.redo ();
8694#endif
8695 }
8696
8697 void action_cut_activate (Gtk.Action action) {
8698 var widget = get_focus ();
8699 if (likely (widget != null) && Signal.lookup ("cut-clipboard", widget.get_type ()) != 0)
8700 Signal.emit_by_name (widget, "cut-clipboard");
8701#if HAVE_WEBKIT2
8702 else if (widget is WebKit.WebView)
8703 ((WebKit.WebView) widget).execute_editing_command (WebKit.EditingCommand.CUT);
8704#endif
8705 }
8706
8707 void action_copy_activate (Gtk.Action action) {
8708 var widget = get_focus ();
8709 if (likely (widget != null) && Signal.lookup ("copy-clipboard", widget.get_type ()) != 0)
8710 Signal.emit_by_name (widget, "copy-clipboard");
8711#if HAVE_WEBKIT2
8712 else if (widget is WebKit.WebView)
8713 ((WebKit.WebView) view).execute_editing_command (WebKit.EditingCommand.COPY);
8714#endif
8715 }
8716
8717 void action_paste_activate (Gtk.Action action) {
8718 var widget = get_focus ();
8719 if (likely (widget != null) && Signal.lookup ("paste-clipboard", widget.get_type ()) != 0)
8720 Signal.emit_by_name (widget, "paste-clipboard");
8721#if HAVE_WEBKIT2
8722 else if (widget is WebKit.WebView)
8723 ((WebKit.WebView) view).execute_editing_command (WebKit.EditingCommand.PASTE);
8724#endif
8725 }
8726
8727 void action_delete_activate (Gtk.Action action) {
8728 var widget = get_focus ();
8729 if (likely (widget != null)) {
8730 if (widget is Gtk.Editable)
8731 ((Gtk.Editable) widget).delete_selection ();
8732#if !HAVE_WEBKIT2
8733 else if (widget is WebKit.WebView)
8734 ((WebKit.WebView) widget).delete_selection ();
8735#endif
8736 else if (widget is Gtk.TextView)
8737 ((Gtk.TextView) widget).buffer.delete_selection (true, false);
8738 }
8739 }
8740
8741 void action_select_all_activate (Gtk.Action action) {
8742 var widget = get_focus ();
8743 if (likely (widget != null)) {
8744 if (widget is Gtk.Editable)
8745 ((Gtk.Editable) widget).select_region (0, -1);
8746#if HAVE_WEBKIT2
8747 else if (widget is WebKit.WebView)
8748 ((WebKit.WebView) widget).execute_editing_command (WebKit.EditingCommand.SELECT_ALL);
8749#endif
8750 else if (Signal.lookup ("select-all", widget.get_type ()) != 0) {
8751 if (widget is Gtk.TextView)
8752 ((Gtk.TextView) widget).select_all (true);
8753 else if (widget is Gtk.TreeView)
8754 ((Gtk.TreeView) widget).select_all ();
8755 else
8756 Signal.emit_by_name (widget, "select-all");
8757 }
8758 }
8759 }
8760
8761 void action_find_activate (Gtk.Action action) {
8762 find.invoke (((View) get_current_tab ()).get_selected_text ());
8763 }
8764
8765 void action_find_next_activate (Gtk.Action action) {
8766 find.@continue (true);
8767 }
8768
8769 void action_find_previous_activate (Gtk.Action action) {
8770 find.@continue (false);
8771 }
8772
8773#if !HAVE_GRANITE
8774 void navigationbar_notify_style_cb (Object object, ParamSpec pspec) {
8775 set_toolbar_style (settings.toolbar_style);
8776 }
8777#endif
8778
8779 static string[] actions = {
8780 "WindowNew", "TabNew", "Open", "SaveAs", "Print", "Find",
8781 "Fullscreen", "Preferences", "Window", "Bookmarks",
8782 "ReloadStop", "ZoomIn", "TabClose", "NextForward", "Location",
8783 "ZoomOut", "Separator", "Back", "Forward", "Homepage",
8784 "Panel", "Trash", "Search", "BookmarkAdd", "Previous", "Next" };
8785
8786 /**
8787 * midori_browser_get_toolbar_actions:
8788 *
8789 * Retrieves a list of actions which are suitable for use in a toolbar.
8790 *
8791 * Return value: a null-terminated array of strings with actions
8792 *
8793 * Since: 0.1.8
8794 **/
8795 public unowned string[] get_toolbar_actions () {
8796 return actions;
8797 }
8798
8799 bool toolbar_popup_context_menu_cb (Gtk.Widget widget, int x, int y, int button) {
8800 var menu = new ContextAction ("ToolbarContextMenu", null, null, null);
8801 menu.add_action_group (action_group);
8802 menu.add_by_name ("Menubar");
8803 menu.add_by_name ("Navigationbar");
8804 menu.add_by_name ("Bookmarkbar");
8805 menu.add_by_name ("Statusbar");
8806
8807 var context_menu = menu.create_menu (null, false);
8808 populate_toolbar_menu (context_menu);
8809 Katze.widget_popup (widget, context_menu, null, button == -1 ? Katze.MenuPos.LEFT : Katze.MenuPos.CURSOR);
8810 return true;
8811 }
8812
8813 void bookmarkbar_activate_item (Katze.ArrayAction action, Katze.Item item) {
8814 open_bookmark (item);
8815 }
8816
8817 bool bookmarkbar_activate_item_alt (Katze.ArrayAction action, Katze.Item item, Gtk.Widget proxy, Gdk.EventButton event) {
8818 assert (event != null);
8819
8820 if (event_new_tab (event)) {
8821 var view = add_item (item);
8822 set_current_tab_smartly (view);
8823 } else if (event_context_menu (event)) {
8824 bookmark_popup (proxy, null, item);
8825 } else if (event.button == 1) {
8826 bookmarkbar_activate_item (action, item);
8827 }
8828
8829 return true;
8830 }
8831
8832 void action_trash_populate_popup (Gtk.Action action, Gtk.Menu menu) {
8833 Gtk.MenuItem menuitem;
8834
8835 menuitem = new Gtk.SeparatorMenuItem ();
8836 menu.append (menuitem);
8837 menuitem.show ();
8838
8839 menuitem = (Gtk.MenuItem) action_group.get_action ("TrashEmpty").create_menu_item ();
8840 menu.append (menuitem);
8841 menuitem.show ();
8842 }
8843
8844 Gtk.Widget restore_tab (Katze.Item item) {
8845 item.@ref ();
8846 trash.remove_item (item);
8847 var view = add_item (item);
8848 item.unref ();
8849
8850 return view;
8851 }
8852
8853 bool action_trash_activate_item_alt (Gtk.Action action, Katze.Item item, Gtk.Widget proxy, Gdk.EventButton event) {
8854 assert (event != null);
8855
8856 if (event_new_tab (event))
8857 set_current_tab_smartly (restore_tab (item));
8858 else if (event.button == 1)
8859 set_current_tab (restore_tab (item));
8860
8861 return true;
8862 }
8863
8864 internal bool open_bookmark (Katze.Item item) {
8865 var uri = item.uri;
8866
8867 if (uri == null || uri == "")
8868 return false;
8869
8870 /* Imported bookmarks may lack a protocol */
8871 var uri_fixed = Sokoke.magic_uri (uri, true, false);
8872 if (uri_fixed == null)
8873 uri_fixed = uri;
8874
8875 if (item.get_meta_boolean ("app"))
8876 Sokoke.spawn_app (uri_fixed, false);
8877 else {
8878 set_current_uri (uri_fixed);
8879 get_current_tab ().grab_focus ();
8880 }
8881 return true;
8882 }
8883
8884 void action_tools_populate_popup (Gtk.Action action, Gtk.Menu default_menu) {
8885 var menu = new ContextAction ("ToolsMenu", null, null, null);
8886 menu.add_action_group (action_group);
8887 menu.add_by_name ("ManageSearchEngines");
8888 menu.add_by_name ("ClearPrivateData");
8889 menu.add_by_name ("InspectPage");
8890 populate_tool_menu (default_menu);
8891 menu.add (null);
8892 var j = 0;
8893 Gtk.Widget widget;
8894 while ((widget = panel.get_nth_page (j++)) != null)
8895 menu.add (widget.get_data<Gtk.Action> ("midori-panel-action"));
8896#if G_OS_WIN32
8897 menu.add (null);
8898 menu.add_by_name ("Preferences");
8899#endif
8900 menu.create_menu (default_menu, true);
8901 }
8902
8903 bool action_bookmarks_populate_folder (Gtk.Action action, Gtk.MenuShell menu, Katze.Array folder) {
8904 if (bookmarks == null)
8905 return false;
8906
8907 bookmarks.populate_folder (folder);
8908
8909 /* Clear items from dummy array here */
8910 menu.@foreach ((widget) => widget.destroy ());
8911
8912 /* "Import Bookmarks" and "Export Bookmarks" at the top */
8913 if (folder == bookmarks) {
8914 Gtk.MenuItem menuitem;
8915 menuitem = (Gtk.MenuItem) action_group.get_action ("BookmarksImport").create_menu_item ();
8916 menu.append (menuitem);
8917 menuitem.show ();
8918 menuitem = (Gtk.MenuItem) action_group.get_action ("BookmarksExport").create_menu_item ();
8919 menu.append (menuitem);
8920 menuitem.show ();
8921 menuitem = new Gtk.SeparatorMenuItem ();
8922 menu.append (menuitem);
8923 menuitem.show ();
8924 }
8925
8926 if (folder.is_empty ()) {
8927 var menuitem = new Gtk.ImageMenuItem.with_label (_("Empty"));
8928 menuitem.sensitive = false;
8929 menu.append (menuitem);
8930 menuitem.show ();
8931 return true;
8932 }
8933
8934 ((Katze.ArrayAction) action).generate_menu (folder, menu, this);
8935 return true;
8936 }
8937
8938 void action_window_populate_popup (Gtk.Action action, Gtk.Menu default_menu) {
8939 var menu = new ContextAction ("WindowMenu", null, null, null);
8940 menu.add_action_group (action_group);
8941 menu.add (null);
8942 menu.add_by_name ("LastSession");
8943 menu.add_by_name ("TabCurrent");
8944 menu.add_by_name ("NextView");
8945 menu.add_by_name ("TabNext");
8946 menu.add_by_name ("TabPrevious");
8947 menu.create_menu (default_menu, true);
8948 }
8949
8950 bool action_window_activate_item_alt (Katze.ArrayAction action, Katze.Item item, Gtk.Widget proxy, Gdk.EventButton event) {
8951 set_current_item (item);
8952 return true;
8953 }
8954
8955 void action_compact_menu_populate_popup (Katze.ArrayAction action, Gtk.Menu default_menu) {
8956 var menu = new ContextAction ("CompactMenu", null, null, null);
8957 menu.add_action_group (action_group);
8958 menu.add_by_name ("WindowNew");
8959 menu.add_by_name ("PrivateBrowsing");
8960 menu.add (null);
8961 menu.add_by_name ("Find");
8962 menu.add_by_name ("Print");
8963 menu.add_by_name ("Fullscreen");
8964 menu.add_by_name ("MailTo");
8965 menu.add (null);
8966 var j = 0;
8967 Gtk.Widget widget;
8968 while ((widget = panel.get_nth_page (j++)) != null)
8969 menu.add (widget.get_data<Gtk.Action> ("midori-panel-action"));
8970 menu.add (null);
8971 menu.add_by_name ("BookmarksImport");
8972 menu.add_by_name ("BookmarksExport");
8973 menu.add_by_name ("ClearPrivateData");
8974 populate_tool_menu (default_menu);
8975 menu.add (null);
8976#if !HAVE_GRANITE
8977 menu.add_by_name ("HelpFAQ");
8978 menu.add_by_name ("HelpBugs");
8979#endif
8980 menu.add_by_name ("Preferences");
8981 menu.add_by_name ("About");
8982 menu.create_menu (default_menu, false);
8983 }
8984
8985 void preferences_response_help_cb (Gtk.Widget preferences, int response) {
8986 if (response == Gtk.ResponseType.HELP)
8987 activate_action ("HelpFAQ");
8988 }
8989
8990 Preferences? preferences_dialog = null;
8991 void action_preferences_activate (Gtk.Action action) {
8992 if (preferences_dialog == null) {
8993 preferences_dialog = new Preferences (this, settings);
8994 show_preferences (preferences_dialog);
8995 preferences_dialog.response.connect (preferences_response_help_cb);
8996 preferences_dialog.destroy.connect (() => { preferences_dialog = null; });
8997 preferences_dialog.show ();
8998 } else
8999 preferences_dialog.present ();
9000 }
9001
9002 string? ubuntu_menuproxy = null;
9003 bool has_native_menubar () {
9004 if (ubuntu_menuproxy == null)
9005 ubuntu_menuproxy = Environ.get_variable (Environ.@get (), "UBUNTU_MENUPROXY");
9006 /*
9007 * Values when the global menu is enabled
9008 * UBUNTU_MENUPROXY=libappmenu.so
9009 * UBUNTU_MENUPROXY=1
9010 * The official way to disable the menu is
9011 * UBUNTU_MENUPROXY=
9012 */
9013 return ubuntu_menuproxy != null && (".so" in ubuntu_menuproxy || ubuntu_menuproxy == "1");
9014 }
9015
9016 void action_menubar_activate (Gtk.Action menubar_action) {
9017 var active = ((Gtk.ToggleAction) menubar_action).active;
9018 var menu_action = action_group.get_action ("CompactMenu");
9019 var toolbar_items = new StringBuilder ();
9020
9021 if (has_native_menubar ())
9022 active = false;
9023
9024 foreach (var child in navigationbar.get_children ()) {
9025 var action = ((Gtk.Activatable) child).related_action;
9026 if (action == null)
9027 continue;
9028 if (action == menu_action) {
9029 if (active)
9030 navigationbar.remove (child);
9031 continue;
9032 } else if (action is PanedAction) {
9033 var paned_action = (PanedAction) action;
9034 toolbar_items.append_printf ("%s,%s", paned_action.get_child1_name (), paned_action.get_child2_name ());
9035 } else
9036 toolbar_items.append (action.name);
9037 toolbar_items.append_c (',');
9038 }
9039
9040 if (settings.show_menubar != active)
9041 settings.show_menubar = active;
9042
9043 settings.toolbar_items = toolbar_items.str;
9044
9045 Sokoke.widget_set_visible (menubar, active);
9046 set_data<bool> ("midori-toolbars-visible", menubar.visible || navigationbar.visible);
9047 }
9048
9049 void action_navigationbar_activate (Gtk.Action action) {
9050 var active = ((Gtk.ToggleAction) action).active;
9051 settings.show_navigationbar = active;
9052 Sokoke.widget_set_visible (navigationbar, active);
9053
9054 set_data<bool> ("midori-toolbars-visible", menubar.visible || navigationbar.visible);
9055 }
9056
9057 void action_bookmarkbar_activate (Gtk.Action action) {
9058 var active = ((Gtk.ToggleAction) action).active;
9059 settings.show_bookmarkbar = active;
9060 Sokoke.widget_set_visible (bookmarkbar, active);
9061 }
9062
9063 void action_statusbar_activate (Gtk.Action action) {
9064 var active = ((Gtk.ToggleAction) action).active;
9065 settings.show_statusbar = active;
9066 Sokoke.widget_set_visible (statusbar, active);
9067 }
9068
9069 void action_reload_stop_activate (Gtk.Action action) {
9070 var view = (View) get_current_tab ();
9071
9072 /* Refresh or stop, depending on the stock id */
9073 if (action.stock_id == Gtk.Stock.REFRESH) {
9074 var state = (Gdk.ModifierType) 0;
9075 Gdk.Window? window = null;
9076 var from_cache = true;
9077
9078 if (action.name == "ReloadUncached")
9079 from_cache = false;
9080 else if ((window = get_window ()) != null) {
9081 window.get_device_position (Gtk.get_current_event_device (), null, null, out state);
9082 if ((state & Gdk.ModifierType.SHIFT_MASK) != 0)
9083 from_cache = false;
9084 }
9085 view.reload (from_cache);
9086 } else
9087 view.stop_loading ();
9088 }
9089
9090 void action_zoom_activate (Gtk.Action action) {
9091 var view = (View) get_current_tab ();
9092
9093 if (action.name == "ZoomIn")
9094 view.zoom_level = view.zoom_level + 0.10f;
9095 else if (action.name == "ZoomOut")
9096 view.zoom_level = view.zoom_level - 0.10f;
9097 else
9098 view.zoom_level = 1.0f;
9099 }
9100
9101 void action_view_encoding_activate (Gtk.Action action, Gtk.Action current) {
9102 var view = (View) get_current_tab ();
9103 var name = current.name;
9104
9105 string? encoding = null;
9106 switch (name) {
9107 case "EncodingAutomatic":
9108 encoding = null;
9109 break;
9110 case "EncodingChinese":
9111 encoding = "BIG5";
9112 break;
9113 case "EncodingChineseSimplified":
9114 encoding = "GB18030";
9115 break;
9116 case "EncodingJapanese":
9117 encoding = "SHIFT_JIS";
9118 break;
9119 case "EncodingKorean":
9120 encoding = "EUC-KR";
9121 break;
9122 case "EncodingRussian":
9123 encoding = "KOI8-R";
9124 break;
9125 case "EncodingUnicode":
9126 encoding = "UTF-8";
9127 break;
9128 case "EncodingWestern":
9129 encoding = "ISO-8859-1";
9130 break;
9131 default:
9132 assert_not_reached ();
9133 }
9134#if HAVE_WEBKIT2
9135 view.web_view.set_custom_charset (encoding);
9136#else
9137 view.web_view.set_custom_encoding (encoding);
9138#endif
9139 }
9140
9141 void action_source_view (Gtk.Action action, bool use_dom) {
9142 var view = (View) get_current_tab ();
9143#if HAVE_WEBKIT2
9144 /* TODO: midori_view_save_source isn't async and not WebKit2-friendly */
9145 var source = new View.with_item (null, settings);
9146 var source_view = source.web_view;
9147 source.view_source = true;
9148 source_view.load_uri (view.uri);
9149 add_tab (source);
9150#else
9151 var filename = view.save_source (null, null, use_dom);
9152 var text_editor = settings.text_editor;
9153 if (text_editor == null || text_editor == "") {
9154 try {
9155 var source_uri = Filename.to_uri (filename, null);
9156 var source = new View.with_item (null, settings);
9157 var source_view = source.web_view;
9158 source.view_source = true;
9159 source_view.load_uri (source_uri);
9160 add_tab (source);
9161 } catch (Error e) {}
9162 } else
9163 Sokoke.spawn_program (text_editor, true, filename, true, false);
9164#endif
9165 }
9166
9167 void action_source_view_activate (Gtk.Action action) {
9168 action_source_view (action, false);
9169 }
9170
9171 void action_source_view_dom_activate (Gtk.Action action) {
9172 action_source_view (action, true);
9173 }
9174
9175 void action_caret_browsing_activate (Gtk.Action action) {
9176 int response;
9177
9178 if (!settings.enable_caret_browsing) {
9179 var dialog = new Gtk.MessageDialog (this, Gtk.DialogFlags.DESTROY_WITH_PARENT,
9180 Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE, _("Toggle text cursor navigation"));
9181 dialog.title = _("Toggle text cursor navigation");
9182 dialog.format_secondary_text (_("Pressing F7 toggles Caret Browsing. When active, a text cursor appears in all websites."));
9183 dialog.add_buttons (Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
9184 _("_Enable Caret Browsing"), Gtk.ResponseType.ACCEPT);
9185
9186 response = Midori.Dialog.run (dialog);
9187 dialog.destroy ();
9188
9189 if (response != Gtk.ResponseType.ACCEPT)
9190 return;
9191 }
9192
9193 settings.enable_caret_browsing = !settings.enable_caret_browsing;
9194 }
9195
9196 void action_fullscreen_activate (Gtk.Action action) {
9197 var window = get_window ();
9198 if (window == null)
9199 return;
9200
9201 var state = window.get_state ();
9202 if ((state & Gdk.WindowState.FULLSCREEN) != 0) {
9203 if (settings.show_menubar)
9204 menubar.show ();
9205
9206 if (settings.show_panel)
9207 panel.show ();
9208
9209 if (settings.show_bookmarkbar)
9210 bookmarkbar.show ();
9211
9212 if (show_navigationbar)
9213 navigationbar.show ();
9214
9215 if (show_statusbar)
9216 statusbar.show ();
9217
9218 toggle_tabbar_smartly (true);
9219
9220 unfullscreen ();
9221 } else {
9222 menubar.hide ();
9223 panel.hide ();
9224 bookmarkbar.hide ();
9225 navigationbar.hide ();
9226 statusbar.hide ();
9227 notebook.labels_visible = false;
9228
9229 fullscreen ();
9230 }
9231 }
9232
9233 void action_scroll_somewhere_activate (Gtk.Action action) {
9234#if !HAVE_WEBKIT2
9235 var view = get_current_tab ();
9236 var web_view = view.web_view;
9237 var name = action.name;
9238
9239 switch (name) {
9240 case "ScrollLeft":
9241 web_view.move_cursor (Gtk.MovementStep.VISUAL_POSITIONS, -1);
9242 break;
9243 case "ScrollDown":
9244 web_view.move_cursor (Gtk.MovementStep.DISPLAY_LINES, 1);
9245 break;
9246 case "ScrollUp":
9247 web_view.move_cursor (Gtk.MovementStep.DISPLAY_LINES, -1);
9248 break;
9249 case "ScrollRight":
9250 web_view.move_cursor (Gtk.MovementStep.VISUAL_POSITIONS, 1);
9251 break;
9252 }
9253#endif
9254 }
9255
9256 void action_readable_activate (Gtk.Action action) {
9257 var view = (View) get_current_tab ();
9258
9259 var filename = Paths.get_res_filename ("faq.css");
9260 string stylesheet = null;
9261 try {
9262 FileUtils.get_contents (filename, out stylesheet);
9263 } catch (Error e) {
9264#if G_OS_WIN32
9265 filename = Paths.get_data_filename ("doc/midori/faq.css", false);
9266#else
9267 filename = Path.build_filename (DOCDIR, "faq.css");
9268#endif
9269 try {
9270 FileUtils.get_contents (filename, out stylesheet);
9271 } catch (Error e2) {
9272 warning (e2.message);
9273 }
9274 }
9275
9276 if (stylesheet == null || stylesheet == "") {
9277 view.add_info_bar (Gtk.MessageType.ERROR, "Stylesheet faq.css not found", null, view,
9278 Gtk.Stock.OK, Gtk.ResponseType.ACCEPT);
9279 return;
9280 }
9281
9282 /* Replace line breaks with spaces and change all single quotes to double quotes */
9283 stylesheet.replace ("\n", " ").replace ("\r", " ").replace ("\'", "\"");
9284
9285 tab.inject_stylesheet (stylesheet);
9286 }
9287
9288 void action_navigation_activate (Gtk.Action action) {
9289 navigation_activate (action);
9290 }
9291
9292 bool navigation_activate (Gtk.Action action) {
9293 bool middle_click = false;
9294
9295 assert (action is Gtk.Action);
9296
9297 if (action.get_data<bool> ("midori-middle-click")) {
9298 middle_click = true;
9299 action.set_data<bool> ("midori-middle-click", false);
9300 }
9301
9302 var tab = (View) get_current_tab ();
9303 var name = action.name;
9304
9305 if (name == "NextForward")
9306 name = tab.can_go_forward () ? "Forward" : "Next";
9307
9308 if (name == "Back") {
9309 if (middle_click) {
9310 var web_view = tab.web_view;
9311#if HAVE_WEBKIT2
9312 var list = web_view.get_back_forward_list ();
9313 var item = list.get_back_item ();
9314 var back_uri = item.uri;
9315#else
9316 var list = web_view.get_back_forward_list ();
9317 var item = list.get_forward_item ();
9318 var back_uri = item.uri;
9319#endif
9320
9321 var new_view = add_uri (back_uri);
9322 set_current_tab_smartly (new_view);
9323 } else
9324 tab.go_back ();
9325
9326 return true;
9327 } else if (name == "Forward") {
9328 if (middle_click) {
9329 var web_view = tab.web_view;
9330#if HAVE_WEBKIT2
9331 var list = web_view.get_back_forward_list ();
9332 var item = list.get_forward_item ();
9333 var forward_uri = item.get_uri ();
9334#else
9335 var list = web_view.get_back_forward_list ();
9336 var item = list.get_forward_item ();
9337 var forward_uri = item.get_uri ();
9338#endif
9339
9340 var new_view = add_uri (forward_uri);
9341 set_current_tab_smartly (new_view);
9342 } else
9343 tab.go_forward ();
9344
9345 return true;
9346 } else if (name == "Previous") {
9347 /* Duplicate here because the URI pointer might change */
9348 var uri = tab.get_previous_page ();
9349
9350 if (middle_click) {
9351 var new_view = add_uri (uri);
9352 set_current_tab_smartly (new_view);
9353 } else
9354 tab.set_uri (uri);
9355
9356 return true;
9357 } else if (name == "Next") {
9358 /* Duplicate here because the URI pointer might change */
9359 var uri = tab.get_next_page ();
9360
9361 if (middle_click) {
9362 var new_view = add_uri (uri);
9363 set_current_tab_smartly (new_view);
9364 } else
9365 tab.set_uri (uri);
9366
9367 return true;
9368 } else if (name == "Homepage") {
9369 if (middle_click) {
9370 var new_view = add_uri ("about:home");
9371 set_current_tab_smartly (new_view);
9372 } else
9373 tab.set_uri ("about:home");
9374
9375 return true;
9376 }
9377 return false;
9378 }
9379
9380 void action_location_activate (Gtk.Action action) {
9381 if (!navigationbar.visible)
9382 navigationbar.show ();
9383 }
9384
9385 void action_location_focus_in (Gtk.Action action) {
9386 var screen = notebook.get_screen ();
9387 var icon_theme = Gtk.IconTheme.get_for_screen (screen);
9388 if (icon_theme.has_icon ("go-jump-symbolic"))
9389 ((LocationAction) action).secondary_icon = "go-jump-symbolic";
9390 else
9391 ((LocationAction) action).secondary_icon = Gtk.Stock.JUMP_TO;
9392 }
9393
9394 void action_location_focus_out (Gtk.Action action) {
9395 var view = (View) get_current_tab ();
9396
9397 if (!show_navigationbar || is_fullscreen ())
9398 navigationbar.hide ();
9399
9400 update_secondary_icon (view, action);
9401 }
9402
9403 void action_location_reset_uri (Gtk.Action action) {
9404 ((LocationAction) action).set_text (get_current_uri ());
9405 }
9406
9407#if !HAVE_WEBKIT2
9408 void item_icon_loaded_cb (WebKit.FaviconDatabase database, string frame_uri, Katze.Item item) {
9409 var uri = item.get_data<string> ("browser-queue-icon");
9410 if (frame_uri != uri)
9411 return;
9412
9413 var icon_uri = database.get_favicon_uri (frame_uri);
9414 if (icon_uri != null) {
9415 item.icon = frame_uri;
9416 /* This signal fires extremely often (WebKit bug?)
9417 we must throttle it (disconnect) once we have an icon */
9418 SignalHandler.disconnect (database, database.get_data<ulong> ("handler-icon-loaded"));
9419 }
9420 }
9421#endif
9422
9423 void queue_item_for_icon (Katze.Item item, string uri) {
9424#if !HAVE_WEBKIT2
9425 if (item.icon != null)
9426 return;
9427 item.set_data<string> ("browser-queue-icon", uri);
9428 var database = WebKit.get_favicon_database ();
9429 database.set_data<ulong> ("handler-icon-loaded", database.icon_loaded.connect ((database, frame_uri) =>
9430 item_icon_loaded_cb (database, frame_uri, item)));
9431#endif
9432 }
9433
9434 Sqlite.Statement? location_submit_uri_stmt = null;
9435 void action_location_submit_uri (Gtk.Action action, string uri, bool new_tab) {
9436 /* Switch to already open tab if possible */
9437 var found = (Katze.Item) proxy_array.find_uri (uri);
9438 if (found != null && !new_tab && get_current_uri () != uri) {
9439 var view = get_current_tab ();
9440 set_current_item (found);
9441 if (view.is_blank ())
9442 close_tab (view);
9443 return;
9444 }
9445
9446 uri = Katze.skip_whitespace (uri);
9447 var new_uri = Sokoke.magic_uri (uri, true, false);
9448 if (new_uri == null) {
9449 string? keywords = null;
9450 string? search_uri = null;
9451 Katze.Item item = null;
9452
9453 /* Do we have a keyword and a string? */
9454 if (search_engines != null && (item = (Katze.Item) search_engines.find_token (uri)) != null) {
9455 var space_index = uri.index_of (" ");
9456 if (space_index > -1)
9457 keywords = uri.substring (space_index + 1);
9458 else
9459 keywords = "";
9460 search_uri = item.uri;
9461 }
9462
9463 if (keywords == null) {
9464 keywords = uri;
9465 search_uri = settings.location_entry_search;
9466 }
9467 new_uri = URI.for_search (search_uri, keywords);
9468
9469 if (search_engines != null && item != null)
9470 queue_item_for_icon (item, new_uri);
9471
9472 if (history != null) {
9473 var now = time_t ();
9474 var day = Sokoke.time_t_to_julian (ref now);
9475 unowned Sqlite.Database db = history.get_data<Sqlite.Database> ("db");
9476
9477 if (location_submit_uri_stmt == null) {
9478 var sqlcmd = "INSERT INTO search (keywords, uri, day) VALUES (?,?,?)";
9479 db.prepare_v2 (sqlcmd, sqlcmd.length + 1, out location_submit_uri_stmt);
9480 }
9481 location_submit_uri_stmt.bind_text (1, keywords);
9482 location_submit_uri_stmt.bind_text (2, search_uri);
9483 location_submit_uri_stmt.bind_int64 (3, day);
9484
9485 if (location_submit_uri_stmt.step () != Sqlite.DONE)
9486 printerr (_("Failed to insert new history item: %s\n"), db.errmsg ());
9487
9488 location_submit_uri_stmt.reset ();
9489 if (location_submit_uri_stmt.step () == Sqlite.DONE)
9490 location_submit_uri_stmt.clear_bindings ();
9491 }
9492 }
9493
9494 if (new_tab) {
9495 var view = add_uri (new_uri);
9496 set_current_tab (view);
9497 } else
9498 set_current_uri (new_uri);
9499 get_current_tab ().grab_focus ();
9500 }
9501
9502 void news_feed_clicked_cb (Gtk.Widget menuitem) {
9503 var uri = menuitem.get_data<string> ("uri");
9504 subscribe_to_news_feed (uri);
9505 }
9506
9507 bool action_location_secondary_icon_released (Gtk.Action action, Gtk.Widget widget) {
9508 var view = (View) get_current_tab ();
9509 var uri = view.get_display_uri ();
9510 string? feed = null;
9511 /* Clicking icon on blank is equal to Paste and Proceed */
9512 if (view.is_blank ()) {
9513 var clipboard = Gtk.Clipboard.get_for_display (view.get_display (), Gdk.SELECTION_CLIPBOARD);
9514 var text = clipboard.wait_for_text ();
9515 if (text != null)
9516 action_location_submit_uri (action, text, false);
9517 } else if (get_focus () == widget) {
9518 var text = ((Gtk.Entry) widget).text;
9519 action_location_submit_uri (action, text, false);
9520 } else if ((feed = view.get_data<string> ("news-feeds")) != null) {
9521 Katze.Item itemm;
9522
9523 var news_feeds = view.news_feeds;
9524 var item = news_feeds.get_nth_item (0);
9525 if ((itemm = news_feeds.get_nth_item (1)) != null) {
9526 Gtk.MenuItem menuitem;
9527
9528 var menu = new Gtk.Menu ();
9529 menuitem = new Gtk.MenuItem.with_label (item.name);
9530 menuitem.set_data<string> ("uri", item.uri);
9531 menuitem.activate.connect (news_feed_clicked_cb);
9532 menu.append (menuitem);
9533 menuitem = new Gtk.MenuItem.with_label (itemm.name);
9534 menuitem.set_data<string> ("uri", itemm.uri);
9535 menuitem.activate.connect (news_feed_clicked_cb);
9536 menu.append (menuitem);
9537
9538 var i = 2;
9539 while ((itemm = news_feeds.get_nth_item (i++)) != null) {
9540 menuitem = new Gtk.MenuItem.with_label (itemm.name);
9541 menuitem.set_data<string> ("uri", itemm.uri);
9542 menuitem.activate.connect (news_feed_clicked_cb);
9543 menu.append (menuitem);
9544 }
9545 menu.@foreach ((widget) => widget.show_all ());
9546 Katze.widget_popup (widget, menu, null,
9547 Katze.MenuPos.RIGHT);
9548 } else
9549 subscribe_to_news_feed (feed);
9550 } else
9551 action_location_submit_uri (action, uri, false);
9552 return true;
9553 }
9554
9555 void action_search_submit (Gtk.Action action, string keywords, bool new_tab) {
9556 string url;
9557 var item = search_engines.get_nth_item (last_web_search);
9558 if (item != null)
9559 url = item.uri;
9560 else /* The location entry search is our fallback */
9561 url = settings.location_entry_search;
9562
9563 var search = URI.for_search (url, keywords);
9564 if (item != null)
9565 queue_item_for_icon (item, search);
9566
9567 if (new_tab) {
9568 var view = add_uri (search);
9569 set_current_tab_smartly (view);
9570 } else
9571 set_current_uri (search);
9572 }
9573
9574 void action_search_activate (Gtk.Action action) {
9575 unowned SList<Gtk.Widget> proxies = action.get_proxies ();
9576 foreach (var proxy in proxies) {
9577 if (proxy is Gtk.ToolItem) {
9578 if (!navigationbar.visible)
9579 navigationbar.show ();
9580 return;
9581 }
9582 }
9583
9584 set_current_uri ("about:search");
9585 get_current_tab ().grab_focus ();
9586 }
9587
9588 void action_search_notify_current_item (Object action, ParamSpec pspec) {
9589 var item = ((SearchAction) action).current_item;
9590 var idx = (item != null ? search_engines.get_item_index (item) : 0);
9591
9592 settings.last_web_search = idx;
9593 last_web_search = idx;
9594 }
9595
9596 void action_search_notify_default_item (Object action, ParamSpec pspec) {
9597 var item = ((SearchAction) action).default_item;
9598 if (item != null)
9599 settings.location_entry_search = item.uri;
9600 }
9601
9602 void action_search_focus_out (Gtk.Action action) {
9603 if ((statusbar.visible && !show_navigationbar) || is_fullscreen ())
9604 navigationbar.hide ();
9605 }
9606
9607 void bookmark_open_activate_cb (Gtk.Action action) {
9608 open_bookmark (action.get_data<Katze.Item> ("KatzeItem"));
9609 }
9610
9611 void bookmark_open_in_tab_activate_cb (Gtk.Action action) {
9612 string uri;
9613 var item = action.get_data<Katze.Item> ("KatzeItem");
9614 if (item is Katze.Array) {
9615 var array = bookmarks.query_recursive ("*", "parentid = %q", item.get_meta_string ("id"), false);
9616
9617 foreach (var child in array.get_items ()) {
9618 if ((uri = child.uri) != null && child.uri != "") {
9619 var view = add_item (child);
9620 set_current_tab_smartly (view);
9621 }
9622 }
9623 } else {
9624 uri = item.uri;
9625 if (uri != null && uri != "") {
9626 var view = add_item (item);
9627 set_current_tab_smartly (view);
9628 }
9629 }
9630 }
9631
9632 void bookmark_open_in_window_activate_cb (Gtk.Action action) {
9633 var item = action.get_data<Katze.Item> ("KatzeItem");
9634 view_new_window_cb (null, item.uri);
9635 }
9636
9637 void bookmark_edit_activate_cb (Gtk.Action action) {
9638 var item = action.get_data<Katze.Item> ("KatzeItem");
9639
9640 if (Katze.item_is_bookmark (item))
9641 edit_bookmark_dialog_new (item, false, false, this);
9642 else
9643 edit_bookmark_dialog_new (item, false, true, this);
9644 }
9645
9646 void bookmark_delete_activate_cb (Gtk.Action action) {
9647 var item = action.get_data<Katze.Item> ("KatzeItem");
9648 bookmarks.remove_item (item);
9649 }
9650
9651 void bookmark_popup (Gtk.Widget widget, Gdk.EventButton? event, Katze.Item item) {
9652 var menu = new ContextAction ("BookmarkContextMenu", null, null, null);
9653 Gtk.Action action;
9654 if (Katze.item_is_folder (item)) {
9655 var child_bookmarks_count = bookmarks.count_recursive ("uri <> ''", null, item, false);
9656
9657 action = new Gtk.Action ("BookmarkOpenAllTabs", _("Open all in _Tabs"), null, Stock.TAB_NEW);
9658 action.sensitive = child_bookmarks_count > 0;
9659 action.set_data<Katze.Item> ("KatzeItem", item);
9660 action.activate.connect (bookmark_open_in_tab_activate_cb);
9661 menu.add (action);
9662 } else {
9663 action = new Gtk.Action ("BookmarkOpen", null, null, Gtk.Stock.OPEN);
9664 action.sensitive = item != null;
9665 action.set_data<Katze.Item> ("KatzeItem", item);
9666 action.activate.connect (bookmark_open_activate_cb);
9667 menu.add (action);
9668
9669 action = new Gtk.Action ("BookmarkOpenTab", null, null, Stock.TAB_NEW);
9670 action.sensitive = item.uri != null;
9671 action.set_data<Katze.Item> ("KatzeItem", item);
9672 action.activate.connect (bookmark_open_in_tab_activate_cb);
9673 menu.add (action);
9674
9675 action = new Gtk.Action ("BookmarkOpenWindow", _("Open in New _Window"), null, Stock.WINDOW_NEW);
9676 action.sensitive = item.uri != null;
9677 action.set_data<Katze.Item> ("KatzeItem", item);
9678 action.activate.connect (bookmark_open_in_window_activate_cb);
9679 menu.add (action);
9680 }
9681
9682 menu.add (null);
9683
9684 action = new Gtk.Action ("BookmarkEdit", null, null, Gtk.Stock.EDIT);
9685 action.sensitive = !Katze.item_is_separator (item);
9686 action.set_data<Katze.Item> ("KatzeItem", item);
9687 action.activate.connect (bookmark_edit_activate_cb);
9688 menu.add (action);
9689
9690 action = new Gtk.Action ("BookmarkDelete", null, null, Gtk.Stock.EDIT);
9691 action.set_data<Katze.Item> ("KatzeItem", item);
9692 action.activate.connect (bookmark_delete_activate_cb);
9693 menu.add (action);
9694
9695 var context_menu = menu.create_menu (null, false);
9696 Katze.widget_popup (widget, context_menu, event, Katze.MenuPos.CURSOR);
9697 }
9698
9699 bool menu_button_press_event_cb (Gtk.Widget toolitem, Gdk.EventButton event) {
9700 if (event.button != 3)
9701 return false;
9702
9703 /* GtkMenuBar catches button events on children with submenus,
9704 so we need to see if the actual widget is the menubar, and if
9705 it is an item, we forward it to the actual widget. */
9706 if (toolitem is Gtk.Box || toolitem is Gtk.MenuBar) {
9707 if (toolitem.get_window () != event.window)
9708 return false;
9709
9710 toolbar_popup_context_menu_cb (toolitem is Gtk.Bin && ((Gtk.Bin) toolitem).get_child () != null ?
9711 toolitem.get_parent () : toolitem,
9712 (int) event.x, (int) event.y, (int) event.button);
9713 return true;
9714 } else if (toolitem is Gtk.MenuItem)
9715 return toolitem.button_press_event (event);;
9716
9717 return false;
9718 }
9719
9720 bool menu_item_middle_click_event_cb (Gtk.Widget toolitem, Gdk.EventButton event) {
9721 if (event_new_tab (event)) {
9722 var action = ((Gtk.ToolItem) toolitem).related_action;
9723 action.set_data<bool> ("midori-middle-click", true);
9724
9725 return navigation_activate (action);
9726 }
9727 return false;
9728 }
9729
9730 void action_bookmark_add_activate (Gtk.Action action) {
9731 Gtk.Widget proxy = null;
9732 foreach (var p in action.get_proxies ()) {
9733 if (p is Gtk.ToolItem) {
9734 proxy = p;
9735 break;
9736 }
9737 }
9738
9739 if (action.name == "BookmarkFolderAdd")
9740 edit_bookmark_dialog_new (null, true, true, proxy);
9741 else
9742 edit_bookmark_dialog_new (null, true, false, proxy);
9743 }
9744
9745 struct BookmarkClient {
9746 string path;
9747 string name;
9748 string icon;
9749 }
9750
9751 static const BookmarkClient[] bookmark_clients = {
9752 { ".local/share/data/Arora/bookmarks.xbel", N_("Arora"), "arora" },
9753 { ".kazehakase/bookmarks.xml", N_("Kazehakase"), "kazehakase-icon" },
9754 { ".opera/bookmarks.adr", N_("Opera"), "opera" },
9755 { ".kde/share/apps/konqueror/bookmarks.xml", N_("Konqueror"), "konqueror" },
9756 { ".gnome2/epiphany/bookmarks.rdf", N_("Epiphany"), "epiphany" },
9757 { ".mozilla/firefox/*/bookmarks.html", N_("Firefox (%s)"), "firefox" },
9758 { ".config/midori/bookmarks.xbel", N_("Midori 0.2.6"), "midori" },
9759 };
9760
9761 void action_bookmarks_import_activate (Gtk.Action action) {
9762 var dialog = new Gtk.Dialog.with_buttons (_("Import bookmarks…"), this,
9763 Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
9764 _("_Import bookmarks"), Gtk.ResponseType.ACCEPT);
9765 var content_area = dialog.get_content_area ();
9766 dialog.icon_name = Stock.BOOKMARKS;
9767
9768 dialog.border_width = 5;
9769 content_area.border_width = 5;
9770 var sizegroup = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL);
9771
9772 var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 8);
9773 hbox.border_width = 5;
9774 var label = new Gtk.Label.with_mnemonic (_("_Application:"));
9775 sizegroup.add_widget (label);
9776 hbox.pack_start (label, false, false, 0);
9777 var model = new Gtk.ListStore (4, typeof (string), typeof (string), typeof (string), typeof (int));
9778 var combo = new Gtk.ComboBox.with_model (model);
9779 var pix_renderer = new Gtk.CellRendererPixbuf ();
9780 combo.pack_start (pix_renderer, false);
9781 combo.add_attribute (pix_renderer, "icon-name", 1);
9782 combo.add_attribute (pix_renderer, "width", 3);
9783 var text_renderer = new Gtk.CellRendererText ();
9784 combo.pack_start (text_renderer, true);
9785 combo.add_attribute (text_renderer, "text", 0);
9786
9787 int icon_width = 16;
9788 Gtk.icon_size_lookup_for_settings (get_settings (), Gtk.IconSize.MENU, out icon_width, null);
9789
9790 foreach (var client in bookmark_clients) {
9791 var location = client.path;
9792 var client_name = client.name;
9793
9794 /* Interpret * as 'any folder' */
9795 if (location.index_of ("*") > -1) {
9796 var parts = location.split ("*", 2);
9797 var path = Path.build_filename (Environment.get_home_dir (), parts[0]);
9798 try {
9799 var dir = Dir.open (path);
9800 string name;
9801 while ((name = dir.read_name ()) != null) {
9802 var file = Path.build_filename (path, name, parts[1]);
9803 if (Posix.access (file, Posix.F_OK) == 0) {
9804 /* If name is XYZ.Name, we use Name only */
9805 var real_name = name.split (".", 2);
9806 var display = _(client_name).index_of ("%s") > -1
9807 ? _(client_name).printf (real_name.length > 1 ? real_name[1] : name)
9808 : _(client_name);
9809 model.insert_with_values (null, int.MAX,
9810 0, display, 1, client.icon,
9811 2, file, 3, icon_width);
9812 }
9813 }
9814 } catch (Error e) {}
9815 continue;
9816 }
9817
9818 var path = Path.build_filename (Environment.get_home_dir (), location);
9819 if (Posix.access (path, Posix.F_OK) == 0)
9820 model.insert_with_values (null, int.MAX,
9821 0, _(client_name), 1, client.icon,
9822 2, path, 3, icon_width);
9823 }
9824
9825 model.insert_with_values (null, int.MAX,
9826 0, _("Import from XBEL or HTML file"), 1, null, 2, null, 3, icon_width);
9827 combo.active = 0;
9828 hbox.pack_start (combo, true, true, 0);
9829 content_area.pack_start (hbox, false, true, 0);
9830
9831 var combobox_folder = bookmark_folder_button_new (bookmarks, 0);
9832 content_area.pack_start (combobox_folder, false, true, 0);
9833 content_area.show_all ();
9834
9835 dialog.set_default_response (Gtk.ResponseType.ACCEPT);
9836 if (Midori.Dialog.run (dialog) == Gtk.ResponseType.ACCEPT) {
9837 Gtk.TreeIter iter;
9838 string? path = null;
9839
9840 if (combo.get_active_iter (out iter))
9841 model.@get (iter, 2, out path);
9842 var selected = combobox_folder.get_active ();
9843
9844 dialog.destroy ();
9845 if (path == null) {
9846 var file_dialog = new FileChooserDialog (_("Import from a file"), this, Gtk.FileChooserAction.OPEN);
9847 if (Midori.Dialog.run (file_dialog) == Gtk.ResponseType.OK)
9848 path = file_dialog.get_filename ();
9849 file_dialog.destroy ();
9850 }
9851
9852 var bookmarks = new Katze.Array (typeof (Katze.Array));
9853 if (path != null) {
9854 try {
9855 array_from_file (bookmarks, path, null);
9856 } catch (Error e) {
9857 Sokoke.message_dialog (Gtk.MessageType.ERROR, _("Failed to import bookmarks"), e.message, false);
9858 }
9859 }
9860 this.bookmarks.import_array (bookmarks, selected);
9861 } else
9862 dialog.destroy ();
9863 }
9864
9865 void action_bookmarks_export_activate (Gtk.Action action) {
9866 string? path = null;
9867 string format;
9868
9869 var wrong_format = true;
9870
9871 while (wrong_format) {
9872 var file_dialog = new FileChooserDialog (_("Save file as"), this, Gtk.FileChooserAction.SAVE);
9873 file_dialog.set_current_name ("bookmarks.xbel");
9874
9875 Gtk.FileFilter filter;
9876
9877 filter = new Gtk.FileFilter ();
9878 filter.set_filter_name (_("XBEL Bookmarks"));
9879 filter.add_mime_type ("application/xml");
9880 filter.add_pattern ("*.xbel");
9881 file_dialog.add_filter (filter);
9882
9883 filter = new Gtk.FileFilter ();
9884 filter.set_name (_("Netscape Bookmarks"));
9885 filter.add_mime_type ("text/html");
9886 filter.add_pattern ("*.html");
9887 file_dialog.add_filter (filter);
9888
9889 if (Dialog.run (file_dialog) == Gtk.ResponseType.OK)
9890 path = file_dialog.get_filename ();
9891 file_dialog.destroy ();
9892
9893 if (path == null)
9894 return;
9895
9896 if (path.has_suffix (".xbel"))
9897 format = "xbel";
9898 else if (path.has_suffix (".html"))
9899 format = "netscape";
9900 else {
9901 Sokoke.message_dialog (Gtk.MessageType.ERROR,
9902 _("Midori can only export to XBEL (*.xbel) and Netscape (*.html)"), "", true);
9903 path = null;
9904 continue;
9905 }
9906
9907 wrong_format = false;
9908
9909 var bookmarks = bookmarks.query_recursive ("*", "parentid IS null", null, true);
9910 try {
9911 array_to_file (bookmarks, path, format);
9912 } catch (Error e) {
9913 Sokoke.message_dialog (Gtk.MessageType.ERROR, _("Failed to export bookmarks"), e.message, false);
9914 }
9915 }
9916 }
9917
9918 Gtk.Dialog? search_engines_dialog = null;
9919 void action_manage_search_engines_activate (Gtk.Action action) {
9920 if (search_engines_dialog == null) {
9921 search_engines_dialog = ((SearchAction) action_group.get_action ("Search")).get_dialog ();
9922 search_engines_dialog.destroy.connect (() => search_engines_dialog = null);
9923 search_engines_dialog.show ();
9924 }
9925 else
9926 search_engines_dialog.present ();
9927 }
9928
9929 Gtk.Dialog? clear_private_data_dialog = null;
9930 void action_clear_private_data_activate (Gtk.Action action) {
9931 if (clear_private_data_dialog == null) {
9932 clear_private_data_dialog = private_data_get_dialog (this);
9933 clear_private_data_dialog.destroy.connect (() => clear_private_data_dialog = null);
9934 clear_private_data_dialog.show ();
9935 } else
9936 clear_private_data_dialog.present ();
9937 }
9938
9939 void action_inspect_page_activate (Gtk.Action action) {
9940 var view = get_current_tab ();
9941 var web_view = view.web_view;
9942 var inspector = web_view.get_inspector ();
9943 inspector.show ();
9944 }
9945
9946 void action_tab_move_activate (Gtk.Action action) {
9947 var name = action.name;
9948 int new_pos;
9949 var cur_pos = get_current_page ();
9950 var widget = get_nth_tab (cur_pos);
9951
9952 if (name == "TabMoveFirst")
9953 new_pos = 0;
9954 else if (name == "TabMoveBackward") {
9955 if (cur_pos > 0)
9956 new_pos = cur_pos - 1;
9957 else
9958 new_pos = get_n_pages () - 1;
9959 } else if (name == "TabMoveForward") {
9960 if (cur_pos == (get_n_pages () - 1))
9961 new_pos = 0;
9962 else
9963 new_pos = cur_pos + 1;
9964 } else if (name == "TabMoveLast")
9965 new_pos = get_n_pages () - 1;
9966 else
9967 assert_not_reached ();
9968
9969 notebook.move ((Tab) widget, new_pos);
9970 move_tab (notebook, cur_pos, new_pos);
9971 }
9972
9973 void action_tab_previous_activate (Gtk.Action action) {
9974 set_current_page (get_current_page () - 1);
9975 }
9976
9977 void action_tab_next_activate (Gtk.Action action) {
9978 /* Advance one tab or jump to the first one if we are at the last one */
9979 var n = get_current_page ();
9980 if (n == get_n_pages () - 1)
9981 n = -1;
9982 set_current_page (n + 1);
9983 }
9984
9985 void action_tab_current_activate (Gtk.Action action) {
9986 var view = get_current_tab ();
9987 view.grab_focus ();
9988 }
9989
9990 void action_next_view_activate (Gtk.Action action) {
9991 get_current_tab ().grab_focus ();
9992 }
9993
9994 void action_tab_minimize_activate (Gtk.Action action) {
9995 var view = get_current_tab ();
9996 view.minimized = !view.minimized;
9997 }
9998
9999 void action_tab_duplicate_activate (Gtk.Action action) {
10000 var view = (View) action.get_data<Tab> ("tab");
10001 if (view == null)
10002 view = (View) get_current_tab ();
10003 view.duplicate ();
10004 }
10005
10006 void action_tab_close_other_activate (Gtk.Action action) {
10007 var view = get_current_tab ();
10008 foreach (var tab in get_tabs ()) {
10009 if (tab != view)
10010 close_tab (tab);
10011 }
10012 }
10013
10014 static const string[] credits_authors = { "Christian Dywan <christian@twotoasts.de>" };
10015 static const string[] credits_documenters = { "Christian Dywan <christian@twotoasts.de>" };
10016 static const string[] credits_artists = { "Nancy Runge <nancy@twotoasts.de>" };
10017
10018 string get_docs (bool error)
10019 {
10020#if G_OS_WIN32
10021 var path = Paths.get_data_filename ("doc/midori/faq.html", false);
10022 try {
10023 return Filename.to_uri (path, null);
10024 } catch (Error e) {}
10025
10026 return "";
10027#else
10028 var path = Paths.get_res_filename ("faq.html");
10029 bool found = Posix.access (path, Posix.F_OK) == 0;
10030 if (found) {
10031 try {
10032 return Filename.to_uri (path, null);
10033 } catch (Error e) {}
10034 }
10035 return "file://" + DOCDIR + "/faq.html";
10036#endif
10037 }
10038
10039 void action_about_activate (Gtk.Action action) {
10040 var comments = "%s\n%s".printf (
10041 _("A lightweight web browser."),
10042 _("See about:version for version info."));
10043 var license =
10044 _("This library is free software; you can redistribute it and/or " +
10045 "modify it under the terms of the GNU Lesser General Public " +
10046 "License as published by the Free Software Foundation; either " +
10047 "version 2.1 of the License, or (at your option) any later version.");
10048
10049#if HAVE_GRANITE
10050 var docs = get_docs (false);
10051 /* Avoid granite_widgets_show_about_dialog for invalid memory and crashes */
10052 /* FIXME: granite: should return GtkWidget* like GTK+ */
10053 var dialog = new Granite.Widgets.AboutDialog ();
10054 dialog.@set (
10055 "translate", "https://translations.xfce.org/projects/p/midori/",
10056 "bug", PACKAGE_BUGREPORT,
10057 "help", docs,
10058 "copyright", "2007-2013 Christian Dywan",
10059#else
10060 var dialog = new Gtk.AboutDialog ();
10061 dialog.@set (
10062 "wrap-license", true,
10063 "copyright", "Copyright © 2007-2013 Christian Dywan",
10064#endif
10065 "transient-for", this,
10066 "logo-icon-name", icon_name,
10067 "program-name", PACKAGE_NAME,
10068 "version", PACKAGE_VERSION,
10069 "comments", comments,
10070 "website", "http://www.midori-browser.org",
10071 "authors", credits_authors,
10072 "documenters", credits_documenters,
10073 "artists", credits_artists,
10074 "license", license,
10075 "translator-credits", _("translator-credits"));
10076 dialog.show ();
10077 dialog.response.connect ((dialog, response) => dialog.destroy ());
10078 }
10079
10080 void action_help_link_activate (Gtk.Action action) {
10081 var action_name = action.name;
10082 string? uri = null;
10083 if (action_name.has_prefix ("HelpFAQ"))
10084 uri = get_docs (true);
10085 else if (action_name.has_prefix ("HelpBugs")) {
10086 try {
10087 Process.spawn_command_line_async ("ubuntu-bug " + PACKAGE_NAME);
10088 } catch (Error e) {
10089 uri = PACKAGE_BUGREPORT;
10090 }
10091 }
10092
10093 if (uri != null)
10094 set_current_tab (add_uri (uri));
10095 }
10096
10097 void action_panel_activate (Gtk.Action action) {
10098 var active = ((Gtk.ToggleAction) action).active;
10099 settings.show_panel = active;
10100 Sokoke.widget_set_visible (panel, active);
10101 }
10102
10103 bool panel_timeout (Gtk.Paned hpaned) {
10104 settings.last_panel_position = hpaned.position;
10105 _panel_timeout = 0;
10106 return false;
10107 }
10108
10109 void panel_notify_position_cb (Object hpaned, ParamSpec pspec) {
10110 if (_panel_timeout == 0)
10111 _panel_timeout = Timeout.add_seconds (5, () => panel_timeout ((Gtk.Paned) hpaned));
10112 }
10113
10114 bool panel_cycle_child_focus_cb (Gtk.Widget hpaned, bool reversed) {
10115 /* Default cycle goes between all GtkPaned widgets.
10116 If focus is in the panel, focus the location as if it's a paned.
10117 If nothing is focussed, simply go to the location.
10118 Be sure to suppress the default because the signal can recurse. */
10119 var focus = get_focus ();
10120 if (focus.get_ancestor (typeof (Panel)) != null
10121 || focus.get_ancestor (typeof (Gtk.Paned)) == null) {
10122 Signal.stop_emission_by_name (hpaned, "cycle-child-focus");
10123 activate_action ("Location");
10124 return true;
10125 }
10126 return false;
10127 }
10128
10129 void panel_notify_page_cb (Object panel, ParamSpec pspec) {
10130 var page = ((Panel) panel).page;
10131 if (page > -1)
10132 settings.last_panel_page = page;
10133 }
10134
10135 void panel_notify_show_titles_cb (Object object, ParamSpec pspec) {
10136 var panel = (Panel) object;
10137 var signal_id = settings.get_data<ulong> ("handler-settings-notify");
10138
10139 SignalHandler.block (settings, signal_id);
10140 settings.compact_sidepanel = !panel.show_titles;
10141 SignalHandler.unblock (settings, signal_id);
10142 }
10143
10144 void panel_notify_right_aligned_cb (Object object, ParamSpec pspec) {
10145 var panel = (Panel) object;
10146
10147 var right_aligned = panel.right_aligned;
10148 var hpaned = (Gtk.Paned) panel.get_parent ();
10149 var vpaned = notebook.get_parent ();
10150 var paned_position = hpaned.position;
10151
10152 Gtk.Allocation allocation;
10153 hpaned.get_allocation (out allocation);
10154 var paned_size = allocation.width;
10155
10156 settings.right_align_sidepanel = right_aligned;
10157
10158 panel.@ref ();
10159 vpaned.@ref ();
10160 hpaned.remove (panel);
10161 hpaned.remove (vpaned);
10162 if (right_aligned) {
10163 hpaned.pack1 (vpaned, true, false);
10164 hpaned.pack2 (panel, false, false);
10165 } else {
10166 hpaned.pack1 (panel, false, false);
10167 hpaned.pack2 (vpaned, true, false);
10168 }
10169 hpaned.position = paned_size - paned_position;
10170 panel.unref ();
10171 vpaned.unref ();
10172 }
10173
10174 bool panel_close_cb (Panel panel) {
10175 action_set_active ("Panel", false);
10176 return false;
10177 }
10178
10179 void switched_tab_cb (Notebook notebook, Tab? old_tab, Tab new_tab) {
10180 if (old_tab != null) {
10181 var action = action_group.get_action ("Location");
10182 var text = ((LocationAction) action).get_text ();
10183 old_tab.set_data<string> ("midori-browser-typed-text", text);
10184 }
10185
10186 return_if_fail (new_tab is View);
10187 return_if_fail (new_tab != old_tab);
10188
10189 var new_view = (View) new_tab;
10190 var uri = new_view.get_data<string> ("midori-browser-typed-text");
10191 if (uri == null)
10192 uri = new_view.get_display_uri ();
10193 title = new_view.get_display_title ();
10194 var action = (LocationAction) action_group.get_action ("Location");
10195 action.set_text (uri);
10196 if (Paths.get_runtime_mode () == RuntimeMode.APP)
10197 icon = new_view.get_icon ();
10198
10199 switch_tab (old_tab, new_view);
10200 _set_statusbar_text (new_view, null);
10201 update_interface (new_view);
10202 update_progress (new_view);
10203 }
10204
10205 void notify_tab_cb (Object? object, ParamSpec? pspec) {
10206 freeze_notify ();
10207 notify_property ("uri");
10208 notify_property ("uri");
10209 notify_property ("title");
10210 notify_property ("tab");
10211 thaw_notify ();
10212 }
10213
10214 void tab_moved_cb (Gtk.Widget notebook, Tab tab, uint page_num) {
10215 var item = ((View) tab).get_proxy_item ();
10216 proxy_array.move_item (item, (int) page_num);
10217 }
10218
10219 void notebook_create_window_cb (Gtk.Widget notebook, Gtk.Widget widget, int x, int y) {
10220 var view = (View) widget;
10221 var new_browser = new_window (null);
10222 if (new_browser != null) {
10223 new_browser.move (x, y);
10224 view.@ref ();
10225 disconnect_tab (view);
10226 ((Notebook) notebook).remove (view);
10227 new_browser.add_tab (view);
10228 view.unref ();
10229 }
10230 }
10231
10232 void notebook_new_tab_cb (Gtk.Widget notebook) {
10233 var view = add_uri ("about:new");
10234 set_current_tab (view);
10235 }
10236
10237 void notebook_context_menu_cb (Notebook notebook, ContextAction menu) {
10238 menu.add_action_group (action_group);
10239 menu.add (null);
10240 menu.add_by_name ("TabNew");
10241 menu.add_by_name ("UndoTabClose");
10242 }
10243
10244 void notebook_tab_context_menu_cb (Notebook notebook, Tab tab, ContextAction menu) {
10245 menu.add_action_group (action_group);
10246 menu.add (null);
10247 menu.add_by_name ("TabNew");
10248 menu.add_by_name ("UndoTabClose");
10249 if (tab is View) {
10250 var action = new Gtk.Action ("TabDuplicate", _("_Duplicate Current Tab"), null, null);
10251 action.set_data<Tab> ("tab", tab);
10252 action.activate.connect (action_tab_duplicate_activate);
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches