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