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