📄 dupe.c
字号:
/* * GQview * (C) 2005 John Ellis * * 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! */#include "gqview.h"#include "dupe.h"#include "cache.h"#include "collect.h"#include "collect-table.h"#include "dnd.h"#include "editors.h"#include "filelist.h"#include "image-load.h"#include "img-view.h"#include "info.h"#include "layout.h"#include "layout_image.h"#include "md5-util.h"#include "menu.h"#include "print.h"#include "thumb.h"#include "utilops.h"#include "ui_bookmark.h"#include "ui_fileops.h"#include "ui_menu.h"#include "ui_misc.h"#include "ui_tree_edit.h"#include <gdk/gdkkeysyms.h> /* for keyboard values */#include <math.h>#define DUPE_DEF_WIDTH 600#define DUPE_DEF_HEIGHT 400/* column assignment order (simply change them here) */enum { DUPE_COLUMN_POINTER = 0, DUPE_COLUMN_RANK, DUPE_COLUMN_THUMB, DUPE_COLUMN_NAME, DUPE_COLUMN_SIZE, DUPE_COLUMN_DATE, DUPE_COLUMN_DIMENSIONS, DUPE_COLUMN_PATH, DUPE_COLUMN_COLOR, DUPE_COLUMN_COUNT /* total columns */};static GList *dupe_window_list = NULL; /* list of open DupeWindow *s *//* * Well, after adding the 'compare two sets' option things got a little sloppy in here * because we have to account for two 'modes' everywhere. (be careful). */static void dupe_match_unlink(DupeItem *a, DupeItem *b);static DupeItem *dupe_match_find_parent(DupeWindow *dw, DupeItem *child);static gint dupe_match(DupeItem *a, DupeItem *b, DupeMatchType mask, gdouble *rank, gint fast);static void dupe_thumb_step(DupeWindow *dw);static gint dupe_check_cb(gpointer data);static void dupe_second_add(DupeWindow *dw, DupeItem *di);static void dupe_second_remove(DupeWindow *dw, DupeItem *di);static GtkWidget *dupe_menu_popup_second(DupeWindow *dw, DupeItem *di);static void dupe_dnd_init(DupeWindow *dw);/* * ------------------------------------------------------------------ * Window updates * ------------------------------------------------------------------ */static void dupe_window_update_count(DupeWindow *dw, gint count_only){ gchar *text; if (!dw->list) { text = g_strdup(_("Drop files to compare them.")); } else if (count_only) { text = g_strdup_printf(_("%d files"), g_list_length(dw->list)); } else { text = g_strdup_printf(_("%d matches found in %d files"), g_list_length(dw->dupes), g_list_length(dw->list)); } if (dw->second_set) { gchar *buf = g_strconcat(text, " ", _("[set 1]"), NULL); g_free(text); text = buf; } gtk_label_set_text(GTK_LABEL(dw->status_label), text); g_free(text);}static guint64 msec_time(void){ struct timeval tv; if (gettimeofday(&tv, NULL) == -1) return 0; return (guint64)tv.tv_sec * 1000000 + (guint64)tv.tv_usec;}static gint dupe_iterations(gint n){ return (n * ((n + 1) / 2));}static void dupe_window_update_progress(DupeWindow *dw, const gchar *status, gdouble value, gint force){ const gchar *status_text; if (status) { guint64 new_time = 0; if (dw->setup_n % 10 == 0) { new_time = msec_time() - dw->setup_time; } if (!force && value != 0.0 && dw->setup_count > 0 && new_time > 2000000) { gchar *buf; gint t; gint d; guint32 rem; if (new_time - dw->setup_time_count < 250000) return; dw->setup_time_count = new_time; if (dw->setup_done) { if (dw->second_set) { t = dw->setup_count; d = dw->setup_count - dw->setup_n; } else { t = dupe_iterations(dw->setup_count); d = dupe_iterations(dw->setup_count - dw->setup_n); } } else { t = dw->setup_count; d = dw->setup_count - dw->setup_n; } rem = (t - d) ? ((gdouble)(dw->setup_time_count / 1000000) / (t - d)) * d : 0; gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dw->extra_label), value); buf = g_strdup_printf("%s %d:%02d ", status, rem / 60, rem % 60); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(dw->extra_label), buf); g_free(buf); return; } else if (force || value == 0.0 || dw->setup_count == 0 || dw->setup_time_count == 0 || (new_time > 0 && new_time - dw->setup_time_count >= 250000)) { if (dw->setup_time_count == 0) dw->setup_time_count = 1; if (new_time > 0) dw->setup_time_count = new_time; gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dw->extra_label), value); status_text = status; } else { status_text = NULL; } } else { gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dw->extra_label), 0.0); status_text = " "; } if (status_text) gtk_progress_bar_set_text(GTK_PROGRESS_BAR(dw->extra_label), status_text);}static void widget_set_cursor(GtkWidget *widget, gint icon){ GdkCursor *cursor; if (!widget->window) return; if (icon == -1) { cursor = NULL; } else { cursor = gdk_cursor_new (icon); } gdk_window_set_cursor(widget->window, cursor); if (cursor) gdk_cursor_unref(cursor);}/* * ------------------------------------------------------------------ * row color utils * ------------------------------------------------------------------ */static void dupe_listview_realign_colors(DupeWindow *dw){ GtkTreeModel *store; GtkTreeIter iter; gboolean color_set = TRUE; DupeItem *parent = NULL; gint valid; store = gtk_tree_view_get_model(GTK_TREE_VIEW(dw->listview)); valid = gtk_tree_model_get_iter_first(store, &iter); while (valid) { DupeItem *child; DupeItem *child_parent; gtk_tree_model_get(store, &iter, DUPE_COLUMN_POINTER, &child, -1); child_parent = dupe_match_find_parent(dw, child); if (!parent || parent != child_parent) { if (!parent) { /* keep the first row as it is */ gtk_tree_model_get(store, &iter, DUPE_COLUMN_COLOR, &color_set, -1); } else { color_set = !color_set; } parent = dupe_match_find_parent(dw, child); } gtk_list_store_set(GTK_LIST_STORE(store), &iter, DUPE_COLUMN_COLOR, color_set, -1); valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); }}/* * ------------------------------------------------------------------ * Dupe item utils * ------------------------------------------------------------------ */static DupeItem *dupe_item_new(const gchar *path, gint64 size, time_t date){ DupeItem *di; di = g_new0(DupeItem, 1); di->path = g_strdup(path); di->name = filename_from_path(di->path); di->size = size; di->date = date; 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;}static void dupe_item_free(DupeItem *di){ g_free(di->path); image_sim_free(di->simd); g_free(di->md5sum); if (di->pixbuf) g_object_unref(di->pixbuf); g_free(di);}static void dupe_list_free(GList *list){ GList *work = list; while (work) { DupeItem *di = work->data; work = work->next; dupe_item_free(di); } g_list_free(list);}static DupeItem *dupe_item_find_path_by_list(const gchar *path, GList *work){ while (work) { DupeItem *di = work->data; if (strcmp(di->path, path) == 0) return di; work = work->next; } return NULL;}static DupeItem *dupe_item_find_path(DupeWindow *dw, const gchar *path){ DupeItem *di; di = dupe_item_find_path_by_list(path, dw->list); if (!di && dw->second_set) di = dupe_item_find_path_by_list(path, dw->second_list); return di;}/* * ------------------------------------------------------------------ * Image property cache * ------------------------------------------------------------------ */static void dupe_item_read_cache(DupeItem *di){ gchar *path; CacheData *cd; if (!di) return; path = cache_find_location(CACHE_TYPE_SIM, di->path); if (!path) return; if (filetime(di->path) != filetime(path)) { g_free(path); return; } cd = cache_sim_data_load(path); g_free(path); if (cd) { if (!di->simd && cd->sim) { di->simd = cd->sim; cd->sim = NULL; } if (di->width == 0 && di->height == 0 && cd->dimensions) { 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); } cache_sim_data_free(cd); }}static void dupe_item_write_cache(DupeItem *di){ gchar *base; mode_t mode = 0755; if (!di) return; base = cache_get_location(CACHE_TYPE_SIM, di->path, FALSE, &mode); if (cache_ensure_dir_exists(base, mode)) { CacheData *cd; cd = cache_sim_data_new(); cd->path = cache_get_location(CACHE_TYPE_SIM, di->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]; if (md5_digest_from_text(di->md5sum, digest)) cache_sim_data_set_md5sum(cd, digest); } if (di->simd) cache_sim_data_set_similarity(cd, di->simd); if (cache_sim_data_save(cd)) { filetime_set(cd->path, filetime(di->path)); } cache_sim_data_free(cd); } g_free(base);}/* * ------------------------------------------------------------------ * Window list utils * ------------------------------------------------------------------ */static gint dupe_listview_find_item(GtkListStore *store, DupeItem *item, GtkTreeIter *iter){ gint valid; gint row = 0; valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), iter); while (valid) { DupeItem *item_n; gtk_tree_model_get(GTK_TREE_MODEL(store), iter, DUPE_COLUMN_POINTER, &item_n, -1); if (item_n == item) return row; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), iter); row++; } return -1;}static void dupe_listview_add(DupeWindow *dw, DupeItem *parent, DupeItem *child){ DupeItem *di; gint row; gchar *text[DUPE_COLUMN_COUNT]; GtkListStore *store; GtkTreeIter iter; gboolean color_set = FALSE; gint rank; if (!parent) return; store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(dw->listview))); if (child) { DupeMatch *dm; row = dupe_listview_find_item(store, parent, &iter); gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, DUPE_COLUMN_COLOR, &color_set, -1); row++; if (child->group) { dm = child->group->data; rank = (gint)floor(dm->rank); } else { rank = 1; printf("NULL group in item!\n"); } } else { if (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; } else { color_set = FALSE; } row = 0; rank = 0; } di = (child) ? child : parent; if (!child && dw->second_set) { text[DUPE_COLUMN_RANK] = g_strdup("[1]"); } else if (rank == 0) { text[DUPE_COLUMN_RANK] = g_strdup((di->second) ? "(2)" : ""); } else { text[DUPE_COLUMN_RANK] = g_strdup_printf("%d%s", rank, (di->second) ? " (2)" : ""); } text[DUPE_COLUMN_THUMB] = ""; text[DUPE_COLUMN_NAME] = (gchar *)di->name; text[DUPE_COLUMN_SIZE] = text_from_size(di->size); text[DUPE_COLUMN_DATE] = (gchar *)text_from_time(di->date); if (di->width > 0 && di->height > 0) { text[DUPE_COLUMN_DIMENSIONS] = g_strdup_printf("%d x %d", di->width, di->height); } else { text[DUPE_COLUMN_DIMENSIONS] = g_strdup(""); } text[DUPE_COLUMN_PATH] = di->path; text[DUPE_COLUMN_COLOR] = NULL; gtk_list_store_insert(store, &iter, row); gtk_list_store_set(store, &iter, DUPE_COLUMN_POINTER, di, DUPE_COLUMN_RANK, text[DUPE_COLUMN_RANK], DUPE_COLUMN_THUMB, NULL, DUPE_COLUMN_NAME, text[DUPE_COLUMN_NAME], DUPE_COLUMN_SIZE, text[DUPE_COLUMN_SIZE], DUPE_COLUMN_DATE, text[DUPE_COLUMN_DATE], DUPE_COLUMN_DIMENSIONS, text[DUPE_COLUMN_DIMENSIONS], DUPE_COLUMN_PATH, text[DUPE_COLUMN_PATH], DUPE_COLUMN_COLOR, color_set, -1); g_free(text[DUPE_COLUMN_RANK]); g_free(text[DUPE_COLUMN_SIZE]); g_free(text[DUPE_COLUMN_DIMENSIONS]);}static void dupe_listview_populate(DupeWindow *dw){ GtkListStore *store; GList *work;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -