Sort/search on Exif.Photo.DateTimeDigitized
authorColin Clark <colin.clark@cclark.uk>
Wed, 3 Jan 2018 20:58:28 +0000 (20:58 +0000)
committerColin Clark <colin.clark@cclark.uk>
Wed, 3 Jan 2018 20:58:28 +0000 (20:58 +0000)
Implement sort and search on Exif.Photo.DateTimeDigitized

Note: pan view not included

12 files changed:
doc/docbook/GuideImageSearchSearch.xml
doc/docbook/GuideMainWindowStatusBar.xml
src/collect.c
src/exif-common.c
src/filedata.c
src/filedata.h
src/lua.c
src/menu.c
src/search.c
src/typedefs.h
web/help/GuideImageSearchSearch.html
web/help/GuideMainWindowStatusBar.html

index 55a7082..210493e 100644 (file)
@@ -89,7 +89,7 @@
           <guilabel>File date</guilabel>\r
         </term>\r
         <listitem>\r
-          The search will match if the file modification time on disk is equal to, before, after, or between the entered date, depending on the method selected from the drop down menu. The\r
+          The search will match if the file date is equal to, before, after, or between the entered date, depending on the method selected from the drop down menu. The\r
           <emphasis>between</emphasis>\r
           test is inclusive, for example a file with date of 10/04/2003 will match if the date parameters are between 10/04/2003 and 12/31/2003.\r
           <para />\r
           </code>\r
           button displays a pop up calendar to enter the date.\r
           <para />\r
-          The\r
-          <emphasis role="strong">Exif date</emphasis>\r
-          checkbox permits searches to be made on the exif date of images. If an image does not have an exif date, it will default to 01 January 1970.\r
+          One of four date types may be selected. They are described in the\r
+          <link linkend="GuideReferenceFileDates">Reference section</link>\r
+          .\r
+          <note>If an image does not have an exif date, it will default to 01 January 1970.</note>\r
         </listitem>\r
       </varlistentry>\r
       <varlistentry>\r
index 6c32703..dbbb36f 100644 (file)
       </varlistentry>\r
       <varlistentry>\r
         <term>\r
-          <guilabel>File Creation Date</guilabel>\r
+          <guilabel>File Date</guilabel>\r
         </term>\r
         <listitem>\r
-          <para>Images are sorted by file creation date.</para>\r
-        </listitem>\r
-      </varlistentry>\r
-      <varlistentry>\r
-        <term>\r
-          <guilabel>Exif Date</guilabel>\r
-        </term>\r
-        <listitem>\r
-          <para>Images are sorted by file Exif date.</para>\r
+          <para>\r
+            Images are sorted by one of four types of file date. They are described in the\r
+            <link linkend="GuideReferenceFileDates">Reference section</link>\r
+            .\r
+          </para>\r
         </listitem>\r
       </varlistentry>\r
       <varlistentry>\r
index cc2dd6e..5773013 100644 (file)
@@ -169,6 +169,18 @@ static gint collection_list_sort_cb(gconstpointer a, gconstpointer b)
                        if (cia->fd->cdate > cib->fd->cdate) return 1;
                        return 0;
                        break;
+               case SORT_EXIFTIME:
+                       if (cia->fd->exifdate < cib->fd->exifdate) return -1;
+                       if (cia->fd->exifdate > cib->fd->exifdate) return 1;
+                       break;
+               case SORT_EXIFTIMEDIGITIZED:
+                       if (cia->fd->exifdate_digitized < cib->fd->exifdate_digitized) return -1;
+                       if (cia->fd->exifdate_digitized > cib->fd->exifdate_digitized) return 1;
+                       break;
+               case SORT_RATING:
+                       if (cia->fd->rating < cib->fd->rating) return -1;
+                       if (cia->fd->rating > cib->fd->rating) return 1;
+                       break;
                case SORT_PATH:
                        return utf8_compare(cia->fd->path, cib->fd->path, options->file_sort.case_sensitive);
                        break;
index 155898e..2af25a5 100644 (file)
@@ -250,6 +250,57 @@ static gchar *exif_build_formatted_DateTime(ExifData *exif)
        return text;
 }
 
+static gchar *exif_build_formatted_DateTimeDigitized(ExifData *exif)
+{
+       gchar *text = exif_get_data_as_text(exif, "Exif.Photo.DateTimeDigitized");
+       gchar *subsec = NULL;
+       gchar buf[128];
+       gchar *tmp;
+       gint buflen;
+       struct tm tm;
+       GError *error = NULL;
+
+       if (text)
+               {
+               subsec = exif_get_data_as_text(exif, "Exif.Photo.SubSecTimeDigitized");
+               }
+       else
+               {
+               text = exif_get_data_as_text(exif, "Exif.Image.DateTime");
+               if (text) subsec = exif_get_data_as_text(exif, "Exif.Photo.SubSecTime");
+               }
+
+       /* Convert the stuff into a tm struct */
+       memset(&tm, 0, sizeof(tm)); /* Uh, strptime could let garbage in tm! */
+       if (text && strptime(text, "%Y:%m:%d %H:%M:%S", &tm))
+               {
+               buflen = strftime(buf, sizeof(buf), "%x %X", &tm);
+               if (buflen > 0)
+                       {
+                       tmp = g_locale_to_utf8(buf, buflen, NULL, NULL, &error);
+                       if (error)
+                               {
+                               log_printf("Error converting locale strftime to UTF-8: %s\n", error->message);
+                               g_error_free(error);
+                               }
+                       else
+                               {
+                               g_free(text);
+                               text = g_strdup(tmp);
+                               }
+                       }
+               }
+
+       if (subsec)
+               {
+               tmp = text;
+               text = g_strconcat(tmp, ".", subsec, NULL);
+               g_free(tmp);
+               g_free(subsec);
+               }
+       return text;
+}
+
 static gchar *exif_build_formatted_ShutterSpeed(ExifData *exif)
 {
        ExifRational *r;
@@ -563,6 +614,7 @@ static gchar *exif_build_formatted_GPSAltitude(ExifData *exif)
 ExifFormattedText ExifFormattedList[] = {
        EXIF_FORMATTED_TAG(Camera,              N_("Camera")),
        EXIF_FORMATTED_TAG(DateTime,            N_("Date")),
+       EXIF_FORMATTED_TAG(DateTimeDigitized,   N_("DateDigitized")),
        EXIF_FORMATTED_TAG(ShutterSpeed,        N_("Shutter speed")),
        EXIF_FORMATTED_TAG(Aperture,            N_("Aperture")),
        EXIF_FORMATTED_TAG(ExposureBias,        N_("Exposure bias")),
index 301f448..d0c6484 100644 (file)
@@ -503,6 +503,41 @@ void read_exif_time_data(FileData *file)
                }
 }
 
+void read_exif_time_digitized_data(FileData *file)
+{
+       if (file->exifdate > 0)
+               {
+               DEBUG_1("%s set_exif_time_digitized_data: Already exists for %s", get_exec_time(), file->path);
+               return;
+               }
+
+       file->exif = exif_read_fd(file);
+
+       if (file->exif)
+               {
+               gchar *tmp = exif_get_data_as_text(file->exif, "Exif.Photo.DateTimeDigitized");
+               DEBUG_2("%s set_exif_time_digitized_data: reading %p %s", get_exec_time(), file, file->path);
+
+               if (tmp)
+                       {
+                       struct tm time_str;
+                       uint year, month, day, hour, min, sec;
+
+                       sscanf(tmp, "%4d:%2d:%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
+                       time_str.tm_year  = year - 1900;
+                       time_str.tm_mon   = month - 1;
+                       time_str.tm_mday  = day;
+                       time_str.tm_hour  = hour;
+                       time_str.tm_min   = min;
+                       time_str.tm_sec   = sec;
+                       time_str.tm_isdst = 0;
+
+                       file->exifdate_digitized = mktime(&time_str);
+                       g_free(tmp);
+                       }
+               }
+}
+
 void set_exif_time_data(GList *files)
 {
        DEBUG_1("%s set_exif_time_data: ...", get_exec_time());
@@ -516,6 +551,19 @@ void set_exif_time_data(GList *files)
                }
 }
 
+void set_exif_time_digitized_data(GList *files)
+{
+       DEBUG_1("%s set_exif_time_digitized_data: ...", get_exec_time());
+
+       while (files)
+               {
+               FileData *file = files->data;
+
+               read_exif_time_digitized_data(file);
+               files = files->next;
+               }
+}
+
 void set_rating_data(GList *files)
 {
        gchar *rating_str;
@@ -1046,6 +1094,11 @@ gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
                        if (fa->exifdate > fb->exifdate) return 1;
                        /* fall back to name */
                        break;
+               case SORT_EXIFTIMEDIGITIZED:
+                       if (fa->exifdate_digitized < fb->exifdate_digitized) return -1;
+                       if (fa->exifdate_digitized > fb->exifdate_digitized) return 1;
+                       /* fall back to name */
+                       break;
                case SORT_RATING:
                        if (fa->rating < fb->rating) return -1;
                        if (fa->rating > fb->rating) return 1;
@@ -1111,6 +1164,10 @@ GList *filelist_sort(GList *list, SortType method, gboolean ascend)
                {
                set_exif_time_data(list);
                }
+       if (method == SORT_EXIFTIMEDIGITIZED)
+               {
+               set_exif_time_digitized_data(list);
+               }
        if (method == SORT_RATING)
                {
                set_rating_data(list);
index ea29f2f..367996d 100644 (file)
@@ -162,5 +162,6 @@ gboolean file_data_register_real_time_monitor(FileData *fd);
 gboolean file_data_unregister_real_time_monitor(FileData *fd);
 
 void read_exif_time_data(FileData *file);
+void read_exif_time_digitized_data(FileData *file);
 #endif
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index 72b02cd..eabbad0 100644 (file)
--- a/src/lua.c
+++ b/src/lua.c
@@ -167,7 +167,22 @@ static int lua_exif_get_datum(lua_State *L)
                        lua_pushnil(L);
                        return 1;
                        }
-               } // if (strcmp(key, "Exif.Photo.Da...
+               }
+       else if (strcmp(key, "Exif.Photo.DateTimeDigitized") == 0)
+               {
+               memset(&tm, 0, sizeof(tm));
+               if (value && strptime(value, "%Y:%m:%d %H:%M:%S", &tm))
+                       {
+                       datetime = mktime(&tm);
+                       lua_pushnumber(L, datetime);
+                       return 1;
+                       }
+               else
+                       {
+                       lua_pushnil(L);
+                       return 1;
+                       }
+               }
        lua_pushstring(L, value);
        return 1;
 }
index 8fc6995..6c1d38b 100644 (file)
@@ -149,7 +149,10 @@ gchar *sort_type_get_text(SortType method)
                        return _("Sort by file creation date");
                        break;
                case SORT_EXIFTIME:
-                       return _("Sort by Exif-date");
+                       return _("Sort by Exif date original");
+                       break;
+               case SORT_EXIFTIMEDIGITIZED:
+                       return _("Sort by Exif date digitized");
                        break;
                case SORT_NONE:
                        return _("Unsorted");
@@ -212,6 +215,7 @@ GtkWidget *submenu_add_sort(GtkWidget *menu, GCallback func, gpointer data,
        submenu_add_sort_item(submenu, func, SORT_TIME, show_current, type);
        submenu_add_sort_item(submenu, func, SORT_CTIME, show_current, type);
        submenu_add_sort_item(submenu, func, SORT_EXIFTIME, show_current, type);
+       submenu_add_sort_item(submenu, func, SORT_EXIFTIMEDIGITIZED, show_current, type);
        submenu_add_sort_item(submenu, func, SORT_SIZE, show_current, type);
        submenu_add_sort_item(submenu, func, SORT_RATING, show_current, type);
        submenu_add_sort_item(submenu, func, SORT_CLASS, show_current, type);
index f046302..cbe235b 100644 (file)
@@ -119,6 +119,7 @@ struct _SearchData
        GtkWidget *menu_date;
        GtkWidget *date_sel;
        GtkWidget *date_sel_end;
+       GtkWidget *date_type;
 
        GtkWidget *check_dimensions;
        GtkWidget *menu_dimensions;
@@ -172,7 +173,6 @@ struct _SearchData
        gint   search_rating;
        gint   search_rating_end;
        gboolean   search_comment_match_case;
-       gboolean   search_date_exif;
 
        MatchType search_type;
 
@@ -1859,11 +1859,23 @@ static gboolean search_file_next(SearchData *sd)
                tested = TRUE;
                match = FALSE;
 
-               if (sd->search_date_exif)
+               if (g_strcmp0(gtk_combo_box_text_get_active_text(
+                                               GTK_COMBO_BOX_TEXT(sd->date_type)), _("Changed")) == 0)
+                       {
+                       file_date = fd->cdate;
+                       }
+               else if (g_strcmp0(gtk_combo_box_text_get_active_text(
+                                               GTK_COMBO_BOX_TEXT(sd->date_type)), _("Original")) == 0)
                        {
                        read_exif_time_data(fd);
                        file_date = fd->exifdate;
                        }
+               else if (g_strcmp0(gtk_combo_box_text_get_active_text(
+                                               GTK_COMBO_BOX_TEXT(sd->date_type)), _("Digitized")) == 0)
+                       {
+                       read_exif_time_digitized_data(fd);
+                       file_date = fd->exifdate_digitized;
+                       }
                else
                        {
                        file_date = fd->date;
@@ -2998,6 +3010,7 @@ void search_new(FileData *dir_fd, FileData *example_file)
                           _("File date is"), &sd->match_date_enable,
                           text_search_menu_date, sizeof(text_search_menu_date) / sizeof(MatchList),
                           G_CALLBACK(menu_choice_date_cb), sd);
+
        sd->date_sel = date_selection_new();
        date_selection_time_set(sd->date_sel, time(NULL));
        gtk_box_pack_start(GTK_BOX(hbox), sd->date_sel, FALSE, FALSE, 0);
@@ -3010,8 +3023,16 @@ void search_new(FileData *dir_fd, FileData *example_file)
        date_selection_time_set(sd->date_sel_end, time(NULL));
        gtk_box_pack_start(GTK_BOX(hbox2), sd->date_sel_end, FALSE, FALSE, 0);
        gtk_widget_show(sd->date_sel_end);
-       pref_checkbox_new_int(hbox, _("Exif date"),
-                               sd->search_date_exif, &sd->search_date_exif);
+
+       sd->date_type = gtk_combo_box_text_new();
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(sd->date_type), _("Modified"));
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(sd->date_type), _("Status Changed"));
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(sd->date_type), _("Original"));
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(sd->date_type), _("Digitized"));
+       gtk_box_pack_start(GTK_BOX(hbox), sd->date_type, FALSE, FALSE, 0);
+       gtk_combo_box_set_active(GTK_COMBO_BOX(sd->date_type), 0);
+       gtk_widget_set_tooltip_text(sd->date_type, "Modified (mtime)\nStatus Changed (ctime)\nOriginal (Exif.Photo.DateTimeOriginal)\nDigitized (Exif.Photo.DateTimeDigitized)");
+       gtk_widget_show(sd->date_type);
 
        /* Search for image dimensions */
        hbox = menu_choice(sd->box_search, &sd->check_dimensions, &sd->menu_dimensions,
index 6f25027..134adb0 100644 (file)
@@ -74,6 +74,7 @@ typedef enum {
        SORT_PATH,
        SORT_NUMBER,
        SORT_EXIFTIME,
+       SORT_EXIFTIMEDIGITIZED,
        SORT_RATING,
        SORT_CLASS
 } SortType;
@@ -588,6 +589,7 @@ struct _FileData {
 
        ExifData *exif;
        time_t exifdate;
+       time_t exifdate_digitized;
        GHashTable *modified_xmp; // hash table which contains unwritten xmp metadata in format: key->list of string values
        GList *cached_metadata;
        gint rating;
index 3018009..82aa5a9 100644 (file)
@@ -543,7 +543,7 @@ dd.answer div.label { float: left; }
           <span class="guilabel">File date</span>
         </dt>
 <dd>
-          The search will match if the file modification time on disk is equal to, before, after, or between the entered date, depending on the method selected from the drop down menu. The
+          The search will match if the file date is equal to, before, after, or between the entered date, depending on the method selected from the drop down menu. The
           <span class="emphasis">between</span>
           test is inclusive, for example a file with date of 10/04/2003 will match if the date parameters are between 10/04/2003 and 12/31/2003.
           <p class="para block"></p>
@@ -557,9 +557,10 @@ dd.answer div.label { float: left; }
           </span>
           button displays a pop up calendar to enter the date.
           <p class="para block"></p>
-          The
-          <span class="emphasis emphasis-bold">Exif date</span>
-          checkbox permits searches to be made on the exif date of images. If an image does not have an exif date, it will default to 01 January 1970.
+          One of four date types may be selected. They are described in the
+          <a class="link" href=".html#GuideReferenceFileDates" title="">Reference section</a>
+          .
+          <div class="admonition block note block-indent"><div class="note-inner">If an image does not have an exif date, it will default to 01 January 1970.</div></div>
         </dd>
 <dt class="term">
           <span class="guilabel">Image dimensions</span>
index 5a1e458..77ebd84 100644 (file)
@@ -501,16 +501,14 @@ dd.answer div.label { float: left; }
           <p class="para block block-first">Images are sorted by file modification date.</p>
         </dd>
 <dt class="term">
-          <span class="guilabel">File Creation Date</span>
+          <span class="guilabel">File Date</span>
         </dt>
 <dd>
-          <p class="para block block-first">Images are sorted by file creation date.</p>
-        </dd>
-<dt class="term">
-          <span class="guilabel">Exif Date</span>
-        </dt>
-<dd>
-          <p class="para block block-first">Images are sorted by file Exif date.</p>
+          <p class="para block block-first">
+            Images are sorted by one of four types of file date. They are described in the
+            <a class="link" href=".html#GuideReferenceFileDates" title="">Reference section</a>
+            .
+          </p>
         </dd>
 <dt class="term">
           <span class="guilabel">Size</span>