]> www.fi.muni.cz Git - evince.git/blobdiff - shell/ev-view.c
LPD printers use postscript, too
[evince.git] / shell / ev-view.c
index e39f34931dc73ef779ee9b9d0af133ac63a447d8..cfcc8454889f69f9dfba28181255cc0644df9ebc 100644 (file)
@@ -75,7 +75,8 @@ typedef enum {
        EV_VIEW_CURSOR_NORMAL,
        EV_VIEW_CURSOR_LINK,
        EV_VIEW_CURSOR_WAIT,
-       EV_VIEW_CURSOR_HIDDEN
+       EV_VIEW_CURSOR_HIDDEN,
+       EV_VIEW_CURSOR_DRAG
 } EvViewCursor;
 
 #define ZOOM_IN_FACTOR  1.2
@@ -84,6 +85,18 @@ typedef enum {
 #define MIN_SCALE 0.05409
 #define MAX_SCALE 6.0
 
+typedef struct {
+       EvRectangle rect;
+       int page;
+} EvViewSelection;
+
+typedef struct {
+        gboolean dragging;
+       GdkPoint start;
+       gdouble hadj;
+       gdouble vadj;
+} DragInfo;
+
 struct _EvView {
        GtkWidget parent_instance;
 
@@ -97,10 +110,10 @@ struct _EvView {
        int scroll_x;
        int scroll_y;
 
+       DragInfo drag_info;
        gboolean pressed_button;
-       gboolean has_selection;
        GdkPoint selection_start;
-       EvRectangle selection;
+       GList *selections;
        EvViewCursor cursor;
 
        GtkAdjustment *hadjustment;
@@ -309,6 +322,13 @@ view_update_range_and_current_page (EvView *view)
                                        view->scale);   
 }
 
+static void
+clear_selection (EvView *view)
+{
+       g_list_foreach (view->selections, (GFunc)g_free, NULL);
+       view->selections = NULL;
+}
+
 /*** Virtual function implementations ***/
 
 static void
@@ -321,6 +341,8 @@ ev_view_finalize (GObject *object)
        g_free (view->status);
        g_free (view->find_status);
 
+       clear_selection (view);
+
        G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
 }
 
@@ -466,19 +488,47 @@ get_page_extents (EvView       *view,
        return TRUE;
 }
 
-#if 0
 static void
-view_rect_to_doc_rect (EvView *view, GdkRectangle *view_rect, EvRectangle *doc_rect)
+view_rect_to_doc_rect (EvView *view,
+                      GdkRectangle *view_rect,
+                      GdkRectangle *page_area,
+                      EvRectangle  *doc_rect)
+{
+       doc_rect->x1 = floor ((view_rect->x - page_area->x) / view->scale);
+       doc_rect->y1 = floor ((view_rect->y - page_area->y) / view->scale);
+       doc_rect->x2 = doc_rect->x1 + ceil (view_rect->width / view->scale);
+       doc_rect->y2 = doc_rect->y1 + ceil (view_rect->height / view->scale);
+}
+
+static void
+compute_selections (EvView *view, GdkRectangle *view_rect)
 {
-       int x_offset, y_offset;
+       int n_pages, i;
+
+       clear_selection (view);
+
+       n_pages = ev_page_cache_get_n_pages (view->page_cache);
+       for (i = 0; i < n_pages; i++) {
+               GdkRectangle page_area;
+               GtkBorder border;
+
+               if (get_page_extents (view, i, &page_area, &border)) {
+                       GdkRectangle overlap;
+
+                       if (gdk_rectangle_intersect (&page_area, view_rect, &overlap)) {
+                               EvViewSelection *selection;
 
-       ev_view_get_offsets (view, &x_offset, &y_offset);
-       doc_rect->x1 = (double) (view_rect->x - x_offset) / view->scale;
-       doc_rect->y1 = (double) (view_rect->y - y_offset) / view->scale;
-       doc_rect->x2 = doc_rect->x1 + (double) view_rect->width / view->scale;
-       doc_rect->y2 = doc_rect->y1 + (double) view_rect->height / view->scale;
+                               selection = g_new0 (EvViewSelection, 1);
+                               selection->page = i;
+                               view_rect_to_doc_rect (view, &overlap, &page_area,
+                                                      &(selection->rect));
+
+                               view->selections = g_list_append
+                                               (view->selections, selection);
+                       }
+               }
+       }
 }
-#endif
 
 static void
 doc_rect_to_view_rect (EvView       *view,
@@ -859,7 +909,12 @@ highlight_find_results (EvView *view, int page)
                GdkRectangle view_rectangle;
                guchar alpha;
 
-               alpha = (i == view->find_result) ? 0x90 : 0x20;
+               if (i == view->find_result && page == view->find_page) {
+                       alpha = 0x90;
+               } else {
+                       alpha = 0x20;
+               }
+
                ev_document_find_get_result (find, page,
                                             i, &rectangle);
                doc_rect_to_view_rect (view, page, &rectangle, &view_rectangle);
@@ -933,6 +988,8 @@ static void
 ev_view_bin_expose (EvView         *view,
                    GdkEventExpose *event)
 {
+       GdkRectangle rubberband;
+       GList *l;
        int i;
 
        if (view->document == NULL)
@@ -952,17 +1009,16 @@ ev_view_bin_expose (EvView         *view,
                }
        }
 
-#if 0
-       if (view->has_selection) {
-               GdkRectangle rubberband;
+       for (l = view->selections; l != NULL; l = l->next) {
+               EvViewSelection *selection = (EvViewSelection *)l->data;
 
-               doc_rect_to_view_rect (view, &view->selection, &rubberband);
+               doc_rect_to_view_rect (view, selection->page,
+                                      &selection->rect, &rubberband);
                if (rubberband.width > 0 && rubberband.height > 0) {
                        draw_rubberband (GTK_WIDGET (view), view->bin_window,
                                         &rubberband, 0x40);
                }
        }
-#endif
 }
 
 static gboolean
@@ -980,32 +1036,56 @@ ev_view_expose_event (GtkWidget      *widget,
 }
 
 void
-ev_view_select_all (EvView *ev_view)
+ev_view_select_all (EvView *view)
 {
-#if 0
-       GtkWidget *widget = GTK_WIDGET (ev_view);
-       GdkRectangle selection;
-       int width, height;
-       int x_offset, y_offset;
+       int n_pages, i;
 
-       g_return_if_fail (EV_IS_VIEW (ev_view));
+       clear_selection (view);
 
+       n_pages = ev_page_cache_get_n_pages (view->page_cache);
+       for (i = 0; i < n_pages; i++) {
+               int width, height;
+               EvViewSelection *selection;
 
-       ev_view_get_offsets (ev_view, &x_offset, &y_offset);
-       ev_page_cache_get_size (ev_view->page_cache,
-                               ev_view->current_page,
-                               ev_view->scale,
-                               &width, &height);
+               ev_page_cache_get_size (view->page_cache,
+                                       i, 1.0, &width, &height);
+
+               selection = g_new0 (EvViewSelection, 1);
+               selection->page = i;
+               selection->rect.x1 = selection->rect.y1 = 0;
+               selection->rect.x2 = width;
+               selection->rect.y2 = height;
+
+               view->selections = g_list_append (view->selections, selection);
+       }
+
+       gtk_widget_queue_draw (GTK_WIDGET (view));
+}
+
+static char *
+get_selected_text (EvView *ev_view)
+{
+       GString *text;
+       GList *l;
+
+       text = g_string_new (NULL);
+
+       ev_document_doc_mutex_lock ();
+
+       for (l = ev_view->selections; l != NULL; l = l->next) {
+               EvViewSelection *selection = (EvViewSelection *)l->data;
+               char *tmp;
 
-       ev_view->has_selection = TRUE;
-       selection.x = x_offset + ev_view->border.left;
-        selection.y = y_offset + ev_view->border.top;
-       selection.width = width;
-       selection.height = height;
-       view_rect_to_doc_rect (ev_view, &selection, &ev_view->selection);
+               tmp = ev_document_get_text (ev_view->document,
+                                           selection->page,
+                                           &selection->rect);
+               g_string_append (text, tmp);
+               g_free (tmp);
+       }
 
-       gtk_widget_queue_draw (widget);
-#endif
+       ev_document_doc_mutex_unlock ();
+
+       return g_string_free (text, FALSE);
 }
 
 void
@@ -1018,12 +1098,7 @@ ev_view_copy (EvView *ev_view)
                return;
        }
 
-       ev_document_doc_mutex_lock ();
-       text = ev_document_get_text (ev_view->document,
-                                    ev_view->current_page,
-                                    &ev_view->selection);
-       ev_document_doc_mutex_unlock ();
-
+       text = get_selected_text (ev_view);
        clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
                                              GDK_SELECTION_CLIPBOARD);
        gtk_clipboard_set_text (clipboard, text, -1);
@@ -1043,21 +1118,18 @@ ev_view_primary_get_cb (GtkClipboard     *clipboard,
                return;
        }
 
-       ev_document_doc_mutex_lock ();
-       text = ev_document_get_text (ev_view->document,
-                                    ev_view->current_page,
-                                    &ev_view->selection);
-       ev_document_doc_mutex_unlock ();
+       text = get_selected_text (ev_view);
        gtk_selection_data_set_text (selection_data, text, -1);
+       g_free (text);
 }
 
 static void
 ev_view_primary_clear_cb (GtkClipboard *clipboard,
                          gpointer      data)
 {
-       EvView *ev_view = EV_VIEW (data);
+       EvView *view = EV_VIEW (data);
 
-       ev_view->has_selection = FALSE;
+       clear_selection (view);
 }
 
 static void
@@ -1068,7 +1140,7 @@ ev_view_update_primary_selection (EvView *ev_view)
        clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
                                               GDK_SELECTION_PRIMARY);
 
-       if (ev_view->has_selection) {
+       if (ev_view->selections) {
                if (!gtk_clipboard_set_with_owner (clipboard,
                                                   targets,
                                                   G_N_ELEMENTS (targets),
@@ -1082,6 +1154,58 @@ ev_view_update_primary_selection (EvView *ev_view)
        }
 }
 
+static GdkCursor *
+ev_view_create_invisible_cursor(void)
+{
+       GdkBitmap *empty;
+       GdkColor black = { 0, 0, 0, 0 };
+       static char bits[] = { 0x00 };
+
+       empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
+
+       return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
+}
+
+static void
+ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
+{
+       GdkCursor *cursor = NULL;
+       GdkDisplay *display;
+       GtkWidget *widget;
+
+       if (view->cursor == new_cursor) {
+               return;
+       }
+
+       widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
+       display = gtk_widget_get_display (widget);
+       view->cursor = new_cursor;
+
+       switch (new_cursor) {
+               case EV_VIEW_CURSOR_NORMAL:
+                       gdk_window_set_cursor (widget->window, NULL);
+                       break;
+               case EV_VIEW_CURSOR_LINK:
+                       cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
+                       break;
+               case EV_VIEW_CURSOR_WAIT:
+                       cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
+                       break;
+                case EV_VIEW_CURSOR_HIDDEN:
+                        cursor = ev_view_create_invisible_cursor ();
+                        break;
+               case EV_VIEW_CURSOR_DRAG:
+                       cursor = gdk_cursor_new_for_display (display, GDK_FLEUR);
+                       break;
+       }
+
+       if (cursor) {
+               gdk_window_set_cursor (widget->window, cursor);
+               gdk_cursor_unref (cursor);
+               gdk_flush();
+       }
+}
+
 static gboolean
 ev_view_button_press_event (GtkWidget      *widget,
                            GdkEventButton *event)
@@ -1096,17 +1220,28 @@ ev_view_button_press_event (GtkWidget      *widget,
 
        switch (event->button) {
                case 1:
-                       if (view->has_selection) {
-                               view->has_selection = FALSE;
+                       if (view->selections) {
+                               clear_selection (view);
                                gtk_widget_queue_draw (widget);
                        }
 
                        view->selection_start.x = event->x;
                        view->selection_start.y = event->y;
-                       break;
+                       return TRUE;
+               case 2:
+                       /* use root coordinates as reference point because
+                        * scrolling changes window relative coordinates */
+                       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);
+                       view->drag_info.vadj = gtk_adjustment_get_value (view->vadjustment);
+
+                       ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG);
+
+                       return TRUE;
        }
 
-       return TRUE;
+       return FALSE;
 }
 
 static char *
@@ -1160,57 +1295,6 @@ ev_view_set_find_status (EvView *view, const char *message)
        g_object_notify (G_OBJECT (view), "find-status");
 }
 
-static GdkCursor *
-ev_view_create_invisible_cursor(void)
-{
-       GdkBitmap *empty;
-       GdkColor black = { 0, 0, 0, 0 };
-       static char bits[] = { 0x00 };
-
-       empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
-
-       return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
-}
-
-static void
-ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
-{
-       GdkCursor *cursor = NULL;
-       GdkDisplay *display;
-       GtkWidget *widget;
-
-       if (view->cursor == new_cursor) {
-               return;
-       }
-
-       widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
-       display = gtk_widget_get_display (widget);
-       view->cursor = new_cursor;
-
-       switch (new_cursor) {
-               case EV_VIEW_CURSOR_NORMAL:
-                       gdk_window_set_cursor (widget->window, NULL);
-                       break;
-               case EV_VIEW_CURSOR_LINK:
-                       cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
-                       break;
-               case EV_VIEW_CURSOR_WAIT:
-                       cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
-                       break;
-                case EV_VIEW_CURSOR_HIDDEN:
-                        cursor = ev_view_create_invisible_cursor ();
-                        break;
-
-       }
-
-       if (cursor) {
-               gdk_window_set_cursor (widget->window, cursor);
-               gdk_cursor_unref (cursor);
-               gdk_flush();
-       }
-}
-
-
 static void
 find_page_at_location (EvView  *view,
                       gdouble  x,
@@ -1278,20 +1362,56 @@ ev_view_motion_notify_event (GtkWidget      *widget,
 {
        EvView *view = EV_VIEW (widget);
 
-       if (view->pressed_button > 0) {
+       if (view->pressed_button == 1) {
                GdkRectangle selection;
 
-               view->has_selection = TRUE;
                selection.x = MIN (view->selection_start.x, event->x);
                selection.y = MIN (view->selection_start.y, event->y);
                selection.width = ABS (view->selection_start.x - event->x) + 1;
                selection.height = ABS (view->selection_start.y - event->y) + 1;
-#if 0
-               view_rect_to_doc_rect (view, &selection, &view->selection);
-#endif
+
+               compute_selections (view, &selection);
 
                gtk_widget_queue_draw (widget);
-       } else if (view->document) {
+
+               return TRUE;
+       } else if (view->pressed_button == 2) {
+               if (!view->drag_info.dragging) {
+                       gboolean start;
+
+                       start = gtk_drag_check_threshold (widget,
+                                                         view->drag_info.start.x,
+                                                         view->drag_info.start.y,
+                                                         event->x_root,
+                                                         event->y_root);
+                       view->drag_info.dragging = start;
+               }
+
+               if (view->drag_info.dragging) {
+                       int dx, dy;
+                       gdouble dhadj_value, dvadj_value;
+
+                       dx = event->x_root - view->drag_info.start.x;
+                       dy = event->y_root - view->drag_info.start.y;
+
+                       dhadj_value = view->hadjustment->page_size *
+                                     (gdouble)dx / widget->allocation.width;
+                       dvadj_value = view->vadjustment->page_size *
+                                     (gdouble)dy / widget->allocation.height;
+
+                       /* clamp scrolling to visible area */
+                       gtk_adjustment_set_value (view->hadjustment,
+                                                 MIN(view->drag_info.hadj - dhadj_value,
+                                                     view->hadjustment->upper -
+                                                     view->hadjustment->page_size));
+                       gtk_adjustment_set_value (view->vadjustment,
+                                                 MIN(view->drag_info.vadj - dvadj_value,
+                                                     view->vadjustment->upper -
+                                                     view->vadjustment->page_size));
+
+                       return TRUE;
+               }
+       } else if (view->pressed_button <= 0 && view->document) {
                EvLink *link;
 
                link = get_link_at_location (view, event->x, event->y);
@@ -1308,9 +1428,10 @@ ev_view_motion_notify_event (GtkWidget      *widget,
                                ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
                        }
                }
+               return TRUE;
        }
 
-       return TRUE;
+       return FALSE;
 }
 
 /* FIXME: standardize this sometime */
@@ -1344,9 +1465,14 @@ ev_view_button_release_event (GtkWidget      *widget,
 {
        EvView *view = EV_VIEW (widget);
 
+       if (view->pressed_button == 2) {
+               ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
+       }
+
        view->pressed_button = -1;
+       view->drag_info.dragging = FALSE;
 
-       if (view->has_selection) {
+       if (view->selections) {
                ev_view_update_primary_selection (view);
        } else if (view->document) {
                EvLink *link;
@@ -1730,6 +1856,7 @@ ev_view_init (EvView *view)
        view->current_page = 0;
        view->pressed_button = -1;
        view->cursor = EV_VIEW_CURSOR_NORMAL;
+       view->drag_info.dragging = FALSE;
 
        view->continuous = TRUE;
        view->dual_page = FALSE;
@@ -1913,7 +2040,6 @@ page_changed_cb (EvPageCache *page_cache,
                                &old_width, &old_height);
 
        view->current_page = new_page;
-       view->has_selection = FALSE;
 
        ev_page_cache_get_size (page_cache,
                                new_page,