+ form_field_mapping = ev_pixbuf_cache_get_form_field_mapping (view->pixbuf_cache,
+ field->page);
+ ev_form_field_mapping_get_area (form_field_mapping, field, &field_area);
+
+ doc_rect_to_view_rect (view, field->page, &field_area, &view_area);
+ view_area.x -= view->scroll_x;
+ view_area.y -= view->scroll_y;
+
+ gtk_widget_size_request (child, &child_requisition);
+ if (child_requisition.width != view_area.width ||
+ child_requisition.height != view_area.height)
+ gtk_widget_set_size_request (child, view_area.width, view_area.height);
+
+ gtk_container_child_get (GTK_CONTAINER (widget),
+ child,
+ "x", &child_allocation.x,
+ "y", &child_allocation.y,
+ NULL);
+ if (child_allocation.x != view_area.x ||
+ child_allocation.y != view_area.y) {
+ gtk_layout_move (GTK_LAYOUT (widget), child, view_area.x, view_area.y);
+ }
+ }
+ g_list_free (children);
+}
+
+static void
+ev_view_realize (GtkWidget *widget)
+{
+ EvView *view = EV_VIEW (widget);
+
+ if (GTK_WIDGET_CLASS (ev_view_parent_class)->realize)
+ (* GTK_WIDGET_CLASS (ev_view_parent_class)->realize) (widget);
+
+ gdk_window_set_events (view->layout.bin_window,
+ (gdk_window_get_events (view->layout.bin_window) |
+ GDK_EXPOSURE_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_SCROLL_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK));
+
+ if (view->presentation)
+ gdk_window_set_background (view->layout.bin_window, &widget->style->black);
+ else
+ gdk_window_set_background (view->layout.bin_window, &widget->style->mid [GTK_STATE_NORMAL]);
+}
+
+static gboolean
+ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event)
+{
+ EvView *view = EV_VIEW (widget);
+ guint state;
+
+ state = event->state & gtk_accelerator_get_default_mod_mask ();
+
+ 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;
+ }
+
+ view->jump_to_find_result = FALSE;
+ /* Shift+Wheel scrolls the in the perpendicular direction */
+ if (state & GDK_SHIFT_MASK) {
+ if (event->direction == GDK_SCROLL_UP)
+ event->direction = GDK_SCROLL_LEFT;
+ if (event->direction == GDK_SCROLL_LEFT)
+ 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;
+}
+
+static EvViewSelection *
+find_selection_for_page (EvView *view,
+ gint page)
+{
+ GList *list;
+
+ for (list = view->selection_info.selections; list != NULL; list = list->next) {
+ EvViewSelection *selection;
+
+ selection = (EvViewSelection *) list->data;
+
+ if (selection->page == page)
+ return selection;
+ }
+
+ return NULL;
+}
+
+static void
+draw_end_presentation_page (EvView *view,
+ GdkRectangle *page_area)
+{
+ PangoLayout *layout;
+ PangoFontDescription *font_desc;
+ gchar *markup;
+ const gchar *text = _("End of presentation. Press Escape to exit.");
+
+ if (view->presentation_state != EV_PRESENTATION_END)
+ return;
+
+ layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), NULL);
+ markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", text);
+ pango_layout_set_markup (layout, markup, -1);
+ g_free (markup);
+
+ font_desc = pango_font_description_new ();
+ pango_font_description_set_size (font_desc, 16 * PANGO_SCALE);
+ pango_layout_set_font_description (layout, font_desc);
+
+ gtk_paint_layout (GTK_WIDGET (view)->style,
+ view->layout.bin_window,
+ GTK_WIDGET_STATE (view),
+ FALSE,
+ page_area,
+ GTK_WIDGET (view),
+ NULL,
+ page_area->x + 15,
+ page_area->y + 15,
+ layout);
+
+ pango_font_description_free (font_desc);
+ g_object_unref (layout);
+}
+
+static gboolean
+ev_view_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ EvView *view = EV_VIEW (widget);
+ cairo_t *cr;
+ gint i;
+
+ if (view->presentation) {
+ switch (view->presentation_state) {
+ case EV_PRESENTATION_END: {
+ GdkRectangle area = {0};
+
+ area.width = widget->allocation.width;
+ area.height = widget->allocation.height;
+
+ draw_end_presentation_page (view, &area);
+ }
+ return FALSE;
+ case EV_PRESENTATION_BLACK:
+ case EV_PRESENTATION_WHITE:
+ return FALSE;
+ case EV_PRESENTATION_NORMAL:
+ default:
+ break;
+ }
+ } else if (view->loading) {
+ GdkRectangle area = {0};
+
+ area.width = widget->allocation.width;
+ area.height = widget->allocation.height;
+
+ draw_loading_text (view,
+ &area,
+ &(event->area));
+ }
+
+ if (view->document == NULL)
+ return FALSE;
+
+ cr = gdk_cairo_create (view->layout.bin_window);
+
+ for (i = view->start_page; i <= view->end_page; i++) {
+ GdkRectangle page_area;
+ GtkBorder border;
+ gboolean page_ready = TRUE;
+
+ if (!get_page_extents (view, i, &page_area, &border))
+ continue;
+
+ page_area.x -= view->scroll_x;
+ page_area.y -= view->scroll_y;
+
+ draw_one_page (view, i, cr, &page_area, &border, &(event->area), &page_ready);
+
+ if (page_ready && EV_IS_DOCUMENT_FIND (view->document) && view->highlight_find_results)
+ highlight_find_results (view, i);
+ }
+
+ cairo_destroy (cr);
+
+ if (GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event)
+ (* GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event) (widget, event);
+
+ return FALSE;
+}
+
+static gboolean
+ev_view_do_popup_menu (EvView *view,
+ gdouble x,
+ gdouble y)
+{
+ EvLink *link;
+ EvImage *image;
+
+ image = ev_view_get_image_at_location (view, x, y);
+ if (image) {
+ g_signal_emit (view, signals[SIGNAL_POPUP_MENU], 0, image);
+ return TRUE;
+ }
+
+ link = ev_view_get_link_at_location (view, x, y);
+ if (link) {
+ g_signal_emit (view, signals[SIGNAL_POPUP_MENU], 0, link);
+ return TRUE;
+ }
+
+ g_signal_emit (view, signals[SIGNAL_POPUP_MENU], 0, NULL);
+
+ return TRUE;
+}
+
+static gboolean
+ev_view_popup_menu (GtkWidget *widget)
+{
+ gint x, y;
+
+ gtk_widget_get_pointer (widget, &x, &y);
+ return ev_view_do_popup_menu (EV_VIEW (widget), x, y);
+}
+
+#if GTK_CHECK_VERSION (2, 11, 7)
+static void
+get_link_area (EvView *view,
+ gint x,
+ gint y,
+ EvLink *link,
+ GdkRectangle *area)
+{
+ EvRectangle ev_rect;
+ GList *link_mapping;
+ gint page;
+ gint x_offset = 0, y_offset = 0;
+
+ x += view->scroll_x;
+ y += view->scroll_y;
+
+ find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
+
+ link_mapping = ev_pixbuf_cache_get_link_mapping (view->pixbuf_cache, page);
+ ev_link_mapping_get_area (link_mapping, link, &ev_rect);
+
+ doc_rect_to_view_rect (view, page, &ev_rect, area);
+ area->y -= view->scroll_y ;
+}
+
+static gboolean
+ev_view_query_tooltip (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_tip,
+ GtkTooltip *tooltip)
+{
+ EvView *view = EV_VIEW (widget);
+ EvLink *link;
+ gchar *text;
+
+ link = ev_view_get_link_at_location (view, x, y);
+ if (!link)
+ return FALSE;
+
+ text = tip_from_link (view, link);
+ if (text && g_utf8_validate (text, -1, NULL)) {
+ GdkRectangle link_area;
+
+ get_link_area (view, x, y, link, &link_area);
+ gtk_tooltip_set_text (tooltip, text);
+ gtk_tooltip_set_tip_area (tooltip, &link_area);
+ }
+ g_free (text);
+
+ return TRUE;
+}
+#endif /* GTK_CHECK_VERSION (2, 11, 7) */
+
+static void
+start_selection_for_event (EvView *view,
+ GdkEventButton *event)
+{
+ EvSelectionStyle style;
+
+ clear_selection (view);
+
+ view->selection_info.start.x = event->x + view->scroll_x;
+ view->selection_info.start.y = event->y + view->scroll_y;
+
+ switch (event->type) {
+ case GDK_2BUTTON_PRESS:
+ style = EV_SELECTION_STYLE_WORD;
+ break;
+ case GDK_3BUTTON_PRESS:
+ style = EV_SELECTION_STYLE_LINE;
+ break;
+ default:
+ style = EV_SELECTION_STYLE_GLYPH;
+ break;
+ }
+
+ view->selection_info.style = style;
+}
+
+static gboolean
+ev_view_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ EvView *view = EV_VIEW (widget);
+
+ if (!view->document)
+ return FALSE;
+
+ if (!GTK_WIDGET_HAS_FOCUS (widget)) {
+ gtk_widget_grab_focus (widget);
+ }
+
+ view->pressed_button = event->button;
+ view->selection_info.in_drag = FALSE;
+
+ switch (event->button) {
+ case 1: {
+ EvImage *image;
+ EvFormField *field;
+
+ if (EV_IS_SELECTION (view->document) && view->selection_info.selections) {
+ if (event->type == GDK_3BUTTON_PRESS) {
+ start_selection_for_event (view, event);
+ } else if (location_in_selected_text (view,
+ event->x + view->scroll_x,
+ event->y + view->scroll_y)) {
+ view->selection_info.in_drag = TRUE;
+ } else {
+ start_selection_for_event (view, event);
+ }
+
+ gtk_widget_queue_draw (widget);
+ } else if (!location_in_text (view, event->x + view->scroll_x, event->y + view->scroll_y) &&
+ (image = ev_view_get_image_at_location (view, event->x, event->y))) {
+ if (view->image_dnd_info.image)
+ g_object_unref (view->image_dnd_info.image);
+ view->image_dnd_info.image = g_object_ref (image);
+ view->image_dnd_info.in_drag = TRUE;
+
+ view->image_dnd_info.start.x = event->x + view->scroll_x;
+ view->image_dnd_info.start.y = event->y + view->scroll_y;
+ } else if ((field = ev_view_get_form_field_at_location (view, event->x, event->y))) {
+ ev_view_remove_all (view);
+ ev_view_handle_form_field (view, field, event->x, event->y);
+ } else {
+ ev_view_remove_all (view);
+
+ if (EV_IS_SELECTION (view->document))
+ start_selection_for_event (view, event);
+ }
+ }
+ return TRUE;
+ case 2:
+ /* use root coordinates as reference point because
+ * scrolling changes window relative coordinates */
+ view->drag_info.start.x = event->x_root;
+ view->drag_info.start.y = event->y_root;
+ view->drag_info.hadj = gtk_adjustment_get_value (view->hadjustment);
+ view->drag_info.vadj = gtk_adjustment_get_value (view->vadjustment);
+
+ ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG);
+
+ return TRUE;
+ case 3:
+ return ev_view_do_popup_menu (view, event->x, event->y);
+ }
+
+ return FALSE;
+}
+
+static void
+ev_view_remove_all (EvView *view)
+{
+ GList *children, *child;
+
+ children = gtk_container_get_children (GTK_CONTAINER (view));
+ for (child = children; child && child->data; child = g_list_next (child)) {
+ gtk_container_remove (GTK_CONTAINER (view),
+ GTK_WIDGET (child->data));
+ }
+ g_list_free (children);
+}
+
+/*** Drag and Drop ***/
+static void
+ev_view_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ EvView *view = EV_VIEW (widget);
+
+ switch (info) {
+ case TARGET_DND_TEXT:
+ if (EV_IS_SELECTION (view->document) &&
+ view->selection_info.selections) {
+ gchar *text;
+
+ text = get_selected_text (view);
+ gtk_selection_data_set_text (selection_data,
+ text,
+ strlen (text));
+ g_free (text);
+ }
+ break;
+ case TARGET_DND_IMAGE:
+ if (view->image_dnd_info.image) {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = ev_image_get_pixbuf (view->image_dnd_info.image);
+ gtk_selection_data_set_pixbuf (selection_data, pixbuf);
+ }
+ break;
+ case TARGET_DND_URI:
+ if (view->image_dnd_info.image) {
+ const gchar *tmp_uri;
+ gchar **uris;
+
+ tmp_uri = ev_image_save_tmp (view->image_dnd_info.image);
+
+ uris = g_new0 (gchar *, 2);
+ uris[0] = (gchar *)tmp_uri;
+
+ gtk_selection_data_set_uris (selection_data, uris);
+
+ /* g_free instead of g_strfreev since tmp_uri is const */
+ g_free (uris);
+ }
+ }
+}
+
+static gboolean
+ev_view_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ if (gtk_drag_get_source_widget (context) == widget)
+ gdk_drag_status (context, 0, time);
+ else
+ gdk_drag_status (context, context->suggested_action, time);
+
+ return TRUE;
+}
+
+static void
+ev_view_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ gchar **uris;
+ gint i = 0;
+ GSList *uri_list = NULL;
+
+ uris = gtk_selection_data_get_uris (selection_data);
+ if (!uris) {
+ gtk_drag_finish (context, FALSE, FALSE, time);
+ return;
+ }
+
+ for (i = 0; uris[i]; i++) {
+ uri_list = g_slist_prepend (uri_list, (gpointer) uris[i]);
+ }
+
+ ev_application_open_uri_list (EV_APP, uri_list,
+ gtk_widget_get_screen (widget),
+ 0);
+ gtk_drag_finish (context, TRUE, FALSE, time);
+
+ g_strfreev (uris);
+ g_slist_free (uri_list);
+}
+
+
+static gboolean
+selection_update_idle_cb (EvView *view)
+{
+ compute_selections (view,
+ view->selection_info.style,
+ &view->selection_info.start,
+ &view->motion);
+ view->selection_update_id = 0;
+ return FALSE;
+}
+
+static gboolean
+selection_scroll_timeout_cb (EvView *view)
+{
+ gint x, y, shift = 0;
+ GtkWidget *widget = GTK_WIDGET (view);
+
+ gtk_widget_get_pointer (widget, &x, &y);
+
+ if (y > widget->allocation.height) {
+ shift = (y - widget->allocation.height) / 2;
+ } else if (y < 0) {
+ shift = y / 2;
+ }
+
+ if (shift)
+ gtk_adjustment_set_value (view->vadjustment,
+ CLAMP (view->vadjustment->value + shift,
+ view->vadjustment->lower,
+ view->vadjustment->upper -
+ view->vadjustment->page_size));
+
+ if (x > widget->allocation.width) {
+ shift = (x - widget->allocation.width) / 2;
+ } else if (x < 0) {
+ shift = x / 2;
+ }
+
+ if (shift)
+ gtk_adjustment_set_value (view->hadjustment,
+ CLAMP (view->hadjustment->value + shift,
+ view->hadjustment->lower,
+ view->hadjustment->upper -
+ view->hadjustment->page_size));
+
+ return TRUE;
+}
+
+static gboolean
+ev_view_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ EvView *view = EV_VIEW (widget);
+ gint x, y;
+
+ if (!view->document)
+ return FALSE;
+
+ if (event->is_hint || event->window != view->layout.bin_window) {
+ gtk_widget_get_pointer (widget, &x, &y);
+ } else {
+ x = event->x;
+ y = event->y;
+ }
+
+ if (view->selection_info.in_drag) {
+ if (gtk_drag_check_threshold (widget,
+ view->selection_info.start.x,
+ view->selection_info.start.y,
+ x, y)) {
+ GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
+
+ gtk_target_list_add_text_targets (target_list, TARGET_DND_TEXT);
+
+ 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;
+ }
+ } else if (view->image_dnd_info.in_drag) {
+ if (gtk_drag_check_threshold (widget,
+ view->selection_info.start.x,
+ view->selection_info.start.y,
+ x, y)) {
+ GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
+
+ gtk_target_list_add_uri_targets (target_list, TARGET_DND_URI);
+ gtk_target_list_add_image_targets (target_list, TARGET_DND_IMAGE, TRUE);
+
+ gtk_drag_begin (widget, target_list,
+ GDK_ACTION_COPY,
+ 1, (GdkEvent *)event);
+
+ view->image_dnd_info.in_drag = FALSE;