X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;ds=sidebyside;f=shell%2Fev-view.c;h=6f87b6a63cb176e2c3c1d8d29de9c8dba77769fa;hb=578c78ee1a5ee3b2aa858ebf257646c195cd9885;hp=ae335665a74222f1cc71a40aaa311f67a0b74316;hpb=72390e1fd5f031d564f18db414c18d082139f385;p=evince.git diff --git a/shell/ev-view.c b/shell/ev-view.c index ae335665..6f87b6a6 100644 --- a/shell/ev-view.c +++ b/shell/ev-view.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -55,11 +56,13 @@ enum { PROP_SIZING_MODE, PROP_ZOOM, PROP_ROTATION, + PROP_HAS_SELECTION, }; enum { SIGNAL_BINDING_ACTIVATED, SIGNAL_ZOOM_INVALID, + SIGNAL_EXTERNAL_LINK, N_SIGNALS, }; @@ -111,6 +114,7 @@ typedef struct { /* Information for handling selection */ typedef struct { gboolean in_selection; + gboolean in_drag; GdkPoint start; GList *selections; } SelectionInfo; @@ -188,6 +192,8 @@ struct _EvViewClass { GtkScrollType scroll, gboolean horizontal); void (*zoom_invalid) (EvView *view); + void (*external_link) (EvView *view, + EvLink *link); }; /*** Scrolling ***/ @@ -931,7 +937,7 @@ view_rect_to_doc_rect (EvView *view, doc_rect->y2 = doc_rect->y1 + (double) view_rect->height / view->scale; } -static void +static gboolean doc_point_to_view_point (EvView *view, int page, EvPoint *doc_point, @@ -939,7 +945,7 @@ doc_point_to_view_point (EvView *view, { GdkRectangle page_area; GtkBorder border; - double x, y; + double x, y, view_x, view_y; int width, height; ev_page_cache_get_size (view->page_cache, page, @@ -965,8 +971,13 @@ doc_point_to_view_point (EvView *view, 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; + view_x = x * view->scale; + view_y = y * view->scale; + view_point->x = view_x + page_area.x; + view_point->y = view_y + page_area.y; + + return (view_x > 0 && view_x <= page_area.width && + view_y > 0 && view_y <= page_area.height); } static void @@ -1089,6 +1100,32 @@ ev_view_get_height (EvView *view) return GTK_WIDGET (view)->allocation.height; } +static gboolean +location_in_selected_text (EvView *view, + gdouble x, + gdouble y) +{ + gint page = -1; + gint x_offset = 0, y_offset = 0; + EvViewSelection *selection; + GList *l = NULL; + + for (l = view->selection_info.selections; l != NULL; l = l->next) { + selection = (EvViewSelection *)l->data; + + find_page_at_location (view, x, y, &page, &x_offset, &y_offset); + + if (page != selection->page) + continue; + + if (selection->covered_region && + gdk_region_point_in (selection->covered_region, x_offset, y_offset)) + return TRUE; + } + + return FALSE; +} + /*** Hyperref ***/ static EvLink * get_link_at_location (EvView *view, @@ -1112,6 +1149,35 @@ get_link_at_location (EvView *view, return NULL; } +static void +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); + + 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); + + if (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); + } +} + static void goto_fitv_link (EvView *view, EvLink *link) { @@ -1125,7 +1191,6 @@ goto_fitv_link (EvView *view, EvLink *link) 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), @@ -1134,7 +1199,10 @@ goto_fitv_link (EvView *view, EvLink *link) 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); + + if (doc_point_to_view_point (view, page, &doc_point, &view_point)) { + gtk_adjustment_set_value (view->hadjustment, view_point.x); + } } static void @@ -1150,7 +1218,6 @@ goto_fith_link (EvView *view, EvLink *link) 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), @@ -1158,7 +1225,12 @@ goto_fith_link (EvView *view, EvLink *link) 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); + + if (doc_point_to_view_point (view, page, &doc_point, &view_point)) { + gtk_adjustment_set_value (view->vadjustment, view_point.y); + } else { + ev_page_cache_set_current_page (view->page_cache, page); + } } static void @@ -1198,17 +1270,19 @@ goto_xyz_link (EvView *view, EvLink *link) 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); + if (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); + } else { + ev_page_cache_set_current_page (view->page_cache, page); + } } void ev_view_goto_link (EvView *view, EvLink *link) { EvLinkType type; - const char *uri; int page; type = ev_link_get_link_type (link); @@ -1229,12 +1303,15 @@ ev_view_goto_link (EvView *view, EvLink *link) 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; } } @@ -1288,9 +1365,13 @@ handle_link_over_xy (EvView *view, gint x, gint y) 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)); + if (msg && g_utf8_validate (msg, -1, NULL)) { + EvTooltip *tooltip = EV_TOOLTIP (view->link_tooltip); + + ev_tooltip_set_position (tooltip, x, y); + ev_tooltip_set_text (tooltip, msg); + ev_tooltip_activate (tooltip); + } g_free (msg); ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK); @@ -1632,22 +1713,31 @@ ev_view_button_press_event (GtkWidget *widget, GdkEventButton *event) { EvView *view = EV_VIEW (widget); - + if (!GTK_WIDGET_HAS_FOCUS (widget)) { gtk_widget_grab_focus (widget); } - + view->pressed_button = event->button; - + view->selection_info.in_drag = FALSE; + switch (event->button) { - case 1: + case 1: if (view->selection_info.selections) { - clear_selection (view); + if (location_in_selected_text (view, + event->x + view->scroll_x, + event->y + view->scroll_y)) { + view->selection_info.in_drag = TRUE; + } else { + clear_selection (view); + } + gtk_widget_queue_draw (widget); } view->selection_info.start.x = event->x + view->scroll_x; view->selection_info.start.y = event->y + view->scroll_y; + return TRUE; case 2: /* use root coordinates as reference point because @@ -1661,10 +1751,30 @@ ev_view_button_press_event (GtkWidget *widget, return TRUE; } - + return FALSE; } +static void +ev_view_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + EvView *view = EV_VIEW (widget); + + if (view->selection_info.selections && + ev_document_can_get_text (view->document)) { + gchar *text; + + text = get_selected_text (view); + + gtk_selection_data_set_text (selection_data, text, strlen (text)); + + g_free (text); + } +} static gboolean selection_update_idle_cb (EvView *view) @@ -1687,19 +1797,40 @@ ev_view_motion_notify_event (GtkWidget *widget, if (!view->document) return FALSE; + if (view->selection_info.in_drag) { + if (gtk_drag_check_threshold (widget, + view->selection_info.start.x, + view->selection_info.start.y, + event->x, event->y)) { + GdkDragContext *context; + GtkTargetList *target_list = gtk_target_list_new (NULL, 0); + + gtk_target_list_add_text_targets (target_list, 0); + + context = gtk_drag_begin (widget, target_list, + GDK_ACTION_COPY, + 1, (GdkEvent *)event); + + view->selection_info.in_drag = FALSE; + + gtk_target_list_unref (target_list); + + return TRUE; + } + } + /* For the Evince 0.4.x release, we limit selection to un-rotated * documents only. */ - if (view->pressed_button == 1 && - view->rotation == 0) { + if (view->pressed_button == 1 && view->rotation == 0) { view->selection_info.in_selection = TRUE; 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 + /* 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); @@ -1776,6 +1907,13 @@ ev_view_button_release_event (GtkWidget *widget, if (view->selection_info.selections) { ev_view_update_primary_selection (view); + + if (view->selection_info.in_drag) { + clear_selection (view); + gtk_widget_queue_draw (widget); + } + + view->selection_info.in_drag = FALSE; } else if (link) { ev_view_goto_link (view, link); } else if (view->presentation) { @@ -2196,6 +2334,10 @@ ev_view_get_property (GObject *object, case PROP_ROTATION: g_value_set_int (value, view->rotation); break; + case PROP_HAS_SELECTION: + g_value_set_boolean (value, + view->selection_info.selections != NULL); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -2226,6 +2368,7 @@ ev_view_class_init (EvViewClass *class) 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; + widget_class->drag_data_get = ev_view_drag_data_get; gtk_object_class->destroy = ev_view_destroy; class->set_scroll_adjustments = ev_view_set_scroll_adjustments; @@ -2259,6 +2402,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, @@ -2333,6 +2484,13 @@ ev_view_class_init (EvViewClass *class) 360, 0, G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_HAS_SELECTION, + g_param_spec_boolean ("has-selection", + "Has selection", + "The view has selections", + FALSE, + G_PARAM_READABLE)); binding_set = gtk_binding_set_by_class (class); @@ -2353,7 +2511,9 @@ ev_view_init (EvView *view) view->pressed_button = -1; view->cursor = EV_VIEW_CURSOR_NORMAL; view->drag_info.in_drag = FALSE; + view->selection_info.selections = NULL; view->selection_info.in_selection = FALSE; + view->selection_info.in_drag = FALSE; view->selection_mode = EV_VIEW_SELECTION_TEXT; view->continuous = TRUE; view->dual_page = FALSE; @@ -3385,6 +3545,7 @@ merge_selection_region (EvView *view, 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); + g_object_notify (G_OBJECT (view), "has-selection"); new_list_ptr = new_list; old_list_ptr = old_list; @@ -3515,6 +3676,7 @@ clear_selection (EvView *view) g_list_foreach (view->selection_info.selections, (GFunc)selection_free, NULL); view->selection_info.selections = NULL; view->selection_info.in_selection = FALSE; + g_object_notify (G_OBJECT (view), "has-selection"); } @@ -3549,9 +3711,16 @@ ev_view_select_all (EvView *view) } ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, view->selection_info.selections); + g_object_notify (G_OBJECT (view), "has-selection"); gtk_widget_queue_draw (GTK_WIDGET (view)); } +gboolean +ev_view_get_has_selection (EvView *view) +{ + return view->selection_info.selections != NULL; +} + static char * get_selected_text (EvView *ev_view) {