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