]> www.fi.muni.cz Git - evince.git/blob - shell/ev-pixbuf-cache.c
Fix build with gtk+ >= 2.11.5 due to gtktooltips deprecation. Fixes bug
[evince.git] / shell / ev-pixbuf-cache.c
1 #include "ev-pixbuf-cache.h"
2 #include "ev-job-queue.h"
3 #include "ev-page-cache.h"
4 #include "ev-selection.h"
5 #include "ev-document-images.h"
6 #include "ev-document-forms.h"
7 #include "ev-image.h"
8 #include "ev-form-field.h"
9
10 typedef struct _CacheJobInfo
11 {
12         EvJob *job;
13         EvRenderContext *rc;
14
15         /* Region of the page that needs to be drawn */
16         GdkRegion *region; 
17
18         /* Data we get from rendering */
19         cairo_surface_t *surface;
20         GList *link_mapping;
21         GList *image_mapping;
22         GList *form_field_mapping;
23         GdkRegion *text_mapping;
24         
25         /* Selection data. 
26          * Selection_points are the coordinates encapsulated in selection.
27          * target_points is the target selection size. */
28         EvRectangle selection_points;
29         EvRectangle target_points;
30         gboolean    points_set;
31         
32         cairo_surface_t *selection;
33         GdkRegion *selection_region;
34 } CacheJobInfo;
35
36 struct _EvPixbufCache
37 {
38         GObject parent;
39
40         /* We keep a link to our containing view just for style information. */
41         GtkWidget *view;
42         EvDocument *document;
43         int start_page;
44         int end_page;
45
46         /* preload_cache_size is the number of pages prior to the current
47          * visible area that we cache.  It's normally 1, but could be 2 in the
48          * case of twin pages.
49          */
50         int preload_cache_size;
51         CacheJobInfo *prev_job;
52         CacheJobInfo *job_list;
53         CacheJobInfo *next_job;
54 };
55
56 struct _EvPixbufCacheClass
57 {
58         GObjectClass parent_class;
59
60         void (* job_finished) (EvPixbufCache *pixbuf_cache);
61 };
62
63
64 enum
65 {
66         JOB_FINISHED,
67         N_SIGNALS,
68 };
69
70 static guint signals[N_SIGNALS] = {0, };
71
72 static void          ev_pixbuf_cache_init       (EvPixbufCache      *pixbuf_cache);
73 static void          ev_pixbuf_cache_class_init (EvPixbufCacheClass *pixbuf_cache);
74 static void          ev_pixbuf_cache_finalize   (GObject            *object);
75 static void          ev_pixbuf_cache_dispose    (GObject            *object);
76 static void          job_finished_cb            (EvJob              *job,
77                                                  EvPixbufCache      *pixbuf_cache);
78 static CacheJobInfo *find_job_cache             (EvPixbufCache      *pixbuf_cache,
79                                                  int                 page);
80 static void          copy_job_to_job_info       (EvJobRender        *job_render,
81                                                  CacheJobInfo       *job_info,
82                                                  EvPixbufCache      *pixbuf_cache);
83 static gboolean      new_selection_surface_needed(EvPixbufCache      *pixbuf_cache,
84                                                   CacheJobInfo       *job_info,
85                                                   gint                page,
86                                                   gfloat              scale);
87
88
89 /* These are used for iterating through the prev and next arrays */
90 #define FIRST_VISABLE_PREV(pixbuf_cache) \
91         (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page))
92 #define VISIBLE_NEXT_LEN(pixbuf_cache, page_cache) \
93         (MIN(pixbuf_cache->preload_cache_size, ev_page_cache_get_n_pages (page_cache) - (1 + pixbuf_cache->end_page)))
94 #define PAGE_CACHE_LEN(pixbuf_cache) \
95         ((pixbuf_cache->end_page - pixbuf_cache->start_page) + 1)
96
97 G_DEFINE_TYPE (EvPixbufCache, ev_pixbuf_cache, G_TYPE_OBJECT)
98
99 static void
100 ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache)
101 {
102         pixbuf_cache->start_page = 0;
103         pixbuf_cache->end_page = 0;
104         pixbuf_cache->job_list = g_new0 (CacheJobInfo, PAGE_CACHE_LEN (pixbuf_cache));
105
106         pixbuf_cache->preload_cache_size = 2;
107         pixbuf_cache->prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
108         pixbuf_cache->next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
109 }
110
111 static void
112 ev_pixbuf_cache_class_init (EvPixbufCacheClass *class)
113 {
114         GObjectClass *object_class;
115
116         object_class = G_OBJECT_CLASS (class);
117
118         object_class->finalize = ev_pixbuf_cache_finalize;
119         object_class->dispose = ev_pixbuf_cache_dispose;
120
121         signals[JOB_FINISHED] =
122                 g_signal_new ("job-finished",
123                               G_OBJECT_CLASS_TYPE (object_class),
124                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
125                               G_STRUCT_OFFSET (EvPixbufCacheClass, job_finished),
126                               NULL, NULL,
127                               g_cclosure_marshal_VOID__POINTER,
128                               G_TYPE_NONE, 1,
129                               G_TYPE_POINTER);
130 }
131
132 static void
133 ev_pixbuf_cache_finalize (GObject *object)
134 {
135         EvPixbufCache *pixbuf_cache;
136
137         pixbuf_cache = EV_PIXBUF_CACHE (object);
138
139         g_free (pixbuf_cache->prev_job);
140         g_free (pixbuf_cache->job_list);
141         g_free (pixbuf_cache->next_job);
142
143         G_OBJECT_CLASS (ev_pixbuf_cache_parent_class)->finalize (object);
144 }
145
146 static void
147 dispose_cache_job_info (CacheJobInfo *job_info,
148                         gpointer      data)
149 {
150         if (job_info == NULL)
151                 return;
152         if (job_info->job) {
153                 g_signal_handlers_disconnect_by_func (job_info->job,
154                                                       G_CALLBACK (job_finished_cb),
155                                                       data);
156                 ev_job_queue_remove_job (job_info->job);
157                 g_object_unref (G_OBJECT (job_info->job));
158                 job_info->job = NULL;
159         }
160         if (job_info->surface) {
161                 cairo_surface_destroy (job_info->surface);
162                 job_info->surface = NULL;
163         }
164         if (job_info->region) {
165                 gdk_region_destroy (job_info->region);
166                 job_info->region = NULL;
167         }
168         if (job_info->link_mapping) {
169                 ev_link_mapping_free (job_info->link_mapping);
170                 job_info->link_mapping = NULL;
171         }
172         if (job_info->image_mapping) {
173                 ev_image_mapping_free (job_info->image_mapping);
174                 job_info->image_mapping = NULL;
175         }
176         if (job_info->form_field_mapping) {
177                 ev_form_field_mapping_free (job_info->form_field_mapping);
178                 job_info->form_field_mapping = NULL;
179         }
180         if (job_info->text_mapping) {
181                 gdk_region_destroy (job_info->text_mapping);
182                 job_info->text_mapping = NULL;
183         }
184         if (job_info->selection) {
185                 cairo_surface_destroy (job_info->selection);
186                 job_info->selection = NULL;
187         }
188         if (job_info->selection_region) {
189                 gdk_region_destroy (job_info->selection_region);
190                 job_info->selection_region = NULL;
191         }
192         if (job_info->rc) {
193                 g_object_unref (G_OBJECT (job_info->rc));
194                 job_info->rc = NULL;
195         }
196
197         job_info->points_set = FALSE;
198 }
199
200 static void
201 ev_pixbuf_cache_dispose (GObject *object)
202 {
203         EvPixbufCache *pixbuf_cache;
204         int i;
205
206         pixbuf_cache = EV_PIXBUF_CACHE (object);
207
208         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
209                 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
210                 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
211         }
212
213         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
214                 dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
215         }
216
217         G_OBJECT_CLASS (ev_pixbuf_cache_parent_class)->dispose (object);
218 }
219
220
221 EvPixbufCache *
222 ev_pixbuf_cache_new (GtkWidget  *view,
223                      EvDocument *document)
224 {
225         EvPixbufCache *pixbuf_cache;
226
227         pixbuf_cache = (EvPixbufCache *) g_object_new (EV_TYPE_PIXBUF_CACHE, NULL);
228         /* This is a backlink, so we don't ref this */ 
229         pixbuf_cache->view = view;
230         pixbuf_cache->document = document;
231
232         return pixbuf_cache;
233 }
234
235 static void
236 job_finished_cb (EvJob         *job,
237                  EvPixbufCache *pixbuf_cache)
238 {
239         CacheJobInfo *job_info;
240         EvJobRender *job_render = EV_JOB_RENDER (job);
241
242         /* If the job is outside of our interest, we silently discard it */
243         if ((job_render->rc->page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)) ||
244             (job_render->rc->page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))) {
245                 g_object_unref (job);
246                 return;
247         }
248         
249         job_info = find_job_cache (pixbuf_cache, job_render->rc->page);
250
251         copy_job_to_job_info (job_render, job_info, pixbuf_cache);
252         g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
253 }
254
255 /* This checks a job to see if the job would generate the right sized pixbuf
256  * given a scale.  If it won't, it removes the job and clears it to NULL.
257  */
258 static void
259 check_job_size_and_unref (EvPixbufCache *pixbuf_cache,
260                           CacheJobInfo *job_info,
261                           EvPageCache  *page_cache,
262                           gfloat        scale)
263 {
264         gint width;
265         gint height;
266
267         g_assert (job_info);
268
269         if (job_info->job == NULL)
270                 return;
271
272         ev_page_cache_get_size (page_cache,
273                                 EV_JOB_RENDER (job_info->job)->rc->page,
274                                 EV_JOB_RENDER (job_info->job)->rc->rotation,
275                                 scale,
276                                 &width, &height);
277                                 
278         if (width == EV_JOB_RENDER (job_info->job)->target_width &&
279             height == EV_JOB_RENDER (job_info->job)->target_height)
280                 return;
281
282         g_signal_handlers_disconnect_by_func (job_info->job,
283                                               G_CALLBACK (job_finished_cb),
284                                               pixbuf_cache);
285         ev_job_queue_remove_job (job_info->job);
286         g_object_unref (job_info->job);
287         job_info->job = NULL;
288 }
289
290 /* Do all function that copies a job from an older cache to it's position in the
291  * new cache.  It clears the old job if it doesn't have a place.
292  */
293 static void
294 move_one_job (CacheJobInfo  *job_info,
295               EvPixbufCache *pixbuf_cache,
296               int            page,
297               CacheJobInfo  *new_job_list,
298               CacheJobInfo  *new_prev_job,
299               CacheJobInfo  *new_next_job,
300               int            start_page,
301               int            end_page,
302               EvJobPriority  priority)
303 {
304         CacheJobInfo *target_page = NULL;
305         int page_offset;
306         EvJobPriority new_priority;
307
308         if (page < (start_page - pixbuf_cache->preload_cache_size) ||
309             page > (end_page + pixbuf_cache->preload_cache_size)) {
310                 dispose_cache_job_info (job_info, pixbuf_cache);
311                 return;
312         }
313
314         /* find the target page to copy it over to. */
315         if (page < start_page) {
316                 page_offset = (page - (start_page - pixbuf_cache->preload_cache_size));
317
318                 g_assert (page_offset >= 0 &&
319                           page_offset < pixbuf_cache->preload_cache_size);
320                 target_page = new_prev_job + page_offset;
321                 new_priority = EV_JOB_PRIORITY_LOW;
322         } else if (page > end_page) {
323                 page_offset = (page - (end_page + 1));
324
325                 g_assert (page_offset >= 0 &&
326                           page_offset < pixbuf_cache->preload_cache_size);
327                 target_page = new_next_job + page_offset;
328                 new_priority = EV_JOB_PRIORITY_LOW;
329         } else {
330                 page_offset = page - start_page;
331                 g_assert (page_offset >= 0 &&
332                           page_offset <= ((end_page - start_page) + 1));
333                 new_priority = EV_JOB_PRIORITY_HIGH;
334                 target_page = new_job_list + page_offset;
335         }
336
337         *target_page = *job_info;
338         job_info->job = NULL;
339         job_info->region = NULL;
340         job_info->surface = NULL;
341         job_info->link_mapping = NULL;
342         job_info->image_mapping = NULL;
343         job_info->form_field_mapping = NULL;
344
345         if (new_priority != priority && target_page->job) {
346                 ev_job_queue_update_job (target_page->job, new_priority);
347         }
348 }
349
350 static void
351 ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
352                               gint           start_page,
353                               gint           end_page)
354 {
355         CacheJobInfo *new_job_list;
356         CacheJobInfo *new_prev_job;
357         CacheJobInfo *new_next_job;
358         EvPageCache *page_cache;
359         int i, page;
360
361         if (pixbuf_cache->start_page == start_page &&
362             pixbuf_cache->end_page == end_page)
363                 return;
364
365         page_cache = ev_page_cache_get (pixbuf_cache->document);
366
367         new_job_list = g_new0 (CacheJobInfo, (end_page - start_page) + 1);
368         new_prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
369         new_next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
370
371         /* We go through each job in the old cache and either clear it or move
372          * it to a new location. */
373
374         /* Start with the prev cache. */
375         page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
376         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
377                 if (page < 0) {
378                         dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
379                 } else {
380                         move_one_job (pixbuf_cache->prev_job + i,
381                                       pixbuf_cache, page,
382                                       new_job_list, new_prev_job, new_next_job,
383                                       start_page, end_page, EV_JOB_PRIORITY_LOW);
384                 }
385                 page ++;
386         }
387
388         page = pixbuf_cache->start_page;
389         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
390                 move_one_job (pixbuf_cache->job_list + i,
391                               pixbuf_cache, page,
392                               new_job_list, new_prev_job, new_next_job,
393                               start_page, end_page, EV_JOB_PRIORITY_HIGH);
394                 page ++;
395         }
396
397         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
398                 if (page >= ev_page_cache_get_n_pages (page_cache)) {
399                         dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
400                 } else {
401                         move_one_job (pixbuf_cache->next_job + i,
402                                       pixbuf_cache, page,
403                                       new_job_list, new_prev_job, new_next_job,
404                                       start_page, end_page, EV_JOB_PRIORITY_LOW);
405                 }
406                 page ++;
407         }
408
409         g_free (pixbuf_cache->job_list);
410         g_free (pixbuf_cache->prev_job);
411         g_free (pixbuf_cache->next_job);
412
413         pixbuf_cache->job_list = new_job_list;
414         pixbuf_cache->prev_job = new_prev_job;
415         pixbuf_cache->next_job = new_next_job;
416
417         pixbuf_cache->start_page = start_page;
418         pixbuf_cache->end_page = end_page;
419 }
420
421 static void
422 copy_job_to_job_info (EvJobRender   *job_render,
423                       CacheJobInfo  *job_info,
424                       EvPixbufCache *pixbuf_cache)
425 {
426
427         job_info->points_set = FALSE;
428
429         if (job_info->surface) {
430                 cairo_surface_destroy (job_info->surface);
431         }
432         job_info->surface = cairo_surface_reference (job_render->surface);
433
434         if (job_info->rc) {
435                 g_object_unref (G_OBJECT (job_info->rc));
436         }
437         job_info->rc = g_object_ref (job_render->rc);
438
439         if (job_render->include_links) {
440                 if (job_info->link_mapping)
441                         ev_link_mapping_free (job_info->link_mapping);
442                 job_info->link_mapping = job_render->link_mapping;
443         }
444
445         if (job_render->include_images) {
446                 if (job_info->image_mapping)
447                         ev_image_mapping_free (job_info->image_mapping);
448                 job_info->image_mapping = job_render->image_mapping;
449         }
450
451         if (job_render->include_forms) {
452                 if (job_info->form_field_mapping)
453                         ev_form_field_mapping_free (job_info->form_field_mapping);
454                 job_info->form_field_mapping = job_render->form_field_mapping;
455         }
456
457         if (job_render->include_text) {
458                 if (job_info->text_mapping)
459                         gdk_region_destroy (job_info->text_mapping);
460                 job_info->text_mapping = job_render->text_mapping;
461         }
462
463         if (job_render->include_selection) {
464                 if (job_info->selection) {
465                         cairo_surface_destroy (job_info->selection);
466                         job_info->selection = NULL;
467                 }
468                 if (job_info->selection_region) {
469                         gdk_region_destroy (job_info->selection_region);
470                         job_info->selection_region = NULL;
471                 }
472                 
473                 job_info->selection_points = job_render->selection_points;
474                 job_info->selection_region = gdk_region_copy (job_render->selection_region);
475                 job_info->selection = cairo_surface_reference (job_render->selection);
476                 g_assert (job_info->selection_points.x1 >= 0);
477                 job_info->points_set = TRUE;
478         }
479
480         if (job_info->job) {
481                 g_signal_handlers_disconnect_by_func (job_info->job,
482                                                       G_CALLBACK (job_finished_cb),
483                                                       pixbuf_cache);
484                 ev_job_queue_remove_job (job_info->job);
485                 g_object_unref (G_OBJECT (job_info->job));
486                 job_info->job = NULL;
487         }
488 }
489
490 static CacheJobInfo *
491 find_job_cache (EvPixbufCache *pixbuf_cache,
492                 int            page)
493 {
494         int page_offset;
495
496         if (page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size) ||
497             page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))
498                 return NULL;
499
500         if (page < pixbuf_cache->start_page) {
501                 page_offset = (page - (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size));
502
503                 g_assert (page_offset >= 0 &&
504                           page_offset < pixbuf_cache->preload_cache_size);
505                 return pixbuf_cache->prev_job + page_offset;
506         }
507
508         if (page > pixbuf_cache->end_page) {
509                 page_offset = (page - (pixbuf_cache->end_page + 1));
510
511                 g_assert (page_offset >= 0 &&
512                           page_offset < pixbuf_cache->preload_cache_size);
513                 return pixbuf_cache->next_job + page_offset;
514         }
515
516         page_offset = page - pixbuf_cache->start_page;
517         g_assert (page_offset >= 0 &&
518                   page_offset <= PAGE_CACHE_LEN(pixbuf_cache));
519         return pixbuf_cache->job_list + page_offset;
520 }
521
522 static void
523 ev_pixbuf_cache_clear_job_sizes (EvPixbufCache *pixbuf_cache,
524                                  gfloat         scale)
525 {
526         EvPageCache *page_cache;
527         int i;
528
529         page_cache = ev_page_cache_get (pixbuf_cache->document);
530
531         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
532                 check_job_size_and_unref (pixbuf_cache, pixbuf_cache->job_list + i, page_cache, scale);
533         }
534
535         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
536                 check_job_size_and_unref (pixbuf_cache, pixbuf_cache->prev_job + i, page_cache, scale);
537                 check_job_size_and_unref (pixbuf_cache, pixbuf_cache->next_job + i, page_cache, scale);
538         }
539 }
540
541 #define FIRST_VISABLE_PREV(pixbuf_cache) \
542         (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page))
543
544 static void
545 get_selection_colors (GtkWidget *widget, GdkColor **text, GdkColor **base)
546 {
547     if (GTK_WIDGET_HAS_FOCUS (widget)) {
548         *text = &widget->style->text [GTK_STATE_SELECTED];
549         *base = &widget->style->base [GTK_STATE_SELECTED];
550     } else {
551         *text = &widget->style->text [GTK_STATE_ACTIVE];
552         *base = &widget->style->base [GTK_STATE_ACTIVE];
553     }
554 }
555
556 static void
557 add_job (EvPixbufCache *pixbuf_cache,
558          CacheJobInfo  *job_info,
559          EvPageCache   *page_cache,
560          GdkRegion     *region,
561          gint           width,
562          gint           height,
563          gint           page,
564          gint           rotation,
565          gfloat         scale,
566          EvJobPriority  priority)
567 {
568         gboolean include_links = FALSE;
569         gboolean include_text = FALSE;
570         gboolean include_selection = FALSE;
571         gboolean include_images = TRUE;
572         gboolean include_forms = FALSE;
573         GdkColor *text, *base;
574         
575         if (job_info->rc == NULL) {
576                 job_info->rc = ev_render_context_new (rotation, page, scale);
577         } else {
578                 ev_render_context_set_rotation (job_info->rc, rotation);
579                 ev_render_context_set_page (job_info->rc, page);
580                 ev_render_context_set_scale (job_info->rc, scale);
581         }
582
583         if (job_info->region)
584                 gdk_region_destroy (job_info->region);
585         job_info->region = region ? gdk_region_copy (region) : NULL;
586
587         /* Figure out what else we need for this job */
588         if (job_info->link_mapping == NULL)
589                 include_links = TRUE;
590         if (job_info->image_mapping == NULL)
591                 include_images = TRUE;
592         if (job_info->form_field_mapping == NULL)
593                 include_forms = TRUE;
594         if (job_info->text_mapping == NULL)
595                 include_text = TRUE;
596         if (new_selection_surface_needed (pixbuf_cache, job_info, page, scale)) {
597                 include_selection = TRUE;
598         }
599
600         gtk_widget_ensure_style (pixbuf_cache->view);
601
602         get_selection_colors (pixbuf_cache->view, &text, &base);
603
604         job_info->job = ev_job_render_new (pixbuf_cache->document,
605                                            job_info->rc,
606                                            width, height,
607                                            &(job_info->target_points),
608                                            text, base,
609                                            include_forms,
610                                            include_links,
611                                            include_images,
612                                            include_text,
613                                            include_selection);
614         ev_job_queue_add_job (job_info->job, priority);
615         g_signal_connect (job_info->job, "finished",
616                           G_CALLBACK (job_finished_cb),
617                           pixbuf_cache);
618 }
619
620 static void
621 add_job_if_needed (EvPixbufCache *pixbuf_cache,
622                    CacheJobInfo  *job_info,
623                    EvPageCache   *page_cache,
624                    gint           page,
625                    gint           rotation,
626                    gfloat         scale,
627                    EvJobPriority  priority)
628 {
629         gint width, height;
630
631         if (job_info->job)
632                 return;
633
634         ev_page_cache_get_size (page_cache, page, rotation,
635                                 scale, &width, &height);
636
637         if (job_info->surface &&
638             cairo_image_surface_get_width (job_info->surface) == width &&
639             cairo_image_surface_get_height (job_info->surface) == height)
640                 return;
641
642         add_job (pixbuf_cache, job_info, page_cache, NULL,
643                  width, height, page, rotation, scale,
644                  priority);
645 }
646
647 static void
648 ev_pixbuf_cache_add_jobs_if_needed (EvPixbufCache *pixbuf_cache,
649                                     gint           rotation,
650                                     gfloat         scale)
651 {
652         EvPageCache *page_cache;
653         CacheJobInfo *job_info;
654         int page;
655         int i;
656
657         page_cache = ev_page_cache_get (pixbuf_cache->document);
658
659         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
660                 job_info = (pixbuf_cache->job_list + i);
661                 page = pixbuf_cache->start_page + i;
662
663                 add_job_if_needed (pixbuf_cache, job_info,
664                                    page_cache, page, rotation, scale,
665                                    EV_JOB_PRIORITY_HIGH);
666         }
667
668         for (i = FIRST_VISABLE_PREV(pixbuf_cache); i < pixbuf_cache->preload_cache_size; i++) {
669                 job_info = (pixbuf_cache->prev_job + i);
670                 page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size + i;
671
672                 add_job_if_needed (pixbuf_cache, job_info,
673                                    page_cache, page, rotation, scale,
674                                    EV_JOB_PRIORITY_LOW);
675         }
676
677         for (i = 0; i < VISIBLE_NEXT_LEN(pixbuf_cache, page_cache); i++) {
678                 job_info = (pixbuf_cache->next_job + i);
679                 page = pixbuf_cache->end_page + 1 + i;
680
681                 add_job_if_needed (pixbuf_cache, job_info,
682                                    page_cache, page, rotation, scale,
683                                    EV_JOB_PRIORITY_LOW);
684         }
685
686 }
687
688 void
689 ev_pixbuf_cache_set_page_range (EvPixbufCache  *pixbuf_cache,
690                                 gint            start_page,
691                                 gint            end_page,
692                                 gint            rotation,
693                                 gfloat          scale,
694                                 GList          *selection_list)
695 {
696         EvPageCache *page_cache;
697
698         g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
699
700         page_cache = ev_page_cache_get (pixbuf_cache->document);
701
702         g_return_if_fail (start_page >= 0 && start_page < ev_page_cache_get_n_pages (page_cache));
703         g_return_if_fail (end_page >= 0 && end_page < ev_page_cache_get_n_pages (page_cache));
704         g_return_if_fail (end_page >= start_page);
705
706         /* First, resize the page_range as needed.  We cull old pages
707          * mercilessly. */
708         ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page);
709
710         /* Then, we update the current jobs to see if any of them are the wrong
711          * size, we remove them if we need to. */
712         ev_pixbuf_cache_clear_job_sizes (pixbuf_cache, scale);
713
714         /* Next, we update the target selection for our pages */
715         ev_pixbuf_cache_set_selection_list (pixbuf_cache, selection_list);
716
717         /* Finally, we add the new jobs for all the sizes that don't have a
718          * pixbuf */
719         ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, rotation, scale);
720 }
721
722 cairo_surface_t *
723 ev_pixbuf_cache_get_surface (EvPixbufCache *pixbuf_cache,
724                              gint           page)
725 {
726         CacheJobInfo *job_info;
727
728         job_info = find_job_cache (pixbuf_cache, page);
729         if (job_info == NULL)
730                 return NULL;
731
732         /* We don't need to wait for the idle to handle the callback */
733         if (job_info->job &&
734             EV_JOB (job_info->job)->finished) {
735                 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
736                 g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
737         }
738
739         return job_info->surface;
740 }
741
742 GList *
743 ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache,
744                                   gint           page)
745 {
746         CacheJobInfo *job_info;
747
748         job_info = find_job_cache (pixbuf_cache, page);
749         if (job_info == NULL)
750                 return NULL;
751
752         /* We don't need to wait for the idle to handle the callback */
753         if (job_info->job &&
754             EV_JOB (job_info->job)->finished) {
755                 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
756                 g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
757         }
758
759         return job_info->link_mapping;
760 }
761
762 GList *
763 ev_pixbuf_cache_get_image_mapping (EvPixbufCache *pixbuf_cache,
764                                    gint           page)
765 {
766         CacheJobInfo *job_info;
767
768         if (!EV_IS_DOCUMENT_IMAGES (pixbuf_cache->document))
769                 return NULL;
770         
771         job_info = find_job_cache (pixbuf_cache, page);
772         if (job_info == NULL)
773                 return NULL;
774
775         /* We don't need to wait for the idle to handle the callback */
776         if (job_info->job &&
777             EV_JOB (job_info->job)->finished) {
778                 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
779                 g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
780         }
781
782         return job_info->image_mapping;
783 }
784
785 GList *
786 ev_pixbuf_cache_get_form_field_mapping (EvPixbufCache *pixbuf_cache,
787                                         gint           page)
788 {
789         CacheJobInfo *job_info;
790
791         if (!EV_IS_DOCUMENT_FORMS (pixbuf_cache->document))
792                 return NULL;
793         
794         job_info = find_job_cache (pixbuf_cache, page);
795         if (job_info == NULL)
796                 return NULL;
797
798         /* We don't need to wait for the idle to handle the callback */
799         if (job_info->job &&
800            EV_JOB (job_info->job)->finished) {
801                 copy_job_to_job_info (EV_JOB_RENDER(job_info->job), job_info, pixbuf_cache);
802                 g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
803         }
804         
805         return job_info->form_field_mapping;
806 }
807
808 static gboolean
809 new_selection_surface_needed (EvPixbufCache *pixbuf_cache,
810                              CacheJobInfo  *job_info,
811                              gint           page,
812                              gfloat         scale)
813 {
814         EvPageCache *page_cache;
815
816         if (job_info->selection) {
817                 gint width, height;
818                 gint selection_width, selection_height;
819                 
820                 page_cache = ev_page_cache_get (pixbuf_cache->document);
821                 ev_page_cache_get_size (page_cache, page,
822                                         job_info->rc->rotation,
823                                         scale, &width, &height);
824
825                 selection_width = cairo_image_surface_get_width (job_info->selection);
826                 selection_height = cairo_image_surface_get_height (job_info->selection);
827                 
828                 if (width != selection_width || height != selection_height)
829                         return TRUE;
830         } else {
831                 if (job_info->points_set)
832                         return TRUE;
833         }
834         
835         return FALSE;
836 }
837
838 static void
839 clear_selection_if_needed (EvPixbufCache *pixbuf_cache,
840                            CacheJobInfo  *job_info,
841                            gint           page,
842                            gfloat         scale)
843 {
844         if (new_selection_surface_needed (pixbuf_cache, job_info, page, scale)) {
845                 if (job_info->selection)
846                         cairo_surface_destroy (job_info->selection);
847                 job_info->selection = NULL;
848                 job_info->selection_points.x1 = -1;
849         }
850 }
851
852 GdkRegion *
853 ev_pixbuf_cache_get_text_mapping (EvPixbufCache *pixbuf_cache,
854                                   gint           page)
855 {
856         CacheJobInfo *job_info;
857
858         job_info = find_job_cache (pixbuf_cache, page);
859         if (job_info == NULL)
860                 return NULL;
861
862         /* We don't need to wait for the idle to handle the callback */
863         if (job_info->job &&
864             EV_JOB (job_info->job)->finished) {
865                 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
866                 g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0, job_info->region);
867         }
868         
869         return job_info->text_mapping;
870 }
871
872 /* Clears the cache of jobs and pixbufs.
873  */
874 void
875 ev_pixbuf_cache_clear (EvPixbufCache *pixbuf_cache)
876 {
877         int i;
878
879         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
880                 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
881                 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
882         }
883
884         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
885                 dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
886         }
887 }
888
889
890 void
891 ev_pixbuf_cache_style_changed (EvPixbufCache *pixbuf_cache)
892 {
893         gint i;
894
895         /* FIXME: doesn't update running jobs. */
896         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
897                 CacheJobInfo *job_info;
898
899                 job_info = pixbuf_cache->prev_job + i;
900                 if (job_info->selection) {
901                         cairo_surface_destroy (job_info->selection);
902                         job_info->selection = NULL;
903                 }
904
905                 job_info = pixbuf_cache->next_job + i;
906                 if (job_info->selection) {
907                         cairo_surface_destroy (job_info->selection);
908                         job_info->selection = NULL;
909                 }
910         }
911
912         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
913                 CacheJobInfo *job_info;
914
915                 job_info = pixbuf_cache->job_list + i;
916                 if (job_info->selection) {
917                         cairo_surface_destroy (job_info->selection);
918                         job_info->selection = NULL;
919                 }
920         }
921 }
922
923 cairo_surface_t *
924 ev_pixbuf_cache_get_selection_surface (EvPixbufCache  *pixbuf_cache,
925                                        gint            page,
926                                        gfloat          scale,
927                                        GdkRegion     **region)
928 {
929         CacheJobInfo *job_info;
930
931         /* the document does not implement the selection interface */
932         if (!EV_IS_SELECTION (pixbuf_cache->document))
933                 return NULL;
934
935         job_info = find_job_cache (pixbuf_cache, page);
936         if (job_info == NULL)
937                 return NULL;
938
939         /* No selection on this page */
940         if (!job_info->points_set)
941                 return NULL;
942
943         /* Update the rc */
944         g_assert (job_info->rc);
945         ev_render_context_set_scale (job_info->rc, scale);
946
947         /* If we have a running job, we just return what we have under the
948          * assumption that it'll be updated later and we can scale it as need
949          * be */
950         if (job_info->job && EV_JOB_RENDER (job_info->job)->include_selection)
951                 return job_info->selection;
952
953         /* Now, lets see if we need to resize the image.  If we do, we clear the
954          * old one. */
955         clear_selection_if_needed (pixbuf_cache, job_info, page, scale);
956
957         /* Finally, we see if the two scales are the same, and get a new pixbuf
958          * if needed.  We do this synchronously for now.  At some point, we
959          * _should_ be able to get rid of the doc_mutex, so the synchronicity
960          * doesn't kill us.  Rendering a few glyphs should really be fast.
961          */
962         if (ev_rect_cmp (&(job_info->target_points), &(job_info->selection_points))) {
963                 EvRectangle *old_points;
964                 GdkColor *text, *base;
965
966                 /* we need to get a new selection pixbuf */
967                 ev_document_doc_mutex_lock ();
968                 if (job_info->selection_points.x1 < 0) {
969                         g_assert (job_info->selection == NULL);
970                         old_points = NULL;
971                 } else {
972                         g_assert (job_info->selection != NULL);
973                         old_points = &(job_info->selection_points);
974                 }
975
976                 if (job_info->selection_region)
977                         gdk_region_destroy (job_info->selection_region);
978                 job_info->selection_region =
979                         ev_selection_get_selection_region (EV_SELECTION (pixbuf_cache->document),
980                                                            job_info->rc,
981                                                            &(job_info->target_points));
982
983                 gtk_widget_ensure_style (pixbuf_cache->view);
984
985                 get_selection_colors (pixbuf_cache->view, &text, &base);
986
987                 ev_selection_render_selection (EV_SELECTION (pixbuf_cache->document),
988                                                job_info->rc, &(job_info->selection),
989                                                &(job_info->target_points),
990                                                old_points,
991                                                text, base);
992                 job_info->selection_points = job_info->target_points;
993                 ev_document_doc_mutex_unlock ();
994         }
995         if (region)
996                 *region = job_info->selection_region;
997         return job_info->selection;
998 }
999
1000 static void
1001 update_job_selection (CacheJobInfo    *job_info,
1002                       EvViewSelection *selection)
1003 {
1004         job_info->points_set = TRUE;            
1005         job_info->target_points = selection->rect;
1006 }
1007
1008 static void
1009 clear_job_selection (CacheJobInfo *job_info)
1010 {
1011         job_info->points_set = FALSE;
1012         job_info->selection_points.x1 = -1;
1013
1014         if (job_info->selection) {
1015                 cairo_surface_destroy (job_info->selection);
1016                 job_info->selection = NULL;
1017         }
1018 }
1019
1020 /* This function will reset the selection on pages that no longer have them, and
1021  * will update the target_selection on those that need it.  It will _not_ free
1022  * the previous selection_list -- that's up to caller to do.
1023  */
1024 void
1025 ev_pixbuf_cache_set_selection_list (EvPixbufCache *pixbuf_cache,
1026                                     GList         *selection_list)
1027 {
1028         EvPageCache *page_cache;
1029         EvViewSelection *selection;
1030         GList *list = selection_list;
1031         int page;
1032         int i;
1033
1034         g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
1035
1036         if (!EV_IS_SELECTION (pixbuf_cache->document))
1037                 return;
1038
1039         page_cache = ev_page_cache_get (pixbuf_cache->document);
1040
1041         /* We check each area to see what needs updating, and what needs freeing; */
1042         page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
1043         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1044                 if (page < 0) {
1045                         page ++;
1046                         continue;
1047                 }
1048
1049                 selection = NULL;
1050                 while (list) {
1051                         if (((EvViewSelection *)list->data)->page == page) {
1052                                 selection = list->data;
1053                                 break;
1054                         } else if (((EvViewSelection *)list->data)->page > page) 
1055                                 break;
1056                         list = list->next;
1057                 }
1058
1059                 if (selection)
1060                         update_job_selection (pixbuf_cache->prev_job + i, selection);
1061                 else
1062                         clear_job_selection (pixbuf_cache->prev_job + i);
1063                 page ++;
1064         }
1065
1066         page = pixbuf_cache->start_page;
1067         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
1068                 selection = NULL;
1069                 while (list) {
1070                         if (((EvViewSelection *)list->data)->page == page) {
1071                                 selection = list->data;
1072                                 break;
1073                         } else if (((EvViewSelection *)list->data)->page > page) 
1074                                 break;
1075                         list = list->next;
1076                 }
1077
1078                 if (selection)
1079                         update_job_selection (pixbuf_cache->job_list + i, selection);
1080                 else
1081                         clear_job_selection (pixbuf_cache->job_list + i);
1082                 page ++;
1083         }
1084
1085         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1086                 if (page >= ev_page_cache_get_n_pages (page_cache))
1087                         break;
1088
1089                 selection = NULL;
1090                 while (list) {
1091                         if (((EvViewSelection *)list->data)->page == page) {
1092                                 selection = list->data;
1093                                 break;
1094                         } else if (((EvViewSelection *)list->data)->page > page) 
1095                                 break;
1096                         list = list->next;
1097                 }
1098
1099                 if (selection)
1100                         update_job_selection (pixbuf_cache->next_job + i, selection);
1101                 else
1102                         clear_job_selection (pixbuf_cache->next_job + i);
1103                 page ++;
1104         }
1105 }
1106
1107
1108 /* Returns what the pixbuf cache thinks is */
1109
1110 GList *
1111 ev_pixbuf_cache_get_selection_list (EvPixbufCache *pixbuf_cache)
1112 {
1113         EvPageCache *page_cache;
1114         EvViewSelection *selection;
1115         GList *retval = NULL;
1116         int page;
1117         int i;
1118
1119         g_return_val_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache), NULL);
1120
1121         page_cache = ev_page_cache_get (pixbuf_cache->document);
1122
1123         /* We check each area to see what needs updating, and what needs freeing; */
1124         page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
1125         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1126                 if (page < 0) {
1127                         page ++;
1128                         continue;
1129                 }
1130
1131                 if (pixbuf_cache->prev_job[i].selection_points.x1 != -1) {
1132                         selection = g_new0 (EvViewSelection, 1);
1133                         selection->page = page;
1134                         selection->rect = pixbuf_cache->prev_job[i].selection_points;
1135                         if (pixbuf_cache->prev_job[i].selection_region)
1136                                 selection->covered_region = gdk_region_copy (pixbuf_cache->prev_job[i].selection_region);
1137                         retval = g_list_append (retval, selection);
1138                 }
1139                 
1140                 page ++;
1141         }
1142
1143         page = pixbuf_cache->start_page;
1144         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
1145                 if (pixbuf_cache->job_list[i].selection_points.x1 != -1) {
1146                         selection = g_new0 (EvViewSelection, 1);
1147                         selection->page = page;
1148                         selection->rect = pixbuf_cache->job_list[i].selection_points;
1149                         if (pixbuf_cache->job_list[i].selection_region)
1150                                 selection->covered_region = gdk_region_copy (pixbuf_cache->job_list[i].selection_region);
1151                         retval = g_list_append (retval, selection);
1152                 }
1153                 
1154                 page ++;
1155         }
1156
1157         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
1158                 if (page >= ev_page_cache_get_n_pages (page_cache))
1159                         break;
1160
1161                 if (pixbuf_cache->next_job[i].selection_points.x1 != -1) {
1162                         selection = g_new0 (EvViewSelection, 1);
1163                         selection->page = page;
1164                         selection->rect = pixbuf_cache->next_job[i].selection_points;
1165                         if (pixbuf_cache->next_job[i].selection_region)
1166                                 selection->covered_region = gdk_region_copy (pixbuf_cache->next_job[i].selection_region);
1167                         retval = g_list_append (retval, selection);
1168                 }
1169                 
1170                 page ++;
1171         }
1172
1173         return retval;
1174 }
1175
1176 void           
1177 ev_pixbuf_cache_reload_page (EvPixbufCache *pixbuf_cache,
1178                              GdkRegion     *region,
1179                              gint           page,
1180                              gint           rotation,
1181                              gdouble        scale)
1182 {
1183         CacheJobInfo *job_info;
1184         EvPageCache *page_cache;
1185         gint width, height;
1186
1187         job_info = find_job_cache (pixbuf_cache, page);
1188         if (job_info == NULL)
1189                 return;
1190         
1191         page_cache = ev_page_cache_get (pixbuf_cache->document);
1192         ev_page_cache_get_size (page_cache, page, rotation, scale,
1193                                 &width, &height);
1194
1195         add_job (pixbuf_cache, job_info, page_cache, region,
1196                  width, height, page, rotation, scale,
1197                  EV_JOB_PRIORITY_HIGH);
1198 }
1199
1200