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