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=815f69d99ddd76437be1bb185203039b12506c24;hpb=cca6f560c79ff1a30b23c08b9918a77876b69d13;p=evince.git diff --git a/shell/ev-view.c b/shell/ev-view.c index 815f69d9..15bd4124 100644 --- a/shell/ev-view.c +++ b/shell/ev-view.c @@ -37,6 +37,7 @@ #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)) @@ -59,6 +60,7 @@ enum { enum { SIGNAL_BINDING_ACTIVATED, SIGNAL_ZOOM_INVALID, + SIGNAL_EXTERNAL_LINK, N_SIGNALS, }; @@ -148,6 +150,8 @@ struct _EvView { int pressed_button; EvViewCursor cursor; + GtkWidget *link_tooltip; + EvLink *hovered_link; EvPageCache *page_cache; EvPixbufCache *pixbuf_cache; @@ -185,6 +189,8 @@ struct _EvViewClass { GtkScrollType scroll, gboolean horizontal); void (*zoom_invalid) (EvView *view); + void (*external_link) (EvView *view, + EvLink *link); }; /*** Scrolling ***/ @@ -242,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, +static char* tip_from_link (EvView *view, EvLink *link); -static char* status_message_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); @@ -261,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, @@ -272,6 +277,8 @@ 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, @@ -323,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, @@ -539,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; @@ -682,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; @@ -920,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, @@ -1028,6 +1080,18 @@ location_in_text (EvView *view, 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, @@ -1051,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); @@ -1068,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; @@ -1090,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); @@ -1104,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 ***/ @@ -1262,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); @@ -1305,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), @@ -1321,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); - } - } - return TRUE; + 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; } - 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; @@ -1525,24 +1777,7 @@ ev_view_motion_notify_event (GtkWidget *widget, */ } else if (view->pressed_button <= 0 && view->rotation == 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 if (location_in_text (view, event->x + view->scroll_x, event->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); - } + handle_link_over_xy (view, event->x, event->y); return TRUE; } @@ -1554,25 +1789,37 @@ 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; } @@ -1609,6 +1856,21 @@ ev_view_leave_notify_event (GtkWidget *widget, GdkEventCrossing *event) 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; } @@ -1877,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); @@ -1985,8 +2253,8 @@ ev_view_class_init (EvViewClass *class) 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; @@ -2022,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, @@ -2117,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; @@ -2534,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, @@ -2894,15 +3186,19 @@ jump_to_find_page (EvView *view, EvViewFindDirection direction) 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 @@ -3456,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 @@ -3473,4 +3805,3 @@ ev_sizing_mode_get_type (void) } return etype; } -