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