]> www.fi.muni.cz Git - evince.git/blob - shell/ev-pixbuf-cache.c
58ba3bff51be64306214b0576282e115caf3d8b4
[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
6 typedef struct _CacheJobInfo
7 {
8         EvJob *job;
9         GdkPixbuf *pixbuf;
10         EvRenderContext *rc;
11         GList *link_mapping;
12
13         /* Selection info.  If the *_points structs are unset, we put -1 in x1.
14          * selection_points are the coordinates encapsulated in selection.
15          * new_points is the target selection size. */
16         EvRectangle selection_points;
17         GdkPixbuf *selection;
18         EvRectangle new_points;
19 } CacheJobInfo;
20
21 struct _EvPixbufCache
22 {
23         GObject parent;
24
25         EvDocument  *document;
26         int start_page;
27         int end_page;
28
29         /* preload_cache_size is the number of pages prior to the current
30          * visible area that we cache.  It's normally 1, but could be 2 in the
31          * case of twin pages.
32          */
33         int preload_cache_size;
34         CacheJobInfo *prev_job;
35         CacheJobInfo *job_list;
36         CacheJobInfo *next_job;
37 };
38
39 struct _EvPixbufCacheClass
40 {
41         GObjectClass parent_class;
42
43         void (* job_finished) (EvPixbufCache *pixbuf_cache);
44 };
45
46
47 enum
48 {
49         JOB_FINISHED,
50         N_SIGNALS,
51 };
52
53 static guint signals[N_SIGNALS] = {0, };
54
55 static void          ev_pixbuf_cache_init       (EvPixbufCache      *pixbuf_cache);
56 static void          ev_pixbuf_cache_class_init (EvPixbufCacheClass *pixbuf_cache);
57 static void          ev_pixbuf_cache_finalize   (GObject            *object);
58 static void          ev_pixbuf_cache_dispose    (GObject            *object);
59 static void          job_finished_cb            (EvJob              *job,
60                                                  EvPixbufCache      *pixbuf_cache);
61 static CacheJobInfo *find_job_cache             (EvPixbufCache      *pixbuf_cache,
62                                                  int                 page);
63 static void          copy_job_to_job_info       (EvJobRender        *job_render,
64                                                  CacheJobInfo       *job_info,
65                                                  EvPixbufCache      *pixbuf_cache);
66 static gboolean      new_selection_pixbuf_needed(EvPixbufCache *pixbuf_cache,
67                                                  CacheJobInfo  *job_info,
68                                                  gint           page,
69                                                  gfloat         scale);
70
71
72 /* These are used for iterating through the prev and next arrays */
73 #define FIRST_VISABLE_PREV(pixbuf_cache) \
74         (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page))
75 #define VISIBLE_NEXT_LEN(pixbuf_cache, page_cache) \
76         (MIN(pixbuf_cache->preload_cache_size, ev_page_cache_get_n_pages (page_cache) - (1 + pixbuf_cache->end_page)))
77 #define PAGE_CACHE_LEN(pixbuf_cache) \
78         ((pixbuf_cache->end_page - pixbuf_cache->start_page) + 1)
79
80 G_DEFINE_TYPE (EvPixbufCache, ev_pixbuf_cache, G_TYPE_OBJECT)
81
82 static void
83 ev_pixbuf_cache_init (EvPixbufCache *pixbuf_cache)
84 {
85         pixbuf_cache->start_page = 0;
86         pixbuf_cache->end_page = 0;
87         pixbuf_cache->job_list = g_new0 (CacheJobInfo, PAGE_CACHE_LEN (pixbuf_cache));
88
89         pixbuf_cache->preload_cache_size = 2;
90         pixbuf_cache->prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
91         pixbuf_cache->next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
92 }
93
94 static void
95 ev_pixbuf_cache_class_init (EvPixbufCacheClass *class)
96 {
97         GObjectClass *object_class;
98
99         object_class = G_OBJECT_CLASS (class);
100
101         object_class->finalize = ev_pixbuf_cache_finalize;
102         object_class->dispose = ev_pixbuf_cache_dispose;
103
104         signals[JOB_FINISHED] = g_signal_new ("job-finished",
105                                             G_OBJECT_CLASS_TYPE (object_class),
106                                             G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
107                                             G_STRUCT_OFFSET (EvPixbufCacheClass, job_finished),
108                                             NULL, NULL,
109                                             g_cclosure_marshal_VOID__VOID,
110                                             G_TYPE_NONE, 0);
111 }
112
113 static void
114 ev_pixbuf_cache_finalize (GObject *object)
115 {
116         EvPixbufCache *pixbuf_cache;
117
118         pixbuf_cache = EV_PIXBUF_CACHE (object);
119
120         g_free (pixbuf_cache->prev_job);
121         g_free (pixbuf_cache->job_list);
122         g_free (pixbuf_cache->next_job);
123 }
124
125 static void
126 dispose_cache_job_info (CacheJobInfo *job_info,
127                         gpointer      data)
128 {
129         if (job_info == NULL)
130                 return;
131         if (job_info->job) {
132                 g_signal_handlers_disconnect_by_func (job_info->job,
133                                                       G_CALLBACK (job_finished_cb),
134                                                       data);
135                 ev_job_queue_remove_job (job_info->job);
136                 g_object_unref (G_OBJECT (job_info->job));
137                 job_info->job = NULL;
138         }
139         if (job_info->pixbuf) {
140                 g_object_unref (G_OBJECT (job_info->pixbuf));
141                 job_info->pixbuf = NULL;
142         }
143         if (job_info->link_mapping) {
144                 ev_link_mapping_free (job_info->link_mapping);
145                 job_info->link_mapping = NULL;
146         }
147         if (job_info->selection) {
148                 g_object_unref (G_OBJECT (job_info->selection));
149                 job_info->selection = NULL;
150         }
151
152         job_info->selection_points.x1 = -1;
153         job_info->new_points.x1 = -1;
154 }
155
156 static void
157 ev_pixbuf_cache_dispose (GObject *object)
158 {
159         EvPixbufCache *pixbuf_cache;
160         int i;
161
162         pixbuf_cache = EV_PIXBUF_CACHE (object);
163
164         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
165                 dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
166                 dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
167         }
168
169         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
170                 dispose_cache_job_info (pixbuf_cache->job_list + i, pixbuf_cache);
171         }
172 }
173
174
175 EvPixbufCache *
176 ev_pixbuf_cache_new (EvDocument *document)
177 {
178         EvPixbufCache *pixbuf_cache;
179
180         pixbuf_cache = (EvPixbufCache *) g_object_new (EV_TYPE_PIXBUF_CACHE, NULL);
181         pixbuf_cache->document = document;
182
183         return pixbuf_cache;
184 }
185
186 static void
187 job_finished_cb (EvJob         *job,
188                  EvPixbufCache *pixbuf_cache)
189 {
190         CacheJobInfo *job_info;
191         EvJobRender *job_render = EV_JOB_RENDER (job);
192         GdkPixbuf *pixbuf;
193
194         /* If the job is outside of our interest, we silently discard it */
195         if ((job_render->rc->page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size)) ||
196             (job_render->rc->page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))) {
197                 g_object_unref (job);
198                 return;
199         }
200         
201         job_info = find_job_cache (pixbuf_cache, job_render->rc->page);
202
203         pixbuf = g_object_ref (job_render->pixbuf);
204         if (job_info->pixbuf)
205                 g_object_unref (job_info->pixbuf);
206         job_info->pixbuf = pixbuf;
207
208         if (job_render->link_mapping) {
209                 if (job_info->link_mapping)
210                         ev_link_mapping_free (job_info->link_mapping);
211                 job_info->link_mapping = job_render->link_mapping;
212         }
213         if (job_render->include_selection) {
214                 if (job_info->selection)
215                         g_object_unref (job_info->selection);
216                 job_info->selection_points = job_render->selection_points;
217                 job_info->selection = job_render->selection;
218                 g_assert (job_info->selection_points.x1 >= 0);
219         }
220         
221         if (job_info->job == job)
222                 job_info->job = NULL;
223         g_object_unref (job);
224
225         g_signal_emit (pixbuf_cache, signals[JOB_FINISHED], 0);
226 }
227
228 /* This checks a job to see if the job would generate the right sized pixbuf
229  * given a scale.  If it won't, it removes the job and clears it to NULL.
230  */
231 static void
232 check_job_size_and_unref (CacheJobInfo *job_info,
233                           EvPageCache  *page_cache,
234                           gfloat        scale)
235 {
236         gint width;
237         gint height;
238
239         g_assert (job_info);
240
241         if (job_info->job == NULL)
242                 return;
243
244         ev_page_cache_get_size (page_cache,
245                                 EV_JOB_RENDER (job_info->job)->rc->page,
246                                 scale,
247                                 &width, &height);
248                                 
249         if (width == EV_JOB_RENDER (job_info->job)->target_width &&
250             height == EV_JOB_RENDER (job_info->job)->target_height)
251                 return;
252
253         /* Try to remove the job.  If we can't, then the thread has already
254          * picked it up and we are going get a signal when it's done.  If we
255          * can, then the job is fully dead and will never rnu.. */
256         if (ev_job_queue_remove_job (job_info->job))
257                 g_object_unref (job_info->job);
258
259         job_info->job = NULL;
260 }
261
262 /* Do all function that copies a job from an older cache to it's position in the
263  * new cache.  It clears the old job if it doesn't have a place.
264  */
265 static void
266 move_one_job (CacheJobInfo  *job_info,
267               EvPixbufCache *pixbuf_cache,
268               int            page,
269               CacheJobInfo  *new_job_list,
270               CacheJobInfo  *new_prev_job,
271               CacheJobInfo  *new_next_job,
272               int            start_page,
273               int            end_page,
274               EvJobPriority  priority)
275 {
276         CacheJobInfo *target_page = NULL;
277         int page_offset;
278         EvJobPriority new_priority;
279
280         if (page < (start_page - pixbuf_cache->preload_cache_size) ||
281             page > (end_page + pixbuf_cache->preload_cache_size)) {
282                 dispose_cache_job_info (job_info, pixbuf_cache);
283                 return;
284         }
285
286         /* find the target page to copy it over to. */
287         if (page < start_page) {
288                 page_offset = (page - (start_page - pixbuf_cache->preload_cache_size));
289
290                 g_assert (page_offset >= 0 &&
291                           page_offset < pixbuf_cache->preload_cache_size);
292                 target_page = new_prev_job + page_offset;
293                 new_priority = EV_JOB_PRIORITY_LOW;
294         } else if (page > end_page) {
295                 page_offset = (page - (end_page + 1));
296
297                 g_assert (page_offset >= 0 &&
298                           page_offset < pixbuf_cache->preload_cache_size);
299                 target_page = new_next_job + page_offset;
300                 new_priority = EV_JOB_PRIORITY_LOW;
301         } else {
302                 page_offset = page - start_page;
303                 g_assert (page_offset >= 0 &&
304                           page_offset <= ((end_page - start_page) + 1));
305                 new_priority = EV_JOB_PRIORITY_HIGH;
306                 target_page = new_job_list + page_offset;
307         }
308
309         *target_page = *job_info;
310         job_info->job = NULL;
311         job_info->pixbuf = NULL;
312         job_info->link_mapping = NULL;
313
314         if (new_priority != priority && target_page->job) {
315                 ev_job_queue_update_job (target_page->job, new_priority);
316         }
317 }
318
319
320
321 static void
322 ev_pixbuf_cache_update_range (EvPixbufCache *pixbuf_cache,
323                               gint           start_page,
324                               gint           end_page)
325 {
326         CacheJobInfo *new_job_list;
327         CacheJobInfo *new_prev_job;
328         CacheJobInfo *new_next_job;
329         EvPageCache *page_cache;
330         int i, page;
331
332         if (pixbuf_cache->start_page == start_page &&
333             pixbuf_cache->end_page == end_page)
334                 return;
335
336         page_cache = ev_page_cache_get (pixbuf_cache->document);
337
338         new_job_list = g_new0 (CacheJobInfo, (end_page - start_page) + 1);
339         new_prev_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
340         new_next_job = g_new0 (CacheJobInfo, pixbuf_cache->preload_cache_size);
341
342         /* We go through each job in the old cache and either clear it or move
343          * it to a new location. */
344
345         /* Start with the prev cache. */
346         page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
347         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
348                 if (page < 0) {
349                         dispose_cache_job_info (pixbuf_cache->prev_job + i, pixbuf_cache);
350                 } else {
351                         move_one_job (pixbuf_cache->prev_job + i,
352                                       pixbuf_cache, page,
353                                       new_job_list, new_prev_job, new_next_job,
354                                       start_page, end_page, EV_JOB_PRIORITY_LOW);
355                 }
356                 page ++;
357         }
358
359         page = pixbuf_cache->start_page;
360         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
361                 move_one_job (pixbuf_cache->job_list + i,
362                               pixbuf_cache, page,
363                               new_job_list, new_prev_job, new_next_job,
364                               start_page, end_page, EV_JOB_PRIORITY_HIGH);
365                 page ++;
366         }
367
368         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
369                 if (page >= ev_page_cache_get_n_pages (page_cache)) {
370                         dispose_cache_job_info (pixbuf_cache->next_job + i, pixbuf_cache);
371                 } else {
372                         move_one_job (pixbuf_cache->next_job + i,
373                                       pixbuf_cache, page,
374                                       new_job_list, new_prev_job, new_next_job,
375                                       start_page, end_page, EV_JOB_PRIORITY_LOW);
376                 }
377                 page ++;
378         }
379
380         g_free (pixbuf_cache->job_list);
381         g_free (pixbuf_cache->prev_job);
382         g_free (pixbuf_cache->next_job);
383
384         pixbuf_cache->job_list = new_job_list;
385         pixbuf_cache->prev_job = new_prev_job;
386         pixbuf_cache->next_job = new_next_job;
387
388         pixbuf_cache->start_page = start_page;
389         pixbuf_cache->end_page = end_page;
390 }
391
392 static void
393 copy_job_to_job_info (EvJobRender   *job_render,
394                       CacheJobInfo  *job_info,
395                       EvPixbufCache *pixbuf_cache)
396 {
397         GdkPixbuf *pixbuf;
398
399         pixbuf = g_object_ref (job_render->pixbuf);
400
401         dispose_cache_job_info (job_info, pixbuf_cache);
402
403         job_info->pixbuf = pixbuf;
404         if (job_render->link_mapping)
405                 job_info->link_mapping = job_render->link_mapping;
406 }
407
408 static CacheJobInfo *
409 find_job_cache (EvPixbufCache *pixbuf_cache,
410                 int            page)
411 {
412         int page_offset;
413
414         if (page < (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size) ||
415             page > (pixbuf_cache->end_page + pixbuf_cache->preload_cache_size))
416                 return NULL;
417
418         if (page < pixbuf_cache->start_page) {
419                 page_offset = (page - (pixbuf_cache->start_page - pixbuf_cache->preload_cache_size));
420
421                 g_assert (page_offset >= 0 &&
422                           page_offset < pixbuf_cache->preload_cache_size);
423                 return pixbuf_cache->prev_job + page_offset;
424         }
425
426         if (page > pixbuf_cache->end_page) {
427                 page_offset = (page - (pixbuf_cache->end_page + 1));
428
429                 g_assert (page_offset >= 0 &&
430                           page_offset < pixbuf_cache->preload_cache_size);
431                 return pixbuf_cache->next_job + page_offset;
432         }
433
434         page_offset = page - pixbuf_cache->start_page;
435         g_assert (page_offset >= 0 &&
436                   page_offset <= PAGE_CACHE_LEN(pixbuf_cache));
437         return pixbuf_cache->job_list + page_offset;
438 }
439
440 static void
441 ev_pixbuf_cache_clear_job_sizes (EvPixbufCache *pixbuf_cache,
442                                  gfloat         scale)
443 {
444         EvPageCache *page_cache;
445         int i;
446
447         page_cache = ev_page_cache_get (pixbuf_cache->document);
448
449         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
450                 check_job_size_and_unref (pixbuf_cache->job_list + i, page_cache, scale);
451         }
452
453         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
454                 check_job_size_and_unref (pixbuf_cache->prev_job + i, page_cache, scale);
455                 check_job_size_and_unref (pixbuf_cache->next_job + i, page_cache, scale);
456         }
457 }
458
459 #define FIRST_VISABLE_PREV(pixbuf_cache) \
460         (MAX (0, pixbuf_cache->preload_cache_size + 1 - pixbuf_cache->start_page))
461
462 static void
463 add_job_if_needed (EvPixbufCache *pixbuf_cache,
464                    CacheJobInfo  *job_info,
465                    EvPageCache   *page_cache,
466                    gint           page,
467                    gfloat         scale,
468                    EvJobPriority  priority)
469 {
470         gboolean include_links = FALSE;
471         gboolean include_selection = FALSE;
472         int width, height;
473
474         if (job_info->job)
475                 return;
476
477         ev_page_cache_get_size (page_cache,
478                                 page, scale,
479                                 &width, &height);
480
481         if (job_info->pixbuf &&
482             gdk_pixbuf_get_width (job_info->pixbuf) == width &&
483             gdk_pixbuf_get_height (job_info->pixbuf) == height)
484                 return;
485
486         /* make a new job now */
487         if (job_info->rc == NULL) {
488                 job_info->rc = ev_render_context_new (EV_ORIENTATION_PORTRAIT,
489                                                       page, scale);
490         } else {
491                 ev_render_context_set_page (job_info->rc, page);
492                 ev_render_context_set_scale (job_info->rc, scale);
493         }
494
495         /* Figure out what else we need for this job */
496         if (job_info->link_mapping == NULL)
497                 include_links = TRUE;
498         if (new_selection_pixbuf_needed (pixbuf_cache, job_info, page, scale)) {
499                 include_selection = TRUE;
500         }
501
502         job_info->job = ev_job_render_new (pixbuf_cache->document,
503                                            job_info->rc,
504                                            width, height,
505                                            &(job_info->new_points),
506                                            include_links,
507                                            include_selection);
508         ev_job_queue_add_job (job_info->job, priority);
509         g_signal_connect (job_info->job, "finished", G_CALLBACK (job_finished_cb), pixbuf_cache);
510 }
511
512
513 static void
514 ev_pixbuf_cache_add_jobs_if_needed (EvPixbufCache *pixbuf_cache,
515                                     gfloat         scale)
516 {
517         EvPageCache *page_cache;
518         CacheJobInfo *job_info;
519         int page;
520         int i;
521
522         page_cache = ev_page_cache_get (pixbuf_cache->document);
523
524         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
525                 job_info = (pixbuf_cache->job_list + i);
526                 page = pixbuf_cache->start_page + i;
527
528                 add_job_if_needed (pixbuf_cache, job_info,
529                                    page_cache, page, scale,
530                                    EV_JOB_PRIORITY_HIGH);
531         }
532
533         for (i = FIRST_VISABLE_PREV(pixbuf_cache); i < pixbuf_cache->preload_cache_size; i++) {
534                 job_info = (pixbuf_cache->prev_job + i);
535                 page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size + i;
536
537                 add_job_if_needed (pixbuf_cache, job_info,
538                                    page_cache, page, scale,
539                                    EV_JOB_PRIORITY_LOW);
540         }
541
542         for (i = 0; i < VISIBLE_NEXT_LEN(pixbuf_cache, page_cache); i++) {
543                 job_info = (pixbuf_cache->next_job + i);
544                 page = pixbuf_cache->end_page + 1 + i;
545
546                 add_job_if_needed (pixbuf_cache, job_info,
547                                    page_cache, page, scale,
548                                    EV_JOB_PRIORITY_LOW);
549         }
550
551 }
552
553 void
554 ev_pixbuf_cache_set_page_range (EvPixbufCache *pixbuf_cache,
555                                 gint           start_page,
556                                 gint           end_page,
557                                 gfloat         scale,
558                                 GList          *selection_list)
559 {
560         EvPageCache *page_cache;
561
562         g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
563
564         page_cache = ev_page_cache_get (pixbuf_cache->document);
565
566         g_return_if_fail (start_page >= 0 && start_page < ev_page_cache_get_n_pages (page_cache));
567         g_return_if_fail (end_page >= 0 && end_page < ev_page_cache_get_n_pages (page_cache));
568         g_return_if_fail (end_page >= start_page);
569
570         /* First, resize the page_range as needed.  We cull old pages
571          * mercilessly. */
572         ev_pixbuf_cache_update_range (pixbuf_cache, start_page, end_page);
573
574         /* Then, we update the current jobs to see if any of them are the wrong
575          * size, we remove them if we need to. */
576         ev_pixbuf_cache_clear_job_sizes (pixbuf_cache, scale);
577
578         /* Next, we update the target selection for our pages */
579         ev_pixbuf_cache_set_selection_list (pixbuf_cache, selection_list);
580
581         /* Finally, we add the new jobs for all the sizes that don't have a
582          * pixbuf */
583         ev_pixbuf_cache_add_jobs_if_needed (pixbuf_cache, scale);
584 }
585
586 GdkPixbuf *
587 ev_pixbuf_cache_get_pixbuf (EvPixbufCache *pixbuf_cache,
588                             gint           page)
589 {
590         CacheJobInfo *job_info;
591
592         job_info = find_job_cache (pixbuf_cache, page);
593         if (job_info == NULL)
594                 return NULL;
595
596         /* We don't need to wait for the idle to handle the callback */
597         if (job_info->job &&
598             EV_JOB (job_info->job)->finished) {
599                 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
600         }
601
602         return job_info->pixbuf;
603 }
604
605 GList *
606 ev_pixbuf_cache_get_link_mapping (EvPixbufCache *pixbuf_cache,
607                                   gint           page)
608 {
609         CacheJobInfo *job_info;
610
611         job_info = find_job_cache (pixbuf_cache, page);
612         if (job_info == NULL)
613                 return NULL;
614
615         /* We don't need to wait for the idle to handle the callback */
616         if (job_info->job &&
617             EV_JOB (job_info->job)->finished) {
618                 copy_job_to_job_info (EV_JOB_RENDER (job_info->job), job_info, pixbuf_cache);
619         }
620         
621         return job_info->link_mapping;
622 }
623
624 /* Selection */
625 static gboolean
626 new_selection_pixbuf_needed (EvPixbufCache *pixbuf_cache,
627                              CacheJobInfo  *job_info,
628                              gint           page,
629                              gfloat         scale)
630 {
631         EvPageCache *page_cache;
632         gint width, height;
633
634         if (job_info->selection) {
635                 page_cache = ev_page_cache_get (pixbuf_cache->document);
636                 ev_page_cache_get_size (page_cache, page, scale,
637                                         &width, &height);
638                 
639                 if (width != gdk_pixbuf_get_width (job_info->selection) ||
640                     height != gdk_pixbuf_get_height (job_info->selection))
641                         return TRUE;
642         } else {
643                 if (job_info->new_points.x1 >= 0)
644                         return TRUE;
645         }
646         return FALSE;
647 }
648
649 static void
650 clear_selection_if_needed (EvPixbufCache *pixbuf_cache,
651                            CacheJobInfo  *job_info,
652                            gint           page,
653                            gfloat         scale)
654 {
655         if (new_selection_pixbuf_needed (pixbuf_cache, job_info, page, scale)) {
656                 if (job_info->selection)
657                         g_object_unref (job_info->selection);
658                 job_info->selection = NULL;
659                 job_info->selection_points.x1 = -1;
660         }
661 }
662
663 GList *
664 ev_pixbuf_cach_get_text_mapping      (EvPixbufCache *pixbuf_cache,
665                                       gint           page)
666 {
667         return NULL;
668 }
669
670 GdkPixbuf *
671 ev_pixbuf_cache_get_selection_pixbuf (EvPixbufCache *pixbuf_cache,
672                                       gint           page,
673                                       gfloat         scale)
674 {
675         CacheJobInfo *job_info;
676
677         job_info = find_job_cache (pixbuf_cache, page);
678         if (job_info == NULL)
679                 return NULL;
680
681         /* No selection on this page */
682         if (job_info->new_points.x1 < 0)
683                 return NULL;
684
685         /* If we have a running job, we just return what we have under the
686          * assumption that it'll be updated later and we can scale it as need
687          * be */
688         if (job_info->job && EV_JOB_RENDER (job_info->job)->include_selection)
689                 return job_info->selection;
690
691         /* Now, lets see if we need to resize the image.  If we do, we clear the
692          * old one. */
693         clear_selection_if_needed (pixbuf_cache, job_info, page, scale);
694
695         /* Finally, we see if the two scales are the same, and get a new pixbuf
696          * if needed.  We do this synchronously for now.  At some point, we
697          * _should_ be able to get rid of the doc_mutex, so the synchronicity
698          * doesn't kill us.  Rendering a few glyphs should really be fast.
699          */
700         if (ev_rect_cmp (&(job_info->new_points), &(job_info->selection_points))) {
701                 EvRenderContext *rc;
702
703                 rc = ev_render_context_new (EV_ORIENTATION_PORTRAIT,
704                                             page,
705                                             scale);
706
707                 /* we need to get a new selection pixbuf */
708                 ev_document_doc_mutex_lock ();
709                 if (job_info->selection_points.x1 < 0) {
710                         g_assert (job_info->selection == NULL);
711                         ev_selection_render_selection (EV_SELECTION (pixbuf_cache->document),
712                                                        rc, &(job_info->selection),
713                                                        &(job_info->new_points),
714                                                        NULL);
715                 } else {
716                         g_assert (job_info->selection != NULL);
717                         ev_selection_render_selection (EV_SELECTION (pixbuf_cache->document),
718                                                        rc, &(job_info->selection),
719                                                        &(job_info->new_points),
720                                                        &(job_info->selection_points));
721                 }
722                 job_info->selection_points = job_info->new_points;
723                 ev_document_doc_mutex_unlock ();
724
725         }
726         return job_info->selection;
727 }
728
729
730 static void
731 update_job_selection (CacheJobInfo    *job_info,
732                       EvViewSelection *selection)
733 {
734         if (job_info->selection == NULL)
735                 job_info->selection_points.x1 = -1;
736         job_info->new_points = selection->rect;
737 }
738
739 static void
740 clear_job_selection (CacheJobInfo *job_info)
741 {
742         job_info->selection_points.x1 = -1;
743         job_info->new_points.x1 = -1;
744
745         if (job_info->selection) {
746                 g_object_unref (job_info->selection);
747                 job_info->selection = NULL;
748         }
749 }
750
751 /* This function will reset the selection on pages that no longer have them, and
752  * will update the target_selection on those that need it.
753  */
754 void
755 ev_pixbuf_cache_set_selection_list (EvPixbufCache *pixbuf_cache,
756                                     GList         *selection_list)
757 {
758         EvPageCache *page_cache;
759         EvViewSelection *selection;
760         GList *list = selection_list;
761         int page;
762         int i;
763
764         g_return_if_fail (EV_IS_PIXBUF_CACHE (pixbuf_cache));
765
766         page_cache = ev_page_cache_get (pixbuf_cache->document);
767
768         /* We check each area to see what needs updating, and what needs freeing; */
769         page = pixbuf_cache->start_page - pixbuf_cache->preload_cache_size;
770         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
771                 if (page < 0) {
772                         page ++;
773                         continue;
774                 }
775
776                 selection = NULL;
777                 while (list) {
778                         if (((EvViewSelection *)list->data)->page == page) {
779                                 selection = list->data;
780                                 break;
781                         } else if (((EvViewSelection *)list->data)->page > page) 
782                                 break;
783                         list = list->next;
784                 }
785
786                 if (selection)
787                         update_job_selection (pixbuf_cache->prev_job + i, selection);
788                 else
789                         clear_job_selection (pixbuf_cache->prev_job + i);
790                 page ++;
791         }
792
793         page = pixbuf_cache->start_page;
794         for (i = 0; i < PAGE_CACHE_LEN (pixbuf_cache); i++) {
795                 selection = NULL;
796                 while (list) {
797                         if (((EvViewSelection *)list->data)->page == page) {
798                                 selection = list->data;
799                                 break;
800                         } else if (((EvViewSelection *)list->data)->page > page) 
801                                 break;
802                         list = list->next;
803                 }
804
805                 if (selection)
806                         update_job_selection (pixbuf_cache->job_list + i, selection);
807                 else
808                         clear_job_selection (pixbuf_cache->job_list + i);
809                 page ++;
810         }
811
812         for (i = 0; i < pixbuf_cache->preload_cache_size; i++) {
813                 if (page >= ev_page_cache_get_n_pages (page_cache))
814                         break;
815                 
816                 selection = NULL;
817                 while (list) {
818                         if (((EvViewSelection *)list->data)->page == page) {
819                                 selection = list->data;
820                                 break;
821                         } else if (((EvViewSelection *)list->data)->page > page) 
822                                 break;
823                         list = list->next;
824                 }
825
826                 if (selection)
827                         update_job_selection (pixbuf_cache->next_job + i, selection);
828                 else
829                         clear_job_selection (pixbuf_cache->next_job + i);
830                 page ++;
831         }
832 }