X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;f=libview%2Fev-view.c;h=4686560b6ff705ab21684fd856593d71f2ca7408;hb=b0e729d2f6401c7cf6988aa044f47b3ca8e48487;hp=7cc0f79e6962981eedd27361643c31b35cf7eed0;hpb=00f74f90ff46e9f434e12b593ac1afcbb47443bf;p=evince.git diff --git a/libview/ev-view.c b/libview/ev-view.c index 7cc0f79e..4686560b 100644 --- a/libview/ev-view.c +++ b/libview/ev-view.c @@ -28,7 +28,7 @@ #include #include -#include "ev-mapping.h" +#include "ev-mapping-list.h" #include "ev-document-forms.h" #include "ev-document-images.h" #include "ev-document-links.h" @@ -54,6 +54,8 @@ enum { SIGNAL_EXTERNAL_LINK, SIGNAL_POPUP_MENU, SIGNAL_SELECTION_CHANGED, + SIGNAL_SYNC_SOURCE, + SIGNAL_ANNOT_ADDED, N_SIGNALS }; @@ -99,10 +101,6 @@ static void compute_border (EvView static void get_page_y_offset (EvView *view, int page, int *y_offset); -static gboolean get_page_extents (EvView *view, - gint page, - GdkRectangle *page_area, - GtkBorder *border); static void view_rect_to_doc_rect (EvView *view, GdkRectangle *view_rect, GdkRectangle *page_area, @@ -179,6 +177,11 @@ static AtkObject *ev_view_get_accessible (GtkWidget *widget) /*** Drawing ***/ static void highlight_find_results (EvView *view, int page); +static void highlight_forward_search_results (EvView *view, + int page); +static void focus_annotation (EvView *view, + int page, + GdkRectangle *clip); static void draw_one_page (EvView *view, gint page, cairo_t *cr, @@ -190,14 +193,14 @@ static void show_loading_window (EvView static void hide_loading_window (EvView *view); static void ev_view_reload_page (EvView *view, gint page, - GdkRegion *region); + cairo_region_t *region); static void ev_view_loading_window_move (EvView *view); /*** Callbacks ***/ static void ev_view_change_page (EvView *view, gint new_page); static void job_finished_cb (EvPixbufCache *pixbuf_cache, - GdkRegion *region, + cairo_region_t *region, EvView *view); static void ev_view_page_changed_cb (EvDocumentModel *model, gint old_page, @@ -257,7 +260,6 @@ static void jump_to_find_result (EvView static void jump_to_find_page (EvView *view, EvViewFindDirection direction, gint shift); - /*** Selection ***/ static void compute_selections (EvView *view, EvSelectionStyle style, @@ -530,7 +532,7 @@ ev_view_scroll_to_page_position (EvView *view, GtkOrientation orientation) GdkRectangle page_area; GtkBorder border; - get_page_extents (view, view->current_page, &page_area, &border); + ev_view_get_page_extents (view, view->current_page, &page_area, &border); x = page_area.x; y = page_area.y; } else { @@ -651,7 +653,7 @@ view_update_range_and_current_page (EvView *view) for (i = 0; i < ev_document_get_n_pages (view->document); i++) { - get_page_extents (view, i, &page_area, &border); + ev_view_get_page_extents (view, i, &page_area, &border); if (gdk_rectangle_intersect (¤t_area, &page_area, &unused)) { area = unused.width * unused.height; @@ -730,8 +732,6 @@ view_update_range_and_current_page (EvView *view) ev_pixbuf_cache_set_page_range (view->pixbuf_cache, view->start_page, view->end_page, - view->rotation, - view->scale, view->selection_info.selections); if (ev_pixbuf_cache_get_surface (view->pixbuf_cache, view->current_page)) @@ -807,15 +807,13 @@ compute_scroll_increment (EvView *view, { GtkWidget *widget = GTK_WIDGET (view); GtkAdjustment *adjustment = view->vadjustment; - GdkRegion *text_region, *region; + cairo_region_t *text_region, *region; GtkAllocation allocation; gint page; GdkRectangle rect; EvRectangle doc_rect; GdkRectangle page_area; GtkBorder border; - GdkRectangle *recs; - gint n_recs; gdouble fraction = 1.0; if (scroll != GTK_SCROLL_PAGE_BACKWARD && scroll != GTK_SCROLL_PAGE_FORWARD) @@ -824,11 +822,11 @@ compute_scroll_increment (EvView *view, page = scroll == GTK_SCROLL_PAGE_BACKWARD ? view->start_page : view->end_page; text_region = ev_page_cache_get_text_mapping (view->page_cache, page); - if (!text_region || gdk_region_empty (text_region)) + if (!text_region || cairo_region_is_empty (text_region)) return gtk_adjustment_get_page_size (adjustment); gtk_widget_get_allocation (widget, &allocation); - get_page_extents (view, page, &page_area, &border); + ev_view_get_page_extents (view, page, &page_area, &border); rect.x = page_area.x + view->scroll_x; rect.y = view->scroll_y + (scroll == GTK_SCROLL_PAGE_BACKWARD ? 5 : allocation.height - 5); rect.width = page_area.width; @@ -840,37 +838,37 @@ compute_scroll_increment (EvView *view, rect.y = doc_rect.y1; rect.width = doc_rect.x2 - doc_rect.x1; rect.height = MAX (1, doc_rect.y2 - doc_rect.y1); - region = gdk_region_rectangle (&rect); + region = cairo_region_create_rectangle (&rect); - gdk_region_intersect (region, text_region); - gdk_region_get_rectangles (region, &recs, &n_recs); - gdk_region_destroy (region); - if (n_recs > 0) { + cairo_region_intersect (region, text_region); + if (cairo_region_num_rectangles (region)) { EvRenderContext *rc; EvPage *ev_page; + cairo_region_t *sel_region; + cairo_region_get_rectangle (region, 0, &rect); ev_page = ev_document_get_page (view->document, page); rc = ev_render_context_new (ev_page, view->rotation, view->scale); g_object_unref (ev_page); /* Get the selection region to know the height of the line */ - doc_rect.x1 = doc_rect.x2 = recs[0].x + 0.5; - doc_rect.y1 = doc_rect.y2 = recs[0].y + 0.5; + doc_rect.x1 = doc_rect.x2 = rect.x + 0.5; + doc_rect.y1 = doc_rect.y2 = rect.y + 0.5; ev_document_doc_mutex_lock (); - region = ev_selection_get_selection_region (EV_SELECTION (view->document), - rc, EV_SELECTION_STYLE_LINE, - &doc_rect); + sel_region = ev_selection_get_selection_region (EV_SELECTION (view->document), + rc, EV_SELECTION_STYLE_LINE, + &doc_rect); ev_document_doc_mutex_unlock (); g_object_unref (rc); - g_free (recs); - gdk_region_get_rectangles (region, &recs, &n_recs); - gdk_region_destroy (region); - if (n_recs > 0) { - fraction = 1 - (recs[0].height / gtk_adjustment_get_page_size (adjustment)); + + if (cairo_region_num_rectangles (sel_region) > 0) { + cairo_region_get_rectangle (sel_region, 0, &rect); + fraction = 1 - (rect.height / gtk_adjustment_get_page_size (adjustment)); } - g_free (recs); + cairo_region_destroy (sel_region); } + cairo_region_destroy (region); return gtk_adjustment_get_page_size (adjustment) * fraction; @@ -1100,11 +1098,11 @@ get_page_y_offset (EvView *view, int page, int *y_offset) return; } -static gboolean -get_page_extents (EvView *view, - gint page, - GdkRectangle *page_area, - GtkBorder *border) +gboolean +ev_view_get_page_extents (EvView *view, + gint page, + GdkRectangle *page_area, + GtkBorder *border) { GtkWidget *widget; int width, height; @@ -1265,7 +1263,7 @@ doc_point_to_view_point (EvView *view, g_assert_not_reached (); } - get_page_extents (view, page, &page_area, &border); + ev_view_get_page_extents (view, page, &page_area, &border); view_x = CLAMP (x * view->scale, 0, page_area.width); view_y = CLAMP (y * view->scale, 0, page_area.height); @@ -1310,7 +1308,7 @@ doc_rect_to_view_rect (EvView *view, g_assert_not_reached (); } - get_page_extents (view, page, &page_area, &border); + ev_view_get_page_extents (view, page, &page_area, &border); view_rect->x = x * view->scale + page_area.x; view_rect->y = y * view->scale + page_area.y; @@ -1345,7 +1343,7 @@ find_page_at_location (EvView *view, GdkRectangle page_area; GtkBorder border; - if (! get_page_extents (view, i, &page_area, &border)) + if (! ev_view_get_page_extents (view, i, &page_area, &border)) continue; if ((x >= page_area.x + border.left) && @@ -1367,7 +1365,7 @@ location_in_text (EvView *view, gdouble x, gdouble y) { - GdkRegion *region; + cairo_region_t *region; gint page = -1; gint x_offset = 0, y_offset = 0; @@ -1379,7 +1377,7 @@ location_in_text (EvView *view, region = ev_page_cache_get_text_mapping (view->page_cache, page); if (region) - return gdk_region_point_in (region, x_offset / view->scale, y_offset / view->scale); + return cairo_region_contains_point (region, x_offset / view->scale, y_offset / view->scale); else return FALSE; } @@ -1403,7 +1401,7 @@ location_in_selected_text (EvView *view, continue; if (selection->covered_region && - gdk_region_point_in (selection->covered_region, x_offset, y_offset)) + cairo_region_contains_point (selection->covered_region, x_offset, y_offset)) return TRUE; } @@ -1468,11 +1466,11 @@ get_doc_point_from_location (EvView *view, } static void -ev_view_get_area_from_mapping (EvView *view, - guint page, - GList *mapping_list, - gconstpointer data, - GdkRectangle *area) +ev_view_get_area_from_mapping (EvView *view, + guint page, + EvMappingList *mapping_list, + gconstpointer data, + GdkRectangle *area) { EvMapping *mapping; @@ -1491,7 +1489,7 @@ ev_view_get_link_at_location (EvView *view, { gint page = -1; gint x_new = 0, y_new = 0; - GList *link_mapping; + EvMappingList *link_mapping; if (!EV_IS_DOCUMENT_LINKS (view->document)) return NULL; @@ -1835,6 +1833,12 @@ ev_view_handle_cursor_over_xy (EvView *view, gint x, gint y) if (view->cursor == EV_VIEW_CURSOR_HIDDEN) return; + if (view->adding_annot) { + if (view->cursor != EV_VIEW_CURSOR_ADD) + ev_view_set_cursor (view, EV_VIEW_CURSOR_ADD); + return; + } + if (view->drag_info.in_drag) { if (view->cursor != EV_VIEW_CURSOR_DRAG) ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG); @@ -1869,7 +1873,8 @@ ev_view_handle_cursor_over_xy (EvView *view, gint x, gint y) if (view->cursor == EV_VIEW_CURSOR_LINK || view->cursor == EV_VIEW_CURSOR_IBEAM || view->cursor == EV_VIEW_CURSOR_DRAG || - view->cursor == EV_VIEW_CURSOR_AUTOSCROLL) + view->cursor == EV_VIEW_CURSOR_AUTOSCROLL || + view->cursor == EV_VIEW_CURSOR_ADD) ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); } @@ -1885,7 +1890,7 @@ ev_view_get_image_at_location (EvView *view, { gint page = -1; gint x_new = 0, y_new = 0; - GList *image_mapping; + EvMappingList *image_mapping; if (!EV_IS_DOCUMENT_IMAGES (view->document)) return NULL; @@ -1909,7 +1914,7 @@ ev_view_get_form_field_at_location (EvView *view, { gint page = -1; gint x_new = 0, y_new = 0; - GList *forms_mapping; + EvMappingList *forms_mapping; if (!EV_IS_DOCUMENT_FORMS (view->document)) return NULL; @@ -1925,12 +1930,12 @@ ev_view_get_form_field_at_location (EvView *view, return NULL; } -static GdkRegion * +static cairo_region_t * ev_view_form_field_get_region (EvView *view, EvFormField *field) { - GdkRectangle view_area; - GList *forms_mapping; + GdkRectangle view_area; + EvMappingList *forms_mapping; forms_mapping = ev_page_cache_get_form_field_mapping (view->page_cache, field->page->index); @@ -1938,7 +1943,7 @@ ev_view_form_field_get_region (EvView *view, forms_mapping, field, &view_area); - return gdk_region_rectangle (&view_area); + return cairo_region_create_rectangle (&view_area); } static gboolean @@ -1961,15 +1966,16 @@ ev_view_form_field_button_create_widget (EvView *view, EvFormField *field) { EvFormFieldButton *field_button = EV_FORM_FIELD_BUTTON (field); - GdkRegion *field_region = NULL; + cairo_region_t *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; + gboolean state; + EvMappingList *forms_mapping; + GList *l; state = ev_document_forms_form_field_button_get_state (EV_DOCUMENT_FORMS (view->document), field); @@ -1986,9 +1992,9 @@ ev_view_form_field_button_create_widget (EvView *view, */ forms_mapping = ev_page_cache_get_form_field_mapping (view->page_cache, field->page->index); - for (l = forms_mapping; l; l = g_list_next (l)) { + for (l = ev_mapping_list_get_list (forms_mapping); l; l = g_list_next (l)) { EvFormField *button = ((EvMapping *)(l->data))->data; - GdkRegion *button_region; + cairo_region_t *button_region; if (button->id == field->id) continue; @@ -2000,8 +2006,8 @@ ev_view_form_field_button_create_widget (EvView *view, continue; button_region = ev_view_form_field_get_region (view, button); - gdk_region_union (field_region, button_region); - gdk_region_destroy (button_region); + cairo_region_union (field_region, button_region); + cairo_region_destroy (button_region); } ev_document_forms_form_field_button_set_state (EV_DOCUMENT_FORMS (view->document), @@ -2012,7 +2018,7 @@ ev_view_form_field_button_create_widget (EvView *view, } ev_view_reload_page (view, field->page->index, field_region); - gdk_region_destroy (field_region); + cairo_region_destroy (field_region); return NULL; } @@ -2030,7 +2036,7 @@ ev_view_form_field_text_save (EvView *view, if (field->changed) { EvFormFieldText *field_text = EV_FORM_FIELD_TEXT (field); - GdkRegion *field_region; + cairo_region_t *field_region; field_region = ev_view_form_field_get_region (view, field); @@ -2038,7 +2044,7 @@ ev_view_form_field_text_save (EvView *view, field, field_text->text); field->changed = FALSE; ev_view_reload_page (view, field->page->index, field_region); - gdk_region_destroy (field_region); + cairo_region_destroy (field_region); } } @@ -2138,7 +2144,7 @@ ev_view_form_field_choice_save (EvView *view, if (field->changed) { GList *l; EvFormFieldChoice *field_choice = EV_FORM_FIELD_CHOICE (field); - GdkRegion *field_region; + cairo_region_t *field_region; field_region = ev_view_form_field_get_region (view, field); @@ -2155,7 +2161,7 @@ ev_view_form_field_choice_save (EvView *view, } field->changed = FALSE; ev_view_reload_page (view, field->page->index, field_region); - gdk_region_destroy (field_region); + cairo_region_destroy (field_region); } } @@ -2344,9 +2350,9 @@ ev_view_handle_form_field (EvView *view, gdouble x, gdouble y) { - GtkWidget *field_widget = NULL; - GList *form_field_mapping; - GdkRectangle view_area; + GtkWidget *field_widget = NULL; + EvMappingList *form_field_mapping; + GdkRectangle view_area; if (field->is_read_only) return; @@ -2406,11 +2412,19 @@ ev_view_window_child_move (EvView *view, gint x, gint y) { + GtkAllocation allocation; + gint width, height; + + gtk_widget_get_allocation (GTK_WIDGET (view), &allocation); + gtk_window_get_size (GTK_WINDOW (child->window), &width, &height); + child->x = x; child->y = y; gtk_window_move (GTK_WINDOW (child->window), - MAX (child->parent_x, x), - MAX (child->parent_y, y)); + CLAMP (x, child->parent_x, + child->parent_x + allocation.width - width), + CLAMP (y, child->parent_y, + child->parent_y + allocation.height - height)); } static void @@ -2488,7 +2502,7 @@ ev_view_find_window_child_for_annot (EvView *view, continue; wannot = ev_annotation_window_get_annotation (EV_ANNOTATION_WINDOW (child->window)); - if (wannot == annot || strcmp (wannot->name, annot->name) == 0) + if (ev_annotation_equal (wannot, annot)) return child; } @@ -2564,44 +2578,80 @@ annotation_window_moved (EvAnnotationWindow *window, view_rect.width = width; view_rect.height = height; - get_page_extents (view, child->page, &page_area, &border); + ev_view_get_page_extents (view, child->page, &page_area, &border); view_rect_to_doc_rect (view, &view_rect, &page_area, &doc_rect); child->orig_x = doc_rect.x1; child->orig_y = doc_rect.y1; } static void -ev_view_annotation_save (EvView *view, - EvAnnotation *annot) +ev_view_annotation_save_contents (EvView *view, + GParamSpec *pspec, + EvAnnotation *annot) { if (!view->document) return; - if (!annot->changed) - return; + ev_document_doc_mutex_lock (); + ev_document_annotations_save_annotation (EV_DOCUMENT_ANNOTATIONS (view->document), + annot, EV_ANNOTATIONS_SAVE_CONTENTS); + ev_document_doc_mutex_unlock (); +} + +static GtkWidget * +ev_view_create_annotation_window (EvView *view, + EvAnnotation *annot, + GtkWindow *parent) +{ + GtkWidget *window; + EvRectangle doc_rect; + GdkRectangle view_rect; + guint page; + + window = ev_annotation_window_new (annot, parent); + g_signal_connect (window, "grab_focus", + G_CALLBACK (annotation_window_grab_focus), + view); + g_signal_connect (window, "closed", + G_CALLBACK (annotation_window_closed), + view); + g_signal_connect (window, "moved", + G_CALLBACK (annotation_window_moved), + view); + g_signal_connect_swapped (annot, "notify::contents", + G_CALLBACK (ev_view_annotation_save_contents), + view); + g_object_set_data (G_OBJECT (annot), "popup", window); + + page = ev_annotation_get_page_index (annot); + ev_annotation_window_get_rectangle (EV_ANNOTATION_WINDOW (window), &doc_rect); + doc_rect_to_view_rect (view, page, &doc_rect, &view_rect); + view_rect.x -= view->scroll_x; + view_rect.y -= view->scroll_y; - ev_document_annotations_annotation_set_contents (EV_DOCUMENT_ANNOTATIONS (view->document), - annot, annot->contents); - annot->changed = FALSE; + ev_view_window_child_put (view, window, page, + view_rect.x, view_rect.y, + doc_rect.x1, doc_rect.y1); + + return window; } static void show_annotation_windows (EvView *view, gint page) { - GList *annots, *l; - GtkWindow *parent; + EvMappingList *annots; + GList *l; + GtkWindow *parent; parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))); annots = ev_page_cache_get_annot_mapping (view->page_cache, page); - for (l = annots; l && l->data; l = g_list_next (l)) { + for (l = ev_mapping_list_get_list (annots); l && l->data; l = g_list_next (l)) { EvAnnotation *annot; EvViewWindowChild *child; GtkWidget *window; - EvRectangle *doc_rect; - GdkRectangle view_rect; annot = ((EvMapping *)(l->data))->data; @@ -2625,30 +2675,7 @@ show_annotation_windows (EvView *view, g_object_set_data (G_OBJECT (annot), "popup", window); ev_view_window_child_move_with_parent (view, window); } else { - window = ev_annotation_window_new (annot, parent); - g_signal_connect (window, "grab_focus", - G_CALLBACK (annotation_window_grab_focus), - view); - g_signal_connect (window, "closed", - G_CALLBACK (annotation_window_closed), - view); - g_signal_connect (window, "moved", - G_CALLBACK (annotation_window_moved), - view); - g_object_set_data (G_OBJECT (annot), "popup", window); - - doc_rect = (EvRectangle *)ev_annotation_window_get_rectangle (EV_ANNOTATION_WINDOW (window)); - doc_rect_to_view_rect (view, page, doc_rect, &view_rect); - view_rect.x -= view->scroll_x; - view_rect.y -= view->scroll_y; - - ev_view_window_child_put (view, window, page, - view_rect.x, view_rect.y, - doc_rect->x1, doc_rect->y1); - - g_object_weak_ref (G_OBJECT (annot), - (GWeakNotify)ev_view_annotation_save, - view); + ev_view_create_annotation_window (view, annot, parent); } } } @@ -2657,11 +2684,12 @@ static void hide_annotation_windows (EvView *view, gint page) { - GList *annots, *l; + EvMappingList *annots; + GList *l; annots = ev_page_cache_get_annot_mapping (view->page_cache, page); - for (l = annots; l && l->data; l = g_list_next (l)) { + for (l = ev_mapping_list_get_list (annots); l && l->data; l = g_list_next (l)) { EvAnnotation *annot; GtkWidget *window; @@ -2683,7 +2711,7 @@ ev_view_get_annotation_at_location (EvView *view, { gint page = -1; gint x_new = 0, y_new = 0; - GList *annotations_mapping; + EvMappingList *annotations_mapping; if (!EV_IS_DOCUMENT_ANNOTATIONS (view->document)) return NULL; @@ -2699,6 +2727,23 @@ ev_view_get_annotation_at_location (EvView *view, return NULL; } +static void +ev_view_annotation_show_popup_window (EvView *view, + GtkWidget *window) +{ + EvViewWindowChild *child; + + if (!window) + return; + + child = ev_view_get_window_child (view, window); + if (!child->visible) { + child->visible = TRUE; + ev_view_window_child_move (view, child, child->x, child->y); + gtk_widget_show (window); + } +} + static void ev_view_handle_annotation (EvView *view, EvAnnotation *annot, @@ -2710,21 +2755,13 @@ ev_view_handle_annotation (EvView *view, GtkWidget *window; window = g_object_get_data (G_OBJECT (annot), "popup"); - if (window) { - EvViewWindowChild *child; - - child = ev_view_get_window_child (view, window); - if (!child->visible) { - child->visible = TRUE; - ev_view_window_child_move (view, child, child->x, child->y); - gtk_widget_show (window); - } - } + ev_view_annotation_show_popup_window (view, window); } if (EV_IS_ANNOTATION_ATTACHMENT (annot)) { - EvAttachment *attachment = EV_ANNOTATION_ATTACHMENT (annot)->attachment; + EvAttachment *attachment; + attachment = ev_annotation_attachment_get_attachment (EV_ANNOTATION_ATTACHMENT (annot)); if (attachment) { GError *error = NULL; @@ -2741,6 +2778,170 @@ ev_view_handle_annotation (EvView *view, } } +static void +ev_view_create_annotation (EvView *view, + EvAnnotationType annot_type, + gint x, + gint y) +{ + EvAnnotation *annot; + GdkPoint point; + GdkRectangle page_area; + GtkBorder border; + EvRectangle doc_rect, popup_rect; + EvPage *page; + GdkColor color = { 0, 65535, 65535, 0 }; + GdkRectangle view_rect; + cairo_region_t *region; + + point.x = x; + point.y = y; + ev_view_get_page_extents (view, view->current_page, &page_area, &border); + view_point_to_doc_point (view, &point, &page_area, + &doc_rect.x1, &doc_rect.y1); + doc_rect.x2 = doc_rect.x1 + 24; + doc_rect.y2 = doc_rect.y1 + 24; + + ev_document_doc_mutex_lock (); + page = ev_document_get_page (view->document, view->current_page); + switch (annot_type) { + case EV_ANNOTATION_TYPE_TEXT: + annot = ev_annotation_text_new (page); + break; + case EV_ANNOTATION_TYPE_ATTACHMENT: + /* TODO */ + g_object_unref (page); + ev_document_doc_mutex_unlock (); + return; + default: + g_assert_not_reached (); + } + g_object_unref (page); + + ev_annotation_set_color (annot, &color); + + if (EV_IS_ANNOTATION_MARKUP (annot)) { + popup_rect.x1 = doc_rect.x2; + popup_rect.x2 = popup_rect.x1 + 200; + popup_rect.y1 = doc_rect.y2; + popup_rect.y2 = popup_rect.y1 + 150; + g_object_set (annot, + "rectangle", &popup_rect, + "has_popup", TRUE, + "popup_is_open", FALSE, + "label", g_get_real_name (), + "opacity", 1.0, + NULL); + } + ev_document_annotations_add_annotation (EV_DOCUMENT_ANNOTATIONS (view->document), + annot, &doc_rect); + ev_document_doc_mutex_unlock (); + + /* If the page didn't have annots, mark the cache as dirty */ + if (!ev_page_cache_get_annot_mapping (view->page_cache, view->current_page)) + ev_page_cache_mark_dirty (view->page_cache, view->current_page); + + if (EV_IS_ANNOTATION_MARKUP (annot)) { + GtkWindow *parent; + GtkWidget *window; + + parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))); + window = ev_view_create_annotation_window (view, annot, parent); + + /* Show the annot window the first time */ + ev_view_annotation_show_popup_window (view, window); + } + + doc_rect_to_view_rect (view, view->current_page, &doc_rect, &view_rect); + view_rect.x -= view->scroll_x; + view_rect.y -= view->scroll_y; + region = cairo_region_create_rectangle (&view_rect); + ev_view_reload_page (view, view->current_page, region); + cairo_region_destroy (region); + + g_signal_emit (view, signals[SIGNAL_ANNOT_ADDED], 0, annot); +} + +void +ev_view_focus_annotation (EvView *view, + EvMapping *annot_mapping) +{ + GdkRectangle view_rect; + EvAnnotation *annot; + guint page; + + if (!EV_IS_DOCUMENT_ANNOTATIONS (view->document)) + return; + + if (view->focus_annotation == annot_mapping) + return; + + view->focus_annotation = annot_mapping; + annot = (EvAnnotation *)annot_mapping->data; + + page = ev_annotation_get_page_index (annot); + ev_document_model_set_page (view->model, page); + + doc_rect_to_view_rect (view, page, + &annot_mapping->area, &view_rect); + ensure_rectangle_is_visible (view, &view_rect); + gtk_widget_queue_draw (GTK_WIDGET (view)); +} + +void +ev_view_begin_add_annotation (EvView *view, + EvAnnotationType annot_type) +{ + if (annot_type == EV_ANNOTATION_TYPE_UNKNOWN) + return; + + if (view->adding_annot) + return; + + view->adding_annot = TRUE; + view->adding_annot_type = annot_type; + ev_view_set_cursor (view, EV_VIEW_CURSOR_ADD); +} + +void +ev_view_cancel_add_annotation (EvView *view) +{ + gint x, y; + + if (!view->adding_annot) + return; + + view->adding_annot = FALSE; + gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y); + ev_view_handle_cursor_over_xy (view, x, y); +} + +static gboolean +ev_view_synctex_backward_search (EvView *view, + gdouble x, + gdouble y) +{ + gint page = -1; + gint x_new = 0, y_new = 0; + EvSourceLink *link; + + if (!ev_document_has_synctex (view->document)) + return FALSE; + + if (!get_doc_point_from_location (view, x, y, &page, &x_new, &y_new)) + return FALSE; + + link = ev_document_synctex_backward_search (view->document, page, x_new, y_new); + if (link) { + g_signal_emit (view, signals[SIGNAL_SYNC_SOURCE], 0, link); + g_free (link); + + return TRUE; + } + + return FALSE; +} + /*** GtkWidget implementation ***/ static void @@ -2939,7 +3140,7 @@ ev_view_size_allocate (GtkWidget *widget, for (l = children; l && l->data; l = g_list_next (l)) { EvFormField *field; GdkRectangle view_area; - GList *form_field_mapping; + EvMappingList *form_field_mapping; GtkAllocation child_allocation; GtkRequisition child_requisition; GtkWidget *child = (GtkWidget *)l->data; @@ -2982,7 +3183,7 @@ ev_view_size_allocate (GtkWidget *widget, child = (EvViewWindowChild *)l->data; - doc_rect = *ev_annotation_window_get_rectangle (EV_ANNOTATION_WINDOW (child->window)); + ev_annotation_window_get_rectangle (EV_ANNOTATION_WINDOW (child->window), &doc_rect); if (child->moved) { doc_rect.x1 = child->orig_x; doc_rect.y1 = child->orig_y; @@ -3132,7 +3333,7 @@ ev_view_expose_event (GtkWidget *widget, GtkBorder border; gboolean page_ready = TRUE; - if (!get_page_extents (view, i, &page_area, &border)) + if (!ev_view_get_page_extents (view, i, &page_area, &border)) continue; page_area.x -= view->scroll_x; @@ -3144,6 +3345,10 @@ ev_view_expose_event (GtkWidget *widget, highlight_find_results (view, i); if (page_ready && EV_IS_DOCUMENT_ANNOTATIONS (view->document)) show_annotation_windows (view, i); + if (page_ready && view->focus_annotation) + focus_annotation (view, i, &event->area); + if (page_ready && view->synctex_result) + highlight_forward_search_results (view, i); } cairo_destroy (cr); @@ -3199,9 +3404,9 @@ get_link_area (EvView *view, EvLink *link, GdkRectangle *area) { - GList *link_mapping; - gint page; - gint x_offset = 0, y_offset = 0; + EvMappingList *link_mapping; + gint page; + gint x_offset = 0, y_offset = 0; x += view->scroll_x; y += view->scroll_y; @@ -3221,9 +3426,9 @@ get_annot_area (EvView *view, EvAnnotation *annot, GdkRectangle *area) { - GList *annot_mapping; - gint page; - gint x_offset = 0, y_offset = 0; + EvMappingList *annot_mapping; + gint page; + gint x_offset = 0, y_offset = 0; x += view->scroll_x; y += view->scroll_y; @@ -3249,14 +3454,18 @@ ev_view_query_tooltip (GtkWidget *widget, gchar *text; annot = ev_view_get_annotation_at_location (view, x, y); - if (annot && annot->contents) { - GdkRectangle annot_area; + if (annot) { + const gchar *contents; - get_annot_area (view, x, y, annot, &annot_area); - gtk_tooltip_set_text (tooltip, annot->contents); - gtk_tooltip_set_tip_area (tooltip, &annot_area); + if ((contents = ev_annotation_get_contents (annot))) { + GdkRectangle annot_area; - return TRUE; + get_annot_area (view, x, y, annot, &annot_area); + gtk_tooltip_set_text (tooltip, contents); + gtk_tooltip_set_tip_area (tooltip, &annot_area); + + return TRUE; + } } link = ev_view_get_link_at_location (view, x, y); @@ -3324,13 +3533,15 @@ ev_view_button_press_event (GtkWidget *widget, window = EV_ANNOTATION_WINDOW (view->window_child_focus->window); annot = ev_annotation_window_get_annotation (window); ev_annotation_window_ungrab_focus (window); - ev_view_annotation_save (view, annot); view->window_child_focus = NULL; } view->pressed_button = event->button; view->selection_info.in_drag = FALSE; + if (view->adding_annot) + return FALSE; + if (view->scroll_info.autoscrolling) return TRUE; @@ -3340,6 +3551,9 @@ ev_view_button_press_event (GtkWidget *widget, EvAnnotation *annot; EvFormField *field; + if (event->state & GDK_CONTROL_MASK) + return ev_view_synctex_backward_search (view, event->x , event->y); + if (EV_IS_SELECTION (view->document) && view->selection_info.selections) { if (event->type == GDK_3BUTTON_PRESS) { start_selection_for_event (view, event); @@ -3368,7 +3582,16 @@ ev_view_button_press_event (GtkWidget *widget, view->image_dnd_info.start.y = event->y + view->scroll_y; } else { ev_view_remove_all (view); - + + if (view->synctex_result) { + g_free (view->synctex_result); + view->synctex_result = NULL; + gtk_widget_queue_draw (widget); + } + + if (view->focus_annotation) + view->focus_annotation = NULL; + if (EV_IS_SELECTION (view->document)) start_selection_for_event (view, event); } @@ -3473,7 +3696,7 @@ ev_view_drag_motion (GtkWidget *widget, if (gtk_drag_get_source_widget (context) == widget) gdk_drag_status (context, 0, time); else - gdk_drag_status (context, context->suggested_action, time); + gdk_drag_status (context, gdk_drag_context_get_suggested_action (context), time); return TRUE; } @@ -3773,7 +3996,12 @@ ev_view_button_release_event (GtkWidget *widget, view->pressed_button = -1; return TRUE; - } + } + + if (view->pressed_button == 1 && event->state & GDK_CONTROL_MASK) { + view->pressed_button = -1; + return TRUE; + } if (view->drag_info.in_drag) { view->drag_info.release_timeout_id = @@ -3787,6 +4015,19 @@ ev_view_button_release_event (GtkWidget *widget, view->drag_info.in_drag = FALSE; + if (view->adding_annot && view->pressed_button == 1) { + view->adding_annot = FALSE; + ev_view_handle_cursor_over_xy (view, event->x, event->y); + view->pressed_button = -1; + + ev_view_create_annotation (view, + view->adding_annot_type, + event->x + view->scroll_x, + event->y + view->scroll_y); + + return FALSE; + } + if (view->pressed_button == 2) { ev_view_handle_cursor_over_xy (view, event->x, event->y); } @@ -3986,6 +4227,53 @@ highlight_find_results (EvView *view, int page) } } +static void +highlight_forward_search_results (EvView *view, int page) +{ + GdkWindow *bin_window; + GdkRectangle rect; + cairo_t *cr; + EvMapping *mapping = view->synctex_result; + + if (GPOINTER_TO_INT (mapping->data) != page) + return; + + bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (view)); + doc_rect_to_view_rect (view, page, &mapping->area, &rect); + + cr = gdk_cairo_create (bin_window); + cairo_set_source_rgb (cr, 1., 0., 0.); + cairo_rectangle (cr, + rect.x - view->scroll_x, + rect.y - view->scroll_y, + rect.width, rect.height); + cairo_stroke (cr); + cairo_destroy (cr); +} + +static void +focus_annotation (EvView *view, + gint page, + GdkRectangle *clip) +{ + GtkWidget *widget = GTK_WIDGET (view); + GdkRectangle rect; + EvMapping *mapping = view->focus_annotation; + EvAnnotation *annot = (EvAnnotation *)mapping->data; + + if (ev_annotation_get_page_index (annot) != page) + return; + + doc_rect_to_view_rect (view, page, &mapping->area, &rect); + gtk_paint_focus (gtk_widget_get_style (widget), + gtk_layout_get_bin_window (GTK_LAYOUT (view)), + gtk_widget_get_state (widget), + NULL, widget, NULL, + rect.x - view->scroll_x, + rect.y - view->scroll_y, + rect.width + 1, rect.height + 1); +} + static void ev_view_loading_window_move (EvView *view) { @@ -4093,7 +4381,7 @@ draw_one_page (EvView *view, bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (view)); current_page = ev_document_model_get_page (view->model); inverted_colors = ev_document_model_get_inverted_colors (view->model); - ev_document_misc_paint_one_page (bin_window, + ev_document_misc_paint_one_page (cr, GTK_WIDGET (view), page_area, border, page == current_page, @@ -4174,6 +4462,7 @@ draw_one_page (EvView *view, 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); @@ -4190,6 +4479,11 @@ ev_view_finalize (GObject *object) clear_selection (view); clear_link_selected (view); + if (view->synctex_result) { + g_free (view->synctex_result); + view->synctex_result = NULL; + } + if (view->image_dnd_info.image) g_object_unref (view->image_dnd_info.image); view->image_dnd_info.image = NULL; @@ -4281,10 +4575,12 @@ ev_view_get_accessible (GtkWidget *widget) factory = atk_registry_get_factory (registry, derived_type); derived_atk_type = atk_object_factory_get_accessible_type (factory); - if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE)) - atk_registry_set_factory_type (registry, + if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE)) { + atk_registry_set_factory_type (registry, EV_TYPE_VIEW, ev_view_accessible_factory_get_type ()); + EV_VIEW (widget)->a11y_enabled = TRUE; + } first_time = FALSE; } return GTK_WIDGET_CLASS (ev_view_parent_class)->get_accessible (widget); @@ -4368,6 +4664,22 @@ ev_view_class_init (EvViewClass *class) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); + signals[SIGNAL_SYNC_SOURCE] = g_signal_new ("sync-source", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EvViewClass, sync_source), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + signals[SIGNAL_ANNOT_ADDED] = g_signal_new ("annot-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EvViewClass, annot_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + EV_TYPE_ANNOTATION); binding_set = gtk_binding_set_by_class (class); @@ -4440,9 +4752,9 @@ ev_view_change_page (EvView *view, } static void -job_finished_cb (EvPixbufCache *pixbuf_cache, - GdkRegion *region, - EvView *view) +job_finished_cb (EvPixbufCache *pixbuf_cache, + cairo_region_t *region, + EvView *view) { if (region) { GdkWindow *bin_window; @@ -4554,8 +4866,16 @@ setup_caches (EvView *view) gboolean inverted_colors; view->height_to_page_cache = ev_view_get_height_to_page_cache (view); - view->pixbuf_cache = ev_pixbuf_cache_new (GTK_WIDGET (view), view->document); + view->pixbuf_cache = ev_pixbuf_cache_new (GTK_WIDGET (view), view->model, view->pixbuf_cache_size); view->page_cache = ev_page_cache_new (view->document); + if (view->a11y_enabled) { + EvJobPageDataFlags flags = ev_page_cache_get_flags (view->page_cache); + + ev_page_cache_set_flags (view->page_cache, + flags | + EV_PAGE_DATA_INCLUDE_TEXT_LAYOUT | + EV_PAGE_DATA_INCLUDE_TEXT); + } inverted_colors = ev_document_model_get_inverted_colors (view->model); ev_pixbuf_cache_set_inverted_colors (view->pixbuf_cache, inverted_colors); g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view); @@ -4575,6 +4895,31 @@ clear_caches (EvView *view) } } +/** + * ev_view_set_page_cache_size: + * @view: + * @cache_size: + * + * Sets the maximum size in bytes that will be used to cache + * rendered pages. Use 0 to disable caching rendered pages. + * + * Note that this limit doesn't affect the current visible page range, + * which will always be rendered. In order to limit the total memory used + * you have to use ev_document_model_set_max_scale() too. + * + */ +void +ev_view_set_page_cache_size (EvView *view, + gsize cache_size) +{ + if (view->pixbuf_cache_size == cache_size) + return; + + view->pixbuf_cache_size = cache_size; + if (view->pixbuf_cache) + ev_pixbuf_cache_set_max_size (view->pixbuf_cache, cache_size); +} + void ev_view_set_loading (EvView *view, gboolean loading) @@ -4859,9 +5204,9 @@ ev_view_set_model (EvView *view, } static void -ev_view_reload_page (EvView *view, - gint page, - GdkRegion *region) +ev_view_reload_page (EvView *view, + gint page, + cairo_region_t *region) { ev_pixbuf_cache_reload_page (view->pixbuf_cache, region, @@ -5255,6 +5600,34 @@ ev_view_find_cancel (EvView *view) view->find_pages = NULL; } +/*** Synctex ***/ +void +ev_view_highlight_forward_search (EvView *view, + EvSourceLink *link) +{ + EvMapping *mapping; + gint page; + GdkRectangle view_rect; + + if (!ev_document_has_synctex (view->document)) + return; + + mapping = ev_document_synctex_forward_search (view->document, link); + if (!mapping) + return; + + if (view->synctex_result) + g_free (view->synctex_result); + view->synctex_result = mapping; + + page = GPOINTER_TO_INT (mapping->data); + ev_document_model_set_page (view->model, page); + + doc_rect_to_view_rect (view, page, &mapping->area, &view_rect); + ensure_rectangle_is_visible (view, &view_rect); + gtk_widget_queue_draw (GTK_WIDGET (view)); +} + /*** Selections ***/ /* compute_new_selection_rect/text calculates the area currently selected by @@ -5282,7 +5655,7 @@ compute_new_selection_rect (EvView *view, GdkRectangle page_area; GtkBorder border; - if (get_page_extents (view, i, &page_area, &border)) { + if (ev_view_get_page_extents (view, i, &page_area, &border)) { GdkRectangle overlap; if (gdk_rectangle_intersect (&page_area, &view_rect, &overlap)) { @@ -5346,7 +5719,7 @@ compute_new_selection_text (EvView *view, GdkRectangle page_area; GtkBorder border; - get_page_extents (view, i, &page_area, &border); + ev_view_get_page_extents (view, i, &page_area, &border); if (gdk_rectangle_point_in (&page_area, start) || gdk_rectangle_point_in (&page_area, stop)) { if (first == n_pages) @@ -5373,7 +5746,7 @@ compute_new_selection_text (EvView *view, selection->rect.x2 = width; selection->rect.y2 = height; - get_page_extents (view, i, &page_area, &border); + ev_view_get_page_extents (view, i, &page_area, &border); if (gdk_rectangle_point_in (&page_area, start)) point = start; @@ -5426,7 +5799,7 @@ merge_selection_region (EvView *view, while (new_list_ptr || old_list_ptr) { EvViewSelection *old_sel, *new_sel; int cur_page; - GdkRegion *region = NULL; + cairo_region_t *region = NULL; new_sel = (new_list_ptr) ? (new_list_ptr->data) : NULL; old_sel = (old_list_ptr) ? (old_list_ptr->data) : NULL; @@ -5461,15 +5834,15 @@ merge_selection_region (EvView *view, /* seed the cache with a new page. We are going to need the new * region too. */ if (new_sel) { - GdkRegion *tmp_region = NULL; + cairo_region_t *tmp_region = NULL; ev_pixbuf_cache_get_selection_surface (view->pixbuf_cache, cur_page, view->scale, &tmp_region); - if (tmp_region && !gdk_region_empty (tmp_region)) { - new_sel->covered_region = gdk_region_copy (tmp_region); + if (tmp_region && !cairo_region_is_empty (tmp_region)) { + new_sel->covered_region = cairo_region_reference (tmp_region); } } @@ -5479,24 +5852,52 @@ merge_selection_region (EvView *view, /* We only want to redraw the areas that have * changed, so we xor the old and new regions * and redraw if it's different */ - region = gdk_region_copy (old_sel->covered_region); - gdk_region_xor (region, new_sel->covered_region); - if (gdk_region_empty (region)) { - gdk_region_destroy (region); + region = cairo_region_copy (old_sel->covered_region); +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 9, 12) + cairo_region_xor (region, new_sel->covered_region); +#else + cairo_region_t *tbr; + tbr = cairo_region_copy (new_sel->covered_region); + + /* xor old_sel, new_sel*/ + cairo_region_subtract (tbr, region); + cairo_region_subtract (region, new_sel->covered_region); + cairo_region_union (region, tbr); + cairo_region_destroy (tbr); +#endif + + if (cairo_region_is_empty (region)) { + cairo_region_destroy (region); region = NULL; + } else { + gint num_rectangles = cairo_region_num_rectangles (region); + GdkRectangle r; + + /* We need to make the damage region a little bigger + * because the edges of the old selection might change + */ + cairo_region_get_rectangle (region, 0, &r); + r.x -= 5; + r.width = 5; + cairo_region_union_rectangle (region, &r); + + cairo_region_get_rectangle (region, num_rectangles - 1, &r); + r.x += r.width; + r.width = 5; + cairo_region_union_rectangle (region, &r); } } else if (old_sel->covered_region) { - region = gdk_region_copy (old_sel->covered_region); + region = cairo_region_copy (old_sel->covered_region); } else if (new_sel->covered_region) { - region = gdk_region_copy (new_sel->covered_region); + region = cairo_region_copy (new_sel->covered_region); } } else if (old_sel && !new_sel) { - if (old_sel->covered_region && !gdk_region_empty (old_sel->covered_region)) { - region = gdk_region_copy (old_sel->covered_region); + if (old_sel->covered_region && !cairo_region_is_empty (old_sel->covered_region)) { + region = cairo_region_copy (old_sel->covered_region); } } else if (!old_sel && new_sel) { - if (new_sel->covered_region && !gdk_region_empty (new_sel->covered_region)) { - region = gdk_region_copy (new_sel->covered_region); + if (new_sel->covered_region && !cairo_region_is_empty (new_sel->covered_region)) { + region = cairo_region_copy (new_sel->covered_region); } } else { g_assert_not_reached (); @@ -5509,19 +5910,12 @@ merge_selection_region (EvView *view, GtkBorder border; bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (view)); - - /* 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, + ev_view_get_page_extents (view, cur_page, &page_area, &border); + cairo_region_translate (region, page_area.x + border.left - view->scroll_x, page_area.y + border.top - view->scroll_y); gdk_window_invalidate_region (bin_window, region, TRUE); - gdk_region_destroy (region); + cairo_region_destroy (region); } } @@ -5551,7 +5945,7 @@ static void selection_free (EvViewSelection *selection) { if (selection->covered_region) - gdk_region_destroy (selection->covered_region); + cairo_region_destroy (selection->covered_region); g_free (selection); }