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=750ee5e4d3c5c5b26a2a92500914329b1edf1d76;hb=5e55b6b5e74175b5638337616b84527fb8286908;hp=cd6d27ec202a98524839300783124de7b4e13f73;hpb=4aa27ea1d50e2812707e2fe06247eb8410310979;p=evince.git diff --git a/shell/ev-view.c b/shell/ev-view.c index cd6d27ec..750ee5e4 100644 --- a/shell/ev-view.c +++ b/shell/ev-view.c @@ -18,41 +18,40 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ +#include "config.h" + #include #include #include -#include + #include -#include -#include -#include +#include #include -#include "ev-marshal.h" -#include "ev-view.h" -#include "ev-view-private.h" -#include "ev-utils.h" -#include "ev-selection.h" -#include "ev-document-links.h" +#include "ev-application.h" +#include "ev-document-forms.h" #include "ev-document-images.h" -#include "ev-document-find.h" -#include "ev-document-transition.h" +#include "ev-document-links.h" #include "ev-document-misc.h" -#include "ev-job-queue.h" +#include "ev-document-transition.h" +#include "ev-marshal.h" #include "ev-page-cache.h" #include "ev-pixbuf-cache.h" +#include "ev-transition-animation.h" +#include "ev-utils.h" +#include "ev-view.h" +#include "ev-view-private.h" + +#if !GTK_CHECK_VERSION (2, 11, 7) #include "ev-tooltip.h" -#include "ev-application.h" +#endif #define EV_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_VIEW, EvViewClass)) #define EV_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_VIEW)) #define EV_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_VIEW, EvViewClass)) - enum { PROP_0, - PROP_STATUS, - PROP_FIND_STATUS, PROP_CONTINUOUS, PROP_DUAL_PAGE, PROP_FULLSCREEN, @@ -66,6 +65,7 @@ enum { enum { SIGNAL_BINDING_ACTIVATED, SIGNAL_ZOOM_INVALID, + SIGNAL_HANDLE_LINK, SIGNAL_EXTERNAL_LINK, SIGNAL_POPUP_MENU, N_SIGNALS, @@ -114,7 +114,7 @@ typedef enum { /*** Scrolling ***/ static void scroll_to_current_page (EvView *view, GtkOrientation orientation); -static void ev_view_set_scroll_adjustments (EvView *view, +static void ev_view_set_scroll_adjustments (GtkLayout *layout, GtkAdjustment *hadjustment, GtkAdjustment *vadjustment); static void view_update_range_and_current_page (EvView *view); @@ -156,19 +156,21 @@ static void find_page_at_location (EvView gint *page, gint *x_offset, gint *y_offset); -static gboolean doc_point_to_view_point (EvView *view, - int page, - EvPoint *doc_point, - GdkPoint *view_point); +static gboolean doc_point_to_view_point (EvView *view, + int page, + EvPoint *doc_point, + GdkPoint *view_point); /*** Hyperrefs ***/ -static EvLink * ev_view_get_link_at_location (EvView *view, - gdouble x, - gdouble y); +static EvLink * ev_view_get_link_at_location (EvView *view, + gdouble x, + gdouble y); static char* tip_from_link (EvView *view, EvLink *link); -static void handle_link_over_xy (EvView *view, - gint x, - gint y); +/*** Forms ***/ +static EvFormField *ev_view_get_form_field_at_location (EvView *view, + gdouble x, + gdouble y); + /*** GtkWidget implementation ***/ static void ev_view_size_request_continuous_dual_page (EvView *view, GtkRequisition *requisition); @@ -200,6 +202,7 @@ static gboolean ev_view_leave_notify_event (GtkWidget GdkEventCrossing *event); static void ev_view_style_set (GtkWidget *widget, GtkStyle *old_style); +static void ev_view_remove_all (EvView *view); static AtkObject *ev_view_get_accessible (GtkWidget *widget); @@ -213,6 +216,7 @@ static void highlight_find_results (EvView int page); static void draw_one_page (EvView *view, gint page, + cairo_t *cr, GdkRectangle *page_area, GtkBorder *border, GdkRectangle *expose_area, @@ -222,10 +226,8 @@ static void draw_loading_text (EvView *view, GdkRectangle *expose_area); /*** Callbacks ***/ -static void find_changed_cb (EvDocument *document, - int page, - EvView *view); static void job_finished_cb (EvPixbufCache *pixbuf_cache, + GdkRegion *region, EvView *view); static void page_changed_cb (EvPageCache *page_cache, int new_page, @@ -291,15 +293,16 @@ static void ev_view_zoom_for_size_single_page (EvView *view, static GdkCursor* ev_view_create_invisible_cursor (void); static void ev_view_set_cursor (EvView *view, EvViewCursor new_cursor); +static void ev_view_handle_cursor_over_xy (EvView *view, + gint x, + gint y); -/*** Status messages ***/ -static void ev_view_set_status (EvView *view, - const char *message); -static void update_find_status_message (EvView *view, - gboolean this_page); -static void ev_view_set_find_status (EvView *view, - const char *message); /*** Find ***/ +static gint ev_view_find_get_n_results (EvView *view, + gint page); +static EvRectangle *ev_view_find_get_result (EvView *view, + gint page, + gint result); static void jump_to_find_result (EvView *view); static void jump_to_find_page (EvView *view, EvViewFindDirection direction, @@ -307,9 +310,11 @@ static void jump_to_find_page (EvView /*** Selection ***/ static void compute_selections (EvView *view, + EvSelectionStyle style, 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, @@ -325,7 +330,7 @@ static void ev_view_presentation_transition_start (EvView static void ev_view_presentation_transition_stop (EvView *ev_view); -G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET) +G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_LAYOUT) static void scroll_to_current_page (EvView *view, GtkOrientation orientation) @@ -481,7 +486,7 @@ view_update_range_and_current_page (EvView *view) } view->end_page = i; - } else if (found) { + } else if (found && view->current_page <= view->end_page) { break; } } @@ -553,13 +558,15 @@ set_scroll_adjustment (EvView *view, } static void -ev_view_set_scroll_adjustments (EvView *view, +ev_view_set_scroll_adjustments (GtkLayout *layout, GtkAdjustment *hadjustment, GtkAdjustment *vadjustment) { + EvView *view = EV_VIEW (layout); + set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment); set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment); - + on_adjustment_value_changed (NULL, view); } @@ -1144,21 +1151,26 @@ static void goto_fitr_dest (EvView *view, EvLinkDest *dest) { EvPoint doc_point; - double zoom; + gdouble zoom, left, top; + gboolean change_left, change_top; - zoom = zoom_for_size_best_fit (ev_link_dest_get_right (dest) - ev_link_dest_get_left (dest), - ev_link_dest_get_bottom (dest) - ev_link_dest_get_top (dest), + left = ev_link_dest_get_left (dest, &change_left); + top = ev_link_dest_get_top (dest, &change_top); + + zoom = zoom_for_size_best_fit (ev_link_dest_get_right (dest) - left, + ev_link_dest_get_bottom (dest) - top, ev_view_get_width (view), ev_view_get_height (view), 0, 0); ev_view_set_sizing_mode (view, EV_SIZING_FREE); ev_view_set_zoom (view, zoom, FALSE); - doc_point.x = ev_link_dest_get_left (dest); - doc_point.y = ev_link_dest_get_top (dest); + doc_point.x = change_left ? left : 0; + doc_point.y = change_top ? top : 0; view->current_page = ev_link_dest_get_page (dest); - view->pending_point = doc_point; + if (change_left || change_top) + view->pending_point = doc_point; view->pending_scroll = SCROLL_TO_PAGE_POSITION; gtk_widget_queue_resize (GTK_WIDGET (view)); @@ -1169,12 +1181,14 @@ goto_fitv_dest (EvView *view, EvLinkDest *dest) { EvPoint doc_point; int doc_width, doc_height, page; - double zoom; + double zoom, left; + gboolean change_left; page = ev_link_dest_get_page (dest); ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height); - doc_point.x = ev_link_dest_get_left (dest); + left = ev_link_dest_get_left (dest, &change_left); + doc_point.x = change_left ? left : 0; doc_point.y = 0; zoom = zoom_for_size_fit_height (doc_width - doc_point.x , doc_height, @@ -1185,7 +1199,8 @@ goto_fitv_dest (EvView *view, EvLinkDest *dest) ev_view_set_zoom (view, zoom, FALSE); view->current_page = page; - view->pending_point = doc_point; + if (change_left) + view->pending_point = doc_point; view->pending_scroll = SCROLL_TO_PAGE_POSITION; gtk_widget_queue_resize (GTK_WIDGET (view)); @@ -1196,15 +1211,18 @@ goto_fith_dest (EvView *view, EvLinkDest *dest) { EvPoint doc_point; int doc_width, doc_height, page; - double zoom; + gdouble zoom, top; + gboolean change_top; page = ev_link_dest_get_page (dest); ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height); + top = ev_link_dest_get_top (dest, &change_top); + doc_point.x = 0; - doc_point.y = ev_link_dest_get_top (dest); + doc_point.y = change_top ? top : 0; - zoom = zoom_for_size_fit_width (doc_width, ev_link_dest_get_top (dest), + zoom = zoom_for_size_fit_width (doc_width, top, ev_view_get_width (view), ev_view_get_height (view), 0); @@ -1212,7 +1230,8 @@ goto_fith_dest (EvView *view, EvLinkDest *dest) ev_view_set_zoom (view, zoom, FALSE); view->current_page = page; - view->pending_point = doc_point; + if (change_top) + view->pending_point = doc_point; view->pending_scroll = SCROLL_TO_PAGE_POSITION; gtk_widget_queue_resize (GTK_WIDGET (view)); @@ -1245,21 +1264,26 @@ goto_xyz_dest (EvView *view, EvLinkDest *dest) { EvPoint doc_point; gint page; - double zoom; + gdouble zoom, left, top; + gboolean change_zoom, change_left, change_top; - zoom = ev_link_dest_get_zoom (dest); + zoom = ev_link_dest_get_zoom (dest, &change_zoom); page = ev_link_dest_get_page (dest); - if (zoom > 1) { + if (change_zoom && zoom > 1) { ev_view_set_sizing_mode (view, EV_SIZING_FREE); ev_view_set_zoom (view, zoom, FALSE); } - doc_point.x = ev_link_dest_get_left (dest); - doc_point.y = ev_link_dest_get_top (dest); + left = ev_link_dest_get_left (dest, &change_left); + top = ev_link_dest_get_top (dest, &change_top); + + doc_point.x = change_left ? left : 0; + doc_point.y = change_top ? top : 0; view->current_page = page; - view->pending_point = doc_point; + if (change_left || change_top) + view->pending_point = doc_point; view->pending_scroll = SCROLL_TO_PAGE_POSITION; gtk_widget_queue_resize (GTK_WIDGET (view)); @@ -1312,7 +1336,7 @@ goto_dest (EvView *view, EvLinkDest *dest) view->current_page); } -void +static void ev_view_goto_dest (EvView *view, EvLinkDest *dest) { EvLinkDestType type; @@ -1346,13 +1370,15 @@ ev_view_handle_link (EvView *view, EvLink *link) action = ev_link_get_action (link); if (!action) return; - + type = ev_link_action_get_action_type (action); switch (type) { case EV_LINK_ACTION_TYPE_GOTO_DEST: { EvLinkDest *dest; + g_signal_emit (view, signals[SIGNAL_HANDLE_LINK], 0, link); + dest = ev_link_action_get_dest (action); ev_view_goto_dest (view, dest); } @@ -1366,8 +1392,8 @@ ev_view_handle_link (EvView *view, EvLink *link) } } -static gchar * -page_label_from_dest (EvView *view, EvLinkDest *dest) +gchar * +ev_view_page_label_from_dest (EvView *view, EvLinkDest *dest) { EvLinkDestType type; gchar *msg = NULL; @@ -1390,6 +1416,10 @@ page_label_from_dest (EvView *view, EvLinkDest *dest) } break; + case EV_LINK_DEST_TYPE_PAGE_LABEL: { + msg = g_strdup (ev_link_dest_get_page_label (dest)); + } + break; default: msg = ev_page_cache_get_page_label (view->page_cache, ev_link_dest_get_page (dest)); @@ -1439,10 +1469,12 @@ tip_from_link (EvView *view, EvLink *link) switch (type) { case EV_LINK_ACTION_TYPE_GOTO_DEST: - page_label = page_label_from_dest (view, - ev_link_action_get_dest (action)); - msg = g_strdup_printf (_("Go to page %s"), page_label); - g_free (page_label); + page_label = ev_view_page_label_from_dest (view, + ev_link_action_get_dest (action)); + if (page_label) { + msg = g_strdup_printf (_("Go to page %s"), page_label); + g_free (page_label); + } break; case EV_LINK_ACTION_TYPE_GOTO_REMOTE: if (title) { @@ -1452,7 +1484,6 @@ tip_from_link (EvView *view, EvLink *link) msg = g_strdup_printf (_("Go to file “%s”"), ev_link_action_get_filename (action)); } - break; case EV_LINK_ACTION_TYPE_EXTERNAL_URI: msg = g_strdup (ev_link_action_get_uri (action)); @@ -1474,12 +1505,29 @@ tip_from_link (EvView *view, EvLink *link) } static void -handle_link_over_xy (EvView *view, gint x, gint y) +ev_view_handle_cursor_over_xy (EvView *view, gint x, gint y) { - EvLink *link; + EvLink *link; + EvFormField *field; - link = ev_view_get_link_at_location (view, x, y); + if (view->cursor == EV_VIEW_CURSOR_HIDDEN) + return; + + if (view->drag_info.in_drag) { + if (view->cursor != EV_VIEW_CURSOR_DRAG) + 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); + +#if !GTK_CHECK_VERSION (2, 11, 7) if (view->link_tooltip == NULL) { view->link_tooltip = ev_tooltip_new (GTK_WIDGET (view)); } @@ -1488,8 +1536,12 @@ handle_link_over_xy (EvView *view, gint x, gint y) view->hovered_link = link; ev_tooltip_deactivate (EV_TOOLTIP (view->link_tooltip)); } +#endif if (link) { +#if GTK_CHECK_VERSION (2, 11, 7) + g_object_set (view, "has-tooltip", TRUE, NULL); +#else char *msg = tip_from_link (view, link); if (msg && g_utf8_validate (msg, -1, NULL)) { @@ -1500,17 +1552,28 @@ handle_link_over_xy (EvView *view, gint x, gint y) ev_tooltip_activate (tooltip); } g_free (msg); - +#endif ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK); + } else if ((field = ev_view_get_form_field_at_location (view, x, y))) { + if (field->is_read_only) { + if (view->cursor == EV_VIEW_CURSOR_LINK || + view->cursor == EV_VIEW_CURSOR_IBEAM || + view->cursor == EV_VIEW_CURSOR_DRAG) + ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); + } else if (EV_IS_FORM_FIELD_TEXT (field)) { + ev_view_set_cursor (view, EV_VIEW_CURSOR_IBEAM); + } else { + ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK); + } } else if (location_in_text (view, x + view->scroll_x, y + view->scroll_y)) { ev_view_set_cursor (view, EV_VIEW_CURSOR_IBEAM); } else { - ev_view_set_status (view, NULL); if (view->cursor == EV_VIEW_CURSOR_LINK || - view->cursor == EV_VIEW_CURSOR_IBEAM) + view->cursor == EV_VIEW_CURSOR_IBEAM || + view->cursor == EV_VIEW_CURSOR_DRAG || + view->cursor == EV_VIEW_CURSOR_AUTOSCROLL) ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); } - return; } /*** Images ***/ @@ -1547,6 +1610,510 @@ ev_view_get_image_at_location (EvView *view, return NULL; } +/*** Forms ***/ +static EvFormField * +ev_view_get_form_field_at_location (EvView *view, + gdouble x, + gdouble y) +{ + gint page = -1; + gint x_offset = 0, y_offset = 0; + gint x_new = 0, y_new = 0; + GList *forms_mapping; + + if (!EV_IS_DOCUMENT_FORMS (view->document)) + return NULL; + + x += view->scroll_x; + y += view->scroll_y; + + find_page_at_location (view, x, y, &page, &x_offset, &y_offset); + + if (page == -1) + return NULL; + + if (get_doc_point_from_offset (view, page, x_offset, + y_offset, &x_new, &y_new) == FALSE) + return NULL; + + forms_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache, page); + + if (forms_mapping) + return ev_form_field_mapping_find (forms_mapping, x_new, y_new); + else + return NULL; +} + +static GdkRegion * +ev_view_form_field_get_region (EvView *view, + EvFormField *field) +{ + EvRectangle field_area; + GdkRectangle view_area; + GList *forms_mapping; + + forms_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache, + field->page->index); + ev_form_field_mapping_get_area (forms_mapping, field, &field_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; + + return gdk_region_rectangle (&view_area); +} + +static gboolean +ev_view_forms_remove_widgets (EvView *view) +{ + ev_view_remove_all (view); + + return FALSE; +} + +static void +ev_view_form_field_destroy (GtkWidget *widget, + EvView *view) +{ + g_idle_add ((GSourceFunc)ev_view_forms_remove_widgets, view); +} + +static GtkWidget * +ev_view_form_field_button_create_widget (EvView *view, + EvFormField *field) +{ + EvFormFieldButton *field_button = EV_FORM_FIELD_BUTTON (field); + GdkRegion *field_region = NULL; + + switch (field_button->type) { + case EV_FORM_FIELD_BUTTON_PUSH: + return NULL; + case EV_FORM_FIELD_BUTTON_CHECK: + case EV_FORM_FIELD_BUTTON_RADIO: { + gboolean state; + GList *forms_mapping, *l; + + state = ev_document_forms_form_field_button_get_state (EV_DOCUMENT_FORMS (view->document), + field); + + /* FIXME: it actually depends on NoToggleToOff flags */ + if (field_button->type == EV_FORM_FIELD_BUTTON_RADIO && + state && field_button->state) + return NULL; + + field_region = ev_view_form_field_get_region (view, field); + + /* For radio buttons and checkbox buttons that are in a set + * 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->index); + for (l = forms_mapping; l; l = g_list_next (l)) { + EvFormField *button = ((EvFormFieldMapping *)(l->data))->field; + GdkRegion *button_region; + + if (button->id == field->id) + continue; + + /* FIXME: only buttons in the same group should be updated */ + if (!EV_IS_FORM_FIELD_BUTTON (button) || + EV_FORM_FIELD_BUTTON (button)->type != field_button->type || + EV_FORM_FIELD_BUTTON (button)->state != TRUE) + continue; + + button_region = ev_view_form_field_get_region (view, button); + gdk_region_union (field_region, button_region); + gdk_region_destroy (button_region); + } + + ev_document_forms_form_field_button_set_state (EV_DOCUMENT_FORMS (view->document), + field, !state); + field_button->state = !state; + } + break; + } + + ev_pixbuf_cache_reload_page (view->pixbuf_cache, + field_region, + field->page->index, + view->rotation, + view->scale); + gdk_region_destroy (field_region); + + return NULL; +} + +static void +ev_view_form_field_text_save (EvView *view, + GtkWidget *widget) +{ + EvFormField *field; + + if (!view->document) + return; + + field = g_object_get_data (G_OBJECT (widget), "form-field"); + + if (field->changed) { + EvFormFieldText *field_text = EV_FORM_FIELD_TEXT (field); + GdkRegion *field_region; + + field_region = ev_view_form_field_get_region (view, field); + + ev_document_forms_form_field_text_set_text (EV_DOCUMENT_FORMS (view->document), + field, field_text->text); + field->changed = FALSE; + ev_pixbuf_cache_reload_page (view->pixbuf_cache, + field_region, + field->page->index, + view->rotation, + view->scale); + gdk_region_destroy (field_region); + } +} + +static void +ev_view_form_field_text_changed (GtkWidget *widget, + EvFormField *field) +{ + EvFormFieldText *field_text = EV_FORM_FIELD_TEXT (field); + gchar *text = NULL; + + if (GTK_IS_ENTRY (widget)) { + text = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget))); + } else if (GTK_IS_TEXT_BUFFER (widget)) { + GtkTextIter start, end; + + gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (widget), &start, &end); + text = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (widget), + &start, &end, FALSE); + } + + if (!field_text->text || + (field_text->text && g_ascii_strcasecmp (field_text->text, text) != 0)) { + g_free (field_text->text); + field_text->text = text; + field->changed = TRUE; + } +} + +static GtkWidget * +ev_view_form_field_text_create_widget (EvView *view, + EvFormField *field) +{ + EvFormFieldText *field_text = EV_FORM_FIELD_TEXT (field); + GtkWidget *text = NULL; + gchar *txt; + + txt = ev_document_forms_form_field_text_get_text (EV_DOCUMENT_FORMS (view->document), + field); + + switch (field_text->type) { + case EV_FORM_FIELD_TEXT_FILE_SELECT: + /* TODO */ + case EV_FORM_FIELD_TEXT_NORMAL: + text = gtk_entry_new (); + gtk_entry_set_has_frame (GTK_ENTRY (text), FALSE); + gtk_entry_set_max_length (GTK_ENTRY (text), field_text->max_len); + gtk_entry_set_visibility (GTK_ENTRY (text), !field_text->is_password); + + if (txt) { + gtk_entry_set_text (GTK_ENTRY (text), txt); + g_free (txt); + } + + g_signal_connect (G_OBJECT (text), "changed", + G_CALLBACK (ev_view_form_field_text_changed), + field); + g_signal_connect_after (G_OBJECT (text), "activate", + G_CALLBACK (ev_view_form_field_destroy), + view); + break; + case EV_FORM_FIELD_TEXT_MULTILINE: { + GtkTextBuffer *buffer; + + text = gtk_text_view_new (); + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text)); + + if (txt) { + gtk_text_buffer_set_text (buffer, txt, -1); + g_free (txt); + } + + g_signal_connect (G_OBJECT (buffer), "changed", + G_CALLBACK (ev_view_form_field_text_changed), + field); + } + break; + } + + g_object_weak_ref (G_OBJECT (text), + (GWeakNotify)ev_view_form_field_text_save, + view); + + return text; +} + +static void +ev_view_form_field_choice_save (EvView *view, + GtkWidget *widget) +{ + EvFormField *field; + + if (!view->document) + return; + + field = g_object_get_data (G_OBJECT (widget), "form-field"); + + if (field->changed) { + GList *l; + EvFormFieldChoice *field_choice = EV_FORM_FIELD_CHOICE (field); + GdkRegion *field_region; + + field_region = ev_view_form_field_get_region (view, field); + + if (field_choice->is_editable) { + ev_document_forms_form_field_choice_set_text (EV_DOCUMENT_FORMS (view->document), + field, field_choice->text); + } else { + ev_document_forms_form_field_choice_unselect_all (EV_DOCUMENT_FORMS (view->document), field); + for (l = field_choice->selected_items; l; l = g_list_next (l)) { + ev_document_forms_form_field_choice_select_item (EV_DOCUMENT_FORMS (view->document), + field, + GPOINTER_TO_INT (l->data)); + } + } + field->changed = FALSE; + ev_pixbuf_cache_reload_page (view->pixbuf_cache, + field_region, + field->page->index, + view->rotation, + view->scale); + gdk_region_destroy (field_region); + } +} + +static void +ev_view_form_field_choice_changed (GtkWidget *widget, + EvFormField *field) +{ + EvFormFieldChoice *field_choice = EV_FORM_FIELD_CHOICE (field); + + if (GTK_IS_COMBO_BOX (widget)) { + gint item; + + item = gtk_combo_box_get_active (GTK_COMBO_BOX (widget)); + 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, + GINT_TO_POINTER (item)); + field->changed = TRUE; + } + + if (GTK_IS_COMBO_BOX_ENTRY (widget)) { + gchar *text; + + text = gtk_combo_box_get_active_text (GTK_COMBO_BOX (widget)); + if (!field_choice->text || + (field_choice->text && g_ascii_strcasecmp (field_choice->text, text) != 0)) { + g_free (field_choice->text); + field_choice->text = text; + field->changed = TRUE; + } + } + } else if (GTK_IS_TREE_SELECTION (widget)) { + GtkTreeSelection *selection = GTK_TREE_SELECTION (widget); + GtkTreeModel *model; + GList *items, *l; + + items = gtk_tree_selection_get_selected_rows (selection, &model); + g_list_free (field_choice->selected_items); + field_choice->selected_items = NULL; + + for (l = items; l && l->data; l = g_list_next (l)) { + GtkTreeIter iter; + GtkTreePath *path = (GtkTreePath *)l->data; + gint item; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, 1, &item, -1); + + field_choice->selected_items = g_list_prepend (field_choice->selected_items, + GINT_TO_POINTER (item)); + + gtk_tree_path_free (path); + } + + g_list_free (items); + + field->changed = TRUE; + } +} + +static GtkWidget * +ev_view_form_field_choice_create_widget (EvView *view, + EvFormField *field) +{ + EvFormFieldChoice *field_choice = EV_FORM_FIELD_CHOICE (field); + GtkWidget *choice; + GtkTreeModel *model; + gint n_items, i; + gint selected_item = 0; + + n_items = ev_document_forms_form_field_choice_get_n_items (EV_DOCUMENT_FORMS (view->document), + field); + model = GTK_TREE_MODEL (gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT)); + for (i = 0; i < n_items; i++) { + GtkTreeIter iter; + gchar *item; + + item = ev_document_forms_form_field_choice_get_item (EV_DOCUMENT_FORMS (view->document), + field, i); + if (ev_document_forms_form_field_choice_is_item_selected ( + EV_DOCUMENT_FORMS (view->document), field, i)) { + selected_item = i; + /* FIXME: we need a get_selected_items function in poppler */ + field_choice->selected_items = g_list_prepend (field_choice->selected_items, + GINT_TO_POINTER (i)); + } + + if (item) { + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + 0, item, + 1, i, + -1); + g_free (item); + } + } + + if (field_choice->type == EV_FORM_FIELD_CHOICE_LIST) { + GtkCellRenderer *renderer; + GtkWidget *tree_view; + GtkTreeSelection *selection; + + tree_view = gtk_tree_view_new_with_model (model); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)); + if (field_choice->multi_select) { + gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); + } + + /* TODO: set selected items */ + + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view), + 0, + "choix", renderer, + "text", 0, + NULL); + + choice = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (choice), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (choice), tree_view); + gtk_widget_show (tree_view); + + g_signal_connect (G_OBJECT (selection), "changed", + G_CALLBACK (ev_view_form_field_choice_changed), + field); + g_signal_connect_after (G_OBJECT (selection), "changed", + G_CALLBACK (ev_view_form_field_destroy), + view); + } else if (field_choice->is_editable) { /* ComboBoxEntry */ + gchar *text; + + choice = gtk_combo_box_entry_new_with_model (model, 0); + text = ev_document_forms_form_field_choice_get_text (EV_DOCUMENT_FORMS (view->document), field); + if (text) { + gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (choice))), text); + g_free (text); + } + + g_signal_connect (G_OBJECT (choice), "changed", + G_CALLBACK (ev_view_form_field_choice_changed), + field); + g_signal_connect_after (G_OBJECT (GTK_BIN (choice)->child), "activate", + G_CALLBACK (ev_view_form_field_destroy), + view); + } else { /* ComboBoxText */ + GtkCellRenderer *renderer; + + choice = gtk_combo_box_new_with_model (model); + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (choice), + renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (choice), + renderer, + "text", 0, + NULL); + gtk_combo_box_set_active (GTK_COMBO_BOX (choice), selected_item); + gtk_combo_box_popup (GTK_COMBO_BOX (choice)); + + g_signal_connect (G_OBJECT (choice), "changed", + G_CALLBACK (ev_view_form_field_choice_changed), + field); + g_signal_connect_after (G_OBJECT (choice), "changed", + G_CALLBACK (ev_view_form_field_destroy), + view); + } + + g_object_unref (model); + + g_object_weak_ref (G_OBJECT (choice), + (GWeakNotify)ev_view_form_field_choice_save, + view); + + return choice; +} + +static void +ev_view_handle_form_field (EvView *view, + EvFormField *field, + gdouble x, + gdouble y) +{ + GtkWidget *field_widget = NULL; + GList *form_field_mapping; + EvRectangle field_area; + GdkRectangle view_area; + + if (field->is_read_only) + return; + + if (EV_IS_FORM_FIELD_BUTTON (field)) { + field_widget = ev_view_form_field_button_create_widget (view, field); + } else if (EV_IS_FORM_FIELD_TEXT (field)) { + field_widget = ev_view_form_field_text_create_widget (view, field); + } else if (EV_IS_FORM_FIELD_CHOICE (field)) { + field_widget = ev_view_form_field_choice_create_widget (view, field); + } else if (EV_IS_FORM_FIELD_SIGNATURE (field)) { + /* TODO */ + } + + /* Form field doesn't require a widget */ + if (!field_widget) + return; + + g_object_set_data_full (G_OBJECT (field_widget), "form-field", + g_object_ref (field), + (GDestroyNotify)g_object_unref); + + 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->index, &field_area, &view_area); + view_area.x -= view->scroll_x; + view_area.y -= view->scroll_y; + + gtk_layout_put (GTK_LAYOUT (view), field_widget, view_area.x, view_area.y); + gtk_widget_show (field_widget); + gtk_widget_grab_focus (field_widget); +} + /*** GtkWidget implementation ***/ static void @@ -1674,7 +2241,7 @@ ev_view_size_request (GtkWidget *widget, GtkRequisition *requisition) { EvView *view = EV_VIEW (widget); - + if (view->document == NULL) { requisition->width = 1; requisition->height = 1; @@ -1702,6 +2269,7 @@ ev_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { EvView *view = EV_VIEW (widget); + GList *children, *l; GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation); @@ -1712,7 +2280,7 @@ ev_view_size_allocate (GtkWidget *widget, ev_view_size_request (widget, &widget->requisition); } - + view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL); view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL); @@ -1721,47 +2289,71 @@ ev_view_size_allocate (GtkWidget *widget, view->pending_scroll = SCROLL_TO_KEEP_POSITION; view->pending_resize = FALSE; + + children = gtk_container_get_children (GTK_CONTAINER (widget)); + for (l = children; l && l->data; l = g_list_next (l)) { + EvFormField *field; + EvRectangle field_area; + GdkRectangle view_area; + GList *form_field_mapping; + GtkAllocation child_allocation; + GtkRequisition child_requisition; + GtkWidget *child = (GtkWidget *)l->data; + + field = g_object_get_data (G_OBJECT (child), "form-field"); + if (!field) + continue; + + 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->index, &field_area, &view_area); + view_area.x -= view->scroll_x; + view_area.y -= view->scroll_y; + + gtk_widget_size_request (child, &child_requisition); + if (child_requisition.width != view_area.width || + child_requisition.height != view_area.height) + gtk_widget_set_size_request (child, view_area.width, view_area.height); + + gtk_container_child_get (GTK_CONTAINER (widget), + child, + "x", &child_allocation.x, + "y", &child_allocation.y, + NULL); + if (child_allocation.x != view_area.x || + child_allocation.y != view_area.y) { + gtk_layout_move (GTK_LAYOUT (widget), child, view_area.x, view_area.y); + } + } + g_list_free (children); } static void ev_view_realize (GtkWidget *widget) { EvView *view = EV_VIEW (widget); - GdkWindowAttr attributes; - - GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); - attributes.window_type = GDK_WINDOW_CHILD; - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.visual = gtk_widget_get_visual (widget); - attributes.colormap = gtk_widget_get_colormap (widget); + if (GTK_WIDGET_CLASS (ev_view_parent_class)->realize) + (* GTK_WIDGET_CLASS (ev_view_parent_class)->realize) (widget); - attributes.x = widget->allocation.x; - attributes.y = widget->allocation.y; - attributes.width = widget->allocation.width; - attributes.height = widget->allocation.height; - attributes.event_mask = GDK_EXPOSURE_MASK | + gdk_window_set_events (view->layout.bin_window, + (gdk_window_get_events (view->layout.bin_window) | + GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | GDK_KEY_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK; - - widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), - &attributes, - GDK_WA_X | GDK_WA_Y | - GDK_WA_COLORMAP | - GDK_WA_VISUAL); - gdk_window_set_user_data (widget->window, widget); - widget->style = gtk_style_attach (widget->style, widget->window); + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK)); if (view->presentation) - gdk_window_set_background (widget->window, &widget->style->black); + gdk_window_set_background (view->layout.bin_window, &widget->style->black); else - gdk_window_set_background (widget->window, &widget->style->mid [GTK_STATE_NORMAL]); + gdk_window_set_background (view->layout.bin_window, &widget->style->mid [GTK_STATE_NORMAL]); } static gboolean @@ -1790,15 +2382,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; @@ -1863,7 +2456,7 @@ draw_end_presentation_page (EvView *view, pango_layout_set_font_description (layout, font_desc); gtk_paint_layout (GTK_WIDGET (view)->style, - GTK_WIDGET (view)->window, + view->layout.bin_window, GTK_WIDGET_STATE (view), FALSE, page_area, @@ -1881,8 +2474,27 @@ static gboolean ev_view_expose_event (GtkWidget *widget, GdkEventExpose *event) { - EvView *view = EV_VIEW (widget); - int i; + EvView *view = EV_VIEW (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) { @@ -1907,7 +2519,7 @@ ev_view_expose_event (GtkWidget *widget, area.width = widget->allocation.width; area.height = widget->allocation.height; - + draw_loading_text (view, &area, &(event->area)); @@ -1916,6 +2528,8 @@ ev_view_expose_event (GtkWidget *widget, if (view->document == NULL) return FALSE; + cr = gdk_cairo_create (view->layout.bin_window); + for (i = view->start_page; i <= view->end_page; i++) { GdkRectangle page_area; GtkBorder border; @@ -1927,12 +2541,17 @@ ev_view_expose_event (GtkWidget *widget, page_area.x -= view->scroll_x; page_area.y -= view->scroll_y; - draw_one_page (view, i, &page_area, &border, &(event->area), &page_ready); + draw_one_page (view, i, cr, &page_area, &border, &(event->area), &page_ready); - if (page_ready && EV_IS_DOCUMENT_FIND (view->document)) + if (page_ready && view->find_pages && view->highlight_find_results) highlight_find_results (view, i); } + cairo_destroy (cr); + + if (GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event) + (* GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event) (widget, event); + return FALSE; } @@ -1970,11 +2589,94 @@ ev_view_popup_menu (GtkWidget *widget) return ev_view_do_popup_menu (EV_VIEW (widget), x, y); } -static gboolean -ev_view_button_press_event (GtkWidget *widget, - GdkEventButton *event) -{ - EvView *view = EV_VIEW (widget); +#if GTK_CHECK_VERSION (2, 11, 7) +static void +get_link_area (EvView *view, + gint x, + gint y, + EvLink *link, + GdkRectangle *area) +{ + EvRectangle ev_rect; + GList *link_mapping; + gint page; + gint x_offset = 0, y_offset = 0; + + x += view->scroll_x; + y += view->scroll_y; + + find_page_at_location (view, x, y, &page, &x_offset, &y_offset); + + link_mapping = ev_pixbuf_cache_get_link_mapping (view->pixbuf_cache, page); + ev_link_mapping_get_area (link_mapping, link, &ev_rect); + + doc_rect_to_view_rect (view, page, &ev_rect, area); + area->y -= view->scroll_y ; +} + +static gboolean +ev_view_query_tooltip (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_tip, + GtkTooltip *tooltip) +{ + EvView *view = EV_VIEW (widget); + EvLink *link; + gchar *text; + + link = ev_view_get_link_at_location (view, x, y); + if (!link) + return FALSE; + + text = tip_from_link (view, link); + if (text && g_utf8_validate (text, -1, NULL)) { + GdkRectangle link_area; + + get_link_area (view, x, y, link, &link_area); + gtk_tooltip_set_text (tooltip, text); + gtk_tooltip_set_tip_area (tooltip, &link_area); + } + g_free (text); + + return TRUE; +} +#endif /* GTK_CHECK_VERSION (2, 11, 7) */ + +static void +start_selection_for_event (EvView *view, + GdkEventButton *event) +{ + EvSelectionStyle style; + + clear_selection (view); + + view->selection_info.start.x = event->x + view->scroll_x; + view->selection_info.start.y = event->y + view->scroll_y; + + switch (event->type) { + case GDK_2BUTTON_PRESS: + style = EV_SELECTION_STYLE_WORD; + break; + case GDK_3BUTTON_PRESS: + style = EV_SELECTION_STYLE_LINE; + break; + default: + style = EV_SELECTION_STYLE_GLYPH; + break; + } + + view->selection_info.style = style; +} + +static gboolean +ev_view_button_press_event (GtkWidget *widget, + GdkEventButton *event) +{ + EvView *view = EV_VIEW (widget); + + if (!view->document) + return FALSE; if (!GTK_WIDGET_HAS_FOCUS (widget)) { gtk_widget_grab_focus (widget); @@ -1982,22 +2684,32 @@ 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: { EvImage *image; + EvFormField *field; - if (view->selection_info.selections) { - if (location_in_selected_text (view, + if (EV_IS_SELECTION (view->document) && view->selection_info.selections) { + if (event->type == GDK_3BUTTON_PRESS) { + start_selection_for_event (view, event); + } else if (location_in_selected_text (view, event->x + view->scroll_x, event->y + view->scroll_y)) { view->selection_info.in_drag = TRUE; } else { - clear_selection (view); + start_selection_for_event (view, event); } - + gtk_widget_queue_draw (widget); - } else if ((image = ev_view_get_image_at_location (view, event->x, event->y))) { + } else if ((field = ev_view_get_form_field_at_location (view, event->x, event->y))) { + ev_view_remove_all (view); + ev_view_handle_form_field (view, field, event->x, event->y); + } else if (!location_in_text (view, event->x + view->scroll_x, event->y + view->scroll_y) && + (image = ev_view_get_image_at_location (view, event->x, event->y))) { if (view->image_dnd_info.image) g_object_unref (view->image_dnd_info.image); view->image_dnd_info.image = g_object_ref (image); @@ -2005,10 +2717,12 @@ ev_view_button_press_event (GtkWidget *widget, view->image_dnd_info.start.x = event->x + view->scroll_x; view->image_dnd_info.start.y = event->y + view->scroll_y; + } else { + ev_view_remove_all (view); + + if (EV_IS_SELECTION (view->document)) + start_selection_for_event (view, event); } - - view->selection_info.start.x = event->x + view->scroll_x; - view->selection_info.start.y = event->y + view->scroll_y; } return TRUE; case 2: @@ -2023,12 +2737,26 @@ 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); } return FALSE; } +static void +ev_view_remove_all (EvView *view) +{ + GList *children, *child; + + children = gtk_container_get_children (GTK_CONTAINER (view)); + for (child = children; child && child->data; child = g_list_next (child)) { + gtk_container_remove (GTK_CONTAINER (view), + GTK_WIDGET (child->data)); + } + g_list_free (children); +} + /*** Drag and Drop ***/ static void ev_view_drag_data_get (GtkWidget *widget, @@ -2041,16 +2769,14 @@ ev_view_drag_data_get (GtkWidget *widget, switch (info) { case TARGET_DND_TEXT: - if (view->selection_info.selections && - ev_document_can_get_text (view->document)) { + if (EV_IS_SELECTION (view->document) && + view->selection_info.selections) { gchar *text; text = get_selected_text (view); - gtk_selection_data_set_text (selection_data, text, strlen (text)); - g_free (text); } break; @@ -2058,16 +2784,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; @@ -2131,7 +2869,10 @@ ev_view_drag_data_received (GtkWidget *widget, static gboolean selection_update_idle_cb (EvView *view) { - compute_selections (view, &view->selection_info.start, &view->motion); + compute_selections (view, + view->selection_info.style, + &view->selection_info.start, + &view->motion); view->selection_update_id = 0; return FALSE; } @@ -2173,6 +2914,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) @@ -2182,14 +2983,20 @@ ev_view_motion_notify_event (GtkWidget *widget, if (!view->document) return FALSE; + - if (event->is_hint || event->window != widget->window) { + if (event->is_hint || event->window != view->layout.bin_window) { gtk_widget_get_pointer (widget, &x, &y); } else { x = event->x; 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, @@ -2259,6 +3066,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, @@ -2266,12 +3074,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; @@ -2293,7 +3114,7 @@ ev_view_motion_notify_event (GtkWidget *widget, return TRUE; } } else if (view->pressed_button <= 0) { - handle_link_over_xy (view, x, y); + ev_view_handle_cursor_over_xy (view, x, y); return TRUE; } @@ -2305,21 +3126,34 @@ ev_view_button_release_event (GtkWidget *widget, GdkEventButton *event) { EvView *view = EV_VIEW (widget); - EvLink *link; + EvLink *link = NULL; - if (view->pressed_button == 2) { - ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); + view->image_dnd_info.in_drag = FALSE; + + 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 == 1) { + if (view->document && !view->drag_info.in_drag && view->pressed_button != 3) { link = ev_view_get_link_at_location (view, event->x, event->y); - } else { - link = NULL; + } + + 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; - view->drag_info.in_drag = FALSE; - view->image_dnd_info.in_drag = FALSE; if (view->selection_scroll_id) { g_source_remove (view->selection_scroll_id); @@ -2330,7 +3164,16 @@ ev_view_button_release_event (GtkWidget *widget, view->selection_update_id = 0; } + if (!view->selection_info.in_selection && + view->selection_info.style != EV_SELECTION_STYLE_GLYPH) { + compute_selections (view, + view->selection_info.style, + &(view->selection_info.start), + &(view->selection_info.start)); + } + if (view->selection_info.selections) { + clear_link_selected (view); ev_view_update_primary_selection (view); if (view->selection_info.in_drag) { @@ -2340,7 +3183,23 @@ ev_view_button_release_event (GtkWidget *widget, view->selection_info.in_drag = FALSE; } else if (link) { - ev_view_handle_link (view, link); + if (event->button == 2) { + EvLinkAction *action; + EvLinkActionType type; + + action = ev_link_get_action (link); + if (!action) + return FALSE; + + type = ev_link_action_get_action_type (action); + if (type == EV_LINK_ACTION_TYPE_GOTO_DEST) { + g_signal_emit (view, + signals[SIGNAL_EXTERNAL_LINK], + 0, action); + } + } else { + ev_view_handle_link (view, link); + } } else if (view->presentation) { switch (event->button) { case 1: @@ -2423,6 +3282,8 @@ ev_view_goto_window_key_press_event (GtkWidget *widget, case GDK_Return: case GDK_KP_Enter: case GDK_ISO_Enter: + case GDK_BackSpace: + case GDK_Delete: return FALSE; default: if (!key_is_numeric (event->keyval)) @@ -2513,7 +3374,7 @@ ev_view_goto_window_create (EvView *view) g_signal_connect (view->goto_entry, "activate", G_CALLBACK (ev_view_goto_entry_activate), view); - gtk_box_pack_start_defaults (GTK_BOX (hbox), view->goto_entry); + gtk_box_pack_start (GTK_BOX (hbox), view->goto_entry, TRUE, TRUE, 0); gtk_widget_show (view->goto_entry); gtk_widget_realize (view->goto_entry); } @@ -2560,6 +3421,9 @@ ev_view_key_press_event (GtkWidget *widget, EvView *view = EV_VIEW (widget); EvPresentationState current; + if (!view->document) + return FALSE; + if (!view->presentation || view->presentation_state == EV_PRESENTATION_END) return gtk_bindings_activate_event (GTK_OBJECT (widget), event); @@ -2570,6 +3434,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; @@ -2608,11 +3474,11 @@ ev_view_key_press_event (GtkWidget *widget, switch (view->presentation_state) { case EV_PRESENTATION_NORMAL: case EV_PRESENTATION_BLACK: - gdk_window_set_background (widget->window, + gdk_window_set_background (view->layout.bin_window, &widget->style->black); break; case EV_PRESENTATION_WHITE: - gdk_window_set_background (widget->window, + gdk_window_set_background (view->layout.bin_window, &widget->style->white); break; default: @@ -2652,18 +3518,17 @@ static gboolean ev_view_leave_notify_event (GtkWidget *widget, GdkEventCrossing *event) { EvView *view = EV_VIEW (widget); - - ev_view_set_status (view, NULL); - if (view->cursor == EV_VIEW_CURSOR_LINK || - view->cursor == EV_VIEW_CURSOR_IBEAM) + if (view->cursor != EV_VIEW_CURSOR_NORMAL) ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); +#if !GTK_CHECK_VERSION (2, 11, 7) if (view->link_tooltip) { view->hovered_link = NULL; ev_tooltip_deactivate (EV_TOOLTIP (view->link_tooltip)); } - +#endif + return FALSE; } @@ -2672,7 +3537,7 @@ ev_view_enter_notify_event (GtkWidget *widget, GdkEventCrossing *event) { EvView *view = EV_VIEW (widget); - handle_link_over_xy (view, event->x, event->y); + ev_view_handle_cursor_over_xy (view, event->x, event->y); return FALSE; } @@ -2739,17 +3604,12 @@ draw_rubberband (GtkWidget *widget, GdkWindow *window, static void highlight_find_results (EvView *view, int page) { - EvDocumentFind *find; - int i, results = 0; + gint i, n_results = 0; - g_return_if_fail (EV_IS_DOCUMENT_FIND (view->document)); + n_results = ev_view_find_get_n_results (view, page); - find = EV_DOCUMENT_FIND (view->document); - - results = ev_document_find_get_n_results (find, page); - - for (i = 0; i < results; i++) { - EvRectangle rectangle; + for (i = 0; i < n_results; i++) { + EvRectangle *rectangle; GdkRectangle view_rectangle; guchar alpha; @@ -2759,9 +3619,9 @@ highlight_find_results (EvView *view, int page) alpha = 0x20; } - ev_document_find_get_result (find, page, i, &rectangle); - doc_rect_to_view_rect (view, page, &rectangle, &view_rectangle); - draw_rubberband (GTK_WIDGET (view), GTK_WIDGET(view)->window, + rectangle = ev_view_find_get_result (view, page, i); + doc_rect_to_view_rect (view, page, rectangle, &view_rectangle); + draw_rubberband (GTK_WIDGET (view), view->layout.bin_window, &view_rectangle, alpha); } } @@ -2773,25 +3633,17 @@ 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; + const gchar *loading_text = _("Loading..."); PangoLayout *layout; PangoFontDescription *font_desc; PangoRectangle logical_rect; - gdouble real_scale; gint target_width; - - loading_text = _("Loading..."); + gdouble real_scale; ev_document_fc_mutex_lock (); - + layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), loading_text); font_desc = pango_font_description_new (); @@ -2800,8 +3652,7 @@ draw_loading_text (EvView *view, pango_font_description_set_size (font_desc, 10 * PANGO_SCALE); pango_layout_set_font_description (layout, font_desc); pango_layout_get_pixel_extents (layout, NULL, &logical_rect); - - /* Make sure we fit the middle of the page */ + target_width = MAX (page_area->width / 2, 1); real_scale = ((double)target_width / (double) logical_rect.width) * (PANGO_SCALE * 10); pango_font_description_set_size (font_desc, (int)real_scale); @@ -2825,43 +3676,35 @@ draw_loading_text (EvView *view, ev_document_fc_mutex_unlock (); } - width = cairo_image_surface_get_width (view->loading_text) / 2; + width = (page_area->width - cairo_image_surface_get_width (view->loading_text)) / 2; height = (page_area->height - cairo_image_surface_get_height (view->loading_text)) / 2; - cr = gdk_cairo_create (GTK_WIDGET (view)->window); - cairo_set_source_surface (cr, view->loading_text, - page_area->x + width, - page_area->y + height); + cr = gdk_cairo_create (view->layout.bin_window); + cairo_translate (cr, + page_area->x + width, + page_area->y + height); + cairo_set_source_surface (cr, view->loading_text, 0, 0); cairo_paint (cr); cairo_destroy (cr); } static void -draw_one_page (EvView *view, - gint page, - GdkRectangle *page_area, - GtkBorder *border, - GdkRectangle *expose_area, - gboolean *page_ready) +draw_one_page (EvView *view, + gint page, + cairo_t *cr, + GdkRectangle *page_area, + GtkBorder *border, + GdkRectangle *expose_area, + gboolean *page_ready) { - gint width, height; - GdkPixbuf *current_pixbuf; GdkRectangle overlap; GdkRectangle real_page_area; - EvViewSelection *selection; - gint current_page; g_assert (view->document); if (! gdk_rectangle_intersect (page_area, expose_area, &overlap)) return; - - current_page = ev_page_cache_get_current_page (view->page_cache); - selection = find_selection_for_page (view, page); - ev_page_cache_get_size (view->page_cache, - page, view->rotation, - view->scale, - &width, &height); + /* Render the document itself */ real_page_area = *page_area; @@ -2872,80 +3715,96 @@ draw_one_page (EvView *view, *page_ready = TRUE; if (!view->presentation) { - ev_document_misc_paint_one_page (GTK_WIDGET(view)->window, + gint current_page; + + current_page = ev_page_cache_get_current_page (view->page_cache); + ev_document_misc_paint_one_page (view->layout.bin_window, GTK_WIDGET (view), page_area, border, page == current_page); } if (gdk_rectangle_intersect (&real_page_area, expose_area, &overlap)) { - GdkPixbuf *selection_pixbuf = NULL; - GdkPixbuf *scaled_image; - GdkPixbuf *scaled_selection; + gint width, height; + gint page_width, page_height; + cairo_surface_t *page_surface = NULL; + gint selection_width, selection_height; + cairo_surface_t *selection_surface = NULL; + + page_surface = ev_pixbuf_cache_get_surface (view->pixbuf_cache, page); + + if (!page_surface) { + if (!view->presentation) { + draw_loading_text (view, + &real_page_area, + expose_area); + } + + *page_ready = FALSE; + + return; + } - current_pixbuf = ev_pixbuf_cache_get_pixbuf (view->pixbuf_cache, page); + ev_page_cache_get_size (view->page_cache, + page, view->rotation, + view->scale, + &width, &height); + + page_width = cairo_image_surface_get_width (page_surface); + page_height = cairo_image_surface_get_height (page_surface); + + 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); + cairo_scale (cr, + (gdouble)width / page_width, + (gdouble)height / page_height); + } + + cairo_surface_set_device_offset (page_surface, + overlap.x - real_page_area.x, + overlap.y - real_page_area.y); + cairo_set_source_surface (cr, page_surface, 0, 0); + cairo_paint (cr); + cairo_restore (cr); + /* Get the selection pixbuf iff we have something to draw */ - if (current_pixbuf && view->selection_mode == EV_VIEW_SELECTION_TEXT && selection) - selection_pixbuf = ev_pixbuf_cache_get_selection_pixbuf (view->pixbuf_cache, - page, - view->scale, - NULL); - - if (current_pixbuf == NULL) - scaled_image = NULL; - else if (width == gdk_pixbuf_get_width (current_pixbuf) && - height == gdk_pixbuf_get_height (current_pixbuf)) - scaled_image = g_object_ref (current_pixbuf); - else - /* FIXME: We don't want to scale the whole area, just the right - * area of it */ - scaled_image = gdk_pixbuf_scale_simple (current_pixbuf, - width, height, - GDK_INTERP_NEAREST); - - if (selection_pixbuf == NULL) - scaled_selection = NULL; - else if (width == gdk_pixbuf_get_width (selection_pixbuf) && - height == gdk_pixbuf_get_height (selection_pixbuf)) - scaled_selection = g_object_ref (selection_pixbuf); - else - /* FIXME: We don't want to scale the whole area, just the right - * area of it */ - scaled_selection = gdk_pixbuf_scale_simple (selection_pixbuf, - width, height, - GDK_INTERP_NEAREST); - - if (scaled_image) { - gdk_draw_pixbuf (GTK_WIDGET(view)->window, - GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL], - scaled_image, - overlap.x - real_page_area.x, - overlap.y - real_page_area.y, - overlap.x, overlap.y, - overlap.width, overlap.height, - GDK_RGB_DITHER_NORMAL, - 0, 0); - g_object_unref (scaled_image); - } else { - draw_loading_text (view, - &real_page_area, - expose_area); - *page_ready = FALSE; + if (find_selection_for_page (view, page) && + view->selection_mode == EV_VIEW_SELECTION_TEXT) { + selection_surface = + ev_pixbuf_cache_get_selection_surface (view->pixbuf_cache, + page, + view->scale, + NULL); } - if (scaled_selection) { - gdk_draw_pixbuf (GTK_WIDGET(view)->window, - GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL], - scaled_selection, - overlap.x - real_page_area.x, - overlap.y - real_page_area.y, - overlap.x, overlap.y, - overlap.width, overlap.height, - GDK_RGB_DITHER_NORMAL, - 0, 0); - g_object_unref (scaled_selection); + if (!selection_surface) { + return; } + + selection_width = cairo_image_surface_get_width (selection_surface); + selection_height = cairo_image_surface_get_height (selection_surface); + + cairo_save (cr); + cairo_translate (cr, overlap.x, overlap.y); + + if (width != selection_width || height != selection_height) { + cairo_pattern_set_filter (cairo_get_source (cr), + CAIRO_FILTER_FAST); + cairo_scale (cr, + (gdouble)width / selection_width, + (gdouble)height / selection_height); + } + + cairo_surface_set_device_offset (selection_surface, + overlap.x - real_page_area.x, + overlap.y - real_page_area.y); + cairo_set_source_surface (cr, selection_surface, 0, 0); + cairo_paint (cr); + cairo_restore (cr); } } @@ -2956,10 +3815,8 @@ ev_view_finalize (GObject *object) { EvView *view = EV_VIEW (object); - g_free (view->status); - 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); @@ -2983,11 +3840,12 @@ ev_view_destroy (GtkObject *object) view->pixbuf_cache = NULL; } +#if !GTK_CHECK_VERSION (2, 11, 7) if (view->link_tooltip) { gtk_widget_destroy (view->link_tooltip); view->link_tooltip = NULL; } - +#endif if (view->goto_window) { gtk_widget_destroy (view->goto_window); view->goto_window = NULL; @@ -3009,9 +3867,24 @@ 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 (view, NULL, NULL); + ev_view_set_scroll_adjustments (GTK_LAYOUT (view), NULL, NULL); GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object); } @@ -3091,12 +3964,6 @@ ev_view_get_property (GObject *object, EvView *view = EV_VIEW (object); switch (prop_id) { - case PROP_STATUS: - g_value_set_string (value, view->status); - break; - case PROP_FIND_STATUS: - g_value_set_string (value, view->status); - break; case PROP_CONTINUOUS: g_value_set_boolean (value, view->continuous); break; @@ -3133,6 +4000,7 @@ ev_view_class_init (EvViewClass *class) GObjectClass *object_class = G_OBJECT_CLASS (class); GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + GtkLayoutClass *layout_class = GTK_LAYOUT_CLASS (class); GtkBindingSet *binding_set; object_class->finalize = ev_view_finalize; @@ -3158,22 +4026,16 @@ ev_view_class_init (EvViewClass *class) widget_class->drag_motion = ev_view_drag_motion; widget_class->drag_data_received = ev_view_drag_data_received; widget_class->popup_menu = ev_view_popup_menu; +#if GTK_CHECK_VERSION (2, 11, 7) + widget_class->query_tooltip = ev_view_query_tooltip; +#endif + gtk_object_class->destroy = ev_view_destroy; - class->set_scroll_adjustments = ev_view_set_scroll_adjustments; + layout_class->set_scroll_adjustments = ev_view_set_scroll_adjustments; + class->binding_activated = ev_view_scroll; - widget_class->set_scroll_adjustments_signal = - g_signal_new ("set-scroll-adjustments", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments), - NULL, NULL, - ev_marshal_VOID__OBJECT_OBJECT, - G_TYPE_NONE, 2, - GTK_TYPE_ADJUSTMENT, - GTK_TYPE_ADJUSTMENT); - signals[SIGNAL_BINDING_ACTIVATED] = g_signal_new ("binding_activated", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, @@ -3191,6 +4053,14 @@ ev_view_class_init (EvViewClass *class) NULL, NULL, ev_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); + signals[SIGNAL_HANDLE_LINK] = g_signal_new ("handle-link", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EvViewClass, handle_link), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); signals[SIGNAL_EXTERNAL_LINK] = g_signal_new ("external-link", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, @@ -3208,21 +4078,6 @@ ev_view_class_init (EvViewClass *class) G_TYPE_NONE, 1, G_TYPE_OBJECT); - g_object_class_install_property (object_class, - PROP_STATUS, - g_param_spec_string ("status", - "Status Message", - "The status message", - NULL, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, - PROP_FIND_STATUS, - g_param_spec_string ("find-status", - "Find Status Message", - "The find status message", - NULL, - G_PARAM_READABLE)); g_object_class_install_property (object_class, PROP_CONTINUOUS, @@ -3299,6 +4154,15 @@ ev_view_class_init (EvViewClass *class) add_scroll_binding_keypad (binding_set, GDK_Down, 0, EV_SCROLL_STEP_FORWARD, FALSE); add_scroll_binding_keypad (binding_set, GDK_Up, GDK_MOD1_MASK, EV_SCROLL_STEP_DOWN, FALSE); add_scroll_binding_keypad (binding_set, GDK_Down, GDK_MOD1_MASK, EV_SCROLL_STEP_UP, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_H, 0, "binding_activated", 2, EV_TYPE_SCROLL_TYPE, + EV_SCROLL_STEP_BACKWARD, G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_J, 0, "binding_activated", 2, EV_TYPE_SCROLL_TYPE, + EV_SCROLL_STEP_FORWARD, G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_K, 0, "binding_activated", 2, EV_TYPE_SCROLL_TYPE, + EV_SCROLL_STEP_BACKWARD, G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_L, 0, "binding_activated", 2, EV_TYPE_SCROLL_TYPE, + EV_SCROLL_STEP_FORWARD, G_TYPE_BOOLEAN, TRUE); + } static void @@ -3312,6 +4176,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; @@ -3324,6 +4189,10 @@ ev_view_init (EvView *view) view->sizing_mode = EV_SIZING_FIT_WIDTH; view->pending_scroll = SCROLL_TO_KEEP_POSITION; view->jump_to_find_result = TRUE; + view->highlight_find_results = FALSE; + + gtk_layout_set_hadjustment (GTK_LAYOUT (view), NULL); + gtk_layout_set_vadjustment (GTK_LAYOUT (view), NULL); gtk_drag_dest_set (GTK_WIDGET (view), GTK_DEST_DEFAULT_ALL, @@ -3335,29 +4204,81 @@ ev_view_init (EvView *view) /*** Callbacks ***/ static void -find_changed_cb (EvDocument *document, int page, EvView *view) +ev_view_change_page (EvView *view, + gint new_page) { - double percent; - int n_pages; + gint x, y; - percent = ev_document_find_get_progress - (EV_DOCUMENT_FIND (view->document)); - n_pages = ev_page_cache_get_n_pages (view->page_cache); - - if (view->jump_to_find_result == TRUE) { - jump_to_find_page (view, EV_VIEW_FIND_NEXT, 0); - jump_to_find_result (view); - } - update_find_status_message (view, percent * n_pages >= n_pages - 1 ); - if (view->current_page == page) - gtk_widget_queue_draw (GTK_WIDGET (view)); + 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) { - gtk_widget_queue_draw (GTK_WIDGET (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); + } else { + gtk_widget_queue_draw (GTK_WIDGET (view)); + } } static void @@ -3366,25 +4287,24 @@ page_changed_cb (EvPageCache *page_cache, EvView *view) { if (view->current_page != new_page) { - view->current_page = new_page; - view->pending_scroll = SCROLL_TO_PAGE_POSITION; if (view->presentation) - ev_view_presentation_transition_start (view); - 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)); } - if (EV_IS_DOCUMENT_FIND (view->document)) { - view->find_result = 0; - update_find_status_message (view, TRUE); - } + view->find_result = 0; } -static void on_adjustment_value_changed (GtkAdjustment *adjustment, - EvView *view) +static void +on_adjustment_value_changed (GtkAdjustment *adjustment, + EvView *view) { int dx = 0, dy = 0; + gint x, y; + GList *children, *l; if (! GTK_WIDGET_REALIZED (view)) return; @@ -3403,12 +4323,27 @@ static void on_adjustment_value_changed (GtkAdjustment *adjustment, view->scroll_y = 0; } - + children = gtk_container_get_children (GTK_CONTAINER (view)); + for (l = children; l && l->data; l = g_list_next (l)) { + gint child_x, child_y; + GtkWidget *child = (GtkWidget *)l->data; + + gtk_container_child_get (GTK_CONTAINER (view), + child, + "x", &child_x, + "y", &child_y, + NULL); + gtk_layout_move (GTK_LAYOUT (view), child, child_x + dx, child_y + dy); + } + g_list_free (children); + if (view->pending_resize) gtk_widget_queue_draw (GTK_WIDGET (view)); else - gdk_window_scroll (GTK_WIDGET (view)->window, dx, dy); - + gdk_window_scroll (view->layout.bin_window, dx, dy); + + gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y); + ev_view_handle_cursor_over_xy (view, x, y); if (view->document) view_update_range_and_current_page (view); @@ -3431,6 +4366,9 @@ setup_caches (EvView *view) g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view); view->pixbuf_cache = ev_pixbuf_cache_new (GTK_WIDGET (view), view->document); g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view); + page_changed_cb (view->page_cache, + ev_page_cache_get_current_page (view->page_cache), + view); } static void @@ -3454,6 +4392,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) @@ -3466,12 +4477,8 @@ ev_view_set_document (EvView *view, clear_caches (view); if (view->document) { - g_signal_handlers_disconnect_by_func (view->document, - find_changed_cb, - view); g_object_unref (view->document); view->page_cache = NULL; - } view->document = document; @@ -3479,13 +4486,6 @@ ev_view_set_document (EvView *view, if (view->document) { g_object_ref (view->document); - if (EV_IS_DOCUMENT_FIND (view->document)) { - g_signal_connect (view->document, - "find_changed", - G_CALLBACK (find_changed_cb), - view); - } - setup_caches (view); } @@ -3508,7 +4508,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; @@ -3517,7 +4519,7 @@ ev_view_set_zoom (EvView *view, cairo_surface_destroy (view->loading_text); view->loading_text = NULL; } - + view->scale = scale; view->pending_resize = TRUE; @@ -3653,15 +4655,21 @@ 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 (GTK_WIDGET(view)->window, + gdk_window_set_background (view->layout.bin_window, >K_WIDGET (view)->style->black); else - gdk_window_set_background (GTK_WIDGET(view)->window, + gdk_window_set_background (view->layout.bin_window, >K_WIDGET (view)->style->mid [GTK_STATE_NORMAL]); } @@ -3704,10 +4712,12 @@ 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) - view->trans_timeout_id = g_timeout_add (duration * 1000, - (GSourceFunc) transition_next_page, - view); + if (duration > 0) { + view->trans_timeout_id = + g_timeout_add_seconds (duration, + (GSourceFunc) transition_next_page, + view); + } } void @@ -3750,6 +4760,9 @@ ev_view_zoom_in (EvView *view) { g_return_if_fail (view->sizing_mode == EV_SIZING_FREE); + if (view->presentation) + return; + view->pending_scroll = SCROLL_TO_CENTER; ev_view_set_zoom (view, ZOOM_IN_FACTOR, TRUE); } @@ -3759,6 +4772,9 @@ ev_view_zoom_out (EvView *view) { g_return_if_fail (view->sizing_mode == EV_SIZING_FREE); + if (view->presentation) + return; + view->pending_scroll = SCROLL_TO_CENTER; ev_view_set_zoom (view, ZOOM_OUT_FACTOR, TRUE); } @@ -4041,13 +5057,14 @@ ev_view_zoom_for_size_single_page (EvView *view, ev_view_set_zoom (view, scale, FALSE); } -void +static void ev_view_set_zoom_for_size (EvView *view, int width, int height, int vsb_width, int hsb_height) { + g_return_if_fail (EV_IS_VIEW (view)); g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH || view->sizing_mode == EV_SIZING_BEST_FIT); g_return_if_fail (width >= 0); @@ -4068,95 +5085,33 @@ ev_view_set_zoom_for_size (EvView *view, ev_view_zoom_for_size_single_page (view, width, height, vsb_width, hsb_height); } -/*** Status text messages ***/ - -const char * -ev_view_get_status (EvView *view) -{ - g_return_val_if_fail (EV_IS_VIEW (view), NULL); - - return view->status; -} - -static void -ev_view_set_status (EvView *view, const char *message) -{ - g_return_if_fail (EV_IS_VIEW (view)); - - if (message != view->status) { - g_free (view->status); - view->status = g_strdup (message); - g_object_notify (G_OBJECT (view), "status"); - } -} - -static void -update_find_status_message (EvView *view, gboolean this_page) -{ - char *message; - - if (this_page) { - int results; - - results = ev_document_find_get_n_results - (EV_DOCUMENT_FIND (view->document), - view->current_page); - /* TRANS: Sometimes this could be better translated as - "%d hit(s) on this page". Therefore this string - contains plural cases. */ - message = g_strdup_printf (ngettext ("%d found on this page", - "%d found on this page", - results), - results); - } else { - double percent; - - percent = ev_document_find_get_progress - (EV_DOCUMENT_FIND (view->document)); - message = g_strdup_printf (_("%3d%% remaining to search"), - (int) ((1.0 - percent) * 100)); - - } - ev_view_set_find_status (view, message); - g_free (message); -} - -const char * -ev_view_get_find_status (EvView *view) +/*** Find ***/ +static gint +ev_view_find_get_n_results (EvView *view, gint page) { - g_return_val_if_fail (EV_IS_VIEW (view), NULL); - - return view->find_status; + return view->find_pages ? g_list_length (view->find_pages[page]) : 0; } -static void -ev_view_set_find_status (EvView *view, const char *message) +static EvRectangle * +ev_view_find_get_result (EvView *view, gint page, gint result) { - g_return_if_fail (EV_IS_VIEW (view)); - - g_free (view->find_status); - view->find_status = g_strdup (message); - g_object_notify (G_OBJECT (view), "find-status"); + return view->find_pages ? (EvRectangle *) g_list_nth_data (view->find_pages[page], result) : NULL; } -/*** Find ***/ - static void jump_to_find_result (EvView *view) { - EvDocumentFind *find = EV_DOCUMENT_FIND (view->document); - EvRectangle rect; - GdkRectangle view_rect; - int n_results; - int page = view->current_page; + gint n_results; + gint page = view->current_page; - n_results = ev_document_find_get_n_results (find, page); + n_results = ev_view_find_get_n_results (view, page); - if (n_results > 0 && view->find_result < n_results) { - ev_document_find_get_result - (find, page, view->find_result, &rect); + if (n_results > 0 && view->find_result < n_results) { + EvRectangle *rect; + GdkRectangle view_rect; - doc_rect_to_view_rect (view, page, &rect, &view_rect); + rect = ev_view_find_get_result (view, page, view->find_result); + doc_rect_to_view_rect (view, page, rect, &view_rect); ensure_rectangle_is_visible (view, &view_rect); } } @@ -4179,7 +5134,6 @@ jump_to_find_page (EvView *view, EvViewFindDirection direction, gint shift) n_pages = ev_page_cache_get_n_pages (view->page_cache); for (i = 0; i < n_pages; i++) { - int has_results; int page; if (direction == EV_VIEW_FIND_NEXT) @@ -4190,53 +5144,39 @@ jump_to_find_page (EvView *view, EvViewFindDirection direction, gint shift) if (page >= n_pages) { page = page - n_pages; - } - if (page < 0) + } else if (page < 0) page = page + n_pages; - - has_results = ev_document_find_page_has_results - (EV_DOCUMENT_FIND (view->document), page); - if (has_results == -1) { - break; - } else if (has_results == 1) { + + if (ev_view_find_get_n_results (view, page) > 0) { ev_page_cache_set_current_page (view->page_cache, page); break; } } } -gboolean -ev_view_can_find_next (EvView *view) +void +ev_view_find_changed (EvView *view, GList **results, gint page) { - if (EV_IS_DOCUMENT_FIND (view->document)) { - EvDocumentFind *find = EV_DOCUMENT_FIND (view->document); - int i, n_pages; - - n_pages = ev_page_cache_get_n_pages (view->page_cache); - for (i = 0; i < n_pages; i++) { - if (ev_document_find_get_n_results (find, i) > 0) { - return TRUE; - } - } + view->find_pages = results; + + if (view->jump_to_find_result == TRUE) { + jump_to_find_page (view, EV_VIEW_FIND_NEXT, 0); + jump_to_find_result (view); } - return FALSE; + if (view->current_page == page) + gtk_widget_queue_draw (GTK_WIDGET (view)); } void ev_view_find_next (EvView *view) { - int n_results, n_pages; - EvDocumentFind *find = EV_DOCUMENT_FIND (view->document); - - n_results = ev_document_find_get_n_results (find, view->current_page); - - n_pages = ev_page_cache_get_n_pages (view->page_cache); + gint n_results; + n_results = ev_view_find_get_n_results (view, view->current_page); view->find_result++; if (view->find_result >= n_results) { - view->find_result = 0; jump_to_find_page (view, EV_VIEW_FIND_NEXT, 1); jump_to_find_result (view); @@ -4246,42 +5186,14 @@ ev_view_find_next (EvView *view) } } -gboolean -ev_view_can_find_previous (EvView *view) -{ - if (EV_IS_DOCUMENT_FIND (view->document)) { - EvDocumentFind *find = EV_DOCUMENT_FIND (view->document); - int i, n_pages; - - n_pages = ev_page_cache_get_n_pages (view->page_cache); - for (i = n_pages - 1; i >= 0; i--) { - if (ev_document_find_get_n_results (find, i) > 0) { - return TRUE; - } - } - } - - return FALSE; -} void ev_view_find_previous (EvView *view) { - int n_results, n_pages; - EvDocumentFind *find = EV_DOCUMENT_FIND (view->document); - EvPageCache *page_cache; - - page_cache = ev_page_cache_get (view->document); - - n_results = ev_document_find_get_n_results (find, view->current_page); - - n_pages = ev_page_cache_get_n_pages (page_cache); - view->find_result--; if (view->find_result < 0) { - jump_to_find_page (view, EV_VIEW_FIND_PREV, -1); - view->find_result = ev_document_find_get_n_results (find, view->current_page) - 1; + view->find_result = ev_view_find_get_n_results (view, view->current_page) - 1; jump_to_find_result (view); } else { jump_to_find_result (view); @@ -4289,10 +5201,25 @@ ev_view_find_previous (EvView *view) } } -void ev_view_search_changed (EvView *view) +void +ev_view_find_search_changed (EvView *view) { /* search string has changed, focus on new search result */ view->jump_to_find_result = TRUE; + view->find_pages = NULL; +} + +void +ev_view_find_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) +{ + view->find_pages = NULL; } /*** Selections ***/ @@ -4352,9 +5279,10 @@ gdk_rectangle_point_in (GdkRectangle *rectangle, } static GList * -compute_new_selection_text (EvView *view, - GdkPoint *start, - GdkPoint *stop) +compute_new_selection_text (EvView *view, + EvSelectionStyle style, + GdkPoint *start, + GdkPoint *stop) { int n_pages, i, first, last; GList *list = NULL; @@ -4395,8 +5323,6 @@ compute_new_selection_text (EvView *view, } - - /* Now create a list of EvViewSelection's for the affected * pages. This could be an empty list, a list of just one * page or a number of pages.*/ @@ -4411,6 +5337,7 @@ compute_new_selection_text (EvView *view, selection = g_new0 (EvViewSelection, 1); selection->page = i; + selection->style = style; selection->rect.x1 = selection->rect.y1 = 0; selection->rect.x2 = width; selection->rect.y2 = height; @@ -4503,10 +5430,12 @@ merge_selection_region (EvView *view, * region too. */ if (new_sel) { GdkRegion *tmp_region = NULL; - ev_pixbuf_cache_get_selection_pixbuf (view->pixbuf_cache, - cur_page, - view->scale, - &tmp_region); + + ev_pixbuf_cache_get_selection_surface (view->pixbuf_cache, + cur_page, + view->scale, + &tmp_region); + if (tmp_region) { new_sel->covered_region = gdk_region_copy (tmp_region); } @@ -4514,8 +5443,7 @@ merge_selection_region (EvView *view, /* Now we figure out what needs redrawing */ if (old_sel && new_sel) { - if (old_sel->covered_region && - new_sel->covered_region) { + if (old_sel->covered_region && new_sel->covered_region) { /* We only want to redraw the areas that have * changed, so we xor the old and new regions * and redraw if it's different */ @@ -4547,11 +5475,17 @@ merge_selection_region (EvView *view, GdkRectangle page_area; GtkBorder border; + /* I don't know why but the region is smaller + * than expected. This hack fixes it, I guess + * 10 pixels more won't hurt + */ + gdk_region_shrink (region, -5, -5); + get_page_extents (view, cur_page, &page_area, &border); gdk_region_offset (region, page_area.x + border.left - view->scroll_x, page_area.y + border.top - view->scroll_y); - gdk_window_invalidate_region (GTK_WIDGET (view)->window, region, TRUE); + gdk_window_invalidate_region (view->layout.bin_window, region, TRUE); gdk_region_destroy (region); } } @@ -4561,16 +5495,17 @@ merge_selection_region (EvView *view, } static void -compute_selections (EvView *view, - GdkPoint *start, - GdkPoint *stop) +compute_selections (EvView *view, + EvSelectionStyle style, + GdkPoint *start, + GdkPoint *stop) { GList *list; if (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE) list = compute_new_selection_rect (view, start, stop); else - list = compute_new_selection_text (view, start, stop); + list = compute_new_selection_text (view, style, start, stop); merge_selection_region (view, list); } @@ -4595,10 +5530,10 @@ clear_selection (EvView *view) g_object_notify (G_OBJECT (view), "has-selection"); } - void ev_view_select_all (EvView *view) { + GList *selections = NULL; int n_pages, i; /* Disable selection on rotated pages for the 0.4.0 series */ @@ -4606,7 +5541,7 @@ ev_view_select_all (EvView *view) return; clear_selection (view); - + n_pages = ev_page_cache_get_n_pages (view->page_cache); for (i = 0; i < n_pages; i++) { int width, height; @@ -4619,15 +5554,15 @@ ev_view_select_all (EvView *view) selection = g_new0 (EvViewSelection, 1); selection->page = i; + selection->style = EV_SELECTION_STYLE_GLYPH; selection->rect.x1 = selection->rect.y1 = 0; selection->rect.x2 = width; selection->rect.y2 = height; - view->selection_info.selections = g_list_append (view->selection_info.selections, selection); + selections = g_list_append (selections, selection); } - ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, view->selection_info.selections); - g_object_notify (G_OBJECT (view), "has-selection"); + merge_selection_region (view, selections); gtk_widget_queue_draw (GTK_WIDGET (view)); } @@ -4638,45 +5573,65 @@ ev_view_get_has_selection (EvView *view) } static char * -get_selected_text (EvView *ev_view) +get_selected_text (EvView *view) { GString *text; GList *l; + gchar *normalized_text; + EvRenderContext *rc; text = g_string_new (NULL); + rc = ev_render_context_new (NULL, view->rotation, view->scale); ev_document_doc_mutex_lock (); - for (l = ev_view->selection_info.selections; l != NULL; l = l->next) { + for (l = view->selection_info.selections; l != NULL; l = l->next) { EvViewSelection *selection = (EvViewSelection *)l->data; - char *tmp; + EvPage *page; + gchar *tmp; + + 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)); - tmp = ev_document_get_text (ev_view->document, - selection->page, - &selection->rect); g_string_append (text, tmp); g_free (tmp); } + 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; +} - return g_string_free (text, FALSE); +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_document_can_get_text (ev_view->document)) { + 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); } @@ -4687,15 +5642,21 @@ ev_view_primary_get_cb (GtkClipboard *clipboard, gpointer data) { EvView *ev_view = EV_VIEW (data); - char *text; - if (!ev_document_can_get_text (ev_view->document)) { - return; + 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); + } } - - text = get_selected_text (ev_view); - gtk_selection_data_set_text (selection_data, text, -1); - g_free (text); } static void @@ -4705,6 +5666,7 @@ ev_view_primary_clear_cb (GtkClipboard *clipboard, EvView *view = EV_VIEW (data); clear_selection (view); + clear_link_selected (view); } static void @@ -4715,7 +5677,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), @@ -4729,6 +5691,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 * @@ -4760,7 +5743,7 @@ ev_view_set_cursor (EvView *view, EvViewCursor new_cursor) switch (new_cursor) { case EV_VIEW_CURSOR_NORMAL: - gdk_window_set_cursor (widget->window, NULL); + gdk_window_set_cursor (view->layout.bin_window, NULL); break; case EV_VIEW_CURSOR_IBEAM: cursor = gdk_cursor_new_for_display (display, GDK_XTERM); @@ -4777,10 +5760,13 @@ 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) { - gdk_window_set_cursor (widget->window, cursor); + gdk_window_set_cursor (view->layout.bin_window, cursor); gdk_cursor_unref (cursor); gdk_flush(); } @@ -4806,7 +5792,7 @@ ev_view_reset_presentation_state (EvView *view) return; view->presentation_state = EV_PRESENTATION_NORMAL; - gdk_window_set_background (GTK_WIDGET (view)->window, + gdk_window_set_background (view->layout.bin_window, >K_WIDGET (view)->style->black); gtk_widget_queue_draw (GTK_WIDGET (view)); } @@ -4821,6 +5807,18 @@ ev_view_next_page (EvView *view) if (!view->page_cache) return FALSE; + if (view->presentation && + (view->presentation_state == EV_PRESENTATION_BLACK || + 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); @@ -4862,6 +5860,18 @@ ev_view_previous_page (EvView *view) ev_view_reset_presentation_state (view); return TRUE; } + + if (view->presentation && + (view->presentation_state == EV_PRESENTATION_BLACK || + 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_reset_presentation_state (view); @@ -4919,3 +5929,37 @@ ev_scroll_type_get_type (void) } return etype; } + +void +ev_view_update_view_size (EvView *view, GtkScrolledWindow * scrolled_window) +{ + int width, height; + GtkRequisition vsb_requisition; + GtkRequisition hsb_requisition; + int scrollbar_spacing; + + /* Calculate the width available for the content */ + width = GTK_WIDGET (scrolled_window)->allocation.width; + height = GTK_WIDGET (scrolled_window)->allocation.height; + + if (gtk_scrolled_window_get_shadow_type (scrolled_window) == GTK_SHADOW_IN + && view) { + width -= 2 * GTK_WIDGET(view)->style->xthickness; + height -= 2 * GTK_WIDGET(view)->style->ythickness; + } + + gtk_widget_size_request (scrolled_window->vscrollbar, &vsb_requisition); + gtk_widget_size_request (scrolled_window->hscrollbar, &hsb_requisition); + gtk_widget_style_get (GTK_WIDGET (scrolled_window), + "scrollbar_spacing", + &scrollbar_spacing, + NULL); + + if (EV_IS_VIEW(view)) { + ev_view_set_zoom_for_size (EV_VIEW (view), + MAX (1, width), + MAX (1, height), + vsb_requisition.width + scrollbar_spacing, + hsb_requisition.height + scrollbar_spacing); + } +}