]> www.fi.muni.cz Git - evince.git/blob - shell/ev-page-cache.c
Patch for case unsensitive page label match by Michael Lee. See bug
[evince.git] / shell / 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->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                 EvPage *page;
293                 double  page_width = 0;
294                 double  page_height = 0;
295                 gint    thumb_width = 0;
296                 gint    thumb_height = 0;
297
298                 page = ev_document_get_page (document, i);
299                 
300                 ev_document_get_page_size (document, page, &page_width, &page_height);
301
302                 page_cache->page_labels[i] = ev_document_get_page_label (document, page);
303                 
304                 if (page_cache->page_labels[i] != NULL) {
305                 
306                         page_cache->max_label_chars = MAX (page_cache->max_label_chars, 
307                                                            g_utf8_strlen (page_cache->page_labels[i], 256));
308                         if (!page_cache->has_labels) {
309                                 gchar *expected_label;
310                         
311                                 expected_label = g_strdup_printf ("%d", i + 1);
312                                 if (strcmp (expected_label, page_cache->page_labels[i]))  
313                                         page_cache->has_labels = TRUE;
314                                 g_free (expected_label);
315                         }
316                 }
317
318                 if (page_width > page_cache->max_width) {
319                         page_cache->max_width = page_width;
320                 }
321
322                 if (page_height > page_cache->max_height) {
323                         page_cache->max_height = page_height;
324                 }
325                         
326                 if (i == 0) {
327                         page_cache->uniform_width = page_width;
328                         page_cache->uniform_height = page_height;
329                 } else if (page_cache->uniform &&
330                            (page_cache->uniform_width != page_width ||
331                             page_cache->uniform_height != page_height)) {
332                         /* It's a different page size.  Backfill the array. */
333                         int j;
334
335                         page_cache->size_cache = g_new0 (EvPageCacheInfo, page_cache->n_pages);
336
337                         for (j = 0; j < i; j++) {
338                                 info = &(page_cache->size_cache [j]);
339                                 info->width = page_cache->uniform_width;
340                                 info->height = page_cache->uniform_height;
341                         }
342                         page_cache->uniform = FALSE;
343
344                 }
345
346                 if (! page_cache->uniform) {
347                         info = &(page_cache->size_cache [i]);
348
349                         info->width = page_width;
350                         info->height = page_height;
351                 }
352
353                 if (!has_thumbs) {
354                         g_object_unref (page);
355                         continue;
356                 }
357
358                 if (!rc) {
359                         rc = ev_render_context_new (page, 0, (gdouble)THUMBNAIL_WIDTH / page_width);
360                 } else {
361                         ev_render_context_set_page (rc, page);
362                         ev_render_context_set_scale (rc, (gdouble)THUMBNAIL_WIDTH / page_width);
363                 }
364
365                 ev_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (document),
366                                                        rc, &thumb_width, &thumb_height);
367                 
368                 if (thumb_width > page_cache->thumbs_max_width) {
369                         page_cache->thumbs_max_width = thumb_width;
370                 }
371
372                 if (thumb_height > page_cache->thumbs_max_height) {
373                         page_cache->thumbs_max_height = thumb_height;
374                 }
375                         
376                 if (i == 0) {
377                         page_cache->thumbs_uniform_width = thumb_width;
378                         page_cache->thumbs_uniform_height = thumb_height;
379                 } else if (page_cache->thumbs_uniform &&
380                            (page_cache->thumbs_uniform_width != thumb_width ||
381                             page_cache->thumbs_uniform_height != thumb_height)) {
382                         /* It's a different thumbnail size.  Backfill the array. */
383                         int j;
384
385                         page_cache->thumbs_size_cache = g_new0 (EvPageThumbsInfo, page_cache->n_pages);
386
387                         for (j = 0; j < i; j++) {
388                                 thumb_info = &(page_cache->thumbs_size_cache [j]);
389                                 thumb_info->width = page_cache->thumbs_uniform_width;
390                                 thumb_info->height = page_cache->thumbs_uniform_height;
391                         }
392                         page_cache->thumbs_uniform = FALSE;
393                 }
394
395                 if (! page_cache->thumbs_uniform) {
396                         thumb_info = &(page_cache->thumbs_size_cache [i]);
397
398                         thumb_info->width = thumb_width;
399                         thumb_info->height = thumb_height;
400                 }
401
402                 g_object_unref (page);
403         }
404
405         if (rc) {
406                 g_object_unref (rc);
407         }
408
409         build_height_to_page (page_cache);
410
411         /* make some sanity check assertions */
412         if (! page_cache->uniform)
413                 g_assert (page_cache->size_cache != NULL);
414         if (page_cache->uniform && page_cache->n_pages > 0)
415                 g_assert (page_cache->uniform_width > 0 && page_cache->uniform_height > 0);
416
417         ev_document_doc_mutex_unlock ();
418
419         if (page_cache->n_pages > 0)
420                 ev_page_cache_set_current_page (page_cache, 0);
421
422         return page_cache;
423 }
424
425 gint
426 ev_page_cache_get_n_pages (EvPageCache *page_cache)
427 {
428         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), 0);
429
430         return page_cache->n_pages;
431 }
432
433 gint
434 ev_page_cache_get_current_page (EvPageCache *page_cache)
435 {
436         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), 0);
437
438         return page_cache->current_page;
439 }
440
441 void
442 ev_page_cache_set_current_page (EvPageCache *page_cache,
443                                 int          page)
444 {
445         g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
446         g_return_if_fail (page >= 0 || page < page_cache->n_pages);
447
448         if (page == page_cache->current_page)
449                 return;
450
451         page_cache->current_page = page;
452         g_signal_emit (page_cache, signals[PAGE_CHANGED], 0, page);
453 }
454
455 void
456 ev_page_cache_set_current_page_history (EvPageCache *page_cache,
457                                         int          page)
458 {
459         if (abs (page - page_cache->current_page) > 1)
460                 g_signal_emit (page_cache, signals [HISTORY_CHANGED], 0, page);
461         
462         ev_page_cache_set_current_page (page_cache, page);
463 }
464
465 gboolean
466 ev_page_cache_set_page_label (EvPageCache *page_cache,
467                               const char  *page_label)
468 {
469         gint i, page;
470         long value;
471         char *endptr = NULL;
472         
473         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), FALSE);
474         g_return_val_if_fail (page_label != NULL, FALSE);
475
476         /* First, look for a literal label match */
477         for (i = 0; i < page_cache->n_pages; i ++) {
478                 if (page_cache->page_labels[i] != NULL &&
479                     ! strcmp (page_label, page_cache->page_labels[i])) {
480                         ev_page_cache_set_current_page (page_cache, i);
481                         return TRUE;
482                 }
483         }
484
485         /* Second, look for a match with case insensitively */
486         for (i = 0; i < page_cache->n_pages; i++) {
487                 if (page_cache->page_labels[i] != NULL &&
488                     ! strcasecmp (page_label, page_cache->page_labels[i])) {
489                         ev_page_cache_set_current_page (page_cache, i);
490                         return TRUE;
491                 }
492         }
493
494         /* Next, parse the label, and see if the number fits */
495         value = strtol (page_label, &endptr, 10);
496         if (endptr[0] == '\0') {
497                 /* Page number is an integer */
498                 page = MIN (G_MAXINT, value);
499
500                 /* convert from a page label to a page offset */
501                 page --;
502                 if (page >= 0 &&
503                     page < page_cache->n_pages) {
504                         ev_page_cache_set_current_page (page_cache, page);
505                         return TRUE;
506                 }
507         }
508
509         return FALSE;
510 }
511
512 const char *
513 ev_page_cache_get_title (EvPageCache *page_cache)
514 {
515         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), NULL);
516
517         return page_cache->title;
518 }
519
520 void
521 ev_page_cache_get_size (EvPageCache  *page_cache,
522                         gint          page,
523                         gint          rotation,
524                         gfloat        scale,
525                         gint         *width,
526                         gint         *height)
527 {
528         double w, h;
529
530         g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
531         g_return_if_fail (page >= 0 && page < page_cache->n_pages);
532
533         if (page_cache->uniform) {
534                 w = page_cache->uniform_width;
535                 h = page_cache->uniform_height;
536         } else {
537                 EvPageCacheInfo *info;
538
539                 info = &(page_cache->size_cache [page]);
540                 
541                 w = info->width;
542                 h = info->height;
543         }
544
545         w = w * scale + 0.5;
546         h = h * scale + 0.5;
547
548         if (rotation == 0 || rotation == 180) {
549                 if (width) *width = (int)w;
550                 if (height) *height = (int)h;
551         } else {
552                 if (width) *width = (int)h;
553                 if (height) *height = (int)w;
554         }
555 }
556
557 void
558 ev_page_cache_get_max_width (EvPageCache   *page_cache,
559                              gint           rotation,
560                              gfloat         scale,
561                              gint          *width)
562 {
563         g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
564
565         if (width) {
566                 if (rotation == 0 || rotation == 180) {
567                         *width = page_cache->max_width * scale;
568                 } else {
569                         *width = page_cache->max_height * scale;
570                 }
571         }
572 }
573
574 void
575 ev_page_cache_get_max_height (EvPageCache   *page_cache,
576                               gint           rotation,
577                               gfloat         scale,
578                               gint          *height)
579 {
580         g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
581
582         if (height) {
583                 if (rotation == 0 || rotation == 180) {
584                         *height = page_cache->max_height * scale;
585                 } else {
586                         *height = page_cache->max_width * scale;
587                 }
588         }
589 }
590
591 void    
592 ev_page_cache_get_height_to_page (EvPageCache   *page_cache,
593                                   gint           page,
594                                   gint           rotation,
595                                   gfloat         scale,
596                                   gint          *height,
597                                   gint          *dual_height)
598 {
599         g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
600         g_return_if_fail (page >= 0);
601         g_return_if_fail (!height || page <= page_cache->n_pages);
602         g_return_if_fail (!dual_height || page <= page_cache->n_pages + 1);
603
604         if (page_cache->rotation != rotation) {
605                 page_cache->rotation = rotation;
606                 build_height_to_page (page_cache);
607         }
608         
609         if (height)
610                 *height = page_cache->height_to_page [page] * scale;
611
612         if (dual_height)
613                 *dual_height = page_cache->dual_height_to_page [page] * scale;
614 }
615
616 void
617 ev_page_cache_get_thumbnail_size (EvPageCache  *page_cache,
618                                   gint          page,
619                                   gint          rotation,
620                                   gint         *width,
621                                   gint         *height)
622 {
623         gint w, h;
624
625         g_return_if_fail (EV_IS_PAGE_CACHE (page_cache));
626         g_return_if_fail (page >= 0 && page < page_cache->n_pages);
627
628         if (page_cache->thumbs_uniform) {
629                 w = page_cache->thumbs_uniform_width;
630                 h = page_cache->thumbs_uniform_height;
631         } else {
632                 EvPageThumbsInfo *info;
633
634                 info = &(page_cache->thumbs_size_cache [page]);
635                 
636                 w = info->width;
637                 h = info->height;
638         }
639
640         if (rotation == 0 || rotation == 180) {
641                 if (width) *width = w;
642                 if (height) *height = h;
643         } else {
644                 if (width) *width = h;
645                 if (height) *height = w;
646         }
647 }
648
649 gint
650 ev_page_cache_get_max_label_chars (EvPageCache *page_cache)
651 {
652         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), 0);
653         
654         return page_cache->max_label_chars;
655 }
656
657 gboolean
658 ev_page_cache_get_dual_even_left (EvPageCache *page_cache)
659 {
660         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), 0);
661         
662         return page_cache->dual_even_left;
663 }
664
665 gchar *
666 ev_page_cache_get_page_label (EvPageCache *page_cache,
667                               gint         page)
668 {
669         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), NULL);
670         g_return_val_if_fail (page >= 0 && page < page_cache->n_pages, NULL);
671
672         if (page_cache->page_labels[page] == NULL)
673                 return g_strdup_printf ("%d", page + 1);
674
675         return g_strdup (page_cache->page_labels[page]);
676 }
677
678 gboolean
679 ev_page_cache_has_nonnumeric_page_labels (EvPageCache *page_cache)
680 {
681         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), FALSE);
682         return page_cache->has_labels;
683 }
684
685 const EvDocumentInfo *
686 ev_page_cache_get_info (EvPageCache *page_cache)
687 {
688         g_return_val_if_fail (EV_IS_PAGE_CACHE (page_cache), NULL);
689
690         return page_cache->page_info;
691 }
692
693 #define PAGE_CACHE_STRING "ev-page-cache"
694
695 EvPageCache *
696 ev_page_cache_get (EvDocument *document)
697 {
698         EvPageCache *page_cache;
699
700         g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
701
702         page_cache = g_object_get_data (G_OBJECT (document), PAGE_CACHE_STRING);
703         if (page_cache == NULL) {
704                 page_cache = ev_page_cache_new (document);
705                 g_object_set_data_full (G_OBJECT (document), PAGE_CACHE_STRING, page_cache, g_object_unref);
706         }
707
708         return page_cache;
709 }