Fix #264, 274, 285, 436: Add 'Losslessly rotate image' keyboard shortcuts
authorColin Clark <colin.clark@cclark.uk>
Mon, 12 Jun 2017 18:15:29 +0000 (19:15 +0100)
committerColin Clark <colin.clark@cclark.uk>
Mon, 12 Jun 2017 18:15:29 +0000 (19:15 +0100)
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.

doc/docbook/GuideMainWindowMenus.xml
plugins/rotate/geeqie-rotate
src/layout_util.c
src/preferences.c

index 754c5bd..edfec79 100644 (file)
           <para>Rotates the current image 180 degrees, does not modify the file on disk.</para>\r
         </listitem>\r
       </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <menuchoice>\r
+            <guimenu>Orientation</guimenu>\r
+            <guimenuitem>Write orientation to file</guimenuitem>\r
+          </menuchoice>\r
+        </term>\r
+        <listitem>\r
+          <para>\r
+            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.\r
+            <note>\r
+              The Preferences/Metadata option "Write altered image orientation to the metadata" must be off.\r
+              <para />\r
+              Exiftran and mogrify must be installed.\r
+            </note>\r
+          </para>\r
+        </listitem>\r
+      </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <menuchoice>\r
+            <guimenu>Orientation</guimenu>\r
+            <guimenuitem>Write orientation to file (preserve timestamp)</guimenuitem>\r
+          </menuchoice>\r
+        </term>\r
+        <listitem>\r
+          <para>As above, but the file date and time are preserved.</para>\r
+        </listitem>\r
+      </varlistentry>\r
       <varlistentry>\r
         <term>\r
           <menuchoice>\r
index 1c1184e..ec82cc0 100755 (executable)
@@ -47,12 +47,83 @@ rotate()
     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 @@ for file in "$@" ; do
             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
 
index 735a9e9..9cc0558 100644 (file)
@@ -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 @@ static void layout_menu_exif_rotate_cb(GtkToggleAction *action, gpointer data)
        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 @@ static GtkActionEntry menu_entries[] = {
   { "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 @@ static const gchar *menu_ui_description =
 "        <separator/>"
 "        <menuitem action='ExifRotate'/>"
 "        <separator/>"
+"        <menuitem action='WriteRotation'/>"
+"        <menuitem action='WriteRotationKeepDate'/>"
+"        <separator/>"
 "      </menu>"
 "      <menu action='RatingMenu'>"
 "        <menuitem action='Rating0'/>"
@@ -2634,6 +2736,13 @@ static void layout_util_sync_views(LayoutWindow *lw)
        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));
 
index 6347b7d..aebc768 100644 (file)
@@ -451,7 +451,11 @@ static void config_window_ok_cb(GtkWidget *widget, gpointer data)
 
 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)