+ dual_page = dual_page != FALSE;
+
+ if (view->dual_page == dual_page)
+ return;
+
+ view->pending_scroll = SCROLL_TO_PAGE_POSITION;
+ view->dual_page = dual_page;
+ /* FIXME: if we're keeping the pixbuf cache around, we should extend the
+ * preload_cache_size to be 2 if dual_page is set.
+ */
+ gtk_widget_queue_resize (GTK_WIDGET (view));
+
+ g_object_notify (G_OBJECT (view), "dual-page");
+}
+
+void
+ev_view_set_fullscreen (EvView *view,
+ gboolean fullscreen)
+{
+ g_return_if_fail (EV_IS_VIEW (view));
+
+ fullscreen = fullscreen != FALSE;
+
+ if (view->fullscreen == fullscreen)
+ return;
+
+ view->fullscreen = fullscreen;
+ gtk_widget_queue_resize (GTK_WIDGET (view));
+
+ g_object_notify (G_OBJECT (view), "fullscreen");
+}
+
+gboolean
+ev_view_get_fullscreen (EvView *view)
+{
+ g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
+
+ return view->fullscreen;
+}
+
+void
+ev_view_set_presentation (EvView *view,
+ gboolean presentation)
+{
+ g_return_if_fail (EV_IS_VIEW (view));
+
+ presentation = presentation != FALSE;
+
+ if (view->presentation == presentation)
+ return;
+
+ view->presentation = presentation;
+ view->pending_scroll = SCROLL_TO_PAGE_POSITION;
+ gtk_widget_queue_resize (GTK_WIDGET (view));
+
+ if (GTK_WIDGET_REALIZED (view)) {
+ if (view->presentation)
+ gdk_window_set_background (GTK_WIDGET(view)->window,
+ >K_WIDGET (view)->style->black);
+ else
+ gdk_window_set_background (GTK_WIDGET(view)->window,
+ >K_WIDGET (view)->style->mid [GTK_STATE_NORMAL]);
+ }
+
+
+ g_object_notify (G_OBJECT (view), "presentation");
+}
+
+gboolean
+ev_view_get_presentation (EvView *view)
+{
+ g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
+
+ return view->presentation;
+}
+
+void
+ev_view_set_sizing_mode (EvView *view,
+ EvSizingMode sizing_mode)
+{
+ g_return_if_fail (EV_IS_VIEW (view));
+
+ if (view->sizing_mode == sizing_mode)
+ return;
+
+ view->sizing_mode = sizing_mode;
+ gtk_widget_queue_resize (GTK_WIDGET (view));
+
+ g_object_notify (G_OBJECT (view), "sizing-mode");
+}
+
+EvSizingMode
+ev_view_get_sizing_mode (EvView *view)
+{
+ g_return_val_if_fail (EV_IS_VIEW (view), EV_SIZING_FREE);
+
+ return view->sizing_mode;
+}
+
+gboolean
+ev_view_can_zoom_in (EvView *view)
+{
+ return view->scale * ZOOM_IN_FACTOR <= MAX_SCALE;
+}
+
+gboolean
+ev_view_can_zoom_out (EvView *view)
+{
+ return view->scale * ZOOM_OUT_FACTOR >= MIN_SCALE;
+}
+
+void
+ev_view_zoom_in (EvView *view)
+{
+ g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
+
+ view->pending_scroll = SCROLL_TO_CENTER;
+ ev_view_set_zoom (view, ZOOM_IN_FACTOR, TRUE);
+}
+
+void
+ev_view_zoom_out (EvView *view)
+{
+ g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
+
+ view->pending_scroll = SCROLL_TO_CENTER;
+ ev_view_set_zoom (view, ZOOM_OUT_FACTOR, TRUE);
+}
+
+void
+ev_view_rotate_right (EvView *view)
+{
+ int rotation = view->rotation + 90;
+
+ if (rotation >= 360) {
+ rotation -= 360;
+ }
+
+ ev_view_set_rotation (view, rotation);
+}
+
+void
+ev_view_rotate_left (EvView *view)
+{
+ int rotation = view->rotation - 90;
+
+ if (rotation < 0) {
+ rotation += 360;
+ }
+
+ ev_view_set_rotation (view, rotation);
+}
+
+void
+ev_view_set_rotation (EvView *view, int rotation)
+{
+ view->rotation = rotation;
+
+ if (view->pixbuf_cache) {
+ ev_pixbuf_cache_clear (view->pixbuf_cache);
+ gtk_widget_queue_resize (GTK_WIDGET (view));
+ }
+
+ if (rotation != 0)
+ clear_selection (view);
+
+ g_object_notify (G_OBJECT (view), "rotation");
+}
+
+int
+ev_view_get_rotation (EvView *view)
+{
+ return view->rotation;
+}
+
+static double
+zoom_for_size_fit_width (int doc_width,
+ int doc_height,
+ int target_width,
+ int target_height,
+ int vsb_width)
+{
+ double scale;
+
+ scale = (double)target_width / doc_width;
+
+ if (doc_height * scale > target_height)
+ scale = (double) (target_width - vsb_width) / 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,
+ int target_width,
+ int target_height,
+ int vsb_width,
+ int hsb_width)
+{
+ double w_scale;
+ double h_scale;
+
+ w_scale = (double)target_width / doc_width;
+ h_scale = (double)target_height / doc_height;
+
+ if (doc_height * w_scale > target_height)
+ w_scale = (double) (target_width - vsb_width) / doc_width;
+ if (doc_width * h_scale > target_width)
+ h_scale = (double) (target_height - hsb_width) / doc_height;
+
+ return MIN (w_scale, h_scale);
+}
+
+
+static void
+ev_view_zoom_for_size_presentation (EvView *view,
+ int width,
+ int height)
+{
+ int doc_width, doc_height;
+ gdouble scale;
+
+ ev_page_cache_get_size (view->page_cache,
+ view->current_page,
+ view->rotation,
+ 1.0,
+ &doc_width,
+ &doc_height);
+ scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, 0, 0);
+ ev_view_set_zoom (view, scale, FALSE);
+}
+
+static void
+ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
+ int width,
+ int height,
+ int vsb_width,
+ int hsb_height)
+{
+ int doc_width, doc_height;
+ GtkBorder border;
+ gdouble scale;
+
+ ev_page_cache_get_max_width (view->page_cache,
+ view->rotation,
+ 1.0,
+ &doc_width);
+ ev_page_cache_get_max_height (view->page_cache,
+ view->rotation,
+ 1.0,
+ &doc_height);
+ compute_border (view, doc_width, doc_height, &border);
+
+ doc_width = doc_width * 2;
+ width -= (2 * (border.left + border.right) + 3 * view->spacing);
+ height -= (border.top + border.bottom + 2 * view->spacing - 1);
+
+ /* FIXME: We really need to calculate the overall height here, not the
+ * page height. We assume there's always a vertical scrollbar for
+ * now. We need to fix this. */
+ if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
+ scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
+ else if (view->sizing_mode == EV_SIZING_BEST_FIT)
+ scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
+ else
+ g_assert_not_reached ();
+
+ ev_view_set_zoom (view, scale, FALSE);
+}
+
+static void
+ev_view_zoom_for_size_continuous (EvView *view,
+ int width,
+ int height,
+ int vsb_width,
+ int hsb_height)
+{
+ int doc_width, doc_height;
+ GtkBorder border;
+ gdouble scale;
+
+ ev_page_cache_get_max_width (view->page_cache,
+ view->rotation,
+ 1.0,
+ &doc_width);
+ ev_page_cache_get_max_height (view->page_cache,
+ view->rotation,
+ 1.0,
+ &doc_height);
+ compute_border (view, doc_width, doc_height, &border);
+
+ width -= (border.left + border.right + 2 * view->spacing);
+ height -= (border.top + border.bottom + 2 * view->spacing - 1);
+
+ /* FIXME: We really need to calculate the overall height here, not the
+ * page height. We assume there's always a vertical scrollbar for
+ * now. We need to fix this. */
+ if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
+ scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
+ else if (view->sizing_mode == EV_SIZING_BEST_FIT)
+ scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
+ else
+ g_assert_not_reached ();
+
+ ev_view_set_zoom (view, scale, FALSE);
+}
+
+static void
+ev_view_zoom_for_size_dual_page (EvView *view,
+ int width,
+ int height,
+ int vsb_width,
+ int hsb_height)
+{
+ GtkBorder border;
+ gint doc_width, doc_height;
+ gdouble scale;
+ gint other_page;
+
+ other_page = view->current_page ^ 1;
+
+ /* Find the largest of the two. */
+ ev_page_cache_get_size (view->page_cache,
+ view->current_page,
+ view->rotation,
+ 1.0,
+ &doc_width, &doc_height);
+
+ if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
+ gint width_2, height_2;
+ ev_page_cache_get_size (view->page_cache,
+ other_page,
+ view->rotation,
+ 1.0,
+ &width_2, &height_2);
+ if (width_2 > doc_width)
+ doc_width = width_2;
+ if (height_2 > doc_height)
+ doc_height = height_2;
+ }
+ compute_border (view, doc_width, doc_height, &border);
+
+ doc_width = doc_width * 2;
+ width -= ((border.left + border.right)* 2 + 3 * 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);
+}
+
+static void
+ev_view_zoom_for_size_single_page (EvView *view,
+ int width,
+ int height,
+ int vsb_width,
+ int hsb_height)
+{
+ int doc_width, doc_height;
+ GtkBorder border;
+ gdouble scale;
+
+ 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;