]> www.fi.muni.cz Git - evince.git/blob - shell/ev-page-view.c
4b019c4b651ab027a3dfdf18b547c2a872c3997a
[evince.git] / shell / ev-page-view.c
1 /*
2  *  Copyright (C) 2005 Jonathan Blandford
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2, or (at your option)
7  *  any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18
19 #include "config.h"
20
21 #include "ev-page-view.h"
22 #include "ev-marshal.h"
23 #include "ev-document-misc.h"
24 #include <gtk/gtk.h>
25
26 /* We keep a cached array of all the page sizes.  The info is accessed via
27  * page_sizes [page - 1], as pages start at 1 */
28 typedef struct _EvPageViewInfo
29 {
30         gint width;
31         gint height;
32 } EvPageViewInfo;
33
34 struct _EvPageViewPrivate
35 {
36         gint width, height;
37         gint page_spacing;
38
39         GdkWindow *bin_window;
40         EvDocument *document;
41         EvPageViewInfo *page_sizes;
42
43         GtkAdjustment *hadjustment;
44         GtkAdjustment *vadjustment;
45
46         gdouble scale;
47
48         /* Page information*/
49         gint n_pages;
50         gint max_page_width;
51
52         /* these two are only set if uniform_page_size is set */
53         gint uniform_page_width;
54         gint uniform_page_height;
55         guint uniform_page_size : 1;
56 };
57
58
59 static void     ev_page_view_init                   (EvPageView      *page_view);
60 static void     ev_page_view_class_init             (EvPageViewClass *klass);
61 static void     ev_page_view_set_scroll_adjustments (EvPageView      *page_view,
62                                                      GtkAdjustment   *hadjustment,
63                                                      GtkAdjustment   *vadjustment);
64 static void     ev_page_view_size_request           (GtkWidget       *widget,
65                                                      GtkRequisition  *requisition);
66 static void     ev_page_view_size_allocate          (GtkWidget       *widget,
67                                                      GtkAllocation   *allocation);
68 static gboolean ev_page_view_expose                 (GtkWidget       *widget,
69                                                      GdkEventExpose  *expose);
70 static void     ev_page_view_realize                (GtkWidget       *widget);
71 static void     ev_page_view_unrealize              (GtkWidget       *widget);
72 static void     ev_page_view_map                    (GtkWidget       *widget);
73 static void     ev_page_view_load                   (EvPageView      *page_view);
74 static void     ev_page_view_adjustment_changed     (GtkAdjustment   *adjustment,
75                                                      EvPageView      *page_view);
76 static void     ev_page_view_update_size            (EvPageView      *page_view);
77
78
79 G_DEFINE_TYPE (EvPageView, ev_page_view, GTK_TYPE_WIDGET)
80
81 #define EV_PAGE_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_PAGE_VIEW, EvPageViewPrivate))
82
83 static void
84 ev_page_view_init (EvPageView *page_view)
85 {
86         page_view->priv = EV_PAGE_VIEW_GET_PRIVATE (page_view);
87
88         page_view->priv->width = 1;
89         page_view->priv->height = 1;
90         page_view->priv->page_spacing = 10;
91         page_view->priv->scale = 1.0;
92
93         /* Make some stuff up */
94         page_view->priv->n_pages = 0;
95         page_view->priv->uniform_page_width = -1;
96         page_view->priv->uniform_page_height = -1;
97         page_view->priv->uniform_page_size = FALSE;
98 }
99
100 static void
101 ev_page_view_dispose (GObject *object)
102 {
103         EvPageView *view = EV_PAGE_VIEW (object);
104
105         if (view->priv->document) {
106                 g_object_unref (view->priv->document);
107                 view->priv->document = NULL;
108         }
109
110         G_OBJECT_CLASS (ev_page_view_parent_class)->dispose (object);
111 }
112
113 static void
114 ev_page_view_class_init (EvPageViewClass *klass)
115 {
116         GObjectClass *o_class;
117         GtkWidgetClass *widget_class;
118
119         o_class = (GObjectClass *) klass;
120         widget_class = (GtkWidgetClass *) klass;
121         klass->set_scroll_adjustments = ev_page_view_set_scroll_adjustments;
122
123         g_type_class_add_private (klass, sizeof (EvPageViewPrivate));
124         widget_class->size_request = ev_page_view_size_request;
125         widget_class->size_allocate = ev_page_view_size_allocate;
126         widget_class->expose_event = ev_page_view_expose;
127         widget_class->realize = ev_page_view_realize;
128         widget_class->unrealize = ev_page_view_unrealize;
129         widget_class->map = ev_page_view_map;
130
131         o_class->dispose = ev_page_view_dispose;
132
133         widget_class->set_scroll_adjustments_signal =
134                 g_signal_new ("set_scroll_adjustments",
135                               G_TYPE_FROM_CLASS (o_class),
136                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
137                               G_STRUCT_OFFSET (EvPageViewClass, set_scroll_adjustments),
138                               NULL, NULL,
139                               ev_marshal_VOID__OBJECT_OBJECT,
140                               G_TYPE_NONE, 2,
141                               GTK_TYPE_ADJUSTMENT,
142                               GTK_TYPE_ADJUSTMENT);
143
144
145 }
146
147
148 static void
149 ev_page_view_set_scroll_adjustments (EvPageView    *page_view,
150                                      GtkAdjustment *hadjustment,
151                                      GtkAdjustment *vadjustment)
152 {
153   gboolean need_adjust = FALSE;
154
155   if (hadjustment)
156     g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
157   else
158     hadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
159   if (vadjustment)
160     g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
161   else
162     vadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
163
164   if (page_view->priv->hadjustment && (page_view->priv->hadjustment != hadjustment))
165     {
166       g_signal_handlers_disconnect_matched (page_view->priv->hadjustment, G_SIGNAL_MATCH_DATA,
167                                            0, 0, NULL, NULL, page_view);
168       g_object_unref (page_view->priv->hadjustment);
169     }
170
171   if (page_view->priv->vadjustment && (page_view->priv->vadjustment != vadjustment))
172     {
173       g_signal_handlers_disconnect_matched (page_view->priv->vadjustment, G_SIGNAL_MATCH_DATA,
174                                             0, 0, NULL, NULL, page_view);
175       g_object_unref (page_view->priv->vadjustment);
176     }
177
178   if (page_view->priv->hadjustment != hadjustment)
179     {
180       page_view->priv->hadjustment = hadjustment;
181       g_object_ref (page_view->priv->hadjustment);
182       gtk_object_sink (GTK_OBJECT (page_view->priv->hadjustment));
183
184       g_signal_connect (page_view->priv->hadjustment, "value_changed",
185                         G_CALLBACK (ev_page_view_adjustment_changed),
186                         page_view);
187       need_adjust = TRUE;
188     }
189
190   if (page_view->priv->vadjustment != vadjustment)
191     {
192       page_view->priv->vadjustment = vadjustment;
193       g_object_ref (page_view->priv->vadjustment);
194       gtk_object_sink (GTK_OBJECT (page_view->priv->vadjustment));
195
196       g_signal_connect (page_view->priv->vadjustment, "value_changed",
197                         G_CALLBACK (ev_page_view_adjustment_changed),
198                         page_view);
199       need_adjust = TRUE;
200     }
201
202   if (need_adjust)
203           ev_page_view_adjustment_changed (NULL, page_view);
204 }
205
206 static void
207 ev_page_view_update_size (EvPageView *page_view)
208 {
209         gint left_border;
210         gint right_border;
211         gint top_border;
212         gint bottom_border;
213         gint width, height;
214
215         g_assert (page_view->priv->scale > 0.0);
216
217         if (page_view->priv->uniform_page_size) {
218                 width = (int) (page_view->priv->uniform_page_width *
219                                page_view->priv->scale);
220                 height = (int) (page_view->priv->uniform_page_height *
221                                 page_view->priv->scale);
222
223                 ev_document_misc_get_page_border_size (width, height,
224                                                        & left_border, & right_border,
225                                                        & top_border, & bottom_border);
226
227                 page_view->priv->width = width
228                         + page_view->priv->page_spacing * 2
229                         + left_border
230                         + right_border;
231                 page_view->priv->height =
232                         ((height
233                           + page_view->priv->page_spacing
234                           + top_border
235                           + bottom_border)
236                          * page_view->priv->n_pages) +
237                         page_view->priv->page_spacing;
238         } else {
239                 int i;
240
241                 page_view->priv->width = 0;
242                 page_view->priv->height = page_view->priv->page_spacing;
243
244                 for (i = 0; i < page_view->priv->n_pages; i++) {
245                         width = page_view->priv->page_sizes[i].width *
246                                 page_view->priv->scale;
247                         height = page_view->priv->page_sizes[i].height *
248                                 page_view->priv->scale;
249
250                         ev_document_misc_get_page_border_size (width, height,
251                                                                & left_border, & right_border,
252                                                                & top_border, & bottom_border);
253
254                         width = width
255                                 + page_view->priv->page_spacing * 2
256                                 + left_border
257                                 + right_border;
258                         height = height
259                                 + page_view->priv->page_spacing
260                                 + top_border
261                                 + bottom_border;
262
263                         page_view->priv->width = MAX (width, page_view->priv->width);
264                         page_view->priv->height += height;
265                 }
266         }
267
268 }
269
270 static void
271 ev_page_view_size_request (GtkWidget      *widget,
272                            GtkRequisition *requisition)
273 {
274         EvPageView *page_view;
275
276         page_view = EV_PAGE_VIEW (widget);
277
278         ev_page_view_update_size (page_view);
279
280         requisition->width = page_view->priv->width;
281         requisition->height = page_view->priv->height;
282 }
283
284 static void
285 ev_page_view_paint_one_page (EvPageView   *page_view,
286                              GdkRectangle *area,
287                              gint          left_border,
288                              gint          right_border,
289                              gint          top_border,
290                              gint          bottom_border)
291 {
292         GtkWidget *widget;
293
294         widget = GTK_WIDGET (page_view);
295
296                 g_print ("paint one page (%d,%d) %dx%d\n",
297                  area->x, area->y,
298                  area->width,
299                  area->height);
300         gdk_draw_rectangle (page_view->priv->bin_window,
301                             widget->style->black_gc,
302                             TRUE,
303                             area->x,
304                             area->y,
305                             area->width,
306                             area->height);
307         gdk_draw_rectangle (page_view->priv->bin_window,
308                             widget->style->white_gc,
309                             TRUE,
310                             area->x + left_border,
311                             area->y + top_border,
312                             area->width - (left_border + right_border),
313                             area->height - (top_border + bottom_border));
314         gdk_draw_rectangle (page_view->priv->bin_window,
315                             widget->style->mid_gc[widget->state],
316                             TRUE,
317                             area->x,
318                             area->y + area->height - (bottom_border - top_border),
319                             bottom_border - top_border,
320                             bottom_border - top_border);
321         gdk_draw_rectangle (page_view->priv->bin_window,
322                             widget->style->mid_gc[widget->state],
323                             TRUE,
324                             area->x + area->width - (right_border - left_border),
325                             area->y,
326                             right_border - left_border,
327                             right_border - left_border);
328 }
329
330 static void
331 ev_page_view_expose_uniform (GtkWidget      *widget,
332                              GdkEventExpose *expose)
333 {
334         EvPageView *page_view;
335         gint left_border;
336         gint right_border;
337         gint top_border;
338         gint bottom_border;
339         int x_offset = 0;
340         GdkRectangle rectangle;
341         gint width, height;
342         int i;
343
344         page_view = EV_PAGE_VIEW (widget);
345
346         width = (int) (page_view->priv->uniform_page_width *
347                        page_view->priv->scale);
348         height = (int) (page_view->priv->uniform_page_height *
349                         page_view->priv->scale);
350
351         if (widget->allocation.width > page_view->priv->width)
352                 x_offset = (widget->allocation.width - page_view->priv->width)/2;
353
354         ev_document_misc_get_page_border_size (width, height,
355                                                & left_border,
356                                                & right_border,
357                                                & top_border,
358                                                & bottom_border);
359
360         rectangle.x = page_view->priv->page_spacing + x_offset;
361         rectangle.y = page_view->priv->page_spacing;
362         rectangle.width = width
363                 + left_border
364                 + right_border;
365         rectangle.height = height
366                 + top_border
367                 + bottom_border;
368         for (i = 0; i < page_view->priv->n_pages; i++) {
369                 GdkRectangle unused;
370
371                 if (gdk_rectangle_intersect (&rectangle,
372                                              &expose->area,
373                                              &unused))
374                         ev_page_view_paint_one_page (page_view,
375                                                      & rectangle,
376                                                      left_border, right_border,
377                                                      top_border, bottom_border);
378                 rectangle.y += rectangle.height
379                         + page_view->priv->page_spacing;
380
381         }
382 }
383
384 static void
385 ev_page_view_expose_pages (GtkWidget      *widget,
386                            GdkEventExpose *expose)
387 {
388         EvPageView *page_view;
389         gint left_border;
390         gint right_border;
391         gint top_border;
392         gint bottom_border;
393         int x_offset = 0;
394         GdkRectangle rectangle;
395         gint width, height;
396         int i;
397
398         page_view = EV_PAGE_VIEW (widget);
399
400         width = (int) (page_view->priv->uniform_page_width *
401                        page_view->priv->scale);
402         height = (int) (page_view->priv->uniform_page_height *
403                         page_view->priv->scale);
404
405         if (widget->allocation.width > page_view->priv->width)
406                 x_offset = (widget->allocation.width - page_view->priv->width)/2;
407
408         ev_document_misc_get_page_border_size (width, height,
409                                                & left_border,
410                                                & right_border,
411                                                & top_border,
412                                                & bottom_border);
413
414         rectangle.x = page_view->priv->page_spacing + x_offset;
415         rectangle.y = page_view->priv->page_spacing;
416         rectangle.width = width
417                 + left_border
418                 + right_border;
419         rectangle.height = height
420                 + top_border
421                 + bottom_border;
422         for (i = 0; i < page_view->priv->n_pages; i++) {
423                 GdkRectangle unused;
424
425                 if (gdk_rectangle_intersect (&rectangle,
426                                              &expose->area,
427                                              &unused))
428                         ev_page_view_paint_one_page (page_view,
429                                                      & rectangle,
430                                                      left_border, right_border,
431                                                      top_border, bottom_border);
432                 rectangle.y += rectangle.height
433                         + page_view->priv->page_spacing;
434
435         }
436 }
437
438 static gboolean
439 ev_page_view_expose (GtkWidget      *widget,
440                      GdkEventExpose *expose)
441 {
442         EvPageView *page_view;
443
444         page_view = EV_PAGE_VIEW (widget);
445
446         if (expose->window != page_view->priv->bin_window)
447                 return FALSE;
448
449         if (page_view->priv->uniform_page_size) {
450                 ev_page_view_expose_uniform (widget, expose);
451         } else {
452                 ev_page_view_expose_pages (widget, expose);
453         }
454
455         return TRUE;
456 }
457
458 static void
459 ev_page_view_size_allocate (GtkWidget     *widget,
460                             GtkAllocation *allocation)
461 {
462   EvPageView *page_view;
463
464   widget->allocation = *allocation;
465
466   page_view = EV_PAGE_VIEW (widget);
467
468   if (GTK_WIDGET_REALIZED (widget))
469     {
470       gdk_window_move_resize (widget->window,
471                               allocation->x, allocation->y,
472                               allocation->width, allocation->height);
473       gdk_window_resize (page_view->priv->bin_window,
474                          MAX (page_view->priv->width, allocation->width),
475                          MAX (page_view->priv->height, allocation->height));
476     }
477
478   page_view->priv->hadjustment->page_size = allocation->width;
479   page_view->priv->hadjustment->page_increment = allocation->width * 0.9;
480   page_view->priv->hadjustment->step_increment = allocation->width * 0.1;
481   page_view->priv->hadjustment->lower = 0;
482   page_view->priv->hadjustment->upper = MAX (allocation->width, page_view->priv->width);
483   gtk_adjustment_changed (page_view->priv->hadjustment);
484
485   page_view->priv->vadjustment->page_size = allocation->height;
486   page_view->priv->vadjustment->page_increment = allocation->height * 0.9;
487   page_view->priv->vadjustment->step_increment = allocation->width * 0.1;
488   page_view->priv->vadjustment->lower = 0;
489   page_view->priv->vadjustment->upper = MAX (allocation->height, page_view->priv->height);
490   gtk_adjustment_changed (page_view->priv->vadjustment);
491 }
492
493 static void
494 ev_page_view_adjustment_changed (GtkAdjustment *adjustment,
495                                  EvPageView    *page_view)
496 {
497         if (GTK_WIDGET_REALIZED (page_view)) {
498                 gdk_window_move (page_view->priv->bin_window,
499                                  - page_view->priv->hadjustment->value,
500                                  - page_view->priv->vadjustment->value);
501
502                 gdk_window_process_updates (page_view->priv->bin_window, TRUE);
503         }
504 }
505
506 static void
507 ev_page_view_realize_document (EvPageView *page_view)
508 {
509         if (page_view->priv->document == NULL)
510                 return;
511
512         ev_document_set_target (page_view->priv->document,
513                                 page_view->priv->bin_window);
514         ev_page_view_load (page_view);
515         gtk_widget_queue_resize (GTK_WIDGET (page_view));
516 }
517
518
519 static void
520 ev_page_view_realize (GtkWidget *widget)
521 {
522         EvPageView *page_view;
523         GdkWindowAttr attributes;
524         gint attributes_mask;
525
526         g_return_if_fail (EV_IS_PAGE_VIEW (widget));
527
528         page_view = EV_PAGE_VIEW (widget);
529         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
530
531         /* Make the main, clipping window */
532         attributes.window_type = GDK_WINDOW_CHILD;
533         attributes.x = widget->allocation.x;
534         attributes.y = widget->allocation.y;
535         attributes.width = widget->allocation.width;
536         attributes.height = widget->allocation.height;
537         attributes.wclass = GDK_INPUT_OUTPUT;
538         attributes.visual = gtk_widget_get_visual (widget);
539         attributes.colormap = gtk_widget_get_colormap (widget);
540         attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
541
542         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
543
544         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
545                                          &attributes, attributes_mask);
546         gdk_window_set_user_data (widget->window, widget);
547
548         /* Make the window for the page view */
549         attributes.x = 0;
550         attributes.y = 0;
551         attributes.width = MAX (page_view->priv->width, widget->allocation.width);
552         attributes.height = MAX (page_view->priv->height, widget->allocation.height);
553         attributes.event_mask = (GDK_EXPOSURE_MASK |
554                                  GDK_SCROLL_MASK |
555                                  GDK_POINTER_MOTION_MASK |
556                                  GDK_BUTTON_PRESS_MASK |
557                                  GDK_BUTTON_RELEASE_MASK |
558                                  GDK_KEY_PRESS_MASK |
559                                  GDK_KEY_RELEASE_MASK) |
560                 gtk_widget_get_events (widget);
561
562         page_view->priv->bin_window = gdk_window_new (widget->window,
563                                                       &attributes, attributes_mask);
564         gdk_window_set_user_data (page_view->priv->bin_window, widget);
565
566         widget->style = gtk_style_attach (widget->style, widget->window);
567         gdk_window_set_background (page_view->priv->bin_window, &widget->style->mid[widget->state]);
568         gdk_window_set_background (widget->window, &widget->style->mid[widget->state]);
569
570         ev_page_view_realize_document (page_view);
571 }
572
573
574 static void
575 ev_page_view_unrealize (GtkWidget *widget)
576 {
577   EvPageView *page_view;
578
579   page_view = EV_PAGE_VIEW (widget);
580
581   gdk_window_set_user_data (page_view->priv->bin_window, NULL);
582   gdk_window_destroy (page_view->priv->bin_window);
583   page_view->priv->bin_window = NULL;
584
585   /* GtkWidget::unrealize destroys children and widget->window */
586   if (GTK_WIDGET_CLASS (ev_page_view_parent_class)->unrealize)
587     (* GTK_WIDGET_CLASS (ev_page_view_parent_class)->unrealize) (widget);
588 }
589
590 static void
591 ev_page_view_map (GtkWidget *widget)
592 {
593   EvPageView *page_view;
594
595   page_view = EV_PAGE_VIEW (widget);
596
597   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
598
599   gdk_window_show (page_view->priv->bin_window);
600   gdk_window_show (widget->window);
601 }
602
603 static void
604 ev_page_view_load (EvPageView *page_view)
605 {
606         int i;
607         gboolean uniform_page_size = TRUE;
608         int width = 0, height = 0;
609
610         page_view->priv->n_pages =
611                 ev_document_get_n_pages (page_view->priv->document);
612
613         for (i = 1; i <= page_view->priv->n_pages; i++) {
614                 EvPageViewInfo *info;
615                 gint page_width = 0;
616                 gint page_height = 0;
617
618                 ev_document_set_scale (page_view->priv->document, page_view->priv->scale);
619                 ev_document_get_page_size (page_view->priv->document,
620                                            i,
621                                            &page_width, &page_height);
622
623                 if (i == 1) {
624                         width = page_width;
625                         height = page_height;
626                 } else if (width != page_width || height != page_height) {
627                         /* It's a different page size.  Backfill the array. */
628                         int j;
629
630                         uniform_page_size = FALSE;
631
632                         page_view->priv->page_sizes =
633                                 g_new0 (EvPageViewInfo, page_view->priv->n_pages);
634
635                         for (j = 1; j < i; j++) {
636
637                                 info = &(page_view->priv->page_sizes[j - 1]);
638                                 info->width = width;
639                                 info->height = height;
640                         }
641                 }
642
643                 if (! uniform_page_size) {
644                         info = &(page_view->priv->page_sizes[i - 1]);
645
646                         info->width = page_width;
647                         info->height = page_height;
648                 }
649         }
650
651         page_view->priv->uniform_page_size = uniform_page_size;
652
653         if (uniform_page_size) {
654                 page_view->priv->uniform_page_width = width;
655                 page_view->priv->uniform_page_height = height;
656         }
657
658         ev_page_view_update_size (page_view);
659
660         gtk_widget_queue_resize (GTK_WIDGET (page_view));
661 }
662
663 /* Public functions */
664 GtkWidget *
665 ev_page_view_new (void)
666 {
667         return g_object_new (EV_TYPE_PAGE_VIEW, NULL);
668 }
669
670 void
671 ev_page_view_set_document (EvPageView *page_view,
672                            EvDocument *document)
673 {
674         g_return_if_fail (EV_IS_PAGE_VIEW (page_view));
675
676         if (document != page_view->priv->document) {
677                 if (page_view->priv->document) {
678                         g_object_unref (page_view->priv->document);
679                 }
680
681                 page_view->priv->document = document;
682
683                 if (page_view->priv->document) {
684                         g_object_ref (page_view->priv->document);
685                 }
686
687                 if (GTK_WIDGET_REALIZED (page_view)) {
688                         ev_page_view_realize_document (page_view);
689                 }
690         }
691
692 }
693