X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;f=shell%2Fev-view.c;h=cfcc8454889f69f9dfba28181255cc0644df9ebc;hb=85c23b189d1c83d7c84774b4ecc09334316ca143;hp=e39f34931dc73ef779ee9b9d0af133ac63a447d8;hpb=42414e4f59ef90ea80e49214ea6c50b38dd4ba2d;p=evince.git diff --git a/shell/ev-view.c b/shell/ev-view.c index e39f3493..cfcc8454 100644 --- a/shell/ev-view.c +++ b/shell/ev-view.c @@ -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,