[Xfce4-commits] <midori:master> Implement drag (finger) and kinetic scrolling with KatzeScrolled

Christian Dywan noreply at xfce.org
Mon Sep 28 22:16:03 CEST 2009


Updating branch refs/heads/master
         to 96093d906754a986ef9b6dae5eae0ba8411653d4 (commit)
       from 7259066e20d982d98a554caf19c7c1134f4658eb (commit)

commit 96093d906754a986ef9b6dae5eae0ba8411653d4
Author: Christian Dywan <christian at twotoasts.de>
Date:   Sun Sep 27 23:48:08 2009 +0200

    Implement drag (finger) and kinetic scrolling with KatzeScrolled
    
    The feature is primarily interesting for mobile devices and
    by default enabled with gtk-touchscreen-mode.

 katze/katze-scrolled.c      | 1086 +++++++++++++++++++++++++++++++++++++++++++
 katze/katze-scrolled.h      |   59 +++
 katze/katze.h               |    1 +
 midori/midori-browser.c     |    3 -
 midori/midori-preferences.c |   17 +-
 midori/midori-view.c        |   24 +-
 midori/midori-websettings.c |   25 +
 7 files changed, 1203 insertions(+), 12 deletions(-)

diff --git a/katze/katze-scrolled.c b/katze/katze-scrolled.c
new file mode 100644
index 0000000..b7596f5
--- /dev/null
+++ b/katze/katze-scrolled.c
@@ -0,0 +1,1086 @@
+/*
+ Copyright (C) 2007 Henrik Hedberg <hhedberg at innologies.fi>
+ Copyright (C) 2009 Nadav Wiener <nadavwr at yahoo.com>
+ Copyright (C) 2009 Christian Dywan <christian at twotoasts.de>
+
+ 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "katze-scrolled.h"
+
+#define DEFAULT_INTERVAL 50
+#define DEFAULT_DECELERATION 0.7
+#define DEFAULT_DRAGGING_STOPPED_DELAY 100
+
+/**
+ * SECTION:katze-scrolled
+ * @short_description: Implements drag scrolling and kinetic scrolling
+ * @see_also: #GtkScrolledWindow
+ *
+ * A scrolled window derived from #GtkScrolledWindow that implements
+ * drag scrolling and kinetic scrolling. Can be used as a drop-in replacement
+ * for the existing #GtkScrolledWindow.
+ *
+ * If a direct child of the #KatzeScrolled has its own window
+ * (InputOnly is enough for events), it is automatically activated when added
+ * as a child. All motion events in that area will be used to scroll.
+ *
+ * If some descendant widgets capture button press, button release and/ or
+ * motion nofity events, the user can not scroll the area by pressing those
+ * widgets (unless the widget is activated). #GtkButton is a typical example
+ * of that. Usually that is the desired behaviour.
+ *
+ * Any widget can be registered to provide pointer events for the
+ * #KatzeScrolled by using the
+ * #katze_scrolled_activate_scrolling function.
+ *
+ **/
+
+G_DEFINE_TYPE (KatzeScrolled, katze_scrolled, GTK_TYPE_SCROLLED_WINDOW);
+
+enum
+{
+    PROP_0,
+
+    PROP_DRAG_SCROLLING,
+    PROP_KINETIC_SCROLLING
+};
+
+static void
+katze_scrolled_set_property (GObject* object,
+                             guint prop_id,
+                             const GValue* value,
+                             GParamSpec* pspec);
+
+static void
+katze_scrolled_get_property (GObject* object,
+                             guint prop_id,
+                             GValue* value,
+                             GParamSpec* pspec);
+
+static void
+katze_scrolled_dispose (GObject* object);
+
+static void
+katze_scrolled_activate_scrolling (KatzeScrolled* scrolled,
+                                   GtkWidget*     widget);
+
+static void
+katze_scrolled_set_drag_scrolling (KatzeScrolled* scrolled,
+                                   gboolean       drag_scrolling);
+
+struct _KatzeScrolledPrivate
+{
+    /* Settings */
+    guint interval;
+    gdouble deceleration;
+    gboolean drag_scrolling;
+    gboolean kinetic_scrolling;
+    guint32 dragging_stopped_delay;
+    gboolean scrolling_hints;
+
+    /* Temporary variables */
+    gboolean dragged;
+    gboolean press_received;
+    GdkWindow* synthetic_crossing_event_window;
+
+    /* Disabling twice happening scrolling adjustment */
+    GtkAdjustment* hadjustment;
+    GtkWidget* viewport;
+
+    /* Motion scrolling */
+    gint start_x;
+    gint start_y;
+    gint previous_x;
+    gint previous_y;
+    gint farest_x;
+    gint farest_y;
+    guint32 start_time;
+    guint32 previous_time;
+    guint32 farest_time;
+    gboolean going_right;
+    gboolean going_down;
+
+    /* Kinetic scrolling */
+    guint scrolling_timeout_id;
+    gdouble horizontal_speed;
+    gdouble vertical_speed;
+    gdouble horizontal_deceleration;
+    gdouble vertical_deceleration;
+
+    /* Internal scrollbars */
+    GdkWindow* vertical_scrollbar_window;
+    GdkWindow* horizontal_scrollbar_window;
+    gint vertical_scrollbar_size;
+    gint horizontal_scrollbar_size;
+    guint hide_scrollbars_timeout_id;
+    GdkGC* hilight_gc;
+    GdkGC* shadow_gc;
+};
+
+typedef struct _KatzeScrolledState KatzeScrolledState;
+typedef gboolean (*KatzeScrolledEventHandler)(GdkEvent*           event,
+                                              KatzeScrolledState* state,
+                                              gpointer            user_data);
+
+typedef struct
+{
+    KatzeScrolledEventHandler event_handler;
+    gpointer user_data;
+} EventHandlerData;
+
+struct _KatzeScrolledState
+{
+    GList* current_event_handler;
+};
+
+static GList* event_handlers = NULL;
+
+static void
+katze_scrolled_event_handler_func (GdkEvent* event,
+                                   gpointer  data);
+
+static void
+katze_scrolled_event_handler_append (KatzeScrolledEventHandler event_handler,
+                                     gpointer                  user_data)
+{
+    EventHandlerData* data;
+
+    data = g_new0 (EventHandlerData, 1);
+    data->event_handler = event_handler;
+    data->user_data = user_data;
+    event_handlers = g_list_append (event_handlers, data);
+
+    gdk_event_handler_set ((GdkEventFunc)katze_scrolled_event_handler_func, NULL, NULL);
+}
+
+static void
+katze_scrolled_event_handler_next (GdkEvent*           event,
+                                   KatzeScrolledState* state)
+{
+    EventHandlerData* data;
+    gboolean stop_propagating;
+
+    state->current_event_handler = g_list_next (state->current_event_handler);
+    if (state->current_event_handler)
+    {
+        data = (EventHandlerData*)state->current_event_handler->data;
+        stop_propagating = data->event_handler (event, state, data->user_data);
+        if (!stop_propagating && state->current_event_handler)
+            g_critical ("%s: handler returned FALSE without calling %s first",
+                        G_STRFUNC, G_STRFUNC);
+    }
+    else
+        gtk_main_do_event (event);
+}
+
+static void
+katze_scrolled_event_handler_func (GdkEvent* event,
+                                   gpointer  user_data)
+{
+    KatzeScrolledState* state;
+    EventHandlerData* data;
+    gboolean stop_propagating;
+
+    state = g_new0 (KatzeScrolledState, 1);
+    state->current_event_handler = g_list_first (event_handlers);
+    if (state->current_event_handler)
+    {
+        data = (EventHandlerData*)state->current_event_handler->data;
+        stop_propagating = data->event_handler (event, state, data->user_data);
+        if (!stop_propagating && state->current_event_handler)
+            g_critical ("%s: handler returned FALSE without calling %s first",
+                        G_STRFUNC, "katze_scrolled_event_handler_next");
+    }
+    else
+        gtk_main_do_event (event);
+
+    g_free (state);
+}
+
+static GdkWindow* current_gdk_window;
+static KatzeScrolled* current_scrolled_window;
+static GtkWidget* current_widget;
+static gboolean synthetized_crossing_event;
+
+static GTree* activated_widgets;
+
+static gint
+compare_pointers (gconstpointer a,
+                  gconstpointer b)
+{
+    return a - b;
+}
+
+static void
+disable_hadjustment (KatzeScrolled* scrolled)
+{
+    KatzeScrolledPrivate* priv = scrolled->priv;
+    GtkAdjustment* hadjustment;
+    GtkWidget* viewport;
+
+    if ((hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled)))
+        && priv->hadjustment != hadjustment)
+    {
+        priv->hadjustment = hadjustment;
+        priv->viewport = NULL;
+        viewport = GTK_WIDGET (scrolled);
+        while (GTK_IS_BIN (viewport))
+        {
+            viewport = gtk_bin_get_child (GTK_BIN (viewport));
+            if (GTK_IS_VIEWPORT (viewport))
+            {
+                priv->viewport = viewport;
+                break;
+            }
+        }
+    }
+    g_signal_handlers_block_matched (priv->hadjustment, G_SIGNAL_MATCH_DATA,
+                                     0, 0, 0, 0, priv->viewport);
+}
+
+static void
+enable_hadjustment (KatzeScrolled* scrolled)
+{
+    KatzeScrolledPrivate* priv = scrolled->priv;
+
+    g_signal_handlers_unblock_matched (priv->hadjustment, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, priv->viewport);
+}
+
+static gboolean
+on_expose_event (GtkWidget*      widget,
+                 GdkEventExpose* event,
+                 KatzeScrolled*  scrolled)
+{
+    KatzeScrolledPrivate* priv = scrolled->priv;
+    gboolean ret = FALSE;
+
+    if (GTK_WIDGET_DRAWABLE (widget))
+    {
+        if (event->window == priv->horizontal_scrollbar_window)
+        {
+            if (priv->horizontal_scrollbar_size)
+            {
+                gdk_draw_line (event->window, priv->hilight_gc, 0, 0, priv->horizontal_scrollbar_size - 1, 0);
+                gdk_draw_line (event->window, priv->hilight_gc, 0, 1, 0, 9);
+                gdk_draw_line (event->window, priv->shadow_gc, priv->horizontal_scrollbar_size - 1, 1, priv->horizontal_scrollbar_size - 1, 9);
+                gdk_draw_line (event->window, priv->shadow_gc, 0, 9, priv->horizontal_scrollbar_size - 1, 9);
+            }
+
+            ret = TRUE;
+        }
+        else if (event->window == priv->vertical_scrollbar_window)
+        {
+            if (priv->vertical_scrollbar_size)
+            {
+                gdk_draw_line (event->window, priv->hilight_gc, 0, 0, 9, 0);
+                gdk_draw_line (event->window, priv->hilight_gc, 0, 1, 0, priv->vertical_scrollbar_size - 1);
+                gdk_draw_line (event->window, priv->shadow_gc, 9, 1, 9, priv->vertical_scrollbar_size - 1);
+                gdk_draw_line (event->window, priv->shadow_gc, 0, priv->vertical_scrollbar_size - 1, 9, priv->vertical_scrollbar_size - 1);
+            }
+
+            ret = TRUE;
+        }
+    }
+
+    return ret;
+}
+
+static gboolean
+adjust_scrollbar (KatzeScrolled* scrolled,
+                  GdkWindow*     scrollbar_window,
+                  GtkAdjustment* adjustment,
+                  gint*          previous_size,
+                  gboolean       horizontal)
+{
+    KatzeScrolledPrivate* priv = scrolled->priv;
+    GtkWidget* widget = GTK_WIDGET (scrolled);
+    gint x, y;
+    gint size;
+    double position;
+    GtkWidget* window;
+
+    if (adjustment->page_size >= adjustment->upper - adjustment->lower)
+    {
+        *previous_size = 0;
+        return FALSE;
+    }
+
+    size = ((double)adjustment->page_size) / (adjustment->upper - adjustment->lower) * (horizontal
+        ? widget->allocation.height : widget->allocation.width);
+    if (size != *previous_size)
+    {
+        *previous_size = size;
+        if (horizontal)
+        {
+            gdk_window_resize (scrollbar_window, 10, size);
+            gdk_window_clear (scrollbar_window);
+            gdk_draw_line (scrollbar_window, priv->hilight_gc, 0, 0, 9, 0);
+            gdk_draw_line (scrollbar_window, priv->hilight_gc, 0, 1, 0, size - 1);
+            gdk_draw_line (scrollbar_window, priv->shadow_gc, 9, 1, 9, size - 1);
+            gdk_draw_line (scrollbar_window, priv->shadow_gc, 0, size - 1, 9, size - 1);
+        }
+        else
+        {
+            gdk_window_resize (scrollbar_window, size, 10);
+            gdk_window_clear (scrollbar_window);
+            gdk_draw_line (scrollbar_window, priv->hilight_gc, 0, 0, size - 1, 0);
+            gdk_draw_line (scrollbar_window, priv->hilight_gc, 0, 1, 0, 9);
+            gdk_draw_line (scrollbar_window, priv->shadow_gc, size - 1, 1, size - 1, 9);
+            gdk_draw_line (scrollbar_window, priv->shadow_gc, 0, 9, size - 1, 9);
+        }
+    }
+
+    position = (adjustment->value - adjustment->lower) / (adjustment->upper - adjustment->lower);
+    window = gtk_widget_get_toplevel (widget);
+    if (horizontal)
+    {
+        gtk_widget_translate_coordinates (widget, window,
+            widget->allocation.width - 20, position * widget->allocation.height, &x, &y);
+        gdk_window_move (scrollbar_window, x, y);
+    }
+    else
+    {
+        gtk_widget_translate_coordinates (widget, window,
+            position * widget->allocation.width, widget->allocation.height - 20, &x, &y);
+        gdk_window_move (scrollbar_window, x, y);
+    }
+
+    return TRUE;
+}
+
+static gboolean
+hide_scrollbars_timeout (gpointer data)
+{
+    KatzeScrolled* scrolled = KATZE_SCROLLED (data);
+    KatzeScrolledPrivate* priv = scrolled->priv;
+
+    gdk_threads_enter ();
+    gdk_window_hide (priv->vertical_scrollbar_window);
+    gdk_window_hide (priv->horizontal_scrollbar_window);
+
+    priv->hide_scrollbars_timeout_id = 0;
+    gdk_threads_leave ();
+
+    return FALSE;
+}
+
+static gdouble
+calculate_timeout_scroll_values (gdouble  old_value,
+                                 gdouble  upper_limit,
+                                 gdouble* scrolling_speed_pointer,
+                                 gdouble  deceleration,
+                                 gdouble* other_deceleration,
+                                 gdouble normal_deceleration)
+{
+    gdouble new_value = old_value;
+
+    if (*scrolling_speed_pointer > deceleration ||
+        *scrolling_speed_pointer < -deceleration)
+    {
+        if (old_value + *scrolling_speed_pointer <= 0.0)
+        {
+            new_value = -1.0;
+            *scrolling_speed_pointer = 0.0;
+            *other_deceleration = normal_deceleration;
+        }
+        else if (old_value + *scrolling_speed_pointer >= upper_limit)
+        {
+            new_value = upper_limit;
+            *scrolling_speed_pointer = 0.0;
+            *other_deceleration = normal_deceleration;
+        }
+        else
+            new_value = old_value + *scrolling_speed_pointer;
+        if (*scrolling_speed_pointer > deceleration)
+            *scrolling_speed_pointer -= deceleration;
+        else if (*scrolling_speed_pointer < -deceleration)
+            *scrolling_speed_pointer += deceleration;
+    }
+
+    return new_value;
+}
+
+static void
+do_timeout_scroll (KatzeScrolled* scrolled)
+{
+    KatzeScrolledPrivate* priv = scrolled->priv;
+    GtkScrolledWindow* gtk_scrolled = GTK_SCROLLED_WINDOW (scrolled);
+    GtkAdjustment* hadjustment;
+    GtkAdjustment* vadjustment;
+    gdouble hvalue;
+    gdouble vvalue;
+
+    hadjustment = gtk_scrolled_window_get_hadjustment (gtk_scrolled);
+    vadjustment = gtk_scrolled_window_get_vadjustment (gtk_scrolled);
+    hvalue = calculate_timeout_scroll_values (hadjustment->value,
+        hadjustment->upper - hadjustment->page_size,
+        &priv->horizontal_speed,
+        priv->horizontal_deceleration,
+        &priv->vertical_deceleration,
+        priv->deceleration);
+    vvalue = calculate_timeout_scroll_values (vadjustment->value,
+        vadjustment->upper - vadjustment->page_size,
+        &priv->vertical_speed,
+        priv->vertical_deceleration,
+        &priv->horizontal_deceleration,
+        priv->deceleration);
+    if (vvalue != vadjustment->value)
+    {
+        if (hvalue != hadjustment->value)
+        {
+            disable_hadjustment (scrolled);
+            gtk_adjustment_set_value (hadjustment, hvalue);
+            enable_hadjustment (scrolled);
+        }
+        gtk_adjustment_set_value (vadjustment, vvalue);
+    }
+    else if (hvalue != hadjustment->value)
+        gtk_adjustment_set_value (hadjustment, hvalue);
+
+    adjust_scrollbar (scrolled, priv->horizontal_scrollbar_window,
+                     gtk_scrolled_window_get_hadjustment (gtk_scrolled),
+                     &priv->horizontal_scrollbar_size, FALSE);
+    adjust_scrollbar (scrolled, priv->vertical_scrollbar_window,
+                     gtk_scrolled_window_get_vadjustment (gtk_scrolled),
+                     &priv->vertical_scrollbar_size, TRUE);
+}
+
+static gboolean
+timeout_scroll (gpointer data)
+{
+    gboolean ret = TRUE;
+    KatzeScrolled* scrolled = KATZE_SCROLLED (data);
+    KatzeScrolledPrivate* priv = scrolled->priv;
+
+    gdk_threads_enter ();
+    do_timeout_scroll (scrolled);
+
+    if (priv->vertical_speed < priv->deceleration &&
+        priv->vertical_speed > -priv->deceleration &&
+        priv->horizontal_speed < priv->deceleration &&
+        priv->horizontal_speed > -priv->deceleration)
+    {
+        priv->scrolling_timeout_id = 0;
+        if (!priv->hide_scrollbars_timeout_id)
+            priv->hide_scrollbars_timeout_id = g_timeout_add (500,
+                hide_scrollbars_timeout, scrolled);
+
+        ret = FALSE;
+    }
+    gdk_threads_leave ();
+
+    return ret;
+}
+
+static gdouble
+calculate_motion_scroll_values (gdouble old_value,
+                                gdouble upper_limit,
+                                gint    current_coordinate,
+                                gint    previous_coordinate)
+{
+    gdouble new_value = old_value;
+    gint movement;
+
+    movement = current_coordinate - previous_coordinate;
+
+    if (old_value - movement < upper_limit)
+        new_value = old_value - movement;
+    else
+        new_value = upper_limit;
+
+    return new_value;
+}
+
+static void
+do_motion_scroll (KatzeScrolled* scrolled,
+                  GtkWidget*     widget,
+                  gint           x,
+                  gint           y,
+                  guint32        timestamp)
+{
+    KatzeScrolledPrivate* priv = scrolled->priv;
+    GtkAdjustment* hadjustment;
+    GtkAdjustment* vadjustment;
+    gdouble hvalue;
+    gdouble vvalue;
+
+    if (priv->dragged || gtk_drag_check_threshold (widget, priv->start_x, priv->start_y, x, y))
+    {
+        if (timestamp - priv->previous_time > priv->dragging_stopped_delay || !priv->dragged)
+        {
+            priv->dragged = TRUE;
+            priv->going_right = priv->start_x < x;
+            priv->going_down = priv->start_y < y;
+            priv->start_x = priv->farest_x = x;
+            priv->start_y = priv->farest_y = y;
+            priv->start_time = priv->farest_time = timestamp;
+        }
+        else
+        {
+            if ((priv->going_right && x > priv->farest_x)
+                || (!priv->going_right && x < priv->farest_x))
+            {
+                priv->farest_x = x;
+                priv->farest_time = timestamp;
+            }
+            if ((priv->going_down && y > priv->farest_y)
+                || (!priv->going_down && y < priv->farest_y))
+            {
+                priv->farest_y = y;
+                priv->farest_time = timestamp;
+            }
+            if (gtk_drag_check_threshold (widget, priv->farest_x, priv->farest_y, x, y))
+            {
+                priv->start_x = priv->farest_x;
+                priv->farest_x = x;
+                priv->start_y = priv->farest_y;
+                priv->farest_y = y;
+                priv->start_time = priv->farest_time;
+                priv->farest_time = timestamp;
+                priv->going_right = priv->start_x < x;
+                priv->going_down = priv->start_y < y;
+            }
+        }
+
+        hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled));
+        vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled));
+        hvalue = calculate_motion_scroll_values (hadjustment->value,
+            hadjustment->upper - hadjustment->page_size, x, priv->previous_x);
+        vvalue = calculate_motion_scroll_values (vadjustment->value,
+            vadjustment->upper - vadjustment->page_size, y, priv->previous_y);
+        if (vvalue != vadjustment->value)
+        {
+            if (hvalue != hadjustment->value)
+            {
+                disable_hadjustment (scrolled);
+                gtk_adjustment_set_value (hadjustment, hvalue);
+                enable_hadjustment (scrolled);
+            }
+            gtk_adjustment_set_value (vadjustment, vvalue);
+        }
+        else if (hvalue != hadjustment->value)
+            gtk_adjustment_set_value (hadjustment, hvalue);
+    }
+
+    priv->previous_y = y;
+    priv->previous_x = x;
+    priv->previous_time = timestamp;
+
+    adjust_scrollbar (scrolled, priv->horizontal_scrollbar_window,
+        gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled)),
+        &priv->horizontal_scrollbar_size, FALSE);
+    adjust_scrollbar (scrolled, priv->vertical_scrollbar_window,
+        gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled)),
+        &priv->vertical_scrollbar_size, TRUE);
+}
+
+static gboolean
+button_press_event (GtkWidget*      widget,
+                    GdkEventButton* event,
+                    KatzeScrolled*  scrolled)
+{
+    KatzeScrolledPrivate* priv = scrolled->priv;
+    gint x;
+    gint y;
+    GdkModifierType mask;
+
+    if (!priv->drag_scrolling)
+        return FALSE;
+
+    if (event->button != 1)
+        return FALSE;
+
+    priv->press_received = TRUE;
+
+    if (event->time - priv->previous_time < priv->dragging_stopped_delay &&
+        gtk_drag_check_threshold (widget, priv->previous_x, priv->previous_y, x, y))
+    {
+        if (priv->scrolling_timeout_id)
+        {
+            g_source_remove (priv->scrolling_timeout_id);
+            priv->scrolling_timeout_id = 0;
+        }
+        gdk_window_get_pointer (GTK_WIDGET (scrolled)->window, &x, &y, &mask);
+        /* do_motion_scroll (scrolled, widget, x, y, event->time); */
+    }
+    else
+    {
+        if (priv->scrolling_timeout_id)
+        {
+            g_source_remove (priv->scrolling_timeout_id);
+            priv->scrolling_timeout_id = 0;
+            priv->previous_time = 0;
+        }
+        else
+        {
+            priv->dragged = FALSE;
+            priv->previous_time = event->time;
+        }
+        gdk_window_get_pointer (GTK_WIDGET (scrolled)->window, &x, &y, &mask);
+        priv->start_x = priv->previous_x = priv->farest_x = x;
+        priv->start_y = priv->previous_y = priv->farest_y = y;
+        priv->start_time  = event->time;
+    }
+
+    if (priv->scrolling_hints && !GTK_SCROLLED_WINDOW (scrolled)->hscrollbar_visible &&
+        adjust_scrollbar (scrolled, priv->horizontal_scrollbar_window,
+                     gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled)),
+                         &priv->horizontal_scrollbar_size, FALSE))
+    {
+        gdk_window_raise (priv->horizontal_scrollbar_window);
+        gdk_window_show (priv->horizontal_scrollbar_window);
+    }
+    if (priv->scrolling_hints && !GTK_SCROLLED_WINDOW (scrolled)->vscrollbar_visible &&
+        adjust_scrollbar (scrolled, priv->vertical_scrollbar_window,
+                     gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled)),
+                         &priv->vertical_scrollbar_size, TRUE))
+    {
+        gdk_window_raise (priv->vertical_scrollbar_window);
+        gdk_window_show (priv->vertical_scrollbar_window);
+    }
+    if (priv->hide_scrollbars_timeout_id)
+    {
+        g_source_remove (priv->hide_scrollbars_timeout_id);
+        priv->hide_scrollbars_timeout_id = 0;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+button_release_event (GtkWidget*      widget,
+                      GdkEventButton* event,
+                      KatzeScrolled*  scrolled)
+{
+    KatzeScrolledPrivate* priv = scrolled->priv;
+    gint x;
+    gint y;
+    GdkModifierType mask;
+
+    gdk_window_get_pointer (GTK_WIDGET (scrolled)->window, &x, &y, &mask);
+    if (priv->press_received &&
+        gtk_drag_check_threshold (widget, priv->start_x, priv->start_y, x, y)) {
+        priv->dragged = TRUE;
+    }
+
+    if (priv->press_received && priv->kinetic_scrolling &&
+        event->time - priv->previous_time < priv->dragging_stopped_delay) {
+        priv->vertical_speed = (gdouble)(priv->start_y - y) / (event->time - priv->start_time) * priv->interval;
+        priv->horizontal_speed = (gdouble)(priv->start_x - x) / (event->time - priv->start_time) * priv->interval;
+        if (ABS (priv->vertical_speed) > ABS (priv->horizontal_speed)) {
+            priv->vertical_deceleration = priv->deceleration;
+            priv->horizontal_deceleration = priv->deceleration * ABS (priv->horizontal_speed / priv->vertical_speed);
+        } else {
+            priv->horizontal_deceleration = priv->deceleration;
+            priv->vertical_deceleration = priv->deceleration * ABS (priv->vertical_speed / priv->horizontal_speed);
+        }
+        priv->scrolling_timeout_id = g_timeout_add (priv->interval, timeout_scroll, scrolled);
+
+        do_timeout_scroll (scrolled);
+    }
+    else if (!priv->hide_scrollbars_timeout_id) {
+        priv->hide_scrollbars_timeout_id = g_timeout_add (500, hide_scrollbars_timeout, scrolled);
+    }
+    priv->previous_x = x;
+    priv->previous_y = y;
+    priv->previous_time = event->time;
+
+    priv->press_received = FALSE;
+
+    return FALSE;
+}
+
+static gboolean
+motion_notify_event (GtkWidget*      widget,
+                     GdkEventMotion* event,
+                     KatzeScrolled*  scrolled)
+{
+    KatzeScrolledPrivate* priv = scrolled->priv;
+    gint x;
+    gint y;
+    GdkModifierType mask;
+
+    if (priv->press_received)
+    {
+        gdk_window_get_pointer (GTK_WIDGET (scrolled)->window, &x, &y, &mask);
+        do_motion_scroll (scrolled, widget, x, y, event->time);
+    }
+
+    return FALSE;
+}
+
+static gboolean
+katze_scrolled_event_handler (GdkEvent*           event,
+                              KatzeScrolledState* state,
+                              gpointer            user_data)
+{
+    gboolean stop_propagating;
+    GdkEventCrossing crossing;
+
+    stop_propagating = FALSE;
+
+    if (event->type == GDK_BUTTON_PRESS)
+    {
+        gdk_window_get_user_data (event->button.window, (gpointer)&current_widget);
+
+        if ((current_scrolled_window = g_tree_lookup (activated_widgets, current_widget)))
+        {
+            current_gdk_window = event->button.window;
+            stop_propagating = button_press_event (current_widget, &event->button, current_scrolled_window);
+        }
+        else
+            current_gdk_window = NULL;
+    }
+    else if (event->any.window == current_gdk_window)
+    {
+        if (event->type == GDK_MOTION_NOTIFY)
+        {
+            if (current_scrolled_window->priv->dragged)
+                stop_propagating = motion_notify_event (current_widget, &event->motion, current_scrolled_window);
+            else
+            {
+                stop_propagating = motion_notify_event (current_widget, &event->motion, current_scrolled_window);
+                if (current_scrolled_window->priv->dragged)
+                {
+                    crossing.type = GDK_LEAVE_NOTIFY;
+                    crossing.window = event->motion.window;
+                    crossing.send_event = event->motion.send_event;
+                    crossing.subwindow = GTK_WIDGET (current_scrolled_window)->window;
+                    crossing.time = event->motion.time;
+                    crossing.x = event->motion.x;
+                    crossing.y = event->motion.y;
+                    crossing.x_root = event->motion.x_root;
+                    crossing.y_root = event->motion.y_root;
+                    crossing.mode = GDK_CROSSING_GRAB;
+                    crossing.detail = GDK_NOTIFY_ANCESTOR;
+                    crossing.focus = TRUE;
+                    crossing.state = event->motion.state;
+
+                    gtk_main_do_event ((GdkEvent*)&crossing);
+                    synthetized_crossing_event = TRUE;
+                }
+            }
+        }
+        else if ((event->type == GDK_ENTER_NOTIFY || event->type == GDK_LEAVE_NOTIFY) &&
+                   synthetized_crossing_event)
+            stop_propagating = TRUE;
+        else if (event->type == GDK_BUTTON_RELEASE)
+            stop_propagating = button_release_event (current_widget, &event->button, current_scrolled_window);
+    }
+
+
+    if (!stop_propagating)
+        katze_scrolled_event_handler_next (event, state);
+
+    if (event->type == GDK_BUTTON_RELEASE && event->button.window == current_gdk_window)
+    {
+        crossing.type = GDK_ENTER_NOTIFY;
+        crossing.window = event->button.window;
+        crossing.send_event = event->button.send_event;
+        crossing.subwindow = GTK_WIDGET (current_scrolled_window)->window;
+        crossing.time = event->button.time;
+        crossing.x = event->button.x;
+        crossing.y = event->button.y;
+        crossing.x_root = event->button.x_root;
+        crossing.y_root = event->button.y_root;
+        crossing.mode = GDK_CROSSING_UNGRAB;
+        crossing.detail = GDK_NOTIFY_ANCESTOR;
+        crossing.focus = TRUE;
+        crossing.state = event->button.state;
+
+        gtk_main_do_event ((GdkEvent*)&crossing);
+        synthetized_crossing_event = FALSE;
+    }
+
+    return stop_propagating;
+}
+
+static void
+katze_scrolled_add (GtkContainer* container,
+                    GtkWidget*    widget)
+{
+    katze_scrolled_activate_scrolling (KATZE_SCROLLED (container), widget);
+
+    (*GTK_CONTAINER_CLASS (katze_scrolled_parent_class)->add) (container, widget);
+}
+
+static void
+katze_scrolled_realize (GtkWidget* widget)
+{
+    KatzeScrolled* scrolled = KATZE_SCROLLED (widget);
+    KatzeScrolledPrivate* priv = scrolled->priv;
+    gboolean drag_scrolling;
+    GtkSettings* settings = gtk_widget_get_settings (widget);
+    GtkPolicyType policy;
+    GdkWindowAttr attr;
+    GdkColor color;
+
+    (*GTK_WIDGET_CLASS (katze_scrolled_parent_class)->realize) (widget);
+
+    g_object_get (settings, "gtk-touchscreen-mode", &drag_scrolling, NULL);
+    policy = drag_scrolling ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC;
+    g_object_set (scrolled, "drag-scrolling", drag_scrolling,
+        "hscrollbar-policy", policy, "vscrollbar-policy", policy, NULL);
+
+    widget->window = g_object_ref (gtk_widget_get_parent_window (widget));
+
+    attr.height = attr.width = 10;
+    attr.event_mask = GDK_EXPOSURE_MASK;
+    attr.wclass = GDK_INPUT_OUTPUT;
+    attr.window_type = GDK_WINDOW_CHILD;
+    attr.override_redirect = TRUE;
+    priv->vertical_scrollbar_window = gdk_window_new (widget->window, &attr, 0);
+    priv->horizontal_scrollbar_window = gdk_window_new (widget->window, &attr, 0);
+
+    gdk_window_set_user_data (priv->vertical_scrollbar_window, widget);
+    gdk_window_set_user_data (priv->horizontal_scrollbar_window, widget);
+    g_signal_connect (widget, "expose-event",
+                      G_CALLBACK (on_expose_event), scrolled);
+
+    color.red = color.green = color.blue = 0x9999;
+    gdk_rgb_find_color (gdk_colormap_get_system (), &color);
+    gdk_window_set_background (priv->vertical_scrollbar_window, &color);
+    gdk_window_set_background (priv->horizontal_scrollbar_window, &color);
+
+    priv->hilight_gc = gdk_gc_new (widget->window);
+    color.red = color.green = color.blue = 0xcccc;
+    gdk_gc_set_rgb_fg_color (priv->hilight_gc, &color);
+    priv->shadow_gc = gdk_gc_new (widget->window);
+    color.red = color.green = color.blue = 0x6666;
+    gdk_gc_set_rgb_fg_color (priv->shadow_gc, &color);
+
+    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+}
+
+static void
+katze_scrolled_dispose (GObject* object)
+{
+    KatzeScrolled* scrolled = KATZE_SCROLLED (object);
+    KatzeScrolledPrivate* priv = scrolled->priv;
+
+    if (priv->scrolling_timeout_id)
+    {
+        g_source_remove (priv->scrolling_timeout_id);
+        priv->scrolling_timeout_id = 0;
+    }
+    if (priv->hide_scrollbars_timeout_id)
+    {
+        g_source_remove (priv->hide_scrollbars_timeout_id);
+        priv->hide_scrollbars_timeout_id = 0;
+    }
+
+    (*G_OBJECT_CLASS (katze_scrolled_parent_class)->dispose) (object);
+}
+
+static void
+katze_scrolled_class_init (KatzeScrolledClass* class)
+{
+    GObjectClass* gobject_class = G_OBJECT_CLASS (class);
+    GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (class);
+    GtkContainerClass* container_class = GTK_CONTAINER_CLASS (class);
+    GParamFlags flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT;
+
+    gobject_class->set_property = katze_scrolled_set_property;
+    gobject_class->get_property = katze_scrolled_get_property;
+    gobject_class->dispose = katze_scrolled_dispose;
+
+    widget_class->realize = katze_scrolled_realize;
+
+    container_class->add = katze_scrolled_add;
+
+    /**
+     * KatzeScrolled:drag-scrolling:
+     *
+     * Whether the widget can be scrolled by dragging its contents.
+     *
+     * If "gtk-touchscreen-mode" is enabled, drag scrolling is
+     * automatically enabled.
+     *
+     * Since: 0.2.0
+     */
+    g_object_class_install_property (gobject_class,
+                                     PROP_DRAG_SCROLLING,
+                                     g_param_spec_boolean (
+                                     "drag-scrolling",
+                                     "Drag Scrolling",
+                                     "Whether the widget can be scrolled by dragging its contents",
+                                     FALSE,
+                                     flags));
+
+    /**
+     * KatzeScrolled:kinetic-scrolling:
+     *
+     * Whether drag scrolling is kinetic, that is releasing the
+     * pointer keeps the contents scrolling further relative to
+     * the speed with which they were dragged.
+     *
+     * Since: 0.2.0
+     */
+    g_object_class_install_property (gobject_class,
+                                     PROP_KINETIC_SCROLLING,
+                                     g_param_spec_boolean (
+                                     "kinetic-scrolling",
+                                     "Kinetic Scrolling",
+                                     "Whether drag scrolling is kinetic",
+                                     TRUE,
+                                     flags));
+
+    activated_widgets = g_tree_new ((GCompareFunc)compare_pointers);
+    current_gdk_window = NULL;
+
+    katze_scrolled_event_handler_append (katze_scrolled_event_handler, NULL);
+
+    g_type_class_add_private (class, sizeof (KatzeScrolledPrivate));
+}
+
+static void
+katze_scrolled_init (KatzeScrolled* scrolled)
+{
+    KatzeScrolledPrivate* priv;
+
+    scrolled->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE ((scrolled),
+        KATZE_TYPE_SCROLLED, KatzeScrolledPrivate);
+
+    priv->interval = DEFAULT_INTERVAL;
+    priv->deceleration = DEFAULT_DECELERATION;
+    priv->drag_scrolling = FALSE;
+    priv->kinetic_scrolling = TRUE;
+    priv->dragging_stopped_delay = DEFAULT_DRAGGING_STOPPED_DELAY;
+}
+
+static void
+katze_scrolled_set_property (GObject*      object,
+                             guint         prop_id,
+                             const GValue* value,
+                             GParamSpec*   pspec)
+{
+    KatzeScrolled* scrolled = KATZE_SCROLLED (object);
+
+    switch (prop_id)
+    {
+    case PROP_DRAG_SCROLLING:
+        katze_scrolled_set_drag_scrolling (scrolled, g_value_get_boolean (value));
+        break;
+    case PROP_KINETIC_SCROLLING:
+        scrolled->priv->kinetic_scrolling = g_value_get_boolean (value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+katze_scrolled_get_property (GObject*    object,
+                             guint       prop_id,
+                             GValue*     value,
+                             GParamSpec* pspec)
+{
+    KatzeScrolled* scrolled = KATZE_SCROLLED (object);
+
+    switch (prop_id)
+    {
+    case PROP_DRAG_SCROLLING:
+        g_value_set_boolean (value, scrolled->priv->drag_scrolling);
+        break;
+    case PROP_KINETIC_SCROLLING:
+        g_value_set_boolean (value, scrolled->priv->kinetic_scrolling);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+/**
+ * katze_scrolled_new:
+ * @hadjustment: a horizontal #GtkAdjustment, or %NULL
+ * @vadjustment: a vertical #GtkAdjustment, or %NULL
+ *
+ * Creates a new #KatzeScrolled.
+ *
+ * Since: 0.2.0
+ **/
+
+GtkWidget*
+katze_scrolled_new (GtkAdjustment* hadjustment,
+                            GtkAdjustment* vadjustment)
+{
+    if (hadjustment)
+        g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
+    if (vadjustment)
+        g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
+
+    return gtk_widget_new (KATZE_TYPE_SCROLLED,
+                           "hadjustment", hadjustment,
+                           "vadjustment", vadjustment, NULL);
+}
+
+/**
+ * katze_scrolled_activate_scrolling:
+ * @scrolled: a #KatzeScrolled
+ * @widget: a #GtkWidget of which area is made active event source for
+ * drag and kinetic scrolling.
+ *
+ * Activates the widget so that pointer motion events inside the widget are
+ * used to scroll the #KatzeScrolled. The widget can be a child of the
+ * #KatzeScrolled or even a separate widget ("touchpad" style).
+ *
+ * The direct child of the #KatzeScrolled (typically #GtkViewport) is
+ * activated automatically when added. This function has to be used if indirect
+ * descendant widgets are stopping propagation of the button press and release
+ * as well as motion events (for example GtkButton is doing so) but scrolling
+ * should be possible inside their area too.
+ *
+ * This function adds #GDK_BUTTON_PRESS_MASK, #GDK_BUTTON_RELEASE_MASK,
+ * #GDK_POINTER_MOTION_MASK, and #GDK_MOTION_HINT_MAKS into the widgets event mask.
+ */
+
+static void
+katze_scrolled_activate_scrolling (KatzeScrolled* scrolled,
+                                   GtkWidget*     widget)
+{
+    gtk_widget_add_events (widget,
+        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+        | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
+    g_tree_insert (activated_widgets, widget, scrolled);
+}
+
+static void
+katze_scrolled_set_drag_scrolling (KatzeScrolled* scrolled,
+                                   gboolean       drag_scrolling)
+{
+    KatzeScrolledPrivate* priv = scrolled->priv;
+
+    if (priv->drag_scrolling && !drag_scrolling)
+    {
+        if (priv->scrolling_timeout_id)
+        {
+            g_source_remove (priv->scrolling_timeout_id);
+            priv->scrolling_timeout_id = 0;
+            priv->previous_time = 0;
+        }
+
+        gdk_window_hide (priv->vertical_scrollbar_window);
+        gdk_window_hide (priv->horizontal_scrollbar_window);
+        if (priv->hide_scrollbars_timeout_id)
+        {
+            g_source_remove (priv->hide_scrollbars_timeout_id);
+            priv->hide_scrollbars_timeout_id = 0;
+        }
+
+        priv->press_received = FALSE;
+    }
+
+    priv->drag_scrolling = drag_scrolling;
+}
diff --git a/katze/katze-scrolled.h b/katze/katze-scrolled.h
new file mode 100644
index 0000000..e8787f1
--- /dev/null
+++ b/katze/katze-scrolled.h
@@ -0,0 +1,59 @@
+/*
+ Copyright (C) 2007 Henrik Hedberg <hhedberg at innologies.fi>
+ Copyright (C) 2009 Nadav Wiener <nadavwr at yahoo.com>
+ Copyright (C) 2009 Christian Dywan <christian at twotoasts.de>
+
+ 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.
+ */
+
+#ifndef KATZE_SCROLLED_H
+#define KATZE_SCROLLED_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define KATZE_TYPE_SCROLLED (katze_scrolled_get_type())
+#define KATZE_SCROLLED(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), KATZE_TYPE_SCROLLED, KatzeScrolled))
+#define KATZE_SCROLLED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), KATZE_TYPE_SCROLLED, KatzeScrolledClass))
+#define KATZE_IS_SCROLLED(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KATZE_TYPE_SCROLLED))
+#define KATZE_IS_SCROLLED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), KATZE_TYPE_SCROLLED))
+#define KATZE_SCROLLED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), KATZE_TYPE_SCROLLED, KatzeScrolledClass))
+
+typedef struct _KatzeScrolled KatzeScrolled;
+typedef struct _KatzeScrolledClass KatzeScrolledClass;
+typedef struct _KatzeScrolledPrivate KatzeScrolledPrivate;
+
+struct _KatzeScrolled
+{
+    GtkScrolledWindow parent;
+
+    KatzeScrolledPrivate* priv;
+};
+
+struct _KatzeScrolledClass
+{
+    GtkScrolledWindowClass parent;
+
+    /* Padding for future expansion */
+    void (*_katze_reserved1) (void);
+    void (*_katze_reserved2) (void);
+    void (*_katze_reserved3) (void);
+    void (*_katze_reserved4) (void);
+};
+
+GType
+katze_scrolled_get_type         (void);
+
+GtkWidget*
+katze_scrolled_new              (GtkAdjustment* hadjustment,
+                                 GtkAdjustment* vadjustment);
+
+G_END_DECLS
+
+#endif /* __KATZE_SCROLLED_H__ */
diff --git a/katze/katze.h b/katze/katze.h
index 8dd286b..ab85d27 100644
--- a/katze/katze.h
+++ b/katze/katze.h
@@ -21,5 +21,6 @@
 #include "katze-arrayaction.h"
 #include "katze-separatoraction.h"
 #include "katze-net.h"
+#include "katze-scrolled.h"
 
 #endif /* __KATZE_H__ */
diff --git a/midori/midori-browser.c b/midori/midori-browser.c
index 1b7bc76..4e7b6b8 100644
--- a/midori/midori-browser.c
+++ b/midori/midori-browser.c
@@ -1554,9 +1554,6 @@ _midori_browser_add_tab (MidoriBrowser* browser,
     KatzeItem* item;
     guint n;
 
-    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view),
-                                    GTK_POLICY_AUTOMATIC,
-                                    GTK_POLICY_AUTOMATIC);
     GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (view),
                                          GTK_SHADOW_ETCHED_IN);
diff --git a/midori/midori-preferences.c b/midori/midori-preferences.c
index 34bfa79..82c19c4 100644
--- a/midori/midori-preferences.c
+++ b/midori/midori-preferences.c
@@ -315,6 +315,9 @@ midori_preferences_set_settings (MidoriPreferences* preferences,
     GtkWidget* header;
     GtkWindow* parent;
     const gchar* icon_name;
+    #if WEBKIT_CHECK_VERSION (1, 1, 15)
+    GtkSettings* gtk_settings;
+    #endif
     GtkSizeGroup* sizegroup;
     GtkWidget* toolbar;
     GtkWidget* toolbutton;
@@ -341,6 +344,9 @@ midori_preferences_set_settings (MidoriPreferences* preferences,
         gtk_window_get_title (GTK_WINDOW (preferences)))))
         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (preferences)->vbox),
             header, FALSE, FALSE, 0);
+    #if WEBKIT_CHECK_VERSION (1, 1, 15)
+    gtk_settings = parent ? gtk_widget_get_settings (GTK_WIDGET (parent)) : NULL;
+    #endif
 
     preferences->notebook = gtk_notebook_new ();
     gtk_container_set_border_width (GTK_CONTAINER (preferences->notebook), 6);
@@ -487,9 +493,14 @@ midori_preferences_set_settings (MidoriPreferences* preferences,
     gtk_widget_set_tooltip_text (button, _("Load and display images automatically"));
     INDENTED_ADD (button, 0, 1, 0, 1);
     #if WEBKIT_CHECK_VERSION (1, 1, 15)
-    button = katze_property_proxy (settings, "auto-shrink-images", NULL);
-    gtk_button_set_label (GTK_BUTTON (button), _("Shrink images automatically"));
-    gtk_widget_set_tooltip_text (button, _("Automatically shrink standalone images to fit"));
+    if (katze_object_get_boolean (gtk_settings, "gtk-touchscreen-mode"))
+        button = katze_property_proxy (settings, "kinetic-scrolling", NULL);
+    else
+    {
+        button = katze_property_proxy (settings, "auto-shrink-images", NULL);
+        gtk_button_set_label (GTK_BUTTON (button), _("Shrink images automatically"));
+        gtk_widget_set_tooltip_text (button, _("Automatically shrink standalone images to fit"));
+    }
     #else
     button = katze_property_proxy (settings, "middle-click-opens-selection", NULL);
     #endif
diff --git a/midori/midori-view.c b/midori/midori-view.c
index 942f9d7..f3c17ba 100644
--- a/midori/midori-view.c
+++ b/midori/midori-view.c
@@ -59,7 +59,7 @@ midori_view_item_meta_data_changed (KatzeItem*   item,
 
 struct _MidoriView
 {
-    GtkScrolledWindow parent_instance;
+    KatzeScrolled parent_instance;
 
     gchar* uri;
     gchar* title;
@@ -104,10 +104,10 @@ struct _MidoriView
 
 struct _MidoriViewClass
 {
-    GtkScrolledWindowClass parent_class;
+    KatzeScrolledClass parent_class;
 };
 
-G_DEFINE_TYPE (MidoriView, midori_view, GTK_TYPE_SCROLLED_WINDOW)
+G_DEFINE_TYPE (MidoriView, midori_view, KATZE_TYPE_SCROLLED);
 
 GType
 midori_load_status_get_type (void)
@@ -1525,6 +1525,9 @@ webkit_web_view_populate_popup_cb (WebKitWebView* web_view,
     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);
@@ -1533,6 +1536,8 @@ webkit_web_view_populate_popup_cb (WebKitWebView* web_view,
     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;
 
@@ -2329,13 +2334,14 @@ midori_view_new (KatzeNet* net)
 static void
 _midori_view_update_settings (MidoriView* view)
 {
-    gboolean zoom_text_and_images;
+    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,
@@ -2345,8 +2351,9 @@ _midori_view_update_settings (MidoriView* view)
         NULL);
 
     if (view->web_view)
-        g_object_set (view->web_view, "full-content-zoom",
-                      zoom_text_and_images, NULL);
+        g_object_set (view->web_view,
+                      "full-content-zoom", zoom_text_and_images, NULL);
+    g_object_set (view, "kinetic-scrolling", kinetic_scrolling, NULL);
 }
 
 static void
@@ -2379,6 +2386,11 @@ midori_view_settings_notify_cb (MidoriWebSettings* settings,
             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);
diff --git a/midori/midori-websettings.c b/midori/midori-websettings.c
index 9ef8b40..336a14f 100644
--- a/midori/midori-websettings.c
+++ b/midori/midori-websettings.c
@@ -75,6 +75,7 @@ struct _MidoriWebSettings
 
     gboolean zoom_text_and_images;
     gboolean find_while_typing;
+    gboolean kinetic_scrolling;
     MidoriAcceptCookies accept_cookies;
     gboolean original_cookies_only;
     gint maximum_cookie_age;
@@ -151,6 +152,7 @@ enum
 
     PROP_ZOOM_TEXT_AND_IMAGES,
     PROP_FIND_WHILE_TYPING,
+    PROP_KINETIC_SCROLLING,
     PROP_ACCEPT_COOKIES,
     PROP_ORIGINAL_COOKIES_ONLY,
     PROP_MAXIMUM_COOKIE_AGE,
@@ -833,6 +835,22 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
                                      FALSE,
                                      flags));
 
+    /**
+    * MidoriWebSettings:kinetic-scrolling:
+    *
+    * Whether scrolling should kinetically move according to speed.
+    *
+    * Since: 0.2.0
+    */
+    g_object_class_install_property (gobject_class,
+                                     PROP_KINETIC_SCROLLING,
+                                     g_param_spec_boolean (
+                                     "kinetic-scrolling",
+                                     _("Kinetic scrolling"),
+                                     _("Whether scrolling should kinetically move according to speed"),
+                                     TRUE,
+                                     flags));
+
     g_object_class_install_property (gobject_class,
                                      PROP_ACCEPT_COOKIES,
                                      g_param_spec_enum (
@@ -1025,6 +1043,7 @@ midori_web_settings_init (MidoriWebSettings* web_settings)
     web_settings->open_popups_in_tabs = TRUE;
     web_settings->remember_last_form_inputs = TRUE;
     web_settings->remember_last_downloaded_files = TRUE;
+    web_settings->kinetic_scrolling = TRUE;
     web_settings->auto_detect_proxy = TRUE;
 
     g_signal_connect (web_settings, "notify::default-encoding",
@@ -1303,6 +1322,9 @@ midori_web_settings_set_property (GObject*      object,
     case PROP_FIND_WHILE_TYPING:
         web_settings->find_while_typing = g_value_get_boolean (value);
         break;
+    case PROP_KINETIC_SCROLLING:
+        web_settings->kinetic_scrolling = g_value_get_boolean (value);
+        break;
     case PROP_ACCEPT_COOKIES:
         web_settings->accept_cookies = g_value_get_enum (value);
         break;
@@ -1506,6 +1528,9 @@ midori_web_settings_get_property (GObject*    object,
     case PROP_FIND_WHILE_TYPING:
         g_value_set_boolean (value, web_settings->find_while_typing);
         break;
+    case PROP_KINETIC_SCROLLING:
+        g_value_set_boolean (value, web_settings->kinetic_scrolling);
+        break;
     case PROP_ACCEPT_COOKIES:
         g_value_set_enum (value, web_settings->accept_cookies);
         break;



More information about the Xfce4-commits mailing list