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