]> www.fi.muni.cz Git - evince.git/blob - shell/ev-view.c
851b5e4d42b08d8772f6974bdf999748370df2e6
[evince.git] / shell / 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 static void
1005 ev_view_primary_get_cb (GtkClipboard     *clipboard,
1006                         GtkSelectionData *selection_data,
1007                         guint             info,
1008                         gpointer          data)
1009 {
1010         EvView *ev_view = EV_VIEW (data);
1011         char *text;
1012
1013         if (!ev_document_can_get_text (ev_view->document)) {
1014                 return;
1015         }
1016
1017         ev_document_doc_mutex_lock ();
1018         text = ev_document_get_text (ev_view->document,
1019                                      ev_view->current_page,
1020                                      &ev_view->selection);
1021         ev_document_doc_mutex_unlock ();
1022         gtk_selection_data_set_text (selection_data, text, -1);
1023 }
1024
1025 static void
1026 ev_view_primary_clear_cb (GtkClipboard *clipboard,
1027                           gpointer      data)
1028 {
1029         EvView *ev_view = EV_VIEW (data);
1030
1031         ev_view->has_selection = FALSE;
1032 }
1033
1034 static void
1035 ev_view_update_primary_selection (EvView *ev_view)
1036 {
1037         GtkClipboard *clipboard;
1038
1039         clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
1040                                               GDK_SELECTION_PRIMARY);
1041
1042         if (ev_view->has_selection) {
1043                 if (!gtk_clipboard_set_with_owner (clipboard,
1044                                                    targets,
1045                                                    G_N_ELEMENTS (targets),
1046                                                    ev_view_primary_get_cb,
1047                                                    ev_view_primary_clear_cb,
1048                                                    G_OBJECT (ev_view)))
1049                         ev_view_primary_clear_cb (clipboard, ev_view);
1050         } else {
1051                 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (ev_view))
1052                         gtk_clipboard_clear (clipboard);
1053         }
1054 }
1055
1056 static gboolean
1057 ev_view_button_press_event (GtkWidget      *widget,
1058                             GdkEventButton *event)
1059 {
1060         EvView *view = EV_VIEW (widget);
1061
1062         if (!GTK_WIDGET_HAS_FOCUS (widget)) {
1063                 gtk_widget_grab_focus (widget);
1064         }
1065
1066         view->pressed_button = event->button;
1067
1068         switch (event->button) {
1069                 case 1:
1070                         if (view->has_selection) {
1071                                 view->has_selection = FALSE;
1072                                 gtk_widget_queue_draw (widget);
1073                         }
1074
1075                         view->selection_start.x = event->x;
1076                         view->selection_start.y = event->y;
1077                         break;
1078         }
1079
1080         return TRUE;
1081 }
1082
1083 static char *
1084 status_message_from_link (EvView *view, EvLink *link)
1085 {
1086         EvLinkType type;
1087         char *msg = NULL;
1088         char *page_label;
1089
1090         type = ev_link_get_link_type (link);
1091
1092         switch (type) {
1093                 case EV_LINK_TYPE_TITLE:
1094                         if (ev_link_get_title (link))
1095                                 msg = g_strdup (ev_link_get_title (link));
1096                         break;
1097                 case EV_LINK_TYPE_PAGE:
1098                         page_label = ev_page_cache_get_page_label (view->page_cache, ev_link_get_page (link));
1099                         msg = g_strdup_printf (_("Go to page %s"), page_label);
1100                         g_free (page_label);
1101                         break;
1102                 case EV_LINK_TYPE_EXTERNAL_URI:
1103                         msg = g_strdup (ev_link_get_uri (link));
1104                         break;
1105                 default:
1106                         break;
1107         }
1108
1109         return msg;
1110 }
1111
1112 static void
1113 ev_view_set_status (EvView *view, const char *message)
1114 {
1115         g_return_if_fail (EV_IS_VIEW (view));
1116
1117         if (message != view->status) {
1118                 g_free (view->status);
1119                 view->status = g_strdup (message);
1120                 g_object_notify (G_OBJECT (view), "status");
1121         }
1122 }
1123
1124 static void
1125 ev_view_set_find_status (EvView *view, const char *message)
1126 {
1127         g_return_if_fail (EV_IS_VIEW (view));
1128
1129         g_free (view->find_status);
1130         view->find_status = g_strdup (message);
1131         g_object_notify (G_OBJECT (view), "find-status");
1132 }
1133
1134 static GdkCursor *
1135 ev_view_create_invisible_cursor(void)
1136 {
1137        GdkBitmap *empty;
1138        GdkColor black = { 0, 0, 0, 0 };
1139        static char bits[] = { 0x00 };
1140
1141        empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1);
1142
1143        return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0);
1144 }
1145
1146 static void
1147 ev_view_set_cursor (EvView *view, EvViewCursor new_cursor)
1148 {
1149         GdkCursor *cursor = NULL;
1150         GdkDisplay *display;
1151         GtkWidget *widget;
1152
1153         if (view->cursor == new_cursor) {
1154                 return;
1155         }
1156
1157         widget = gtk_widget_get_toplevel (GTK_WIDGET (view));
1158         display = gtk_widget_get_display (widget);
1159         view->cursor = new_cursor;
1160
1161         switch (new_cursor) {
1162                 case EV_VIEW_CURSOR_NORMAL:
1163                         gdk_window_set_cursor (widget->window, NULL);
1164                         break;
1165                 case EV_VIEW_CURSOR_LINK:
1166                         cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
1167                         break;
1168                 case EV_VIEW_CURSOR_WAIT:
1169                         cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
1170                         break;
1171                 case EV_VIEW_CURSOR_HIDDEN:
1172                         cursor = ev_view_create_invisible_cursor ();
1173                         break;
1174
1175         }
1176
1177         if (cursor) {
1178                 gdk_window_set_cursor (widget->window, cursor);
1179                 gdk_cursor_unref (cursor);
1180                 gdk_flush();
1181         }
1182 }
1183
1184
1185 static void
1186 find_page_at_location (EvView  *view,
1187                        gdouble  x,
1188                        gdouble  y,
1189                        gint    *page,
1190                        gint    *x_offset,
1191                        gint    *y_offset)
1192 {
1193         int i;
1194
1195         if (view->document == NULL)
1196                 return;
1197
1198         g_assert (page);
1199         g_assert (x_offset);
1200         g_assert (y_offset);
1201
1202         for (i = view->start_page; i <= view->end_page; i++) {
1203                 GdkRectangle page_area;
1204                 GtkBorder border;
1205
1206                 if (! get_page_extents (view, i, &page_area, &border))
1207                         continue;
1208
1209                 if ((x >= page_area.x + border.left) &&
1210                     (x < page_area.x + page_area.width - border.right) &&
1211                     (y >= page_area.y + border.top) &&
1212                     (y < page_area.y + page_area.height - border.bottom)) {
1213                         *page = i;
1214                         *x_offset = x - (page_area.x + border.left);
1215                         *y_offset = y - (page_area.y + border.top);
1216                         return;
1217                 }
1218         }
1219
1220         *page = -1;
1221 }
1222
1223 static EvLink *
1224 get_link_at_location (EvView  *view,
1225                       gdouble  x,
1226                       gdouble  y)
1227 {
1228         gint page = -1;
1229         gint x_offset = 0, y_offset = 0;
1230         GList *link_mapping;
1231
1232         find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
1233
1234         if (page == -1)
1235                 return NULL;
1236
1237         link_mapping = ev_pixbuf_cache_get_link_mapping (view->pixbuf_cache, page);
1238
1239         if (link_mapping)
1240                 return ev_link_mapping_find (link_mapping, x_offset / view->scale, y_offset / view->scale);
1241         else
1242                 return NULL;
1243 }
1244
1245
1246 static gboolean
1247 ev_view_motion_notify_event (GtkWidget      *widget,
1248                              GdkEventMotion *event)
1249 {
1250         EvView *view = EV_VIEW (widget);
1251
1252         if (view->pressed_button > 0) {
1253                 GdkRectangle selection;
1254
1255                 view->has_selection = TRUE;
1256                 selection.x = MIN (view->selection_start.x, event->x);
1257                 selection.y = MIN (view->selection_start.y, event->y);
1258                 selection.width = ABS (view->selection_start.x - event->x) + 1;
1259                 selection.height = ABS (view->selection_start.y - event->y) + 1;
1260                 view_rect_to_doc_rect (view, &selection, &view->selection);
1261
1262                 gtk_widget_queue_draw (widget);
1263         } else if (view->document) {
1264                 EvLink *link;
1265
1266                 link = get_link_at_location (view, event->x, event->y);
1267                 if (link) {
1268                         char *msg;
1269
1270                         msg = status_message_from_link (view, link);
1271                         ev_view_set_status (view, msg);
1272                         ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
1273                         g_free (msg);
1274                 } else {
1275                         ev_view_set_status (view, NULL);
1276                         if (view->cursor == EV_VIEW_CURSOR_LINK) {
1277                                 ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
1278                         }
1279                 }
1280         }
1281
1282         return TRUE;
1283 }
1284
1285 /* FIXME: standardize this sometime */
1286 static void
1287 go_to_link (EvView *view, EvLink *link)
1288 {
1289         EvLinkType type;
1290         const char *uri;
1291         int page;
1292
1293         type = ev_link_get_link_type (link);
1294
1295         switch (type) {
1296                 case EV_LINK_TYPE_TITLE:
1297                         break;
1298                 case EV_LINK_TYPE_PAGE:
1299                         page = ev_link_get_page (link);
1300                         ev_page_cache_set_current_page (view->page_cache, page);
1301                         break;
1302                 case EV_LINK_TYPE_EXTERNAL_URI:
1303                         uri = ev_link_get_uri (link);
1304                         gnome_vfs_url_show (uri);
1305                         break;
1306         }
1307 }
1308
1309
1310 static gboolean
1311 ev_view_button_release_event (GtkWidget      *widget,
1312                               GdkEventButton *event)
1313 {
1314         EvView *view = EV_VIEW (widget);
1315
1316         view->pressed_button = -1;
1317
1318         if (view->has_selection) {
1319                 ev_view_update_primary_selection (view);
1320         } else if (view->document) {
1321                 EvLink *link;
1322
1323                 link = get_link_at_location (view, event->x, event->y);
1324                 if (link) {
1325                         go_to_link (view, link);
1326                 }
1327         }
1328
1329         return FALSE;
1330 }
1331
1332 static void
1333 on_adjustment_value_changed (GtkAdjustment  *adjustment,
1334                              EvView *view)
1335 {
1336         view_update_adjustments (view);
1337 }
1338
1339 static void
1340 set_scroll_adjustment (EvView *view,
1341                        GtkOrientation  orientation,
1342                        GtkAdjustment  *adjustment)
1343 {
1344         GtkAdjustment **to_set;
1345
1346         if (orientation == GTK_ORIENTATION_HORIZONTAL)
1347                 to_set = &view->hadjustment;
1348         else
1349                 to_set = &view->vadjustment;
1350
1351         if (*to_set != adjustment) {
1352                 if (*to_set) {
1353                         g_signal_handlers_disconnect_by_func (*to_set,
1354                                                               (gpointer) on_adjustment_value_changed,
1355                                                               view);
1356                         g_object_unref (*to_set);
1357                 }
1358
1359                 *to_set = adjustment;
1360                 view_set_adjustment_values (view, orientation);
1361
1362                 if (*to_set) {
1363                         g_object_ref (*to_set);
1364                         g_signal_connect (*to_set, "value_changed",
1365                                           G_CALLBACK (on_adjustment_value_changed), view);
1366                 }
1367         }
1368 }
1369
1370 static void
1371 ev_view_set_scroll_adjustments (EvView *view,
1372                                 GtkAdjustment  *hadjustment,
1373                                 GtkAdjustment  *vadjustment)
1374 {
1375         set_scroll_adjustment (view, GTK_ORIENTATION_HORIZONTAL, hadjustment);
1376         set_scroll_adjustment (view, GTK_ORIENTATION_VERTICAL, vadjustment);
1377
1378         view_update_adjustments (view);
1379 }
1380
1381 static void
1382 add_scroll_binding_keypad (GtkBindingSet  *binding_set,
1383                            guint           keyval,
1384                            GtkScrollType   scroll,
1385                            gboolean        horizontal)
1386 {
1387   guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
1388
1389   gtk_binding_entry_add_signal (binding_set, keyval, 0,
1390                                 "scroll_view", 2,
1391                                 GTK_TYPE_SCROLL_TYPE, scroll,
1392                                 G_TYPE_BOOLEAN, horizontal);
1393   gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
1394                                 "scroll_view", 2,
1395                                 GTK_TYPE_SCROLL_TYPE, scroll,
1396                                 G_TYPE_BOOLEAN, horizontal);
1397 }
1398
1399 static void
1400 add_scroll_binding_shifted (GtkBindingSet  *binding_set,
1401                             guint           keyval,
1402                             GtkScrollType   scroll_normal,
1403                             GtkScrollType   scroll_shifted,
1404                             gboolean        horizontal)
1405 {
1406   gtk_binding_entry_add_signal (binding_set, keyval, 0,
1407                                 "scroll_view", 2,
1408                                 GTK_TYPE_SCROLL_TYPE, scroll_normal,
1409                                 G_TYPE_BOOLEAN, horizontal);
1410   gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
1411                                 "scroll_view", 2,
1412                                 GTK_TYPE_SCROLL_TYPE, scroll_shifted,
1413                                 G_TYPE_BOOLEAN, horizontal);
1414 }
1415
1416 static void
1417 ev_view_jump (EvView        *view,
1418               GtkScrollType  scroll)
1419 {
1420         GtkAdjustment *adjustment;
1421         double value, increment;
1422         gboolean first_page = FALSE;
1423         gboolean last_page = FALSE;
1424
1425         /* Assign values for increment and vertical adjustment */
1426         adjustment = view->vadjustment;
1427         increment = adjustment->page_size * 0.75;
1428         value = adjustment->value;
1429
1430         /* Assign boolean for first and last page */
1431         if (view->current_page == 0)
1432                 first_page = TRUE;
1433         if (view->current_page == ev_page_cache_get_n_pages (view->page_cache) - 1)
1434                 last_page = TRUE;
1435
1436         switch (scroll) {
1437                 case EV_SCROLL_PAGE_BACKWARD:
1438                         /* Do not jump backwards if at the first page */
1439                         if (value == (adjustment->lower) && first_page) {
1440                                 /* Do nothing */
1441                                 /* At the top of a page, assign the upper bound limit of previous page */
1442                         } else if (value == (adjustment->lower)) {
1443                                 value = adjustment->upper - adjustment->page_size;
1444                                 ev_page_cache_set_current_page (view->page_cache, view->current_page - 1);
1445                                 /* Jump to the top */
1446                         } else {
1447                                 value = MAX (value - increment, adjustment->lower);
1448                         }
1449                         break;
1450                 case EV_SCROLL_PAGE_FORWARD:
1451                         /* Do not jump forward if at the last page */
1452                         if (value == (adjustment->upper - adjustment->page_size) && last_page) {
1453                                 /* Do nothing */
1454                         /* At the bottom of a page, assign the lower bound limit of next page */
1455                         } else if (value == (adjustment->upper - adjustment->page_size)) {
1456                                 value = 0;
1457                                 ev_page_cache_set_current_page (view->page_cache, view->current_page + 1);
1458                         /* Jump to the bottom */
1459                         } else {
1460                                 value = MIN (value + increment, adjustment->upper - adjustment->page_size);
1461                         }
1462                         break;
1463                 default:
1464                         break;
1465         }
1466
1467         gtk_adjustment_set_value (adjustment, value);
1468 }
1469
1470 static void
1471 ev_view_scroll_view (EvView *view,
1472                      GtkScrollType scroll,
1473                      gboolean horizontal)
1474 {
1475         if (scroll == GTK_SCROLL_PAGE_BACKWARD) {
1476                 ev_page_cache_prev_page (view->page_cache);
1477         } else if (scroll == GTK_SCROLL_PAGE_FORWARD) {
1478                 ev_page_cache_next_page (view->page_cache);
1479         } else if (scroll == EV_SCROLL_PAGE_BACKWARD || scroll == EV_SCROLL_PAGE_FORWARD) {
1480                 ev_view_jump (view, scroll);
1481         } else {
1482                 GtkAdjustment *adjustment;
1483                 double value;
1484
1485                 if (horizontal) {
1486                         adjustment = view->hadjustment;
1487                 } else {
1488                         adjustment = view->vadjustment;
1489                 }
1490
1491                 value = adjustment->value;
1492
1493                 switch (scroll) {
1494                         case GTK_SCROLL_STEP_BACKWARD:
1495                                 value -= adjustment->step_increment;
1496                                 break;
1497                         case GTK_SCROLL_STEP_FORWARD:
1498                                 value += adjustment->step_increment;
1499                                 break;
1500                         default:
1501                                 break;
1502                 }
1503
1504                 value = CLAMP (value, adjustment->lower,
1505                                adjustment->upper - adjustment->page_size);
1506
1507                 gtk_adjustment_set_value (adjustment, value);
1508         }
1509 }
1510
1511 static void
1512 ev_view_set_property (GObject      *object,
1513                       guint         prop_id,
1514                       const GValue *value,
1515                       GParamSpec   *pspec)
1516 {
1517         EvView *view = EV_VIEW (object);
1518
1519         switch (prop_id)
1520         {
1521         case PROP_CONTINUOUS:
1522                 ev_view_set_continuous (view, g_value_get_boolean (value));
1523                 break;
1524         case PROP_DUAL_PAGE:
1525                 ev_view_set_dual_page (view, g_value_get_boolean (value));
1526                 break;
1527         case PROP_FULL_SCREEN:
1528                 ev_view_set_full_screen (view, g_value_get_boolean (value));
1529                 break;
1530         case PROP_PRESENTATION:
1531                 ev_view_set_presentation (view, g_value_get_boolean (value));
1532                 break;
1533         case PROP_SIZING_MODE:
1534                 ev_view_set_sizing_mode (view, g_value_get_enum (value));
1535                 break;
1536         default:
1537                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1538         }
1539 }
1540
1541 static void
1542 ev_view_get_property (GObject *object,
1543                       guint prop_id,
1544                       GValue *value,
1545                       GParamSpec *pspec)
1546 {
1547         EvView *view = EV_VIEW (object);
1548
1549         switch (prop_id)
1550         {
1551         case PROP_STATUS:
1552                 g_value_set_string (value, view->status);
1553                 break;
1554         case PROP_FIND_STATUS:
1555                 g_value_set_string (value, view->status);
1556                 break;
1557         case PROP_CONTINUOUS:
1558                 g_value_set_boolean (value, view->continuous);
1559                 break;
1560         case PROP_DUAL_PAGE:
1561                 g_value_set_boolean (value, view->dual_page);
1562                 break;
1563         case PROP_FULL_SCREEN:
1564                 g_value_set_boolean (value, view->full_screen);
1565                 break;
1566         case PROP_PRESENTATION:
1567                 g_value_set_boolean (value, view->presentation);
1568                 break;
1569         case PROP_SIZING_MODE:
1570                 g_value_set_enum (value, view->sizing_mode);
1571                 break;
1572         default:
1573                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1574         }
1575 }
1576
1577 static void
1578 ev_view_class_init (EvViewClass *class)
1579 {
1580         GObjectClass *object_class = G_OBJECT_CLASS (class);
1581         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
1582         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1583         GtkBindingSet *binding_set;
1584
1585         object_class->finalize = ev_view_finalize;
1586         object_class->set_property = ev_view_set_property;
1587         object_class->get_property = ev_view_get_property;
1588
1589         widget_class->expose_event = ev_view_expose_event;
1590         widget_class->button_press_event = ev_view_button_press_event;
1591         widget_class->motion_notify_event = ev_view_motion_notify_event;
1592         widget_class->button_release_event = ev_view_button_release_event;
1593         widget_class->size_request = ev_view_size_request;
1594         widget_class->size_allocate = ev_view_size_allocate;
1595         widget_class->realize = ev_view_realize;
1596         widget_class->unrealize = ev_view_unrealize;
1597         gtk_object_class->destroy = ev_view_destroy;
1598
1599         class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
1600         class->scroll_view = ev_view_scroll_view;
1601
1602         widget_class->set_scroll_adjustments_signal =  g_signal_new ("set-scroll-adjustments",
1603                                                                      G_OBJECT_CLASS_TYPE (object_class),
1604                                                                      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1605                                                                      G_STRUCT_OFFSET (EvViewClass, set_scroll_adjustments),
1606                                                                      NULL, NULL,
1607                                                                      ev_marshal_VOID__OBJECT_OBJECT,
1608                                                                      G_TYPE_NONE, 2,
1609                                                                      GTK_TYPE_ADJUSTMENT,
1610                                                                      GTK_TYPE_ADJUSTMENT);
1611
1612         g_signal_new ("scroll_view",
1613                       G_TYPE_FROM_CLASS (object_class),
1614                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1615                       G_STRUCT_OFFSET (EvViewClass, scroll_view),
1616                       NULL, NULL,
1617                       ev_marshal_VOID__ENUM_BOOLEAN,
1618                       G_TYPE_NONE, 2,
1619                       GTK_TYPE_SCROLL_TYPE,
1620                       G_TYPE_BOOLEAN);
1621
1622         g_object_class_install_property (object_class,
1623                                          PROP_STATUS,
1624                                          g_param_spec_string ("status",
1625                                                               "Status Message",
1626                                                               "The status message",
1627                                                               NULL,
1628                                                               G_PARAM_READABLE));
1629
1630         g_object_class_install_property (object_class,
1631                                          PROP_FIND_STATUS,
1632                                          g_param_spec_string ("find-status",
1633                                                               "Find Status Message",
1634                                                               "The find status message",
1635                                                               NULL,
1636                                                               G_PARAM_READABLE));
1637
1638         g_object_class_install_property (object_class,
1639                                          PROP_CONTINUOUS,
1640                                          g_param_spec_boolean ("continuous",
1641                                                                "Continuous",
1642                                                                "Continuous scrolling mode",
1643                                                                TRUE,
1644                                                                G_PARAM_READWRITE));
1645
1646         g_object_class_install_property (object_class,
1647                                          PROP_DUAL_PAGE,
1648                                          g_param_spec_boolean ("dual-page",
1649                                                                "Dual Page",
1650                                                                "Two pages visible at once",
1651                                                                FALSE,
1652                                                                G_PARAM_READWRITE));
1653         g_object_class_install_property (object_class,
1654                                          PROP_FULL_SCREEN,
1655                                          g_param_spec_boolean ("full-screen",
1656                                                                "Full Screen",
1657                                                                "Draw page in a full-screen fashion",
1658                                                                FALSE,
1659                                                                G_PARAM_READWRITE));
1660         g_object_class_install_property (object_class,
1661                                          PROP_PRESENTATION,
1662                                          g_param_spec_boolean ("presentation",
1663                                                                "Presentation",
1664                                                                "Draw page in presentation mode",
1665                                                                TRUE,
1666                                                                G_PARAM_READWRITE));
1667
1668         g_object_class_install_property (object_class,
1669                                          PROP_SIZING_MODE,
1670                                          g_param_spec_enum ("sizing-mode",
1671                                                             "Sizing Mode",
1672                                                             "Sizing Mode",
1673                                                             EV_TYPE_SIZING_MODE,
1674                                                             EV_SIZING_FIT_WIDTH,
1675                                                             G_PARAM_READWRITE));
1676
1677         binding_set = gtk_binding_set_by_class (class);
1678
1679         add_scroll_binding_keypad (binding_set, GDK_Left,  GTK_SCROLL_STEP_BACKWARD, TRUE);
1680         add_scroll_binding_keypad (binding_set, GDK_Right, GTK_SCROLL_STEP_FORWARD,  TRUE);
1681         add_scroll_binding_keypad (binding_set, GDK_Up,    GTK_SCROLL_STEP_BACKWARD, FALSE);
1682         add_scroll_binding_keypad (binding_set, GDK_Down,  GTK_SCROLL_STEP_FORWARD,  FALSE);
1683
1684         add_scroll_binding_keypad (binding_set, GDK_Page_Up,   GTK_SCROLL_PAGE_BACKWARD, FALSE);
1685         add_scroll_binding_keypad (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_FORWARD,  FALSE);
1686
1687         add_scroll_binding_shifted (binding_set, GDK_space, EV_SCROLL_PAGE_FORWARD, EV_SCROLL_PAGE_BACKWARD, FALSE);
1688         add_scroll_binding_shifted (binding_set, GDK_BackSpace, EV_SCROLL_PAGE_BACKWARD, EV_SCROLL_PAGE_FORWARD, FALSE);
1689 }
1690
1691 static void
1692 ev_view_init (EvView *view)
1693 {
1694         GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
1695
1696         view->spacing = 10;
1697         view->scale = 1.0;
1698         view->current_page = 0;
1699         view->pressed_button = -1;
1700         view->cursor = EV_VIEW_CURSOR_NORMAL;
1701         view->show_border = TRUE;
1702
1703         view->continuous = TRUE;
1704         view->dual_page = FALSE;
1705         view->presentation = FALSE;
1706         view->full_screen = FALSE;
1707         view->sizing_mode = EV_SIZING_FIT_WIDTH;
1708 }
1709
1710 static void
1711 update_find_status_message (EvView *view)
1712 {
1713         char *message;
1714
1715 //      ev_document_doc_mutex_lock ();
1716         if (view->current_page == view->find_page) {
1717                 int results;
1718
1719 //              ev_document_doc_mutex_lock ();
1720                 results = ev_document_find_get_n_results
1721                                 (EV_DOCUMENT_FIND (view->document),
1722                                  view->current_page);
1723 //              ev_document_doc_mutex_unlock ();
1724                 /* TRANS: Sometimes this could be better translated as
1725                    "%d hit(s) on this page".  Therefore this string
1726                    contains plural cases. */
1727                 message = g_strdup_printf (ngettext ("%d found on this page",
1728                                                      "%d found on this page",
1729                                                      results),
1730                                            results);
1731         } else {
1732                 double percent;
1733
1734                 ev_document_doc_mutex_lock ();
1735                 percent = ev_document_find_get_progress
1736                                 (EV_DOCUMENT_FIND (view->document));
1737                 ev_document_doc_mutex_unlock ();
1738                 if (percent >= (1.0 - 1e-10)) {
1739                         message = g_strdup (_("Not found"));
1740                 } else {
1741                         message = g_strdup_printf (_("%3d%% remaining to search"),
1742                                                    (int) ((1.0 - percent) * 100));
1743                 }
1744
1745         }
1746 //      ev_document_doc_mutex_unlock ();
1747
1748         ev_view_set_find_status (view, message);
1749 //      g_free (message);
1750 }
1751
1752 #define MARGIN 5
1753
1754 static void
1755 ensure_rectangle_is_visible (EvView *view, GdkRectangle *rect)
1756 {
1757         GtkWidget *widget = GTK_WIDGET (view);
1758         GtkAdjustment *adjustment;
1759         int value;
1760
1761         adjustment = view->vadjustment;
1762
1763         if (rect->y < adjustment->value) {
1764                 value = MAX (adjustment->lower, rect->y - MARGIN);
1765                 gtk_adjustment_set_value (view->vadjustment, value);
1766         } else if (rect->y + rect->height >
1767                    adjustment->value + widget->allocation.height) {
1768                 value = MIN (adjustment->upper, rect->y + rect->height -
1769                              widget->allocation.height + MARGIN);
1770                 gtk_adjustment_set_value (view->vadjustment, value);
1771         }
1772
1773         adjustment = view->hadjustment;
1774
1775         if (rect->x < adjustment->value) {
1776                 value = MAX (adjustment->lower, rect->x - MARGIN);
1777                 gtk_adjustment_set_value (view->hadjustment, value);
1778         } else if (rect->x + rect->height >
1779                    adjustment->value + widget->allocation.width) {
1780                 value = MIN (adjustment->upper, rect->x + rect->width -
1781                              widget->allocation.width + MARGIN);
1782                 gtk_adjustment_set_value (view->hadjustment, value);
1783         }
1784 }
1785
1786 static void
1787 jump_to_find_result (EvView *view)
1788 {
1789         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
1790         EvRectangle rect;
1791         GdkRectangle view_rect;
1792         int n_results;
1793
1794         ev_document_doc_mutex_lock ();
1795         n_results = ev_document_find_get_n_results (find, view->current_page);
1796         ev_document_doc_mutex_unlock ();
1797
1798         if (n_results > view->find_result) {
1799                 ev_document_doc_mutex_lock ();
1800                 ev_document_find_get_result
1801                         (find, view->current_page, view->find_result, &rect);
1802                 ev_document_doc_mutex_unlock ();
1803
1804                 doc_rect_to_view_rect (view, &rect, &view_rect);
1805                 ensure_rectangle_is_visible (view, &view_rect);
1806         }
1807 }
1808
1809 static void
1810 jump_to_find_page (EvView *view)
1811 {
1812         int n_pages, i;
1813
1814         n_pages = ev_page_cache_get_n_pages (view->page_cache);
1815
1816         for (i = 0; i < n_pages; i++) {
1817                 int has_results;
1818                 int page;
1819
1820                 page = i + view->find_page;
1821                 if (page >= n_pages) {
1822                         page = page - n_pages;
1823                 }
1824
1825                 //              ev_document_doc_mutex_lock ();
1826                 has_results = ev_document_find_page_has_results
1827                                 (EV_DOCUMENT_FIND (view->document), page);
1828                 if (has_results == -1) {
1829                         view->find_page = page;
1830                         break;
1831                 } else if (has_results == 1) {
1832                         ev_page_cache_set_current_page (view->page_cache, page);
1833                         jump_to_find_result (view);
1834                         break;
1835                 }
1836         }
1837 }
1838
1839 static void
1840 find_changed_cb (EvDocument *document, int page, EvView *view)
1841 {
1842         jump_to_find_page (view);
1843         jump_to_find_result (view);
1844         update_find_status_message (view);
1845
1846         if (view->current_page == page)
1847                 gtk_widget_queue_draw (GTK_WIDGET (view));
1848 }
1849 /*** Public API ***/
1850
1851 GtkWidget*
1852 ev_view_new (void)
1853 {
1854         GtkWidget *view;
1855
1856         view = g_object_new (EV_TYPE_VIEW, NULL);
1857
1858         return view;
1859 }
1860
1861 static void
1862 job_finished_cb (EvPixbufCache *pixbuf_cache,
1863                  EvView        *view)
1864 {
1865         gtk_widget_queue_draw (GTK_WIDGET (view));
1866 }
1867
1868
1869 static void
1870 page_changed_cb (EvPageCache *page_cache,
1871                  int          new_page,
1872                  EvView      *view)
1873 {
1874         int old_page = view->current_page;
1875         int old_width, old_height;
1876         int new_width, new_height;
1877         int max_height, n_rows;
1878
1879         if (old_page == new_page)
1880                 return;
1881
1882         ev_page_cache_get_size (page_cache,
1883                                 old_page,
1884                                 view->scale,
1885                                 &old_width, &old_height);
1886
1887         view->current_page = new_page;
1888         view->has_selection = FALSE;
1889
1890         ev_page_cache_get_size (page_cache,
1891                                 new_page,
1892                                 view->scale,
1893                                 &new_width, &new_height);
1894
1895         compute_border (view, new_width, new_height, &(view->border));
1896
1897         if (new_width != old_width || new_height != old_height)
1898                 gtk_widget_queue_resize (GTK_WIDGET (view));
1899         else
1900                 gtk_widget_queue_draw (GTK_WIDGET (view));
1901         
1902         if (view->continuous) {
1903                 
1904                 n_rows = view->dual_page ? new_page / 2 : new_page;
1905                 
1906                 get_bounding_box_size (view, NULL, &max_height);
1907
1908                 gtk_adjustment_clamp_page(view->vadjustment,
1909                                           (max_height + view->spacing) * n_rows, 
1910                                           (max_height + view->spacing) * n_rows +
1911                                            view->vadjustment->page_size);
1912         } else {
1913                 gtk_adjustment_set_value (view->vadjustment,
1914                                           view->vadjustment->lower);
1915         }
1916
1917         if (EV_IS_DOCUMENT_FIND (view->document)) {
1918                 view->find_page = new_page;
1919                 view->find_result = 0;
1920                 update_find_status_message (view);
1921         }
1922
1923         view_update_range_and_current_page (view);
1924 }
1925
1926 void
1927 ev_view_set_document (EvView     *view,
1928                       EvDocument *document)
1929 {
1930         g_return_if_fail (EV_IS_VIEW (view));
1931
1932         if (document != view->document) {
1933                 if (view->document) {
1934                         g_signal_handlers_disconnect_by_func (view->document,
1935                                                               find_changed_cb,
1936                                                               view);
1937                         g_object_unref (view->document);
1938                         view->page_cache = NULL;
1939
1940                 }
1941
1942                 view->document = document;
1943                 view->find_page = 0;
1944                 view->find_result = 0;
1945
1946                 if (view->document) {
1947                         g_object_ref (view->document);
1948                         if (EV_IS_DOCUMENT_FIND (view->document)) {
1949                                 g_signal_connect (view->document,
1950                                                   "find_changed",
1951                                                   G_CALLBACK (find_changed_cb),
1952                                                   view);
1953                         }
1954                         view->page_cache = ev_document_get_page_cache (view->document);
1955                         g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
1956                         view->pixbuf_cache = ev_pixbuf_cache_new (view->document);
1957                         g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
1958                 }
1959
1960                 gtk_widget_queue_resize (GTK_WIDGET (view));
1961         }
1962 }
1963
1964 #define EPSILON 0.0000001
1965 static void
1966 ev_view_zoom (EvView   *view,
1967               double    factor,
1968               gboolean  relative)
1969 {
1970         double scale;
1971
1972         if (relative)
1973                 scale = view->scale * factor;
1974         else
1975                 scale = factor;
1976
1977         scale = CLAMP (scale, MIN_SCALE, MAX_SCALE);
1978
1979         if (ABS (view->scale - scale) < EPSILON)
1980                 return;
1981         view->scale = scale;
1982         gtk_widget_queue_resize (GTK_WIDGET (view));
1983 }
1984
1985
1986 void
1987 ev_view_set_continuous (EvView   *view,
1988                         gboolean  continuous)
1989 {
1990         g_return_if_fail (EV_IS_VIEW (view));
1991
1992         continuous = continuous != FALSE;
1993
1994         if (view->continuous != continuous) {
1995                 view->continuous = continuous;
1996                 gtk_widget_queue_resize (GTK_WIDGET (view));
1997         }
1998
1999         g_object_notify (G_OBJECT (view), "continuous");
2000 }
2001
2002 void
2003 ev_view_set_dual_page (EvView   *view,
2004                        gboolean  dual_page)
2005 {
2006         g_return_if_fail (EV_IS_VIEW (view));
2007
2008         dual_page = dual_page != FALSE;
2009
2010         if (view->dual_page == dual_page)
2011                 return;
2012
2013         view->dual_page = dual_page;
2014         /* FIXME: if we're keeping the pixbuf cache around, we should extend the
2015          * preload_cache_size to be 2 if dual_page is set.
2016          */
2017         gtk_widget_queue_resize (GTK_WIDGET (view));
2018
2019         g_object_notify (G_OBJECT (view), "dual-page");
2020 }
2021
2022 void
2023 ev_view_set_full_screen (EvView   *view,
2024                          gboolean  full_screen)
2025 {
2026         g_return_if_fail (EV_IS_VIEW (view));
2027
2028         full_screen = full_screen != FALSE;
2029
2030         if (view->full_screen != full_screen) {
2031                 view->full_screen = full_screen;
2032                 gtk_widget_queue_resize (GTK_WIDGET (view));
2033         }
2034
2035         g_object_notify (G_OBJECT (view), "full-screen");
2036 }
2037
2038 void
2039 ev_view_set_presentation (EvView   *view,
2040                           gboolean  presentation)
2041 {
2042         g_return_if_fail (EV_IS_VIEW (view));
2043
2044         presentation = presentation != FALSE;
2045
2046         if (view->presentation != presentation) {
2047                 view->presentation = presentation;
2048                 gtk_widget_queue_resize (GTK_WIDGET (view));
2049         }
2050
2051         g_object_notify (G_OBJECT (view), "presentation");
2052 }
2053
2054 void
2055 ev_view_set_sizing_mode (EvView       *view,
2056                          EvSizingMode  sizing_mode)
2057 {
2058         g_return_if_fail (EV_IS_VIEW (view));
2059
2060         if (view->sizing_mode != sizing_mode) {
2061                 view->sizing_mode = sizing_mode;
2062                 gtk_widget_queue_resize (GTK_WIDGET (view));
2063         }
2064
2065         g_object_notify (G_OBJECT (view), "sizing-mode");
2066 }
2067
2068
2069 gboolean
2070 ev_view_can_zoom_in (EvView *view)
2071 {
2072         return view->scale * ZOOM_IN_FACTOR <= MAX_SCALE;
2073 }
2074
2075 gboolean
2076 ev_view_can_zoom_out (EvView *view)
2077 {
2078         return view->scale * ZOOM_OUT_FACTOR >= MIN_SCALE;
2079 }
2080
2081 void
2082 ev_view_zoom_in (EvView *view)
2083 {
2084         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2085
2086         ev_view_zoom (view, ZOOM_IN_FACTOR, TRUE);
2087 }
2088
2089 void
2090 ev_view_zoom_out (EvView *view)
2091 {
2092         g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
2093
2094         ev_view_zoom (view, ZOOM_OUT_FACTOR, TRUE);
2095 }
2096
2097
2098
2099 static double
2100 zoom_for_size_fit_width (int doc_width,
2101                          int doc_height,
2102                          int target_width,
2103                          int target_height,
2104                          int vsb_width)
2105 {
2106         double scale;
2107
2108         scale = (double)target_width / doc_width;
2109
2110         if (doc_height * scale > target_height)
2111                 scale = (double) (target_width - vsb_width) / doc_width;
2112
2113         return scale;
2114 }
2115
2116 static double
2117 zoom_for_size_best_fit (int doc_width,
2118                         int doc_height,
2119                         int target_width,
2120                         int target_height,
2121                         int vsb_width,
2122                         int hsb_width)
2123 {
2124         double w_scale;
2125         double h_scale;
2126
2127         w_scale = (double)target_width / doc_width;
2128         h_scale = (double)target_height / doc_height;
2129
2130         if (doc_height * w_scale > target_height)
2131                 w_scale = (double) (target_width - vsb_width) / doc_width;
2132         if (doc_width * h_scale > target_width)
2133                 h_scale = (double) (target_height - hsb_width) / doc_height;
2134
2135         return MIN (w_scale, h_scale);
2136 }
2137
2138 static void
2139 ev_view_zoom_for_size_continuous_and_dual_page (EvView *view,
2140                            int     width,
2141                            int     height,
2142                            int     vsb_width,
2143                            int     hsb_height)
2144 {
2145         int doc_width, doc_height;
2146         GtkBorder border;
2147         gdouble scale;
2148
2149         ev_page_cache_get_max_width_size (view->page_cache,
2150                                           1.0,
2151                                           &doc_width, NULL);
2152         ev_page_cache_get_max_height_size (view->page_cache,
2153                                            1.0,
2154                                            NULL, &doc_height);
2155         compute_border (view, doc_width, doc_height, &border);
2156
2157         doc_width = doc_width * 2;
2158         width -= (2 * (border.left + border.right) + 3 * view->spacing);
2159         height -= (border.top + border.bottom + 2 * view->spacing);
2160
2161         /* FIXME: We really need to calculate the overall height here, not the
2162          * page height.  We assume there's always a vertical scrollbar for
2163          * now.  We need to fix this. */
2164         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2165                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2166         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2167                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2168         else
2169                 g_assert_not_reached ();
2170
2171         ev_view_zoom (view, scale, FALSE);
2172 }
2173
2174 static void
2175 ev_view_zoom_for_size_continuous (EvView *view,
2176                                   int     width,
2177                                   int     height,
2178                                   int     vsb_width,
2179                                   int     hsb_height)
2180 {
2181         int doc_width, doc_height;
2182         GtkBorder border;
2183         gdouble scale;
2184
2185         ev_page_cache_get_max_width_size (view->page_cache,
2186                                           1.0,
2187                                           &doc_width, NULL);
2188         ev_page_cache_get_max_height_size (view->page_cache,
2189                                            1.0,
2190                                            NULL, &doc_height);
2191         compute_border (view, doc_width, doc_height, &border);
2192
2193         width -= (border.left + border.right + 2 * view->spacing);
2194         height -= (border.top + border.bottom + 2 * view->spacing);
2195
2196         /* FIXME: We really need to calculate the overall height here, not the
2197          * page height.  We assume there's always a vertical scrollbar for
2198          * now.  We need to fix this. */
2199         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2200                 scale = zoom_for_size_fit_width (doc_width, doc_height, width - vsb_width, height, 0);
2201         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2202                 scale = zoom_for_size_best_fit (doc_width, doc_height, width - vsb_width, height, 0, hsb_height);
2203         else
2204                 g_assert_not_reached ();
2205
2206         ev_view_zoom (view, scale, FALSE);
2207 }
2208
2209 static void
2210 ev_view_zoom_for_size_dual_page (EvView *view,
2211                                  int     width,
2212                                  int     height,
2213                                  int     vsb_width,
2214                                  int     hsb_height)
2215 {
2216         GtkBorder border;
2217         gint doc_width, doc_height;
2218         gdouble scale;
2219         gint other_page;
2220
2221         other_page = view->current_page ^ 1;
2222
2223         /* Find the largest of the two. */
2224         ev_page_cache_get_size (view->page_cache,
2225                                 view->current_page,
2226                                 1.0,
2227                                 &doc_width, &doc_height);
2228
2229         if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
2230                 gint width_2, height_2;
2231                 ev_page_cache_get_size (view->page_cache,
2232                                         other_page,
2233                                         1.0,
2234                                         &width_2, &height_2);
2235                 if (width_2 > doc_width)
2236                         doc_width = width_2;
2237                 if (height_2 > doc_height)
2238                         doc_height = height_2;
2239         }
2240         compute_border (view, doc_width, doc_height, &border);
2241
2242         doc_width = doc_width * 2;
2243         width -= ((border.left + border.right)* 2 + 3 * view->spacing);
2244         height -= (border.top + border.bottom + 2 * view->spacing);
2245
2246         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2247                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2248         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2249                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2250         else
2251                 g_assert_not_reached ();
2252
2253         ev_view_zoom (view, scale, FALSE);
2254 }
2255
2256 static void
2257 ev_view_zoom_for_size_single_page (EvView *view,
2258                                    int     width,
2259                                    int     height,
2260                                    int     vsb_width,
2261                                    int     hsb_height)
2262 {
2263         int doc_width, doc_height;
2264         GtkBorder border;
2265         gdouble scale;
2266
2267         ev_page_cache_get_size (view->page_cache,
2268                                 view->current_page,
2269                                 1.0,
2270                                 &doc_width,
2271                                 &doc_height);
2272         /* Get an approximate border */
2273         compute_border (view, width, height, &border);
2274
2275         width -= (border.left + border.right + 2 * view->spacing);
2276         height -= (border.top + border.bottom + 2 * view->spacing);
2277
2278         if (view->sizing_mode == EV_SIZING_FIT_WIDTH)
2279                 scale = zoom_for_size_fit_width (doc_width, doc_height, width, height, vsb_width);
2280         else if (view->sizing_mode == EV_SIZING_BEST_FIT)
2281                 scale = zoom_for_size_best_fit (doc_width, doc_height, width, height, vsb_width, hsb_height);
2282         else
2283                 g_assert_not_reached ();
2284
2285         ev_view_zoom (view, scale, FALSE);
2286 }
2287
2288
2289 void
2290 ev_view_zoom_normal (EvView *view)
2291 {
2292         ev_view_zoom (view, 1.0, FALSE);
2293 }
2294
2295 void
2296 ev_view_set_zoom_for_size (EvView *view,
2297                            int     width,
2298                            int     height,
2299                            int     vsb_width,
2300                            int     hsb_height)
2301 {
2302         g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
2303                           view->sizing_mode == EV_SIZING_BEST_FIT);
2304         g_return_if_fail (width >= 0);
2305         g_return_if_fail (height >= 0);
2306
2307         if (view->document == NULL)
2308                 return;
2309
2310         if (view->continuous && view->dual_page)
2311                 ev_view_zoom_for_size_continuous_and_dual_page (view, width, height, vsb_width, hsb_height);
2312         else if (view->continuous)
2313                 ev_view_zoom_for_size_continuous (view, width, height, vsb_width, hsb_height);
2314         else if (view->dual_page)
2315                 ev_view_zoom_for_size_dual_page (view, width, height, vsb_width, hsb_height);
2316         else
2317                 ev_view_zoom_for_size_single_page (view, width, height, vsb_width, hsb_height);
2318 }
2319
2320 const char *
2321 ev_view_get_status (EvView *view)
2322 {
2323         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2324
2325         return view->status;
2326 }
2327
2328 const char *
2329 ev_view_get_find_status (EvView *view)
2330 {
2331         g_return_val_if_fail (EV_IS_VIEW (view), NULL);
2332
2333         return view->find_status;
2334 }
2335
2336 gboolean
2337 ev_view_can_find_next (EvView *view)
2338 {
2339         int n_results = 0;
2340
2341         if (EV_IS_DOCUMENT_FIND (view->document)) {
2342                 EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2343
2344                 ev_document_doc_mutex_lock ();
2345                 n_results = ev_document_find_get_n_results (find, view->current_page);
2346                 ev_document_doc_mutex_unlock ();
2347         }
2348
2349         return n_results > 0;
2350 }
2351
2352 void
2353 ev_view_find_next (EvView *view)
2354 {
2355         EvPageCache *page_cache;
2356         int n_results, n_pages;
2357         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2358
2359         page_cache = ev_document_get_page_cache (view->document);
2360         ev_document_doc_mutex_lock ();
2361         n_results = ev_document_find_get_n_results (find, view->current_page);
2362         ev_document_doc_mutex_unlock ();
2363
2364         n_pages = ev_page_cache_get_n_pages (page_cache);
2365
2366         view->find_result++;
2367
2368         if (view->find_result >= n_results) {
2369                 view->find_result = 0;
2370                 view->find_page++;
2371
2372                 if (view->find_page >= n_pages) {
2373                         view->find_page = 0;
2374                 }
2375
2376                 jump_to_find_page (view);
2377         } else {
2378                 jump_to_find_result (view);
2379                 gtk_widget_queue_draw (GTK_WIDGET (view));
2380         }
2381 }
2382
2383 void
2384 ev_view_find_previous (EvView *view)
2385 {
2386         int n_results, n_pages;
2387         EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
2388         EvPageCache *page_cache;
2389
2390         page_cache = ev_document_get_page_cache (view->document);
2391
2392         ev_document_doc_mutex_lock ();
2393         n_results = ev_document_find_get_n_results (find, view->current_page);
2394         ev_document_doc_mutex_unlock ();
2395
2396         n_pages = ev_page_cache_get_n_pages (page_cache);
2397
2398         view->find_result--;
2399
2400         if (view->find_result < 0) {
2401                 view->find_result = 0;
2402                 view->find_page--;
2403
2404                 if (view->find_page < 0) {
2405                         view->find_page = n_pages - 1;
2406                 }
2407
2408                 jump_to_find_page (view);
2409         } else {
2410                 jump_to_find_result (view);
2411                 gtk_widget_queue_draw (GTK_WIDGET (view));
2412         }
2413 }
2414 void
2415 ev_view_hide_cursor (EvView *view)
2416 {
2417        ev_view_set_cursor (view, EV_VIEW_CURSOR_HIDDEN);
2418 }
2419
2420 void
2421 ev_view_show_cursor (EvView *view)
2422 {
2423        ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
2424 }
2425
2426
2427 GType
2428 ev_sizing_mode_get_type (void)
2429 {
2430   static GType etype = 0;
2431   if (etype == 0) {
2432     static const GEnumValue values[] = {
2433       { EV_SIZING_FIT_WIDTH, "EV_SIZING_FIT_WIDTH", "fit-width" },
2434       { EV_SIZING_BEST_FIT, "EV_SIZING_BEST_FIT", "best-fit" },
2435       { EV_SIZING_FREE, "EV_SIZING_FREE", "free" },
2436       { 0, NULL, NULL }
2437     };
2438     etype = g_enum_register_static ("EvSizingMode", values);
2439   }
2440   return etype;
2441 }