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