X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;ds=sidebyside;f=shell%2Fev-view.c;h=bc3cb980daf295efb03aad5d20d5f0fb6b504b65;hb=f343927df4325959193353c52cff1ea4b20b2286;hp=bbfeecfa64d2b4acb153f331b32813a44120a07a;hpb=012d467f71a44b916e060de7329629c252e4fc01;p=evince.git diff --git a/shell/ev-view.c b/shell/ev-view.c index bbfeecfa..bc3cb980 100644 --- a/shell/ev-view.c +++ b/shell/ev-view.c @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ +#include #include #include #include @@ -41,6 +42,7 @@ #include "ev-job-queue.h" #include "ev-page-cache.h" #include "ev-pixbuf-cache.h" +#include "ev-transition-animation.h" #if !GTK_CHECK_VERSION (2, 11, 7) #include "ev-tooltip.h" #endif @@ -318,6 +320,7 @@ static void compute_selections (EvView GdkPoint *start, GdkPoint *stop); static void clear_selection (EvView *view); +static void clear_link_selected (EvView *view); static void selection_free (EvViewSelection *selection); static char* get_selected_text (EvView *ev_view); static void ev_view_primary_get_cb (GtkClipboard *clipboard, @@ -1521,6 +1524,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); @@ -1567,7 +1576,8 @@ ev_view_handle_cursor_over_xy (EvView *view, gint x, gint y) } else { if (view->cursor == EV_VIEW_CURSOR_LINK || view->cursor == EV_VIEW_CURSOR_IBEAM || - view->cursor == EV_VIEW_CURSOR_DRAG) + view->cursor == EV_VIEW_CURSOR_DRAG || + view->cursor == EV_VIEW_CURSOR_AUTOSCROLL) ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); } } @@ -1649,9 +1659,9 @@ ev_view_form_field_get_region (EvView *view, GList *forms_mapping; forms_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache, - field->page); + field->page->index); ev_form_field_mapping_get_area (forms_mapping, field, &field_area); - doc_rect_to_view_rect (view, field->page, &field_area, &view_area); + doc_rect_to_view_rect (view, field->page->index, &field_area, &view_area); view_area.x -= view->scroll_x; view_area.y -= view->scroll_y; @@ -1702,7 +1712,7 @@ ev_view_form_field_button_create_widget (EvView *view, * we need to update also the region for the current selected item */ forms_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache, - field->page); + field->page->index); for (l = forms_mapping; l; l = g_list_next (l)) { EvFormField *button = ((EvFormFieldMapping *)(l->data))->field; GdkRegion *button_region; @@ -1730,7 +1740,7 @@ ev_view_form_field_button_create_widget (EvView *view, ev_pixbuf_cache_reload_page (view->pixbuf_cache, field_region, - field->page, + field->page->index, view->rotation, view->scale); gdk_region_destroy (field_region); @@ -1760,7 +1770,7 @@ ev_view_form_field_text_save (EvView *view, field->changed = FALSE; ev_pixbuf_cache_reload_page (view->pixbuf_cache, field_region, - field->page, + field->page->index, view->rotation, view->scale); gdk_region_destroy (field_region); @@ -1881,7 +1891,7 @@ ev_view_form_field_choice_save (EvView *view, field->changed = FALSE; ev_pixbuf_cache_reload_page (view->pixbuf_cache, field_region, - field->page, + field->page->index, view->rotation, view->scale); gdk_region_destroy (field_region); @@ -1898,7 +1908,8 @@ ev_view_form_field_choice_changed (GtkWidget *widget, gint item; item = gtk_combo_box_get_active (GTK_COMBO_BOX (widget)); - if (GPOINTER_TO_INT (field_choice->selected_items->data) != item) { + if (!field_choice->selected_items || + GPOINTER_TO_INT (field_choice->selected_items->data) != item) { g_list_free (field_choice->selected_items); field_choice->selected_items = NULL; field_choice->selected_items = g_list_prepend (field_choice->selected_items, @@ -2097,10 +2108,10 @@ ev_view_handle_form_field (EvView *view, g_object_ref (field), (GDestroyNotify)g_object_unref); - form_field_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache, field->page); + form_field_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache, field->page->index); ev_form_field_mapping_get_area (form_field_mapping, field, &field_area); - doc_rect_to_view_rect (view, field->page, &field_area, &view_area); + doc_rect_to_view_rect (view, field->page->index, &field_area, &view_area); view_area.x -= view->scroll_x; view_area.y -= view->scroll_y; @@ -2300,10 +2311,10 @@ ev_view_size_allocate (GtkWidget *widget, continue; form_field_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache, - field->page); + field->page->index); ev_form_field_mapping_get_area (form_field_mapping, field, &field_area); - doc_rect_to_view_rect (view, field->page, &field_area, &view_area); + doc_rect_to_view_rect (view, field->page->index, &field_area, &view_area); view_area.x -= view->scroll_x; view_area.y -= view->scroll_y; @@ -2377,15 +2388,16 @@ ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event) } view->jump_to_find_result = FALSE; + /* Shift+Wheel scrolls the in the perpendicular direction */ if (state & GDK_SHIFT_MASK) { if (event->direction == GDK_SCROLL_UP) event->direction = GDK_SCROLL_LEFT; - if (event->direction == GDK_SCROLL_LEFT) + else if (event->direction == GDK_SCROLL_LEFT) event->direction = GDK_SCROLL_UP; - if (event->direction == GDK_SCROLL_DOWN) + else if (event->direction == GDK_SCROLL_DOWN) event->direction = GDK_SCROLL_RIGHT; - if (event->direction == GDK_SCROLL_RIGHT) + else if (event->direction == GDK_SCROLL_RIGHT) event->direction = GDK_SCROLL_DOWN; event->state &= ~GDK_SHIFT_MASK; @@ -2472,6 +2484,24 @@ ev_view_expose_event (GtkWidget *widget, cairo_t *cr; gint i; + if (view->animation && ev_transition_animation_ready (view->animation)) { + GdkRectangle page_area; + GtkBorder border; + + if (get_page_extents (view, view->current_page, &page_area, &border)) { + cr = gdk_cairo_create (view->layout.bin_window); + + /* normalize to x=0, y=0 */ + cairo_translate (cr, page_area.x, page_area.y); + page_area.x = page_area.y = 0; + + ev_transition_animation_paint (view->animation, cr, page_area); + cairo_destroy (cr); + } + + return TRUE; + } + if (view->presentation) { switch (view->presentation_state) { case EV_PRESENTATION_END: { @@ -2660,6 +2690,9 @@ ev_view_button_press_event (GtkWidget *widget, view->pressed_button = event->button; view->selection_info.in_drag = FALSE; + + if (view->scroll_info.autoscrolling) + return TRUE; switch (event->button) { case 1: { @@ -2710,6 +2743,7 @@ ev_view_button_press_event (GtkWidget *widget, return TRUE; case 3: + view->scroll_info.start_y = event->y; return ev_view_do_popup_menu (view, event->x, event->y); } @@ -2756,16 +2790,28 @@ ev_view_drag_data_get (GtkWidget *widget, if (view->image_dnd_info.image) { GdkPixbuf *pixbuf; - pixbuf = ev_image_get_pixbuf (view->image_dnd_info.image); + ev_document_doc_mutex_lock (); + pixbuf = ev_document_images_get_image (EV_DOCUMENT_IMAGES (view->document), + view->image_dnd_info.image); + ev_document_doc_mutex_unlock (); + gtk_selection_data_set_pixbuf (selection_data, pixbuf); + g_object_unref (pixbuf); } break; case TARGET_DND_URI: if (view->image_dnd_info.image) { + GdkPixbuf *pixbuf; const gchar *tmp_uri; gchar **uris; - tmp_uri = ev_image_save_tmp (view->image_dnd_info.image); + ev_document_doc_mutex_lock (); + pixbuf = ev_document_images_get_image (EV_DOCUMENT_IMAGES (view->document), + view->image_dnd_info.image); + ev_document_doc_mutex_unlock (); + + tmp_uri = ev_image_save_tmp (view->image_dnd_info.image, pixbuf); + g_object_unref (pixbuf); uris = g_new0 (gchar *, 2); uris[0] = (gchar *)tmp_uri; @@ -2874,6 +2920,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 +2989,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 +2998,11 @@ ev_view_motion_notify_event (GtkWidget *widget, y = event->y; } + if (view->scroll_info.autoscrolling) { + view->scroll_info.last_y = y; + return TRUE; + } + if (view->selection_info.in_drag) { if (gtk_drag_check_threshold (widget, view->selection_info.start.x, @@ -2960,6 +3072,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 +3080,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; @@ -3008,16 +3134,30 @@ ev_view_button_release_event (GtkWidget *widget, EvView *view = EV_VIEW (widget); EvLink *link = NULL; - view->drag_info.in_drag = FALSE; view->image_dnd_info.in_drag = FALSE; - if (view->pressed_button == 2) { - ev_view_handle_cursor_over_xy (view, event->x, event->y); + if (view->scroll_info.autoscrolling) { + ev_view_autoscroll_stop (view); + view->pressed_button = -1; + + return TRUE; + } + + if (view->drag_info.in_drag) { + view->drag_info.release_timeout_id = + g_timeout_add (20, + (GSourceFunc)ev_view_scroll_drag_release, view); } - if (view->document && view->pressed_button != 3) { + if (view->document && !view->drag_info.in_drag && view->pressed_button != 3) { link = ev_view_get_link_at_location (view, event->x, event->y); } + + view->drag_info.in_drag = FALSE; + + if (view->pressed_button == 2) { + ev_view_handle_cursor_over_xy (view, event->x, event->y); + } view->pressed_button = -1; @@ -3039,6 +3179,7 @@ ev_view_button_release_event (GtkWidget *widget, } if (view->selection_info.selections) { + clear_link_selected (view); ev_view_update_primary_selection (view); if (view->selection_info.in_drag) { @@ -3299,6 +3440,8 @@ ev_view_key_press_event (GtkWidget *widget, switch (event->keyval) { case GDK_b: case GDK_B: + case GDK_period: + case GDK_KP_Decimal: view->presentation_state = (view->presentation_state == EV_PRESENTATION_BLACK) ? EV_PRESENTATION_NORMAL : EV_PRESENTATION_BLACK; @@ -3500,12 +3643,6 @@ draw_loading_text (EvView *view, cairo_t *cr; gint width, height; - /* Don't annoy users with loading messages during presentations. - * FIXME: Temporary "workaround" for - * http://bugzilla.gnome.org/show_bug.cgi?id=320352 */ - if (view->presentation) - return; - if (!view->loading_text) { const gchar *loading_text = _("Loading..."); PangoLayout *layout; @@ -3606,9 +3743,12 @@ draw_one_page (EvView *view, page_surface = ev_pixbuf_cache_get_surface (view->pixbuf_cache, page); if (!page_surface) { - draw_loading_text (view, - &real_page_area, - expose_area); + if (!view->presentation) { + draw_loading_text (view, + &real_page_area, + expose_area); + } + *page_ready = FALSE; return; @@ -3624,7 +3764,7 @@ draw_one_page (EvView *view, cairo_save (cr); cairo_translate (cr, overlap.x, overlap.y); - + if (width != page_width || height != page_height) { cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST); @@ -3687,6 +3827,7 @@ ev_view_finalize (GObject *object) g_free (view->find_status); clear_selection (view); + clear_link_selected (view); if (view->image_dnd_info.image) g_object_unref (view->image_dnd_info.image); @@ -3737,6 +3878,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 +4198,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; @@ -4087,11 +4244,76 @@ find_changed_cb (EvDocument *document, int page, EvView *view) gtk_widget_queue_draw (GTK_WIDGET (view)); } +static void +ev_view_change_page (EvView *view, + gint new_page) +{ + gint x, y; + + view->current_page = new_page; + view->pending_scroll = SCROLL_TO_PAGE_POSITION; + + if (view->presentation) + ev_view_presentation_transition_start (view); + + gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y); + ev_view_handle_cursor_over_xy (view, x, y); + + gtk_widget_queue_resize (GTK_WIDGET (view)); +} + +static void +ev_view_transition_animation_finish (EvTransitionAnimation *animation, + EvView *view) +{ + g_object_unref (view->animation); + view->animation = NULL; + ev_view_change_page (view, view->current_page); +} + +static void +ev_view_transition_animation_frame (EvTransitionAnimation *animation, + gdouble progress, + EvView *view) +{ + gtk_widget_queue_draw (GTK_WIDGET (view)); +} + +static void +ev_view_presentation_animation_start (EvView *view, + int new_page) +{ + EvTransitionEffect *effect = NULL; + cairo_surface_t *surface; + + if (EV_IS_DOCUMENT_TRANSITION (view->document)) + effect = ev_document_transition_get_effect (EV_DOCUMENT_TRANSITION (view->document), + view->current_page); + if (!effect) + return; + + surface = ev_pixbuf_cache_get_surface (view->pixbuf_cache, view->current_page); + view->animation = ev_transition_animation_new (effect); + ev_transition_animation_set_origin_surface (view->animation, surface); + + g_signal_connect (view->animation, "frame", + G_CALLBACK (ev_view_transition_animation_frame), view); + g_signal_connect (view->animation, "finished", + G_CALLBACK (ev_view_transition_animation_finish), view); +} + static void job_finished_cb (EvPixbufCache *pixbuf_cache, GdkRegion *region, EvView *view) { + if (view->animation) { + cairo_surface_t *surface; + + surface = ev_pixbuf_cache_get_surface (pixbuf_cache, view->current_page); + ev_transition_animation_set_dest_surface (view->animation, surface); + } + if (region) { gdk_window_invalidate_region (view->layout.bin_window, region, TRUE); @@ -4106,18 +4328,10 @@ page_changed_cb (EvPageCache *page_cache, EvView *view) { if (view->current_page != new_page) { - gint x, y; - - view->current_page = new_page; - view->pending_scroll = SCROLL_TO_PAGE_POSITION; - if (view->presentation) - ev_view_presentation_transition_start (view); - - gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y); - ev_view_handle_cursor_over_xy (view, x, y); - - gtk_widget_queue_resize (GTK_WIDGET (view)); + ev_view_presentation_animation_start (view, new_page); + + ev_view_change_page (view, new_page); } else { gtk_widget_queue_draw (GTK_WIDGET (view)); } @@ -4222,6 +4436,79 @@ 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_start (EvView *view) +{ + gint x, y; + + g_return_if_fail (EV_IS_VIEW (view)); + + if (view->scroll_info.autoscrolling) + return; + + view->scroll_info.autoscrolling = TRUE; + view->scroll_info.timeout_id = + g_timeout_add (20, (GSourceFunc)ev_view_autoscroll_cb, + view); + + gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y); + ev_view_handle_cursor_over_xy (view, x, y); +} + +void +ev_view_autoscroll_stop (EvView *view) +{ + gint x, y; + + g_return_if_fail (EV_IS_VIEW (view)); + + if (!view->scroll_info.autoscrolling) + return; + + view->scroll_info.autoscrolling = FALSE; + if (view->scroll_info.timeout_id) { + g_source_remove (view->scroll_info.timeout_id); + view->scroll_info.timeout_id = 0; + } + + gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y); + ev_view_handle_cursor_over_xy (view, x, y); +} + void ev_view_set_document (EvView *view, EvDocument *document) @@ -4423,9 +4710,15 @@ ev_view_set_presentation (EvView *view, if (presentation) ev_view_presentation_transition_start (view); - else + else { ev_view_presentation_transition_stop (view); + if (view->animation) { + /* stop any running animation */ + ev_view_transition_animation_finish (view->animation, view); + } + } + if (GTK_WIDGET_REALIZED (view)) { if (view->presentation) gdk_window_set_background (view->layout.bin_window, @@ -4475,17 +4768,10 @@ ev_view_presentation_transition_start (EvView *view) duration = ev_document_transition_get_page_duration (EV_DOCUMENT_TRANSITION (view->document), view->current_page); if (duration > 0) { -#if GLIB_CHECK_VERSION (2, 13, 0) view->trans_timeout_id = g_timeout_add_seconds (duration, (GSourceFunc) transition_next_page, view); -#else - view->trans_timeout_id = - g_timeout_add (duration * 1000, - (GSourceFunc) transition_next_page, - view); -#endif } } @@ -5052,18 +5338,30 @@ ev_view_find_previous (EvView *view) } } -void ev_view_search_changed (EvView *view) +void +ev_view_search_changed (EvView *view) { /* search string has changed, focus on new search result */ view->jump_to_find_result = TRUE; } -void ev_view_set_highlight_search (EvView *view, gboolean value) +void +ev_view_set_highlight_search (EvView *view, gboolean value) { view->highlight_find_results = value; gtk_widget_queue_draw (GTK_WIDGET (view)); } +void +ev_view_find_cancel (EvView *view) +{ + if (EV_IS_DOCUMENT_FIND (view->document)) { + EvDocumentFind *find = EV_DOCUMENT_FIND (view->document); + + ev_document_find_cancel (find); + } +} + /*** Selections ***/ /* compute_new_selection_rect/text calculates the area currently selected by @@ -5423,15 +5721,19 @@ get_selected_text (EvView *view) EvRenderContext *rc; text = g_string_new (NULL); - rc = ev_render_context_new (view->rotation, 1, view->scale); + rc = ev_render_context_new (NULL, view->rotation, view->scale); ev_document_doc_mutex_lock (); for (l = view->selection_info.selections; l != NULL; l = l->next) { EvViewSelection *selection = (EvViewSelection *)l->data; + EvPage *page; gchar *tmp; - ev_render_context_set_page (rc, selection->page); + page = ev_document_get_page (view->document, selection->page); + ev_render_context_set_page (rc, page); + g_object_unref (page); + tmp = ev_selection_get_selected_text (EV_SELECTION (view->document), rc, selection->style, &(selection->rect)); @@ -5440,28 +5742,36 @@ get_selected_text (EvView *view) g_free (tmp); } - ev_document_doc_mutex_unlock (); - g_object_unref (rc); + ev_document_doc_mutex_unlock (); + normalized_text = g_utf8_normalize (text->str, text->len, G_NORMALIZE_NFKC); g_string_free (text, TRUE); return normalized_text; } +static void +ev_view_clipboard_copy (EvView *view, + const gchar *text) +{ + GtkClipboard *clipboard; + + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), + GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_text (clipboard, text, -1); +} + void ev_view_copy (EvView *ev_view) { - GtkClipboard *clipboard; char *text; if (!EV_IS_SELECTION (ev_view->document)) return; 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); + ev_view_clipboard_copy (ev_view, text); g_free (text); } @@ -5472,15 +5782,20 @@ ev_view_primary_get_cb (GtkClipboard *clipboard, gpointer data) { EvView *ev_view = EV_VIEW (data); - char *text; - - if (!EV_IS_SELECTION (ev_view->document)) - return; - text = get_selected_text (ev_view); - if (text) { - gtk_selection_data_set_text (selection_data, text, -1); - g_free (text); + if (ev_view->link_selected) { + gtk_selection_data_set_text (selection_data, + ev_link_action_get_uri (ev_view->link_selected), + -1); + } else if (EV_IS_SELECTION (ev_view->document) && + ev_view->selection_info.selections) { + gchar *text; + + text = get_selected_text (ev_view); + if (text) { + gtk_selection_data_set_text (selection_data, text, -1); + g_free (text); + } } } @@ -5491,6 +5806,7 @@ ev_view_primary_clear_cb (GtkClipboard *clipboard, EvView *view = EV_VIEW (data); clear_selection (view); + clear_link_selected (view); } static void @@ -5501,7 +5817,7 @@ ev_view_update_primary_selection (EvView *ev_view) clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view), GDK_SELECTION_PRIMARY); - if (ev_view->selection_info.selections) { + if (ev_view->selection_info.selections || ev_view->link_selected) { if (!gtk_clipboard_set_with_owner (clipboard, clipboard_targets, G_N_ELEMENTS (clipboard_targets), @@ -5515,6 +5831,27 @@ ev_view_update_primary_selection (EvView *ev_view) } } +static void +clear_link_selected (EvView *view) +{ + if (view->link_selected) { + g_object_unref (view->link_selected); + view->link_selected = NULL; + } +} + +void +ev_view_copy_link_address (EvView *view, + EvLinkAction *action) +{ + clear_link_selected (view); + + ev_view_clipboard_copy (view, ev_link_action_get_uri (action)); + + view->link_selected = g_object_ref (action); + ev_view_update_primary_selection (view); +} + /*** Cursor operations ***/ static GdkCursor * @@ -5563,6 +5900,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) { @@ -5612,7 +5952,12 @@ ev_view_next_page (EvView *view) view->presentation_state == EV_PRESENTATION_WHITE)) { ev_view_reset_presentation_state (view); return FALSE; - } + } + + if (view->animation) { + ev_view_transition_animation_finish (view->animation, view); + return TRUE; + } ev_view_presentation_transition_stop (view); ev_view_reset_presentation_state (view); @@ -5663,6 +6008,11 @@ ev_view_previous_page (EvView *view) return FALSE; } + if (view->animation) { + ev_view_transition_animation_finish (view->animation, view); + return TRUE; + } + ev_view_reset_presentation_state (view); page = ev_page_cache_get_current_page (view->page_cache);