/*
- * Geeqie
- * (C) 2005 John Ellis
- * Copyright (C) 2008 The Geeqie Team
+ * Copyright (C) 2005 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 <inttypes.h>
#include "main.h"
#include "dupe.h"
#include "dnd.h"
#include "editors.h"
#include "filedata.h"
+#include "history_list.h"
#include "image-load.h"
#include "img-view.h"
-#include "info.h"
#include "layout.h"
#include "layout_image.h"
+#include "layout_util.h"
#include "md5-util.h"
#include "menu.h"
+#include "misc.h"
+#include "pixbuf_util.h"
#include "print.h"
#include "thumb.h"
-#include "utilops.h"
#include "ui_fileops.h"
#include "ui_menu.h"
#include "ui_misc.h"
#include "ui_tree_edit.h"
#include "uri_utils.h"
+#include "utilops.h"
#include "window.h"
#include <gdk/gdkkeysyms.h> /* for keyboard values */
#include <math.h>
-#define DUPE_DEF_WIDTH 600
+#define DUPE_DEF_WIDTH 800
#define DUPE_DEF_HEIGHT 400
+#define DUPE_PROGRESS_PULSE_STEP 0.0001
/* column assignment order (simply change them here) */
enum {
DUPE_COLUMN_DIMENSIONS,
DUPE_COLUMN_PATH,
DUPE_COLUMN_COLOR,
+ DUPE_COLUMN_SET,
DUPE_COLUMN_COUNT /* total columns */
};
static void dupe_notify_cb(FileData *fd, NotifyType type, gpointer data);
+static GtkWidget *submenu_add_export(GtkWidget *menu, GtkWidget **menu_item, GCallback func, gpointer data);
+static void dupe_pop_menu_export_cb(GtkWidget *widget, gpointer data);
/*
* ------------------------------------------------------------------
* Window updates
*/
-static void dupe_window_update_count(DupeWindow *dw, gint count_only)
+static void dupe_window_update_count(DupeWindow *dw, gboolean count_only)
{
gchar *text;
return (n * ((n + 1) / 2));
}
-static void dupe_window_update_progress(DupeWindow *dw, const gchar *status, gdouble value, gint force)
+static void dupe_window_update_progress(DupeWindow *dw, const gchar *status, gdouble value, gboolean force)
{
const gchar *status_text;
{
GdkCursor *cursor;
- if (!widget->window) return;
+ if (!gtk_widget_get_window(widget)) return;
if (icon == -1)
{
cursor = gdk_cursor_new(icon);
}
- gdk_window_set_cursor(widget->window, cursor);
+ gdk_window_set_cursor(gtk_widget_get_window(widget), cursor);
if (cursor) gdk_cursor_unref(cursor);
}
GtkTreeIter iter;
gboolean color_set = TRUE;
DupeItem *parent = NULL;
- gint valid;
+ gboolean valid;
store = gtk_tree_view_get_model(GTK_TREE_VIEW(dw->listview));
valid = gtk_tree_model_get_iter_first(store, &iter);
di = g_new0(DupeItem, 1);
di->fd = file_data_ref(fd);
-
- di->group = NULL;
di->group_rank = 0.0;
- di->simd = NULL;
- di->checksum = 0;
- di->md5sum = NULL;
- di->width = 0;
- di->height = 0;
-
- di->second = FALSE;
-
return di;
}
di->width = cd->width;
di->height = cd->height;
}
- if (di->checksum == 0 && cd->have_checksum)
- {
- di->checksum = cd->checksum;
- }
if (!di->md5sum && cd->have_md5sum)
{
di->md5sum = md5_digest_to_text(cd->md5sum);
if (!di) return;
base = cache_get_location(CACHE_TYPE_SIM, di->fd->path, FALSE, &mode);
- if (cache_ensure_dir_exists(base, mode))
+ if (recursive_mkdir_if_not_exists(base, mode))
{
CacheData *cd;
cd->path = cache_get_location(CACHE_TYPE_SIM, di->fd->path, TRUE, NULL);
if (di->width != 0) cache_sim_data_set_dimensions(cd, di->width, di->height);
- if (di->checksum != 0) cache_sim_data_set_checksum(cd, di->checksum);
if (di->md5sum)
{
guchar digest[16];
static gint dupe_listview_find_item(GtkListStore *store, DupeItem *item, GtkTreeIter *iter)
{
- gint valid;
+ gboolean valid;
gint row = 0;
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), iter);
{
gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, DUPE_COLUMN_COLOR, &color_set, -1);
color_set = !color_set;
+ dw->set_count++;
}
else
{
DUPE_COLUMN_DIMENSIONS, text[DUPE_COLUMN_DIMENSIONS],
DUPE_COLUMN_PATH, text[DUPE_COLUMN_PATH],
DUPE_COLUMN_COLOR, color_set,
+ DUPE_COLUMN_SET, dw->set_count,
-1);
g_free(text[DUPE_COLUMN_RANK]);
g_free(text[DUPE_COLUMN_DIMENSIONS]);
}
+static void dupe_listview_select_dupes(DupeWindow *dw, DupeSelectType parents);
+
static void dupe_listview_populate(DupeWindow *dw)
{
GtkListStore *store;
}
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(dw->listview));
+
+ if (options->duplicates_select_type == DUPE_SELECT_GROUP1)
+ {
+ dupe_listview_select_dupes(dw, DUPE_SELECT_GROUP1);
+ }
+ else if (options->duplicates_select_type == DUPE_SELECT_GROUP2)
+ {
+ dupe_listview_select_dupes(dw, DUPE_SELECT_GROUP2);
+ }
+
}
static void dupe_listview_remove(DupeWindow *dw, DupeItem *di)
{
GtkTreeModel *store;
GtkTreeIter iter;
- gint valid;
+ gboolean valid;
GList *list = NULL;
store = gtk_tree_view_get_model(GTK_TREE_VIEW(listview));
}
+
+
+void gtk_tree_path_free_wrapper(void *data, void *useradata)
+{
+ gtk_tree_path_free(data);
+}
+
+
static GList *dupe_listview_get_selection(DupeWindow *dw, GtkWidget *listview)
{
GtkTreeModel *store;
}
work = work->next;
}
- g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
+ g_list_foreach(slist, (GFunc)gtk_tree_path_free_wrapper, NULL);
g_list_free(slist);
return g_list_reverse(list);
}
-static gint dupe_listview_item_is_selected(DupeWindow *dw, DupeItem *di, GtkWidget *listview)
+static gboolean dupe_listview_item_is_selected(DupeWindow *dw, DupeItem *di, GtkWidget *listview)
{
GtkTreeModel *store;
GtkTreeSelection *selection;
GList *slist;
GList *work;
- gint found = FALSE;
+ gboolean found = FALSE;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(listview));
slist = gtk_tree_selection_get_selected_rows(selection, &store);
if (di_n == di) found = TRUE;
work = work->next;
}
- g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
+ g_list_foreach(slist, (GFunc)gtk_tree_path_free_wrapper, NULL);
g_list_free(slist);
return found;
}
-static void dupe_listview_select_dupes(DupeWindow *dw, gint parents)
+static void dupe_listview_select_dupes(DupeWindow *dw, DupeSelectType parents)
{
GtkTreeModel *store;
GtkTreeSelection *selection;
GtkTreeIter iter;
- gint valid;
+ gboolean valid;
+ gint set_count = 0;
+ gint set_count_last = -1;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dw->listview));
gtk_tree_selection_unselect_all(selection);
{
DupeItem *di;
- gtk_tree_model_get(store, &iter, DUPE_COLUMN_POINTER, &di, -1);
- if ( (dupe_match_find_parent(dw, di) == di) == (parents) )
+ gtk_tree_model_get(store, &iter, DUPE_COLUMN_POINTER, &di, DUPE_COLUMN_SET, &set_count, -1);
+ if (set_count != set_count_last)
{
- gtk_tree_selection_select_iter(selection, &iter);
+ set_count_last = set_count;
+ if (parents == DUPE_SELECT_GROUP1)
+ {
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+ }
+ else
+ {
+ if (parents == DUPE_SELECT_GROUP2)
+ {
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
}
valid = gtk_tree_model_iter_next(store, &iter);
}
dupe_match_unlink_child(b, a);
}
-static void dupe_match_link_clear(DupeItem *parent, gint unlink_children)
+static void dupe_match_link_clear(DupeItem *parent, gboolean unlink_children)
{
GList *work;
}
}
+static gint dupe_match_totals_sort_cb(gconstpointer a, gconstpointer b)
+{
+ DupeItem *da = (DupeItem *)a;
+ DupeItem *db = (DupeItem *)b;
+
+ if (g_list_length(da->group) > g_list_length(db->group)) return -1;
+ if (g_list_length(da->group) < g_list_length(db->group)) return 1;
+
+ if (da->group_rank < db->group_rank) return -1;
+ if (da->group_rank > db->group_rank) return 1;
+
+ return 0;
+}
+
static gint dupe_match_rank_sort_cb(gconstpointer a, gconstpointer b)
{
DupeItem *da = (DupeItem *)a;
return g_list_sort(list, dupe_match_rank_sort_cb);
}
+/* returns allocated GList of dupes sorted by totals */
+static GList *dupe_match_totals_sort(GList *source_list)
+{
+ source_list = g_list_sort(source_list, dupe_match_totals_sort_cb);
+
+ source_list = g_list_first(source_list);
+ return g_list_reverse(source_list);
+}
+
static void dupe_match_rank(DupeWindow *dw)
{
GList *list;
if (required_debug_level(2)) dupe_match_print_list(list);
list = dupe_match_rank_sort(list);
+ if (options->sort_totals)
+ {
+ list = dupe_match_totals_sort(list);
+ }
+ if (required_debug_level(2)) dupe_match_print_list(list);
g_list_free(dw->dupes);
dw->dupes = list;
* ------------------------------------------------------------------
*/
-static gint dupe_match(DupeItem *a, DupeItem *b, DupeMatchType mask, gdouble *rank, gint fast)
+static gboolean dupe_match(DupeItem *a, DupeItem *b, DupeMatchType mask, gdouble *rank, gint fast)
{
*rank = 0.0;
- if (a == b) return FALSE;
+ if (a->fd->path == b->fd->path) return FALSE;
+ if (mask & DUPE_MATCH_ALL)
+ {
+ return TRUE;
+ }
if (mask & DUPE_MATCH_PATH)
{
if (utf8_compare(a->fd->path, b->fd->path, TRUE) != 0) return FALSE;
{
if (strcmp(a->fd->collate_key_name_nocase, b->fd->collate_key_name_nocase) != 0) return FALSE;
}
+ if (mask & DUPE_MATCH_NAME_CONTENT)
+ {
+ if (strcmp(a->fd->collate_key_name, b->fd->collate_key_name) == 0)
+ {
+ if (!a->md5sum) a->md5sum = md5_text_from_file_utf8(a->fd->path, "");
+ if (!b->md5sum) b->md5sum = md5_text_from_file_utf8(b->fd->path, "");
+ if (a->md5sum[0] == '\0' ||
+ b->md5sum[0] == '\0' ||
+ strcmp(a->md5sum, b->md5sum) != 0)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+ if (mask & DUPE_MATCH_NAME_CI_CONTENT)
+ {
+ if (strcmp(a->fd->collate_key_name_nocase, b->fd->collate_key_name_nocase) == 0)
+ {
+ if (!a->md5sum) a->md5sum = md5_text_from_file_utf8(a->fd->path, "");
+ if (!b->md5sum) b->md5sum = md5_text_from_file_utf8(b->fd->path, "");
+ if (a->md5sum[0] == '\0' ||
+ b->md5sum[0] == '\0' ||
+ strcmp(a->md5sum, b->md5sum) != 0)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
if (mask & DUPE_MATCH_SIZE)
{
if (a->fd->size != b->fd->size) return FALSE;
GtkTreeModel *store;
GtkTreeIter iter;
DupeItem *di = NULL;
- gint valid;
+ gboolean valid;
gint row = 0;
gint length = 0;
static void dupe_check_stop(DupeWindow *dw)
{
- if (dw->idle_id != -1 || dw->img_loader || dw->thumb_loader)
+ if (dw->idle_id || dw->img_loader || dw->thumb_loader)
{
g_source_remove(dw->idle_id);
- dw->idle_id = -1;
+ dw->idle_id = 0;
+ dupe_window_update_progress(dw, NULL, 0.0, FALSE);
+ widget_set_cursor(dw->listview, -1);
+ }
+
+ if (dw->add_files_queue_id)
+ {
+ g_source_remove(dw->add_files_queue_id);
+ dw->add_files_queue_id = 0;
+ gtk_widget_set_sensitive(dw->controls_box, TRUE);
+ if (g_list_length(dw->add_files_queue) > 0)
+ {
+ filelist_free(dw->add_files_queue);
+ }
+ dw->add_files_queue = NULL;
dupe_window_update_progress(dw, NULL, 0.0, FALSE);
widget_set_cursor(dw->listview, -1);
}
dw->img_loader = NULL;
}
+static void dupe_check_stop_cb(GtkWidget *widget, gpointer data)
+{
+ DupeWindow *dw = data;
+
+ dupe_check_stop(dw);
+}
+
static void dupe_loader_done_cb(ImageLoader *il, gpointer data)
{
DupeWindow *dw = data;
return NULL;
}
-static gint dupe_check_cb(gpointer data)
+static gboolean dupe_check_cb(gpointer data)
{
DupeWindow *dw = data;
- if (dw->idle_id == -1) return FALSE;
+ if (!dw->idle_id) return FALSE;
if (!dw->setup_done)
{
dw->img_loader = image_loader_new(di->fd);
image_loader_set_buffer_size(dw->img_loader, 8);
- image_loader_set_error_func(dw->img_loader, dupe_loader_done_cb, dw);
+ g_signal_connect(G_OBJECT(dw->img_loader), "error", (GCallback)dupe_loader_done_cb, dw);
+ g_signal_connect(G_OBJECT(dw->img_loader), "done", (GCallback)dupe_loader_done_cb, dw);
- if (!image_loader_start(dw->img_loader, dupe_loader_done_cb, dw))
+ if (!image_loader_start(dw->img_loader))
{
image_sim_free(di->simd);
di->simd = image_sim_new();
dw->img_loader = NULL;
return TRUE;
}
- dw->idle_id = -1;
+ dw->idle_id = 0;
return FALSE;
}
dupe_window_update_progress(dw, _("Sorting..."), 1.0, TRUE);
return TRUE;
}
- dw->idle_id = -1;
+ dw->idle_id = 0;
dupe_window_update_progress(dw, NULL, 0.0, FALSE);
dupe_match_rank(dw);
dupe_window_update_count(dw, TRUE);
widget_set_cursor(dw->listview, GDK_WATCH);
- if (dw->idle_id != -1) return;
+ if (dw->idle_id) return;
dw->idle_id = g_idle_add(dupe_check_cb, dw);
}
+static gboolean dupe_check_start_cb(gpointer data)
+{
+ DupeWindow *dw = data;
+
+ dupe_check_start(dw);
+
+ return FALSE;
+}
+
/*
* ------------------------------------------------------------------
* Item addition, removal
dupe_window_update_count(dw, FALSE);
}
-static gint dupe_item_remove_by_path(DupeWindow *dw, const gchar *path)
+static gboolean dupe_item_remove_by_path(DupeWindow *dw, const gchar *path)
{
DupeItem *di;
return TRUE;
}
+static gboolean dupe_files_add_queue_cb(gpointer data)
+{
+ DupeItem *di = NULL;
+ DupeWindow *dw = data;
+ FileData *fd;
+ GList *queue = dw->add_files_queue;
+
+ gtk_progress_bar_pulse(GTK_PROGRESS_BAR(dw->extra_label));
+
+ if (g_list_length(queue) == 0)
+ {
+ dw->add_files_queue_id = 0;
+ g_idle_add(dupe_check_start_cb, dw);
+ gtk_widget_set_sensitive(dw->controls_box, TRUE);
+ return FALSE;
+ }
+
+ fd = queue->data;
+ if (fd)
+ {
+ if (isfile(fd->path))
+ {
+ di = dupe_item_new(fd);
+ }
+ else if (isdir(fd->path))
+ {
+ GList *f, *d;
+ dw->add_files_queue = g_list_remove(dw->add_files_queue, g_list_first(dw->add_files_queue)->data);
+
+ if (filelist_read(fd, &f, &d))
+ {
+ GList *work;
+
+ f = filelist_filter(f, FALSE);
+ d = filelist_filter(d, TRUE);
+
+ work = f;
+ while (work)
+ {
+ dw->add_files_queue = g_list_prepend(dw->add_files_queue, work->data);
+ work = work->next;
+ }
+ g_list_free(f);
+ work = d;
+ while (work)
+ {
+ dw->add_files_queue = g_list_prepend(dw->add_files_queue, work->data);
+ work = work->next;
+ }
+ g_list_free(d);
+ }
+ }
+ else
+ {
+ /* Not a file and not a dir */
+ dw->add_files_queue = g_list_remove(dw->add_files_queue, g_list_first(dw->add_files_queue)->data);
+ }
+ }
+
+ if (!di)
+ {
+ /* A dir was found. Process the contents on next entry */
+ return TRUE;
+ }
+
+ dw->add_files_queue = g_list_remove(dw->add_files_queue, g_list_first(dw->add_files_queue)->data);
+
+ dupe_item_read_cache(di);
+
+ /* Ensure images in the lists have unique FileDatas */
+ GList *work;
+ DupeItem *di_list;
+ work = g_list_first(dw->list);
+ while (work)
+ {
+ di_list = work->data;
+ if (di_list->fd == di->fd)
+ {
+ return TRUE;
+ }
+ else
+ {
+ work = work->next;
+ }
+ }
+
+ if (dw->second_list)
+ {
+ work = g_list_first(dw->second_list);
+ while (work)
+ {
+ di_list = work->data;
+ if (di_list->fd == di->fd)
+ {
+ return TRUE;
+ }
+ else
+ {
+ work = work->next;
+ }
+ }
+ }
+
+ if (dw->second_drop)
+ {
+ dupe_second_add(dw, di);
+ }
+ else
+ {
+ dw->list = g_list_prepend(dw->list, di);
+ }
+
+ if (g_list_length(dw->add_files_queue) > 0)
+ {
+ return TRUE;
+ }
+ else
+ {
+ dw->add_files_queue_id = 0;
+ g_idle_add(dupe_check_start_cb, dw);
+ gtk_widget_set_sensitive(dw->controls_box, TRUE);
+ return FALSE;
+ }
+}
+
static void dupe_files_add(DupeWindow *dw, CollectionData *collection, CollectInfo *info,
- FileData *fd, gint recurse)
+ FileData *fd, gboolean recurse)
{
DupeItem *di = NULL;
}
else if (fd)
{
- if (isfile(fd->path))
+ if (isfile(fd->path) && !g_file_test(fd->path, G_FILE_TEST_IS_SYMLINK))
{
di = dupe_item_new(fd);
}
if (!di) return;
+ dupe_item_read_cache(di);
+
+ /* Ensure images in the lists have unique FileDatas */
+ GList *work;
+ DupeItem *di_list;
+ work = g_list_first(dw->list);
+ while (work)
+ {
+ di_list = work->data;
+ if (di_list->fd == di->fd)
+ {
+ return;
+ }
+ else
+ {
+ work = work->next;
+ }
+ }
+
+ if (dw->second_list)
+ {
+ work = g_list_first(dw->second_list);
+ while (work)
+ {
+ di_list = work->data;
+ if (di_list->fd == di->fd)
+ {
+ return;
+ }
+ else
+ {
+ work = work->next;
+ }
+ }
+ }
+
if (dw->second_drop)
{
dupe_second_add(dw, di);
dupe_check_start(dw);
}
-void dupe_window_add_files(DupeWindow *dw, GList *list, gint recurse)
+void dupe_window_add_files(DupeWindow *dw, GList *list, gboolean recurse)
{
GList *work;
{
FileData *fd = work->data;
work = work->next;
+ if (isdir(fd->path) && !recurse)
+ {
+ GList *f, *d;
- dupe_files_add(dw, NULL, NULL, fd, recurse);
+ if (filelist_read(fd, &f, &d))
+ {
+ GList *work_file;
+ work_file = f;
+
+ while (work_file)
+ {
+ /* Add only the files, ignore the dirs when no recurse */
+ dw->add_files_queue = g_list_prepend(dw->add_files_queue, work_file->data);
+ work_file = work_file->next;
+ }
+ g_list_free(f);
+ g_list_free(d);
+ }
+ }
+ else
+ {
+ dw->add_files_queue = g_list_prepend(dw->add_files_queue, fd);
+ }
}
+ if (dw->add_files_queue_id == 0)
+ {
+ gtk_progress_bar_pulse(GTK_PROGRESS_BAR(dw->extra_label));
+ gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(dw->extra_label), DUPE_PROGRESS_PULSE_STEP);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(dw->extra_label), _("Loading file list"));
- dupe_check_start(dw);
+ dw->add_files_queue_id = g_idle_add(dupe_files_add_queue_cb, dw);
+ gtk_widget_set_sensitive(dw->controls_box, FALSE);
+ }
}
static void dupe_item_update(DupeWindow *dw, DupeItem *di)
if (!di) return;
- gd = file_util_gen_dlg("Image thumbprint debug info", GQ_WMCLASS, "thumbprint",
+ gd = file_util_gen_dlg("Image thumbprint debug info", "thumbprint",
dw->window, TRUE,
NULL, NULL);
generic_dialog_add_button(gd, GTK_STOCK_CLOSE, NULL, NULL, TRUE);
gtk_box_pack_start(GTK_BOX(gd->vbox), image, FALSE, FALSE, 0);
gtk_widget_show(image);
- gdk_pixbuf_unref(pixbuf);
+ g_object_unref(pixbuf);
}
gtk_widget_show(gd->dialog);
dupe_match_reset_list(dw->list);
dupe_match_reset_list(dw->second_list);
+ dw->set_count = 0;
dupe_check_start(dw);
}
}
else
{
- layout_image_set_fd(NULL, di->fd);
+ layout_set_fd(NULL, di->fd);
}
}
}
if (di) list = g_list_prepend(list, di);
work = work->next;
}
- g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
+ g_list_foreach(slist, (GFunc)gtk_tree_path_free_wrapper, NULL);
g_list_free(slist);
dw->color_frozen = TRUE;
dupe_listview_realign_colors(dw);
}
-static void dupe_window_edit_selected(DupeWindow *dw, gint n)
+static void dupe_window_edit_selected(DupeWindow *dw, const gchar *key)
{
- GList *list;
-
- list = dupe_listview_get_selection(dw, dw->listview);
-
- file_util_start_editor_from_filelist(n, list, dw->window);
-
- filelist_free(list);
+ file_util_start_editor_from_filelist(key, dupe_listview_get_selection(dw, dw->listview), NULL, dw->window);
}
static void dupe_window_collection_from_selection(DupeWindow *dw)
DupeWindow *dw = data;
GtkTreeSelection *selection;
+ options->duplicates_select_type = DUPE_SELECT_NONE;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dw->listview));
gtk_tree_selection_select_all(selection);
}
DupeWindow *dw = data;
GtkTreeSelection *selection;
+ options->duplicates_select_type = DUPE_SELECT_NONE;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dw->listview));
gtk_tree_selection_unselect_all(selection);
}
{
DupeWindow *dw = data;
- dupe_listview_select_dupes(dw, TRUE);
+ options->duplicates_select_type = DUPE_SELECT_GROUP1;
+ dupe_listview_select_dupes(dw, DUPE_SELECT_GROUP1);
}
static void dupe_menu_select_dupes_set2_cb(GtkWidget *widget, gpointer data)
{
DupeWindow *dw = data;
- dupe_listview_select_dupes(dw, FALSE);
+ options->duplicates_select_type = DUPE_SELECT_GROUP2;
+ dupe_listview_select_dupes(dw, DUPE_SELECT_GROUP2);
}
static void dupe_menu_edit_cb(GtkWidget *widget, gpointer data)
{
DupeWindow *dw;
- gint n;
+ const gchar *key = data;
dw = submenu_item_get_data(widget);
- n = GPOINTER_TO_INT(data);
if (!dw) return;
- dupe_window_edit_selected(dw, n);
-}
-
-static void dupe_menu_info_cb(GtkWidget *widget, gpointer data)
-{
- DupeWindow *dw = data;
-
- info_window_new(NULL, dupe_listview_get_selection(dw, dw->listview), NULL);
-}
-
-static void dupe_menu_collection_cb(GtkWidget *widget, gpointer data)
-{
- DupeWindow *dw = data;
-
- dupe_window_collection_from_selection(dw);
+ dupe_window_edit_selected(dw, key);
}
static void dupe_menu_print_cb(GtkWidget *widget, gpointer data)
{
DupeWindow *dw = data;
+ options->file_ops.safe_delete_enable = FALSE;
+ file_util_delete(NULL, dupe_listview_get_selection(dw, dw->listview), dw->window);
+}
+
+static void dupe_menu_move_to_trash_cb(GtkWidget *widget, gpointer data)
+{
+ DupeWindow *dw = data;
+
+ options->file_ops.safe_delete_enable = TRUE;
file_util_delete(NULL, dupe_listview_get_selection(dw, dw->listview), dw->window);
}
{
DupeWindow *dw = data;
- file_util_copy_path_list_to_clipboard(dupe_listview_get_selection(dw, dw->listview));
+ file_util_copy_path_list_to_clipboard(dupe_listview_get_selection(dw, dw->listview), TRUE);
+}
+
+static void dupe_menu_copy_path_unquoted_cb(GtkWidget *widget, gpointer data)
+{
+ DupeWindow *dw = data;
+
+ file_util_copy_path_list_to_clipboard(dupe_listview_get_selection(dw, dw->listview), FALSE);
}
static void dupe_menu_remove_cb(GtkWidget *widget, gpointer data)
dupe_window_close(dw);
}
+static void dupe_menu_popup_destroy_cb(GtkWidget *widget, gpointer data)
+{
+ GList *editmenu_fd_list = data;
+
+ filelist_free(editmenu_fd_list);
+}
+
+static GList *dupe_window_get_fd_list(DupeWindow *dw)
+{
+ GList *list;
+
+ if (gtk_widget_has_focus(dw->second_listview))
+ {
+ list = dupe_listview_get_selection(dw, dw->second_listview);
+ }
+ else
+ {
+ list = dupe_listview_get_selection(dw, dw->listview);
+ }
+
+ return list;
+}
+
+/**
+ * @brief Add file selection list to a collection
+ * @param[in] widget
+ * @param[in] data Index to the collection list menu item selected, or -1 for new collection
+ *
+ *
+ */
+static void dupe_pop_menu_collections_cb(GtkWidget *widget, gpointer data)
+{
+ DupeWindow *dw;
+ GList *selection_list;
+
+ dw = submenu_item_get_data(widget);
+ selection_list = dupe_listview_get_selection(dw, dw->listview);
+ pop_menu_collections(selection_list, data);
+
+ filelist_free(selection_list);
+}
+
static GtkWidget *dupe_menu_popup_main(DupeWindow *dw, DupeItem *di)
{
GtkWidget *menu;
GtkWidget *item;
gint on_row;
+ GList *editmenu_fd_list;
on_row = (di != NULL);
menu = popup_menu_short_lived();
+
menu_item_add_sensitive(menu, _("_View"), on_row,
G_CALLBACK(dupe_menu_view_cb), dw);
menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, on_row,
menu_item_add_sensitive(menu, _("Select group _2 duplicates"), (dw->dupes != NULL),
G_CALLBACK(dupe_menu_select_dupes_set2_cb), dw);
menu_item_add_divider(menu);
- submenu_add_edit(menu, &item, G_CALLBACK(dupe_menu_edit_cb), dw);
+
+ submenu_add_export(menu, &item, G_CALLBACK(dupe_pop_menu_export_cb), dw);
+ gtk_widget_set_sensitive(item, on_row);
+ menu_item_add_divider(menu);
+
+ editmenu_fd_list = dupe_window_get_fd_list(dw);
+ g_signal_connect(G_OBJECT(menu), "destroy",
+ G_CALLBACK(dupe_menu_popup_destroy_cb), editmenu_fd_list);
+ submenu_add_edit(menu, &item, G_CALLBACK(dupe_menu_edit_cb), dw, editmenu_fd_list);
if (!on_row) gtk_widget_set_sensitive(item, FALSE);
- menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, on_row,
- G_CALLBACK(dupe_menu_info_cb), dw);
- menu_item_add_stock_sensitive(menu, _("Add to new collection"), GTK_STOCK_INDEX, on_row,
- G_CALLBACK(dupe_menu_collection_cb), dw);
+
+ submenu_add_collections(menu, &item,
+ G_CALLBACK(dupe_pop_menu_collections_cb), dw);
+ gtk_widget_set_sensitive(item, on_row);
+
menu_item_add_stock_sensitive(menu, _("Print..."), GTK_STOCK_PRINT, on_row,
G_CALLBACK(dupe_menu_print_cb), dw);
menu_item_add_divider(menu);
G_CALLBACK(dupe_menu_move_cb), dw);
menu_item_add_sensitive(menu, _("_Rename..."), on_row,
G_CALLBACK(dupe_menu_rename_cb), dw);
- menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, on_row,
+ menu_item_add_sensitive(menu, _("_Copy path"), on_row,
+ G_CALLBACK(dupe_menu_copy_path_cb), dw);
+ menu_item_add_sensitive(menu, _("_Copy path unquoted"), on_row,
+ G_CALLBACK(dupe_menu_copy_path_unquoted_cb), dw);
+
+ menu_item_add_divider(menu);
+ menu_item_add_stock_sensitive(menu,
+ options->file_ops.confirm_move_to_trash ? _("Move to Trash...") :
+ _("Move to Trash"), PIXBUF_INLINE_ICON_TRASH, on_row,
+ G_CALLBACK(dupe_menu_move_to_trash_cb), dw);
+ menu_item_add_stock_sensitive(menu,
+ options->file_ops.confirm_delete ? _("_Delete...") :
+ _("_Delete"), GTK_STOCK_DELETE, on_row,
G_CALLBACK(dupe_menu_delete_cb), dw);
- if (options->show_copy_path)
- menu_item_add_sensitive(menu, _("_Copy path"), on_row,
- G_CALLBACK(dupe_menu_copy_path_cb), dw);
+
menu_item_add_divider(menu);
menu_item_add_stock_sensitive(menu, _("Rem_ove"), GTK_STOCK_REMOVE, on_row,
G_CALLBACK(dupe_menu_remove_cb), dw);
return menu;
}
-static gint dupe_listview_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
+static gboolean dupe_listview_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
{
DupeWindow *dw = data;
GtkTreeModel *store;
return FALSE;
}
-static gint dupe_listview_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
+static gboolean dupe_listview_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
{
DupeWindow *dw = data;
GtkTreeModel *store;
static GtkWidget *dupe_menu_popup_second(DupeWindow *dw, DupeItem *di)
{
GtkWidget *menu;
- gint notempty;
- gint on_row;
-
- on_row = (di != NULL);
- notempty = (dw->second_list != NULL);
+ gboolean notempty = (dw->second_list != NULL);
+ gboolean on_row = (di != NULL);
menu = popup_menu_short_lived();
menu_item_add_sensitive(menu, _("_View"), on_row,
{
DupeWindow *dw = data;
- dw->second_set = GTK_TOGGLE_BUTTON(widget)->active;
+ dw->second_set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
if (dw->second_set)
{
dupe_window_recompare(dw);
}
+static void dupe_sort_totals_toggle_cb(GtkWidget *widget, gpointer data)
+{
+ DupeWindow *dw = data;
+
+ options->sort_totals = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+ dupe_window_recompare(dw);
+
+}
+
/*
*-------------------------------------------------------------------
* match type menu
DUPE_MENU_COLUMN_MASK
};
+static void dupe_listview_show_rank(GtkWidget *listview, gboolean rank);
+
static void dupe_menu_type_cb(GtkWidget *combo, gpointer data)
{
DupeWindow *dw = data;
if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) return;
gtk_tree_model_get(store, &iter, DUPE_MENU_COLUMN_MASK, &dw->match_mask, -1);
+ options->duplicates_match = dw->match_mask;
+
+ if (dw->match_mask & (DUPE_MATCH_SIM_HIGH | DUPE_MATCH_SIM_MED | DUPE_MATCH_SIM_LOW | DUPE_MATCH_SIM_CUSTOM))
+ {
+ dupe_listview_show_rank(dw->listview, TRUE);
+ }
+ else
+ {
+ dupe_listview_show_rank(dw->listview, FALSE);
+ }
dupe_window_recompare(dw);
}
dupe_menu_add_item(store, _("Similarity"), DUPE_MATCH_SIM_MED, dw);
dupe_menu_add_item(store, _("Similarity (low)"), DUPE_MATCH_SIM_LOW, dw);
dupe_menu_add_item(store, _("Similarity (custom)"), DUPE_MATCH_SIM_CUSTOM, dw);
+ dupe_menu_add_item(store, _("Name ≠content"), DUPE_MATCH_NAME_CONTENT, dw);
+ dupe_menu_add_item(store, _("Name case-insensitive ≠content"), DUPE_MATCH_NAME_CI_CONTENT, dw);
+ dupe_menu_add_item(store, _("Show all"), DUPE_MATCH_ALL, dw);
g_signal_connect(G_OBJECT(dw->combo), "changed",
G_CALLBACK(dupe_menu_type_cb), dw);
"cell-background-set", set, NULL);
}
-static void dupe_listview_add_column(DupeWindow *dw, GtkWidget *listview, gint n, const gchar *title, gint image, gint right_justify)
+static void dupe_listview_add_column(DupeWindow *dw, GtkWidget *listview, gint n, const gchar *title, gboolean image, gboolean right_justify)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, title);
gtk_tree_view_column_set_min_width(column, 4);
+ gtk_tree_view_column_set_sort_column_id(column, n);
if (n != DUPE_COLUMN_RANK &&
n != DUPE_COLUMN_THUMB)
gtk_tree_view_append_column(GTK_TREE_VIEW(listview), column);
}
-static void dupe_listview_set_height(GtkWidget *listview, gint thumb)
+static void dupe_listview_set_height(GtkWidget *listview, gboolean thumb)
{
GtkTreeViewColumn *column;
GtkCellRenderer *cell;
if (!column) return;
gtk_tree_view_column_set_fixed_width(column, (thumb) ? options->thumbnails.max_width : 4);
+ gtk_tree_view_column_set_visible(column, thumb);
- list = gtk_tree_view_column_get_cell_renderers(column);
+ list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
if (!list) return;
cell = list->data;
g_list_free(list);
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
}
+static void dupe_listview_show_rank(GtkWidget *listview, gboolean rank)
+{
+ GtkTreeViewColumn *column;
+
+ column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), DUPE_COLUMN_RANK - 1);
+ if (!column) return;
+
+ gtk_tree_view_column_set_visible(column, rank);
+}
/*
*-------------------------------------------------------------------
{
DupeWindow *dw = data;
- dw->show_thumbs = GTK_TOGGLE_BUTTON(widget)->active;
+ dw->show_thumbs = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+ options->duplicates_thumbnails = dw->show_thumbs;
if (dw->show_thumbs)
{
{
GtkTreeModel *store;
GtkTreeIter iter;
- gint valid;
+ gboolean valid;
thumb_loader_free(dw->thumb_loader);
dw->thumb_loader = NULL;
dupe_listview_set_height(dw->listview, dw->show_thumbs);
}
+static void dupe_window_rotation_invariant_cb(GtkWidget *widget, gpointer data)
+{
+ DupeWindow *dw = data;
+
+ options->rot_invariant_sim = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+ dupe_window_recompare(dw);
+}
+
+static void dupe_window_custom_threshold_cb(GtkWidget *widget, gpointer data)
+{
+ DupeWindow *dw = data;
+ DupeMatchType match_type;
+ GtkTreeModel *store;
+ gboolean valid;
+ GtkTreeIter iter;
+
+ options->duplicates_similarity_threshold = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ dw->match_mask = DUPE_MATCH_SIM_CUSTOM;
+
+ store = gtk_combo_box_get_model(GTK_COMBO_BOX(dw->combo));
+ valid = gtk_tree_model_get_iter_first(store, &iter);
+ while (valid)
+ {
+ gtk_tree_model_get(store, &iter, DUPE_MENU_COLUMN_MASK, &match_type, -1);
+ if (match_type == DUPE_MATCH_SIM_CUSTOM)
+ {
+ break;
+ }
+ valid = gtk_tree_model_iter_next(store, &iter);
+ }
+
+ gtk_combo_box_set_active_iter(GTK_COMBO_BOX(dw->combo), &iter);
+ dupe_window_recompare(dw);
+}
+
static void dupe_popup_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
{
GtkWidget *view = data;
*y = cy;
}
-static gint dupe_window_keypress_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
+static gboolean dupe_window_keypress_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
{
DupeWindow *dw = data;
- gint stop_signal = FALSE;
- gint on_second;
+ gboolean stop_signal = FALSE;
+ gboolean on_second;
GtkWidget *listview;
GtkTreeModel *store;
GtkTreeSelection *selection;
GList *slist;
DupeItem *di = NULL;
- on_second = GTK_WIDGET_HAS_FOCUS(dw->second_listview);
+ on_second = gtk_widget_has_focus(dw->second_listview);
if (on_second)
{
gtk_tree_model_get_iter(store, &iter, tpath);
gtk_tree_model_get(store, &iter, DUPE_COLUMN_POINTER, &di, -1);
}
- g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
+ g_list_foreach(slist, (GFunc)gtk_tree_path_free_wrapper, NULL);
g_list_free(slist);
if (event->state & GDK_CONTROL_MASK)
{
- gint edit_val = -1;
-
if (!on_second)
{
stop_signal = TRUE;
switch (event->keyval)
{
case '1':
- edit_val = 0;
- break;
case '2':
- edit_val = 1;
- break;
case '3':
- edit_val = 2;
- break;
case '4':
- edit_val = 3;
- break;
case '5':
- edit_val = 4;
- break;
case '6':
- edit_val = 5;
- break;
case '7':
- edit_val = 6;
- break;
case '8':
- edit_val = 7;
- break;
case '9':
- edit_val = 8;
- break;
case '0':
- edit_val = 9;
break;
case 'C': case 'c':
file_util_copy(NULL, dupe_listview_get_selection(dw, listview),
file_util_rename(NULL, dupe_listview_get_selection(dw, listview), dw->window);
break;
case 'D': case 'd':
+ options->file_ops.safe_delete_enable = TRUE;
file_util_delete(NULL, dupe_listview_get_selection(dw, listview), dw->window);
break;
- case 'P': case 'p':
- info_window_new(NULL, dupe_listview_get_selection(dw, listview), NULL);
- break;
default:
stop_signal = FALSE;
break;
gtk_tree_selection_select_all(selection);
}
break;
- case GDK_Delete: case GDK_KP_Delete:
+ case GDK_KEY_Delete: case GDK_KEY_KP_Delete:
if (on_second)
{
dupe_second_clear(dw);
break;
}
}
-
- if (edit_val >= 0)
- {
- dupe_window_edit_selected(dw, edit_val);
- }
}
else
{
stop_signal = TRUE;
switch (event->keyval)
{
- case GDK_Return: case GDK_KP_Enter:
+ case GDK_KEY_Return: case GDK_KEY_KP_Enter:
dupe_menu_view(dw, di, listview, FALSE);
break;
case 'V': case 'v':
dupe_menu_view(dw, di, listview, TRUE);
break;
- case GDK_Delete: case GDK_KP_Delete:
+ case GDK_KEY_Delete: case GDK_KEY_KP_Delete:
dupe_window_remove_selection(dw, listview);
break;
case 'C': case 'c':
dupe_window_collection_from_selection(dw);
}
break;
+ case '0':
+ options->duplicates_select_type = DUPE_SELECT_NONE;
+ dupe_listview_select_dupes(dw, DUPE_SELECT_NONE);
+ break;
case '1':
- dupe_listview_select_dupes(dw, TRUE);
+ options->duplicates_select_type = DUPE_SELECT_GROUP1;
+ dupe_listview_select_dupes(dw, DUPE_SELECT_GROUP1);
break;
case '2':
- dupe_listview_select_dupes(dw, FALSE);
+ options->duplicates_select_type = DUPE_SELECT_GROUP2;
+ dupe_listview_select_dupes(dw, DUPE_SELECT_GROUP2);
break;
- case GDK_Menu:
- case GDK_F10:
+ case GDK_KEY_Menu:
+ case GDK_KEY_F10:
if (!on_second)
{
GtkWidget *menu;
break;
}
}
+ if (!stop_signal && is_help_key(event))
+ {
+ help_window_show("GuideImageSearchFindingDuplicates.html");
+ stop_signal = TRUE;
+ }
return stop_signal;
}
dupe_list_free(dw->list);
dw->list = NULL;
+ dw->set_count = 0;
dupe_match_reset_list(dw->second_list);
dupe_window_update_progress(dw, NULL, 0.0, FALSE);
}
+static void dupe_window_get_geometry(DupeWindow *dw)
+{
+ GdkWindow *window;
+ LayoutWindow *lw = NULL;
+
+ layout_valid(&lw);
+
+ if (!dw || !lw) return;
+
+ window = gtk_widget_get_window(dw->window);
+ gdk_window_get_position(window, &lw->options.dupe_window.x, &lw->options.dupe_window.y);
+ lw->options.dupe_window.w = gdk_window_get_width(window);
+ lw->options.dupe_window.h = gdk_window_get_height(window);
+}
+
void dupe_window_close(DupeWindow *dw)
{
dupe_check_stop(dw);
+ dupe_window_get_geometry(dw);
+
dupe_window_list = g_list_remove(dupe_window_list, dw);
gtk_widget_destroy(dw->window);
g_free(dw);
}
+static gint dupe_window_close_cb(GtkWidget *widget, gpointer data)
+{
+ DupeWindow *dw = data;
+
+ dupe_window_close(dw);
+
+ return TRUE;
+}
+
static gint dupe_window_delete(GtkWidget *widget, GdkEvent *event, gpointer data)
{
DupeWindow *dw = data;
return TRUE;
}
+static void dupe_help_cb(GtkAction *action, gpointer data)
+{
+ help_window_show("GuideImageSearchFindingDuplicates.html");
+}
+
+static gint default_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
+{
+ return 0;
+}
+
+static gint column_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
+{
+ GtkTreeSortable *sortable = data;
+ gint ret = 0;
+ gchar *rank_str_a, *rank_str_b;
+ gint rank_int_a;
+ gint rank_int_b;
+ gint group_a;
+ gint group_b;
+ gint sort_column_id;
+ GtkSortType sort_order;
+ DupeItem *di_a;
+ DupeItem *di_b;
+
+ gtk_tree_sortable_get_sort_column_id(sortable, &sort_column_id, &sort_order);
+
+ gtk_tree_model_get(model, a, DUPE_COLUMN_RANK, &rank_str_a, DUPE_COLUMN_SET, &group_a, DUPE_COLUMN_POINTER, &di_a, -1);
+
+ gtk_tree_model_get(model, b, DUPE_COLUMN_RANK, &rank_str_b, DUPE_COLUMN_SET, &group_b, DUPE_COLUMN_POINTER, &di_b, -1);
+
+ if (group_a == group_b)
+ {
+ switch (sort_column_id)
+ {
+ case DUPE_COLUMN_NAME:
+ ret = utf8_compare(di_a->fd->name, di_b->fd->name, TRUE);
+ break;
+ case DUPE_COLUMN_SIZE:
+ if (di_a->fd->size == di_b->fd->size)
+ {
+ ret = 0;
+ }
+ else
+ {
+ ret = (di_a->fd->size > di_b->fd->size) ? 1 : -1;
+ }
+ break;
+ case DUPE_COLUMN_DATE:
+ if (di_a->fd->date == di_b->fd->date)
+ {
+ ret = 0;
+ }
+ else
+ {
+ ret = (di_a->fd->date > di_b->fd->date) ? 1 : -1;
+ }
+ break;
+ case DUPE_COLUMN_DIMENSIONS:
+ if ((di_a->width == di_b->width) && (di_a->height == di_b->height))
+ {
+ ret = 0;
+ }
+ else
+ {
+ ret = ((di_a->width * di_a->height) > (di_b->width * di_b->height)) ? 1 : -1;
+ }
+ break;
+ case DUPE_COLUMN_RANK:
+ rank_int_a = atoi(rank_str_a);
+ rank_int_b = atoi(rank_str_b);
+ if (rank_int_a == 0) rank_int_a = 101;
+ if (rank_int_b == 0) rank_int_b = 101;
+
+ if (rank_int_a == rank_int_b)
+ {
+ ret = 0;
+ }
+ else
+ {
+ ret = (rank_int_a > rank_int_b) ? 1 : -1;
+ }
+ break;
+ case DUPE_COLUMN_PATH:
+ ret = utf8_compare(di_a->fd->path, di_b->fd->path, TRUE);
+ break;
+ }
+ }
+ else if (group_a < group_b)
+ {
+ ret = (sort_order == GTK_SORT_ASCENDING) ? 1 : -1;
+ }
+ else
+ {
+ ret = (sort_order == GTK_SORT_ASCENDING) ? -1 : 1;
+ }
+
+ return ret;
+}
+
+static void column_clicked_cb(GtkWidget *widget, gpointer data)
+{
+ DupeWindow *dw = data;
+
+ options->duplicates_match = DUPE_SELECT_NONE;
+ dupe_listview_select_dupes(dw, DUPE_SELECT_NONE);
+}
+
/* collection and files can be NULL */
-DupeWindow *dupe_window_new(DupeMatchType match_mask)
+DupeWindow *dupe_window_new()
{
DupeWindow *dw;
GtkWidget *vbox;
+ GtkWidget *hbox;
GtkWidget *scrolled;
GtkWidget *frame;
GtkWidget *status_box;
+ GtkWidget *controls_box;
+ GtkWidget *button_box;
GtkWidget *label;
GtkWidget *button;
GtkListStore *store;
GtkTreeSelection *selection;
GdkGeometry geometry;
+ LayoutWindow *lw = NULL;
- dw = g_new0(DupeWindow, 1);
-
- dw->list = NULL;
- dw->dupes = NULL;
- dw->match_mask = match_mask;
- dw->show_thumbs = FALSE;
-
- dw->idle_id = -1;
+ layout_valid(&lw);
- dw->second_set = FALSE;
+ dw = g_new0(DupeWindow, 1);
+ dw->add_files_queue = NULL;
+ dw->add_files_queue_id = 0;
+
+ dw->match_mask = DUPE_MATCH_NAME;
+ if (options->duplicates_match == DUPE_MATCH_NAME) dw->match_mask = DUPE_MATCH_NAME;
+ if (options->duplicates_match == DUPE_MATCH_SIZE) dw->match_mask = DUPE_MATCH_SIZE;
+ if (options->duplicates_match == DUPE_MATCH_DATE) dw->match_mask = DUPE_MATCH_DATE;
+ if (options->duplicates_match == DUPE_MATCH_DIM) dw->match_mask = DUPE_MATCH_DIM;
+ if (options->duplicates_match == DUPE_MATCH_SUM) dw->match_mask = DUPE_MATCH_SUM;
+ if (options->duplicates_match == DUPE_MATCH_PATH) dw->match_mask = DUPE_MATCH_PATH;
+ if (options->duplicates_match == DUPE_MATCH_SIM_HIGH) dw->match_mask = DUPE_MATCH_SIM_HIGH;
+ if (options->duplicates_match == DUPE_MATCH_SIM_MED) dw->match_mask = DUPE_MATCH_SIM_MED;
+ if (options->duplicates_match == DUPE_MATCH_SIM_LOW) dw->match_mask = DUPE_MATCH_SIM_LOW;
+ if (options->duplicates_match == DUPE_MATCH_SIM_CUSTOM) dw->match_mask = DUPE_MATCH_SIM_CUSTOM;
+ if (options->duplicates_match == DUPE_MATCH_NAME_CI) dw->match_mask = DUPE_MATCH_NAME_CI;
+ if (options->duplicates_match == DUPE_MATCH_NAME_CONTENT) dw->match_mask = DUPE_MATCH_NAME_CONTENT;
+ if (options->duplicates_match == DUPE_MATCH_NAME_CI_CONTENT) dw->match_mask = DUPE_MATCH_NAME_CI_CONTENT;
+ if (options->duplicates_match == DUPE_MATCH_ALL) dw->match_mask = DUPE_MATCH_ALL;
dw->window = window_new(GTK_WINDOW_TOPLEVEL, "dupe", NULL, NULL, _("Find duplicates"));
+ DEBUG_NAME(dw->window);
- geometry.min_width = 32;
- geometry.min_height = 32;
+ geometry.min_width = DEFAULT_MINIMAL_WINDOW_SIZE;
+ geometry.min_height = DEFAULT_MINIMAL_WINDOW_SIZE;
geometry.base_width = DUPE_DEF_WIDTH;
geometry.base_height = DUPE_DEF_HEIGHT;
gtk_window_set_geometry_hints(GTK_WINDOW(dw->window), NULL, &geometry,
GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE);
- gtk_window_set_default_size(GTK_WINDOW(dw->window), DUPE_DEF_WIDTH, DUPE_DEF_HEIGHT);
+ if (lw && options->save_window_positions)
+ {
+ gtk_window_set_default_size(GTK_WINDOW(dw->window), lw->options.dupe_window.w, lw->options.dupe_window.h);
+ gtk_window_move(GTK_WINDOW(dw->window), lw->options.dupe_window.x, lw->options.dupe_window.y);
+ }
+ else
+ {
+ gtk_window_set_default_size(GTK_WINDOW(dw->window), DUPE_DEF_WIDTH, DUPE_DEF_HEIGHT);
+ }
gtk_window_set_resizable(GTK_WINDOW(dw->window), TRUE);
gtk_container_set_border_width(GTK_CONTAINER(dw->window), 0);
gtk_table_attach_defaults(GTK_TABLE(dw->table), scrolled, 0, 2, 0, 1);
gtk_widget_show(scrolled);
- store = gtk_list_store_new(9, G_TYPE_POINTER, G_TYPE_STRING, GDK_TYPE_PIXBUF,
- G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
+ store = gtk_list_store_new(DUPE_COLUMN_COUNT, G_TYPE_POINTER, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_INT);
dw->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
+ dw->sortable = GTK_TREE_SORTABLE(store);
+
+ gtk_tree_sortable_set_sort_func(dw->sortable, DUPE_COLUMN_RANK, column_sort_cb, dw->sortable, NULL);
+ gtk_tree_sortable_set_sort_func(dw->sortable, DUPE_COLUMN_SET, default_sort_cb, dw->sortable, NULL);
+ gtk_tree_sortable_set_sort_func(dw->sortable, DUPE_COLUMN_THUMB, default_sort_cb, dw->sortable, NULL);
+ gtk_tree_sortable_set_sort_func(dw->sortable, DUPE_COLUMN_NAME, column_sort_cb, dw->sortable, NULL);
+ gtk_tree_sortable_set_sort_func(dw->sortable, DUPE_COLUMN_SIZE, column_sort_cb, dw->sortable, NULL);
+ gtk_tree_sortable_set_sort_func(dw->sortable, DUPE_COLUMN_DATE, column_sort_cb, dw->sortable, NULL);
+ gtk_tree_sortable_set_sort_func(dw->sortable, DUPE_COLUMN_DIMENSIONS, column_sort_cb, dw->sortable, NULL);
+ gtk_tree_sortable_set_sort_func(dw->sortable, DUPE_COLUMN_PATH, column_sort_cb, dw->sortable, NULL);
+
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dw->listview));
gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(dw->listview), TRUE);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(dw->listview), FALSE);
- dupe_listview_add_column(dw, dw->listview, DUPE_COLUMN_RANK, "", FALSE, TRUE);
- dupe_listview_add_column(dw, dw->listview, DUPE_COLUMN_THUMB, "", TRUE, FALSE);
+ dupe_listview_add_column(dw, dw->listview, DUPE_COLUMN_RANK, _("Rank"), FALSE, TRUE);
+ dupe_listview_add_column(dw, dw->listview, DUPE_COLUMN_THUMB, _("Thumb"), TRUE, FALSE);
dupe_listview_add_column(dw, dw->listview, DUPE_COLUMN_NAME, _("Name"), FALSE, FALSE);
dupe_listview_add_column(dw, dw->listview, DUPE_COLUMN_SIZE, _("Size"), FALSE, TRUE);
dupe_listview_add_column(dw, dw->listview, DUPE_COLUMN_DATE, _("Date"), FALSE, TRUE);
dupe_listview_add_column(dw, dw->listview, DUPE_COLUMN_DIMENSIONS, _("Dimensions"), FALSE, FALSE);
dupe_listview_add_column(dw, dw->listview, DUPE_COLUMN_PATH, _("Path"), FALSE, FALSE);
+ dupe_listview_add_column(dw, dw->listview, DUPE_COLUMN_SET, _("Set"), FALSE, FALSE);
+
+ g_signal_connect(gtk_tree_view_get_column(GTK_TREE_VIEW(dw->listview), DUPE_COLUMN_RANK - 1), "clicked", (GCallback)column_clicked_cb, dw);
+ g_signal_connect(gtk_tree_view_get_column(GTK_TREE_VIEW(dw->listview), DUPE_COLUMN_NAME - 1), "clicked", (GCallback)column_clicked_cb, dw);
+ g_signal_connect(gtk_tree_view_get_column(GTK_TREE_VIEW(dw->listview), DUPE_COLUMN_SIZE - 1), "clicked", (GCallback)column_clicked_cb, dw);
+ g_signal_connect(gtk_tree_view_get_column(GTK_TREE_VIEW(dw->listview), DUPE_COLUMN_DATE - 1), "clicked", (GCallback)column_clicked_cb, dw);
+ g_signal_connect(gtk_tree_view_get_column(GTK_TREE_VIEW(dw->listview), DUPE_COLUMN_DIMENSIONS - 1), "clicked", (GCallback)column_clicked_cb, dw);
+ g_signal_connect(gtk_tree_view_get_column(GTK_TREE_VIEW(dw->listview), DUPE_COLUMN_PATH - 1), "clicked", (GCallback)column_clicked_cb, dw);
gtk_container_add(GTK_CONTAINER(scrolled), dw->listview);
gtk_widget_show(dw->listview);
pref_line(dw->second_vbox, GTK_ORIENTATION_HORIZONTAL);
- status_box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
+ status_box = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), status_box, FALSE, FALSE, 0);
+ gtk_widget_show(status_box);
- label = gtk_label_new(_("Compare by:"));
- gtk_box_pack_start(GTK_BOX(status_box), label, FALSE, FALSE, PREF_PAD_SPACE);
- gtk_widget_show(label);
+ frame = gtk_frame_new(NULL);
+ DEBUG_NAME(frame);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(status_box), frame, TRUE, TRUE, 0);
+ gtk_widget_show(frame);
- dupe_menu_setup(dw);
- gtk_box_pack_start(GTK_BOX(status_box), dw->combo, FALSE, FALSE, 0);
- gtk_widget_show(dw->combo);
+ dw->status_label = gtk_label_new("");
+ gtk_container_add(GTK_CONTAINER(frame), dw->status_label);
+ gtk_widget_show(dw->status_label);
+
+ dw->extra_label = gtk_progress_bar_new();
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dw->extra_label), 0.0);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(dw->extra_label), "");
+ gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(dw->extra_label), TRUE);
+#endif
+ gtk_box_pack_start(GTK_BOX(status_box), dw->extra_label, FALSE, FALSE, PREF_PAD_SPACE);
+ gtk_widget_show(dw->extra_label);
+
+ controls_box = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
+ dw->controls_box = controls_box;
dw->button_thumbs = gtk_check_button_new_with_label(_("Thumbnails"));
+ dw->show_thumbs = options->duplicates_thumbnails;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dw->button_thumbs), dw->show_thumbs);
g_signal_connect(G_OBJECT(dw->button_thumbs), "toggled",
G_CALLBACK(dupe_window_show_thumb_cb), dw);
- gtk_box_pack_start(GTK_BOX(status_box), dw->button_thumbs, FALSE, FALSE, PREF_PAD_SPACE);
+ gtk_box_pack_start(GTK_BOX(controls_box), dw->button_thumbs, FALSE, FALSE, PREF_PAD_SPACE);
gtk_widget_show(dw->button_thumbs);
+ label = gtk_label_new(_("Compare by:"));
+ gtk_box_pack_start(GTK_BOX(controls_box), label, FALSE, FALSE, PREF_PAD_SPACE);
+ gtk_widget_show(label);
+
+ dupe_menu_setup(dw);
+ gtk_box_pack_start(GTK_BOX(controls_box), dw->combo, FALSE, FALSE, 0);
+ gtk_widget_show(dw->combo);
+
+ label = gtk_label_new(_("Custom Threshold"));
+ gtk_box_pack_start(GTK_BOX(controls_box), label, FALSE, FALSE, PREF_PAD_SPACE);
+ gtk_widget_show(label);
+ dw->custom_threshold = gtk_spin_button_new_with_range(1, 100, 1);
+ gtk_widget_set_tooltip_text(GTK_WIDGET(dw->custom_threshold), "Custom similarity threshold");
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(dw->custom_threshold), options->duplicates_similarity_threshold);
+ g_signal_connect(G_OBJECT(dw->custom_threshold), "value_changed", G_CALLBACK(dupe_window_custom_threshold_cb), dw);
+ gtk_box_pack_start(GTK_BOX(controls_box), dw->custom_threshold, FALSE, FALSE, PREF_PAD_SPACE);
+ gtk_widget_show(dw->custom_threshold);
+
+ button = gtk_check_button_new_with_label(_("Sort"));
+ gtk_widget_set_tooltip_text(GTK_WIDGET(button), "Sort by group totals");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), options->sort_totals);
+ g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(dupe_sort_totals_toggle_cb), dw);
+ gtk_box_pack_start(GTK_BOX(controls_box), button, FALSE, FALSE, PREF_PAD_SPACE);
+ gtk_widget_show(button);
+
+ dw->button_rotation_invariant = gtk_check_button_new_with_label(_("Ignore Orientation"));
+ gtk_widget_set_tooltip_text(GTK_WIDGET(dw->button_rotation_invariant), "Ignore image orientation");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dw->button_rotation_invariant), options->rot_invariant_sim);
+ g_signal_connect(G_OBJECT(dw->button_rotation_invariant), "toggled",
+ G_CALLBACK(dupe_window_rotation_invariant_cb), dw);
+ gtk_box_pack_start(GTK_BOX(controls_box), dw->button_rotation_invariant, FALSE, FALSE, PREF_PAD_SPACE);
+ gtk_widget_show(dw->button_rotation_invariant);
+
button = gtk_check_button_new_with_label(_("Compare two file sets"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), dw->second_set);
g_signal_connect(G_OBJECT(button), "toggled",
G_CALLBACK(dupe_second_set_toggle_cb), dw);
- gtk_box_pack_end(GTK_BOX(status_box), button, FALSE, FALSE, PREF_PAD_SPACE);
+ gtk_box_pack_start(GTK_BOX(controls_box), button, FALSE, FALSE, PREF_PAD_SPACE);
gtk_widget_show(button);
- status_box = gtk_hbox_new(FALSE, 0);
- gtk_box_pack_start(GTK_BOX(vbox), status_box, FALSE, FALSE, 0);
- gtk_widget_show(status_box);
+ button_box = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), button_box, FALSE, FALSE, 0);
+ gtk_widget_show(button_box);
- frame = gtk_frame_new(NULL);
- gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
- gtk_box_pack_start(GTK_BOX(status_box), frame, TRUE, TRUE, 0);
- gtk_widget_show(frame);
+ hbox = gtk_hbutton_box_new();
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_SPACE);
+ gtk_box_pack_end(GTK_BOX(button_box), hbox, FALSE, FALSE, 0);
+ gtk_widget_show(hbox);
- dw->status_label = gtk_label_new("");
- gtk_container_add(GTK_CONTAINER(frame), dw->status_label);
- gtk_widget_show(dw->status_label);
+ button = pref_button_new(NULL, GTK_STOCK_HELP, NULL, FALSE, G_CALLBACK(dupe_help_cb), NULL);
+ gtk_container_add(GTK_CONTAINER(hbox), button);
+ gtk_widget_set_can_default(button, TRUE);
+ gtk_widget_show(button);
- dw->extra_label = gtk_progress_bar_new();
- gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dw->extra_label), 0.0);
- gtk_box_pack_end(GTK_BOX(status_box), dw->extra_label, FALSE, FALSE, 0);
- gtk_widget_show(dw->extra_label);
+ button = pref_button_new(NULL, GTK_STOCK_STOP, NULL, FALSE, G_CALLBACK(dupe_check_stop_cb), dw);
+ gtk_container_add(GTK_CONTAINER(hbox), button);
+ gtk_widget_set_can_default(button, TRUE);
+ gtk_widget_show(button);
+ button = pref_button_new(NULL, GTK_STOCK_CLOSE, NULL, FALSE, G_CALLBACK(dupe_window_close_cb), dw);
+ gtk_container_add(GTK_CONTAINER(hbox), button);
+ gtk_widget_set_can_default(button, TRUE);
+ gtk_widget_grab_default(button);
+ gtk_widget_show(button);
dupe_dnd_init(dw);
/* order is important here, dnd_init should be seeing mouse
gtk_widget_show(dw->window);
+ dupe_listview_set_height(dw->listview, dw->show_thumbs);
+ g_signal_emit_by_name(G_OBJECT(dw->combo), "changed");
+
dupe_window_update_count(dw, TRUE);
dupe_window_update_progress(dw, NULL, 0.0, FALSE);
guint time, gpointer data)
{
DupeWindow *dw = data;
- gchar *uri_text;
- gint length;
GList *list;
switch (info)
case TARGET_TEXT_PLAIN:
list = dupe_listview_get_selection(dw, widget);
if (!list) return;
- uri_text = uri_text_from_filelist(list, &length, (info == TARGET_TEXT_PLAIN));
+ uri_selection_data_set_uris_from_filelist(selection_data, list);
filelist_free(list);
break;
default:
- uri_text = NULL;
break;
}
-
- if (uri_text) gtk_selection_data_set(selection_data, selection_data->target,
- 8, (guchar *)uri_text, length);
- g_free(uri_text);
}
static void dupe_dnd_data_get(GtkWidget *widget, GdkDragContext *context,
GList *list = NULL;
GList *work;
+ if (dw->add_files_queue_id > 0)
+ {
+ warning_dialog(_("Find duplicates"), _("Please wait for the current file selection to be loaded."), GTK_STOCK_DIALOG_INFO, dw->window);
+
+ return;
+ }
+
source = gtk_drag_get_source_widget(context);
if (source == dw->listview || source == dw->second_listview) return;
switch (info)
{
case TARGET_APP_COLLECTION_MEMBER:
- collection_from_dnd_data((gchar *)selection_data->data, &list, NULL);
+ collection_from_dnd_data((gchar *)gtk_selection_data_get_data(selection_data), &list, NULL);
break;
case TARGET_URI_LIST:
- list = uri_filelist_from_text((gchar *)selection_data->data, TRUE);
+ list = uri_filelist_from_gtk_selection_data(selection_data);
work = list;
while (work)
{
}
}
-static void dupe_dest_set(GtkWidget *widget, gint enable)
+static void dupe_dest_set(GtkWidget *widget, gboolean enable)
{
if (enable)
{
{
DupeWindow *dw = data;
- if (type != NOTIFY_TYPE_CHANGE || !fd->change) return;
-
- switch(fd->change->type)
+ if (!(type & NOTIFY_CHANGE) || !fd->change) return;
+
+ DEBUG_1("Notify dupe: %s %04x", fd->path, type);
+
+ switch (fd->change->type)
{
case FILEDATA_CHANGE_MOVE:
case FILEDATA_CHANGE_RENAME:
while (dupe_item_remove_by_path(dw, fd->path));
break;
case FILEDATA_CHANGE_UNSPECIFIED:
+ case FILEDATA_CHANGE_WRITE_METADATA:
break;
}
}
+
+/*
+ *-------------------------------------------------------------------
+ * Export duplicates data
+ *-------------------------------------------------------------------
+ */
+
+ typedef enum {
+ EXPORT_CSV = 0,
+ EXPORT_TSV
+} SeparatorType;
+
+typedef struct _ExportDupesData ExportDupesData;
+struct _ExportDupesData
+{
+ FileDialog *dialog;
+ SeparatorType separator;
+ DupeWindow *dupewindow;
+};
+
+static void export_duplicates_close(ExportDupesData *edd)
+{
+ if (edd->dialog) file_dialog_close(edd->dialog);
+ edd->dialog = NULL;
+}
+
+static void export_duplicates_data_cancel_cb(FileDialog *fdlg, gpointer data)
+{
+ ExportDupesData *edd = data;
+
+ export_duplicates_close(edd);
+}
+
+static void export_duplicates_data_save_cb(FileDialog *fdlg, gpointer data)
+{
+ ExportDupesData *edd = data;
+ GError *error = NULL;
+ GtkTreeModel *store;
+ GtkTreeIter iter;
+ DupeItem *di;
+ GFileOutputStream *gfstream;
+ GFile *out_file;
+ GString *output_string;
+ gchar *sep;
+ gchar* rank;
+ GList *work;
+ GtkTreeSelection *selection;
+ GList *slist;
+ gchar *thumb_cache;
+ gchar **rank_split;
+ GtkTreePath *tpath;
+ gboolean color_old = FALSE;
+ gboolean color_new = FALSE;
+ gint match_count;
+ gchar *name;
+
+ history_list_add_to_key("export_duplicates", fdlg->dest_path, -1);
+
+ out_file = g_file_new_for_path(fdlg->dest_path);
+
+ gfstream = g_file_replace(out_file, NULL, TRUE, G_FILE_CREATE_NONE, NULL, &error);
+ if (error)
+ {
+ log_printf(_("Error creating Export duplicates data file: Error: %s\n"), error->message);
+ g_error_free(error);
+ return;
+ }
+
+ sep = g_strdup((edd->separator == EXPORT_CSV) ? "," : "\t");
+ output_string = g_string_new(g_strjoin(sep, _("Match"), _("Group"), _("Similarity"), _("Set"), _("Thumbnail"), _("Name"), _("Size"), _("Date"), _("Width"), _("Height"), _("Path\n"), NULL));
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(edd->dupewindow->listview));
+ slist = gtk_tree_selection_get_selected_rows(selection, &store);
+ work = slist;
+
+ tpath = work->data;
+ gtk_tree_model_get_iter(store, &iter, tpath);
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, DUPE_COLUMN_COLOR, &color_new, -1);
+ color_old = !color_new;
+ match_count = 0;
+
+ while (work)
+ {
+ tpath = work->data;
+ gtk_tree_model_get_iter(store, &iter, tpath);
+
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, DUPE_COLUMN_POINTER, &di, -1);
+
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, DUPE_COLUMN_COLOR, &color_new, -1);
+ if (color_new != color_old)
+ {
+ match_count++;
+ }
+ color_old = color_new;
+ output_string = g_string_append(output_string, g_strdup_printf("%d", match_count));
+ output_string = g_string_append(output_string, sep);
+
+ if ((dupe_match_find_parent(edd->dupewindow, di) == di))
+ {
+ output_string = g_string_append(output_string, "1");
+ }
+ else
+ {
+ output_string = g_string_append(output_string, "2");
+ }
+ output_string = g_string_append(output_string, sep);
+
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, DUPE_COLUMN_RANK, &rank, -1);
+ rank_split = g_strsplit_set(rank, " [(", -1);
+ if (rank_split[0] == NULL)
+ {
+ output_string = g_string_append(output_string, "");
+ }
+ else
+ {
+ output_string = g_string_append(output_string, g_strdup_printf("%s", rank_split[0]));
+ }
+ output_string = g_string_append(output_string, sep);
+ g_free(rank);
+ g_strfreev(rank_split);
+
+ output_string = g_string_append(output_string, g_strdup_printf("%d", (di->second + 1)));
+ output_string = g_string_append(output_string, sep);
+
+ thumb_cache = cache_find_location(CACHE_TYPE_THUMB, di->fd->path);
+ if (thumb_cache)
+ {
+ output_string = g_string_append(output_string, thumb_cache);
+ g_free(thumb_cache);
+ }
+ else
+ {
+ output_string = g_string_append(output_string, "");
+ }
+ output_string = g_string_append(output_string, sep);
+
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, DUPE_COLUMN_NAME, &name, -1);
+ output_string = g_string_append(output_string, name);
+ output_string = g_string_append(output_string, sep);
+ g_free(name);
+
+ output_string = g_string_append(output_string, g_strdup_printf("%"PRIu64, di->fd->size));
+ output_string = g_string_append(output_string, sep);
+ output_string = g_string_append(output_string, text_from_time(di->fd->date));
+ output_string = g_string_append(output_string, sep);
+ output_string = g_string_append(output_string, g_strdup_printf("%d", (di->width ? di->width : 0)));
+ output_string = g_string_append(output_string, sep);
+ output_string = g_string_append(output_string, g_strdup_printf("%d", (di->height ? di->height : 0)));
+ output_string = g_string_append(output_string, sep);
+ output_string = g_string_append(output_string, di->fd->path);
+ output_string = g_string_append_c(output_string, '\n');
+
+ work = work->next;
+ }
+
+ g_output_stream_write(G_OUTPUT_STREAM(gfstream), output_string->str, strlen(output_string->str), NULL, &error);
+
+ g_free(sep);
+ g_string_free(output_string, TRUE);
+ g_object_unref(gfstream);
+ g_object_unref(out_file);
+
+ export_duplicates_close(edd);
+}
+
+static void pop_menu_export(GList *selection_list, gpointer dupe_window, gpointer data)
+{
+ const gint index = GPOINTER_TO_INT(data);
+ DupeWindow *dw = dupe_window;
+ gchar *title = "Export duplicates data";
+ gchar *default_path = "/tmp/";
+ gchar *file_extension;
+ const gchar *stock_id;
+ ExportDupesData *edd;
+ const gchar *previous_path;
+
+ edd = g_new0(ExportDupesData, 1);
+ edd->dialog = file_util_file_dlg(title, "export_duplicates", NULL, export_duplicates_data_cancel_cb, edd);
+
+ switch (index)
+ {
+ case EXPORT_CSV:
+ edd->separator = EXPORT_CSV;
+ file_extension = g_strdup(".csv");
+ break;
+ case EXPORT_TSV:
+ edd->separator = EXPORT_TSV;
+ file_extension = g_strdup(".tsv");
+ break;
+ default:
+ return;
+ }
+
+ stock_id = GTK_STOCK_SAVE;
+
+ generic_dialog_add_message(GENERIC_DIALOG(edd->dialog), NULL, title, NULL, FALSE);
+ file_dialog_add_button(edd->dialog, stock_id, NULL, export_duplicates_data_save_cb, TRUE);
+
+ previous_path = history_list_find_last_path_by_key("export_duplicates");
+
+ file_dialog_add_path_widgets(edd->dialog, default_path, previous_path, "export_duplicates", file_extension, _("Export Files"));
+
+ edd->dupewindow = dw;
+
+ gtk_widget_show(GENERIC_DIALOG(edd->dialog)->dialog);
+
+ g_free(file_extension);
+}
+
+static void dupe_pop_menu_export_cb(GtkWidget *widget, gpointer data)
+{
+ DupeWindow *dw;
+ GList *selection_list;
+
+ dw = submenu_item_get_data(widget);
+ selection_list = dupe_listview_get_selection(dw, dw->listview);
+ pop_menu_export(selection_list, dw, data);
+
+ filelist_free(selection_list);
+}
+
+static GtkWidget *submenu_add_export(GtkWidget *menu, GtkWidget **menu_item, GCallback func, gpointer data)
+{
+ GtkWidget *item;
+ GtkWidget *submenu;
+
+ item = menu_item_add(menu, _("Export"), NULL, NULL);
+
+ submenu = gtk_menu_new();
+ g_object_set_data(G_OBJECT(submenu), "submenu_data", data);
+
+ menu_item_add_stock_sensitive(submenu, _("Export to csv"),
+ GTK_STOCK_INDEX, TRUE, G_CALLBACK(func), GINT_TO_POINTER(0));
+ menu_item_add_stock_sensitive(submenu, _("Export to tab-delimited"),
+ GTK_STOCK_INDEX, TRUE, G_CALLBACK(func), GINT_TO_POINTER(1));
+
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
+ if (menu_item) *menu_item = item;
+
+ return submenu;
+}
+
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */