Index: libeog/eog-job-queue.c
===================================================================
RCS file: /cvs/gnome/eog/libeog/Attic/eog-job-queue.c,v
retrieving revision 1.1.2.4
diff -u -r1.1.2.4 eog-job-queue.c
--- libeog/eog-job-queue.c	9 Oct 2006 13:14:59 -0000	1.1.2.4
+++ libeog/eog-job-queue.c	25 Nov 2006 14:00:21 -0000
@@ -32,6 +32,7 @@
 static GQueue *load_queue = NULL;
 static GQueue *model_queue = NULL;
 static GQueue *transform_queue = NULL;
+static GQueue *save_queue = NULL;
 
 static gboolean
 remove_job_from_queue (GQueue *queue, EogJob *job)
@@ -79,6 +80,8 @@
 		eog_job_model_run (EOG_JOB_MODEL (job));
 	else if (EOG_IS_JOB_TRANSFORM (job))
 		eog_job_transform_run (EOG_JOB_TRANSFORM (job));
+	else if (EOG_IS_JOB_SAVE (job))
+		eog_job_save_run (EOG_JOB_SAVE (job));
 
 	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
 			 (GSourceFunc) notify_finished,
@@ -92,7 +95,8 @@
 	return  g_queue_is_empty (load_queue)      &&
 		g_queue_is_empty (transform_queue) &&
 		g_queue_is_empty (thumbnail_queue) &&
-		g_queue_is_empty (model_queue);
+		g_queue_is_empty (model_queue) &&
+		g_queue_is_empty (save_queue);
 }
 
 static EogJob *
@@ -115,6 +119,10 @@
 	job = (EogJob *) g_queue_pop_head (model_queue);
 	if (job)
 		return job;
+	
+	job = (EogJob *) g_queue_pop_head (save_queue);
+	if (job)
+		return job;
 
 	return NULL;
 }
@@ -157,6 +165,7 @@
 	load_queue = g_queue_new ();
 	model_queue = g_queue_new ();
 	transform_queue = g_queue_new ();
+	save_queue = g_queue_new ();
 
 	g_thread_create (eog_render_thread, NULL, FALSE, NULL);
 }
@@ -172,6 +181,8 @@
 		return model_queue;
 	} else if (EOG_IS_JOB_TRANSFORM (job)) {
 		return transform_queue;
+	} else if (EOG_IS_JOB_SAVE (job)) {
+		return save_queue;
 	}
 
 	g_assert_not_reached ();
@@ -212,6 +223,8 @@
 		retval = remove_job_from_queue (model_queue, job);
 	} else if (EOG_IS_JOB_TRANSFORM (job)) {
 		retval = remove_job_from_queue (transform_queue, job);
+	} else if (EOG_IS_JOB_SAVE (job)) {
+		retval = remove_job_from_queue (save_queue, job);
 	} else {
 		g_assert_not_reached ();
 	}
Index: libeog/eog-jobs.c
===================================================================
RCS file: /cvs/gnome/eog/libeog/Attic/eog-jobs.c,v
retrieving revision 1.1.2.4
diff -u -r1.1.2.4 eog-jobs.c
--- libeog/eog-jobs.c	5 Oct 2006 23:14:09 -0000	1.1.2.4
+++ libeog/eog-jobs.c	25 Nov 2006 14:00:21 -0000
@@ -40,6 +40,7 @@
 G_DEFINE_TYPE (EogJobLoad, eog_job_load, EOG_TYPE_JOB);
 G_DEFINE_TYPE (EogJobModel, eog_job_model, EOG_TYPE_JOB);
 G_DEFINE_TYPE (EogJobTransform, eog_job_transform, EOG_TYPE_JOB);
+G_DEFINE_TYPE (EogJobSave, eog_job_save, EOG_TYPE_JOB);
 
 enum
 {
@@ -247,6 +248,29 @@
 	oclass->dispose = eog_job_transform_dispose;
 }
 
+static void eog_job_save_init (EogJobSave *job) { /* do nothing */ }
+
+static void eog_job_save_dispose (GObject *object)
+{
+	EogJobSave *job;
+
+	job = EOG_JOB_SAVE (object);
+
+	if (job->images) {
+		g_list_foreach (job->images, (GFunc) g_object_unref, NULL);
+		g_list_free (job->images);
+		job->images = NULL;
+	}
+
+	(* G_OBJECT_CLASS (eog_job_save_parent_class)->dispose) (object);
+}
+
+static void
+eog_job_save_class_init (EogJobSaveClass *class)
+{
+	G_OBJECT_CLASS (class)->dispose = eog_job_save_dispose;
+}
+
 EogJob *
 eog_job_thumbnail_new (GnomeVFSURI *uri_entry)
 {
@@ -494,3 +518,50 @@
 	EOG_JOB (job)->finished = TRUE;
 }
 
+EogJob *
+eog_job_save_new (GList *images)
+{
+	EogJobSave *job;
+
+	job = g_object_new (EOG_TYPE_JOB_SAVE, NULL);
+
+	job->images = images;
+
+	return EOG_JOB (job);
+}
+
+void
+eog_job_save_run (EogJobSave *job)
+{
+	GList *it;
+	
+	for (it = job->images; it != NULL; it = it->next)
+	{
+		EogImage *image = EOG_IMAGE (it->data);
+		EogImageSaveInfo *save_info = NULL;
+		gboolean success = FALSE;
+
+		/* Make sure the image doesn't go away while saving */
+		eog_image_data_ref (image);
+
+		if (!eog_image_has_data (image, EOG_IMAGE_DATA_ALL)) {
+			/* This hopefully doesn't create new race conditions */
+			eog_image_load (image, EOG_IMAGE_DATA_ALL,
+					EOG_JOB (job), &EOG_JOB (job)->error);
+		}
+
+		save_info = eog_image_save_info_from_image (image);
+		success = eog_image_save_by_info (image, save_info, &EOG_JOB (job)->error);
+
+		if (save_info)
+			g_object_unref (save_info);
+
+		eog_image_data_unref (image);
+
+		if (!success)
+			break;
+	}
+
+
+	EOG_JOB (job)->finished = TRUE;
+}
Index: libeog/eog-jobs.h
===================================================================
RCS file: /cvs/gnome/eog/libeog/Attic/eog-jobs.h,v
retrieving revision 1.1.2.3
diff -u -r1.1.2.3 eog-jobs.h
--- libeog/eog-jobs.h	7 Jun 2006 03:57:06 -0000	1.1.2.3
+++ libeog/eog-jobs.h	25 Nov 2006 14:00:21 -0000
@@ -54,6 +54,9 @@
 typedef struct _EogJobTransform EogJobTransform;
 typedef struct _EogJobTransformClass EogJobTransformClass;
 
+typedef struct _EogJobSave EogJobSave;
+typedef struct _EogJobSaveClass EogJobSaveClass;
+
 #define EOG_TYPE_JOB		       (eog_job_get_type())
 #define EOG_JOB(obj)		       (G_TYPE_CHECK_INSTANCE_CAST((obj), EOG_TYPE_JOB, EogJob))
 #define EOG_JOB_CLASS(klass)	       (G_TYPE_CHECK_CLASS_CAST((klass),  EOG_TYPE_JOB, EogJobClass))
@@ -79,6 +82,11 @@
 #define EOG_JOB_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),  EOG_TYPE_JOB_TRANSFORM, EogJobTransformClass))
 #define EOG_IS_JOB_TRANSFORM(obj)      (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOG_TYPE_JOB_TRANSFORM))
 
+#define EOG_TYPE_JOB_SAVE              (eog_job_save_get_type())
+#define EOG_JOB_SAVE(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), EOG_TYPE_JOB_SAVE, EogJobSave))
+#define EOG_JOB_SAVE_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), EOG_TYPE_JOB_SAVE, EogJobSaveClass))
+#define EOG_IS_JOB_SAVE(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), EOG_TYPE_JOB_SAVE))
+
 struct _EogJob
 {
 	GObject  parent;
@@ -144,6 +152,28 @@
         EogJobClass parent_class;
 };
 
+/*typedef enum {
+	EOG_SAVE_RESPONSE_NONE,
+	EOG_SAVE_RESPONSE_RETRY,
+	EOG_SAVE_RESPONSE_SKIP,
+	EOG_SAVE_RESPONSE_OVERWRITE,
+	EOG_SAVE_RESPONSE_CANCEL,
+	EOG_SAVE_RESPONSE_LAST
+} EogJobSaveResponse;*/
+
+struct _EogJobSave
+{
+	EogJob   parent;
+	GList	 *images;
+};
+
+struct _EogJobSaveClass
+{
+	EogJobClass parent_class;
+
+	
+};
+
 /* Base job class */
 GType           eog_job_get_type           (void);
 void            eog_job_finished           (EogJob          *job);
@@ -170,6 +200,11 @@
 EogJob 	       *eog_job_transform_new      (GList           *images,
 					    EogTransform    *trans);
 void            eog_job_transform_run      (EogJobTransform *model);
+
+/* EogJobSave */
+GType		eog_job_save_get_type      (void);
+EogJob         *eog_job_save_new           (GList           *images);
+void            eog_job_save_run           (EogJobSave      *job);
 
 G_END_DECLS
 
Index: shell/eog-window.c
===================================================================
RCS file: /cvs/gnome/eog/shell/eog-window.c,v
retrieving revision 1.189.2.34
diff -u -r1.189.2.34 eog-window.c
--- shell/eog-window.c	25 Nov 2006 00:59:10 -0000	1.189.2.34
+++ shell/eog-window.c	25 Nov 2006 14:00:22 -0000
@@ -111,6 +111,7 @@
 
         EogJob              *load_job;
         EogJob              *transform_job;
+	gboolean             save_job_active;
 
         guint image_info_message_cid;
         guint tip_message_cid;
@@ -1511,9 +1512,30 @@
 }
 
 static void
+eog_job_save_cb (EogJobSave *job, gpointer user_data)
+{
+	EogWindow *window = EOG_WINDOW (user_data);
+
+	g_signal_handlers_disconnect_by_func (job, eog_job_save_cb, window);
+	window->priv->save_job_active = FALSE;
+}
+
+static void
 eog_window_cmd_save (GtkAction *action, gpointer user_data)
 {
-	g_print ("Not implemented yet!\n");
+	GList *images;
+	EogJob *save_job;
+	EogWindow *window = EOG_WINDOW (user_data);
+
+	if (window->priv->save_job_active)
+		return;
+	window->priv->save_job_active = TRUE;
+	images = eog_thumb_view_get_selected_images (EOG_THUMB_VIEW (window->priv->thumbview));
+	save_job = eog_job_save_new (images);
+	g_signal_connect (save_job, "finished",
+			  G_CALLBACK (eog_job_save_cb), window);
+	eog_job_queue_add_job (save_job);
+	g_object_unref (save_job);
 }
 
 static void
