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