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