From 6f2bfb6ee048d2363a3ae3e7c330d4326cc03bf0 Mon Sep 17 00:00:00 2001
From: Daniel Mueller <deso@posteo.net>
Date: Sat, 7 Nov 2015 15:16:41 -0800
Subject: [PATCH] Add png saving support

Geeqie has very nice RAW image viewing capabilities. The usual way for
easy conversion of such RAW files into a more widely spread format
(read: jpeg, png, or tiff) is likely via invocation of ufraw by means of
the UI.
Unfortunately, ufraw might display the image in a (potentially vastly)
different way, using other lens and color corrections or the like. In
such a case one has to tediously try to adjust the parameters to get a
better version. The image displayed by geeqie itself, however, most of
the time looks nicer than what is achievable by manual corrections in
ufraw within a reasonable amount of time. Unfortunately, it has no way
of exporting images.
This change adds support for directly exporting the image as displayed
in geeqie as a png file.
---
 src/image.c               | 50 +++++++++++++++++++++++++++++++++++++++
 src/image.h               |  4 ++++
 src/layout_image.c        | 14 +++++++++++
 src/layout_util.c         | 14 +++++++++++
 src/view_file/view_file.c | 39 ++++++++++++++++++++++++++++++
 5 files changed, 121 insertions(+)

diff --git a/src/image.c b/src/image.c
index 1326ff..34c18b 100644
--- a/src/image.c
+++ b/src/image.c
@@ -37,6 +37,7 @@
 #include "pixbuf-renderer.h"
 #include "pixbuf_util.h"
 #include "ui_fileops.h"
+#include "utilops.h"
 
 #include "filedata.h"
 #include "filecache.h"
@@ -2324,4 +2325,53 @@ void image_get_rectangle(gint *x1, gint *y1, gint *x2, gint *y2)
 	*y2 = rect_y2;
 }
 
+#define image_show_error_message(parent, ...)							\
+	{											\
+		gchar *text = g_strdup_printf(__VA_ARGS__);					\
+		file_util_warning_dialog(_("Error"), text, GTK_STOCK_DIALOG_ERROR, parent);	\
+		g_free(text);									\
+	}
+
+void image_save_as_png(ImageWindow *imd)
+{
+	const gchar const png_ext[] = ".png";
+	GdkPixbuf *pixbuf;
+	gchar const *path;
+	gchar *png_path;
+	gboolean result;
+	size_t size;
+
+	path = image_get_path(imd);
+	g_assert(path != NULL);
+
+	size = strlen(path);
+	png_path = malloc(size + sizeof(png_ext) + 1 /* NULL byte */);
+	if (png_path == NULL)
+	{
+		image_show_error_message(imd->widget, "%s", "Allocating memory for path failed.");
+		return;
+	}
+
+	memcpy(png_path, path, size);
+	memcpy(png_path + size, png_ext, sizeof(png_ext) /* Includes NULL byte. */);
+
+	if (access_file(png_path, F_OK))
+	{
+		/* The file already exists. */
+		/*
+		 * TODO: We could use some more elaborate handling here but for now
+		 *       we just bail out.
+		 */
+		image_show_error_message(imd->widget, "The file %s already exists.", png_path);
+		goto out;
+	}
+	pixbuf = image_get_pixbuf(imd);
+	result = pixbuf_to_file_as_png(pixbuf, png_path);
+
+	if (!result)
+		image_show_error_message(imd->widget, "Failed to save png: %s", png_path);
+
+out:
+	free(png_path);
+}
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
diff --git a/src/image.h b/src/image.h
index b8233f..674e3b 100644
--- a/src/image.h
+++ b/src/image.h
@@ -176,5 +176,9 @@ void image_options_sync(void);
 
 void image_get_rectangle(gint *x1, gint *y1, gint *x2, gint *y2);
 void image_update_title(ImageWindow *imd);
+
+/* export to png */
+void image_save_as_png(ImageWindow *imd);
+
 #endif
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
diff --git a/src/layout_image.c b/src/layout_image.c
index 9c38f1..e3dfd8 100644
--- a/src/layout_image.c
+++ b/src/layout_image.c
@@ -516,6 +516,18 @@ static void li_pop_menu_edit_cb(GtkWidget *widget, gpointer data)
 	file_util_start_editor_from_file(key, layout_image_get_fd(lw), lw->window);
 }
 
+static void li_pop_memu_save_as_png_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+	ImageWindow *imd = lw->image;
+
+	/*
+	 * TODO: We should update the image view afterwards to have the new
+	 *       image listed properly.
+	 */
+	image_save_as_png(imd);
+}
+
 #if !GTK_CHECK_VERSION(3,0,0)
 static void li_pop_menu_wallpaper_cb(GtkWidget *widget, gpointer data)
 {
@@ -810,6 +822,8 @@ static GtkWidget *layout_image_pop_menu(LayoutWindow *lw)
 
 	menu_item_add_divider(menu);
 
+	item = menu_item_add_stock(menu, _("_Save as png"), GTK_STOCK_SAVE, G_CALLBACK(li_pop_memu_save_as_png_cb), lw);
+	if (!path) gtk_widget_set_sensitive(item, FALSE);
 	item = menu_item_add_stock(menu, _("_Copy..."), GTK_STOCK_COPY, G_CALLBACK(li_pop_menu_copy_cb), lw);
 	if (!path) gtk_widget_set_sensitive(item, FALSE);
 	item = menu_item_add(menu, _("_Move..."), G_CALLBACK(li_pop_menu_move_cb), lw);
diff --git a/src/layout_util.c b/src/layout_util.c
index e77b8c..8cced2 100644
--- a/src/layout_util.c
+++ b/src/layout_util.c
@@ -307,6 +307,18 @@ static void layout_menu_dir_cb(GtkAction *action, gpointer data)
 	if (lw->vd) vd_new_folder(lw->vd, lw->dir_fd);
 }
 
+static void layout_menu_save_as_png_cb(GtkAction *action, gpointer data)
+{
+	LayoutWindow *lw = data;
+	ImageWindow *imd = lw->image;
+
+	/*
+	 * TODO: We should update the image view afterwards to have the new
+	 *       image listed properly.
+	 */
+	image_save_as_png(imd);
+}
+
 static void layout_menu_copy_cb(GtkAction *action, gpointer data)
 {
 	LayoutWindow *lw = data;
@@ -2516,6 +2528,7 @@ static GtkActionEntry menu_entries[] = {
   { "PanView",	PIXBUF_INLINE_ICON_PANORAMA,	N_("Pa_n view"),			"<control>J",		N_("Pan view"),				CB(layout_menu_pan_cb) },
   { "Print",		GTK_STOCK_PRINT,	N_("_Print..."),			"<shift>P",		N_("Print..."),				CB(layout_menu_print_cb) },
   { "NewFolder",	GTK_STOCK_DIRECTORY,	N_("N_ew folder..."),			"<control>F",		N_("New folder..."),			CB(layout_menu_dir_cb) },
+  { "Png",		GTK_STOCK_SAVE,		N_("_Save as png"),			"<control>S",		N_("Save as png"),			CB(layout_menu_save_as_png_cb) },
   { "EnableGrouping",	NULL,			N_("Enable file _grouping"),		NULL,			N_("Enable file grouping"),		CB(layout_menu_enable_grouping_cb) },
   { "DisableGrouping",	NULL,			N_("Disable file groupi_ng"),		NULL,			N_("Disable file grouping"),		CB(layout_menu_disable_grouping_cb) },
   { "CopyPath",		NULL,			N_("_Copy path to clipboard"),		NULL,			N_("Copy path to clipboard"),		CB(layout_menu_copy_path_cb) },
@@ -2697,6 +2710,7 @@ static const gchar *menu_ui_description =
 "      <placeholder name='PrintSection'/>"
 "      <separator/>"
 "      <menuitem action='NewFolder'/>"
+"      <menuitem action='Png'/>"
 "      <menuitem action='Copy'/>"
 "      <menuitem action='Move'/>"
 "      <menuitem action='Rename'/>"
diff --git a/src/view_file/view_file.c b/src/view_file/view_file.c
index ba0f51..a53b05 100644
--- a/src/view_file/view_file.c
+++ b/src/view_file/view_file.c
@@ -26,7 +26,9 @@
 #include "collect-table.h"
 #include "editors.h"
 #include "history_list.h"
+#include "image.h"
 #include "layout.h"
+#include "layout_image.h"
 #include "menu.h"
 #include "misc.h"
 #include "pixbuf_util.h"
@@ -397,6 +399,35 @@ static void vf_pop_menu_open_archive_cb(GtkWidget *widget, gpointer data)
 		}
 }
 
+static gboolean vf_is_current_selection_displayed(ViewFile *vf)
+{
+	GList *list;
+	gboolean result = FALSE;
+
+	list = vflist_pop_menu_file_list(vf);
+	if (g_list_length(list) == 1)
+	{
+		FileData *sel_fd = g_list_first(list)->data;
+		FileData *displayed_fd = layout_image_get_fd(vf->layout);
+
+		result = sel_fd == displayed_fd;
+	}
+	filelist_free(list);
+	return result;
+}
+
+static void vf_pop_menu_save_as_png_cb(GtkWidget *widget, gpointer data)
+{
+	ViewFile *vf = data;
+
+	g_assert(vf_is_current_selection_displayed(vf));
+	/*
+	 * TODO: We should update the image view afterwards to have the new
+	 *       image listed properly.
+	 */
+	image_save_as_png(vf->layout->image);
+}
+
 static void vf_pop_menu_copy_cb(GtkWidget *widget, gpointer data)
 {
 	ViewFile *vf = data;
@@ -617,6 +648,7 @@ GtkWidget *vf_pop_menu(ViewFile *vf)
 	GtkWidget *item;
 	GtkWidget *submenu;
 	gboolean active = FALSE;
+	gboolean displayed = vf_is_current_selection_displayed(vf);
 	gboolean class_archive = FALSE;
 	GtkAccelGroup *accel_group;
 
@@ -701,6 +733,13 @@ GtkWidget *vf_pop_menu(ViewFile *vf)
 	menu_item_add_stock_sensitive(menu, _("Open archive"), GTK_STOCK_OPEN, active & class_archive, G_CALLBACK(vf_pop_menu_open_archive_cb), vf);
 
 	menu_item_add_divider(menu);
+	/*
+	 * We only allow exporting of a single item -- the selected one.
+	 * The reason is that the image must be displayed currently in
+	 * order for the pixel data to be available.
+	 */
+	menu_item_add_stock_sensitive(menu, _("_Save as png"), GTK_STOCK_SAVE, active && displayed,
+				      G_CALLBACK(vf_pop_menu_save_as_png_cb), vf);
 	menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
 				      G_CALLBACK(vf_pop_menu_copy_cb), vf);
 	menu_item_add_sensitive(menu, _("_Move..."), active,
-- 
2.32.0

