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