changeset 2501:b5cec98159e7

Fix #264, 274, 285, 436: Add 'Losslessly rotate image' keyboard shortcuts https://github.com/BestImageViewer/geeqie/issues/264 https://github.com/BestImageViewer/geeqie/issues/274 https://github.com/BestImageViewer/geeqie/issues/285 https://github.com/BestImageViewer/geeqie/issues/436 Additional entries on Edit/Orientation menu: Write orientation to file Write orientation to file (preserve timestamp) Preferences/Metadata option "Write altered image orientation to the metadata" must be off. Exiftran and mogrify must be installed.
author Colin Clark <colin.clark@cclark.uk>
date Mon, 12 Jun 2017 19:15:29 +0100
parents eb2ce489ceea
children 728c2b544eec
files doc/docbook/GuideMainWindowMenus.xml plugins/rotate/geeqie-rotate src/layout_util.c src/preferences.c
diffstat 4 files changed, 232 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/doc/docbook/GuideMainWindowMenus.xml	Thu Jun 08 20:46:52 2017 +0100
+++ b/doc/docbook/GuideMainWindowMenus.xml	Mon Jun 12 19:15:29 2017 +0100
@@ -742,6 +742,35 @@
       <varlistentry>
         <term>
           <menuchoice>
+            <guimenu>Orientation</guimenu>
+            <guimenuitem>Write orientation to file</guimenuitem>
+          </menuchoice>
+        </term>
+        <listitem>
+          <para>
+            Saves the current image orientation to the disk file. This operation can only be carried out on jpeg, tiff and png files. Jpeg rotations are lossless.
+            <note>
+              The Preferences/Metadata option "Write altered image orientation to the metadata" must be off.
+              <para />
+              Exiftran and mogrify must be installed.
+            </note>
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <menuchoice>
+            <guimenu>Orientation</guimenu>
+            <guimenuitem>Write orientation to file (preserve timestamp)</guimenuitem>
+          </menuchoice>
+        </term>
+        <listitem>
+          <para>As above, but the file date and time are preserved.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <menuchoice>
             <guimenu>Rating</guimenu>
           </menuchoice>
         </term>
--- a/plugins/rotate/geeqie-rotate	Thu Jun 08 20:46:52 2017 +0100
+++ b/plugins/rotate/geeqie-rotate	Mon Jun 12 19:15:29 2017 +0100
@@ -47,12 +47,83 @@
     esac
 }
 
+rotate_image_file()
+{
+	ext=`echo "${3##*.}" |tr "[:upper:]" "[:lower:]"`
+	[ "x$ext" = "x" ] && return 4 #no extension
+
+	case "$ext" in
+	jpg|jpeg)
+		exiftran -i "$1" "$3"
+		return 0
+		;;
+
+	tif|tiff|png)
+		mogrify $2 "$3"
+		return 0
+		;;
+
+	*)	#not supported
+		return 4
+		;;
+	esac
+}
+
 get_sidecars=
 if [ "x$1" = "x-g" ] ; then
     get_sidecars=yes
     shift
 fi
 
+rotate_image_file=
+rotation=
+if [ "x$1" = "x-r" ] ; then
+	rotate_image_file=yes
+	shift
+	rotation="$1"
+	shift
+fi
+
+preserve_mtime=
+if [ "x$1" = "x-t" ] ; then
+	preserve_mtime=yes
+	shift
+fi
+
+if [ -n "$rotation" ] ; then
+	if [ "x$rotation" = "x0" ] ; then
+		exit 0
+	fi
+	if [ "x$rotation" = "x2" ] ; then
+		mogrify_param="-flop"
+		exiftran_param="-F"
+	fi
+	if [ "x$rotation" = "x3" ] ; then
+		mogrify_param="-rotate 180"
+		exiftran_param="-1"
+	fi
+	if [ "x$rotation" = "x4" ] ; then
+		mogrify_param="-flip"
+		exiftran_param="-f"
+	fi
+	if [ "x$rotation" = "x5" ] ; then
+		mogrify_param="-transpose"
+		exiftran_param="-t"
+	fi
+	if [ "x$rotation" = "x6" ] ; then
+		mogrify_param="-rotate 90"
+		exiftran_param="-9"
+	fi
+	if [ "x$rotation" = "x7" ] ; then
+		mogrify_param="-transverse"
+		exiftran_param="-T"
+	fi
+	if [ "x$rotation" = "x8" ] ; then
+		mogrify_param="-rotate -90"
+		exiftran_param="-2"
+	fi
+fi
+
 # iterate over files on commandline
 for file in "$@" ; do
     if [ -n "$get_sidecars" ] ; then
@@ -63,7 +134,25 @@
             rotate "$sidecar"
         done
     else
-        rotate "$file"
+		if [ -n "$rotate_image_file" ] ; then
+			if [ -n "$preserve_mtime" ] ; then
+				mtime=`mktemp /tmp/geeqie-rotate.XXXXXXXXXX` || exit 3
+				touch --reference="$file" "$mtime"
+			fi
+
+			rotate_image_file "$exiftran_param" "$mogrify_param" "$file"
+			ret=$?
+
+			if [ -n "$preserve_mtime" ] ; then
+				touch --reference="$mtime" "$file"
+				rm "$mtime"
+			fi
+			if [ $ret -eq 4 ] ; then
+				exit 4
+			fi
+		else
+			rotate "$file"
+		fi
     fi
 done
 
--- a/src/layout_util.c	Thu Jun 08 20:46:52 2017 +0100
+++ b/src/layout_util.c	Mon Jun 12 19:15:29 2017 +0100
@@ -51,6 +51,7 @@
 #include "ui_tabcomp.h"
 #include "utilops.h"
 #include "view_dir.h"
+#include "view_file.h"
 #include "window.h"
 #include "metadata.h"
 #include "desktop_file.h"
@@ -59,6 +60,7 @@
 #include "keymap_template.c"
 
 #define MENU_EDIT_ACTION_OFFSET 16
+#define FILE_COLUMN_POINTER 0
 
 static gboolean layout_bar_enabled(LayoutWindow *lw);
 static gboolean layout_bar_sort_enabled(LayoutWindow *lw);
@@ -451,6 +453,101 @@
 	layout_image_reset_orientation(lw);
 }
 
+static void layout_menu_write_rotate(GtkToggleAction *action, gpointer data, gboolean keep_date)
+{
+	LayoutWindow *lw = data;
+	GtkTreeModel *store;
+	GList *work;
+	GtkTreeSelection *selection;
+	GtkTreePath *tpath;
+	FileData *fd_n;
+	GtkTreeIter iter;
+	IconData *id;
+	gchar *rotation;
+	gchar *command;
+	gint run_result;
+	GenericDialog *gd;
+	GString *message;
+
+	if (!layout_valid(&lw)) return;
+
+	if (!lw || !lw->vf) return;
+
+	if (lw->vf->type == FILEVIEW_ICON)
+		{
+		if (!VFICON(lw->vf)->selection) return;
+		work = VFICON(lw->vf)->selection;
+		}
+	else
+		{
+		selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(lw->vf->listview));
+		work = gtk_tree_selection_get_selected_rows(selection, &store);
+		}
+
+	while (work)
+		{
+		if (lw->vf->type == FILEVIEW_ICON)
+			{
+			id = work->data;
+			fd_n = id->fd;
+			work = work->next;
+			}
+		else
+			{
+			tpath = work->data;
+			gtk_tree_model_get_iter(store, &iter, tpath);
+			gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
+			work = work->next;
+			}
+
+		rotation = g_strdup_printf("%d", fd_n->user_orientation);
+		command = g_strconcat(GQ_BIN_DIR, "/geeqie-rotate -r ", rotation,
+													keep_date ? " -t " : " ", fd_n->path, NULL);
+
+		run_result = WEXITSTATUS(runcmd(command));
+		if (!run_result)
+			{
+			fd_n->user_orientation = 0;
+			}
+		else
+			{
+			message = g_string_new("");
+			message = g_string_append(message, _("Operation failed:\n"));
+
+			if (run_result == 3)
+				message = g_string_append(message, _("Cannot create tmp file"));
+			else
+				{
+				message = g_string_append(message, _("File: "));
+				message = g_string_append(message, fd_n->name);
+				}
+
+			gd = generic_dialog_new(_("Image orientation"),
+			"Image orientation", NULL, TRUE, NULL, NULL);
+			generic_dialog_add_message(gd, GTK_STOCK_DIALOG_ERROR,
+			"Image orientation", message->str);
+			generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, NULL, TRUE);
+
+			gtk_widget_show(gd->dialog);
+
+			g_string_free(message, TRUE);
+			}
+
+		g_free(rotation);
+		g_free(command);
+		}
+}
+
+static void layout_menu_write_rotate_keep_date_cb(GtkToggleAction *action, gpointer data)
+{
+	layout_menu_write_rotate(action, data, TRUE);
+}
+
+static void layout_menu_write_rotate_cb(GtkToggleAction *action, gpointer data)
+{
+	layout_menu_write_rotate(action, data, FALSE);
+}
+
 static void layout_menu_config_cb(GtkAction *action, gpointer data)
 {
 	LayoutWindow *lw = data;
@@ -1694,6 +1791,8 @@
   { "StereoCycle",	NULL,			N_("_Cycle through stereo modes"),	NULL,			N_("Cycle through stereo modes"),	CB(layout_menu_stereo_mode_next_cb) },
   { "SplitNextPane",	NULL,			N_("_Next Pane"),	"<alt>Right",			N_("Next Pane"),	CB(layout_menu_split_pane_next_cb) },
   { "SplitPreviousPane",	NULL,			N_("_Previous Pane"),	"<alt>Left",			N_("Previous Pane"),	CB(layout_menu_split_pane_prev_cb) },
+  { "WriteRotation",	NULL,			N_("_Write orientation to file"),  		NULL,		N_("Write orientation to file"),			CB(layout_menu_write_rotate_cb) },
+  { "WriteRotationKeepDate",	NULL,			N_("_Write orientation to file (preserve timestamp)"),  		NULL,		N_("Write orientation to file (preserve timestamp)"),			CB(layout_menu_write_rotate_keep_date_cb) },
 
 };
 
@@ -1833,6 +1932,9 @@
 "        <separator/>"
 "        <menuitem action='ExifRotate'/>"
 "        <separator/>"
+"        <menuitem action='WriteRotation'/>"
+"        <menuitem action='WriteRotationKeepDate'/>"
+"        <separator/>"
 "      </menu>"
 "      <menu action='RatingMenu'>"
 "        <menuitem action='Rating0'/>"
@@ -2634,6 +2736,13 @@
 	action = gtk_action_group_get_action(lw->action_group, "ConnectZoomMenu");
 	gtk_action_set_sensitive(action, lw->split_mode != SPLIT_NONE);
 
+	action = gtk_action_group_get_action(lw->action_group, "WriteRotation");
+	gtk_action_set_sensitive(action, !(runcmd("which exiftran >null") ||
+							runcmd("which mogrify >null") || options->metadata.write_orientation));
+	action = gtk_action_group_get_action(lw->action_group, "WriteRotationKeepDate");
+	gtk_action_set_sensitive(action, !(runcmd("which exiftran >null") ||
+							runcmd("which mogrify >null") || options->metadata.write_orientation));
+
 	action = gtk_action_group_get_action(lw->action_group, "StereoAuto");
 	gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), layout_image_stereo_pixbuf_get(lw));
 
--- a/src/preferences.c	Thu Jun 08 20:46:52 2017 +0100
+++ b/src/preferences.c	Mon Jun 12 19:15:29 2017 +0100
@@ -451,7 +451,11 @@
 
 static void config_window_apply_cb(GtkWidget *widget, gpointer data)
 {
+	LayoutWindow *lw;
+	lw = layout_window_list->data;
+
 	config_window_apply();
+	layout_util_sync(lw);
 }
 
 static void config_window_save_cb(GtkWidget *widget, gpointer data)