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