/*
* e-web-view.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/>
*
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#include "e-web-view.h"
#include <config.h>
#include <string.h>
#include <glib/gi18n-lib.h>
#include <camel/camel-internet-address.h>
#include <camel/camel-url.h>
#include "e-util/e-util.h"
#include "e-util/e-binding.h"
#include "e-util/e-plugin-ui.h"
#include "e-popup-action.h"
#include "e-selectable.h"
#define E_WEB_VIEW_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_WEB_VIEW, EWebViewPrivate))
typedef struct _EWebViewRequest EWebViewRequest;
struct _EWebViewPrivate {
GList *requests;
GtkUIManager *ui_manager;
gchar *selected_uri;
GtkAction *open_proxy;
GtkAction *print_proxy;
GtkAction *save_as_proxy;
/* 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];
};
enum {
PROP_0,
PROP_ANIMATE,
PROP_CARET_MODE,
PROP_DISABLE_PRINTING,
PROP_DISABLE_SAVE_TO_DISK,
PROP_OPEN_PROXY,
PROP_PRINT_PROXY,
PROP_SAVE_AS_PROXY,
PROP_SELECTED_URI
};
enum {
POPUP_EVENT,
STATUS_MESSAGE,
STOP_LOADING,
UPDATE_ACTIONS,
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'/>"
" </placeholder>"
" <placeholder name='custom-actions-3'/>"
" <separator/>"
" <menuitem action='select-all'/>"
" </popup>"
"</ui>";
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;
/* 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)
{
e_web_view_copy_clipboard (web_view);
}
static void
action_http_open_cb (GtkAction *action,
EWebView *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_get_selected_uri (web_view);
g_return_if_fail (uri != NULL);
e_show_uri (parent, uri);
}
static void
action_mailto_copy_cb (GtkAction *action,
EWebView *web_view)
{
CamelURL *curl;
CamelInternetAddress *inet_addr;
GtkClipboard *clipboard;
const gchar *uri;
gchar *text;
uri = e_web_view_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_encode (CAMEL_ADDRESS (inet_addr));
if (text == NULL || *text == '\0')
text = g_strdup (uri + strlen ("mailto:"));
camel_object_unref (inet_addr);
camel_url_free (curl);
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,
EWebView *web_view)
{
e_web_view_select_all (web_view);
}
static void
action_send_message_cb (GtkAction *action,
EWebView *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_get_selected_uri (web_view);
g_return_if_fail (uri != NULL);
e_show_uri (parent, uri);
}
static void
action_uri_copy_cb (GtkAction *action,
EWebView *web_view)
{
GtkClipboard *clipboard;
const gchar *uri;
clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
uri = e_web_view_get_selected_uri (web_view);
g_return_if_fail (uri != NULL);
gtk_clipboard_set_text (clipboard, uri, -1);
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 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_button_press_event_cb (EWebView *web_view,
GdkEventButton *event,
GtkHTML *frame)
{
gboolean event_handled = FALSE;
gchar *uri;
if (event != NULL && event->button != 3)
return FALSE;
uri = e_web_view_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_menu_item_select_cb (EWebView *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_status_message (web_view, tooltip);
}
static void
web_view_menu_item_deselect_cb (EWebView *web_view)
{
e_web_view_status_message (web_view, NULL);
}
static void
web_view_connect_proxy_cb (EWebView *web_view,
GtkAction *action,
GtkWidget *proxy)
{
if (!GTK_IS_MENU_ITEM (proxy))
return;
g_signal_connect_swapped (
proxy, "select",
G_CALLBACK (web_view_menu_item_select_cb), web_view);
g_signal_connect_swapped (
proxy, "deselect",
G_CALLBACK (web_view_menu_item_deselect_cb), web_view);
}
static void
web_view_set_property (GObject *object,
guint property_id,
const GValue *value,
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),
g_value_get_boolean (value));
return;
case PROP_DISABLE_PRINTING:
e_web_view_set_disable_printing (
E_WEB_VIEW (object),
g_value_get_boolean (value));
return;
case PROP_DISABLE_SAVE_TO_DISK:
e_web_view_set_disable_save_to_disk (
E_WEB_VIEW (object),
g_value_get_boolean (value));
return;
case PROP_OPEN_PROXY:
e_web_view_set_open_proxy (
E_WEB_VIEW (object),
g_value_get_object (value));
return;
case PROP_PRINT_PROXY:
e_web_view_set_print_proxy (
E_WEB_VIEW (object),
g_value_get_object (value));
return;
case PROP_SAVE_AS_PROXY:
e_web_view_set_save_as_proxy (
E_WEB_VIEW (object),
g_value_get_object (value));
return;
case PROP_SELECTED_URI:
e_web_view_set_selected_uri (
E_WEB_VIEW (object),
g_value_get_string (value));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
web_view_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_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_DISABLE_PRINTING:
g_value_set_boolean (
value, e_web_view_get_disable_printing (
E_WEB_VIEW (object)));
return;
case PROP_DISABLE_SAVE_TO_DISK:
g_value_set_boolean (
value, e_web_view_get_disable_save_to_disk (
E_WEB_VIEW (object)));
return;
case PROP_OPEN_PROXY:
g_value_set_object (
value, e_web_view_get_open_proxy (
E_WEB_VIEW (object)));
return;
case PROP_PRINT_PROXY:
g_value_set_object (
value, e_web_view_get_print_proxy (
E_WEB_VIEW (object)));
return;
case PROP_SAVE_AS_PROXY:
g_value_set_object (
value, e_web_view_get_save_as_proxy (
E_WEB_VIEW (object)));
return;
case PROP_SELECTED_URI:
g_value_set_string (
value, e_web_view_get_selected_uri (
E_WEB_VIEW (object)));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
web_view_dispose (GObject *object)
{
EWebViewPrivate *priv;
priv = E_WEB_VIEW_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;
}
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
web_view_finalize (GObject *object)
{
EWebViewPrivate *priv;
priv = E_WEB_VIEW_GET_PRIVATE (object);
/* All URI requests should be complete or cancelled by now. */
if (priv->requests != NULL)
g_warning ("Finalizing EWebView with active URI requests");
g_free (priv->selected_uri);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
web_view_button_press_event (GtkWidget *widget,
GdkEventButton *event)
{
GtkWidgetClass *widget_class;
EWebView *web_view;
web_view = E_WEB_VIEW (widget);
if (web_view_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_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_url_requested (GtkHTML *html,
const gchar *uri,
GtkHTMLStream *stream)
{
EWebViewRequest *request;
request = web_view_request_new (E_WEB_VIEW (html), uri, stream);
g_file_read_async (
request->file, G_PRIORITY_DEFAULT,
request->cancellable, (GAsyncReadyCallback)
web_view_request_read_cb, request);
}
static void
web_view_link_clicked (GtkHTML *html,
const gchar *uri)
{
gpointer parent;
parent = gtk_widget_get_toplevel (GTK_WIDGET (html));
parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
e_show_uri (parent, uri);
}
static void
web_view_on_url (GtkHTML *html,
const gchar *uri)
{
EWebView *web_view;
CamelInternetAddress *address;
CamelURL *curl;
const gchar *format = NULL;
gchar *message = NULL;
gchar *who;
web_view = E_WEB_VIEW (html);
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));
camel_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_status_message (web_view, message);
g_free (message);
}
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);
}
static gchar *
web_view_extract_uri (EWebView *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 gboolean
web_view_popup_event (EWebView *web_view,
GdkEventButton *event,
const gchar *uri)
{
if (uri != NULL)
e_web_view_unselect_all (web_view);
e_web_view_set_selected_uri (web_view, uri);
e_web_view_show_popup_menu (web_view, event, NULL, NULL);
return TRUE;
}
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));
}
static void
web_view_update_actions (EWebView *web_view)
{
GtkActionGroup *action_group;
gboolean have_selection;
gboolean scheme_is_http = FALSE;
gboolean scheme_is_mailto = FALSE;
gboolean uri_is_valid = FALSE;
gboolean visible;
const gchar *group_name;
const gchar *uri;
uri = e_web_view_get_selected_uri (web_view);
have_selection = e_web_view_is_selection_active (web_view);
/* 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_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_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_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_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_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_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_get_action_group (web_view, group_name);
gtk_action_group_set_visible (action_group, visible);
}
static void
web_view_selectable_update_actions (ESelectable *selectable,
EFocusTracker *focus_tracker,
GdkAtom *clipboard_targets,
gint n_clipboard_targets)
{
EWebView *web_view;
GtkAction *action;
const gchar *tooltip;
gboolean sensitive;
web_view = E_WEB_VIEW (selectable);
/* Copy Clipboard */
action = e_web_view_get_action (web_view, "copy-clipboard");
sensitive = gtk_action_get_sensitive (action);
tooltip = gtk_action_get_tooltip (action);
action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
gtk_action_set_sensitive (action, sensitive);
gtk_action_set_tooltip (action, tooltip);
/* Select All */
action = e_web_view_get_action (web_view, "select-all");
sensitive = gtk_action_get_sensitive (action);
tooltip = gtk_action_get_tooltip (action);
action = e_focus_tracker_get_select_all_action (focus_tracker);
gtk_action_set_sensitive (action, sensitive);
gtk_action_set_tooltip (action, tooltip);
}
static void
web_view_selectable_copy_clipboard (ESelectable *selectable)
{
e_web_view_copy_clipboard (E_WEB_VIEW (selectable));
}
static void
web_view_selectable_select_all (ESelectable *selectable)
{
e_web_view_select_all (E_WEB_VIEW (selectable));
}
static void
web_view_class_init (EWebViewClass *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 (EWebViewPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->set_property = web_view_set_property;
object_class->get_property = web_view_get_property;
object_class->dispose = web_view_dispose;
object_class->finalize = web_view_finalize;
widget_class = GTK_WIDGET_CLASS (class);
widget_class->button_press_event = web_view_button_press_event;
widget_class->scroll_event = web_view_scroll_event;
html_class = GTK_HTML_CLASS (class);
html_class->url_requested = web_view_url_requested;
html_class->link_clicked = web_view_link_clicked;
html_class->on_url = web_view_on_url;
html_class->iframe_created = web_view_iframe_created;
class->extract_uri = web_view_extract_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",
"Caret Mode",
NULL,
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_DISABLE_PRINTING,
g_param_spec_boolean (
"disable-printing",
"Disable Printing",
NULL,
FALSE,
G_PARAM_READWRITE));
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_object_class_install_property (
object_class,
PROP_OPEN_PROXY,
g_param_spec_object (
"open-proxy",
"Open Proxy",
NULL,
GTK_TYPE_ACTION,
G_PARAM_READWRITE));
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));
signals[POPUP_EVENT] = g_signal_new (
"popup-event",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EWebViewClass, 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 (EWebViewClass, 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 (EWebViewClass, 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 (EWebViewClass, update_actions),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
web_view_selectable_init (ESelectableInterface *interface)
{
interface->update_actions = web_view_selectable_update_actions;
interface->copy_clipboard = web_view_selectable_copy_clipboard;
interface->select_all = web_view_selectable_select_all;
}
static void
web_view_init (EWebView *web_view)
{
GtkUIManager *ui_manager;
GtkActionGroup *action_group;
EPopupAction *popup_action;
const gchar *domain = GETTEXT_PACKAGE;
const gchar *id;
GError *error = NULL;
web_view->priv = E_WEB_VIEW_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_connect_proxy_cb), web_view);
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 ("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);
e_mutual_binding_new (
web_view, "open-proxy",
popup_action, "related-action");
/* 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);
e_mutual_binding_new (
web_view, "print-proxy",
popup_action, "related-action");
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);
e_mutual_binding_new (
web_view, "save-as-proxy",
popup_action, "related-action");
/* 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);
}
GType
e_web_view_get_type (void)
{
static GType type = 0;
if (G_UNLIKELY (type == 0)) {
static const GTypeInfo type_info = {
sizeof (EWebViewClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) web_view_class_init,
(GClassFinalizeFunc) NULL,
NULL, /* class_data */
sizeof (EWebView),
0, /* n_preallocs */
(GInstanceInitFunc) web_view_init,
NULL /* value_table */
};
static const GInterfaceInfo selectable_info = {
(GInterfaceInitFunc) web_view_selectable_init,
(GInterfaceFinalizeFunc) NULL,
NULL /* interface_data */
};
type = g_type_register_static (
GTK_TYPE_HTML, "EWebView", &type_info, 0);
g_type_add_interface_static (
type, E_TYPE_SELECTABLE, &selectable_info);
}
return type;
}
GtkWidget *
e_web_view_new (void)
{
return g_object_new (E_TYPE_WEB_VIEW, NULL);
}
void
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));
}
void
e_web_view_load_string (EWebView *web_view,
const gchar *string)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
if (string != NULL && *string != '\0')
gtk_html_load_from_string (GTK_HTML (web_view), string, -1);
else
e_web_view_clear (web_view);
}
gboolean
e_web_view_get_animate (EWebView *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 (web_view), FALSE);
return gtk_html_get_animate (GTK_HTML (web_view));
}
void
e_web_view_set_animate (EWebView *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 (web_view));
gtk_html_set_animate (GTK_HTML (web_view), animate);
g_object_notify (G_OBJECT (web_view), "animate");
}
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));
}
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);
g_object_notify (G_OBJECT (web_view), "caret-mode");
}
gboolean
e_web_view_get_disable_printing (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
return web_view->priv->disable_printing;
}
void
e_web_view_set_disable_printing (EWebView *web_view,
gboolean disable_printing)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
web_view->priv->disable_printing = disable_printing;
g_object_notify (G_OBJECT (web_view), "disable-printing");
}
gboolean
e_web_view_get_disable_save_to_disk (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
return web_view->priv->disable_save_to_disk;
}
void
e_web_view_set_disable_save_to_disk (EWebView *web_view,
gboolean disable_save_to_disk)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
web_view->priv->disable_save_to_disk = disable_save_to_disk;
g_object_notify (G_OBJECT (web_view), "disable-save-to-disk");
}
const gchar *
e_web_view_get_selected_uri (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
return web_view->priv->selected_uri;
}
void
e_web_view_set_selected_uri (EWebView *web_view,
const gchar *selected_uri)
{
g_return_if_fail (E_IS_WEB_VIEW (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");
}
GtkAction *
e_web_view_get_open_proxy (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
return web_view->priv->open_proxy;
}
void
e_web_view_set_open_proxy (EWebView *web_view,
GtkAction *open_proxy)
{
g_return_if_fail (E_IS_WEB_VIEW (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");
}
GtkAction *
e_web_view_get_print_proxy (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
return web_view->priv->print_proxy;
}
void
e_web_view_set_print_proxy (EWebView *web_view,
GtkAction *print_proxy)
{
g_return_if_fail (E_IS_WEB_VIEW (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_get_save_as_proxy (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
return web_view->priv->save_as_proxy;
}
void
e_web_view_set_save_as_proxy (EWebView *web_view,
GtkAction *save_as_proxy)
{
g_return_if_fail (E_IS_WEB_VIEW (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_get_action (EWebView *web_view,
const gchar *action_name)
{
GtkUIManager *ui_manager;
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
g_return_val_if_fail (action_name != NULL, NULL);
ui_manager = e_web_view_get_ui_manager (web_view);
return e_lookup_action (ui_manager, action_name);
}
GtkActionGroup *
e_web_view_get_action_group (EWebView *web_view,
const gchar *group_name)
{
GtkUIManager *ui_manager;
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
g_return_val_if_fail (group_name != NULL, NULL);
ui_manager = e_web_view_get_ui_manager (web_view);
return e_lookup_action_group (ui_manager, group_name);
}
gchar *
e_web_view_extract_uri (EWebView *web_view,
GdkEventButton *event,
GtkHTML *frame)
{
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);
}
void
e_web_view_copy_clipboard (EWebView *web_view)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
gtk_html_command (GTK_HTML (web_view), "copy");
}
gboolean
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");
}
gboolean
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");
}
gboolean
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");
}
void
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");
}
void
e_web_view_unselect_all (EWebView *web_view)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
gtk_html_command (GTK_HTML (web_view), "unselect-all");
}
void
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");
}
void
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");
}
void
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");
}
GtkUIManager *
e_web_view_get_ui_manager (EWebView *web_view)
{
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
return web_view->priv->ui_manager;
}
GtkWidget *
e_web_view_get_popup_menu (EWebView *web_view)
{
GtkUIManager *ui_manager;
GtkWidget *menu;
g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL);
ui_manager = e_web_view_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_show_popup_menu (EWebView *web_view,
GdkEventButton *event,
GtkMenuPositionFunc func,
gpointer user_data)
{
GtkWidget *menu;
g_return_if_fail (E_IS_WEB_VIEW (web_view));
e_web_view_update_actions (web_view);
menu = e_web_view_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_status_message (EWebView *web_view,
const gchar *status_message)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
g_signal_emit (web_view, signals[STATUS_MESSAGE], 0, status_message);
}
void
e_web_view_stop_loading (EWebView *web_view)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
g_signal_emit (web_view, signals[STOP_LOADING], 0);
}
void
e_web_view_update_actions (EWebView *web_view)
{
g_return_if_fail (E_IS_WEB_VIEW (web_view));
g_signal_emit (web_view, signals[UPDATE_ACTIONS], 0);
}