From 48e488b596bc73000d172234f87ffd46e9f4032e Mon Sep 17 00:00:00 2001
From: Carlos Garcia Campos <carlosgc@gnome.org>
Date: Sat, 27 Dec 2008 18:37:13 +0000
Subject: [PATCH] First step of the printing system rework. EvJobPrint has been
 replaced by

2008-12-24  Carlos Garcia Campos  <carlosgc@gnome.org>

	* shell/Makefile.am:
	* shell/ev-print-operation.[ch]:
	* shell/ev-jobs.[ch]: (ev_job_export_init),
	(ev_job_export_dispose), (ev_job_export_run),
	(ev_job_export_class_init), (ev_job_export_new),
	(ev_job_export_set_page):
	* shell/ev-window.c: (ev_window_print_operation_done),
	(ev_window_print_range), (ev_window_dispose):

	First step of the printing system rework. EvJobPrint has been
	replaced by EvJobExport so that every page is scheduled to be
	exported in a thread instead of scheduling the whole printing
	process. This way the gui is responsive during printing.

svn path=/trunk/; revision=3306
---
 ChangeLog                  |   16 +
 shell/Makefile.am          |    2 +
 shell/ev-jobs.c            |  406 ++++----------
 shell/ev-jobs.h            |   72 +--
 shell/ev-print-operation.c | 1043 ++++++++++++++++++++++++++++++++++++
 shell/ev-print-operation.h |   59 ++
 shell/ev-window.c          |  328 ++----------
 7 files changed, 1277 insertions(+), 649 deletions(-)
 create mode 100644 shell/ev-print-operation.c
 create mode 100644 shell/ev-print-operation.h

diff --git a/ChangeLog b/ChangeLog
index e2b6718c..30685690 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2008-12-24  Carlos Garcia Campos  <carlosgc@gnome.org>
+
+	* shell/Makefile.am:
+	* shell/ev-print-operation.[ch]:
+	* shell/ev-jobs.[ch]: (ev_job_export_init),
+	(ev_job_export_dispose), (ev_job_export_run),
+	(ev_job_export_class_init), (ev_job_export_new),
+	(ev_job_export_set_page):
+	* shell/ev-window.c: (ev_window_print_operation_done),
+	(ev_window_print_range), (ev_window_dispose):
+
+	First step of the printing system rework. EvJobPrint has been
+	replaced by EvJobExport so that every page is scheduled to be
+	exported in a thread instead of scheduling the whole printing
+	process. This way the gui is responsive during printing.
+	
 2008-12-24  Carlos Garcia Campos  <carlosgc@gnome.org>
 
 	* shell/ev-window.c: (setup_size_from_metadata),
diff --git a/shell/Makefile.am b/shell/Makefile.am
index 1a154942..7f7ad4d7 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -61,6 +61,8 @@ evince_SOURCES=				\
 	ev-password-view.c		\
 	ev-pixbuf-cache.c		\
 	ev-pixbuf-cache.h		\
+	ev-print-operation.h		\
+	ev-print-operation.c		\
 	ev-properties-dialog.c		\
 	ev-properties-dialog.h		\
 	ev-properties-fonts.c		\
diff --git a/shell/ev-jobs.c b/shell/ev-jobs.c
index 1a5dd475..a952243e 100644
--- a/shell/ev-jobs.c
+++ b/shell/ev-jobs.c
@@ -54,12 +54,12 @@ static void ev_job_load_init    	  (EvJobLoad	         *job);
 static void ev_job_load_class_init 	  (EvJobLoadClass	 *class);
 static void ev_job_save_init              (EvJobSave             *job);
 static void ev_job_save_class_init        (EvJobSaveClass        *class);
-static void ev_job_print_init             (EvJobPrint            *job);
-static void ev_job_print_class_init       (EvJobPrintClass       *class);
 static void ev_job_find_init              (EvJobFind             *job);
 static void ev_job_find_class_init        (EvJobFindClass        *class);
 static void ev_job_layers_init            (EvJobLayers           *job);
 static void ev_job_layers_class_init      (EvJobLayersClass      *class);
+static void ev_job_export_init            (EvJobExport           *job);
+static void ev_job_export_class_init      (EvJobExportClass      *class);
 
 enum {
 	CANCELLED,
@@ -95,9 +95,9 @@ G_DEFINE_TYPE (EvJobThumbnail, ev_job_thumbnail, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobFonts, ev_job_fonts, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobLoad, ev_job_load, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobSave, ev_job_save, EV_TYPE_JOB)
-G_DEFINE_TYPE (EvJobPrint, ev_job_print, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobFind, ev_job_find, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobLayers, ev_job_layers, EV_TYPE_JOB)
+G_DEFINE_TYPE (EvJobExport, ev_job_export, EV_TYPE_JOB)
 
 /* EvJob */
 static void
@@ -1069,317 +1069,6 @@ ev_job_save_new (EvDocument  *document,
 	return EV_JOB (job);
 }
 
-/* EvJobPrint */
-static void
-ev_job_print_init (EvJobPrint *job)
-{
-	EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
-}
-
-static void
-ev_job_print_dispose (GObject *object)
-{
-	EvJobPrint *job;
-
-	job = EV_JOB_PRINT (object);
-
-	ev_debug_message (DEBUG_JOBS, NULL);
-	
-	if (job->temp_file) {
-		g_unlink (job->temp_file);
-		g_free (job->temp_file);
-		job->temp_file = NULL;
-	}
-
-	if (job->ranges) {
-		g_free (job->ranges);
-		job->ranges = NULL;
-		job->n_ranges = 0;
-	}
-
-	(* G_OBJECT_CLASS (ev_job_print_parent_class)->dispose) (object);
-}
-
-static gint
-ev_print_job_get_first_page (EvJobPrint *job)
-{
-	gint i;
-	gint first_page = G_MAXINT;
-	
-	if (job->n_ranges == 0)
-		return 0;
-
-	for (i = 0; i < job->n_ranges; i++) {
-		if (job->ranges[i].start < first_page)
-			first_page = job->ranges[i].start;
-	}
-
-	return MAX (0, first_page);
-}
-
-static gint
-ev_print_job_get_last_page (EvJobPrint *job)
-{
-	gint i;
-	gint last_page = G_MININT;
-	gint max_page;
-
-	max_page = ev_document_get_n_pages (EV_JOB (job)->document) - 1;
-
-	if (job->n_ranges == 0)
-		return max_page;
-
-	for (i = 0; i < job->n_ranges; i++) {
-		if (job->ranges[i].end > last_page)
-			last_page = job->ranges[i].end;
-	}
-
-	return MIN (max_page, last_page);
-}
-
-static gboolean
-ev_print_job_print_page_in_set (EvJobPrint *job,
-				gint        page)
-{
-	switch (job->page_set) {
-	        case EV_PRINT_PAGE_SET_EVEN:
-			return page % 2 == 0;
-	        case EV_PRINT_PAGE_SET_ODD:
-			return page % 2 != 0;
-	        case EV_PRINT_PAGE_SET_ALL:
-			return TRUE;
-	}
-
-	return FALSE;
-}
-
-static gint *
-ev_job_print_get_page_list (EvJobPrint *job,
-			    gint       *n_pages)
-{
-	gint  i, j, page, max_page;
-	gint  pages = 0;
-	gint *page_list;
-
-	max_page = ev_document_get_n_pages (EV_JOB (job)->document) - 1;
-
-	for (i = 0; i < job->n_ranges; i++) {
-		gint rsize;
-		gint start, end;
-
-		if (job->ranges[i].start == -1)
-			job->ranges[i].start = 0;
-		if (job->ranges[i].end == -1)
-			job->ranges[i].end = max_page;
-
-		if (job->ranges[i].start > max_page)
-			continue;
-		
-		start = job->ranges[i].start + 1;
-		end = job->ranges[i].end <= max_page ? job->ranges[i].end + 1 : max_page + 1;
-		rsize = end - start + 1;
-
-		switch (job->page_set) {
-		        case EV_PRINT_PAGE_SET_EVEN:
-				pages += start % 2 == 0 ? (rsize / 2) + (rsize % 2) : (rsize / 2);
-				break;
-		        case EV_PRINT_PAGE_SET_ODD:
-				pages += start % 2 != 0 ? (rsize / 2) + (rsize % 2) : (rsize / 2);
-				break;
-		        default:
-				pages += rsize;
-				break;
-		}
-	}
-
-	*n_pages = pages;
-
-	if (pages == 0)
-		return NULL;
-
-	page_list = g_new (gint, pages);
-
-	page = 0;
-	for (i = 0; i < job->n_ranges; i++) {
-		for (j = job->ranges[i].start; j <= job->ranges[i].end; j++) {
-			if (j > max_page)
-				break;
-		
-			if (ev_print_job_print_page_in_set (job, j + 1))
-				page_list[page++] = j;
-		}
-	}
-
-	return page_list;
-}
-
-static gboolean
-ev_job_print_run (EvJob *job)
-{
-	EvDocument            *document = EV_JOB (job)->document;
-	EvJobPrint            *job_print = EV_JOB_PRINT (job);
-	EvFileExporterContext  fc;
-	EvRenderContext       *rc;
-	gint                   fd;
-	gint                  *page_list;
-	gint                   n_pages;
-	gint                   last_page;
-	gint                   first_page;
-	gint                   i, j;
-	gchar                 *filename;
-	GError                *error = NULL;
-	
-	ev_debug_message (DEBUG_JOBS, NULL);
-	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
-	
-	if (job_print->temp_file)
-		g_free (job_print->temp_file);
-	job_print->temp_file = NULL;
-	
-	filename = g_strdup_printf ("evince_print.%s.XXXXXX", job_print->format);
-	fd = g_file_open_tmp (filename, &job_print->temp_file, &error);
-	g_free (filename);
-	if (fd <= -1) {
-		ev_job_failed_from_error (job, error);
-		g_error_free (error);
-		
-		return FALSE;
-	}
-
-	page_list = ev_job_print_get_page_list (job_print, &n_pages);
-	if (n_pages == 0) {
-		close (fd);
-		/* TODO: error */
-		ev_job_succeeded (job);
-		
-		return FALSE;
-	}
-
-	first_page = ev_print_job_get_first_page (job_print);
-	last_page = ev_print_job_get_last_page (job_print);
-
-	fc.format = g_ascii_strcasecmp (job_print->format, "pdf") == 0 ?
-		EV_FILE_FORMAT_PDF : EV_FILE_FORMAT_PS;
-	fc.filename = job_print->temp_file;
-	fc.first_page = MIN (first_page, last_page);
-	fc.last_page = MAX (first_page, last_page);
-	fc.paper_width = job_print->width;
-	fc.paper_height = job_print->height;
-	fc.duplex = FALSE;
-	fc.pages_per_sheet = MAX (1, job_print->pages_per_sheet);
-
-	rc = ev_render_context_new (NULL, 0, 1.0);
-
-	ev_document_doc_mutex_lock ();
-	ev_file_exporter_begin (EV_FILE_EXPORTER (document), &fc);
-
-	for (i = 0; i < job_print->copies; i++) {
-		gint page, step;
-		gint n_copies;
-		
-		step = job_print->reverse ? -1 * job_print->pages_per_sheet : job_print->pages_per_sheet;
-		page = job_print->reverse ? ((n_pages - 1) / job_print->pages_per_sheet) * job_print->pages_per_sheet : 0;
-		n_copies = job_print->collate ? 1 : job_print->copies;
-
-		while ((job_print->reverse && (page >= 0)) || (!job_print->reverse && (page < n_pages))) {
-			gint k;
-
-			for (k = 0; k < n_copies; k++) {
-				ev_file_exporter_begin_page (EV_FILE_EXPORTER (document));
-				
-				for (j = 0; j < job_print->pages_per_sheet; j++) {
-					EvPage *ev_page;
-					
-					gint p = page + j;
-
-					if (p < 0 || p >= n_pages)
-						break;
-
-					ev_page = ev_document_get_page (document, page_list[p]);
-					ev_render_context_set_page (rc, ev_page);
-					g_object_unref (ev_page);
-					
-					ev_file_exporter_do_page (EV_FILE_EXPORTER (document), rc);
-				}
-
-				ev_file_exporter_end_page (EV_FILE_EXPORTER (document));
-			}
-
-			page += step;
-		}
-
-		if (!job_print->collate)
-			break;
-	}
-
-	ev_file_exporter_end (EV_FILE_EXPORTER (document));
-	ev_document_doc_mutex_unlock ();
-	
-	g_free (page_list);
-	close (fd);
-	g_object_unref (rc);
-	
-	ev_job_succeeded (job);
-	
-	return FALSE;
-}
-
-static void
-ev_job_print_class_init (EvJobPrintClass *class)
-{
-	GObjectClass *oclass = G_OBJECT_CLASS (class);
-	EvJobClass   *job_class = EV_JOB_CLASS (class);
-
-	oclass->dispose = ev_job_print_dispose;
-	job_class->run = ev_job_print_run;
-}
-
-EvJob *
-ev_job_print_new (EvDocument    *document,
-		  const gchar   *format,
-		  gdouble        width,
-		  gdouble        height,
-		  EvPrintRange  *ranges,
-		  gint           n_ranges,
-		  EvPrintPageSet page_set,
-		  gint           pages_per_sheet,
-		  gint           copies,
-		  gdouble        collate,
-		  gdouble        reverse)
-{
-	EvJobPrint *job;
-
-	ev_debug_message (DEBUG_JOBS, "format: %s, width: %f, height:%f,"
-			  "n_ranges: %d, pages_per_sheet: %d, copies: %d,"
-			  "collate: %s, reverse: %s",
-			  format, width, height, n_ranges, pages_per_sheet, copies,
-			  collate ? "True" : "False", reverse  ? "True" : "False");
-
-	job = g_object_new (EV_TYPE_JOB_PRINT, NULL);
-
-	EV_JOB (job)->document = g_object_ref (document);
-
-	job->format = format;
-	
-	job->temp_file = NULL;
-
-	job->width = width;
-	job->height = height;
-
-	job->ranges = ranges;
-	job->n_ranges = n_ranges;
-
-	job->page_set = page_set;
-
-	job->pages_per_sheet = CLAMP (pages_per_sheet, 1, 16);
-	
-	job->copies = copies;
-	job->collate = collate;
-	job->reverse = reverse;
-	
-	return EV_JOB (job);
-}
-
 /* EvJobFind */
 static void
 ev_job_find_init (EvJobFind *job)
@@ -1603,3 +1292,92 @@ ev_job_layers_new (EvDocument *document)
 	
 	return job;
 }
+
+/* EvJobExport */
+static void
+ev_job_export_init (EvJobExport *job)
+{
+	EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
+	job->page = -1;
+}
+
+static void
+ev_job_export_dispose (GObject *object)
+{
+	EvJobExport *job;
+
+	ev_debug_message (DEBUG_JOBS, NULL);
+	
+	job = EV_JOB_EXPORT (object);
+
+	if (job->rc) {
+		g_object_unref (job->rc);
+		job->rc = NULL;
+	}
+
+	(* G_OBJECT_CLASS (ev_job_export_parent_class)->dispose) (object);
+}
+
+static gboolean
+ev_job_export_run (EvJob *job)
+{
+	EvJobExport *job_export = EV_JOB_EXPORT (job);
+	EvPage      *ev_page;
+
+	g_assert (job_export->page != -1);
+
+	ev_debug_message (DEBUG_JOBS, NULL);
+	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
+	
+	ev_document_doc_mutex_lock ();
+	
+	ev_page = ev_document_get_page (job->document, job_export->page);
+	if (job_export->rc) {
+		job->failed = FALSE;
+		job->finished = FALSE;
+		g_clear_error (&job->error);
+		
+		ev_render_context_set_page (job_export->rc, ev_page);
+	} else {
+		job_export->rc = ev_render_context_new (ev_page, 0, 1.0);
+	}
+	g_object_unref (ev_page);
+	
+	ev_file_exporter_do_page (EV_FILE_EXPORTER (job->document), job_export->rc);
+	
+	ev_document_doc_mutex_unlock ();
+	
+	ev_job_succeeded (job);
+	
+	return FALSE;
+}
+
+static void
+ev_job_export_class_init (EvJobExportClass *class)
+{
+	GObjectClass *oclass = G_OBJECT_CLASS (class);
+	EvJobClass   *job_class = EV_JOB_CLASS (class);
+
+	oclass->dispose = ev_job_export_dispose;
+	job_class->run = ev_job_export_run;
+}
+
+EvJob *
+ev_job_export_new (EvDocument *document)
+{
+	EvJob *job;
+
+	ev_debug_message (DEBUG_JOBS, NULL);
+
+	job = g_object_new (EV_TYPE_JOB_EXPORT, NULL);
+	job->document = g_object_ref (document);
+	
+	return job;
+}
+
+void
+ev_job_export_set_page (EvJobExport *job,
+			gint         page)
+{
+	job->page = page;
+}
diff --git a/shell/ev-jobs.h b/shell/ev-jobs.h
index 6d6581a3..27b40a1c 100644
--- a/shell/ev-jobs.h
+++ b/shell/ev-jobs.h
@@ -26,6 +26,7 @@
 
 #include "ev-document.h"
 #include "ev-selection.h"
+#include "ev-render-context.h"
 #include "ev-window.h"
 
 G_BEGIN_DECLS
@@ -54,15 +55,15 @@ typedef struct _EvJobLoadClass EvJobLoadClass;
 typedef struct _EvJobSave EvJobSave;
 typedef struct _EvJobSaveClass EvJobSaveClass;
 
-typedef struct _EvJobPrint EvJobPrint;
-typedef struct _EvJobPrintClass EvJobPrintClass;
-
 typedef struct _EvJobFind EvJobFind;
 typedef struct _EvJobFindClass EvJobFindClass;
 
 typedef struct _EvJobLayers EvJobLayers;
 typedef struct _EvJobLayersClass EvJobLayersClass;
 
+typedef struct _EvJobExport EvJobExport;
+typedef struct _EvJobExportClass EvJobExportClass;
+
 #define EV_TYPE_JOB		     	     (ev_job_get_type())
 #define EV_JOB(object)		             (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB, EvJob))
 #define EV_JOB_CLASS(klass)	             (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_JOB, EvJobClass))
@@ -104,11 +105,6 @@ typedef struct _EvJobLayersClass EvJobLayersClass;
 #define EV_JOB_SAVE_CLASS(klass)	     (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_JOB_SAVE, EvJobSaveClass))
 #define EV_IS_JOB_SAVE(object)		     (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB_SAVE))
 
-#define EV_TYPE_JOB_PRINT                    (ev_job_print_get_type())
-#define EV_JOB_PRINT(object)                 (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB_PRINT, EvJobPrint))
-#define EV_JOB_PRINT_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_JOB_PRINT, EvJobPrintClass))
-#define EV_IS_JOB_PRINT(object)              (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB_PRINT))
-
 #define EV_TYPE_JOB_FIND                     (ev_job_find_get_type())
 #define EV_JOB_FIND(object)                  (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB_FIND, EvJobFind))
 #define EV_JOB_FIND_CLASS(klass)             (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_JOB_FIND, EvJobFindClass))
@@ -119,6 +115,11 @@ typedef struct _EvJobLayersClass EvJobLayersClass;
 #define EV_JOB_LAYERS_CLASS(klass)           (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_JOB_LAYERS, EvJobLayersClass))
 #define EV_IS_JOB_LAYERS(object)             (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB_LAYERS))
 
+#define EV_TYPE_JOB_EXPORT                    (ev_job_export_get_type())
+#define EV_JOB_EXPORT(object)                 (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_JOB_EXPORT, EvJobExport))
+#define EV_JOB_EXPORT_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_JOB_EXPORT, EvJobExportClass))
+#define EV_IS_JOB_EXPORT(object)              (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_JOB_EXPORT))
+
 typedef enum {
 	EV_JOB_RUN_THREAD,
 	EV_JOB_RUN_MAIN_LOOP
@@ -284,28 +285,6 @@ struct _EvJobSaveClass
 	EvJobClass parent_class;
 };
 
-struct _EvJobPrint
-{
-	EvJob parent;
-
-	const gchar *format;
-	gchar  *temp_file;
-	EvPrintRange *ranges;
-	gint n_ranges;
-	EvPrintPageSet page_set;
-	gint copies;
-	gint pages_per_sheet;
-	gboolean collate;
-	gboolean reverse;
-	gdouble width;
-	gdouble height;
-};
-
-struct _EvJobPrintClass
-{
-	EvJobClass parent_class;
-};
-
 struct _EvJobFind
 {
 	EvJob parent;
@@ -340,6 +319,19 @@ struct _EvJobLayersClass
 	EvJobClass parent_class;
 };
 
+struct _EvJobExport
+{
+	EvJob parent;
+
+	gint page;
+	EvRenderContext *rc;
+};
+
+struct _EvJobExportClass
+{
+	EvJobClass parent_class;
+};
+
 /* Base job class */
 GType           ev_job_get_type           (void) G_GNUC_CONST;
 gboolean        ev_job_run                (EvJob          *job);
@@ -407,20 +399,6 @@ GType           ev_job_save_get_type      (void) G_GNUC_CONST;
 EvJob          *ev_job_save_new           (EvDocument      *document,
 					   const gchar     *uri,
 					   const gchar     *document_uri);
-
-/* EvJobPrint */
-GType           ev_job_print_get_type     (void) G_GNUC_CONST;
-EvJob          *ev_job_print_new          (EvDocument      *document,
-					   const gchar     *format,
-					   gdouble          width,
-					   gdouble          height,
-					   EvPrintRange    *ranges,
-					   gint             n_ranges,
-					   EvPrintPageSet   page_set,
-					   gint             pages_per_sheet,
-					   gint             copies,
-					   gdouble          collate,
-					   gdouble          reverse);
 /* EvJobFind */
 GType           ev_job_find_get_type      (void) G_GNUC_CONST;
 EvJob          *ev_job_find_new           (EvDocument      *document,
@@ -438,6 +416,12 @@ GList         **ev_job_find_get_results   (EvJobFind       *job);
 GType           ev_job_layers_get_type    (void) G_GNUC_CONST;
 EvJob          *ev_job_layers_new         (EvDocument     *document);
 
+/* EvJobExport */
+GType           ev_job_export_get_type    (void) G_GNUC_CONST;
+EvJob          *ev_job_export_new         (EvDocument     *document);
+void            ev_job_export_set_page    (EvJobExport    *job,
+					   gint            page);
+
 G_END_DECLS
 
 #endif /* __EV_JOBS_H__ */
diff --git a/shell/ev-print-operation.c b/shell/ev-print-operation.c
new file mode 100644
index 00000000..c71ff42c
--- /dev/null
+++ b/shell/ev-print-operation.c
@@ -0,0 +1,1043 @@
+/* this file is part of evince, a gnome document viewer
+ *
+ *  Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
+ *
+ * Evince is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Evince is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include "ev-print-operation.h"
+
+#if GTK_CHECK_VERSION (2, 14, 0)
+#include <gtk/gtkunixprint.h>
+#else
+#include <gtk/gtkprintunixdialog.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <unistd.h>
+
+#include "ev-page-cache.h"
+#include "ev-file-exporter.h"
+#include "ev-jobs.h"
+#include "ev-job-scheduler.h"
+#include "ev-application.h"
+#include "ev-file-helpers.h"
+
+enum {
+	PROP_0,
+	PROP_DOCUMENT
+};
+
+enum {
+	DONE,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _EvPrintOperation {
+	GObject parent;
+
+	EvDocument *document;
+};
+
+struct _EvPrintOperationClass {
+	GObjectClass parent_class;
+
+	void              (* set_current_page)       (EvPrintOperation       *op,
+						      gint                    current_page);
+	void              (* set_print_settings)     (EvPrintOperation       *op,
+						      GtkPrintSettings       *print_settings);
+	GtkPrintSettings *(* get_print_settings)     (EvPrintOperation       *op);
+	void              (* set_default_page_setup) (EvPrintOperation       *op,
+						      GtkPageSetup           *page_setup);
+	GtkPageSetup     *(* get_default_page_setup) (EvPrintOperation       *op);
+	void              (* set_job_name)           (EvPrintOperation       *op,
+						      const gchar            *job_name);
+	void              (* run)                    (EvPrintOperation       *op,
+						      GtkWindow              *parent);
+	void              (* cancel)                 (EvPrintOperation       *op);
+	void              (* get_error)              (EvPrintOperation       *op,
+						      GError                **error);
+
+	/* signals */
+	void              (* done)                   (EvPrintOperation       *op,
+						      GtkPrintOperationResult result);
+};
+
+G_DEFINE_ABSTRACT_TYPE (EvPrintOperation, ev_print_operation, G_TYPE_OBJECT)
+
+static void
+ev_print_operation_finalize (GObject *object)
+{
+	EvPrintOperation *op = EV_PRINT_OPERATION (object);
+
+	if (op->document) {
+		g_object_unref (op->document);
+		op->document = NULL;
+	}
+
+	(* G_OBJECT_CLASS (ev_print_operation_parent_class)->finalize) (object);
+}
+
+static void
+ev_print_operation_set_property (GObject      *object,
+				 guint         prop_id,
+				 const GValue *value,
+				 GParamSpec   *pspec)
+{
+	EvPrintOperation *op = EV_PRINT_OPERATION (object);
+
+	switch (prop_id) {
+	case PROP_DOCUMENT:
+		op->document = g_value_dup_object (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
+ev_print_operation_init (EvPrintOperation *op)
+{
+}
+
+static void
+ev_print_operation_class_init (EvPrintOperationClass *klass)
+{
+	GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
+
+	g_object_class->set_property = ev_print_operation_set_property;
+	g_object_class->finalize = ev_print_operation_finalize;
+
+	g_object_class_install_property (g_object_class,
+					 PROP_DOCUMENT,
+					 g_param_spec_object ("document",
+							      "Document",
+							      "The document to print",
+							      EV_TYPE_DOCUMENT,
+							      G_PARAM_WRITABLE |
+							      G_PARAM_CONSTRUCT_ONLY));
+	signals[DONE] =
+		g_signal_new ("done",
+			      G_TYPE_FROM_CLASS (g_object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (EvPrintOperationClass, done),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__ENUM,
+			      G_TYPE_NONE, 1,
+			      GTK_TYPE_PRINT_OPERATION_RESULT);
+	
+}
+
+/* Public methods */
+void
+ev_print_operation_set_current_page (EvPrintOperation *op,
+				     gint              current_page)
+{
+	EvPrintOperationClass *class = EV_PRINT_OPERATION_GET_CLASS (op);
+
+	g_return_if_fail (EV_IS_PRINT_OPERATION (op));
+	g_return_if_fail (current_page >= 0);
+
+	class->set_current_page (op, current_page);
+}
+
+void
+ev_print_operation_set_print_settings (EvPrintOperation *op,
+				       GtkPrintSettings *print_settings)
+{
+	EvPrintOperationClass *class = EV_PRINT_OPERATION_GET_CLASS (op);
+
+	g_return_if_fail (EV_IS_PRINT_OPERATION (op));
+	g_return_if_fail (GTK_IS_PRINT_SETTINGS (print_settings));
+
+	class->set_print_settings (op, print_settings);
+}
+
+GtkPrintSettings *
+ev_print_operation_get_print_settings (EvPrintOperation *op)
+{
+	EvPrintOperationClass *class = EV_PRINT_OPERATION_GET_CLASS (op);
+
+	g_return_val_if_fail (EV_IS_PRINT_OPERATION (op), NULL);
+
+	return class->get_print_settings (op);
+}
+
+void
+ev_print_operation_set_default_page_setup (EvPrintOperation *op,
+					   GtkPageSetup     *page_setup)
+{
+	EvPrintOperationClass *class = EV_PRINT_OPERATION_GET_CLASS (op);
+
+	g_return_if_fail (EV_IS_PRINT_OPERATION (op));
+	g_return_if_fail (GTK_IS_PAGE_SETUP (page_setup));
+
+	class->set_default_page_setup (op, page_setup);
+}
+
+GtkPageSetup *
+ev_print_operation_get_default_page_setup (EvPrintOperation *op)
+{
+	EvPrintOperationClass *class = EV_PRINT_OPERATION_GET_CLASS (op);
+
+	g_return_val_if_fail (EV_IS_PRINT_OPERATION (op), NULL);
+
+	return class->get_default_page_setup (op);
+}
+
+void
+ev_print_operation_set_job_name (EvPrintOperation *op,
+				 const gchar      *job_name)
+{
+	EvPrintOperationClass *class = EV_PRINT_OPERATION_GET_CLASS (op);
+
+	g_return_if_fail (EV_IS_PRINT_OPERATION (op));
+	g_return_if_fail (job_name != NULL);
+
+	class->set_job_name (op, job_name);
+}
+
+void
+ev_print_operation_run (EvPrintOperation *op,
+			GtkWindow        *parent)
+{
+	EvPrintOperationClass *class = EV_PRINT_OPERATION_GET_CLASS (op);
+
+	g_return_if_fail (EV_IS_PRINT_OPERATION (op));
+
+	class->run (op, parent);
+}
+
+void
+ev_print_operation_cancel (EvPrintOperation *op)
+{
+	EvPrintOperationClass *class = EV_PRINT_OPERATION_GET_CLASS (op);
+
+	g_return_if_fail (EV_IS_PRINT_OPERATION (op));
+
+	class->cancel (op);
+}
+
+void
+ev_print_operation_get_error (EvPrintOperation *op,
+			      GError          **error)
+{
+	EvPrintOperationClass *class = EV_PRINT_OPERATION_GET_CLASS (op);
+
+	g_return_if_fail (EV_IS_PRINT_OPERATION (op));
+
+	class->get_error (op, error);
+}
+
+/* Export interface */
+#define EV_TYPE_PRINT_OPERATION_EXPORT         (ev_print_operation_export_get_type())
+#define EV_PRINT_OPERATION_EXPORT(object)      (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_PRINT_OPERATION_EXPORT, EvPrintOperationExport))
+#define EV_PRINT_OPERATION_EXPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_PRINT_OPERATION_EXPORT, EvPrintOperationExportClass))
+#define EV_IS_PRINT_OPERATION_EXPORT(object)   (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_PRINT_OPERATION_EXPORT))
+
+typedef struct _EvPrintOperationExport      EvPrintOperationExport;
+typedef struct _EvPrintOperationExportClass EvPrintOperationExportClass;
+
+GType           ev_print_operation_export_get_type (void) G_GNUC_CONST;
+
+static gboolean export_print_page                  (EvPrintOperationExport *export);
+
+struct _EvPrintOperationExport {
+	EvPrintOperation parent;
+
+	GtkWindow *parent_window;
+	EvJob *job_export;
+	GError *error;
+
+	gboolean print_preview;
+	gint n_pages;
+	gint current_page;
+	GtkPrinter *printer;
+	GtkPageSetup *page_setup;
+	GtkPrintSettings *print_settings;
+	GtkPageSet page_set;
+	gint copies;
+	guint collate     : 1;
+	guint reverse     : 1;
+	gint pages_per_sheet;
+	gint fd;
+	gchar *temp_file;
+	gchar *job_name;
+	
+	guint idle_id;
+	
+	/* Context */
+	gint uncollated_copies;
+	gint collated_copies;
+	gint uncollated, collated, total;
+
+	gint range, n_ranges;
+	GtkPageRange *ranges;
+	GtkPageRange one_range;
+
+	gint page, start, end, inc;
+};
+
+struct _EvPrintOperationExportClass {
+	EvPrintOperationClass parent_class;
+};
+
+G_DEFINE_TYPE (EvPrintOperationExport, ev_print_operation_export, EV_TYPE_PRINT_OPERATION)
+
+static void
+ev_print_operation_export_set_current_page (EvPrintOperation *op,
+					    gint              current_page)
+{
+	EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (op);
+
+	g_return_if_fail (current_page < export->n_pages);
+	
+	export->current_page = current_page;
+}
+
+static void
+ev_print_operation_export_set_print_settings (EvPrintOperation *op,
+					      GtkPrintSettings *print_settings)
+{
+	EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (op);
+
+	if (print_settings == export->print_settings)
+		return;
+
+	g_object_ref (print_settings);
+	if (export->print_settings)
+		g_object_unref (export->print_settings);
+	export->print_settings = print_settings;
+}
+
+static GtkPrintSettings *
+ev_print_operation_export_get_print_settings (EvPrintOperation *op)
+{
+	EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (op);
+
+	return export->print_settings;
+}
+
+static void
+ev_print_operation_export_set_default_page_setup (EvPrintOperation *op,
+						  GtkPageSetup     *page_setup)
+{
+	EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (op);
+
+	if (page_setup == export->page_setup)
+		return;
+
+	g_object_ref (page_setup);
+	if (export->page_setup)
+		g_object_unref (export->page_setup);
+	export->page_setup = page_setup;
+}
+
+static GtkPageSetup *
+ev_print_operation_export_get_default_page_setup (EvPrintOperation *op)
+{
+	EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (op);
+
+	return export->page_setup;
+}
+
+static void
+ev_print_operation_export_set_job_name (EvPrintOperation *op,
+					const gchar      *job_name)
+{
+	EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (op);
+
+	g_free (export->job_name);
+	export->job_name = g_strdup (job_name);
+}
+
+static void
+ev_print_operation_export_set_printer (EvPrintOperationExport *export,
+				       GtkPrinter             *printer)
+{
+	if (printer == export->printer)
+		return;
+
+	g_object_ref (printer);
+	if (export->printer)
+		g_object_unref (export->printer);
+	export->printer = printer;
+}
+
+static void
+find_range (EvPrintOperationExport *export)
+{
+	GtkPageRange *range;
+
+	range = &export->ranges[export->range];
+
+	if (export->inc < 0) {
+		export->start = range->end;
+		export->end = range->start - 1;
+	} else {
+		export->start = range->start;
+		export->end = range->end + 1;
+	}
+}
+
+static void
+clamp_ranges (EvPrintOperationExport *export)
+{
+	gint num_of_correct_ranges = 0;
+	gint i;
+
+	for (i = 0; i < export->n_ranges; i++) {
+		if ((export->ranges[i].start >= 0) &&
+		    (export->ranges[i].start < export->n_pages) &&
+		    (export->ranges[i].end >= 0) &&
+		    (export->ranges[i].end < export->n_pages)) {
+			export->ranges[num_of_correct_ranges] = export->ranges[i];
+			num_of_correct_ranges++;
+		} else if ((export->ranges[i].start >= 0) &&
+			   (export->ranges[i].start < export->n_pages) &&
+			   (export->ranges[i].end >= export->n_pages)) {
+			export->ranges[i].end = export->n_pages - 1;
+			export->ranges[num_of_correct_ranges] = export->ranges[i];
+			num_of_correct_ranges++;
+		} else if ((export->ranges[i].end >= 0) &&
+			   (export->ranges[i].end < export->n_pages) &&
+			   (export->ranges[i].start < 0)) {
+			export->ranges[i].start = 0;
+			export->ranges[num_of_correct_ranges] = export->ranges[i];
+			num_of_correct_ranges++;
+		}
+	}
+
+	export->n_ranges = num_of_correct_ranges;
+}
+
+static gint
+get_first_page (EvPrintOperationExport *export)
+{
+	gint i;
+	gint first_page = G_MAXINT;
+
+	if (export->n_ranges == 0)
+		return 0;
+
+	for (i = 0; i < export->n_ranges; i++) {
+		if (export->ranges[i].start < first_page)
+			first_page = export->ranges[i].start;
+	}
+
+	return MAX (0, first_page);
+}
+
+static gint
+get_last_page (EvPrintOperationExport *export)
+{
+	gint i;
+	gint last_page = G_MININT;
+	gint max_page = export->n_pages - 1;
+
+	if (export->n_ranges == 0)
+		return max_page;
+
+	for (i = 0; i < export->n_ranges; i++) {
+		if (export->ranges[i].end > last_page)
+			last_page = export->ranges[i].end;
+	}
+
+	return MIN (max_page, last_page);
+}
+
+static gboolean
+export_print_inc_page (EvPrintOperationExport *export)
+{
+	do {
+		export->page += export->inc;
+		if (export->page == export->end) {
+			export->range += export->inc;
+			if (export->range == -1 || export->range == export->n_ranges) {
+				export->uncollated++;
+				if (export->uncollated == export->uncollated_copies)
+					return FALSE;
+
+				export->range = export->inc < 0 ? export->n_ranges - 1 : 0;
+			}
+			find_range (export);
+			export->page = export->start;
+		}
+	} while ((export->page_set == GTK_PAGE_SET_EVEN && export->page % 2 == 0) ||
+		 (export->page_set == GTK_PAGE_SET_ODD && export->page % 2 == 1));
+
+	return TRUE;
+}
+
+static void
+ev_print_operation_export_clear_temp_file (EvPrintOperationExport *export)
+{
+	if (!export->temp_file)
+		return;
+
+	g_unlink (export->temp_file);
+	g_free (export->temp_file);
+	export->temp_file = NULL;
+}
+
+static void
+print_job_finished (GtkPrintJob            *print_job,
+		    EvPrintOperationExport *export,
+		    GError                 *error)
+{
+	EvPrintOperation *op = EV_PRINT_OPERATION (export);
+	
+	if (error) {
+		g_set_error_literal (&export->error,
+				     GTK_PRINT_ERROR,
+				     GTK_PRINT_ERROR_GENERAL,
+				     error->message);
+		g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_ERROR);
+	} else {
+		g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_APPLY);
+	}
+
+	ev_print_operation_export_clear_temp_file (export);
+	g_object_unref (print_job);
+}
+
+static void
+export_print_done (EvPrintOperationExport *export)
+{
+	EvPrintOperation *op = EV_PRINT_OPERATION (export);
+	GtkPrintSettings *settings;
+	EvFileExporterCapabilities capabilities;
+
+	/* Some printers take into account some print settings,
+	 * and others don't. However we have exported the document
+	 * to a ps or pdf file according to such print settings. So,
+	 * we want to send the exported file to printer with those
+	 * settings set to default values.
+	 */
+	settings = gtk_print_settings_copy (export->print_settings);
+	capabilities = ev_file_exporter_get_capabilities (EV_FILE_EXPORTER (op->document));
+
+	gtk_print_settings_set_page_ranges (settings, NULL, 0);
+	gtk_print_settings_set_print_pages (settings, GTK_PRINT_PAGES_ALL);
+	if (capabilities & EV_FILE_EXPORTER_CAN_COPIES)
+		gtk_print_settings_set_n_copies (settings, 1);
+	if (capabilities & EV_FILE_EXPORTER_CAN_PAGE_SET)
+		gtk_print_settings_set_page_set (settings, GTK_PAGE_SET_ALL);
+	if (capabilities & EV_FILE_EXPORTER_CAN_SCALE)
+		gtk_print_settings_set_scale (settings, 1.0);
+	if (capabilities & EV_FILE_EXPORTER_CAN_COLLATE)
+		gtk_print_settings_set_collate (settings, FALSE);
+	if (capabilities & EV_FILE_EXPORTER_CAN_REVERSE)
+		gtk_print_settings_set_reverse (settings, FALSE);
+	if (capabilities & EV_FILE_EXPORTER_CAN_NUMBER_UP) {
+		gtk_print_settings_set_number_up (settings, 1);
+		gtk_print_settings_set_int (settings, "cups-"GTK_PRINT_SETTINGS_NUMBER_UP, 1);
+	}
+
+	if (export->print_preview) {
+		gchar *uri;
+		gchar *print_settings_file = NULL;
+
+		print_settings_file = ev_tmp_filename ("print-settings");
+		gtk_print_settings_to_file (settings, print_settings_file, NULL);
+
+		uri = g_filename_to_uri (export->temp_file, NULL, NULL);
+		ev_application_open_uri_at_dest (EV_APP,
+						 uri,
+						 gtk_window_get_screen (export->parent_window),
+						 NULL,
+						 EV_WINDOW_MODE_PREVIEW,
+						 NULL,
+						 TRUE,
+						 print_settings_file,
+						 GDK_CURRENT_TIME);
+		g_free (print_settings_file);
+		g_free (uri);
+
+		g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_APPLY);
+		/* temp_file will be deleted by the previewer */
+	} else {
+		GtkPrintJob *job;
+		GError      *error = NULL;
+		
+		job = gtk_print_job_new (export->job_name,
+					 export->printer,
+					 settings,
+					 export->page_setup);
+		gtk_print_job_set_source_file (job, export->temp_file, &error);
+		if (error) {
+			g_set_error_literal (&export->error,
+					     GTK_PRINT_ERROR,
+					     GTK_PRINT_ERROR_GENERAL,
+					     error->message);
+			g_error_free (error);
+			ev_print_operation_export_clear_temp_file (export);
+			g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_ERROR);
+		} else {
+			gtk_print_job_send (job,
+					    (GtkPrintJobCompleteFunc)print_job_finished,
+					    g_object_ref (export),
+					    (GDestroyNotify)g_object_unref);
+		}
+	}
+	g_object_unref (settings);
+}
+
+static void
+export_print_page_idle_finished (EvPrintOperationExport *export)
+{
+	export->idle_id = 0;
+}
+
+static void
+export_job_finished (EvJobExport            *job,
+		     EvPrintOperationExport *export)
+{
+	EvPrintOperation *op = EV_PRINT_OPERATION (export);
+
+	if (export->pages_per_sheet == 1 || export->total % export->pages_per_sheet == 0) {
+		ev_document_doc_mutex_lock ();
+		ev_file_exporter_end_page (EV_FILE_EXPORTER (op->document));
+		ev_document_doc_mutex_unlock ();
+	}
+
+	/* Reschedule */
+	export->idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+					   (GSourceFunc)export_print_page,
+					   export,
+					   (GDestroyNotify)export_print_page_idle_finished);
+}
+
+static void
+export_job_cancelled (EvJobExport            *job,
+		      EvPrintOperationExport *export)
+{
+	EvPrintOperation *op = EV_PRINT_OPERATION (export);
+
+	if (export->idle_id > 0)
+		g_source_remove (export->idle_id);
+	export->idle_id = 0;
+
+	g_signal_handlers_disconnect_by_func (export->job_export,
+					      export_job_finished,
+					      export);
+	g_signal_handlers_disconnect_by_func (export->job_export,
+					      export_job_cancelled,
+					      export);
+	g_object_unref (export->job_export);
+	export->job_export = NULL;
+
+	if (export->fd != -1) {
+		close (export->fd);
+		export->fd = -1;
+	}
+
+	ev_print_operation_export_clear_temp_file (export);
+
+	g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_CANCEL);
+}
+
+static gboolean
+export_print_page (EvPrintOperationExport *export)
+{
+	EvPrintOperation *op = EV_PRINT_OPERATION (export);
+	
+	export->total++;
+	export->collated++;
+
+	if (export->collated == export->collated_copies) {
+		export->collated = 0;
+		if (!export_print_inc_page (export)) {
+			ev_document_doc_mutex_lock ();
+			if (export->pages_per_sheet > 1 &&
+			    export->total - 1 % export->pages_per_sheet == 0)
+				ev_file_exporter_end_page (EV_FILE_EXPORTER (op->document));
+			ev_file_exporter_end (EV_FILE_EXPORTER (op->document));
+			ev_document_doc_mutex_unlock ();
+
+			close (export->fd);
+			export->fd = -1;
+
+			export_print_done (export);
+			
+			return FALSE;
+		}
+	}
+
+	if (export->pages_per_sheet == 1 || export->total % export->pages_per_sheet == 1) {
+		ev_document_doc_mutex_lock ();
+		ev_file_exporter_begin_page (EV_FILE_EXPORTER (op->document));
+		ev_document_doc_mutex_unlock ();
+	}
+	
+	if (!export->job_export) {
+		export->job_export = ev_job_export_new (op->document);
+		g_signal_connect (G_OBJECT (export->job_export), "finished",
+				  G_CALLBACK (export_job_finished),
+				  (gpointer)export);
+		g_signal_connect (G_OBJECT (export->job_export), "cancelled",
+				  G_CALLBACK (export_job_cancelled),
+				  (gpointer)export);
+	}
+
+	ev_job_export_set_page (EV_JOB_EXPORT (export->job_export), export->page);
+	ev_job_scheduler_push_job (export->job_export, EV_JOB_PRIORITY_NONE);
+	
+	return FALSE;
+}
+
+static gboolean
+ev_print_operation_export_print_dialog_response_cb (GtkDialog              *dialog,
+						    gint                    response,
+						    EvPrintOperationExport *export)
+{
+	GtkPrintPages         print_pages;
+	GtkPrintSettings     *print_settings;
+	GtkPageSetup         *page_setup;
+	GtkPrinter           *printer;
+	gdouble               scale;
+	gdouble               width;
+	gdouble               height;
+	gint                  first_page;
+	gint                  last_page;
+	const gchar          *file_format;
+	gchar                *filename;
+	EvFileExporterContext fc;
+	GError               *error = NULL;
+	EvPrintOperation     *op = EV_PRINT_OPERATION (export);
+	
+	if (response != GTK_RESPONSE_OK &&
+	    response != GTK_RESPONSE_APPLY) {
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+
+		return FALSE;
+	}
+
+	export->print_preview = (response == GTK_RESPONSE_APPLY);
+	
+	printer = gtk_print_unix_dialog_get_selected_printer (GTK_PRINT_UNIX_DIALOG (dialog));
+	ev_print_operation_export_set_printer (export, printer);
+
+	print_settings = gtk_print_unix_dialog_get_settings (GTK_PRINT_UNIX_DIALOG (dialog));
+	ev_print_operation_export_set_print_settings (op, print_settings);
+
+	page_setup = gtk_print_unix_dialog_get_page_setup (GTK_PRINT_UNIX_DIALOG (dialog));
+	ev_print_operation_export_set_default_page_setup (op, page_setup);
+
+	if (!gtk_printer_accepts_ps (export->printer)) {
+		g_set_error (&export->error,
+			     GTK_PRINT_ERROR,
+			     GTK_PRINT_ERROR_GENERAL,
+			     "%s", _("Printing is not supported on this printer."));
+		g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_ERROR);
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		
+		return FALSE;
+	}
+
+	file_format = gtk_print_settings_get (print_settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT);
+	
+	filename = g_strdup_printf ("evince_print.%s.XXXXXX", file_format);
+	export->fd = g_file_open_tmp (filename, &export->temp_file, &error);
+	g_free (filename);
+	if (export->fd <= -1) {
+		g_set_error_literal (&export->error,
+				     GTK_PRINT_ERROR,
+				     GTK_PRINT_ERROR_GENERAL,
+				     error->message);
+		g_error_free (error);
+		g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_ERROR);
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+
+		return FALSE;
+	}
+
+	export->current_page = gtk_print_unix_dialog_get_current_page (GTK_PRINT_UNIX_DIALOG (dialog));
+	print_pages = gtk_print_settings_get_print_pages (print_settings);
+	
+	switch (print_pages) {
+	case GTK_PRINT_PAGES_CURRENT:
+		export->ranges = &export->one_range;
+		
+		export->ranges[0].start = export->current_page;
+		export->ranges[0].end = export->current_page;
+		export->n_ranges = 1;
+				
+		break;
+	case GTK_PRINT_PAGES_RANGES: {
+		gint i;
+		
+		export->ranges = gtk_print_settings_get_page_ranges (print_settings, &export->n_ranges);
+		for (i = 0; i < export->n_ranges; i++)
+			if (export->ranges[i].end == -1 || export->ranges[i].end >= export->n_pages)
+				export->ranges[i].end = export->n_pages - 1;
+	}
+		break;
+	case GTK_PRINT_PAGES_ALL:
+		export->ranges = &export->one_range;
+
+		export->ranges[0].start = 0;
+		export->ranges[0].end = export->n_pages - 1;
+		export->n_ranges = 1;
+		
+		break;
+	}
+	clamp_ranges (export);
+
+	export->page_set = gtk_print_settings_get_page_set (print_settings);
+
+	width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_POINTS);
+	height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS);
+	scale = gtk_print_settings_get_scale (print_settings) * 0.01;
+	if (scale != 1.0) {
+		width *= scale;
+		height *= scale;
+	}
+
+	export->pages_per_sheet = gtk_print_settings_get_number_up (print_settings);
+	
+	export->copies = gtk_print_settings_get_n_copies (print_settings);
+	export->collate = gtk_print_settings_get_collate (print_settings);
+	export->reverse = gtk_print_settings_get_reverse (print_settings);
+
+	if (export->collate) {
+		export->uncollated_copies = export->copies;
+		export->collated_copies = 1;
+	} else {
+		export->uncollated_copies = 1;
+		export->collated_copies = export->copies;
+	}
+
+	if (export->reverse) {
+		export->range = export->n_ranges - 1;
+		export->inc = -1;
+	} else {
+		export->range = 0;
+		export->inc = 1;
+	}
+	find_range (export);
+
+	export->page = export->start - export->inc;
+	export->collated = export->collated_copies - 1;
+
+	first_page = get_first_page (export);
+	last_page = get_last_page (export);
+
+	fc.format = g_ascii_strcasecmp (file_format, "pdf") == 0 ?
+		EV_FILE_FORMAT_PDF : EV_FILE_FORMAT_PS;
+	fc.filename = export->temp_file;
+	fc.first_page = MIN (first_page, last_page);
+	fc.last_page = MAX (first_page, last_page);
+	fc.paper_width = width;
+	fc.paper_height = height;
+	fc.duplex = FALSE;
+	fc.pages_per_sheet = MAX (1, export->pages_per_sheet);
+
+	ev_document_doc_mutex_lock ();
+	ev_file_exporter_begin (EV_FILE_EXPORTER (op->document), &fc);
+	ev_document_doc_mutex_unlock ();
+
+	export->idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+					   (GSourceFunc)export_print_page,
+					   export,
+					   (GDestroyNotify)export_print_page_idle_finished);
+	
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+
+	return TRUE;
+}
+
+static void
+ev_print_operation_export_run (EvPrintOperation *op,
+			       GtkWindow        *parent)
+{
+	EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (op);
+	GtkWidget              *dialog;
+	GtkPrintCapabilities    capabilities;
+
+	export->parent_window = parent;
+	export->error = NULL;
+	
+	dialog = gtk_print_unix_dialog_new (_("Print"), parent);
+	gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+	
+	capabilities = GTK_PRINT_CAPABILITY_PREVIEW |
+		ev_file_exporter_get_capabilities (EV_FILE_EXPORTER (op->document));
+	gtk_print_unix_dialog_set_manual_capabilities (GTK_PRINT_UNIX_DIALOG (dialog),
+						       capabilities);
+
+	gtk_print_unix_dialog_set_current_page (GTK_PRINT_UNIX_DIALOG (dialog),
+						export->current_page);
+	
+	gtk_print_unix_dialog_set_settings (GTK_PRINT_UNIX_DIALOG (dialog),
+					    export->print_settings);
+	
+	if (export->page_setup)
+		gtk_print_unix_dialog_set_page_setup (GTK_PRINT_UNIX_DIALOG (dialog),
+						      export->page_setup);
+	
+	g_signal_connect (G_OBJECT (dialog), "response",
+			  G_CALLBACK (ev_print_operation_export_print_dialog_response_cb),
+			  export);
+
+	gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+ev_print_operation_export_cancel (EvPrintOperation *op)
+{
+	EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (op);
+
+	if (export->job_export) {
+		ev_job_cancel (export->job_export);
+	}
+}
+
+static void
+ev_print_operation_export_get_error (EvPrintOperation *op,
+				     GError          **error)
+{
+	EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (op);
+
+	g_propagate_error (error, export->error);
+	export->error = NULL;
+}
+
+static void
+ev_print_operation_export_finalize (GObject *object)
+{
+	EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (object);
+
+	if (export->idle_id > 0) {
+		g_source_remove (export->idle_id);
+		export->idle_id = 0;
+	}
+
+	if (export->fd != -1) {
+		close (export->fd);
+		export->fd = -1;
+	}
+	
+	if (export->ranges) {
+		if (export->ranges != &export->one_range)
+			g_free (export->ranges);
+		export->ranges = NULL;
+		export->n_ranges = 0;
+	}
+
+	if (export->temp_file) {
+		g_free (export->temp_file);
+		export->temp_file = NULL;
+	}
+
+	if (export->job_name) {
+		g_free (export->job_name);
+		export->job_name = NULL;
+	}
+
+	if (export->job_export) {
+		if (!ev_job_is_finished (export->job_export))
+			ev_job_cancel (export->job_export);
+		g_signal_handlers_disconnect_by_func (export->job_export,
+						      export_job_finished,
+						      export);
+		g_signal_handlers_disconnect_by_func (export->job_export,
+						      export_job_cancelled,
+						      export);
+		g_object_unref (export->job_export);
+		export->job_export = NULL;
+	}
+
+	if (export->error) {
+		g_error_free (export->error);
+		export->error = NULL;
+	}
+
+	if (export->print_settings) {
+		g_object_unref (export->print_settings);
+		export->print_settings = NULL;
+	}
+
+	if (export->page_setup) {
+		g_object_unref (export->page_setup);
+		export->page_setup = NULL;
+	}
+
+	if (export->printer) {
+		g_object_unref (export->printer);
+		export->printer = NULL;
+	}
+
+	(* G_OBJECT_CLASS (ev_print_operation_export_parent_class)->finalize) (object);
+}
+
+static void
+ev_print_operation_export_init (EvPrintOperationExport *export)
+{
+}
+
+static GObject *
+ev_print_operation_export_constructor (GType                  type,
+				       guint                  n_construct_properties,
+				       GObjectConstructParam *construct_params)
+{
+	GObject                *object;
+	EvPrintOperationExport *export;
+	EvPrintOperation       *op;
+	
+	object = G_OBJECT_CLASS (ev_print_operation_export_parent_class)->constructor (type,
+										       n_construct_properties,
+										       construct_params);
+	export = EV_PRINT_OPERATION_EXPORT (object);
+	op = EV_PRINT_OPERATION (object);
+	export->n_pages = ev_page_cache_get_n_pages (ev_page_cache_get (op->document));
+
+	return object;
+}
+
+static void
+ev_print_operation_export_class_init (EvPrintOperationExportClass *klass)
+{
+	GObjectClass          *g_object_class = G_OBJECT_CLASS (klass);
+	EvPrintOperationClass *ev_print_op_class = EV_PRINT_OPERATION_CLASS (klass);
+
+	ev_print_op_class->set_current_page = ev_print_operation_export_set_current_page;
+	ev_print_op_class->set_print_settings = ev_print_operation_export_set_print_settings;
+	ev_print_op_class->get_print_settings = ev_print_operation_export_get_print_settings;
+	ev_print_op_class->set_default_page_setup = ev_print_operation_export_set_default_page_setup;
+	ev_print_op_class->get_default_page_setup = ev_print_operation_export_get_default_page_setup;
+	ev_print_op_class->set_job_name = ev_print_operation_export_set_job_name;
+	ev_print_op_class->run = ev_print_operation_export_run;
+	ev_print_op_class->cancel = ev_print_operation_export_cancel;
+	ev_print_op_class->get_error = ev_print_operation_export_get_error;
+
+	g_object_class->constructor = ev_print_operation_export_constructor;
+	g_object_class->finalize = ev_print_operation_export_finalize;
+}
+
+/* Factory method */
+EvPrintOperation *
+ev_print_operation_new (EvDocument *document)
+{
+	/* TODO: EvPrintOperationPrint */
+
+	return EV_PRINT_OPERATION (g_object_new (EV_TYPE_PRINT_OPERATION_EXPORT,
+						 "document", document, NULL));
+}
diff --git a/shell/ev-print-operation.h b/shell/ev-print-operation.h
new file mode 100644
index 00000000..8e7d6f85
--- /dev/null
+++ b/shell/ev-print-operation.h
@@ -0,0 +1,59 @@
+/* this file is part of evince, a gnome document viewer
+ *
+ *  Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
+ *
+ * Evince is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Evince is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EV_PRINT_OPERATION_H__
+#define __EV_PRINT_OPERATION_H__
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+#include "ev-document.h"
+
+G_BEGIN_DECLS
+
+typedef struct _EvPrintOperation      EvPrintOperation;
+typedef struct _EvPrintOperationClass EvPrintOperationClass;
+
+#define EV_TYPE_PRINT_OPERATION              (ev_print_operation_get_type())
+#define EV_PRINT_OPERATION(object)           (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_PRINT_OPERATION, EvPrintOperation))
+#define EV_PRINT_OPERATION_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_PRINT_OPERATION, EvPrintOperationClass))
+#define EV_IS_PRINT_OPERATION(object)        (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_PRINT_OPERATION))
+#define EV_PRINT_OPERATION_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_PRINT_OPERATION, EvPrintOperationClass))
+
+GType             ev_print_operation_get_type               (void) G_GNUC_CONST;
+
+EvPrintOperation *ev_print_operation_new                    (EvDocument       *document);
+void              ev_print_operation_set_current_page       (EvPrintOperation *op,
+							     gint              current_page);
+void              ev_print_operation_set_print_settings     (EvPrintOperation *op,
+							     GtkPrintSettings *print_settings);
+GtkPrintSettings *ev_print_operation_get_print_settings     (EvPrintOperation *op);
+void              ev_print_operation_set_default_page_setup (EvPrintOperation *op,
+							     GtkPageSetup     *page_setup);
+GtkPageSetup     *ev_print_operation_get_default_page_setup (EvPrintOperation *op);
+void              ev_print_operation_set_job_name           (EvPrintOperation *op,
+							     const gchar      *job_name);
+void              ev_print_operation_run                    (EvPrintOperation *op,
+							     GtkWindow        *parent);
+void              ev_print_operation_cancel                 (EvPrintOperation *op);
+void              ev_print_operation_get_error              (EvPrintOperation *op,
+							     GError          **error);
+G_END_DECLS
+	
+#endif /* __EV_PRINT_OPERATION_H__ */
diff --git a/shell/ev-window.c b/shell/ev-window.c
index e3c225c8..324cddad 100644
--- a/shell/ev-window.c
+++ b/shell/ev-window.c
@@ -87,6 +87,7 @@
 #include "ev-view.h"
 #include "ev-window.h"
 #include "ev-window-title.h"
+#include "ev-print-operation.h"
 
 #ifdef ENABLE_DBUS
 #include "ev-media-player-keys.h"
@@ -184,12 +185,10 @@ struct _EvWindowPrivate {
 	EvJob            *reload_job;
 	EvJob            *thumbnail_job;
 	EvJob            *save_job;
-	EvJob            *print_job;
 	EvJob            *find_job;
 
 	/* Printing */
 	gboolean          print_preview;
-	GtkPrintJob      *gtk_print_job;
 	GtkPrinter       *printer;
 	GtkPrintSettings *print_settings;
 	GtkPageSetup     *print_page_setup;
@@ -239,8 +238,6 @@ static void     ev_window_reload_job_cb                 (EvJob            *job,
 							 EvWindow         *window);
 static void     ev_window_set_icon_from_thumbnail       (EvJobThumbnail   *job,
 							 EvWindow         *ev_window);
-static void     ev_window_print_job_cb                  (EvJob            *job,
-							 EvWindow         *window);
 static void     ev_window_save_job_cb                   (EvJob            *save,
 							 EvWindow         *window);
 static void     ev_window_sizing_mode_changed_cb        (EvView           *view,
@@ -2291,26 +2288,6 @@ ev_window_cmd_file_print_setup (GtkAction *action, EvWindow *ev_window)
 		ev_window);
 }
 
-static void
-ev_window_clear_print_job (EvWindow *window)
-{
-	if (window->priv->print_job) {
-		if (!ev_job_is_finished (window->priv->print_job))
-			ev_job_cancel (window->priv->print_job);
-
-		g_signal_handlers_disconnect_by_func (window->priv->print_job,
-						      ev_window_print_job_cb,
-						      window);
-		g_object_unref (window->priv->print_job);
-		window->priv->print_job = NULL;
-	}
-
-	if (window->priv->gtk_print_job) {
-		g_object_unref (window->priv->gtk_print_job);
-		window->priv->gtk_print_job = NULL;
-	}
-}
-
 static void
 ev_window_load_print_settings_from_metadata (EvWindow *window)
 {
@@ -2348,267 +2325,58 @@ ev_window_save_print_settings (EvWindow *window)
 }
 
 static void
-ev_window_print_finished (GtkPrintJob *print_job,
-			  EvWindow    *window,
-			  GError      *error)
-{
-	ev_window_clear_print_job (window);
-	
-	if (error) {
-		ev_window_error_message (window, error,
-					 "%s", _("Failed to print document"));
-	} else {
-		/* If printed successfully, save print settings */
-		ev_application_set_print_settings (EV_APP,
-						   window->priv->print_settings);
-		ev_window_save_print_settings (window);
-	}
-}
-
-static void
-ev_window_print_send (EvWindow    *window,
-		      const gchar *filename)
-{
-	GtkPrintSettings *settings;
-	EvFileExporterCapabilities capabilities;
-	
-	/* Some printers take into account some print settings,
-	 * and others don't. However we have exported the document
-	 * to a ps or pdf file according to such print settings. So,
-	 * we want to send the exported file to printer with those
-	 * settings set to default values. 
-	 */
-	settings = gtk_print_settings_copy (window->priv->print_settings);
-	capabilities = ev_file_exporter_get_capabilities (EV_FILE_EXPORTER (window->priv->document));
-
-	gtk_print_settings_set_page_ranges (settings, NULL, 0);
-	gtk_print_settings_set_print_pages (settings, GTK_PRINT_PAGES_ALL);
-	if (capabilities & EV_FILE_EXPORTER_CAN_COPIES)
-		gtk_print_settings_set_n_copies (settings, 1);
-	if (capabilities & EV_FILE_EXPORTER_CAN_PAGE_SET)
-		gtk_print_settings_set_page_set (settings, GTK_PAGE_SET_ALL);
-	if (capabilities & EV_FILE_EXPORTER_CAN_SCALE)
-		gtk_print_settings_set_scale (settings, 1.0);
-	if (capabilities & EV_FILE_EXPORTER_CAN_COLLATE)
-		gtk_print_settings_set_collate (settings, FALSE);
-	if (capabilities & EV_FILE_EXPORTER_CAN_REVERSE)
-		gtk_print_settings_set_reverse (settings, FALSE);
-	if (capabilities & EV_FILE_EXPORTER_CAN_NUMBER_UP) {
-		gtk_print_settings_set_number_up (settings, 1);
-		gtk_print_settings_set_int (settings, "cups-"GTK_PRINT_SETTINGS_NUMBER_UP, 1);
-	}
-	
-	if (window->priv->print_preview) {
-		gchar *uri;
-		gchar *print_settings_file = NULL;
-
-		ev_application_set_print_settings (EV_APP,
-						   window->priv->print_settings);
-		
-		print_settings_file = ev_tmp_filename ("print-settings");
-		gtk_print_settings_to_file (settings, print_settings_file, NULL);
-
-		uri = g_filename_to_uri (filename, NULL, NULL);
-		ev_application_open_uri_at_dest (EV_APP,
-						 uri, 
-						 gtk_window_get_screen (GTK_WINDOW (window)),
-						 NULL,
-						 EV_WINDOW_MODE_PREVIEW,
-						 NULL, 
-						 TRUE,
-						 print_settings_file,
-						 GDK_CURRENT_TIME);
-		g_free (print_settings_file);
-		g_free (uri);
-	} else {
-		GtkPrintJob *job;
-		GError      *error = NULL;
-	
-		job = gtk_print_job_new (gtk_window_get_title (GTK_WINDOW (window)),
-					 window->priv->printer,
-					 settings,
-					 window->priv->print_page_setup);
-
-		if (window->priv->gtk_print_job)
-			g_object_unref (window->priv->gtk_print_job);
-		window->priv->gtk_print_job = job;
-
-		if (gtk_print_job_set_source_file (job, filename, &error)) {
-			gtk_print_job_send (job,
-					    (GtkPrintJobCompleteFunc)ev_window_print_finished,
-					    window, NULL);
-		} else {
-			ev_window_clear_print_job (window);
-			g_warning ("%s", error->message);
-			g_error_free (error);
-		}
-	}
-
-	g_object_unref (settings);
-}
-
-static void
-ev_window_print_job_cb (EvJob    *job,
-			EvWindow *window)
+ev_window_print_operation_done (EvPrintOperation       *op,
+				GtkPrintOperationResult result,
+				EvWindow               *ev_window)
 {
-	if (ev_job_is_failed (job)) {
-		g_warning ("%s", job->error->message);
-		ev_window_clear_print_job (window);
-		return;
-	}
-
-	g_assert (EV_JOB_PRINT (job)->temp_file != NULL);
-
-	ev_window_print_send (window, EV_JOB_PRINT (job)->temp_file);
-}
-
-static gboolean
-ev_window_print_dialog_response_cb (GtkDialog *dialog,
-				    gint       response,
-				    EvWindow  *window)
-{
-	EvPrintRange  *ranges = NULL;
-	EvPrintPageSet page_set;
-	gint           n_ranges = 0;
-	gint           copies;
-	gint           pages_per_sheet;
-	gboolean       collate;
-	gboolean       reverse;
-	gdouble        scale;
-	gint           current_page;
-	gdouble        width;
-	gdouble        height;
-	GtkPrintPages  print_pages;
-	const gchar   *file_format;
-	
-	if (response != GTK_RESPONSE_OK &&
-	    response != GTK_RESPONSE_APPLY) {
-		gtk_widget_destroy (GTK_WIDGET (dialog));
-		window->priv->print_dialog = NULL;
-
-		return FALSE;
-	}
-
-	window->priv->print_preview = (response == GTK_RESPONSE_APPLY);
-
-	if (window->priv->printer)
-		g_object_unref (window->priv->printer);
-	if (window->priv->print_settings)
-		g_object_unref (window->priv->print_settings);
-	if (window->priv->print_page_setup)
-		g_object_unref (window->priv->print_page_setup);
-	
-	window->priv->printer = g_object_ref (
-		gtk_print_unix_dialog_get_selected_printer (GTK_PRINT_UNIX_DIALOG (dialog)));
-	window->priv->print_settings = g_object_ref (
-		gtk_print_unix_dialog_get_settings (GTK_PRINT_UNIX_DIALOG (dialog)));
-	window->priv->print_page_setup = g_object_ref (
-		gtk_print_unix_dialog_get_page_setup (GTK_PRINT_UNIX_DIALOG (dialog)));
-
-	file_format = gtk_print_settings_get (window->priv->print_settings,
-					      GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT);
-	
-	if (!gtk_printer_accepts_ps (window->priv->printer)) {
-		ev_window_error_message (window, NULL, "%s",
-					 _("Printing is not supported on this printer."));
-		return FALSE;
-	}
+	switch (result) {
+	case GTK_PRINT_OPERATION_RESULT_APPLY: {
+		GtkPrintSettings *print_settings;
 
-	ev_window_clear_print_job (window);
-	
-	current_page = gtk_print_unix_dialog_get_current_page (GTK_PRINT_UNIX_DIALOG (dialog));
-	print_pages = gtk_print_settings_get_print_pages (window->priv->print_settings);
-	
-	switch (print_pages) {
-	case GTK_PRINT_PAGES_CURRENT:
-		ranges = g_new0 (EvPrintRange, 1);
-		
-		ranges->start = current_page;
-		ranges->end = current_page;
-		n_ranges = 1;
-				
-		break;
-	case GTK_PRINT_PAGES_RANGES: {
-		GtkPageRange *page_range;
+		if (ev_window->priv->print_settings)
+			g_object_unref (ev_window->priv->print_settings);
+		print_settings = ev_print_operation_get_print_settings (op);
 		
-		page_range = gtk_print_settings_get_page_ranges (window->priv->print_settings,
-								 &n_ranges);
-		if (n_ranges > 0)
-			ranges = g_memdup (page_range, n_ranges * sizeof (GtkPageRange));
+		ev_application_set_print_settings (EV_APP, print_settings);
+		ev_window->priv->print_settings = g_object_ref (print_settings);
+		ev_window_save_print_settings (ev_window);
 	}
 		break;
-	case GTK_PRINT_PAGES_ALL: {
-		gint n_pages;
-
-		n_pages = ev_page_cache_get_n_pages (ev_page_cache_get (window->priv->document));
-		
-		ranges = g_new0 (EvPrintRange, 1);
+	case GTK_PRINT_OPERATION_RESULT_ERROR: {
+		GError *error = NULL;
 
-		ranges->start = 0;
-		ranges->end = n_pages - 1;
-		n_ranges = 1;
+		ev_print_operation_get_error (op, &error);
+		ev_window_error_message (ev_window, error,
+					 "%s", _("Failed to print document"));
+		g_error_free (error);
 	}
 		break;
+	case GTK_PRINT_OPERATION_RESULT_CANCEL:
+	default:
+		break;
 	}
 
-	page_set = (EvPrintPageSet)gtk_print_settings_get_page_set (window->priv->print_settings);
-
-	scale = gtk_print_settings_get_scale (window->priv->print_settings) * 0.01;
-	
-	width = gtk_page_setup_get_paper_width (window->priv->print_page_setup,
-						GTK_UNIT_POINTS);
-	height = gtk_page_setup_get_paper_height (window->priv->print_page_setup,
-						  GTK_UNIT_POINTS);
-	
-	if (scale != 1.0) {
-		width *= scale;
-		height *= scale;
-	}
-
-	pages_per_sheet = gtk_print_settings_get_number_up (window->priv->print_settings);
-	
-	copies = gtk_print_settings_get_n_copies (window->priv->print_settings);
-	collate = gtk_print_settings_get_collate (window->priv->print_settings);
-	reverse = gtk_print_settings_get_reverse (window->priv->print_settings);
-	
-	window->priv->print_job = ev_job_print_new (window->priv->document,
-						    file_format ? file_format : "ps",
-						    width, height,
-						    ranges, n_ranges,
-						    page_set,
-						    pages_per_sheet,
-						    copies, collate,
-						    reverse);
-	
-	g_signal_connect (window->priv->print_job, "finished",
-			  G_CALLBACK (ev_window_print_job_cb),
-			  window);
-	/* The priority doesn't matter for this job */
-	ev_job_scheduler_push_job (window->priv->print_job, EV_JOB_PRIORITY_NONE);
-	
-	gtk_widget_destroy (GTK_WIDGET (dialog));
-	window->priv->print_dialog = NULL;
-
-	return TRUE;
+	g_object_unref (op);
 }
 
 void
-ev_window_print_range (EvWindow *ev_window, int first_page, int last_page)
+ev_window_print_range (EvWindow *ev_window,
+		       gint      first_page,
+		       gint      last_page)
 {
-	GtkWidget           *dialog;
+	EvPrintOperation    *op;
 	EvPageCache         *page_cache;
 	gint                 current_page;
 	gint                 document_last_page;
-	GtkPrintCapabilities capabilities;
 
 	g_return_if_fail (EV_IS_WINDOW (ev_window));
 	g_return_if_fail (ev_window->priv->document != NULL);
 
-	if (ev_window->priv->print_dialog) {
-		gtk_window_present (GTK_WINDOW (ev_window->priv->print_dialog));
-		return;
-	}
-	
+	op = ev_print_operation_new (ev_window->priv->document);
+	g_signal_connect (G_OBJECT (op), "done",
+			  G_CALLBACK (ev_window_print_operation_done),
+			  (gpointer)ev_window);
+
 	page_cache = ev_page_cache_get (ev_window->priv->document);
 	current_page = ev_page_cache_get_current_page (page_cache);
 	document_last_page = ev_page_cache_get_n_pages (page_cache);
@@ -2632,29 +2400,13 @@ ev_window_print_range (EvWindow *ev_window, int first_page, int last_page)
 						    &range, 1);
 	}
 
-	dialog = gtk_print_unix_dialog_new (_("Print"), GTK_WINDOW (ev_window));
-	ev_window->priv->print_dialog = dialog;
-	
-	capabilities = GTK_PRINT_CAPABILITY_PREVIEW |
-		ev_file_exporter_get_capabilities (EV_FILE_EXPORTER (ev_window->priv->document));
-	gtk_print_unix_dialog_set_manual_capabilities (GTK_PRINT_UNIX_DIALOG (dialog),
-						       capabilities);
-
-	gtk_print_unix_dialog_set_current_page (GTK_PRINT_UNIX_DIALOG (dialog),
-						current_page);
-	
-	gtk_print_unix_dialog_set_settings (GTK_PRINT_UNIX_DIALOG (dialog),
-					    ev_window->priv->print_settings);
-	
+	ev_print_operation_set_job_name (op, gtk_window_get_title (GTK_WINDOW (ev_window)));
+	ev_print_operation_set_current_page (op, current_page);
+	ev_print_operation_set_print_settings (op, ev_window->priv->print_settings);
 	if (ev_window->priv->print_page_setup)
-		gtk_print_unix_dialog_set_page_setup (GTK_PRINT_UNIX_DIALOG (dialog),
-						      ev_window->priv->print_page_setup);
-	
-	g_signal_connect (G_OBJECT (dialog), "response",
-			  G_CALLBACK (ev_window_print_dialog_response_cb),
-			  ev_window);
+		ev_print_operation_set_default_page_setup (op, ev_window->priv->print_page_setup);
 
-	gtk_widget_show (dialog);
+	ev_print_operation_run (op, GTK_WINDOW (ev_window));
 }
 
 static void
@@ -4395,13 +4147,7 @@ ev_window_dispose (GObject *object)
 	}
 
 	ev_window_close_dialogs (window);
-	ev_window_clear_print_job (window);
 
-	if (window->priv->gtk_print_job) {
-		g_object_unref (window->priv->gtk_print_job);
-		window->priv->gtk_print_job = NULL;
-	}
-	
 	if (window->priv->printer) {
 		g_object_unref (window->priv->printer);
 		window->priv->printer = NULL;
-- 
2.43.5