]> www.fi.muni.cz Git - evince.git/blob - ev-view.c
07a71ead20aca43ec66f1d267df493ab3d398b30
[evince.git] / ev-view.c
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
3  *
4  *  Copyright (C) 2004 Red Hat, Inc
5  *
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.
10  *
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.
15  *
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.
19  */
20
21 #include <math.h>
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>
29
30 #include "ev-marshal.h"
31 #include "ev-view.h"
32 #include "ev-document-find.h"
33 #include "ev-document-misc.h"
34 #include "ev-debug.h"
35 #include "ev-job-queue.h"
36 #include "ev-page-cache.h"
37 #include "ev-pixbuf-cache.h"
38
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))
42
43 enum {
44         PROP_0,
45         PROP_STATUS,
46         PROP_FIND_STATUS,
47         PROP_CONTINUOUS,
48         PROP_DUAL_PAGE,
49         PROP_FULL_SCREEN,
50         PROP_PRESENTATION,
51         PROP_SIZING_MODE,
52 };
53
54 enum {
55   TARGET_STRING,
56   TARGET_TEXT,
57   TARGET_COMPOUND_TEXT,
58   TARGET_UTF8_STRING,
59   TARGET_TEXT_BUFFER_CONTENTS
60 };
61
62 enum {
63         EV_SCROLL_PAGE_FORWARD,
64         EV_SCROLL_PAGE_BACKWARD
65 };
66
67 static const GtkTargetEntry targets[] = {
68         { "STRING", 0, TARGET_STRING },
69         { "TEXT",   0, TARGET_TEXT },
70         { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
71         { "UTF8_STRING", 0, TARGET_UTF8_STRING },
72 };
73
74 typedef enum {
75         EV_VIEW_CURSOR_NORMAL,
76         EV_VIEW_CURSOR_LINK,
77         EV_VIEW_CURSOR_WAIT,
78         EV_VIEW_CURSOR_HIDDEN
79 } EvViewCursor;
80
81 #define ZOOM_IN_FACTOR  1.2
82 #define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR)
83
84 #define MIN_SCALE 0.05409
85 #define MAX_SCALE 6.0
86
87 struct _EvView {
88         GtkWidget parent_instance;
89
90         EvDocument *document;
91
92         GdkWindow *bin_window;
93
94         char *status;
95         char *find_status;
96
97         int scroll_x;
98         int scroll_y;
99
100         gboolean pressed_button;
101         gboolean has_selection;
102         GdkPoint selection_start;
103         EvRectangle selection;
104         EvViewCursor cursor;
105
106         GtkAdjustment *hadjustment;
107         GtkAdjustment *vadjustment;
108
109         EvPageCache *page_cache;
110         EvPixbufCache *pixbuf_cache;
111
112         gint start_page;
113         gint end_page;
114         gint current_page;
115
116         EvJobRender *current_job;
117
118         int find_page;
119         int find_result;
120         int spacing;
121
122         double scale;
123         GtkBorder border;
124         gboolean show_border;
125
126         gboolean continuous;
127         gboolean dual_page;
128         gboolean full_screen;
129         gboolean presentation;
130         EvSizingMode sizing_mode;
131 };
132
133 struct _EvViewClass {
134         GtkWidgetClass parent_class;
135
136         void    (*set_scroll_adjustments) (EvView         *view,
137                                            GtkAdjustment  *hadjustment,
138                                            GtkAdjustment  *vadjustment);
139         void    (*scroll_view)            (EvView         *view,
140                                            GtkScrollType   scroll,
141                                            gboolean        horizontal);
142
143 };
144
145
146 static void ev_view_set_scroll_adjustments     (EvView        *view,
147                                                 GtkAdjustment *hadjustment,
148                                                 GtkAdjustment *vadjustment);
149 static void get_bounding_box_size              (EvView        *view,
150                                                 int           *max_width,
151                                                 int           *max_height);
152 static void view_update_range_and_current_page (EvView        *view);
153
154
155 G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET)
156
157 /*** Helper functions ***/
158
159 static void
160 view_update_adjustments (EvView *view)
161 {
162         int old_x = view->scroll_x;
163         int old_y = view->scroll_y;
164
165         if (view->hadjustment)
166                 view->scroll_x = view->hadjustment->value;
167         else
168                 view->scroll_x = 0;
169
170         if (view->vadjustment)
171                 view->scroll_y = view->vadjustment->value;
172         else
173                 view->scroll_y = 0;
174
175         if (GTK_WIDGET_REALIZED (view) &&
176             (view->scroll_x != old_x || view->scroll_y != old_y)) {
177                 gdk_window_move (view->bin_window, - view->scroll_x, - view->scroll_y);
178                 //              gdk_window_process_updates (view->bin_window, TRUE);
179         }
180
181         if (view->document)
182                 view_update_range_and_current_page (view);
183 }
184
185 static void
186 view_set_adjustment_values (EvView         *view,
187                             GtkOrientation  orientation)
188 {
189         GtkWidget *widget = GTK_WIDGET (view);
190         GtkAdjustment *adjustment;
191         gboolean value_changed = FALSE;
192         int requisition;
193         int allocation;
194
195         if (orientation == GTK_ORIENTATION_HORIZONTAL)  {
196                 requisition = widget->requisition.width;
197                 allocation = widget->allocation.width;
198                 adjustment = view->hadjustment;
199         } else {
200                 requisition = widget->requisition.height;
201                 allocation = widget->allocation.height;
202                 adjustment = view->vadjustment;
203         }
204
205         if (!adjustment)
206                 return;
207
208         adjustment->page_size = allocation;
209         adjustment->step_increment = allocation * 0.1;
210         adjustment->page_increment = allocation * 0.9;
211         adjustment->lower = 0;
212         adjustment->upper = MAX (allocation, requisition);
213
214         if (adjustment->value > adjustment->upper - adjustment->page_size) {
215                 adjustment->value = adjustment->upper - adjustment->page_size;
216                 value_changed = TRUE;
217         }
218
219         gtk_adjustment_changed (adjustment);
220         if (value_changed)
221                 gtk_adjustment_value_changed (adjustment);
222 }
223
224 static void
225 view_update_range_and_current_page (EvView *view)
226 {
227
228         /* Presentation trumps all other modes */
229         if (view->presentation) {
230                 view->start_page = view->current_page;
231                 view->end_page = view->current_page;
232         } else if (view->continuous) {
233                 GdkRectangle current_area, unused, page_area;
234                 gboolean found = FALSE;
235                 int i;
236                 
237                 get_bounding_box_size (view, &(page_area.width), &(page_area.height));
238                 page_area.x = view->spacing;
239                 page_area.y = view->spacing;
240
241                 if (view->hadjustment) {
242                         current_area.x = view->hadjustment->value;
243                         current_area.width = view->hadjustment->page_size;
244                 } else {
245                         current_area.x = page_area.x;
246                         current_area.width = page_area.width;
247                 }
248
249                 if (view->vadjustment) {
250                         current_area.y = view->vadjustment->value;
251                         current_area.height = view->vadjustment->page_size;
252                 } else {
253                         current_area.y = page_area.y;
254                         current_area.height = page_area.height;
255                 }
256
257                 for (i = 0; i < ev_page_cache_get_n_pages (view->page_cache); i++) {
258                         if (gdk_rectangle_intersect (&current_area, &page_area, &unused)) {
259                                 if (! found) {
260                                         view->start_page = i;
261                                         found = TRUE;
262                                 }
263                                 view->end_page = i;
264                         } else if (found) {
265                                 break;
266                         }
267                         if (view->dual_page) {
268                                 if (i % 2 == 0) {
269                                         page_area.x += page_area.width + view->spacing;
270                                 } else {
271                                         page_area.x = view->spacing;
272                                         page_area.y += page_area.height + view->spacing;
273                                 }
274                         } else {
275                                 page_area.y += page_area.height + view->spacing;
276                         }
277                 }
278         } else {
279                 if (view->dual_page) {
280                         if (view->current_page % 2 == 0) {
281                                 view->start_page = view->current_page;
282                                 if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache))
283                                         view->end_page = view->start_page + 1;
284                         } else {
285                                 view->start_page = view->current_page - 1;
286                                 view->end_page = view->current_page;
287                         }
288                 } else {
289                         view->start_page = view->current_page;
290                         view->end_page = view->current_page;
291                 }
292         }
293
294         ev_pixbuf_cache_set_page_range (view->pixbuf_cache,
295                                         view->start_page,
296                                         view->end_page,
297                                         view->scale);
298 }
299
300 /*** Virtual function implementations ***/
301
302 static void
303 ev_view_finalize (GObject *object)
304 {
305         EvView *view = EV_VIEW (object);
306
307         LOG ("Finalize");
308
309         g_free (view->status);
310         g_free (view->find_status);
311
312         G_OBJECT_CLASS (ev_view_parent_class)->finalize (object);
313 }
314
315 static void
316 ev_view_destroy (GtkObject *object)
317 {
318         EvView *view = EV_VIEW (object);
319
320         if (view->document) {
321                 g_object_unref (view->document);
322                 view->document = NULL;
323         }
324         if (view->pixbuf_cache) {
325                 g_object_unref (view->pixbuf_cache);
326                 view->pixbuf_cache = NULL;
327         }
328         ev_view_set_scroll_adjustments (view, NULL, NULL);
329
330         GTK_OBJECT_CLASS (ev_view_parent_class)->destroy (object);
331 }
332
333 static void
334 ev_view_get_offsets (EvView *view, int *x_offset, int *y_offset)
335 {
336         EvDocument *document = view->document;
337         GtkWidget *widget = GTK_WIDGET (view);
338         int width, height, target_width, target_height;
339
340         g_return_if_fail (EV_IS_DOCUMENT (document));
341
342         ev_page_cache_get_size (view->page_cache,
343                                 view->current_page,
344                                 view->scale,
345                                 &width, &height);
346
347         *x_offset = view->spacing;
348         *y_offset = view->spacing;
349         target_width = width + view->border.left +
350                        view->border.right + view->spacing * 2;
351         target_height = height + view->border.top +
352                         view->border.bottom + view->spacing * 2;
353         *x_offset += MAX (0, (widget->allocation.width - target_width) / 2);
354         *y_offset += MAX (0, (widget->allocation.height - target_height) / 2);
355 }
356
357 static void
358 view_rect_to_doc_rect (EvView *view, GdkRectangle *view_rect, EvRectangle *doc_rect)
359 {
360         int x_offset, y_offset;
361
362         ev_view_get_offsets (view, &x_offset, &y_offset);
363         doc_rect->x1 = (double) (view_rect->x - x_offset) / view->scale;
364         doc_rect->y1 = (double) (view_rect->y - y_offset) / view->scale;
365         doc_rect->x2 = doc_rect->x1 + (double) view_rect->width / view->scale;
366         doc_rect->y2 = doc_rect->y1 + (double) view_rect->height / view->scale;
367 }
368
369 static void
370 doc_rect_to_view_rect (EvView *view, EvRectangle *doc_rect, GdkRectangle *view_rect)
371 {
372         int x_offset, y_offset;
373
374         ev_view_get_offsets (view, &x_offset, &y_offset);
375         view_rect->x = floor (doc_rect->x1 * view->scale) + x_offset;
376         view_rect->y = floor (doc_rect->y1 * view->scale) + y_offset;
377         view_rect->width = ceil (doc_rect->x2 * view->scale) + x_offset - view_rect->x;
378         view_rect->height = ceil (doc_rect->y2 * view->scale) + y_offset - view_rect->y;
379 }
380
381 static void
382 compute_border (EvView *view, int width, int height, GtkBorder *border)
383 {
384         if (view->show_border) {
385                 ev_document_misc_get_page_border_size (width, height, border);
386         } else {
387                 border->left = 0;
388                 border->right = 0;
389                 border->top = 0;
390                 border->bottom = 0;
391         }
392 }
393
394 static void
395 get_bounding_box_size (EvView *view, int *max_width, int *max_height)
396 {
397         GtkBorder border;
398         int width, height;
399
400         if (max_width) {
401                 ev_page_cache_get_max_width_size (view->page_cache,
402                                                   view->scale,
403                                                   &width, &height);
404                 compute_border (view, width, height, &border);
405                 *max_width = width + border.left + border.right;
406         }
407
408
409         if (max_height) {
410                 ev_page_cache_get_max_height_size (view->page_cache,
411                                                    view->scale,
412                                                    &width, &height);
413                 compute_border (view, width, height, &border);
414                 *max_height = height + border.top + border.bottom;
415         }
416 }
417
418
419 static void
420 ev_view_size_request_continuous_and_dual_page (EvView         *view,
421                                                GtkRequisition *requisition)
422 {
423         int max_width, max_height;
424         int n_rows;
425
426         get_bounding_box_size (view, &max_width, &max_height);
427
428         n_rows = (1 + ev_page_cache_get_n_pages (view->page_cache)) / 2;
429
430         requisition->width = (max_width * 2) + (view->spacing * 3);
431         requisition->height = max_height * n_rows + (view->spacing * (n_rows + 1));
432
433         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
434                 requisition->width = 1;
435         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
436                 requisition->width = 1;
437                 /* FIXME: This could actually be set on one page docs or docs
438                  * with a strange aspect ratio. */
439                 /* requisition->height = 1;*/
440         }
441 }
442
443 static void
444 ev_view_size_request_continuous (EvView         *view,
445                                  GtkRequisition *requisition)
446 {
447         int max_width, max_height;
448         int n_pages;
449
450         get_bounding_box_size (view, &max_width, &max_height);
451
452         n_pages = ev_page_cache_get_n_pages (view->page_cache);
453
454         requisition->width = max_width + (view->spacing * 2);
455         requisition->height = max_height * n_pages + (view->spacing * (n_pages + 1));
456
457         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
458                 requisition->width = 1;
459         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
460                 requisition->width = 1;
461                 /* FIXME: This could actually be set on one page docs or docs
462                  * with a strange aspect ratio. */
463                 /* requisition->height = 1;*/
464         }
465 }
466
467 static void
468 ev_view_size_request_dual_page (EvView         *view,
469                                 GtkRequisition *requisition)
470 {
471         GtkBorder border;
472         gint width, height;
473
474         /* Find the largest of the two. */
475         ev_page_cache_get_size (view->page_cache,
476                                 view->current_page,
477                                 view->scale,
478                                 &width, &height);
479         if (view->current_page + 1 < ev_page_cache_get_n_pages (view->page_cache)) {
480                 gint width_2, height_2;
481                 ev_page_cache_get_size (view->page_cache,
482                                         view->current_page + 1,
483                                         view->scale,
484                                         &width_2, &height_2);
485                 if (width_2 > width) {
486                         width = width_2;
487                         height = height_2;
488                 }
489         }
490         compute_border (view, width, height, &border);
491
492         requisition->width = ((width + border.left + border.right) * 2) +
493                 (view->spacing * 3);
494         requisition->height = (height + border.top + border.bottom) +
495                 (view->spacing * 2);
496
497         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
498                 requisition->width = 1;
499         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
500                 requisition->width = 1;
501                 requisition->height = 1;
502         }
503 }
504
505 static void
506 ev_view_size_request_single_page (EvView         *view,
507                                   GtkRequisition *requisition)
508 {
509         GtkBorder border;
510         gint width, height;
511
512         ev_page_cache_get_size (view->page_cache,
513                                 view->current_page,
514                                 view->scale,
515                                 &width, &height);
516         compute_border (view, width, height, &border);
517
518         requisition->width = width + border.left + border.right + (2 * view->spacing);
519         requisition->height = height + border.top + border.bottom + (2 * view->spacing);
520
521         if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
522                 requisition->width = 1;
523                 requisition->height = height + border.top + border.bottom + (2 * view->spacing);
524         } else if (view->sizing_mode == EV_SIZING_BEST_FIT) {
525                 requisition->width = 1;
526                 requisition->height = 1;
527         }
528 }
529
530 static void
531 ev_view_size_request (GtkWidget      *widget,
532                       GtkRequisition *requisition)
533 {
534         EvView *view = EV_VIEW (widget);
535
536         if (!GTK_WIDGET_REALIZED (widget))
537                 return;
538
539         if (view->document == NULL) {
540                 requisition->width = 1;
541                 requisition->height = 1;
542                 return;
543         }
544
545         if (view->presentation) {
546                 requisition->width = 1;
547                 requisition->height = 1;
548                 return;
549         }
550
551         if (view->continuous && view->dual_page)
552                 ev_view_size_request_continuous_and_dual_page (view, requisition);
553         else if (view->continuous)
554                 ev_view_size_request_continuous (view, requisition);
555         else if (view->dual_page)
556                 ev_view_size_request_dual_page (view, requisition);
557         else
558                 ev_view_size_request_single_page (view, requisition);
559 }
560
561 static void
562 ev_view_size_allocate (GtkWidget      *widget,
563                        GtkAllocation  *allocation)
564 {
565         EvView *view = EV_VIEW (widget);
566
567         GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation);
568
569         view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
570         view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
571
572         if (GTK_WIDGET_REALIZED (widget)) {
573                 gdk_window_resize (view->bin_window,
574                                    MAX (widget->allocation.width, widget->requisition.width),
575                                    MAX (widget->allocation.height, widget->requisition.height));
576         }
577
578         if (view->document)
579                 view_update_range_and_current_page (view);
580 }
581
582 static void
583 ev_view_realize (GtkWidget *widget)
584 {
585         EvView *view = EV_VIEW (widget);
586         GdkWindowAttr attributes;
587
588         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
589
590
591         attributes.window_type = GDK_WINDOW_CHILD;
592         attributes.wclass = GDK_INPUT_OUTPUT;
593         attributes.visual = gtk_widget_get_visual (widget);
594         attributes.colormap = gtk_widget_get_colormap (widget);
595
596         attributes.x = widget->allocation.x;
597         attributes.y = widget->allocation.y;
598         attributes.width = widget->allocation.width;
599         attributes.height = widget->allocation.height;
600         attributes.event_mask = 0;
601
602         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
603                                          &attributes,
604                                          GDK_WA_X | GDK_WA_Y |
605                                          GDK_WA_COLORMAP |
606                                          GDK_WA_VISUAL);
607         gdk_window_set_user_data (widget->window, widget);
608         widget->style = gtk_style_attach (widget->style, widget->window);
609         gdk_window_set_background (widget->window, &widget->style->mid[widget->state]);
610
611         attributes.x = 0;
612         attributes.y = 0;
613         attributes.width = MAX (widget->allocation.width, widget->requisition.width);
614         attributes.height = MAX (widget->allocation.height, widget->requisition.height);
615         attributes.event_mask = GDK_EXPOSURE_MASK |
616                                 GDK_BUTTON_PRESS_MASK |
617                                 GDK_BUTTON_RELEASE_MASK |
618                                 GDK_SCROLL_MASK |
619                                 GDK_KEY_PRESS_MASK |
620                                 GDK_POINTER_MOTION_MASK |
621                                 GDK_LEAVE_NOTIFY_MASK;
622
623         view->bin_window = gdk_window_new (widget->window,
624                                            &attributes,
625                                            GDK_WA_X | GDK_WA_Y |
626                                            GDK_WA_COLORMAP |
627                                            GDK_WA_VISUAL);
628         gdk_window_set_user_data (view->bin_window, widget);
629         gdk_window_show (view->bin_window);
630
631         widget->style = gtk_style_attach (widget->style, view->bin_window);
632         gdk_window_set_background (view->bin_window, &widget->style->mid[widget->state]);
633
634         if (view->document) {
635                 /* We can't get page size without a target, so we have to
636                  * queue a size request at realization. Could be fixed
637                  * with EvDocument changes to allow setting a GdkScreen
638                  * without setting a target.
639                  */
640                 gtk_widget_queue_resize (widget);
641         }
642 }
643
644 static void
645 ev_view_unrealize (GtkWidget *widget)
646 {
647         EvView *view = EV_VIEW (widget);
648
649         gdk_window_set_user_data (view->bin_window, NULL);
650         gdk_window_destroy (view->bin_window);
651         view->bin_window = NULL;
652
653         GTK_WIDGET_CLASS (ev_view_parent_class)->unrealize (widget);
654 }
655
656 #if 0
657 static guint32
658 ev_gdk_color_to_rgb (const GdkColor *color)
659 {
660   guint32 result;
661   result = (0xff0000 | (color->red & 0xff00));
662   result <<= 8;
663   result |= ((color->green & 0xff00) | (color->blue >> 8));
664   return result;
665 }
666
667 static void
668 draw_rubberband (GtkWidget *widget, GdkWindow *window,
669                  const GdkRectangle *rect, guchar alpha)
670 {
671         GdkGC *gc;
672         GdkPixbuf *pixbuf;
673         GdkColor *fill_color_gdk;
674         guint fill_color;
675
676         fill_color_gdk = gdk_color_copy (&GTK_WIDGET (widget)->style->base[GTK_STATE_SELECTED]);
677         fill_color = ev_gdk_color_to_rgb (fill_color_gdk) << 8 | alpha;
678
679         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
680                                  rect->width, rect->height);
681         gdk_pixbuf_fill (pixbuf, fill_color);
682
683         gdk_draw_pixbuf (window, NULL, pixbuf,
684                          0, 0,
685                          rect->x, rect->y,
686                          rect->width, rect->height,
687                          GDK_RGB_DITHER_NONE,
688                          0, 0);
689
690         g_object_unref (pixbuf);
691
692         gc = gdk_gc_new (window);
693         gdk_gc_set_rgb_fg_color (gc, fill_color_gdk);
694         gdk_draw_rectangle (window, gc, FALSE,
695                             rect->x, rect->y,
696                             rect->width - 1,
697                             rect->height - 1);
698         g_object_unref (gc);
699
700         gdk_color_free (fill_color_gdk);
701 }
702
703
704 static void
705 highlight_find_results (EvView *view)
706 {
707         EvDocumentFind *find;
708         int i, results = 0;
709
710         g_return_if_fail (EV_IS_DOCUMENT_FIND (view->document));
711
712         find = EV_DOCUMENT_FIND (view->document);
713
714         results = ev_document_find_get_n_results (find, view->current_page);
715
716         for (i = 0; i < results; i++) {
717                 EvRectangle rectangle;
718                 GdkRectangle view_rectangle;
719                 guchar alpha;
720
721                 alpha = (i == view->find_result) ? 0x90 : 0x20;
722                 ev_document_find_get_result (find, view->current_page,
723                                              i, &rectangle);
724                 doc_rect_to_view_rect (view, &rectangle, &view_rectangle);
725                 draw_rubberband (GTK_WIDGET (view), view->bin_window,
726                                  &view_rectangle, alpha);
727         }
728 }
729 #endif
730
731
732 static gboolean
733 get_page_extents (EvView       *view,
734                   gint          page,
735                   GdkRectangle *page_area,
736                   GtkBorder    *border)
737 {
738         GtkWidget *widget;
739         int width, height;
740
741         widget = GTK_WIDGET (view);
742
743         /* Quick sanity check */
744         if (view->presentation) {
745                 if (view->current_page != page)
746                         return FALSE;
747         } else if (view->continuous) {
748                 if (page < view->start_page ||
749                     page > view->end_page)
750                         return FALSE;
751         } else if (view->dual_page) {
752                 if (ABS (page - view->current_page) > 1)
753                         return FALSE;
754         } else {
755                 if (view->current_page != page)
756                         return FALSE;
757         }
758
759         /* Get the size of the page */
760         ev_page_cache_get_size (view->page_cache, page,
761                                 view->scale,
762                                 &width, &height);
763         compute_border (view, width, height, border);
764         page_area->width = width + border->left + border->right;
765         page_area->height = height + border->top + border->bottom;
766
767         if (view->presentation) {
768                 page_area->x = (MAX (0, widget->allocation.width - width))/2;
769                 page_area->y = (MAX (0, widget->allocation.height - height))/2;
770         } else if (view->continuous) {
771                 gint max_width, max_height;
772                 gint x, y;
773
774                 get_bounding_box_size (view, &max_width, &max_height);
775                 /* Get the location of the bounding box */
776                 if (view->dual_page) {
777                         x = view->spacing + (page % 2) * (max_width + view->spacing);
778                         y = view->spacing + (page / 2) * (max_height + view->spacing);
779                         x = x + MAX (0, widget->allocation.width - (max_width * 2 + view->spacing * 3))/2;
780                 } else {
781                         x = view->spacing;
782                         y = view->spacing + page * (max_height + view->spacing);
783                         x = x + MAX (0, widget->allocation.width - (max_width + view->spacing * 2))/2;
784                 }
785                 page_area->x = x;
786                 page_area->y = y;
787         } else {
788                 gint x, y;
789                 if (view->dual_page) {
790                         gint width_2, height_2;
791                         gint max_width = width;
792                         gint max_height = height;
793                         GtkBorder overall_border;
794                         gint other_page;
795
796                         other_page = page ^ 1;
797
798                         /* First, we get the bounding box of the two pages */
799                         if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
800                                 ev_page_cache_get_size (view->page_cache,
801                                                         page + 1,
802                                                         view->scale,
803                                                         &width_2, &height_2);
804                                 if (width_2 > width)
805                                         max_width = width_2;
806                                 if (height_2 > height)
807                                         max_height = height_2;
808                         }
809                         compute_border (view, max_width, max_height, &overall_border);
810
811                         /* Find the offsets */
812                         x = view->spacing;
813                         y = view->spacing;
814
815                         /* Adjust for being the left or right page */
816                         if (page % 2 == 0)
817                                 x = x + max_width - width;
818                         else
819                                 x = x + (max_width + overall_border.left + overall_border.right) + view->spacing;
820
821                         y = y + (max_height - height)/2;
822
823                         /* Adjust for extra allocation */
824                         x = x + MAX (0, widget->allocation.width -
825                                      ((max_width + overall_border.left + overall_border.right) * 2 + view->spacing * 3))/2;
826                         y = y + MAX (0, widget->allocation.height - (height + view->spacing * 2))/2;
827                 } else {
828                         x = view->spacing;
829                         y = view->spacing;
830
831                         /* Adjust for extra allocation */
832                         x = x + MAX (0, widget->allocation.width - (width + view->spacing * 2))/2;
833                         y = y + MAX (0, widget->allocation.height - (height + view->spacing * 2))/2;
834                 }
835
836                 page_area->x = x;
837                 page_area->y = y;
838         }
839
840         return TRUE;
841 }
842
843 static void
844 draw_one_page (EvView       *view,
845                gint          page,
846                GdkRectangle *page_area,
847                GtkBorder    *border,
848                GdkRectangle *expose_area)
849 {
850         gint width, height;
851         GdkPixbuf *scaled_image;
852         GdkPixbuf *current_pixbuf;
853         GdkRectangle overlap;
854         GdkRectangle real_page_area;
855
856         g_assert (view->document);
857
858         if (! gdk_rectangle_intersect (page_area, expose_area, &overlap))
859                 return;
860
861         ev_page_cache_get_size (view->page_cache,
862                                 page, view->scale,
863                                 &width, &height);
864
865         if (view->show_border)
866                 ev_document_misc_paint_one_page (view->bin_window,
867                                                  GTK_WIDGET (view),
868                                                  page_area, border);
869
870         /* Render the document itself */
871         real_page_area = *page_area;
872
873         real_page_area.x += border->left;
874         real_page_area.y += border->top;
875         real_page_area.width -= (border->left + border->right);
876         real_page_area.height -= (border->top + border->bottom);
877
878         if (! gdk_rectangle_intersect (&real_page_area, expose_area, &overlap))
879                 return;
880
881         current_pixbuf = ev_pixbuf_cache_get_pixbuf (view->pixbuf_cache, page);
882         if (current_pixbuf == NULL)
883                 scaled_image = NULL;
884         else if (width == gdk_pixbuf_get_width (current_pixbuf) &&
885                  height == gdk_pixbuf_get_height (current_pixbuf))
886                 scaled_image = g_object_ref (current_pixbuf);
887         else
888                 scaled_image = gdk_pixbuf_scale_simple (current_pixbuf,
889                                                         width, height,
890                                                         GDK_INTERP_NEAREST);
891
892         if (scaled_image) {
893                 gdk_draw_pixbuf (view->bin_window,
894                                  GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
895                                  scaled_image,
896                                  overlap.x - real_page_area.x,
897                                  overlap.y - real_page_area.y,
898                                  overlap.x, overlap.y,
899                                  overlap.width, overlap.height,
900                                  GDK_RGB_DITHER_NORMAL,
901                                  0, 0);
902                 g_object_unref (scaled_image);
903         }
904 }
905
906 static void
907 ev_view_bin_expose (EvView         *view,
908                     GdkEventExpose *event)
909 {
910         int i;
911
912         if (view->document == NULL)
913                 return;
914
915         for (i = view->start_page; i <= view->end_page; i++) {
916                 GdkRectangle page_area;
917                 GtkBorder border;
918
919                 if (! get_page_extents (view, i, &page_area, &border))
920                         continue;
921                 draw_one_page (view, i, &page_area, &border, &(event->area));
922         }
923
924 #if 0
925         if (EV_IS_DOCUMENT_FIND (view->document)) {
926                 highlight_find_results (view);
927         }
928
929         if (view->has_selection) {
930                 GdkRectangle rubberband;
931
932                 doc_rect_to_view_rect (view, &view->selection, &rubberband);
933                 if (rubberband.width > 0 && rubberband.height > 0) {
934                         draw_rubberband (GTK_WIDGET (view), view->bin_window,
935                                          &rubberband, 0x40);
936                 }
937         }
938 #endif
939 }
940
941 static gboolean
942 ev_view_expose_event (GtkWidget      *widget,
943                       GdkEventExpose *event)
944 {
945         EvView *view = EV_VIEW (widget);
946
947         if (event->window == view->bin_window)
948                 ev_view_bin_expose (view, event);
949         else
950                 return GTK_WIDGET_CLASS (ev_view_parent_class)->expose_event (widget, event);
951
952         return FALSE;
953 }
954
955 void
956 ev_view_select_all (EvView *ev_view)
957 {
958         GtkWidget *widget = GTK_WIDGET (ev_view);
959         GdkRectangle selection;
960         int width, height;
961         int x_offset, y_offset;
962
963         g_return_if_fail (EV_IS_VIEW (ev_view));
964
965
966         ev_view_get_offsets (ev_view, &x_offset, &y_offset);
967         ev_page_cache_get_size (ev_view->page_cache,
968                                 ev_view->current_page,
969                                 ev_view->scale,
970                                 &width, &height);
971
972         ev_view->has_selection = TRUE;
973         selection.x = x_offset + ev_view->border.left;
974         selection.y = y_offset + ev_view->border.top;
975         selection.width = width;
976         selection.height = height;
977         view_rect_to_doc_rect (ev_view, &selection, &ev_view->selection);
978
979         gtk_widget_queue_draw (widget);
980 }
981
982 void
983 ev_view_copy (EvView *ev_view)
984 {
985         GtkClipboard *clipboard;
986         char *text;
987
988         if (!ev_document_can_get_text (ev_view->document)) {
989                 return;
990         }
991
992         ev_document_doc_mutex_lock ();
993         text = ev_document_get_text (ev_view->document,
994                                      ev_view->current_page,
995                                      &ev_view->selection);
996         ev_document_doc_mutex_unlock ();
997
998         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
999                                               GDK_SELECTION_CLIPBOARD);
1000         gtk_clipboard_set_text (clipboard, text, -1);
1001         g_free (text);
1002 }
1003
1004 void
1005 ev_view_set_show_border (EvView *view, gboolean show_border)
1006 {
1007         g_return_if_fail (EV_IS_VIEW (view));
1008
1009         view->show_border = show_border;
1010
1011         gtk_widget_queue_resize (GTK_WIDGET (view));
1012 }
1013
1014 static void
1015 ev_view_primary_get_cb (GtkClipboard     *clipboard,
1016                         GtkSelectionData *selection_data,
1017                         guint             info,
1018                         gpointer          data)
1019 {
1020         EvView *ev_view = EV_VIEW (data);
1021         char *text;
1022
1023         if (!ev_document_can_get_text (ev_view->document)) {
1024                 return;
1025         }
1026
1027         ev_document_doc_mutex_lock ();
1028         text = ev_document_get_text (ev_view->document,
1029                                      ev_view->current_page,
1030                                      &ev_view->selection);
1031         ev_document_doc_mutex_unlock ();
1032         gtk_selection_data_set_text (selection_data, text, -1);
1033 }
1034
1035 static void
1036 ev_view_primary_clear_cb (GtkClipboard *clipboard,
1037                           gpointer      data)
1038 {
1039         EvView *ev_view = EV_VIEW (data);
1040
1041         ev_view->has_selection = FALSE;
1042 }
1043
1044 static void
1045 ev_view_update_primary_selection (EvView *ev_view)
1046 {
1047         GtkClipboard *clipboard;
1048
1049         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
1050                                               GDK_SELECTION_PRIMARY);
1051
1052         if (ev_view->has_selection) {
1053                 if (!gtk_clipboard_set_with_owner (clipboard,
1054                                                    targets,
1055                                                    G_N_ELEMENTS (targets),
1056                                                    ev_view_primary_get_cb,
1057                                                    ev_view_primary_clear_cb,
1058                                                    G_OBJECT (ev_view)))
1059                         ev_view_primary_clear_cb (clipboard, ev_view);
1060         } else {
1061                 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (ev_view))
1062                         gtk_clipboard_clear (clipboard);
1063         }
1064 }
1065
1066 static gboolean
1067 ev_view_button_press_event (GtkWidget      *widget,
1068                             GdkEventButton *event)
1069 {
1070         EvView *view = EV_VIEW (widget);
1071
1072         if (!GTK_WIDGET_HAS_FOCUS (widget)) {
1073                 gtk_widget_grab_focus (widget);
1074         }
1075
1076         view->pressed_button = event->button;
1077
1078         switch (event->button) {
1079                 case 1:
1080                         if (view->has_selection) {
1081                                 view->has_selection = FALSE;
1082                                 gtk_widget_queue_draw (widget);
1083                         }
1084
1085                         view->selection_start.x = event->x;
1086                         view->selection_start.y = event->y;
1087                         break;
1088         }
1089
1090         return TRUE;
1091 }
1092
1093 static char *
1094 status_message_from_link (EvView *view, EvLink *link)
1095 {
1096         EvLinkType type;
1097         char *msg = NULL;
1098         char *page_label;
1099
1100         type = ev_link_get_link_type (link);
1101
1102         switch (type) {
1103                 case EV_LINK_TYPE_TITLE:
1104                         if (ev_link_get_title (link))
1105                                 msg = g_strdup (ev_link_get_title (link));
1106                         break;
1107                 case EV_LINK_TYPE_PAGE:
1108                         page_label = ev_page_cache_get_page_label (view->page_cache, ev_link_get_page (link));
1109                         msg = g_strdup_printf (_("Go to page %s"), page_label);
1110                         g_free (page_label);
1111                         break;
1112                 case EV_LINK_TYPE_EXTERNAL_URI:
1113                         msg = g_strdup (ev_link_get_uri (link));
1114                         break;
1115                 default:
1116                         break;
1117         }
1118
1119         return msg;
1120 }
1121
1122 static void
1123 ev_view_set_status (EvView *view, const char *message)
1124 {
1125         g_return_if_fail (EV_IS_VIEW (view));
1126
1127         if (message != view->status) {
1128                 g_free (view->status);
1129                 view->status = g_strdup (message);
1130                 g_object_notify (G_OBJECT (view), "status");
1131         }
1132 }
1133
1134 static void
1135 ev_view_set_find_status (EvView *view, const char *message)
1136 {
1137         g_return_if_fail (EV_IS_VIEW (view));
1138
1139         g_free (view->find_status);
1140         view->find_status = g_strdup (message);
1141         g_object_notify (G_OBJECT (view), "find-status");
1142 }
1143
1144 static GdkCursor *
1145 ev_view_create_invisible_cursor(void)
1146 {
1147        GdkBitmap *empty;
1148        GdkColor black = { 0, 0, 0, 0 };
1149        static char bits[] = { 0x00 };
1150
1151        empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
1152
1153        return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
1154 }
1155
1156 static void
1157 ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
1158 {
1159         GdkCursor *cursor = NULL;
1160         GdkDisplay *display;
1161         GtkWidget *widget;
1162
1163         if (view->cursor == new_cursor) {
1164                 return;
1165         }
1166
1167         widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
1168         display = gtk_widget_get_display (widget);
1169         view->cursor = new_cursor;
1170
1171         switch (new_cursor) {
1172                 case EV_VIEW_CURSOR_NORMAL:
1173                         gdk_window_set_cursor (widget->window, NULL);
1174                         break;
1175                 case EV_VIEW_CURSOR_LINK:
1176                         cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
1177                         break;
1178                 case EV_VIEW_CURSOR_WAIT:
1179                         cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
1180                         break;
1181                 case EV_VIEW_CURSOR_HIDDEN:
1182                         cursor = ev_view_create_invisible_cursor ();
1183                         break;
1184
1185         }
1186
1187         if (cursor) {
1188                 gdk_window_set_cursor (widget->window, cursor);
1189                 gdk_cursor_unref (cursor);
1190                 gdk_flush();
1191         }
1192 }
1193
1194
1195 static void
1196 find_page_at_location (EvView  *view,
1197                        gdouble  x,
1198                        gdouble  y,
1199                        gint    *page,
1200                        gint    *x_offset,
1201                        gint    *y_offset)
1202 {
1203         gint width, height;
1204
1205         ev_page_cache_get_size (view->page_cache,
1206                                 view->current_page,
1207                                 view->scale,
1208                                 &width, &height);
1209
1210         x -= (view->border.left + view->spacing);
1211         y -= (view->border.top + view->spacing);
1212
1213         if ((x < 0) || (y < 0) ||
1214             (x >= width) || (y >= height)) {
1215                 *page = -1;
1216                 return;
1217         }
1218         *page = view->current_page;
1219         *x_offset = (gint) x;
1220         *y_offset = (gint) y;
1221 }
1222
1223 static EvLink *
1224 get_link_at_location (EvView  *view,
1225                       gdouble  x,
1226                       gdouble  y)
1227 {
1228         gint page;
1229         gint x_offset, y_offset;
1230         GList *link_mapping;
1231
1232         find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
1233         if (page == -1)
1234                 return NULL;
1235
1236         link_mapping = ev_pixbuf_cache_get_link_mapping (view->pixbuf_cache, page);
1237
1238         return ev_link_mapping_find (link_mapping, x_offset /view->scale, y_offset /view->scale);
1239 }
1240
1241
1242 static gboolean
1243 ev_view_motion_notify_event (GtkWidget      *widget,
1244                              GdkEventMotion *event)
1245 {
1246         EvView *view = EV_VIEW (widget);
1247
1248         if (view->pressed_button > 0) {
1249                 GdkRectangle selection;
1250
1251                 view->has_selection = TRUE;
1252                 selection.x = MIN (view->selection_start.x, event->x);
1253                 selection.y = MIN (view->selection_start.y, event->y);
1254                 selection.width = ABS (view->selection_start.x - event->x) + 1;
1255                 selection.height = ABS (view->selection_start.y - event->y) + 1;
1256                 view_rect_to_doc_rect (view, &selection, &view->selection);
1257
1258                 gtk_widget_queue_draw (widget);
1259         } else if (view->document) {
1260                 EvLink *link;
1261
1262                 link = get_link_at_location (view, event->x, event->y);
1263                 if (link) {
1264                         char *msg;
1265
1266                         msg = status_message_from_link (view, link);
1267                         ev_view_set_status (view, msg);
1268                         ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
1269                         g_free (msg);
1270                 } else {
1271                         ev_view_set_status (view, NULL);
1272                         if (view->cursor == EV_VIEW_CURSOR_LINK) {
1273                                 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1274                         }
1275                 }
1276         }
1277
1278         return TRUE;
1279 }
1280
1281 /* FIXME: standardize this sometime */
1282 static void
1283 go_to_link (EvView *view, EvLink *link)
1284 {
1285         EvLinkType type;
1286         const char *uri;
1287         int page;
1288
1289         type = ev_link_get_link_type (link);
1290
1291         switch (type) {
1292                 case EV_LINK_TYPE_TITLE:
1293                         break;
1294                 case EV_LINK_TYPE_PAGE:
1295                         page = ev_link_get_page (link);
1296                         ev_page_cache_set_current_page (view->page_cache, page);
1297                         break;
1298                 case EV_LINK_TYPE_EXTERNAL_URI:
1299                         uri = ev_link_get_uri (link);
1300                         gnome_vfs_url_show (uri);
1301                         break;
1302         }
1303 }
1304
1305
1306 static gboolean
1307 ev_view_button_release_event (GtkWidget      *widget,
1308                               GdkEventButton *event)
1309 {
1310         EvView *view = EV_VIEW (widget);
1311
1312         view->pressed_button = -1;
1313
1314         if (view->has_selection) {
1315                 ev_view_update_primary_selection (view);
1316         } else if (view->document) {
1317                 EvLink *link;
1318
1319                 link = get_link_at_location (view, event->x, event->y);
1320                 if (link) {
1321                         go_to_link (view, link);
1322                 }
1323         }
1324
1325         return FALSE;
1326 }
1327
1328 static void
1329 on_adjustment_value_changed (GtkAdjustment  *adjustment,
1330                              EvView *view)
1331 {
1332         view_update_adjustments (view);
1333 }
1334
1335 static void
1336 set_scroll_adjustment (EvView *view,
1337                        GtkOrientation  orientation,
1338                        GtkAdjustment  *adjustment)
1339 {
1340         GtkAdjustment **to_set;
1341
1342         if (orientation == GTK_ORIENTATION_HORIZONTAL)
1343                 to_set = &view->hadjustment;
1344         else
1345                 to_set = &view->vadjustment;
1346
1347         if (*to_set != adjustment) {
1348                 if (*to_set) {
1349                         g_signal_handlers_disconnect_by_func (*to_set,
1350                                                               (gpointer) on_adjustment_value_changed,
1351                                                               view);
1352                         g_object_unref (*to_set);
1353                 }
1354
1355                 *to_set = adjustment;
1356                 view_set_adjustment_values (view, orientation);
1357
1358                 if (*to_set) {
1359                         g_object_ref (*to_set);
1360                         g_signal_connect (*to_set, "value_changed",
1361                                           G_CALLBACK (on_adjustment_value_changed), view);
1362                 }
1363         }
1364 }
1365
1366 static void
1367 ev_view_set_scroll_adjustments (EvView *view,
1368                                 GtkAdjustment  *hadjustment,
1369                                 GtkAdjustment  *vadjustment)
1370 {
1371         set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
1372         set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
1373
1374         view_update_adjustments (view);
1375 }
1376
1377 static void
1378 add_scroll_binding_keypad (GtkBindingSet  *binding_set,
1379                            guint           keyval,
1380                            GtkScrollType   scroll,
1381                            gboolean        horizontal)
1382 {
1383   guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
1384
1385   gtk_binding_entry_add_signal (binding_set, keyval, 0,
1386                                 "scroll_view", 2,
1387                                 GTK_TYPE_SCROLL_TYPE, scroll,
1388                                 G_TYPE_BOOLEAN, horizontal);
1389   gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
1390                                 "scroll_view", 2,
1391                                 GTK_TYPE_SCROLL_TYPE, scroll,
1392                                 G_TYPE_BOOLEAN, horizontal);
1393 }
1394
1395 static void
1396 add_scroll_binding_shifted (GtkBindingSet  *binding_set,
1397                             guint           keyval,
1398                             GtkScrollType   scroll_normal,
1399                             GtkScrollType   scroll_shifted,
1400                             gboolean        horizontal)
1401 {
1402   gtk_binding_entry_add_signal (binding_set, keyval, 0,
1403                                 "scroll_view", 2,
1404                                 GTK_TYPE_SCROLL_TYPE, scroll_normal,
1405                                 G_TYPE_BOOLEAN, horizontal);
1406   gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
1407                                 "scroll_view", 2,
1408                                 GTK_TYPE_SCROLL_TYPE, scroll_shifted,
1409                                 G_TYPE_BOOLEAN, horizontal);
1410 }
1411
1412 static void
1413 ev_view_jump (EvView        *view,
1414               GtkScrollType  scroll)
1415 {
1416         GtkAdjustment *adjustment;
1417         double value, increment;
1418         gboolean first_page = FALSE;
1419         gboolean last_page = FALSE;
1420
1421         /* Assign values for increment and vertical adjustment */
1422         adjustment = view->vadjustment;
1423         increment = adjustment->page_size * 0.75;
1424         value = adjustment->value;
1425
1426         /* Assign boolean for first and last page */
1427         if (view->current_page == 0)
1428                 first_page = TRUE;
1429         if (view->current_page == ev_page_cache_get_n_pages (view->page_cache) - 1)
1430                 last_page = TRUE;
1431
1432         switch (scroll) {
1433                 case EV_SCROLL_PAGE_BACKWARD:
1434                         /* Do not jump backwards if at the first page */
1435                         if (value == (adjustment->lower) && first_page) {
1436                                 /* Do nothing */
1437                                 /* At the top of a page, assign the upper bound limit of previous page */
1438                         } else if (value == (adjustment->lower)) {
1439                                 value = adjustment->upper - adjustment->page_size;
1440                                 ev_page_cache_set_current_page (view->page_cache, view->current_page - 1);
1441                                 /* Jump to the top */
1442                         } else {
1443                                 value = MAX (value - increment, adjustment->lower);
1444                         }
1445                         break;
1446                 case EV_SCROLL_PAGE_FORWARD:
1447                         /* Do not jump forward if at the last page */
1448                         if (value == (adjustment->upper - adjustment->page_size) && last_page) {
1449                                 /* Do nothing */
1450                         /* At the bottom of a page, assign the lower bound limit of next page */
1451                         } else if (value == (adjustment->upper - adjustment->page_size)) {
1452                                 value = 0;
1453                                 ev_page_cache_set_current_page (view->page_cache, view->current_page + 1);
1454                         /* Jump to the bottom */
1455                         } else {
1456                                 value = MIN (value + increment, adjustment->upper - adjustment->page_size);
1457                         }
1458                         break;
1459                 default:
1460                         break;
1461         }
1462
1463         gtk_adjustment_set_value (adjustment, value);
1464 }
1465
1466 static void
1467 ev_view_scroll_view (EvView *view,
1468                      GtkScrollType scroll,
1469                      gboolean horizontal)
1470 {
1471         if (scroll == GTK_SCROLL_PAGE_BACKWARD) {
1472                 ev_page_cache_prev_page (view->page_cache);
1473         } else if (scroll == GTK_SCROLL_PAGE_FORWARD) {
1474                 ev_page_cache_next_page (view->page_cache);
1475         } else if (scroll == EV_SCROLL_PAGE_BACKWARD || scroll == EV_SCROLL_PAGE_FORWARD) {
1476                 ev_view_jump (view, scroll);
1477         } else {
1478                 GtkAdjustment *adjustment;
1479                 double value;
1480
1481                 if (horizontal) {
1482                         adjustment = view->hadjustment;
1483                 } else {
1484                         adjustment = view->vadjustment;
1485                 }
1486
1487                 value = adjustment->value;
1488
1489                 switch (scroll) {
1490                         case GTK_SCROLL_STEP_BACKWARD:
1491                                 value -= adjustment->step_increment;
1492                                 break;
1493                         case GTK_SCROLL_STEP_FORWARD:
1494                                 value += adjustment->step_increment;
1495                                 break;
1496                         default:
1497                                 break;
1498                 }
1499
1500                 value = CLAMP (value, adjustment->lower,
1501                                adjustment->upper - adjustment->page_size);
1502
1503                 gtk_adjustment_set_value (adjustment, value);
1504         }
1505 }
1506
1507 static void
1508 ev_view_set_property (GObject      *object,
1509                       guint         prop_id,
1510                       const GValue *value,
1511                       GParamSpec   *pspec)
1512 {
1513         EvView *view = EV_VIEW (object);
1514
1515         switch (prop_id)
1516         {
1517         case PROP_CONTINUOUS:
1518                 ev_view_set_continuous (view, g_value_get_boolean (value));
1519                 break;
1520         case PROP_DUAL_PAGE:
1521                 ev_view_set_dual_page (view, g_value_get_boolean (value));
1522                 break;
1523         case PROP_FULL_SCREEN:
1524                 ev_view_set_full_screen (view, g_value_get_boolean (value));
1525                 break;
1526         case PROP_PRESENTATION:
1527                 ev_view_set_presentation (view, g_value_get_boolean (value));
1528                 break;
1529         case PROP_SIZING_MODE:
1530                 ev_view_set_sizing_mode (view, g_value_get_enum (value));
1531                 break;
1532         default:
1533                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1534         }
1535 }
1536
1537 static void
1538 ev_view_get_property (GObject *object,
1539                       guint prop_id,
1540                       GValue *value,
1541                       GParamSpec *pspec)
1542 {
1543         EvView *view = EV_VIEW (object);
1544
1545         switch (prop_id)
1546         {
1547         case PROP_STATUS:
1548                 g_value_set_string (value, view->status);
1549                 break;
1550         case PROP_FIND_STATUS:
1551                 g_value_set_string (value, view->status);
1552                 break;
1553         case PROP_CONTINUOUS:
1554                 g_value_set_boolean (value, view->continuous);
1555                 break;
1556         case PROP_DUAL_PAGE:
1557                 g_value_set_boolean (value, view->dual_page);
1558                 break;
1559         case PROP_FULL_SCREEN:
1560                 g_value_set_boolean (value, view->full_screen);
1561                 break;
1562         case PROP_PRESENTATION:
1563                 g_value_set_boolean (value, view->presentation);
1564                 break;
1565         case PROP_SIZING_MODE:
1566                 g_value_set_enum (value, view->sizing_mode);
1567                 break;
1568         default:
1569                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1570         }
1571 }
1572
1573 static void
1574 ev_view_class_init (EvViewClass *class)
1575 {
1576         GObjectClass *object_class = G_OBJECT_CLASS (class);
1577         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
1578         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1579         GtkBindingSet *binding_set;
1580
1581         object_class->finalize = ev_view_finalize;
1582         object_class->set_property = ev_view_set_property;
1583         object_class->get_property = ev_view_get_property;
1584
1585         widget_class->expose_event = ev_view_expose_event;
1586         widget_class->button_press_event = ev_view_button_press_event;
1587         widget_class->motion_notify_event = ev_view_motion_notify_event;
1588         widget_class->button_release_event = ev_view_button_release_event;
1589         widget_class->size_request = ev_view_size_request;
1590         widget_class->size_allocate = ev_view_size_allocate;
1591         widget_class->realize = ev_view_realize;
1592         widget_class->unrealize = ev_view_unrealize;
1593         gtk_object_class->destroy = ev_view_destroy;
1594
1595         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
1596         class->scroll_view = ev_view_scroll_view;
1597
1598         widget_class->set_scroll_adjustments_signal =  g_signal_new ("set-scroll-adjustments",
1599                                                                      G_OBJECT_CLASS_TYPE (object_class),
1600                                                                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1601                                                                      G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
1602                                                                      NULL, NULL,
1603                                                                      ev_marshal_VOID__OBJECT_OBJECT,
1604                                                                      G_TYPE_NONE, 2,
1605                                                                      GTK_TYPE_ADJUSTMENT,
1606                                                                      GTK_TYPE_ADJUSTMENT);
1607
1608         g_signal_new ("scroll_view",
1609                       G_TYPE_FROM_CLASS (object_class),
1610                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1611                       G_STRUCT_OFFSET (EvViewClass, scroll_view),
1612                       NULL, NULL,
1613                       ev_marshal_VOID__ENUM_BOOLEAN,
1614                       G_TYPE_NONE, 2,
1615                       GTK_TYPE_SCROLL_TYPE,
1616                       G_TYPE_BOOLEAN);
1617
1618         g_object_class_install_property (object_class,
1619                                          PROP_STATUS,
1620                                          g_param_spec_string ("status",
1621                                                               "Status Message",
1622                                                               "The status message",
1623                                                               NULL,
1624                                                               G_PARAM_READABLE));
1625
1626         g_object_class_install_property (object_class,
1627                                          PROP_FIND_STATUS,
1628                                          g_param_spec_string ("find-status",
1629                                                               "Find Status Message",
1630                                                               "The find status message",
1631                                                               NULL,
1632                                                               G_PARAM_READABLE));
1633
1634         g_object_class_install_property (object_class,
1635                                          PROP_CONTINUOUS,
1636                                          g_param_spec_boolean ("continuous",
1637                                                                "Continuous",
1638                                                                "Continuous scrolling mode",
1639                                                                TRUE,
1640                                                                G_PARAM_READWRITE));
1641
1642         g_object_class_install_property (object_class,
1643                                          PROP_DUAL_PAGE,
1644                                          g_param_spec_boolean ("dual-page",
1645                                                                "Dual Page",
1646                                                                "Two pages visible at once",
1647                                                                FALSE,
1648                                                                G_PARAM_READWRITE));
1649         g_object_class_install_property (object_class,
1650                                          PROP_FULL_SCREEN,
1651                                          g_param_spec_boolean ("full-screen",
1652                                                                "Full Screen",
1653                                                                "Draw page in a full-screen fashion",
1654                                                                FALSE,
1655                                                                G_PARAM_READWRITE));
1656         g_object_class_install_property (object_class,
1657                                          PROP_PRESENTATION,
1658                                          g_param_spec_boolean ("presentation",
1659                                                                "Presentation",
1660                                                                "Draw page in presentation mode",
1661                                                                TRUE,
1662                                                                G_PARAM_READWRITE));
1663
1664         g_object_class_install_property (object_class,
1665                                          PROP_SIZING_MODE,
1666                                          g_param_spec_enum ("sizing-mode",
1667                                                             "Sizing Mode",
1668                                                             "Sizing Mode",
1669                                                             EV_TYPE_SIZING_MODE,
1670                                                             EV_SIZING_FIT_WIDTH,
1671                                                             G_PARAM_READWRITE));
1672
1673         binding_set = gtk_binding_set_by_class (class);
1674
1675         add_scroll_binding_keypad (binding_set, GDK_Left,  GTK_SCROLL_STEP_BACKWARD, TRUE);
1676         add_scroll_binding_keypad (binding_set, GDK_Right, GTK_SCROLL_STEP_FORWARD,  TRUE);
1677         add_scroll_binding_keypad (binding_set, GDK_Up,    GTK_SCROLL_STEP_BACKWARD, FALSE);
1678         add_scroll_binding_keypad (binding_set, GDK_Down,  GTK_SCROLL_STEP_FORWARD,  FALSE);
1679
1680         add_scroll_binding_keypad (binding_set, GDK_Page_Up,   GTK_SCROLL_PAGE_BACKWARD, FALSE);
1681         add_scroll_binding_keypad (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_FORWARD,  FALSE);
1682
1683         add_scroll_binding_shifted (binding_set, GDK_space, EV_SCROLL_PAGE_FORWARD, EV_SCROLL_PAGE_BACKWARD, FALSE);
1684         add_scroll_binding_shifted (binding_set, GDK_BackSpace, EV_SCROLL_PAGE_BACKWARD, EV_SCROLL_PAGE_FORWARD, FALSE);
1685 }
1686
1687 void
1688 ev_view_set_spacing (EvView     *view,
1689                      int         spacing)
1690 {
1691         g_return_if_fail (EV_IS_VIEW (view));
1692
1693         view->spacing = spacing;
1694
1695         if (view->document) {
1696                 gtk_widget_queue_resize (GTK_WIDGET (view));
1697         }
1698 }
1699
1700 static void
1701 ev_view_init (EvView *view)
1702 {
1703         GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
1704
1705         view->spacing = 0;
1706         view->scale = 1.0;
1707         view->current_page = 0;
1708         view->pressed_button = -1;
1709         view->cursor = EV_VIEW_CURSOR_NORMAL;
1710         view->show_border = TRUE;
1711
1712         view->continuous = TRUE;
1713         view->dual_page = FALSE;
1714         view->presentation = FALSE;
1715         view->full_screen = FALSE;
1716         view->sizing_mode = EV_SIZING_FIT_WIDTH;
1717 }
1718
1719 static void
1720 update_find_status_message (EvView *view)
1721 {
1722         char *message;
1723
1724 //      ev_document_doc_mutex_lock ();
1725         if (view->current_page == view->find_page) {
1726                 int results;
1727
1728 //              ev_document_doc_mutex_lock ();
1729                 results = ev_document_find_get_n_results
1730                                 (EV_DOCUMENT_FIND (view->document),
1731                                  view->current_page);
1732 //              ev_document_doc_mutex_unlock ();
1733                 /* TRANS: Sometimes this could be better translated as
1734                    "%d hit(s) on this page".  Therefore this string
1735                    contains plural cases. */
1736                 message = g_strdup_printf (ngettext ("%d found on this page",
1737                                                      "%d found on this page",
1738                                                      results),
1739                                            results);
1740         } else {
1741                 double percent;
1742
1743                 ev_document_doc_mutex_lock ();
1744                 percent = ev_document_find_get_progress
1745                                 (EV_DOCUMENT_FIND (view->document));
1746                 ev_document_doc_mutex_unlock ();
1747                 if (percent >= (1.0 - 1e-10)) {
1748                         message = g_strdup (_("Not found"));
1749                 } else {
1750                         message = g_strdup_printf (_("%3d%% remaining to search"),
1751                                                    (int) ((1.0 - percent) * 100));
1752                 }
1753
1754         }
1755 //      ev_document_doc_mutex_unlock ();
1756
1757         ev_view_set_find_status (view, message);
1758 //      g_free (message);
1759 }
1760
1761 #define MARGIN 5
1762
1763 static void
1764 ensure_rectangle_is_visible (EvView *view, GdkRectangle *rect)
1765 {
1766         GtkWidget *widget = GTK_WIDGET (view);
1767         GtkAdjustment *adjustment;
1768         int value;
1769
1770         adjustment = view->vadjustment;
1771
1772         if (rect->y < adjustment->value) {
1773                 value = MAX (adjustment->lower, rect->y - MARGIN);
1774                 gtk_adjustment_set_value (view->vadjustment, value);
1775         } else if (rect->y + rect->height >
1776                    adjustment->value + widget->allocation.height) {
1777                 value = MIN (adjustment->upper, rect->y + rect->height -
1778                              widget->allocation.height + MARGIN);
1779                 gtk_adjustment_set_value (view->vadjustment, value);
1780         }
1781
1782         adjustment = view->hadjustment;
1783
1784         if (rect->x < adjustment->value) {
1785                 value = MAX (adjustment->lower, rect->x - MARGIN);
1786                 gtk_adjustment_set_value (view->hadjustment, value);
1787         } else if (rect->x + rect->height >
1788                    adjustment->value + widget->allocation.width) {
1789                 value = MIN (adjustment->upper, rect->x + rect->width -
1790                              widget->allocation.width + MARGIN);
1791                 gtk_adjustment_set_value (view->hadjustment, value);
1792         }
1793 }
1794
1795 static void
1796 jump_to_find_result (EvView *view)
1797 {
1798         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
1799         EvRectangle rect;
1800         GdkRectangle view_rect;
1801         int n_results;
1802
1803         ev_document_doc_mutex_lock ();
1804         n_results = ev_document_find_get_n_results (find, view->current_page);
1805         ev_document_doc_mutex_unlock ();
1806
1807         if (n_results > view->find_result) {
1808                 ev_document_doc_mutex_lock ();
1809                 ev_document_find_get_result
1810                         (find, view->current_page, view->find_result, &rect);
1811                 ev_document_doc_mutex_unlock ();
1812
1813                 doc_rect_to_view_rect (view, &rect, &view_rect);
1814                 ensure_rectangle_is_visible (view, &view_rect);
1815         }
1816 }
1817
1818 static void
1819 jump_to_find_page (EvView *view)
1820 {
1821         int n_pages, i;
1822
1823         n_pages = ev_page_cache_get_n_pages (view->page_cache);
1824
1825         for (i = 0; i < n_pages; i++) {
1826                 int has_results;
1827                 int page;
1828
1829                 page = i + view->find_page;
1830                 if (page >= n_pages) {
1831                         page = page - n_pages;
1832                 }
1833
1834                 //              ev_document_doc_mutex_lock ();
1835                 has_results = ev_document_find_page_has_results
1836                                 (EV_DOCUMENT_FIND (view->document), page);
1837                 if (has_results == -1) {
1838                         view->find_page = page;
1839                         break;
1840                 } else if (has_results == 1) {
1841                         ev_page_cache_set_current_page (view->page_cache, page);
1842                         jump_to_find_result (view);
1843                         break;
1844                 }
1845         }
1846 }
1847
1848 static void
1849 find_changed_cb (EvDocument *document, int page, EvView *view)
1850 {
1851         jump_to_find_page (view);
1852         jump_to_find_result (view);
1853         update_find_status_message (view);
1854
1855         if (view->current_page == page)
1856                 gtk_widget_queue_draw (GTK_WIDGET (view));
1857 }
1858 /*** Public API ***/
1859
1860 GtkWidget*
1861 ev_view_new (void)
1862 {
1863         GtkWidget *view;
1864
1865         view = g_object_new (EV_TYPE_VIEW, NULL);
1866
1867         return view;
1868 }
1869
1870 static void
1871 job_finished_cb (EvPixbufCache *pixbuf_cache,
1872                  EvView        *view)
1873 {
1874         gtk_widget_queue_draw (GTK_WIDGET (view));
1875 }
1876
1877
1878 static void
1879 page_changed_cb (EvPageCache *page_cache,
1880                  int          new_page,
1881                  EvView      *view)
1882 {
1883         int old_page = view->current_page;
1884         int old_width, old_height;
1885         int new_width, new_height;
1886         int max_height, n_rows;
1887
1888         if (old_page == new_page)
1889                 return;
1890
1891         ev_page_cache_get_size (page_cache,
1892                                 old_page,
1893                                 view->scale,
1894                                 &old_width, &old_height);
1895
1896         view->current_page = new_page;
1897         view->has_selection = FALSE;
1898
1899         ev_page_cache_get_size (page_cache,
1900                                 new_page,
1901                                 view->scale,
1902                                 &new_width, &new_height);
1903
1904         compute_border (view, new_width, new_height, &(view->border));
1905
1906         if (new_width != old_width || new_height != old_height)
1907                 gtk_widget_queue_resize (GTK_WIDGET (view));
1908         else
1909                 gtk_widget_queue_draw (GTK_WIDGET (view));
1910         
1911         if (view->continuous) {
1912                 
1913                 n_rows = view->dual_page ? new_page / 2 : new_page;
1914                 
1915                 get_bounding_box_size (view, NULL, &max_height);
1916
1917                 gtk_adjustment_clamp_page(view->vadjustment,
1918                                           (max_height + view->spacing) * n_rows, 
1919                                           (max_height + view->spacing) * n_rows +
1920                                            view->vadjustment->page_size);
1921         } else {
1922                 gtk_adjustment_set_value (view->vadjustment,
1923                                           view->vadjustment->lower);
1924         }
1925
1926         if (EV_IS_DOCUMENT_FIND (view->document)) {
1927                 view->find_page = new_page;
1928                 view->find_result = 0;
1929                 update_find_status_message (view);
1930         }
1931
1932         view_update_range_and_current_page (view);
1933 }
1934
1935 void
1936 ev_view_set_document (EvView     *view,
1937                       EvDocument *document)
1938 {
1939         g_return_if_fail (EV_IS_VIEW (view));
1940
1941         if (document != view->document) {
1942                 if (view->document) {
1943                         g_signal_handlers_disconnect_by_func (view->document,
1944                                                               find_changed_cb,
1945                                                               view);
1946                         g_object_unref (view->document);
1947                         view->page_cache = NULL;
1948
1949                 }
1950
1951                 view->document = document;
1952                 view->find_page = 0;
1953                 view->find_result = 0;
1954
1955                 if (view->document) {
1956                         g_object_ref (view->document);
1957                         if (EV_IS_DOCUMENT_FIND (view->document)) {
1958                                 g_signal_connect (view->document,
1959                                                   "find_changed",
1960                                                   G_CALLBACK (find_changed_cb),
1961                                                   view);
1962                         }
1963                         view->page_cache = ev_document_get_page_cache (view->document);
1964                         g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
1965                         view->pixbuf_cache = ev_pixbuf_cache_new (view->document);
1966                         g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
1967                 }
1968
1969                 gtk_widget_queue_resize (GTK_WIDGET (view));
1970         }
1971 }
1972
1973 #define EPSILON 0.0000001
1974 static void
1975 ev_view_zoom (EvView   *view,
1976               double    factor,
1977               gboolean  relative)
1978 {
1979         double scale;
1980
1981         if (relative)
1982                 scale = view->scale * factor;
1983         else
1984                 scale = factor;
1985
1986         scale = CLAMP (scale, MIN_SCALE, MAX_SCALE);
1987
1988         if (ABS (view->scale - scale) < EPSILON)
1989                 return;
1990         view->scale = scale;
1991         gtk_widget_queue_resize (GTK_WIDGET (view));
1992 }
1993
1994
1995 void
1996 ev_view_set_continuous (EvView   *view,
1997                         gboolean  continuous)
1998 {
1999         g_return_if_fail (EV_IS_VIEW (view));
2000
2001         continuous = continuous != FALSE;
2002
2003         if (view->continuous != continuous) {
2004                 view->continuous = continuous;
2005                 gtk_widget_queue_resize (GTK_WIDGET (view));
2006         }
2007
2008         g_object_notify (G_OBJECT (view), "continuous");
2009 }
2010
2011 void
2012 ev_view_set_dual_page (EvView   *view,
2013                        gboolean  dual_page)
2014 {
2015         g_return_if_fail (EV_IS_VIEW (view));
2016
2017         dual_page = dual_page != FALSE;
2018
2019         if (view->dual_page == dual_page)
2020                 return;
2021
2022         view->dual_page = dual_page;
2023         /* FIXME: if we're keeping the pixbuf cache around, we should extend the
2024          * preload_cache_size to be 2 if dual_page is set.
2025          */
2026         gtk_widget_queue_resize (GTK_WIDGET (view));
2027
2028         g_object_notify (G_OBJECT (view), "dual-page");
2029 }
2030
2031 void
2032 ev_view_set_full_screen (EvView   *view,
2033                          gboolean  full_screen)
2034 {
2035         g_return_if_fail (EV_IS_VIEW (view));
2036
2037         full_screen = full_screen != FALSE;
2038
2039         if (view->full_screen != full_screen) {
2040                 view->full_screen = full_screen;
2041                 gtk_widget_queue_resize (GTK_WIDGET (view));
2042         }
2043
2044         g_object_notify (G_OBJECT (view), "full-screen");
2045 }
2046
2047 void
2048 ev_view_set_presentation (EvView   *view,
2049                           gboolean  presentation)
2050 {
2051         g_return_if_fail (EV_IS_VIEW (view));
2052
2053         presentation = presentation != FALSE;
2054
2055         if (view->presentation != presentation) {
2056                 view->presentation = presentation;
2057                 gtk_widget_queue_resize (GTK_WIDGET (view));
2058         }
2059
2060         g_object_notify (G_OBJECT (view), "presentation");
2061 }
2062
2063 void
2064 ev_view_set_sizing_mode (EvView       *view,
2065                          EvSizingMode  sizing_mode)
2066 {
2067         g_return_if_fail (EV_IS_VIEW (view));
2068
2069         if (view->sizing_mode != sizing_mode) {
2070                 view->sizing_mode = sizing_mode;
2071                 gtk_widget_queue_resize (GTK_WIDGET (view));
2072         }
2073
2074         g_object_notify (G_OBJECT (view), "sizing-mode");
2075 }
2076
2077
2078 gboolean
2079 ev_view_can_zoom_in (EvView *view)
2080 {
2081         return view->scale * ZOOM_IN_FACTOR <= MAX_SCALE;
2082 }
2083
2084 gboolean
2085 ev_view_can_zoom_out (EvView *view)
2086 {
2087         return view->scale * ZOOM_OUT_FACTOR >= MIN_SCALE;
2088 }
2089
2090 void
2091 ev_view_zoom_in (EvView *view)
2092 {
2093         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2094
2095         ev_view_zoom (view, ZOOM_IN_FACTOR, TRUE);
2096 }
2097
2098 void
2099 ev_view_zoom_out (EvView *view)
2100 {
2101         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2102
2103         ev_view_zoom (view, ZOOM_OUT_FACTOR, TRUE);
2104 }
2105
2106
2107
2108 static double
2109 zoom_for_size_fit_width (int doc_width,
2110                          int doc_height,
2111                          int target_width,
2112                          int target_height,
2113                          int vsb_width)
2114 {
2115         double scale;
2116
2117         scale = (double)target_width / doc_width;
2118
2119         if (doc_height * scale > target_height)
2120                 scale = (double) (target_width - vsb_width) / doc_width;
2121
2122         return scale;
2123 }
2124
2125 static double
2126 zoom_for_size_best_fit (int doc_width,
2127                         int doc_height,
2128                         int target_width,
2129                         int target_height,
2130                         int vsb_width,
2131                         int hsb_width)
2132 {
2133         double w_scale;
2134         double h_scale;
2135
2136         w_scale = (double)target_width / doc_width;
2137         h_scale = (double)target_height / doc_height;
2138
2139         if (doc_height * w_scale > target_height)
2140                 w_scale = (double) (target_width - vsb_width) / doc_width;
2141         if (doc_width * h_scale > target_width)
2142                 h_scale = (double) (target_height - hsb_width) / doc_height;
2143
2144         return MIN (w_scale, h_scale);
2145 }
2146
2147 static void
2148 ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
2149                            int     width,
2150                            int     height,
2151                            int     vsb_width,
2152                            int     hsb_height)
2153 {
2154         int doc_width, doc_height;
2155         GtkBorder border;
2156         gdouble scale;
2157
2158         ev_page_cache_get_max_width_size (view->page_cache,
2159                                           1.0,
2160                                           &doc_width, NULL);
2161         ev_page_cache_get_max_height_size (view->page_cache,
2162                                            1.0,
2163                                            NULL, &doc_height);
2164         compute_border (view, doc_width, doc_height, &border);
2165
2166         doc_width = doc_width * 2;
2167         width -= ((border.left + border.right)* 2 + 3 * view->spacing);
2168         height -= (border.top + border.bottom + 2 * view->spacing);
2169
2170         /* FIXME: We really need to calculat the overall height here, not the
2171          * page height.  We assume there's always a vertical scrollbar for
2172          * now.  We need to fix this. */
2173         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2174                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2175         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2176                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2177         else
2178                 g_assert_not_reached ();
2179
2180         ev_view_zoom (view, scale, FALSE);
2181 }
2182
2183 static void
2184 ev_view_zoom_for_size_continuous (EvView *view,
2185                                   int     width,
2186                                   int     height,
2187                                   int     vsb_width,
2188                                   int     hsb_height)
2189 {
2190         int doc_width, doc_height;
2191         GtkBorder border;
2192         gdouble scale;
2193
2194         ev_page_cache_get_max_width_size (view->page_cache,
2195                                           1.0,
2196                                           &doc_width, NULL);
2197         ev_page_cache_get_max_height_size (view->page_cache,
2198                                            1.0,
2199                                            NULL, &doc_height);
2200         compute_border (view, doc_width, doc_height, &border);
2201
2202         width -= (border.left + border.right + 2 * view->spacing);
2203         height -= (border.top + border.bottom + 2 * view->spacing);
2204
2205         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2206                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2207         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2208                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2209         else
2210                 g_assert_not_reached ();
2211
2212         ev_view_zoom (view, scale, FALSE);
2213 }
2214
2215 static void
2216 ev_view_zoom_for_size_dual_page (EvView *view,
2217                                  int     width,
2218                                  int     height,
2219                                  int     vsb_width,
2220                                  int     hsb_height)
2221 {
2222         GtkBorder border;
2223         gint doc_width, doc_height;
2224         gdouble scale;
2225         gint other_page;
2226
2227         other_page = view->current_page ^ 1;
2228
2229         /* Find the largest of the two. */
2230         ev_page_cache_get_size (view->page_cache,
2231                                 view->current_page,
2232                                 1.0,
2233                                 &doc_width, &doc_height);
2234
2235         if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
2236                 gint width_2, height_2;
2237                 ev_page_cache_get_size (view->page_cache,
2238                                         other_page,
2239                                         1.0,
2240                                         &width_2, &height_2);
2241                 if (width_2 > doc_width)
2242                         doc_width = width_2;
2243                 if (height_2 > doc_height)
2244                         doc_height = height_2;
2245         }
2246         compute_border (view, doc_width, doc_height, &border);
2247
2248         doc_width = doc_width * 2;
2249         width -= ((border.left + border.right)* 2 + 3 * view->spacing);
2250         height -= (border.top + border.bottom + 2 * view->spacing);
2251
2252         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2253                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2254         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2255                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2256         else
2257                 g_assert_not_reached ();
2258
2259         ev_view_zoom (view, scale, FALSE);
2260 }
2261
2262 static void
2263 ev_view_zoom_for_size_single_page (EvView *view,
2264                                    int     width,
2265                                    int     height,
2266                                    int     vsb_width,
2267                                    int     hsb_height)
2268 {
2269         int doc_width, doc_height;
2270         GtkBorder border;
2271         gdouble scale;
2272
2273         ev_page_cache_get_size (view->page_cache,
2274                                 view->current_page,
2275                                 1.0,
2276                                 &doc_width,
2277                                 &doc_height);
2278         /* Get an approximate border */
2279         compute_border (view, width, height, &border);
2280
2281         width -= (border.left + border.right + 2 * view->spacing);
2282         height -= (border.top + border.bottom + 2 * view->spacing);
2283
2284         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2285                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2286         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2287                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2288         else
2289                 g_assert_not_reached ();
2290
2291         ev_view_zoom (view, scale, FALSE);
2292 }
2293
2294
2295 void
2296 ev_view_zoom_normal (EvView *view)
2297 {
2298         ev_view_zoom (view, 1.0, FALSE);
2299 }
2300
2301 void
2302 ev_view_set_zoom_for_size (EvView *view,
2303                            int     width,
2304                            int     height,
2305                            int     vsb_width,
2306                            int     hsb_height)
2307 {
2308         g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
2309                           view->sizing_mode == EV_SIZING_BEST_FIT);
2310         g_return_if_fail (width >= 0);
2311         g_return_if_fail (height >= 0);
2312
2313         if (view->document == NULL)
2314                 return;
2315
2316         if (view->continuous && view->dual_page)
2317                 ev_view_zoom_for_size_continuous_and_dual_page (view, width, height, vsb_width, hsb_height);
2318         else if (view->continuous)
2319                 ev_view_zoom_for_size_continuous (view, width, height, vsb_width, hsb_height);
2320         else if (view->dual_page)
2321                 ev_view_zoom_for_size_dual_page (view, width, height, vsb_width, hsb_height);
2322         else
2323                 ev_view_zoom_for_size_single_page (view, width, height, vsb_width, hsb_height);
2324 }
2325
2326 const char *
2327 ev_view_get_status (EvView *view)
2328 {
2329         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2330
2331         return view->status;
2332 }
2333
2334 const char *
2335 ev_view_get_find_status (EvView *view)
2336 {
2337         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2338
2339         return view->find_status;
2340 }
2341
2342 gboolean
2343 ev_view_can_find_next (EvView *view)
2344 {
2345         int n_results = 0;
2346
2347         if (EV_IS_DOCUMENT_FIND (view->document)) {
2348                 EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2349
2350                 ev_document_doc_mutex_lock ();
2351                 n_results = ev_document_find_get_n_results (find, view->current_page);
2352                 ev_document_doc_mutex_unlock ();
2353         }
2354
2355         return n_results > 0;
2356 }
2357
2358 void
2359 ev_view_find_next (EvView *view)
2360 {
2361         EvPageCache *page_cache;
2362         int n_results, n_pages;
2363         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2364
2365         page_cache = ev_document_get_page_cache (view->document);
2366         ev_document_doc_mutex_lock ();
2367         n_results = ev_document_find_get_n_results (find, view->current_page);
2368         ev_document_doc_mutex_unlock ();
2369
2370         n_pages = ev_page_cache_get_n_pages (page_cache);
2371
2372         view->find_result++;
2373
2374         if (view->find_result >= n_results) {
2375                 view->find_result = 0;
2376                 view->find_page++;
2377
2378                 if (view->find_page >= n_pages) {
2379                         view->find_page = 0;
2380                 }
2381
2382                 jump_to_find_page (view);
2383         } else {
2384                 jump_to_find_result (view);
2385                 gtk_widget_queue_draw (GTK_WIDGET (view));
2386         }
2387 }
2388
2389 void
2390 ev_view_find_previous (EvView *view)
2391 {
2392         int n_results, n_pages;
2393         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2394         EvPageCache *page_cache;
2395
2396         page_cache = ev_document_get_page_cache (view->document);
2397
2398         ev_document_doc_mutex_lock ();
2399         n_results = ev_document_find_get_n_results (find, view->current_page);
2400         ev_document_doc_mutex_unlock ();
2401
2402         n_pages = ev_page_cache_get_n_pages (page_cache);
2403
2404         view->find_result--;
2405
2406         if (view->find_result < 0) {
2407                 view->find_result = 0;
2408                 view->find_page--;
2409
2410                 if (view->find_page < 0) {
2411                         view->find_page = n_pages - 1;
2412                 }
2413
2414                 jump_to_find_page (view);
2415         } else {
2416                 jump_to_find_result (view);
2417                 gtk_widget_queue_draw (GTK_WIDGET (view));
2418         }
2419 }
2420 void
2421 ev_view_hide_cursor (EvView *view)
2422 {
2423        ev_view_set_cursor (view, EV_VIEW_CURSOR_HIDDEN);
2424 }
2425
2426 void
2427 ev_view_show_cursor (EvView *view)
2428 {
2429        ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
2430 }
2431
2432
2433 GType
2434 ev_sizing_mode_get_type (void)
2435 {
2436   static GType etype = 0;
2437   if (etype == 0) {
2438     static const GEnumValue values[] = {
2439       { EV_SIZING_FIT_WIDTH, "EV_SIZING_FIT_WIDTH", "fit-width" },
2440       { EV_SIZING_BEST_FIT, "EV_SIZING_BEST_FIT", "best-fit" },
2441       { EV_SIZING_FREE, "EV_SIZING_FREE", "free" },
2442       { 0, NULL, NULL }
2443     };
2444     etype = g_enum_register_static ("EvSizingMode", values);
2445   }
2446   return etype;
2447 }