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