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