1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2
3 /*
4 * GImageView
5 * Copyright (C) 2001-2003 Takuro Ashie
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * $Id: gimv_dupl_finder.c,v 1.5 2004/10/16 12:11:23 makeinu Exp $
22 */
23
24 #include "gimv_dupl_finder.h"
25
26 #include <string.h>
27
28 #include "gimv_thumb.h"
29 #include "gtk2-compat.h"
30 #include "prefs.h"
31 #include "gimv_dupl_win.h"
32
33
34 typedef enum {
35 START_SIGNAL,
36 STOP_SIGNAL,
37 PROGRESS_UPDATE_SIGNAL,
38 FOUND_SIGNAL,
39 LAST_SIGNAL
40 } GimvDuplFinderSignalType;
41
42
43 static void gimv_dupl_finder_class_init (GimvDuplFinderClass *klass);
44 static void gimv_dupl_finder_init (GimvDuplFinder *finder);
45 static void gimv_dupl_finder_destroy (GtkObject *object);
46
47 gboolean idle_duplicates_find (gpointer user_data);
48 gboolean timeout_duplicates_find (gpointer data);
49
50
51 static GtkObjectClass *parent_class = NULL;
52 static gint gimv_dupl_finder_signals[LAST_SIGNAL] = {0};
53
54
55 extern GimvDuplCompFuncTable gimv_dupl_similar_funcs;
56 extern GimvDuplCompFuncTable gimv_dupl_file_size_funcs;
57 extern GimvDuplCompFuncTable gimv_dupl_md5_funcs;
58
59 GimvDuplCompFuncTable *duplicates_funcs_table[] =
60 {
61 &gimv_dupl_similar_funcs,
62 &gimv_dupl_md5_funcs,
63 &gimv_dupl_file_size_funcs,
64 };
65
66
67 static const gchar **labels = NULL;
68
69 const gchar **
gimv_dupl_finder_get_algol_types(void)70 gimv_dupl_finder_get_algol_types (void)
71 {
72
73 if (!labels) {
74 gint i, num;
75
76 num = sizeof (duplicates_funcs_table) / sizeof (GimvDuplCompFuncTable *);
77 labels = g_malloc0 (sizeof (gchar*) * (num + 1));
78
79 for (i = 0; i < num; i++) {
80 GimvDuplCompFuncTable *funcs = duplicates_funcs_table[i];
81
82 labels[i] = g_strdup (funcs->label);
83 }
84 }
85
86 return labels;
87 }
88
89
90 GtkType
gimv_dupl_finder_get_type(void)91 gimv_dupl_finder_get_type (void)
92 {
93 static GtkType gimv_dupl_finder_type = 0;
94
95 if (!gimv_dupl_finder_type) {
96 static const GtkTypeInfo gimv_dupl_finder_info = {
97 "GimvDuplFinder",
98 sizeof (GimvDuplFinder),
99 sizeof (GimvDuplFinderClass),
100 (GtkClassInitFunc) gimv_dupl_finder_class_init,
101 (GtkObjectInitFunc) gimv_dupl_finder_init,
102 NULL,
103 NULL,
104 (GtkClassInitFunc) NULL,
105 };
106
107 gimv_dupl_finder_type = gtk_type_unique (gtk_object_get_type (),
108 &gimv_dupl_finder_info);
109 }
110
111 return gimv_dupl_finder_type;
112 }
113
114
115 static void
gimv_dupl_finder_class_init(GimvDuplFinderClass * klass)116 gimv_dupl_finder_class_init (GimvDuplFinderClass *klass)
117 {
118 GtkObjectClass *object_class;
119
120 object_class = (GtkObjectClass *) klass;
121 parent_class = gtk_type_class (gtk_object_get_type ());
122
123 gimv_dupl_finder_signals[START_SIGNAL]
124 = gtk_signal_new ("start",
125 GTK_RUN_FIRST,
126 GTK_CLASS_TYPE (object_class),
127 GTK_SIGNAL_OFFSET (GimvDuplFinderClass, start),
128 gtk_signal_default_marshaller,
129 GTK_TYPE_NONE, 0);
130
131 gimv_dupl_finder_signals[STOP_SIGNAL]
132 = gtk_signal_new ("stop",
133 GTK_RUN_FIRST,
134 GTK_CLASS_TYPE (object_class),
135 GTK_SIGNAL_OFFSET (GimvDuplFinderClass, stop),
136 gtk_signal_default_marshaller,
137 GTK_TYPE_NONE, 0);
138
139 gimv_dupl_finder_signals[PROGRESS_UPDATE_SIGNAL]
140 = gtk_signal_new ("progress_update",
141 GTK_RUN_FIRST,
142 GTK_CLASS_TYPE (object_class),
143 GTK_SIGNAL_OFFSET (GimvDuplFinderClass,
144 progress_update),
145 gtk_signal_default_marshaller,
146 GTK_TYPE_NONE, 0);
147
148 gimv_dupl_finder_signals[FOUND_SIGNAL]
149 = gtk_signal_new ("found",
150 GTK_RUN_FIRST,
151 GTK_CLASS_TYPE (object_class),
152 GTK_SIGNAL_OFFSET (GimvDuplFinderClass, found),
153 gtk_marshal_NONE__POINTER,
154 GTK_TYPE_NONE, 1, GTK_TYPE_POINTER);
155
156 gtk_object_class_add_signals (object_class,
157 gimv_dupl_finder_signals, LAST_SIGNAL);
158
159 object_class->destroy = gimv_dupl_finder_destroy;
160
161 klass->start = NULL;
162 klass->stop = NULL;
163 klass->progress_update = NULL;
164 klass->found = NULL;
165 }
166
167
168 static void
gimv_dupl_finder_init(GimvDuplFinder * finder)169 gimv_dupl_finder_init (GimvDuplFinder *finder)
170 {
171 finder->src_list = NULL;
172 finder->dest_list = NULL;
173 finder->cur1 = NULL;
174 finder->cur2 = NULL;
175 finder->table = NULL;
176 finder->funcs = &gimv_dupl_similar_funcs;
177 finder->progress = 0.0;
178 finder->pos = 0;
179 finder->len = 0;
180 finder->pairs_found = 0;
181 finder->refresh_rate = 1000;
182 finder->timer_rate = 10;
183 finder->timer_id = 0;
184 finder->idle_id = 0;
185
186 #ifdef USE_GTK2
187 gtk_object_ref (GTK_OBJECT (finder));
188 gtk_object_sink (GTK_OBJECT (finder));
189 #endif
190 }
191
192
193 GimvDuplFinder *
gimv_dupl_finder_new(const gchar * type)194 gimv_dupl_finder_new (const gchar *type)
195 {
196 GimvDuplFinder *finder;
197
198 finder = GIMV_DUPL_FINDER (gtk_type_new (gimv_dupl_finder_get_type ()));
199 gimv_dupl_finder_set_algol_type (finder, type);
200
201 return finder;
202 }
203
204
205 static void
gimv_dupl_finder_destroy(GtkObject * object)206 gimv_dupl_finder_destroy (GtkObject *object)
207 {
208 GimvDuplFinder *finder = GIMV_DUPL_FINDER (object);
209
210 gimv_dupl_finder_stop (finder);
211
212 g_list_foreach (finder->src_list, (GFunc) gtk_object_unref, NULL);
213 g_list_foreach (finder->dest_list, (GFunc) gtk_object_unref, NULL);
214 g_list_free (finder->src_list);
215 g_list_free (finder->dest_list);
216 finder->src_list = NULL;
217 finder->dest_list = NULL;
218
219 if (GTK_OBJECT_CLASS (parent_class)->destroy)
220 (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
221 }
222
223
224 void
gimv_dupl_finder_set_algol_type(GimvDuplFinder * finder,const gchar * type)225 gimv_dupl_finder_set_algol_type (GimvDuplFinder *finder, const gchar *type)
226 {
227 gint i, num = sizeof (duplicates_funcs_table) / sizeof (GimvDuplCompFuncTable *);
228
229 g_return_if_fail (GIMV_IS_DUPL_FINDER (finder));
230
231 finder->funcs = NULL;
232
233 if (!type || !*type) {
234 finder->funcs = duplicates_funcs_table[0];
235 return;
236 }
237
238 for (i = 0; i < num; i++) {
239 GimvDuplCompFuncTable *funcs = duplicates_funcs_table[i];
240
241 if (!funcs->label || !*funcs->label) continue;
242 if (!strcmp (funcs->label, type)) {
243 finder->funcs = funcs;
244 return;
245 }
246 }
247
248 finder->funcs = duplicates_funcs_table[0];
249 }
250
251
252 void
gimv_dupl_finder_append_src(GimvDuplFinder * finder,GimvThumb * thumb)253 gimv_dupl_finder_append_src (GimvDuplFinder *finder,
254 GimvThumb *thumb)
255 {
256 g_return_if_fail (GIMV_DUPL_FINDER (finder));
257 g_return_if_fail (GIMV_IS_THUMB (thumb));
258
259 gtk_object_ref(GTK_OBJECT(thumb));
260 finder->src_list = g_list_append (finder->src_list, thumb);
261 }
262
263
264 void
gimv_dupl_finder_append_dest(GimvDuplFinder * finder,GimvThumb * thumb)265 gimv_dupl_finder_append_dest (GimvDuplFinder *finder,
266 GimvThumb *thumb)
267 {
268 g_return_if_fail (GIMV_DUPL_FINDER (finder));
269 g_return_if_fail (GIMV_IS_THUMB (thumb));
270
271 gtk_object_ref(GTK_OBJECT(thumb));
272 finder->dest_list = g_list_append (finder->dest_list, thumb);
273 }
274
275
276 static void
free_image_sim(gpointer key,gpointer value,gpointer data)277 free_image_sim (gpointer key, gpointer value, gpointer data)
278 {
279 GimvDuplFinder *finder = data;
280
281 if (finder->funcs->data_delete)
282 finder->funcs->data_delete (value);
283 }
284
285
286 gboolean
timeout_duplicates_find(gpointer data)287 timeout_duplicates_find (gpointer data)
288 {
289 GimvDuplFinder *finder = data;
290
291 finder->timer_id = 0;
292 finder->idle_id = gtk_idle_add (idle_duplicates_find, data);
293
294 return FALSE;
295 }
296
297
298 gboolean
idle_duplicates_find(gpointer user_data)299 idle_duplicates_find (gpointer user_data)
300 {
301 GimvDuplFinder *finder = user_data;
302 gint i;
303
304 if (!GIMV_IS_DUPL_FINDER (finder)) return FALSE;
305 if (!finder->table) goto STOP;
306 if (!finder->cur1 || !finder->cur2) goto STOP;
307
308 for (i = 0; i < finder->refresh_rate; i++) {
309 GimvThumb *thumb1, *thumb2;
310 gpointer data1, data2;
311 gfloat similar = 0.0;
312
313 thumb1 = finder->cur1->data;
314 thumb2 = finder->cur2->data;
315
316 data1 = g_hash_table_lookup (finder->table, thumb1);
317 if (!data1) {
318 if (finder->funcs->get_data)
319 data1 = finder->funcs->get_data (thumb1);
320 else
321 data1 = thumb1;
322 g_hash_table_insert (finder->table, thumb1, data1);
323 }
324 data2 = g_hash_table_lookup (finder->table, thumb2);
325 if (!data2) {
326 if (finder->funcs->get_data)
327 data2 = finder->funcs->get_data (thumb2);
328 else
329 data2 = thumb2;
330 g_hash_table_insert (finder->table, thumb2, data2);
331 }
332
333 if (!finder->funcs->compare (data1, data2, &similar)) {
334 GimvDuplPair pair;
335
336 pair.thumb1 = thumb1;
337 pair.thumb2 = thumb2;
338 pair.similarity = similar;
339
340 finder->pairs_found++;
341
342 gtk_signal_emit (GTK_OBJECT (finder),
343 gimv_dupl_finder_signals[FOUND_SIGNAL],
344 &pair);
345 }
346
347 finder->pos++;
348 finder->progress = (gfloat) finder->pos / (gfloat) finder->len;
349
350 finder->cur2 = g_list_next (finder->cur2);
351 if (!finder->cur2) {
352 finder->cur1 = g_list_next (finder->cur1);
353 finder->cur2 = g_list_next (finder->cur1);
354 }
355 if (!finder->cur1 || !finder->cur2) goto STOP;
356 }
357
358 gtk_signal_emit (GTK_OBJECT (finder),
359 gimv_dupl_finder_signals[PROGRESS_UPDATE_SIGNAL]);
360
361 finder->timer_id = gtk_timeout_add (finder->timer_rate,
362 idle_duplicates_find, finder);
363 finder->idle_id = 0;
364
365 return FALSE;
366
367 STOP:
368 gimv_dupl_finder_stop (finder);
369
370 return FALSE;
371 }
372
373
374 void
gimv_dupl_finder_start(GimvDuplFinder * finder)375 gimv_dupl_finder_start (GimvDuplFinder *finder)
376 {
377 gint num;
378
379 g_return_if_fail (GIMV_IS_DUPL_FINDER (finder));
380
381 gtk_signal_emit (GTK_OBJECT (finder), gimv_dupl_finder_signals[START_SIGNAL]);
382
383 if (!finder->table)
384 finder->table = g_hash_table_new (g_direct_hash, g_direct_equal);
385
386 finder->cur1 = finder->dest_list;
387 finder->cur2 = g_list_next (finder->dest_list);
388
389 num = g_list_length (finder->dest_list);
390 finder->len = num * (num - 1) / 2;
391 finder->pos = 0;
392 finder->pairs_found = 0;
393
394 finder->timer_id = gtk_idle_add (idle_duplicates_find, finder);
395 }
396
397
398 void
gimv_dupl_finder_stop(GimvDuplFinder * finder)399 gimv_dupl_finder_stop (GimvDuplFinder *finder)
400 {
401 g_return_if_fail (GIMV_IS_DUPL_FINDER (finder));
402
403 if (finder->timer_id)
404 gtk_timeout_remove (finder->timer_id);
405 finder->timer_id = 0;
406
407 if (finder->idle_id)
408 gtk_idle_remove (finder->idle_id);
409 finder->idle_id = 0;
410
411 finder->progress = 0.0;
412 finder->pos = 0;
413
414 if (finder->table) {
415 g_hash_table_foreach (finder->table, (GHFunc) free_image_sim, finder);
416 g_hash_table_destroy (finder->table);
417 finder->table = NULL;
418 }
419
420 gtk_signal_emit (GTK_OBJECT (finder),
421 gimv_dupl_finder_signals[STOP_SIGNAL]);
422 }
423
424
425 gfloat
gimv_dupl_finder_get_progress(GimvDuplFinder * finder)426 gimv_dupl_finder_get_progress (GimvDuplFinder *finder)
427 {
428 g_return_val_if_fail (GIMV_IS_DUPL_FINDER (finder), 0.0);
429 return finder->progress;
430 }
431
432