1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /* this file is part of evince, a gnome document viewer
4 * Copyright (C) 2004 Red Hat, Inc
6 * Evince is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Evince is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22 #include <gtk/gtkalignment.h>
23 #include <glib/gi18n.h>
24 #include <gtk/gtkbindings.h>
25 #include <gtk/gtkselection.h>
26 #include <gtk/gtkclipboard.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <libgnomevfs/gnome-vfs-utils.h>
30 #include "ev-marshal.h"
32 #include "ev-document-find.h"
33 #include "ev-document-misc.h"
35 #include "ev-job-queue.h"
36 #include "ev-page-cache.h"
37 #include "ev-pixbuf-cache.h"
39 #define EV_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_VIEW, EvViewClass))
40 #define EV_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_VIEW))
41 #define EV_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_VIEW, EvViewClass))
59 TARGET_TEXT_BUFFER_CONTENTS
62 static const GtkTargetEntry targets[] = {
63 { "STRING", 0, TARGET_STRING },
64 { "TEXT", 0, TARGET_TEXT },
65 { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
66 { "UTF8_STRING", 0, TARGET_UTF8_STRING },
70 EV_VIEW_CURSOR_NORMAL,
73 EV_VIEW_CURSOR_HIDDEN,
77 #define ZOOM_IN_FACTOR 1.2
78 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR)
80 #define MIN_SCALE 0.05409
96 GtkWidget parent_instance;
100 GdkWindow *bin_window;
109 gboolean pressed_button;
110 GdkPoint selection_start;
114 GtkAdjustment *hadjustment;
115 GtkAdjustment *vadjustment;
117 EvPageCache *page_cache;
118 EvPixbufCache *pixbuf_cache;
124 EvJobRender *current_job;
136 gboolean presentation;
137 EvSizingMode sizing_mode;
140 struct _EvViewClass {
141 GtkWidgetClass parent_class;
143 void (*set_scroll_adjustments) (EvView *view,
144 GtkAdjustment *hadjustment,
145 GtkAdjustment *vadjustment);
146 void (*scroll_view) (EvView *view,
147 GtkScrollType scroll,
148 gboolean horizontal);
153 static void ev_view_set_scroll_adjustments (EvView *view,
154 GtkAdjustment *hadjustment,
155 GtkAdjustment *vadjustment);
156 static void get_bounding_box_size (EvView *view,
159 static void view_update_range_and_current_page (EvView *view);
161 static void page_changed_cb (EvPageCache *page_cache,
166 G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET)
168 /*** Helper functions ***/
171 view_update_adjustments (EvView *view)
173 int old_x = view->scroll_x;
174 int old_y = view->scroll_y;
176 if (view->hadjustment)
177 view->scroll_x = view->hadjustment->value;
181 if (view->vadjustment)
182 view->scroll_y = view->vadjustment->value;
186 if (GTK_WIDGET_REALIZED (view) &&
187 (view->scroll_x != old_x || view->scroll_y != old_y)) {
188 gdk_window_move (view->bin_window, - view->scroll_x, - view->scroll_y);
189 // gdk_window_process_updates (view->bin_window, TRUE);
193 view_update_range_and_current_page (view);
197 view_set_adjustment_values (EvView *view,
198 GtkOrientation orientation)
200 GtkWidget *widget = GTK_WIDGET (view);
201 GtkAdjustment *adjustment;
202 gboolean value_changed = FALSE;
206 if (orientation == GTK_ORIENTATION_HORIZONTAL) {
207 requisition = widget->requisition.width;
208 allocation = widget->allocation.width;
209 adjustment = view->hadjustment;
211 requisition = widget->requisition.height;
212 allocation = widget->allocation.height;
213 adjustment = view->vadjustment;
219 adjustment->page_size = allocation;
220 adjustment->step_increment = allocation * 0.1;
221 adjustment->page_increment = allocation * 0.9;
222 adjustment->lower = 0;
223 adjustment->upper = MAX (allocation, requisition);
225 if (adjustment->value > adjustment->upper - adjustment->page_size) {
226 adjustment->value = adjustment->upper - adjustment->page_size;
227 value_changed = TRUE;
230 gtk_adjustment_changed (adjustment);
232 gtk_adjustment_value_changed (adjustment);
236 view_update_range_and_current_page (EvView *view)
238 /* Presentation trumps all other modes */
239 if (view->presentation) {
240 view->start_page = view->current_page;
241 view->end_page = view->current_page;
242 } else if (view->continuous) {
243 GdkRectangle current_area, unused, page_area;
245 gboolean found = FALSE;
248 get_bounding_box_size (view, &(page_area.width), &(page_area.height));
249 page_area.x = view->spacing;
250 page_area.y = view->spacing;
252 if (view->hadjustment) {
253 current_area.x = view->hadjustment->value;
254 current_area.width = view->hadjustment->page_size;
256 current_area.x = page_area.x;
257 current_area.width = page_area.width;
260 if (view->vadjustment) {
261 current_area.y = view->vadjustment->value;
262 current_area.height = view->vadjustment->page_size;
264 current_area.y = page_area.y;
265 current_area.height = page_area.height;
268 for (i = 0; i < ev_page_cache_get_n_pages (view->page_cache); i++) {
269 if (gdk_rectangle_intersect (¤t_area, &page_area, &unused)) {
271 view->start_page = i;
279 if (view->dual_page) {
281 page_area.x += page_area.width + view->spacing;
283 page_area.x = view->spacing;
284 page_area.y += page_area.height + view->spacing;
287 page_area.y += page_area.height + view->spacing;
291 current_page = ev_page_cache_get_current_page (view->page_cache);
293 if (current_page < view->start_page || current_page > view->end_page) {
294 g_signal_handlers_block_by_func (view->page_cache, page_changed_cb, view);
295 ev_page_cache_set_current_page (view->page_cache, view->start_page);
296 g_signal_handlers_unblock_by_func (view->page_cache, page_changed_cb, view);
299 if (view->dual_page) {
300 if (view->current_page % 2 == 0) {
301 view->start_page = view->current_page;
302 if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache))
303 view->end_page = view->start_page + 1;
305 view->start_page = view->current_page - 1;
306 view->end_page = view->current_page;
309 view->start_page = view->current_page;
310 view->end_page = view->current_page;
314 ev_pixbuf_cache_set_page_range (view->pixbuf_cache,
321 clear_selection (EvView *view)
323 g_list_foreach (view->selections, (GFunc)g_free, NULL);
324 view->selections = NULL;
327 /*** Virtual function implementations ***/
330 ev_view_finalize (GObject *object)
332 EvView *view = EV_VIEW (object);
336 g_free (view->status);
337 g_free (view->find_status);
339 clear_selection (view);
341 G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
345 ev_view_destroy (GtkObject *object)
347 EvView *view = EV_VIEW (object);
349 if (view->document) {
350 g_object_unref (view->document);
351 view->document = NULL;
353 if (view->pixbuf_cache) {
354 g_object_unref (view->pixbuf_cache);
355 view->pixbuf_cache = NULL;
357 ev_view_set_scroll_adjustments (view, NULL, NULL);
359 GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object);
363 compute_border (EvView *view, int width, int height, GtkBorder *border)
365 if (view->presentation) {
371 ev_document_misc_get_page_border_size (width, height, border);
376 get_page_extents (EvView *view,
378 GdkRectangle *page_area,
384 widget = GTK_WIDGET (view);
386 /* Quick sanity check */
387 if (view->presentation) {
388 if (view->current_page != page)
390 } else if (view->continuous) {
391 if (page < view->start_page ||
392 page > view->end_page)
394 } else if (view->dual_page) {
395 if (ABS (page - view->current_page) > 1)
398 if (view->current_page != page)
402 /* Get the size of the page */
403 ev_page_cache_get_size (view->page_cache, page,
406 compute_border (view, width, height, border);
407 page_area->width = width + border->left + border->right;
408 page_area->height = height + border->top + border->bottom;
410 if (view->presentation) {
411 page_area->x = (MAX (0, widget->allocation.width - width))/2;
412 page_area->y = (MAX (0, widget->allocation.height - height))/2;
413 } else if (view->continuous) {
414 gint max_width, max_height;
417 get_bounding_box_size (view, &max_width, &max_height);
418 /* Get the location of the bounding box */
419 if (view->dual_page) {
420 x = view->spacing + (page % 2) * (max_width + view->spacing);
421 y = view->spacing + (page / 2) * (max_height + view->spacing);
422 x = x + MAX (0, widget->allocation.width - (max_width * 2 + view->spacing * 3))/2;
425 y = view->spacing + page * (max_height + view->spacing);
426 x = x + MAX (0, widget->allocation.width - (max_width + view->spacing * 2))/2;
432 if (view->dual_page) {
433 gint width_2, height_2;
434 gint max_width = width;
435 gint max_height = height;
436 GtkBorder overall_border;
439 other_page = page ^ 1;
441 /* First, we get the bounding box of the two pages */
442 if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
443 ev_page_cache_get_size (view->page_cache,
446 &width_2, &height_2);
449 if (height_2 > height)
450 max_height = height_2;
452 compute_border (view, max_width, max_height, &overall_border);
454 /* Find the offsets */
458 /* Adjust for being the left or right page */
460 x = x + max_width - width;
462 x = x + (max_width + overall_border.left + overall_border.right) + view->spacing;
464 y = y + (max_height - height)/2;
466 /* Adjust for extra allocation */
467 x = x + MAX (0, widget->allocation.width -
468 ((max_width + overall_border.left + overall_border.right) * 2 + view->spacing * 3))/2;
469 y = y + MAX (0, widget->allocation.height - (height + view->spacing * 2))/2;
474 /* Adjust for extra allocation */
475 x = x + MAX (0, widget->allocation.width - (width + view->spacing * 2))/2;
476 y = y + MAX (0, widget->allocation.height - (height + view->spacing * 2))/2;
487 view_rect_to_doc_rect (EvView *view,
488 GdkRectangle *view_rect,
489 GdkRectangle *page_area,
490 EvRectangle *doc_rect)
492 doc_rect->x1 = floor ((view_rect->x - page_area->x) / view->scale);
493 doc_rect->y1 = floor ((view_rect->y - page_area->y) / view->scale);
494 doc_rect->x2 = doc_rect->x1 + ceil (view_rect->width / view->scale);
495 doc_rect->y2 = doc_rect->y1 + ceil (view_rect->height / view->scale);
499 compute_selections (EvView *view, GdkRectangle *view_rect)
503 clear_selection (view);
505 n_pages = ev_page_cache_get_n_pages (view->page_cache);
506 for (i = 0; i < n_pages; i++) {
507 GdkRectangle page_area;
510 if (get_page_extents (view, i, &page_area, &border)) {
511 GdkRectangle overlap;
513 if (gdk_rectangle_intersect (&page_area, view_rect, &overlap)) {
514 EvViewSelection *selection;
516 selection = g_new0 (EvViewSelection, 1);
518 view_rect_to_doc_rect (view, &overlap, &page_area,
521 view->selections = g_list_append
522 (view->selections, selection);
529 doc_rect_to_view_rect (EvView *view,
531 EvRectangle *doc_rect,
532 GdkRectangle *view_rect)
534 GdkRectangle page_area;
538 get_page_extents (view, page, &page_area, &border);
540 width = doc_rect->x2 - doc_rect->x1;
541 height = doc_rect->y2 - doc_rect->y1;
542 view_rect->x = floor (doc_rect->x1 * view->scale) + page_area.x;
543 view_rect->y = floor (doc_rect->y1 * view->scale) + page_area.y;
544 view_rect->width = ceil (width * view->scale);
545 view_rect->height = ceil (height * view->scale);
549 get_bounding_box_size (EvView *view, int *max_width, int *max_height)
555 ev_page_cache_get_max_width_size (view->page_cache,
558 compute_border (view, width, height, &border);
559 *max_width = width + border.left + border.right;
564 ev_page_cache_get_max_height_size (view->page_cache,
567 compute_border (view, width, height, &border);
568 *max_height = height + border.top + border.bottom;
574 ev_view_size_request_continuous_and_dual_page (EvView *view,
575 GtkRequisition *requisition)
577 int max_width, max_height;
580 get_bounding_box_size (view, &max_width, &max_height);
582 n_rows = (1 + ev_page_cache_get_n_pages (view->page_cache)) / 2;
584 requisition->width = (max_width * 2) + (view->spacing * 3);
585 requisition->height = max_height * n_rows + (view->spacing * (n_rows + 1));
587 if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
588 requisition->width = 1;
589 } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
590 requisition->width = 1;
591 /* FIXME: This could actually be set on one page docs or docs
592 * with a strange aspect ratio. */
593 /* requisition->height = 1;*/
598 ev_view_size_request_continuous (EvView *view,
599 GtkRequisition *requisition)
601 int max_width, max_height;
604 get_bounding_box_size (view, &max_width, &max_height);
606 n_pages = ev_page_cache_get_n_pages (view->page_cache);
608 requisition->width = max_width + (view->spacing * 2);
609 requisition->height = max_height * n_pages + (view->spacing * (n_pages + 1));
611 if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
612 requisition->width = 1;
613 } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
614 requisition->width = 1;
615 /* FIXME: This could actually be set on one page docs or docs
616 * with a strange aspect ratio. */
617 /* requisition->height = 1;*/
622 ev_view_size_request_dual_page (EvView *view,
623 GtkRequisition *requisition)
628 /* Find the largest of the two. */
629 ev_page_cache_get_size (view->page_cache,
633 if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache)) {
634 gint width_2, height_2;
635 ev_page_cache_get_size (view->page_cache,
636 view->current_page + 1,
638 &width_2, &height_2);
639 if (width_2 > width) {
644 compute_border (view, width, height, &border);
646 requisition->width = ((width + border.left + border.right) * 2) +
648 requisition->height = (height + border.top + border.bottom) +
651 if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
652 requisition->width = 1;
653 } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
654 requisition->width = 1;
655 requisition->height = 1;
660 ev_view_size_request_single_page (EvView *view,
661 GtkRequisition *requisition)
666 ev_page_cache_get_size (view->page_cache,
670 compute_border (view, width, height, &border);
672 requisition->width = width + border.left + border.right + (2 * view->spacing);
673 requisition->height = height + border.top + border.bottom + (2 * view->spacing);
675 if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
676 requisition->width = 1;
677 requisition->height = height + border.top + border.bottom + (2 * view->spacing);
678 } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
679 requisition->width = 1;
680 requisition->height = 1;
685 ev_view_size_request (GtkWidget *widget,
686 GtkRequisition *requisition)
688 EvView *view = EV_VIEW (widget);
690 if (!GTK_WIDGET_REALIZED (widget))
693 if (view->document == NULL) {
694 requisition->width = 1;
695 requisition->height = 1;
699 if (view->presentation) {
700 requisition->width = 1;
701 requisition->height = 1;
705 if (view->continuous && view->dual_page)
706 ev_view_size_request_continuous_and_dual_page (view, requisition);
707 else if (view->continuous)
708 ev_view_size_request_continuous (view, requisition);
709 else if (view->dual_page)
710 ev_view_size_request_dual_page (view, requisition);
712 ev_view_size_request_single_page (view, requisition);
716 ev_view_size_allocate (GtkWidget *widget,
717 GtkAllocation *allocation)
719 EvView *view = EV_VIEW (widget);
721 GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation);
723 view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
724 view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
726 if (GTK_WIDGET_REALIZED (widget)) {
727 gdk_window_resize (view->bin_window,
728 MAX (widget->allocation.width, widget->requisition.width),
729 MAX (widget->allocation.height, widget->requisition.height));
733 view_update_range_and_current_page (view);
737 ev_view_realize (GtkWidget *widget)
739 EvView *view = EV_VIEW (widget);
740 GdkWindowAttr attributes;
742 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
745 attributes.window_type = GDK_WINDOW_CHILD;
746 attributes.wclass = GDK_INPUT_OUTPUT;
747 attributes.visual = gtk_widget_get_visual (widget);
748 attributes.colormap = gtk_widget_get_colormap (widget);
750 attributes.x = widget->allocation.x;
751 attributes.y = widget->allocation.y;
752 attributes.width = widget->allocation.width;
753 attributes.height = widget->allocation.height;
754 attributes.event_mask = 0;
756 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
758 GDK_WA_X | GDK_WA_Y |
761 gdk_window_set_user_data (widget->window, widget);
762 widget->style = gtk_style_attach (widget->style, widget->window);
763 gdk_window_set_background (widget->window, &widget->style->mid[widget->state]);
767 attributes.width = MAX (widget->allocation.width, widget->requisition.width);
768 attributes.height = MAX (widget->allocation.height, widget->requisition.height);
769 attributes.event_mask = GDK_EXPOSURE_MASK |
770 GDK_BUTTON_PRESS_MASK |
771 GDK_BUTTON_RELEASE_MASK |
774 GDK_POINTER_MOTION_MASK |
775 GDK_LEAVE_NOTIFY_MASK;
777 view->bin_window = gdk_window_new (widget->window,
779 GDK_WA_X | GDK_WA_Y |
782 gdk_window_set_user_data (view->bin_window, widget);
783 gdk_window_show (view->bin_window);
785 widget->style = gtk_style_attach (widget->style, view->bin_window);
786 if (view->presentation)
787 gdk_window_set_background (view->bin_window, &widget->style->black);
789 gdk_window_set_background (view->bin_window, &widget->style->mid [GTK_STATE_NORMAL]);
791 if (view->document) {
792 /* We can't get page size without a target, so we have to
793 * queue a size request at realization. Could be fixed
794 * with EvDocument changes to allow setting a GdkScreen
795 * without setting a target.
797 gtk_widget_queue_resize (widget);
802 ev_view_unrealize (GtkWidget *widget)
804 EvView *view = EV_VIEW (widget);
806 gdk_window_set_user_data (view->bin_window, NULL);
807 gdk_window_destroy (view->bin_window);
808 view->bin_window = NULL;
810 GTK_WIDGET_CLASS (ev_view_parent_class)->unrealize (widget);
814 ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event)
816 EvView *view = EV_VIEW (widget);
818 if ((event->state & GDK_CONTROL_MASK) != 0) {
820 ev_view_set_sizing_mode (view, EV_SIZING_FREE);
822 if ((event->direction == GDK_SCROLL_UP ||
823 event->direction == GDK_SCROLL_LEFT) &&
824 ev_view_can_zoom_in (view)) {
825 ev_view_zoom_in (view);
826 } else if (ev_view_can_zoom_out (view)) {
827 ev_view_zoom_out (view);
833 if ((event->state & GDK_SHIFT_MASK) != 0) {
834 if (event->direction == GDK_SCROLL_UP)
835 event->direction = GDK_SCROLL_LEFT;
836 if (event->direction == GDK_SCROLL_DOWN)
837 event->direction = GDK_SCROLL_RIGHT;
844 ev_gdk_color_to_rgb (const GdkColor *color)
847 result = (0xff0000 | (color->red & 0xff00));
849 result |= ((color->green & 0xff00) | (color->blue >> 8));
854 draw_rubberband (GtkWidget *widget, GdkWindow *window,
855 const GdkRectangle *rect, guchar alpha)
859 GdkColor *fill_color_gdk;
862 fill_color_gdk = gdk_color_copy (>K_WIDGET (widget)->style->base[GTK_STATE_SELECTED]);
863 fill_color = ev_gdk_color_to_rgb (fill_color_gdk) << 8 | alpha;
865 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
866 rect->width, rect->height);
867 gdk_pixbuf_fill (pixbuf, fill_color);
869 gdk_draw_pixbuf (window, NULL, pixbuf,
872 rect->width, rect->height,
876 g_object_unref (pixbuf);
878 gc = gdk_gc_new (window);
879 gdk_gc_set_rgb_fg_color (gc, fill_color_gdk);
880 gdk_draw_rectangle (window, gc, FALSE,
886 gdk_color_free (fill_color_gdk);
891 highlight_find_results (EvView *view, int page)
893 EvDocumentFind *find;
896 g_return_if_fail (EV_IS_DOCUMENT_FIND (view->document));
898 find = EV_DOCUMENT_FIND (view->document);
900 results = ev_document_find_get_n_results (find, page);
902 for (i = 0; i < results; i++) {
903 EvRectangle rectangle;
904 GdkRectangle view_rectangle;
907 if (i == view->find_result && page == view->find_page) {
913 ev_document_find_get_result (find, page,
915 doc_rect_to_view_rect (view, page, &rectangle, &view_rectangle);
916 draw_rubberband (GTK_WIDGET (view), view->bin_window,
917 &view_rectangle, alpha);
922 draw_one_page (EvView *view,
924 GdkRectangle *page_area,
926 GdkRectangle *expose_area)
929 GdkPixbuf *scaled_image;
930 GdkPixbuf *current_pixbuf;
931 GdkRectangle overlap;
932 GdkRectangle real_page_area;
934 g_assert (view->document);
936 if (! gdk_rectangle_intersect (page_area, expose_area, &overlap))
939 ev_page_cache_get_size (view->page_cache,
943 ev_document_misc_paint_one_page (view->bin_window,
947 /* Render the document itself */
948 real_page_area = *page_area;
950 real_page_area.x += border->left;
951 real_page_area.y += border->top;
952 real_page_area.width -= (border->left + border->right);
953 real_page_area.height -= (border->top + border->bottom);
955 if (! gdk_rectangle_intersect (&real_page_area, expose_area, &overlap))
958 current_pixbuf = ev_pixbuf_cache_get_pixbuf (view->pixbuf_cache, page);
959 if (current_pixbuf == NULL)
961 else if (width == gdk_pixbuf_get_width (current_pixbuf) &&
962 height == gdk_pixbuf_get_height (current_pixbuf))
963 scaled_image = g_object_ref (current_pixbuf);
965 scaled_image = gdk_pixbuf_scale_simple (current_pixbuf,
969 gdk_draw_pixbuf (view->bin_window,
970 GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
972 overlap.x - real_page_area.x,
973 overlap.y - real_page_area.y,
974 overlap.x, overlap.y,
975 overlap.width, overlap.height,
976 GDK_RGB_DITHER_NORMAL,
978 g_object_unref (scaled_image);
983 ev_view_bin_expose (EvView *view,
984 GdkEventExpose *event)
986 GdkRectangle rubberband;
990 if (view->document == NULL)
993 for (i = view->start_page; i <= view->end_page; i++) {
994 GdkRectangle page_area;
997 if (! get_page_extents (view, i, &page_area, &border))
1000 draw_one_page (view, i, &page_area, &border, &(event->area));
1002 if (EV_IS_DOCUMENT_FIND (view->document)) {
1003 highlight_find_results (view, i);
1007 for (l = view->selections; l != NULL; l = l->next) {
1008 EvViewSelection *selection = (EvViewSelection *)l->data;
1010 doc_rect_to_view_rect (view, selection->page,
1011 &selection->rect, &rubberband);
1012 if (rubberband.width > 0 && rubberband.height > 0) {
1013 draw_rubberband (GTK_WIDGET (view), view->bin_window,
1020 ev_view_expose_event (GtkWidget *widget,
1021 GdkEventExpose *event)
1023 EvView *view = EV_VIEW (widget);
1025 if (event->window == view->bin_window)
1026 ev_view_bin_expose (view, event);
1028 return GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event (widget, event);
1034 ev_view_select_all (EvView *view)
1038 clear_selection (view);
1040 n_pages = ev_page_cache_get_n_pages (view->page_cache);
1041 for (i = 0; i < n_pages; i++) {
1043 EvViewSelection *selection;
1045 ev_page_cache_get_size (view->page_cache,
1046 i, 1.0, &width, &height);
1048 selection = g_new0 (EvViewSelection, 1);
1049 selection->page = i;
1050 selection->rect.x1 = selection->rect.y1 = 0;
1051 selection->rect.x2 = width;
1052 selection->rect.y2 = height;
1054 view->selections = g_list_append (view->selections, selection);
1057 gtk_widget_queue_draw (GTK_WIDGET (view));
1061 get_selected_text (EvView *ev_view)
1066 text = g_string_new (NULL);
1068 ev_document_doc_mutex_lock ();
1070 for (l = ev_view->selections; l != NULL; l = l->next) {
1071 EvViewSelection *selection = (EvViewSelection *)l->data;
1074 tmp = ev_document_get_text (ev_view->document,
1077 g_string_append (text, tmp);
1081 ev_document_doc_mutex_unlock ();
1083 return g_string_free (text, FALSE);
1087 ev_view_copy (EvView *ev_view)
1089 GtkClipboard *clipboard;
1092 if (!ev_document_can_get_text (ev_view->document)) {
1096 text = get_selected_text (ev_view);
1097 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
1098 GDK_SELECTION_CLIPBOARD);
1099 gtk_clipboard_set_text (clipboard, text, -1);
1104 ev_view_primary_get_cb (GtkClipboard *clipboard,
1105 GtkSelectionData *selection_data,
1109 EvView *ev_view = EV_VIEW (data);
1112 if (!ev_document_can_get_text (ev_view->document)) {
1116 text = get_selected_text (ev_view);
1117 gtk_selection_data_set_text (selection_data, text, -1);
1122 ev_view_primary_clear_cb (GtkClipboard *clipboard,
1125 EvView *view = EV_VIEW (data);
1127 clear_selection (view);
1131 ev_view_update_primary_selection (EvView *ev_view)
1133 GtkClipboard *clipboard;
1135 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
1136 GDK_SELECTION_PRIMARY);
1138 if (ev_view->selections) {
1139 if (!gtk_clipboard_set_with_owner (clipboard,
1141 G_N_ELEMENTS (targets),
1142 ev_view_primary_get_cb,
1143 ev_view_primary_clear_cb,
1144 G_OBJECT (ev_view)))
1145 ev_view_primary_clear_cb (clipboard, ev_view);
1147 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (ev_view))
1148 gtk_clipboard_clear (clipboard);
1153 ev_view_create_invisible_cursor(void)
1156 GdkColor black = { 0, 0, 0, 0 };
1157 static char bits[] = { 0x00 };
1159 empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
1161 return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
1165 ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
1167 GdkCursor *cursor = NULL;
1168 GdkDisplay *display;
1171 if (view->cursor == new_cursor) {
1175 widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
1176 display = gtk_widget_get_display (widget);
1177 view->cursor = new_cursor;
1179 switch (new_cursor) {
1180 case EV_VIEW_CURSOR_NORMAL:
1181 gdk_window_set_cursor (widget->window, NULL);
1183 case EV_VIEW_CURSOR_LINK:
1184 cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
1186 case EV_VIEW_CURSOR_WAIT:
1187 cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
1189 case EV_VIEW_CURSOR_HIDDEN:
1190 cursor = ev_view_create_invisible_cursor ();
1192 case EV_VIEW_CURSOR_DRAG:
1193 cursor = gdk_cursor_new_for_display (display, GDK_FLEUR);
1198 gdk_window_set_cursor (widget->window, cursor);
1199 gdk_cursor_unref (cursor);
1205 ev_view_button_press_event (GtkWidget *widget,
1206 GdkEventButton *event)
1208 EvView *view = EV_VIEW (widget);
1210 if (!GTK_WIDGET_HAS_FOCUS (widget)) {
1211 gtk_widget_grab_focus (widget);
1214 view->pressed_button = event->button;
1216 switch (event->button) {
1218 if (view->selections) {
1219 clear_selection (view);
1220 gtk_widget_queue_draw (widget);
1223 view->selection_start.x = event->x;
1224 view->selection_start.y = event->y;
1227 /* use root coordinates as reference point because
1228 * scrolling changes window relative coordinates */
1229 view->drag_info.start.x = event->x_root;
1230 view->drag_info.start.y = event->y_root;
1231 view->drag_info.hadj = gtk_adjustment_get_value (view->hadjustment);
1232 view->drag_info.vadj = gtk_adjustment_get_value (view->vadjustment);
1234 ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG);
1243 status_message_from_link (EvView *view, EvLink *link)
1249 type = ev_link_get_link_type (link);
1252 case EV_LINK_TYPE_TITLE:
1253 if (ev_link_get_title (link))
1254 msg = g_strdup (ev_link_get_title (link));
1256 case EV_LINK_TYPE_PAGE:
1257 page_label = ev_page_cache_get_page_label (view->page_cache, ev_link_get_page (link));
1258 msg = g_strdup_printf (_("Go to page %s"), page_label);
1259 g_free (page_label);
1261 case EV_LINK_TYPE_EXTERNAL_URI:
1262 msg = g_strdup (ev_link_get_uri (link));
1272 ev_view_set_status (EvView *view, const char *message)
1274 g_return_if_fail (EV_IS_VIEW (view));
1276 if (message != view->status) {
1277 g_free (view->status);
1278 view->status = g_strdup (message);
1279 g_object_notify (G_OBJECT (view), "status");
1284 ev_view_set_find_status (EvView *view, const char *message)
1286 g_return_if_fail (EV_IS_VIEW (view));
1288 g_free (view->find_status);
1289 view->find_status = g_strdup (message);
1290 g_object_notify (G_OBJECT (view), "find-status");
1294 find_page_at_location (EvView *view,
1303 if (view->document == NULL)
1307 g_assert (x_offset);
1308 g_assert (y_offset);
1310 for (i = view->start_page; i <= view->end_page; i++) {
1311 GdkRectangle page_area;
1314 if (! get_page_extents (view, i, &page_area, &border))
1317 if ((x >= page_area.x + border.left) &&
1318 (x < page_area.x + page_area.width - border.right) &&
1319 (y >= page_area.y + border.top) &&
1320 (y < page_area.y + page_area.height - border.bottom)) {
1322 *x_offset = x - (page_area.x + border.left);
1323 *y_offset = y - (page_area.y + border.top);
1332 get_link_at_location (EvView *view,
1337 gint x_offset = 0, y_offset = 0;
1338 GList *link_mapping;
1340 find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
1345 link_mapping = ev_pixbuf_cache_get_link_mapping (view->pixbuf_cache, page);
1348 return ev_link_mapping_find (link_mapping, x_offset / view->scale, y_offset / view->scale);
1355 ev_view_motion_notify_event (GtkWidget *widget,
1356 GdkEventMotion *event)
1358 EvView *view = EV_VIEW (widget);
1360 if (view->pressed_button == 1) {
1361 GdkRectangle selection;
1363 selection.x = MIN (view->selection_start.x, event->x);
1364 selection.y = MIN (view->selection_start.y, event->y);
1365 selection.width = ABS (view->selection_start.x - event->x) + 1;
1366 selection.height = ABS (view->selection_start.y - event->y) + 1;
1368 compute_selections (view, &selection);
1370 gtk_widget_queue_draw (widget);
1373 } else if (view->pressed_button == 2) {
1374 if (!view->drag_info.dragging) {
1377 start = gtk_drag_check_threshold (widget,
1378 view->drag_info.start.x,
1379 view->drag_info.start.y,
1382 view->drag_info.dragging = start;
1385 if (view->drag_info.dragging) {
1387 gdouble dhadj_value, dvadj_value;
1389 dx = event->x_root - view->drag_info.start.x;
1390 dy = event->y_root - view->drag_info.start.y;
1392 dhadj_value = view->hadjustment->page_size *
1393 (gdouble)dx / widget->allocation.width;
1394 dvadj_value = view->vadjustment->page_size *
1395 (gdouble)dy / widget->allocation.height;
1397 /* clamp scrolling to visible area */
1398 gtk_adjustment_set_value (view->hadjustment,
1399 MIN(view->drag_info.hadj - dhadj_value,
1400 view->hadjustment->upper -
1401 view->hadjustment->page_size));
1402 gtk_adjustment_set_value (view->vadjustment,
1403 MIN(view->drag_info.vadj - dvadj_value,
1404 view->vadjustment->upper -
1405 view->vadjustment->page_size));
1409 } else if (view->pressed_button <= 0 && view->document) {
1412 link = get_link_at_location (view, event->x, event->y);
1416 msg = status_message_from_link (view, link);
1417 ev_view_set_status (view, msg);
1418 ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
1421 ev_view_set_status (view, NULL);
1422 if (view->cursor == EV_VIEW_CURSOR_LINK) {
1423 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1432 /* FIXME: standardize this sometime */
1434 go_to_link (EvView *view, EvLink *link)
1440 type = ev_link_get_link_type (link);
1443 case EV_LINK_TYPE_TITLE:
1445 case EV_LINK_TYPE_PAGE:
1446 page = ev_link_get_page (link);
1447 ev_page_cache_set_current_page (view->page_cache, page);
1449 case EV_LINK_TYPE_EXTERNAL_URI:
1450 uri = ev_link_get_uri (link);
1451 gnome_vfs_url_show (uri);
1458 ev_view_button_release_event (GtkWidget *widget,
1459 GdkEventButton *event)
1461 EvView *view = EV_VIEW (widget);
1463 if (view->pressed_button == 2) {
1464 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1467 view->pressed_button = -1;
1468 view->drag_info.dragging = FALSE;
1470 if (view->selections) {
1471 ev_view_update_primary_selection (view);
1472 } else if (view->document) {
1475 link = get_link_at_location (view, event->x, event->y);
1477 go_to_link (view, link);
1485 on_adjustment_value_changed (GtkAdjustment *adjustment,
1488 view_update_adjustments (view);
1492 set_scroll_adjustment (EvView *view,
1493 GtkOrientation orientation,
1494 GtkAdjustment *adjustment)
1496 GtkAdjustment **to_set;
1498 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1499 to_set = &view->hadjustment;
1501 to_set = &view->vadjustment;
1503 if (*to_set != adjustment) {
1505 g_signal_handlers_disconnect_by_func (*to_set,
1506 (gpointer) on_adjustment_value_changed,
1508 g_object_unref (*to_set);
1511 *to_set = adjustment;
1512 view_set_adjustment_values (view, orientation);
1515 g_object_ref (*to_set);
1516 g_signal_connect (*to_set, "value_changed",
1517 G_CALLBACK (on_adjustment_value_changed), view);
1523 ev_view_set_scroll_adjustments (EvView *view,
1524 GtkAdjustment *hadjustment,
1525 GtkAdjustment *vadjustment)
1527 set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
1528 set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
1530 view_update_adjustments (view);
1534 add_scroll_binding_keypad (GtkBindingSet *binding_set,
1536 GtkScrollType scroll,
1537 gboolean horizontal)
1539 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
1541 gtk_binding_entry_add_signal (binding_set, keyval, 0,
1543 GTK_TYPE_SCROLL_TYPE, scroll,
1544 G_TYPE_BOOLEAN, horizontal);
1545 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
1547 GTK_TYPE_SCROLL_TYPE, scroll,
1548 G_TYPE_BOOLEAN, horizontal);
1552 ev_view_scroll (EvView *view,
1553 EvScrollType scroll)
1555 GtkAdjustment *adjustment;
1556 double value, increment;
1557 gboolean first_page = FALSE;
1558 gboolean last_page = FALSE;
1560 /* Assign values for increment and vertical adjustment */
1561 adjustment = view->vadjustment;
1562 increment = adjustment->page_size * 0.75;
1563 value = adjustment->value;
1565 /* Assign boolean for first and last page */
1566 if (view->current_page == 0)
1568 if (view->current_page == ev_page_cache_get_n_pages (view->page_cache) - 1)
1572 case EV_SCROLL_PAGE_BACKWARD:
1573 /* Do not jump backwards if at the first page */
1574 if (value == (adjustment->lower) && first_page) {
1576 /* At the top of a page, assign the upper bound limit of previous page */
1577 } else if (value == (adjustment->lower)) {
1578 value = adjustment->upper - adjustment->page_size;
1579 ev_page_cache_set_current_page (view->page_cache, view->current_page - 1);
1580 /* Jump to the top */
1582 value = MAX (value - increment, adjustment->lower);
1585 case EV_SCROLL_PAGE_FORWARD:
1586 /* Do not jump forward if at the last page */
1587 if (value == (adjustment->upper - adjustment->page_size) && last_page) {
1589 /* At the bottom of a page, assign the lower bound limit of next page */
1590 } else if (value == (adjustment->upper - adjustment->page_size)) {
1592 ev_page_cache_set_current_page (view->page_cache, view->current_page + 1);
1593 /* Jump to the bottom */
1595 value = MIN (value + increment, adjustment->upper - adjustment->page_size);
1602 gtk_adjustment_set_value (adjustment, value);
1606 ev_view_scroll_view (EvView *view,
1607 GtkScrollType scroll,
1608 gboolean horizontal)
1610 if (scroll == GTK_SCROLL_PAGE_BACKWARD) {
1611 ev_page_cache_prev_page (view->page_cache);
1612 } else if (scroll == GTK_SCROLL_PAGE_FORWARD) {
1613 ev_page_cache_next_page (view->page_cache);
1615 GtkAdjustment *adjustment;
1619 adjustment = view->hadjustment;
1621 adjustment = view->vadjustment;
1624 value = adjustment->value;
1627 case GTK_SCROLL_STEP_BACKWARD:
1628 value -= adjustment->step_increment;
1630 case GTK_SCROLL_STEP_FORWARD:
1631 value += adjustment->step_increment;
1637 value = CLAMP (value, adjustment->lower,
1638 adjustment->upper - adjustment->page_size);
1640 gtk_adjustment_set_value (adjustment, value);
1645 ev_view_set_property (GObject *object,
1647 const GValue *value,
1650 EvView *view = EV_VIEW (object);
1654 case PROP_CONTINUOUS:
1655 ev_view_set_continuous (view, g_value_get_boolean (value));
1657 case PROP_DUAL_PAGE:
1658 ev_view_set_dual_page (view, g_value_get_boolean (value));
1660 case PROP_FULLSCREEN:
1661 ev_view_set_fullscreen (view, g_value_get_boolean (value));
1663 case PROP_PRESENTATION:
1664 ev_view_set_presentation (view, g_value_get_boolean (value));
1666 case PROP_SIZING_MODE:
1667 ev_view_set_sizing_mode (view, g_value_get_enum (value));
1670 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1675 ev_view_get_property (GObject *object,
1680 EvView *view = EV_VIEW (object);
1685 g_value_set_string (value, view->status);
1687 case PROP_FIND_STATUS:
1688 g_value_set_string (value, view->status);
1690 case PROP_CONTINUOUS:
1691 g_value_set_boolean (value, view->continuous);
1693 case PROP_DUAL_PAGE:
1694 g_value_set_boolean (value, view->dual_page);
1696 case PROP_FULLSCREEN:
1697 g_value_set_boolean (value, view->fullscreen);
1699 case PROP_PRESENTATION:
1700 g_value_set_boolean (value, view->presentation);
1702 case PROP_SIZING_MODE:
1703 g_value_set_enum (value, view->sizing_mode);
1706 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1711 ev_view_class_init (EvViewClass *class)
1713 GObjectClass *object_class = G_OBJECT_CLASS (class);
1714 GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
1715 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1716 GtkBindingSet *binding_set;
1718 object_class->finalize = ev_view_finalize;
1719 object_class->set_property = ev_view_set_property;
1720 object_class->get_property = ev_view_get_property;
1722 widget_class->expose_event = ev_view_expose_event;
1723 widget_class->button_press_event = ev_view_button_press_event;
1724 widget_class->motion_notify_event = ev_view_motion_notify_event;
1725 widget_class->button_release_event = ev_view_button_release_event;
1726 widget_class->size_request = ev_view_size_request;
1727 widget_class->size_allocate = ev_view_size_allocate;
1728 widget_class->realize = ev_view_realize;
1729 widget_class->unrealize = ev_view_unrealize;
1730 widget_class->scroll_event = ev_view_scroll_event;
1731 gtk_object_class->destroy = ev_view_destroy;
1733 class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
1734 class->scroll_view = ev_view_scroll_view;
1736 widget_class->set_scroll_adjustments_signal = g_signal_new ("set-scroll-adjustments",
1737 G_OBJECT_CLASS_TYPE (object_class),
1738 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1739 G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
1741 ev_marshal_VOID__OBJECT_OBJECT,
1743 GTK_TYPE_ADJUSTMENT,
1744 GTK_TYPE_ADJUSTMENT);
1746 g_signal_new ("scroll_view",
1747 G_TYPE_FROM_CLASS (object_class),
1748 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1749 G_STRUCT_OFFSET (EvViewClass, scroll_view),
1751 ev_marshal_VOID__ENUM_BOOLEAN,
1753 GTK_TYPE_SCROLL_TYPE,
1756 g_object_class_install_property (object_class,
1758 g_param_spec_string ("status",
1760 "The status message",
1764 g_object_class_install_property (object_class,
1766 g_param_spec_string ("find-status",
1767 "Find Status Message",
1768 "The find status message",
1772 g_object_class_install_property (object_class,
1774 g_param_spec_boolean ("continuous",
1776 "Continuous scrolling mode",
1778 G_PARAM_READWRITE));
1780 g_object_class_install_property (object_class,
1782 g_param_spec_boolean ("dual-page",
1784 "Two pages visible at once",
1786 G_PARAM_READWRITE));
1787 g_object_class_install_property (object_class,
1789 g_param_spec_boolean ("fullscreen",
1791 "Draw page in a fullscreen fashion",
1793 G_PARAM_READWRITE));
1794 g_object_class_install_property (object_class,
1796 g_param_spec_boolean ("presentation",
1798 "Draw page in presentation mode",
1800 G_PARAM_READWRITE));
1802 g_object_class_install_property (object_class,
1804 g_param_spec_enum ("sizing-mode",
1807 EV_TYPE_SIZING_MODE,
1808 EV_SIZING_FIT_WIDTH,
1809 G_PARAM_READWRITE));
1811 binding_set = gtk_binding_set_by_class (class);
1813 add_scroll_binding_keypad (binding_set, GDK_Left, GTK_SCROLL_STEP_BACKWARD, TRUE);
1814 add_scroll_binding_keypad (binding_set, GDK_Right, GTK_SCROLL_STEP_FORWARD, TRUE);
1815 add_scroll_binding_keypad (binding_set, GDK_Up, GTK_SCROLL_STEP_BACKWARD, FALSE);
1816 add_scroll_binding_keypad (binding_set, GDK_Down, GTK_SCROLL_STEP_FORWARD, FALSE);
1818 add_scroll_binding_keypad (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_BACKWARD, FALSE);
1819 add_scroll_binding_keypad (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_FORWARD, FALSE);
1823 ev_view_init (EvView *view)
1825 GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
1829 view->current_page = 0;
1830 view->pressed_button = -1;
1831 view->cursor = EV_VIEW_CURSOR_NORMAL;
1832 view->drag_info.dragging = FALSE;
1834 view->continuous = TRUE;
1835 view->dual_page = FALSE;
1836 view->presentation = FALSE;
1837 view->fullscreen = FALSE;
1838 view->sizing_mode = EV_SIZING_FIT_WIDTH;
1842 update_find_status_message (EvView *view)
1846 if (view->current_page == view->find_page) {
1849 results = ev_document_find_get_n_results
1850 (EV_DOCUMENT_FIND (view->document),
1851 view->current_page);
1852 /* TRANS: Sometimes this could be better translated as
1853 "%d hit(s) on this page". Therefore this string
1854 contains plural cases. */
1855 message = g_strdup_printf (ngettext ("%d found on this page",
1856 "%d found on this page",
1862 ev_document_doc_mutex_lock ();
1863 percent = ev_document_find_get_progress
1864 (EV_DOCUMENT_FIND (view->document));
1865 ev_document_doc_mutex_unlock ();
1866 if (percent >= (1.0 - 1e-10)) {
1867 message = g_strdup (_("Not found"));
1869 message = g_strdup_printf (_("%3d%% remaining to search"),
1870 (int) ((1.0 - percent) * 100));
1874 // ev_document_doc_mutex_unlock ();
1876 ev_view_set_find_status (view, message);
1877 // g_free (message);
1883 ensure_rectangle_is_visible (EvView *view, GdkRectangle *rect)
1885 GtkWidget *widget = GTK_WIDGET (view);
1886 GtkAdjustment *adjustment;
1889 adjustment = view->vadjustment;
1891 if (rect->y < adjustment->value) {
1892 value = MAX (adjustment->lower, rect->y - MARGIN);
1893 gtk_adjustment_set_value (view->vadjustment, value);
1894 } else if (rect->y + rect->height >
1895 adjustment->value + widget->allocation.height) {
1896 value = MIN (adjustment->upper, rect->y + rect->height -
1897 widget->allocation.height + MARGIN);
1898 gtk_adjustment_set_value (view->vadjustment, value);
1901 adjustment = view->hadjustment;
1903 if (rect->x < adjustment->value) {
1904 value = MAX (adjustment->lower, rect->x - MARGIN);
1905 gtk_adjustment_set_value (view->hadjustment, value);
1906 } else if (rect->x + rect->height >
1907 adjustment->value + widget->allocation.width) {
1908 value = MIN (adjustment->upper, rect->x + rect->width -
1909 widget->allocation.width + MARGIN);
1910 gtk_adjustment_set_value (view->hadjustment, value);
1915 jump_to_find_result (EvView *view)
1917 EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
1919 GdkRectangle view_rect;
1921 int page = view->find_page;
1923 ev_document_doc_mutex_lock ();
1924 n_results = ev_document_find_get_n_results (find, page);
1925 ev_document_doc_mutex_unlock ();
1927 if (n_results > view->find_result) {
1928 ev_document_doc_mutex_lock ();
1929 ev_document_find_get_result
1930 (find, page, view->find_result, &rect);
1931 ev_document_doc_mutex_unlock ();
1933 doc_rect_to_view_rect (view, page, &rect, &view_rect);
1934 ensure_rectangle_is_visible (view, &view_rect);
1939 jump_to_find_page (EvView *view)
1943 n_pages = ev_page_cache_get_n_pages (view->page_cache);
1945 for (i = 0; i < n_pages; i++) {
1949 page = i + view->find_page;
1950 if (page >= n_pages) {
1951 page = page - n_pages;
1954 has_results = ev_document_find_page_has_results
1955 (EV_DOCUMENT_FIND (view->document), page);
1956 if (has_results == -1) {
1957 view->find_page = page;
1959 } else if (has_results == 1) {
1960 ev_page_cache_set_current_page (view->page_cache, page);
1961 jump_to_find_result (view);
1968 find_changed_cb (EvDocument *document, int page, EvView *view)
1970 jump_to_find_page (view);
1971 jump_to_find_result (view);
1972 update_find_status_message (view);
1974 if (view->current_page == page)
1975 gtk_widget_queue_draw (GTK_WIDGET (view));
1977 /*** Public API ***/
1984 view = g_object_new (EV_TYPE_VIEW, NULL);
1990 job_finished_cb (EvPixbufCache *pixbuf_cache,
1993 gtk_widget_queue_draw (GTK_WIDGET (view));
1998 page_changed_cb (EvPageCache *page_cache,
2002 int old_page = view->current_page;
2003 int old_width, old_height;
2004 int new_width, new_height;
2005 int max_height, n_rows;
2007 if (old_page == new_page)
2010 ev_page_cache_get_size (page_cache,
2013 &old_width, &old_height);
2015 view->current_page = new_page;
2017 ev_page_cache_get_size (page_cache,
2020 &new_width, &new_height);
2022 compute_border (view, new_width, new_height, &(view->border));
2024 if (new_width != old_width || new_height != old_height)
2025 gtk_widget_queue_resize (GTK_WIDGET (view));
2027 gtk_widget_queue_draw (GTK_WIDGET (view));
2029 if (view->continuous) {
2031 n_rows = view->dual_page ? new_page / 2 : new_page;
2033 get_bounding_box_size (view, NULL, &max_height);
2035 gtk_adjustment_clamp_page(view->vadjustment,
2036 (max_height + view->spacing) * n_rows,
2037 (max_height + view->spacing) * n_rows +
2038 view->vadjustment->page_size);
2040 gtk_adjustment_set_value (view->vadjustment,
2041 view->vadjustment->lower);
2044 if (EV_IS_DOCUMENT_FIND (view->document)) {
2045 view->find_page = new_page;
2046 view->find_result = 0;
2047 update_find_status_message (view);
2050 view_update_range_and_current_page (view);
2054 ev_view_set_document (EvView *view,
2055 EvDocument *document)
2057 g_return_if_fail (EV_IS_VIEW (view));
2059 if (document != view->document) {
2060 if (view->document) {
2061 g_signal_handlers_disconnect_by_func (view->document,
2064 g_object_unref (view->document);
2065 view->page_cache = NULL;
2069 view->document = document;
2070 view->find_page = 0;
2071 view->find_result = 0;
2073 if (view->document) {
2074 g_object_ref (view->document);
2075 if (EV_IS_DOCUMENT_FIND (view->document)) {
2076 g_signal_connect (view->document,
2078 G_CALLBACK (find_changed_cb),
2081 view->page_cache = ev_document_get_page_cache (view->document);
2082 g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
2083 view->pixbuf_cache = ev_pixbuf_cache_new (view->document);
2084 g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
2087 gtk_widget_queue_resize (GTK_WIDGET (view));
2091 #define EPSILON 0.0000001
2093 ev_view_set_zoom (EvView *view,
2100 scale = view->scale * factor;
2104 scale = CLAMP (scale, MIN_SCALE, MAX_SCALE);
2106 if (ABS (view->scale - scale) < EPSILON)
2108 view->scale = scale;
2109 gtk_widget_queue_resize (GTK_WIDGET (view));
2113 ev_view_get_zoom (EvView *view)
2119 ev_view_set_continuous (EvView *view,
2120 gboolean continuous)
2122 g_return_if_fail (EV_IS_VIEW (view));
2124 continuous = continuous != FALSE;
2126 if (view->continuous != continuous) {
2127 view->continuous = continuous;
2128 gtk_widget_queue_resize (GTK_WIDGET (view));
2131 g_object_notify (G_OBJECT (view), "continuous");
2135 ev_view_set_dual_page (EvView *view,
2138 g_return_if_fail (EV_IS_VIEW (view));
2140 dual_page = dual_page != FALSE;
2142 if (view->dual_page == dual_page)
2145 view->dual_page = dual_page;
2146 /* FIXME: if we're keeping the pixbuf cache around, we should extend the
2147 * preload_cache_size to be 2 if dual_page is set.
2149 gtk_widget_queue_resize (GTK_WIDGET (view));
2151 g_object_notify (G_OBJECT (view), "dual-page");
2155 ev_view_set_fullscreen (EvView *view,
2156 gboolean fullscreen)
2158 g_return_if_fail (EV_IS_VIEW (view));
2160 fullscreen = fullscreen != FALSE;
2162 if (view->fullscreen != fullscreen) {
2163 view->fullscreen = fullscreen;
2164 gtk_widget_queue_resize (GTK_WIDGET (view));
2167 g_object_notify (G_OBJECT (view), "fullscreen");
2171 ev_view_get_fullscreen (EvView *view)
2173 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2175 return view->fullscreen;
2179 ev_view_set_presentation (EvView *view,
2180 gboolean presentation)
2182 g_return_if_fail (EV_IS_VIEW (view));
2184 presentation = presentation != FALSE;
2186 if (view->presentation == presentation)
2189 view->presentation = presentation;
2190 gtk_widget_queue_resize (GTK_WIDGET (view));
2191 if (GTK_WIDGET_REALIZED (view)) {
2192 if (view->presentation)
2193 gdk_window_set_background (view->bin_window,
2194 >K_WIDGET (view)->style->black);
2196 gdk_window_set_background (view->bin_window,
2197 >K_WIDGET (view)->style->mid [GTK_STATE_NORMAL]);
2201 g_object_notify (G_OBJECT (view), "presentation");
2205 ev_view_get_presentation (EvView *view)
2207 g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
2209 return view->presentation;
2213 ev_view_set_sizing_mode (EvView *view,
2214 EvSizingMode sizing_mode)
2216 g_return_if_fail (EV_IS_VIEW (view));
2218 if (view->sizing_mode == sizing_mode)
2221 view->sizing_mode = sizing_mode;
2222 gtk_widget_queue_resize (GTK_WIDGET (view));
2224 g_object_notify (G_OBJECT (view), "sizing-mode");
2228 ev_view_get_sizing_mode (EvView *view)
2230 g_return_val_if_fail (EV_IS_VIEW (view), EV_SIZING_FREE);
2232 return view->sizing_mode;
2236 ev_view_can_zoom_in (EvView *view)
2238 return view->scale * ZOOM_IN_FACTOR <= MAX_SCALE;
2242 ev_view_can_zoom_out (EvView *view)
2244 return view->scale * ZOOM_OUT_FACTOR >= MIN_SCALE;
2248 ev_view_zoom_in (EvView *view)
2250 g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2252 ev_view_set_zoom (view, ZOOM_IN_FACTOR, TRUE);
2256 ev_view_zoom_out (EvView *view)
2258 g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2260 ev_view_set_zoom (view, ZOOM_OUT_FACTOR, TRUE);
2266 zoom_for_size_fit_width (int doc_width,
2274 scale = (double)target_width / doc_width;
2276 if (doc_height * scale > target_height)
2277 scale = (double) (target_width - vsb_width) / doc_width;
2283 zoom_for_size_best_fit (int doc_width,
2293 w_scale = (double)target_width / doc_width;
2294 h_scale = (double)target_height / doc_height;
2296 if (doc_height * w_scale > target_height)
2297 w_scale = (double) (target_width - vsb_width) / doc_width;
2298 if (doc_width * h_scale > target_width)
2299 h_scale = (double) (target_height - hsb_width) / doc_height;
2301 return MIN (w_scale, h_scale);
2306 ev_view_zoom_for_size_presentation (EvView *view,
2310 int doc_width, doc_height;
2313 ev_page_cache_get_size (view->page_cache,
2318 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, 0, 0);
2319 ev_view_set_zoom (view, scale, FALSE);
2323 ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
2329 int doc_width, doc_height;
2333 ev_page_cache_get_max_width_size (view->page_cache,
2336 ev_page_cache_get_max_height_size (view->page_cache,
2339 compute_border (view, doc_width, doc_height, &border);
2341 doc_width = doc_width * 2;
2342 width -= (2 * (border.left + border.right) + 3 * view->spacing);
2343 height -= (border.top + border.bottom + 2 * view->spacing - 1);
2345 /* FIXME: We really need to calculate the overall height here, not the
2346 * page height. We assume there's always a vertical scrollbar for
2347 * now. We need to fix this. */
2348 if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2349 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2350 else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2351 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2353 g_assert_not_reached ();
2355 ev_view_set_zoom (view, scale, FALSE);
2359 ev_view_zoom_for_size_continuous (EvView *view,
2365 int doc_width, doc_height;
2369 ev_page_cache_get_max_width_size (view->page_cache,
2372 ev_page_cache_get_max_height_size (view->page_cache,
2375 compute_border (view, doc_width, doc_height, &border);
2377 width -= (border.left + border.right + 2 * view->spacing);
2378 height -= (border.top + border.bottom + 2 * view->spacing - 1);
2380 /* FIXME: We really need to calculate the overall height here, not the
2381 * page height. We assume there's always a vertical scrollbar for
2382 * now. We need to fix this. */
2383 if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2384 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2385 else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2386 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2388 g_assert_not_reached ();
2390 ev_view_set_zoom (view, scale, FALSE);
2394 ev_view_zoom_for_size_dual_page (EvView *view,
2401 gint doc_width, doc_height;
2405 other_page = view->current_page ^ 1;
2407 /* Find the largest of the two. */
2408 ev_page_cache_get_size (view->page_cache,
2411 &doc_width, &doc_height);
2413 if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
2414 gint width_2, height_2;
2415 ev_page_cache_get_size (view->page_cache,
2418 &width_2, &height_2);
2419 if (width_2 > doc_width)
2420 doc_width = width_2;
2421 if (height_2 > doc_height)
2422 doc_height = height_2;
2424 compute_border (view, doc_width, doc_height, &border);
2426 doc_width = doc_width * 2;
2427 width -= ((border.left + border.right)* 2 + 3 * view->spacing);
2428 height -= (border.top + border.bottom + 2 * view->spacing);
2430 if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2431 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2432 else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2433 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2435 g_assert_not_reached ();
2437 ev_view_set_zoom (view, scale, FALSE);
2441 ev_view_zoom_for_size_single_page (EvView *view,
2447 int doc_width, doc_height;
2451 ev_page_cache_get_size (view->page_cache,
2456 /* Get an approximate border */
2457 compute_border (view, width, height, &border);
2459 width -= (border.left + border.right + 2 * view->spacing);
2460 height -= (border.top + border.bottom + 2 * view->spacing);
2462 if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2463 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2464 else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2465 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2467 g_assert_not_reached ();
2469 ev_view_set_zoom (view, scale, FALSE);
2474 ev_view_zoom_normal (EvView *view)
2476 ev_view_set_zoom (view, 1.0, FALSE);
2480 ev_view_set_zoom_for_size (EvView *view,
2486 g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
2487 view->sizing_mode == EV_SIZING_BEST_FIT);
2488 g_return_if_fail (width >= 0);
2489 g_return_if_fail (height >= 0);
2491 if (view->document == NULL)
2494 if (view->presentation)
2495 ev_view_zoom_for_size_presentation (view, width, height);
2496 else if (view->continuous && view->dual_page)
2497 ev_view_zoom_for_size_continuous_and_dual_page (view, width, height, vsb_width, hsb_height);
2498 else if (view->continuous)
2499 ev_view_zoom_for_size_continuous (view, width, height, vsb_width, hsb_height);
2500 else if (view->dual_page)
2501 ev_view_zoom_for_size_dual_page (view, width, height, vsb_width, hsb_height);
2503 ev_view_zoom_for_size_single_page (view, width, height, vsb_width, hsb_height);
2507 ev_view_get_status (EvView *view)
2509 g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2511 return view->status;
2515 ev_view_get_find_status (EvView *view)
2517 g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2519 return view->find_status;
2523 ev_view_can_find_next (EvView *view)
2527 if (EV_IS_DOCUMENT_FIND (view->document)) {
2528 EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2530 ev_document_doc_mutex_lock ();
2531 n_results = ev_document_find_get_n_results (find, view->current_page);
2532 ev_document_doc_mutex_unlock ();
2535 return n_results > 0;
2539 ev_view_find_next (EvView *view)
2541 EvPageCache *page_cache;
2542 int n_results, n_pages;
2543 EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2545 page_cache = ev_document_get_page_cache (view->document);
2546 ev_document_doc_mutex_lock ();
2547 n_results = ev_document_find_get_n_results (find, view->current_page);
2548 ev_document_doc_mutex_unlock ();
2550 n_pages = ev_page_cache_get_n_pages (page_cache);
2552 view->find_result++;
2554 if (view->find_result >= n_results) {
2555 view->find_result = 0;
2558 if (view->find_page >= n_pages) {
2559 view->find_page = 0;
2562 jump_to_find_page (view);
2564 jump_to_find_result (view);
2565 gtk_widget_queue_draw (GTK_WIDGET (view));
2570 ev_view_find_previous (EvView *view)
2572 int n_results, n_pages;
2573 EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2574 EvPageCache *page_cache;
2576 page_cache = ev_document_get_page_cache (view->document);
2578 ev_document_doc_mutex_lock ();
2579 n_results = ev_document_find_get_n_results (find, view->current_page);
2580 ev_document_doc_mutex_unlock ();
2582 n_pages = ev_page_cache_get_n_pages (page_cache);
2584 view->find_result--;
2586 if (view->find_result < 0) {
2587 view->find_result = 0;
2590 if (view->find_page < 0) {
2591 view->find_page = n_pages - 1;
2594 jump_to_find_page (view);
2596 jump_to_find_result (view);
2597 gtk_widget_queue_draw (GTK_WIDGET (view));
2602 ev_view_hide_cursor (EvView *view)
2604 ev_view_set_cursor (view, EV_VIEW_CURSOR_HIDDEN);
2608 ev_view_show_cursor (EvView *view)
2610 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
2615 ev_sizing_mode_get_type (void)
2617 static GType etype = 0;
2619 static const GEnumValue values[] = {
2620 { EV_SIZING_FIT_WIDTH, "EV_SIZING_FIT_WIDTH", "fit-width" },
2621 { EV_SIZING_BEST_FIT, "EV_SIZING_BEST_FIT", "best-fit" },
2622 { EV_SIZING_FREE, "EV_SIZING_FREE", "free" },
2625 etype = g_enum_register_static ("EvSizingMode", values);