<commit_before>/*
 Copyright (C) 2007-2008 <NAME> <<EMAIL>>

 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.
*/

#include "midori-webview.h"

#include "global.h"
#include "sokoke.h"

#include <webkit/webkitwebframe.h>
#include <string.h>

// This is unstable API, so we need to declare it
gchar*
webkit_web_view_get_selected_text (WebKitWebView* web_view);

G_DEFINE_TYPE (MidoriWebView, midori_web_view, WEBKIT_TYPE_WEB_VIEW)

struct _MidoriWebViewPrivate
{
    GtkWidget* tab_icon;
    GtkWidget* tab_label;
    GtkWidget* tab_close;
    GdkPixbuf* icon;
    gchar* uri;
    gchar* title;
    gboolean is_loading;
    gint progress;
    gchar* statusbar_text;
    gchar* link_uri;

    gint tab_label_size;
    gboolean close_button;
    gboolean middle_click_goto;
    MidoriWebSettings* settings;

    GtkWidget* proxy_menu_item;
    GtkWidget* proxy_tab_label;
    KatzeXbelItem* proxy_xbel_item;
};

#define MIDORI_WEB_VIEW_GET_PRIVATE(obj) \
    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
     MIDORI_TYPE_WEB_VIEW, MidoriWebViewPrivate))

enum
{
    PROP_0,

    PROP_ICON,
    PROP_URI,
    PROP_TITLE,
    PROP_STATUSBAR_TEXT,
    PROP_SETTINGS
};

enum {
    LOAD_STARTED,
    PROGRESS_STARTED,
    PROGRESS_CHANGED,
    PROGRESS_DONE,
    LOAD_DONE,
    ELEMENT_MOTION,
    CLOSE,
    NEW_TAB,
    NEW_WINDOW,

    LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];

static void
midori_web_view_finalize (GObject* object);

static void
midori_web_view_set_property (GObject* object,
                              guint prop_id,
                              const GValue* value,
                              GParamSpec* pspec);

static void
midori_web_view_get_property (GObject* object,
                              guint prop_id,
                              GValue* value,
                              GParamSpec* pspec);

/*static WebKitWebView*
midori_web_view_create_web_view (WebKitWebView* web_view)
{
    MidoriWebView* new_web_view = NULL;
    g_signal_emit (web_view, signals[NEW_WINDOW], 0, &new_web_view);
    if (new_web_view)
        return WEBKIT_WEB_VIEW (new_web_view);
    return WEBKIT_WEB_VIEW (midori_web_view_new ());
}*/

static void
midori_web_view_class_init (MidoriWebViewClass* class)
{
    signals[PROGRESS_STARTED] = g_signal_new (
        "progress-started",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, progress_started),
        0,
        NULL,
        g_cclosure_marshal_VOID__INT,
        G_TYPE_NONE, 1,
        G_TYPE_INT);

    signals[PROGRESS_CHANGED] = g_signal_new (
        "progress-changed",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, progress_changed),
        0,
        NULL,
        g_cclosure_marshal_VOID__INT,
        G_TYPE_NONE, 1,
        G_TYPE_INT);

    signals[PROGRESS_DONE] = g_signal_new (
        "progress-done",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, progress_done),
        0,
        NULL,
        g_cclosure_marshal_VOID__INT,
        G_TYPE_NONE, 1,
        G_TYPE_INT);

    signals[LOAD_DONE] = g_signal_new (
        "load-done",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, load_done),
        0,
        NULL,
        g_cclosure_marshal_VOID__OBJECT,
        G_TYPE_NONE, 1,
        WEBKIT_TYPE_WEB_FRAME);

    signals[ELEMENT_MOTION] = g_signal_new(
        "element-motion",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, element_motion),
        0,
        NULL,
        g_cclosure_marshal_VOID__STRING,
        G_TYPE_NONE, 1,
        G_TYPE_STRING);

    signals[CLOSE] = g_signal_new(
        "close",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, close),
        0,
        NULL,
        g_cclosure_marshal_VOID__VOID,
        G_TYPE_NONE, 0);

    signals[NEW_TAB] = g_signal_new(
        "new-tab",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, new_tab),
        0,
        NULL,
        g_cclosure_marshal_VOID__STRING,
        G_TYPE_NONE, 1,
        G_TYPE_STRING);

    signals[NEW_WINDOW] = g_signal_new(
        "new-window",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, new_window),
        0,
        NULL,
        g_cclosure_marshal_VOID__STRING,
        G_TYPE_NONE, 1,
        G_TYPE_STRING);

    /*WEBKIT_WEB_VIEW_CLASS (class)->create_web_view = g_signal_new ("create-web-view",
            G_TYPE_FROM_CLASS(class),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            G_STRUCT_OFFSET(MidoriWebViewClass, create_web_view),
            0,
            NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE, 1,
            MIDORI_TYPE_WEB_VIEW);*/

    GObjectClass* gobject_class = G_OBJECT_CLASS (class);
    gobject_class->finalize = midori_web_view_finalize;
    gobject_class->set_property = midori_web_view_set_property;
    gobject_class->get_property = midori_web_view_get_property;

    GParamFlags flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT;

    g_object_class_install_property (gobject_class,
                                     PROP_ICON,
                                     g_param_spec_object (
                                     "icon",
                                     "Icon",
                                     _("The icon of the currently loaded page"),
                                     GDK_TYPE_PIXBUF,
                                     G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class,
                                     PROP_URI,
                                     g_param_spec_string (
                                     "uri",
                                     "Uri",
                                     _("The URI of the currently loaded page"),
                                     "",
                                     flags));

    g_object_class_install_property (gobject_class,
                                     PROP_TITLE,
                                     g_param_spec_string (
                                     "title",
                                     "Title",
                                     _("The title of the currently loaded page"),
                                     NULL,
                                     flags));

    g_object_class_install_property (gobject_class,
                                     PROP_STATUSBAR_TEXT,
                                     g_param_spec_string (
                                     "statusbar-text",
                                     "Statusbar Text",
                                     _("The text that is displayed in the statusbar"),
                                     "",
                                     flags));

    g_object_class_install_property (gobject_class,
                                     PROP_SETTINGS,
                                     g_param_spec_object (
                                     "settings",
                                     "Settings",
                                     _("The associated settings"),
                                     MIDORI_TYPE_WEB_SETTINGS,
                                     G_PARAM_READWRITE));

    g_type_class_add_private (class, sizeof (MidoriWebViewPrivate));
}

/*static void
webkit_web_view_load_started (MidoriWebView* web_view,
                              WebKitWebFrame* web_frame)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    priv->is_loading = TRUE;
    priv->progress = -1;
    katze_throbber_set_animated(KATZE_THROBBER(priv->tab_icon), TRUE);
}*/

static void
_midori_web_view_set_uri (MidoriWebView* web_view,
                          const gchar*   uri)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    katze_assign (priv->uri, g_strdup (uri));
    if (priv->proxy_xbel_item)
    {
        const gchar* uri = midori_web_view_get_display_uri (web_view);
        katze_xbel_bookmark_set_href (priv->proxy_xbel_item, uri);
    }
    g_object_set (web_view, "title", NULL, NULL);
}

static void
webkit_web_view_load_committed (MidoriWebView*  web_view,
                                WebKitWebFrame* web_frame)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    priv->progress = 0;
    const gchar* uri = webkit_web_frame_get_uri (web_frame);
    _midori_web_view_set_uri (web_view, uri);
}

static void
webkit_web_view_load_started (MidoriWebView*  web_view,
                              WebKitWebFrame* web_frame)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    // FIXME: This is a hack, until signals are fixed upstream
    priv->is_loading = TRUE;
    if (priv->tab_icon)
        katze_throbber_set_animated (KATZE_THROBBER (priv->tab_icon), TRUE);

    priv->progress = 0;
    g_signal_emit (web_view, signals[PROGRESS_STARTED], 0, priv->progress);
}

static void
webkit_web_view_progress_changed (MidoriWebView* web_view, gint progress)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    priv->progress = progress;
    g_signal_emit (web_view, signals[PROGRESS_CHANGED], 0, priv->progress);
}

static void
webkit_web_view_load_finished (MidoriWebView* web_view)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    priv->progress = 100;
    g_signal_emit (web_view, signals[PROGRESS_DONE], 0, priv->progress);
}

static void
webkit_web_frame_load_done (WebKitWebFrame* web_frame, gboolean success,
                            MidoriWebView*  web_view)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    priv->is_loading = FALSE;
    priv->progress = -1;
    if (priv->tab_icon)
        katze_throbber_set_animated (KATZE_THROBBER (priv->tab_icon), FALSE);
    g_signal_emit (web_view, signals[LOAD_DONE], 0, web_frame);
}

static void
webkit_web_view_title_changed (MidoriWebView*  web_view,
                               WebKitWebFrame* web_frame, const gchar* title)
{
    g_object_set (web_view, "title", title, NULL);
}

static void
webkit_web_view_statusbar_text_changed (MidoriWebView*  web_view,
                                        const gchar*    text)
{
    g_object_set (web_view, "statusbar-text", text, NULL);
}

static void
webkit_web_view_hovering_over_link (MidoriWebView* web_view,
                                    const gchar*   tooltip,
                                    const gchar*   link_uri)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    katze_assign (priv->link_uri, g_strdup (link_uri));
    g_signal_emit (web_view, signals[ELEMENT_MOTION], 0, link_uri);
}

static gboolean
gtk_widget_button_press_event (MidoriWebView*  web_view,
                               GdkEventButton* event)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    GdkModifierType state = (GdkModifierType)0;
    gint x, y;
    gdk_window_get_pointer (NULL, &x, &y, &state);
    switch (event->button)
    {
    case 1:
        if (!priv->link_uri)
            return FALSE;
        if (state & GDK_SHIFT_MASK)
        {
            // Open link in new window
            g_signal_emit (web_view, signals[NEW_WINDOW], 0, priv->link_uri);
            return TRUE;
        }
        else if(state & GDK_MOD1_MASK)
        {
            // Open link in new tab
            g_signal_emit (web_view, signals[NEW_TAB], 0, priv->link_uri);
            return TRUE;
        }
        break;
    case 2:
        if (state & GDK_CONTROL_MASK)
        {
            // FIXME: Reset font multiplier or zoom level
            return TRUE;
        }
        else
        {
            if (!priv->link_uri)
                return FALSE;
            // Open link in new tab
            g_signal_emit (web_view, signals[NEW_TAB], 0, priv->link_uri);
            return TRUE;
        }
        break;
    case 3:
        return FALSE;
    }
    return FALSE;
}

static gboolean
gtk_widget_button_press_event_after (MidoriWebView*  web_view,
                                     GdkEventButton* event)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    if (event->button == 2 && priv->middle_click_goto)
    {
        GtkClipboard* clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
        gchar* uri = gtk_clipboard_wait_for_text (clipboard);
        if (uri && strchr (uri, '.') && !strchr (uri, ' '))
        {
            g_object_set (web_view, "uri", uri, NULL);
            g_free (uri);
            return TRUE;
        }
    }
    return FALSE;
}

static gboolean
gtk_widget_scroll_event (MidoriWebView*  web_view,
                         GdkEventScroll* event)
{<commit_msg>Open selection in new tab via Control.<commit_after>/*
 Copyright (C) 2007-2008 <NAME> <<EMAIL>>

 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.
*/

#include "midori-webview.h"

#include "global.h"
#include "sokoke.h"

#include <webkit/webkitwebframe.h>
#include <string.h>

// This is unstable API, so we need to declare it
gchar*
webkit_web_view_get_selected_text (WebKitWebView* web_view);

G_DEFINE_TYPE (MidoriWebView, midori_web_view, WEBKIT_TYPE_WEB_VIEW)

struct _MidoriWebViewPrivate
{
    GtkWidget* tab_icon;
    GtkWidget* tab_label;
    GtkWidget* tab_close;
    GdkPixbuf* icon;
    gchar* uri;
    gchar* title;
    gboolean is_loading;
    gint progress;
    gchar* statusbar_text;
    gchar* link_uri;

    gint tab_label_size;
    gboolean close_button;
    gboolean middle_click_goto;
    MidoriWebSettings* settings;

    GtkWidget* proxy_menu_item;
    GtkWidget* proxy_tab_label;
    KatzeXbelItem* proxy_xbel_item;
};

#define MIDORI_WEB_VIEW_GET_PRIVATE(obj) \
    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
     MIDORI_TYPE_WEB_VIEW, MidoriWebViewPrivate))

enum
{
    PROP_0,

    PROP_ICON,
    PROP_URI,
    PROP_TITLE,
    PROP_STATUSBAR_TEXT,
    PROP_SETTINGS
};

enum {
    LOAD_STARTED,
    PROGRESS_STARTED,
    PROGRESS_CHANGED,
    PROGRESS_DONE,
    LOAD_DONE,
    ELEMENT_MOTION,
    CLOSE,
    NEW_TAB,
    NEW_WINDOW,

    LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];

static void
midori_web_view_finalize (GObject* object);

static void
midori_web_view_set_property (GObject* object,
                              guint prop_id,
                              const GValue* value,
                              GParamSpec* pspec);

static void
midori_web_view_get_property (GObject* object,
                              guint prop_id,
                              GValue* value,
                              GParamSpec* pspec);

/*static WebKitWebView*
midori_web_view_create_web_view (WebKitWebView* web_view)
{
    MidoriWebView* new_web_view = NULL;
    g_signal_emit (web_view, signals[NEW_WINDOW], 0, &new_web_view);
    if (new_web_view)
        return WEBKIT_WEB_VIEW (new_web_view);
    return WEBKIT_WEB_VIEW (midori_web_view_new ());
}*/

static void
midori_web_view_class_init (MidoriWebViewClass* class)
{
    signals[PROGRESS_STARTED] = g_signal_new (
        "progress-started",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, progress_started),
        0,
        NULL,
        g_cclosure_marshal_VOID__INT,
        G_TYPE_NONE, 1,
        G_TYPE_INT);

    signals[PROGRESS_CHANGED] = g_signal_new (
        "progress-changed",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, progress_changed),
        0,
        NULL,
        g_cclosure_marshal_VOID__INT,
        G_TYPE_NONE, 1,
        G_TYPE_INT);

    signals[PROGRESS_DONE] = g_signal_new (
        "progress-done",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, progress_done),
        0,
        NULL,
        g_cclosure_marshal_VOID__INT,
        G_TYPE_NONE, 1,
        G_TYPE_INT);

    signals[LOAD_DONE] = g_signal_new (
        "load-done",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, load_done),
        0,
        NULL,
        g_cclosure_marshal_VOID__OBJECT,
        G_TYPE_NONE, 1,
        WEBKIT_TYPE_WEB_FRAME);

    signals[ELEMENT_MOTION] = g_signal_new(
        "element-motion",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, element_motion),
        0,
        NULL,
        g_cclosure_marshal_VOID__STRING,
        G_TYPE_NONE, 1,
        G_TYPE_STRING);

    signals[CLOSE] = g_signal_new(
        "close",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, close),
        0,
        NULL,
        g_cclosure_marshal_VOID__VOID,
        G_TYPE_NONE, 0);

    signals[NEW_TAB] = g_signal_new(
        "new-tab",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, new_tab),
        0,
        NULL,
        g_cclosure_marshal_VOID__STRING,
        G_TYPE_NONE, 1,
        G_TYPE_STRING);

    signals[NEW_WINDOW] = g_signal_new(
        "new-window",
        G_TYPE_FROM_CLASS(class),
        (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
        G_STRUCT_OFFSET (MidoriWebViewClass, new_window),
        0,
        NULL,
        g_cclosure_marshal_VOID__STRING,
        G_TYPE_NONE, 1,
        G_TYPE_STRING);

    /*WEBKIT_WEB_VIEW_CLASS (class)->create_web_view = g_signal_new ("create-web-view",
            G_TYPE_FROM_CLASS(class),
            (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
            G_STRUCT_OFFSET(MidoriWebViewClass, create_web_view),
            0,
            NULL,
            g_cclosure_marshal_VOID__OBJECT,
            G_TYPE_NONE, 1,
            MIDORI_TYPE_WEB_VIEW);*/

    GObjectClass* gobject_class = G_OBJECT_CLASS (class);
    gobject_class->finalize = midori_web_view_finalize;
    gobject_class->set_property = midori_web_view_set_property;
    gobject_class->get_property = midori_web_view_get_property;

    GParamFlags flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT;

    g_object_class_install_property (gobject_class,
                                     PROP_ICON,
                                     g_param_spec_object (
                                     "icon",
                                     "Icon",
                                     _("The icon of the currently loaded page"),
                                     GDK_TYPE_PIXBUF,
                                     G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class,
                                     PROP_URI,
                                     g_param_spec_string (
                                     "uri",
                                     "Uri",
                                     _("The URI of the currently loaded page"),
                                     "",
                                     flags));

    g_object_class_install_property (gobject_class,
                                     PROP_TITLE,
                                     g_param_spec_string (
                                     "title",
                                     "Title",
                                     _("The title of the currently loaded page"),
                                     NULL,
                                     flags));

    g_object_class_install_property (gobject_class,
                                     PROP_STATUSBAR_TEXT,
                                     g_param_spec_string (
                                     "statusbar-text",
                                     "Statusbar Text",
                                     _("The text that is displayed in the statusbar"),
                                     "",
                                     flags));

    g_object_class_install_property (gobject_class,
                                     PROP_SETTINGS,
                                     g_param_spec_object (
                                     "settings",
                                     "Settings",
                                     _("The associated settings"),
                                     MIDORI_TYPE_WEB_SETTINGS,
                                     G_PARAM_READWRITE));

    g_type_class_add_private (class, sizeof (MidoriWebViewPrivate));
}

/*static void
webkit_web_view_load_started (MidoriWebView* web_view,
                              WebKitWebFrame* web_frame)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    priv->is_loading = TRUE;
    priv->progress = -1;
    katze_throbber_set_animated(KATZE_THROBBER(priv->tab_icon), TRUE);
}*/

static void
_midori_web_view_set_uri (MidoriWebView* web_view,
                          const gchar*   uri)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    katze_assign (priv->uri, g_strdup (uri));
    if (priv->proxy_xbel_item)
    {
        const gchar* uri = midori_web_view_get_display_uri (web_view);
        katze_xbel_bookmark_set_href (priv->proxy_xbel_item, uri);
    }
    g_object_set (web_view, "title", NULL, NULL);
}

static void
webkit_web_view_load_committed (MidoriWebView*  web_view,
                                WebKitWebFrame* web_frame)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    priv->progress = 0;
    const gchar* uri = webkit_web_frame_get_uri (web_frame);
    _midori_web_view_set_uri (web_view, uri);
}

static void
webkit_web_view_load_started (MidoriWebView*  web_view,
                              WebKitWebFrame* web_frame)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    // FIXME: This is a hack, until signals are fixed upstream
    priv->is_loading = TRUE;
    if (priv->tab_icon)
        katze_throbber_set_animated (KATZE_THROBBER (priv->tab_icon), TRUE);

    priv->progress = 0;
    g_signal_emit (web_view, signals[PROGRESS_STARTED], 0, priv->progress);
}

static void
webkit_web_view_progress_changed (MidoriWebView* web_view, gint progress)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    priv->progress = progress;
    g_signal_emit (web_view, signals[PROGRESS_CHANGED], 0, priv->progress);
}

static void
webkit_web_view_load_finished (MidoriWebView* web_view)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    priv->progress = 100;
    g_signal_emit (web_view, signals[PROGRESS_DONE], 0, priv->progress);
}

static void
webkit_web_frame_load_done (WebKitWebFrame* web_frame, gboolean success,
                            MidoriWebView*  web_view)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    priv->is_loading = FALSE;
    priv->progress = -1;
    if (priv->tab_icon)
        katze_throbber_set_animated (KATZE_THROBBER (priv->tab_icon), FALSE);
    g_signal_emit (web_view, signals[LOAD_DONE], 0, web_frame);
}

static void
webkit_web_view_title_changed (MidoriWebView*  web_view,
                               WebKitWebFrame* web_frame, const gchar* title)
{
    g_object_set (web_view, "title", title, NULL);
}

static void
webkit_web_view_statusbar_text_changed (MidoriWebView*  web_view,
                                        const gchar*    text)
{
    g_object_set (web_view, "statusbar-text", text, NULL);
}

static void
webkit_web_view_hovering_over_link (MidoriWebView* web_view,
                                    const gchar*   tooltip,
                                    const gchar*   link_uri)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    katze_assign (priv->link_uri, g_strdup (link_uri));
    g_signal_emit (web_view, signals[ELEMENT_MOTION], 0, link_uri);
}

static gboolean
gtk_widget_button_press_event (MidoriWebView*  web_view,
                               GdkEventButton* event)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    GdkModifierType state = (GdkModifierType)0;
    gint x, y;
    gdk_window_get_pointer (NULL, &x, &y, &state);
    switch (event->button)
    {
    case 1:
        if (!priv->link_uri)
            return FALSE;
        if (state & GDK_SHIFT_MASK)
        {
            // Open link in new window
            g_signal_emit (web_view, signals[NEW_WINDOW], 0, priv->link_uri);
            return TRUE;
        }
        else if(state & GDK_MOD1_MASK)
        {
            // Open link in new tab
            g_signal_emit (web_view, signals[NEW_TAB], 0, priv->link_uri);
            return TRUE;
        }
        break;
    case 2:
        if (state & GDK_CONTROL_MASK)
        {
            // FIXME: Reset font multiplier or zoom level
            return FALSE; // Allow Ctrl + Middle click
        }
        else
        {
            if (!priv->link_uri)
                return FALSE;
            // Open link in new tab
            g_signal_emit (web_view, signals[NEW_TAB], 0, priv->link_uri);
            return TRUE;
        }
        break;
    case 3:
        return FALSE;
    }
    return FALSE;
}

static gboolean
gtk_widget_button_press_event_after (MidoriWebView*  web_view,
                                     GdkEventButton* event)
{
    MidoriWebViewPrivate* priv = web_view->priv;

    if (event->button == 2 && priv->middle_click_goto)
    {
        GdkModifierType state = (GdkModifierType) event->state;
        GtkClipboard* clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
        gchar* uri = gtk_clipboard_wait_for_text (clipboard);
        if (uri && strchr (uri, '.') && !strchr (uri, ' '))
        {
            if (state & GDK_CONTROL_MASK)
                g_signal_emit (web_view, signals[NEW_TAB], 0, uri);
            else
                g_object_set (web_view, "uri", uri, NULL);
            g_free (uri);
            return TRUE;
        }
    }
    return FALSE;
}

static gboolean
gtk_widget_scroll_event (MidoriWebView*  web_view,
                         GdkEventScroll* event)
{
{'fraction_non_alphanumeric': 0.07961664677804296, 'fraction_numerical': 0.0038409904534606205, 'mean_word_length': 1.9737192282102463, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 100, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 78, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6018725', 'n_tokens_mistral': 8772, 'n_tokens_neox': 8444, 'n_words': 1583}