+ ev_page_cache_get_size (view->page_cache,
+ view->current_page,
+ view->rotation,
+ 1.0,
+ &doc_width,
+ &doc_height);
+ /* Get an approximate border */
+ compute_border (view, width, height, &border);
+
+ width -= (border.left + border.right + 2 * view->spacing);
+ height -= (border.top + border.bottom + 2 * view->spacing);
+
+ if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
+ scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
+ else if (view->sizing_mode == EV_SIZING_BEST_FIT)
+ scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
+ else
+ g_assert_not_reached ();
+
+ ev_view_set_zoom (view, scale, FALSE);
+}
+
+void
+ev_view_set_zoom_for_size (EvView *view,
+ int width,
+ int height,
+ int vsb_width,
+ int hsb_height)
+{
+ g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
+ view->sizing_mode == EV_SIZING_BEST_FIT);
+ g_return_if_fail (width >= 0);
+ g_return_if_fail (height >= 0);
+
+ if (view->document == NULL)
+ return;
+
+ if (view->presentation)
+ ev_view_zoom_for_size_presentation (view, width, height);
+ else if (view->continuous && view->dual_page)
+ ev_view_zoom_for_size_continuous_and_dual_page (view, width, height, vsb_width, hsb_height);
+ else if (view->continuous)
+ ev_view_zoom_for_size_continuous (view, width, height, vsb_width, hsb_height);
+ else if (view->dual_page)
+ ev_view_zoom_for_size_dual_page (view, width, height, vsb_width, hsb_height);
+ else
+ ev_view_zoom_for_size_single_page (view, width, height, vsb_width, hsb_height);
+}
+
+/*** Status text messages ***/
+
+const char *
+ev_view_get_status (EvView *view)
+{
+ g_return_val_if_fail (EV_IS_VIEW (view), NULL);
+
+ return view->status;
+}
+
+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
+update_find_status_message (EvView *view, gboolean this_page)
+{
+ char *message;
+
+ if (this_page) {
+ int results;
+
+ results = ev_document_find_get_n_results
+ (EV_DOCUMENT_FIND (view->document),
+ view->current_page);
+ /* TRANS: Sometimes this could be better translated as
+ "%d hit(s) on this page". Therefore this string
+ contains plural cases. */
+ message = g_strdup_printf (ngettext ("%d found on this page",
+ "%d found on this page",
+ results),
+ results);
+ } else {
+ double percent;
+
+ percent = ev_document_find_get_progress
+ (EV_DOCUMENT_FIND (view->document));
+ message = g_strdup_printf (_("%3d%% remaining to search"),
+ (int) ((1.0 - percent) * 100));
+
+ }
+ ev_view_set_find_status (view, message);
+ g_free (message);
+}
+
+const char *
+ev_view_get_find_status (EvView *view)
+{
+ g_return_val_if_fail (EV_IS_VIEW (view), NULL);
+
+ return view->find_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");
+}
+
+/*** Find ***/
+
+static void
+jump_to_find_result (EvView *view)
+{
+ EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
+ EvRectangle rect;
+ GdkRectangle view_rect;
+ int n_results;
+ int page = view->current_page;
+
+ n_results = ev_document_find_get_n_results (find, page);
+
+ if (n_results > 0 && view->find_result < n_results) {
+ ev_document_find_get_result
+ (find, page, view->find_result, &rect);
+
+ doc_rect_to_view_rect (view, page, &rect, &view_rect);
+ ensure_rectangle_is_visible (view, &view_rect);
+ }
+}
+
+/**
+ * jump_to_find_page
+ * @view: #EvView instance
+ * @direction: Direction to look
+ * @shift: Shift from current page
+ *
+ * Jumps to the first page that has occurences of searched word.
+ * Uses a direction where to look and a shift from current page.
+ *
+ */
+static void
+jump_to_find_page (EvView *view, EvViewFindDirection direction, gint shift)
+{
+ int n_pages, i;
+
+ n_pages = ev_page_cache_get_n_pages (view->page_cache);
+
+ for (i = 0; i < n_pages; i++) {
+ int has_results;
+ int page;
+
+ if (direction == EV_VIEW_FIND_NEXT)
+ page = view->current_page + i;
+ else
+ page = view->current_page - i;
+ page += shift;
+
+ 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) {
+ break;
+ } else if (has_results == 1) {
+ ev_page_cache_set_current_page (view->page_cache, page);
+ break;
+ }
+ }
+}
+
+gboolean
+ev_view_can_find_next (EvView *view)
+{
+ if (EV_IS_DOCUMENT_FIND (view->document)) {
+ EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
+ int i, n_pages;
+
+ 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 FALSE;
+}
+
+void
+ev_view_find_next (EvView *view)
+{
+ int n_results, n_pages;
+ EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
+
+ n_results = ev_document_find_get_n_results (find, view->current_page);
+
+ n_pages = ev_page_cache_get_n_pages (view->page_cache);
+
+ view->find_result++;
+
+ if (view->find_result >= n_results) {
+
+ view->find_result = 0;
+ jump_to_find_page (view, EV_VIEW_FIND_NEXT, 1);
+ jump_to_find_result (view);
+ } else {
+ jump_to_find_result (view);
+ gtk_widget_queue_draw (GTK_WIDGET (view));
+ }
+}
+
+gboolean
+ev_view_can_find_previous (EvView *view)
+{
+ if (EV_IS_DOCUMENT_FIND (view->document)) {
+ EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
+ int i, n_pages;
+
+ n_pages = ev_page_cache_get_n_pages (view->page_cache);
+ for (i = n_pages - 1; i >= 0; i--) {
+ if (ev_document_find_get_n_results (find, i) > 0) {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+void
+ev_view_find_previous (EvView *view)
+{
+ int n_results, n_pages;
+ EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
+ EvPageCache *page_cache;
+
+ 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);
+
+ view->find_result--;
+
+ if (view->find_result < 0) {
+
+ jump_to_find_page (view, EV_VIEW_FIND_PREV, -1);
+ 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));
+ }
+}
+
+void ev_view_search_changed (EvView *view)
+{
+ /* search string has changed, focus on new search result */
+ view->jump_to_find_result = TRUE;
+}
+
+/*** Selections ***/
+
+/* compute_new_selection_rect/text calculates the area currently selected by
+ * view_rect. each handles a different mode;
+ */
+static GList *
+compute_new_selection_rect (EvView *view,
+ GdkPoint *start,
+ GdkPoint *stop)
+{
+ GdkRectangle view_rect;
+ int n_pages, i;
+ GList *list = NULL;
+
+ g_assert (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE);
+
+ view_rect.x = MIN (start->x, stop->x);
+ view_rect.y = MIN (start->y, stop->y);
+ view_rect.width = MAX (start->x, stop->x) - view_rect.x;
+ view_rect.width = MAX (start->y, stop->y) - view_rect.y;
+
+ n_pages = ev_page_cache_get_n_pages (view->page_cache);
+
+ for (i = 0; i < n_pages; i++) {
+ GdkRectangle page_area;
+ GtkBorder border;
+
+ if (get_page_extents (view, i, &page_area, &border)) {
+ GdkRectangle overlap;
+
+ if (gdk_rectangle_intersect (&page_area, &view_rect, &overlap)) {
+ EvViewSelection *selection;
+
+ selection = g_new0 (EvViewSelection, 1);
+ selection->page = i;
+ view_rect_to_doc_rect (view, &overlap, &page_area,
+ &(selection->rect));
+
+ list = g_list_append (list, selection);
+ }
+ }
+ }
+
+ return list;
+}
+
+static gboolean
+gdk_rectangle_point_in (GdkRectangle *rectangle,
+ GdkPoint *point)
+{
+ return rectangle->x <= point->x &&
+ rectangle->y <= point->y &&
+ point->x < rectangle->x + rectangle->width &&
+ point->y < rectangle->y + rectangle->height;
+}
+
+static GList *
+compute_new_selection_text (EvView *view,
+ GdkPoint *start,
+ GdkPoint *stop)
+{
+ int n_pages, i, first, last;
+ GList *list = NULL;
+ EvViewSelection *selection;
+ gint width, height;
+ int start_page, end_page;
+
+ g_assert (view->selection_mode == EV_VIEW_SELECTION_TEXT);
+
+ n_pages = ev_page_cache_get_n_pages (view->page_cache);
+
+ /* First figure out the range of pages the selection
+ * affects. */
+ first = n_pages;
+ last = 0;
+ 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;
+
+ get_page_extents (view, i, &page_area, &border);
+ if (gdk_rectangle_point_in (&page_area, start) ||
+ gdk_rectangle_point_in (&page_area, stop)) {
+ if (first == n_pages)
+ first = i;
+ last = i;
+ }
+
+ }
+
+
+
+ /* Now create a list of EvViewSelection's for the affected
+ * pages. This could be an empty list, a list of just one
+ * page or a number of pages.*/
+ for (i = first; i < last + 1; i++) {
+ GdkRectangle page_area;
+ GtkBorder border;
+ GdkPoint *point;
+
+ ev_page_cache_get_size (view->page_cache, i,
+ view->rotation,
+ 1.0, &width, &height);
+
+ selection = g_new0 (EvViewSelection, 1);
+ selection->page = i;
+ selection->rect.x1 = selection->rect.y1 = 0;
+ selection->rect.x2 = width;
+ selection->rect.y2 = height;
+
+ get_page_extents (view, i, &page_area, &border);
+
+ if (gdk_rectangle_point_in (&page_area, start))
+ point = start;
+ else
+ point = stop;
+
+ if (i == first)
+ view_point_to_doc_point (view, point, &page_area,
+ &selection->rect.x1,
+ &selection->rect.y1);
+
+ /* If the selection is contained within just one page,
+ * make sure we don't write 'start' into both points
+ * in selection->rect. */
+ if (first == last)
+ point = stop;
+
+ if (i == last)
+ view_point_to_doc_point (view, point, &page_area,
+ &selection->rect.x2,
+ &selection->rect.y2);
+
+ list = g_list_append (list, selection);
+ }
+
+ return list;
+}
+
+/* This function takes the newly calculated list, and figures out which regions
+ * have changed. It then queues a redraw approporiately.
+ */
+static void
+merge_selection_region (EvView *view,
+ GList *new_list)
+{
+ GList *old_list;
+ GList *new_list_ptr, *old_list_ptr;
+
+ /* 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);
+ g_object_notify (G_OBJECT (view), "has-selection");
+
+ 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 ();
+ }
+
+ /* 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);
+}