]> www.fi.muni.cz Git - evince.git/blob - shell/ev-page-view.c
New file with some random thoughts.
[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_class_init (EvPageViewClass *klass)
102 {
103         GObjectClass *o_class;
104         GtkWidgetClass *widget_class;
105
106         o_class = (GObjectClass *) klass;
107         widget_class = (GtkWidgetClass *) klass;
108         klass->set_scroll_adjustments = ev_page_view_set_scroll_adjustments;
109
110         g_type_class_add_private (klass, sizeof (EvPageViewPrivate));
111         widget_class->size_request = ev_page_view_size_request;
112         widget_class->size_allocate = ev_page_view_size_allocate;
113         widget_class->expose_event = ev_page_view_expose;
114         widget_class->realize = ev_page_view_realize;
115         widget_class->unrealize = ev_page_view_unrealize;
116         widget_class->map = ev_page_view_map;
117
118         widget_class->set_scroll_adjustments_signal =
119                 g_signal_new ("set_scroll_adjustments",
120                               G_TYPE_FROM_CLASS (o_class),
121                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
122                               G_STRUCT_OFFSET (EvPageViewClass, set_scroll_adjustments),
123                               NULL, NULL,
124                               ev_marshal_VOID__OBJECT_OBJECT,
125                               G_TYPE_NONE, 2,
126                               GTK_TYPE_ADJUSTMENT,
127                               GTK_TYPE_ADJUSTMENT);
128
129
130 }
131
132
133 static void
134 ev_page_view_set_scroll_adjustments (EvPageView    *page_view,
135                                      GtkAdjustment *hadjustment,
136                                      GtkAdjustment *vadjustment)
137 {
138   gboolean need_adjust = FALSE;
139
140   if (hadjustment)
141     g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
142   else
143     hadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
144   if (vadjustment)
145     g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
146   else
147     vadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
148
149   if (page_view->priv->hadjustment && (page_view->priv->hadjustment != hadjustment))
150     {
151       g_signal_handlers_disconnect_matched (page_view->priv->hadjustment, G_SIGNAL_MATCH_DATA,
152                                            0, 0, NULL, NULL, page_view);
153       g_object_unref (page_view->priv->hadjustment);
154     }
155
156   if (page_view->priv->vadjustment && (page_view->priv->vadjustment != vadjustment))
157     {
158       g_signal_handlers_disconnect_matched (page_view->priv->vadjustment, G_SIGNAL_MATCH_DATA,
159                                             0, 0, NULL, NULL, page_view);
160       g_object_unref (page_view->priv->vadjustment);
161     }
162
163   if (page_view->priv->hadjustment != hadjustment)
164     {
165       page_view->priv->hadjustment = hadjustment;
166       g_object_ref (page_view->priv->hadjustment);
167       gtk_object_sink (GTK_OBJECT (page_view->priv->hadjustment));
168
169       g_signal_connect (page_view->priv->hadjustment, "value_changed",
170                         G_CALLBACK (ev_page_view_adjustment_changed),
171                         page_view);
172       need_adjust = TRUE;
173     }
174
175   if (page_view->priv->vadjustment != vadjustment)
176     {
177       page_view->priv->vadjustment = vadjustment;
178       g_object_ref (page_view->priv->vadjustment);
179       gtk_object_sink (GTK_OBJECT (page_view->priv->vadjustment));
180
181       g_signal_connect (page_view->priv->vadjustment, "value_changed",
182                         G_CALLBACK (ev_page_view_adjustment_changed),
183                         page_view);
184       need_adjust = TRUE;
185     }
186
187   if (need_adjust)
188           ev_page_view_adjustment_changed (NULL, page_view);
189 }
190
191 static void
192 ev_page_view_update_size (EvPageView *page_view)
193 {
194         gint left_border;
195         gint right_border;
196         gint top_border;
197         gint bottom_border;
198         gint width, height;
199
200         g_assert (page_view->priv->scale > 0.0);
201
202         if (page_view->priv->uniform_page_size) {
203                 width = (int) (page_view->priv->uniform_page_width *
204                                page_view->priv->scale);
205                 height = (int) (page_view->priv->uniform_page_height *
206                                 page_view->priv->scale);
207
208                 ev_document_misc_get_page_border_size (width, height,
209                                                        & left_border, & right_border,
210                                                        & top_border, & bottom_border);
211
212                 page_view->priv->width = width
213                         + page_view->priv->page_spacing * 2
214                         + left_border
215                         + right_border;
216                 page_view->priv->height =
217                         ((height
218                           + page_view->priv->page_spacing
219                           + top_border
220                           + bottom_border)
221                          * page_view->priv->n_pages) +
222                         page_view->priv->page_spacing;
223         } else {
224                 int i;
225
226                 page_view->priv->width = 0;
227                 page_view->priv->height = page_view->priv->page_spacing;
228
229                 for (i = 0; i < page_view->priv->n_pages; i++) {
230                         width = page_view->priv->page_sizes[i].width *
231                                 page_view->priv->scale;
232                         height = page_view->priv->page_sizes[i].height *
233                                 page_view->priv->scale;
234
235                         ev_document_misc_get_page_border_size (width, height,
236                                                                & left_border, & right_border,
237                                                                & top_border, & bottom_border);
238
239                         width = width
240                                 + page_view->priv->page_spacing * 2
241                                 + left_border
242                                 + right_border;
243                         height = height
244                                 + page_view->priv->page_spacing
245                                 + top_border
246                                 + bottom_border;
247
248                         page_view->priv->width = MAX (width, page_view->priv->width);
249                         page_view->priv->height += height;
250                 }
251         }
252
253 }
254
255 static void
256 ev_page_view_size_request (GtkWidget      *widget,
257                            GtkRequisition *requisition)
258 {
259         EvPageView *page_view;
260
261         page_view = EV_PAGE_VIEW (widget);
262
263         ev_page_view_update_size (page_view);
264
265         requisition->width = page_view->priv->width;
266         requisition->height = page_view->priv->height;
267 }
268
269 static void
270 ev_page_view_paint_one_page (EvPageView   *page_view,
271                              GdkRectangle *area,
272                              gint          left_border,
273                              gint          right_border,
274                              gint          top_border,
275                              gint          bottom_border)
276 {
277         GtkWidget *widget;
278
279         widget = GTK_WIDGET (page_view);
280
281                 g_print ("paint one page (%d,%d) %dx%d\n",
282                  area->x, area->y,
283                  area->width,
284                  area->height);
285         gdk_draw_rectangle (page_view->priv->bin_window,
286                             widget->style->black_gc,
287                             TRUE,
288                             area->x,
289                             area->y,
290                             area->width,
291                             area->height);
292         gdk_draw_rectangle (page_view->priv->bin_window,
293                             widget->style->white_gc,
294                             TRUE,
295                             area->x + left_border,
296                             area->y + top_border,
297                             area->width - (left_border + right_border),
298                             area->height - (top_border + bottom_border));
299         gdk_draw_rectangle (page_view->priv->bin_window,
300                             widget->style->mid_gc[widget->state],
301                             TRUE,
302                             area->x,
303                             area->y + area->height - (bottom_border - top_border),
304                             bottom_border - top_border,
305                             bottom_border - top_border);
306         gdk_draw_rectangle (page_view->priv->bin_window,
307                             widget->style->mid_gc[widget->state],
308                             TRUE,
309                             area->x + area->width - (right_border - left_border),
310                             area->y,
311                             right_border - left_border,
312                             right_border - left_border);
313 }
314
315 static void
316 ev_page_view_expose_uniform (GtkWidget      *widget,
317                              GdkEventExpose *expose)
318 {
319         EvPageView *page_view;
320         gint left_border;
321         gint right_border;
322         gint top_border;
323         gint bottom_border;
324         int x_offset = 0;
325         GdkRectangle rectangle;
326         gint width, height;
327         int i;
328
329         page_view = EV_PAGE_VIEW (widget);
330
331         width = (int) (page_view->priv->uniform_page_width *
332                        page_view->priv->scale);
333         height = (int) (page_view->priv->uniform_page_height *
334                         page_view->priv->scale);
335
336         if (widget->allocation.width > page_view->priv->width)
337                 x_offset = (widget->allocation.width - page_view->priv->width)/2;
338
339         ev_document_misc_get_page_border_size (width, height,
340                                                & left_border,
341                                                & right_border,
342                                                & top_border,
343                                                & bottom_border);
344
345         rectangle.x = page_view->priv->page_spacing + x_offset;
346         rectangle.y = page_view->priv->page_spacing;
347         rectangle.width = width
348                 + left_border
349                 + right_border;
350         rectangle.height = height
351                 + top_border
352                 + bottom_border;
353         for (i = 0; i < page_view->priv->n_pages; i++) {
354                 GdkRectangle unused;
355
356                 if (gdk_rectangle_intersect (&rectangle,
357                                              &expose->area,
358                                              &unused))
359                         ev_page_view_paint_one_page (page_view,
360                                                      & rectangle,
361                                                      left_border, right_border,
362                                                      top_border, bottom_border);
363                 rectangle.y += rectangle.height
364                         + page_view->priv->page_spacing;
365
366         }
367 }
368
369 static void
370 ev_page_view_expose_pages (GtkWidget      *widget,
371                            GdkEventExpose *expose)
372 {
373         EvPageView *page_view;
374         gint left_border;
375         gint right_border;
376         gint top_border;
377         gint bottom_border;
378         int x_offset = 0;
379         GdkRectangle rectangle;
380         gint width, height;
381         int i;
382
383         page_view = EV_PAGE_VIEW (widget);
384
385         width = (int) (page_view->priv->uniform_page_width *
386                        page_view->priv->scale);
387         height = (int) (page_view->priv->uniform_page_height *
388                         page_view->priv->scale);
389
390         if (widget->allocation.width > page_view->priv->width)
391                 x_offset = (widget->allocation.width - page_view->priv->width)/2;
392
393         ev_document_misc_get_page_border_size (width, height,
394                                                & left_border,
395                                                & right_border,
396                                                & top_border,
397                                                & bottom_border);
398
399         rectangle.x = page_view->priv->page_spacing + x_offset;
400         rectangle.y = page_view->priv->page_spacing;
401         rectangle.width = width
402                 + left_border
403                 + right_border;
404         rectangle.height = height
405                 + top_border
406                 + bottom_border;
407         for (i = 0; i < page_view->priv->n_pages; i++) {
408                 GdkRectangle unused;
409
410                 if (gdk_rectangle_intersect (&rectangle,
411                                              &expose->area,
412                                              &unused))
413                         ev_page_view_paint_one_page (page_view,
414                                                      & rectangle,
415                                                      left_border, right_border,
416                                                      top_border, bottom_border);
417                 rectangle.y += rectangle.height
418                         + page_view->priv->page_spacing;
419
420         }
421 }
422
423 static gboolean
424 ev_page_view_expose (GtkWidget      *widget,
425                      GdkEventExpose *expose)
426 {
427         EvPageView *page_view;
428
429         page_view = EV_PAGE_VIEW (widget);
430
431         if (expose->window != page_view->priv->bin_window)
432                 return FALSE;
433
434         if (page_view->priv->uniform_page_size) {
435                 ev_page_view_expose_uniform (widget, expose);
436         } else {
437                 ev_page_view_expose_pages (widget, expose);
438         }
439
440         return TRUE;
441 }
442
443 static void
444 ev_page_view_size_allocate (GtkWidget     *widget,
445                             GtkAllocation *allocation)
446 {
447   EvPageView *page_view;
448
449   widget->allocation = *allocation;
450
451   page_view = EV_PAGE_VIEW (widget);
452
453   if (GTK_WIDGET_REALIZED (widget))
454     {
455       gdk_window_move_resize (widget->window,
456                               allocation->x, allocation->y,
457                               allocation->width, allocation->height);
458       gdk_window_resize (page_view->priv->bin_window,
459                          MAX (page_view->priv->width, allocation->width),
460                          MAX (page_view->priv->height, allocation->height));
461     }
462
463   page_view->priv->hadjustment->page_size = allocation->width;
464   page_view->priv->hadjustment->page_increment = allocation->width * 0.9;
465   page_view->priv->hadjustment->step_increment = allocation->width * 0.1;
466   page_view->priv->hadjustment->lower = 0;
467   page_view->priv->hadjustment->upper = MAX (allocation->width, page_view->priv->width);
468   gtk_adjustment_changed (page_view->priv->hadjustment);
469
470   page_view->priv->vadjustment->page_size = allocation->height;
471   page_view->priv->vadjustment->page_increment = allocation->height * 0.9;
472   page_view->priv->vadjustment->step_increment = allocation->width * 0.1;
473   page_view->priv->vadjustment->lower = 0;
474   page_view->priv->vadjustment->upper = MAX (allocation->height, page_view->priv->height);
475   gtk_adjustment_changed (page_view->priv->vadjustment);
476 }
477
478 static void
479 ev_page_view_adjustment_changed (GtkAdjustment *adjustment,
480                                  EvPageView    *page_view)
481 {
482         if (GTK_WIDGET_REALIZED (page_view)) {
483                 gdk_window_move (page_view->priv->bin_window,
484                                  - page_view->priv->hadjustment->value,
485                                  - page_view->priv->vadjustment->value);
486
487                 gdk_window_process_updates (page_view->priv->bin_window, TRUE);
488         }
489 }
490
491 static void
492 ev_page_view_realize_document (EvPageView *page_view)
493 {
494         if (page_view->priv->document == NULL)
495                 return;
496
497         ev_document_set_target (page_view->priv->document,
498                                 page_view->priv->bin_window);
499         ev_page_view_load (page_view);
500         gtk_widget_queue_resize (GTK_WIDGET (page_view));
501 }
502
503
504 static void
505 ev_page_view_realize (GtkWidget *widget)
506 {
507         EvPageView *page_view;
508         GdkWindowAttr attributes;
509         gint attributes_mask;
510
511         g_return_if_fail (EV_IS_PAGE_VIEW (widget));
512
513         page_view = EV_PAGE_VIEW (widget);
514         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
515
516         /* Make the main, clipping window */
517         attributes.window_type = GDK_WINDOW_CHILD;
518         attributes.x = widget->allocation.x;
519         attributes.y = widget->allocation.y;
520         attributes.width = widget->allocation.width;
521         attributes.height = widget->allocation.height;
522         attributes.wclass = GDK_INPUT_OUTPUT;
523         attributes.visual = gtk_widget_get_visual (widget);
524         attributes.colormap = gtk_widget_get_colormap (widget);
525         attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
526
527         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
528
529         widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
530                                          &attributes, attributes_mask);
531         gdk_window_set_user_data (widget->window, widget);
532
533         /* Make the window for the page view */
534         attributes.x = 0;
535         attributes.y = 0;
536         attributes.width = MAX (page_view->priv->width, widget->allocation.width);
537         attributes.height = MAX (page_view->priv->height, widget->allocation.height);
538         attributes.event_mask = (GDK_EXPOSURE_MASK |
539                                  GDK_SCROLL_MASK |
540                                  GDK_POINTER_MOTION_MASK |
541                                  GDK_BUTTON_PRESS_MASK |
542                                  GDK_BUTTON_RELEASE_MASK |
543                                  GDK_KEY_PRESS_MASK |
544                                  GDK_KEY_RELEASE_MASK) |
545                 gtk_widget_get_events (widget);
546
547         page_view->priv->bin_window = gdk_window_new (widget->window,
548                                                       &attributes, attributes_mask);
549         gdk_window_set_user_data (page_view->priv->bin_window, widget);
550
551         widget->style = gtk_style_attach (widget->style, widget->window);
552         gdk_window_set_background (page_view->priv->bin_window, &widget->style->mid[widget->state]);
553         gdk_window_set_background (widget->window, &widget->style->mid[widget->state]);
554
555         ev_page_view_realize_document (page_view);
556 }
557
558
559 static void
560 ev_page_view_unrealize (GtkWidget *widget)
561 {
562   EvPageView *page_view;
563
564   page_view = EV_PAGE_VIEW (widget);
565
566   gdk_window_set_user_data (page_view->priv->bin_window, NULL);
567   gdk_window_destroy (page_view->priv->bin_window);
568   page_view->priv->bin_window = NULL;
569
570   /* GtkWidget::unrealize destroys children and widget->window */
571   if (GTK_WIDGET_CLASS (ev_page_view_parent_class)->unrealize)
572     (* GTK_WIDGET_CLASS (ev_page_view_parent_class)->unrealize) (widget);
573 }
574
575 static void
576 ev_page_view_map (GtkWidget *widget)
577 {
578   EvPageView *page_view;
579
580   page_view = EV_PAGE_VIEW (widget);
581
582   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
583
584   gdk_window_show (page_view->priv->bin_window);
585   gdk_window_show (widget->window);
586 }
587
588 static void
589 ev_page_view_load (EvPageView *page_view)
590 {
591         int i;
592         gboolean uniform_page_size = TRUE;
593         int width = 0, height = 0;
594
595         page_view->priv->n_pages =
596                 ev_document_get_n_pages (page_view->priv->document);
597
598         for (i = 1; i <= page_view->priv->n_pages; i++) {
599                 EvPageViewInfo *info;
600                 gint page_width = 0;
601                 gint page_height = 0;
602
603                 ev_document_set_scale (page_view->priv->document, page_view->priv->scale);
604                 ev_document_get_page_size (page_view->priv->document,
605                                            i,
606                                            &page_width, &page_height);
607
608                 if (i == 1) {
609                         width = page_width;
610                         height = page_height;
611                 } else if (width != page_width || height != page_height) {
612                         /* It's a different page size.  Backfill the array. */
613                         int j;
614
615                         uniform_page_size = FALSE;
616
617                         page_view->priv->page_sizes =
618                                 g_new0 (EvPageViewInfo, page_view->priv->n_pages);
619
620                         for (j = 1; j < i; j++) {
621
622                                 info = &(page_view->priv->page_sizes[j - 1]);
623                                 info->width = width;
624                                 info->height = height;
625                         }
626                 }
627
628                 if (! uniform_page_size) {
629                         info = &(page_view->priv->page_sizes[i - 1]);
630
631                         info->width = page_width;
632                         info->height = page_height;
633                 }
634         }
635
636         page_view->priv->uniform_page_size = uniform_page_size;
637
638         if (uniform_page_size) {
639                 page_view->priv->uniform_page_width = width;
640                 page_view->priv->uniform_page_height = height;
641         }
642
643         ev_page_view_update_size (page_view);
644
645         gtk_widget_queue_resize (GTK_WIDGET (page_view));
646 }
647
648 /* Public functions */
649 GtkWidget *
650 ev_page_view_new (void)
651 {
652         return g_object_new (EV_TYPE_PAGE_VIEW, NULL);
653 }
654
655 void
656 ev_page_view_set_document (EvPageView *page_view,
657                            EvDocument *document)
658 {
659         g_return_if_fail (EV_IS_PAGE_VIEW (page_view));
660
661         if (document != page_view->priv->document) {
662                 if (page_view->priv->document) {
663                         g_object_unref (page_view->priv->document);
664                 }
665
666                 page_view->priv->document = document;
667
668                 if (page_view->priv->document) {
669                         g_object_ref (page_view->priv->document);
670                 }
671
672                 if (GTK_WIDGET_REALIZED (page_view)) {
673                         ev_page_view_realize_document (page_view);
674                 }
675         }
676
677 }
678