midori/midori/midori-view.c
Christian Dywan 6c4d94942f Render stock:// as pixbufs and pass as data URIs
Stock icons do not match filenames in many icon themes and the
appropriate sizes may not be available. Thus we now always
render the icon through the theme engine and encode it as a
data URI with BASE64/ PNG.

As a side effect, we use stock sizes now instead of pixel sizes,
where 1 means menu size, 4 means button size and 6 dialog size;
the value 16 is translated to 4 to keep existing files working.
2010-05-21 02:23:15 +02:00

5164 lines
171 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);
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;
gboolean speed_dial_in_new_tabs;
gchar* download_manager;
gchar* news_aggregator;
gboolean ask_for_destination_folder;
gboolean middle_click_opens_selection;
gboolean open_tabs_in_the_background;
gboolean close_buttons_on_tabs;
MidoriNewPage open_new_pages_in;
gboolean find_while_typing;
GtkWidget* menu_item;
GtkWidget* tab_label;
/* GtkWidget* tooltip_image; */
GtkWidget* tab_icon;
GtkWidget* tab_title;
GtkWidget* tab_close;
KatzeItem* item;
gint scrollh, scrollv;
gboolean back_forward_set;
KatzeNet* net;
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,
PROP_NET
};
enum {
ACTIVATE_ACTION,
CONSOLE_MESSAGE,
CONTEXT_READY,
ATTACH_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_get_thumb (GtkWidget* web_view,
const gchar* message,
MidoriView* view);
static void
midori_view_speed_dial_save (GtkWidget* web_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);
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);
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,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* 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));
g_object_class_install_property (gobject_class,
PROP_NET,
g_param_spec_object (
"net",
"Net",
"The associated net",
KATZE_TYPE_NET,
flags));
}
static void
midori_view_update_title (MidoriView* view)
{
#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);
#if 1
gtk_widget_set_tooltip_text (view->tab_title, title);
#endif
}
if (view->menu_item)
gtk_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (
view->menu_item))), title);
if (view->item)
katze_item_set_name (view->item, title);
#undef title
}
static void
midori_view_apply_icon (MidoriView* view,
GdkPixbuf* icon,
const gchar* icon_name)
{
if (view->item)
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;
void
katze_net_icon_priv_free (KatzeNetIconPriv* priv)
{
g_free (priv->icon_file);
g_free (priv->icon_uri);
g_free (priv);
}
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;
}
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"))
|| 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] == '/')
{
icon_uri = g_strdup (view->uri);
icon_uri[i] = '\0';
icon_uri = g_strdup_printf ("%s/favicon.ico", icon_uri);
}
else
icon_uri = g_strdup_printf ("%s/favicon.ico", view->uri);
}
icon_file = katze_net_get_cached_path (view->net, 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
{
priv = g_new0 (KatzeNetIconPriv, 1);
priv->icon_file = icon_file;
priv->icon_uri = icon_uri;
priv->view = view;
katze_net_load_uri (view->net, 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)
{
const gchar* uri = webkit_network_request_get_uri (request);
if (g_str_has_prefix (uri, "mailto:") || g_str_has_prefix (uri, "tel:"))
{
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;
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;
g_object_freeze_notify (G_OBJECT (view));
uri = webkit_web_frame_get_uri (web_frame);
g_return_if_fail (uri != NULL);
katze_assign (view->uri, sokoke_format_uri_for_display (uri));
katze_assign (view->icon_uri, NULL);
if (view->item)
{
#if 0
/* Load back forward history from meta data. WebKit does not seem to
respect the order of items, so the feature is unusable. */
if (!view->back_forward_set)
{
WebKitWebBackForwardList* list;
gchar* key;
guint i;
const gchar* data;
WebKitWebHistoryItem* item;
list = webkit_web_view_get_back_forward_list (web_view);
key = g_strdup ("back4");
for (i = 4; i > 0; i--)
{
key[4] = 48 + i;
if ((data = katze_item_get_meta_string (view->item, key)))
{
item = webkit_web_history_item_new_with_data (data, NULL);
webkit_web_back_forward_list_add_item (list, item);
g_object_unref (item);
}
}
#if 0
key[0] = 'f';
key[1] = 'o';
key[2] = 'r';
key[3] = 'e';
for (i = 4; i > 0; i--)
{
key[4] = 48 + i;
item = webkit_web_history_item_new_with_data (data, NULL);
webkit_web_back_forward_list_add_item (list, item);
g_object_unref (item);
}
#endif
g_free (key);
view->back_forward_set = TRUE;
}
#endif
katze_item_set_uri (view->item, uri);
katze_item_set_added (view->item, time (NULL));
}
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");
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, 6)
#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, "stock://"))
{
GdkPixbuf* pixbuf;
const gchar* icon_name = &uri[8] ? &uri[8] : "";
gint icon_size = GTK_ICON_SIZE_MENU;
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];
}
}
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
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
}
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* 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
result = sokoke_replace_variables (template,
"{title}", title,
"{message}", message,
"{description}", description,
"{tryagain}", try_again,
"{res}", res_root,
"{stock}", stock_root,
NULL);
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 = g_strdup_printf (_("Error - %s"), uri);
gchar* message = g_strdup_printf (_("The page '%s' couldn't be loaded."), uri);
gboolean 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)
{
GtkAdjustment* adjustment = katze_object_get_object (view->scrolled_window, "hadjustment");
gtk_adjustment_set_value (adjustment, view->scrollh);
g_object_unref (adjustment);
}
view->scrollh = -3;
}
if (view->scrollv > -2)
{
if (view->scrollv > 0)
{
GtkAdjustment* adjustment = katze_object_get_object (view->scrolled_window, "vadjustment");
gtk_adjustment_set_value (adjustment, view->scrollv);
g_object_unref (adjustment);
}
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)
{
katze_assign (view->icon_uri, katze_object_get_string (web_view, "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)
{
#if 0
if (view->item)
{
/* Save back forward history as meta data. This is disabled
because we can't reliably restore these atm. */
WebKitWebView* web_view;
WebKitWebBackForwardList* list;
GList* back;
GList* forward;
web_view = WEBKIT_WEB_VIEW (view->web_view);
list = webkit_web_view_get_back_forward_list (web_view);
back = webkit_web_back_forward_list_get_back_list_with_limit (list, 5);
forward = webkit_web_back_forward_list_get_forward_list_with_limit (list, 5);
guint i;
WebKitWebHistoryItem* item;
gchar* key = g_strdup ("back0");
i = 0;
while ((item = g_list_nth_data (back, i++)))
{
katze_item_set_meta_string (view->item, key,
webkit_web_history_item_get_uri (item));
key[4] = 48 + i;
}
#if 0
key[0] = 'f';
key[1] = 'o';
key[2] = 'r';
key[3] = 'e';
key[4] = 48;
i = 0;
while ((item = g_list_nth_data (forward, i++)))
{
katze_item_set_meta_string (view->item, key,
webkit_web_history_item_get_uri (item));
key[4] = 48 + i;
}
#endif
g_free (key);
}
#endif
g_object_get (web_view, "uri", &view->uri, NULL);
g_object_notify (G_OBJECT (view), "uri");
}
static void
webkit_web_view_notify_title_cb (WebKitWebView* web_view,
GParamSpec* pspec,
MidoriView* view)
{
g_object_get (web_view, "title", &view->title, NULL);
midori_view_update_title (view);
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 (2, 18, 0) && defined (HAVE_LIBSOUP_2_29_3))
sokoke_prefetch_uri (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);
}
#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;
link_uri = midori_view_get_link_uri (MIDORI_VIEW (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;
if (event->keyval == '.' || event->keyval == '/' || event->keyval == GDK_KP_Divide)
character = '\0';
else if (view->find_while_typing)
character = gdk_unicode_to_keyval (event->keyval);
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_set_clipboard (GtkWidget* widget,
const gchar* text)
{
GdkDisplay* display = gtk_widget_get_display (widget);
GtkClipboard* clipboard;
clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
gtk_clipboard_set_text (clipboard, text, -1);
clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY);
gtk_clipboard_set_text (clipboard, text, -1);
}
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)
{
/* FIXME: Use the same binary that is running right now */
sokoke_spawn_program ("midori -a", view->link_uri, FALSE);
}
static void
midori_web_view_menu_link_copy_activate_cb (GtkWidget* widget,
MidoriView* view)
{
midori_web_view_set_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);
if (view->ask_for_destination_folder)
g_object_set_data (G_OBJECT (download), "save-as-download", (void*)0xdeadbeef);
g_signal_emit (view, signals[DOWNLOAD_REQUESTED], 0, download, &handled);
if (!view->ask_for_destination_folder)
webkit_download_start (download);
}
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");
midori_web_view_set_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);
if (view->ask_for_destination_folder)
g_object_set_data (G_OBJECT (download), "save-as-download", (void*)0xdeadbeef);
g_signal_emit (view, signals[DOWNLOAD_REQUESTED], 0, download, &handled);
if (!view->ask_for_destination_folder)
webkit_download_start (download);
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");
midori_web_view_set_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);
if (view->ask_for_destination_folder)
g_object_set_data (G_OBJECT (download), "save-as-download", (void*)0xdeadbeef);
g_signal_emit (view, signals[DOWNLOAD_REQUESTED], 0, download, &handled);
if (!view->ask_for_destination_folder)
webkit_download_start (download);
g_free (uri);
}
static void
midori_web_view_menu_video_download_activate_cb (GtkWidget* widget,
MidoriView* view)
{
gchar* uri = katze_object_get_string (view->hit_test, "media-uri");
sokoke_spawn_program (view->download_manager, uri, FALSE);
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)
{
midori_web_view_set_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_web_view_menu_download_activate_cb (GtkWidget* widget,
MidoriView* view)
{
sokoke_spawn_program (view->download_manager, view->link_uri, FALSE);
}
#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);
}
#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;
GdkEventButton event;
WebKitHitTestResultContext context;
gboolean is_image;
gboolean is_media;
gdk_window_get_pointer (view->web_view->window, &x, &y, NULL);
event.x = x;
event.y = y;
katze_object_assign (view->hit_test,
webkit_web_view_get_hit_test_result (web_view, &event));
context = katze_object_get_int (view->hit_test, "context");
/* Ensure view->link_uri is correct. */
katze_assign (view->link_uri,
katze_object_get_string (view->hit_test, "link-uri"));
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,
view->ask_for_destination_folder ? _("_Save Link destination")
: _("_Download Link destination"), NULL,
G_CALLBACK (midori_web_view_menu_save_activate_cb), widget);
if (view->download_manager && *view->download_manager)
midori_view_insert_menu_item (menu_shell, -1,
_("Download with Download _Manager"), STOCK_TRANSFER,
G_CALLBACK (midori_web_view_menu_download_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,
view->ask_for_destination_folder ? _("Save I_mage")
: _("Download 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 (view->download_manager && *view->download_manager)
midori_view_insert_menu_item (menu_shell, -1,
_("Download with Download _Manager"), STOCK_TRANSFER,
G_CALLBACK (midori_web_view_menu_video_download_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));
gtk_label_set_label (GTK_LABEL (label), _("_Download Link destination"));
#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,
_("_Save Link destination"), NULL,
G_CALLBACK (midori_web_view_menu_save_as_activate_cb), widget);
#endif
if (view->download_manager && *view->download_manager)
midori_view_insert_menu_item (menu_shell, 4,
_("Download with Download _Manager"), STOCK_TRANSFER,
G_CALLBACK (midori_web_view_menu_download_activate_cb), widget);
}
#endif
if (!view->link_uri && has_selection)
{
GtkWidget* window;
window = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
i = 0;
if (katze_object_has_property (window, "search-engines"))
{
KatzeArray* search_engines;
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);
search_engines = katze_object_get_object (window, "search-engines");
while ((item = katze_array_get_nth_item (search_engines, i++)))
{
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 - 1);
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);
}
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);
#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 (!g_object_get_data (G_OBJECT (browser), "midori-toolbars-visible"))
{
menuitem = sokoke_action_create_popup_menu_item (
gtk_action_group_get_action (actions, "Menubar"));
gtk_menu_shell_append (menu_shell, menuitem);
}
#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 (view->speed_dial_in_new_tabs && !midori_view_is_blank (view))
{
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);
gtk_widget_set_no_show_all (menuitem, TRUE);
#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 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. */
gdk_window_get_pointer (web_view->window, &x, &y, NULL);
event.any.type = GDK_MOTION_NOTIFY;
event.any.window = web_view->window;
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.any.window = web_view->window;
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 = g_object_new (MIDORI_TYPE_VIEW,
"net", view->net,
"settings", view->settings,
NULL);
midori_view_construct_web_view (new_view);
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;
#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 = 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);
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))
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("File Type: '%s'"), mime_type);
else
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("File Type: %s ('%s')"), description, mime_type);
g_free (description);
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;
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-get-thumbnail", 22))
midori_view_speed_dial_get_thumb (web_view, message, view);
else if (!strncmp (message, "speed_dial-save", 13))
midori_view_speed_dial_save (web_view, message);
else
g_signal_emit (view, signals[CONSOLE_MESSAGE], 0, message, line, source_id);
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);
if (view->item)
katze_item_set_meta_integer (view->item, "scrollh", value);
}
static void
midori_view_notify_hadjustment_cb (MidoriView* view,
GParamSpec* pspec,
gpointer data)
{
GtkAdjustment* hadjustment = katze_object_get_object (view->scrolled_window, "hadjustment");
g_signal_connect (hadjustment, "notify::value",
G_CALLBACK (midori_view_hadjustment_notify_value_cb), view);
g_object_unref (hadjustment);
}
static void
midori_view_vadjustment_notify_value_cb (GtkAdjustment* vadjustment,
GParamSpec* pspec,
MidoriView* view)
{
gint value = (gint)gtk_adjustment_get_value (vadjustment);
if (view->item)
katze_item_set_meta_integer (view->item, "scrollv", value);
}
static void
midori_view_notify_vadjustment_cb (MidoriView* view,
GParamSpec* pspec,
gpointer data)
{
GtkAdjustment* vadjustment = katze_object_get_object (view->scrolled_window, "vadjustment");
g_signal_connect (vadjustment, "notify::value",
G_CALLBACK (midori_view_vadjustment_notify_value_cb), view);
g_object_unref (vadjustment);
}
void
katze_net_object_maybe_unref (gpointer object)
{
if (object)
g_object_unref (object);
}
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 = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, katze_net_object_maybe_unref);
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->item = NULL;
view->scrollh = view->scrollv = -2;
view->back_forward_set = FALSE;
view->download_manager = NULL;
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_ETCHED_OUT);
gtk_container_add (GTK_CONTAINER (view), view->scrolled_window);
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);
}
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);
if (view->item)
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);
katze_assign (view->uri, NULL);
katze_assign (view->title, NULL);
katze_object_assign (view->icon, NULL);
katze_assign (view->icon_uri, NULL);
g_hash_table_destroy (view->memory);
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->download_manager, NULL);
katze_assign (view->news_aggregator, NULL);
katze_object_assign (view->net, 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:
katze_assign (view->title, g_value_dup_string (value));
midori_view_update_title (view);
break;
case PROP_MINIMIZED:
view->minimized = g_value_get_boolean (value);
if (view->item)
{
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;
case PROP_NET:
katze_object_assign (view->net, g_value_dup_object (value));
if (!view->net)
view->net = katze_net_new ();
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;
case PROP_NET:
g_value_set_object (value, view->net);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
midori_view_focus_in_event (GtkWidget* widget,
GdkEventFocus* event)
{
MidoriView* view = MIDORI_VIEW (widget);
/* Always propagate focus to the child web view,
* create it if it's not there yet. */
if (!view->web_view)
midori_view_construct_web_view (view);
gtk_widget_grab_focus (view->web_view);
return TRUE;
}
/**
* midori_view_new:
* @net: a #KatzeNet
*
* Creates a new view.
*
* Return value: a new #MidoriView
**/
GtkWidget*
midori_view_new (KatzeNet* net)
{
g_return_val_if_fail (!net || KATZE_IS_NET (net), NULL);
return g_object_new (MIDORI_TYPE_VIEW, "net", net, NULL);
}
static void
_midori_view_update_settings (MidoriView* view)
{
gboolean zoom_text_and_images, kinetic_scrolling;
g_object_get (view->settings,
"speed-dial-in-new-tabs", &view->speed_dial_in_new_tabs,
"download-manager", &view->download_manager,
"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,
"ask-for-destination-folder", &view->ask_for_destination_folder,
"middle-click-opens-selection", &view->middle_click_opens_selection,
"open-tabs-in-the-background", &view->open_tabs_in_the_background,
"find-while-typing", &view->find_while_typing,
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);
}
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 ("speed-dial-in-new-tabs"))
{
view->speed_dial_in_new_tabs = g_value_get_boolean (&value);
}
else if (name == g_intern_string ("download-manager"))
{
katze_assign (view->download_manager, g_value_dup_string (&value));
}
else 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 ("ask-for-destination-folder"))
{
view->ask_for_destination_folder = g_value_get_boolean (&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);
}
else if (name == g_intern_string ("find-while-typing"))
{
view->find_while_typing = 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 (!settings || MIDORI_IS_WEB_SETTINGS (settings));
if (view->settings == settings)
return;
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)
{
g_object_ref (settings);
if (view->web_view)
g_object_set (view->web_view, "settings", settings, NULL);
_midori_view_update_settings (view);
g_signal_connect (settings, "notify",
G_CALLBACK (midori_view_settings_notify_cb), view);
}
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;
GtkWidget* window;
GtkWidget* toplevel;
GdkScreen* screen;
gint width, height;
GtkIconTheme* icon_theme;
GdkPixbuf* icon;
GdkPixbuf* gray_icon;
title = g_strdup_printf (_("Inspect page - %s"), "");
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), title);
g_free (title);
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_attach_window_cb (gpointer inspector,
MidoriView* view)
{
GtkWidget* inspector_view = katze_object_get_object (inspector, "web-view");
g_signal_emit (view, signals[ATTACH_INSPECTOR], 0, inspector_view);
g_object_unref (inspector_view);
return TRUE;
}
static gboolean
midori_view_web_inspector_detach_window_cb (gpointer inspector,
MidoriView* view)
{
GtkWidget* inspector_view = katze_object_get_object (inspector, "web-view");
GtkWidget* parent = gtk_widget_get_parent (inspector_view);
if (GTK_IS_WINDOW (parent))
return FALSE;
gtk_widget_hide (parent);
gtk_container_remove (GTK_CONTAINER (parent), inspector_view);
midori_view_web_inspector_construct_window (inspector,
WEBKIT_WEB_VIEW (view->web_view), inspector_view, view);
g_object_unref (inspector_view);
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
"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::window-object-cleared",
webkit_web_view_window_object_cleared_cb, view,
"signal::create-web-view",
webkit_web_view_create_web_view_cb, view,
"signal::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 = katze_object_get_object (view->web_view, "web-inspector");
g_object_connect (inspector,
"signal::inspect-web-view",
midori_view_web_inspector_inspect_web_view_cb, view,
"signal::attach-window",
midori_view_web_inspector_attach_window_cb, view,
"signal::detach-window",
midori_view_web_inspector_detach_window_cb, view,
NULL);
g_object_unref (inspector);
}
/**
* midori_view_set_uri:
* @view: a #MidoriView
*
* Opens the specified URI in the view.
**/
void
midori_view_set_uri (MidoriView* view,
const gchar* uri)
{
gchar* data;
g_return_if_fail (MIDORI_IS_VIEW (view));
/* Treat "about:blank" and "" equally, see midori_view_is_blank(). */
if (!uri || !strcmp (uri, "about:blank")) uri = "";
if (1)
{
if (!view->web_view)
midori_view_construct_web_view (view);
if (view->speed_dial_in_new_tabs && !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* body_fname;
gchar* stock_root;
gchar* filepath;
katze_assign (view->uri, g_strdup (""));
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
body_fname = g_build_filename (sokoke_set_config_dir (NULL),
"speeddial.json", NULL);
if (g_access (body_fname, F_OK) != 0)
{
filepath = sokoke_find_data_filename ("midori/res/speeddial.json");
if (g_file_get_contents (filepath,
&speed_dial_body, NULL, NULL))
g_file_set_contents (body_fname, speed_dial_body, -1, NULL);
else
speed_dial_body = g_strdup ("");
g_free (filepath);
}
else
g_file_get_contents (body_fname, &speed_dial_body, NULL, NULL);
data = sokoke_replace_variables (speed_dial_head,
"{res}", res_root,
"{stock}", stock_root,
"{json_data}", speed_dial_body,
"{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_width}", _("Set number of columns"),
"{enter_dial_width}", _("Enter number of columns:"),
"{set_shortcut_count}", _("Set number of shortcuts"),
"{enter_shortcut_count}", _("Enter number of shortcuts:"), NULL);
midori_view_load_alternate_string (view,
data, 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 (body_fname);
}
/* 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: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");
#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>"
"<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>sqlite3</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>"
"</body></html>",
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_SQLITE ? "Yes" : "No",
HAVE_LIBNOTIFY ? "Yes" : "No",
HAVE_LIBIDN ? "Yes" : "No",
HAVE_UNIQUE ? "Yes" : "No",
HAVE_HILDON ? "Yes" : "No",
sys_name, ident);
g_free (command_line);
g_free (ident);
g_free (sys_name);
}
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);
g_object_notify (G_OBJECT (view), "uri");
if (view->item)
katze_item_set_uri (view->item, uri);
return;
}
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);
g_free (exception);
}
}
else if (g_str_has_prefix (uri, "mailto:")
|| g_str_has_prefix (uri, "tel:")
|| g_str_has_prefix (uri, "callto:"))
{
sokoke_show_uri (NULL, uri, GDK_CURRENT_TIME, NULL);
}
else
{
katze_assign (view->uri, sokoke_format_uri_for_display (uri));
g_object_notify (G_OBJECT (view), "uri");
if (view->item)
katze_item_set_uri (view->item, 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_window_new_cb (GtkWidget* menuitem,
GtkWidget* view)
{
g_signal_emit (view, signals[NEW_WINDOW], 0,
midori_view_get_display_uri (MIDORI_VIEW (view)));
}
static void
midori_view_tab_label_menu_duplicate_tab_cb (GtkWidget* menuitem,
MidoriView* view)
{
MidoriNewView where = MIDORI_NEW_VIEW_TAB;
GtkWidget* new_view = g_object_new (MIDORI_TYPE_VIEW,
"net", view->net, "settings", view->settings, NULL);
midori_view_set_uri (MIDORI_VIEW (new_view),
midori_view_get_display_uri (view));
gtk_widget_show (new_view);
g_signal_emit (view, signals[NEW_VIEW], 0, new_view, where);
}
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_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 ? _("_Restore Tab") : _("_Minimize Tab"));
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_release_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)
{
gint width;
sokoke_widget_get_text_size (label, "M", &width, NULL);
if (angle == 0.0 || angle == 360.0)
{
gtk_widget_set_size_request (label, width * size, -1);
if (gtk_label_get_ellipsize (GTK_LABEL (label)) != PANGO_ELLIPSIZE_START)
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
}
else
{
gtk_widget_set_size_request (label, -1, width * size);
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 */
}
}
#if 0
static gboolean
midori_view_tab_label_query_tooltip_cb (GtkWidget* tab_label,
gint x,
gint y,
gboolean keyboard,
GtkTooltip* tooltip,
MidoriView* view)
{
if (view->speed_dial_in_new_tabs)
gtk_tooltip_set_icon (tooltip, midori_view_get_snapshot (view, -160, -107));
else
gtk_tooltip_set_text (tooltip, midori_view_get_display_title (view));
return TRUE;
}
#endif
/**
* 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;
}
/**
* 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 HAVE_OSX
gtk_box_pack_end (GTK_BOX (hbox), view->tab_icon, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (hbox), view->tab_title, FALSE, 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, FALSE, TRUE, 0);
gtk_box_pack_end (GTK_BOX (hbox), align, FALSE, FALSE, 0);
#endif
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-release-event",
G_CALLBACK (midori_view_tab_label_button_release_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;
#if 0
gtk_widget_set_has_tooltip (view->tab_label, TRUE);
g_signal_connect (view->tab_label, "query-tooltip",
G_CALLBACK (midori_view_tab_label_query_tooltip_cb), view);
#endif
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);
}
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 is created on the first call and will be updated to reflect
* changes to the title and uri automatically.
*
* Return value: the proxy #KatzeItem
**/
KatzeItem*
midori_view_get_proxy_item (MidoriView* view)
{
const gchar* uri;
const gchar* title;
g_return_val_if_fail (MIDORI_IS_VIEW (view), NULL);
if (!view->item)
{
view->item = katze_item_new ();
uri = midori_view_get_display_uri (view);
katze_item_set_uri (view->item, uri);
title = midori_view_get_display_title (view);
katze_item_set_name (view->item, title);
g_signal_connect (view->item, "meta-data-changed",
G_CALLBACK (midori_view_item_meta_data_changed), view);
}
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 && !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 && !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));
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);
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 (l) { for (i in l) "
"if ((l[i].rel && l[i].rel == 'prev') "
" || (l[i].innerHTML"
" && l[i].innerHTML.toLowerCase ().indexOf ('prev') != -1)) "
"{ return l[i].href; } return 0; })("
"document.getElementsByTagName ('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);
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 (l) { for (i in l) "
"if ((l[i].rel && l[i].rel == 'next') "
" || (l[i].innerHTML"
" && l[i].innerHTML.toLowerCase ().indexOf ('next') != -1)) "
"{ return l[i].href; } return 0; })("
"document.getElementsByTagName ('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 = gtk_check_button_new ();
g_object_set_data (G_OBJECT (operation), "print-backgrounds", button);
gtk_button_set_label (GTK_BUTTON (button), _("Print background images"));
gtk_widget_set_tooltip_text (button, _("Whether background images should be printed"));
if (katze_object_get_boolean (view->settings, "print-backgrounds"))
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
gtk_widget_show_all (box);
return box;
}
static void
midori_view_print_custom_widget_apply_cb (GtkPrintOperation* operation,
GtkWidget* widget,
MidoriView* view)
{
GtkWidget* button;
button = g_object_get_data (G_OBJECT (operation), "print-backgrounds");
g_object_set (view->settings,
"print-backgrounds",
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)),
NULL);
}
#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);
g_signal_connect (operation, "custom-widget-apply",
G_CALLBACK (midori_view_print_custom_widget_apply_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;
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;
g_return_val_if_fail (web_view->window, NULL);
x = web_view->allocation.x;
y = web_view->allocation.y;
w = web_view->allocation.width;
h = web_view->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 (web_view->window, w, h,
gdk_drawable_get_depth (web_view->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 (web_view->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_content;
gchar* encoded;
gchar* dom_id;
gchar* js;
gsize sz;
if (katze_object_get_enum (thumb_view, "load-status") != MIDORI_LOAD_FINISHED)
return;
img = midori_view_get_snapshot (MIDORI_VIEW (thumb_view), 160, 107);
gdk_pixbuf_save_to_buffer (img, &file_content, &sz, "png", NULL, "compression", "7", NULL);
encoded = g_base64_encode ((guchar *)file_content, sz );
/* Call Javascript function to replace shortcut's content */
dom_id = g_object_get_data (G_OBJECT (thumb_view), "dom-id");
js = g_strdup_printf ("setThumbnail('%s','%s','%s');",
dom_id, encoded, thumb_view->uri);
webkit_web_view_execute_script (WEBKIT_WEB_VIEW (view->web_view), js);
free (js);
g_object_unref (img);
g_free (dom_id);
g_free (encoded);
g_free (file_content);
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
}
/**
* midori_view_speed_dial_inject_thumb
* @view: a #MidoriView
* @filename: filename of the thumbnail
* @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_inject_thumb (MidoriView* view,
gchar* filename,
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 (view->net);
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), "dom-id", dom_id);
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_get_thumb
* @web_view: a #WebkitView
* @message: Console log data
*
* Load a thumbnail, and set the DOM
*
* message[0] == console message call
* message[1] == shortcut id in the DOM
* message[2] == shortcut uri
*
**/
static void
midori_view_speed_dial_get_thumb (GtkWidget* web_view,
const gchar* message,
MidoriView* view)
{
gchar** t_data = g_strsplit (message," ", 4);
if (t_data[1] == NULL || t_data[2] == NULL )
return;
midori_view_speed_dial_inject_thumb (view, NULL,
g_strdup (t_data[1]), g_strdup (t_data[2]));
g_strfreev (t_data);
}
/**
* midori_view_speed_dial_save
* @web_view: a #WebkitView
*
* Save speed_dial DOM structure to body template
*
**/
static void
midori_view_speed_dial_save (GtkWidget* web_view,
const gchar* message)
{
gchar* json = g_strdup (message + 15);
gchar* fname = g_build_filename (sokoke_set_config_dir (NULL),
"speeddial.json", NULL);
GRegex* reg_double = g_regex_new ("\\\\\"", 0, 0, NULL);
gchar* safe = g_regex_replace_literal (reg_double, json, -1, 0, "\\\\\"", 0, NULL);
g_file_set_contents (fname, safe, -1, NULL);
g_free (fname);
g_free (json);
g_free (safe);
g_regex_unref (reg_double);
}