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