]> www.fi.muni.cz Git - evince.git/blob - libview/ev-jobs.c
102cd9083d83e982b8f8eab541d531e69b1941a3
[evince.git] / libview / ev-jobs.c
1 /* this file is part of evince, a gnome document viewer
2  *
3  *  Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
4  *  Copyright (C) 2005 Red Hat, Inc
5  *
6  * Evince is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Evince is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22
23 #include "ev-jobs.h"
24 #include "ev-document-thumbnails.h"
25 #include "ev-document-links.h"
26 #include "ev-document-images.h"
27 #include "ev-document-forms.h"
28 #include "ev-file-exporter.h"
29 #include "ev-document-factory.h"
30 #include "ev-document-misc.h"
31 #include "ev-file-helpers.h"
32 #include "ev-document-fonts.h"
33 #include "ev-document-security.h"
34 #include "ev-document-find.h"
35 #include "ev-document-layers.h"
36 #include "ev-debug.h"
37
38 #include <errno.h>
39 #include <glib/gstdio.h>
40 #include <glib/gi18n.h>
41 #include <unistd.h>
42
43 static void ev_job_init                   (EvJob                 *job);
44 static void ev_job_class_init             (EvJobClass            *class);
45 static void ev_job_links_init             (EvJobLinks            *job);
46 static void ev_job_links_class_init       (EvJobLinksClass       *class);
47 static void ev_job_attachments_init       (EvJobAttachments      *job);
48 static void ev_job_attachments_class_init (EvJobAttachmentsClass *class);
49 static void ev_job_render_init            (EvJobRender           *job);
50 static void ev_job_render_class_init      (EvJobRenderClass      *class);
51 static void ev_job_thumbnail_init         (EvJobThumbnail        *job);
52 static void ev_job_thumbnail_class_init   (EvJobThumbnailClass   *class);
53 static void ev_job_load_init              (EvJobLoad             *job);
54 static void ev_job_load_class_init        (EvJobLoadClass        *class);
55 static void ev_job_save_init              (EvJobSave             *job);
56 static void ev_job_save_class_init        (EvJobSaveClass        *class);
57 static void ev_job_find_init              (EvJobFind             *job);
58 static void ev_job_find_class_init        (EvJobFindClass        *class);
59 static void ev_job_layers_init            (EvJobLayers           *job);
60 static void ev_job_layers_class_init      (EvJobLayersClass      *class);
61 static void ev_job_export_init            (EvJobExport           *job);
62 static void ev_job_export_class_init      (EvJobExportClass      *class);
63
64 enum {
65         CANCELLED,
66         FINISHED,
67         LAST_SIGNAL
68 };
69
70 enum {
71         PAGE_READY,
72         RENDER_LAST_SIGNAL
73 };
74
75 enum {
76         FONTS_UPDATED,
77         FONTS_LAST_SIGNAL
78 };
79
80 enum {
81         FIND_UPDATED,
82         FIND_LAST_SIGNAL
83 };
84
85 static guint job_signals[LAST_SIGNAL] = { 0 };
86 static guint job_render_signals[RENDER_LAST_SIGNAL] = { 0 };
87 static guint job_fonts_signals[FONTS_LAST_SIGNAL] = { 0 };
88 static guint job_find_signals[FIND_LAST_SIGNAL] = { 0 };
89
90 G_DEFINE_ABSTRACT_TYPE (EvJob, ev_job, G_TYPE_OBJECT)
91 G_DEFINE_TYPE (EvJobLinks, ev_job_links, EV_TYPE_JOB)
92 G_DEFINE_TYPE (EvJobAttachments, ev_job_attachments, EV_TYPE_JOB)
93 G_DEFINE_TYPE (EvJobRender, ev_job_render, EV_TYPE_JOB)
94 G_DEFINE_TYPE (EvJobThumbnail, ev_job_thumbnail, EV_TYPE_JOB)
95 G_DEFINE_TYPE (EvJobFonts, ev_job_fonts, EV_TYPE_JOB)
96 G_DEFINE_TYPE (EvJobLoad, ev_job_load, EV_TYPE_JOB)
97 G_DEFINE_TYPE (EvJobSave, ev_job_save, EV_TYPE_JOB)
98 G_DEFINE_TYPE (EvJobFind, ev_job_find, EV_TYPE_JOB)
99 G_DEFINE_TYPE (EvJobLayers, ev_job_layers, EV_TYPE_JOB)
100 G_DEFINE_TYPE (EvJobExport, ev_job_export, EV_TYPE_JOB)
101
102 /* EvJob */
103 static void
104 ev_job_init (EvJob *job)
105 {
106         job->cancellable = g_cancellable_new ();
107 }
108
109 static void
110 ev_job_dispose (GObject *object)
111 {
112         EvJob *job;
113
114         job = EV_JOB (object);
115
116         if (job->document) {
117                 g_object_unref (job->document);
118                 job->document = NULL;
119         }
120
121         if (job->cancellable) {
122                 g_object_unref (job->cancellable);
123                 job->cancellable = NULL;
124         }
125
126         if (job->error) {
127                 g_error_free (job->error);
128                 job->error = NULL;
129         }
130
131         (* G_OBJECT_CLASS (ev_job_parent_class)->dispose) (object);
132 }
133
134 static void
135 ev_job_class_init (EvJobClass *class)
136 {
137         GObjectClass *oclass;
138
139         oclass = G_OBJECT_CLASS (class);
140
141         oclass->dispose = ev_job_dispose;
142
143         job_signals[CANCELLED] =
144                 g_signal_new ("cancelled",
145                               EV_TYPE_JOB,
146                               G_SIGNAL_RUN_LAST,
147                               G_STRUCT_OFFSET (EvJobClass, cancelled),
148                               NULL, NULL,
149                               g_cclosure_marshal_VOID__VOID,
150                               G_TYPE_NONE, 0);
151         job_signals [FINISHED] =
152                 g_signal_new ("finished",
153                               EV_TYPE_JOB,
154                               G_SIGNAL_RUN_FIRST,
155                               G_STRUCT_OFFSET (EvJobClass, finished),
156                               NULL, NULL,
157                               g_cclosure_marshal_VOID__VOID,
158                               G_TYPE_NONE, 0);
159 }
160
161 static gboolean
162 emit_finished (EvJob *job)
163 {
164         ev_debug_message (DEBUG_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
165
166         job->idle_finished_id = 0;
167         
168         if (job->cancelled) {
169                 ev_debug_message (DEBUG_JOBS, "%s (%p) job was cancelled, do not emit finished", EV_GET_TYPE_NAME (job), job);
170         } else {
171                 ev_profiler_stop (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
172                 g_signal_emit (job, job_signals[FINISHED], 0);
173         }
174         
175         return FALSE;
176 }
177
178 static void
179 ev_job_emit_finished (EvJob *job)
180 {
181         ev_debug_message (DEBUG_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
182
183         if (g_cancellable_is_cancelled (job->cancellable)) {
184                 ev_debug_message (DEBUG_JOBS, "%s (%p) job was cancelled, returning", EV_GET_TYPE_NAME (job), job);
185                 return;
186         }
187         
188         job->finished = TRUE;
189         
190         if (job->run_mode == EV_JOB_RUN_THREAD) {
191                 job->idle_finished_id =
192                         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
193                                          (GSourceFunc)emit_finished,
194                                          g_object_ref (job),
195                                          (GDestroyNotify)g_object_unref);
196         } else {
197                 ev_profiler_stop (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
198                 g_signal_emit (job, job_signals[FINISHED], 0);
199         }
200 }
201
202 gboolean
203 ev_job_run (EvJob *job)
204 {
205         EvJobClass *class = EV_JOB_GET_CLASS (job);
206         
207         return class->run (job);
208 }
209
210 void
211 ev_job_cancel (EvJob *job)
212 {
213         if (job->cancelled || (job->finished && job->idle_finished_id == 0))
214                 return;
215
216         ev_debug_message (DEBUG_JOBS, "job %s (%p) cancelled", EV_GET_TYPE_NAME (job), job);
217         ev_profiler_stop (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
218         
219         /* This should never be called from a thread */
220         job->cancelled = TRUE;
221         g_cancellable_cancel (job->cancellable);
222         g_signal_emit (job, job_signals[CANCELLED], 0);
223 }
224
225 void
226 ev_job_failed (EvJob       *job,
227                GQuark       domain,
228                gint         code,
229                const gchar *format,
230                ...)
231 {
232         va_list args;
233         gchar  *message;
234         
235         if (job->failed || job->finished)
236                 return;
237
238         ev_debug_message (DEBUG_JOBS, "job %s (%p) failed", EV_GET_TYPE_NAME (job), job);
239         
240         job->failed = TRUE;
241         
242         va_start (args, format);
243         message = g_strdup_vprintf (format, args);
244         va_end (args);
245         
246         job->error = g_error_new_literal (domain, code, message);
247         g_free (message);
248         
249         ev_job_emit_finished (job);                                                                                                               
250 }
251
252 void
253 ev_job_failed_from_error (EvJob  *job,
254                           GError *error)
255 {
256         if (job->failed || job->finished)
257                 return;
258         
259         ev_debug_message (DEBUG_JOBS, "job %s (%p) failed", EV_GET_TYPE_NAME (job), job);
260
261         job->failed = TRUE;
262         job->error = g_error_copy (error);
263
264         ev_job_emit_finished (job);
265 }
266
267 void
268 ev_job_succeeded (EvJob *job)
269 {
270         if (job->finished)
271                 return;
272
273         ev_debug_message (DEBUG_JOBS, "job %s (%p) succeeded", EV_GET_TYPE_NAME (job), job);
274         
275         job->failed = FALSE;
276         ev_job_emit_finished (job);
277 }
278
279 gboolean
280 ev_job_is_finished (EvJob *job)
281 {
282         return job->finished;
283 }
284
285 gboolean
286 ev_job_is_failed (EvJob *job)
287 {
288         return job->failed;
289 }
290
291 EvJobRunMode
292 ev_job_get_run_mode (EvJob *job)
293 {
294         return job->run_mode;
295 }
296
297 void
298 ev_job_set_run_mode (EvJob       *job,
299                      EvJobRunMode run_mode)
300 {
301         job->run_mode = run_mode;
302 }
303
304 /* EvJobLinks */
305 static void
306 ev_job_links_init (EvJobLinks *job)
307 {
308         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
309 }
310
311 static void
312 ev_job_links_dispose (GObject *object)
313 {
314         EvJobLinks *job;
315
316         ev_debug_message (DEBUG_JOBS, NULL);
317         
318         job = EV_JOB_LINKS (object);
319
320         if (job->model) {
321                 g_object_unref (job->model);
322                 job->model = NULL;
323         }
324
325         (* G_OBJECT_CLASS (ev_job_links_parent_class)->dispose) (object);
326 }
327
328 static gboolean
329 ev_job_links_run (EvJob *job)
330 {
331         EvJobLinks *job_links = EV_JOB_LINKS (job);
332
333         ev_debug_message (DEBUG_JOBS, NULL);
334         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
335         
336         ev_document_doc_mutex_lock ();
337         job_links->model = ev_document_links_get_links_model (EV_DOCUMENT_LINKS (job->document));
338         ev_document_doc_mutex_unlock ();
339         
340         ev_job_succeeded (job);
341         
342         return FALSE;
343 }
344
345 static void
346 ev_job_links_class_init (EvJobLinksClass *class)
347 {
348         GObjectClass *oclass = G_OBJECT_CLASS (class);
349         EvJobClass   *job_class = EV_JOB_CLASS (class);
350
351         oclass->dispose = ev_job_links_dispose;
352         job_class->run = ev_job_links_run;
353 }
354
355 EvJob *
356 ev_job_links_new (EvDocument *document)
357 {
358         EvJob *job;
359
360         ev_debug_message (DEBUG_JOBS, NULL);
361
362         job = g_object_new (EV_TYPE_JOB_LINKS, NULL);
363         job->document = g_object_ref (document);
364         
365         return job;
366 }
367
368 /* EvJobAttachments */
369 static void
370 ev_job_attachments_init (EvJobAttachments *job)
371 {
372         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
373 }
374
375 static void
376 ev_job_attachments_dispose (GObject *object)
377 {
378         EvJobAttachments *job;
379
380         ev_debug_message (DEBUG_JOBS, NULL);
381         
382         job = EV_JOB_ATTACHMENTS (object);
383
384         if (job->attachments) {
385                 g_list_foreach (job->attachments, (GFunc)g_object_unref, NULL);
386                 g_list_free (job->attachments);
387                 job->attachments = NULL;
388         }
389
390         (* G_OBJECT_CLASS (ev_job_attachments_parent_class)->dispose) (object);
391 }
392
393 static gboolean
394 ev_job_attachments_run (EvJob *job)
395 {
396         EvJobAttachments *job_attachments = EV_JOB_ATTACHMENTS (job);
397
398         ev_debug_message (DEBUG_JOBS, NULL);
399         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
400         
401         ev_document_doc_mutex_lock ();
402         job_attachments->attachments = ev_document_get_attachments (job->document);
403         ev_document_doc_mutex_unlock ();
404         
405         ev_job_succeeded (job);
406         
407         return FALSE;
408 }
409
410 static void
411 ev_job_attachments_class_init (EvJobAttachmentsClass *class)
412 {
413         GObjectClass *oclass = G_OBJECT_CLASS (class);
414         EvJobClass   *job_class = EV_JOB_CLASS (class);
415
416         oclass->dispose = ev_job_attachments_dispose;
417         job_class->run = ev_job_attachments_run;
418 }
419
420 EvJob *
421 ev_job_attachments_new (EvDocument *document)
422 {
423         EvJob *job;
424
425         ev_debug_message (DEBUG_JOBS, NULL);
426
427         job = g_object_new (EV_TYPE_JOB_ATTACHMENTS, NULL);
428         job->document = g_object_ref (document);
429         
430         return job;
431 }
432
433 /* EvJobRender */
434 static void
435 ev_job_render_init (EvJobRender *job)
436 {
437         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
438 }
439
440 static void
441 ev_job_render_dispose (GObject *object)
442 {
443         EvJobRender *job;
444
445         job = EV_JOB_RENDER (object);
446
447         if (job->ev_page) {
448                 ev_debug_message (DEBUG_JOBS, "page: %d (%p)", job->ev_page->index, job);
449                 g_object_unref (job->ev_page);
450                 job->ev_page = NULL;
451         }
452         
453         if (job->surface) {
454                 cairo_surface_destroy (job->surface);
455                 job->surface = NULL;
456         }
457
458         if (job->selection) {
459                 cairo_surface_destroy (job->selection);
460                 job->selection = NULL;
461         }
462
463         if (job->selection_region) {
464                 gdk_region_destroy (job->selection_region);
465                 job->selection_region = NULL;
466         }
467
468         (* G_OBJECT_CLASS (ev_job_render_parent_class)->dispose) (object);
469 }
470
471 static gboolean
472 notify_page_ready (EvJobRender *job)
473 {
474         ev_debug_message (DEBUG_JOBS, "%d (%p)", job->ev_page->index, job);
475         ev_profiler_stop (EV_PROFILE_JOBS, "Rendering page %d", job->ev_page->index);
476
477         if (EV_JOB (job)->cancelled) {
478                 ev_debug_message (DEBUG_JOBS, "%s (%p) job was cancelled, do not emit page_ready", EV_GET_TYPE_NAME (job), job);
479         } else {
480                 g_signal_emit (job, job_render_signals[PAGE_READY], 0);
481         }
482
483         return FALSE;
484 }
485
486 static void
487 ev_job_render_page_ready (EvJobRender *job)
488 {
489         ev_debug_message (DEBUG_JOBS, "%d (%p)", job->ev_page->index, job);
490         
491         job->page_ready = TRUE;
492         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
493                          (GSourceFunc)notify_page_ready,
494                          g_object_ref (job),
495                          (GDestroyNotify)g_object_unref);
496 }
497
498 static gboolean
499 ev_job_render_run (EvJob *job)
500 {
501         EvJobRender     *job_render = EV_JOB_RENDER (job);
502         EvRenderContext *rc;
503
504         ev_debug_message (DEBUG_JOBS, "page: %d (%p)", job_render->page, job);
505         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
506         
507         ev_document_doc_mutex_lock ();
508
509         ev_profiler_start (EV_PROFILE_JOBS, "Rendering page %d", job_render->page);
510                 
511         ev_document_fc_mutex_lock ();
512
513         job_render->ev_page = ev_document_get_page (job->document, job_render->page);
514         rc = ev_render_context_new (job_render->ev_page, job_render->rotation, job_render->scale);
515                 
516         job_render->surface = ev_document_render (job->document, rc);
517         /* If job was cancelled during the page rendering,
518          * we return now, so that the thread is finished ASAP
519          */
520         if (g_cancellable_is_cancelled (job->cancellable)) {
521                 ev_document_fc_mutex_unlock ();
522                 ev_document_doc_mutex_unlock ();
523                 g_object_unref (rc);
524
525                 return FALSE;
526         }
527         
528         if ((job_render->flags & EV_RENDER_INCLUDE_SELECTION) && EV_IS_SELECTION (job->document)) {
529                 ev_selection_render_selection (EV_SELECTION (job->document),
530                                                rc,
531                                                &(job_render->selection),
532                                                &(job_render->selection_points),
533                                                NULL,
534                                                job_render->selection_style,
535                                                &(job_render->text), &(job_render->base));
536                 job_render->selection_region =
537                         ev_selection_get_selection_region (EV_SELECTION (job->document),
538                                                            rc,
539                                                            job_render->selection_style,
540                                                            &(job_render->selection_points));
541         }
542
543         ev_job_render_page_ready (job_render);
544                 
545         ev_document_fc_mutex_unlock ();
546                 
547         if ((job_render->flags & EV_RENDER_INCLUDE_TEXT) && EV_IS_SELECTION (job->document))
548                 job_render->text_mapping =
549                         ev_selection_get_selection_map (EV_SELECTION (job->document), rc);
550         if ((job_render->flags & EV_RENDER_INCLUDE_LINKS) && EV_IS_DOCUMENT_LINKS (job->document))
551                 job_render->link_mapping =
552                         ev_document_links_get_links (EV_DOCUMENT_LINKS (job->document), job_render->page);
553         if ((job_render->flags & EV_RENDER_INCLUDE_FORMS) && EV_IS_DOCUMENT_FORMS (job->document))
554                 job_render->form_field_mapping =
555                         ev_document_forms_get_form_fields (EV_DOCUMENT_FORMS (job->document),
556                                                            job_render->ev_page);
557         if ((job_render->flags & EV_RENDER_INCLUDE_IMAGES) && EV_IS_DOCUMENT_IMAGES (job->document))
558                 job_render->image_mapping =
559                         ev_document_images_get_image_mapping (EV_DOCUMENT_IMAGES (job->document),
560                                                               job_render->page);
561         g_object_unref (rc);
562         ev_document_doc_mutex_unlock ();
563         
564         ev_job_succeeded (job);
565         
566         return FALSE;
567 }
568
569 static void
570 ev_job_render_class_init (EvJobRenderClass *class)
571 {
572         GObjectClass *oclass = G_OBJECT_CLASS (class);
573         EvJobClass   *job_class = EV_JOB_CLASS (class);
574
575         job_render_signals [PAGE_READY] =
576                 g_signal_new ("page-ready",
577                               EV_TYPE_JOB_RENDER,
578                               G_SIGNAL_RUN_LAST,
579                               G_STRUCT_OFFSET (EvJobRenderClass, page_ready),
580                               NULL, NULL,
581                               g_cclosure_marshal_VOID__VOID,
582                               G_TYPE_NONE, 0);
583
584         oclass->dispose = ev_job_render_dispose;
585         job_class->run = ev_job_render_run;
586 }
587
588 EvJob *
589 ev_job_render_new (EvDocument   *document,
590                    gint          page,
591                    gint          rotation,
592                    gdouble       scale, 
593                    gint          width,
594                    gint          height,
595                    EvRenderFlags flags)
596 {
597         EvJobRender *job;
598
599         ev_debug_message (DEBUG_JOBS, "page: %d", page);
600         
601         job = g_object_new (EV_TYPE_JOB_RENDER, NULL);
602
603         EV_JOB (job)->document = g_object_ref (document);
604         job->page = page;
605         job->rotation = rotation;
606         job->scale = scale;
607         job->target_width = width;
608         job->target_height = height;
609         job->flags = flags;
610
611         return EV_JOB (job);
612 }
613
614 void
615 ev_job_render_set_selection_info (EvJobRender     *job,
616                                   EvRectangle     *selection_points,
617                                   EvSelectionStyle selection_style,
618                                   GdkColor        *text,
619                                   GdkColor        *base)
620 {
621         job->flags |= EV_RENDER_INCLUDE_SELECTION;
622         
623         job->selection_points = *selection_points;
624         job->selection_style = selection_style;
625         job->text = *text;
626         job->base = *base;
627 }
628
629 /* EvJobThumbnail */
630 static void
631 ev_job_thumbnail_init (EvJobThumbnail *job)
632 {
633         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
634 }
635
636 static void
637 ev_job_thumbnail_dispose (GObject *object)
638 {
639         EvJobThumbnail *job;
640
641         job = EV_JOB_THUMBNAIL (object);
642
643         ev_debug_message (DEBUG_JOBS, "%d (%p)", job->page, job);
644         
645         if (job->thumbnail) {
646                 g_object_unref (job->thumbnail);
647                 job->thumbnail = NULL;
648         }
649
650         (* G_OBJECT_CLASS (ev_job_thumbnail_parent_class)->dispose) (object);
651 }
652
653 static gboolean
654 ev_job_thumbnail_run (EvJob *job)
655 {
656         EvJobThumbnail  *job_thumb = EV_JOB_THUMBNAIL (job);
657         EvRenderContext *rc;
658         EvPage          *page;
659
660         ev_debug_message (DEBUG_JOBS, "%d (%p)", job_thumb->page, job);
661         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
662         
663         ev_document_doc_mutex_lock ();
664
665         page = ev_document_get_page (job->document, job_thumb->page);
666         rc = ev_render_context_new (page, job_thumb->rotation, job_thumb->scale);
667         g_object_unref (page);
668
669         job_thumb->thumbnail = ev_document_thumbnails_get_thumbnail (EV_DOCUMENT_THUMBNAILS (job->document),
670                                                                      rc, TRUE);
671         g_object_unref (rc);
672         ev_document_doc_mutex_unlock ();
673
674         ev_job_succeeded (job);
675         
676         return FALSE;
677 }
678
679 static void
680 ev_job_thumbnail_class_init (EvJobThumbnailClass *class)
681 {
682         GObjectClass *oclass = G_OBJECT_CLASS (class);
683         EvJobClass   *job_class = EV_JOB_CLASS (class);
684
685         oclass->dispose = ev_job_thumbnail_dispose;
686         job_class->run = ev_job_thumbnail_run;
687 }
688
689 EvJob *
690 ev_job_thumbnail_new (EvDocument *document,
691                       gint        page,
692                       gint        rotation,
693                       gdouble     scale)
694 {
695         EvJobThumbnail *job;
696
697         ev_debug_message (DEBUG_JOBS, "%d", page);
698         
699         job = g_object_new (EV_TYPE_JOB_THUMBNAIL, NULL);
700
701         EV_JOB (job)->document = g_object_ref (document);
702         job->page = page;
703         job->rotation = rotation;
704         job->scale = scale;
705
706         return EV_JOB (job);
707 }
708
709 /* EvJobFonts */
710 static void
711 ev_job_fonts_init (EvJobFonts *job)
712 {
713         EV_JOB (job)->run_mode = EV_JOB_RUN_MAIN_LOOP;
714 }
715
716 static gboolean
717 ev_job_fonts_run (EvJob *job)
718 {
719         EvJobFonts      *job_fonts = EV_JOB_FONTS (job);
720         EvDocumentFonts *fonts = EV_DOCUMENT_FONTS (job->document);
721
722         ev_debug_message (DEBUG_JOBS, NULL);
723         
724         /* Do not block the main loop */
725         if (!ev_document_doc_mutex_trylock ())
726                 return TRUE;
727         
728         if (!ev_document_fc_mutex_trylock ())
729                 return TRUE;
730
731 #ifdef EV_ENABLE_DEBUG
732         /* We use the #ifdef in this case because of the if */
733         if (ev_document_fonts_get_progress (fonts) == 0)
734                 ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
735 #endif
736
737         job_fonts->scan_completed = !ev_document_fonts_scan (fonts, 20);
738         g_signal_emit (job_fonts, job_fonts_signals[FONTS_UPDATED], 0,
739                        ev_document_fonts_get_progress (fonts));
740
741         ev_document_fc_mutex_unlock ();
742         ev_document_doc_mutex_unlock ();
743
744         if (job_fonts->scan_completed)
745                 ev_job_succeeded (job);
746         
747         return !job_fonts->scan_completed;
748 }
749
750 static void
751 ev_job_fonts_class_init (EvJobFontsClass *class)
752 {
753         EvJobClass *job_class = EV_JOB_CLASS (class);
754         
755         job_class->run = ev_job_fonts_run;
756         
757         job_fonts_signals[FONTS_UPDATED] =
758                 g_signal_new ("updated",
759                               EV_TYPE_JOB_FONTS,
760                               G_SIGNAL_RUN_LAST,
761                               G_STRUCT_OFFSET (EvJobFontsClass, updated),
762                               NULL, NULL,
763                               g_cclosure_marshal_VOID__DOUBLE,
764                               G_TYPE_NONE,
765                               1, G_TYPE_DOUBLE);
766 }
767
768 EvJob *
769 ev_job_fonts_new (EvDocument *document)
770 {
771         EvJobFonts *job;
772
773         ev_debug_message (DEBUG_JOBS, NULL);
774         
775         job = g_object_new (EV_TYPE_JOB_FONTS, NULL);
776
777         EV_JOB (job)->document = g_object_ref (document);
778
779         return EV_JOB (job);
780 }
781
782 /* EvJobLoad */
783 static void
784 ev_job_load_init (EvJobLoad *job)
785 {
786         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
787 }
788
789 static void
790 ev_job_load_dispose (GObject *object)
791 {
792         EvJobLoad *job = EV_JOB_LOAD (object);
793
794         ev_debug_message (DEBUG_JOBS, "%s", job->uri);
795         
796         if (job->uri) {
797                 g_free (job->uri);
798                 job->uri = NULL;
799         }
800
801         if (job->password) {
802                 g_free (job->password);
803                 job->password = NULL;
804         }
805
806         (* G_OBJECT_CLASS (ev_job_load_parent_class)->dispose) (object);
807 }
808
809 static gboolean
810 ev_job_load_run (EvJob *job)
811 {
812         EvJobLoad *job_load = EV_JOB_LOAD (job);
813         GError    *error = NULL;
814         
815         ev_debug_message (DEBUG_JOBS, "%s", job_load->uri);
816         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
817         
818         ev_document_fc_mutex_lock ();
819
820         /* This job may already have a document even if the job didn't complete
821            because, e.g., a password is required - if so, just reload rather than
822            creating a new instance */
823         if (job->document) {
824                 if (job_load->password) {
825                         ev_document_security_set_password (EV_DOCUMENT_SECURITY (job->document),
826                                                            job_load->password);
827                 }
828                 
829                 job->failed = FALSE;
830                 job->finished = FALSE;
831                 g_clear_error (&job->error);
832                 
833                 ev_document_load (job->document,
834                                   job_load->uri,
835                                   &error);
836         } else {
837                 job->document = ev_document_factory_get_document (job_load->uri,
838                                                                   &error);
839         }
840
841         ev_document_fc_mutex_unlock ();
842
843         if (error) {
844                 ev_job_failed_from_error (job, error);
845                 g_error_free (error);
846         } else {
847                 ev_job_succeeded (job);
848         }
849
850         return FALSE;
851 }
852
853 static void
854 ev_job_load_class_init (EvJobLoadClass *class)
855 {
856         GObjectClass *oclass = G_OBJECT_CLASS (class);
857         EvJobClass   *job_class = EV_JOB_CLASS (class);
858
859         oclass->dispose = ev_job_load_dispose;
860         job_class->run = ev_job_load_run;
861 }
862
863 EvJob *
864 ev_job_load_new (const gchar *uri)
865 {
866         EvJobLoad *job;
867
868         ev_debug_message (DEBUG_JOBS, "%s", uri);
869         
870         job = g_object_new (EV_TYPE_JOB_LOAD, NULL);
871         job->uri = g_strdup (uri);
872
873         return EV_JOB (job);
874 }
875
876 void
877 ev_job_load_set_uri (EvJobLoad *job, const gchar *uri)
878 {
879         ev_debug_message (DEBUG_JOBS, "%s", uri);
880         
881         if (job->uri)
882                 g_free (job->uri);
883         job->uri = g_strdup (uri);
884 }
885
886 void
887 ev_job_load_set_password (EvJobLoad *job, const gchar *password)
888 {
889         ev_debug_message (DEBUG_JOBS, NULL);
890
891         if (job->password)
892                 g_free (job->password);
893         job->password = password ? g_strdup (password) : NULL;
894 }
895
896 /* EvJobSave */
897 static void
898 ev_job_save_init (EvJobSave *job)
899 {
900         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
901 }
902
903 static void
904 ev_job_save_dispose (GObject *object)
905 {
906         EvJobSave *job = EV_JOB_SAVE (object);
907
908         ev_debug_message (DEBUG_JOBS, "%s", job->uri);
909         
910         if (job->uri) {
911                 g_free (job->uri);
912                 job->uri = NULL;
913         }
914
915         if (job->document_uri) {
916                 g_free (job->document_uri);
917                 job->document_uri = NULL;
918         }
919
920         (* G_OBJECT_CLASS (ev_job_save_parent_class)->dispose) (object);
921 }
922
923 static gboolean
924 ev_job_save_run (EvJob *job)
925 {
926         EvJobSave *job_save = EV_JOB_SAVE (job);
927         gint       fd;
928         gchar     *filename;
929         gchar     *tmp_filename;
930         gchar     *local_uri;
931         GError    *error = NULL;
932         
933         ev_debug_message (DEBUG_JOBS, "uri: %s, document_uri: %s", job_save->uri, job_save->document_uri);
934         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
935         
936         filename = ev_tmp_filename ("saveacopy");
937         tmp_filename = g_strdup_printf ("%s.XXXXXX", filename);
938         g_free (filename);
939
940         fd = g_mkstemp (tmp_filename);
941         if (fd == -1) {
942                 gchar *display_name;
943                 gint   save_errno = errno;
944
945                 display_name = g_filename_display_name (tmp_filename);
946                 ev_job_failed (job,
947                                G_FILE_ERROR,
948                                g_file_error_from_errno (save_errno),
949                                _("Failed to create file “%s”: %s"),
950                                display_name, g_strerror (save_errno));
951                 g_free (display_name);
952                 g_free (tmp_filename);
953
954                 return FALSE;
955         }
956
957         ev_document_doc_mutex_lock ();
958
959         /* Save document to temp filename */
960         local_uri = g_filename_to_uri (tmp_filename, NULL, NULL);
961         ev_document_save (job->document, local_uri, &error);
962         close (fd);
963
964         ev_document_doc_mutex_unlock ();
965
966         if (error) {
967                 g_free (local_uri);
968                 ev_job_failed_from_error (job, error);
969                 g_error_free (error);
970                 
971                 return FALSE;
972         }
973
974         /* If original document was compressed,
975          * compress it again before saving
976          */
977         if (g_object_get_data (G_OBJECT (job->document), "uri-uncompressed")) {
978                 EvCompressionType ctype = EV_COMPRESSION_NONE;
979                 const gchar      *ext;
980                 gchar            *uri_comp;
981                 
982                 ext = g_strrstr (job_save->document_uri, ".gz");
983                 if (ext && g_ascii_strcasecmp (ext, ".gz") == 0)
984                         ctype = EV_COMPRESSION_GZIP;
985                 
986                 ext = g_strrstr (job_save->document_uri, ".bz2");
987                 if (ext && g_ascii_strcasecmp (ext, ".bz2") == 0)
988                         ctype = EV_COMPRESSION_BZIP2;
989
990                 uri_comp = ev_file_compress (local_uri, ctype, &error);
991                 g_free (local_uri);
992                 ev_tmp_filename_unlink (tmp_filename);
993
994                 if (!uri_comp || error) {
995                         local_uri = NULL;
996                 } else {
997                         local_uri = uri_comp;
998                 }
999         }
1000
1001         g_free (tmp_filename);
1002         
1003         if (error) {
1004                 g_free (local_uri);
1005                 ev_job_failed_from_error (job, error);
1006                 g_error_free (error);
1007                 
1008                 return FALSE;
1009         }
1010
1011         if (!local_uri)
1012                 return FALSE;
1013
1014         ev_xfer_uri_simple (local_uri, job_save->uri, &error);
1015         ev_tmp_uri_unlink (local_uri);
1016
1017         if (error) {
1018                 ev_job_failed_from_error (job, error);
1019                 g_error_free (error);
1020         } else {
1021                 ev_job_succeeded (job);
1022         }
1023         
1024         return FALSE;
1025 }
1026
1027 static void
1028 ev_job_save_class_init (EvJobSaveClass *class)
1029 {
1030         GObjectClass *oclass = G_OBJECT_CLASS (class);
1031         EvJobClass   *job_class = EV_JOB_CLASS (class);
1032
1033         oclass->dispose = ev_job_save_dispose;
1034         job_class->run = ev_job_save_run;
1035 }
1036
1037 EvJob *
1038 ev_job_save_new (EvDocument  *document,
1039                  const gchar *uri,
1040                  const gchar *document_uri)
1041 {
1042         EvJobSave *job;
1043
1044         ev_debug_message (DEBUG_JOBS, "uri: %s, document_uri: %s", uri, document_uri);
1045
1046         job = g_object_new (EV_TYPE_JOB_SAVE, NULL);
1047
1048         EV_JOB (job)->document = g_object_ref (document);
1049         job->uri = g_strdup (uri);
1050         job->document_uri = g_strdup (document_uri);
1051
1052         return EV_JOB (job);
1053 }
1054
1055 /* EvJobFind */
1056 static void
1057 ev_job_find_init (EvJobFind *job)
1058 {
1059         EV_JOB (job)->run_mode = EV_JOB_RUN_MAIN_LOOP;
1060 }
1061
1062 static void
1063 ev_job_find_dispose (GObject *object)
1064 {
1065         EvJobFind *job = EV_JOB_FIND (object);
1066
1067         ev_debug_message (DEBUG_JOBS, NULL);
1068
1069         if (job->text) {
1070                 g_free (job->text);
1071                 job->text = NULL;
1072         }
1073
1074         if (job->pages) {
1075                 gint i;
1076
1077                 for (i = 0; i < job->n_pages; i++) {
1078                         g_list_foreach (job->pages[i], (GFunc)g_free, NULL);
1079                         g_list_free (job->pages[i]);
1080                 }
1081
1082                 g_free (job->pages);
1083                 job->pages = NULL;
1084         }
1085         
1086         (* G_OBJECT_CLASS (ev_job_find_parent_class)->dispose) (object);
1087 }
1088
1089 static gboolean
1090 ev_job_find_run (EvJob *job)
1091 {
1092         EvJobFind      *job_find = EV_JOB_FIND (job);
1093         EvDocumentFind *find = EV_DOCUMENT_FIND (job->document);
1094         EvPage         *ev_page;
1095         GList          *matches;
1096
1097         ev_debug_message (DEBUG_JOBS, NULL);
1098         
1099         /* Do not block the main loop */
1100         if (!ev_document_doc_mutex_trylock ())
1101                 return TRUE;
1102         
1103 #ifdef EV_ENABLE_DEBUG
1104         /* We use the #ifdef in this case because of the if */
1105         if (job_find->current_page == job_find->start_page)
1106                 ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
1107 #endif
1108
1109         ev_page = ev_document_get_page (job->document, job_find->current_page);
1110         matches = ev_document_find_find_text (find, ev_page, job_find->text,
1111                                               job_find->case_sensitive);
1112         g_object_unref (ev_page);
1113         
1114         ev_document_doc_mutex_unlock ();
1115
1116         if (!job_find->has_results)
1117                 job_find->has_results = (matches != NULL);
1118
1119         job_find->pages[job_find->current_page] = matches;
1120         g_signal_emit (job_find, job_find_signals[FIND_UPDATED], 0, job_find->current_page);
1121                        
1122         job_find->current_page = (job_find->current_page + 1) % job_find->n_pages;
1123         if (job_find->current_page == job_find->start_page) {
1124                 ev_job_succeeded (job);
1125
1126                 return FALSE;
1127         }
1128
1129         return TRUE;
1130 }
1131
1132 static void
1133 ev_job_find_class_init (EvJobFindClass *class)
1134 {
1135         EvJobClass   *job_class = EV_JOB_CLASS (class);
1136         GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1137         
1138         job_class->run = ev_job_find_run;
1139         gobject_class->dispose = ev_job_find_dispose;
1140         
1141         job_find_signals[FIND_UPDATED] =
1142                 g_signal_new ("updated",
1143                               EV_TYPE_JOB_FIND,
1144                               G_SIGNAL_RUN_LAST,
1145                               G_STRUCT_OFFSET (EvJobFindClass, updated),
1146                               NULL, NULL,
1147                               g_cclosure_marshal_VOID__INT,
1148                               G_TYPE_NONE,
1149                               1, G_TYPE_INT);
1150 }
1151
1152 EvJob *
1153 ev_job_find_new (EvDocument  *document,
1154                  gint         start_page,
1155                  gint         n_pages,
1156                  const gchar *text,
1157                  gboolean     case_sensitive)
1158 {
1159         EvJobFind *job;
1160         
1161         ev_debug_message (DEBUG_JOBS, NULL);
1162         
1163         job = g_object_new (EV_TYPE_JOB_FIND, NULL);
1164
1165         EV_JOB (job)->document = g_object_ref (document);
1166         job->start_page = start_page;
1167         job->current_page = start_page;
1168         job->n_pages = n_pages;
1169         job->pages = g_new0 (GList *, n_pages);
1170         job->text = g_strdup (text);
1171         job->case_sensitive = case_sensitive;
1172         job->has_results = FALSE;
1173
1174         return EV_JOB (job);
1175 }
1176
1177 gint
1178 ev_job_find_get_n_results (EvJobFind *job,
1179                            gint       page)
1180 {
1181         return g_list_length (job->pages[page]);
1182 }
1183
1184 gdouble
1185 ev_job_find_get_progress (EvJobFind *job)
1186 {
1187         gint pages_done;
1188
1189         if (ev_job_is_finished (EV_JOB (job)))
1190                 return 1.0;
1191         
1192         if (job->current_page > job->start_page) {
1193                 pages_done = job->current_page - job->start_page + 1;
1194         } else if (job->current_page == job->start_page) {
1195                 pages_done = job->n_pages;
1196         } else {
1197                 pages_done = job->n_pages - job->start_page + job->current_page;
1198         }
1199
1200         return pages_done / (gdouble) job->n_pages;
1201 }
1202
1203 gboolean
1204 ev_job_find_has_results (EvJobFind *job)
1205 {
1206         return job->has_results;
1207 }
1208
1209 GList **
1210 ev_job_find_get_results (EvJobFind *job)
1211 {
1212         return job->pages;
1213 }
1214
1215 /* EvJobLayers */
1216 static void
1217 ev_job_layers_init (EvJobLayers *job)
1218 {
1219         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
1220 }
1221
1222 static void
1223 ev_job_layers_dispose (GObject *object)
1224 {
1225         EvJobLayers *job;
1226
1227         ev_debug_message (DEBUG_JOBS, NULL);
1228         
1229         job = EV_JOB_LAYERS (object);
1230
1231         if (job->model) {
1232                 g_object_unref (job->model);
1233                 job->model = NULL;
1234         }
1235
1236         (* G_OBJECT_CLASS (ev_job_layers_parent_class)->dispose) (object);
1237 }
1238
1239 static gboolean
1240 ev_job_layers_run (EvJob *job)
1241 {
1242         EvJobLayers *job_layers = EV_JOB_LAYERS (job);
1243
1244         ev_debug_message (DEBUG_JOBS, NULL);
1245         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
1246         
1247         ev_document_doc_mutex_lock ();
1248         job_layers->model = ev_document_layers_get_layers (EV_DOCUMENT_LAYERS (job->document));
1249         ev_document_doc_mutex_unlock ();
1250         
1251         ev_job_succeeded (job);
1252         
1253         return FALSE;
1254 }
1255
1256 static void
1257 ev_job_layers_class_init (EvJobLayersClass *class)
1258 {
1259         GObjectClass *oclass = G_OBJECT_CLASS (class);
1260         EvJobClass   *job_class = EV_JOB_CLASS (class);
1261
1262         oclass->dispose = ev_job_layers_dispose;
1263         job_class->run = ev_job_layers_run;
1264 }
1265
1266 EvJob *
1267 ev_job_layers_new (EvDocument *document)
1268 {
1269         EvJob *job;
1270
1271         ev_debug_message (DEBUG_JOBS, NULL);
1272
1273         job = g_object_new (EV_TYPE_JOB_LAYERS, NULL);
1274         job->document = g_object_ref (document);
1275         
1276         return job;
1277 }
1278
1279 /* EvJobExport */
1280 static void
1281 ev_job_export_init (EvJobExport *job)
1282 {
1283         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
1284         job->page = -1;
1285 }
1286
1287 static void
1288 ev_job_export_dispose (GObject *object)
1289 {
1290         EvJobExport *job;
1291
1292         ev_debug_message (DEBUG_JOBS, NULL);
1293         
1294         job = EV_JOB_EXPORT (object);
1295
1296         if (job->rc) {
1297                 g_object_unref (job->rc);
1298                 job->rc = NULL;
1299         }
1300
1301         (* G_OBJECT_CLASS (ev_job_export_parent_class)->dispose) (object);
1302 }
1303
1304 static gboolean
1305 ev_job_export_run (EvJob *job)
1306 {
1307         EvJobExport *job_export = EV_JOB_EXPORT (job);
1308         EvPage      *ev_page;
1309
1310         g_assert (job_export->page != -1);
1311
1312         ev_debug_message (DEBUG_JOBS, NULL);
1313         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
1314         
1315         ev_document_doc_mutex_lock ();
1316         
1317         ev_page = ev_document_get_page (job->document, job_export->page);
1318         if (job_export->rc) {
1319                 job->failed = FALSE;
1320                 job->finished = FALSE;
1321                 g_clear_error (&job->error);
1322                 
1323                 ev_render_context_set_page (job_export->rc, ev_page);
1324         } else {
1325                 job_export->rc = ev_render_context_new (ev_page, 0, 1.0);
1326         }
1327         g_object_unref (ev_page);
1328         
1329         ev_file_exporter_do_page (EV_FILE_EXPORTER (job->document), job_export->rc);
1330         
1331         ev_document_doc_mutex_unlock ();
1332         
1333         ev_job_succeeded (job);
1334         
1335         return FALSE;
1336 }
1337
1338 static void
1339 ev_job_export_class_init (EvJobExportClass *class)
1340 {
1341         GObjectClass *oclass = G_OBJECT_CLASS (class);
1342         EvJobClass   *job_class = EV_JOB_CLASS (class);
1343
1344         oclass->dispose = ev_job_export_dispose;
1345         job_class->run = ev_job_export_run;
1346 }
1347
1348 EvJob *
1349 ev_job_export_new (EvDocument *document)
1350 {
1351         EvJob *job;
1352
1353         ev_debug_message (DEBUG_JOBS, NULL);
1354
1355         job = g_object_new (EV_TYPE_JOB_EXPORT, NULL);
1356         job->document = g_object_ref (document);
1357         
1358         return job;
1359 }
1360
1361 void
1362 ev_job_export_set_page (EvJobExport *job,
1363                         gint         page)
1364 {
1365         job->page = page;
1366 }