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