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