X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;f=shell%2Fev-view.c;h=15bd41244edcf10215eae5d720374955b0372632;hb=85c366bda9f25b1249ba8333604eb3e757bc6edf;hp=9164267203113fedf353f8cbea1cec23637dd430;hpb=695641059974320c2ae485b81710a750686cff6a;p=evince.git diff --git a/shell/ev-view.c b/shell/ev-view.c index 91642672..15bd4124 100644 --- a/shell/ev-view.c +++ b/shell/ev-view.c @@ -29,6 +29,7 @@ #include "ev-marshal.h" #include "ev-view.h" +#include "ev-utils.h" #include "ev-selection.h" #include "ev-document-find.h" #include "ev-document-misc.h" @@ -36,11 +37,13 @@ #include "ev-job-queue.h" #include "ev-page-cache.h" #include "ev-pixbuf-cache.h" +#include "ev-tooltip.h" #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, @@ -51,11 +54,13 @@ enum { PROP_PRESENTATION, PROP_SIZING_MODE, PROP_ZOOM, + PROP_ROTATION, }; enum { SIGNAL_BINDING_ACTIVATED, SIGNAL_ZOOM_INVALID, + SIGNAL_EXTERNAL_LINK, N_SIGNALS, }; @@ -78,12 +83,18 @@ static guint signals[N_SIGNALS]; typedef enum { EV_VIEW_CURSOR_NORMAL, + EV_VIEW_CURSOR_IBEAM, EV_VIEW_CURSOR_LINK, EV_VIEW_CURSOR_WAIT, EV_VIEW_CURSOR_HIDDEN, EV_VIEW_CURSOR_DRAG } EvViewCursor; +typedef enum { + EV_VIEW_FIND_NEXT, + EV_VIEW_FIND_PREV +} EvViewFindDirection; + #define ZOOM_IN_FACTOR 1.2 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR) @@ -119,6 +130,10 @@ struct _EvView { char *status; char *find_status; + /* Scrolling */ + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + gint scroll_x; gint scroll_y; @@ -126,14 +141,17 @@ struct _EvView { DragInfo drag_info; /* Selection */ + gint motion_x; + gint motion_y; + guint selection_update_id; + EvViewSelectionMode selection_mode; SelectionInfo selection_info; - gboolean pressed_button; + int pressed_button; EvViewCursor cursor; - - GtkAdjustment *hadjustment; - GtkAdjustment *vadjustment; + GtkWidget *link_tooltip; + EvLink *hovered_link; EvPageCache *page_cache; EvPixbufCache *pixbuf_cache; @@ -148,6 +166,7 @@ struct _EvView { int find_result; int spacing; + int rotation; double scale; gboolean continuous; @@ -170,6 +189,8 @@ struct _EvViewClass { GtkScrollType scroll, gboolean horizontal); void (*zoom_invalid) (EvView *view); + void (*external_link) (EvView *view, + EvLink *link); }; /*** Scrolling ***/ @@ -227,11 +248,11 @@ static void find_page_at_location (EvView static EvLink* get_link_at_location (EvView *view, gdouble x, gdouble y); -static void go_to_link (EvView *view, - EvLink *link); -static char* status_message_from_link (EvView *view, +static char* tip_from_link (EvView *view, EvLink *link); - +static void handle_link_over_xy (EvView *view, + gint x, + gint y); /*** GtkWidget implementation ***/ static void ev_view_size_request_continuous_dual_page (EvView *view, GtkRequisition *requisition); @@ -246,7 +267,6 @@ static void ev_view_size_request (GtkWidget static void ev_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static void ev_view_realize (GtkWidget *widget); -static void ev_view_unrealize (GtkWidget *widget); static gboolean ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event); static gboolean ev_view_expose_event (GtkWidget *widget, @@ -257,6 +277,12 @@ static gboolean ev_view_motion_notify_event (GtkWidget GdkEventMotion *event); static gboolean ev_view_button_release_event (GtkWidget *widget, GdkEventButton *event); +static gboolean ev_view_enter_notify_event (GtkWidget *widget, + GdkEventCrossing *event); +static gboolean ev_view_leave_notify_event (GtkWidget *widget, + GdkEventCrossing *event); +static void ev_view_style_set (GtkWidget *widget, + GtkStyle *old_style); /*** Drawing ***/ static guint32 ev_gdk_color_to_rgb (const GdkColor *color); @@ -304,6 +330,11 @@ static double zoom_for_size_fit_width (int doc_width, int target_width, int target_height, int vsb_width); +static double zoom_for_size_fit_height (int doc_width, + int doc_height, + int target_width, + int target_height, + int vsb_height); static double zoom_for_size_best_fit (int doc_width, int doc_height, int target_width, @@ -346,13 +377,15 @@ static void ev_view_set_find_status (EvView const char *message); /*** Find ***/ static void jump_to_find_result (EvView *view); -static void jump_to_find_page (EvView *view); +static void jump_to_find_page (EvView *view, + EvViewFindDirection direction); /*** Selection ***/ static void compute_selections (EvView *view, GdkPoint *start, GdkPoint *stop); static void clear_selection (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, GtkSelectionData *selection_data, @@ -371,6 +404,10 @@ scroll_to_current_page (EvView *view, GtkOrientation orientation) GdkRectangle page_area; GtkBorder border; + if (view->document == NULL) { + return; + } + get_page_extents (view, view->current_page, &page_area, &border); if (orientation == GTK_ORIENTATION_VERTICAL) { @@ -464,6 +501,9 @@ view_set_adjustment_values (EvView *view, static void view_update_range_and_current_page (EvView *view) { + if (view->pending_scroll != SCROLL_TO_KEEP_POSITION) + return; + /* Presentation trumps all other modes */ if (view->presentation) { view->start_page = view->current_page; @@ -511,6 +551,8 @@ view_update_range_and_current_page (EvView *view) view->start_page = view->current_page; if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache)) view->end_page = view->start_page + 1; + else + view->end_page = view->start_page; } else { view->start_page = view->current_page - 1; view->end_page = view->current_page; @@ -524,6 +566,7 @@ 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); } @@ -653,10 +696,10 @@ ev_view_binding_activated (EvView *view, if (view->presentation) { switch (scroll) { case GTK_SCROLL_STEP_BACKWARD: - ev_page_cache_prev_page (view->page_cache); + ev_view_previous_page (view); break; case GTK_SCROLL_STEP_FORWARD: - ev_page_cache_next_page (view->page_cache); + ev_view_next_page (view); break; default: break; @@ -698,6 +741,8 @@ ensure_rectangle_is_visible (EvView *view, GdkRectangle *rect) GtkAdjustment *adjustment; int value; + view->pending_scroll = SCROLL_TO_KEEP_POSITION; + adjustment = view->vadjustment; if (rect->y < adjustment->value) { @@ -738,25 +783,25 @@ compute_border (EvView *view, int width, int height, GtkBorder *border) } } -static void get_page_y_offset (EvView *view, - int page, - double zoom, - int *y_offset) +static void +get_page_y_offset (EvView *view, int page, double zoom, int *y_offset) { int max_width, offset; GtkBorder border; g_return_if_fail (y_offset != NULL); - ev_page_cache_get_max_width (view->page_cache, zoom, &max_width); + ev_page_cache_get_max_width (view->page_cache, view->rotation, zoom, &max_width); compute_border (view, max_width, max_width, &border); if (view->dual_page) { - ev_page_cache_get_height_to_page (view->page_cache, page, zoom, NULL, &offset); + ev_page_cache_get_height_to_page (view->page_cache, page, + view->rotation, zoom, NULL, &offset); offset += (page / 2 + 1) * view->spacing + (page / 2) * (border.top + border.bottom); } else { - ev_page_cache_get_height_to_page (view->page_cache, page, zoom, &offset, NULL); + ev_page_cache_get_height_to_page (view->page_cache, page, + view->rotation, zoom, &offset, NULL); offset += (page + 1) * view->spacing + page * (border.top + border.bottom); } @@ -777,6 +822,7 @@ get_page_extents (EvView *view, /* Get the size of the page */ ev_page_cache_get_size (view->page_cache, page, + view->rotation, view->scale, &width, &height); compute_border (view, width, height, border); @@ -790,7 +836,8 @@ get_page_extents (EvView *view, gint max_width; gint x, y; - ev_page_cache_get_max_width (view->page_cache, view->scale, &max_width); + ev_page_cache_get_max_width (view->page_cache, view->rotation, + view->scale, &max_width); max_width = max_width + border->left + border->right; /* Get the location of the bounding box */ if (view->dual_page) { @@ -822,6 +869,7 @@ get_page_extents (EvView *view, if (other_page < ev_page_cache_get_n_pages (view->page_cache)) { ev_page_cache_get_size (view->page_cache, other_page, + view->rotation, view->scale, &width_2, &height_2); if (width_2 > width) @@ -886,6 +934,44 @@ view_rect_to_doc_rect (EvView *view, doc_rect->y2 = doc_rect->y1 + (double) view_rect->height / view->scale; } +static void +doc_point_to_view_point (EvView *view, + int page, + EvPoint *doc_point, + GdkPoint *view_point) +{ + GdkRectangle page_area; + GtkBorder border; + double x, y; + int width, height; + + ev_page_cache_get_size (view->page_cache, page, + view->rotation, + 1.0, + &width, &height); + + if (view->rotation == 0) { + x = doc_point->x; + y = doc_point->y; + } else if (view->rotation == 90) { + x = width - doc_point->y; + y = doc_point->x; + } else if (view->rotation == 180) { + x = width - doc_point->x; + y = height - doc_point->y; + } else if (view->rotation == 270) { + x = doc_point->y; + y = height - doc_point->x; + } else { + g_assert_not_reached (); + } + + get_page_extents (view, page, &page_area, &border); + + view_point->x = x * view->scale + page_area.x; + view_point->y = y * view->scale + page_area.y; +} + static void doc_rect_to_view_rect (EvView *view, int page, @@ -894,16 +980,44 @@ doc_rect_to_view_rect (EvView *view, { GdkRectangle page_area; GtkBorder border; + double x, y, w, h; int width, height; + ev_page_cache_get_size (view->page_cache, page, + view->rotation, + 1.0, + &width, &height); + + if (view->rotation == 0) { + x = doc_rect->x1; + y = doc_rect->y1; + w = doc_rect->x2 - doc_rect->x1; + h = doc_rect->y2 - doc_rect->y1; + } else if (view->rotation == 90) { + x = width - doc_rect->y2; + y = doc_rect->x1; + w = doc_rect->y2 - doc_rect->y1; + h = doc_rect->x2 - doc_rect->x1; + } else if (view->rotation == 180) { + x = width - doc_rect->x2; + y = height - doc_rect->y2; + w = doc_rect->x2 - doc_rect->x1; + h = doc_rect->y2 - doc_rect->y1; + } else if (view->rotation == 270) { + x = doc_rect->y1; + y = height - doc_rect->x2; + w = doc_rect->y2 - doc_rect->y1; + h = doc_rect->x2 - doc_rect->x1; + } else { + g_assert_not_reached (); + } + get_page_extents (view, page, &page_area, &border); - width = doc_rect->x2 - doc_rect->x1; - height = doc_rect->y2 - doc_rect->y1; - view_rect->x = floor (doc_rect->x1 * view->scale) + page_area.x; - view_rect->y = floor (doc_rect->y1 * view->scale) + page_area.y; - view_rect->width = ceil (width * view->scale); - view_rect->height = ceil (height * view->scale); + view_rect->x = x * view->scale + page_area.x; + view_rect->y = y * view->scale + page_area.y; + view_rect->width = w * view->scale; + view_rect->height = h * view->scale; } static void @@ -944,6 +1058,40 @@ find_page_at_location (EvView *view, *page = -1; } +static gboolean +location_in_text (EvView *view, + gdouble x, + gdouble y) +{ + GdkRegion *region; + gint page = -1; + gint x_offset = 0, y_offset = 0; + + find_page_at_location (view, x, y, &page, &x_offset, &y_offset); + + if (page == -1) + return FALSE; + + region = ev_pixbuf_cache_get_text_mapping (view->pixbuf_cache, page); + + if (region) + return gdk_region_point_in (region, x_offset / view->scale, y_offset / view->scale); + else + return FALSE; +} + +static int +ev_view_get_width (EvView *view) +{ + return GTK_WIDGET (view)->allocation.width; +} + +static int +ev_view_get_height (EvView *view) +{ + return GTK_WIDGET (view)->allocation.height; +} + /*** Hyperref ***/ static EvLink * get_link_at_location (EvView *view, @@ -967,12 +1115,128 @@ get_link_at_location (EvView *view, return NULL; } -/* FIXME: standardize this sometime */ static void -go_to_link (EvView *view, EvLink *link) +goto_fitr_link (EvView *view, EvLink *link) +{ + GdkPoint view_point; + EvPoint doc_point; + int doc_width, doc_height, page; + double zoom; + + page = ev_link_get_page (link); + ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height); + + doc_point.x = ev_link_get_left (link); + doc_point.y = ev_link_get_top (link); + doc_point_to_view_point (view, page, &doc_point, &view_point); + + zoom = zoom_for_size_best_fit (ev_link_get_right (link) - ev_link_get_left (link), + ev_link_get_top (link) - ev_link_get_bottom (link), + 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); + ev_page_cache_set_current_page (view->page_cache, page); + gtk_adjustment_set_value (view->hadjustment, view_point.x); +} + +static void +goto_fitv_link (EvView *view, EvLink *link) +{ + GdkPoint view_point; + EvPoint doc_point; + int doc_width, doc_height, page; + double zoom; + + page = ev_link_get_page (link); + ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height); + + doc_point.x = ev_link_get_left (link); + doc_point.y = 0; + doc_point_to_view_point (view, page, &doc_point, &view_point); + + zoom = zoom_for_size_fit_height (doc_width - doc_point.x , doc_height, + ev_view_get_width (view), + ev_view_get_height (view), 0); + + ev_view_set_sizing_mode (view, EV_SIZING_FREE); + ev_view_set_zoom (view, zoom, FALSE); + ev_page_cache_set_current_page (view->page_cache, page); + gtk_adjustment_set_value (view->hadjustment, view_point.x); +} + +static void +goto_fith_link (EvView *view, EvLink *link) +{ + GdkPoint view_point; + EvPoint doc_point; + int doc_width, doc_height, page; + double zoom; + + page = ev_link_get_page (link); + ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height); + + doc_point.x = 0; + doc_point.y = doc_height - ev_link_get_top (link); + doc_point_to_view_point (view, page, &doc_point, &view_point); + + zoom = zoom_for_size_fit_width (doc_width, ev_link_get_top (link), + ev_view_get_width (view), + ev_view_get_height (view), 0); + + ev_view_set_sizing_mode (view, EV_SIZING_FREE); + ev_view_set_zoom (view, zoom, FALSE); + gtk_adjustment_set_value (view->vadjustment, view_point.y); +} + +static void +goto_fit_link (EvView *view, EvLink *link) +{ + double zoom; + int doc_width, doc_height; + int page; + + page = ev_link_get_page (link); + ev_page_cache_get_size (view->page_cache, page, 0, 1.0, &doc_width, &doc_height); + + zoom = zoom_for_size_best_fit (doc_width, doc_height, 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); + ev_page_cache_set_current_page (view->page_cache, page); +} + +static void +goto_xyz_link (EvView *view, EvLink *link) +{ + GdkPoint view_point; + EvPoint doc_point; + int height, page; + double zoom; + + zoom = ev_link_get_zoom (link); + page = ev_link_get_page (link); + ev_page_cache_get_size (view->page_cache, page, 0, 1.0, NULL, &height); + + ev_view_set_sizing_mode (view, EV_SIZING_FREE); + if (zoom != 0) { + ev_view_set_zoom (view, zoom, FALSE); + } + + doc_point.x = ev_link_get_left (link); + doc_point.y = height - ev_link_get_top (link); + doc_point_to_view_point (view, page, &doc_point, &view_point); + + gtk_adjustment_set_value (view->hadjustment, view_point.x); + gtk_adjustment_set_value (view->vadjustment, view_point.y); +} + +void +ev_view_goto_link (EvView *view, EvLink *link) { EvLinkType type; - const char *uri; int page; type = ev_link_get_link_type (link); @@ -984,15 +1248,30 @@ go_to_link (EvView *view, EvLink *link) page = ev_link_get_page (link); ev_page_cache_set_current_page (view->page_cache, page); break; + case EV_LINK_TYPE_PAGE_FIT: + goto_fit_link (view, link); + break; + case EV_LINK_TYPE_PAGE_FITH: + goto_fith_link (view, link); + break; + case EV_LINK_TYPE_PAGE_FITV: + goto_fitv_link (view, link); + break; + case EV_LINK_TYPE_PAGE_FITR: + goto_fitr_link (view, link); + break; + case EV_LINK_TYPE_PAGE_XYZ: + goto_xyz_link (view, link); + break; case EV_LINK_TYPE_EXTERNAL_URI: - uri = ev_link_get_uri (link); - gnome_vfs_url_show (uri); + case EV_LINK_TYPE_LAUNCH: + g_signal_emit (view, signals[SIGNAL_EXTERNAL_LINK], 0, link); break; } } static char * -status_message_from_link (EvView *view, EvLink *link) +tip_from_link (EvView *view, EvLink *link) { EvLinkType type; char *msg = NULL; @@ -1006,6 +1285,7 @@ status_message_from_link (EvView *view, EvLink *link) msg = g_strdup (ev_link_get_title (link)); break; case EV_LINK_TYPE_PAGE: + case EV_LINK_TYPE_PAGE_XYZ: page_label = ev_page_cache_get_page_label (view->page_cache, ev_link_get_page (link)); msg = g_strdup_printf (_("Go to page %s"), page_label); g_free (page_label); @@ -1020,6 +1300,41 @@ status_message_from_link (EvView *view, EvLink *link) return msg; } +static void +handle_link_over_xy (EvView *view, gint x, gint y) +{ + EvLink *link; + + link = get_link_at_location (view, x + view->scroll_x, y + view->scroll_y); + + if (view->link_tooltip == NULL) { + view->link_tooltip = ev_tooltip_new (GTK_WIDGET (view)); + } + + if (view->hovered_link != link) { + view->hovered_link = link; + ev_tooltip_deactivate (EV_TOOLTIP (view->link_tooltip)); + } + + if (link) { + char *msg = tip_from_link (view, link); + + ev_tooltip_set_position (EV_TOOLTIP (view->link_tooltip), x, y); + ev_tooltip_set_text (EV_TOOLTIP (view->link_tooltip), msg); + ev_tooltip_activate (EV_TOOLTIP (view->link_tooltip)); + g_free (msg); + + 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) + ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); + } + return; +} /*** GtkWidget implementation ***/ @@ -1031,7 +1346,8 @@ ev_view_size_request_continuous_dual_page (EvView *view, gint n_pages; GtkBorder border; - ev_page_cache_get_max_width (view->page_cache, view->scale, &max_width); + ev_page_cache_get_max_width (view->page_cache, view->rotation, + view->scale, &max_width); compute_border (view, max_width, max_width, &border); n_pages = ev_page_cache_get_n_pages (view->page_cache) + 1; @@ -1058,7 +1374,8 @@ ev_view_size_request_continuous (EvView *view, GtkBorder border; - ev_page_cache_get_max_width (view->page_cache, view->scale, &max_width); + ev_page_cache_get_max_width (view->page_cache, view->rotation, + view->scale, &max_width); n_pages = ev_page_cache_get_n_pages (view->page_cache); compute_border (view, max_width, max_width, &border); @@ -1085,12 +1402,14 @@ ev_view_size_request_dual_page (EvView *view, /* Find the largest of the two. */ ev_page_cache_get_size (view->page_cache, view->current_page, + view->rotation, view->scale, &width, &height); if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache)) { gint width_2, height_2; ev_page_cache_get_size (view->page_cache, view->current_page + 1, + view->rotation, view->scale, &width_2, &height_2); if (width_2 > width) { @@ -1122,6 +1441,7 @@ ev_view_size_request_single_page (EvView *view, ev_page_cache_get_size (view->page_cache, view->current_page, + view->rotation, view->scale, &width, &height); compute_border (view, width, height, &border); @@ -1173,7 +1493,7 @@ ev_view_size_allocate (GtkWidget *widget, EvView *view = EV_VIEW (widget); if (view->sizing_mode == EV_SIZING_FIT_WIDTH || - view->sizing_mode == EV_SIZING_BEST_FIT) { + view->sizing_mode == EV_SIZING_BEST_FIT) { g_signal_emit (view, signals[SIGNAL_ZOOM_INVALID], 0); @@ -1216,6 +1536,7 @@ ev_view_realize (GtkWidget *widget) GDK_SCROLL_MASK | GDK_KEY_PRESS_MASK | GDK_POINTER_MOTION_MASK | + GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK; widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), @@ -1232,39 +1553,59 @@ ev_view_realize (GtkWidget *widget) gdk_window_set_background (widget->window, &widget->style->mid [GTK_STATE_NORMAL]); } -static void -ev_view_unrealize (GtkWidget *widget) -{ - GTK_WIDGET_CLASS (ev_view_parent_class)->unrealize (widget); -} - static gboolean ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event) { - EvView *view = EV_VIEW (widget); + EvView *view = EV_VIEW (widget); + guint state; - if ((event->state & GDK_CONTROL_MASK) != 0) { + state = event->state & gtk_accelerator_get_default_mod_mask (); - ev_view_set_sizing_mode (view, EV_SIZING_FREE); + if (state == GDK_CONTROL_MASK && view->presentation == FALSE) { + ev_view_set_sizing_mode (view, EV_SIZING_FREE); + + if (event->direction == GDK_SCROLL_UP || + event->direction == GDK_SCROLL_LEFT) { + if (ev_view_can_zoom_in (view)) { + ev_view_zoom_in (view); + } + } else { + if (ev_view_can_zoom_out (view)) { + ev_view_zoom_out (view); + } + } - if (event->direction == GDK_SCROLL_UP || - event->direction == GDK_SCROLL_LEFT) { - if (ev_view_can_zoom_in (view)) { - ev_view_zoom_in (view); - } - } else { - if (ev_view_can_zoom_out (view)) { - ev_view_zoom_out (view); - } - } - return TRUE; + return TRUE; } - if ((event->state & GDK_SHIFT_MASK) != 0) { + /* 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) + event->direction = GDK_SCROLL_UP; if (event->direction == GDK_SCROLL_DOWN) event->direction = GDK_SCROLL_RIGHT; + if (event->direction == GDK_SCROLL_RIGHT) + event->direction = GDK_SCROLL_DOWN; + + event->state &= ~GDK_SHIFT_MASK; + state &= ~GDK_SHIFT_MASK; + } + + if (state == 0 && view->presentation) { + switch (event->direction) { + case GDK_SCROLL_DOWN: + case GDK_SCROLL_RIGHT: + ev_view_next_page (view); + break; + case GDK_SCROLL_UP: + case GDK_SCROLL_LEFT: + ev_view_previous_page (view); + break; + } + + return TRUE; } return FALSE; @@ -1355,6 +1696,19 @@ ev_view_button_press_event (GtkWidget *widget, return FALSE; } + +static gboolean +selection_update_idle_cb (EvView *view) +{ + GdkPoint point; + point.x = view->motion_x; + point.y = view->motion_y; + compute_selections (view, &view->selection_info.start, &point); + + view->selection_update_id = 0; + return FALSE; +} + static gboolean ev_view_motion_notify_event (GtkWidget *widget, GdkEventMotion *event) @@ -1364,13 +1718,22 @@ ev_view_motion_notify_event (GtkWidget *widget, if (!view->document) return FALSE; - if (view->pressed_button == 1) { - GdkPoint point; - + /* For the Evince 0.4.x release, we limit selection to un-rotated + * documents only. + */ + if (view->pressed_button == 1 && + view->rotation == 0) { view->selection_info.in_selection = TRUE; - point.x = event->x + view->scroll_x; - point.y = event->y + view->scroll_y; - compute_selections (view, &view->selection_info.start, &point); + view->motion_x = event->x + view->scroll_x; + view->motion_y = event->y + view->scroll_y; + + /* Queue an idle to handle the motion. We do this because + * handling any selection events in the motion could be slower + * than new motion events reach us. We always put it in the + * idle to make sure we catch up and don't visibly lag the + * mouse. */ + if (! view->selection_update_id) + view->selection_update_id = g_idle_add ((GSourceFunc)selection_update_idle_cb, view); return TRUE; } else if (view->pressed_button == 2) { @@ -1409,23 +1772,12 @@ ev_view_motion_notify_event (GtkWidget *widget, return TRUE; } - } else if (view->pressed_button <= 0) { - EvLink *link; - - link = get_link_at_location (view, event->x + view->scroll_x, event->y + view->scroll_y); - if (link) { - char *msg; - - msg = status_message_from_link (view, link); - ev_view_set_status (view, msg); - ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK); - g_free (msg); - } else { - ev_view_set_status (view, NULL); - if (view->cursor == EV_VIEW_CURSOR_LINK) { - ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); - } - } + /* For the Evince 0.4.x release, we limit links to un-rotated documents + * only. + */ + } else if (view->pressed_button <= 0 && + view->rotation == 0) { + handle_link_over_xy (view, event->x, event->y); return TRUE; } @@ -1437,28 +1789,102 @@ ev_view_button_release_event (GtkWidget *widget, GdkEventButton *event) { EvView *view = EV_VIEW (widget); + EvLink *link; if (view->pressed_button == 2) { ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); } + if (view->document) { + link = get_link_at_location (view, event->x + view->scroll_x, + event->y + view->scroll_y); + } else { + link = NULL; + } + view->pressed_button = -1; view->drag_info.in_drag = FALSE; if (view->selection_info.selections) { ev_view_update_primary_selection (view); - } else if (view->document) { - EvLink *link; - - link = get_link_at_location (view, event->x + view->scroll_x, event->y + view->scroll_y); - if (link) { - go_to_link (view, link); + } else if (link) { + ev_view_goto_link (view, link); + } else if (view->presentation) { + switch (event->button) { + case 1: + ev_view_next_page (view); + return TRUE; + case 3: + ev_view_previous_page (view); + return TRUE; } } + + return FALSE; +} + +static gint +ev_view_focus_in (GtkWidget *widget, + GdkEventFocus *event) +{ + if (EV_VIEW (widget)->pixbuf_cache) + ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache); + gtk_widget_queue_draw (widget); + + return FALSE; +} + +static gint +ev_view_focus_out (GtkWidget *widget, + GdkEventFocus *event) +{ + if (EV_VIEW (widget)->pixbuf_cache) + ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache); + gtk_widget_queue_draw (widget); + + return FALSE; +} + +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) + ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); + + if (view->link_tooltip) { + view->hovered_link = NULL; + ev_tooltip_deactivate (EV_TOOLTIP (view->link_tooltip)); + } + + return FALSE; +} + +static gboolean +ev_view_enter_notify_event (GtkWidget *widget, GdkEventCrossing *event) +{ + EvView *view = EV_VIEW (widget); + handle_link_over_xy (view, event->x, event->y); + return FALSE; } +static void +ev_view_style_set (GtkWidget *widget, + GtkStyle *old_style) +{ + if (EV_VIEW (widget)->pixbuf_cache) + ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache); + + GTK_WIDGET_CLASS (ev_view_parent_class)->style_set (widget, old_style); +} + + /*** Drawing ***/ static guint32 @@ -1531,14 +1957,58 @@ highlight_find_results (EvView *view, int page) alpha = 0x20; } - ev_document_find_get_result (find, page, - i, &rectangle); + 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, &view_rectangle, alpha); } } +static void +draw_loading_text (EvView *view, + GdkRectangle *page_area, + GdkRectangle *expose_area) +{ + PangoLayout *layout; + PangoFontDescription *font_desc; + PangoRectangle logical_rect; + double real_scale; + int target_width; + + const char *loading_text = _("Loading..."); + + layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), loading_text); + + font_desc = pango_font_description_new (); + + + /* We set the font to be 10 points, get the size, and scale appropriately */ + 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); + pango_layout_set_font_description (layout, font_desc); + pango_layout_get_pixel_extents (layout, NULL, &logical_rect); + + gtk_paint_layout (GTK_WIDGET (view)->style, + GTK_WIDGET (view)->window, + GTK_WIDGET_STATE (view), + FALSE, + page_area, + GTK_WIDGET (view), + NULL, + page_area->x + (target_width/2), + page_area->y + (page_area->height - logical_rect.height) / 2, + layout); + + pango_font_description_free (font_desc); + g_object_unref (layout); +} + static void draw_one_page (EvView *view, gint page, @@ -1558,7 +2028,8 @@ draw_one_page (EvView *view, selection = find_selection_for_page (view, page); ev_page_cache_get_size (view->page_cache, - page, view->scale, + page, view->rotation, + view->scale, &width, &height); /* Render the document itself */ real_page_area = *page_area; @@ -1583,7 +2054,8 @@ draw_one_page (EvView *view, 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); + view->scale, + NULL); if (current_pixbuf == NULL) scaled_image = NULL; @@ -1620,6 +2092,10 @@ draw_one_page (EvView *view, GDK_RGB_DITHER_NORMAL, 0, 0); g_object_unref (scaled_image); + } else { + draw_loading_text (view, + &real_page_area, + expose_area); } if (scaled_selection) { @@ -1663,10 +2139,16 @@ ev_view_destroy (GtkObject *object) g_object_unref (view->document); view->document = NULL; } + if (view->pixbuf_cache) { g_object_unref (view->pixbuf_cache); view->pixbuf_cache = NULL; } + + if (view->link_tooltip) { + gtk_widget_destroy (view->link_tooltip); + } + ev_view_set_scroll_adjustments (view, NULL, NULL); GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object); @@ -1700,6 +2182,9 @@ ev_view_set_property (GObject *object, case PROP_ZOOM: ev_view_set_zoom (view, g_value_get_double (value), FALSE); break; + case PROP_ROTATION: + ev_view_set_rotation (view, g_value_get_int (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -1739,6 +2224,9 @@ ev_view_get_property (GObject *object, case PROP_ZOOM: g_value_set_double (value, view->scale); break; + case PROP_ROTATION: + g_value_set_int (value, view->rotation); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -1760,11 +2248,15 @@ ev_view_class_init (EvViewClass *class) widget_class->button_press_event = ev_view_button_press_event; widget_class->motion_notify_event = ev_view_motion_notify_event; widget_class->button_release_event = ev_view_button_release_event; + widget_class->focus_in_event = ev_view_focus_in; + widget_class->focus_out_event = ev_view_focus_out; widget_class->size_request = ev_view_size_request; widget_class->size_allocate = ev_view_size_allocate; widget_class->realize = ev_view_realize; - widget_class->unrealize = ev_view_unrealize; widget_class->scroll_event = ev_view_scroll_event; + widget_class->enter_notify_event = ev_view_enter_notify_event; + widget_class->leave_notify_event = ev_view_leave_notify_event; + widget_class->style_set = ev_view_style_set; gtk_object_class->destroy = ev_view_destroy; class->set_scroll_adjustments = ev_view_set_scroll_adjustments; @@ -1798,6 +2290,14 @@ ev_view_class_init (EvViewClass *class) NULL, NULL, ev_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); + signals[SIGNAL_EXTERNAL_LINK] = g_signal_new ("external-link", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EvViewClass, external_link), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); g_object_class_install_property (object_class, PROP_STATUS, @@ -1863,6 +2363,15 @@ ev_view_class_init (EvViewClass *class) MAX_SCALE, 1.0, G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_ROTATION, + g_param_spec_double ("rotation", + "Rotation", + "Rotation", + 0, + 360, + 0, + G_PARAM_READWRITE)); binding_set = gtk_binding_set_by_class (class); @@ -1884,7 +2393,6 @@ ev_view_init (EvView *view) view->cursor = EV_VIEW_CURSOR_NORMAL; view->drag_info.in_drag = FALSE; view->selection_info.in_selection = FALSE; - view->selection_mode = EV_VIEW_SELECTION_TEXT; view->continuous = TRUE; view->dual_page = FALSE; @@ -1899,7 +2407,7 @@ ev_view_init (EvView *view) static void find_changed_cb (EvDocument *document, int page, EvView *view) { - jump_to_find_page (view); + jump_to_find_page (view, EV_VIEW_FIND_NEXT); jump_to_find_result (view); update_find_status_message (view); @@ -1981,7 +2489,7 @@ setup_caches (EvView *view) { view->page_cache = ev_page_cache_get (view->document); g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view); - view->pixbuf_cache = ev_pixbuf_cache_new (view->document); + 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); } @@ -1993,8 +2501,8 @@ clear_caches (EvView *view) view->pixbuf_cache = NULL; } - if (view->document) { - ev_page_cache_clear (view->document); + if (view->page_cache) { + view->page_cache = NULL; } } @@ -2070,6 +2578,14 @@ ev_view_get_zoom (EvView *view) return view->scale; } +gboolean +ev_view_get_continuous (EvView *view) +{ + g_return_val_if_fail (EV_IS_VIEW (view), FALSE); + + return view->continuous; +} + void ev_view_set_continuous (EvView *view, gboolean continuous) @@ -2087,6 +2603,14 @@ ev_view_set_continuous (EvView *view, g_object_notify (G_OBJECT (view), "continuous"); } +gboolean +ev_view_get_dual_page (EvView *view) +{ + g_return_val_if_fail (EV_IS_VIEW (view), FALSE); + + return view->dual_page; +} + void ev_view_set_dual_page (EvView *view, gboolean dual_page) @@ -2116,11 +2640,12 @@ ev_view_set_fullscreen (EvView *view, fullscreen = fullscreen != FALSE; - if (view->fullscreen != fullscreen) { - view->fullscreen = fullscreen; - gtk_widget_queue_resize (GTK_WIDGET (view)); - } - + if (view->fullscreen == fullscreen) + return; + + view->fullscreen = fullscreen; + gtk_widget_queue_resize (GTK_WIDGET (view)); + g_object_notify (G_OBJECT (view), "fullscreen"); } @@ -2144,7 +2669,9 @@ ev_view_set_presentation (EvView *view, return; view->presentation = presentation; + view->pending_scroll = SCROLL_TO_CURRENT_PAGE; gtk_widget_queue_resize (GTK_WIDGET (view)); + if (GTK_WIDGET_REALIZED (view)) { if (view->presentation) gdk_window_set_background (GTK_WIDGET(view)->window, @@ -2219,52 +2746,50 @@ ev_view_zoom_out (EvView *view) ev_view_set_zoom (view, ZOOM_OUT_FACTOR, TRUE); } -static void -ev_view_set_orientation (EvView *view, - EvOrientation orientation) +void +ev_view_rotate_right (EvView *view) { - ev_document_set_orientation (view->document, orientation); + int rotation = view->rotation + 90; - clear_caches (view); - setup_caches (view); + if (rotation >= 360) { + rotation -= 360; + } - gtk_widget_queue_resize (GTK_WIDGET (view)); + ev_view_set_rotation (view, rotation); } void -ev_view_rotate_right (EvView *view) +ev_view_rotate_left (EvView *view) { - EvOrientation orientation, new_orientation; + int rotation = view->rotation - 90; - orientation = ev_document_get_orientation (view->document); - if (orientation == EV_ORIENTATION_PORTRAIT) { - new_orientation = EV_ORIENTATION_LANDSCAPE; - } else if (orientation == EV_ORIENTATION_LANDSCAPE) { - new_orientation = EV_ORIENTATION_UPSIDEDOWN; - } else if (orientation == EV_ORIENTATION_UPSIDEDOWN) { - new_orientation = EV_ORIENTATION_SEASCAPE; - } else { - new_orientation = EV_ORIENTATION_PORTRAIT; + if (rotation < 0) { + rotation += 360; } - ev_view_set_orientation (view, new_orientation); + + ev_view_set_rotation (view, rotation); } void -ev_view_rotate_left (EvView *view) +ev_view_set_rotation (EvView *view, int rotation) { - EvOrientation orientation, new_orientation; + view->rotation = rotation; - orientation = ev_document_get_orientation (view->document); - if (orientation == EV_ORIENTATION_PORTRAIT) { - new_orientation = EV_ORIENTATION_SEASCAPE; - } else if (orientation == EV_ORIENTATION_SEASCAPE) { - new_orientation = EV_ORIENTATION_UPSIDEDOWN; - } else if (orientation == EV_ORIENTATION_UPSIDEDOWN) { - new_orientation = EV_ORIENTATION_LANDSCAPE; - } else { - new_orientation = EV_ORIENTATION_PORTRAIT; + if (view->pixbuf_cache) { + ev_pixbuf_cache_clear (view->pixbuf_cache); + gtk_widget_queue_resize (GTK_WIDGET (view)); } - ev_view_set_orientation (view, new_orientation); + + if (rotation != 0) + clear_selection (view); + + g_object_notify (G_OBJECT (view), "rotation"); +} + +int +ev_view_get_rotation (EvView *view) +{ + return view->rotation; } static double @@ -2284,6 +2809,23 @@ zoom_for_size_fit_width (int doc_width, return scale; } +static double +zoom_for_size_fit_height (int doc_width, + int doc_height, + int target_width, + int target_height, + int vsb_height) +{ + double scale; + + scale = (double)target_height / doc_height; + + if (doc_width * scale > target_width) + scale = (double) (target_height - vsb_height) / doc_height; + + return scale; +} + static double zoom_for_size_best_fit (int doc_width, int doc_height, @@ -2317,6 +2859,7 @@ ev_view_zoom_for_size_presentation (EvView *view, ev_page_cache_get_size (view->page_cache, view->current_page, + view->rotation, 1.0, &doc_width, &doc_height); @@ -2336,9 +2879,11 @@ ev_view_zoom_for_size_continuous_and_dual_page (EvView *view, gdouble scale; ev_page_cache_get_max_width (view->page_cache, + view->rotation, 1.0, &doc_width); ev_page_cache_get_max_height (view->page_cache, + view->rotation, 1.0, &doc_height); compute_border (view, doc_width, doc_height, &border); @@ -2372,9 +2917,11 @@ ev_view_zoom_for_size_continuous (EvView *view, gdouble scale; ev_page_cache_get_max_width (view->page_cache, + view->rotation, 1.0, &doc_width); ev_page_cache_get_max_height (view->page_cache, + view->rotation, 1.0, &doc_height); compute_border (view, doc_width, doc_height, &border); @@ -2412,6 +2959,7 @@ ev_view_zoom_for_size_dual_page (EvView *view, /* Find the largest of the two. */ ev_page_cache_get_size (view->page_cache, view->current_page, + view->rotation, 1.0, &doc_width, &doc_height); @@ -2419,6 +2967,7 @@ ev_view_zoom_for_size_dual_page (EvView *view, gint width_2, height_2; ev_page_cache_get_size (view->page_cache, other_page, + view->rotation, 1.0, &width_2, &height_2); if (width_2 > doc_width) @@ -2455,6 +3004,7 @@ ev_view_zoom_for_size_single_page (EvView *view, ev_page_cache_get_size (view->page_cache, view->current_page, + view->rotation, 1.0, &doc_width, &doc_height); @@ -2589,7 +3139,7 @@ jump_to_find_result (EvView *view) n_results = ev_document_find_get_n_results (find, page); - if (n_results > view->find_result) { + if (n_results > 0 && view->find_result < n_results) { ev_document_find_get_result (find, page, view->find_result, &rect); @@ -2599,7 +3149,7 @@ jump_to_find_result (EvView *view) } static void -jump_to_find_page (EvView *view) +jump_to_find_page (EvView *view, EvViewFindDirection direction) { int n_pages, i; @@ -2608,12 +3158,19 @@ jump_to_find_page (EvView *view) for (i = 0; i < n_pages; i++) { int has_results; int page; + + if (direction == EV_VIEW_FIND_NEXT) + page = view->find_page + i; + else + page = view->find_page - i; + - page = i + view->find_page; if (page >= n_pages) { page = page - n_pages; } - + 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) { @@ -2621,7 +3178,6 @@ jump_to_find_page (EvView *view) break; } else if (has_results == 1) { ev_page_cache_set_current_page (view->page_cache, page); - jump_to_find_result (view); break; } } @@ -2630,40 +3186,43 @@ jump_to_find_page (EvView *view) gboolean ev_view_can_find_next (EvView *view) { - int n_results = 0; - if (EV_IS_DOCUMENT_FIND (view->document)) { EvDocumentFind *find = EV_DOCUMENT_FIND (view->document); + int i, n_pages; - n_results = ev_document_find_get_n_results (find, view->current_page); + 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; + } + } } - return n_results > 0; + return FALSE; } void ev_view_find_next (EvView *view) { - EvPageCache *page_cache; int n_results, n_pages; EvDocumentFind *find = EV_DOCUMENT_FIND (view->document); - 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); + n_pages = ev_page_cache_get_n_pages (view->page_cache); view->find_result++; if (view->find_result >= n_results) { + view->find_result = 0; view->find_page++; - if (view->find_page >= n_pages) { view->find_page = 0; } - jump_to_find_page (view); + jump_to_find_page (view, EV_VIEW_FIND_NEXT); + jump_to_find_result (view); } else { jump_to_find_result (view); gtk_widget_queue_draw (GTK_WIDGET (view)); @@ -2686,14 +3245,15 @@ ev_view_find_previous (EvView *view) view->find_result--; if (view->find_result < 0) { - view->find_result = 0; - view->find_page--; + view->find_page--; if (view->find_page < 0) { view->find_page = n_pages - 1; } - jump_to_find_page (view); + jump_to_find_page (view, EV_VIEW_FIND_PREV); + view->find_result = ev_document_find_get_n_results (find, view->current_page) - 1; + jump_to_find_result (view); } else { jump_to_find_result (view); gtk_widget_queue_draw (GTK_WIDGET (view)); @@ -2765,6 +3325,7 @@ compute_new_selection_text (EvView *view, GList *list = NULL; EvViewSelection *selection; gint width, height; + int start_page, end_page; g_assert (view->selection_mode == EV_VIEW_SELECTION_TEXT); @@ -2774,7 +3335,18 @@ compute_new_selection_text (EvView *view, * affects. */ first = n_pages; last = 0; - for (i = 0; i < n_pages; i++) { + if (view->continuous) { + start_page = 0; + end_page = n_pages; + } else if (view->dual_page) { + start_page = view->start_page; + end_page = view->end_page + 1; + } else { + start_page = view->current_page; + end_page = view->current_page + 1; + } + + for (i = start_page; i < end_page; i++) { GdkRectangle page_area; GtkBorder border; @@ -2799,6 +3371,7 @@ compute_new_selection_text (EvView *view, GdkPoint *point; ev_page_cache_get_size (view->page_cache, i, + view->rotation, 1.0, &width, &height); selection = g_new0 (EvViewSelection, 1); @@ -2841,15 +3414,114 @@ compute_new_selection_text (EvView *view, */ static void merge_selection_region (EvView *view, - GList *list) + GList *new_list) { + GList *old_list; + GList *new_list_ptr, *old_list_ptr; - /* FIXME: actually write... */ - clear_selection (view); - gtk_widget_queue_draw (GTK_WIDGET (view)); + /* Update the selection */ + old_list = ev_pixbuf_cache_get_selection_list (view->pixbuf_cache); + g_list_foreach (view->selection_info.selections, (GFunc)selection_free, NULL); + view->selection_info.selections = new_list; + ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, new_list); + + new_list_ptr = new_list; + old_list_ptr = old_list; + + while (new_list_ptr || old_list_ptr) { + EvViewSelection *old_sel, *new_sel; + int cur_page; + GdkRegion *region = NULL; + + new_sel = (new_list_ptr) ? (new_list_ptr->data) : NULL; + old_sel = (old_list_ptr) ? (old_list_ptr->data) : NULL; + + /* Assume that the lists are in order, and we run through them + * comparing them, one page at a time. We come out with the + * first page we see. */ + if (new_sel && old_sel) { + if (new_sel->page < old_sel->page) { + new_list_ptr = new_list_ptr->next; + old_sel = NULL; + } else if (new_sel->page > old_sel->page) { + old_list_ptr = old_list_ptr->next; + new_sel = NULL; + } else { + new_list_ptr = new_list_ptr->next; + old_list_ptr = old_list_ptr->next; + } + } else if (new_sel) { + new_list_ptr = new_list_ptr->next; + } else if (old_sel) { + old_list_ptr = old_list_ptr->next; + } + + g_assert (new_sel || old_sel); + + /* is the page we're looking at on the screen?*/ + cur_page = new_sel ? new_sel->page : old_sel->page; + if (cur_page < view->start_page || cur_page > view->end_page) + continue; + + /* seed the cache with a new page. We are going to need the new + * region too. */ + if (new_sel) { + GdkRegion *tmp_region = NULL; + ev_pixbuf_cache_get_selection_pixbuf (view->pixbuf_cache, + cur_page, + view->scale, + &tmp_region); + if (tmp_region) { + new_sel->covered_region = gdk_region_copy (tmp_region); + } + } + + /* Now we figure out what needs redrawing */ + if (old_sel && new_sel) { + 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 */ + 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 = NULL; + } + } else if (old_sel->covered_region) { + region = gdk_region_copy (old_sel->covered_region); + } else if (new_sel->covered_region) { + region = gdk_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); + } + } 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); + } + } else { + g_assert_not_reached (); + } - view->selection_info.selections = list; - ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, list); + /* Redraw the damaged region! */ + if (region) { + GdkRectangle page_area; + GtkBorder border; + + 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_region_destroy (region); + } + } + + /* Free the old list, now that we're done with it. */ + g_list_foreach (old_list, (GFunc) selection_free, NULL); } static void @@ -2871,6 +3543,8 @@ compute_selections (EvView *view, static void selection_free (EvViewSelection *selection) { + if (selection->covered_region) + gdk_region_destroy (selection->covered_region); g_free (selection); } @@ -2888,6 +3562,10 @@ ev_view_select_all (EvView *view) { int n_pages, i; + /* Disable selection on rotated pages for the 0.4.0 series */ + if (view->rotation != 0) + return; + clear_selection (view); n_pages = ev_page_cache_get_n_pages (view->page_cache); @@ -2896,7 +3574,9 @@ ev_view_select_all (EvView *view) EvViewSelection *selection; ev_page_cache_get_size (view->page_cache, - i, 1.0, &width, &height); + i, + view->rotation, + 1.0, &width, &height); selection = g_new0 (EvViewSelection, 1); selection->page = i; @@ -3036,6 +3716,9 @@ ev_view_set_cursor (EvView *view, EvViewCursor new_cursor) case EV_VIEW_CURSOR_NORMAL: gdk_window_set_cursor (widget->window, NULL); break; + case EV_VIEW_CURSOR_IBEAM: + cursor = gdk_cursor_new_for_display (display, GDK_XTERM); + break; case EV_VIEW_CURSOR_LINK: cursor = gdk_cursor_new_for_display (display, GDK_HAND2); break; @@ -3069,6 +3752,42 @@ ev_view_show_cursor (EvView *view) ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); } +gboolean +ev_view_next_page (EvView *view) +{ + int page; + + g_return_val_if_fail (EV_IS_VIEW (view), FALSE); + + page = ev_page_cache_get_current_page (view->page_cache); + page = ev_view_get_dual_page (view) ? page + 2 : page + 1; + + if (page < ev_page_cache_get_n_pages (view->page_cache)) { + ev_page_cache_set_current_page (view->page_cache, page); + return TRUE; + } else { + return FALSE; + } +} + +gboolean +ev_view_previous_page (EvView *view) +{ + int page; + + g_return_val_if_fail (EV_IS_VIEW (view), FALSE); + + page = ev_page_cache_get_current_page (view->page_cache); + page = ev_view_get_dual_page (view) ? page - 2 : page - 1; + + if (page >= 0) { + ev_page_cache_set_current_page (view->page_cache, page); + return TRUE; + } else { + return FALSE; + } +} + /*** Enum description for usage in signal ***/ GType @@ -3086,4 +3805,3 @@ ev_sizing_mode_get_type (void) } return etype; } -