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