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