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