Index: app/actions/file-commands.c
===================================================================
--- app/actions/file-commands.c	(revision 24501)
+++ app/actions/file-commands.c	(working copy)
@@ -72,7 +72,8 @@
                                             GtkWidget   *parent,
                                             const gchar *title,
                                             gboolean     save_a_copy,
-                                            gboolean     close_after_saving);
+                                            gboolean     close_after_saving,
+                                            gboolean     is_blocking);
 static void   file_save_dialog_destroyed   (GtkWidget   *dialog,
                                             GimpImage   *image);
 static void   file_new_template_callback   (GtkWidget   *widget,
@@ -199,6 +200,7 @@
   switch (save_mode)
     {
     case GIMP_SAVE_MODE_SAVE:
+    case GIMP_SAVE_MODE_SAVE_SEQUENTIAL:
     case GIMP_SAVE_MODE_SAVE_AND_CLOSE:
       /*  Only save if the image has been modified  */
       if (image->dirty ||
@@ -284,13 +286,14 @@
     case GIMP_SAVE_MODE_SAVE_AS:
       file_save_dialog_show (display->image, widget,
                              _("Save Image"), FALSE,
-                             save_mode == GIMP_SAVE_MODE_SAVE_AND_CLOSE);
+                             save_mode == GIMP_SAVE_MODE_SAVE_AND_CLOSE,
+                             save_mode == GIMP_SAVE_MODE_SAVE_SEQUENTIAL);
       break;
 
     case GIMP_SAVE_MODE_SAVE_A_COPY:
       file_save_dialog_show (display->image, widget,
                              _("Save a Copy of the Image"), TRUE,
-                             FALSE);
+                             FALSE, FALSE);
       break;
     }
 
@@ -320,7 +323,65 @@
   gtk_widget_show (dialog);
 }
 
+static void
+file_save_one (gpointer image_p,
+               gpointer action_data)
+{
+  GimpImage *image = GIMP_IMAGE (image_p);
+  GimpDisplay gd;
+  GimpDisplay *display = &gd;
+  GimpDisplay *current_display;
+  Gimp *gimp;
+  gint i;
+
+  return_if_no_gimp (gimp, action_data);
+  return_if_no_display (current_display, action_data);
+
+  display->image = NULL;
+  
+  /* make sure we're not taking a different display of the current image */
+  if (current_display->image == image)
+    {
+      display = current_display;
+    }
+  else
+    {
+      for (i = 0; display->image != image; i++)
+        {
+          display = (GimpDisplay *) gimp_container_get_child_by_index (gimp->displays, i);
+          if (i >= gimp_container_num_children (gimp->displays))
+            {
+              return;
+            }
+        }
+     }
+  
+  /* bring this window to front, we want to make sure that you can tell which 
+   * image you're saving
+   */
+  gtk_window_present (GTK_WINDOW (display->shell));
+
+  file_save_cmd_callback (NULL, GIMP_SAVE_MODE_SAVE_SEQUENTIAL, (gpointer)display);
+}
+
 void
+file_save_all_cmd_callback (GtkAction *action,
+                            gpointer   data)
+{
+  Gimp *gimp;
+  GimpContainer *dirty_images;
+
+  return_if_no_gimp (gimp, data);
+
+  if ((dirty_images = gimp_displays_get_dirty_images (gimp)) != NULL)
+    {
+      gimp_container_foreach (dirty_images,
+                              (GFunc) file_save_one,
+                              data);
+    }
+}
+
+void
 file_revert_cmd_callback (GtkAction *action,
                           gpointer   data)
 {
@@ -479,9 +540,11 @@
                        GtkWidget   *parent,
                        const gchar *title,
                        gboolean     save_a_copy,
-                       gboolean     close_after_saving)
+                       gboolean     close_after_saving,
+                       gboolean     is_blocking)
 {
   GtkWidget *dialog;
+  gchar     *full_title;
 
   dialog = g_object_get_data (G_OBJECT (image), "gimp-file-save-dialog");
 
@@ -508,12 +571,24 @@
 
   if (dialog)
     {
-      gtk_window_set_title (GTK_WINDOW (dialog), title);
+      const gchar *uri = gimp_image_get_uri (image);
+      full_title = g_strdup_printf ("%s: %s",
+                                    title,
+                                    file_utils_uri_display_basename (uri));
+      gtk_window_set_title (GTK_WINDOW (dialog), full_title);
+      g_free (full_title);
 
       gimp_file_dialog_set_image (GIMP_FILE_DIALOG (dialog),
                                   image, save_a_copy, close_after_saving);
 
-      gtk_window_present (GTK_WINDOW (dialog));
+      if (is_blocking)
+        {
+          gtk_dialog_run (GTK_DIALOG (dialog));
+        }
+      else
+        {
+          gtk_window_present (GTK_WINDOW (dialog));
+        }
     }
 }
 
Index: app/actions/file-commands.h
===================================================================
--- app/actions/file-commands.h	(revision 24501)
+++ app/actions/file-commands.h	(working copy)
@@ -36,6 +36,9 @@
 void   file_save_template_cmd_callback   (GtkAction   *action,
                                           gpointer     data);
 
+void   file_save_all_cmd_callback        (GtkAction   *action,
+                                          gpointer data);
+
 void   file_revert_cmd_callback          (GtkAction   *action,
                                           gpointer     data);
 void   file_close_all_cmd_callback       (GtkAction   *action,
Index: app/actions/file-actions.c
===================================================================
--- app/actions/file-actions.c	(revision 24501)
+++ app/actions/file-actions.c	(working copy)
@@ -105,7 +105,13 @@
     N_("_Quit"), "<control>Q",
     N_("Quit the GNU Image Manipulation Program"),
     G_CALLBACK (file_quit_cmd_callback),
-    GIMP_HELP_FILE_QUIT }
+    GIMP_HELP_FILE_QUIT },
+ 
+  { "file-save-all", NULL,
+    N_("Save all"), "<alt><control>S",
+    N_("Save all opened images"),
+    G_CALLBACK (file_save_all_cmd_callback),
+    GIMP_HELP_FILE_SAVE_ALL }
 };
 
 static const GimpEnumActionEntry file_save_actions[] =
Index: app/actions/actions-types.h
===================================================================
--- app/actions/actions-types.h	(revision 24501)
+++ app/actions/actions-types.h	(working copy)
@@ -42,6 +42,7 @@
 typedef enum
 {
   GIMP_SAVE_MODE_SAVE,
+  GIMP_SAVE_MODE_SAVE_SEQUENTIAL, /* block until dialog receives a response */
   GIMP_SAVE_MODE_SAVE_AS,
   GIMP_SAVE_MODE_SAVE_A_COPY,
   GIMP_SAVE_MODE_SAVE_AND_CLOSE
Index: app/widgets/gimphelp-ids.h
===================================================================
--- app/widgets/gimphelp-ids.h	(revision 24501)
+++ app/widgets/gimphelp-ids.h	(working copy)
@@ -36,6 +36,7 @@
 #define GIMP_HELP_FILE_SAVE_A_COPY                "gimp-file-save-a-copy"
 #define GIMP_HELP_FILE_SAVE_AS_TEMPLATE           "gimp-file-save-as-template"
 #define GIMP_HELP_FILE_SAVE_BY_EXTENSION          "gimp-file-save-by-extension"
+#define GIMP_HELP_FILE_SAVE_ALL                   "gimp-file-save-all"
 #define GIMP_HELP_FILE_REVERT                     "gimp-file-revert"
 #define GIMP_HELP_FILE_CLOSE                      "gimp-file-close"
 #define GIMP_HELP_FILE_CLOSE_ALL                  "gimp-file-close-all"
Index: menus/image-menu.xml.in
===================================================================
--- menus/image-menu.xml.in	(revision 24501)
+++ menus/image-menu.xml.in	(working copy)
@@ -25,6 +25,7 @@
 	<menuitem action="file-save-as" />
 	<menuitem action="file-save-a-copy" />
 	<menuitem action="file-save-as-template" />
+	<menuitem action="file-save-all" />
       </placeholder>
       <menuitem action="file-revert" />
       <separator />
Index: menus/toolbox-menu.xml.in
===================================================================
--- menus/toolbox-menu.xml.in	(revision 24501)
+++ menus/toolbox-menu.xml.in	(working copy)
@@ -43,6 +43,7 @@
       </menu>
 
       <separator />
+      <menuitem action="file-save-all" />
       <menuitem action="file-close-all" />
       <menuitem action="file-quit" />
     </menu>
