/*
* Geeqie
* (C) 2006 John Ellis
- * Copyright (C) 2008 - 2010 The Geeqie Team
+ * Copyright (C) 2008 - 2012 The Geeqie Team
*
* Author: John Ellis
*
#include "trash.h"
#include "histogram.h"
+#include "exif.h"
+
#include <errno.h>
static GHashTable *file_data_pool = NULL;
static gint sidecar_file_priority(const gchar *extension);
static void file_data_check_sidecars(const GList *basename_list);
-static FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd);
+static void file_data_disconnect_sidecar_file(FileData *target, FileData *sfd);
+static SortType filelist_sort_method = SORT_NONE;
+static gboolean filelist_sort_ascend = TRUE;
/*
*-----------------------------------------------------------------------------
{
fd->size = 0;
fd->date = 0;
+ file_data_ref(sfd);
file_data_disconnect_sidecar_file(fd, sfd);
ret = TRUE;
+ file_data_increment_version(sfd);
+ file_data_send_notification(sfd, NOTIFY_REREAD);
+ file_data_unref(sfd);
continue;
}
/* file_data_disconnect_sidecar_file might delete the file,
we have to keep the reference to prevent this */
sidecars = filelist_copy(fd->sidecar_files);
+ file_data_ref(fd);
work = sidecars;
while (work)
{
file_data_check_sidecars(sidecars); /* this will group the sidecars back together */
/* now we can release the sidecars */
filelist_free(sidecars);
+ file_data_increment_version(fd);
file_data_send_notification(fd, NOTIFY_REREAD);
+ file_data_unref(fd);
}
else
{
static void file_data_set_collate_keys(FileData *fd)
{
gchar *caseless_name;
+ gchar *valid_name;
- caseless_name = g_utf8_casefold(fd->name, -1);
+ valid_name = g_filename_display_name(fd->name);
+ caseless_name = g_utf8_casefold(valid_name, -1);
g_free(fd->collate_key_name);
g_free(fd->collate_key_name_nocase);
-#if 0 && GLIB_CHECK_VERSION(2, 8, 0)
- fd->collate_key_name = g_utf8_collate_key_for_filename(fd->name, -1);
- fd->collate_key_name_nocase = g_utf8_collate_key_for_filename(caseless_name, -1);
-#else
- fd->collate_key_name = g_utf8_collate_key(fd->name, -1);
+ fd->collate_key_name = g_utf8_collate_key(valid_name, -1);
fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1);
-#endif
+
+ g_free(valid_name);
g_free(caseless_name);
}
fd->date = st->st_mtime;
fd->mode = st->st_mode;
fd->ref = 1;
- fd->magick = 0x12345678;
+ fd->magick = FD_MAGICK;
if (disable_sidecars) fd->disable_grouping = TRUE;
return ret;
}
+void init_exif_time_data(GList *files)
+{
+ FileData *file;
+ DEBUG_1("%s init_exif_time_data: ...", get_exec_time());
+ while (files)
+ {
+ file = files->data;
+
+ if (file)
+ file->exifdate = 0;
+
+ files = files->next;
+ }
+}
+
+void read_exif_time_data(FileData *file)
+{
+ if (file->exifdate > 0)
+ {
+ DEBUG_1("%s set_exif_time_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.DateTimeOriginal");
+ DEBUG_2("%s set_exif_time_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 = mktime(&time_str);
+ g_free(tmp);
+ }
+ }
+}
+
+void set_exif_time_data(GList *files)
+{
+ DEBUG_1("%s set_exif_time_data: ...", get_exec_time());
+
+ while (files)
+ {
+ FileData *file = files->data;
+
+ read_exif_time_data(file);
+ files = files->next;
+ }
+}
+
FileData *file_data_new_no_grouping(const gchar *path_utf8)
{
struct stat st;
#endif
{
if (fd == NULL) return NULL;
+ if (fd->magick != FD_MAGICK)
#ifdef DEBUG_FILEDATA
- if (fd->magick != 0x12345678)
- DEBUG_0("fd magick mismatch at %s:%d", file, line);
+ DEBUG_0("fd magick mismatch @ %s:%d fd=%p", file, line, fd);
+#else
+ DEBUG_0("fd magick mismatch fd=%p", fd);
#endif
- g_assert(fd->magick == 0x12345678);
+ g_assert(fd->magick == FD_MAGICK);
fd->ref++;
#ifdef DEBUG_FILEDATA
- DEBUG_2("file_data_ref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
+ DEBUG_2("file_data_ref fd=%p (%d): '%s' @ %s:%d", fd, fd->ref, fd->path, file, line);
#else
- DEBUG_2("file_data_ref (%d): '%s'", fd->ref, fd->path);
+ DEBUG_2("file_data_ref fd=%p (%d): '%s'", fd, fd->ref, fd->path);
#endif
return fd;
}
static void file_data_free(FileData *fd)
{
- g_assert(fd->magick == 0x12345678);
+ g_assert(fd->magick == FD_MAGICK);
g_assert(fd->ref == 0);
metadata_cache_free(fd);
#endif
{
if (fd == NULL) return;
+ if (fd->magick != FD_MAGICK)
#ifdef DEBUG_FILEDATA
- if (fd->magick != 0x12345678)
- DEBUG_0("fd magick mismatch @ %s:%d", file, line);
+ DEBUG_0("fd magick mismatch @ %s:%d fd=%p", file, line, fd);
+#else
+ DEBUG_0("fd magick mismatch fd=%p", fd);
#endif
- g_assert(fd->magick == 0x12345678);
+ g_assert(fd->magick == FD_MAGICK);
fd->ref--;
#ifdef DEBUG_FILEDATA
- DEBUG_2("file_data_unref (%d): '%s' @ %s:%d", fd->ref, fd->path, file, line);
+ DEBUG_2("file_data_unref fd=%p (%d): '%s' @ %s:%d", fd, fd->ref, fd->path, file, line);
#else
- DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path);
+ DEBUG_2("file_data_unref fd=%p (%d): '%s'", fd, fd->ref, fd->path);
#endif
if (fd->ref == 0)
{
return 0;
}
-static FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd)
+static void file_data_check_sidecars(const GList *basename_list)
{
- sfd->parent = target;
- if (!g_list_find(target->sidecar_files, sfd))
- target->sidecar_files = g_list_insert_sorted(target->sidecar_files, sfd, file_data_sort_by_ext);
- file_data_increment_version(sfd); /* increments both sfd and target */
- return target;
-}
+ /* basename_list contains the new group - first is the parent, then sorted sidecars */
+ /* all files in the list have ref count > 0 */
+ const GList *work;
+ GList *s_work, *new_sidecars;
+ FileData *parent_fd;
-static FileData *file_data_merge_sidecar_files(FileData *target, FileData *source)
-{
- GList *work;
-
- file_data_add_sidecar_file(target, source);
+ if (!basename_list) return;
- work = source->sidecar_files;
+
+ DEBUG_2("basename start");
+ work = basename_list;
while (work)
{
- FileData *sfd = work->data;
- file_data_add_sidecar_file(target, sfd);
+ FileData *fd = work->data;
work = work->next;
+ g_assert(fd->magick == FD_MAGICK);
+ DEBUG_2("basename: %p %s", fd, fd->name);
+ if (fd->parent)
+ {
+ g_assert(fd->parent->magick == FD_MAGICK);
+ DEBUG_2(" parent: %p", fd->parent);
+ }
+ s_work = fd->sidecar_files;
+ while (s_work)
+ {
+ FileData *sfd = s_work->data;
+ s_work = s_work->next;
+ g_assert(sfd->magick == FD_MAGICK);
+ DEBUG_2(" sidecar: %p %s", sfd, sfd->name);
+ }
+
+ g_assert(fd->parent == NULL || fd->sidecar_files == NULL);
}
- g_list_free(source->sidecar_files);
- source->sidecar_files = NULL;
-
- return target;
-}
-
-static void file_data_check_sidecars(const GList *basename_list)
-{
- GList *work;
- FileData *parent_fd;
- if (!basename_list) return;
- /* process the group list - the first one is the parent file, others are sidecars */
parent_fd = basename_list->data;
+
+ /* check if the second and next entries of basename_list are already connected
+ as sidecars of the first entry (parent_fd) */
work = basename_list->next;
- while (work)
+ s_work = parent_fd->sidecar_files;
+
+ while (work && s_work)
{
- FileData *sfd = work->data;
+ if (work->data != s_work->data) break;
work = work->next;
-
- file_data_merge_sidecar_files(parent_fd, sfd);
+ s_work = s_work->next;
}
- /* there may be some sidecars that are already deleted - disconnect them */
- work = parent_fd->sidecar_files;
+ if (!work && !s_work)
+ {
+ DEBUG_2("basename no change");
+ return; /* no change in grouping */
+ }
+
+ /* we have to regroup it */
+
+ /* first, disconnect everything and send notification*/
+
+ work = basename_list;
while (work)
{
- FileData *sfd = work->data;
+ FileData *fd = work->data;
work = work->next;
+ g_assert(fd->parent == NULL || fd->sidecar_files == NULL);
- if (!g_list_find((GList *)basename_list, sfd))
+ if (fd->parent)
{
- printf("removing unknown %s: %s \n", parent_fd->path, sfd->path);
- file_data_disconnect_sidecar_file(parent_fd, sfd);
+ FileData *old_parent = fd->parent;
+ g_assert(old_parent->parent == NULL || old_parent->sidecar_files == NULL);
+ file_data_ref(old_parent);
+ file_data_disconnect_sidecar_file(old_parent, fd);
+ file_data_send_notification(old_parent, NOTIFY_REREAD);
+ file_data_unref(old_parent);
+ }
+
+ while (fd->sidecar_files)
+ {
+ FileData *sfd = fd->sidecar_files->data;
+ g_assert(sfd->parent == NULL || sfd->sidecar_files == NULL);
+ file_data_ref(sfd);
+ file_data_disconnect_sidecar_file(fd, sfd);
file_data_send_notification(sfd, NOTIFY_REREAD);
- file_data_send_notification(parent_fd, NOTIFY_REREAD);
+ file_data_unref(sfd);
}
+ file_data_send_notification(fd, NOTIFY_GROUPING);
+
+ g_assert(fd->parent == NULL && fd->sidecar_files == NULL);
+ }
+
+ /* now we can form the new group */
+ work = basename_list->next;
+ new_sidecars = NULL;
+ while (work)
+ {
+ FileData *sfd = work->data;
+ g_assert(sfd->magick == FD_MAGICK);
+ g_assert(sfd->parent == NULL && sfd->sidecar_files == NULL);
+ sfd->parent = parent_fd;
+ new_sidecars = g_list_prepend(new_sidecars, sfd);
+ work = work->next;
}
+ g_assert(parent_fd->sidecar_files == NULL);
+ parent_fd->sidecar_files = g_list_reverse(new_sidecars);
+ DEBUG_1("basename group changed for %s", parent_fd->path);
}
-static FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
+
+static void file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
{
- sfd->parent = target;
+ g_assert(target->magick == FD_MAGICK);
+ g_assert(sfd->magick == FD_MAGICK);
g_assert(g_list_find(target->sidecar_files, sfd));
+
+ file_data_ref(target);
+ file_data_ref(sfd);
+
+ g_assert(sfd->parent == target);
file_data_increment_version(sfd); /* increments both sfd and target */
target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
sfd->parent = NULL;
- if (sfd->ref == 0)
- {
- file_data_free(sfd);
- return NULL;
- }
-
- return sfd;
+ file_data_unref(target);
+ file_data_unref(sfd);
}
/* disables / enables grouping for particular file, sends UPDATE notification */
*-----------------------------------------------------------------------------
*/
-static SortType filelist_sort_method = SORT_NONE;
-static gboolean filelist_sort_ascend = TRUE;
-
gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
{
if (fa->date > fb->date) return 1;
/* fall back to name */
break;
+ case SORT_EXIFTIME:
+ if (fa->exifdate < fb->exifdate) return -1;
+ if (fa->exifdate > fb->exifdate) return 1;
+ /* fall back to name */
+ break;
#ifdef HAVE_STRVERSCMP
case SORT_NUMBER:
ret = strverscmp(fa->name, fb->name);
GList *filelist_sort(GList *list, SortType method, gboolean ascend)
{
+ if (method == SORT_EXIFTIME)
+ {
+ set_exif_time_data(list);
+ }
return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
}
return list;
}
-#if 0
-static void file_data_basename_hash_remove(GHashTable *basename_hash, FileData *fd)
-{
- GList *list;
- gchar *basename = g_strndup(fd->path, fd->extension - fd->path);
-
- list = g_hash_table_lookup(basename_hash, basename);
-
- if (!g_list_find(list, fd)) return;
-
- list = g_list_remove(list, fd);
- file_data_unref(fd);
-
- if (list)
- {
- g_hash_table_insert(basename_hash, basename, list);
- }
- else
- {
- g_hash_table_remove(basename_hash, basename);
- g_free(basename);
- }
-}
-#endif
-
static void file_data_basename_hash_remove_list(gpointer key, gpointer value, gpointer data)
{
filelist_free((GList *)value);
g_free(pathl);
if (dirs) *dirs = dlist;
+
if (files)
{
g_hash_table_foreach(basename_hash, file_data_basename_hash_to_sidecars, NULL);
}
if (basename_hash) file_data_basename_hash_free(basename_hash);
+ // Call a separate function to initialize the exif datestamps for the found files..
+ if (files) init_exif_time_data(*files);
+
return TRUE;
}
}
else if (!access_file(dest_dir, W_OK))
{
- ret |= CHANGE_NO_WRITE_PERM_DEST_DIR;
+ ret |= CHANGE_WARN_NO_WRITE_PERM_DEST_DIR;
DEBUG_1("Change checked: destination dir is readonly: %s -> %s", fd->path, fd->change->dest);
}
else if (!same)
g_string_append(result, _("destination can't be overwritten"));
}
- if (error & CHANGE_NO_WRITE_PERM_DEST_DIR)
+ if (error & CHANGE_WARN_NO_WRITE_PERM_DEST_DIR)
{
if (result->len > 0) g_string_append(result, ", ");
g_string_append(result, _("destination directory is not writable"));
return TRUE;
}
-#if 0
-static gboolean file_data_list_dump(GList *list)
-{
- GList *work, *work2;
-
- work = list;
- while (work)
- {
- FileData *fd = work->data;
- printf("%s\n", fd->name);
- work2 = fd->sidecar_files;
- while (work2)
- {
- FileData *fd = work2->data;
- printf(" %s\n", fd->name);
- work2 = work2->next;
- }
- work = work->next;
- }
- return TRUE;
-}
-#endif
-
GList *file_data_process_groups_in_selection(GList *list, gboolean ungroup, GList **ungrouped_list)
{
GList *out = NULL;