midori/midori/midori-view.c
Christian Dywan 10c5ac4597 Use setting for old and new prefetching option
So prefetching in private mode is disabled regardless of
whether prefetching is done in Midori or in new WebKit
versions.
Menu items also adhere to that same setting now.

And use the chance to correct version checks.
2011-04-17 22:29:51 +02:00

5792 lines
196 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Copyright (C) 2007-2010 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2009 Jean-François Guchens <zcx000@gmail.com>
This library 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.1 of the License, or (at your option) any later version.
See the file COPYING for the full license text.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "midori-view.h"
#include "midori-stock.h"
#include "midori-browser.h"
#include "marshal.h"
#include "sokoke.h"
#include <string.h>
#include <stdlib.h>
#include <glib/gi18n.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include <gdk/gdkkeysyms.h>
#include <webkit/webkit.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifndef G_OS_WIN32
#include <sys/utsname.h>
#endif
/* This is unstable API, so we need to declare it */
gchar*
webkit_web_view_get_selected_text (WebKitWebView* web_view);
/* This is public API since WebKitGTK+ 1.1.6 */
#if !WEBKIT_CHECK_VERSION (1, 1, 6)
void
webkit_web_frame_print (WebKitWebFrame* web_frame);
#endif
GdkPixbuf*
midori_search_action_get_icon (KatzeItem* item,
GtkWidget* widget,
const gchar** icon_name);
static void
midori_view_construct_web_view (MidoriView* view);
static void
midori_view_item_meta_data_changed (KatzeItem* item,
const gchar* key,
MidoriView* view);
static void
_midori_view_set_settings (MidoriView* view,
MidoriWebSettings* settings);
struct _MidoriView
{
GtkVBox parent_instance;
gchar* uri;
gboolean special;
gchar* title;
MidoriSecurity security;
gchar* mime_type;
GdkPixbuf* icon;
gchar* icon_uri;
gdouble progress;
MidoriLoadStatus load_status;
gboolean minimized;
gchar* statusbar_text;
#if WEBKIT_CHECK_VERSION (1, 1, 15)
WebKitHitTestResult* hit_test;
#endif
gchar* link_uri;
gboolean has_selection;
gchar* selected_text;
MidoriWebSettings* settings;
GtkWidget* web_view;
GtkWidget* thumb_view;
KatzeArray* news_feeds;
gchar* news_aggregator;
gboolean middle_click_opens_selection;
gboolean open_tabs_in_the_background;
gboolean close_buttons_on_tabs;
MidoriNewPage open_new_pages_in;
gint find_links;
gint alerts;
GtkWidget* menu_item;
GtkWidget* tab_label;
GtkWidget* tab_icon;
GtkWidget* tab_title;
GtkWidget* tab_close;
KatzeItem* item;
gint scrollh, scrollv;
gboolean back_forward_set;
GHashTable* memory;
GtkWidget* scrolled_window;
};
struct _MidoriViewClass
{
GtkVBoxClass parent_class;
};
G_DEFINE_TYPE (MidoriView, midori_view, GTK_TYPE_VBOX);
GType
midori_load_status_get_type (void)
{
static GType type = 0;
static const GEnumValue values[] = {
{ MIDORI_LOAD_PROVISIONAL, "MIDORI_LOAD_PROVISIONAL", "Load Provisional" },
{ MIDORI_LOAD_COMMITTED, "MIDORI_LOAD_COMMITTED", "Load Committed" },
{ MIDORI_LOAD_FINISHED, "MIDORI_LOAD_FINISHED", "Load Finished" },
{ 0, NULL, NULL }
};
if (type)
return type;
type = g_enum_register_static ("MidoriLoadStatus", values);
return type;
}
GType
midori_new_view_get_type (void)
{
static GType type = 0;
if (!type)
{
static const GEnumValue values[] = {
{ MIDORI_NEW_VIEW_TAB, "MIDORI_NEW_VIEW_TAB", "New view in a tab" },
{ MIDORI_NEW_VIEW_BACKGROUND, "MIDORI_NEW_VIEW_BACKGROUND",
"New view in a background tab" },
{ MIDORI_NEW_VIEW_WINDOW, "MIDORI_NEW_VIEW_WINDOW",
"New view in a window" },
{ 0, NULL, NULL }
};
type = g_enum_register_static ("MidoriNewView", values);
}
return type;
}
GType
midori_security_get_type (void)
{
static GType type = 0;
if (!type)
{
static const GEnumValue values[] = {
{ MIDORI_SECURITY_NONE, "MIDORI_SECURITY_NONE", "No security" },
{ MIDORI_SECURITY_UNKNOWN, "MIDORI_SECURITY_UNKNOWN", "Security unknown" },
{ MIDORI_SECURITY_TRUSTED, "MIDORI_SECURITY_TRUSTED", "Trusted security" },
{ 0, NULL, NULL }
};
type = g_enum_register_static ("MidoriSecurity", values);
}
return type;
}
enum
{
PROP_0,
PROP_URI,
PROP_TITLE,
PROP_SECURITY,
PROP_MIME_TYPE,
PROP_ICON,
PROP_LOAD_STATUS,
PROP_PROGRESS,
PROP_MINIMIZED,
PROP_ZOOM_LEVEL,
PROP_NEWS_FEEDS,
PROP_STATUSBAR_TEXT,
PROP_SETTINGS
};
enum {
ACTIVATE_ACTION,
CONSOLE_MESSAGE,
CONTEXT_READY,
ATTACH_INSPECTOR,
DETACH_INSPECTOR,
NEW_TAB,
NEW_WINDOW,
NEW_VIEW,
DOWNLOAD_REQUESTED,
SEARCH_TEXT,
ADD_BOOKMARK,
SAVE_AS,
ADD_SPEED_DIAL,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
static void
midori_view_finalize (GObject* object);
static void
midori_view_set_property (GObject* object,
guint prop_id,
const GValue* value,
GParamSpec* pspec);
static void
midori_view_get_property (GObject* object,
guint prop_id,
GValue* value,
GParamSpec* pspec);
static gboolean
midori_view_focus_in_event (GtkWidget* widget,
GdkEventFocus* event);
static void
midori_view_settings_notify_cb (MidoriWebSettings* settings,
GParamSpec* pspec,
MidoriView* view);
static void
midori_view_speed_dial_save (MidoriView* view,
const gchar* message);
static void
midori_view_class_init (MidoriViewClass* class)
{
GObjectClass* gobject_class;
GtkWidgetClass* gtkwidget_class;
GParamFlags flags;
signals[ACTIVATE_ACTION] = g_signal_new (
"activate-action",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
signals[CONSOLE_MESSAGE] = g_signal_new (
"console-message",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
midori_cclosure_marshal_VOID__STRING_INT_STRING,
G_TYPE_NONE, 3,
G_TYPE_STRING,
G_TYPE_INT,
G_TYPE_STRING);
signals[CONTEXT_READY] = g_signal_new (
"context-ready",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST),
0,
0,
NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1,
G_TYPE_POINTER);
signals[ATTACH_INSPECTOR] = g_signal_new (
"attach-inspector",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
GTK_TYPE_WIDGET);
/**
* MidoriView::detach-inspector:
* @view: the object on which the signal is emitted
*
* Emitted when an open inspector that was previously
* attached to the window is now detached again.
*
* Since: 0.3.4
*/
signals[DETACH_INSPECTOR] = g_signal_new (
"detach-inspector",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
GTK_TYPE_WIDGET);
signals[NEW_TAB] = g_signal_new (
"new-tab",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
midori_cclosure_marshal_VOID__STRING_BOOLEAN,
G_TYPE_NONE, 2,
G_TYPE_STRING,
G_TYPE_BOOLEAN);
signals[NEW_WINDOW] = g_signal_new (
"new-window",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
/**
* MidoriView::new-view:
* @view: the object on which the signal is emitted
* @new_view: a newly created view
* @where: where to open the view
*
* Emitted when a new view is created. The value of
* @where determines where to open the view according
* to how it was opened and user preferences.
*
* Since: 0.1.2
*/
signals[NEW_VIEW] = g_signal_new (
"new-view",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
midori_cclosure_marshal_VOID__OBJECT_ENUM,
G_TYPE_NONE, 2,
MIDORI_TYPE_VIEW,
MIDORI_TYPE_NEW_VIEW);
/**
* MidoriView::download-requested:
* @view: the object on which the signal is emitted
* @download: a new download
*
* Emitted when a new download is requested, if a
* file cannot be displayed or a download was started
* from the context menu.
*
* If the download should be accepted, a callback
* has to return %TRUE, and the download will also
* be started automatically.
*
* Note: This requires WebKitGTK 1.1.3.
*
* Return value: %TRUE if the download was handled
*
* Since: 0.1.5
*/
signals[DOWNLOAD_REQUESTED] = g_signal_new (
"download-requested",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
g_signal_accumulator_true_handled,
NULL,
midori_cclosure_marshal_BOOLEAN__OBJECT,
G_TYPE_BOOLEAN, 1,
G_TYPE_OBJECT);
/**
* MidoriView::search-text:
* @view: the object on which the signal is emitted
* @found: whether the search was successful
* @typing: whether the search was initiated by typing
*
* Emitted when a search is performed. Either manually
* invoked or automatically by typing. The value of typing
* is actually the text the user typed.
*
* Note that in 0.1.3 the argument @typing was introduced.
*/
signals[SEARCH_TEXT] = g_signal_new (
"search-text",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST),
0,
0,
NULL,
midori_cclosure_marshal_VOID__BOOLEAN_STRING,
G_TYPE_NONE, 2,
G_TYPE_BOOLEAN,
G_TYPE_STRING);
/**
* MidoriView::add-bookmark:
* @view: the object on which the signal is emitted
* @uri: the bookmark URI
*
* Emitted when a bookmark is added.
*
* Deprecated: 0.2.7
*/
signals[ADD_BOOKMARK] = g_signal_new (
"add-bookmark",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
signals[SAVE_AS] = g_signal_new (
"save-as",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
/**
* MidoriView::add-speed-dial:
* @view: the object on which the signal is emitted
* @uri: the URI to add to the speed dial
*
* Emitted when an URI is added to the spee dial page.
*
* Since: 0.1.7
*/
signals[ADD_SPEED_DIAL] = g_signal_new (
"add-speed-dial",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
0,
NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
gobject_class = G_OBJECT_CLASS (class);
gobject_class->finalize = midori_view_finalize;
gobject_class->set_property = midori_view_set_property;
gobject_class->get_property = midori_view_get_property;
gtkwidget_class = GTK_WIDGET_CLASS (class);
gtkwidget_class->focus_in_event = midori_view_focus_in_event;
flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS;
g_object_class_install_property (gobject_class,
PROP_URI,
g_param_spec_string (
"uri",
"Uri",
"The URI of the currently loaded page",
"about:blank",
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_TITLE,
g_param_spec_string (
"title",
"Title",
"The title of the currently loaded page",
NULL,
flags));
/**
* MidoriView:security:
*
* The security status of the loaded page.
*
* Since: 0.2.5
*/
g_object_class_install_property (gobject_class,
PROP_SECURITY,
g_param_spec_enum (
"security",
"Security",
"The security of the currently loaded page",
MIDORI_TYPE_SECURITY,
MIDORI_SECURITY_NONE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* MidoriView:mime-type:
*
* The MIME type of the currently loaded page.
*
* Since: 0.1.2
*/
g_object_class_install_property (gobject_class,
PROP_MIME_TYPE,
g_param_spec_string (
"mime-type",
"MIME Type",
"The MIME type of the currently loaded page",
"text/html",
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_ICON,
g_param_spec_object (
"icon",
"Icon",
"The icon of the view",
GDK_TYPE_PIXBUF,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_LOAD_STATUS,
g_param_spec_enum (
"load-status",
"Load Status",
"The current loading status",
MIDORI_TYPE_LOAD_STATUS,
MIDORI_LOAD_FINISHED,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_PROGRESS,
g_param_spec_double (
"progress",
"Progress",
"The current loading progress",
0.0, 1.0, 0.0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* MidoriView:minimized:
*
* Whether the view is minimized or in normal state.
*
* Minimizing a view indicates that only the icon should
* be advertised rather than the full blown tab label and
* it might otherwise be presented specially.
*
* Since: 0.1.8
*/
g_object_class_install_property (gobject_class,
PROP_MINIMIZED,
g_param_spec_boolean (
"minimized",
"Minimized",
"Whether the view is minimized or in normal state",
FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_ZOOM_LEVEL,
g_param_spec_float (
"zoom-level",
"Zoom Level",
"The current zoom level",
G_MINFLOAT,
G_MAXFLOAT,
1.0f,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* MidoriView:news-feeds:
*
* The news feeds advertised by the currently loaded page.
*
* Since: 0.1.7
*/
g_object_class_install_property (gobject_class,
PROP_NEWS_FEEDS,
g_param_spec_object (
"news-feeds",
"News Feeds",
"The list of available news feeds",
KATZE_TYPE_ARRAY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_STATUSBAR_TEXT,
g_param_spec_string (
"statusbar-text",
"Statusbar Text",
"The text displayed in the statusbar",
"",
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_SETTINGS,
g_param_spec_object (
"settings",
"Settings",
"The associated settings",
MIDORI_TYPE_WEB_SETTINGS,
flags));
}
static void
midori_view_set_title (MidoriView* view, const gchar* title)
{
katze_assign (view->title, g_strdup (title));
#ifndef G_OS_WIN32
/* If left-to-right text is combined with right-to-left text the default
behaviour of Pango can result in awkwardly aligned text. For example
"‪بستيان نوصر (hadess) | An era comes to an end - Midori" becomes
"hadess) | An era comes to an end - Midori) بستيان نوصر". So to prevent
this we insert an LRE character before the title which indicates that
we want left-to-right but retains the direction of right-to-left text. */
if (view->title && !g_str_has_prefix (view->title, ""))
{
gchar* new_title = g_strconcat ("", view->title, NULL);
katze_assign (view->title, new_title);
}
#endif
#define title midori_view_get_display_title (view)
if (view->tab_label)
{
/* If the title starts with the presumed name of the website, we
ellipsize differently, to emphasize the subtitle */
if (gtk_label_get_angle (GTK_LABEL (view->tab_title)) == 0.0)
{
SoupURI* uri = soup_uri_new (view->uri);
const gchar* host = uri ? (uri->host ? uri->host : "") : "";
const gchar* name = g_str_has_prefix (host, "www.") ? &host[4] : host;
guint i = 0;
while (name[i++])
if (name[i] == '.')
break;
if (!g_ascii_strncasecmp (title, name, i))
gtk_label_set_ellipsize (GTK_LABEL (view->tab_title), PANGO_ELLIPSIZE_START);
else
gtk_label_set_ellipsize (GTK_LABEL (view->tab_title), PANGO_ELLIPSIZE_END);
if (uri)
soup_uri_free (uri);
}
gtk_label_set_text (GTK_LABEL (view->tab_title), title);
gtk_widget_set_tooltip_text (view->tab_icon, title);
gtk_widget_set_tooltip_text (view->tab_title, title);
}
if (view->menu_item)
gtk_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (
view->menu_item))), title);
katze_item_set_name (view->item, title);
#undef title
}
static void
midori_view_apply_icon (MidoriView* view,
GdkPixbuf* icon,
const gchar* icon_name)
{
katze_item_set_icon (view->item, icon_name);
katze_object_assign (view->icon, icon);
g_object_notify (G_OBJECT (view), "icon");
if (view->tab_icon)
{
if (icon_name)
katze_throbber_set_static_icon_name (KATZE_THROBBER (view->tab_icon),
icon_name);
else
katze_throbber_set_static_pixbuf (KATZE_THROBBER (view->tab_icon),
view->icon);
}
if (view->menu_item)
{
GtkWidget* image;
if (icon_name)
image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
else
image = gtk_image_new_from_pixbuf (view->icon);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (view->menu_item), image);
}
}
static gboolean
midori_view_mime_icon (MidoriView* view,
GtkIconTheme* icon_theme,
const gchar* format,
const gchar* part1,
const gchar* part2)
{
gchar* icon_name;
GdkPixbuf* icon;
icon_name = part2 ? g_strdup_printf (format, part1, part2)
: g_strdup_printf (format, part1);
if (!(icon = gtk_icon_theme_load_icon (icon_theme, icon_name, 16, 0, NULL)))
{
g_free (icon_name);
return FALSE;
}
g_object_ref (icon);
midori_view_apply_icon (view, icon, icon_name);
g_free (icon_name);
return TRUE;
}
static void
midori_view_update_icon (MidoriView* view,
GdkPixbuf* icon)
{
GdkScreen* screen;
GtkIconTheme* theme;
gchar** parts = NULL;
if (icon)
{
midori_view_apply_icon (view, icon, NULL);
return;
}
if (!((screen = gtk_widget_get_screen (GTK_WIDGET (view)))
&& (theme = gtk_icon_theme_get_for_screen (screen))))
return;
if (!((parts = g_strsplit (view->mime_type, "/", 2)) && (*parts && parts[1])))
{
g_strfreev (parts);
/* This is a hack to have a Find icon in the location while the
blank page has a File icon. */
icon = gtk_widget_render_icon (GTK_WIDGET (view),
GTK_STOCK_FIND, GTK_ICON_SIZE_MENU, NULL);
midori_view_apply_icon (view, icon, GTK_STOCK_FILE);
return;
}
if (midori_view_mime_icon (view, theme, "%s-%s", *parts, parts[1]))
return;
if (midori_view_mime_icon (view, theme, "gnome-mime-%s-%s", *parts, parts[1]))
return;
if (midori_view_mime_icon (view, theme, "%s-x-generic", *parts, NULL))
return;
if (midori_view_mime_icon (view, theme, "gnome-mime-%s-x-generic", *parts, NULL))
return;
icon = gtk_widget_render_icon (GTK_WIDGET (view),
GTK_STOCK_FILE, GTK_ICON_SIZE_MENU, NULL);
midori_view_apply_icon (view, icon, NULL);
}
static void
midori_view_icon_cb (GdkPixbuf* icon,
MidoriView* view)
{
midori_view_update_icon (view, icon);
}
typedef void (*KatzeNetIconCb) (GdkPixbuf* icon,
MidoriView* view);
typedef struct
{
gchar* icon_file;
gchar* icon_uri;
MidoriView* view;
} KatzeNetIconPriv;
static void
katze_net_icon_priv_free (KatzeNetIconPriv* priv)
{
g_free (priv->icon_file);
g_free (priv->icon_uri);
g_slice_free (KatzeNetIconPriv, priv);
}
static gboolean
katze_net_icon_status_cb (KatzeNetRequest* request,
KatzeNetIconPriv* priv)
{
switch (request->status)
{
case KATZE_NET_VERIFIED:
if (request->mime_type && strncmp (request->mime_type, "image/", 6))
{
katze_net_icon_priv_free (priv);
return FALSE;
}
break;
case KATZE_NET_MOVED:
break;
default:
katze_net_icon_priv_free (priv);
return FALSE;
}
return TRUE;
}
static void
katze_net_icon_transfer_cb (KatzeNetRequest* request,
KatzeNetIconPriv* priv)
{
GdkPixbuf* pixbuf;
FILE* fp;
GdkPixbuf* pixbuf_scaled;
gint icon_width, icon_height;
size_t ret;
GtkSettings* settings;
if (request->status == KATZE_NET_MOVED)
return;
pixbuf = NULL;
if (request->data)
{
if ((fp = fopen (priv->icon_file, "wb")))
{
ret = fwrite (request->data, 1, request->length, fp);
fclose (fp);
if ((ret - request->length) != 0)
{
g_warning ("Error writing to file %s "
"in katze_net_icon_transfer_cb()", priv->icon_file);
}
pixbuf = gdk_pixbuf_new_from_file (priv->icon_file, NULL);
}
else
pixbuf = katze_pixbuf_new_from_buffer ((guchar*)request->data,
request->length, request->mime_type, NULL);
if (pixbuf)
g_object_ref (pixbuf);
g_hash_table_insert (priv->view->memory,
g_strdup (priv->icon_file), pixbuf);
}
if (!pixbuf)
{
midori_view_icon_cb (NULL, priv->view);
katze_net_icon_priv_free (priv);
return;
}
settings = gtk_widget_get_settings (priv->view->web_view);
gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
&icon_width, &icon_height);
pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, icon_width, icon_height,
GDK_INTERP_BILINEAR);
g_object_unref (pixbuf);
katze_assign (priv->view->icon_uri, g_strdup (priv->icon_uri));
midori_view_icon_cb (pixbuf_scaled, priv->view);
katze_net_icon_priv_free (priv);
}
static void
_midori_web_view_load_icon (MidoriView* view)
{
GdkPixbuf* pixbuf;
KatzeNetIconPriv* priv;
gchar* icon_uri;
gchar* icon_file;
gint icon_width, icon_height;
GdkPixbuf* pixbuf_scaled;
GtkSettings* settings;
pixbuf = NULL;
icon_uri = g_strdup (view->icon_uri);
if ((icon_uri && g_str_has_prefix (icon_uri, "http"))
|| (view->uri && g_str_has_prefix (view->uri, "http")))
{
if (!icon_uri)
{
guint i = 8;
while (view->uri[i] != '\0' && view->uri[i] != '/')
i++;
if (view->uri[i] == '/')
{
gchar* path = g_strndup (view->uri, i);
icon_uri = g_strdup_printf ("%s/favicon.ico", path);
g_free (path);
}
else
icon_uri = g_strdup_printf ("%s/favicon.ico", view->uri);
}
icon_file = katze_net_get_cached_path (NULL, icon_uri, "icons");
if (g_hash_table_lookup_extended (view->memory,
icon_file, NULL, (gpointer)&pixbuf))
{
g_free (icon_file);
if (pixbuf)
{
g_object_ref (pixbuf);
katze_assign (view->icon_uri, icon_uri);
}
}
else if ((pixbuf = gdk_pixbuf_new_from_file (icon_file, NULL)))
{
g_free (icon_file);
katze_assign (view->icon_uri, icon_uri);
}
else if (!view->special)
{
priv = g_slice_new (KatzeNetIconPriv);
priv->icon_file = icon_file;
priv->icon_uri = icon_uri;
priv->view = view;
katze_net_load_uri (NULL, icon_uri,
(KatzeNetStatusCb)katze_net_icon_status_cb,
(KatzeNetTransferCb)katze_net_icon_transfer_cb, priv);
}
}
if (pixbuf)
{
settings = gtk_widget_get_settings (view->web_view);
gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
&icon_width, &icon_height);
pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, icon_width,
icon_height, GDK_INTERP_BILINEAR);
g_object_unref (pixbuf);
pixbuf = pixbuf_scaled;
}
midori_view_update_icon (view, pixbuf);
}
static void
midori_view_update_load_status (MidoriView* view,
MidoriLoadStatus load_status)
{
if (view->load_status == load_status)
return;
if (load_status == MIDORI_LOAD_FINISHED)
view->special = FALSE;
view->load_status = load_status;
g_object_notify (G_OBJECT (view), "load-status");
if (view->tab_icon)
katze_throbber_set_animated (KATZE_THROBBER (view->tab_icon),
view->load_status != MIDORI_LOAD_FINISHED);
}
static gboolean
midori_view_web_view_navigation_decision_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
WebKitNetworkRequest* request,
WebKitWebNavigationAction* action,
WebKitWebPolicyDecision* decision,
MidoriView* view)
{
JSContextRef js_context;
gchar* result;
const gchar* uri = webkit_network_request_get_uri (request);
if (g_str_has_prefix (uri, "mailto:") || sokoke_external_uri (uri))
{
if (sokoke_show_uri (gtk_widget_get_screen (GTK_WIDGET (web_view)),
uri, GDK_CURRENT_TIME, NULL))
{
webkit_web_policy_decision_ignore (decision);
return TRUE;
}
}
view->special = FALSE;
/* Remove link labels */
js_context = webkit_web_frame_get_global_context (web_frame);
result = sokoke_js_script_eval (js_context,
"(function (links) {"
"if (links != undefined && links.length > 0) {"
" for (var i = links.length - 1; i >= 0; i--) {"
" var parent = links[i].parentNode;"
" parent.removeChild(links[i]); } } }) ("
"document.getElementsByClassName ('midoriHKD87346'));",
NULL);
g_free (result);
result = sokoke_js_script_eval (js_context,
"(function (links) {"
"if (links != undefined && links.length > 0) {"
" for (var i = links.length - 1; i >= 0; i--) {"
" var parent = links[i].parentNode;"
" parent.removeChild(links[i]); } } }) ("
"document.getElementsByClassName ('midori_access_key_fc04de'));",
NULL);
g_free (result);
view->find_links = -1;
return FALSE;
}
static void
webkit_web_view_load_started_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
MidoriView* view)
{
g_object_freeze_notify (G_OBJECT (view));
midori_view_update_load_status (view, MIDORI_LOAD_PROVISIONAL);
view->progress = 0.0;
g_object_notify (G_OBJECT (view), "progress");
g_object_thaw_notify (G_OBJECT (view));
}
static void
webkit_web_view_load_committed_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
MidoriView* view)
{
const gchar* uri;
GList* children;
g_object_freeze_notify (G_OBJECT (view));
uri = webkit_web_frame_get_uri (web_frame);
g_return_if_fail (uri != NULL);
katze_assign (view->icon_uri, NULL);
children = gtk_container_get_children (GTK_CONTAINER (view));
for (; children; children = g_list_next (children))
if (g_object_get_data (G_OBJECT (children->data), "midori-infobar-cb"))
gtk_widget_destroy (children->data);
view->alerts = 0;
if (g_strcmp0 (uri, katze_item_get_uri (view->item)))
{
katze_assign (view->uri, sokoke_format_uri_for_display (uri));
katze_item_set_uri (view->item, uri);
}
katze_item_set_added (view->item, time (NULL));
katze_item_set_meta_integer (view->item, "history-step", -1);
g_object_notify (G_OBJECT (view), "uri");
g_object_set (view, "title", NULL, NULL);
midori_view_update_icon (view, NULL);
if (!strncmp (uri, "https", 5))
{
#if WEBKIT_CHECK_VERSION (1, 1, 14) && defined (HAVE_LIBSOUP_2_29_91)
WebKitWebDataSource *source;
WebKitNetworkRequest *request;
SoupMessage *message;
source = webkit_web_frame_get_data_source (web_frame);
request = webkit_web_data_source_get_request (source);
message = webkit_network_request_get_message (request);
if (message
&& soup_message_get_flags (message) & SOUP_MESSAGE_CERTIFICATE_TRUSTED)
view->security = MIDORI_SECURITY_TRUSTED;
else
#endif
view->security = MIDORI_SECURITY_UNKNOWN;
}
else
view->security = MIDORI_SECURITY_NONE;
g_object_notify (G_OBJECT (view), "security");
view->find_links = -1;
midori_view_update_load_status (view, MIDORI_LOAD_COMMITTED);
g_object_thaw_notify (G_OBJECT (view));
}
static void
webkit_web_view_progress_changed_cb (WebKitWebView* web_view,
gint progress,
MidoriView* view)
{
view->progress = progress ? progress / 100.0 : 0.0;
g_object_notify (G_OBJECT (view), "progress");
}
#if WEBKIT_CHECK_VERSION (1, 1, 14)
static void
midori_view_web_view_resource_request_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
WebKitWebResource* web_resource,
WebKitNetworkRequest* request,
WebKitNetworkResponse* response,
MidoriView* view)
{
const gchar* uri = webkit_network_request_get_uri (request);
/* Only apply custom URIs to special pages for security purposes */
if (!view->special)
return;
if (g_str_has_prefix (uri, "res://"))
{
gchar* filename = g_build_filename ("midori/res", &uri[5], NULL);
gchar* filepath = sokoke_find_data_filename (filename);
gchar* file_uri;
g_free (filename);
file_uri = g_filename_to_uri (filepath, NULL, NULL);
g_free (filepath);
webkit_network_request_set_uri (request, file_uri);
g_free (file_uri);
}
else if (g_str_has_prefix (uri, "thumb://"))
{
gchar* file_path = g_build_filename (g_get_user_cache_dir (), "midori", "thumbnails",
g_strdup (uri + 8), NULL);
gchar* file_uri = g_filename_to_uri (file_path, NULL, NULL);
webkit_network_request_set_uri (request, file_uri);
g_free (file_uri);
g_free (file_path);
}
else if (g_str_has_prefix (uri, "stock://"))
{
GdkPixbuf* pixbuf;
const gchar* icon_name = &uri[8] ? &uri[8] : "";
gint icon_size = GTK_ICON_SIZE_MENU;
GdkScreen* screen = gtk_widget_get_screen (GTK_WIDGET (view));
GtkIconTheme* icon_theme = gtk_icon_theme_get_for_screen (screen);
gint real_icon_size;
GtkIconInfo* icon_info;
const gchar* icon_filename;
if (g_ascii_isalpha (icon_name[0]))
icon_size = strstr (icon_name, "dialog") ?
GTK_ICON_SIZE_DIALOG : GTK_ICON_SIZE_BUTTON;
else if (g_ascii_isdigit (icon_name[0]))
{
guint i = 0;
while (icon_name[i])
if (icon_name[i++] == '/')
{
gchar* size = g_strndup (icon_name, i - 1);
icon_size = atoi (size);
/* Compatibility: map pixel to symbolic size */
if (icon_size == 16)
icon_size = GTK_ICON_SIZE_MENU;
g_free (size);
icon_name = &icon_name[i];
}
}
/* If available, load SVG icon as SVG markup */
icon_size = gtk_icon_size_lookup_for_settings (
gtk_widget_get_settings (GTK_WIDGET (view)),
icon_size, &real_icon_size, &real_icon_size);
icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name,
real_icon_size, GTK_ICON_LOOKUP_FORCE_SVG);
icon_filename = icon_info ? gtk_icon_info_get_filename (icon_info) : NULL;
if (icon_filename && g_str_has_suffix (icon_filename, ".svg"))
{
gchar* buffer;
gsize buffer_size;
if (g_file_get_contents (icon_filename, &buffer, &buffer_size, NULL))
{
gchar* encoded = g_base64_encode ((guchar*)buffer, buffer_size);
gchar* data_uri = g_strconcat ("data:image/svg+xml;base64,", encoded, NULL);
g_free (buffer);
g_free (encoded);
webkit_network_request_set_uri (request, data_uri);
g_free (data_uri);
return;
}
}
/* Render icon as a PNG at the desired size */
pixbuf = gtk_widget_render_icon (GTK_WIDGET (view), icon_name, icon_size, NULL);
if (!pixbuf)
pixbuf = gtk_widget_render_icon (GTK_WIDGET (view),
GTK_STOCK_MISSING_IMAGE, icon_size, NULL);
if (pixbuf)
{
gboolean success;
gchar* buffer;
gsize buffer_size;
gchar* encoded;
gchar* data_uri;
success = gdk_pixbuf_save_to_buffer (pixbuf, &buffer, &buffer_size, "png", NULL, NULL);
g_object_unref (pixbuf);
if (!success)
return;
encoded = g_base64_encode ((guchar*)buffer, buffer_size);
g_free (buffer);
data_uri = g_strconcat ("data:image/png;base64,", encoded, NULL);
g_free (encoded);
webkit_network_request_set_uri (request, data_uri);
g_free (data_uri);
return;
}
}
}
#endif
#define HAVE_GTK_INFO_BAR GTK_CHECK_VERSION (2, 18, 0)
#if HAVE_GTK_INFO_BAR
static void
midori_view_infobar_response_cb (GtkWidget* infobar,
gint response,
gpointer data_object)
{
void (*response_cb) (GtkWidget*, gint, gpointer);
response_cb = g_object_get_data (G_OBJECT (infobar), "midori-infobar-cb");
response_cb (infobar, response, data_object);
gtk_widget_destroy (infobar);
}
#else
static void
midori_view_info_bar_button_cb (GtkWidget* button,
gpointer data_object)
{
GtkWidget* infobar = gtk_widget_get_parent (gtk_widget_get_parent (button));
gint response = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "midori-infobar-response"));
void (*response_cb) (GtkWidget*, gint, gpointer);
response_cb = g_object_get_data (G_OBJECT (infobar), "midori-infobar-cb");
response_cb (infobar, response, data_object);
gtk_widget_destroy (infobar);
}
#endif
/**
* midori_view_add_info_bar
* @view: a #MidoriView
* @message_type: a #GtkMessageType
* @message: a message string
* @response_cb: a response callback
* @user_data: user data passed to the callback
* @first_button_text: button text or stock ID
* @...: first response ID, then more text - response ID pairs
*
* Adds an infobar (or equivalent) to the view. Activation of a
* button invokes the specified callback. The infobar is
* automatically destroyed if the location changes or reloads.
*
* Return value: an infobar widget
*
* Since: 0.2.9
**/
GtkWidget*
midori_view_add_info_bar (MidoriView* view,
GtkMessageType message_type,
const gchar* message,
GCallback response_cb,
gpointer data_object,
const gchar* first_button_text,
...)
{
GtkWidget* infobar;
GtkWidget* action_area;
GtkWidget* content_area;
va_list args;
const gchar* button_text;
g_return_val_if_fail (message != NULL, NULL);
g_return_val_if_fail (response_cb != NULL, NULL);
va_start (args, first_button_text);
#if HAVE_GTK_INFO_BAR
infobar = gtk_info_bar_new ();
for (button_text = first_button_text; button_text;
button_text = va_arg (args, const gchar*))
{
gint response_id = va_arg (args, gint);
gtk_info_bar_add_button (GTK_INFO_BAR (infobar),
button_text, response_id);
}
gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), message_type);
content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar));
action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (infobar));
gtk_orientable_set_orientation (GTK_ORIENTABLE (action_area),
GTK_ORIENTATION_HORIZONTAL);
g_signal_connect (infobar, "response",
G_CALLBACK (midori_view_infobar_response_cb), data_object);
#else
infobar = gtk_hbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (infobar), 4);
content_area = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (infobar), content_area, TRUE, TRUE, 0);
action_area = gtk_hbutton_box_new ();
for (button_text = first_button_text; button_text;
button_text = va_arg (args, const gchar*))
{
gint response_id = va_arg (args, gint);
GtkWidget* button = gtk_button_new_with_mnemonic (button_text);
g_object_set_data (G_OBJECT (button), "midori-infobar-response",
GINT_TO_POINTER (response_id));
g_signal_connect (button, "clicked",
G_CALLBACK (midori_view_info_bar_button_cb), data_object);
gtk_box_pack_start (GTK_BOX (action_area), button, FALSE, FALSE, 0);
if (response_id == GTK_RESPONSE_HELP)
gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (action_area),
button, TRUE);
}
gtk_box_pack_start (GTK_BOX (infobar), action_area, FALSE, FALSE, 0);
#endif
va_end (args);
gtk_container_add (GTK_CONTAINER (content_area), gtk_label_new (message));
gtk_widget_show_all (infobar);
gtk_box_pack_start (GTK_BOX (view), infobar, FALSE, FALSE, 0);
gtk_box_reorder_child (GTK_BOX (view), infobar, 0);
g_object_set_data (G_OBJECT (infobar), "midori-infobar-cb", response_cb);
return infobar;
}
#if WEBKIT_CHECK_VERSION (1, 1, 14)
static void
midori_view_database_response_cb (GtkWidget* infobar,
gint response,
WebKitWebDatabase* database)
{
if (response != GTK_RESPONSE_ACCEPT)
{
WebKitSecurityOrigin* origin = webkit_web_database_get_security_origin (database);
webkit_security_origin_set_web_database_quota (origin, 0);
webkit_web_database_remove (database);
}
/* TODO: Remember the decision */
}
static void
midori_view_web_view_database_quota_exceeded_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
WebKitWebDatabase* database,
MidoriView* view)
{
const gchar* uri = webkit_web_frame_get_uri (web_frame);
const gchar* hostname = sokoke_hostname_from_uri (uri, NULL);
gchar* message = g_strdup_printf (_("%s wants to save an HTML5 database."),
hostname && *hostname ? hostname : uri);
midori_view_add_info_bar (view, GTK_MESSAGE_QUESTION, message,
G_CALLBACK (midori_view_database_response_cb), database,
_("_Deny"), GTK_RESPONSE_REJECT, _("_Allow"), GTK_RESPONSE_ACCEPT,
NULL);
g_free (message);
}
#endif
#if WEBKIT_CHECK_VERSION (1, 1, 23)
static void
midori_view_location_response_cb (GtkWidget* infobar,
gint response,
WebKitGeolocationPolicyDecision* decision)
{
if (response == GTK_RESPONSE_ACCEPT)
webkit_geolocation_policy_allow (decision);
else
webkit_geolocation_policy_deny (decision);
}
static gboolean
midori_view_web_view_geolocation_decision_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
WebKitGeolocationPolicyDecision* decision,
MidoriView* view)
{
const gchar* uri = webkit_web_frame_get_uri (web_frame);
const gchar* hostname = sokoke_hostname_from_uri (uri, NULL);
gchar* message = g_strdup_printf (_("%s wants to know your location."),
hostname && *hostname ? hostname : uri);
midori_view_add_info_bar (view, GTK_MESSAGE_QUESTION,
message, G_CALLBACK (midori_view_location_response_cb), decision,
_("_Deny"), GTK_RESPONSE_REJECT, _("_Allow"), GTK_RESPONSE_ACCEPT,
NULL);
g_free (message);
return TRUE;
}
#endif
static void
midori_view_load_alternate_string (MidoriView* view,
const gchar* data,
const gchar* res_root,
const gchar* uri,
WebKitWebFrame* web_frame)
{
WebKitWebView* web_view = WEBKIT_WEB_VIEW (view->web_view);
if (!web_frame)
web_frame = webkit_web_view_get_main_frame (web_view);
view->special = TRUE;
#if WEBKIT_CHECK_VERSION (1, 1, 14)
webkit_web_frame_load_alternate_string (
web_frame, data, uri, uri);
#elif WEBKIT_CHECK_VERSION (1, 1, 6)
webkit_web_frame_load_alternate_string (
web_frame, data, res_root, uri);
#else
webkit_web_view_load_html_string (
web_view, data, res_root);
#endif
}
#if WEBKIT_CHECK_VERSION (1, 1, 6)
static gboolean
midori_view_display_error (MidoriView* view,
const gchar* uri,
const gchar* title,
const gchar* message,
const gchar* description,
const gchar* try_again,
WebKitWebFrame* web_frame)
{
gchar* template_file = g_build_filename ("midori", "res", "error.html", NULL);
gchar* path = sokoke_find_data_filename (template_file);
gchar* template;
g_free (template_file);
if (g_file_get_contents (path, &template, NULL, NULL))
{
#if !WEBKIT_CHECK_VERSION (1, 1, 14)
SoupServer* res_server;
guint port;
#endif
gchar* res_root;
gchar* stock_root;
gchar* title_escaped;
gchar* result;
#if WEBKIT_CHECK_VERSION (1, 1, 14)
res_root = g_strdup ("res:/");
stock_root = g_strdup ("stock:/");
#else
res_server = sokoke_get_res_server ();
port = soup_server_get_port (res_server);
res_root = g_strdup_printf ("http://localhost:%d/res", port);
stock_root = g_strdup_printf ("http://localhost:%d/stock", port);
#endif
title_escaped = g_markup_escape_text (title, -1);
result = sokoke_replace_variables (template,
"{title}", title_escaped,
"{message}", message,
"{description}", description,
"{tryagain}", try_again,
"{res}", res_root,
"{stock}", stock_root,
NULL);
g_free (title_escaped);
g_free (template);
midori_view_load_alternate_string (view,
result, res_root, uri, web_frame);
g_free (res_root);
g_free (stock_root);
g_free (result);
g_free (path);
return TRUE;
}
g_free (path);
return FALSE;
}
static gboolean
webkit_web_view_load_error_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
const gchar* uri,
GError* error,
MidoriView* view)
{
gchar* title;
gchar* message;
gboolean result;
switch (error->code)
{
case WEBKIT_PLUGIN_ERROR_WILL_HANDLE_LOAD:
/* A plugin will take over. That's expected, it's not fatal. */
return FALSE;
}
title = g_strdup_printf (_("Error - %s"), uri);
message = g_strdup_printf (_("The page '%s' couldn't be loaded."), uri);
result = midori_view_display_error (view, uri, title,
message, error->message, _("Try again"), web_frame);
g_free (message);
g_free (title);
return result;
}
#else
static void
webkit_web_frame_load_done_cb (WebKitWebFrame* web_frame,
gboolean success,
MidoriView* view)
{
gchar* title;
gchar* data;
gchar* logo_path;
gchar* logo_uri;
if (!success)
{
/* i18n: The title of the 404 - Not found error page */
title = g_strdup_printf (_("Not found - %s"), view->uri);
katze_assign (view->title, title);
logo_path = sokoke_find_data_filename ("midori/logo-shade.png");
logo_uri = g_filename_to_uri (logo_path, NULL, NULL);
g_free (logo_path);
data = g_strdup_printf (
"<html><head><title>%s</title></head>"
"<body><h1>%s</h1>"
"<img src=\"%s\" "
"style=\"position: absolute; right: 15px; bottom: 15px; z-index: -9;\">"
"<p />The page you were opening doesn't exist."
"<p />Try to <a href=\"%s\">load the page again</a>, "
"or move on to another page."
"</body></html>",
title, title, logo_uri, view->uri);
webkit_web_view_load_html_string (
WEBKIT_WEB_VIEW (view->web_view), data, view->uri);
g_free (title);
g_free (data);
g_free (logo_uri);
}
midori_view_update_load_status (view, MIDORI_LOAD_FINISHED);
}
#endif
static void
midori_view_apply_scroll_position (MidoriView* view)
{
if (view->scrollh > -2)
{
if (view->scrollh > 0)
{
GtkScrolledWindow* scrolled = GTK_SCROLLED_WINDOW (view->scrolled_window);
GtkAdjustment* adjustment = gtk_scrolled_window_get_hadjustment (scrolled);
gtk_adjustment_set_value (adjustment, view->scrollh);
}
view->scrollh = -3;
}
if (view->scrollv > -2)
{
if (view->scrollv > 0)
{
GtkScrolledWindow* scrolled = GTK_SCROLLED_WINDOW (view->scrolled_window);
GtkAdjustment* adjustment = gtk_scrolled_window_get_vadjustment (scrolled);
gtk_adjustment_set_value (adjustment, view->scrollv);
}
view->scrollv = -3;
}
}
static void
webkit_web_view_load_finished_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
MidoriView* view)
{
g_object_freeze_notify (G_OBJECT (view));
/* TODO: Find a better condition than a finished load.
Apparently WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT is too early. */
midori_view_apply_scroll_position (view);
view->progress = 1.0;
g_object_notify (G_OBJECT (view), "progress");
midori_view_update_load_status (view, MIDORI_LOAD_FINISHED);
if (1)
{
JSContextRef js_context = webkit_web_frame_get_global_context (web_frame);
/* This snippet joins the available news feeds into a string like this:
URI1|title1,URI2|title2
FIXME: Ensure separators contained in the string can't break it */
gchar* value = sokoke_js_script_eval (js_context,
"(function (l) { var f = new Array (); for (i in l) "
"{ var t = l[i].type; var r = l[i].rel; "
"if (t && (t.indexOf ('rss') != -1 || t.indexOf ('atom') != -1)) "
"f.push (l[i].href + '|' + l[i].title);"
#if !WEBKIT_CHECK_VERSION (1, 1, 18)
"else if (r && r.indexOf ('icon') != -1) f.push (l[i].href); "
#endif
"} return f; })("
"document.getElementsByTagName ('link'));", NULL);
gchar** items = g_strsplit (value, ",", 0);
guint i = 0;
gchar* default_uri = NULL;
katze_array_clear (view->news_feeds);
if (items != NULL)
while (items[i] != NULL)
{
gchar** parts = g_strsplit (items[i], "|", 2);
if (parts == NULL)
;
else if (*parts && parts[1])
{
KatzeItem* item = g_object_new (KATZE_TYPE_ITEM,
"uri", parts[0], "name", parts[1], NULL);
katze_array_add_item (view->news_feeds, item);
g_object_unref (item);
if (!default_uri)
default_uri = g_strdup (parts[0]);
}
#if !WEBKIT_CHECK_VERSION (1, 1, 18)
else
katze_assign (view->icon_uri, g_strdup (*parts));
#endif
g_strfreev (parts);
i++;
}
g_strfreev (items);
g_object_set_data_full (G_OBJECT (view), "news-feeds", default_uri, g_free);
g_free (value);
/* Ensure load-status is notified again, whether it changed or not */
g_object_notify (G_OBJECT (view), "load-status");
}
_midori_web_view_load_icon (view);
g_object_thaw_notify (G_OBJECT (view));
}
#if WEBKIT_CHECK_VERSION (1, 1, 18)
static void
midori_web_view_notify_icon_uri_cb (WebKitWebView* web_view,
GParamSpec* pspec,
MidoriView* view)
{
const gchar* icon_uri = webkit_web_view_get_icon_uri (web_view);
katze_assign (view->icon_uri, g_strdup (icon_uri));
_midori_web_view_load_icon (view);
}
#endif
#if WEBKIT_CHECK_VERSION (1, 1, 4)
static void
webkit_web_view_notify_uri_cb (WebKitWebView* web_view,
GParamSpec* pspec,
MidoriView* view)
{
katze_assign (view->uri, g_strdup (webkit_web_view_get_uri (web_view)));
g_object_notify (G_OBJECT (view), "uri");
}
static void
webkit_web_view_notify_title_cb (WebKitWebView* web_view,
GParamSpec* pspec,
MidoriView* view)
{
const gchar* title = webkit_web_view_get_title (web_view);
midori_view_set_title (view, title);
g_object_notify (G_OBJECT (view), "title");
}
#else
static void
webkit_web_view_title_changed_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
const gchar* title,
MidoriView* view)
{
g_object_set (view, "title", title, NULL);
}
#endif
static void
webkit_web_view_statusbar_text_changed_cb (WebKitWebView* web_view,
const gchar* text,
MidoriView* view)
{
g_object_set (G_OBJECT (view), "statusbar-text", text, NULL);
}
static gboolean
midori_view_web_view_leave_notify_event_cb (WebKitWebView* web_view,
GdkEventCrossing* event,
MidoriView* view)
{
g_object_set (G_OBJECT (view), "statusbar-text", NULL, NULL);
return FALSE;
}
static void
webkit_web_view_hovering_over_link_cb (WebKitWebView* web_view,
const gchar* tooltip,
const gchar* link_uri,
MidoriView* view)
{
#if !(WEBKIT_CHECK_VERSION (1, 3, 1) && defined (HAVE_LIBSOUP_2_29_3))
sokoke_prefetch_uri (view->settings, link_uri, NULL, NULL);
#endif
katze_assign (view->link_uri, g_strdup (link_uri));
if (link_uri && g_str_has_prefix (link_uri, "mailto:"))
{
gchar* text = g_strdup_printf (_("Send a message to %s"), &link_uri[7]);
g_object_set (view, "statusbar-text", text, NULL);
g_free (text);
}
else
g_object_set (view, "statusbar-text", link_uri, NULL);
}
static void
midori_view_ensure_link_uri (MidoriView* view,
gint *x,
gint *y,
GdkEventButton* event)
{
g_return_if_fail (MIDORI_IS_VIEW (view));
#if WEBKIT_CHECK_VERSION (1, 1, 15)
if (view->web_view && gtk_widget_get_window (view->web_view))
{
GdkEventButton ev;
if (!event) {
gint ex, ey;
event = &ev;
gdk_window_get_pointer (gtk_widget_get_window (view->web_view), &ex, &ey, NULL);
event->x = ex;
event->y = ey;
}
if (x != NULL)
*x = event->x;
if (y != NULL)
*y = event->y;
katze_object_assign (view->hit_test,
g_object_ref (
webkit_web_view_get_hit_test_result (
WEBKIT_WEB_VIEW (view->web_view), event)));
katze_assign (view->link_uri,
katze_object_get_string (view->hit_test, "link-uri"));
}
#endif
}
#define MIDORI_KEYS_MODIFIER_MASK (GDK_SHIFT_MASK | GDK_CONTROL_MASK \
| GDK_MOD1_MASK | GDK_META_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK )
static gboolean
gtk_widget_button_press_event_cb (WebKitWebView* web_view,
GdkEventButton* event,
MidoriView* view)
{
GtkClipboard* clipboard;
gchar* uri;
gchar* new_uri;
const gchar* link_uri;
gboolean background;
event->state = event->state & MIDORI_KEYS_MODIFIER_MASK;
midori_view_ensure_link_uri (view, NULL, NULL, event);
link_uri = midori_view_get_link_uri (view);
switch (event->button)
{
case 1:
if (!link_uri)
return FALSE;
if (MIDORI_MOD_NEW_TAB (event->state))
{
/* Open link in new tab */
background = view->open_tabs_in_the_background;
if (MIDORI_MOD_BACKGROUND (event->state))
background = !background;
g_signal_emit (view, signals[NEW_TAB], 0, link_uri, background);
return TRUE;
}
else if (MIDORI_MOD_NEW_WINDOW (event->state))
{
/* Open link in new window */
g_signal_emit (view, signals[NEW_WINDOW], 0, link_uri);
return TRUE;
}
break;
case 2:
if (link_uri)
{
/* Open link in new tab */
background = view->open_tabs_in_the_background;
if (MIDORI_MOD_BACKGROUND (event->state))
background = !background;
g_signal_emit (view, signals[NEW_TAB], 0, link_uri, background);
return TRUE;
}
else if (MIDORI_MOD_SCROLL (event->state))
{
midori_view_set_zoom_level (MIDORI_VIEW (view), 1.0);
return FALSE; /* Allow Ctrl + Middle click */
}
else if (view->middle_click_opens_selection)
{
gboolean is_editable;
#if WEBKIT_CHECK_VERSION (1, 1, 15)
WebKitHitTestResult* result;
WebKitHitTestResultContext context;
result = webkit_web_view_get_hit_test_result (web_view, event);
context = katze_object_get_int (result, "context");
is_editable = context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE;
g_object_unref (result);
#else
is_editable = webkit_web_view_can_paste_clipboard (WEBKIT_WEB_VIEW (view->web_view));
#endif
if (is_editable)
return FALSE;
clipboard = gtk_clipboard_get_for_display (
gtk_widget_get_display (GTK_WIDGET (view)),
GDK_SELECTION_PRIMARY);
if ((uri = gtk_clipboard_wait_for_text (clipboard)))
{
guint i = 0;
while (uri[i++] != '\0')
if (uri[i] == '\n' || uri[i] == '\r')
uri[i] = ' ';
g_strstrip (uri);
/* Hold Alt to search for the selected word */
if (event->state & GDK_MOD1_MASK)
{
new_uri = sokoke_magic_uri (uri);
if (!new_uri)
{
gchar* search;
g_object_get (view->settings, "location-entry-search",
&search, NULL);
new_uri = sokoke_search_uri (search, uri);
}
katze_assign (uri, new_uri);
}
else if (!strstr (uri, "://"))
{
g_free (uri);
return FALSE;
}
if (MIDORI_MOD_NEW_TAB (event->state))
{
background = view->open_tabs_in_the_background;
if (MIDORI_MOD_BACKGROUND (event->state))
background = !background;
g_signal_emit (view, signals[NEW_TAB], 0, uri, background);
}
else
{
midori_view_set_uri (MIDORI_VIEW (view), uri);
gtk_widget_grab_focus (GTK_WIDGET (view));
}
g_free (uri);
return TRUE;
}
}
break;
#if WEBKIT_CHECK_VERSION (1, 1, 15)
case 3:
if (event->state & GDK_CONTROL_MASK)
{
/* Ctrl + Right-click suppresses javascript button handling */
GtkWidget* menu = gtk_menu_new ();
midori_view_populate_popup (view, menu, TRUE);
katze_widget_popup (GTK_WIDGET (web_view), GTK_MENU (menu), event,
KATZE_MENU_POSITION_CURSOR);
return TRUE;
}
break;
#endif
case 8:
midori_view_go_back (view);
return TRUE;
case 9:
midori_view_go_forward (view);
return TRUE;
/*
* On some fancier mice the scroll wheel can be used to scroll horizontally.
* A middle click usually registers both a middle click (2) and a
* horizontal scroll (11 or 12).
* We catch horizontal scrolls and ignore them to prevent middle clicks from
* accidentally being interpreted as first button clicks.
*/
case 11:
return TRUE;
case 12:
return TRUE;
}
/* We propagate the event, since it may otherwise be stuck in WebKit */
g_signal_emit_by_name (view, "event", event, &background);
return FALSE;
}
static gboolean
gtk_widget_key_press_event_cb (WebKitWebView* web_view,
GdkEventKey* event,
MidoriView* view)
{
guint character;
gint digit = g_ascii_digit_value (event->keyval);
event->state = event->state & MIDORI_KEYS_MODIFIER_MASK;
/* Handle oddities in Russian keyboard layouts */
if (event->hardware_keycode == ';')
event->keyval = ',';
else if (event->hardware_keycode == '<')
event->keyval = '.';
/* Find links by number: . to show links, type number, Return to go */
if (event->keyval == '.'
|| (view->find_links > -1
&& (digit != -1 || event->keyval == GDK_Return || event->keyval == GDK_Escape)))
{
WebKitWebFrame* web_frame = webkit_web_view_get_main_frame (web_view);
JSContextRef js_context = webkit_web_frame_get_global_context (web_frame);
gchar* result;
if (view->find_links == -1)
{
result = sokoke_js_script_eval (js_context,
" var style_func = (function (selector, rule) { "
" var style = document.createElement ('style');"
" style.setAttribute ('type', 'text/css');"
" var heads = document.getElementsByTagName ('head');"
" heads[0].appendChild (style);"
" document.styleSheets[0].insertRule (selector + ' ' + rule);"
" } );"
" style_func ('.midoriHKD87346', '{ "
" font-size:small !important; font-weight:bold !important;"
" z-index:500; border-radius:0.3em; line-height:1 !important;"
" background: white !important; color: black !important;"
" border:1px solid gray; padding:0 0.1em !important;"
" position:absolute; display:inline !important; }');"
" style_func ('.midori_access_key_fc04de', '{ "
" font-size:small !important; font-weight:bold !important;"
" z-index:500; border-radius:0.3em; line-height:1 !important;"
" background: black !important; color: white !important;"
" border:1px solid gray; padding:0 0.1em 0.2em 0.1em !important;"
" position:absolute; display:inline !important; }');"
" var label_count = 0;"
" for (i in document.links) {"
" if (document.links[i].href && document.links[i].insertBefore) {"
" var child = document.createElement ('span');"
" if (document.links[i].accessKey && isNaN (document.links[i].accessKey)) {"
" child.setAttribute ('class', 'midori_access_key_fc04de');"
" child.appendChild (document.createTextNode (document.links[i].accessKey));"
" } else {"
" child.setAttribute ('class', 'midoriHKD87346');"
" child.appendChild (document.createTextNode (label_count));"
" label_count++;"
" }"
" document.links[i].insertBefore (child); } }",
NULL);
view->find_links = 0;
}
else if (digit != -1 && event->keyval != GDK_Return && event->keyval != GDK_Escape)
{
if (view->find_links > -1)
view->find_links *= 10;
view->find_links += digit;
}
else if (event->keyval == GDK_Escape)
{
view->find_links = 0;
}
else if (event->keyval == GDK_Return)
{
gchar* script;
script = g_strdup_printf (
"var links = document.getElementsByClassName ('midoriHKD87346');"
"var i = %d; var return_key = %d;"
"if (return_key) {"
" if (typeof links[i] != 'undefined')"
" links[i].parentNode.href; }",
view->find_links, event->keyval == GDK_Return);
result = sokoke_js_script_eval (js_context, script, NULL);
if (result && strstr (result, "://"))
{
if (MIDORI_MOD_NEW_TAB (event->state))
{
gboolean background = view->open_tabs_in_the_background;
if (MIDORI_MOD_BACKGROUND (event->state))
background = !background;
g_signal_emit (view, signals[NEW_TAB], 0, result, background);
}
else
midori_view_set_uri (view, result);
}
g_free (script);
g_free (result);
view->find_links = 0;
}
else
{
result = sokoke_js_script_eval (js_context,
"var links = document.getElementsByClassName ('midoriHKD87346');"
"for (var i = links.length - 1; i >= 0; i--) {"
" var parent = links[i].parentNode;"
" parent.removeChild(links[i]); }",
NULL);
g_free (result);
result = sokoke_js_script_eval (js_context,
"var links = document.getElementsByClassName ('midori_access_key_fc04de');"
"if (links != undefined && links.length > 0) {"
" for (var i = links.length - 1; i >= 0; i--) {"
" var parent = links[i].parentNode;"
" parent.removeChild(links[i]); } }",
NULL);
g_free (result);
view->find_links = -1;
}
return FALSE;
}
/* Find inline */
if (event->keyval == ',' || event->keyval == '/' || event->keyval == GDK_KP_Divide)
character = '\0';
else
return FALSE;
/* Skip control characters */
if (character == (event->keyval | 0x01000000))
return FALSE;
if (!webkit_web_view_can_cut_clipboard (web_view)
&& !webkit_web_view_can_paste_clipboard (web_view))
{
gchar* text = character ? g_strdup_printf ("%c", character) : g_strdup ("");
g_signal_emit (view, signals[SEARCH_TEXT], 0, TRUE, text);
g_free (text);
return TRUE;
}
return FALSE;
}
static gboolean
gtk_widget_scroll_event_cb (WebKitWebView* web_view,
GdkEventScroll* event,
MidoriView* view)
{
event->state = event->state & MIDORI_KEYS_MODIFIER_MASK;
if (MIDORI_MOD_SCROLL (event->state))
{
if (event->direction == GDK_SCROLL_DOWN)
midori_view_set_zoom_level (view,
midori_view_get_zoom_level (view) - 0.25f);
else if(event->direction == GDK_SCROLL_UP)
midori_view_set_zoom_level (view,
midori_view_get_zoom_level (view) + 0.25f);
return TRUE;
}
else
return FALSE;
}
#if WEBKIT_CHECK_VERSION (1, 1, 15)
static void
midori_web_view_menu_new_window_activate_cb (GtkWidget* widget,
MidoriView* view)
{
g_signal_emit (view, signals[NEW_WINDOW], 0, view->link_uri);
}
static void
midori_web_view_menu_web_app_activate_cb (GtkWidget* widget,
MidoriView* view)
{
sokoke_spawn_app (view->link_uri, FALSE);
}
static void
midori_web_view_menu_link_copy_activate_cb (GtkWidget* widget,
MidoriView* view)
{
if (g_str_has_prefix (view->link_uri, "mailto:"))
sokoke_widget_copy_clipboard (widget, view->link_uri + 7);
else
sokoke_widget_copy_clipboard (widget, view->link_uri);
}
static void
midori_web_view_menu_save_activate_cb (GtkWidget* widget,
MidoriView* view)
{
WebKitNetworkRequest* request = webkit_network_request_new (view->link_uri);
WebKitDownload* download = webkit_download_new (request);
gboolean handled;
g_object_unref (request);
g_object_set_data (G_OBJECT (download), "save-as-download", (void*)0xdeadbeef);
g_signal_emit (view, signals[DOWNLOAD_REQUESTED], 0, download, &handled);
}
static void
midori_web_view_menu_image_new_tab_activate_cb (GtkWidget* widget,
MidoriView* view)
{
gchar* uri = katze_object_get_string (view->hit_test, "image-uri");
g_signal_emit (view, signals[NEW_TAB], 0, uri,
view->open_tabs_in_the_background);
g_free (uri);
}
static void
midori_web_view_menu_image_copy_activate_cb (GtkWidget* widget,
MidoriView* view)
{
gchar* uri = katze_object_get_string (view->hit_test, "image-uri");
sokoke_widget_copy_clipboard (widget, uri);
g_free (uri);
}
static void
midori_web_view_menu_image_save_activate_cb (GtkWidget* widget,
MidoriView* view)
{
gchar* uri = katze_object_get_string (view->hit_test, "image-uri");
WebKitNetworkRequest* request = webkit_network_request_new (uri);
WebKitDownload* download = webkit_download_new (request);
gboolean handled;
g_object_unref (request);
g_object_set_data (G_OBJECT (download), "save-as-download", (void*)0xdeadbeef);
g_signal_emit (view, signals[DOWNLOAD_REQUESTED], 0, download, &handled);
g_free (uri);
}
static void
midori_web_view_menu_video_copy_activate_cb (GtkWidget* widget,
MidoriView* view)
{
gchar* uri = katze_object_get_string (view->hit_test, "media-uri");
sokoke_widget_copy_clipboard (widget, uri);
g_free (uri);
}
static void
midori_web_view_menu_video_save_activate_cb (GtkWidget* widget,
MidoriView* view)
{
gchar* uri = katze_object_get_string (view->hit_test, "media-uri");
WebKitNetworkRequest* request = webkit_network_request_new (uri);
WebKitDownload* download = webkit_download_new (request);
gboolean handled;
g_object_unref (request);
g_object_set_data (G_OBJECT (download), "save-as-download", (void*)0xdeadbeef);
g_signal_emit (view, signals[DOWNLOAD_REQUESTED], 0, download, &handled);
g_free (uri);
}
#endif
static void
midori_web_view_menu_new_tab_activate_cb (GtkWidget* widget,
MidoriView* view)
{
if (view->link_uri)
g_signal_emit (view, signals[NEW_TAB], 0, view->link_uri,
view->open_tabs_in_the_background);
else
{
gchar* data = (gchar*)g_object_get_data (G_OBJECT (widget), "uri");
if (strchr (data, '@'))
{
gchar* uri = g_strconcat ("mailto:", data, NULL);
sokoke_show_uri (gtk_widget_get_screen (widget),
uri, GDK_CURRENT_TIME, NULL);
g_free (uri);
}
else
{
gchar* uri = sokoke_magic_uri (data);
if (!uri)
uri = g_strdup (data);
g_signal_emit (view, signals[NEW_TAB], 0, uri,
view->open_tabs_in_the_background);
g_free (uri);
}
}
}
#if WEBKIT_CHECK_VERSION (1, 1, 15)
static void
midori_web_view_menu_background_tab_activate_cb (GtkWidget* widget,
MidoriView* view)
{
g_signal_emit (view, signals[NEW_TAB], 0, view->link_uri,
!view->open_tabs_in_the_background);
}
#endif
static void
midori_web_view_menu_search_web_activate_cb (GtkWidget* widget,
MidoriView* view)
{
gchar* search;
gchar* uri;
if ((search = g_object_get_data (G_OBJECT (widget), "search")))
search = g_strdup (search);
else
g_object_get (view->settings, "location-entry-search",
&search, NULL);
uri = sokoke_search_uri (search, view->selected_text);
g_free (search);
g_signal_emit (view, signals[NEW_TAB], 0, uri,
view->open_tabs_in_the_background);
g_free (uri);
}
#if WEBKIT_CHECK_VERSION (1, 1, 15)
static void
midori_web_view_menu_copy_activate_cb (GtkWidget* widget,
MidoriView* view)
{
sokoke_widget_copy_clipboard (widget, view->selected_text);
}
#endif
#if !WEBKIT_CHECK_VERSION (1, 1, 3)
static void
midori_web_view_menu_save_as_activate_cb (GtkWidget* widget,
MidoriView* view)
{
g_signal_emit (view, signals[SAVE_AS], 0, view->link_uri);
}
#endif
static void
midori_view_tab_label_menu_window_new_cb (GtkWidget* menuitem,
GtkWidget* view)
{
g_signal_emit (view, signals[NEW_WINDOW], 0,
midori_view_get_display_uri (MIDORI_VIEW (view)));
}
#if WEBKIT_CHECK_VERSION (1, 1, 17)
static void
midori_web_view_menu_inspect_element_activate_cb (GtkWidget* widget,
MidoriView* view)
{
WebKitWebInspector* inspector;
gint x, y;
inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (view->web_view));
x = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "x"));
y = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "y"));
webkit_web_inspector_inspect_coordinates (inspector, x, y);
webkit_web_inspector_show (inspector);
}
#endif
static GtkWidget*
midori_view_insert_menu_item (GtkMenuShell* menu,
gint position,
const gchar* label,
const gchar* stock_id,
GCallback callback,
GtkWidget* widget)
{
GtkWidget* menuitem;
if (label)
{
menuitem = gtk_image_menu_item_new_with_mnemonic (label);
if (stock_id)
{
GdkScreen* screen = gtk_widget_get_screen (widget);
GtkIconTheme* icon_theme = gtk_icon_theme_get_for_screen (screen);
if (gtk_icon_theme_has_icon (icon_theme, stock_id))
{
GtkWidget* icon = gtk_image_new_from_stock (stock_id,
GTK_ICON_SIZE_MENU);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem),
icon);
}
}
}
else
menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, position);
if (callback)
g_signal_connect (menuitem, "activate", callback, widget);
else
gtk_widget_set_sensitive (menuitem, FALSE);
return menuitem;
}
/**
* midori_view_populate_popup:
* @view: a #MidoriView
* @menu: a #GtkMenu
* @manual: %TRUE if this a manually created popup
*
* Populates the given @menu with context menu items
* according to the position of the mouse pointer. This
* can be used in situations where a custom hotkey
* opens the context menu or the default behaviour
* needs to be intercepted.
*
* @manual should usually be %TRUE, except for the
* case where @menu was created by the #WebKitWebView.
*
* Since: 0.2.5
*/
void
midori_view_populate_popup (MidoriView* view,
GtkWidget* menu,
gboolean manual)
{
WebKitWebView* web_view = WEBKIT_WEB_VIEW (view->web_view);
GtkWidget* widget = GTK_WIDGET (view);
MidoriBrowser* browser = midori_browser_get_for_widget (widget);
GtkActionGroup* actions = midori_browser_get_action_group (browser);
GtkMenuShell* menu_shell = GTK_MENU_SHELL (menu);
GtkWidget* menuitem;
GtkWidget* icon;
gchar* stock_id;
GList* items;
gboolean has_selection;
gboolean is_editable;
gboolean is_document;
GtkWidget* label;
guint i;
#if WEBKIT_CHECK_VERSION (1, 1, 15)
gint x, y;
WebKitHitTestResultContext context;
gboolean is_image;
gboolean is_media;
midori_view_ensure_link_uri (view, &x, &y, NULL);
context = katze_object_get_int (view->hit_test, "context");
has_selection = context & WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION;
/* Ensure view->selected_text */
midori_view_has_selection (view);
is_editable = context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE;
is_image = context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE;
is_media = context & WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA;
is_document = !view->link_uri && !has_selection && !is_image && !is_media;
#else
/* There is no guarantee view->link_uri is correct in case
gtk-touchscreen-mode is enabled, nothing we can do. */
has_selection = midori_view_has_selection (view);
is_document = !view->link_uri && !has_selection;
/* Unfortunately inspecting the menu is the only way to
determine that the mouse is over a text area or selection. */
items = gtk_container_get_children (GTK_CONTAINER (menu));
menuitem = (GtkWidget*)g_list_nth_data (items, 0);
if (GTK_IS_IMAGE_MENU_ITEM (menuitem))
{
icon = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (menuitem));
gtk_image_get_stock (GTK_IMAGE (icon), &stock_id, NULL);
if (!strcmp (stock_id, GTK_STOCK_FIND))
{
gtk_widget_hide (menuitem);
gtk_widget_set_no_show_all (menuitem, TRUE);
menuitem = (GtkWidget*)g_list_nth_data (items, 1);
gtk_widget_hide (menuitem);
menuitem = (GtkWidget*)g_list_nth_data (items, 2);
icon = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (menuitem));
gtk_image_get_stock (GTK_IMAGE (icon), &stock_id, NULL);
}
is_editable = !strcmp (stock_id, GTK_STOCK_CUT);
if (is_document && !strcmp (stock_id, GTK_STOCK_OPEN))
is_document = FALSE;
}
else
is_editable = FALSE;
g_list_free (items);
#endif
if (is_editable)
{
#if WEBKIT_CHECK_VERSION (1, 1, 14)
menuitem = gtk_separator_menu_item_new ();
gtk_menu_shell_prepend (menu_shell, menuitem);
gtk_widget_show (menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Redo"));
gtk_widget_set_sensitive (menuitem,
webkit_web_view_can_redo (web_view));
gtk_menu_shell_prepend (menu_shell, menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Undo"));
gtk_widget_set_sensitive (menuitem,
webkit_web_view_can_undo (web_view));
gtk_menu_shell_prepend (menu_shell, menuitem);
#endif
if (manual)
{
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Cut"));
gtk_widget_set_sensitive (menuitem,
webkit_web_view_can_cut_clipboard (web_view));
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Copy"));
gtk_widget_set_sensitive (menuitem,
webkit_web_view_can_copy_clipboard (web_view));
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Paste"));
gtk_widget_set_sensitive (menuitem,
webkit_web_view_can_paste_clipboard (web_view));
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Delete"));
gtk_widget_set_sensitive (menuitem,
webkit_web_view_can_cut_clipboard (web_view));
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = gtk_separator_menu_item_new ();
gtk_widget_show (menuitem);
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "SelectAll"));
gtk_menu_shell_append (menu_shell, menuitem);
/* FIXME: We are missing Font, Input Methods and Insert Character */
#if WEBKIT_CHECK_VERSION (1, 1, 17)
if (katze_object_get_boolean (view->settings, "enable-developer-extras"))
{
menuitem = gtk_separator_menu_item_new ();
gtk_widget_show (menuitem);
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = midori_view_insert_menu_item (menu_shell, -1,
_("Inspect _Element"), NULL,
G_CALLBACK (midori_web_view_menu_inspect_element_activate_cb),
widget);
gtk_widget_show (menuitem);
g_object_set_data (G_OBJECT (menuitem), "x", GINT_TO_POINTER (x));
g_object_set_data (G_OBJECT (menuitem), "y", GINT_TO_POINTER (y));
}
#endif
}
return;
}
items = gtk_container_get_children (GTK_CONTAINER (menu));
menuitem = (GtkWidget*)g_list_nth_data (items, 0);
/* Form control: no items */
if (!manual && !menuitem)
{
g_list_free (items);
return;
}
/* Form control: separator and Inspect element */
if (!manual && GTK_IS_SEPARATOR_MENU_ITEM (menuitem) && g_list_length (items) == 2)
{
gtk_widget_destroy (menuitem);
g_list_free (items);
return;
}
g_list_free (items);
/* Link and/ or image, but falsely reported as document */
if (is_document)
{
if (GTK_IS_IMAGE_MENU_ITEM (menuitem))
{
icon = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (menuitem));
gtk_image_get_stock (GTK_IMAGE (icon), &stock_id, NULL);
if (stock_id && !strcmp (stock_id, GTK_STOCK_OPEN))
return;
}
}
#if WEBKIT_CHECK_VERSION (1, 1, 15)
if (!is_document)
{
items = gtk_container_get_children (GTK_CONTAINER (menu));
i = 0;
while ((menuitem = g_list_nth_data (items, i++)))
gtk_widget_destroy (menuitem);
g_list_free (items);
}
if (view->link_uri)
{
midori_view_insert_menu_item (menu_shell, -1,
_("Open Link in New _Tab"), STOCK_TAB_NEW,
G_CALLBACK (midori_web_view_menu_new_tab_activate_cb), widget);
midori_view_insert_menu_item (menu_shell, -1,
view->open_tabs_in_the_background
? _("Open Link in _Foreground Tab")
: _("Open Link in _Background Tab"), NULL,
G_CALLBACK (midori_web_view_menu_background_tab_activate_cb), widget);
midori_view_insert_menu_item (menu_shell, -1,
_("Open Link in New _Window"), STOCK_WINDOW_NEW,
G_CALLBACK (midori_web_view_menu_new_window_activate_cb), widget);
midori_view_insert_menu_item (menu_shell, -1,
_("Open Link as Web A_pplication"), NULL,
G_CALLBACK (midori_web_view_menu_web_app_activate_cb), widget);
midori_view_insert_menu_item (menu_shell, -1,
_("Copy Link de_stination"), NULL,
G_CALLBACK (midori_web_view_menu_link_copy_activate_cb), widget);
midori_view_insert_menu_item (menu_shell, -1,
NULL, GTK_STOCK_SAVE_AS,
G_CALLBACK (midori_web_view_menu_save_activate_cb), widget);
}
if (is_image)
{
if (view->link_uri)
gtk_menu_shell_append (menu_shell, gtk_separator_menu_item_new ());
midori_view_insert_menu_item (menu_shell, -1,
_("Open _Image in New Tab"), STOCK_TAB_NEW,
G_CALLBACK (midori_web_view_menu_image_new_tab_activate_cb), widget);
midori_view_insert_menu_item (menu_shell, -1,
_("Copy Image _Address"), NULL,
G_CALLBACK (midori_web_view_menu_image_copy_activate_cb), widget);
midori_view_insert_menu_item (menu_shell, -1,
_("Save I_mage"), GTK_STOCK_SAVE,
G_CALLBACK (midori_web_view_menu_image_save_activate_cb), widget);
}
if (is_media)
{
midori_view_insert_menu_item (menu_shell, -1,
_("Copy Video _Address"), NULL,
G_CALLBACK (midori_web_view_menu_video_copy_activate_cb), widget);
midori_view_insert_menu_item (menu_shell, -1,
FALSE ? _("Save _Video") : _("Download _Video"), GTK_STOCK_SAVE,
G_CALLBACK (midori_web_view_menu_video_save_activate_cb), widget);
}
if (has_selection)
{
gtk_menu_shell_append (menu_shell, gtk_separator_menu_item_new ());
midori_view_insert_menu_item (menu_shell, -1, NULL, GTK_STOCK_COPY,
G_CALLBACK (midori_web_view_menu_copy_activate_cb), widget);
}
#else
if (view->link_uri)
{
items = gtk_container_get_children (GTK_CONTAINER (menu));
menuitem = (GtkWidget*)g_list_nth_data (items, 0);
/* hack to hide menu item */
gtk_widget_hide (menuitem);
midori_view_insert_menu_item (menu_shell, 1,
_("Open Link in New _Tab"), STOCK_TAB_NEW,
G_CALLBACK (midori_web_view_menu_new_tab_activate_cb), widget);
g_list_free (items);
items = gtk_container_get_children (GTK_CONTAINER (menu));
menuitem = (GtkWidget*)g_list_nth_data (items, 2);
/* hack to localize menu item */
label = gtk_bin_get_child (GTK_BIN (menuitem));
gtk_label_set_label (GTK_LABEL (label), _("Open Link in New _Window"));
menuitem = (GtkWidget*)g_list_nth_data (items, 3);
g_list_free (items);
#if WEBKIT_CHECK_VERSION (1, 1, 3)
/* hack to localize menu item */
label = gtk_bin_get_child (GTK_BIN (menuitem));
GtkStockItem stock_item;
if (gtk_stock_lookup (GTK_STOCK_SAVE_AS, &stock_item))
gtk_label_set_label (GTK_LABEL (label), stock_item.label);
#else
/* hack to disable non-functional Download File */
gtk_widget_hide (menuitem);
gtk_widget_set_no_show_all (menuitem, TRUE);
midori_view_insert_menu_item (menu_shell, 3,
NULL, GTK_STOCK_SAVE_AS,
G_CALLBACK (midori_web_view_menu_save_as_activate_cb), widget);
#endif
}
#endif
if (!view->link_uri && has_selection)
{
GtkWidget* window;
KatzeArray* search_engines = NULL;
window = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
i = 0;
if (katze_object_has_property (window, "search-engines"))
search_engines = katze_object_get_object (window, "search-engines");
if (search_engines != NULL)
{
KatzeItem* item;
GtkWidget* sub_menu = gtk_menu_new ();
menuitem = gtk_image_menu_item_new_with_mnemonic (_("Search _with"));
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), sub_menu);
gtk_menu_shell_insert (menu_shell, menuitem, 1);
KATZE_ARRAY_FOREACH_ITEM (item, search_engines)
{
GdkPixbuf* pixbuf;
const gchar* icon_name;
menuitem = gtk_image_menu_item_new_with_mnemonic (katze_item_get_name (item));
pixbuf = midori_search_action_get_icon (item,
GTK_WIDGET (web_view), &icon_name);
if (pixbuf)
{
icon = gtk_image_new_from_pixbuf (pixbuf);
g_object_unref (pixbuf);
}
else
icon = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), icon);
#if GTK_CHECK_VERSION (2, 16, 0)
gtk_image_menu_item_set_always_show_image (
GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
#endif
gtk_menu_shell_insert (GTK_MENU_SHELL (sub_menu), menuitem, i);
g_object_set_data (G_OBJECT (menuitem), "search",
(gchar*)katze_item_get_uri (item));
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_web_view_menu_search_web_activate_cb), view);
i++;
}
g_object_unref (search_engines);
}
#if WEBKIT_CHECK_VERSION (1, 1, 15)
midori_view_insert_menu_item (menu_shell, 0,
_("_Search the Web"), GTK_STOCK_FIND,
G_CALLBACK (midori_web_view_menu_search_web_activate_cb), widget);
#else
items = gtk_container_get_children (GTK_CONTAINER (menu));
menuitem = (GtkWidget*)g_list_nth_data (items, 0);
/* hack to localize menu item */
label = gtk_bin_get_child (GTK_BIN (menuitem));
gtk_label_set_label (GTK_LABEL (label), _("_Search the Web"));
/* hack to implement Search the Web */
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_web_view_menu_search_web_activate_cb), view);
g_list_free (items);
#endif
g_strstrip (view->selected_text);
if (view->selected_text && !strchr (view->selected_text, ' ')
&& (strchr (view->selected_text, '.') || g_strstr_len (view->selected_text, 9, "://")))
{
if (strchr (view->selected_text, '@'))
{
gchar* text = g_strdup_printf (_("Send a message to %s"), view->selected_text);
menuitem = midori_view_insert_menu_item (menu_shell, -1,
text, GTK_STOCK_JUMP_TO,
G_CALLBACK (midori_web_view_menu_new_tab_activate_cb), widget);
g_free (text);
}
else
menuitem = midori_view_insert_menu_item (menu_shell, -1,
_("Open Address in New _Tab"), GTK_STOCK_JUMP_TO,
G_CALLBACK (midori_web_view_menu_new_tab_activate_cb), widget);
g_object_set_data (G_OBJECT (menuitem), "uri", view->selected_text);
}
}
if (is_document)
{
if (manual)
{
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Back"));
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Forward"));
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Stop"));
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Reload"));
gtk_menu_shell_append (menu_shell, menuitem);
}
else
{
items = gtk_container_get_children (GTK_CONTAINER (menu));
#if HAVE_HILDON
gtk_widget_hide (g_list_nth_data (items, 2));
gtk_widget_set_no_show_all (g_list_nth_data (items, 2), TRUE);
gtk_widget_hide (g_list_nth_data (items, 3));
gtk_widget_set_no_show_all (g_list_nth_data (items, 3), TRUE);
#endif
menuitem = (GtkWidget*)g_list_nth_data (items, 3);
/* hack to localize menu item */
if (GTK_IS_BIN (menuitem))
{
GtkStockItem stock_item;
if (gtk_stock_lookup (GTK_STOCK_REFRESH, &stock_item))
{
label = gtk_bin_get_child (GTK_BIN (menuitem));
gtk_label_set_label (GTK_LABEL (label), stock_item.label);
}
}
g_list_free (items);
}
gtk_menu_shell_append (menu_shell, gtk_separator_menu_item_new ());
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "UndoTabClose"));
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = gtk_image_menu_item_new_from_stock (STOCK_WINDOW_NEW, NULL);
gtk_menu_item_set_label (GTK_MENU_ITEM (menuitem), _("Open in New _Window"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_view_tab_label_menu_window_new_cb), view);
#if WEBKIT_CHECK_VERSION (1, 1, 15)
/* if (webkit_web_view_get_main_frame (web_view) != frame_under_mouse)
{
midori_view_insert_menu_item (menu_shell, -1,
_("Open _Frame in New Tab"), NULL,
G_CALLBACK (midori_web_view_menu_frame_new_tab_activate_cb), widget);
midori_view_insert_menu_item (menu_shell, -1,
_("Open _Frame in New Window"), NULL,
G_CALLBACK (midori_web_view_menu_frame_new_window_activate_cb), widget);
} */
#endif
#if !HAVE_HILDON
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "ZoomIn"));
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "ZoomOut"));
gtk_menu_shell_append (menu_shell, menuitem);
#endif
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Encoding"));
gtk_menu_shell_append (menu_shell, menuitem);
if (gtk_widget_get_sensitive (menuitem))
{
GtkWidget* sub_menu;
static const GtkActionEntry encodings[] = {
{ "EncodingAutomatic" },
{ "EncodingChinese" },
{ "EncodingJapanese" },
{ "EncodingKorean" },
{ "EncodingRussian" },
{ "EncodingUnicode" },
{ "EncodingWestern" },
{ "EncodingCustom" },
};
sub_menu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), sub_menu);
for (i = 0; i < G_N_ELEMENTS (encodings); i++)
{
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, encodings[i].name));
gtk_menu_shell_append (GTK_MENU_SHELL (sub_menu), menuitem);
}
}
#if HAVE_HILDON
gtk_menu_shell_append (menu_shell, gtk_separator_menu_item_new ());
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "CompactAdd"));
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Fullscreen"));
gtk_menu_shell_append (menu_shell, menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "PrivateBrowsing"));
gtk_menu_shell_append (menu_shell, menuitem);
#else
gtk_menu_shell_append (menu_shell, gtk_separator_menu_item_new ());
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "BookmarkAdd"));
gtk_menu_shell_append (menu_shell, menuitem);
if (!midori_view_is_blank (view)
&& strcmp ("/", sokoke_set_config_dir (NULL)))
{
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "AddSpeedDial"));
gtk_menu_shell_append (menu_shell, menuitem);
}
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "AddDesktopShortcut"));
gtk_menu_shell_append (menu_shell, menuitem);
#endif
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "SaveAs"));
gtk_menu_shell_append (menu_shell, menuitem);
/* Currently views that don't support source, don't support
saving either. If that changes, we need to think of something. */
if (!midori_view_can_view_source (view))
gtk_widget_set_sensitive (menuitem, FALSE);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "SourceView"));
gtk_menu_shell_append (menu_shell, menuitem);
if (!g_object_get_data (G_OBJECT (browser), "midori-toolbars-visible"))
{
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Navigationbar"));
gtk_menu_shell_append (menu_shell, menuitem);
}
}
#if WEBKIT_CHECK_VERSION (1, 1, 17)
if ((!is_document || manual)
&& katze_object_get_boolean (view->settings, "enable-developer-extras"))
{
gtk_menu_shell_append (menu_shell, gtk_separator_menu_item_new ());
menuitem = midori_view_insert_menu_item (menu_shell, -1,
_("Inspect _Element"), NULL,
G_CALLBACK (midori_web_view_menu_inspect_element_activate_cb), widget);
g_object_set_data (G_OBJECT (menuitem), "x", GINT_TO_POINTER (x));
g_object_set_data (G_OBJECT (menuitem), "y", GINT_TO_POINTER (y));
}
#endif
gtk_widget_show_all (menu);
}
static void
webkit_web_view_populate_popup_cb (WebKitWebView* web_view,
GtkWidget* menu,
MidoriView* view)
{
midori_view_populate_popup (view, menu, FALSE);
}
#if HAVE_HILDON
static void
midori_view_web_view_tap_and_hold_cb (GtkWidget* web_view,
gpointer data)
{
gint x, y;
GdkEvent event;
gboolean result;
/* Emulate a pointer motion above the tap position
and a right click at the according position. */
event.any.window = gtk_widget_get_window (web_view);
gdk_window_get_pointer (event.any.window, &x, &y, NULL);
event.any.type = GDK_MOTION_NOTIFY;
event.motion.x = x;
event.motion.y = y;
g_signal_emit_by_name (web_view, "motion-notify-event", &event, &result);
event.any.type = GDK_BUTTON_PRESS;
event.button.axes = NULL;
event.button.x = x;
event.button.y = y;
event.button.button = 3;
g_signal_emit_by_name (web_view, "button-press-event", &event, &result);
}
#endif
static gboolean
webkit_web_view_web_view_ready_cb (GtkWidget* web_view,
MidoriView* view)
{
GtkWidget* new_view = gtk_widget_get_parent (gtk_widget_get_parent (web_view));
MidoriNewView where = MIDORI_NEW_VIEW_TAB;
/* FIXME: Open windows opened by scripts in tabs if they otherwise
would be replacing the page the user opened. */
if (view->open_new_pages_in == MIDORI_NEW_PAGE_CURRENT)
return TRUE;
if (view->open_new_pages_in == MIDORI_NEW_PAGE_TAB)
{
if (view->open_tabs_in_the_background)
where = MIDORI_NEW_VIEW_BACKGROUND;
}
else if (view->open_new_pages_in == MIDORI_NEW_PAGE_WINDOW)
where = MIDORI_NEW_VIEW_WINDOW;
gtk_widget_show (new_view);
g_signal_emit (view, signals[NEW_VIEW], 0, new_view, where);
return TRUE;
}
static GtkWidget*
webkit_web_view_create_web_view_cb (GtkWidget* web_view,
WebKitWebFrame* web_frame,
MidoriView* view)
{
MidoriView* new_view;
if (view->open_new_pages_in == MIDORI_NEW_PAGE_CURRENT)
new_view = view;
else
{
new_view = (MidoriView*)midori_view_new_with_title (NULL,
view->settings, FALSE);
g_signal_connect (new_view->web_view, "web-view-ready",
G_CALLBACK (webkit_web_view_web_view_ready_cb), view);
}
return new_view->web_view;
}
static gboolean
webkit_web_view_mime_type_decision_cb (GtkWidget* web_view,
WebKitWebFrame* web_frame,
WebKitNetworkRequest* request,
const gchar* mime_type,
WebKitWebPolicyDecision* decision,
MidoriView* view)
{
#if WEBKIT_CHECK_VERSION (1, 1, 3)
GtkWidget* dialog;
gchar* content_type;
gchar* description;
gchar* file_type;
#if WEBKIT_CHECK_VERSION (1, 1, 14)
WebKitWebDataSource* datasource;
WebKitNetworkRequest* original_request;
#endif
const gchar* original_uri;
gchar** fingerprint;
#if GTK_CHECK_VERSION (2, 14, 0)
GIcon* icon;
GtkWidget* image;
#endif
gchar* title;
GdkScreen* screen;
GtkIconTheme* icon_theme;
gint response;
#else
gchar* uri;
#endif
if (web_frame != webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view)))
return FALSE;
if (webkit_web_view_can_show_mime_type (WEBKIT_WEB_VIEW (web_view), mime_type))
{
#if WEBKIT_CHECK_VERSION (1, 1, 14)
gboolean view_source = FALSE;
/* Dedicated source code views are always pseudo-blank pages */
if (midori_view_is_blank (view))
view_source = webkit_web_view_get_view_source_mode (WEBKIT_WEB_VIEW (web_view));
/* Render raw XML, including news feeds, as source */
if (!view_source && (!strcmp (mime_type, "application/xml")
|| !strcmp (mime_type, "text/xml")))
view_source = TRUE;
webkit_web_view_set_view_source_mode (WEBKIT_WEB_VIEW (web_view), view_source);
#endif
katze_assign (view->mime_type, g_strdup (mime_type));
midori_view_update_icon (view, NULL);
g_object_notify (G_OBJECT (view), "mime-type");
return FALSE;
}
#if WEBKIT_CHECK_VERSION (1, 1, 3)
dialog = gtk_message_dialog_new (
NULL, 0, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
_("Open or download file"));
content_type = g_content_type_from_mime_type (mime_type);
if (!content_type)
#ifdef G_OS_WIN32
content_type = g_content_type_get_mime_type ("*");
#else
content_type = g_strdup ("application/octet-stream");
#endif
description = g_content_type_get_description (content_type);
#if GTK_CHECK_VERSION (2, 14, 0)
icon = g_content_type_get_icon (content_type);
g_themed_icon_append_name (G_THEMED_ICON (icon), "document-x-generic");
image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
g_object_unref (icon);
gtk_widget_show (image);
gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image);
#endif
g_free (content_type);
if (g_strrstr (description, mime_type))
file_type = g_strdup_printf (_("File Type: '%s'"), mime_type);
else
file_type = g_strdup_printf (_("File Type: %s ('%s')"), description, mime_type);
g_free (description);
/* Link Fingerprint */
#if WEBKIT_CHECK_VERSION (1, 1, 14)
/* We look at the original URI because redirection would lose the fragment */
datasource = webkit_web_frame_get_provisional_data_source (web_frame);
original_request = webkit_web_data_source_get_initial_request (datasource);
original_uri = webkit_network_request_get_uri (original_request);
#else
original_uri = webkit_network_request_get_uri (request);
#endif
fingerprint = g_strsplit (original_uri, "#!md5!", 2);
if (fingerprint && fingerprint[0] && fingerprint[1])
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
"%s\n%s %s", file_type, _("MD5-Checksum:"), fingerprint[1]);
else
{
g_strfreev (fingerprint);
fingerprint = g_strsplit (original_uri, "#!sha1!", 2);
if (fingerprint && fingerprint[0] && fingerprint[1])
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
"%s\n%s %s", file_type, _("SHA1-Checksum:"), fingerprint[1]);
else
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
"%s", file_type);
}
g_strfreev (fingerprint);
g_free (file_type);
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), FALSE);
/* i18n: A file open dialog title, ie. "Open http://fila.com/manual.tgz" */
title = g_strdup_printf (_("Open %s"),
webkit_network_request_get_uri (request));
gtk_window_set_title (GTK_WINDOW (dialog), title);
g_free (title);
screen = gtk_widget_get_screen (dialog);
if (screen)
{
icon_theme = gtk_icon_theme_get_for_screen (screen);
if (gtk_icon_theme_has_icon (icon_theme, STOCK_TRANSFER))
gtk_window_set_icon_name (GTK_WINDOW (dialog), STOCK_TRANSFER);
else
gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_OPEN);
}
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
GTK_STOCK_SAVE, 1,
GTK_STOCK_SAVE_AS, 4,
GTK_STOCK_CANCEL, 2,
GTK_STOCK_OPEN, 3,
NULL);
response = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
g_object_set_data (G_OBJECT (view), "open-download", (gpointer)0);
switch (response)
{
case 4:
g_object_set_data (G_OBJECT (view), "save-as-download", (gpointer)1);
webkit_web_policy_decision_download (decision);
webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (view->web_view));
return TRUE;
case 3:
g_object_set_data (G_OBJECT (view), "open-download", (gpointer)1);
case 1:
webkit_web_policy_decision_download (decision);
/* Apparently WebKit will continue loading which ends in an error.
It's unclear whether it's a bug or we are doing something wrong. */
webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (view->web_view));
return TRUE;
case 2:
default:
/* Apparently WebKit will continue loading which ends in an error.
It's unclear whether it's a bug or we are doing something wrong. */
webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (view->web_view));
return FALSE;
}
#else
katze_assign (view->mime_type, NULL);
midori_view_update_icon (view, NULL);
g_object_notify (G_OBJECT (view), "mime-type");
uri = g_strdup_printf ("error:nodisplay %s",
webkit_network_request_get_uri (request));
midori_view_set_uri (view, uri);
g_free (uri);
return TRUE;
#endif
}
#if WEBKIT_CHECK_VERSION (1, 1, 3)
static gboolean
webkit_web_view_download_requested_cb (GtkWidget* web_view,
WebKitDownload* download,
MidoriView* view)
{
gboolean handled;
#if WEBKIT_CHECK_VERSION (1, 1, 14)
/* Propagate original URI to make it available when the download finishes */
WebKitWebFrame* web_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
WebKitWebDataSource* datasource = webkit_web_frame_get_provisional_data_source (web_frame);
WebKitNetworkRequest* original_request = webkit_web_data_source_get_initial_request (datasource);
const gchar* original_uri = webkit_network_request_get_uri (original_request);
WebKitNetworkRequest* request = webkit_download_get_network_request (download);
g_object_set_data_full (G_OBJECT (request), "midori-original-uri",
g_strdup (original_uri), g_free);
#endif
g_object_set_data (G_OBJECT (download), "open-download",
g_object_get_data (G_OBJECT (view), "open-download"));
g_object_set_data (G_OBJECT (download), "save-as-download",
g_object_get_data (G_OBJECT (view), "save-as-download"));
g_object_set_data (G_OBJECT (view), "open-download", (gpointer)0);
g_object_set_data (G_OBJECT (view), "save-as-download", (gpointer)0);
g_signal_emit (view, signals[DOWNLOAD_REQUESTED], 0, download, &handled);
return handled;
}
#endif
static gboolean
webkit_web_view_console_message_cb (GtkWidget* web_view,
const gchar* message,
guint line,
const gchar* source_id,
MidoriView* view)
{
if (!strncmp (message, "speed_dial-save", 13))
midori_view_speed_dial_save (view, message);
else
g_signal_emit (view, signals[CONSOLE_MESSAGE], 0, message, line, source_id);
return TRUE;
}
static void
midori_view_script_response_cb (GtkWidget* infobar,
gint response,
MidoriView* view)
{
view->alerts--;
}
static gboolean
midori_view_web_view_script_alert_cb (GtkWidget* web_view,
WebKitWebFrame* web_frame,
const gchar* message,
MidoriView* view)
{
gchar* text;
/* Allow a maximum of 5 alerts */
if (view->alerts > 4)
return TRUE;
view->alerts++;
/* i18n: The text of an infobar for JavaScript alert messages */
text = g_strdup_printf ("JavaScript: %s", message);
midori_view_add_info_bar (view, GTK_MESSAGE_WARNING, text,
G_CALLBACK (midori_view_script_response_cb), view,
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
g_free (text);
return TRUE;
}
#if WEBKIT_CHECK_VERSION (1, 1, 5)
static gboolean
midori_view_web_view_print_requested_cb (GtkWidget* web_view,
WebKitWebFrame* web_frame,
MidoriView* view)
{
midori_view_print (view);
return TRUE;
}
#endif
static void
webkit_web_view_window_object_cleared_cb (GtkWidget* web_view,
WebKitWebFrame* web_frame,
JSContextRef js_context,
JSObjectRef js_window,
MidoriView* view)
{
g_signal_emit (view, signals[CONTEXT_READY], 0, js_context);
}
static void
midori_view_hadjustment_notify_value_cb (GtkAdjustment* hadjustment,
GParamSpec* pspec,
MidoriView* view)
{
gint value = (gint)gtk_adjustment_get_value (hadjustment);
katze_item_set_meta_integer (view->item, "scrollh", value);
}
static void
midori_view_notify_hadjustment_cb (MidoriView* view,
GParamSpec* pspec,
gpointer data)
{
GtkScrolledWindow* scrolled = GTK_SCROLLED_WINDOW (view->scrolled_window);
GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment (scrolled);
g_signal_connect (hadjustment, "notify::value",
G_CALLBACK (midori_view_hadjustment_notify_value_cb), view);
}
static void
midori_view_vadjustment_notify_value_cb (GtkAdjustment* vadjustment,
GParamSpec* pspec,
MidoriView* view)
{
gint value = (gint)gtk_adjustment_get_value (vadjustment);
katze_item_set_meta_integer (view->item, "scrollv", value);
}
static void
midori_view_notify_vadjustment_cb (MidoriView* view,
GParamSpec* pspec,
gpointer data)
{
GtkScrolledWindow* scrolled = GTK_SCROLLED_WINDOW (view->scrolled_window);
GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment (scrolled);
g_signal_connect (vadjustment, "notify::value",
G_CALLBACK (midori_view_vadjustment_notify_value_cb), view);
}
static void
katze_net_object_maybe_unref (gpointer object)
{
if (object)
g_object_unref (object);
}
static GHashTable* midori_view_get_memory (void)
{
static GHashTable* memory = NULL;
if (!memory)
memory = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, katze_net_object_maybe_unref);
return g_hash_table_ref (memory);
}
static void
midori_view_init (MidoriView* view)
{
view->uri = NULL;
view->title = NULL;
view->security = MIDORI_SECURITY_NONE;
view->mime_type = g_strdup ("");
view->icon = NULL;
view->icon_uri = NULL;
view->memory = midori_view_get_memory ();
view->progress = 0.0;
view->load_status = MIDORI_LOAD_FINISHED;
view->minimized = FALSE;
view->statusbar_text = NULL;
#if WEBKIT_CHECK_VERSION (1, 1, 15)
view->hit_test = NULL;
#endif
view->link_uri = NULL;
view->selected_text = NULL;
view->news_feeds = katze_array_new (KATZE_TYPE_ITEM);
view->find_links = -1;
view->alerts = 0;
view->item = katze_item_new ();
view->scrollh = view->scrollv = -2;
view->back_forward_set = FALSE;
view->news_aggregator = NULL;
view->web_view = NULL;
/* Adjustments are not created initially, but overwritten later */
view->scrolled_window = katze_scrolled_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (view->scrolled_window),
GTK_SHADOW_NONE);
gtk_container_add (GTK_CONTAINER (view), view->scrolled_window);
g_signal_connect (view->item, "meta-data-changed",
G_CALLBACK (midori_view_item_meta_data_changed), view);
g_signal_connect (view->scrolled_window, "notify::hadjustment",
G_CALLBACK (midori_view_notify_hadjustment_cb), view);
g_signal_connect (view->scrolled_window, "notify::vadjustment",
G_CALLBACK (midori_view_notify_vadjustment_cb), view);
midori_view_construct_web_view (view);
}
static void
midori_view_finalize (GObject* object)
{
MidoriView* view;
view = MIDORI_VIEW (object);
if (view->settings)
g_signal_handlers_disconnect_by_func (view->settings,
midori_view_settings_notify_cb, view);
g_signal_handlers_disconnect_by_func (view->item,
midori_view_item_meta_data_changed, view);
if (view->thumb_view)
{
gtk_widget_destroy (view->thumb_view);
view->thumb_view = NULL;
}
katze_assign (view->uri, NULL);
katze_assign (view->title, NULL);
katze_object_assign (view->icon, NULL);
katze_assign (view->icon_uri, NULL);
if (view->memory)
{
g_hash_table_unref (view->memory);
view->memory = NULL;
}
katze_assign (view->statusbar_text, NULL);
katze_assign (view->link_uri, NULL);
katze_assign (view->selected_text, NULL);
katze_object_assign (view->news_feeds, NULL);
katze_object_assign (view->settings, NULL);
katze_object_assign (view->item, NULL);
katze_assign (view->news_aggregator, NULL);
G_OBJECT_CLASS (midori_view_parent_class)->finalize (object);
}
static void
midori_view_set_property (GObject* object,
guint prop_id,
const GValue* value,
GParamSpec* pspec)
{
MidoriView* view;
view = MIDORI_VIEW (object);
switch (prop_id)
{
case PROP_TITLE:
midori_view_set_title (view, g_value_get_string (value));
break;
case PROP_MINIMIZED:
view->minimized = g_value_get_boolean (value);
g_signal_handlers_block_by_func (view->item,
midori_view_item_meta_data_changed, view);
katze_item_set_meta_integer (view->item, "minimized",
view->minimized ? 1 : -1);
g_signal_handlers_unblock_by_func (view->item,
midori_view_item_meta_data_changed, view);
if (view->tab_label)
sokoke_widget_set_visible (view->tab_title, !view->minimized);
break;
case PROP_ZOOM_LEVEL:
midori_view_set_zoom_level (view, g_value_get_float (value));
break;
case PROP_STATUSBAR_TEXT:
katze_assign (view->statusbar_text, g_value_dup_string (value));
break;
case PROP_SETTINGS:
_midori_view_set_settings (view, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
midori_view_get_property (GObject* object,
guint prop_id,
GValue* value,
GParamSpec* pspec)
{
MidoriView* view = MIDORI_VIEW (object);
switch (prop_id)
{
case PROP_URI:
g_value_set_string (value, view->uri);
break;
case PROP_TITLE:
g_value_set_string (value, view->title);
break;
case PROP_SECURITY:
g_value_set_enum (value, view->security);
break;
case PROP_MIME_TYPE:
g_value_set_string (value, view->mime_type);
break;
case PROP_ICON:
g_value_set_object (value, view->icon);
break;
case PROP_PROGRESS:
g_value_set_double (value, midori_view_get_progress (view));
break;
case PROP_LOAD_STATUS:
g_value_set_enum (value, midori_view_get_load_status (view));
break;
case PROP_MINIMIZED:
g_value_set_boolean (value, view->minimized);
break;
case PROP_ZOOM_LEVEL:
g_value_set_float (value, midori_view_get_zoom_level (view));
break;
case PROP_NEWS_FEEDS:
g_value_set_object (value, view->news_feeds);
break;
case PROP_STATUSBAR_TEXT:
g_value_set_string (value, view->statusbar_text);
break;
case PROP_SETTINGS:
g_value_set_object (value, view->settings);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
midori_view_focus_in_event (GtkWidget* widget,
GdkEventFocus* event)
{
/* Always propagate focus to the child web view */
gtk_widget_grab_focus (midori_view_get_web_view (MIDORI_VIEW (widget)));
return TRUE;
}
/**
* midori_view_new:
* @net: %NULL
*
* Creates a new view.
*
* Return value: a new #MidoriView
*
* Deprecated: 0.2.8: Use midori_view_new_with_title() instead.
**/
GtkWidget*
midori_view_new (KatzeNet* net)
{
return g_object_new (MIDORI_TYPE_VIEW, NULL);
}
static void
_midori_view_set_settings (MidoriView* view,
MidoriWebSettings* settings)
{
gboolean zoom_text_and_images, kinetic_scrolling;
if (view->settings)
g_signal_handlers_disconnect_by_func (view->settings,
midori_view_settings_notify_cb, view);
katze_object_assign (view->settings, settings);
if (!settings)
return;
g_object_ref (settings);
g_signal_connect (settings, "notify",
G_CALLBACK (midori_view_settings_notify_cb), view);
g_object_set (view->web_view, "settings", settings, NULL);
g_free (view->news_aggregator);
g_object_get (view->settings,
"news-aggregator", &view->news_aggregator,
"zoom-text-and-images", &zoom_text_and_images,
"kinetic-scrolling", &kinetic_scrolling,
"close-buttons-on-tabs", &view->close_buttons_on_tabs,
"open-new-pages-in", &view->open_new_pages_in,
"middle-click-opens-selection", &view->middle_click_opens_selection,
"open-tabs-in-the-background", &view->open_tabs_in_the_background,
NULL);
if (view->web_view)
g_object_set (view->web_view,
"full-content-zoom", zoom_text_and_images, NULL);
g_object_set (view->scrolled_window, "kinetic-scrolling", kinetic_scrolling, NULL);
}
/**
* midori_view_new_with_title:
* @uri: an URI string, or %NULL
* @title: a title, or %NULL
* @settings: a #MidoriWebSettings, or %NULL
* @append: if %TRUE, the view should be appended
*
* Creates a new view with the specified parameters that
* is visible by default.
*
* Return value: a new #MidoriView
*
* Since: 0.3.0
**/
GtkWidget*
midori_view_new_with_title (const gchar* title,
MidoriWebSettings* settings,
gboolean append)
{
MidoriView* view = g_object_new (MIDORI_TYPE_VIEW, "title", title, NULL);
if (settings)
_midori_view_set_settings (view, settings);
if (append)
g_object_set_data (G_OBJECT (view), "midori-view-append", (void*)1);
gtk_widget_show ((GtkWidget*)view);
return (GtkWidget*)view;
}
static void
midori_view_settings_notify_cb (MidoriWebSettings* settings,
GParamSpec* pspec,
MidoriView* view)
{
const gchar* name;
GValue value = { 0, };
name = g_intern_string (g_param_spec_get_name (pspec));
g_value_init (&value, pspec->value_type);
g_object_get_property (G_OBJECT (view->settings), name, &value);
if (name == g_intern_string ("news-aggregator"))
katze_assign (view->news_aggregator, g_value_dup_string (&value));
else if (name == g_intern_string ("zoom-text-and-images"))
{
if (view->web_view)
g_object_set (view->web_view, "full-content-zoom",
g_value_get_boolean (&value), NULL);
}
else if (name == g_intern_string ("kinetic-scrolling"))
{
g_object_set (view, "kinetic-scrolling",
g_value_get_boolean (&value), NULL);
}
else if (name == g_intern_string ("close-buttons-on-tabs"))
{
view->close_buttons_on_tabs = g_value_get_boolean (&value);
sokoke_widget_set_visible (view->tab_close,
view->close_buttons_on_tabs);
}
else if (name == g_intern_string ("open-new-pages-in"))
view->open_new_pages_in = g_value_get_enum (&value);
else if (name == g_intern_string ("middle-click-opens-selection"))
view->middle_click_opens_selection = g_value_get_boolean (&value);
else if (name == g_intern_string ("open-tabs-in-the-background"))
view->open_tabs_in_the_background = g_value_get_boolean (&value);
g_value_unset (&value);
}
/**
* midori_view_set_settings:
* @view: a #MidoriView
* @settings: a #MidoriWebSettings
*
* Assigns a settings instance to the view.
**/
void
midori_view_set_settings (MidoriView* view,
MidoriWebSettings* settings)
{
g_return_if_fail (MIDORI_IS_VIEW (view));
g_return_if_fail (MIDORI_IS_WEB_SETTINGS (settings));
if (view->settings == settings)
return;
_midori_view_set_settings (view, settings);
g_object_notify (G_OBJECT (view), "settings");
}
/**
* midori_view_load_status:
* @web_view: a #MidoriView
*
* Determines the current loading status of a view.
*
* Return value: the current #MidoriLoadStatus
**/
MidoriLoadStatus
midori_view_get_load_status (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), MIDORI_LOAD_FINISHED);
return view->load_status;
}
/**
* midori_view_get_progress:
* @view: a #MidoriView
*
* Retrieves the current loading progress as
* a fraction between 0.0 and 1.0.
*
* Return value: the current loading progress
**/
gdouble
midori_view_get_progress (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), 0.0);
return view->progress;
}
static void
midori_view_web_inspector_construct_window (gpointer inspector,
WebKitWebView* web_view,
GtkWidget* inspector_view,
MidoriView* view)
{
gchar* title;
gchar* label;
GtkWidget* window;
GtkWidget* toplevel;
GdkScreen* screen;
gint width, height;
GtkIconTheme* icon_theme;
GdkPixbuf* icon;
GdkPixbuf* gray_icon;
label = g_strdup (midori_view_get_display_title (view));
title = g_strdup_printf (_("Inspect page - %s"), label);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), title);
g_free (title);
g_free (label);
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
if (gtk_widget_is_toplevel (toplevel))
{
screen = gtk_window_get_screen (GTK_WINDOW (toplevel));
width = gdk_screen_get_width (screen) / 1.7;
height = gdk_screen_get_height (screen) / 1.7;
gtk_window_set_default_size (GTK_WINDOW (window), width, height);
}
/* Attempt to make a gray version of the icon on the fly */
icon_theme = gtk_icon_theme_get_for_screen (
gtk_widget_get_screen (GTK_WIDGET (view)));
icon = gtk_icon_theme_load_icon (icon_theme, "midori", 32,
GTK_ICON_LOOKUP_USE_BUILTIN, NULL);
if (icon)
{
gray_icon = gdk_pixbuf_copy (icon);
if (gray_icon)
{
gdk_pixbuf_saturate_and_pixelate (gray_icon, gray_icon, 0.1f, FALSE);
gtk_window_set_icon (GTK_WINDOW (window), gray_icon);
g_object_unref (gray_icon);
}
g_object_unref (icon);
}
else
gtk_window_set_icon_name (GTK_WINDOW (window), "midori");
gtk_container_add (GTK_CONTAINER (window), inspector_view);
gtk_widget_show_all (window);
/* FIXME: Update window title with URI */
}
static WebKitWebView*
midori_view_web_inspector_inspect_web_view_cb (gpointer inspector,
WebKitWebView* web_view,
MidoriView* view)
{
GtkWidget* inspector_view = webkit_web_view_new ();
#if HAVE_HILDON
gtk_widget_tap_and_hold_setup (view->web_view, NULL, NULL, 0);
g_signal_connect (view->web_view, "tap-and-hold",
G_CALLBACK (midori_view_web_view_tap_and_hold_cb), NULL);
#endif
midori_view_web_inspector_construct_window (inspector,
web_view, inspector_view, view);
return WEBKIT_WEB_VIEW (inspector_view);
}
static gboolean
midori_view_web_inspector_show_window_cb (WebKitWebInspector* inspector,
MidoriView* view)
{
WebKitWebView* inspector_view = webkit_web_inspector_get_web_view (inspector);
GtkWidget* window = gtk_widget_get_toplevel (GTK_WIDGET (inspector_view));
if (!window)
return FALSE;
gtk_window_present (GTK_WINDOW (window));
return TRUE;
}
static gboolean
midori_view_web_inspector_attach_window_cb (gpointer inspector,
MidoriView* view)
{
WebKitWebView* inspector_view = webkit_web_inspector_get_web_view (inspector);
g_signal_emit (view, signals[ATTACH_INSPECTOR], 0, inspector_view);
return TRUE;
}
static gboolean
midori_view_web_inspector_detach_window_cb (gpointer inspector,
MidoriView* view)
{
WebKitWebView* inspector_view = webkit_web_inspector_get_web_view (inspector);
GtkWidget* parent = gtk_widget_get_parent (GTK_WIDGET (inspector_view));
if (GTK_IS_WINDOW (parent))
return FALSE;
gtk_widget_hide (parent);
g_signal_emit (view, signals[DETACH_INSPECTOR], 0, inspector_view);
midori_view_web_inspector_construct_window (inspector,
WEBKIT_WEB_VIEW (view->web_view), GTK_WIDGET (inspector_view), view);
return TRUE;
}
static gboolean
midori_view_web_inspector_close_window_cb (gpointer inspector,
MidoriView* view)
{
WebKitWebView* inspector_view = webkit_web_inspector_get_web_view (inspector);
GtkWidget* scrolled = gtk_widget_get_parent (GTK_WIDGET (inspector_view));
if (!scrolled)
return FALSE;
gtk_widget_destroy (gtk_widget_get_parent (scrolled));
return TRUE;
}
static void
midori_view_construct_web_view (MidoriView* view)
{
WebKitWebFrame* web_frame;
gpointer inspector;
g_return_if_fail (!view->web_view);
view->web_view = webkit_web_view_new ();
/* Load something to avoid a bug where WebKit might not set a main frame */
webkit_web_view_open (WEBKIT_WEB_VIEW (view->web_view), "");
web_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view->web_view));
#if HAVE_HILDON
gtk_widget_tap_and_hold_setup (view->web_view, NULL, NULL, 0);
g_signal_connect (view->web_view, "tap-and-hold",
G_CALLBACK (midori_view_web_view_tap_and_hold_cb), NULL);
#endif
g_object_connect (view->web_view,
"signal::navigation-policy-decision-requested",
midori_view_web_view_navigation_decision_cb, view,
#if WEBKIT_CHECK_VERSION (1, 1, 14)
"signal::resource-request-starting",
midori_view_web_view_resource_request_cb, view,
#endif
#if WEBKIT_CHECK_VERSION (1, 1, 14)
"signal::database-quota-exceeded",
midori_view_web_view_database_quota_exceeded_cb, view,
#endif
#if WEBKIT_CHECK_VERSION (1, 1, 23)
"signal::geolocation-policy-decision-requested",
midori_view_web_view_geolocation_decision_cb, view,
#endif
"signal::load-started",
webkit_web_view_load_started_cb, view,
"signal::load-committed",
webkit_web_view_load_committed_cb, view,
"signal::load-progress-changed",
webkit_web_view_progress_changed_cb, view,
"signal::load-finished",
webkit_web_view_load_finished_cb, view,
#if WEBKIT_CHECK_VERSION (1, 1, 18)
"signal::notify::icon-uri",
midori_web_view_notify_icon_uri_cb, view,
#endif
#if WEBKIT_CHECK_VERSION (1, 1, 4)
"signal::notify::uri",
webkit_web_view_notify_uri_cb, view,
"signal::notify::title",
webkit_web_view_notify_title_cb, view,
#else
"signal::title-changed",
webkit_web_view_title_changed_cb, view,
#endif
"signal::status-bar-text-changed",
webkit_web_view_statusbar_text_changed_cb, view,
"signal::leave-notify-event",
midori_view_web_view_leave_notify_event_cb, view,
"signal::hovering-over-link",
webkit_web_view_hovering_over_link_cb, view,
"signal::button-press-event",
gtk_widget_button_press_event_cb, view,
"signal-after::key-press-event",
gtk_widget_key_press_event_cb, view,
"signal::scroll-event",
gtk_widget_scroll_event_cb, view,
"signal::populate-popup",
webkit_web_view_populate_popup_cb, view,
"signal::console-message",
webkit_web_view_console_message_cb, view,
"signal::script-alert",
midori_view_web_view_script_alert_cb, view,
"signal::window-object-cleared",
webkit_web_view_window_object_cleared_cb, view,
"signal::create-web-view",
webkit_web_view_create_web_view_cb, view,
"signal-after::mime-type-policy-decision-requested",
webkit_web_view_mime_type_decision_cb, view,
#if WEBKIT_CHECK_VERSION (1, 1, 3)
"signal::download-requested",
webkit_web_view_download_requested_cb, view,
#endif
#if WEBKIT_CHECK_VERSION (1, 1, 5)
"signal::print-requested",
midori_view_web_view_print_requested_cb, view,
#endif
#if WEBKIT_CHECK_VERSION (1, 1, 6)
"signal-after::load-error",
webkit_web_view_load_error_cb, view,
#endif
NULL);
#if !WEBKIT_CHECK_VERSION (1, 1, 6)
g_object_connect (web_frame,
"signal::load-done",
webkit_web_frame_load_done_cb, view,
NULL);
#endif
if (view->settings)
{
g_object_set (view->web_view, "settings", view->settings,
"full-content-zoom", katze_object_get_boolean (view->settings,
"zoom-text-and-images"), NULL);
}
gtk_container_add (GTK_CONTAINER (view->scrolled_window), view->web_view);
gtk_widget_show_all (view->scrolled_window);
inspector = webkit_web_view_get_inspector ((WebKitWebView*)view->web_view);
g_object_connect (inspector,
"signal::inspect-web-view",
midori_view_web_inspector_inspect_web_view_cb, view,
"signal::show-window",
midori_view_web_inspector_show_window_cb, view,
"signal::attach-window",
midori_view_web_inspector_attach_window_cb, view,
"signal::detach-window",
midori_view_web_inspector_detach_window_cb, view,
"signal::close-window",
midori_view_web_inspector_close_window_cb, view,
NULL);
}
static gchar* list_netscape_plugins ()
{
GtkWidget* web_view = webkit_web_view_new ();
WebKitWebFrame* web_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
JSContextRef js_context = webkit_web_frame_get_global_context (web_frame);
/* This snippet joins the available plugins into a string like this:
URI1|title1,URI2|title2 */
gchar* value = sokoke_js_script_eval (js_context,
"function plugins (l) { var f = new Array (); for (i in l) "
"{ var p = l[i].name + '|' + l[i].filename; "
"if (f.indexOf (p) == -1) f.push (p); } return f; }"
"plugins (navigator.plugins)", NULL);
gchar** items = g_strsplit (value, ",", 0);
guint i = 0;
GString* ns_plugins = g_string_new (NULL);
if (items != NULL)
while (items[i] != NULL)
{
gchar** parts = g_strsplit (items[i], "|", 2);
if (parts && *parts && !g_str_equal (parts[1], "undefined"))
{
g_string_append (ns_plugins, "<tr><td>");
g_string_append (ns_plugins, parts[1]);
g_string_append (ns_plugins, "</td><td>");
g_string_append (ns_plugins, parts[0]);
g_string_append (ns_plugins, "</tr>");
}
g_strfreev (parts);
i++;
}
if (g_str_has_prefix (value, "undefined"))
g_string_append (ns_plugins, "<tr><td>No plugins found</td></tr>");
g_strfreev (items);
g_free (value);
return g_string_free (ns_plugins, FALSE);
}
static gchar*
prepare_speed_dial_html (MidoriView* view)
{
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (view));
GKeyFile* key_file;
GString* markup = g_string_new (NULL);
guint rows, cols, slot = 1;
gchar* thumb_size_type;
guint thumb_size = 160;
g_object_get (browser, "speed-dial", &key_file, NULL);
if (!key_file)
return g_string_free (markup, FALSE);
rows = g_key_file_get_integer (key_file, "settings", "rows", NULL);
cols = g_key_file_get_integer (key_file, "settings", "columns", NULL);
thumb_size_type = g_key_file_get_string (key_file, "settings", "size", NULL);
if (thumb_size_type == NULL)
thumb_size_type = g_strdup ("MEDIUM");
if (g_str_equal (thumb_size_type, "SMALL"))
thumb_size = 80;
else if (g_str_equal (thumb_size_type, "MEDIUM"))
thumb_size = 160;
else if (g_str_equal (thumb_size_type, "BIG"))
thumb_size = 240;
g_free (thumb_size_type);
g_string_append_printf (markup,
"<script>var columns = %d; var rows = %d;"
"setThumbSize(%d);</script>\n",
cols, rows, thumb_size);
while (slot <= rows * cols)
{
gchar* position;
gchar* dial_entry = g_strdup_printf ("Dial %d", slot);
if (slot < cols)
position = g_strdup (" top");
else if (slot == cols)
position = g_strdup (" top right");
else if (slot > cols && slot % cols == 0)
position = g_strdup (" right");
else
position = g_strdup ("");
if (g_key_file_has_group (key_file, dial_entry))
{
gchar* slot_id = g_strdup_printf ("s%d", slot);
gchar* uri = g_key_file_get_string (key_file, dial_entry, "uri", NULL);
gchar* title = g_key_file_get_string (key_file, dial_entry, "title", NULL);
gchar* thumb_file = sokoke_build_thumbnail_path (uri);
gchar* encoded;
if (g_access (thumb_file, F_OK) == 0)
{
gsize sz;
gchar* thumb_content;
g_file_get_contents (thumb_file, &thumb_content, &sz, NULL);
encoded = g_base64_encode ((guchar*)thumb_content, sz);
g_free (thumb_content);
}
else
{
encoded = g_strdup ("");
}
g_free (slot_id);
g_free (thumb_file);
g_string_append_printf (markup,
"<div class=\"shortcut%s activated\" id=\"s%d\">\n"
"<div onclick='javascript:clearShortcut(\"s%d\");' "
"class='cross'></div>\n<a href=\"%s\">"
"<img src=\"data:image/png;base64,%s\"></a>\n"
"<p onclick='javascript:renameShortcut(\"s%d\");'>"
"%s</p></div>\n",
position, slot, slot, uri, encoded, slot, title);
g_free (uri);
g_free (title);
g_free (encoded);
}
else
{
g_string_append_printf (markup,
"<div class=\"shortcut%s\" id=\"s%d\">"
"\n<a href=\"#\" onclick='javascript:return"
" getAction(\"s%d\");'>"
"<h1>%d</h1>\n<h4><span></span></h4>"
"</a>\n<p></p></div>\n",
position, slot, slot, slot);
}
slot++;
g_free (position);
g_free (dial_entry);
}
g_string_append_printf (markup,
"</div>\n</div>\n</body>\n</html>\n");
return g_string_free (markup, FALSE);
}
/**
* midori_view_set_uri:
* @view: a #MidoriView
*
* Opens the specified URI in the view.
*
* Since 0.3.0 a warning is shown if the view is not yet
* contained in a browser. This is because extensions
* can't monitor page loading if that happens.
**/
void
midori_view_set_uri (MidoriView* view,
const gchar* uri)
{
gchar* data;
g_return_if_fail (MIDORI_IS_VIEW (view));
if (!gtk_widget_get_parent (GTK_WIDGET (view)))
g_warning ("Calling %s() before adding the view to a browser. This "
"breaks extensions that monitor page loading.", G_STRFUNC);
/* Treat "about:blank" and "" equally, see midori_view_is_blank(). */
if (!uri || !strcmp (uri, "about:blank")) uri = "";
if (g_getenv ("MIDORI_UNARMED") == NULL)
{
if (!strcmp (uri, ""))
{
#if !WEBKIT_CHECK_VERSION (1, 1, 14)
SoupServer* res_server;
guint port;
#endif
gchar* res_root;
gchar* speed_dial_head;
gchar* speed_dial_body;
gchar* speed_dial_markup;
gchar* stock_root;
gchar* filepath;
#ifdef G_ENABLE_DEBUG
GTimer* timer = NULL;
if (g_getenv ("MIDORI_STARTTIME") != NULL)
timer = g_timer_new ();
#endif
katze_assign (view->uri, g_strdup (""));
katze_item_set_uri (view->item, "");
filepath = sokoke_find_data_filename ("midori/res/speeddial-head.html");
g_file_get_contents (filepath, &speed_dial_head, NULL, NULL);
g_free (filepath);
if (G_UNLIKELY (!speed_dial_head))
speed_dial_head = g_strdup ("");
#if WEBKIT_CHECK_VERSION (1, 1, 14)
res_root = g_strdup ("res:/");
stock_root = g_strdup ("stock:/");
#else
res_server = sokoke_get_res_server ();
port = soup_server_get_port (res_server);
res_root = g_strdup_printf ("http://localhost:%d/res", port);
stock_root = g_strdup_printf ("http://localhost:%d/stock", port);
#endif
data = sokoke_replace_variables (speed_dial_head,
"{res}", res_root,
"{stock}", stock_root,
"{title}", _("Speed Dial"),
"{click_to_add}", _("Click to add a shortcut"),
"{enter_shortcut_address}", _("Enter shortcut address"),
"{enter_shortcut_name}", _("Enter shortcut title"),
"{are_you_sure}", _("Are you sure you want to delete this shortcut?"),
"{set_dial_size}", _("Set number of columns and rows"),
"{enter_dial_size}", _("Enter number of columns and rows:"),
"{invalid_dial_size}", _("Invalid input for the size of the speed dial"),
"{set_thumb_size}", _("Thumb size:"),
"{set_thumb_small}", _("Small"),
"{set_thumb_normal}", _("Medium"),
"{set_thumb_big}", _("Big"), NULL);
#ifdef G_ENABLE_DEBUG
if (g_getenv ("MIDORI_STARTTIME") != NULL)
{
g_debug ("Speed Dial: \t%fs", g_timer_elapsed (timer, NULL));
g_timer_destroy (timer);
}
#endif
speed_dial_body = prepare_speed_dial_html (view);
speed_dial_markup = g_strdup_printf ("%s\n%s", data, speed_dial_body);
midori_view_load_alternate_string (view,
speed_dial_markup, res_root, "about:blank", NULL);
g_free (res_root);
g_free (stock_root);
g_free (data);
g_free (speed_dial_head);
g_free (speed_dial_body);
g_free (speed_dial_markup);
}
/* This is not prefectly elegant, but creating
special pages inline is the simplest solution. */
else if (g_str_has_prefix (uri, "error:") || g_str_has_prefix (uri, "about:"))
{
data = NULL;
#if !WEBKIT_CHECK_VERSION (1, 1, 3)
if (!strncmp (uri, "error:nodisplay ", 16))
{
gchar* title;
gchar* logo_path;
gchar* logo_uri;
katze_assign (view->uri, g_strdup (&uri[16]));
title = g_strdup_printf (_("Document cannot be displayed"));
logo_path = sokoke_find_data_filename ("midori/logo-shade.png");
logo_uri = g_filename_to_uri (logo_path, NULL, NULL);
g_free (logo_path);
data = g_strdup_printf (
"<html><head><title>%s</title></head>"
"<body><h1>%s</h1>"
"<img src=\"%s\" "
"style=\"position: absolute; right: 15px; bottom: 15px; z-index: -9;\">"
"<p />The document %s of type '%s' cannot be displayed."
"</body></html>",
title, title, logo_uri, view->uri, view->mime_type);
g_free (title);
g_free (logo_uri);
}
#endif
if (!strncmp (uri, "error:nodocs ", 13))
{
gchar* title;
gchar* logo_path;
gchar* logo_uri;
katze_assign (view->uri, g_strdup (&uri[13]));
title = g_strdup_printf (_("No documentation installed"));
logo_path = sokoke_find_data_filename ("midori/logo-shade.png");
logo_uri = g_filename_to_uri (logo_path, NULL, NULL);
g_free (logo_path);
data = g_strdup_printf (
"<html><head><title>%s</title></head>"
"<body><h1>%s</h1>"
"<img src=\"%s\" "
"style=\"position: absolute; right: 15px; bottom: 15px; z-index: -9;\">"
"<p />There is no documentation installed at %s."
"You may want to ask your distribution or "
"package maintainer for it or if this a custom build "
"verify that the build is setup properly."
"</body></html>",
title, title, logo_uri, view->uri);
g_free (title);
g_free (logo_uri);
}
else if (!strcmp (uri, "about:") || !strcmp (uri, "about:version"))
{
gchar** argument_vector = sokoke_get_argv (NULL);
gchar* command_line = g_strjoinv (" ", argument_vector);
gchar* ident = katze_object_get_string (view->settings, "user-agent");
gchar* netscape_plugins = list_netscape_plugins ();
#if defined (G_OS_WIN32)
gchar* sys_name = g_strdup ("Windows");
#else
gchar* sys_name;
struct utsname name;
if (uname (&name) != -1)
sys_name = g_strdup_printf ("%s %s", name.sysname, name.machine);
else
sys_name = g_strdup ("Unix");
#endif
katze_assign (view->uri, g_strdup (uri));
#ifndef WEBKIT_USER_AGENT_MAJOR_VERSION
#define WEBKIT_USER_AGENT_MAJOR_VERSION 532
#define WEBKIT_USER_AGENT_MINOR_VERSION 1
#endif
#if defined (HAVE_LIBSOUP_2_29_3)
#define LIBSOUP_VERSION "2.29.3"
#elif defined (HAVE_LIBSOUP_2_27_90)
#define LIBSOUP_VERSION "2.27.90"
#else
#define LIBSOUP_VERSION "2.25.2"
#endif
#ifdef G_ENABLE_DEBUG
#define DEBUGGING " (Debug)"
#else
#define DEBUGGING ""
#endif
data = g_strdup_printf (
"<html><head><title>about:version</title></head>"
"<body><h1>about:version</h1>"
"<p>%s</p>"
"<img src=\"res://logo-shade.png\" "
"style=\"position: absolute; right: 15px; bottom: 15px; z-index: -9;\">"
"<table>"
"<tr><td>Command line</td><td>%s</td></tr>"
"<tr><td>Midori</td><td>" PACKAGE_VERSION "%s</td></tr>"
"<tr><td>WebKitGTK+</td><td>%d.%d.%d (%d.%d.%d)</td></tr>"
"<tr><td>GTK+</td><td>%d.%d.%d (%d.%d.%d)</td></tr>"
"<tr><td>Glib</td><td>%d.%d.%d (%d.%d.%d)</td></tr>"
"<tr><td>libsoup</td><td>%s</td></tr>"
"<tr><td>libnotify</td><td>%s</td></tr>"
"<tr><td>libidn</td><td>%s</td></tr>"
"<tr><td>libunique</td><td>%s</td></tr>"
"<tr><td>libhildon</td><td>%s</td></tr>"
"<tr><td>Platform</td><td>%s</td></tr>"
"<tr><td>Identification</td><td>%s</td></tr>"
"</table>"
"<h2>Netscape Plugins:</h2><table>%s</table>"
"</body></html>",
_("Version numbers in brackets show the version used at runtime."),
command_line,
DEBUGGING,
WEBKIT_MAJOR_VERSION,
WEBKIT_MINOR_VERSION,
WEBKIT_MICRO_VERSION,
webkit_major_version (),
webkit_minor_version (),
webkit_micro_version (),
GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION,
gtk_major_version, gtk_minor_version, gtk_micro_version,
GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION,
glib_major_version, glib_minor_version, glib_micro_version,
LIBSOUP_VERSION,
HAVE_LIBNOTIFY ? "Yes" : "No",
HAVE_LIBIDN ? "Yes" : "No",
HAVE_UNIQUE ? "Yes" : "No",
HAVE_HILDON ? "Yes" : "No",
sys_name, ident,
netscape_plugins);
g_free (command_line);
g_free (ident);
g_free (sys_name);
g_free (netscape_plugins);
}
else
{
katze_assign (view->uri, g_strdup (uri));
data = g_strdup_printf (
"<html><head><title>%s</title></head><body><h1>%s</h1>"
"<img src=\"file://" MDATADIR "/midori/logo-shade.png\" "
"style=\"position: absolute; right: 15px; bottom: 15px; z-index: -9;\">"
"</body></html>", view->uri, view->uri);
}
webkit_web_view_load_html_string (
WEBKIT_WEB_VIEW (view->web_view), data, view->uri);
g_free (data);
katze_item_set_uri (view->item, uri);
g_object_notify (G_OBJECT (view), "uri");
return;
}
#if WEBKIT_CHECK_VERSION (1, 1, 6)
else if (g_str_has_prefix (uri, "pause:"))
{
katze_assign (view->uri, g_strdup (&uri[6]));
midori_view_display_error (
view, view->uri, view->title,
_("Page loading delayed"),
_("Loading delayed either due to a recent crash or startup preferences."),
_("Load Page"),
NULL);
katze_item_set_uri (view->item, uri);
g_object_notify (G_OBJECT (view), "uri");
}
#endif
else if (g_str_has_prefix (uri, "javascript:"))
{
gboolean result;
gchar* exception;
result = midori_view_execute_script (view, &uri[11], &exception);
if (!result)
{
sokoke_message_dialog (GTK_MESSAGE_ERROR, "javascript:",
exception, FALSE);
g_free (exception);
}
}
else if (g_str_has_prefix (uri, "mailto:") || sokoke_external_uri (uri))
{
sokoke_show_uri (NULL, uri, GDK_CURRENT_TIME, NULL);
}
else
{
katze_assign (view->uri, sokoke_format_uri_for_display (uri));
katze_item_set_uri (view->item, uri);
katze_item_set_meta_integer (view->item, "delay", -1);
g_object_notify (G_OBJECT (view), "uri");
webkit_web_view_open (WEBKIT_WEB_VIEW (view->web_view), uri);
}
}
}
/**
* midori_view_is_blank:
* @view: a #MidoriView
*
* Determines whether the view is currently empty.
**/
gboolean
midori_view_is_blank (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), TRUE);
return midori_view_get_display_uri (view)[0] == '\0';
}
/**
* midori_view_get_icon:
* @view: a #MidoriView
*
* Retrieves the icon of the view, or a default icon. See
* midori_view_get_icon_uri() if you need to distinguish
* the origin of an icon.
*
* The returned icon is owned by the @view and must not be modified.
*
* Return value: a #GdkPixbuf, or %NULL
**/
GdkPixbuf*
midori_view_get_icon (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
return view->icon;
}
/**
* midori_view_get_icon_uri:
* @view: a #MidoriView
*
* Retrieves the address of the icon of the view
* if the loaded website has an icon, otherwise
* %NULL.
* Note that if there is no icon uri, midori_view_get_icon()
* will still return a default icon.
*
* The returned string is owned by the @view and must not be freed.
*
* Return value: a string, or %NULL
*
* Since: 0.2.5
**/
const gchar*
midori_view_get_icon_uri (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
return view->icon_uri;
}
/**
* midori_view_get_display_uri:
* @view: a #MidoriView
*
* Retrieves a string that is suitable for displaying.
*
* Note that "about:blank" is represented as "".
*
* You can assume that the string is not %NULL.
*
* Return value: an URI string
**/
const gchar*
midori_view_get_display_uri (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), "");
/* Something in the stack tends to turn "" into "about:blank".
Yet for practical purposes we prefer "". */
if (view->uri && !strcmp (view->uri, "about:blank"))
return "";
if (view->uri && *view->uri)
return view->uri;
return "";
}
/**
* midori_view_get_display_title:
* @view: a #MidoriView
*
* Retrieves a string that is suitable for displaying
* as a title. Most of the time this will be the title
* or the current URI.
*
* An empty page is represented as "about:blank".
*
* You can assume that the string is not %NULL.
*
* Return value: a title string
**/
const gchar*
midori_view_get_display_title (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), "about:blank");
if (view->title && *view->title)
return view->title;
if (midori_view_is_blank (view))
return _("Blank page");
return midori_view_get_display_uri (view);
}
/**
* midori_view_get_link_uri:
* @view: a #MidoriView
*
* Retrieves the uri of the currently focused link,
* particularly while the mouse hovers a link or a
* context menu is being opened.
*
* Return value: an URI string, or %NULL if there is no link focussed
**/
const gchar*
midori_view_get_link_uri (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
return view->link_uri;
}
/**
* midori_view_has_selection:
* @view: a #MidoriView
*
* Determines whether something in the view is selected.
*
* This function returns %FALSE if there is a selection
* that effectively only consists of whitespace.
*
* Return value: %TRUE if effectively there is a selection
**/
gboolean
midori_view_has_selection (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), FALSE);
katze_assign (view->selected_text, webkit_web_view_get_selected_text (
WEBKIT_WEB_VIEW (view->web_view)));
if (view->selected_text && *view->selected_text)
return TRUE;
else
return FALSE;
}
/**
* midori_view_get_selected_text:
* @view: a #MidoriView
*
* Retrieves the currently selected text.
*
* Return value: the selected text, or %NULL
**/
const gchar*
midori_view_get_selected_text (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
if (midori_view_has_selection (view))
return view->selected_text;
return NULL;
}
/**
* midori_view_can_cut_clipboard:
* @view: a #MidoriView
*
* Determines whether a selection can be cut.
*
* Return value: %TRUE if a selection can be cut
**/
gboolean
midori_view_can_cut_clipboard (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), FALSE);
if (view->web_view)
return webkit_web_view_can_cut_clipboard (
WEBKIT_WEB_VIEW (view->web_view));
else
return FALSE;
}
/**
* midori_view_can_copy_clipboard:
* @view: a #MidoriView
*
* Determines whether a selection can be copied.
*
* Return value: %TRUE if a selection can be copied
**/
gboolean
midori_view_can_copy_clipboard (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), FALSE);
if (view->web_view)
return webkit_web_view_can_copy_clipboard (
WEBKIT_WEB_VIEW (view->web_view));
else
return FALSE;
}
/**
* midori_view_can_paste_clipboard:
* @view: a #MidoriView
*
* Determines whether a selection can be pasted.
*
* Return value: %TRUE if a selection can be pasted
**/
gboolean
midori_view_can_paste_clipboard (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), FALSE);
if (view->web_view)
return webkit_web_view_can_paste_clipboard (
WEBKIT_WEB_VIEW (view->web_view));
else
return FALSE;
}
/**
* midori_view_get_proxy_menu_item:
* @view: a #MidoriView
*
* Retrieves a proxy menu item that is typically added to a Window menu
* and which on activation switches to the right window/ tab.
*
* The item is created on the first call and will be updated to reflect
* changes to the icon and title automatically.
*
* The menu item is valid until it is removed from its container.
*
* Return value: the proxy #GtkMenuItem
**/
GtkWidget*
midori_view_get_proxy_menu_item (MidoriView* view)
{
const gchar* title;
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
if (!view->menu_item)
{
title = midori_view_get_display_title (view);
view->menu_item = katze_image_menu_item_new_ellipsized (title);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (view->menu_item),
gtk_image_new_from_pixbuf (view->icon));
g_signal_connect (view->menu_item, "destroy",
G_CALLBACK (gtk_widget_destroyed),
&view->menu_item);
}
return view->menu_item;
}
static void
midori_view_tab_label_menu_open_cb (GtkWidget* menuitem,
GtkWidget* view)
{
MidoriBrowser* browser = midori_browser_get_for_widget (view);
midori_browser_set_current_tab (browser, view);
}
static void
midori_view_tab_label_menu_duplicate_tab_cb (GtkWidget* menuitem,
MidoriView* view)
{
MidoriNewView where = MIDORI_NEW_VIEW_TAB;
GtkWidget* new_view = midori_view_new_with_title (
NULL, view->settings, FALSE);
const gchar* uri = midori_view_get_display_uri (MIDORI_VIEW (view));
g_signal_emit (view, signals[NEW_VIEW], 0, new_view, where);
midori_view_set_uri (MIDORI_VIEW (new_view), uri);
}
static void
midori_view_browser_close_tabs_cb (GtkWidget* view,
gpointer data)
{
GtkWidget* remaining_view = data;
if (view != remaining_view)
gtk_widget_destroy (view);
}
static void
midori_view_tab_label_menu_close_other_tabs_cb (GtkWidget* menuitem,
GtkWidget* view)
{
MidoriBrowser* browser = midori_browser_get_for_widget (view);
midori_browser_foreach (browser, midori_view_browser_close_tabs_cb, view);
}
static void
midori_view_tab_label_menu_minimize_tab_cb (GtkWidget* menuitem,
MidoriView* view)
{
g_object_set (view, "minimized", !view->minimized, NULL);
}
static void
midori_view_tab_label_menu_close_cb (GtkWidget* menuitem,
GtkWidget* view)
{
gtk_widget_destroy (view);
}
/**
* midori_view_get_tab_menu:
* @view: a #MidoriView
*
* Retrieves a menu that is typically shown when right-clicking
* a tab label or equivalent representation.
*
* Return value: a #GtkMenu
*
* Since: 0.1.8
**/
GtkWidget*
midori_view_get_tab_menu (MidoriView* view)
{
MidoriBrowser* browser;
GtkActionGroup* actions;
GtkWidget* menu;
GtkWidget* menuitem;
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
browser = midori_browser_get_for_widget (GTK_WIDGET (view));
actions = midori_browser_get_action_group (browser);
menu = gtk_menu_new ();
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "TabNew"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "UndoTabClose"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
menuitem = gtk_separator_menu_item_new ();
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_OPEN, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_view_tab_label_menu_open_cb), view);
menuitem = gtk_image_menu_item_new_from_stock (STOCK_WINDOW_NEW, NULL);
gtk_menu_item_set_label (GTK_MENU_ITEM (menuitem), _("Open in New _Window"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_view_tab_label_menu_window_new_cb), view);
menuitem = gtk_menu_item_new_with_mnemonic (_("_Duplicate Tab"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_view_tab_label_menu_duplicate_tab_cb), view);
menuitem = gtk_menu_item_new_with_mnemonic (
view->minimized ? _("Show Tab _Label") : _("Show Tab _Icon Only"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_view_tab_label_menu_minimize_tab_cb), view);
menuitem = gtk_separator_menu_item_new ();
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
menuitem = gtk_menu_item_new_with_mnemonic (_("Close ot_her Tabs"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_view_tab_label_menu_close_other_tabs_cb), view);
menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLOSE, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_view_tab_label_menu_close_cb), view);
gtk_widget_show_all (menu);
return menu;
}
static gboolean
midori_view_tab_label_button_press_event (GtkWidget* tab_label,
GdkEventButton* event,
GtkWidget* widget)
{
if (event->button == 2)
{
/* Close the widget on middle click */
gtk_widget_destroy (widget);
return TRUE;
}
else if (event->button == 3)
{
/* Show a context menu on right click */
GtkWidget* menu = midori_view_get_tab_menu (MIDORI_VIEW (widget));
katze_widget_popup (widget, GTK_MENU (menu),
event, KATZE_MENU_POSITION_CURSOR);
return TRUE;
}
return FALSE;
}
static void
midori_view_tab_close_clicked (GtkWidget* tab_close,
GtkWidget* widget)
{
gtk_widget_destroy (widget);
}
static void
midori_view_tab_icon_style_set_cb (GtkWidget* tab_close,
GtkStyle* previous_style)
{
GtkRequisition size;
gtk_widget_size_request (gtk_bin_get_child (GTK_BIN (tab_close)), &size);
gtk_widget_set_size_request (tab_close, size.width, size.height);
}
static void
midori_view_update_tab_title (GtkWidget* label,
gint size,
gdouble angle)
{
if (angle == 0.0 || angle == 360.0)
{
if (gtk_label_get_ellipsize (GTK_LABEL (label)) != PANGO_ELLIPSIZE_START)
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
}
else
{
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_NONE);
}
gtk_label_set_angle (GTK_LABEL (label), angle);
}
static void
gtk_box_repack (GtkBox* box,
GtkWidget* child)
{
GtkWidget* old_box;
gboolean expand, fill;
guint padding;
GtkPackType pack_type;
old_box = gtk_widget_get_parent (child);
g_return_if_fail (GTK_IS_BOX (old_box));
gtk_box_query_child_packing (GTK_BOX (old_box), child,
&expand, &fill, &padding, &pack_type);
g_object_ref (child);
gtk_container_remove (GTK_CONTAINER (old_box), child);
if (pack_type == GTK_PACK_START)
gtk_box_pack_start (box, child, expand, fill, padding);
else
gtk_box_pack_end (box, child, expand, fill, padding);
g_object_unref (child);
}
static void
midori_view_tab_label_parent_set (GtkWidget* tab_label,
GtkObject* old_parent,
MidoriView* view)
{
GtkWidget* parent;
/* FIXME: Disconnect orientation notification
if (old_parent)
; */
if (!(parent = gtk_widget_get_parent (tab_label)))
return;
if (GTK_IS_NOTEBOOK (parent))
{
GtkPositionType pos;
gdouble old_angle, angle;
GtkWidget* box;
pos = gtk_notebook_get_tab_pos (GTK_NOTEBOOK (parent));
old_angle = gtk_label_get_angle (GTK_LABEL (view->tab_title));
switch (pos)
{
case GTK_POS_LEFT:
angle = 90.0;
break;
case GTK_POS_RIGHT:
angle = 270.0;
break;
default:
angle = 0.0;
}
if (old_angle != angle)
{
if (angle == 0.0)
box = gtk_hbox_new (FALSE, 1);
else
box = gtk_vbox_new (FALSE, 1);
gtk_box_repack (GTK_BOX (box), view->tab_icon);
gtk_box_repack (GTK_BOX (box), view->tab_title);
gtk_box_repack (GTK_BOX (box), view->tab_close);
gtk_container_remove (GTK_CONTAINER (tab_label),
gtk_bin_get_child (GTK_BIN (tab_label)));
gtk_container_add (GTK_CONTAINER (tab_label), GTK_WIDGET (box));
gtk_widget_show (box);
}
midori_view_update_tab_title (view->tab_title, 10, angle);
/* FIXME: Connect orientation notification */
}
}
/**
* midori_view_get_label_ellipsize:
* @view: a #MidoriView
*
* Determines how labels representing the view should be
* ellipsized, which is helpful for alternative labels.
*
* Return value: how to ellipsize the label
*
* Since: 0.1.9
**/
PangoEllipsizeMode
midori_view_get_label_ellipsize (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), PANGO_ELLIPSIZE_END);
if (view->tab_label)
return gtk_label_get_ellipsize (GTK_LABEL (view->tab_title));
return PANGO_ELLIPSIZE_END;
}
static void midori_view_tab_label_data_received (GtkWidget* widget,
GdkDragContext* context,
gint x,
gint y,
GtkSelectionData* data,
guint ttype,
guint timestamp,
MidoriView* view)
{
gchar **uri;
gchar* text;
uri = gtk_selection_data_get_uris (data);
if (uri != NULL)
{
midori_view_set_uri (view, uri[0]);
g_strfreev (uri);
}
else
{
text = gtk_selection_data_get_text (data);
midori_view_set_uri (view, text);
g_free (text);
}
}
/**
* midori_view_get_proxy_tab_label:
* @view: a #MidoriView
*
* Retrieves a proxy tab label that is typically used when
* adding the view to a notebook.
*
* Note that the label actually adjusts its orientation
* to the according tab position when used in a notebook.
*
* The label is created on the first call and will be updated to reflect
* changes of the loading progress and title.
*
* The label is valid until it is removed from its container.
*
* Return value: the proxy #GtkEventBox
**/
GtkWidget*
midori_view_get_proxy_tab_label (MidoriView* view)
{
GtkWidget* event_box;
GtkWidget* hbox;
GtkRcStyle* rcstyle;
GtkWidget* image;
GtkWidget* align;
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
if (!view->tab_label)
{
view->tab_icon = katze_throbber_new ();
katze_throbber_set_static_pixbuf (KATZE_THROBBER (view->tab_icon),
midori_view_get_icon (view));
view->tab_title = gtk_label_new (midori_view_get_display_title (view));
gtk_misc_set_alignment (GTK_MISC (view->tab_title), 0.0, 0.5);
event_box = gtk_event_box_new ();
gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
hbox = gtk_hbox_new (FALSE, 1);
gtk_container_add (GTK_CONTAINER (event_box), GTK_WIDGET (hbox));
midori_view_update_tab_title (view->tab_title, 10, 0.0);
view->tab_close = gtk_button_new ();
gtk_button_set_relief (GTK_BUTTON (view->tab_close), GTK_RELIEF_NONE);
gtk_button_set_focus_on_click (GTK_BUTTON (view->tab_close), FALSE);
rcstyle = gtk_rc_style_new ();
rcstyle->xthickness = rcstyle->ythickness = 0;
gtk_widget_modify_style (view->tab_close, rcstyle);
g_object_unref (rcstyle);
image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
gtk_container_add (GTK_CONTAINER (view->tab_close), image);
align = gtk_alignment_new (1.0, 0.0, 0.0, 0.0);
gtk_container_add (GTK_CONTAINER (align), view->tab_close);
if (katze_object_get_boolean (view->settings, "close-buttons-left"))
{
gtk_box_pack_end (GTK_BOX (hbox), view->tab_icon, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (hbox), view->tab_title, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (hbox), align, FALSE, FALSE, 0);
}
else
{
gtk_box_pack_start (GTK_BOX (hbox), view->tab_icon, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), view->tab_title, TRUE, TRUE, 0);
gtk_box_pack_end (GTK_BOX (hbox), align, FALSE, FALSE, 0);
}
gtk_widget_show_all (GTK_WIDGET (event_box));
if (view->minimized)
gtk_widget_hide (view->tab_title);
if (!view->close_buttons_on_tabs)
gtk_widget_hide (view->tab_close);
g_signal_connect (event_box, "button-press-event",
G_CALLBACK (midori_view_tab_label_button_press_event), view);
g_signal_connect (view->tab_close, "style-set",
G_CALLBACK (midori_view_tab_icon_style_set_cb), NULL);
g_signal_connect (view->tab_close, "clicked",
G_CALLBACK (midori_view_tab_close_clicked), view);
view->tab_label = event_box;
g_signal_connect (view->tab_icon, "destroy",
G_CALLBACK (gtk_widget_destroyed),
&view->tab_icon);
g_signal_connect (view->tab_label, "destroy",
G_CALLBACK (gtk_widget_destroyed),
&view->tab_label);
g_signal_connect (view->tab_label, "parent-set",
G_CALLBACK (midori_view_tab_label_parent_set),
view);
gtk_drag_dest_set (view->tab_label, GTK_DEST_DEFAULT_ALL, NULL,
0, GDK_ACTION_COPY);
gtk_drag_dest_add_text_targets (view->tab_label);
gtk_drag_dest_add_uri_targets (view->tab_label);
g_signal_connect (view->tab_label, "drag-data-received",
G_CALLBACK (midori_view_tab_label_data_received),
view);
}
return view->tab_label;
}
static void
midori_view_item_meta_data_changed (KatzeItem* item,
const gchar* key,
MidoriView* view)
{
if (g_str_equal (key, "minimized"))
g_object_set (view, "minimized",
katze_item_get_meta_string (item, key) != NULL, NULL);
else if (g_str_has_prefix (key, "scroll"))
{
gint value = katze_item_get_meta_integer (item, key);
if (view->scrollh == -2 && key[6] == 'h')
view->scrollh = value > -1 ? value : 0;
else if (view->scrollv == -2 && key[6] == 'v')
view->scrollv = value > -1 ? value : 0;
else
return;
}
}
/**
* midori_view_get_proxy_item:
* @view: a #MidoriView
*
* Retrieves a proxy item that can be used for bookmark storage as
* well as session management.
*
* The item reflects changes to the title and uri automatically.
*
* Return value: the proxy #KatzeItem
**/
KatzeItem*
midori_view_get_proxy_item (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
return view->item;
}
/**
* midori_view_get_zoom_level:
* @view: a #MidoriView
*
* Determines the current zoom level of the view.
*
* Return value: the current zoom level
**/
gfloat
midori_view_get_zoom_level (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), 1.0f);
if (view->web_view != NULL)
return webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (view->web_view));
return 1.0f;
}
/**
* midori_view_set_zoom_level:
* @view: a #MidoriView
* @zoom_level: the new zoom level
*
* Sets the current zoom level of the view.
**/
void
midori_view_set_zoom_level (MidoriView* view,
gfloat zoom_level)
{
g_return_if_fail (MIDORI_IS_VIEW (view));
webkit_web_view_set_zoom_level (
WEBKIT_WEB_VIEW (view->web_view), zoom_level);
g_object_notify (G_OBJECT (view), "zoom-level");
}
gboolean
midori_view_can_zoom_in (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), FALSE);
return view->web_view != NULL
&& (katze_object_get_boolean (view->settings, "zoom-text-and-images")
|| !g_str_has_prefix (view->mime_type, "image/"));
}
gboolean
midori_view_can_zoom_out (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), FALSE);
return view->web_view != NULL
&& (katze_object_get_boolean (view->settings, "zoom-text-and-images")
|| !g_str_has_prefix (view->mime_type, "image/"));
}
gboolean
midori_view_can_view_source (MidoriView* view)
{
gchar* content_type;
gchar* text_type;
gboolean is_text;
g_return_val_if_fail (MIDORI_IS_VIEW (view), FALSE);
if (midori_view_is_blank (view))
return FALSE;
content_type = g_content_type_from_mime_type (view->mime_type);
text_type = g_content_type_from_mime_type ("text/plain");
is_text = g_content_type_is_a (content_type, text_type);
g_free (content_type);
g_free (text_type);
return is_text;
}
#define can_do(what) \
gboolean \
midori_view_can_##what (MidoriView* view) \
{ \
g_return_val_if_fail (MIDORI_IS_VIEW (view), FALSE); \
\
return view->web_view != NULL; \
}
can_do (reload)
can_do (print)
can_do (find)
/**
* midori_view_reload:
* @view: a #MidoriView
* @from_cache: whether to allow caching
*
* Reloads the view.
**/
void
midori_view_reload (MidoriView* view,
gboolean from_cache)
{
gchar* title;
g_return_if_fail (MIDORI_IS_VIEW (view));
#if WEBKIT_CHECK_VERSION (1, 1, 14)
title = NULL;
#elif WEBKIT_CHECK_VERSION (1, 1, 6)
/* WebKit 1.1.6 doesn't handle "alternate content" flawlessly,
so reloading via Javascript works but not via API calls. */
title = g_strdup_printf (_("Error - %s"), view->uri);
#else
/* Error pages are special, we want to try loading the destination
again, not the error page which isn't even a proper page */
title = g_strdup_printf (_("Error - %s"), view->uri);
#endif
if (view->title && title && strstr (title, view->title))
webkit_web_view_open (WEBKIT_WEB_VIEW (view->web_view), view->uri);
else if (!(view->uri && *view->uri && strncmp (view->uri, "about:", 6)))
{
gchar* uri = g_strdup (view->uri);
midori_view_set_uri (view, uri);
g_free (uri);
}
else if (from_cache)
webkit_web_view_reload (WEBKIT_WEB_VIEW (view->web_view));
else
webkit_web_view_reload_bypass_cache (WEBKIT_WEB_VIEW (view->web_view));
katze_item_set_meta_integer (view->item, "delay", -1);
g_free (title);
}
/**
* midori_view_stop_loading
* @view: a #MidoriView
*
* Stops loading the view if it is currently loading.
**/
void
midori_view_stop_loading (MidoriView* view)
{
g_return_if_fail (MIDORI_IS_VIEW (view));
webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (view->web_view));
}
/**
* midori_view_can_go_back
* @view: a #MidoriView
*
* Determines whether the view can go back.
**/
gboolean
midori_view_can_go_back (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), FALSE);
if (view->web_view)
return webkit_web_view_can_go_back (WEBKIT_WEB_VIEW (view->web_view));
else
return FALSE;
}
/**
* midori_view_go_back
* @view: a #MidoriView
*
* Goes back one page in the view.
**/
void
midori_view_go_back (MidoriView* view)
{
g_return_if_fail (MIDORI_IS_VIEW (view));
webkit_web_view_go_back (WEBKIT_WEB_VIEW (view->web_view));
}
/**
* midori_view_can_go_forward
* @view: a #MidoriView
*
* Determines whether the view can go forward.
**/
gboolean
midori_view_can_go_forward (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), FALSE);
if (view->web_view)
return webkit_web_view_can_go_forward (WEBKIT_WEB_VIEW (view->web_view));
else
return FALSE;
}
/**
* midori_view_go_forward
* @view: a #MidoriView
*
* Goes forward one page in the view.
**/
void
midori_view_go_forward (MidoriView* view)
{
g_return_if_fail (MIDORI_IS_VIEW (view));
webkit_web_view_go_forward (WEBKIT_WEB_VIEW (view->web_view));
}
/**
* midori_view_get_previous_page
* @view: a #MidoriView
*
* Determines the previous sub-page in the view.
*
* Return value: an URI, or %NULL
*
* Since: 0.2.3
**/
const gchar*
midori_view_get_previous_page (MidoriView* view)
{
static gchar* uri = NULL;
WebKitWebFrame* web_frame;
JSContextRef js_context;
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
if (!view->web_view)
return NULL;
web_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view->web_view));
js_context = webkit_web_frame_get_global_context (web_frame);
katze_assign (uri, sokoke_js_script_eval (js_context,
"(function (tags) {"
"for (var tag in tags) {"
"var l = document.getElementsByTagName (tag);"
"for (var i in l) { "
"if ((l[i].rel && l[i].rel.toLowerCase () == 'prev') "
" || (l[i].innerHTML"
" && l[i].innerHTML.toLowerCase ().indexOf ('prev') != -1)) "
"{ return l[i].href; } } } return 0; })("
"{ link:'link', a:'a' });", NULL));
return uri && uri[0] != '0' ? uri : NULL;
}
/**
* midori_view_get_next_page
* @view: a #MidoriView
*
* Determines the next sub-page in the view.
*
* Return value: an URI, or %NULL
*
* Since: 0.2.3
**/
const gchar*
midori_view_get_next_page (MidoriView* view)
{
static gchar* uri = NULL;
WebKitWebFrame* web_frame;
JSContextRef js_context;
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
if (!view->web_view)
return NULL;
web_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view->web_view));
js_context = webkit_web_frame_get_global_context (web_frame);
katze_assign (uri, sokoke_js_script_eval (js_context,
"(function (tags) {"
"for (var tag in tags) {"
"var l = document.getElementsByTagName (tag);"
"for (var i in l) { "
"if ((l[i].rel && l[i].rel.toLowerCase () == 'next') "
" || (l[i].innerHTML"
" && l[i].innerHTML.toLowerCase ().indexOf ('next') != -1)) "
"{ return l[i].href; } } } return 0; })("
"{ link:'link', a:'a' });", NULL));
return uri && uri[0] != '0' ? uri : NULL;
}
#if WEBKIT_CHECK_VERSION (1, 1, 5)
static GtkWidget*
midori_view_print_create_custom_widget_cb (GtkPrintOperation* operation,
MidoriView* view)
{
GtkWidget* box;
GtkWidget* button;
box = gtk_vbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (box), 4);
button = katze_property_proxy (view->settings, "print-backgrounds", NULL);
gtk_button_set_label (GTK_BUTTON (button), _("Print background images"));
gtk_widget_set_tooltip_text (button, _("Whether background images should be printed"));
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
gtk_widget_show_all (box);
return box;
}
#endif
static void
midori_view_print_response_cb (GtkWidget* dialog,
gint response,
gpointer data)
{
gtk_widget_destroy (dialog);
}
/**
* midori_view_print
* @view: a #MidoriView
*
* Prints the contents of the view.
**/
void
midori_view_print (MidoriView* view)
{
WebKitWebFrame* frame;
#if WEBKIT_CHECK_VERSION (1, 1, 5)
GtkPrintOperation* operation;
GError* error;
#endif
g_return_if_fail (MIDORI_IS_VIEW (view));
frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view->web_view));
#if WEBKIT_CHECK_VERSION (1, 1, 5)
operation = gtk_print_operation_new ();
gtk_print_operation_set_custom_tab_label (operation, _("Features"));
#if GTK_CHECK_VERSION (2, 18, 0)
gtk_print_operation_set_embed_page_setup (operation, TRUE);
#endif
g_signal_connect (operation, "create-custom-widget",
G_CALLBACK (midori_view_print_create_custom_widget_cb), view);
error = NULL;
webkit_web_frame_print_full (frame, operation,
GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, &error);
g_object_unref (operation);
if (error)
{
GtkWidget* window = gtk_widget_get_toplevel (GTK_WIDGET (view));
GtkWidget* dialog = gtk_message_dialog_new (
gtk_widget_is_toplevel (window) ? GTK_WINDOW (window) : NULL,
GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE, "%s", error->message);
g_error_free (error);
g_signal_connect (dialog, "response",
G_CALLBACK (midori_view_print_response_cb), NULL);
gtk_widget_show (dialog);
}
#else
webkit_web_frame_print (frame);
#endif
}
/**
* midori_view_unmark_text_matches
* @view: a #MidoriView
*
* Unmarks the text matches in the view.
**/
void
midori_view_unmark_text_matches (MidoriView* view)
{
g_return_if_fail (MIDORI_IS_VIEW (view));
webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (view->web_view));
}
/**
* midori_view_search_text
* @view: a #MidoriView
* @text: a string
* @case_sensitive: case sensitivity
* @forward: whether to search forward
*
* Searches a text within the view.
**/
void
midori_view_search_text (MidoriView* view,
const gchar* text,
gboolean case_sensitive,
gboolean forward)
{
g_return_if_fail (MIDORI_IS_VIEW (view));
g_signal_emit (view, signals[SEARCH_TEXT], 0,
webkit_web_view_search_text (WEBKIT_WEB_VIEW (view->web_view),
text, case_sensitive, forward, TRUE), NULL);
}
/**
* midori_view_mark_text_matches
* @view: a #MidoriView
* @text: a string
* @case_sensitive: case sensitivity
*
* Marks all text matches within the view.
**/
void
midori_view_mark_text_matches (MidoriView* view,
const gchar* text,
gboolean case_sensitive)
{
g_return_if_fail (MIDORI_IS_VIEW (view));
webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (view->web_view),
text, case_sensitive, 0);
}
/**
* midori_view_set_highlight_text_matches
* @view: a #MidoriView
* @highlight: whether to highlight matches
*
* Whether to highlight all matches within the view.
**/
void
midori_view_set_highlight_text_matches (MidoriView* view,
gboolean highlight)
{
g_return_if_fail (MIDORI_IS_VIEW (view));
webkit_web_view_set_highlight_text_matches (
WEBKIT_WEB_VIEW (view->web_view), highlight);
}
/**
* midori_view_execute_script
* @view: a #MidoriView
* @script: script code
* @exception: location to store an exception message
*
* Execute a script on the view.
*
* Returns: %TRUE if the script was executed successfully
**/
gboolean
midori_view_execute_script (MidoriView* view,
const gchar* script,
gchar** exception)
{
WebKitWebFrame* web_frame;
JSContextRef js_context;
gchar* script_decoded;
gchar* result;
gboolean success;
g_return_val_if_fail (MIDORI_IS_VIEW (view), FALSE);
g_return_val_if_fail (script != NULL, FALSE);
web_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view->web_view));
js_context = webkit_web_frame_get_global_context (web_frame);
if ((script_decoded = soup_uri_decode (script)))
{
result = sokoke_js_script_eval (js_context, script_decoded, exception);
g_free (script_decoded);
}
else
result = sokoke_js_script_eval (js_context, script, exception);
success = result != NULL;
g_free (result);
return success;
}
/**
* midori_view_get_snapshot
* @view: a #MidoriView
* @width: the desired width
* @height: the desired height
*
* Take a snapshot of the view at the given dimensions. The
* view has to be mapped on the screen.
*
* If width and height are negative, the resulting
* image is going to be optimized for speed.
*
* Returns: a newly allocated #GdkPixbuf
*
* Since: 0.2.1
**/
GdkPixbuf*
midori_view_get_snapshot (MidoriView* view,
gint width,
gint height)
{
GtkWidget* web_view;
GdkWindow* window;
GtkAllocation allocation;
gboolean fast;
gint x, y, w, h;
GdkRectangle rect;
GdkPixmap* pixmap;
GdkEvent event;
gboolean result;
GdkColormap* colormap;
GdkPixbuf* pixbuf;
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
web_view = view->web_view;
window = gtk_widget_get_window (web_view);
g_return_val_if_fail (window != NULL, NULL);
gtk_widget_get_allocation (web_view, &allocation);
x = allocation.x;
y = allocation.y;
w = allocation.width;
h = allocation.height;
/* If width and height are both negative, we try to render faster at
the cost of correctness or beauty. Only a part of the page is
rendered which makes it a lot faster and scaling isn't as nice. */
fast = FALSE;
if (width < 0 && height < 0)
{
width *= -1;
height *= -1;
w = w > 320 ? 320 : w;
h = h > 240 ? 240 : h;
fast = TRUE;
}
rect.x = x;
rect.y = y;
rect.width = w;
rect.height = h;
pixmap = gdk_pixmap_new (window, w, h, gdk_drawable_get_depth (window));
event.expose.type = GDK_EXPOSE;
event.expose.window = pixmap;
event.expose.send_event = FALSE;
event.expose.count = 0;
event.expose.area.x = 0;
event.expose.area.y = 0;
gdk_drawable_get_size (GDK_DRAWABLE (window),
&event.expose.area.width, &event.expose.area.height);
event.expose.region = gdk_region_rectangle (&event.expose.area);
g_signal_emit_by_name (web_view, "expose-event", &event, &result);
colormap = gdk_drawable_get_colormap (pixmap);
pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, colormap, 0, 0,
0, 0, rect.width, rect.height);
g_object_unref (pixmap);
if (width || height)
{
GdkPixbuf* scaled;
if (!width)
width = rect.width;
if (!height)
height = rect.height;
scaled = gdk_pixbuf_scale_simple (pixbuf, width, height,
fast ? GDK_INTERP_NEAREST : GDK_INTERP_TILES);
g_object_unref (pixbuf);
return scaled;
}
return pixbuf;
}
/**
* midori_view_get_web_view
* @view: a #MidoriView
*
* Returns: The #WebKitWebView for this view
*
* Since: 0.2.5
**/
GtkWidget*
midori_view_get_web_view (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
return view->web_view;
}
/**
* midori_view_get_security
* @view: a #MidoriView
*
* Returns: The #MidoriSecurity for this view
*
* Since: 0.2.5
**/
MidoriSecurity
midori_view_get_security (MidoriView* view)
{
g_return_val_if_fail (MIDORI_IS_VIEW (view), MIDORI_SECURITY_NONE);
return view->security;
}
static void
thumb_view_load_status_cb (MidoriView* thumb_view,
GParamSpec* pspec,
MidoriView* view)
{
GdkPixbuf* img;
gchar* file_path;
gchar* thumb_dir;
gchar* thumb_uri;
MidoriBrowser* browser;
gint i;
GtkWidget* tab;
if (midori_view_get_load_status (thumb_view) != MIDORI_LOAD_FINISHED)
return;
gtk_widget_realize (midori_view_get_web_view (MIDORI_VIEW (thumb_view)));
img = midori_view_get_snapshot (MIDORI_VIEW (thumb_view), 240, 160);
thumb_uri = g_object_get_data (G_OBJECT (thumb_view), "thumb-uri");
file_path = sokoke_build_thumbnail_path (thumb_uri);
thumb_dir = g_build_path (G_DIR_SEPARATOR_S, g_get_user_cache_dir (),
PACKAGE_NAME, "thumbnails", NULL);
if (!g_file_test (thumb_dir, G_FILE_TEST_EXISTS))
katze_mkdir_with_parents (thumb_dir, 0700);
gdk_pixbuf_save (img, file_path, "png", NULL, "compression", "7", NULL);
g_object_unref (img);
g_free (thumb_uri);
g_free (file_path);
g_free (thumb_dir);
g_signal_handlers_disconnect_by_func (
thumb_view, thumb_view_load_status_cb, view);
/* Destroying the view here may trigger a WebKitGTK+ 1.1.14 bug */
#if !WEBKIT_CHECK_VERSION (1, 1, 14) || WEBKIT_CHECK_VERSION (1, 1, 15)
gtk_widget_destroy (GTK_WIDGET (thumb_view));
view->thumb_view = NULL;
#endif
browser = midori_browser_get_for_widget (GTK_WIDGET (view));
i = 0;
while ((tab = midori_browser_get_nth_tab (browser, i++)))
if (midori_view_is_blank (MIDORI_VIEW (tab)))
midori_view_reload (MIDORI_VIEW (tab), FALSE);
}
/**
* midori_view_speed_dial_get_thumb
* @view: a #MidoriView
* @dom_id: Id of the shortcut on speed_dial page in wich to inject content
* @url: url of the shortcut
*/
static void
midori_view_speed_dial_get_thumb (MidoriView* view,
gchar* dom_id,
gchar* url)
{
GtkWidget* thumb_view;
MidoriWebSettings* settings;
GtkWidget* browser;
GtkWidget* notebook;
GtkWidget* label;
browser = gtk_widget_get_toplevel (GTK_WIDGET (view));
if (!GTK_IS_WINDOW (browser))
return;
/* What we are doing here is a bit of a hack. In order to render a
thumbnail we need a new view and load the url in it. But it has
to be visible and packed in a container. So we secretly pack it
into the notebook of the parent browser. */
notebook = katze_object_get_object (browser, "notebook");
if (!notebook)
return;
if (!view->thumb_view)
{
view->thumb_view = midori_view_new_with_title (NULL, NULL, FALSE);
gtk_container_add (GTK_CONTAINER (notebook), view->thumb_view);
/* We use an empty label. It's not invisible but at least hard to spot. */
label = gtk_event_box_new ();
gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), view->thumb_view, label);
gtk_widget_show (view->thumb_view);
}
g_object_unref (notebook);
thumb_view = view->thumb_view;
settings = g_object_new (MIDORI_TYPE_WEB_SETTINGS, "enable-scripts", FALSE,
"enable-plugins", FALSE, "auto-load-images", TRUE, NULL);
_midori_view_set_settings (MIDORI_VIEW (thumb_view), settings);
g_object_set_data (G_OBJECT (thumb_view), "thumb-uri", url);
g_signal_connect (thumb_view, "notify::load-status",
G_CALLBACK (thumb_view_load_status_cb), view);
midori_view_set_uri (MIDORI_VIEW (thumb_view), url);
}
/**
* midori_view_speed_dial_save
* @view: a #MidoriView
* @message: message from JavaScript
*
* Save speed_dial settings
*
**/
static void
midori_view_speed_dial_save (MidoriView* view,
const gchar* message)
{
gchar* action;
gchar* config_file;
GKeyFile* key_file;
MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (view));
gchar* msg = g_strdup (message + 16);
gchar** parts = g_strsplit (msg, " ", 4);
gint i;
GtkWidget* tab;
g_object_get (browser, "speed-dial", &key_file, NULL);
action = parts[0];
if (g_str_equal (action, "size"))
{
g_key_file_set_string (key_file, "settings", "rows", parts[2]);
g_key_file_set_string (key_file, "settings", "columns", parts[1]);
}
else if (g_str_equal (action, "thumbsize"))
{
gchar* saved_size;
gchar* thumb_size_type;
guint size = atoi (parts[1]);
if (size == 80)
thumb_size_type = g_strdup ("SMALL");
else if (size == 240)
thumb_size_type = g_strdup ("BIG");
else /* if (size == 160) */
thumb_size_type = g_strdup ("MEDIUM");
saved_size = g_key_file_get_string (key_file, "settings", "size", NULL);
if (saved_size != NULL && g_str_equal (saved_size, thumb_size_type))
{
g_free (action);
g_free (msg);
g_free (thumb_size_type);
g_free (saved_size);
return;
}
g_key_file_set_string (key_file, "settings", "size", thumb_size_type);
g_free (thumb_size_type);
g_free (saved_size);
}
else if (g_str_equal (action, "add") || g_str_equal (action, "rename")
|| g_str_equal (action, "delete"))
{
gchar* tmp = g_strdup (parts[1] + 1);
guint slot_id = atoi (tmp);
gchar* dial_id = g_strdup_printf ("Dial %d", slot_id);
g_free (tmp);
if (g_str_equal (action, "delete"))
{
gchar* uri = g_key_file_get_string (key_file, dial_id, "uri", NULL);
gchar* file_path = sokoke_build_thumbnail_path (uri);
g_key_file_remove_group (key_file, dial_id, NULL);
g_unlink (file_path);
g_free (uri);
g_free (file_path);
}
else if (g_str_equal (action, "add"))
{
g_key_file_set_string (key_file, dial_id, "uri", parts[2]);
g_key_file_set_string (key_file, dial_id, "title", parts[3]);
midori_view_speed_dial_get_thumb (view, parts[1], parts[2]);
}
else if (g_str_equal (action, "rename"))
{
g_key_file_set_string (key_file, dial_id, "title", parts[2]);
}
g_free (dial_id);
}
config_file = g_build_filename (sokoke_set_config_dir (NULL), "speeddial", NULL);
sokoke_key_file_save_to_file (key_file, config_file, NULL);
i = 0;
while ((tab = midori_browser_get_nth_tab (browser, i++)))
if (midori_view_is_blank (MIDORI_VIEW (tab)))
midori_view_reload (MIDORI_VIEW (tab), FALSE);
g_free (msg);
g_free (action);
g_free (config_file);
}