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