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