[eog] Fixed Bug 615376 - if saving an image fails it's done silently.



commit 3da565496c3e9aa623d6287fa1c1aea5158b3f61
Author: Javier SÃnchez <jsanchez deskblue com>
Date:   Fri Jul 15 13:09:39 2011 +0200

    Fixed Bug 615376 - if saving an image fails it's done silently.
    
    - Refactored EogErrorMessageArea to include new buttons like CANCEL,
      RELOAD and SAVEAS.
    - Added new custom responses to EogErrorMessageArea for new buttons.
    - Refactored EogCloseConfirmationDialog to include new buttons like
      CLOSE, CANCEL, SAVE and SAVE AS.
    - Added new custom responses to EogCloseConfirmationDialog for new
      buttons.
    - Refactored EogWindow to handle new EogErrorMessageArea and
      EogCloseConfirmationDialog responses.
    - Refactored EogImage to include write permission verification in
      SAVE and SAVE AS processes.
    - Added a new method to EogImage API called eog-image-is-file-writable
      to determine is when the user has write permission on the image
      file.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=615376

 src/eog-close-confirmation-dialog.c |  220 +++++++++++++++++++++--------------
 src/eog-close-confirmation-dialog.h |   31 ++++-
 src/eog-error-message-area.c        |   96 +++++++++++++---
 src/eog-error-message-area.h        |   24 ++++-
 src/eog-image.c                     |  106 ++++++++++++++++-
 src/eog-image.h                     |    3 +
 src/eog-jobs.c                      |    9 +-
 src/eog-window.c                    |  209 +++++++++++++++++++++++++---------
 8 files changed, 525 insertions(+), 173 deletions(-)
---
diff --git a/src/eog-close-confirmation-dialog.c b/src/eog-close-confirmation-dialog.c
index 5b66929..c9f874d 100644
--- a/src/eog-close-confirmation-dialog.c
+++ b/src/eog-close-confirmation-dialog.c
@@ -6,7 +6,7 @@
  *
  * Based on gedit code (gedit/gedit-close-confirmation.c) by gedit Team
  *
- * Copyright (C) 2004-2009 GNOME Foundation 
+ * Copyright (C) 2004-2009 GNOME Foundation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -15,13 +15,13 @@
  *
  * This program 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
+ * 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. 
+ * Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -33,10 +33,18 @@
 #include "eog-close-confirmation-dialog.h"
 #include <eog-window.h>
 
+typedef enum {
+	EOG_CLOSE_CONFIRMATION_DIALOG_NO_BUTTONS    = 0,
+	EOG_CLOSE_CONFIRMATION_DIALOG_CLOSE_BUTTON  = 1 << 0,
+	EOG_CLOSE_CONFIRMATION_DIALOG_CANCEL_BUTTON = 1 << 1,
+	EOG_CLOSE_CONFIRMATION_DIALOG_SAVE_BUTTON   = 1 << 2,
+	EOG_CLOSE_CONFIRMATION_DIALOG_SAVEAS_BUTTON = 1 << 3
+} EogCloseConfirmationDialogButtons;
+
 /* Properties */
-enum 
+enum
 {
-	PROP_0,	
+	PROP_0,
 	PROP_UNSAVED_IMAGES
 };
 
@@ -57,11 +65,11 @@ enum
 	N_COLUMNS
 };
 
-struct _EogCloseConfirmationDialogPrivate 
+struct _EogCloseConfirmationDialogPrivate
 {
-	GList       *unsaved_images;
-	
-	GList       *selected_images;
+	GList	    *unsaved_images;
+
+	GList	    *selected_images;
 
 	GtkTreeModel *list_store;
 	GtkCellRenderer *toggle_renderer;
@@ -80,10 +88,10 @@ struct _EogCloseConfirmationDialogPrivate
 
 G_DEFINE_TYPE(EogCloseConfirmationDialog, eog_close_confirmation_dialog, GTK_TYPE_DIALOG)
 
-static void 	 set_unsaved_image 		(EogCloseConfirmationDialog *dlg,
-						 const GList                  *list);
+static void	 set_unsaved_image		(EogCloseConfirmationDialog *dlg,
+						 const GList		      *list);
 
-static GList 	*get_selected_imgs 		(GtkTreeModel                 *store);
+static GList	*get_selected_imgs		(GtkTreeModel		      *store);
 
 static GdkPixbuf *
 eog_close_confirmation_dialog_get_icon (const gchar *icon_name)
@@ -116,28 +124,29 @@ get_nothumb_pixbuf (void)
 	return GDK_PIXBUF (g_object_ref (nothumb_once.retval));
 }
 
-/*  Since we connect in the costructor we are sure this handler will be called 
+/*  Since we connect in the costructor we are sure this handler will be called
  *  before the user ones
  */
 static void
 response_cb (EogCloseConfirmationDialog *dlg,
-             gint                        response_id,
-             gpointer                    data)
+	     gint			 response_id,
+	     gpointer			 data)
 {
 	EogCloseConfirmationDialogPrivate *priv;
 
 	g_return_if_fail (EOG_IS_CLOSE_CONFIRMATION_DIALOG (dlg));
 
 	priv = dlg->priv;
-	
+
 	if (priv->selected_images != NULL)
 		g_list_free (priv->selected_images);
 
-	if (response_id == GTK_RESPONSE_YES)
+	if (response_id == EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVE ||
+	    response_id == EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVEAS)
 	{
 		if (GET_MODE (priv) == SINGLE_IMG_MODE)
 		{
-			priv->selected_images = 
+			priv->selected_images =
 				g_list_copy (priv->unsaved_images);
 		}
 		else
@@ -153,35 +162,51 @@ response_cb (EogCloseConfirmationDialog *dlg,
 }
 
 static void
-add_buttons (EogCloseConfirmationDialog *dlg)
+add_buttons (EogCloseConfirmationDialog	       *dlg,
+	     EogCloseConfirmationDialogButtons	buttons)
 {
-	gtk_dialog_add_button (GTK_DIALOG (dlg),
-			       _("Close _without Saving"),
-			       GTK_RESPONSE_NO);
-
-	gtk_dialog_add_button (GTK_DIALOG (dlg),
-			       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
-	
-	gtk_dialog_add_button (GTK_DIALOG (dlg),
-			       GTK_STOCK_SAVE,
-			       GTK_RESPONSE_YES);
-
-	gtk_dialog_set_default_response	(GTK_DIALOG (dlg), 
-					 GTK_RESPONSE_YES);
+	/* add requested buttons to the dialog */
+	if (buttons & EOG_CLOSE_CONFIRMATION_DIALOG_CLOSE_BUTTON) {
+		gtk_dialog_add_button (GTK_DIALOG (dlg),
+				       _("Close _without Saving"),
+				       EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_CLOSE);
+	}
+
+	if (buttons & EOG_CLOSE_CONFIRMATION_DIALOG_CANCEL_BUTTON) {
+		gtk_dialog_add_button (GTK_DIALOG (dlg),
+				       GTK_STOCK_CANCEL,
+				       EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_CANCEL);
+	}
+
+	if (buttons & EOG_CLOSE_CONFIRMATION_DIALOG_SAVE_BUTTON) {
+		gtk_dialog_add_button (GTK_DIALOG (dlg),
+				       GTK_STOCK_SAVE,
+				       EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVE);
+	}
+
+	if (buttons & EOG_CLOSE_CONFIRMATION_DIALOG_SAVEAS_BUTTON) {
+		gtk_dialog_add_button (GTK_DIALOG (dlg),
+				       GTK_STOCK_SAVE_AS,
+				       EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVEAS);
+	}
+
+	/* set default response */
+	gtk_dialog_set_default_response	(GTK_DIALOG (dlg),
+					 EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVE);
 }
 
-static void 
+static void
 eog_close_confirmation_dialog_init (EogCloseConfirmationDialog *dlg)
 {
 	AtkObject *atk_obj;
 
 	dlg->priv = EOG_CLOSE_CONFIRMATION_DIALOG_GET_PRIVATE (dlg);
 
-	gtk_container_set_border_width (GTK_CONTAINER (dlg), 5);		
+	gtk_container_set_border_width (GTK_CONTAINER (dlg), 5);
 	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), 14);
 	gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
 	gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dlg), TRUE);
-	
+
 	gtk_window_set_title (GTK_WINDOW (dlg), "");
 
 	gtk_window_set_modal (GTK_WINDOW (dlg), TRUE);
@@ -190,14 +215,14 @@ eog_close_confirmation_dialog_init (EogCloseConfirmationDialog *dlg)
 	atk_obj = gtk_widget_get_accessible (GTK_WIDGET (dlg));
 	atk_object_set_role (atk_obj, ATK_ROLE_ALERT);
 	atk_object_set_name (atk_obj, _("Question"));
-	
+
 	g_signal_connect (dlg,
 			  "response",
 			  G_CALLBACK (response_cb),
 			  NULL);
 }
 
-static void 
+static void
 eog_close_confirmation_dialog_finalize (GObject *object)
 {
 	EogCloseConfirmationDialogPrivate *priv;
@@ -215,9 +240,9 @@ eog_close_confirmation_dialog_finalize (GObject *object)
 }
 
 static void
-eog_close_confirmation_dialog_set_property (GObject      *object, 
-					      guint         prop_id, 
-					      const GValue *value, 
+eog_close_confirmation_dialog_set_property (GObject	 *object,
+					      guint	    prop_id,
+					      const GValue *value,
 					      GParamSpec   *pspec)
 {
 	EogCloseConfirmationDialog *dlg;
@@ -229,7 +254,7 @@ eog_close_confirmation_dialog_set_property (GObject      *object,
 		case PROP_UNSAVED_IMAGES:
 			set_unsaved_image (dlg, g_value_get_pointer (value));
 			break;
-			
+
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
@@ -237,9 +262,9 @@ eog_close_confirmation_dialog_set_property (GObject      *object,
 }
 
 static void
-eog_close_confirmation_dialog_get_property (GObject    *object, 
-					      guint       prop_id, 
-					      GValue     *value, 
+eog_close_confirmation_dialog_get_property (GObject    *object,
+					      guint	  prop_id,
+					      GValue	 *value,
 					      GParamSpec *pspec)
 {
 	EogCloseConfirmationDialogPrivate *priv;
@@ -258,7 +283,7 @@ eog_close_confirmation_dialog_get_property (GObject    *object,
 	}
 }
 
-static void 
+static void
 eog_close_confirmation_dialog_class_init (EogCloseConfirmationDialogClass *klass)
 {
 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
@@ -272,16 +297,16 @@ eog_close_confirmation_dialog_class_init (EogCloseConfirmationDialogClass *klass
 	g_object_class_install_property (gobject_class,
 					 PROP_UNSAVED_IMAGES,
 					 g_param_spec_pointer ("unsaved_images",
-						 	       "Unsaved Images",
+							       "Unsaved Images",
 							       "List of Unsaved Images",
-							       (G_PARAM_READWRITE | 
-							        G_PARAM_CONSTRUCT_ONLY)));
+							       (G_PARAM_READWRITE |
+								G_PARAM_CONSTRUCT_ONLY)));
 }
 
 static GList *
 get_selected_imgs (GtkTreeModel *store)
 {
-	GList       *list;
+	GList	    *list;
 	gboolean     valid;
 	GtkTreeIter  iter;
 
@@ -293,7 +318,7 @@ get_selected_imgs (GtkTreeModel *store)
 		gboolean to_save;
 		EogImage *img;
 
-		gtk_tree_model_get (store, &iter, 
+		gtk_tree_model_get (store, &iter,
 				    SAVE_COLUMN, &to_save,
 				    IMG_COLUMN, &img,
 				    -1);
@@ -317,7 +342,7 @@ eog_close_confirmation_dialog_get_selected_images (EogCloseConfirmationDialog *d
 }
 
 GtkWidget *
-eog_close_confirmation_dialog_new (GtkWindow *parent, 
+eog_close_confirmation_dialog_new (GtkWindow *parent,
 				   GList     *unsaved_images)
 {
 	GtkWidget *dlg;
@@ -326,8 +351,8 @@ eog_close_confirmation_dialog_new (GtkWindow *parent,
 	g_return_val_if_fail (unsaved_images != NULL, NULL);
 
 	dlg = GTK_WIDGET (g_object_new (EOG_TYPE_CLOSE_CONFIRMATION_DIALOG,
-				        "unsaved_images", unsaved_images,
-				        NULL));
+					"unsaved_images", unsaved_images,
+					NULL));
 	g_return_val_if_fail (dlg != NULL, NULL);
 
 	if (parent != NULL)
@@ -340,26 +365,26 @@ eog_close_confirmation_dialog_new (GtkWindow *parent,
 		 * group. It makes no difference if it is already. */
 		gtk_window_group_add_window (wg, parent);
 		gtk_window_group_add_window (wg, GTK_WINDOW (dlg));
-		
-		gtk_window_set_transient_for (GTK_WINDOW (dlg), parent);					     
+
+		gtk_window_set_transient_for (GTK_WINDOW (dlg), parent);
 	}
 
 	return dlg;
 }
 
 GtkWidget *
-eog_close_confirmation_dialog_new_single (GtkWindow     *parent, 
+eog_close_confirmation_dialog_new_single (GtkWindow	*parent,
 					  EogImage	*image)
 {
 	GtkWidget *dlg;
 	GList *unsaved_images;
 	g_return_val_if_fail (image != NULL, NULL);
-	
+
 	unsaved_images = g_list_prepend (NULL, image);
 
-	dlg = eog_close_confirmation_dialog_new (parent, 
+	dlg = eog_close_confirmation_dialog_new (parent,
 						 unsaved_images);
-	
+
 	g_list_free (unsaved_images);
 
 	return dlg;
@@ -369,9 +394,9 @@ static gchar *
 get_text_secondary_label (EogImage *image)
 {
 	gchar *secondary_msg;
-	
+
 	secondary_msg = g_strdup (_("If you don't save, your changes will be lost."));
-	
+
 	return secondary_msg;
 }
 
@@ -385,14 +410,16 @@ build_single_img_dialog (EogCloseConfirmationDialog *dlg)
 	GtkWidget     *image;
 	EogImage      *img;
 	const gchar   *image_name;
-	gchar         *str;
-	gchar         *markup_str;
+	gchar	      *str;
+	gchar	      *markup_str;
+
+	EogCloseConfirmationDialogButtons buttons;
 
 	g_return_if_fail (dlg->priv->unsaved_images->data != NULL);
 	img = EOG_IMAGE (dlg->priv->unsaved_images->data);
 
 	/* Image */
-	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, 
+	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING,
 					  GTK_ICON_SIZE_DIALOG);
 	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
 
@@ -428,20 +455,31 @@ build_single_img_dialog (EogCloseConfirmationDialog *dlg)
 	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
 
 	vbox = gtk_vbox_new (FALSE, 12);
-	
+
 	gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
 
 	gtk_box_pack_start (GTK_BOX (vbox), primary_label, FALSE, FALSE, 0);
-		      
+
 	gtk_box_pack_start (GTK_BOX (vbox), secondary_label, FALSE, FALSE, 0);
 
-	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), 
-			    hbox, 
-	                    FALSE, 
-			    FALSE, 
+	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+			    hbox,
+			    FALSE,
+			    FALSE,
 			    0);
 
-	add_buttons (dlg);
+	if (eog_image_is_file_writable (img)) {
+		buttons = EOG_CLOSE_CONFIRMATION_DIALOG_CLOSE_BUTTON |
+			EOG_CLOSE_CONFIRMATION_DIALOG_CANCEL_BUTTON  |
+			EOG_CLOSE_CONFIRMATION_DIALOG_SAVE_BUTTON;
+	} else {
+		buttons = EOG_CLOSE_CONFIRMATION_DIALOG_CLOSE_BUTTON |
+			EOG_CLOSE_CONFIRMATION_DIALOG_CANCEL_BUTTON  |
+			EOG_CLOSE_CONFIRMATION_DIALOG_SAVEAS_BUTTON;
+	}
+
+	add_buttons (dlg, buttons);
+
 
 	gtk_widget_show_all (hbox);
 }
@@ -478,7 +516,7 @@ populate_model (GtkTreeModel *store, GList *imgs)
 				    IMAGE_COLUMN, buf_scaled,
 				    NAME_COLUMN, name,
 				    IMG_COLUMN, img,
-			            -1);
+				    -1);
 
 		imgs = g_list_next (imgs);
 		g_object_unref (buf_scaled);
@@ -525,7 +563,7 @@ create_treeview (EogCloseConfirmationDialogPrivate *priv)
 	g_object_unref (store);
 
 	priv->list_store = GTK_TREE_MODEL (store);
-	
+
 	/* Add columns */
 	priv->toggle_renderer = renderer = gtk_cell_renderer_toggle_new ();
 	g_signal_connect (renderer, "toggled",
@@ -547,7 +585,7 @@ create_treeview (EogCloseConfirmationDialogPrivate *priv)
 							   NULL);
 	gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
 
-	
+
 
 	renderer = gtk_cell_renderer_text_new ();
 	column = gtk_tree_view_column_new_with_attributes ("Name",
@@ -573,18 +611,20 @@ build_multiple_imgs_dialog (EogCloseConfirmationDialog *dlg)
 	GtkWidget *scrolledwindow;
 	GtkWidget *treeview;
 	GtkWidget *secondary_label;
-	gchar     *str;
-	gchar     *markup_str;
+	gchar	  *str;
+	gchar	  *markup_str;
+
+	EogCloseConfirmationDialogButtons buttons;
 
 	priv = dlg->priv;
 
 	hbox = gtk_hbox_new (FALSE, 12);
 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
-  	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), 
+	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
 			    hbox, TRUE, TRUE, 0);
 
 	/* Image */
-	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, 
+	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING,
 					  GTK_ICON_SIZE_DIALOG);
 	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
 	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
@@ -609,11 +649,11 @@ build_multiple_imgs_dialog (EogCloseConfirmationDialog *dlg)
 
 	markup_str = g_strconcat ("<span weight=\"bold\" size=\"larger\">", str, "</span>", NULL);
 	g_free (str);
-	
+
 	gtk_label_set_markup (GTK_LABEL (primary_label), markup_str);
 	g_free (markup_str);
 	gtk_box_pack_start (GTK_BOX (vbox), primary_label, FALSE, FALSE, 0);
-	
+
 	vbox2 = gtk_vbox_new (FALSE, 8);
 	gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
 
@@ -625,10 +665,10 @@ build_multiple_imgs_dialog (EogCloseConfirmationDialog *dlg)
 
 	scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
 	gtk_box_pack_start (GTK_BOX (vbox2), scrolledwindow, TRUE, TRUE, 0);
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), 
-					GTK_POLICY_AUTOMATIC, 
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
+					GTK_POLICY_AUTOMATIC,
 					GTK_POLICY_AUTOMATIC);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow), 
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow),
 					     GTK_SHADOW_IN);
 
 	treeview = create_treeview (priv);
@@ -645,18 +685,22 @@ build_multiple_imgs_dialog (EogCloseConfirmationDialog *dlg)
 
 	gtk_label_set_mnemonic_widget (GTK_LABEL (select_label), treeview);
 
-	add_buttons (dlg);
+	buttons = EOG_CLOSE_CONFIRMATION_DIALOG_CLOSE_BUTTON |
+		EOG_CLOSE_CONFIRMATION_DIALOG_CANCEL_BUTTON  |
+		EOG_CLOSE_CONFIRMATION_DIALOG_SAVE_BUTTON;
+
+	add_buttons (dlg, buttons);
 
 	gtk_widget_show_all (hbox);
 }
 
 static void
 set_unsaved_image (EogCloseConfirmationDialog *dlg,
-		   const GList                *list)
+		   const GList		      *list)
 {
 	EogCloseConfirmationDialogPrivate *priv;
 
-	g_return_if_fail (list != NULL);	
+	g_return_if_fail (list != NULL);
 
 	priv = dlg->priv;
 	g_return_if_fail (priv->unsaved_images == NULL);
@@ -670,7 +714,7 @@ set_unsaved_image (EogCloseConfirmationDialog *dlg,
 	else
 	{
 		build_multiple_imgs_dialog (dlg);
-	}	
+	}
 }
 
 const GList *
diff --git a/src/eog-close-confirmation-dialog.h b/src/eog-close-confirmation-dialog.h
index a4fb35e..0696933 100644
--- a/src/eog-close-confirmation-dialog.h
+++ b/src/eog-close-confirmation-dialog.h
@@ -6,7 +6,7 @@
  *
  * Based on gedit code (gedit/gedit-close-confirmation.h) by gedit Team
  *
- * Copyright (C) 2004-2009 GNOME Foundation 
+ * Copyright (C) 2004-2009 GNOME Foundation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,8 +20,8 @@
  *
  * 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. 
+ * Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
  */
 
 #ifndef __EOG_CLOSE_CONFIRMATION_DIALOG_H__
@@ -43,7 +43,7 @@ typedef struct _EogCloseConfirmationDialog 		EogCloseConfirmationDialog;
 typedef struct _EogCloseConfirmationDialogClass 	EogCloseConfirmationDialogClass;
 typedef struct _EogCloseConfirmationDialogPrivate 	EogCloseConfirmationDialogPrivate;
 
-struct _EogCloseConfirmationDialog 
+struct _EogCloseConfirmationDialog
 {
 	GtkDialog parent;
 
@@ -51,11 +51,30 @@ struct _EogCloseConfirmationDialog
 	EogCloseConfirmationDialogPrivate *priv;
 };
 
-struct _EogCloseConfirmationDialogClass 
+struct _EogCloseConfirmationDialogClass
 {
 	GtkDialogClass parent_class;
 };
 
+/**
+ * EogCloseConfirmationDialogResponseType:
+ * @EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_NONE: Returned if the dialog has no response id,
+ * or if the message area gets programmatically hidden or destroyed
+ * @EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_CLOSE: Returned by CLOSE button in the dialog
+ * @EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_CANCEL: Returned by CANCEL button in the dialog
+ * @EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVE: Returned by SAVE button in the dialog
+ * @EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVEAS: Returned by SAVE AS button in the dialog
+ *
+ */
+typedef enum
+{
+	EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_NONE   = 0,
+	EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_CLOSE  = 1,
+	EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_CANCEL = 2,
+	EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVE   = 3,
+	EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVEAS = 4
+} EogCloseConfirmationDialogResponseType;
+
 G_GNUC_INTERNAL
 GType 		 eog_close_confirmation_dialog_get_type		(void) G_GNUC_CONST;
 
@@ -63,7 +82,7 @@ G_GNUC_INTERNAL
 GtkWidget	*eog_close_confirmation_dialog_new			(GtkWindow     *parent,
 									 GList         *unsaved_documents);
 G_GNUC_INTERNAL
-GtkWidget 	*eog_close_confirmation_dialog_new_single 		(GtkWindow     *parent, 
+GtkWidget 	*eog_close_confirmation_dialog_new_single 		(GtkWindow     *parent,
 									 EogImage      *image);
 
 G_GNUC_INTERNAL
diff --git a/src/eog-error-message-area.c b/src/eog-error-message-area.c
index 29a89c0..85ba54e 100644
--- a/src/eog-error-message-area.c
+++ b/src/eog-error-message-area.c
@@ -34,6 +34,13 @@
 #include <glib-object.h>
 #include <gtk/gtk.h>
 
+typedef enum {
+	EOG_ERROR_MESSAGE_AREA_NO_BUTTONS    = 0,
+	EOG_ERROR_MESSAGE_AREA_CANCEL_BUTTON = 1 << 0,
+	EOG_ERROR_MESSAGE_AREA_RELOAD_BUTTON = 1 << 1,
+	EOG_ERROR_MESSAGE_AREA_SAVEAS_BUTTON = 1 << 2
+} EogErrorMessageAreaButtons;
+
 static void
 set_message_area_text_and_icon (GtkInfoBar   *message_area,
 				const gchar  *icon_stock_id,
@@ -97,22 +104,36 @@ set_message_area_text_and_icon (GtkInfoBar   *message_area,
 }
 
 static GtkWidget *
-create_error_message_area (const gchar *primary_text,
-			   const gchar *secondary_text,
-			   gboolean     recoverable)
+create_error_message_area (const gchar                 *primary_text,
+			   const gchar                 *secondary_text,
+			   EogErrorMessageAreaButtons   buttons)
 {
 	GtkWidget *message_area;
 
-	if (recoverable)
-		message_area = gtk_info_bar_new_with_buttons (_("_Retry"),
-							      GTK_RESPONSE_OK,
-							      NULL);
-	else
-		message_area = gtk_info_bar_new ();
+	/* create a new message area */
+	message_area = gtk_info_bar_new ();
+
+	/* add requested buttons to the message area */
+	if (buttons & EOG_ERROR_MESSAGE_AREA_CANCEL_BUTTON)
+		gtk_info_bar_add_button (GTK_INFO_BAR (message_area),
+					 _("_Cancel"),
+					 EOG_ERROR_MESSAGE_AREA_RESPONSE_CANCEL);
+
+	if (buttons & EOG_ERROR_MESSAGE_AREA_RELOAD_BUTTON)
+		gtk_info_bar_add_button (GTK_INFO_BAR (message_area),
+					 _("_Reload"),
+					 EOG_ERROR_MESSAGE_AREA_RESPONSE_RELOAD);
 
+	if (buttons & EOG_ERROR_MESSAGE_AREA_SAVEAS_BUTTON)
+		gtk_info_bar_add_button (GTK_INFO_BAR (message_area),
+					 _("Save _Asâ"),
+					 EOG_ERROR_MESSAGE_AREA_RESPONSE_SAVEAS);
+
+	/* set message type */
 	gtk_info_bar_set_message_type (GTK_INFO_BAR (message_area),
 				       GTK_MESSAGE_ERROR);
 
+	/* set text and icon */
 	set_message_area_text_and_icon (GTK_INFO_BAR (message_area),
 					GTK_STOCK_DIALOG_ERROR,
 					primary_text,
@@ -123,10 +144,10 @@ create_error_message_area (const gchar *primary_text,
 
 /**
  * eog_image_load_error_message_area_new: (skip):
- * @caption: 
- * @error: 
+ * @caption:
+ * @error:
+ *
  *
- * 
  *
  * Returns: (transfer full): a new #GtkInfoBar
  **/
@@ -154,7 +175,50 @@ eog_image_load_error_message_area_new (const gchar  *caption,
 
 	message_area = create_error_message_area (error_message,
 						  message_details,
-						  TRUE);
+						  EOG_ERROR_MESSAGE_AREA_CANCEL_BUTTON);
+
+	g_free (pango_escaped_caption);
+	g_free (error_message);
+	g_free (message_details);
+
+	return message_area;
+}
+
+/**
+ * eog_image_save_error_message_area_new: (skip):
+ * @caption:
+ * @error:
+ *
+ *
+ *
+ * Returns: (transfer full): a new #GtkInfoBar
+ **/
+GtkWidget *
+eog_image_save_error_message_area_new (const gchar  *caption,
+				       const GError *error)
+{
+	GtkWidget *message_area;
+	gchar *error_message = NULL;
+	gchar *message_details = NULL;
+	gchar *pango_escaped_caption = NULL;
+
+	g_return_val_if_fail (caption != NULL, NULL);
+	g_return_val_if_fail (error != NULL, NULL);
+
+	/* Escape the caption string with respect to pango markup.
+	   This is necessary because otherwise characters like "&" will
+	   be interpreted as the beginning of a pango entity inside
+	   the message area GtkLabel. */
+	pango_escaped_caption = g_markup_escape_text (caption, -1);
+	error_message = g_strdup_printf (_("Could not save image '%s'."),
+					 pango_escaped_caption);
+
+	message_details = g_strdup (error->message);
+
+	message_area = create_error_message_area (error_message,
+						  message_details,
+						  EOG_ERROR_MESSAGE_AREA_CANCEL_BUTTON |
+						  EOG_ERROR_MESSAGE_AREA_SAVEAS_BUTTON);
 
 	g_free (pango_escaped_caption);
 	g_free (error_message);
@@ -165,9 +229,9 @@ eog_image_load_error_message_area_new (const gchar  *caption,
 
 /**
  * eog_no_images_error_message_area_new: (skip):
- * @file: 
+ * @file:
+ *
  *
- * 
  *
  * Returns: (transfer full): a new #GtkInfoBar
  **/
@@ -201,7 +265,7 @@ eog_no_images_error_message_area_new (GFile *file)
 
 	message_area = create_error_message_area (error_message,
 						  NULL,
-						  FALSE);
+						  EOG_ERROR_MESSAGE_AREA_NO_BUTTONS);
 
 	g_free (error_message);
 
diff --git a/src/eog-error-message-area.h b/src/eog-error-message-area.h
index d3aac06..0a6a23d 100644
--- a/src/eog-error-message-area.h
+++ b/src/eog-error-message-area.h
@@ -29,8 +29,28 @@
 #include <gtk/gtk.h>
 #include <gio/gio.h>
 
-GtkWidget   *eog_image_load_error_message_area_new   (const gchar       *caption,
-						      const GError      *error);
+/**
+ * EogErrorMessageAreaResponseType:
+ * @EOG_ERROR_MESSAGE_AREA_RESPONSE_NONE: Returned if the message area has no response id,
+ * or if the message area gets programmatically hidden or destroyed
+ * @EOG_ERROR_MESSAGE_AREA_RESPONSE_CANCEL: Returned by CANCEL button in the message area
+ * @EOG_ERROR_MESSAGE_AREA_RESPONSE_RELOAD: Returned by RELOAD button in the message area
+ * @EOG_ERROR_MESSAGE_AREA_RESPONSE_SAVEAS: Returned by SAVE AS button in the message area
+ *
+ */
+typedef enum
+{
+	EOG_ERROR_MESSAGE_AREA_RESPONSE_NONE   = 0,
+	EOG_ERROR_MESSAGE_AREA_RESPONSE_CANCEL = 1,
+	EOG_ERROR_MESSAGE_AREA_RESPONSE_RELOAD = 2,
+	EOG_ERROR_MESSAGE_AREA_RESPONSE_SAVEAS = 3
+} EogErrorMessageAreaResponseType;
+
+GtkWidget   *eog_image_load_error_message_area_new   (const gchar  *caption,
+						      const GError *error);
+
+GtkWidget   *eog_image_save_error_message_area_new   (const gchar  *caption,
+						      const GError *error);
 
 GtkWidget   *eog_no_images_error_message_area_new    (GFile *file);
 
diff --git a/src/eog-image.c b/src/eog-image.c
index 0e06112..0d9fea5 100644
--- a/src/eog-image.c
+++ b/src/eog-image.c
@@ -1400,7 +1400,7 @@ eog_image_get_profile (EogImage *img)
  * eog_image_get_thumbnail:
  * @img: a #EogImage
  *
- * Gets the thumbnail pixbuf for @img 
+ * Gets the thumbnail pixbuf for @img
  *
  * Returns: (transfer full): a #GdkPixbuf with a thumbnail
  **/
@@ -1625,6 +1625,59 @@ eog_image_link_with_target (EogImage *image, EogImageSaveInfo *target)
 	priv->file_type = g_strdup (target->format);
 }
 
+static gboolean
+check_if_file_is_writable (GFile *file)
+{
+	GFile           *file_to_check;
+	GFileInfo	*file_info;
+	GError		*error;
+	gboolean	 is_writable;
+
+	g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+	/* check if file exists */
+	if (!g_file_query_exists (file, NULL)) {
+		g_warning ("%s don't exist. Checking parent directory.",
+			   g_file_get_parse_name (file));
+
+		file_to_check = g_file_get_parent (file);
+
+		/* recover file information */
+		error = NULL;
+		file_info = g_file_query_info (file_to_check,
+					       G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+					       G_FILE_QUERY_INFO_NONE,
+					       NULL,
+					       &error);
+
+		g_object_unref (file_to_check);
+	} else {
+		file_to_check = g_file_dup (file);
+
+		/* recover file information */
+		error = NULL;
+		file_info = g_file_query_info (file_to_check,
+					       G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+					       G_FILE_QUERY_INFO_NONE,
+					       NULL,
+					       &error);
+	}
+
+	/* we assume that the image can't be saved when
+	   we can't retrieve any file information */
+	if (file_info == NULL)
+		return FALSE;
+
+	/* check if file can be writed */
+	is_writable = g_file_info_get_attribute_boolean (file_info,
+							 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
+
+	/* free objects */
+	g_object_unref (file_info);
+
+	return is_writable;
+}
+
 gboolean
 eog_image_save_by_info (EogImage *img, EogImageSaveInfo *source, GError **error)
 {
@@ -1657,6 +1710,17 @@ eog_image_save_by_info (EogImage *img, EogImageSaveInfo *source, GError **error)
 		return FALSE;
 	}
 
+	/* fail if there is not write rights to save */
+	if (!check_if_file_is_writable (priv->file)) {
+		g_warning ("Could not save image '%s'.",
+			   g_file_get_parse_name (priv->file));
+
+		g_set_error (error, EOG_IMAGE_ERROR,
+			     EOG_IMAGE_ERROR_NOT_SAVED,
+			     _("You do not have the permissions necessary to save the file."));
+		return FALSE;
+	}
+
 	/* generate temporary file */
 	tmp_file = tmp_file_get ();
 
@@ -1760,6 +1824,17 @@ eog_image_save_as_by_info (EogImage *img, EogImageSaveInfo *source, EogImageSave
 		return FALSE;
 	}
 
+	/* fail if there is not write rights to save on target */
+	if (!check_if_file_is_writable (target->file)) {
+		g_warning ("Could not save image '%s'.",
+			   g_file_get_parse_name (target->file));
+
+		g_set_error (error, EOG_IMAGE_ERROR,
+			     EOG_IMAGE_ERROR_NOT_SAVED,
+			     _("You do not have the permissions necessary to save the file."));
+		return FALSE;
+	}
+
 	/* generate temporary file name */
 	tmp_file = tmp_file_get ();
 
@@ -2205,7 +2280,7 @@ eog_image_iter_advance (EogImage *img)
 	priv = img->priv;
 
 	if ((new_frame = gdk_pixbuf_animation_iter_advance (img->priv->anim_iter, NULL)) == TRUE)
-	  {      
+	  {
 		g_mutex_lock (priv->status_mutex);
 		g_object_unref (priv->image);
 		priv->image = gdk_pixbuf_animation_iter_get_pixbuf (priv->anim_iter);
@@ -2217,7 +2292,7 @@ eog_image_iter_advance (EogImage *img)
 			priv->image = transformed;
 			priv->width = gdk_pixbuf_get_width (transformed);
 			priv->height = gdk_pixbuf_get_height (transformed);
-		}      
+		}
 		g_mutex_unlock (priv->status_mutex);
 		/* Emit next frame signal so we can update the display */
 		g_signal_emit (img, signals[SIGNAL_NEXT_FRAME], 0,
@@ -2234,7 +2309,7 @@ eog_image_iter_advance (EogImage *img)
  * Checks whether a given image is animated.
  *
  * Returns: #TRUE if it is an animated image, #FALSE otherwise.
- * 
+ *
  **/
 gboolean
 eog_image_is_animation (EogImage *img)
@@ -2249,7 +2324,7 @@ private_timeout (gpointer data)
 	EogImage *img = EOG_IMAGE (data);
 	EogImagePrivate *priv = img->priv;
 
-	if (eog_image_is_animation (img) && 
+	if (eog_image_is_animation (img) &&
 	    !g_source_is_destroyed (g_main_current_source ()) &&
 	    priv->is_playing) {
 		while (eog_image_iter_advance (img) != TRUE) {}; /* cpu-sucking ? */
@@ -2339,3 +2414,24 @@ eog_image_is_file_changed (EogImage *img)
 
 	return img->priv->file_is_changed;
 }
+
+/**
+ * eog_image_is_file_writable:
+ * @img: a #EogImage
+ *
+ * Evaluate if the user has write permission on the image file.
+ *
+ * Returns: %TRUE on success, %FALSE if the user hasn't write permissions on it,
+ * or @img is not an #EogImage.
+ **/
+gboolean
+eog_image_is_file_writable (EogImage *img)
+{
+	gboolean is_writable;
+
+	g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
+
+	is_writable = check_if_file_is_writable (img->priv->file);
+
+	return is_writable;
+}
diff --git a/src/eog-image.h b/src/eog-image.h
index 85bf11e..d886278 100644
--- a/src/eog-image.h
+++ b/src/eog-image.h
@@ -68,6 +68,7 @@ typedef struct _EogImagePrivate EogImagePrivate;
 typedef enum {
 	EOG_IMAGE_ERROR_SAVE_NOT_LOCAL,
 	EOG_IMAGE_ERROR_NOT_LOADED,
+	EOG_IMAGE_ERROR_NOT_SAVED,
 	EOG_IMAGE_ERROR_VFS,
 	EOG_IMAGE_ERROR_FILE_EXISTS,
 	EOG_IMAGE_ERROR_TMP_FILE_FAILED,
@@ -216,6 +217,8 @@ void              eog_image_file_changed             (EogImage *img);
 
 gboolean          eog_image_is_file_changed          (EogImage *img);
 
+gboolean          eog_image_is_file_writable         (EogImage *img);
+
 G_END_DECLS
 
 #endif /* __EOG_IMAGE_H__ */
diff --git a/src/eog-jobs.c b/src/eog-jobs.c
index f415e6a..ed90725 100644
--- a/src/eog-jobs.c
+++ b/src/eog-jobs.c
@@ -771,7 +771,12 @@ eog_job_save_as_run (EogJob *ejob)
 					  NULL,
 					  NULL);
 
-		file_permissions = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE);
+		/* if we can't retrieve file permission apply default */
+		if (info != NULL) {
+			file_permissions = g_file_info_get_attribute_uint32 (info,
+									     G_FILE_ATTRIBUTE_UNIX_MODE);
+			g_object_unref (info);
+		}
 
 		/* apply permission mask to file permissions */
 		file_permissions |= permissions_mask;
@@ -783,8 +788,6 @@ eog_job_save_as_run (EogJob *ejob)
 					     NULL,
 					     NULL);
 
-		g_object_unref (info);
-
 		if (src_info)
 			g_object_unref (src_info);
 
diff --git a/src/eog-window.c b/src/eog-window.c
index 224a210..2735f99 100644
--- a/src/eog-window.c
+++ b/src/eog-window.c
@@ -194,6 +194,8 @@ struct _EogWindowPrivate {
 
 static void eog_window_cmd_fullscreen (GtkAction *action, gpointer user_data);
 static void eog_window_run_fullscreen (EogWindow *window, gboolean slideshow);
+static void eog_window_cmd_save (GtkAction *action, gpointer user_data);
+static void eog_window_cmd_save_as (GtkAction *action, gpointer user_data);
 static void eog_window_cmd_slideshow (GtkAction *action, gpointer user_data);
 static void eog_window_cmd_pause_slideshow (GtkAction *action, gpointer user_data);
 static void eog_window_stop_fullscreen (EogWindow *window, gboolean slideshow);
@@ -239,7 +241,7 @@ _eog_zoom_shrink_to_boolean (GBinding *binding, const GValue *source,
 	is_fit = (mode == EOG_ZOOM_MODE_SHRINK_TO_FIT);
 	g_value_set_boolean (target, is_fit);
 
-	return TRUE;	
+	return TRUE;
 }
 
 static void
@@ -1044,7 +1046,7 @@ eog_window_update_openwith_menu (EogWindow *window, EogImage *image)
                                 GTK_UI_MANAGER_MENUITEM,
                                 FALSE);
 
-                path = g_strdup_printf ("/MainMenu/Image/ImageOpenWith/Applications Placeholder/%s", name);	
+                path = g_strdup_printf ("/MainMenu/Image/ImageOpenWith/Applications Placeholder/%s", name);
 
                 menuitem = gtk_ui_manager_get_widget (priv->ui_mgr, path);
 
@@ -1053,7 +1055,7 @@ eog_window_update_openwith_menu (EogWindow *window, EogImage *image)
 
                 g_free (path);
 
-                path = g_strdup_printf ("/ThumbnailPopup/ImageOpenWith/Applications Placeholder/%s", name);	
+                path = g_strdup_printf ("/ThumbnailPopup/ImageOpenWith/Applications Placeholder/%s", name);
 
                 menuitem = gtk_ui_manager_get_widget (priv->ui_mgr, path);
 
@@ -1062,7 +1064,7 @@ eog_window_update_openwith_menu (EogWindow *window, EogImage *image)
 
                 g_free (path);
 
-                path = g_strdup_printf ("/ViewPopup/ImageOpenWith/Applications Placeholder/%s", name);	
+                path = g_strdup_printf ("/ViewPopup/ImageOpenWith/Applications Placeholder/%s", name);
 
                 menuitem = gtk_ui_manager_get_widget (priv->ui_mgr, path);
 
@@ -1254,15 +1256,30 @@ eog_window_error_message_area_response (GtkInfoBar       *message_area,
 					gint              response_id,
 					EogWindow        *window)
 {
-	if (response_id != GTK_RESPONSE_OK) {
-		eog_window_set_message_area (window, NULL);
+	GtkAction *action_save_as;
 
-		return;
-	}
+	g_return_if_fail (GTK_IS_INFO_BAR (message_area));
+	g_return_if_fail (EOG_IS_WINDOW (window));
 
-	/* Trigger loading for current image again */
-	eog_thumb_view_select_single (EOG_THUMB_VIEW (window->priv->thumbview),
-				      EOG_THUMB_VIEW_SELECT_CURRENT);
+	/* remove message area */
+	eog_window_set_message_area (window, NULL);
+
+	/* evaluate message area response */
+	switch (response_id) {
+	case EOG_ERROR_MESSAGE_AREA_RESPONSE_NONE:
+	case EOG_ERROR_MESSAGE_AREA_RESPONSE_CANCEL:
+		/* nothing to do in this case */
+		break;
+	case EOG_ERROR_MESSAGE_AREA_RESPONSE_RELOAD:
+		/* TODO: trigger loading for current image again */
+		break;
+	case EOG_ERROR_MESSAGE_AREA_RESPONSE_SAVEAS:
+		/* trigger save as command for current image */
+		action_save_as = gtk_action_group_get_action (window->priv->actions_image,
+							      "ImageSaveAs");
+		eog_window_cmd_save_as (action_save_as, window);
+		break;
+	}
 }
 
 static void
@@ -2258,12 +2275,54 @@ static void
 eog_job_close_save_cb (EogJobSave *job, gpointer user_data)
 {
 	EogWindow *window = EOG_WINDOW (user_data);
+	GtkAction *action_save;
 
 	g_signal_handlers_disconnect_by_func (job,
 					      eog_job_close_save_cb,
 					      window);
 
-	gtk_widget_destroy (GTK_WIDGET (window));
+	/* clean the last save job */
+	g_object_unref (window->priv->save_job);
+	window->priv->save_job = NULL;
+
+	/* recover save action from actions group */
+	action_save = gtk_action_group_get_action (window->priv->actions_image,
+						   "ImageSave");
+
+	/* check if job contains any error */
+	if (EOG_JOB (job)->error == NULL) {
+		gtk_widget_destroy (GTK_WIDGET (window));
+	} else {
+		GtkWidget *message_area;
+
+		eog_thumb_view_set_current_image (EOG_THUMB_VIEW (window->priv->thumbview),
+						  job->current_image,
+						  TRUE);
+
+		message_area = eog_image_save_error_message_area_new (
+					eog_image_get_caption (job->current_image),
+					EOG_JOB (job)->error);
+
+		g_signal_connect (message_area,
+				  "response",
+				  G_CALLBACK (eog_window_error_message_area_response),
+				  window);
+
+		gtk_window_set_icon (GTK_WINDOW (window), NULL);
+		gtk_window_set_title (GTK_WINDOW (window),
+				      eog_image_get_caption (job->current_image));
+
+		eog_window_set_message_area (window, message_area);
+
+		gtk_info_bar_set_default_response (GTK_INFO_BAR (message_area),
+						   GTK_RESPONSE_CANCEL);
+
+		gtk_widget_show (message_area);
+
+		update_status_bar (window);
+
+		gtk_action_set_sensitive (action_save, TRUE);
+	}
 }
 
 static void
@@ -2271,38 +2330,49 @@ close_confirmation_dialog_response_handler (EogCloseConfirmationDialog *dlg,
 					    gint                        response_id,
 					    EogWindow                  *window)
 {
-	GList *selected_images;
+	GList            *selected_images;
 	EogWindowPrivate *priv;
+	GtkAction        *action_save_as;
 
 	priv = window->priv;
 
-	switch (response_id)
-	{
-		case GTK_RESPONSE_YES:
-			/* save selected images */
-			selected_images = eog_close_confirmation_dialog_get_selected_images (dlg);
-			eog_close_confirmation_dialog_set_sensitive (dlg, FALSE);
-			if (eog_window_save_images (window, selected_images)) {
-				g_signal_connect (priv->save_job,
-							  "finished",
-							  G_CALLBACK (eog_job_close_save_cb),
-							  window);
-
-				eog_job_queue_add_job (priv->save_job);
-			}
+	switch (response_id) {
+	case EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVE:
+		selected_images = eog_close_confirmation_dialog_get_selected_images (dlg);
+		gtk_widget_destroy (GTK_WIDGET (dlg));
 
-			break;
+		if (eog_window_save_images (window, selected_images)) {
+			g_signal_connect (priv->save_job,
+					  "finished",
+					  G_CALLBACK (eog_job_close_save_cb),
+					  window);
 
-		case GTK_RESPONSE_NO:
-			/* dont save */
-			gtk_widget_destroy (GTK_WIDGET (window));
-			break;
+			eog_job_queue_add_job (priv->save_job);
+		}
 
-		default:
-			/* Cancel */
-			gtk_widget_destroy (GTK_WIDGET (dlg));
-			break;
-	}	
+		break;
+
+	case EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_SAVEAS:
+		selected_images = eog_close_confirmation_dialog_get_selected_images (dlg);
+		gtk_widget_destroy (GTK_WIDGET (dlg));
+
+		eog_thumb_view_set_current_image (EOG_THUMB_VIEW (priv->thumbview),
+						  g_list_first (selected_images)->data,
+						  TRUE);
+
+		action_save_as = gtk_action_group_get_action (priv->actions_image,
+							      "ImageSaveAs");
+		eog_window_cmd_save_as (action_save_as, window);
+		break;
+
+	case EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_CLOSE:
+		gtk_widget_destroy (GTK_WIDGET (window));
+		break;
+
+	case EOG_CLOSE_CONFIRMATION_DIALOG_RESPONSE_CANCEL:
+		gtk_widget_destroy (GTK_WIDGET (dlg));
+		break;
+	}
 }
 
 static gboolean
@@ -2338,9 +2408,9 @@ eog_window_unsaved_images_confirm (EogWindow *window)
 				list = g_list_prepend (list, image);
 			}
 		} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &iter));
-	}		
+	}
 
-	if (list) {	
+	if (list) {
 		list = g_list_reverse (list);
 		dialog = eog_close_confirmation_dialog_new (GTK_WINDOW (window),
 							    list);
@@ -2497,13 +2567,13 @@ eog_window_cmd_edit_toolbar (GtkAction *action, gpointer *user_data)
 	 */
 	if(tb_action)
 		g_object_bind_property (dialog, "visible",
-					tb_action, "sensitive", 
+					tb_action, "sensitive",
 					G_BINDING_SYNC_CREATE |
 					G_BINDING_INVERT_BOOLEAN);
 	/* Do the same for the EditToolbar action to avoid spawning
 	 * additional (useless) editor windows. */
 	g_object_bind_property (dialog, "visible",
-				action, "sensitive", 
+				action, "sensitive",
 				G_BINDING_SYNC_CREATE |
 				G_BINDING_INVERT_BOOLEAN);
 
@@ -2763,13 +2833,46 @@ eog_job_save_cb (EogJobSave *job, gpointer user_data)
 					      eog_job_save_progress_cb,
 					      window);
 
+	/* clean the last save job */
 	g_object_unref (window->priv->save_job);
 	window->priv->save_job = NULL;
 
-	update_status_bar (window);
+	/* recover save action from actions group */
 	action_save = gtk_action_group_get_action (window->priv->actions_image,
 						   "ImageSave");
-	gtk_action_set_sensitive (action_save, FALSE);
+
+	/* check if job contains any error */
+	if (EOG_JOB (job)->error == NULL) {
+		update_status_bar (window);
+
+		gtk_action_set_sensitive (action_save, FALSE);
+	} else {
+		GtkWidget *message_area;
+
+		message_area = eog_image_save_error_message_area_new (
+					eog_image_get_caption (job->current_image),
+					EOG_JOB (job)->error);
+
+		g_signal_connect (message_area,
+				  "response",
+				  G_CALLBACK (eog_window_error_message_area_response),
+				  window);
+
+		gtk_window_set_icon (GTK_WINDOW (window), NULL);
+		gtk_window_set_title (GTK_WINDOW (window),
+				      eog_image_get_caption (job->current_image));
+
+		eog_window_set_message_area (window, message_area);
+
+		gtk_info_bar_set_default_response (GTK_INFO_BAR (message_area),
+						   GTK_RESPONSE_CANCEL);
+
+		gtk_widget_show (message_area);
+
+		update_status_bar (window);
+
+		gtk_action_set_sensitive (action_save, TRUE);
+	}
 }
 
 static void
@@ -2992,17 +3095,17 @@ eog_window_cmd_open_containing_folder (GtkAction *action, gpointer user_data)
 {
 	EogWindow *window = EOG_WINDOW (user_data);
 	EogWindowPrivate *priv;
-	
+
 	GtkWidget *eog_window_widget;
-  
+
 	GFile *file;
 	GFile *parent = NULL;
-	
-	eog_window_widget = GTK_WIDGET (window);	
+
+	eog_window_widget = GTK_WIDGET (window);
 	priv = window->priv;
-	
-	g_return_if_fail (priv->image != NULL);	
-	
+
+	g_return_if_fail (priv->image != NULL);
+
 	file = eog_image_get_file (priv->image);
 
 	if (file) {
@@ -3012,7 +3115,7 @@ eog_window_cmd_open_containing_folder (GtkAction *action, gpointer user_data)
 
 	if (parent) {
 		char *parent_uri;
-		
+
 		parent_uri = g_file_get_uri (parent);
 		if (parent_uri) {
 			GdkScreen *screen;
@@ -3236,7 +3339,7 @@ show_move_to_trash_confirm_dialog (EogWindow *window, GList *images, gboolean ca
 	 * asked and the trash is available */
 	if (can_trash && (dontaskagain || neverask))
 		return GTK_RESPONSE_OK;
-	
+
 	n_images = g_list_length (images);
 
 	if (n_images == 1) {
@@ -4121,7 +4224,7 @@ eog_window_drag_data_received (GtkWidget *widget,
 		return;
 	}
 
-        if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY) 
+        if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY)
         {
                 window = EOG_WINDOW (widget);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]