]> www.fi.muni.cz Git - evince.git/blob - shell/ev-page-cache.c
Add EvPage so that we can hold a reference to the backend page. Form
[evince.git] / shell / ev-page-cache.c
1 #include <config.h>
2 #include "ev-page-cache.h"
3 #include "ev-job-queue.h"
4 #include "ev-document-thumbnails.h"
5 #include "ev-page.h"
6 #include <stdlib.h>
7 #include <string.h>
8
9 #define THUMBNAIL_WIDTH 100
10
11 typedef struct _EvPageCacheInfo
12 {
13         double width;
14         double height;
15 } EvPageCacheInfo;
16
17 typedef struct _EvPageThumbsInfo
18 {
19         gint width;
20         gint height;
21 } EvPageThumbsInfo;
22
23 struct _EvPageCache
24 {
25         GObject parent;
26
27         gint current_page;
28         int n_pages;
29         char *title;
30         char **page_labels;
31         
32         gint max_label_chars;
33         gboolean has_labels;
34         gboolean uniform;
35         gboolean dual_even_left;
36         
37         double uniform_width;
38         double uniform_height;
39
40         double  max_width;
41         double  max_height;
42
43         double* height_to_page;
44         double* dual_height_to_page;
45
46         int rotation;
47
48         EvPageCacheInfo *size_cache;
49         EvDocumentInfo *page_info;
50
51         /* Thumbnail dimensions */
52         gboolean thumbs_uniform;
53         gint thumbs_uniform_width;
54         gint thumbs_uniform_height;
55         gint thumbs_max_width;
56         gint thumbs_max_height;
57         EvPageThumbsInfo *thumbs_size_cache;
58 };
59
60 struct _EvPageCacheClass
61 {
62         GObjectClass parent_class;
63
64         void (* page_changed) (EvPageCache *page_cache, gint page);
65         void (* history_changed) (EvPageCache *page_cache, gint page);
66 };
67
68 enum
69 {
70         PAGE_CHANGED,
71         HISTORY_CHANGED,
72         N_SIGNALS,
73 };
74
75 static guint signals[N_SIGNALS] = {0, };
76
77 static void ev_page_cache_init       (EvPageCache      *page_cache);
78 static void ev_page_cache_class_init (EvPageCacheClass *page_cache);
79 static void ev_page_cache_finalize   (GObject *object);
80
81 G_DEFINE_TYPE (EvPageCache, ev_page_cache, G_TYPE_OBJECT)
82
83 static void
84 ev_page_cache_init (EvPageCache *page_cache)
85 {
86         page_cache->current_page = -1;
87         page_cache->max_label_chars = 0;
88 }
89
90 static void
91 ev_page_cache_class_init (EvPageCacheClass *class)
92 {
93         GObjectClass *object_class;
94
95         object_class = G_OBJECT_CLASS (class);
96
97         object_class->finalize = ev_page_cache_finalize;
98
99         signals [PAGE_CHANGED] =
100                 g_signal_new ("page-changed",
101                               EV_TYPE_PAGE_CACHE,
102                               G_SIGNAL_RUN_LAST,
103                               G_STRUCT_OFFSET (EvPageCacheClass, page_changed),
104                               NULL, NULL,
105                               g_cclosure_marshal_VOID__INT,
106                               G_TYPE_NONE, 1,
107                               G_TYPE_INT);
108
109         signals [HISTORY_CHANGED] =
110                 g_signal_new ("history-changed",
111                               EV_TYPE_PAGE_CACHE,
112                               G_SIGNAL_RUN_LAST,
113                               G_STRUCT_OFFSET (EvPageCacheClass, history_changed),
114                               NULL, NULL,
115                               g_cclosure_marshal_VOID__INT,
116                               G_TYPE_NONE, 1,
117                               G_TYPE_INT);
118
119 }
120
121 static void
122 ev_page_cache_finalize (GObject *object)
123 {
124         EvPageCache *page_cache;
125
126         page_cache = EV_PAGE_CACHE (object);
127
128         if (page_cache->title) {
129                 g_free (page_cache->title);
130                 page_cache->title = NULL;
131         }
132
133         if (page_cache->size_cache) {
134                 g_free (page_cache->size_cache);
135                 page_cache->size_cache = NULL;
136         }
137
138         if (page_cache->height_to_page) {
139                 g_free (page_cache->height_to_page);
140                 page_cache->height_to_page = NULL;
141         }
142
143         if (page_cache->dual_height_to_page) {
144                 g_free (page_cache->dual_height_to_page);
145                 page_cache->dual_height_to_page = NULL;
146         }
147
148         if (page_cache->page_labels) {
149                 gint i;
150
151                 for (i = 0; i < page_cache->n_pages; i++) {
152                         if (page_cache->page_labels[i])
153                                 g_free (page_cache->page_labels[i]);
154                 }
155                 g_free (page_cache->page_labels);
156                 page_cache->page_labels = NULL;
157         }
158
159         if (page_cache->page_info) {
160                 ev_document_info_free (page_cache->page_info);
161                 page_cache->page_info = NULL;
162         }
163
164         G_OBJECT_CLASS (ev_page_cache_parent_class)->finalize (object);
165 }
166
167 static void
168 build_height_to_page (EvPageCache *page_cache)
169 {
170         gboolean swap;
171         int i;
172         double uniform_height, page_height, next_page_height;
173         double saved_height;
174
175         swap = (page_cache->rotation == 90 ||
176                 page_cache->rotation == 270);
177
178         g_free (page_cache->height_to_page);
179         g_free (page_cache->dual_height_to_page);
180
181         page_cache->height_to_page = g_new0(double, page_cache->n_pages + 1);
182         page_cache->dual_height_to_page = g_new0(double, page_cache->n_pages + 2);
183         
184         saved_height = 0;
185         for (i = 0; i <= page_cache->n_pages; i++) {
186                 if (page_cache->uniform) {
187                         if (!swap) {
188                                 uniform_height = page_cache->uniform_height;
189                         } else {
190                                 uniform_height = page_cache->uniform_width;
191                         }
192                         page_cache->height_to_page [i] = i * uniform_height;
193                 } else {
194                         if (i < page_cache->n_pages) {
195                                 if (!swap) {
196                                         page_height = page_cache->size_cache [i].height;
197                                 } else {
198                                         page_height = page_cache->size_cache [i].width;
199                                 }
200                         } else {
201                                 page_height = 0;
202                         }
203                         page_cache->height_to_page [i] = saved_height;
204                         saved_height += page_height;
205                 }
206         }
207
208         if (page_cache->dual_even_left && !page_cache->uniform) {
209                 if (!swap) {
210                         saved_height = page_cache->size_cache [0].height;
211                 } else {
212                         saved_height = page_cache->size_cache [0].width;
213                 }
214         } else {
215                 saved_height = 0;
216         }
217         for (i = page_cache->dual_even_left; i < page_cache->n_pages + 2; i += 2) {
218                 if (page_cache->uniform) {
219                         if (!swap) {
220                                 uniform_height = page_cache->uniform_height;
221                         } else {
222                                 uniform_height = page_cache->uniform_width;
223                         }
224                         page_cache->dual_height_to_page [i] = ((i + page_cache->dual_even_left) / 2) * uniform_height;
225                         if (i + 1 < page_cache->n_pages + 2)
226                                 page_cache->dual_height_to_page [i + 1] = ((i + page_cache->dual_even_left) / 2) * uniform_height;
227                 } else {
228                         if (i + 1 < page_cache->n_pages) {
229                                 if (!swap) {
230                                         next_page_height = page_cache->size_cache [i + 1].height;
231                                 } else {
232                                         next_page_height = page_cache->size_cache [i + 1].width;
233                                 }
234                         } else {
235                                 next_page_height = 0;
236                         }
237                         if (i < page_cache->n_pages) {
238                                 if (!swap) {
239                                         page_height = page_cache->size_cache [i].height;
240                                 } else {
241                                         page_height = page_cache->size_cache [i].width;
242                                 }
243                         } else {
244                                 page_height = 0;
245                         }
246                         if (i + 1 < page_cache->n_pages + 2) {
247                                 page_cache->dual_height_to_page [i] = saved_height;
248                                 page_cache->dual_height_to_page [i + 1] = saved_height;
249                                 saved_height += MAX(page_height, next_page_height);
250                         } else {
251                                 page_cache->dual_height_to_page [i] = saved_height;
252                         }
253                 }
254         }
255 }
256
257 EvPageCache *
258 ev_page_cache_new (EvDocument *document)
259 {
260         EvPageCache *page_cache;
261         EvPageCacheInfo *info;
262         EvPageThumbsInfo *thumb_info;
263         EvRenderContext *rc = NULL;
264         gboolean has_thumbs;
265         gint i;
266
267         page_cache = (EvPageCache *) g_object_new (EV_TYPE_PAGE_CACHE, NULL);
268
269         ev_document_doc_mutex_lock ();
270
271         /* We read page information out of the document */
272
273         /* Assume all pages are the same size until proven otherwise */
274         page_cache->uniform = TRUE;
275         page_cache->has_labels = FALSE;
276         page_cache->n_pages = ev_document_get_n_pages (document);
277         page_cache->dual_even_left = (page_cache->n_pages > 2);
278         page_cache->page_labels = g_new0 (char *, page_cache->n_pages);
279         page_cache->max_width = 0;
280         page_cache->max_height = 0;
281         page_cache->page_info = ev_document_get_info (document);
282         page_cache->thumbs_uniform = TRUE;
283
284         if (page_cache->page_info->fields_mask & EV_DOCUMENT_INFO_TITLE) {
285                 page_cache->title = g_strdup (page_cache->page_info->title);
286         } else {
287                 page_cache->title = NULL;
288         }
289
290         has_thumbs = EV_IS_DOCUMENT_THUMBNAILS (document);
291         
292         for (i = 0; i < page_cache->n_pages; i++) {
293                 EvPage *page;
294                 double  page_width = 0;
295                 double  page_height = 0;
296                 gint    thumb_width = 0;
297                 gint    thumb_height = 0;
298
299                 page = ev_document_get_page (document, i);
300                 
301                 ev_document_get_page_size (document, page, &page_width, &page_height);
302
303                 page_cache->page_labels[i] = ev_document_get_page_label (document, page);
304                 
305                 if (page_cache->page_labels[i] != NULL) {
306                 
307                         page_cache->max_label_chars = MAX (page_cache->max_label_chars, 
308                                                            g_utf8_strlen (page_cache->page_labels[i], 256));
309                         if (!page_cache->has_labels) {
310                                 gchar *expected_label;
311                         
312                                 expected_label = g_strdup_printf ("%d", i + 1);
313                                 if (strcmp (expected_label, page_cache->page_labels[i]))  
314                                         page_cache->has_labels = TRUE;
315                                 g_free (expected_label);
316                         }
317                 }
318
319                 if (page_width > page_cache->max_width) {
320                         page_cache->max_width = page_width;
321                 }
322
323                 if (page_height > page_cache->max_height) {
324                         page_cache->max_height = page_height;
325                 }
326                         
327                 if (i == 0) {
328                         page_cache->uniform_width = page_width;
329                         page_cache->uniform_height = page_height;
330                 } else if (page_cache->uniform &&
331                            (page_cache->uniform_width != page_width ||
332                             page_cache->uniform_height != page_height)) {
333                         /* It's a different page size.  Backfill the array. */
334                         int j;
335
336                         page_cache->size_cache = g_new0 (EvPageCacheInfo, page_cache->n_pages);
337
338                         for (j = 0; j < i; j++) {
339                                 info = &(page_cache->size_cache [j]);
340                                 info->width = page_cache->uniform_width;
341                                 info->height = page_cache->uniform_height;
342                         }
343                         page_cache->uniform = FALSE;
344
345                 }
346
347                 if (! page_cache->uniform) {
348                         info = &(page_cache->size_cache [i]);
349
350                         info->width = page_width;
351                         info->height = page_height;
352                 }
353
354                 if (!has_thumbs) {
355                         g_object_unref (page);
356                         continue;
357                 }
358
359                 if (!rc) {
360                         rc = ev_render_context_new (page, 0, (gdouble)THUMBNAIL_WIDTH / page_width);
361                 } else {
362                         ev_render_context_set_page (rc, page);
363                         ev_render_context_set_scale (rc, (gdouble)THUMBNAIL_WIDTH / page_width);
364                 }
365
366                 ev_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (document),
367                                                        rc, &thumb_width, &thumb_height);
368                 
369                 if (thumb_width > page_cache->thumbs_max_width) {
370                         page_cache->thumbs_max_width = thumb_width;
371                 }
372
373                 if (thumb_height > page_cache->thumbs_max_height) {
374                         page_cache->thumbs_max_height = thumb_height;
375                 }
376                         
377                 if (i == 0) {
378                         page_cache->thumbs_uniform_width = thumb_width;
379                         page_cache->thumbs_uniform_height = thumb_height;
380                 } else if (page_cache->thumbs_uniform &&
381                            (page_cache->thumbs_uniform_width != thumb_width ||
382                             page_cache->thumbs_uniform_height != thumb_height)) {
383                         /* It's a different thumbnail size.  Backfill the array. */
384                         int j;
385
386                         page_cache->thumbs_size_cache = g_new0 (EvPageThumbsInfo, page_cache->n_pages);
387
388                         for (j = 0; j < i; j++) {
389                                 thumb_info = &(page_cache->thumbs_size_cache [j]);
390                                 thumb_info->width = page_cache->thumbs_uniform_width;
391                                 thumb_info->height = page_cache->thumbs_uniform_height;
392                         }
393                         page_cache->thumbs_uniform = FALSE;
394                 }
395
396                 if (! page_cache->thumbs_uniform) {
397                         thumb_info = &(page_cache->thumbs_size_cache [i]);
398
399                         thumb_info->width = thumb_width;
400                         thumb_info->height = thumb_height;
401                 }
402
403                 g_object_unref (page);
404         }
405
406         if (rc) {
407                 g_object_unref (rc);
408         }
409
410         build_height_to_page (page_cache);
411
412         /* make some sanity check assertions */
413         if (! page_cache->uniform)
414                 g_assert (page_cache->size_cache != NULL);
415         if (page_cache->uniform && page_cache->n_pages > 0)
416                 g_assert (page_cache->uniform_width > 0 && page_cache->uniform_height > 0);
417
418         ev_document_doc_mutex_unlock ();
419
420         if (page_cache->n_pages > 0)
421                 ev_page_cache_set_current_page (page_cache, 0);
422
423         return page_cache;
424 }
425
426 gint
427 ev_page_cache_get_n_pages (EvPageCache *page_cache)
428 {
429         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), 0);
430
431         return page_cache->n_pages;
432 }
433
434 gint
435 ev_page_cache_get_current_page (EvPageCache *page_cache)
436 {
437         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), 0);
438
439         return page_cache->current_page;
440 }
441
442 void
443 ev_page_cache_set_current_page (EvPageCache *page_cache,
444                                 int          page)
445 {
446         g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
447         g_return_if_fail (page >= 0 || page < page_cache->n_pages);
448
449         if (page == page_cache->current_page)
450                 return;
451
452         page_cache->current_page = page;
453         g_signal_emit (page_cache, signals[PAGE_CHANGED], 0, page);
454 }
455
456 void
457 ev_page_cache_set_current_page_history (EvPageCache *page_cache,
458                                         int          page)
459 {
460         if (abs (page - page_cache->current_page) > 1)
461                 g_signal_emit (page_cache, signals [HISTORY_CHANGED], 0, page);
462         
463         ev_page_cache_set_current_page (page_cache, page);
464 }
465
466 gboolean
467 ev_page_cache_set_page_label (EvPageCache *page_cache,
468                               const char  *page_label)
469 {
470         gint i, page;
471         long value;
472         char *endptr = NULL;
473         
474         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), FALSE);
475         g_return_val_if_fail (page_label != NULL, FALSE);
476
477         /* First, look for a literal label match */
478         for (i = 0; i < page_cache->n_pages; i ++) {
479                 if (page_cache->page_labels[i] != NULL &&
480                     ! strcmp (page_label, page_cache->page_labels[i])) {
481                         ev_page_cache_set_current_page (page_cache, i);
482                         return TRUE;
483                 }
484         }
485
486         /* Next, parse the label, and see if the number fits */
487         value = strtol (page_label, &endptr, 10);
488         if (endptr[0] == '\0') {
489                 /* Page number is an integer */
490                 page = MIN (G_MAXINT, value);
491
492                 /* convert from a page label to a page offset */
493                 page --;
494                 if (page >= 0 &&
495                     page < page_cache->n_pages) {
496                         ev_page_cache_set_current_page (page_cache, page);
497                         return TRUE;
498                 }
499         }
500
501         return FALSE;
502 }
503
504 const char *
505 ev_page_cache_get_title (EvPageCache *page_cache)
506 {
507         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), NULL);
508
509         return page_cache->title;
510 }
511
512 void
513 ev_page_cache_get_size (EvPageCache  *page_cache,
514                         gint          page,
515                         gint          rotation,
516                         gfloat        scale,
517                         gint         *width,
518                         gint         *height)
519 {
520         double w, h;
521
522         g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
523         g_return_if_fail (page >= 0 && page < page_cache->n_pages);
524
525         if (page_cache->uniform) {
526                 w = page_cache->uniform_width;
527                 h = page_cache->uniform_height;
528         } else {
529                 EvPageCacheInfo *info;
530
531                 info = &(page_cache->size_cache [page]);
532                 
533                 w = info->width;
534                 h = info->height;
535         }
536
537         w = w * scale + 0.5;
538         h = h * scale + 0.5;
539
540         if (rotation == 0 || rotation == 180) {
541                 if (width) *width = (int)w;
542                 if (height) *height = (int)h;
543         } else {
544                 if (width) *width = (int)h;
545                 if (height) *height = (int)w;
546         }
547 }
548
549 void
550 ev_page_cache_get_max_width (EvPageCache   *page_cache,
551                              gint           rotation,
552                              gfloat         scale,
553                              gint          *width)
554 {
555         g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
556
557         if (width) {
558                 if (rotation == 0 || rotation == 180) {
559                         *width = page_cache->max_width * scale;
560                 } else {
561                         *width = page_cache->max_height * scale;
562                 }
563         }
564 }
565
566 void
567 ev_page_cache_get_max_height (EvPageCache   *page_cache,
568                               gint           rotation,
569                               gfloat         scale,
570                               gint          *height)
571 {
572         g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
573
574         if (height) {
575                 if (rotation == 0 || rotation == 180) {
576                         *height = page_cache->max_height * scale;
577                 } else {
578                         *height = page_cache->max_width * scale;
579                 }
580         }
581 }
582
583 void    
584 ev_page_cache_get_height_to_page (EvPageCache   *page_cache,
585                                   gint           page,
586                                   gint           rotation,
587                                   gfloat         scale,
588                                   gint          *height,
589                                   gint          *dual_height)
590 {
591         g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
592         g_return_if_fail (page >= 0);
593         g_return_if_fail (!height || page <= page_cache->n_pages);
594         g_return_if_fail (!dual_height || page <= page_cache->n_pages + 1);
595
596         if (page_cache->rotation != rotation) {
597                 page_cache->rotation = rotation;
598                 build_height_to_page (page_cache);
599         }
600         
601         if (height)
602                 *height = page_cache->height_to_page [page] * scale;
603
604         if (dual_height)
605                 *dual_height = page_cache->dual_height_to_page [page] * scale;
606 }
607
608 void
609 ev_page_cache_get_thumbnail_size (EvPageCache  *page_cache,
610                                   gint          page,
611                                   gint          rotation,
612                                   gint         *width,
613                                   gint         *height)
614 {
615         gint w, h;
616
617         g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
618         g_return_if_fail (page >= 0 && page < page_cache->n_pages);
619
620         if (page_cache->thumbs_uniform) {
621                 w = page_cache->thumbs_uniform_width;
622                 h = page_cache->thumbs_uniform_height;
623         } else {
624                 EvPageThumbsInfo *info;
625
626                 info = &(page_cache->thumbs_size_cache [page]);
627                 
628                 w = info->width;
629                 h = info->height;
630         }
631
632         if (rotation == 0 || rotation == 180) {
633                 if (width) *width = w;
634                 if (height) *height = h;
635         } else {
636                 if (width) *width = h;
637                 if (height) *height = w;
638         }
639 }
640
641 gint
642 ev_page_cache_get_max_label_chars (EvPageCache *page_cache)
643 {
644         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), 0);
645         
646         return page_cache->max_label_chars;
647 }
648
649 gboolean
650 ev_page_cache_get_dual_even_left (EvPageCache *page_cache)
651 {
652         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), 0);
653         
654         return page_cache->dual_even_left;
655 }
656
657 gchar *
658 ev_page_cache_get_page_label (EvPageCache *page_cache,
659                               gint         page)
660 {
661         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), NULL);
662         g_return_val_if_fail (page >= 0 && page < page_cache->n_pages, NULL);
663
664         if (page_cache->page_labels[page] == NULL)
665                 return g_strdup_printf ("%d", page + 1);
666
667         return g_strdup (page_cache->page_labels[page]);
668 }
669
670 gboolean
671 ev_page_cache_has_nonnumeric_page_labels (EvPageCache *page_cache)
672 {
673         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), FALSE);
674         return page_cache->has_labels;
675 }
676
677 const EvDocumentInfo *
678 ev_page_cache_get_info (EvPageCache *page_cache)
679 {
680         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), NULL);
681
682         return page_cache->page_info;
683 }
684
685 #define PAGE_CACHE_STRING "ev-page-cache"
686
687 EvPageCache *
688 ev_page_cache_get (EvDocument *document)
689 {
690         EvPageCache *page_cache;
691
692         g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
693
694         page_cache = g_object_get_data (G_OBJECT (document), PAGE_CACHE_STRING);
695         if (page_cache == NULL) {
696                 page_cache = ev_page_cache_new (document);
697                 g_object_set_data_full (G_OBJECT (document), PAGE_CACHE_STRING, page_cache, g_object_unref);
698         }
699
700         return page_cache;
701 }