+ev_view_bin_expose (EvView *view,
+ GdkEventExpose *event)
+{
+ int i;
+
+ if (view->document == NULL)
+ return;
+
+ for (i = view->start_page; i <= view->end_page; i++) {
+ GdkRectangle page_area;
+ GtkBorder border;
+
+ if (! get_page_extents (view, i, &page_area, &border))
+ continue;
+ draw_one_page (view, i, &page_area, &border, &(event->area));
+ }
+
+#if 0
+ if (EV_IS_DOCUMENT_FIND (view->document)) {
+ highlight_find_results (view);
+ }
+
+ if (view->has_selection) {
+ GdkRectangle rubberband;
+
+ doc_rect_to_view_rect (view, &view->selection, &rubberband);
+ if (rubberband.width > 0 && rubberband.height > 0) {
+ draw_rubberband (GTK_WIDGET (view), view->bin_window,
+ &rubberband, 0x40);
+ }
+ }
+#endif
+}
+
+static gboolean
+ev_view_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ EvView *view = EV_VIEW (widget);
+
+ if (event->window == view->bin_window)
+ ev_view_bin_expose (view, event);
+ else
+ return GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event (widget, event);
+
+ return FALSE;
+}
+
+void
+ev_view_select_all (EvView *ev_view)
+{
+ GtkWidget *widget = GTK_WIDGET (ev_view);
+ GdkRectangle selection;
+ int width, height;
+ int x_offset, y_offset;
+
+ g_return_if_fail (EV_IS_VIEW (ev_view));
+
+
+ ev_view_get_offsets (ev_view, &x_offset, &y_offset);
+ ev_page_cache_get_size (ev_view->page_cache,
+ ev_view->current_page,
+ ev_view->scale,
+ &width, &height);
+
+ ev_view->has_selection = TRUE;
+ selection.x = x_offset + ev_view->border.left;
+ selection.y = y_offset + ev_view->border.top;
+ selection.width = width;
+ selection.height = height;
+ view_rect_to_doc_rect (ev_view, &selection, &ev_view->selection);
+
+ gtk_widget_queue_draw (widget);
+}
+
+void
+ev_view_copy (EvView *ev_view)
+{
+ GtkClipboard *clipboard;
+ char *text;
+
+ if (!ev_document_can_get_text (ev_view->document)) {
+ return;
+ }
+
+ ev_document_doc_mutex_lock ();
+ text = ev_document_get_text (ev_view->document,
+ ev_view->current_page,
+ &ev_view->selection);
+ ev_document_doc_mutex_unlock ();
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
+ GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text (clipboard, text, -1);
+ g_free (text);
+}
+
+static void
+ev_view_primary_get_cb (GtkClipboard *clipboard,
+ GtkSelectionData *selection_data,
+ guint info,
+ gpointer data)
+{
+ EvView *ev_view = EV_VIEW (data);
+ char *text;
+
+ if (!ev_document_can_get_text (ev_view->document)) {
+ return;
+ }
+
+ ev_document_doc_mutex_lock ();
+ text = ev_document_get_text (ev_view->document,
+ ev_view->current_page,
+ &ev_view->selection);
+ ev_document_doc_mutex_unlock ();
+ gtk_selection_data_set_text (selection_data, text, -1);
+}
+
+static void
+ev_view_primary_clear_cb (GtkClipboard *clipboard,
+ gpointer data)
+{
+ EvView *ev_view = EV_VIEW (data);
+
+ ev_view->has_selection = FALSE;
+}
+
+static void
+ev_view_update_primary_selection (EvView *ev_view)
+{
+ GtkClipboard *clipboard;
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
+ GDK_SELECTION_PRIMARY);
+
+ if (ev_view->has_selection) {
+ if (!gtk_clipboard_set_with_owner (clipboard,
+ targets,
+ G_N_ELEMENTS (targets),
+ ev_view_primary_get_cb,
+ ev_view_primary_clear_cb,
+ G_OBJECT (ev_view)))
+ ev_view_primary_clear_cb (clipboard, ev_view);
+ } else {
+ if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (ev_view))
+ gtk_clipboard_clear (clipboard);
+ }
+}
+
+static gboolean
+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;
+
+ switch (event->button) {
+ case 1:
+ if (view->has_selection) {
+ view->has_selection = FALSE;
+ gtk_widget_queue_draw (widget);
+ }
+
+ view->selection_start.x = event->x;
+ view->selection_start.y = event->y;
+ break;
+ }
+
+ return TRUE;
+}
+
+static char *
+status_message_from_link (EvView *view, EvLink *link)
+{
+ EvLinkType type;
+ char *msg = NULL;
+ char *page_label;
+
+ type = ev_link_get_link_type (link);
+
+ switch (type) {
+ case EV_LINK_TYPE_TITLE:
+ if (ev_link_get_title (link))
+ msg = g_strdup (ev_link_get_title (link));
+ break;
+ case EV_LINK_TYPE_PAGE:
+ 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);
+ break;
+ case EV_LINK_TYPE_EXTERNAL_URI:
+ msg = g_strdup (ev_link_get_uri (link));
+ break;
+ default:
+ break;
+ }
+
+ return msg;
+}
+
+static void
+ev_view_set_status (EvView *view, const char *message)
+{
+ g_return_if_fail (EV_IS_VIEW (view));
+
+ if (message != view->status) {
+ g_free (view->status);
+ view->status = g_strdup (message);
+ g_object_notify (G_OBJECT (view), "status");
+ }
+}
+
+static void
+ev_view_set_find_status (EvView *view, const char *message)
+{
+ g_return_if_fail (EV_IS_VIEW (view));
+
+ g_free (view->find_status);
+ view->find_status = g_strdup (message);
+ g_object_notify (G_OBJECT (view), "find-status");
+}
+
+static GdkCursor *
+ev_view_create_invisible_cursor(void)
+{
+ GdkBitmap *empty;
+ GdkColor black = { 0, 0, 0, 0 };
+ static char bits[] = { 0x00 };
+
+ empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
+
+ return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
+}
+
+static void
+ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
+{
+ GdkCursor *cursor = NULL;
+ GdkDisplay *display;
+ GtkWidget *widget;
+
+ if (view->cursor == new_cursor) {
+ return;
+ }
+
+ widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ display = gtk_widget_get_display (widget);
+ view->cursor = new_cursor;
+
+ switch (new_cursor) {
+ case EV_VIEW_CURSOR_NORMAL:
+ gdk_window_set_cursor (widget->window, NULL);
+ break;
+ case EV_VIEW_CURSOR_LINK:
+ cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
+ break;
+ case EV_VIEW_CURSOR_WAIT:
+ cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
+ break;
+ case EV_VIEW_CURSOR_HIDDEN:
+ cursor = ev_view_create_invisible_cursor ();
+ break;
+
+ }
+
+ if (cursor) {
+ gdk_window_set_cursor (widget->window, cursor);
+ gdk_cursor_unref (cursor);
+ gdk_flush();
+ }
+}
+
+
+static void
+find_page_at_location (EvView *view,
+ gdouble x,
+ gdouble y,
+ gint *page,
+ gint *x_offset,
+ gint *y_offset)
+{
+ int i;
+
+ if (view->document == NULL)
+ return;
+
+ g_assert (page);
+ g_assert (x_offset);
+ g_assert (y_offset);
+
+ for (i = view->start_page; i <= view->end_page; i++) {
+ GdkRectangle page_area;
+ GtkBorder border;
+
+ if (! get_page_extents (view, i, &page_area, &border))
+ continue;
+
+ if ((x >= page_area.x + border.left) &&
+ (x < page_area.x + page_area.width - border.right) &&
+ (y >= page_area.y + border.top) &&
+ (y < page_area.y + page_area.height - border.bottom)) {
+ *page = i;
+ *x_offset = x - (page_area.x + border.left);
+ *y_offset = y - (page_area.y + border.top);
+ return;
+ }
+ }
+
+ *page = -1;
+}
+
+static EvLink *
+get_link_at_location (EvView *view,
+ gdouble x,
+ gdouble y)
+{
+ gint page = -1;
+ gint x_offset = 0, y_offset = 0;
+ GList *link_mapping;
+
+ find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
+
+ if (page == -1)
+ return NULL;
+
+ link_mapping = ev_pixbuf_cache_get_link_mapping (view->pixbuf_cache, page);
+
+ if (link_mapping)
+ return ev_link_mapping_find (link_mapping, x_offset / view->scale, y_offset / view->scale);
+ else
+ return NULL;
+}
+
+
+static gboolean
+ev_view_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ EvView *view = EV_VIEW (widget);
+
+ if (view->pressed_button > 0) {
+ GdkRectangle selection;
+
+ view->has_selection = TRUE;
+ selection.x = MIN (view->selection_start.x, event->x);
+ selection.y = MIN (view->selection_start.y, event->y);
+ selection.width = ABS (view->selection_start.x - event->x) + 1;
+ selection.height = ABS (view->selection_start.y - event->y) + 1;
+ view_rect_to_doc_rect (view, &selection, &view->selection);
+
+ gtk_widget_queue_draw (widget);
+ } else if (view->document) {
+ EvLink *link;
+
+ link = get_link_at_location (view, event->x, event->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);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/* FIXME: standardize this sometime */
+static void
+go_to_link (EvView *view, EvLink *link)
+{
+ EvLinkType type;
+ const char *uri;
+ int page;
+
+ type = ev_link_get_link_type (link);
+
+ switch (type) {
+ case EV_LINK_TYPE_TITLE:
+ break;
+ case EV_LINK_TYPE_PAGE:
+ page = ev_link_get_page (link);
+ ev_page_cache_set_current_page (view->page_cache, page);
+ break;
+ case EV_LINK_TYPE_EXTERNAL_URI:
+ uri = ev_link_get_uri (link);
+ gnome_vfs_url_show (uri);
+ break;
+ }
+}
+
+
+static gboolean
+ev_view_button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ EvView *view = EV_VIEW (widget);
+
+ view->pressed_button = -1;
+
+ if (view->has_selection) {
+ ev_view_update_primary_selection (view);
+ } else if (view->document) {
+ EvLink *link;
+
+ link = get_link_at_location (view, event->x, event->y);
+ if (link) {
+ go_to_link (view, link);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+on_adjustment_value_changed (GtkAdjustment *adjustment,
+ EvView *view)
+{
+ view_update_adjustments (view);
+}
+
+static void
+set_scroll_adjustment (EvView *view,
+ GtkOrientation orientation,
+ GtkAdjustment *adjustment)
+{
+ GtkAdjustment **to_set;
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ to_set = &view->hadjustment;
+ else
+ to_set = &view->vadjustment;
+
+ if (*to_set != adjustment) {
+ if (*to_set) {
+ g_signal_handlers_disconnect_by_func (*to_set,
+ (gpointer) on_adjustment_value_changed,
+ view);
+ g_object_unref (*to_set);
+ }
+
+ *to_set = adjustment;
+ view_set_adjustment_values (view, orientation);
+
+ if (*to_set) {
+ g_object_ref (*to_set);
+ g_signal_connect (*to_set, "value_changed",
+ G_CALLBACK (on_adjustment_value_changed), view);
+ }
+ }
+}
+
+static void
+ev_view_set_scroll_adjustments (EvView *view,
+ GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment)