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