]> www.fi.muni.cz Git - evince.git/blobdiff - shell/ev-view.c
Include config.h. Bug #504721.
[evince.git] / shell / ev-view.c
index a6007c911897f83665166ce0f5c98cc3b1b9f70f..5944e348305db28d0e60026f86090b3a48e4969f 100644 (file)
@@ -18,6 +18,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#include <config.h>
 #include <stdlib.h>
 #include <math.h>
 #include <string.h>
@@ -1521,6 +1522,12 @@ ev_view_handle_cursor_over_xy (EvView *view, gint x, gint y)
                        ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG);
                return;
        }
+
+       if (view->scroll_info.autoscrolling) {
+               if (view->cursor != EV_VIEW_CURSOR_AUTOSCROLL)
+                       ev_view_set_cursor (view, EV_VIEW_CURSOR_AUTOSCROLL);
+               return;
+       }
        
        link = ev_view_get_link_at_location (view, x, y);
        
@@ -2663,6 +2670,12 @@ ev_view_button_press_event (GtkWidget      *widget,
        
        switch (event->button) {
                case 1: {
+
+                       if (view->scroll_info.autoscrolling == TRUE) {
+                               view->scroll_info.autoscrolling = FALSE;
+                               return TRUE;
+                       }
+
                        EvImage *image;
                        EvFormField *field;
 
@@ -2701,6 +2714,10 @@ ev_view_button_press_event (GtkWidget      *widget,
                case 2:
                        /* use root coordinates as reference point because
                         * scrolling changes window relative coordinates */
+                       if (view->scroll_info.autoscrolling == TRUE) {
+                               view->scroll_info.autoscrolling = FALSE;
+                               return TRUE;
+                       }
                        view->drag_info.start.x = event->x_root;
                        view->drag_info.start.y = event->y_root;
                        view->drag_info.hadj = gtk_adjustment_get_value (view->hadjustment);
@@ -2710,6 +2727,8 @@ ev_view_button_press_event (GtkWidget      *widget,
 
                        return TRUE;
                case 3:
+                       if (!view->scroll_info.autoscrolling)
+                               view->scroll_info.start_y = event->y;
                        return ev_view_do_popup_menu (view, event->x, event->y);
        }
        
@@ -2874,6 +2893,66 @@ selection_scroll_timeout_cb (EvView *view)
        return TRUE;
 }
 
+static gboolean
+ev_view_drag_update_momentum (EvView *view)
+{
+       int i;
+       if (!view->drag_info.in_drag)
+               return FALSE;
+       
+       for (i = DRAG_HISTORY - 1; i > 0; i--) {
+               view->drag_info.buffer[i].x = view->drag_info.buffer[i-1].x;
+               view->drag_info.buffer[i].y = view->drag_info.buffer[i-1].y;
+       }
+
+       /* Momentum is a moving average of 10ms granularity over
+        * the last 100ms with each 10ms stored in buffer. 
+        */
+       
+       view->drag_info.momentum.x = (view->drag_info.buffer[DRAG_HISTORY - 1].x - view->drag_info.buffer[0].x);
+       view->drag_info.momentum.y = (view->drag_info.buffer[DRAG_HISTORY - 1].y - view->drag_info.buffer[0].y);
+
+       return TRUE;
+}
+
+static gboolean
+ev_view_scroll_drag_release (EvView *view)
+{
+       gdouble dhadj_value, dvadj_value;
+       gdouble oldhadjustment, oldvadjustment;
+
+       view->drag_info.momentum.x /= 1.2;
+       view->drag_info.momentum.y /= 1.2; /* Alter these constants to change "friction" */
+
+       dhadj_value = view->hadjustment->page_size *
+                     (gdouble)view->drag_info.momentum.x / GTK_WIDGET (view)->allocation.width;
+       dvadj_value = view->vadjustment->page_size *
+                     (gdouble)view->drag_info.momentum.y / GTK_WIDGET (view)->allocation.height;
+
+       oldhadjustment = gtk_adjustment_get_value (view->hadjustment);
+       oldvadjustment = gtk_adjustment_get_value (view->vadjustment);
+
+       if (((oldhadjustment + dhadj_value) > (view->hadjustment->upper - view->hadjustment->page_size)) ||
+          ((oldhadjustment + dhadj_value) < 0))
+               view->drag_info.momentum.x *= -0.5; /* 0.5 rather than 1 means the edges absorb some momentum */
+       if (((oldvadjustment + dvadj_value) > (view->vadjustment->upper - view->vadjustment->page_size)) ||
+          ((oldvadjustment + dvadj_value) < 0))
+               view->drag_info.momentum.y *= -0.5;
+
+       gtk_adjustment_set_value (view->hadjustment,
+                               MIN (oldhadjustment + dhadj_value,
+                               view->hadjustment->upper - view->hadjustment->page_size));
+       gtk_adjustment_set_value (view->vadjustment,
+                               MIN (oldvadjustment + dvadj_value,
+                               view->vadjustment->upper - view->vadjustment->page_size));
+
+       if (((view->drag_info.momentum.x < 1) && (view->drag_info.momentum.x > -1)) &&
+          ((view->drag_info.momentum.y < 1) && (view->drag_info.momentum.y > -1)))
+               return FALSE;
+       else
+               return TRUE;
+}
+
 static gboolean
 ev_view_motion_notify_event (GtkWidget      *widget,
                             GdkEventMotion *event)
@@ -2883,6 +2962,7 @@ ev_view_motion_notify_event (GtkWidget      *widget,
 
        if (!view->document)
                return FALSE;
+       
                
         if (event->is_hint || event->window != view->layout.bin_window) {
            gtk_widget_get_pointer (widget, &x, &y);
@@ -2891,6 +2971,10 @@ ev_view_motion_notify_event (GtkWidget      *widget,
            y = event->y;
        }
 
+       if (view->scroll_info.autoscrolling) {
+               view->scroll_info.last_y = y;
+       }
+
        if (view->selection_info.in_drag) {
                if (gtk_drag_check_threshold (widget,
                                              view->selection_info.start.x,
@@ -2960,6 +3044,7 @@ ev_view_motion_notify_event (GtkWidget      *widget,
        } else if (view->pressed_button == 2) {
                if (!view->drag_info.in_drag) {
                        gboolean start;
+                       int i;
 
                        start = gtk_drag_check_threshold (widget,
                                                          view->drag_info.start.x,
@@ -2967,12 +3052,25 @@ ev_view_motion_notify_event (GtkWidget      *widget,
                                                          event->x_root,
                                                          event->y_root);
                        view->drag_info.in_drag = start;
+                       view->drag_info.drag_timeout_id = g_timeout_add (10,
+                               (GSourceFunc)ev_view_drag_update_momentum, view);
+                       /* Set 100 to choose how long it takes to build up momentum */
+                       /* Clear out previous momentum info: */
+                       for (i = 0; i < DRAG_HISTORY; i++) {
+                               view->drag_info.buffer[i].x = event->x;
+                               view->drag_info.buffer[i].y = event->y;
+                       }
+                       view->drag_info.momentum.x = 0;
+                       view->drag_info.momentum.y = 0;
                }
 
                if (view->drag_info.in_drag) {
                        int dx, dy;
                        gdouble dhadj_value, dvadj_value;
 
+                       view->drag_info.buffer[0].x = event->x;
+                       view->drag_info.buffer[0].y = event->y;
+
                        dx = event->x_root - view->drag_info.start.x;
                        dy = event->y_root - view->drag_info.start.y;
 
@@ -3010,6 +3108,9 @@ ev_view_button_release_event (GtkWidget      *widget,
 
        view->drag_info.in_drag = FALSE;
        view->image_dnd_info.in_drag = FALSE;
+       
+       view->drag_info.release_timeout_id = g_timeout_add (20,
+                       (GSourceFunc)ev_view_scroll_drag_release, view);
 
        if (view->pressed_button == 2) {
                ev_view_handle_cursor_over_xy (view, event->x, event->y);
@@ -3737,6 +3838,21 @@ ev_view_destroy (GtkObject *object)
                view->loading_text = NULL;
        }
 
+       if (view->scroll_info.timeout_id) {
+           g_source_remove (view->scroll_info.timeout_id);
+           view->scroll_info.timeout_id = 0;
+       }
+
+       if (view->drag_info.drag_timeout_id) {
+               g_source_remove (view->drag_info.drag_timeout_id);
+               view->drag_info.drag_timeout_id = 0;
+       }
+
+       if (view->drag_info.release_timeout_id) {
+               g_source_remove (view->drag_info.release_timeout_id);
+               view->drag_info.release_timeout_id = 0;
+       }
+
        ev_view_presentation_transition_stop (view);
 
        ev_view_set_scroll_adjustments (GTK_LAYOUT (view), NULL, NULL);
@@ -4042,6 +4158,7 @@ ev_view_init (EvView *view)
        view->pressed_button = -1;
        view->cursor = EV_VIEW_CURSOR_NORMAL;
        view->drag_info.in_drag = FALSE;
+       view->scroll_info.autoscrolling = FALSE;
        view->selection_info.selections = NULL;
        view->selection_info.in_selection = FALSE;
        view->selection_info.in_drag = FALSE;
@@ -4222,6 +4339,45 @@ ev_view_set_loading (EvView        *view,
        gtk_widget_queue_draw (GTK_WIDGET (view));
 }
 
+static gboolean ev_view_autoscroll_cb (EvView *view)
+{
+       gdouble speed, value;
+
+       /* If the user stops autoscrolling, autoscrolling will be
+        * set to false but the timeout will continue; stop the timeout: */
+       if (!view->scroll_info.autoscrolling) {
+               view->scroll_info.timeout_id = 0;
+               return FALSE;
+       }
+       
+       if (view->scroll_info.last_y > view->scroll_info.start_y && 
+               (view->scroll_info.last_y < view->scroll_info.start_y))
+               return TRUE; 
+
+       /* Replace 100 with your speed of choice: The lower the faster.
+        * Replace 3 with another speed of choice: The higher, the faster it accelerated
+        *      based on the distance of the starting point from the mouse
+        * (All also effected by the timeout interval of this callback) */
+
+       if (view->scroll_info.start_y > view->scroll_info.last_y)
+               speed = -pow ((((gdouble)view->scroll_info.start_y - view->scroll_info.last_y) / 100), 3);
+       else
+               speed = pow ((((gdouble)view->scroll_info.last_y - view->scroll_info.start_y) / 100), 3);
+       
+       value = gtk_adjustment_get_value (view->vadjustment);
+       value = CLAMP (value + speed, 0, view->vadjustment->upper - view->vadjustment->page_size);
+       gtk_adjustment_set_value (view->vadjustment, value);
+       
+       return TRUE;
+
+}
+
+void ev_view_autoscroll(EvView *view)
+{
+       view->scroll_info.autoscrolling = TRUE;
+       view->scroll_info.timeout_id = g_timeout_add (20, (GSourceFunc)(ev_view_autoscroll_cb), view);
+}
+
 void
 ev_view_set_document (EvView     *view,
                      EvDocument *document)
@@ -4276,7 +4432,9 @@ ev_view_set_zoom (EvView   *view,
        else
                scale = factor;
 
-       scale = CLAMP (scale, view->min_scale, view->max_scale);
+       scale = CLAMP (scale,
+                      view->sizing_mode == EV_SIZING_FREE ? view->min_scale : 0,
+                      view->max_scale);
 
        if (ABS (view->scale - scale) < EPSILON)
                return;
@@ -5561,6 +5719,9 @@ ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
                case EV_VIEW_CURSOR_DRAG:
                        cursor = gdk_cursor_new_for_display (display, GDK_FLEUR);
                        break;
+               case EV_VIEW_CURSOR_AUTOSCROLL:
+                       cursor = gdk_cursor_new_for_display (display, GDK_DOUBLE_ARROW);
+                       break;
        }
 
        if (cursor) {