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