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