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