aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/misc
diff options
context:
space:
mode:
authorDan Vrátil <dvratil@redhat.com>2012-03-29 00:37:35 +0800
committerDan Vrátil <dvratil@redhat.com>2012-03-29 00:37:35 +0800
commit6bd1c6833a2c51898ac45865767dd01ba66a95c5 (patch)
tree51f9cc360c49e71c455f74f72f1605965e73a932 /widgets/misc
parent038e0eccec595ce1cc39fe95262272e29d5a6fbf (diff)
downloadgsoc2013-evolution-6bd1c6833a2c51898ac45865767dd01ba66a95c5.tar
gsoc2013-evolution-6bd1c6833a2c51898ac45865767dd01ba66a95c5.tar.gz
gsoc2013-evolution-6bd1c6833a2c51898ac45865767dd01ba66a95c5.tar.bz2
gsoc2013-evolution-6bd1c6833a2c51898ac45865767dd01ba66a95c5.tar.lz
gsoc2013-evolution-6bd1c6833a2c51898ac45865767dd01ba66a95c5.tar.xz
gsoc2013-evolution-6bd1c6833a2c51898ac45865767dd01ba66a95c5.tar.zst
gsoc2013-evolution-6bd1c6833a2c51898ac45865767dd01ba66a95c5.zip
WebKit port - port widgets
Diffstat (limited to 'widgets/misc')
-rw-r--r--widgets/misc/Makefile.am9
-rw-r--r--widgets/misc/e-attachment-button.c58
-rw-r--r--widgets/misc/e-attachment-button.h4
-rw-r--r--widgets/misc/e-attachment-store.h1
-rw-r--r--widgets/misc/e-contact-map-window.c4
-rw-r--r--widgets/misc/e-contact-map.c4
-rw-r--r--widgets/misc/e-contact-marker.c4
-rw-r--r--widgets/misc/e-port-entry.c2
-rw-r--r--widgets/misc/e-preview-pane.c4
-rw-r--r--widgets/misc/e-search-bar.c129
-rw-r--r--widgets/misc/e-search-bar.h3
-rw-r--r--widgets/misc/e-searching-tokenizer.c1210
-rw-r--r--widgets/misc/e-searching-tokenizer.h91
-rw-r--r--widgets/misc/e-web-view-gtkhtml.c2301
-rw-r--r--widgets/misc/e-web-view-gtkhtml.h173
-rw-r--r--widgets/misc/e-web-view.c1402
-rw-r--r--widgets/misc/e-web-view.h79
17 files changed, 3560 insertions, 1918 deletions
diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am
index 1772e7af4b..ab7efa0667 100644
--- a/widgets/misc/Makefile.am
+++ b/widgets/misc/Makefile.am
@@ -49,7 +49,6 @@ widgetsinclude_HEADERS = \
e-preview-pane.h \
e-printable.h \
e-search-bar.h \
- e-searching-tokenizer.h \
e-selectable.h \
e-selection-model.h \
e-selection-model-array.h \
@@ -63,6 +62,7 @@ widgetsinclude_HEADERS = \
e-signature-tree-view.h \
e-url-entry.h \
e-web-view.h \
+ e-web-view-gtkhtml.h \
e-web-view-preview.h \
ea-calendar-cell.h \
ea-calendar-item.h \
@@ -76,13 +76,14 @@ libemiscwidgets_la_CPPFLAGS = \
-I$(top_srcdir)/widgets \
-DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\" \
-DEVOLUTION_UIDIR=\""$(uidir)"\" \
+ -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \
-DG_LOG_DOMAIN=__FILE__ \
$(EVOLUTION_DATA_SERVER_CFLAGS) \
$(GNOME_PLATFORM_CFLAGS) \
+ $(GTKHTML_CFLAGS) \
$(CHAMPLAIN_CFLAGS) \
$(GEOCLUE_CFLAGS) \
- $(CLUTTER_CFLAGS) \
- $(GTKHTML_CFLAGS)
+ $(CLUTTER_CFLAGS)
libemiscwidgets_la_SOURCES = \
$(widgetsinclude_HEADERS) \
@@ -130,7 +131,6 @@ libemiscwidgets_la_SOURCES = \
e-preview-pane.c \
e-printable.c \
e-search-bar.c \
- e-searching-tokenizer.c \
e-selectable.c \
e-selection-model.c \
e-selection-model-array.c \
@@ -144,6 +144,7 @@ libemiscwidgets_la_SOURCES = \
e-signature-tree-view.c \
e-url-entry.c \
e-web-view.c \
+ e-web-view-gtkhtml.c \
e-web-view-preview.c \
ea-calendar-cell.c \
ea-calendar-item.c \
diff --git a/widgets/misc/e-attachment-button.c b/widgets/misc/e-attachment-button.c
index 44bd372f13..0d46807307 100644
--- a/widgets/misc/e-attachment-button.c
+++ b/widgets/misc/e-attachment-button.c
@@ -355,27 +355,6 @@ attachment_button_toggle_button_press_event_cb (EAttachmentButton *button,
}
static void
-attachment_button_set_view (EAttachmentButton *button,
- EAttachmentView *view)
-{
- GtkWidget *popup_menu;
-
- g_return_if_fail (button->priv->view == NULL);
-
- button->priv->view = g_object_ref (view);
-
- popup_menu = e_attachment_view_get_popup_menu (view);
-
- g_signal_connect_swapped (
- popup_menu, "deactivate",
- G_CALLBACK (attachment_button_menu_deactivate_cb), button);
-
- /* Keep a reference to the popup menu so we can
- * disconnect the signal handler in dispose(). */
- button->priv->popup_menu = g_object_ref (popup_menu);
-}
-
-static void
attachment_button_set_property (GObject *object,
guint property_id,
const GValue *value,
@@ -401,7 +380,7 @@ attachment_button_set_property (GObject *object,
return;
case PROP_VIEW:
- attachment_button_set_view (
+ e_attachment_button_set_view (
E_ATTACHMENT_BUTTON (object),
g_value_get_object (value));
return;
@@ -566,8 +545,7 @@ e_attachment_button_class_init (EAttachmentButtonClass *class)
"View",
NULL,
E_TYPE_ATTACHMENT_VIEW,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY));
+ G_PARAM_READWRITE));
}
static void
@@ -701,13 +679,10 @@ e_attachment_button_init (EAttachmentButton *button)
}
GtkWidget *
-e_attachment_button_new (EAttachmentView *view)
+e_attachment_button_new ()
{
- g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), NULL);
-
return g_object_new (
- E_TYPE_ATTACHMENT_BUTTON,
- "view", view, NULL);
+ E_TYPE_ATTACHMENT_BUTTON, NULL);
}
EAttachmentView *
@@ -718,6 +693,31 @@ e_attachment_button_get_view (EAttachmentButton *button)
return button->priv->view;
}
+void
+e_attachment_button_set_view (EAttachmentButton *button,
+ EAttachmentView *view)
+{
+ GtkWidget *popup_menu;
+
+ g_return_if_fail (button->priv->view == NULL);
+
+ if (button->priv->view)
+ g_object_unref (button->priv->view);
+ button->priv->view = g_object_ref (view);
+
+ popup_menu = e_attachment_view_get_popup_menu (view);
+
+ g_signal_connect_swapped (
+ popup_menu, "deactivate",
+ G_CALLBACK (attachment_button_menu_deactivate_cb), button);
+
+ /* Keep a reference to the popup menu so we can
+ * disconnect the signal handler in dispose(). */
+ if (button->priv->popup_menu)
+ g_object_unref (button->priv->popup_menu);
+ button->priv->popup_menu = g_object_ref (popup_menu);
+}
+
EAttachment *
e_attachment_button_get_attachment (EAttachmentButton *button)
{
diff --git a/widgets/misc/e-attachment-button.h b/widgets/misc/e-attachment-button.h
index a27d8ae146..b8a0cbb16d 100644
--- a/widgets/misc/e-attachment-button.h
+++ b/widgets/misc/e-attachment-button.h
@@ -61,9 +61,11 @@ struct _EAttachmentButtonClass {
};
GType e_attachment_button_get_type (void);
-GtkWidget * e_attachment_button_new (EAttachmentView *view);
+GtkWidget * e_attachment_button_new (void);
EAttachmentView *
e_attachment_button_get_view (EAttachmentButton *button);
+void e_attachment_button_set_view (EAttachmentButton *button,
+ EAttachmentView *view);
EAttachment * e_attachment_button_get_attachment
(EAttachmentButton *button);
void e_attachment_button_set_attachment
diff --git a/widgets/misc/e-attachment-store.h b/widgets/misc/e-attachment-store.h
index 7309dd9415..a963f0558f 100644
--- a/widgets/misc/e-attachment-store.h
+++ b/widgets/misc/e-attachment-store.h
@@ -130,3 +130,4 @@ gchar ** e_attachment_store_save_finish (EAttachmentStore *store,
G_END_DECLS
#endif /* E_ATTACHMENT_STORE_H */
+
diff --git a/widgets/misc/e-contact-map-window.c b/widgets/misc/e-contact-map-window.c
index d80aa17359..38fe56ccdf 100644
--- a/widgets/misc/e-contact-map-window.c
+++ b/widgets/misc/e-contact-map-window.c
@@ -40,8 +40,8 @@
#include <glib-object.h>
#define E_CONTACT_MAP_WINDOW_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE \
- ((obj), E_TYPE_CONTACT_MAP, EContactMapWindowPrivate))
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_CONTACT_MAP_WINDOW, EContactMapWindowPrivate))
G_DEFINE_TYPE (EContactMapWindow, e_contact_map_window, GTK_TYPE_WINDOW)
diff --git a/widgets/misc/e-contact-map.c b/widgets/misc/e-contact-map.c
index b9c2a99905..6e20ac10c9 100644
--- a/widgets/misc/e-contact-map.c
+++ b/widgets/misc/e-contact-map.c
@@ -43,8 +43,8 @@
#include <math.h>
#define E_CONTACT_MAP_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE \
- ((obj), E_TYPE_CONTACT_MAP, EContactMapPrivate))
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_CONTACT_MAP, EContactMapPrivate))
G_DEFINE_TYPE (EContactMap, e_contact_map, GTK_CHAMPLAIN_TYPE_EMBED)
diff --git a/widgets/misc/e-contact-marker.c b/widgets/misc/e-contact-marker.c
index d7e5ad679c..98f1ea1cd4 100644
--- a/widgets/misc/e-contact-marker.c
+++ b/widgets/misc/e-contact-marker.c
@@ -39,8 +39,8 @@
#include <string.h>
#define E_CONTACT_MARKER_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE \
- ((obj), E_TYPE_CONTACT_MARKER, EContactMarkerPrivate))
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_CONTACT_MARKER, EContactMarkerPrivate))
G_DEFINE_TYPE (EContactMarker, e_contact_marker, CHAMPLAIN_TYPE_LABEL);
diff --git a/widgets/misc/e-port-entry.c b/widgets/misc/e-port-entry.c
index 470249a772..cf857b5d55 100644
--- a/widgets/misc/e-port-entry.c
+++ b/widgets/misc/e-port-entry.c
@@ -128,7 +128,7 @@ port_entry_method_changed (EPortEntry *port_entry)
if (valid && port_entry->priv->entries != NULL) {
for (ii = 0; port_entry->priv->entries[ii].port > 0 && (!have_ssl || !have_nossl); ii++) {
/* Use only the first SSL/no-SSL port as a default in the list
- and skip the others */
+ * and skip the others */
if (port_entry->priv->entries[ii].is_ssl) {
if (have_ssl)
continue;
diff --git a/widgets/misc/e-preview-pane.c b/widgets/misc/e-preview-pane.c
index a94b3139b9..92644ec883 100644
--- a/widgets/misc/e-preview-pane.c
+++ b/widgets/misc/e-preview-pane.c
@@ -157,14 +157,12 @@ preview_pane_constructed (GObject *object)
/* EAlertBar controls its own visibility. */
widget = gtk_scrolled_window_new (NULL, NULL);
- gtk_scrolled_window_set_policy (
- GTK_SCROLLED_WINDOW (widget),
- GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (
GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (object), widget, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (widget), priv->web_view);
gtk_widget_show (widget);
+ gtk_widget_show (priv->web_view);
widget = e_search_bar_new (E_WEB_VIEW (priv->web_view));
gtk_box_pack_start (GTK_BOX (object), widget, FALSE, FALSE, 0);
diff --git a/widgets/misc/e-search-bar.c b/widgets/misc/e-search-bar.c
index eafb1b6ad4..9ed0c2d1c9 100644
--- a/widgets/misc/e-search-bar.c
+++ b/widgets/misc/e-search-bar.c
@@ -27,7 +27,6 @@
#include <glib/gi18n.h>
#include <gdk/gdkkeysyms.h>
-#include <gtkhtml/gtkhtml-search.h>
#define E_SEARCH_BAR_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -41,7 +40,6 @@ struct _ESearchBarPrivate {
GtkWidget *wrapped_prev_box;
GtkWidget *matches_label;
- ESearchingTokenizer *tokenizer;
gchar *active_search;
guint rerun_search : 1;
@@ -69,46 +67,33 @@ G_DEFINE_TYPE (
GTK_TYPE_HBOX)
static void
-search_bar_update_matches (ESearchBar *search_bar)
+search_bar_update_matches (ESearchBar *search_bar,
+ guint matches)
{
- ESearchingTokenizer *tokenizer;
GtkWidget *matches_label;
- gint matches;
gchar *text;
search_bar->priv->rerun_search = FALSE;
-
- tokenizer = e_search_bar_get_tokenizer (search_bar);
matches_label = search_bar->priv->matches_label;
- matches = e_searching_tokenizer_match_count (tokenizer);
- text = g_strdup_printf (_("Matches: %d"), matches);
-
+ text = g_strdup_printf (_("Matches: %u"), matches);
gtk_label_set_text (GTK_LABEL (matches_label), text);
gtk_widget_show (matches_label);
-
g_free (text);
}
static void
-search_bar_update_tokenizer (ESearchBar *search_bar)
+search_bar_update_highlights (ESearchBar *search_bar)
{
- ESearchingTokenizer *tokenizer;
- gboolean case_sensitive;
- gchar *active_search;
+ EWebView *web_view;
+ gboolean visible;
- tokenizer = e_search_bar_get_tokenizer (search_bar);
- case_sensitive = e_search_bar_get_case_sensitive (search_bar);
+ web_view = e_search_bar_get_web_view (search_bar);
- if (gtk_widget_get_visible (GTK_WIDGET (search_bar)))
- active_search = search_bar->priv->active_search;
- else
- active_search = NULL;
+ visible = gtk_widget_get_visible (GTK_WIDGET (search_bar));
- e_searching_tokenizer_set_primary_case_sensitivity (
- tokenizer, case_sensitive);
- e_searching_tokenizer_set_primary_search_string (
- tokenizer, active_search);
+ webkit_web_view_set_highlight_text_matches (
+ WEBKIT_WEB_VIEW (web_view), visible);
e_search_bar_changed (search_bar);
}
@@ -122,6 +107,7 @@ search_bar_find (ESearchBar *search_bar,
gboolean case_sensitive;
gboolean new_search;
gboolean wrapped = FALSE;
+ gboolean success;
gchar *text;
web_view = e_search_bar_get_web_view (search_bar);
@@ -138,44 +124,30 @@ search_bar_find (ESearchBar *search_bar,
(search_bar->priv->active_search == NULL) ||
(g_strcmp0 (text, search_bar->priv->active_search) != 0);
- /* XXX On a new search, the HTMLEngine's search state gets
- * destroyed when we redraw the message with highlighted
- * matches (EMHTMLStream's write() method triggers this,
- * but it's really GtkHtml's fault). That's why the first
- * match isn't selected automatically. It also causes
- * gtk_html_engine_search_next() to return FALSE, which we
- * assume to mean the search wrapped.
- *
- * So to avoid mistakenly thinking the search wrapped when
- * it hasn't, we have to trap the first button click after a
- * search and re-run the search to recreate the HTMLEngine's
- * search state, so that gtk_html_engine_search_next() will
- * succeed. */
if (new_search) {
- g_free (search_bar->priv->active_search);
- search_bar->priv->active_search = text;
- search_bar->priv->rerun_search = TRUE;
- search_bar_update_tokenizer (search_bar);
- } else if (search_bar->priv->rerun_search) {
- gtk_html_engine_search (
- GTK_HTML (web_view),
- search_bar->priv->active_search,
- case_sensitive, search_forward, FALSE);
- search_bar->priv->rerun_search = FALSE;
- g_free (text);
- } else {
- gtk_html_engine_search_set_forward (
- GTK_HTML (web_view), search_forward);
- if (!gtk_html_engine_search_next (GTK_HTML (web_view)))
- wrapped = TRUE;
- g_free (text);
+ guint matches;
+
+ webkit_web_view_unmark_text_matches (
+ WEBKIT_WEB_VIEW (web_view));
+ matches = webkit_web_view_mark_text_matches (
+ WEBKIT_WEB_VIEW (web_view),
+ text, case_sensitive, 0);
+ webkit_web_view_set_highlight_text_matches (
+ WEBKIT_WEB_VIEW (web_view), TRUE);
+ search_bar_update_matches (search_bar, matches);
}
- if (new_search || wrapped)
- gtk_html_engine_search (
- GTK_HTML (web_view),
- search_bar->priv->active_search,
- case_sensitive, search_forward, FALSE);
+ success = webkit_web_view_search_text (
+ WEBKIT_WEB_VIEW (web_view),
+ text, case_sensitive, search_forward, FALSE);
+
+ if (!success)
+ wrapped = webkit_web_view_search_text (
+ WEBKIT_WEB_VIEW (web_view),
+ text, case_sensitive, search_forward, TRUE);
+
+ g_free (search_bar->priv->active_search);
+ search_bar->priv->active_search = text;
g_object_notify (G_OBJECT (search_bar), "active-search");
@@ -239,16 +211,10 @@ static void
search_bar_set_web_view (ESearchBar *search_bar,
EWebView *web_view)
{
- GtkHTML *html;
- ESearchingTokenizer *tokenizer;
-
+ g_return_if_fail (E_IS_WEB_VIEW (web_view));
g_return_if_fail (search_bar->priv->web_view == NULL);
search_bar->priv->web_view = g_object_ref (web_view);
-
- html = GTK_HTML (web_view);
- tokenizer = e_search_bar_get_tokenizer (search_bar);
- gtk_html_set_tokenizer (html, HTML_TOKENIZER (tokenizer));
}
static void
@@ -352,11 +318,6 @@ search_bar_dispose (GObject *object)
priv->matches_label = NULL;
}
- if (priv->tokenizer != NULL) {
- g_object_unref (priv->tokenizer);
- priv->tokenizer = NULL;
- }
-
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_search_bar_parent_class)->dispose (object);
}
@@ -403,7 +364,7 @@ search_bar_show (GtkWidget *widget)
gtk_widget_grab_focus (search_bar->priv->entry);
- search_bar_update_tokenizer (search_bar);
+ search_bar_update_highlights (search_bar);
}
static void
@@ -416,7 +377,7 @@ search_bar_hide (GtkWidget *widget)
/* Chain up to parent's hide() method. */
GTK_WIDGET_CLASS (e_search_bar_parent_class)->hide (widget);
- search_bar_update_tokenizer (search_bar);
+ search_bar_update_highlights (search_bar);
}
static gboolean
@@ -438,6 +399,8 @@ search_bar_key_press_event (GtkWidget *widget,
static void
search_bar_clear (ESearchBar *search_bar)
{
+ WebKitWebView *web_view;
+
g_free (search_bar->priv->active_search);
search_bar->priv->active_search = NULL;
@@ -447,7 +410,10 @@ search_bar_clear (ESearchBar *search_bar)
gtk_widget_hide (search_bar->priv->wrapped_prev_box);
gtk_widget_hide (search_bar->priv->matches_label);
- search_bar_update_tokenizer (search_bar);
+ search_bar_update_highlights (search_bar);
+
+ web_view = WEBKIT_WEB_VIEW (search_bar->priv->web_view);
+ webkit_web_view_unmark_text_matches (web_view);
g_object_notify (G_OBJECT (search_bar), "active-search");
}
@@ -542,11 +508,6 @@ e_search_bar_init (ESearchBar *search_bar)
GtkWidget *container;
search_bar->priv = E_SEARCH_BAR_GET_PRIVATE (search_bar);
- search_bar->priv->tokenizer = e_searching_tokenizer_new ();
-
- g_signal_connect_swapped (
- search_bar->priv->tokenizer, "match",
- G_CALLBACK (search_bar_update_matches), search_bar);
gtk_box_set_spacing (GTK_BOX (search_bar), 12);
@@ -743,14 +704,6 @@ e_search_bar_get_web_view (ESearchBar *search_bar)
return search_bar->priv->web_view;
}
-ESearchingTokenizer *
-e_search_bar_get_tokenizer (ESearchBar *search_bar)
-{
- g_return_val_if_fail (E_IS_SEARCH_BAR (search_bar), NULL);
-
- return search_bar->priv->tokenizer;
-}
-
gboolean
e_search_bar_get_active_search (ESearchBar *search_bar)
{
diff --git a/widgets/misc/e-search-bar.h b/widgets/misc/e-search-bar.h
index 87e1023baf..4df8c97e6d 100644
--- a/widgets/misc/e-search-bar.h
+++ b/widgets/misc/e-search-bar.h
@@ -23,7 +23,6 @@
#define E_SEARCH_BAR_H
#include <gtk/gtk.h>
-#include <misc/e-searching-tokenizer.h>
#include <misc/e-web-view.h>
/* Standard GObject macros */
@@ -69,8 +68,6 @@ GtkWidget * e_search_bar_new (EWebView *web_view);
void e_search_bar_clear (ESearchBar *search_bar);
void e_search_bar_changed (ESearchBar *search_bar);
EWebView * e_search_bar_get_web_view (ESearchBar *search_bar);
-ESearchingTokenizer *
- e_search_bar_get_tokenizer (ESearchBar *search_bar);
gboolean e_search_bar_get_active_search
(ESearchBar *search_bar);
gboolean e_search_bar_get_case_sensitive
diff --git a/widgets/misc/e-searching-tokenizer.c b/widgets/misc/e-searching-tokenizer.c
deleted file mode 100644
index 878efa1654..0000000000
--- a/widgets/misc/e-searching-tokenizer.c
+++ /dev/null
@@ -1,1210 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Developed by Jon Trowbridge <trow@ximian.com>
- * Rewritten significantly to handle multiple strings and improve performance
- * by Michael Zucchi <notzed@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
-#include "e-searching-tokenizer.h"
-
-#include "libedataserver/e-memory.h"
-
-#define d(x)
-
-#define E_SEARCHING_TOKENIZER_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE \
- ((obj), E_TYPE_SEARCHING_TOKENIZER, ESearchingTokenizerPrivate))
-
-enum {
- MATCH_SIGNAL,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL];
-
-G_DEFINE_TYPE (
- ESearchingTokenizer,
- e_searching_tokenizer,
- HTML_TYPE_TOKENIZER)
-
-/* Utility functions */
-
-/* This is faster and safer than glib2's utf8 abomination,
- * but isn't exported from camel as yet */
-static inline guint32
-camel_utf8_getc (const guchar **ptr)
-{
- register guchar *p = (guchar *) * ptr;
- register guchar c, r;
- register guint32 v, m;
-
-again:
- r = *p++;
-loop:
- if (r < 0x80) {
- *ptr = p;
- v = r;
- } else if (r < 0xfe) { /* valid start char? */
- v = r;
- m = 0x7f80; /* used to mask out the length bits */
- do {
- c = *p++;
- if ((c & 0xc0) != 0x80) {
- r = c;
- goto loop;
- }
- v = (v << 6) | (c & 0x3f);
- r<<=1;
- m<<=5;
- } while (r & 0x40);
-
- *ptr = p;
-
- v &= ~m;
- } else {
- goto again;
- }
-
- return v;
-}
-
-/* note: our tags of interest are 7 bit ascii
- * only no need to do any fancy utf8 stuff */
-/* tags should be upper case
- * if this list gets longer than 10 entries, consider binary search */
-static const gchar *ignored_tags[] = {
- "B", "I", "FONT", "TT", "EM", /* and more? */};
-
-static gint
-ignore_tag (const gchar *tag)
-{
- gchar *t = g_alloca (strlen (tag) + 1), c, *out;
- const gchar *in;
- gint i;
-
- /* we could use a aho-corasick matcher here too ... but we wont */
-
- /* normalise tag into 't'.
- * Note we use the property that the only tags we're interested in
- * are 7 bit ascii to shortcut and simplify case insensitivity */
- in = tag+2; /* skip: TAG_ESCAPE '<' */
- if (*in == '/')
- in++;
- out = t;
- while ((c = *in++)) {
- if (c >= 'A' && c <= 'Z')
- *out++ = c;
- else if (c >= 'a' && c <= 'z')
- *out++ = c & 0xdf; /* convert ASCII to upper case */
- else
- /* maybe should check for > or ' ' etc? */
- break;
- }
- *out = 0;
-
- for (i = 0; i < G_N_ELEMENTS (ignored_tags); i++) {
- if (strcmp (t, ignored_tags[i]) == 0)
- return 1;
- }
-
- return 0;
-}
-
-/* ********************************************************************** */
-
-/* Aho-Corasick search tree implmeentation */
-
-/* next state if we match a character */
-struct _match {
- struct _match *next;
- guint32 ch;
- struct _state *match;
-};
-
-/* tree state node */
-struct _state {
- struct _match *matches;
- guint final; /* max no of chars we just matched */
- struct _state *fail; /* where to try next if we fail */
- struct _state *next; /* next on this level? */
-};
-
-/* base tree structure */
-struct _trie {
- struct _state root;
- gint max_depth;
-
- EMemChunk *state_chunks;
- EMemChunk *match_chunks;
-};
-
-/* will be enabled only if debug is enabled */
-#if d(1) -1 != -1
-static void
-dump_trie (struct _state *s,
- gint d)
-{
- gchar *p = g_alloca (d *2 + 1);
- struct _match *m;
-
- memset (p, ' ', d *2);
- p[d *2]=0;
-
- printf("%s[state] %p: %d fail->%p\n", p, s, s->final, s->fail);
- m = s->matches;
- while (m) {
- printf(" %s'%c' -> %p\n", p, m->ch, m->match);
- if (m->match)
- dump_trie (m->match, d + 1);
- m = m->next;
- }
-}
-#endif
-
-/* This builds an Aho-Corasick search trie for a set of utf8 words */
-/* See
- * http://www-sr.informatik.uni-tuebingen.de/~buehler/AC/AC.html
- * for a neat demo */
-
-static inline struct _match *
-g (struct _state *q,
- guint32 c)
-{
- struct _match *m = q->matches;
-
- while (m && m->ch != c)
- m = m->next;
-
- return m;
-}
-
-static struct _trie *
-build_trie (gint nocase,
- gint len,
- guchar **words)
-{
- struct _state *q, *qt, *r;
- const guchar *word;
- struct _match *m, *n = NULL;
- gint i, depth;
- guint32 c;
- struct _trie *trie;
- gint state_depth_max, state_depth_size;
- struct _state **state_depth;
-
- trie = g_malloc (sizeof (*trie));
- trie->root.matches = NULL;
- trie->root.final = 0;
- trie->root.fail = NULL;
- trie->root.next = NULL;
-
- trie->state_chunks = e_memchunk_new (8, sizeof (struct _state));
- trie->match_chunks = e_memchunk_new (8, sizeof (struct _match));
-
- /* This will correspond to the length of the longest pattern */
- state_depth_size = 0;
- state_depth_max = 64;
- state_depth = g_malloc (sizeof (*state_depth[0]) * 64);
- state_depth[0] = NULL;
-
- /* Step 1: Build trie */
-
- /* This just builds a tree that merges all common prefixes into the same branch */
-
- for (i = 0; i < len; i++) {
- word = words[i];
- q = &trie->root;
- depth = 0;
- while ((c = camel_utf8_getc (&word))) {
- if (nocase)
- c = g_unichar_tolower (c);
- m = g (q, c);
- if (m == NULL) {
- m = e_memchunk_alloc (trie->match_chunks);
- m->ch = c;
- m->next = q->matches;
- q->matches = m;
- q = m->match = e_memchunk_alloc (trie->state_chunks);
- q->matches = NULL;
- q->fail = &trie->root;
- q->final = 0;
- if (state_depth_max < depth) {
- state_depth_max += 64;
- state_depth = g_realloc (
- state_depth,
- sizeof (*state_depth[0]) *
- state_depth_max);
- }
- if (state_depth_size < depth) {
- state_depth[depth] = NULL;
- state_depth_size = depth;
- }
- q->next = state_depth[depth];
- state_depth[depth] = q;
- } else {
- q = m->match;
- }
- depth++;
- }
- q->final = depth;
- }
-
- d(printf("Dumping trie:\n"));
- d (dump_trie (&trie->root, 0));
-
- /* Step 2: Build failure graph */
-
- /* This searches for the longest substring which is a prefix of
- * another string and builds a graph of failure links so you can
- * find multiple substrings concurrently, using aho-corasick's
- * algorithm. */
-
- for (i = 0; i < state_depth_size; i++) {
- q = state_depth[i];
- while (q) {
- m = q->matches;
- while (m) {
- c = m->ch;
- qt = m->match;
- r = q->fail;
- while (r && (n = g (r, c)) == NULL)
- r = r->fail;
- if (r != NULL) {
- qt->fail = n->match;
- if (qt->fail->final > qt->final)
- qt->final = qt->fail->final;
- } else {
- if ((n = g (&trie->root, c)))
- qt->fail = n->match;
- else
- qt->fail = &trie->root;
- }
- m = m->next;
- }
- q = q->next;
- }
- }
-
- d (printf("After failure analysis\n"));
- d (dump_trie (&trie->root, 0));
-
- g_free (state_depth);
-
- trie->max_depth = state_depth_size;
-
- return trie;
-}
-
-static void
-free_trie (struct _trie *t)
-{
- e_memchunk_destroy (t->match_chunks);
- e_memchunk_destroy (t->state_chunks);
-
- g_free (t);
-}
-
-/* ********************************************************************** */
-
-/* html token searcher */
-
-struct _token {
- struct _token *next;
- struct _token *prev;
- guint offset;
- /* we need to copy the token for memory management, so why not copy it whole */
- gchar tok[1];
-};
-
-/* stack of submatches currently being scanned, used for merging */
-struct _submatch {
- guint offstart, offend; /* in bytes */
-};
-
-/* flags for new func */
-#define SEARCH_CASE (1)
-#define SEARCH_BOLD (2)
-
-struct _searcher {
- struct _trie *t;
-
- gchar *(*next_token)(); /* callbacks for more tokens */
- gpointer next_data;
-
- gint words; /* how many words */
- gchar *tags, *tage; /* the tag we used to highlight */
-
- gint flags; /* case sensitive or not */
-
- struct _state *state; /* state is the current trie state */
-
- gint matchcount;
-
- GQueue input; /* pending 'input' tokens, processed but might match */
- GQueue output; /* output tokens ready for source */
-
- struct _token *current; /* for token output memory management */
-
- guint32 offset; /* current offset through searchable stream? */
- guint32 offout; /* last output position */
-
- guint lastp; /* current position in rotating last buffer */
- guint32 *last; /* buffer that goes back last 'n' positions */
- guint32 last_mask; /* bitmask for efficient rotation calculation */
-
- guint submatchp; /* submatch stack */
- struct _submatch *submatches;
-};
-
-static void
-searcher_set_tokenfunc (struct _searcher *s,
- gchar *(*next)(),
- gpointer data)
-{
- s->next_token = next;
- s->next_data = data;
-}
-
-static struct _searcher *
-searcher_new (gint flags,
- gint argc,
- guchar **argv,
- const gchar *tags,
- const gchar *tage)
-{
- gint i, m;
- struct _searcher *s;
-
- s = g_malloc (sizeof (*s));
-
- s->t = build_trie ((flags&SEARCH_CASE) == 0, argc, argv);
- s->words = argc;
- s->tags = g_strdup (tags);
- s->tage = g_strdup (tage);
- s->flags = flags;
- s->state = &s->t->root;
- s->matchcount = -1;
-
- g_queue_init (&s->input);
- g_queue_init (&s->output);
- s->current = NULL;
-
- s->offset = 0;
- s->offout = 0;
-
- /* rotating queue of previous character positions */
- m = s->t->max_depth + 1;
- i = 2;
- while (i < m)
- i<<=2;
- s->last = g_malloc (sizeof (s->last[0]) * i);
- s->last_mask = i - 1;
- s->lastp = 0;
-
- /* a stack of possible submatches */
- s->submatchp = 0;
- s->submatches = g_malloc (sizeof (s->submatches[0]) * argc + 1);
-
- return s;
-}
-
-static void
-searcher_free (struct _searcher *s)
-{
- struct _token *t;
-
- while ((t = g_queue_pop_head (&s->input)) != NULL)
- g_free (t);
- while ((t = g_queue_pop_head (&s->output)) != NULL)
- g_free (t);
- g_free (s->tags);
- g_free (s->tage);
- g_free (s->last);
- g_free (s->submatches);
- free_trie (s->t);
- g_free (s);
-}
-
-static struct _token *
-append_token (GQueue *queue,
- const gchar *tok,
- gint len)
-{
- struct _token *token;
-
- if (len == -1)
- len = strlen (tok);
- token = g_malloc (sizeof (*token) + len + 1);
- token->offset = 0; /* set by caller when required */
- memcpy (token->tok, tok, len);
- token->tok[len] = 0;
- g_queue_push_tail (queue, token);
-
- return token;
-}
-
-#define free_token(x) (g_free (x))
-
-static void
-output_token (struct _searcher *s,
- struct _token *token)
-{
- gint offend;
- gint left, pre;
-
- if (token->tok[0] == TAG_ESCAPE) {
- if (token->offset >= s->offout) {
- g_queue_push_tail (&s->output, token);
- } else {
- free_token (token);
- }
- } else {
- offend = token->offset + strlen (token->tok);
- left = offend - s->offout;
- if (left > 0) {
- pre = s->offout - token->offset;
- if (pre > 0)
- memmove (token->tok, token->tok + pre, left + 1);
- s->offout = offend;
- g_queue_push_tail (&s->output, token);
- } else {
- free_token (token);
- }
- }
-}
-
-static struct _token *
-find_token (struct _searcher *s,
- gint start)
-{
- GList *link;
-
- /* find token which is start token, from end of list back */
- link = g_queue_peek_tail_link (&s->input);
- while (link != NULL) {
- struct _token *token = link->data;
-
- if (token->offset <= start)
- return token;
-
- link = g_list_previous (link);
- }
-
- return NULL;
-}
-
-static void
-output_match (struct _searcher *s,
- guint start,
- guint end)
-{
- register struct _token *token;
- struct _token *starttoken, *endtoken;
- gchar b[8];
-
- d (printf("output match: %d-%d at %d\n", start, end, s->offout));
-
- starttoken = find_token (s, start);
- endtoken = find_token (s, end);
-
- if (starttoken == NULL || endtoken == NULL) {
- d (printf("Cannot find match history for match %d-%d\n", start, end));
- return;
- }
-
- /* output pending stuff that didn't match afterall */
- while ((struct _token *) g_queue_peek_head (&s->input) != starttoken) {
- token = g_queue_pop_head (&s->input);
- output_token (s, token);
- }
-
- /* output any pre-match text */
- if (s->offout < start) {
- token = append_token (
- &s->output, starttoken->tok +
- (s->offout - starttoken->offset),
- start - s->offout);
- s->offout = start;
- }
-
- /* output highlight/bold */
- if (s->flags & SEARCH_BOLD) {
- sprintf(b, "%c<b>", (gchar)TAG_ESCAPE);
- append_token (&s->output, b, -1);
- }
- if (s->tags)
- append_token (&s->output, s->tags, -1);
-
- /* output match node (s) */
- if (starttoken != endtoken) {
- while ((struct _token *) g_queue_peek_head (&s->input) != endtoken) {
- token = g_queue_pop_head (&s->input);
- output_token (s, token);
- }
- }
-
- /* any remaining partial content */
- if (s->offout < end) {
- token = append_token (
- &s->output, endtoken->tok +
- (s->offout - endtoken->offset),
- end - s->offout);
- s->offout = end;
- }
-
- /* end highlight */
- if (s->tage)
- append_token (&s->output, s->tage, -1);
-
- /* and close bold if we need to */
- if (s->flags & SEARCH_BOLD) {
- sprintf(b, "%c</b>", (gchar)TAG_ESCAPE);
- append_token (&s->output, b, -1);
- }
-}
-
-/* output any sub-pending blocks */
-static void
-output_subpending (struct _searcher *s)
-{
- gint i;
-
- for (i = s->submatchp - 1; i >= 0; i--)
- output_match (s, s->submatches[i].offstart, s->submatches[i].offend);
- s->submatchp = 0;
-}
-
-/* returns true if a merge took place */
-static gint
-merge_subpending (struct _searcher *s,
- gint offstart,
- gint offend)
-{
- gint i;
-
- /* merges overlapping or abutting match strings */
- if (s->submatchp &&
- s->submatches[s->submatchp - 1].offend >= offstart) {
-
- /* go from end, any that match 'invalidate' follow-on ones too */
- for (i = s->submatchp - 1; i >= 0; i--) {
- if (s->submatches[i].offend >= offstart) {
- if (offstart < s->submatches[i].offstart)
- s->submatches[i].offstart = offstart;
- s->submatches[i].offend = offend;
- if (s->submatchp > i)
- s->submatchp = i + 1;
- }
- }
- return 1;
- }
-
- return 0;
-}
-
-static void
-push_subpending (struct _searcher *s,
- gint offstart,
- gint offend)
-{
- /* This is really an assertion, we just ignore the
- * last pending match instead of crashing though. */
- if (s->submatchp >= s->words) {
- d (printf("ERROR: submatch pending stack overflow\n"));
- s->submatchp = s->words - 1;
- }
-
- s->submatches[s->submatchp].offstart = offstart;
- s->submatches[s->submatchp].offend = offend;
- s->submatchp++;
-}
-
-/* move any (partial) tokens from input to output
- * if they are beyond the current output position */
-static void
-output_pending (struct _searcher *s)
-{
- struct _token *token;
-
- while ((token = g_queue_pop_head (&s->input)) != NULL)
- output_token (s, token);
-}
-
-/* flushes any nodes we cannot possibly match anymore */
-static void
-flush_extra (struct _searcher *s)
-{
- guint start;
- gint i;
- struct _token *starttoken, *token;
-
- /* find earliest gchar that can be in contention */
- start = s->offset - s->t->max_depth;
- for (i = 0; i < s->submatchp; i++)
- if (s->submatches[i].offstart < start)
- start = s->submatches[i].offstart;
-
- /* now, flush out any tokens which are before this point */
- starttoken = find_token (s, start);
- if (starttoken == NULL)
- return;
-
- while ((struct _token *) g_queue_peek_head (&s->input) != starttoken) {
- token = g_queue_pop_head (&s->input);
- output_token (s, token);
- }
-}
-
-static gchar *
-searcher_next_token (struct _searcher *s)
-{
- struct _token *token;
- const guchar *tok, *stok, *pre_tok;
- struct _trie *t = s->t;
- struct _state *q = s->state;
- struct _match *m = NULL;
- gint offstart, offend;
- guint32 c;
-
- while (g_queue_is_empty (&s->output)) {
- /* get next token */
- tok = (guchar *) s->next_token (s->next_data);
- if (tok == NULL) {
- output_subpending (s);
- output_pending (s);
- break;
- }
-
- /* we dont always have to copy each token, e.g. if we dont match anything */
- token = append_token (&s->input, (gchar *) tok, -1);
- token->offset = s->offset;
- tok = (guchar *) token->tok;
-
- /* tag test, reset state on unknown tags */
- if (tok[0] == TAG_ESCAPE) {
- if (!ignore_tag ((gchar *) tok)) {
- /* force reset */
- output_subpending (s);
- output_pending (s);
- q = &t->root;
- }
-
- continue;
- }
-
- /* process whole token */
- pre_tok = stok = tok;
- while ((c = camel_utf8_getc (&tok))) {
- if ((s->flags & SEARCH_CASE) == 0)
- c = g_unichar_tolower (c);
- while (q && (m = g (q, c)) == NULL)
- q = q->fail;
- if (q == NULL) {
- /* mismatch ... reset state */
- output_subpending (s);
- q = &t->root;
- } else if (m != NULL) {
- /* keep track of previous offsets of utf8 chars, rotating buffer */
- s->last[s->lastp] = s->offset + (pre_tok - stok);
- s->lastp = (s->lastp + 1) &s->last_mask;
-
- q = m->match;
- /* we have a match of q->final characters for a matching word */
- if (q->final) {
- s->matchcount++;
-
- /* use the last buffer to find the real offset of this gchar */
- offstart = s->last[(s->lastp - q->final) &s->last_mask];
- offend = s->offset + (tok - stok);
-
- if (q->matches == NULL) {
- if (s->submatchp == 0) {
- /* nothing pending, always put
- * something in so we can try merge */
- push_subpending (s, offstart, offend);
- } else if (!merge_subpending (s, offstart, offend)) {
- /* can't merge, output what we have, and start againt */
- output_subpending (s);
- push_subpending (s, offstart, offend);
- /*output_match(s, offstart, offend);*/
- } else if (g_queue_get_length (&s->input) > 8) {
- /* we're continuing to match and merge,
- * but we have a lot of stuff waiting,
- * so flush it out now since this is a
- * safe point to do it */
- output_subpending (s);
- }
- } else {
- /* merge/add subpending */
- if (!merge_subpending (s, offstart, offend))
- push_subpending (s, offstart, offend);
- }
- }
- }
- pre_tok = tok;
- }
-
- s->offset += (pre_tok - stok);
-
- flush_extra (s);
- }
-
- s->state = q;
-
- if (s->current)
- free_token (s->current);
-
- s->current = token = g_queue_pop_head (&s->output);
-
- return token ? g_strdup (token->tok) : NULL;
-}
-
-static gchar *
-searcher_peek_token (struct _searcher *s)
-{
- gchar *tok;
-
- /* we just get it and then put it back, it's fast enuf */
- tok = searcher_next_token (s);
- if (tok) {
- /* need to clear this so we dont free it while its still active */
- g_queue_push_head (&s->output, s->current);
- s->current = NULL;
- }
-
- return tok;
-}
-
-static gint
-searcher_pending (struct _searcher *s)
-{
- return !(g_queue_is_empty (&s->input) && g_queue_is_empty (&s->output));
-}
-
-/* ********************************************************************** */
-
-struct _search_info {
- GPtrArray *strv;
- gchar *color;
- guint size : 8;
- guint flags : 8;
-};
-
-/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/
-
-static struct _search_info *
-search_info_new (void)
-{
- struct _search_info *s;
-
- s = g_malloc0 (sizeof (struct _search_info));
- s->strv = g_ptr_array_new ();
-
- return s;
-}
-
-static void
-search_info_set_flags (struct _search_info *si,
- guint flags,
- guint mask)
-{
- si->flags = (si->flags & ~mask) | (flags & mask);
-}
-
-static void
-search_info_set_color (struct _search_info *si,
- const gchar *color)
-{
- g_free (si->color);
- si->color = g_strdup (color);
-}
-
-static void
-search_info_add_string (struct _search_info *si,
- const gchar *s)
-{
- const guchar *start;
- guint32 c;
-
- if (s && s[0]) {
- const guchar *us = (guchar *) s;
- /* strip leading whitespace */
- start = us;
- while ((c = camel_utf8_getc (&us))) {
- if (!g_unichar_isspace (c)) {
- break;
- }
- start = us;
- }
- /* should probably also strip trailing, but i'm lazy today */
- if (start[0])
- g_ptr_array_add (si->strv, g_strdup ((gchar *) start));
- }
-}
-
-static void
-search_info_clear (struct _search_info *si)
-{
- gint i;
-
- for (i = 0; i < si->strv->len; i++)
- g_free (si->strv->pdata[i]);
-
- g_ptr_array_set_size (si->strv, 0);
-}
-
-static void
-search_info_free (struct _search_info *si)
-{
- gint i;
-
- for (i = 0; i < si->strv->len; i++)
- g_free (si->strv->pdata[i]);
-
- g_ptr_array_free (si->strv, TRUE);
- g_free (si->color);
- g_free (si);
-}
-
-static struct _search_info *
-search_info_clone (struct _search_info *si)
-{
- struct _search_info *out;
- gint i;
-
- out = search_info_new ();
- for (i = 0; i < si->strv->len; i++)
- g_ptr_array_add (out->strv, g_strdup (si->strv->pdata[i]));
- out->color = g_strdup (si->color);
- out->flags = si->flags;
- out->size = si->size;
-
- return out;
-}
-
-static struct _searcher *
-search_info_to_searcher (struct _search_info *si)
-{
- gchar *tags, *tage;
- const gchar *col;
-
- if (si->strv->len == 0)
- return NULL;
-
- if (si->color == NULL)
- col = "red";
- else
- col = si->color;
-
- tags = g_alloca (20 + strlen (col));
- sprintf(tags, "%c<font color=\"%s\">", TAG_ESCAPE, col);
- tage = g_alloca (20);
- sprintf(tage, "%c</font>", TAG_ESCAPE);
-
- return searcher_new (
- si->flags, si->strv->len,
- (guchar **) si->strv->pdata, tags, tage);
-}
-
-/* ********************************************************************** */
-
-struct _ESearchingTokenizerPrivate {
- struct _search_info *primary, *secondary;
- struct _searcher *engine;
-};
-
-/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/
-
-/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/
-
-/* blah blah the htmltokeniser doesn't like being asked
- * for a token if it doens't hvae any! */
-static gchar *
-get_token (HTMLTokenizer *tokenizer)
-{
- HTMLTokenizerClass *class;
-
- class = HTML_TOKENIZER_CLASS (e_searching_tokenizer_parent_class);
-
- if (class->has_more (tokenizer))
- return class->next_token (tokenizer);
-
- return NULL;
-}
-
-/* proxy matched event, not sure what its for otherwise */
-static void
-matched (ESearchingTokenizer *tokenizer)
-{
- /*++tokenizer->priv->match_count;*/
- g_signal_emit (tokenizer, signals[MATCH_SIGNAL], 0);
-}
-
-/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
-
-static void
-searching_tokenizer_finalize (GObject *object)
-{
- ESearchingTokenizerPrivate *priv;
-
- priv = E_SEARCHING_TOKENIZER_GET_PRIVATE (object);
-
- search_info_free (priv->primary);
- search_info_free (priv->secondary);
-
- if (priv->engine != NULL)
- searcher_free (priv->engine);
-
- /* Chain up to parent's finalize () method. */
- G_OBJECT_CLASS (e_searching_tokenizer_parent_class)->finalize (object);
-}
-
-static void
-searching_tokenizer_begin (HTMLTokenizer *tokenizer,
- const gchar *content_type)
-{
- ESearchingTokenizerPrivate *priv;
-
- priv = E_SEARCHING_TOKENIZER_GET_PRIVATE (tokenizer);
-
- /* reset search */
- if (priv->engine != NULL) {
- searcher_free (priv->engine);
- priv->engine = NULL;
- }
-
- if ((priv->engine = search_info_to_searcher (priv->primary))
- || (priv->engine = search_info_to_searcher (priv->secondary))) {
- searcher_set_tokenfunc (priv->engine, get_token, tokenizer);
- }
- /* else - no engine, no search active */
-
- /* Chain up to parent's begin() method. */
- HTML_TOKENIZER_CLASS (e_searching_tokenizer_parent_class)->
- begin (tokenizer, content_type);
-}
-
-static gchar *
-searching_tokenizer_peek_token (HTMLTokenizer *tokenizer)
-{
- ESearchingTokenizerPrivate *priv;
-
- priv = E_SEARCHING_TOKENIZER_GET_PRIVATE (tokenizer);
-
- if (priv->engine != NULL)
- return searcher_peek_token (priv->engine);
-
- /* Chain up to parent's peek_token() method. */
- return HTML_TOKENIZER_CLASS (e_searching_tokenizer_parent_class)->
- peek_token (tokenizer);
-}
-
-static gchar *
-searching_tokenizer_next_token (HTMLTokenizer *tokenizer)
-{
- ESearchingTokenizerPrivate *priv;
- gint oldmatched;
- gchar *token;
-
- priv = E_SEARCHING_TOKENIZER_GET_PRIVATE (tokenizer);
-
- /* If no search is active, just use the default method. */
- if (priv->engine == NULL)
- return HTML_TOKENIZER_CLASS (
- e_searching_tokenizer_parent_class)->
- next_token (tokenizer);
-
- oldmatched = priv->engine->matchcount;
- if (priv->engine->matchcount == -1)
- priv->engine->matchcount = 0;
-
- token = searcher_next_token (priv->engine);
-
- /* not sure if this has to be accurate or just say we had some matches */
- if (oldmatched != priv->engine->matchcount)
- g_signal_emit (tokenizer, signals[MATCH_SIGNAL], 0);
-
- return token;
-}
-
-static gboolean
-searching_tokenizer_has_more (HTMLTokenizer *tokenizer)
-{
- ESearchingTokenizerPrivate *priv;
-
- priv = E_SEARCHING_TOKENIZER_GET_PRIVATE (tokenizer);
-
- return (priv->engine != NULL && searcher_pending (priv->engine)) ||
- HTML_TOKENIZER_CLASS (e_searching_tokenizer_parent_class)->
- has_more (tokenizer);
-}
-
-static HTMLTokenizer *
-searching_tokenizer_clone (HTMLTokenizer *tokenizer)
-{
- ESearchingTokenizer *orig_st;
- ESearchingTokenizer *new_st;
-
- orig_st = E_SEARCHING_TOKENIZER (tokenizer);
- new_st = e_searching_tokenizer_new ();
-
- search_info_free (new_st->priv->primary);
- search_info_free (new_st->priv->secondary);
-
- new_st->priv->primary = search_info_clone (orig_st->priv->primary);
- new_st->priv->secondary = search_info_clone (orig_st->priv->secondary);
-
- g_signal_connect_swapped (
- new_st, "match", G_CALLBACK (matched), orig_st);
-
- return HTML_TOKENIZER (new_st);
-}
-static void
-e_searching_tokenizer_class_init (ESearchingTokenizerClass *class)
-{
- GObjectClass *object_class;
- HTMLTokenizerClass *tokenizer_class;
-
- g_type_class_add_private (class, sizeof (ESearchingTokenizerPrivate));
-
- object_class = G_OBJECT_CLASS (class);
- object_class->finalize = searching_tokenizer_finalize;
-
- tokenizer_class = HTML_TOKENIZER_CLASS (class);
- tokenizer_class->begin = searching_tokenizer_begin;
- tokenizer_class->peek_token = searching_tokenizer_peek_token;
- tokenizer_class->next_token = searching_tokenizer_next_token;
- tokenizer_class->has_more = searching_tokenizer_has_more;
- tokenizer_class->clone = searching_tokenizer_clone;
-
- signals[MATCH_SIGNAL] = g_signal_new (
- "match",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (ESearchingTokenizerClass, match),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-}
-
-static void
-e_searching_tokenizer_init (ESearchingTokenizer *tokenizer)
-{
- tokenizer->priv = E_SEARCHING_TOKENIZER_GET_PRIVATE (tokenizer);
-
- tokenizer->priv->primary = search_info_new ();
- search_info_set_flags (
- tokenizer->priv->primary,
- SEARCH_BOLD, SEARCH_CASE | SEARCH_BOLD);
- search_info_set_color (tokenizer->priv->primary, "red");
-
- tokenizer->priv->secondary = search_info_new ();
- search_info_set_flags (
- tokenizer->priv->secondary,
- SEARCH_BOLD, SEARCH_CASE | SEARCH_BOLD);
- search_info_set_color (tokenizer->priv->secondary, "purple");
-}
-
-ESearchingTokenizer *
-e_searching_tokenizer_new (void)
-{
- return g_object_new (E_TYPE_SEARCHING_TOKENIZER, NULL);
-}
-
-void
-e_searching_tokenizer_set_primary_search_string (ESearchingTokenizer *tokenizer,
- const gchar *primary_string)
-{
- g_return_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer));
-
- search_info_clear (tokenizer->priv->primary);
- search_info_add_string (tokenizer->priv->primary, primary_string);
-}
-
-void
-e_searching_tokenizer_add_primary_search_string (ESearchingTokenizer *tokenizer,
- const gchar *primary_string)
-{
- g_return_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer));
-
- search_info_add_string (tokenizer->priv->primary, primary_string);
-}
-
-void
-e_searching_tokenizer_set_primary_case_sensitivity (ESearchingTokenizer *tokenizer,
- gboolean case_sensitive)
-{
- g_return_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer));
-
- search_info_set_flags (
- tokenizer->priv->primary,
- case_sensitive ? SEARCH_CASE : 0, SEARCH_CASE);
-}
-
-void
-e_searching_tokenizer_set_secondary_search_string (ESearchingTokenizer *tokenizer,
- const gchar *secondary_string)
-{
- g_return_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer));
-
- search_info_clear (tokenizer->priv->secondary);
- search_info_add_string (tokenizer->priv->secondary, secondary_string);
-}
-
-void
-e_searching_tokenizer_add_secondary_search_string (ESearchingTokenizer *tokenizer,
- const gchar *secondary_string)
-{
- g_return_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer));
-
- search_info_add_string (tokenizer->priv->secondary, secondary_string);
-}
-
-void
-e_searching_tokenizer_set_secondary_case_sensitivity (ESearchingTokenizer *tokenizer,
- gboolean case_sensitive)
-{
- g_return_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer));
-
- search_info_set_flags (
- tokenizer->priv->secondary,
- case_sensitive ? SEARCH_CASE : 0, SEARCH_CASE);
-}
-
-/* Note: only returns the primary search string count */
-gint
-e_searching_tokenizer_match_count (ESearchingTokenizer *tokenizer)
-{
- g_return_val_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer), -1);
-
- if (tokenizer->priv->engine && tokenizer->priv->primary->strv->len)
- return tokenizer->priv->engine->matchcount;
-
- return 0;
-}
diff --git a/widgets/misc/e-searching-tokenizer.h b/widgets/misc/e-searching-tokenizer.h
deleted file mode 100644
index ab13203c36..0000000000
--- a/widgets/misc/e-searching-tokenizer.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Authors:
- * Jon Trowbridge <trow@ximian.com>
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef E_SEARCHING_TOKENIZER_H
-#define E_SEARCHING_TOKENIZER_H
-
-#include <gtkhtml/htmltokenizer.h>
-
-/* Standard GObject macros */
-#define E_TYPE_SEARCHING_TOKENIZER \
- (e_searching_tokenizer_get_type ())
-#define E_SEARCHING_TOKENIZER(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST \
- ((obj), E_TYPE_SEARCHING_TOKENIZER, ESearchingTokenizer))
-#define E_SEARCHING_TOKENIZER_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_CAST \
- ((cls), E_TYPE_SEARCHING_TOKENIZER, ESearchingTokenizerClass))
-#define E_IS_SEARCHING_TOKENIZER(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE \
- ((obj), E_TYPE_SEARCHING_TOKENIZER))
-#define E_IS_SEARCHING_TOKENIZER_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_TYPE \
- ((cls), E_TYPE_SEARCHING_TOKENIZER))
-#define E_SEARCH_TOKENIZER_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS \
- ((obj), E_TYPE_SEARCHING_TOKENIZER, ESearchingTokenizerClass))
-
-G_BEGIN_DECLS
-
-typedef struct _ESearchingTokenizer ESearchingTokenizer;
-typedef struct _ESearchingTokenizerClass ESearchingTokenizerClass;
-typedef struct _ESearchingTokenizerPrivate ESearchingTokenizerPrivate;
-
-struct _ESearchingTokenizer {
- HTMLTokenizer parent;
- ESearchingTokenizerPrivate *priv;
-};
-
-struct _ESearchingTokenizerClass {
- HTMLTokenizerClass parent_class;
-
- void (*match) (ESearchingTokenizer *tokenizer);
-};
-
-GType e_searching_tokenizer_get_type (void);
-ESearchingTokenizer *
- e_searching_tokenizer_new (void);
-void e_searching_tokenizer_set_primary_search_string
- (ESearchingTokenizer *tokenizer,
- const gchar *primary_string);
-void e_searching_tokenizer_add_primary_search_string
- (ESearchingTokenizer *tokenizer,
- const gchar *primary_string);
-void e_searching_tokenizer_set_primary_case_sensitivity
- (ESearchingTokenizer *tokenizer,
- gboolean case_sensitive);
-void e_searching_tokenizer_set_secondary_search_string
- (ESearchingTokenizer *tokenizer,
- const gchar *secondary_string);
-void e_searching_tokenizer_add_secondary_search_string
- (ESearchingTokenizer *tokenizer,
- const gchar *secondary_string);
-void e_searching_tokenizer_set_secondary_case_sensitivity
- (ESearchingTokenizer *tokenizer,
- gboolean case_sensitive);
-gint e_searching_tokenizer_match_count
- (ESearchingTokenizer *tokenizer);
-
-G_END_DECLS
-
-#endif /* E_SEARCHING_TOKENIZER_H */
diff --git a/widgets/misc/e-web-view-gtkhtml.c b/widgets/misc/e-web-view-gtkhtml.c
new file mode 100644
index 0000000000..be8b1c596f
--- /dev/null
+++ b/widgets/misc/e-web-view-gtkhtml.c
@@ -0,0 +1,2301 @@
+/*
+ * e-web-view-gtkhtml.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-web-view-gtkhtml.h"
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#include <camel/camel.h>
+
+#include <e-util/e-util.h>
+#include <e-util/e-plugin-ui.h>
+#include <libevolution-utils/e-alert-dialog.h>
+#include <libevolution-utils/e-alert-sink.h>
+
+#include <libebackend/e-extensible.h>
+
+#include "e-popup-action.h"
+#include "e-selectable.h"
+
+#define E_WEB_VIEW_GTKHTML_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_WEB_VIEW_GTKHTML, EWebViewGtkHTMLPrivate))
+
+typedef struct _EWebViewGtkHTMLRequest EWebViewGtkHTMLRequest;
+
+struct _EWebViewGtkHTMLPrivate {
+ GList *requests;
+ GtkUIManager *ui_manager;
+ gchar *selected_uri;
+ GdkPixbufAnimation *cursor_image;
+
+ GtkAction *open_proxy;
+ GtkAction *print_proxy;
+ GtkAction *save_as_proxy;
+
+ GtkTargetList *copy_target_list;
+ GtkTargetList *paste_target_list;
+
+ /* Lockdown Options */
+ guint disable_printing : 1;
+ guint disable_save_to_disk : 1;
+};
+
+struct _EWebViewGtkHTMLRequest {
+ GFile *file;
+ EWebViewGtkHTML *web_view;
+ GCancellable *cancellable;
+ GInputStream *input_stream;
+ GtkHTMLStream *output_stream;
+ gchar buffer[4096];
+};
+
+enum {
+ PROP_0,
+ PROP_ANIMATE,
+ PROP_CARET_MODE,
+ PROP_COPY_TARGET_LIST,
+ PROP_DISABLE_PRINTING,
+ PROP_DISABLE_SAVE_TO_DISK,
+ PROP_EDITABLE,
+ PROP_INLINE_SPELLING,
+ PROP_MAGIC_LINKS,
+ PROP_MAGIC_SMILEYS,
+ PROP_OPEN_PROXY,
+ PROP_PASTE_TARGET_LIST,
+ PROP_PRINT_PROXY,
+ PROP_SAVE_AS_PROXY,
+ PROP_SELECTED_URI,
+ PROP_CURSOR_IMAGE
+};
+
+enum {
+ COPY_CLIPBOARD,
+ CUT_CLIPBOARD,
+ PASTE_CLIPBOARD,
+ POPUP_EVENT,
+ STATUS_MESSAGE,
+ STOP_LOADING,
+ UPDATE_ACTIONS,
+ PROCESS_MAILTO,
+ LAST_SIGNAL
+};
+
+static gpointer parent_class;
+static guint signals[LAST_SIGNAL];
+
+static const gchar *ui =
+"<ui>"
+" <popup name='context'>"
+" <menuitem action='copy-clipboard'/>"
+" <separator/>"
+" <placeholder name='custom-actions-1'>"
+" <menuitem action='open'/>"
+" <menuitem action='save-as'/>"
+" <menuitem action='http-open'/>"
+" <menuitem action='send-message'/>"
+" <menuitem action='print'/>"
+" </placeholder>"
+" <placeholder name='custom-actions-2'>"
+" <menuitem action='uri-copy'/>"
+" <menuitem action='mailto-copy'/>"
+" <menuitem action='image-copy'/>"
+" </placeholder>"
+" <placeholder name='custom-actions-3'/>"
+" <separator/>"
+" <menuitem action='select-all'/>"
+" </popup>"
+"</ui>";
+
+/* Forward Declarations */
+static void e_web_view_gtkhtml_alert_sink_init (EAlertSinkInterface *interface);
+static void e_web_view_gtkhtml_selectable_init (ESelectableInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ EWebViewGtkHTML,
+ e_web_view_gtkhtml,
+ GTK_TYPE_HTML,
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_EXTENSIBLE, NULL)
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_ALERT_SINK,
+ e_web_view_gtkhtml_alert_sink_init)
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_SELECTABLE,
+ e_web_view_gtkhtml_selectable_init))
+
+static EWebViewGtkHTMLRequest *
+web_view_gtkhtml_request_new (EWebViewGtkHTML *web_view,
+ const gchar *uri,
+ GtkHTMLStream *stream)
+{
+ EWebViewGtkHTMLRequest *request;
+ GList *list;
+
+ request = g_slice_new (EWebViewGtkHTMLRequest);
+
+ /* Try to detect file paths posing as URIs. */
+ if (*uri == '/')
+ request->file = g_file_new_for_path (uri);
+ else
+ request->file = g_file_new_for_uri (uri);
+
+ request->web_view = g_object_ref (web_view);
+ request->cancellable = g_cancellable_new ();
+ request->input_stream = NULL;
+ request->output_stream = stream;
+
+ list = request->web_view->priv->requests;
+ list = g_list_prepend (list, request);
+ request->web_view->priv->requests = list;
+
+ return request;
+}
+
+static void
+web_view_gtkhtml_request_free (EWebViewGtkHTMLRequest *request)
+{
+ GList *list;
+
+ list = request->web_view->priv->requests;
+ list = g_list_remove (list, request);
+ request->web_view->priv->requests = list;
+
+ g_object_unref (request->file);
+ g_object_unref (request->web_view);
+ g_object_unref (request->cancellable);
+
+ if (request->input_stream != NULL)
+ g_object_unref (request->input_stream);
+
+ g_slice_free (EWebViewGtkHTMLRequest, request);
+}
+
+static void
+web_view_gtkhtml_request_cancel (EWebViewGtkHTMLRequest *request)
+{
+ g_cancellable_cancel (request->cancellable);
+}
+
+static gboolean
+web_view_gtkhtml_request_check_for_error (EWebViewGtkHTMLRequest *request,
+ GError *error)
+{
+ GtkHTML *html;
+ GtkHTMLStream *stream;
+
+ if (error == NULL)
+ return FALSE;
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
+ /* use this error, but do not close the stream */
+ g_error_free (error);
+ return TRUE;
+ }
+
+ /* XXX Should we log errors that are not cancellations? */
+
+ html = GTK_HTML (request->web_view);
+ stream = request->output_stream;
+
+ gtk_html_end (html, stream, GTK_HTML_STREAM_ERROR);
+ web_view_gtkhtml_request_free (request);
+ g_error_free (error);
+
+ return TRUE;
+}
+
+static void
+web_view_gtkhtml_request_stream_read_cb (GInputStream *input_stream,
+ GAsyncResult *result,
+ EWebViewGtkHTMLRequest *request)
+{
+ gssize bytes_read;
+ GError *error = NULL;
+
+ bytes_read = g_input_stream_read_finish (input_stream, result, &error);
+
+ if (web_view_gtkhtml_request_check_for_error (request, error))
+ return;
+
+ if (bytes_read == 0) {
+ gtk_html_end (
+ GTK_HTML (request->web_view),
+ request->output_stream, GTK_HTML_STREAM_OK);
+ web_view_gtkhtml_request_free (request);
+ return;
+ }
+
+ gtk_html_write (
+ GTK_HTML (request->web_view),
+ request->output_stream, request->buffer, bytes_read);
+
+ g_input_stream_read_async (
+ request->input_stream, request->buffer,
+ sizeof (request->buffer), G_PRIORITY_DEFAULT,
+ request->cancellable, (GAsyncReadyCallback)
+ web_view_gtkhtml_request_stream_read_cb, request);
+}
+
+static void
+web_view_gtkhtml_request_read_cb (GFile *file,
+ GAsyncResult *result,
+ EWebViewGtkHTMLRequest *request)
+{
+ GFileInputStream *input_stream;
+ GError *error = NULL;
+
+ /* Input stream might be NULL, so don't use cast macro. */
+ input_stream = g_file_read_finish (file, result, &error);
+ request->input_stream = (GInputStream *) input_stream;
+
+ if (web_view_gtkhtml_request_check_for_error (request, error))
+ return;
+
+ g_input_stream_read_async (
+ request->input_stream, request->buffer,
+ sizeof (request->buffer), G_PRIORITY_DEFAULT,
+ request->cancellable, (GAsyncReadyCallback)
+ web_view_gtkhtml_request_stream_read_cb, request);
+}
+
+static void
+action_copy_clipboard_cb (GtkAction *action,
+ EWebViewGtkHTML *web_view)
+{
+ e_web_view_gtkhtml_copy_clipboard (web_view);
+}
+
+static void
+action_http_open_cb (GtkAction *action,
+ EWebViewGtkHTML *web_view)
+{
+ const gchar *uri;
+ gpointer parent;
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
+ parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
+
+ uri = e_web_view_gtkhtml_get_selected_uri (web_view);
+ g_return_if_fail (uri != NULL);
+
+ e_show_uri (parent, uri);
+}
+
+static void
+action_mailto_copy_cb (GtkAction *action,
+ EWebViewGtkHTML *web_view)
+{
+ CamelURL *curl;
+ CamelInternetAddress *inet_addr;
+ GtkClipboard *clipboard;
+ const gchar *uri;
+ gchar *text;
+
+ uri = e_web_view_gtkhtml_get_selected_uri (web_view);
+ g_return_if_fail (uri != NULL);
+
+ /* This should work because we checked it in update_actions(). */
+ curl = camel_url_new (uri, NULL);
+ g_return_if_fail (curl != NULL);
+
+ inet_addr = camel_internet_address_new ();
+ camel_address_decode (CAMEL_ADDRESS (inet_addr), curl->path);
+ text = camel_address_format (CAMEL_ADDRESS (inet_addr));
+ if (text == NULL || *text == '\0')
+ text = g_strdup (uri + strlen ("mailto:"));
+
+ g_object_unref (inet_addr);
+ camel_url_free (curl);
+
+ clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+ gtk_clipboard_set_text (clipboard, text, -1);
+ gtk_clipboard_store (clipboard);
+
+ clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text (clipboard, text, -1);
+ gtk_clipboard_store (clipboard);
+
+ g_free (text);
+}
+
+static void
+action_select_all_cb (GtkAction *action,
+ EWebViewGtkHTML *web_view)
+{
+ e_web_view_gtkhtml_select_all (web_view);
+}
+
+static void
+action_send_message_cb (GtkAction *action,
+ EWebViewGtkHTML *web_view)
+{
+ const gchar *uri;
+ gpointer parent;
+ gboolean handled;
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
+ parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
+
+ uri = e_web_view_gtkhtml_get_selected_uri (web_view);
+ g_return_if_fail (uri != NULL);
+
+ handled = FALSE;
+ g_signal_emit (web_view, signals[PROCESS_MAILTO], 0, uri, &handled);
+
+ if (!handled)
+ e_show_uri (parent, uri);
+}
+
+static void
+action_uri_copy_cb (GtkAction *action,
+ EWebViewGtkHTML *web_view)
+{
+ GtkClipboard *clipboard;
+ const gchar *uri;
+
+ clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+ uri = e_web_view_gtkhtml_get_selected_uri (web_view);
+ g_return_if_fail (uri != NULL);
+
+ gtk_clipboard_set_text (clipboard, uri, -1);
+ gtk_clipboard_store (clipboard);
+}
+
+static void
+action_image_copy_cb (GtkAction *action,
+ EWebViewGtkHTML *web_view)
+{
+ GtkClipboard *clipboard;
+ GdkPixbufAnimation *animation;
+ GdkPixbuf *pixbuf;
+
+ clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+ animation = e_web_view_gtkhtml_get_cursor_image (web_view);
+ g_return_if_fail (animation != NULL);
+
+ pixbuf = gdk_pixbuf_animation_get_static_image (animation);
+ if (!pixbuf)
+ return;
+
+ gtk_clipboard_set_image (clipboard, pixbuf);
+ gtk_clipboard_store (clipboard);
+}
+
+static GtkActionEntry uri_entries[] = {
+
+ { "uri-copy",
+ GTK_STOCK_COPY,
+ N_("_Copy Link Location"),
+ NULL,
+ N_("Copy the link to the clipboard"),
+ G_CALLBACK (action_uri_copy_cb) }
+};
+
+static GtkActionEntry http_entries[] = {
+
+ { "http-open",
+ "emblem-web",
+ N_("_Open Link in Browser"),
+ NULL,
+ N_("Open the link in a web browser"),
+ G_CALLBACK (action_http_open_cb) }
+};
+
+static GtkActionEntry mailto_entries[] = {
+
+ { "mailto-copy",
+ GTK_STOCK_COPY,
+ N_("_Copy Email Address"),
+ NULL,
+ N_("Copy the email address to the clipboard"),
+ G_CALLBACK (action_mailto_copy_cb) },
+
+ { "send-message",
+ "mail-message-new",
+ N_("_Send New Message To..."),
+ NULL,
+ N_("Send a mail message to this address"),
+ G_CALLBACK (action_send_message_cb) }
+};
+
+static GtkActionEntry image_entries[] = {
+
+ { "image-copy",
+ GTK_STOCK_COPY,
+ N_("_Copy Image"),
+ NULL,
+ N_("Copy the image to the clipboard"),
+ G_CALLBACK (action_image_copy_cb) }
+};
+
+static GtkActionEntry selection_entries[] = {
+
+ { "copy-clipboard",
+ GTK_STOCK_COPY,
+ NULL,
+ NULL,
+ N_("Copy the selection"),
+ G_CALLBACK (action_copy_clipboard_cb) },
+};
+
+static GtkActionEntry standard_entries[] = {
+
+ { "select-all",
+ GTK_STOCK_SELECT_ALL,
+ NULL,
+ NULL,
+ N_("Select all text and images"),
+ G_CALLBACK (action_select_all_cb) }
+};
+
+static gboolean
+web_view_gtkhtml_button_press_event_cb (EWebViewGtkHTML *web_view,
+ GdkEventButton *event,
+ GtkHTML *frame)
+{
+ gboolean event_handled = FALSE;
+ gchar *uri = NULL;
+
+ if (event) {
+ GdkPixbufAnimation *anim;
+
+ if (frame == NULL)
+ frame = GTK_HTML (web_view);
+
+ anim = gtk_html_get_image_at (frame, event->x, event->y);
+ e_web_view_gtkhtml_set_cursor_image (web_view, anim);
+ if (anim != NULL)
+ g_object_unref (anim);
+ }
+
+ if (event != NULL && event->button != 3)
+ return FALSE;
+
+ /* Only extract a URI if no selection is active. Selected text
+ * implies the user is more likely to want to copy the selection
+ * to the clipboard than open a link within the selection. */
+ if (!e_web_view_gtkhtml_is_selection_active (web_view))
+ uri = e_web_view_gtkhtml_extract_uri (web_view, event, frame);
+
+ if (uri != NULL && g_str_has_prefix (uri, "##")) {
+ g_free (uri);
+ return FALSE;
+ }
+
+ g_signal_emit (
+ web_view, signals[POPUP_EVENT], 0,
+ event, uri, &event_handled);
+
+ g_free (uri);
+
+ return event_handled;
+}
+
+static void
+web_view_gtkhtml_menu_item_select_cb (EWebViewGtkHTML *web_view,
+ GtkWidget *widget)
+{
+ GtkAction *action;
+ GtkActivatable *activatable;
+ const gchar *tooltip;
+
+ activatable = GTK_ACTIVATABLE (widget);
+ action = gtk_activatable_get_related_action (activatable);
+ tooltip = gtk_action_get_tooltip (action);
+
+ if (tooltip == NULL)
+ return;
+
+ e_web_view_gtkhtml_status_message (web_view, tooltip);
+}
+
+static void
+web_view_gtkhtml_menu_item_deselect_cb (EWebViewGtkHTML *web_view)
+{
+ e_web_view_gtkhtml_status_message (web_view, NULL);
+}
+
+static void
+web_view_gtkhtml_connect_proxy_cb (EWebViewGtkHTML *web_view,
+ GtkAction *action,
+ GtkWidget *proxy)
+{
+ if (!GTK_IS_MENU_ITEM (proxy))
+ return;
+
+ g_signal_connect_swapped (
+ proxy, "select",
+ G_CALLBACK (web_view_gtkhtml_menu_item_select_cb), web_view);
+
+ g_signal_connect_swapped (
+ proxy, "deselect",
+ G_CALLBACK (web_view_gtkhtml_menu_item_deselect_cb), web_view);
+}
+
+static void
+web_view_gtkhtml_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ANIMATE:
+ e_web_view_gtkhtml_set_animate (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_CARET_MODE:
+ e_web_view_gtkhtml_set_caret_mode (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_DISABLE_PRINTING:
+ e_web_view_gtkhtml_set_disable_printing (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_DISABLE_SAVE_TO_DISK:
+ e_web_view_gtkhtml_set_disable_save_to_disk (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_EDITABLE:
+ e_web_view_gtkhtml_set_editable (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_INLINE_SPELLING:
+ e_web_view_gtkhtml_set_inline_spelling (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_MAGIC_LINKS:
+ e_web_view_gtkhtml_set_magic_links (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_MAGIC_SMILEYS:
+ e_web_view_gtkhtml_set_magic_smileys (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_OPEN_PROXY:
+ e_web_view_gtkhtml_set_open_proxy (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_PRINT_PROXY:
+ e_web_view_gtkhtml_set_print_proxy (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_SAVE_AS_PROXY:
+ e_web_view_gtkhtml_set_save_as_proxy (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_SELECTED_URI:
+ e_web_view_gtkhtml_set_selected_uri (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_string (value));
+ return;
+ case PROP_CURSOR_IMAGE:
+ e_web_view_gtkhtml_set_cursor_image (
+ E_WEB_VIEW_GTKHTML (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+web_view_gtkhtml_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ANIMATE:
+ g_value_set_boolean (
+ value, e_web_view_gtkhtml_get_animate (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_CARET_MODE:
+ g_value_set_boolean (
+ value, e_web_view_gtkhtml_get_caret_mode (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_COPY_TARGET_LIST:
+ g_value_set_boxed (
+ value, e_web_view_gtkhtml_get_copy_target_list (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_DISABLE_PRINTING:
+ g_value_set_boolean (
+ value, e_web_view_gtkhtml_get_disable_printing (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_DISABLE_SAVE_TO_DISK:
+ g_value_set_boolean (
+ value, e_web_view_gtkhtml_get_disable_save_to_disk (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_EDITABLE:
+ g_value_set_boolean (
+ value, e_web_view_gtkhtml_get_editable (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_INLINE_SPELLING:
+ g_value_set_boolean (
+ value, e_web_view_gtkhtml_get_inline_spelling (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_MAGIC_LINKS:
+ g_value_set_boolean (
+ value, e_web_view_gtkhtml_get_magic_links (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_MAGIC_SMILEYS:
+ g_value_set_boolean (
+ value, e_web_view_gtkhtml_get_magic_smileys (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_OPEN_PROXY:
+ g_value_set_object (
+ value, e_web_view_gtkhtml_get_open_proxy (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_PASTE_TARGET_LIST:
+ g_value_set_boxed (
+ value, e_web_view_gtkhtml_get_paste_target_list (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_PRINT_PROXY:
+ g_value_set_object (
+ value, e_web_view_gtkhtml_get_print_proxy (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_SAVE_AS_PROXY:
+ g_value_set_object (
+ value, e_web_view_gtkhtml_get_save_as_proxy (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_SELECTED_URI:
+ g_value_set_string (
+ value, e_web_view_gtkhtml_get_selected_uri (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+
+ case PROP_CURSOR_IMAGE:
+ g_value_set_object (
+ value, e_web_view_gtkhtml_get_cursor_image (
+ E_WEB_VIEW_GTKHTML (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+web_view_gtkhtml_dispose (GObject *object)
+{
+ EWebViewGtkHTMLPrivate *priv;
+
+ priv = E_WEB_VIEW_GTKHTML_GET_PRIVATE (object);
+
+ if (priv->ui_manager != NULL) {
+ g_object_unref (priv->ui_manager);
+ priv->ui_manager = NULL;
+ }
+
+ if (priv->open_proxy != NULL) {
+ g_object_unref (priv->open_proxy);
+ priv->open_proxy = NULL;
+ }
+
+ if (priv->print_proxy != NULL) {
+ g_object_unref (priv->print_proxy);
+ priv->print_proxy = NULL;
+ }
+
+ if (priv->save_as_proxy != NULL) {
+ g_object_unref (priv->save_as_proxy);
+ priv->save_as_proxy = NULL;
+ }
+
+ if (priv->copy_target_list != NULL) {
+ gtk_target_list_unref (priv->copy_target_list);
+ priv->copy_target_list = NULL;
+ }
+
+ if (priv->paste_target_list != NULL) {
+ gtk_target_list_unref (priv->paste_target_list);
+ priv->paste_target_list = NULL;
+ }
+
+ if (priv->cursor_image != NULL) {
+ g_object_unref (priv->cursor_image);
+ priv->cursor_image = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+web_view_gtkhtml_finalize (GObject *object)
+{
+ EWebViewGtkHTMLPrivate *priv;
+
+ priv = E_WEB_VIEW_GTKHTML_GET_PRIVATE (object);
+
+ /* All URI requests should be complete or cancelled by now. */
+ if (priv->requests != NULL)
+ g_warning ("Finalizing EWebViewGtkHTML with active URI requests");
+
+ g_free (priv->selected_uri);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+web_view_gtkhtml_constructed (GObject *object)
+{
+#ifndef G_OS_WIN32
+ GSettings *settings;
+
+ settings = g_settings_new ("org.gnome.desktop.lockdown");
+
+ g_settings_bind (
+ settings, "disable-printing",
+ object, "disable-printing",
+ G_SETTINGS_BIND_GET);
+
+ g_settings_bind (
+ settings, "disable-save-to-disk",
+ object, "disable-save-to-disk",
+ G_SETTINGS_BIND_GET);
+
+ g_object_unref (settings);
+#endif
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+}
+
+static gboolean
+web_view_gtkhtml_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkWidgetClass *widget_class;
+ EWebViewGtkHTML *web_view;
+
+ web_view = E_WEB_VIEW_GTKHTML (widget);
+
+ if (web_view_gtkhtml_button_press_event_cb (web_view, event, NULL))
+ return TRUE;
+
+ /* Chain up to parent's button_press_event() method. */
+ widget_class = GTK_WIDGET_CLASS (parent_class);
+ return widget_class->button_press_event (widget, event);
+}
+
+static gboolean
+web_view_gtkhtml_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event)
+{
+ if (event->state & GDK_CONTROL_MASK) {
+ switch (event->direction) {
+ case GDK_SCROLL_UP:
+ gtk_html_zoom_in (GTK_HTML (widget));
+ return TRUE;
+ case GDK_SCROLL_DOWN:
+ gtk_html_zoom_out (GTK_HTML (widget));
+ return TRUE;
+ default:
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+web_view_gtkhtml_url_requested (GtkHTML *html,
+ const gchar *uri,
+ GtkHTMLStream *stream)
+{
+ EWebViewGtkHTMLRequest *request;
+
+ request = web_view_gtkhtml_request_new (E_WEB_VIEW_GTKHTML (html), uri, stream);
+
+ g_file_read_async (
+ request->file, G_PRIORITY_DEFAULT,
+ request->cancellable, (GAsyncReadyCallback)
+ web_view_gtkhtml_request_read_cb, request);
+}
+
+static void
+web_view_gtkhtml_gtkhtml_link_clicked (GtkHTML *html,
+ const gchar *uri)
+{
+ EWebViewGtkHTMLClass *class;
+ EWebViewGtkHTML *web_view;
+
+ web_view = E_WEB_VIEW_GTKHTML (html);
+
+ class = E_WEB_VIEW_GTKHTML_GET_CLASS (web_view);
+ g_return_if_fail (class->link_clicked != NULL);
+
+ class->link_clicked (web_view, uri);
+}
+
+static void
+web_view_gtkhtml_on_url (GtkHTML *html,
+ const gchar *uri)
+{
+ EWebViewGtkHTMLClass *class;
+ EWebViewGtkHTML *web_view;
+
+ web_view = E_WEB_VIEW_GTKHTML (html);
+
+ class = E_WEB_VIEW_GTKHTML_GET_CLASS (web_view);
+ g_return_if_fail (class->hovering_over_link != NULL);
+
+ /* XXX WebKit would supply a title here. */
+ class->hovering_over_link (web_view, NULL, uri);
+}
+
+static void
+web_view_gtkhtml_iframe_created (GtkHTML *html,
+ GtkHTML *iframe)
+{
+ g_signal_connect_swapped (
+ iframe, "button-press-event",
+ G_CALLBACK (web_view_gtkhtml_button_press_event_cb), html);
+}
+
+static gchar *
+web_view_gtkhtml_extract_uri (EWebViewGtkHTML *web_view,
+ GdkEventButton *event,
+ GtkHTML *html)
+{
+ gchar *uri;
+
+ if (event != NULL)
+ uri = gtk_html_get_url_at (html, event->x, event->y);
+ else
+ uri = gtk_html_get_cursor_url (html);
+
+ return uri;
+}
+
+static void
+web_view_gtkhtml_hovering_over_link (EWebViewGtkHTML *web_view,
+ const gchar *title,
+ const gchar *uri)
+{
+ CamelInternetAddress *address;
+ CamelURL *curl;
+ const gchar *format = NULL;
+ gchar *message = NULL;
+ gchar *who;
+
+ if (uri == NULL || *uri == '\0')
+ goto exit;
+
+ if (g_str_has_prefix (uri, "mailto:"))
+ format = _("Click to mail %s");
+ else if (g_str_has_prefix (uri, "callto:"))
+ format = _("Click to call %s");
+ else if (g_str_has_prefix (uri, "h323:"))
+ format = _("Click to call %s");
+ else if (g_str_has_prefix (uri, "sip:"))
+ format = _("Click to call %s");
+ else if (g_str_has_prefix (uri, "##"))
+ message = g_strdup (_("Click to hide/unhide addresses"));
+ else
+ message = g_strdup_printf (_("Click to open %s"), uri);
+
+ if (format == NULL)
+ goto exit;
+
+ /* XXX Use something other than Camel here. Surely
+ * there's other APIs around that can do this. */
+ curl = camel_url_new (uri, NULL);
+ address = camel_internet_address_new ();
+ camel_address_decode (CAMEL_ADDRESS (address), curl->path);
+ who = camel_address_format (CAMEL_ADDRESS (address));
+ g_object_unref (address);
+ camel_url_free (curl);
+
+ if (who == NULL)
+ who = g_strdup (strchr (uri, ':') + 1);
+
+ message = g_strdup_printf (format, who);
+
+ g_free (who);
+
+exit:
+ e_web_view_gtkhtml_status_message (web_view, message);
+
+ g_free (message);
+}
+
+static void
+web_view_gtkhtml_link_clicked (EWebViewGtkHTML *web_view,
+ const gchar *uri)
+{
+ gpointer parent;
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
+ parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
+
+ e_show_uri (parent, uri);
+}
+
+static void
+web_view_gtkhtml_load_string (EWebViewGtkHTML *web_view,
+ const gchar *string)
+{
+ if (string != NULL && *string != '\0')
+ gtk_html_load_from_string (GTK_HTML (web_view), string, -1);
+ else
+ e_web_view_gtkhtml_clear (web_view);
+}
+
+static void
+web_view_gtkhtml_copy_clipboard (EWebViewGtkHTML *web_view)
+{
+ gtk_html_command (GTK_HTML (web_view), "copy");
+}
+
+static void
+web_view_gtkhtml_cut_clipboard (EWebViewGtkHTML *web_view)
+{
+ if (e_web_view_gtkhtml_get_editable (web_view))
+ gtk_html_command (GTK_HTML (web_view), "cut");
+}
+
+static void
+web_view_gtkhtml_paste_clipboard (EWebViewGtkHTML *web_view)
+{
+ if (e_web_view_gtkhtml_get_editable (web_view))
+ gtk_html_command (GTK_HTML (web_view), "paste");
+}
+
+static gboolean
+web_view_gtkhtml_popup_event (EWebViewGtkHTML *web_view,
+ GdkEventButton *event,
+ const gchar *uri)
+{
+ e_web_view_gtkhtml_set_selected_uri (web_view, uri);
+ e_web_view_gtkhtml_show_popup_menu (web_view, event, NULL, NULL);
+
+ return TRUE;
+}
+
+static void
+web_view_gtkhtml_stop_loading (EWebViewGtkHTML *web_view)
+{
+ g_list_foreach (
+ web_view->priv->requests, (GFunc)
+ web_view_gtkhtml_request_cancel, NULL);
+
+ gtk_html_stop (GTK_HTML (web_view));
+}
+
+static void
+web_view_gtkhtml_update_actions (EWebViewGtkHTML *web_view)
+{
+ GtkActionGroup *action_group;
+ gboolean have_selection;
+ gboolean scheme_is_http = FALSE;
+ gboolean scheme_is_mailto = FALSE;
+ gboolean uri_is_valid = FALSE;
+ gboolean has_cursor_image;
+ gboolean visible;
+ const gchar *group_name;
+ const gchar *uri;
+
+ uri = e_web_view_gtkhtml_get_selected_uri (web_view);
+ have_selection = e_web_view_gtkhtml_is_selection_active (web_view);
+ has_cursor_image = e_web_view_gtkhtml_get_cursor_image (web_view) != NULL;
+
+ /* Parse the URI early so we know if the actions will work. */
+ if (uri != NULL) {
+ CamelURL *curl;
+
+ curl = camel_url_new (uri, NULL);
+ uri_is_valid = (curl != NULL);
+ camel_url_free (curl);
+
+ scheme_is_http =
+ (g_ascii_strncasecmp (uri, "http:", 5) == 0) ||
+ (g_ascii_strncasecmp (uri, "https:", 6) == 0);
+
+ scheme_is_mailto =
+ (g_ascii_strncasecmp (uri, "mailto:", 7) == 0);
+ }
+
+ /* Allow copying the URI even if it's malformed. */
+ group_name = "uri";
+ visible = (uri != NULL) && !scheme_is_mailto;
+ action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
+ gtk_action_group_set_visible (action_group, visible);
+
+ group_name = "http";
+ visible = uri_is_valid && scheme_is_http;
+ action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
+ gtk_action_group_set_visible (action_group, visible);
+
+ group_name = "mailto";
+ visible = uri_is_valid && scheme_is_mailto;
+ action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
+ gtk_action_group_set_visible (action_group, visible);
+
+ group_name = "image";
+ visible = has_cursor_image;
+ action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
+ gtk_action_group_set_visible (action_group, visible);
+
+ group_name = "selection";
+ visible = have_selection;
+ action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
+ gtk_action_group_set_visible (action_group, visible);
+
+ group_name = "standard";
+ visible = (uri == NULL);
+ action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
+ gtk_action_group_set_visible (action_group, visible);
+
+ group_name = "lockdown-printing";
+ visible = (uri == NULL) && !web_view->priv->disable_printing;
+ action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
+ gtk_action_group_set_visible (action_group, visible);
+
+ group_name = "lockdown-save-to-disk";
+ visible = (uri == NULL) && !web_view->priv->disable_save_to_disk;
+ action_group = e_web_view_gtkhtml_get_action_group (web_view, group_name);
+ gtk_action_group_set_visible (action_group, visible);
+}
+
+static void
+web_view_gtkhtml_submit_alert (EAlertSink *alert_sink,
+ EAlert *alert)
+{
+ GtkIconInfo *icon_info;
+ EWebViewGtkHTML *web_view;
+ GtkWidget *dialog;
+ GString *buffer;
+ const gchar *icon_name = NULL;
+ const gchar *filename;
+ gpointer parent;
+ gchar *icon_uri;
+ gint size = 0;
+ GError *error = NULL;
+
+ web_view = E_WEB_VIEW_GTKHTML (alert_sink);
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
+ parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
+
+ /* We use equivalent named icons instead of stock IDs,
+ * since it's easier to get the filename of the icon. */
+ switch (e_alert_get_message_type (alert)) {
+ case GTK_MESSAGE_INFO:
+ icon_name = "dialog-information";
+ break;
+
+ case GTK_MESSAGE_WARNING:
+ icon_name = "dialog-warning";
+ break;
+
+ case GTK_MESSAGE_ERROR:
+ icon_name = "dialog-error";
+ break;
+
+ default:
+ dialog = e_alert_dialog_new (parent, alert);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ return;
+ }
+
+ gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &size, NULL);
+
+ icon_info = gtk_icon_theme_lookup_icon (
+ gtk_icon_theme_get_default (),
+ icon_name, size, GTK_ICON_LOOKUP_NO_SVG);
+ g_return_if_fail (icon_info != NULL);
+
+ filename = gtk_icon_info_get_filename (icon_info);
+ icon_uri = g_filename_to_uri (filename, NULL, &error);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ buffer = g_string_sized_new (512);
+
+ g_string_append (
+ buffer,
+ "<html>"
+ "<head>"
+ "<meta http-equiv=\"content-type\""
+ " content=\"text/html; charset=utf-8\">"
+ "</head>"
+ "<body>");
+
+ g_string_append (
+ buffer,
+ "<table bgcolor='#000000' width='100%'"
+ " cellpadding='1' cellspacing='0'>"
+ "<tr>"
+ "<td>"
+ "<table bgcolor='#dddddd' width='100%' cellpadding='6'>"
+ "<tr>");
+
+ g_string_append_printf (
+ buffer,
+ "<tr>"
+ "<td valign='top'>"
+ "<img src='%s'/>"
+ "</td>"
+ "<td align='left' width='100%%'>"
+ "<h3>%s</h3>"
+ "%s"
+ "</td>"
+ "</tr>",
+ icon_uri,
+ e_alert_get_primary_text (alert),
+ e_alert_get_secondary_text (alert));
+
+ g_string_append (
+ buffer,
+ "</table>"
+ "</td>"
+ "</tr>"
+ "</table>"
+ "</body>"
+ "</html>");
+
+ e_web_view_gtkhtml_load_string (web_view, buffer->str);
+
+ g_string_free (buffer, TRUE);
+
+ gtk_icon_info_free (icon_info);
+ g_free (icon_uri);
+}
+
+static void
+web_view_gtkhtml_selectable_update_actions (ESelectable *selectable,
+ EFocusTracker *focus_tracker,
+ GdkAtom *clipboard_targets,
+ gint n_clipboard_targets)
+{
+ EWebViewGtkHTML *web_view;
+ GtkAction *action;
+ /*GtkTargetList *target_list;*/
+ gboolean can_paste = FALSE;
+ gboolean editable;
+ gboolean have_selection;
+ gboolean sensitive;
+ const gchar *tooltip;
+ /*gint ii;*/
+
+ web_view = E_WEB_VIEW_GTKHTML (selectable);
+ editable = e_web_view_gtkhtml_get_editable (web_view);
+ have_selection = e_web_view_gtkhtml_is_selection_active (web_view);
+
+ /* XXX GtkHtml implements its own clipboard instead of using
+ * GDK_SELECTION_CLIPBOARD, so we don't get notifications
+ * when the clipboard contents change. The logic below
+ * is what we would do if GtkHtml worked properly.
+ * Instead, we need to keep the Paste action sensitive so
+ * its accelerator overrides GtkHtml's key binding. */
+#if 0
+ target_list = e_selectable_get_paste_target_list (selectable);
+ for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++)
+ can_paste = gtk_target_list_find (
+ target_list, clipboard_targets[ii], NULL);
+#endif
+ can_paste = TRUE;
+
+ action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
+ sensitive = editable && have_selection;
+ tooltip = _("Cut the selection");
+ gtk_action_set_sensitive (action, sensitive);
+ gtk_action_set_tooltip (action, tooltip);
+
+ action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
+ sensitive = have_selection;
+ tooltip = _("Copy the selection");
+ gtk_action_set_sensitive (action, sensitive);
+ gtk_action_set_tooltip (action, tooltip);
+
+ action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
+ sensitive = editable && can_paste;
+ tooltip = _("Paste the clipboard");
+ gtk_action_set_sensitive (action, sensitive);
+ gtk_action_set_tooltip (action, tooltip);
+
+ action = e_focus_tracker_get_select_all_action (focus_tracker);
+ sensitive = TRUE;
+ tooltip = _("Select all text and images");
+ gtk_action_set_sensitive (action, sensitive);
+ gtk_action_set_tooltip (action, tooltip);
+}
+
+static void
+web_view_gtkhtml_selectable_cut_clipboard (ESelectable *selectable)
+{
+ e_web_view_gtkhtml_cut_clipboard (E_WEB_VIEW_GTKHTML (selectable));
+}
+
+static void
+web_view_gtkhtml_selectable_copy_clipboard (ESelectable *selectable)
+{
+ e_web_view_gtkhtml_copy_clipboard (E_WEB_VIEW_GTKHTML (selectable));
+}
+
+static void
+web_view_gtkhtml_selectable_paste_clipboard (ESelectable *selectable)
+{
+ e_web_view_gtkhtml_paste_clipboard (E_WEB_VIEW_GTKHTML (selectable));
+}
+
+static void
+web_view_gtkhtml_selectable_select_all (ESelectable *selectable)
+{
+ e_web_view_gtkhtml_select_all (E_WEB_VIEW_GTKHTML (selectable));
+}
+
+static void
+e_web_view_gtkhtml_class_init (EWebViewGtkHTMLClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkHTMLClass *html_class;
+
+ parent_class = g_type_class_peek_parent (class);
+ g_type_class_add_private (class, sizeof (EWebViewGtkHTMLPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = web_view_gtkhtml_set_property;
+ object_class->get_property = web_view_gtkhtml_get_property;
+ object_class->dispose = web_view_gtkhtml_dispose;
+ object_class->finalize = web_view_gtkhtml_finalize;
+ object_class->constructed = web_view_gtkhtml_constructed;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->button_press_event = web_view_gtkhtml_button_press_event;
+ widget_class->scroll_event = web_view_gtkhtml_scroll_event;
+
+ html_class = GTK_HTML_CLASS (class);
+ html_class->url_requested = web_view_gtkhtml_url_requested;
+ html_class->link_clicked = web_view_gtkhtml_gtkhtml_link_clicked;
+ html_class->on_url = web_view_gtkhtml_on_url;
+ html_class->iframe_created = web_view_gtkhtml_iframe_created;
+
+ class->extract_uri = web_view_gtkhtml_extract_uri;
+ class->hovering_over_link = web_view_gtkhtml_hovering_over_link;
+ class->link_clicked = web_view_gtkhtml_link_clicked;
+ class->load_string = web_view_gtkhtml_load_string;
+ class->copy_clipboard = web_view_gtkhtml_copy_clipboard;
+ class->cut_clipboard = web_view_gtkhtml_cut_clipboard;
+ class->paste_clipboard = web_view_gtkhtml_paste_clipboard;
+ class->popup_event = web_view_gtkhtml_popup_event;
+ class->stop_loading = web_view_gtkhtml_stop_loading;
+ class->update_actions = web_view_gtkhtml_update_actions;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ANIMATE,
+ g_param_spec_boolean (
+ "animate",
+ "Animate Images",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CARET_MODE,
+ g_param_spec_boolean (
+ "caret-mode",
+ "Caret Mode",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ /* Inherited from ESelectableInterface */
+ g_object_class_override_property (
+ object_class,
+ PROP_COPY_TARGET_LIST,
+ "copy-target-list");
+
+ g_object_class_install_property (
+ object_class,
+ PROP_DISABLE_PRINTING,
+ g_param_spec_boolean (
+ "disable-printing",
+ "Disable Printing",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_DISABLE_SAVE_TO_DISK,
+ g_param_spec_boolean (
+ "disable-save-to-disk",
+ "Disable Save-to-Disk",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_EDITABLE,
+ g_param_spec_boolean (
+ "editable",
+ "Editable",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_INLINE_SPELLING,
+ g_param_spec_boolean (
+ "inline-spelling",
+ "Inline Spelling",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_MAGIC_LINKS,
+ g_param_spec_boolean (
+ "magic-links",
+ "Magic Links",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_MAGIC_SMILEYS,
+ g_param_spec_boolean (
+ "magic-smileys",
+ "Magic Smileys",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_OPEN_PROXY,
+ g_param_spec_object (
+ "open-proxy",
+ "Open Proxy",
+ NULL,
+ GTK_TYPE_ACTION,
+ G_PARAM_READWRITE));
+
+ /* Inherited from ESelectableInterface */
+ g_object_class_override_property (
+ object_class,
+ PROP_PASTE_TARGET_LIST,
+ "paste-target-list");
+
+ g_object_class_install_property (
+ object_class,
+ PROP_PRINT_PROXY,
+ g_param_spec_object (
+ "print-proxy",
+ "Print Proxy",
+ NULL,
+ GTK_TYPE_ACTION,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SAVE_AS_PROXY,
+ g_param_spec_object (
+ "save-as-proxy",
+ "Save As Proxy",
+ NULL,
+ GTK_TYPE_ACTION,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SELECTED_URI,
+ g_param_spec_string (
+ "selected-uri",
+ "Selected URI",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CURSOR_IMAGE,
+ g_param_spec_object (
+ "cursor-image",
+ "Image animation at the mouse cursor",
+ NULL,
+ GDK_TYPE_PIXBUF_ANIMATION,
+ G_PARAM_READWRITE));
+
+ signals[COPY_CLIPBOARD] = g_signal_new (
+ "copy-clipboard",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EWebViewGtkHTMLClass, copy_clipboard),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[CUT_CLIPBOARD] = g_signal_new (
+ "cut-clipboard",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EWebViewGtkHTMLClass, cut_clipboard),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[PASTE_CLIPBOARD] = g_signal_new (
+ "paste-clipboard",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EWebViewGtkHTMLClass, paste_clipboard),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[POPUP_EVENT] = g_signal_new (
+ "popup-event",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EWebViewGtkHTMLClass, popup_event),
+ g_signal_accumulator_true_handled, NULL,
+ e_marshal_BOOLEAN__BOXED_STRING,
+ G_TYPE_BOOLEAN, 2,
+ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE,
+ G_TYPE_STRING);
+
+ signals[STATUS_MESSAGE] = g_signal_new (
+ "status-message",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EWebViewGtkHTMLClass, status_message),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+ signals[STOP_LOADING] = g_signal_new (
+ "stop-loading",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EWebViewGtkHTMLClass, stop_loading),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[UPDATE_ACTIONS] = g_signal_new (
+ "update-actions",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EWebViewGtkHTMLClass, update_actions),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /* return TRUE when a signal handler processed the mailto URI */
+ signals[PROCESS_MAILTO] = g_signal_new (
+ "process-mailto",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EWebViewGtkHTMLClass, process_mailto),
+ NULL, NULL,
+ e_marshal_BOOLEAN__STRING,
+ G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
+}
+
+static void
+e_web_view_gtkhtml_alert_sink_init (EAlertSinkInterface *interface)
+{
+ interface->submit_alert = web_view_gtkhtml_submit_alert;
+}
+
+static void
+e_web_view_gtkhtml_selectable_init (ESelectableInterface *interface)
+{
+ interface->update_actions = web_view_gtkhtml_selectable_update_actions;
+ interface->cut_clipboard = web_view_gtkhtml_selectable_cut_clipboard;
+ interface->copy_clipboard = web_view_gtkhtml_selectable_copy_clipboard;
+ interface->paste_clipboard = web_view_gtkhtml_selectable_paste_clipboard;
+ interface->select_all = web_view_gtkhtml_selectable_select_all;
+}
+
+static void
+e_web_view_gtkhtml_init (EWebViewGtkHTML *web_view)
+{
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ GtkTargetList *target_list;
+ EPopupAction *popup_action;
+ const gchar *domain = GETTEXT_PACKAGE;
+ const gchar *id;
+ GError *error = NULL;
+
+ web_view->priv = E_WEB_VIEW_GTKHTML_GET_PRIVATE (web_view);
+
+ ui_manager = gtk_ui_manager_new ();
+ web_view->priv->ui_manager = ui_manager;
+
+ g_signal_connect_swapped (
+ ui_manager, "connect-proxy",
+ G_CALLBACK (web_view_gtkhtml_connect_proxy_cb), web_view);
+
+ target_list = gtk_target_list_new (NULL, 0);
+ web_view->priv->copy_target_list = target_list;
+
+ target_list = gtk_target_list_new (NULL, 0);
+ web_view->priv->paste_target_list = target_list;
+
+ action_group = gtk_action_group_new ("uri");
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ g_object_unref (action_group);
+
+ gtk_action_group_add_actions (
+ action_group, uri_entries,
+ G_N_ELEMENTS (uri_entries), web_view);
+
+ action_group = gtk_action_group_new ("http");
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ g_object_unref (action_group);
+
+ gtk_action_group_add_actions (
+ action_group, http_entries,
+ G_N_ELEMENTS (http_entries), web_view);
+
+ action_group = gtk_action_group_new ("mailto");
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ g_object_unref (action_group);
+
+ gtk_action_group_add_actions (
+ action_group, mailto_entries,
+ G_N_ELEMENTS (mailto_entries), web_view);
+
+ action_group = gtk_action_group_new ("image");
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ g_object_unref (action_group);
+
+ gtk_action_group_add_actions (
+ action_group, image_entries,
+ G_N_ELEMENTS (image_entries), web_view);
+
+ action_group = gtk_action_group_new ("selection");
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ g_object_unref (action_group);
+
+ gtk_action_group_add_actions (
+ action_group, selection_entries,
+ G_N_ELEMENTS (selection_entries), web_view);
+
+ action_group = gtk_action_group_new ("standard");
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ g_object_unref (action_group);
+
+ gtk_action_group_add_actions (
+ action_group, standard_entries,
+ G_N_ELEMENTS (standard_entries), web_view);
+
+ popup_action = e_popup_action_new ("open");
+ gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
+ g_object_unref (popup_action);
+
+ g_object_bind_property (
+ web_view, "open-proxy",
+ popup_action, "related-action",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ /* Support lockdown. */
+
+ action_group = gtk_action_group_new ("lockdown-printing");
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ g_object_unref (action_group);
+
+ popup_action = e_popup_action_new ("print");
+ gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
+ g_object_unref (popup_action);
+
+ g_object_bind_property (
+ web_view, "print-proxy",
+ popup_action, "related-action",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ action_group = gtk_action_group_new ("lockdown-save-to-disk");
+ gtk_action_group_set_translation_domain (action_group, domain);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ g_object_unref (action_group);
+
+ popup_action = e_popup_action_new ("save-as");
+ gtk_action_group_add_action (action_group, GTK_ACTION (popup_action));
+ g_object_unref (popup_action);
+
+ g_object_bind_property (
+ web_view, "save-as-proxy",
+ popup_action, "related-action",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ /* Because we are loading from a hard-coded string, there is
+ * no chance of I/O errors. Failure here implies a malformed
+ * UI definition. Full stop. */
+ gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+ if (error != NULL)
+ g_error ("%s", error->message);
+
+ id = "org.gnome.evolution.webview";
+ e_plugin_ui_register_manager (ui_manager, id, web_view);
+ e_plugin_ui_enable_manager (ui_manager, id);
+
+ e_extensible_load_extensions (E_EXTENSIBLE (web_view));
+}
+
+GtkWidget *
+e_web_view_gtkhtml_new (void)
+{
+ return g_object_new (E_TYPE_WEB_VIEW_GTKHTML, NULL);
+}
+
+void
+e_web_view_gtkhtml_clear (EWebViewGtkHTML *web_view)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ gtk_html_load_empty (GTK_HTML (web_view));
+}
+
+void
+e_web_view_gtkhtml_load_string (EWebViewGtkHTML *web_view,
+ const gchar *string)
+{
+ EWebViewGtkHTMLClass *class;
+
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ class = E_WEB_VIEW_GTKHTML_GET_CLASS (web_view);
+ g_return_if_fail (class->load_string != NULL);
+
+ class->load_string (web_view, string);
+}
+
+gboolean
+e_web_view_gtkhtml_get_animate (EWebViewGtkHTML *web_view)
+{
+ /* XXX This is just here to maintain symmetry
+ * with e_web_view_set_animate(). */
+
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return gtk_html_get_animate (GTK_HTML (web_view));
+}
+
+void
+e_web_view_gtkhtml_set_animate (EWebViewGtkHTML *web_view,
+ gboolean animate)
+{
+ /* XXX GtkHTML does not utilize GObject properties as well
+ * as it could. This just wraps gtk_html_set_animate()
+ * so we can get a "notify::animate" signal. */
+
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ gtk_html_set_animate (GTK_HTML (web_view), animate);
+
+ g_object_notify (G_OBJECT (web_view), "animate");
+}
+
+gboolean
+e_web_view_gtkhtml_get_caret_mode (EWebViewGtkHTML *web_view)
+{
+ /* XXX This is just here to maintain symmetry
+ * with e_web_view_set_caret_mode(). */
+
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return gtk_html_get_caret_mode (GTK_HTML (web_view));
+}
+
+void
+e_web_view_gtkhtml_set_caret_mode (EWebViewGtkHTML *web_view,
+ gboolean caret_mode)
+{
+ /* XXX GtkHTML does not utilize GObject properties as well
+ * as it could. This just wraps gtk_html_set_caret_mode()
+ * so we can get a "notify::caret-mode" signal. */
+
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ gtk_html_set_caret_mode (GTK_HTML (web_view), caret_mode);
+
+ g_object_notify (G_OBJECT (web_view), "caret-mode");
+}
+
+GtkTargetList *
+e_web_view_gtkhtml_get_copy_target_list (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
+
+ return web_view->priv->copy_target_list;
+}
+
+gboolean
+e_web_view_gtkhtml_get_disable_printing (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return web_view->priv->disable_printing;
+}
+
+void
+e_web_view_gtkhtml_set_disable_printing (EWebViewGtkHTML *web_view,
+ gboolean disable_printing)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ web_view->priv->disable_printing = disable_printing;
+
+ g_object_notify (G_OBJECT (web_view), "disable-printing");
+}
+
+gboolean
+e_web_view_gtkhtml_get_disable_save_to_disk (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return web_view->priv->disable_save_to_disk;
+}
+
+void
+e_web_view_gtkhtml_set_disable_save_to_disk (EWebViewGtkHTML *web_view,
+ gboolean disable_save_to_disk)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ web_view->priv->disable_save_to_disk = disable_save_to_disk;
+
+ g_object_notify (G_OBJECT (web_view), "disable-save-to-disk");
+}
+
+gboolean
+e_web_view_gtkhtml_get_editable (EWebViewGtkHTML *web_view)
+{
+ /* XXX This is just here to maintain symmetry
+ * with e_web_view_set_editable(). */
+
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return gtk_html_get_editable (GTK_HTML (web_view));
+}
+
+void
+e_web_view_gtkhtml_set_editable (EWebViewGtkHTML *web_view,
+ gboolean editable)
+{
+ /* XXX GtkHTML does not utilize GObject properties as well
+ * as it could. This just wraps gtk_html_set_editable()
+ * so we can get a "notify::editable" signal. */
+
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ gtk_html_set_editable (GTK_HTML (web_view), editable);
+
+ g_object_notify (G_OBJECT (web_view), "editable");
+}
+
+gboolean
+e_web_view_gtkhtml_get_inline_spelling (EWebViewGtkHTML *web_view)
+{
+ /* XXX This is just here to maintain symmetry
+ * with e_web_view_set_inline_spelling(). */
+
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return gtk_html_get_inline_spelling (GTK_HTML (web_view));
+}
+
+void
+e_web_view_gtkhtml_set_inline_spelling (EWebViewGtkHTML *web_view,
+ gboolean inline_spelling)
+{
+ /* XXX GtkHTML does not utilize GObject properties as well
+ * as it could. This just wraps gtk_html_set_inline_spelling()
+ * so we get a "notify::inline-spelling" signal. */
+
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ gtk_html_set_inline_spelling (GTK_HTML (web_view), inline_spelling);
+
+ g_object_notify (G_OBJECT (web_view), "inline-spelling");
+}
+
+gboolean
+e_web_view_gtkhtml_get_magic_links (EWebViewGtkHTML *web_view)
+{
+ /* XXX This is just here to maintain symmetry
+ * with e_web_view_set_magic_links(). */
+
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return gtk_html_get_magic_links (GTK_HTML (web_view));
+}
+
+void
+e_web_view_gtkhtml_set_magic_links (EWebViewGtkHTML *web_view,
+ gboolean magic_links)
+{
+ /* XXX GtkHTML does not utilize GObject properties as well
+ * as it could. This just wraps gtk_html_set_magic_links()
+ * so we can get a "notify::magic-links" signal. */
+
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ gtk_html_set_magic_links (GTK_HTML (web_view), magic_links);
+
+ g_object_notify (G_OBJECT (web_view), "magic-links");
+}
+
+gboolean
+e_web_view_gtkhtml_get_magic_smileys (EWebViewGtkHTML *web_view)
+{
+ /* XXX This is just here to maintain symmetry
+ * with e_web_view_set_magic_smileys(). */
+
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return gtk_html_get_magic_smileys (GTK_HTML (web_view));
+}
+
+void
+e_web_view_gtkhtml_set_magic_smileys (EWebViewGtkHTML *web_view,
+ gboolean magic_smileys)
+{
+ /* XXX GtkHTML does not utilize GObject properties as well
+ * as it could. This just wraps gtk_html_set_magic_smileys()
+ * so we can get a "notify::magic-smileys" signal. */
+
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ gtk_html_set_magic_smileys (GTK_HTML (web_view), magic_smileys);
+
+ g_object_notify (G_OBJECT (web_view), "magic-smileys");
+}
+
+const gchar *
+e_web_view_gtkhtml_get_selected_uri (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
+
+ return web_view->priv->selected_uri;
+}
+
+void
+e_web_view_gtkhtml_set_selected_uri (EWebViewGtkHTML *web_view,
+ const gchar *selected_uri)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ g_free (web_view->priv->selected_uri);
+ web_view->priv->selected_uri = g_strdup (selected_uri);
+
+ g_object_notify (G_OBJECT (web_view), "selected-uri");
+}
+
+GdkPixbufAnimation *
+e_web_view_gtkhtml_get_cursor_image (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
+
+ return web_view->priv->cursor_image;
+}
+
+void
+e_web_view_gtkhtml_set_cursor_image (EWebViewGtkHTML *web_view,
+ GdkPixbufAnimation *image)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ if (image != NULL)
+ g_object_ref (image);
+
+ if (web_view->priv->cursor_image != NULL)
+ g_object_unref (web_view->priv->cursor_image);
+
+ web_view->priv->cursor_image = image;
+
+ g_object_notify (G_OBJECT (web_view), "cursor-image");
+}
+
+GtkAction *
+e_web_view_gtkhtml_get_open_proxy (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return web_view->priv->open_proxy;
+}
+
+void
+e_web_view_gtkhtml_set_open_proxy (EWebViewGtkHTML *web_view,
+ GtkAction *open_proxy)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ if (open_proxy != NULL) {
+ g_return_if_fail (GTK_IS_ACTION (open_proxy));
+ g_object_ref (open_proxy);
+ }
+
+ if (web_view->priv->open_proxy != NULL)
+ g_object_unref (web_view->priv->open_proxy);
+
+ web_view->priv->open_proxy = open_proxy;
+
+ g_object_notify (G_OBJECT (web_view), "open-proxy");
+}
+
+GtkTargetList *
+e_web_view_gtkhtml_get_paste_target_list (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
+
+ return web_view->priv->paste_target_list;
+}
+
+GtkAction *
+e_web_view_gtkhtml_get_print_proxy (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return web_view->priv->print_proxy;
+}
+
+void
+e_web_view_gtkhtml_set_print_proxy (EWebViewGtkHTML *web_view,
+ GtkAction *print_proxy)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ if (print_proxy != NULL) {
+ g_return_if_fail (GTK_IS_ACTION (print_proxy));
+ g_object_ref (print_proxy);
+ }
+
+ if (web_view->priv->print_proxy != NULL)
+ g_object_unref (web_view->priv->print_proxy);
+
+ web_view->priv->print_proxy = print_proxy;
+
+ g_object_notify (G_OBJECT (web_view), "print-proxy");
+}
+
+GtkAction *
+e_web_view_gtkhtml_get_save_as_proxy (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return web_view->priv->save_as_proxy;
+}
+
+void
+e_web_view_gtkhtml_set_save_as_proxy (EWebViewGtkHTML *web_view,
+ GtkAction *save_as_proxy)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ if (save_as_proxy != NULL) {
+ g_return_if_fail (GTK_IS_ACTION (save_as_proxy));
+ g_object_ref (save_as_proxy);
+ }
+
+ if (web_view->priv->save_as_proxy != NULL)
+ g_object_unref (web_view->priv->save_as_proxy);
+
+ web_view->priv->save_as_proxy = save_as_proxy;
+
+ g_object_notify (G_OBJECT (web_view), "save-as-proxy");
+}
+
+GtkAction *
+e_web_view_gtkhtml_get_action (EWebViewGtkHTML *web_view,
+ const gchar *action_name)
+{
+ GtkUIManager *ui_manager;
+
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
+ g_return_val_if_fail (action_name != NULL, NULL);
+
+ ui_manager = e_web_view_gtkhtml_get_ui_manager (web_view);
+
+ return e_lookup_action (ui_manager, action_name);
+}
+
+GtkActionGroup *
+e_web_view_gtkhtml_get_action_group (EWebViewGtkHTML *web_view,
+ const gchar *group_name)
+{
+ GtkUIManager *ui_manager;
+
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
+ g_return_val_if_fail (group_name != NULL, NULL);
+
+ ui_manager = e_web_view_gtkhtml_get_ui_manager (web_view);
+
+ return e_lookup_action_group (ui_manager, group_name);
+}
+
+gchar *
+e_web_view_gtkhtml_extract_uri (EWebViewGtkHTML *web_view,
+ GdkEventButton *event,
+ GtkHTML *frame)
+{
+ EWebViewGtkHTMLClass *class;
+
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
+
+ if (frame == NULL)
+ frame = GTK_HTML (web_view);
+
+ class = E_WEB_VIEW_GTKHTML_GET_CLASS (web_view);
+ g_return_val_if_fail (class->extract_uri != NULL, NULL);
+
+ return class->extract_uri (web_view, event, frame);
+}
+
+void
+e_web_view_gtkhtml_copy_clipboard (EWebViewGtkHTML *web_view)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ g_signal_emit (web_view, signals[COPY_CLIPBOARD], 0);
+}
+
+void
+e_web_view_gtkhtml_cut_clipboard (EWebViewGtkHTML *web_view)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ g_signal_emit (web_view, signals[CUT_CLIPBOARD], 0);
+}
+
+gboolean
+e_web_view_gtkhtml_is_selection_active (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return gtk_html_command (GTK_HTML (web_view), "is-selection-active");
+}
+
+void
+e_web_view_gtkhtml_paste_clipboard (EWebViewGtkHTML *web_view)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ g_signal_emit (web_view, signals[PASTE_CLIPBOARD], 0);
+}
+
+gboolean
+e_web_view_gtkhtml_scroll_forward (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return gtk_html_command (GTK_HTML (web_view), "scroll-forward");
+}
+
+gboolean
+e_web_view_gtkhtml_scroll_backward (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), FALSE);
+
+ return gtk_html_command (GTK_HTML (web_view), "scroll-backward");
+}
+
+void
+e_web_view_gtkhtml_select_all (EWebViewGtkHTML *web_view)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ gtk_html_command (GTK_HTML (web_view), "select-all");
+}
+
+void
+e_web_view_gtkhtml_unselect_all (EWebViewGtkHTML *web_view)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ gtk_html_command (GTK_HTML (web_view), "unselect-all");
+}
+
+void
+e_web_view_gtkhtml_zoom_100 (EWebViewGtkHTML *web_view)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ gtk_html_command (GTK_HTML (web_view), "zoom-reset");
+}
+
+void
+e_web_view_gtkhtml_zoom_in (EWebViewGtkHTML *web_view)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ gtk_html_command (GTK_HTML (web_view), "zoom-in");
+}
+
+void
+e_web_view_gtkhtml_zoom_out (EWebViewGtkHTML *web_view)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ gtk_html_command (GTK_HTML (web_view), "zoom-out");
+}
+
+GtkUIManager *
+e_web_view_gtkhtml_get_ui_manager (EWebViewGtkHTML *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
+
+ return web_view->priv->ui_manager;
+}
+
+GtkWidget *
+e_web_view_gtkhtml_get_popup_menu (EWebViewGtkHTML *web_view)
+{
+ GtkUIManager *ui_manager;
+ GtkWidget *menu;
+
+ g_return_val_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view), NULL);
+
+ ui_manager = e_web_view_gtkhtml_get_ui_manager (web_view);
+ menu = gtk_ui_manager_get_widget (ui_manager, "/context");
+ g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
+
+ return menu;
+}
+
+void
+e_web_view_gtkhtml_show_popup_menu (EWebViewGtkHTML *web_view,
+ GdkEventButton *event,
+ GtkMenuPositionFunc func,
+ gpointer user_data)
+{
+ GtkWidget *menu;
+
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ e_web_view_gtkhtml_update_actions (web_view);
+
+ menu = e_web_view_gtkhtml_get_popup_menu (web_view);
+
+ if (event != NULL)
+ gtk_menu_popup (
+ GTK_MENU (menu), NULL, NULL, func,
+ user_data, event->button, event->time);
+ else
+ gtk_menu_popup (
+ GTK_MENU (menu), NULL, NULL, func,
+ user_data, 0, gtk_get_current_event_time ());
+}
+
+void
+e_web_view_gtkhtml_status_message (EWebViewGtkHTML *web_view,
+ const gchar *status_message)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ g_signal_emit (web_view, signals[STATUS_MESSAGE], 0, status_message);
+}
+
+void
+e_web_view_gtkhtml_stop_loading (EWebViewGtkHTML *web_view)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ g_signal_emit (web_view, signals[STOP_LOADING], 0);
+}
+
+void
+e_web_view_gtkhtml_update_actions (EWebViewGtkHTML *web_view)
+{
+ g_return_if_fail (E_IS_WEB_VIEW_GTKHTML (web_view));
+
+ g_signal_emit (web_view, signals[UPDATE_ACTIONS], 0);
+}
diff --git a/widgets/misc/e-web-view-gtkhtml.h b/widgets/misc/e-web-view-gtkhtml.h
new file mode 100644
index 0000000000..aab06e8b54
--- /dev/null
+++ b/widgets/misc/e-web-view-gtkhtml.h
@@ -0,0 +1,173 @@
+/*
+ * e-web-view-gtkhtml.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/* This is intended to serve as a common base class for all HTML viewing
+ * needs in Evolution. Currently based on GtkHTML, the idea is to wrap
+ * the GtkHTML API enough that we no longer have to make direct calls to
+ * it. This should help smooth the transition to WebKit/GTK+.
+ *
+ * This class handles basic tasks like mouse hovers over links, clicked
+ * links, and servicing URI requests asynchronously via GIO. */
+
+#ifndef E_WEB_VIEW_GTKHTML_H
+#define E_WEB_VIEW_GTKHTML_H
+
+#include <gtkhtml/gtkhtml.h>
+
+/* Standard GObject macros */
+#define E_TYPE_WEB_VIEW_GTKHTML \
+ (e_web_view_gtkhtml_get_type ())
+#define E_WEB_VIEW_GTKHTML(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_WEB_VIEW_GTKHTML, EWebViewGtkHTML))
+#define E_WEB_VIEW_GTKHTML_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_WEB_VIEW_GTKHTML, EWebViewGtkHTMLClass))
+#define E_IS_WEB_VIEW_GTKHTML(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_WEB_VIEW_GTKHTML))
+#define E_IS_WEB_VIEW_GTKHTML_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_WEB_VIEW_GTKHTML))
+#define E_WEB_VIEW_GTKHTML_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_WEB_VIEW_GTKHTML, EWebViewGtkHTMLClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EWebViewGtkHTML EWebViewGtkHTML;
+typedef struct _EWebViewGtkHTMLClass EWebViewGtkHTMLClass;
+typedef struct _EWebViewGtkHTMLPrivate EWebViewGtkHTMLPrivate;
+
+struct _EWebViewGtkHTML {
+ GtkHTML parent;
+ EWebViewGtkHTMLPrivate *priv;
+};
+
+struct _EWebViewGtkHTMLClass {
+ GtkHTMLClass parent_class;
+
+ /* Methods */
+ gchar * (*extract_uri) (EWebViewGtkHTML *web_view,
+ GdkEventButton *event,
+ GtkHTML *frame);
+ void (*hovering_over_link) (EWebViewGtkHTML *web_view,
+ const gchar *title,
+ const gchar *uri);
+ void (*link_clicked) (EWebViewGtkHTML *web_view,
+ const gchar *uri);
+ void (*load_string) (EWebViewGtkHTML *web_view,
+ const gchar *load_string);
+
+ /* Signals */
+ void (*copy_clipboard) (EWebViewGtkHTML *web_view);
+ void (*cut_clipboard) (EWebViewGtkHTML *web_view);
+ void (*paste_clipboard) (EWebViewGtkHTML *web_view);
+ gboolean (*popup_event) (EWebViewGtkHTML *web_view,
+ GdkEventButton *event,
+ const gchar *uri);
+ void (*status_message) (EWebViewGtkHTML *web_view,
+ const gchar *status_message);
+ void (*stop_loading) (EWebViewGtkHTML *web_view);
+ void (*update_actions) (EWebViewGtkHTML *web_view);
+ gboolean (*process_mailto) (EWebViewGtkHTML *web_view,
+ const gchar *mailto_uri);
+};
+
+GType e_web_view_gtkhtml_get_type (void);
+GtkWidget * e_web_view_gtkhtml_new (void);
+void e_web_view_gtkhtml_clear (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_load_string (EWebViewGtkHTML *web_view,
+ const gchar *string);
+gboolean e_web_view_gtkhtml_get_animate (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_animate (EWebViewGtkHTML *web_view,
+ gboolean animate);
+gboolean e_web_view_gtkhtml_get_caret_mode (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_caret_mode (EWebViewGtkHTML *web_view,
+ gboolean caret_mode);
+GtkTargetList * e_web_view_gtkhtml_get_copy_target_list (EWebViewGtkHTML *web_view);
+gboolean e_web_view_gtkhtml_get_disable_printing (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_disable_printing (EWebViewGtkHTML *web_view,
+ gboolean disable_printing);
+gboolean e_web_view_gtkhtml_get_disable_save_to_disk
+ (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_disable_save_to_disk
+ (EWebViewGtkHTML *web_view,
+ gboolean disable_save_to_disk);
+gboolean e_web_view_gtkhtml_get_editable (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_editable (EWebViewGtkHTML *web_view,
+ gboolean editable);
+gboolean e_web_view_gtkhtml_get_inline_spelling (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_inline_spelling (EWebViewGtkHTML *web_view,
+ gboolean inline_spelling);
+gboolean e_web_view_gtkhtml_get_magic_links (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_magic_links (EWebViewGtkHTML *web_view,
+ gboolean magic_links);
+gboolean e_web_view_gtkhtml_get_magic_smileys (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_magic_smileys (EWebViewGtkHTML *web_view,
+ gboolean magic_smileys);
+const gchar * e_web_view_gtkhtml_get_selected_uri (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_selected_uri (EWebViewGtkHTML *web_view,
+ const gchar *selected_uri);
+GdkPixbufAnimation *
+ e_web_view_gtkhtml_get_cursor_image (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_cursor_image (EWebViewGtkHTML *web_view,
+ GdkPixbufAnimation *animation);
+GtkAction * e_web_view_gtkhtml_get_open_proxy (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_open_proxy (EWebViewGtkHTML *web_view,
+ GtkAction *open_proxy);
+GtkTargetList * e_web_view_gtkhtml_get_paste_target_list
+ (EWebViewGtkHTML *web_view);
+GtkAction * e_web_view_gtkhtml_get_print_proxy (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_print_proxy (EWebViewGtkHTML *web_view,
+ GtkAction *print_proxy);
+GtkAction * e_web_view_gtkhtml_get_save_as_proxy (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_set_save_as_proxy (EWebViewGtkHTML *web_view,
+ GtkAction *save_as_proxy);
+GtkAction * e_web_view_gtkhtml_get_action (EWebViewGtkHTML *web_view,
+ const gchar *action_name);
+GtkActionGroup *e_web_view_gtkhtml_get_action_group (EWebViewGtkHTML *web_view,
+ const gchar *group_name);
+gchar * e_web_view_gtkhtml_extract_uri (EWebViewGtkHTML *web_view,
+ GdkEventButton *event,
+ GtkHTML *frame);
+void e_web_view_gtkhtml_copy_clipboard (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_cut_clipboard (EWebViewGtkHTML *web_view);
+gboolean e_web_view_gtkhtml_is_selection_active (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_paste_clipboard (EWebViewGtkHTML *web_view);
+gboolean e_web_view_gtkhtml_scroll_forward (EWebViewGtkHTML *web_view);
+gboolean e_web_view_gtkhtml_scroll_backward (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_select_all (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_unselect_all (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_zoom_100 (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_zoom_in (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_zoom_out (EWebViewGtkHTML *web_view);
+GtkUIManager * e_web_view_gtkhtml_get_ui_manager (EWebViewGtkHTML *web_view);
+GtkWidget * e_web_view_gtkhtml_get_popup_menu (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_show_popup_menu (EWebViewGtkHTML *web_view,
+ GdkEventButton *event,
+ GtkMenuPositionFunc func,
+ gpointer user_data);
+void e_web_view_gtkhtml_status_message (EWebViewGtkHTML *web_view,
+ const gchar *status_message);
+void e_web_view_gtkhtml_stop_loading (EWebViewGtkHTML *web_view);
+void e_web_view_gtkhtml_update_actions (EWebViewGtkHTML *web_view);
+
+G_END_DECLS
+
+#endif /* E_WEB_VIEW_GTKHTML_H */
diff --git a/widgets/misc/e-web-view.c b/widgets/misc/e-web-view.c
index 26c922470b..1fdc5559d9 100644
--- a/widgets/misc/e-web-view.c
+++ b/widgets/misc/e-web-view.c
@@ -22,6 +22,10 @@
#include "e-web-view.h"
+#include <math.h>
+
+#include <JavaScriptCore/JavaScript.h>
+
#include <string.h>
#include <glib/gi18n-lib.h>
@@ -33,8 +37,11 @@
#include <libevolution-utils/e-alert-sink.h>
#include <e-util/e-plugin-ui.h>
+#include <mail/e-mail-request.h>
+
#include "e-popup-action.h"
#include "e-selectable.h"
+#include <stdlib.h>
#define E_WEB_VIEW_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -49,51 +56,39 @@ struct _EWebViewPrivate {
GdkPixbufAnimation *cursor_image;
gchar *cursor_image_src;
+ GHashTable *js_callbacks;
+
+ GSList *highlights;
+
GtkAction *open_proxy;
GtkAction *print_proxy;
GtkAction *save_as_proxy;
- GtkTargetList *copy_target_list;
- GtkTargetList *paste_target_list;
-
/* Lockdown Options */
guint disable_printing : 1;
guint disable_save_to_disk : 1;
-};
-struct _EWebViewRequest {
- GFile *file;
- EWebView *web_view;
- GCancellable *cancellable;
- GInputStream *input_stream;
- GtkHTMLStream *output_stream;
- gchar buffer[4096];
+ guint caret_mode : 1;
};
enum {
PROP_0,
- PROP_ANIMATE,
PROP_CARET_MODE,
PROP_COPY_TARGET_LIST,
PROP_CURSOR_IMAGE,
PROP_CURSOR_IMAGE_SRC,
PROP_DISABLE_PRINTING,
PROP_DISABLE_SAVE_TO_DISK,
- PROP_EDITABLE,
PROP_INLINE_SPELLING,
PROP_MAGIC_LINKS,
PROP_MAGIC_SMILEYS,
PROP_OPEN_PROXY,
- PROP_PASTE_TARGET_LIST,
PROP_PRINT_PROXY,
PROP_SAVE_AS_PROXY,
PROP_SELECTED_URI
};
enum {
- COPY_CLIPBOARD,
- CUT_CLIPBOARD,
- PASTE_CLIPBOARD,
POPUP_EVENT,
STATUS_MESSAGE,
STOP_LOADING,
@@ -125,6 +120,7 @@ static const gchar *ui =
" <placeholder name='custom-actions-3'/>"
" <separator/>"
" <menuitem action='select-all'/>"
+" <placeholder name='inspect-menu' />"
" </popup>"
"</ui>";
@@ -135,7 +131,7 @@ static void e_web_view_selectable_init (ESelectableInterface *interface);
G_DEFINE_TYPE_WITH_CODE (
EWebView,
e_web_view,
- GTK_TYPE_HTML,
+ WEBKIT_TYPE_WEB_VIEW,
G_IMPLEMENT_INTERFACE (
E_TYPE_EXTENSIBLE, NULL)
G_IMPLEMENT_INTERFACE (
@@ -145,141 +141,6 @@ G_DEFINE_TYPE_WITH_CODE (
E_TYPE_SELECTABLE,
e_web_view_selectable_init))
-static EWebViewRequest *
-web_view_request_new (EWebView *web_view,
- const gchar *uri,
- GtkHTMLStream *stream)
-{
- EWebViewRequest *request;
- GList *list;
-
- request = g_slice_new (EWebViewRequest);
-
- /* Try to detect file paths posing as URIs. */
- if (*uri == '/')
- request->file = g_file_new_for_path (uri);
- else
- request->file = g_file_new_for_uri (uri);
-
- request->web_view = g_object_ref (web_view);
- request->cancellable = g_cancellable_new ();
- request->input_stream = NULL;
- request->output_stream = stream;
-
- list = request->web_view->priv->requests;
- list = g_list_prepend (list, request);
- request->web_view->priv->requests = list;
-
- return request;
-}
-
-static void
-web_view_request_free (EWebViewRequest *request)
-{
- GList *list;
-
- list = request->web_view->priv->requests;
- list = g_list_remove (list, request);
- request->web_view->priv->requests = list;
-
- g_object_unref (request->file);
- g_object_unref (request->web_view);
- g_object_unref (request->cancellable);
-
- if (request->input_stream != NULL)
- g_object_unref (request->input_stream);
-
- g_slice_free (EWebViewRequest, request);
-}
-
-static void
-web_view_request_cancel (EWebViewRequest *request)
-{
- g_cancellable_cancel (request->cancellable);
-}
-
-static gboolean
-web_view_request_check_for_error (EWebViewRequest *request,
- GError *error)
-{
- GtkHTML *html;
- GtkHTMLStream *stream;
-
- if (error == NULL)
- return FALSE;
-
- if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
- /* use this error, but do not close the stream */
- g_error_free (error);
- return TRUE;
- }
-
- /* XXX Should we log errors that are not cancellations? */
-
- html = GTK_HTML (request->web_view);
- stream = request->output_stream;
-
- gtk_html_end (html, stream, GTK_HTML_STREAM_ERROR);
- web_view_request_free (request);
- g_error_free (error);
-
- return TRUE;
-}
-
-static void
-web_view_request_stream_read_cb (GInputStream *input_stream,
- GAsyncResult *result,
- EWebViewRequest *request)
-{
- gssize bytes_read;
- GError *error = NULL;
-
- bytes_read = g_input_stream_read_finish (input_stream, result, &error);
-
- if (web_view_request_check_for_error (request, error))
- return;
-
- if (bytes_read == 0) {
- gtk_html_end (
- GTK_HTML (request->web_view),
- request->output_stream, GTK_HTML_STREAM_OK);
- web_view_request_free (request);
- return;
- }
-
- gtk_html_write (
- GTK_HTML (request->web_view),
- request->output_stream, request->buffer, bytes_read);
-
- g_input_stream_read_async (
- request->input_stream, request->buffer,
- sizeof (request->buffer), G_PRIORITY_DEFAULT,
- request->cancellable, (GAsyncReadyCallback)
- web_view_request_stream_read_cb, request);
-}
-
-static void
-web_view_request_read_cb (GFile *file,
- GAsyncResult *result,
- EWebViewRequest *request)
-{
- GFileInputStream *input_stream;
- GError *error = NULL;
-
- /* Input stream might be NULL, so don't use cast macro. */
- input_stream = g_file_read_finish (file, result, &error);
- request->input_stream = (GInputStream *) input_stream;
-
- if (web_view_request_check_for_error (request, error))
- return;
-
- g_input_stream_read_async (
- request->input_stream, request->buffer,
- sizeof (request->buffer), G_PRIORITY_DEFAULT,
- request->cancellable, (GAsyncReadyCallback)
- web_view_request_stream_read_cb, request);
-}
-
static void
action_copy_clipboard_cb (GtkAction *action,
EWebView *web_view)
@@ -470,71 +331,149 @@ static GtkActionEntry standard_entries[] = {
G_CALLBACK (action_select_all_cb) }
};
-static gboolean
-web_view_button_press_event_cb (EWebView *web_view,
- GdkEventButton *event,
- GtkHTML *frame)
+static void
+web_view_menu_item_select_cb (EWebView *web_view,
+ GtkWidget *widget)
{
- gboolean event_handled = FALSE;
- gchar *uri = NULL;
+ GtkAction *action;
+ GtkActivatable *activatable;
+ const gchar *tooltip;
- if (event) {
- GdkPixbufAnimation *anim;
- gchar *image_src;
+ activatable = GTK_ACTIVATABLE (widget);
+ action = gtk_activatable_get_related_action (activatable);
+ tooltip = gtk_action_get_tooltip (action);
- if (frame == NULL)
- frame = GTK_HTML (web_view);
+ if (tooltip == NULL)
+ return;
- anim = gtk_html_get_image_at (frame, event->x, event->y);
- e_web_view_set_cursor_image (web_view, anim);
- if (anim != NULL)
- g_object_unref (anim);
+ e_web_view_status_message (web_view, tooltip);
+}
- image_src = gtk_html_get_image_src_at (
- frame, event->x, event->y);
- e_web_view_set_cursor_image_src (web_view, image_src);
- g_free (image_src);
- }
+static void
+replace_text (WebKitDOMNode *node,
+ const gchar *text,
+ WebKitDOMNode *replacement)
+{
+ /* NodeType 3 = TEXT_NODE */
+ if (webkit_dom_node_get_node_type (node) == 3) {
+
+ gint text_length = strlen (text);
+
+ while (node) {
+
+ WebKitDOMNode *current_node, *replacement_node;
+ const gchar *node_data, *offset;
+ goffset split_offset;
+ gint data_length;
+
+ current_node = node;
+
+ /* Don't use the WEBKIT_DOM_CHARACTER_DATA macro for
+ * casting. WebKit lies about type of the object and
+ * GLib will throw runtime warning about node not being
+ * WebKitDOMCharacterData, but the function will return
+ * correct and valid data.
+ * IMO it's bug in the Gtk bindings and WebKit internally
+ * handles it by the nodeType so therefor it works
+ * event for "invalid" objects. But really, who knows..?
+ */
+ node_data = webkit_dom_character_data_get_data (
+ (WebKitDOMCharacterData *) node);
+
+ offset = strstr (node_data, text);
+ if (!offset) {
+ node = NULL;
+ continue;
+ }
+
+ split_offset = offset - node_data + text_length;
+ replacement_node =
+ webkit_dom_node_clone_node (replacement, TRUE);
+
+ data_length = webkit_dom_character_data_get_length (
+ (WebKitDOMCharacterData *) node);
+ if (split_offset < data_length) {
+
+ WebKitDOMNode *parent_node;
+
+ node = WEBKIT_DOM_NODE (
+ webkit_dom_text_split_text (
+ (WebKitDOMText *) node,
+ offset - node_data + text_length,
+ NULL));
+ parent_node = webkit_dom_node_get_parent_node (node);
+ webkit_dom_node_insert_before (
+ parent_node, replacement_node,
+ node, NULL);
+
+ } else {
+ WebKitDOMNode *parent_node;
+
+ parent_node = webkit_dom_node_get_parent_node (node);
+ webkit_dom_node_append_child (
+ parent_node,
+ replacement_node, NULL);
+ }
+
+ webkit_dom_character_data_delete_data (
+ (WebKitDOMCharacterData *) (current_node),
+ offset - node_data, text_length, NULL);
+ }
- if (event != NULL && event->button != 3)
- return FALSE;
+ } else {
- /* Only extract a URI if no selection is active. Selected text
- * implies the user is more likely to want to copy the selection
- * to the clipboard than open a link within the selection. */
- if (!e_web_view_is_selection_active (web_view))
- uri = e_web_view_extract_uri (web_view, event, frame);
+ WebKitDOMNode *child, *next_child;
- if (uri != NULL && g_str_has_prefix (uri, "##")) {
- g_free (uri);
- return FALSE;
- }
+ /* Iframe? Let's traverse inside! */
+ if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (node)) {
- g_signal_emit (
- web_view, signals[POPUP_EVENT], 0,
- event, uri, &event_handled);
+ WebKitDOMDocument *frame_document;
- g_free (uri);
+ frame_document =
+ webkit_dom_html_iframe_element_get_content_document (
+ WEBKIT_DOM_HTML_IFRAME_ELEMENT (node));
+ replace_text (WEBKIT_DOM_NODE (frame_document),
+ text, replacement);
+
+ } else {
+
+ child = webkit_dom_node_get_first_child (node);
+ while (child) {
+ next_child = webkit_dom_node_get_next_sibling (child);
+ replace_text (child, text, replacement);
+ child = next_child;
+ }
+ }
+ }
- return event_handled;
}
static void
-web_view_menu_item_select_cb (EWebView *web_view,
- GtkWidget *widget)
+web_view_update_document_highlights (EWebView *web_view)
{
- GtkAction *action;
- GtkActivatable *activatable;
- const gchar *tooltip;
+ WebKitDOMDocument *document;
+ GSList *iter;
- activatable = GTK_ACTIVATABLE (widget);
- action = gtk_activatable_get_related_action (activatable);
- tooltip = gtk_action_get_tooltip (action);
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (web_view));
- if (tooltip == NULL)
- return;
+ for (iter = web_view->priv->highlights; iter; iter = iter->next) {
- e_web_view_status_message (web_view, tooltip);
+ WebKitDOMDocumentFragment *frag;
+ WebKitDOMElement *span;
+
+ span = webkit_dom_document_create_element (document, "span", NULL);
+ webkit_dom_html_element_set_class_name (
+ WEBKIT_DOM_HTML_ELEMENT (span), "__evo-highlight");
+ webkit_dom_html_element_set_inner_text (
+ WEBKIT_DOM_HTML_ELEMENT (span), iter->data, NULL);
+
+ frag = webkit_dom_document_create_document_fragment (document);
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (frag), WEBKIT_DOM_NODE (span), NULL);
+
+ replace_text (WEBKIT_DOM_NODE (document),
+ iter->data, WEBKIT_DOM_NODE (frag));
+ }
}
static void
@@ -560,6 +499,86 @@ web_view_connect_proxy_cb (EWebView *web_view,
G_CALLBACK (web_view_menu_item_deselect_cb), web_view);
}
+static GtkWidget *
+web_view_create_plugin_widget_cb (EWebView *web_view,
+ const gchar *mime_type,
+ const gchar *uri,
+ GHashTable *param)
+{
+ EWebViewClass *class;
+
+ /* XXX WebKitWebView does not provide a class method for
+ * this signal, so we do so we can override the default
+ * behavior from subclasses for special URI types. */
+
+ class = E_WEB_VIEW_GET_CLASS (web_view);
+ g_return_val_if_fail (class->create_plugin_widget != NULL, NULL);
+
+ return class->create_plugin_widget (web_view, mime_type, uri, param);
+}
+
+static void
+web_view_hovering_over_link_cb (EWebView *web_view,
+ const gchar *title,
+ const gchar *uri)
+{
+ EWebViewClass *class;
+
+ /* XXX WebKitWebView does not provide a class method for
+ * this signal, so we do so we can override the default
+ * behavior from subclasses for special URI types. */
+
+ class = E_WEB_VIEW_GET_CLASS (web_view);
+ g_return_if_fail (class->hovering_over_link != NULL);
+
+ class->hovering_over_link (web_view, title, uri);
+}
+
+static gboolean
+web_view_navigation_policy_decision_requested_cb (EWebView *web_view,
+ WebKitWebFrame *frame,
+ WebKitNetworkRequest *request,
+ WebKitWebNavigationAction *navigation_action,
+ WebKitWebPolicyDecision *policy_decision)
+{
+ EWebViewClass *class;
+ WebKitWebNavigationReason reason;
+ const gchar *uri;
+
+ reason = webkit_web_navigation_action_get_reason (navigation_action);
+ if (reason != WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED)
+ return FALSE;
+
+ /* XXX WebKitWebView does not provide a class method for
+ * this signal, so we do so we can override the default
+ * behavior from subclasses for special URI types. */
+
+ class = E_WEB_VIEW_GET_CLASS (web_view);
+ g_return_val_if_fail (class->link_clicked != NULL, FALSE);
+
+ webkit_web_policy_decision_ignore (policy_decision);
+
+ uri = webkit_network_request_get_uri (request);
+
+ class->link_clicked (web_view, uri);
+
+ return TRUE;
+}
+
+static void
+web_view_load_status_changed_cb (WebKitWebView *web_view,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ WebKitLoadStatus status;
+
+ status = webkit_web_view_get_load_status (web_view);
+ if (status != WEBKIT_LOAD_FINISHED)
+ return;
+
+ web_view_update_document_highlights (E_WEB_VIEW (web_view));
+}
+
static void
web_view_set_property (GObject *object,
guint property_id,
@@ -567,12 +586,6 @@ web_view_set_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
- case PROP_ANIMATE:
- e_web_view_set_animate (
- E_WEB_VIEW (object),
- g_value_get_boolean (value));
- return;
-
case PROP_CARET_MODE:
e_web_view_set_caret_mode (
E_WEB_VIEW (object),
@@ -603,12 +616,6 @@ web_view_set_property (GObject *object,
g_value_get_boolean (value));
return;
- case PROP_EDITABLE:
- e_web_view_set_editable (
- E_WEB_VIEW (object),
- g_value_get_boolean (value));
- return;
-
case PROP_INLINE_SPELLING:
e_web_view_set_inline_spelling (
E_WEB_VIEW (object),
@@ -662,24 +669,12 @@ web_view_get_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
- case PROP_ANIMATE:
- g_value_set_boolean (
- value, e_web_view_get_animate (
- E_WEB_VIEW (object)));
- return;
-
case PROP_CARET_MODE:
g_value_set_boolean (
value, e_web_view_get_caret_mode (
E_WEB_VIEW (object)));
return;
- case PROP_COPY_TARGET_LIST:
- g_value_set_boxed (
- value, e_web_view_get_copy_target_list (
- E_WEB_VIEW (object)));
- return;
-
case PROP_CURSOR_IMAGE:
g_value_set_object (
value, e_web_view_get_cursor_image (
@@ -704,12 +699,6 @@ web_view_get_property (GObject *object,
E_WEB_VIEW (object)));
return;
- case PROP_EDITABLE:
- g_value_set_boolean (
- value, e_web_view_get_editable (
- E_WEB_VIEW (object)));
- return;
-
case PROP_INLINE_SPELLING:
g_value_set_boolean (
value, e_web_view_get_inline_spelling (
@@ -734,12 +723,6 @@ web_view_get_property (GObject *object,
E_WEB_VIEW (object)));
return;
- case PROP_PASTE_TARGET_LIST:
- g_value_set_boxed (
- value, e_web_view_get_paste_target_list (
- E_WEB_VIEW (object)));
- return;
-
case PROP_PRINT_PROXY:
g_value_set_object (
value, e_web_view_get_print_proxy (
@@ -789,16 +772,6 @@ web_view_dispose (GObject *object)
priv->save_as_proxy = NULL;
}
- if (priv->copy_target_list != NULL) {
- gtk_target_list_unref (priv->copy_target_list);
- priv->copy_target_list = NULL;
- }
-
- if (priv->paste_target_list != NULL) {
- gtk_target_list_unref (priv->paste_target_list);
- priv->paste_target_list = NULL;
- }
-
if (priv->cursor_image != NULL) {
g_object_unref (priv->cursor_image);
priv->cursor_image = NULL;
@@ -809,6 +782,16 @@ web_view_dispose (GObject *object)
priv->cursor_image_src = NULL;
}
+ if (priv->js_callbacks != NULL) {
+ g_hash_table_destroy (priv->js_callbacks);
+ priv->js_callbacks = NULL;
+ }
+
+ if (priv->highlights != NULL) {
+ g_slist_free_full (priv->highlights, g_free);
+ priv->highlights = NULL;
+ }
+
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@@ -861,12 +844,103 @@ web_view_button_press_event (GtkWidget *widget,
{
GtkWidgetClass *widget_class;
EWebView *web_view;
+ gboolean event_handled = FALSE;
+ gchar *uri;
web_view = E_WEB_VIEW (widget);
- if (web_view_button_press_event_cb (web_view, event, NULL))
+ if (event) {
+ WebKitHitTestResult *test;
+ WebKitHitTestResultContext context;
+
+ if (web_view->priv->cursor_image) {
+ g_object_unref (web_view->priv->cursor_image);
+ web_view->priv->cursor_image = NULL;
+ }
+
+ if (web_view->priv->cursor_image_src) {
+ g_free (web_view->priv->cursor_image_src);
+ web_view->priv->cursor_image_src = NULL;
+ }
+
+ test = webkit_web_view_get_hit_test_result (WEBKIT_WEB_VIEW (web_view), event);
+
+ if (!test)
+ goto chainup;
+
+ g_object_get (G_OBJECT (test), "context", &context, NULL);
+ if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) {
+ WebKitWebDataSource *data_source;
+ WebKitWebFrame *frame;
+ GList *subresources, *res;
+
+ g_object_get (G_OBJECT (test), "image-uri", &uri, NULL);
+
+ if (!uri)
+ goto chainup;
+
+ if (web_view->priv->cursor_image_src)
+ g_free (web_view->priv->cursor_image_src);
+ web_view->priv->cursor_image_src = uri;
+
+ /* Iterate through all resources of the loaded webpage and
+ try to find resource with URI matching cursor_image_src */
+ frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
+ data_source = webkit_web_frame_get_data_source (frame);
+ subresources = webkit_web_data_source_get_subresources (data_source);
+ for (res = subresources; res; res = res->next) {
+ WebKitWebResource *src = res->data;
+ GdkPixbufLoader *loader;
+ GString *data;
+
+ if (g_strcmp0 (webkit_web_resource_get_uri (src),
+ web_view->priv->cursor_image_src) != 0)
+ continue;
+
+ data = webkit_web_resource_get_data (src);
+ if (!data)
+ break;
+
+ loader = gdk_pixbuf_loader_new ();
+ if (!gdk_pixbuf_loader_write (loader,
+ (guchar *) data->str, data->len, NULL)) {
+ g_object_unref (loader);
+ break;
+ }
+ gdk_pixbuf_loader_close (loader, NULL);
+
+ if (web_view->priv->cursor_image)
+ g_object_unref (web_view->priv->cursor_image);
+
+ web_view->priv->cursor_image =
+ g_object_ref (gdk_pixbuf_loader_get_animation (loader));
+
+ g_object_unref (loader);
+ break;
+ }
+ }
+ }
+
+ if (event != NULL && event->button != 3)
+ goto chainup;
+
+ uri = e_web_view_extract_uri (web_view, event);
+
+ if (uri != NULL && g_str_has_prefix (uri, "##")) {
+ g_free (uri);
+ goto chainup;
+ }
+
+ g_signal_emit (
+ web_view, signals[POPUP_EVENT], 0,
+ event, uri, &event_handled);
+
+ g_free (uri);
+
+ if (event_handled)
return TRUE;
+chainup:
/* Chain up to parent's button_press_event() method. */
widget_class = GTK_WIDGET_CLASS (parent_class);
return widget_class->button_press_event (widget, event);
@@ -879,10 +953,10 @@ web_view_scroll_event (GtkWidget *widget,
if (event->state & GDK_CONTROL_MASK) {
switch (event->direction) {
case GDK_SCROLL_UP:
- gtk_html_zoom_in (GTK_HTML (widget));
+ e_web_view_zoom_in (E_WEB_VIEW (widget));
return TRUE;
case GDK_SCROLL_DOWN:
- gtk_html_zoom_out (GTK_HTML (widget));
+ e_web_view_zoom_out (E_WEB_VIEW (widget));
return TRUE;
default:
break;
@@ -892,74 +966,71 @@ web_view_scroll_event (GtkWidget *widget,
return FALSE;
}
-static void
-web_view_url_requested (GtkHTML *html,
- const gchar *uri,
- GtkHTMLStream *stream)
+static GtkWidget *
+web_view_create_plugin_widget (EWebView *web_view,
+ const gchar *mime_type,
+ const gchar *uri,
+ GHashTable *param)
{
- EWebViewRequest *request;
+ GtkWidget *widget = NULL;
- request = web_view_request_new (E_WEB_VIEW (html), uri, stream);
+ if (g_strcmp0 (mime_type, "image/x-themed-icon") == 0) {
+ GtkIconTheme *icon_theme;
+ GdkPixbuf *pixbuf;
+ gpointer data;
+ glong size = 0;
+ GError *error = NULL;
- g_file_read_async (
- request->file, G_PRIORITY_DEFAULT,
- request->cancellable, (GAsyncReadyCallback)
- web_view_request_read_cb, request);
-}
+ icon_theme = gtk_icon_theme_get_default ();
-static void
-web_view_gtkhtml_link_clicked (GtkHTML *html,
- const gchar *uri)
-{
- EWebViewClass *class;
- EWebView *web_view;
+ if (size == 0) {
+ data = g_hash_table_lookup (param, "width");
+ if (data != NULL)
+ size = MAX (size, strtol (data, NULL, 10));
+ }
- web_view = E_WEB_VIEW (html);
+ if (size == 0) {
+ data = g_hash_table_lookup (param, "height");
+ if (data != NULL)
+ size = MAX (size, strtol (data, NULL, 10));
+ }
- class = E_WEB_VIEW_GET_CLASS (web_view);
- g_return_if_fail (class->link_clicked != NULL);
+ if (size == 0)
+ size = 32; /* arbitrary default */
+
+ pixbuf = gtk_icon_theme_load_icon (
+ icon_theme, uri, size, 0, &error);
+ if (pixbuf != NULL) {
+ widget = gtk_image_new_from_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+ } else if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ }
- class->link_clicked (web_view, uri);
+ return widget;
}
-static void
-web_view_on_url (GtkHTML *html,
- const gchar *uri)
+static gchar *
+web_view_extract_uri (EWebView *web_view,
+ GdkEventButton *event)
{
- EWebViewClass *class;
- EWebView *web_view;
-
- web_view = E_WEB_VIEW (html);
-
- class = E_WEB_VIEW_GET_CLASS (web_view);
- g_return_if_fail (class->hovering_over_link != NULL);
+ WebKitHitTestResult *result;
+ WebKitHitTestResultContext context;
+ gchar *uri = NULL;
- /* XXX WebKit would supply a title here. */
- class->hovering_over_link (web_view, NULL, uri);
-}
+ result = webkit_web_view_get_hit_test_result (
+ WEBKIT_WEB_VIEW (web_view), event);
-static void
-web_view_iframe_created (GtkHTML *html,
- GtkHTML *iframe)
-{
- g_signal_connect_swapped (
- iframe, "button-press-event",
- G_CALLBACK (web_view_button_press_event_cb), html);
-}
+ g_object_get (result, "context", &context, "link-uri", &uri, NULL);
-static gchar *
-web_view_extract_uri (EWebView *web_view,
- GdkEventButton *event,
- GtkHTML *html)
-{
- gchar *uri;
+ if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK)
+ return uri;
- if (event != NULL)
- uri = gtk_html_get_url_at (html, event->x, event->y);
- else
- uri = gtk_html_get_cursor_url (html);
+ g_free (uri);
- return uri;
+ return NULL;
}
static void
@@ -1030,30 +1101,62 @@ static void
web_view_load_string (EWebView *web_view,
const gchar *string)
{
- if (string != NULL && *string != '\0')
- gtk_html_load_from_string (GTK_HTML (web_view), string, -1);
- else
- e_web_view_clear (web_view);
+ if (string == NULL)
+ string = "";
+
+ webkit_web_view_load_string (
+ WEBKIT_WEB_VIEW (web_view),
+ string, "text/html", "UTF-8", "file://");
}
static void
-web_view_copy_clipboard (EWebView *web_view)
+web_view_load_uri (EWebView *web_view,
+ const gchar *uri)
{
- gtk_html_command (GTK_HTML (web_view), "copy");
+ if (uri == NULL)
+ uri = "about:blank";
+
+ webkit_web_view_load_uri (
+ WEBKIT_WEB_VIEW (web_view), uri);
}
static void
-web_view_cut_clipboard (EWebView *web_view)
+web_view_frame_load_string (EWebView *web_view,
+ const gchar *frame_name,
+ const gchar *string)
{
- if (e_web_view_get_editable (web_view))
- gtk_html_command (GTK_HTML (web_view), "cut");
+ WebKitWebFrame *main_frame, *frame;
+
+ if (string == NULL)
+ string = "";
+
+ main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
+ if (main_frame) {
+ frame = webkit_web_frame_find_frame (main_frame, frame_name);
+
+ if (frame)
+ webkit_web_frame_load_string (
+ frame, string, "text/html", "UTF-8", "file://");
+ }
}
static void
-web_view_paste_clipboard (EWebView *web_view)
+web_view_frame_load_uri (EWebView *web_view,
+ const gchar *frame_name,
+ const gchar *uri)
{
- if (e_web_view_get_editable (web_view))
- gtk_html_command (GTK_HTML (web_view), "paste");
+ WebKitWebFrame *main_frame, *frame;
+
+ if (uri == NULL)
+ uri = "about:blank";
+
+ main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
+ if (main_frame) {
+ frame = webkit_web_frame_find_frame (main_frame, frame_name);
+
+ if (frame)
+ webkit_web_frame_load_uri (frame, uri);
+ }
}
static gboolean
@@ -1070,11 +1173,7 @@ web_view_popup_event (EWebView *web_view,
static void
web_view_stop_loading (EWebView *web_view)
{
- g_list_foreach (
- web_view->priv->requests, (GFunc)
- web_view_request_cancel, NULL);
-
- gtk_html_stop (GTK_HTML (web_view));
+ webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (web_view));
}
static void
@@ -1267,48 +1366,27 @@ web_view_selectable_update_actions (ESelectable *selectable,
GdkAtom *clipboard_targets,
gint n_clipboard_targets)
{
- EWebView *web_view;
+ WebKitWebView *web_view;
GtkAction *action;
- /*GtkTargetList *target_list;*/
- gboolean can_paste = FALSE;
- gboolean editable;
- gboolean have_selection;
gboolean sensitive;
const gchar *tooltip;
- /*gint ii;*/
- web_view = E_WEB_VIEW (selectable);
- editable = e_web_view_get_editable (web_view);
- have_selection = e_web_view_is_selection_active (web_view);
-
- /* XXX GtkHtml implements its own clipboard instead of using
- * GDK_SELECTION_CLIPBOARD, so we don't get notifications
- * when the clipboard contents change. The logic below
- * is what we would do if GtkHtml worked properly.
- * Instead, we need to keep the Paste action sensitive so
- * its accelerator overrides GtkHtml's key binding. */
-#if 0
- target_list = e_selectable_get_paste_target_list (selectable);
- for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++)
- can_paste = gtk_target_list_find (
- target_list, clipboard_targets[ii], NULL);
-#endif
- can_paste = TRUE;
+ web_view = WEBKIT_WEB_VIEW (selectable);
action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
- sensitive = editable && have_selection;
+ sensitive = webkit_web_view_can_cut_clipboard (web_view);
tooltip = _("Cut the selection");
gtk_action_set_sensitive (action, sensitive);
gtk_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
- sensitive = have_selection;
+ sensitive = webkit_web_view_can_copy_clipboard (web_view);
tooltip = _("Copy the selection");
gtk_action_set_sensitive (action, sensitive);
gtk_action_set_tooltip (action, tooltip);
action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
- sensitive = editable && can_paste;
+ sensitive = webkit_web_view_can_paste_clipboard (web_view);
tooltip = _("Paste the clipboard");
gtk_action_set_sensitive (action, sensitive);
gtk_action_set_tooltip (action, tooltip);
@@ -1349,7 +1427,9 @@ e_web_view_class_init (EWebViewClass *class)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
+#if 0 /* WEBKIT */
GtkHTMLClass *html_class;
+#endif
parent_class = g_type_class_peek_parent (class);
g_type_class_add_private (class, sizeof (EWebViewPrivate));
@@ -1365,35 +1445,25 @@ e_web_view_class_init (EWebViewClass *class)
widget_class->button_press_event = web_view_button_press_event;
widget_class->scroll_event = web_view_scroll_event;
+#if 0 /* WEBKIT */
html_class = GTK_HTML_CLASS (class);
html_class->url_requested = web_view_url_requested;
- html_class->link_clicked = web_view_gtkhtml_link_clicked;
- html_class->on_url = web_view_on_url;
- html_class->iframe_created = web_view_iframe_created;
+#endif
+ class->create_plugin_widget = web_view_create_plugin_widget;
class->extract_uri = web_view_extract_uri;
class->hovering_over_link = web_view_hovering_over_link;
class->link_clicked = web_view_link_clicked;
class->load_string = web_view_load_string;
- class->copy_clipboard = web_view_copy_clipboard;
- class->cut_clipboard = web_view_cut_clipboard;
- class->paste_clipboard = web_view_paste_clipboard;
+ class->load_uri = web_view_load_uri;
+ class->frame_load_string = web_view_frame_load_string;
+ class->frame_load_uri = web_view_frame_load_uri;
class->popup_event = web_view_popup_event;
class->stop_loading = web_view_stop_loading;
class->update_actions = web_view_update_actions;
g_object_class_install_property (
object_class,
- PROP_ANIMATE,
- g_param_spec_boolean (
- "animate",
- "Animate Images",
- NULL,
- FALSE,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (
- object_class,
PROP_CARET_MODE,
g_param_spec_boolean (
"caret-mode",
@@ -1402,12 +1472,6 @@ e_web_view_class_init (EWebViewClass *class)
FALSE,
G_PARAM_READWRITE));
- /* Inherited from ESelectableInterface */
- g_object_class_override_property (
- object_class,
- PROP_COPY_TARGET_LIST,
- "copy-target-list");
-
g_object_class_install_property (
object_class,
PROP_CURSOR_IMAGE,
@@ -1452,16 +1516,6 @@ e_web_view_class_init (EWebViewClass *class)
g_object_class_install_property (
object_class,
- PROP_EDITABLE,
- g_param_spec_boolean (
- "editable",
- "Editable",
- NULL,
- FALSE,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (
- object_class,
PROP_INLINE_SPELLING,
g_param_spec_boolean (
"inline-spelling",
@@ -1500,12 +1554,6 @@ e_web_view_class_init (EWebViewClass *class)
GTK_TYPE_ACTION,
G_PARAM_READWRITE));
- /* Inherited from ESelectableInterface */
- g_object_class_override_property (
- object_class,
- PROP_PASTE_TARGET_LIST,
- "paste-target-list");
-
g_object_class_install_property (
object_class,
PROP_PRINT_PROXY,
@@ -1536,33 +1584,6 @@ e_web_view_class_init (EWebViewClass *class)
NULL,
G_PARAM_READWRITE));
- signals[COPY_CLIPBOARD] = g_signal_new (
- "copy-clipboard",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (EWebViewClass, copy_clipboard),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- signals[CUT_CLIPBOARD] = g_signal_new (
- "cut-clipboard",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (EWebViewClass, cut_clipboard),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- signals[PASTE_CLIPBOARD] = g_signal_new (
- "paste-clipboard",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (EWebViewClass, paste_clipboard),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
signals[POPUP_EVENT] = g_signal_new (
"popup-event",
G_TYPE_FROM_CLASS (class),
@@ -1634,26 +1655,46 @@ e_web_view_init (EWebView *web_view)
{
GtkUIManager *ui_manager;
GtkActionGroup *action_group;
- GtkTargetList *target_list;
EPopupAction *popup_action;
+ WebKitWebSettings *web_settings;
const gchar *domain = GETTEXT_PACKAGE;
const gchar *id;
GError *error = NULL;
web_view->priv = E_WEB_VIEW_GET_PRIVATE (web_view);
+ web_view->priv->highlights = NULL;
+
+ g_signal_connect (
+ web_view, "create-plugin-widget",
+ G_CALLBACK (web_view_create_plugin_widget_cb), NULL);
+
+ g_signal_connect (
+ web_view, "hovering-over-link",
+ G_CALLBACK (web_view_hovering_over_link_cb), NULL);
+
+ g_signal_connect (
+ web_view, "navigation-policy-decision-requested",
+ G_CALLBACK (web_view_navigation_policy_decision_requested_cb),
+ NULL);
+
+ g_signal_connect (
+ web_view, "notify::load-status",
+ G_CALLBACK (web_view_load_status_changed_cb), NULL);
+
ui_manager = gtk_ui_manager_new ();
web_view->priv->ui_manager = ui_manager;
+ web_view->priv->js_callbacks = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free, NULL);
+
g_signal_connect_swapped (
ui_manager, "connect-proxy",
G_CALLBACK (web_view_connect_proxy_cb), web_view);
- target_list = gtk_target_list_new (NULL, 0);
- web_view->priv->copy_target_list = target_list;
-
- target_list = gtk_target_list_new (NULL, 0);
- web_view->priv->paste_target_list = target_list;
+ web_settings = e_web_view_get_default_settings (GTK_WIDGET (web_view));
+ e_web_view_set_settings (web_view, web_settings);
+ g_object_unref (web_settings);
action_group = gtk_action_group_new ("uri");
gtk_action_group_set_translation_domain (action_group, domain);
@@ -1763,6 +1804,7 @@ e_web_view_init (EWebView *web_view)
e_plugin_ui_enable_manager (ui_manager, id);
e_extensible_load_extensions (E_EXTENSIBLE (web_view));
+
}
GtkWidget *
@@ -1776,7 +1818,7 @@ e_web_view_clear (EWebView *web_view)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
- gtk_html_load_empty (GTK_HTML (web_view));
+ e_web_view_load_string (web_view, NULL);
}
void
@@ -1793,54 +1835,316 @@ e_web_view_load_string (EWebView *web_view,
class->load_string (web_view, string);
}
-gboolean
-e_web_view_get_animate (EWebView *web_view)
+void
+e_web_view_load_uri (EWebView *web_view,
+ const gchar *uri)
{
- /* XXX This is just here to maintain symmetry
- * with e_web_view_set_animate(). */
+ EWebViewClass *class;
- g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
+ g_return_if_fail (E_IS_WEB_VIEW (web_view));
- return gtk_html_get_animate (GTK_HTML (web_view));
+ class = E_WEB_VIEW_GET_CLASS (web_view);
+ g_return_if_fail (class->load_uri != NULL);
+
+ class->load_uri (web_view, uri);
}
void
-e_web_view_set_animate (EWebView *web_view,
- gboolean animate)
+e_web_view_reload (EWebView *web_view)
{
- /* XXX GtkHTML does not utilize GObject properties as well
- * as it could. This just wraps gtk_html_set_animate()
- * so we can get a "notify::animate" signal. */
+ g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+ webkit_web_view_reload (WEBKIT_WEB_VIEW (web_view));
+}
+
+const gchar *
+e_web_view_get_uri (EWebView *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+
+ return webkit_web_view_get_uri (WEBKIT_WEB_VIEW (web_view));
+}
+
+void
+e_web_view_frame_load_string (EWebView *web_view,
+ const gchar *frame_name,
+ const gchar *string)
+{
+ EWebViewClass *class;
+
+ g_return_if_fail (E_IS_WEB_VIEW (web_view));
+ g_return_if_fail (frame_name != NULL);
+
+ class = E_WEB_VIEW_GET_CLASS (web_view);
+ g_return_if_fail (class->frame_load_string != NULL);
+
+ class->frame_load_string (web_view, frame_name, string);
+}
+
+void
+e_web_view_frame_load_uri (EWebView *web_view,
+ const gchar *frame_name,
+ const gchar *uri)
+{
+ EWebViewClass *class;
+
+ g_return_if_fail (E_IS_WEB_VIEW (web_view));
+ g_return_if_fail (frame_name != NULL);
+
+ class = E_WEB_VIEW_GET_CLASS (web_view);
+ g_return_if_fail (class->frame_load_uri != NULL);
+
+ class->frame_load_uri (web_view, frame_name, uri);
+}
+
+const gchar *
+e_web_view_frame_get_uri (EWebView *web_view,
+ const gchar *frame_name)
+{
+ WebKitWebFrame *main_frame, *frame;
+
+ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+ g_return_val_if_fail (frame_name != NULL, NULL);
+
+ main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
+ if (main_frame) {
+ frame = webkit_web_frame_find_frame (main_frame, frame_name);
+
+ if (frame)
+ return webkit_web_frame_get_uri (frame);
+ }
+
+ return NULL;
+}
+
+gchar *
+e_web_view_get_html (EWebView *web_view)
+{
+ GValue html = {0};
+
+ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+
+ if (e_web_view_exec_script (web_view, "return document.documentElement.innerHTML;", &html) == G_TYPE_STRING)
+ return g_strdup (g_value_get_string (&html));
+ else
+ return NULL;
+}
+
+JSGlobalContextRef
+e_web_view_get_global_context (EWebView *web_view)
+{
+ WebKitWebFrame *main_frame;
+
+ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), 0);
+
+ main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
+ return webkit_web_frame_get_global_context (main_frame);
+}
+
+GType
+e_web_view_exec_script (EWebView *web_view,
+ const gchar *script,
+ GValue *value)
+{
+ WebKitWebFrame *main_frame;
+
+ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), G_TYPE_INVALID);
+ g_return_val_if_fail (script != NULL, G_TYPE_INVALID);
+
+ main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
+
+ return e_web_view_frame_exec_script (web_view,
+ webkit_web_frame_get_name (main_frame),
+ script, value);
+}
+
+GType
+e_web_view_frame_exec_script (EWebView *web_view,
+ const gchar *frame_name,
+ const gchar *script,
+ GValue *value)
+{
+ WebKitWebFrame *main_frame, *frame;
+ JSGlobalContextRef context;
+ JSValueRef js_value, error = NULL;
+ JSType js_type;
+ JSStringRef js_script;
+ JSStringRef js_str;
+ size_t str_len;
+ gchar *str;
+
+ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), G_TYPE_INVALID);
+ g_return_val_if_fail (script != NULL, G_TYPE_INVALID);
+
+ main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
+ frame = webkit_web_frame_find_frame (main_frame, frame_name);
+
+ context = webkit_web_frame_get_global_context (frame);
+
+ js_script = JSStringCreateWithUTF8CString (script);
+ js_value = JSEvaluateScript (context, js_script, NULL, NULL, 0, &error);
+ JSStringRelease (js_script);
+
+ if (error) {
+ gchar *msg;
+ js_str = JSValueToStringCopy (context, error, NULL);
+ str_len = JSStringGetLength (js_str);
+
+ msg = g_malloc (str_len + 1);
+ JSStringGetUTF8CString (js_str, msg, str_len + 1);
+ JSStringRelease (js_str);
+
+ g_message ("JavaScript Execution Failed: %s", msg);
+ g_free (msg);
+
+ return G_TYPE_INVALID;
+ }
+
+ if (!value)
+ return G_TYPE_NONE;
+
+ js_type = JSValueGetType (context, js_value);
+ switch (js_type) {
+ case kJSTypeBoolean:
+ g_value_init (value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (value, JSValueToBoolean (context, js_value));
+ break;
+ case kJSTypeNumber:
+ g_value_init (value, G_TYPE_DOUBLE);
+ g_value_set_double (value, JSValueToNumber (context, js_value, NULL));
+ break;
+ case kJSTypeString:
+ js_str = JSValueToStringCopy (context, js_value, NULL);
+ str_len = JSStringGetLength (js_str);
+ str = g_malloc (str_len + 1);
+ JSStringGetUTF8CString (js_str, str, str_len + 1);
+ JSStringRelease (js_str);
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_string (value, str);
+ g_free (str);
+ break;
+ case kJSTypeObject:
+ g_value_init (value, G_TYPE_OBJECT);
+ g_value_set_object (value, JSValueToObject (context, js_value, NULL));
+ break;
+ case kJSTypeNull:
+ g_value_init (value, G_TYPE_POINTER);
+ g_value_set_pointer (value, NULL);
+ break;
+ case kJSTypeUndefined:
+ break;
+ }
+
+ return G_VALUE_TYPE (value);
+}
+
+static JSValueRef
+web_view_handle_js_callback (JSContextRef ctx,
+ JSObjectRef function,
+ JSObjectRef this_object,
+ size_t argument_count,
+ const JSValueRef arguments[],
+ JSValueRef *exception)
+{
+ gpointer web_view;
+ gpointer user_data;
+ gchar *fnc_name;
+ size_t fnc_name_len;
+
+ EWebViewJSFunctionCallback callback;
+
+ JSStringRef js_webview_prop = JSStringCreateWithUTF8CString ("webview");
+ JSStringRef js_userdata_prop = JSStringCreateWithUTF8CString ("user-data");
+ JSStringRef js_fncname_prop = JSStringCreateWithUTF8CString ("fnc-name");
+ JSStringRef js_fncname_str;
+
+ JSValueRef js_webview = JSObjectGetProperty (ctx, function, js_webview_prop, NULL);
+ JSValueRef js_userdata = JSObjectGetProperty (ctx, function, js_userdata_prop, NULL);
+ JSValueRef js_fncname = JSObjectGetProperty (ctx, function, js_fncname_prop, NULL);
+
+ web_view = GINT_TO_POINTER ((gint) JSValueToNumber (ctx, js_webview, NULL));
+ user_data = GINT_TO_POINTER ((gint) JSValueToNumber (ctx, js_userdata, NULL));
+ js_fncname_str = JSValueToStringCopy (ctx, js_fncname, NULL);
+ fnc_name_len = JSStringGetLength (js_fncname_str);
+
+ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), 0);
+
+ /* Convert fncname to gchar* and lookup the callback in hashtable */
+ fnc_name = g_malloc (fnc_name_len + 1);
+ JSStringGetUTF8CString (js_fncname_str, fnc_name, fnc_name_len + 1);
+ callback = g_hash_table_lookup (E_WEB_VIEW (web_view)->priv->js_callbacks, fnc_name);
+
+ g_return_val_if_fail (callback != NULL, 0);
+
+ /* Call the callback function */
+ callback (E_WEB_VIEW (web_view), argument_count, arguments, user_data);
+
+ JSStringRelease (js_fncname_str);
+ JSStringRelease (js_fncname_prop);
+ JSStringRelease (js_webview_prop);
+ JSStringRelease (js_userdata_prop);
+
+ g_free (fnc_name);
+
+ return 0;
+}
+
+void
+e_web_view_install_js_callback (EWebView *web_view,
+ const gchar *fnc_name,
+ EWebViewJSFunctionCallback callback,
+ gpointer user_data)
+{
+ WebKitWebFrame *frame;
+ JSGlobalContextRef ctx;
+ JSObjectRef global_obj, js_func;
+ JSStringRef js_fnc_name, js_webview_prop, js_userdata_prop, js_fncname_prop;
g_return_if_fail (E_IS_WEB_VIEW (web_view));
+ g_return_if_fail (fnc_name != NULL);
+ g_return_if_fail (callback != NULL);
+
+ frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
+ ctx = webkit_web_frame_get_global_context (frame);
+ global_obj = JSContextGetGlobalObject (ctx);
+ js_fnc_name = JSStringCreateWithUTF8CString (fnc_name);
+ js_func = JSObjectMakeFunctionWithCallback (ctx, NULL,
+ (JSObjectCallAsFunctionCallback) web_view_handle_js_callback);
+ js_webview_prop = JSStringCreateWithUTF8CString ("webview");
+ js_userdata_prop = JSStringCreateWithUTF8CString ("user-data");
+ js_fncname_prop = JSStringCreateWithUTF8CString ("fnc-name");
- gtk_html_set_animate (GTK_HTML (web_view), animate);
+ /* Set some properties to the function */
+ JSObjectSetProperty (ctx, js_func, js_webview_prop, JSValueMakeNumber (ctx, GPOINTER_TO_INT (web_view)), 0, NULL);
+ JSObjectSetProperty (ctx, js_func, js_userdata_prop, JSValueMakeNumber (ctx, GPOINTER_TO_INT (user_data)), 0, NULL);
+ JSObjectSetProperty (ctx, js_func, js_fncname_prop, JSValueMakeString (ctx, js_fnc_name), 0, NULL);
- g_object_notify (G_OBJECT (web_view), "animate");
+ /* Set the function as a property of global object */
+ JSObjectSetProperty (ctx, global_obj, js_fnc_name, js_func, 0, NULL);
+
+ JSStringRelease (js_fncname_prop);
+ JSStringRelease (js_userdata_prop);
+ JSStringRelease (js_webview_prop);
+ JSStringRelease (js_fnc_name);
+
+ g_hash_table_insert (web_view->priv->js_callbacks, g_strdup (fnc_name), callback);
}
gboolean
e_web_view_get_caret_mode (EWebView *web_view)
{
- /* XXX This is just here to maintain symmetry
- * with e_web_view_set_caret_mode(). */
-
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
- return gtk_html_get_caret_mode (GTK_HTML (web_view));
+ return web_view->priv->caret_mode;
}
void
e_web_view_set_caret_mode (EWebView *web_view,
gboolean caret_mode)
{
- /* XXX GtkHTML does not utilize GObject properties as well
- * as it could. This just wraps gtk_html_set_caret_mode()
- * so we can get a "notify::caret-mode" signal. */
-
g_return_if_fail (E_IS_WEB_VIEW (web_view));
- gtk_html_set_caret_mode (GTK_HTML (web_view), caret_mode);
+ web_view->priv->caret_mode = caret_mode;
g_object_notify (G_OBJECT (web_view), "caret-mode");
}
@@ -1850,7 +2154,8 @@ e_web_view_get_copy_target_list (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
- return web_view->priv->copy_target_list;
+ return webkit_web_view_get_copy_target_list (
+ WEBKIT_WEB_VIEW (web_view));
}
gboolean
@@ -1892,46 +2197,74 @@ e_web_view_set_disable_save_to_disk (EWebView *web_view,
}
gboolean
-e_web_view_get_editable (EWebView *web_view)
+e_web_view_get_enable_frame_flattening (EWebView * web_view)
{
- /* XXX This is just here to maintain symmetry
- * with e_web_view_set_editable(). */
+ WebKitWebSettings *settings;
+ gboolean flattening;
+
+ /* Return TRUE with fail since it's default value we set in _init(). */
+ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), TRUE);
+
+ settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view));
+ g_return_val_if_fail (settings != NULL, TRUE);
+
+ g_object_get (G_OBJECT (settings), "enable-frame-flattening", &flattening, NULL);
+
+ return flattening;
+}
+
+void
+e_web_view_set_enable_frame_flattening (EWebView * web_view,
+ gboolean enable_frame_flattening)
+{
+ WebKitWebSettings *settings;
+
+ g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+ settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view));
+ g_return_if_fail (settings != NULL);
+
+ g_object_set (G_OBJECT (settings), "enable-frame-flattening",
+ enable_frame_flattening, NULL);
+}
+gboolean
+e_web_view_get_editable (EWebView *web_view)
+{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
- return gtk_html_get_editable (GTK_HTML (web_view));
+ return webkit_web_view_get_editable (WEBKIT_WEB_VIEW (web_view));
}
void
e_web_view_set_editable (EWebView *web_view,
gboolean editable)
{
- /* XXX GtkHTML does not utilize GObject properties as well
- * as it could. This just wraps gtk_html_set_editable()
- * so we can get a "notify::editable" signal. */
-
g_return_if_fail (E_IS_WEB_VIEW (web_view));
- gtk_html_set_editable (GTK_HTML (web_view), editable);
-
- g_object_notify (G_OBJECT (web_view), "editable");
+ webkit_web_view_set_editable (WEBKIT_WEB_VIEW (web_view), editable);
}
gboolean
e_web_view_get_inline_spelling (EWebView *web_view)
{
+#if 0 /* WEBKIT - XXX No equivalent property? */
/* XXX This is just here to maintain symmetry
* with e_web_view_set_inline_spelling(). */
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
return gtk_html_get_inline_spelling (GTK_HTML (web_view));
+#endif
+
+ return FALSE;
}
void
e_web_view_set_inline_spelling (EWebView *web_view,
gboolean inline_spelling)
{
+#if 0 /* WEBKIT - XXX No equivalent property? */
/* XXX GtkHTML does not utilize GObject properties as well
* as it could. This just wraps gtk_html_set_inline_spelling()
* so we get a "notify::inline-spelling" signal. */
@@ -1941,23 +2274,29 @@ e_web_view_set_inline_spelling (EWebView *web_view,
gtk_html_set_inline_spelling (GTK_HTML (web_view), inline_spelling);
g_object_notify (G_OBJECT (web_view), "inline-spelling");
+#endif
}
gboolean
e_web_view_get_magic_links (EWebView *web_view)
{
+#if 0 /* WEBKIT - XXX No equivalent property? */
/* XXX This is just here to maintain symmetry
* with e_web_view_set_magic_links(). */
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
return gtk_html_get_magic_links (GTK_HTML (web_view));
+#endif
+
+ return FALSE;
}
void
e_web_view_set_magic_links (EWebView *web_view,
gboolean magic_links)
{
+#if 0 /* WEBKIT - XXX No equivalent property? */
/* XXX GtkHTML does not utilize GObject properties as well
* as it could. This just wraps gtk_html_set_magic_links()
* so we can get a "notify::magic-links" signal. */
@@ -1967,23 +2306,29 @@ e_web_view_set_magic_links (EWebView *web_view,
gtk_html_set_magic_links (GTK_HTML (web_view), magic_links);
g_object_notify (G_OBJECT (web_view), "magic-links");
+#endif
}
gboolean
e_web_view_get_magic_smileys (EWebView *web_view)
{
+#if 0 /* WEBKIT - No equivalent property? */
/* XXX This is just here to maintain symmetry
* with e_web_view_set_magic_smileys(). */
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
return gtk_html_get_magic_smileys (GTK_HTML (web_view));
+#endif
+
+ return FALSE;
}
void
e_web_view_set_magic_smileys (EWebView *web_view,
gboolean magic_smileys)
{
+#if 0 /* WEBKIT - No equivalent property? */
/* XXX GtkHTML does not utilize GObject properties as well
* as it could. This just wraps gtk_html_set_magic_smileys()
* so we can get a "notify::magic-smileys" signal. */
@@ -1993,6 +2338,7 @@ e_web_view_set_magic_smileys (EWebView *web_view,
gtk_html_set_magic_smileys (GTK_HTML (web_view), magic_smileys);
g_object_notify (G_OBJECT (web_view), "magic-smileys");
+#endif
}
const gchar *
@@ -2092,7 +2438,8 @@ e_web_view_get_paste_target_list (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
- return web_view->priv->paste_target_list;
+ return webkit_web_view_get_paste_target_list (
+ WEBKIT_WEB_VIEW (web_view));
}
GtkAction *
@@ -2149,6 +2496,40 @@ e_web_view_set_save_as_proxy (EWebView *web_view,
g_object_notify (G_OBJECT (web_view), "save-as-proxy");
}
+GSList *
+e_web_view_get_highlights (EWebView *web_view)
+{
+ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+
+ return web_view->priv->highlights;
+}
+
+void
+e_web_view_add_highlight (EWebView *web_view,
+ const gchar *highlight)
+{
+ g_return_if_fail (E_IS_WEB_VIEW (web_view));
+ g_return_if_fail (highlight && *highlight);
+
+ web_view->priv->highlights =
+ g_slist_append (web_view->priv->highlights, g_strdup (highlight));
+
+ web_view_update_document_highlights (web_view);
+}
+
+void e_web_view_clear_highlights (EWebView *web_view)
+{
+ g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+ if (!web_view->priv->highlights)
+ return;
+
+ g_slist_free_full (web_view->priv->highlights, g_free);
+ web_view->priv->highlights = NULL;
+
+ web_view_update_document_highlights (web_view);
+}
+
GtkAction *
e_web_view_get_action (EWebView *web_view,
const gchar *action_name)
@@ -2179,20 +2560,16 @@ e_web_view_get_action_group (EWebView *web_view,
gchar *
e_web_view_extract_uri (EWebView *web_view,
- GdkEventButton *event,
- GtkHTML *frame)
+ GdkEventButton *event)
{
EWebViewClass *class;
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
- if (frame == NULL)
- frame = GTK_HTML (web_view);
-
class = E_WEB_VIEW_GET_CLASS (web_view);
g_return_val_if_fail (class->extract_uri != NULL, NULL);
- return class->extract_uri (web_view, event, frame);
+ return class->extract_uri (web_view, event);
}
void
@@ -2200,7 +2577,7 @@ e_web_view_copy_clipboard (EWebView *web_view)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
- g_signal_emit (web_view, signals[COPY_CLIPBOARD], 0);
+ webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (web_view));
}
void
@@ -2208,7 +2585,7 @@ e_web_view_cut_clipboard (EWebView *web_view)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
- g_signal_emit (web_view, signals[CUT_CLIPBOARD], 0);
+ webkit_web_view_cut_clipboard (WEBKIT_WEB_VIEW (web_view));
}
gboolean
@@ -2216,7 +2593,7 @@ e_web_view_is_selection_active (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
- return gtk_html_command (GTK_HTML (web_view), "is-selection-active");
+ return webkit_web_view_has_selection (WEBKIT_WEB_VIEW (web_view));
}
void
@@ -2224,7 +2601,7 @@ e_web_view_paste_clipboard (EWebView *web_view)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
- g_signal_emit (web_view, signals[PASTE_CLIPBOARD], 0);
+ webkit_web_view_paste_clipboard (WEBKIT_WEB_VIEW (web_view));
}
gboolean
@@ -2232,7 +2609,10 @@ e_web_view_scroll_forward (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
- return gtk_html_command (GTK_HTML (web_view), "scroll-forward");
+ webkit_web_view_move_cursor (
+ WEBKIT_WEB_VIEW (web_view), GTK_MOVEMENT_PAGES, 1);
+
+ return TRUE; /* XXX This means nothing. */
}
gboolean
@@ -2240,7 +2620,10 @@ e_web_view_scroll_backward (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
- return gtk_html_command (GTK_HTML (web_view), "scroll-backward");
+ webkit_web_view_move_cursor (
+ WEBKIT_WEB_VIEW (web_view), GTK_MOVEMENT_PAGES, -1);
+
+ return TRUE; /* XXX This means nothing. */
}
void
@@ -2248,15 +2631,17 @@ e_web_view_select_all (EWebView *web_view)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
- gtk_html_command (GTK_HTML (web_view), "select-all");
+ webkit_web_view_select_all (WEBKIT_WEB_VIEW (web_view));
}
void
e_web_view_unselect_all (EWebView *web_view)
{
+#if 0 /* WEBKIT */
g_return_if_fail (E_IS_WEB_VIEW (web_view));
gtk_html_command (GTK_HTML (web_view), "unselect-all");
+#endif
}
void
@@ -2264,7 +2649,7 @@ e_web_view_zoom_100 (EWebView *web_view)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
- gtk_html_command (GTK_HTML (web_view), "zoom-reset");
+ webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (web_view), 1.0);
}
void
@@ -2272,7 +2657,7 @@ e_web_view_zoom_in (EWebView *web_view)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
- gtk_html_command (GTK_HTML (web_view), "zoom-in");
+ webkit_web_view_zoom_in (WEBKIT_WEB_VIEW (web_view));
}
void
@@ -2280,7 +2665,7 @@ e_web_view_zoom_out (EWebView *web_view)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
- gtk_html_command (GTK_HTML (web_view), "zoom-out");
+ webkit_web_view_zoom_out (WEBKIT_WEB_VIEW (web_view));
}
GtkUIManager *
@@ -2354,3 +2739,84 @@ e_web_view_update_actions (EWebView *web_view)
g_signal_emit (web_view, signals[UPDATE_ACTIONS], 0);
}
+
+gchar *
+e_web_view_get_selection_html (EWebView *web_view)
+{
+ WebKitDOMDocument *document;
+ WebKitDOMDOMWindow *window;
+ WebKitDOMDOMSelection *selection;
+ WebKitDOMRange *range;
+ WebKitDOMDocumentFragment *fragment;
+ WebKitDOMHTMLElement *element;
+
+ g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
+
+ if (!webkit_web_view_has_selection (WEBKIT_WEB_VIEW (web_view)))
+ return NULL;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (web_view));
+ window = webkit_dom_document_get_default_view (document);
+ selection = webkit_dom_dom_window_get_selection (window);
+ if (!selection)
+ return NULL;
+
+ range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
+ if (!range)
+ return NULL;
+
+ fragment = webkit_dom_range_clone_contents (range, NULL);
+ if (!fragment)
+ return NULL;
+
+ element = WEBKIT_DOM_HTML_ELEMENT (webkit_dom_document_create_element (document, "div", NULL));
+ webkit_dom_node_append_child (WEBKIT_DOM_NODE (element),
+ WEBKIT_DOM_NODE (fragment), NULL);
+
+ return webkit_dom_html_element_get_inner_html (element);
+}
+
+void
+e_web_view_set_settings (EWebView *web_view,
+ WebKitWebSettings *settings)
+{
+ g_return_if_fail (E_IS_WEB_VIEW (web_view));
+
+ if (settings == webkit_web_view_get_settings (WEBKIT_WEB_VIEW (web_view)))
+ return;
+
+ g_object_bind_property (settings, "enable-caret-browsing", web_view, "caret-mode",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ webkit_web_view_set_settings (WEBKIT_WEB_VIEW (web_view), settings);
+}
+
+WebKitWebSettings *
+e_web_view_get_default_settings (GtkWidget *parent_widget)
+{
+ GtkStyleContext *context;
+ const PangoFontDescription *font;
+ WebKitWebSettings *settings;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (parent_widget), NULL);
+
+ settings = webkit_web_settings_new ();
+
+ /* Use same font-size as rest of Evolution */
+ context = gtk_widget_get_style_context (parent_widget);
+ font = gtk_style_context_get_font (context, GTK_STATE_FLAG_NORMAL);
+
+ g_object_set (G_OBJECT (settings),
+ "default-font-size", (pango_font_description_get_size (font) / PANGO_SCALE),
+ "default-monospace-font-size", (pango_font_description_get_size (font) / PANGO_SCALE),
+ "enable-frame-flattening", TRUE,
+ "enable-java-applet", FALSE,
+ "enable-html5-database", FALSE,
+ "enable-html5-local-storage", FALSE,
+ "enable-offline-web-application-cache", FALSE,
+ "enable-site-specific-quirks", TRUE,
+ "enable-scripts", FALSE,
+ NULL);
+
+ return settings;
+}
diff --git a/widgets/misc/e-web-view.h b/widgets/misc/e-web-view.h
index 1ca9915d5e..d71cf04822 100644
--- a/widgets/misc/e-web-view.h
+++ b/widgets/misc/e-web-view.h
@@ -27,7 +27,8 @@
#ifndef E_WEB_VIEW_H
#define E_WEB_VIEW_H
-#include <gtkhtml/gtkhtml.h>
+#include <webkit/webkit.h>
+#include <JavaScriptCore/JavaScript.h>
/* Standard GObject macros */
#define E_TYPE_WEB_VIEW \
@@ -55,17 +56,25 @@ typedef struct _EWebViewClass EWebViewClass;
typedef struct _EWebViewPrivate EWebViewPrivate;
struct _EWebView {
- GtkHTML parent;
+ WebKitWebView parent;
EWebViewPrivate *priv;
};
+typedef void (*EWebViewJSFunctionCallback) (EWebView *web_view,
+ size_t arg_count,
+ const JSValueRef args[],
+ gpointer user_data);
+
struct _EWebViewClass {
- GtkHTMLClass parent_class;
+ WebKitWebViewClass parent_class;
/* Methods */
+ GtkWidget * (*create_plugin_widget) (EWebView *web_view,
+ const gchar *mime_type,
+ const gchar *uri,
+ GHashTable *param);
gchar * (*extract_uri) (EWebView *web_view,
- GdkEventButton *event,
- GtkHTML *frame);
+ GdkEventButton *event);
void (*hovering_over_link) (EWebView *web_view,
const gchar *title,
const gchar *uri);
@@ -73,11 +82,15 @@ struct _EWebViewClass {
const gchar *uri);
void (*load_string) (EWebView *web_view,
const gchar *load_string);
-
+ void (*load_uri) (EWebView *web_view,
+ const gchar *load_uri);
+ void (*frame_load_string) (EWebView *web_view,
+ const gchar *frame_name,
+ const gchar *string);
+ void (*frame_load_uri) (EWebView *web_view,
+ const gchar *frame_name,
+ const gchar *uri);
/* Signals */
- void (*copy_clipboard) (EWebView *web_view);
- void (*cut_clipboard) (EWebView *web_view);
- void (*paste_clipboard) (EWebView *web_view);
gboolean (*popup_event) (EWebView *web_view,
GdkEventButton *event,
const gchar *uri);
@@ -94,9 +107,32 @@ GtkWidget * e_web_view_new (void);
void e_web_view_clear (EWebView *web_view);
void e_web_view_load_string (EWebView *web_view,
const gchar *string);
-gboolean e_web_view_get_animate (EWebView *web_view);
-void e_web_view_set_animate (EWebView *web_view,
- gboolean animate);
+void e_web_view_load_uri (EWebView *web_view,
+ const gchar *uri);
+const gchar * e_web_view_get_uri (EWebView *web_view);
+void e_web_view_reload (EWebView *web_view);
+void e_web_view_frame_load_string (EWebView *web_view,
+ const gchar *frame_name,
+ const gchar *string);
+void e_web_view_frame_load_uri (EWebView *web_view,
+ const gchar *frame_name,
+ const gchar *uri);
+const gchar * e_web_view_frame_get_uri (EWebView *web_view,
+ const gchar *frame_name);
+JSGlobalContextRef
+ e_web_view_get_global_context (EWebView *web_view);
+GType e_web_view_exec_script (EWebView *web_view,
+ const gchar *script,
+ GValue *value);
+GType e_web_view_frame_exec_script (EWebView *web_view,
+ const gchar *frame_name,
+ const gchar *script,
+ GValue *value);
+void e_web_view_install_js_callback (EWebView *web_view,
+ const gchar *fnc_name,
+ EWebViewJSFunctionCallback callback,
+ gpointer user_data);
+gchar * e_web_view_get_html (EWebView *web_view);
gboolean e_web_view_get_caret_mode (EWebView *web_view);
void e_web_view_set_caret_mode (EWebView *web_view,
gboolean caret_mode);
@@ -109,6 +145,11 @@ gboolean e_web_view_get_disable_save_to_disk
void e_web_view_set_disable_save_to_disk
(EWebView *web_view,
gboolean disable_save_to_disk);
+gboolean e_web_view_get_enable_frame_flattening
+ (EWebView *web_view);
+void e_web_view_set_enable_frame_flattening
+ (EWebView *web_view,
+ gboolean enable_frame_flattening);
gboolean e_web_view_get_editable (EWebView *web_view);
void e_web_view_set_editable (EWebView *web_view,
gboolean editable);
@@ -142,13 +183,16 @@ void e_web_view_set_print_proxy (EWebView *web_view,
GtkAction * e_web_view_get_save_as_proxy (EWebView *web_view);
void e_web_view_set_save_as_proxy (EWebView *web_view,
GtkAction *save_as_proxy);
+GSList * e_web_view_get_highlights (EWebView *web_view);
+void e_web_view_add_highlight (EWebView *web_view,
+ const gchar *highlight);
+void e_web_view_clear_highlights (EWebView *web_view);
GtkAction * e_web_view_get_action (EWebView *web_view,
const gchar *action_name);
GtkActionGroup *e_web_view_get_action_group (EWebView *web_view,
const gchar *group_name);
gchar * e_web_view_extract_uri (EWebView *web_view,
- GdkEventButton *event,
- GtkHTML *frame);
+ GdkEventButton *event);
void e_web_view_copy_clipboard (EWebView *web_view);
void e_web_view_cut_clipboard (EWebView *web_view);
gboolean e_web_view_is_selection_active (EWebView *web_view);
@@ -171,6 +215,13 @@ void e_web_view_status_message (EWebView *web_view,
void e_web_view_stop_loading (EWebView *web_view);
void e_web_view_update_actions (EWebView *web_view);
+gchar * e_web_view_get_selection_html (EWebView *web_view);
+
+void e_web_view_set_settings (EWebView *web_view,
+ WebKitWebSettings *settings);
+WebKitWebSettings *
+ e_web_view_get_default_settings ();
+
G_END_DECLS
#endif /* E_WEB_VIEW_H */