/*
- * Geeqie
- * (C) 2006 John Ellis
- * Copyright (C) 2008 - 2012 The Geeqie Team
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
*
* Author: John Ellis
*
- * This software is released under the GNU General Public License (GNU GPL).
- * Please read the included file COPYING for more information.
- * This software comes with no warranty of any kind, use at your own risk!
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-
#include "main.h"
#include "filedata.h"
#include "metadata.h"
#include "trash.h"
#include "histogram.h"
+#include "secure_save.h"
#include "exif.h"
+#include "misc.h"
#include <errno.h>
+#include <grp.h>
#ifdef DEBUG_FILEDATA
gint global_file_data_count = 0;
/* what I would like to use is printf("%'d", size)
* BUT: not supported on every libc :(
*/
- if (size > G_MAXUINT)
+ if (size > G_MAXINT)
{
/* the %lld conversion is not valid in all libcs, so use a simple work-around */
a = g_strdup_printf("%d%09d", (guint)(size / 1000000000), (guint)(size % 1000000000));
}
if (size < (gint64)1048576)
{
- return g_strdup_printf(_("%.1f K"), (gdouble)size / 1024.0);
+ return g_strdup_printf(_("%.1f KiB"), (gdouble)size / 1024.0);
}
if (size < (gint64)1073741824)
{
- return g_strdup_printf(_("%.1f MB"), (gdouble)size / 1048576.0);
+ return g_strdup_printf(_("%.1f MiB"), (gdouble)size / 1048576.0);
}
/* to avoid overflowing the gdouble, do division in two steps */
size /= 1048576;
- return g_strdup_printf(_("%.1f GB"), (gdouble)size / 1024.0);
+ return g_strdup_printf(_("%.1f GiB"), (gdouble)size / 1024.0);
}
/* note: returned string is valid until next call to text_from_time() */
{
fd->size = st->st_size;
fd->date = st->st_mtime;
+ fd->cdate = st->st_ctime;
fd->mode = st->st_mode;
if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
fd->thumb_pixbuf = NULL;
g_free(fd->collate_key_name);
g_free(fd->collate_key_name_nocase);
+#if GTK_CHECK_VERSION(2, 8, 0)
+ if (options->file_sort.natural)
+ {
+ 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(valid_name, -1);
+ fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1);
+ }
+#else
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);
static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean disable_sidecars)
{
FileData *fd;
+ struct passwd *user;
+ struct group *group;
DEBUG_2("file_data_new: '%s' %d", path_utf8, disable_sidecars);
if (fd)
{
DEBUG_1("planned change: using %s -> %s", path_utf8, fd->path);
- file_data_ref(fd);
- file_data_apply_ci(fd);
+ if (!isfile(fd->path))
+ {
+ file_data_ref(fd);
+ file_data_apply_ci(fd);
+ }
+ else
+ {
+ fd = NULL;
+ }
}
}
fd->size = st->st_size;
fd->date = st->st_mtime;
+ fd->cdate = st->st_ctime;
fd->mode = st->st_mode;
fd->ref = 1;
fd->magick = FD_MAGICK;
+ fd->exifdate = 0;
+ fd->rating = STAR_RATING_NOT_READ;
+ fd->format_class = filter_file_get_class(path_utf8);
+ fd->page_num = 0;
+ fd->page_total = 0;
+
+ user = getpwuid(st->st_uid);
+ if (!user)
+ {
+ fd->owner = g_strdup_printf("%u", st->st_uid);
+ }
+ else
+ {
+ fd->owner = g_strdup(user->pw_name);
+ }
+
+ group = getgrgid(st->st_gid);
+ if (!group)
+ {
+ fd->group = g_strdup_printf("%u", st->st_gid);
+ }
+ else
+ {
+ fd->group = g_strdup(group->gr_name);
+ }
+
+ fd->sym_link = get_symbolic_link(path_utf8);
if (disable_sidecars) fd->disable_grouping = TRUE;
return fd;
}
-void init_exif_time_data(GList *files)
+void read_exif_time_data(FileData *file)
{
- FileData *file;
- DEBUG_1("%s init_exif_time_data: ...", get_exec_time());
- while (files)
+ if (file->exifdate > 0)
+ {
+ DEBUG_1("%s set_exif_time_data: Already exists for %s", get_exec_time(), file->path);
+ return;
+ }
+
+ if (!file->exif)
{
- file = files->data;
+ exif_read_fd(file);
+ }
- if (file)
- file->exifdate = 0;
+ 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);
- files = files->next;
+ 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 read_exif_time_data(FileData *file)
+void read_exif_time_digitized_data(FileData *file)
{
- if (file->exifdate > 0)
+ if (file->exifdate_digitized > 0)
{
- DEBUG_1("%s set_exif_time_data: Already exists for %s", get_exec_time(), file->path);
+ 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)
+ {
+ 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);
+ 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)
{
time_str.tm_sec = sec;
time_str.tm_isdst = 0;
- file->exifdate = mktime(&time_str);
+ file->exifdate_digitized = mktime(&time_str);
g_free(tmp);
}
}
}
+void read_rating_data(FileData *file)
+{
+ gchar *rating_str;
+
+ rating_str = metadata_read_string(file, RATING_KEY, METADATA_PLAIN);
+ if (rating_str)
+ {
+ file->rating = atoi(rating_str);
+ g_free(rating_str);
+ }
+ else
+ {
+ file->rating = 0;
+ }
+}
+
void set_exif_time_data(GList *files)
{
DEBUG_1("%s set_exif_time_data: ...", get_exec_time());
}
}
+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;
+ DEBUG_1("%s set_rating_data: ...", get_exec_time());
+
+ while (files)
+ {
+ FileData *file = files->data;
+ rating_str = metadata_read_string(file, RATING_KEY, METADATA_PLAIN);
+ if (rating_str )
+ {
+ file->rating = atoi(rating_str);
+ g_free(rating_str);
+ }
+ files = files->next;
+ }
+}
+
FileData *file_data_new_no_grouping(const gchar *path_utf8)
{
struct stat st;
if (fd == NULL) return NULL;
if (fd->magick != FD_MAGICK)
#ifdef DEBUG_FILEDATA
- DEBUG_0("fd magick mismatch @ %s:%d fd=%p", file, line, fd);
+ log_printf("Error: fd magick mismatch @ %s:%d fd=%p", file, line, fd);
#else
- DEBUG_0("fd magick mismatch fd=%p", fd);
+ log_printf("Error: fd magick mismatch fd=%p", fd);
#endif
g_assert(fd->magick == FD_MAGICK);
fd->ref++;
g_free(fd->original_path);
g_free(fd->collate_key_name);
g_free(fd->collate_key_name_nocase);
+ g_free(fd->extended_extension);
if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
histmap_free(fd->histmap);
-
+ g_free(fd->owner);
+ g_free(fd->group);
+ g_free(fd->sym_link);
g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */
file_data_change_info_free(NULL, fd);
}
/**
- * \brief Checks if the FileData is referenced
+ * @brief Checks if the FileData is referenced
*
* Checks the refcount and whether the FileData is locked.
*/
}
/**
- * \brief Consider freeing a FileData.
+ * @brief Consider freeing a FileData.
*
* This function will free a FileData and its children provided that neither its parent nor it has
* a positive refcount, and provided that neither is locked.
if (fd == NULL) return;
if (fd->magick != FD_MAGICK)
#ifdef DEBUG_FILEDATA
- DEBUG_0("fd magick mismatch @ %s:%d fd=%p", file, line, fd);
+ log_printf("Error: fd magick mismatch @ %s:%d fd=%p", file, line, fd);
#else
- DEBUG_0("fd magick mismatch fd=%p", fd);
+ log_printf("Error: fd magick mismatch fd=%p", fd);
#endif
g_assert(fd->magick == FD_MAGICK);
}
/**
- * \brief Lock the FileData in memory.
+ * @brief Lock the FileData in memory.
*
* This allows the caller to prevent a FileData from being freed, even after its refcount is zero.
* This is intended to be used in cases where a FileData _should_ stay in memory as an optimization,
void file_data_lock(FileData *fd)
{
if (fd == NULL) return;
- if (fd->magick != FD_MAGICK) DEBUG_0("fd magick mismatch fd=%p", fd);
+ if (fd->magick != FD_MAGICK) log_printf("Error: fd magick mismatch fd=%p", fd);
g_assert(fd->magick == FD_MAGICK);
fd->locked = TRUE;
}
/**
- * \brief Reset the maintain-FileData-in-memory lock
+ * @brief Reset the maintain-FileData-in-memory lock
*
* This again allows the FileData to be freed when its refcount drops to zero. Automatically frees
* the FileData if its refcount is already zero (which will happen if the lock is the only thing
void file_data_unlock(FileData *fd)
{
if (fd == NULL) return;
- if (fd->magick != FD_MAGICK) DEBUG_0("fd magick mismatch fd=%p", fd);
+ if (fd->magick != FD_MAGICK) log_printf("Error: fd magick mismatch fd=%p", fd);
g_assert(fd->magick == FD_MAGICK);
fd->locked = FALSE;
}
/**
- * \brief Lock all of the FileDatas in the provided list
+ * @brief Lock all of the FileDatas in the provided list
*
- * \see file_data_lock(FileData)
+ * @see file_data_lock(#FileData)
*/
void file_data_lock_list(GList *list)
{
}
/**
- * \brief Unlock all of the FileDatas in the provided list
+ * @brief Unlock all of the FileDatas in the provided list
*
- * \see file_data_unlock(FileData)
+ * @see #file_data_unlock(#FileData)
*/
void file_data_unlock_list(GList *list)
{
target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
sfd->parent = NULL;
+ g_free(sfd->extended_extension);
+ sfd->extended_extension = NULL;
file_data_unref(target);
file_data_unref(sfd);
if (fa->date > fb->date) return 1;
/* fall back to name */
break;
+ case SORT_CTIME:
+ if (fa->cdate < fb->cdate) return -1;
+ if (fa->cdate > fb->cdate) 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;
+ 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;
+ /* fall back to name */
+ break;
+ case SORT_CLASS:
+ if (fa->format_class < fb->format_class) return -1;
+ if (fa->format_class > fb->format_class) 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);
}
list = g_hash_table_lookup(basename_hash, basename);
+ if (!list)
+ {
+ DEBUG_1("TG: basename_hash not found for %s",fd->path);
+ const gchar *parent_extension = registered_extension_from_path(basename);
+
+ if (parent_extension)
+ {
+ DEBUG_1("TG: parent extension %s",parent_extension);
+ gchar *parent_basename = g_strndup(basename, parent_extension - basename);
+ DEBUG_1("TG: parent basename %s",parent_basename);
+ FileData *parent_fd = g_hash_table_lookup(file_data_pool, basename);
+ if (parent_fd)
+ {
+ DEBUG_1("TG: parent fd found");
+ list = g_hash_table_lookup(basename_hash, parent_basename);
+ if (!g_list_find(list, parent_fd))
+ {
+ DEBUG_1("TG: parent fd doesn't fit");
+ g_free(parent_basename);
+ list = NULL;
+ }
+ else
+ {
+ g_free(basename);
+ basename = parent_basename;
+ fd->extended_extension = g_strconcat(parent_extension, fd->extension, NULL);
+ }
+ }
+ }
+ }
+
if (!g_list_find(list, fd))
{
list = g_list_insert_sorted(list, file_data_ref(fd), file_data_sort_by_ext);
return list;
}
+static void file_data_basename_hash_insert_cb(gpointer fd, gpointer basename_hash)
+{
+ file_data_basename_hash_insert((GHashTable *)basename_hash, (FileData *)fd);
+}
+
static void file_data_basename_hash_remove_list(gpointer key, gpointer value, gpointer data)
{
filelist_free((GList *)value);
gchar *pathl;
GList *dlist = NULL;
GList *flist = NULL;
+ GList *xmp_files = NULL;
gint (*stat_func)(const gchar *path, struct stat *buf);
GHashTable *basename_hash = NULL;
flist = g_list_prepend(flist, fd);
if (fd->sidecar_priority && !fd->disable_grouping)
{
- file_data_basename_hash_insert(basename_hash, fd);
+ if (strcmp(fd->extension, ".xmp") != 0)
+ file_data_basename_hash_insert(basename_hash, fd);
+ else
+ xmp_files = g_list_append(xmp_files, fd);
}
}
}
g_free(pathl);
+ if (xmp_files)
+ {
+ g_list_foreach(xmp_files,file_data_basename_hash_insert_cb,basename_hash);
+ g_list_free(xmp_files);
+ }
+
if (dirs) *dirs = dlist;
if (files)
}
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;
}
}
}
+static void filelist_recursive_append_full(GList **list, GList *dirs, SortType method, gboolean ascend)
+{
+ GList *work;
+
+ work = dirs;
+ while (work)
+ {
+ FileData *fd = (FileData *)(work->data);
+ GList *f;
+ GList *d;
+
+ if (filelist_read(fd, &f, &d))
+ {
+ f = filelist_filter(f, FALSE);
+ f = filelist_sort_full(f, method, ascend, (GCompareFunc) filelist_sort_file_cb);
+ *list = g_list_concat(*list, f);
+
+ d = filelist_filter(d, TRUE);
+ d = filelist_sort_path(d);
+ filelist_recursive_append_full(list, d, method, ascend);
+ filelist_free(d);
+ }
+
+ work = work->next;
+ }
+}
+
GList *filelist_recursive(FileData *dir_fd)
{
GList *list;
return list;
}
+GList *filelist_recursive_full(FileData *dir_fd, SortType method, gboolean ascend)
+{
+ GList *list;
+ GList *d;
+
+ if (!filelist_read(dir_fd, &list, &d)) return NULL;
+ list = filelist_filter(list, FALSE);
+ list = filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
+
+ d = filelist_filter(d, TRUE);
+ d = filelist_sort_path(d);
+ filelist_recursive_append_full(&list, d, method, ascend);
+ filelist_free(d);
+
+ return list;
+}
+
/*
*-----------------------------------------------------------------------------
* file modification support
if (!file_data_can_write_sidecar(fd)) return NULL;
work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
+ gchar *extended_extension = g_strconcat(fd->parent ? fd->parent->extension : fd->extension, ".xmp", NULL);
while (work)
{
FileData *sfd = work->data;
work = work->next;
- if (g_ascii_strcasecmp(sfd->extension, ".xmp") == 0)
+ if (g_ascii_strcasecmp(sfd->extension, ".xmp") == 0 || g_ascii_strcasecmp(sfd->extension, extended_extension) == 0)
{
sidecar_path = g_strdup(sfd->path);
break;
}
}
+ g_free(extended_extension);
if (!existing_only && !sidecar_path)
{
- gchar *base = g_strndup(fd->path, fd->extension - fd->path);
- sidecar_path = g_strconcat(base, ".xmp", NULL);
- g_free(base);
+ if (options->metadata.sidecar_extended_name)
+ sidecar_path = g_strconcat(fd->path, ".xmp", NULL);
+ else
+ {
+ gchar *base = g_strndup(fd->path, fd->extension - fd->path);
+ sidecar_path = g_strconcat(base, ".xmp", NULL);
+ g_free(base);
+ }
}
return sidecar_path;
return list;
}
+gboolean file_data_filter_file_filter(FileData *fd, GRegex *filter)
+{
+ return g_regex_match(filter, fd->name, 0, NULL);
+}
+
+GList *file_data_filter_file_filter_list(GList *list, GRegex *filter)
+{
+ GList *work;
+
+ work = list;
+ while (work)
+ {
+ FileData *fd = work->data;
+ GList *link = work;
+ work = work->next;
+
+ if (!file_data_filter_file_filter(fd, filter))
+ {
+ list = g_list_remove_link(list, link);
+ file_data_unref(fd);
+ g_list_free(link);
+ }
+ }
+
+ return list;
+}
+
+static gboolean file_data_filter_class(FileData *fd, guint filter)
+{
+ gint i;
+
+ for (i = 0; i < FILE_FORMAT_CLASSES; i++)
+ {
+ if (filter & (1 << i))
+ {
+ if ((FileFormatClass)i == filter_file_get_class(fd->path))
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+GList *file_data_filter_class_list(GList *list, guint filter)
+{
+ GList *work;
+
+ work = list;
+ while (work)
+ {
+ FileData *fd = work->data;
+ GList *link = work;
+ work = work->next;
+
+ if (!file_data_filter_class(fd, filter))
+ {
+ list = g_list_remove_link(list, link);
+ file_data_unref(fd);
+ g_list_free(link);
+ }
+ }
+
+ return list;
+}
+
static void file_data_notify_mark_func(gpointer key, gpointer value, gpointer user_data)
{
FileData *fd = value;
file_data_mark_func_data[n] = data;
file_data_destroy_mark_func[n] = notify;
- if (get_mark_func)
+ if (get_mark_func && file_data_pool)
{
/* this effectively changes all known files */
g_hash_table_foreach(file_data_pool, file_data_notify_mark_func, NULL);
COPY
MOVE - path is changed, name may be changed too
RENAME - path remains unchanged, name is changed
- extension should remain (FIXME should we allow editing extension? it will make problems wth grouping)
+ extension should remain (FIXME should we allow editing extension? it will make problems with grouping)
sidecar names are changed too, extensions are not changed
DELETE
UPDATE - file size, date or grouping has been changed
static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
{
- const gchar *extension = extension_from_path(fd->change->source);
+ const gchar *extension = registered_extension_from_path(fd->change->source);
gchar *base = remove_extension_from_path(dest_path);
gchar *old_path = fd->change->dest;
- fd->change->dest = g_strconcat(base, extension, NULL);
+ fd->change->dest = g_strconcat(base, fd->extended_extension ? fd->extended_extension : extension, NULL);
file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
g_free(old_path);
* it should detect all possible problems with the planned operation
*/
-gint file_data_verify_ci(FileData *fd)
+gint file_data_verify_ci(FileData *fd, GList *list)
{
gint ret = CHANGE_OK;
gchar *dir;
+ GList *work = NULL;
+ FileData *fd1 = NULL;
if (!fd->change)
{
if (!same)
{
- const gchar *dest_ext = extension_from_path(fd->change->dest);
+ const gchar *dest_ext = registered_extension_from_path(fd->change->dest);
if (!dest_ext) dest_ext = "";
-
- if (g_ascii_strcasecmp(fd->extension, dest_ext) != 0)
+ if (!options->file_filter.disable_file_extension_checks)
{
- ret |= CHANGE_WARN_CHANGED_EXT;
- DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
+ if (g_ascii_strcasecmp(fd->extension, dest_ext) != 0)
+ {
+ ret |= CHANGE_WARN_CHANGED_EXT;
+ DEBUG_1("Change checked: source and destination have different extensions: %s -> %s", fd->path, fd->change->dest);
+ }
}
}
else
{
- if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /* FIXME this is now needed for running editors */
+ if (fd->change->type != FILEDATA_CHANGE_UNSPECIFIED) /** @FIXME this is now needed for running editors */
{
ret |= CHANGE_WARN_SAME;
DEBUG_1("Change checked: source and destination are the same: %s -> %s", fd->path, fd->change->dest);
g_free(dest_dir);
}
+ /* During a rename operation, check if another planned destination file has
+ * the same filename
+ */
+ if(fd->change->type == FILEDATA_CHANGE_RENAME ||
+ fd->change->type == FILEDATA_CHANGE_COPY ||
+ fd->change->type == FILEDATA_CHANGE_MOVE)
+ {
+ work = list;
+ while (work)
+ {
+ fd1 = work->data;
+ work = work->next;
+ if (fd1 != NULL && fd != fd1 )
+ {
+ if (!strcmp(fd->change->dest, fd1->change->dest))
+ {
+ ret |= CHANGE_DUPLICATE_DEST;
+ }
+ }
+ }
+ }
+
fd->change->error = ret;
if (ret == 0) DEBUG_1("Change checked: OK: %s", fd->path);
}
-gint file_data_sc_verify_ci(FileData *fd)
+gint file_data_sc_verify_ci(FileData *fd, GList *list)
{
GList *work;
gint ret;
- ret = file_data_verify_ci(fd);
+ ret = file_data_verify_ci(fd, list);
work = fd->sidecar_files;
while (work)
{
FileData *sfd = work->data;
- ret |= file_data_verify_ci(sfd);
+ ret |= file_data_verify_ci(sfd, list);
work = work->next;
}
g_string_append(result, _("there are unsaved metadata changes for the file"));
}
+ if (error & CHANGE_DUPLICATE_DEST)
+ {
+ if (result->len > 0) g_string_append(result, ", ");
+ g_string_append(result, _("another destination file has the same filename"));
+ }
+
return g_string_free(result, FALSE);
}
fd = work->data;
work = work->next;
- error = with_sidecars ? file_data_sc_verify_ci(fd) : file_data_verify_ci(fd);
+ error = with_sidecars ? file_data_sc_verify_ci(fd, list) : file_data_verify_ci(fd, list);
all_errors |= error;
common_errors &= error;
{
FileDataChangeType type = fd->change->type;
- /* FIXME delete ?*/
+ /** @FIXME delete ?*/
if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME)
{
DEBUG_1("planned change: applying %s -> %s", fd->change->dest, fd->path);
/* this change overwrites another file which is already known to other modules
renaming fd would create duplicate FileData structure
the best thing we can do is nothing
- FIXME: maybe we could copy stuff like marks
+ */
+ /** @FIXME maybe we could copy stuff like marks
*/
DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path);
}
}
/* remove sidecars from the list,
- they can be still acessed via main_fd->sidecar_files */
+ they can be still accessed via main_fd->sidecar_files */
work = list;
while (work)
{
* notify other modules about the change described by FileDataChangeInfo
*/
-/* might use file_maint_ functions for now, later it should be changed to a system of callbacks
- FIXME do we need the ignore_list? It looks like a workaround for ineffective
+/* might use file_maint_ functions for now, later it should be changed to a system of callbacks */
+/** @FIXME do we need the ignore_list? It looks like a workaround for ineffective
implementation in view_file_list.c */
void file_data_send_notification(FileData *fd, NotifyType type)
{
+ GList *work = notify_func_list;
+
+ while (work)
+ {
+ NotifyData *nd = (NotifyData *)work->data;
+
+ nd->func(fd, type, nd->data);
+ work = work->next;
+ }
+ /*
NotifyIdleData *nid = g_new0(NotifyIdleData, 1);
nid->fd = file_data_ref(fd);
nid->type = type;
g_idle_add_full(G_PRIORITY_HIGH, file_data_send_notification_idle_cb, nid, NULL);
+ */
}
static GHashTable *file_data_monitor_pool = NULL;
return TRUE;
}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Saving marks list, clearing marks
+ * Uses file_data_pool
+ *-----------------------------------------------------------------------------
+ */
+
+static void marks_get_files(gpointer key, gpointer value, gpointer userdata)
+{
+ gchar *file_name = key;
+ GString *result = userdata;
+ FileData *fd;
+
+ if (isfile(file_name))
+ {
+ fd = value;
+ if (fd && fd->marks > 0)
+ {
+ g_string_append_printf(result, "%s,%i\n", fd->path, fd->marks);
+ }
+ }
+}
+
+gboolean marks_list_load(const gchar *path)
+{
+ FILE *f;
+ gchar s_buf[1024];
+ gchar *pathl;
+ gchar *file_path;
+ gchar *marks_value;
+
+ pathl = path_from_utf8(path);
+ f = fopen(pathl, "r");
+ g_free(pathl);
+ if (!f) return FALSE;
+
+ /* first line must start with Marks comment */
+ if (!fgets(s_buf, sizeof(s_buf), f) ||
+ strncmp(s_buf, "#Marks", 6) != 0)
+ {
+ fclose(f);
+ return FALSE;
+ }
+
+ while (fgets(s_buf, sizeof(s_buf), f))
+ {
+ if (s_buf[0]=='#') continue;
+ file_path = strtok(s_buf, ",");
+ marks_value = strtok(NULL, ",");
+ if (isfile(file_path))
+ {
+ FileData *fd = file_data_new_no_grouping(file_path);
+ file_data_ref(fd);
+ gint n = 0;
+ while (n <= 9)
+ {
+ gint mark_no = 1 << n;
+ if (atoi(marks_value) & mark_no)
+ {
+ file_data_set_mark(fd, n , 1);
+ }
+ n++;
+ }
+ }
+ }
+
+ fclose(f);
+ return TRUE;
+}
+
+gboolean marks_list_save(gchar *path, gboolean save)
+{
+ SecureSaveInfo *ssi;
+ gchar *pathl;
+ GString *marks = g_string_new("");
+
+ pathl = path_from_utf8(path);
+ ssi = secure_open(pathl);
+ g_free(pathl);
+ if (!ssi)
+ {
+ log_printf(_("Error: Unable to write marks lists to: %s\n"), path);
+ return FALSE;
+ }
+
+ secure_fprintf(ssi, "#Marks lists\n");
+
+ if (save)
+ {
+ g_hash_table_foreach(file_data_pool, marks_get_files, marks);
+ }
+ secure_fprintf(ssi, "%s", marks->str);
+ g_string_free(marks, FALSE);
+
+ secure_fprintf(ssi, "#end\n");
+ return (secure_close(ssi) == 0);
+}
+
+static void marks_clear(gpointer key, gpointer value, gpointer userdata)
+{
+ gchar *file_name = key;
+ gint mark_no;
+ gint n;
+ FileData *fd;
+
+ if (isfile(file_name))
+ {
+ fd = value;
+ if (fd && fd->marks > 0)
+ {
+ n = 0;
+ while (n <= 9)
+ {
+ mark_no = 1 << n;
+ if (fd->marks & mark_no)
+ {
+ file_data_set_mark(fd, n , 0);
+ }
+ n++;
+ }
+ }
+ }
+}
+
+void marks_clear_all()
+{
+ g_hash_table_foreach(file_data_pool, marks_clear, NULL);
+}
+
+void file_data_set_page_num(FileData *fd, gint page_num)
+{
+ if (fd->page_total > 1 && page_num < 0)
+ {
+ fd->page_num = fd->page_total - 1;
+ }
+ else if (fd->page_total > 1 && page_num <= fd->page_total)
+ {
+ fd->page_num = page_num - 1;
+ }
+ else
+ {
+ fd->page_num = 0;
+ }
+ file_data_send_notification(fd, NOTIFY_REREAD);
+}
+
+void file_data_inc_page_num(FileData *fd)
+{
+ if (fd->page_total > 0 && fd->page_num < fd->page_total - 1)
+ {
+ fd->page_num = fd->page_num + 1;
+ }
+ else if (fd->page_total == 0)
+ {
+ fd->page_num = fd->page_num + 1;
+ }
+ file_data_send_notification(fd, NOTIFY_REREAD);
+}
+
+void file_data_dec_page_num(FileData *fd)
+{
+ if (fd->page_num > 0)
+ {
+ fd->page_num = fd->page_num - 1;
+ }
+ file_data_send_notification(fd, NOTIFY_REREAD);
+}
+
+void file_data_set_page_total(FileData *fd, gint page_total)
+{
+ fd->page_total = page_total;
+}
+
/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */