1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2 
3 /*
4  * GImageView
5  * Copyright (C) 2001-2002 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_win.c,v 1.5 2004/04/08 13:39:25 makeinu Exp $
22  */
23 
24 #include "gimv_dupl_win.h"
25 
26 #include "charset.h"
27 #include "fileutil.h"
28 #include "gimv_cell_pixmap.h"
29 #include "gimv_icon_stock.h"
30 #include "gimv_image_info.h"
31 #include "gimv_thumb.h"
32 #include "prefs.h"
33 
34 struct GimvDuplWinPriv_Tag
35 {
36    gint              thumbnail_size;
37 
38    GList            *thumb_list;
39 
40 #ifdef USE_GTK2
41    GtkTreeViewColumn *pixmap_col;
42    GtkCellRenderer   *pixmap_renderer;
43 #endif /* USE_GTK2 */
44 };
45 
46 static void gimv_dupl_win_init       (GimvDuplWin      *sw);
47 static void gimv_dupl_win_class_init (GimvDuplWinClass *klass);
48 static void gimv_dupl_win_destroy    (GtkObject        *object);
49 
50 
51 static void cb_select_all_button      (GtkButton        *button,
52                                        GimvDuplWin      *sw);
53 static void cb_finder_stop_button     (GtkWidget        *widget,
54                                        GimvDuplWin      *sw);
55 static void cb_finder_start           (GimvDuplFinder   *finder,
56                                        GimvDuplWin      *sw);
57 static void cb_finder_stop            (GimvDuplFinder   *finder,
58                                        GimvDuplWin      *sw);
59 static void cb_finder_progress_update (GimvDuplFinder   *finder,
60                                        GimvDuplWin      *sw);
61 static void cb_finder_found           (GimvDuplFinder   *finder,
62                                        GimvDuplPair     *pair,
63                                        GimvDuplWin      *sw);
64 static void cb_select_thumb           (GimvThumb        *thumb,
65                                        GimvDuplWin      *sw);
66 
67 #ifdef ENABLE_TREEVIEW
68 
69 typedef enum {
70    COLUMN_TERMINATOR = -1,
71    COLUMN_THUMBNAIL,
72    COLUMN_THUMBNAIL_MASK,
73    COLUMN_ICON,
74    COLUMN_ICON_MASK,
75    COLUMN_NAME,
76    COLUMN_ACCURACY,
77    COLUMN_SIZE,
78    COLUMN_MTIME,
79    COLUMN_THUMBDATA,
80    N_COLUMN
81 } TreeStoreColumn;
82 
83 static void     cb_tree_cursor_changed        (GtkTreeView  *treeview,
84                                                GimvDuplWin  *sw);
85 static void     cb_change_to_thumbnail_button (GtkButton    *button,
86                                                GimvDuplWin  *sw);
87 static void     cb_change_to_icon_button      (GtkButton    *button,
88                                                GimvDuplWin  *sw);
89 static gboolean find_row                      (GimvDuplWin  *sw,
90                                                GimvThumb    *thumb,
91                                                GtkTreeIter  *iter,
92                                                GtkTreeIter  *parent_iter);
93 static gboolean insert_node                   (GimvDuplWin  *sw,
94                                                GtkTreeIter  *iter,
95                                                GtkTreeIter  *parent_iter,
96                                                GimvThumb    *thumb,
97                                                gfloat        similar);
98 
99 #else /* ENABLE_TREEVIEW */
100 
101 static void    set_pixtext                    (GtkCTree     *ctree,
102                                                GtkCTreeNode *node,
103                                                gpointer      data);
104 static void    cb_change_to_thumbnail_button  (GtkButton    *button,
105                                                GimvDuplWin  *sw);
106 static void    cb_change_to_icon_button       (GtkButton    *button,
107                                                GimvDuplWin  *sw);
108 static void    cb_ctree_select_row            (GtkCTree     *ctree,
109                                                GList        *node,
110                                                gint          column,
111                                                GimvDuplWin  *sw);
112 static GtkCTreeNode *insert_node              (GimvDuplWin  *sw,
113                                                GtkCTreeNode *parent,
114                                                GimvThumb    *thumb,
115                                                gfloat        similar);
116 
117 #endif /* ENABLE_TREEVIEW */
118 
119 
120 gchar *simwin_titles[4] = {
121    N_("Name"),
122    N_("Accuracy"),
123    N_("Size (byte)"),
124    N_("Modification Time")
125 };
126 gint simwin_column_num = sizeof (simwin_titles) / sizeof (gchar *);
127 
128 static GtkDialogClass *parent_class = NULL;
129 
130 
131 GtkType
gimv_dupl_win_get_type(void)132 gimv_dupl_win_get_type (void)
133 {
134    static GtkType gimv_dupl_win_type = 0;
135 
136    if (!gimv_dupl_win_type) {
137       static const GtkTypeInfo gimv_dupl_win_info = {
138          "GimvDuplWin",
139          sizeof (GimvDuplWin),
140          sizeof (GimvDuplWinClass),
141          (GtkClassInitFunc) gimv_dupl_win_class_init,
142          (GtkObjectInitFunc) gimv_dupl_win_init,
143          NULL,
144          NULL,
145          (GtkClassInitFunc) NULL,
146       };
147 
148       gimv_dupl_win_type = gtk_type_unique (gtk_dialog_get_type (),
149                                             &gimv_dupl_win_info);
150    }
151 
152    return gimv_dupl_win_type;
153 }
154 
155 
156 static void
gimv_dupl_win_init(GimvDuplWin * sw)157 gimv_dupl_win_init (GimvDuplWin *sw)
158 {
159    GtkWidget *hbox;
160    GtkWidget *scrolledwin, *radio, *button;
161    gint i;
162 
163    sw->ctree           = NULL;
164    sw->radio_thumb     = NULL;
165    sw->radio_icon      = NULL;
166    sw->select_button   = NULL;
167    sw->progressbar     = NULL;
168 
169    sw->finder          = gimv_dupl_finder_new (NULL);
170    sw->tv              = NULL;
171 
172    sw->priv            = g_new0 (GimvDuplWinPriv, 1);
173    sw->priv->thumbnail_size  = 96;
174    sw->priv->thumb_list      = NULL;
175 #ifdef ENABLE_TREEVIEW
176    sw->priv->pixmap_col      = NULL;
177    sw->priv->pixmap_renderer = NULL;
178 #endif /* ENABLE_TREEVIEW */
179 
180    /* window */
181    gtk_window_set_title (GTK_WINDOW (sw), _("Find Duplicates - result"));
182    gtk_window_set_default_size (GTK_WINDOW (sw), 500, 400);
183    gtk_window_set_position (GTK_WINDOW (sw), GTK_WIN_POS_MOUSE);
184 
185    /* ctree */
186    scrolledwin = gtk_scrolled_window_new (NULL, NULL);
187    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrolledwin),
188                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
189    gtk_container_set_border_width (GTK_CONTAINER (scrolledwin), 5);
190 
191    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (sw)->vbox),
192                        scrolledwin, TRUE, TRUE, 0);
193 
194 #ifdef ENABLE_TREEVIEW
195    {
196       GtkTreeStore *store;
197       GtkTreeViewColumn *col;
198       GtkCellRenderer *render;
199 
200       store = gtk_tree_store_new (N_COLUMN,
201                                   GDK_TYPE_PIXMAP,
202                                   GDK_TYPE_PIXMAP,
203                                   GDK_TYPE_PIXMAP,
204                                   GDK_TYPE_PIXMAP,
205                                   G_TYPE_STRING,
206                                   G_TYPE_STRING,
207                                   G_TYPE_STRING,
208                                   G_TYPE_STRING,
209                                   G_TYPE_POINTER);
210       sw->ctree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
211       gtk_container_add (GTK_CONTAINER (scrolledwin), sw->ctree);
212 
213       gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (sw->ctree), TRUE);
214 
215       /* name column */
216       col = gtk_tree_view_column_new();
217       sw->priv->pixmap_col = col;
218       gtk_tree_view_column_set_title (col, _(simwin_titles[0]));
219 
220       render = gimv_cell_renderer_pixmap_new ();
221       sw->priv->pixmap_renderer = render;
222       gtk_tree_view_column_pack_start (col, render, FALSE);
223       gtk_tree_view_column_add_attribute (col, render,
224                                           "pixmap", COLUMN_ICON);
225       gtk_tree_view_column_add_attribute (col, render,
226                                           "mask", COLUMN_ICON_MASK);
227 
228       render = gtk_cell_renderer_text_new ();
229       gtk_tree_view_column_pack_start (col, render, TRUE);
230       gtk_tree_view_column_add_attribute (col, render, "text", COLUMN_NAME);
231 
232       gtk_tree_view_append_column (GTK_TREE_VIEW (sw->ctree), col);
233       gtk_tree_view_set_expander_column (GTK_TREE_VIEW (sw->ctree), col);
234 
235       /* other column */
236       for (i = 1; i < simwin_column_num; i++) {
237          col = gtk_tree_view_column_new();
238          gtk_tree_view_column_set_title (col, _(simwin_titles[i]));
239 
240          render = gtk_cell_renderer_text_new ();
241          gtk_tree_view_column_pack_start (col, render, TRUE);
242          gtk_tree_view_column_add_attribute (col, render, "text",
243                                              COLUMN_NAME + i);
244 
245          gtk_tree_view_append_column (GTK_TREE_VIEW (sw->ctree), col);
246       }
247 
248       g_signal_connect (G_OBJECT (sw->ctree), "cursor_changed",
249                         G_CALLBACK (cb_tree_cursor_changed), sw);
250    }
251 #else /* ENABLE_TREEVIEW */
252    {
253       for (i = 0; i < simwin_column_num; i++)
254          simwin_titles[i] = _(simwin_titles[i]);
255       sw->ctree = gtk_ctree_new_with_titles (simwin_column_num, 0, simwin_titles);
256       gtk_clist_set_column_width (GTK_CLIST (sw->ctree), 0, 250);
257       gtk_clist_set_column_width (GTK_CLIST (sw->ctree), 1, 50);
258       gtk_clist_set_column_width (GTK_CLIST (sw->ctree), 2, 50);
259       gtk_clist_set_column_width (GTK_CLIST (sw->ctree), 3, 150);
260       /*
261       for (i = 0; i < simwin_column_num; i++)
262          gtk_clist_set_column_auto_resize (GTK_CLIST (sw->ctree), i, TRUE);
263       */
264       gtk_clist_set_column_justification(GTK_CLIST (sw->ctree), 1,
265                                          GTK_JUSTIFY_CENTER);
266       gtk_clist_set_column_justification(GTK_CLIST (sw->ctree), 2,
267                                          GTK_JUSTIFY_RIGHT);
268       gtk_container_add (GTK_CONTAINER (scrolledwin), sw->ctree);
269 
270       gtk_signal_connect (GTK_OBJECT (sw->ctree), "tree_select_row",
271                           GTK_SIGNAL_FUNC (cb_ctree_select_row), sw);
272    }
273 #endif /* ENABLE_TREEVIEW */
274 
275    /* button */
276    hbox = gtk_hbox_new (FALSE, 0);
277    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (sw)->action_area),
278                        hbox, TRUE, TRUE, 0);
279 
280    /* radio button */
281    radio = gtk_radio_button_new_with_label (NULL, _("Thumbnail"));
282    sw->radio_thumb = radio;
283    gtk_signal_connect (GTK_OBJECT (radio), "clicked",
284                        GTK_SIGNAL_FUNC (cb_change_to_thumbnail_button), sw);
285    gtk_box_pack_start (GTK_BOX (hbox), radio, FALSE, FALSE, 0);
286 
287    radio = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio),
288                                                         _("Icon"));
289    sw->radio_icon  = radio;
290    gtk_signal_connect (GTK_OBJECT (radio), "clicked",
291                        GTK_SIGNAL_FUNC (cb_change_to_icon_button), sw);
292    gtk_box_pack_start (GTK_BOX (hbox), radio, FALSE, FALSE, 0);
293 
294    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE);
295 
296    /* Select All */
297    button = gtk_button_new_with_label (_("Select All"));
298    sw->select_button = button;
299    gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
300    gtk_signal_connect (GTK_OBJECT (button), "clicked",
301                        GTK_SIGNAL_FUNC (cb_select_all_button), sw);
302    GTK_WIDGET_SET_FLAGS(button,GTK_CAN_DEFAULT);
303    gtk_widget_show (button);
304 
305    /* close button */
306    button = gtk_button_new_with_label (_("Stop"));
307    sw->stop_button = button;
308    gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
309    gtk_signal_connect (GTK_OBJECT (button), "clicked",
310                        GTK_SIGNAL_FUNC (cb_finder_stop_button), sw);
311    GTK_WIDGET_SET_FLAGS(button,GTK_CAN_DEFAULT);
312    gtk_widget_show (button);
313 
314    /* gtk_widget_grab_focus (button); */
315 
316    /* progress bar */
317    sw->progressbar = gtk_progress_bar_new();
318    gtk_box_pack_end (GTK_BOX (hbox), sw->progressbar, FALSE, FALSE, 0);
319 
320    /* finder */
321    gtk_signal_connect (GTK_OBJECT (sw->finder), "start",
322                        GTK_SIGNAL_FUNC (cb_finder_start), sw);
323    gtk_signal_connect (GTK_OBJECT (sw->finder), "stop",
324                        GTK_SIGNAL_FUNC (cb_finder_stop), sw);
325    gtk_signal_connect (GTK_OBJECT (sw->finder), "progress_update",
326                        GTK_SIGNAL_FUNC (cb_finder_progress_update), sw);
327    gtk_signal_connect (GTK_OBJECT (sw->finder), "found",
328                        GTK_SIGNAL_FUNC (cb_finder_found), sw);
329 }
330 
331 
332 static void
gimv_dupl_win_class_init(GimvDuplWinClass * klass)333 gimv_dupl_win_class_init (GimvDuplWinClass *klass)
334 {
335    GtkObjectClass *object_class;
336 
337    object_class = (GtkObjectClass *) klass;
338    parent_class = gtk_type_class (gtk_dialog_get_type ());
339 
340    object_class->destroy = gimv_dupl_win_destroy;
341 }
342 
343 
344 static void
gimv_dupl_win_destroy(GtkObject * object)345 gimv_dupl_win_destroy (GtkObject *object)
346 {
347    GimvDuplWin *sw = GIMV_DUPL_WIN (object);
348 
349    g_return_if_fail (sw);
350 
351    if (sw->priv) {
352       g_list_foreach (sw->priv->thumb_list, (GFunc) gtk_object_unref, NULL);
353       g_list_free (sw->priv->thumb_list);
354       sw->priv->thumb_list = NULL;
355       g_free (sw->priv);
356       sw->priv = NULL;
357    }
358 
359    if (sw->finder) {
360       gtk_object_unref (GTK_OBJECT (sw->finder));
361       sw->finder = NULL;
362    }
363 
364    if (GTK_OBJECT_CLASS (parent_class)->destroy)
365       (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
366 }
367 
368 
369 static void
ghfunc_select_thumb(GimvThumb * thumb)370 ghfunc_select_thumb (GimvThumb *thumb)
371 {
372    gimv_thumb_view_set_selection (thumb, TRUE);
373 }
374 
375 
376 static void
cb_select_all_button(GtkButton * button,GimvDuplWin * sw)377 cb_select_all_button (GtkButton *button, GimvDuplWin *sw)
378 {
379    g_return_if_fail (sw);
380    if (!sw->tv) return;
381 
382    gimv_thumb_view_set_selection_all (sw->tv, FALSE);
383    g_list_foreach (sw->priv->thumb_list, (GFunc) ghfunc_select_thumb, NULL);
384 }
385 
386 
387 static void
cb_finder_stop_button(GtkWidget * widget,GimvDuplWin * sw)388 cb_finder_stop_button (GtkWidget *widget, GimvDuplWin *sw)
389 {
390    g_return_if_fail (sw);
391 
392    if (sw->finder)
393       gimv_dupl_finder_stop (sw->finder);
394 }
395 
396 
397 static void
cb_finder_start(GimvDuplFinder * finder,GimvDuplWin * sw)398 cb_finder_start (GimvDuplFinder *finder, GimvDuplWin *sw)
399 {
400    gfloat progress;
401 
402    g_return_if_fail (GIMV_IS_DUPL_FINDER (finder));
403    g_return_if_fail (sw);
404 
405    gtk_widget_set_sensitive (sw->stop_button, TRUE);
406 
407    gtk_progress_set_show_text (GTK_PROGRESS (sw->progressbar), TRUE);
408    gtk_progress_set_format_string (GTK_PROGRESS (sw->progressbar),
409                                    _("Finding similar images..."));
410    progress = gimv_dupl_finder_get_progress (finder);
411    gtk_progress_bar_update (GTK_PROGRESS_BAR (sw->progressbar), progress);
412 }
413 
414 
415 static void
cb_finder_stop(GimvDuplFinder * finder,GimvDuplWin * sw)416 cb_finder_stop (GimvDuplFinder *finder, GimvDuplWin *sw)
417 {
418    g_return_if_fail (GIMV_IS_DUPL_FINDER (finder));
419    g_return_if_fail (sw);
420 
421    gtk_widget_set_sensitive (sw->stop_button, FALSE);
422 
423    gtk_progress_bar_update (GTK_PROGRESS_BAR (sw->progressbar), 0.0);
424    /* gtk_progress_set_show_text (GTK_PROGRESS (sw->progressbar), FALSE); */
425    gtk_progress_set_format_string (GTK_PROGRESS (sw->progressbar),
426                                    _("Completed"));
427 }
428 
429 
430 static void
cb_finder_progress_update(GimvDuplFinder * finder,GimvDuplWin * sw)431 cb_finder_progress_update (GimvDuplFinder *finder, GimvDuplWin *sw)
432 {
433    gfloat progress;
434 
435    g_return_if_fail (GIMV_IS_DUPL_FINDER (finder));
436    g_return_if_fail (sw);
437 
438    progress = gimv_dupl_finder_get_progress (finder);
439    gtk_progress_bar_update (GTK_PROGRESS_BAR (sw->progressbar), progress);
440 }
441 
442 
443 static void
cb_finder_found(GimvDuplFinder * finder,GimvDuplPair * pair,GimvDuplWin * sw)444 cb_finder_found (GimvDuplFinder *finder, GimvDuplPair *pair, GimvDuplWin *sw)
445 {
446    g_return_if_fail (GIMV_IS_DUPL_FINDER (finder));
447    g_return_if_fail (sw);
448    g_return_if_fail (pair);
449 
450    gimv_dupl_win_set_thumb (sw, pair->thumb1, pair->thumb2, pair->similarity);
451 }
452 
453 
454 static void
cb_select_thumb(GimvThumb * thumb,GimvDuplWin * sw)455 cb_select_thumb (GimvThumb *thumb, GimvDuplWin *sw)
456 {
457    g_return_if_fail (GIMV_IS_THUMB (thumb));
458    g_return_if_fail (sw);
459 
460   if (conf.simwin_sel_thumbview) {
461       gimv_thumb_view_set_selection_all (sw->tv, FALSE);
462       gimv_thumb_view_set_selection (thumb, TRUE);
463       gimv_thumb_view_adjust (sw->tv, thumb);
464    }
465 
466    if (conf.simwin_sel_preview)
467       gimv_thumb_view_open_image (sw->tv, thumb,
468                                   GIMV_THUMB_VIEW_OPEN_IMAGE_PREVIEW);
469    if (conf.simwin_sel_new_win)
470       gimv_thumb_view_open_image (sw->tv, thumb,
471                                   GIMV_THUMB_VIEW_OPEN_IMAGE_NEW_WIN);
472    if (conf.simwin_sel_shared_win)
473       gimv_thumb_view_open_image (sw->tv, thumb,
474                                   GIMV_THUMB_VIEW_OPEN_IMAGE_SHARED_WIN);
475 }
476 
477 
478 #ifdef ENABLE_TREEVIEW
479 
480 static void
cb_tree_cursor_changed(GtkTreeView * treeview,GimvDuplWin * sw)481 cb_tree_cursor_changed (GtkTreeView *treeview, GimvDuplWin *sw)
482 {
483    GtkTreeSelection *selection;
484    GtkTreeModel *model;
485    GtkTreeIter iter;
486    GimvThumb *thumb;
487 
488    g_return_if_fail (treeview);
489    g_return_if_fail (sw);
490 
491    if (!sw->tv) return;
492 
493    selection = gtk_tree_view_get_selection (treeview);
494    if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return;
495    gtk_tree_model_get (model, &iter,
496                        COLUMN_THUMBDATA, &thumb,
497                        COLUMN_TERMINATOR);
498 
499    g_return_if_fail (GIMV_IS_THUMB (thumb));
500 
501    cb_select_thumb (thumb, sw);
502 }
503 
504 
505 static void
cb_change_to_thumbnail_button(GtkButton * button,GimvDuplWin * sw)506 cb_change_to_thumbnail_button (GtkButton *button, GimvDuplWin *sw)
507 {
508    g_return_if_fail (sw);
509 
510    gtk_tree_view_column_clear_attributes (sw->priv->pixmap_col,
511                                           sw->priv->pixmap_renderer);
512    gtk_tree_view_column_add_attribute (sw->priv->pixmap_col,
513                                        sw->priv->pixmap_renderer,
514                                        "pixmap", COLUMN_THUMBNAIL);
515    gtk_tree_view_column_add_attribute (sw->priv->pixmap_col,
516                                        sw->priv->pixmap_renderer,
517                                        "mask", COLUMN_THUMBNAIL_MASK);
518 }
519 
520 
521 static void
cb_change_to_icon_button(GtkButton * button,GimvDuplWin * sw)522 cb_change_to_icon_button (GtkButton *button, GimvDuplWin *sw)
523 {
524    g_return_if_fail (sw);
525 
526    gtk_tree_view_column_clear_attributes (sw->priv->pixmap_col,
527                                           sw->priv->pixmap_renderer);
528    gtk_tree_view_column_add_attribute (sw->priv->pixmap_col,
529                                        sw->priv->pixmap_renderer,
530                                        "pixmap", COLUMN_ICON);
531    gtk_tree_view_column_add_attribute (sw->priv->pixmap_col,
532                                        sw->priv->pixmap_renderer,
533                                        "mask", COLUMN_ICON_MASK);
534 }
535 
536 
537 static gboolean
find_row(GimvDuplWin * sw,GimvThumb * thumb,GtkTreeIter * iter,GtkTreeIter * parent_iter)538 find_row (GimvDuplWin *sw, GimvThumb *thumb,
539           GtkTreeIter *iter, GtkTreeIter *parent_iter)
540 {
541    GtkTreeModel *model;
542    GtkTreeIter tmp_iter;
543    GimvThumb *tmp_thumb;
544    gboolean go_next;
545 
546    g_return_val_if_fail (sw && thumb && iter, FALSE);
547 
548    model = gtk_tree_view_get_model (GTK_TREE_VIEW (sw->ctree));
549 
550    if (!parent_iter)
551       go_next = gtk_tree_model_get_iter_first (model, iter);
552    else
553       go_next = gtk_tree_model_iter_children (model, iter, parent_iter);
554 
555    for (; go_next; go_next = gtk_tree_model_iter_next (model, iter)) {
556       gtk_tree_model_get (model, iter,
557                           COLUMN_THUMBDATA, &tmp_thumb,
558                           COLUMN_TERMINATOR);
559 
560       if (tmp_thumb == thumb)
561          return TRUE;
562 
563       if (find_row (sw, thumb, &tmp_iter, iter)) {
564          *iter = tmp_iter;
565          return TRUE;
566       }
567    }
568 
569    return FALSE;
570 }
571 
572 
573 static gboolean
insert_node(GimvDuplWin * sw,GtkTreeIter * iter,GtkTreeIter * parent_iter,GimvThumb * thumb,gfloat similar)574 insert_node (GimvDuplWin *sw,
575              GtkTreeIter *iter, GtkTreeIter *parent_iter,
576              GimvThumb *thumb, gfloat similar)
577 {
578    GtkTreeModel *model;
579    GdkPixmap *thumb_pixmap, *icon_pixmap;
580    GdkBitmap *thumb_mask, *icon_mask;
581    gchar *text[32], accuracy[32], *tmpstr;
582 
583    g_return_val_if_fail (GIMV_IS_THUMB (thumb), FALSE);
584 
585    gimv_thumb_get_thumb (thumb, &thumb_pixmap, &thumb_mask);
586    gimv_thumb_get_icon (thumb,  &icon_pixmap,  &icon_mask);
587 
588    text[0] = (gchar *) gimv_image_info_get_path (thumb->info);
589    text[0] = charset_to_internal (text[0],
590                                   conf.charset_filename,
591                                   conf.charset_auto_detect_fn,
592                                   conf.charset_filename_mode);
593 
594    if (similar > 0) {
595       g_snprintf (accuracy, 32, "%2.1f%%", similar * 100);
596       text[1] = accuracy;
597    } else {
598       text[1] = NULL;
599    }
600 
601    tmpstr  = fileutil_size2str (thumb->info->st.st_size, FALSE);
602    text[2] = charset_locale_to_internal (tmpstr);
603    g_free (tmpstr);
604 
605    tmpstr  = fileutil_time2str (thumb->info->st.st_mtime);
606    text[3] = charset_locale_to_internal (tmpstr);
607    g_free (tmpstr);
608 
609    gtk_object_ref (GTK_OBJECT(thumb));
610    sw->priv->thumb_list = g_list_append (sw->priv->thumb_list, thumb);
611 
612    model = gtk_tree_view_get_model (GTK_TREE_VIEW (sw->ctree));
613    gtk_tree_store_append (GTK_TREE_STORE (model), iter, parent_iter);
614    gtk_tree_store_set (GTK_TREE_STORE (model), iter,
615                        COLUMN_THUMBNAIL,      thumb_pixmap,
616                        COLUMN_THUMBNAIL_MASK, thumb_mask,
617                        COLUMN_ICON,           icon_pixmap,
618                        COLUMN_ICON_MASK,      icon_mask,
619                        COLUMN_NAME,           text[0],
620                        COLUMN_ACCURACY,       text[1],
621                        COLUMN_SIZE,           text[2],
622                        COLUMN_MTIME,          text[3],
623                        COLUMN_THUMBDATA,      thumb,
624                        COLUMN_TERMINATOR);
625 
626    g_free (text[0]);
627    g_free (text[2]);
628    g_free (text[3]);
629 
630    return TRUE;
631 }
632 
633 #else /* ENABLE_TREEVIEW */
634 
635 static void
set_pixtext(GtkCTree * ctree,GtkCTreeNode * node,gpointer data)636 set_pixtext (GtkCTree *ctree, GtkCTreeNode *node, gpointer data)
637 {
638    gboolean thumbnail = GPOINTER_TO_INT (data);
639    GimvThumb *thumb;
640    GdkPixmap *pixmap;
641    GdkBitmap *mask;
642    guint8 spacing;
643    gboolean is_leaf, expanded;
644    gchar *text;
645 
646    g_return_if_fail (ctree);
647    g_return_if_fail (node);
648 
649    thumb = gtk_ctree_node_get_row_data (ctree, node);
650    g_return_if_fail (GIMV_IS_THUMB (thumb));
651 
652    if (thumbnail)
653       gimv_thumb_get_thumb (thumb, &pixmap, &mask);
654    else
655       gimv_thumb_get_icon (thumb, &pixmap, &mask);
656 
657    gtk_ctree_get_node_info (ctree, node, &text, &spacing,
658                             NULL, NULL, NULL, NULL,
659                             &is_leaf, &expanded);
660    gtk_ctree_set_node_info (ctree, node,
661                             text, spacing,
662                             pixmap, mask, pixmap, mask,
663                             is_leaf, expanded);
664 }
665 
666 
667 static void
cb_change_to_thumbnail_button(GtkButton * button,GimvDuplWin * sw)668 cb_change_to_thumbnail_button (GtkButton *button, GimvDuplWin *sw)
669 {
670    g_return_if_fail (sw);
671 
672    gtk_clist_set_row_height (GTK_CLIST (sw->ctree), sw->priv->thumbnail_size);
673    gtk_ctree_post_recursive (GTK_CTREE (sw->ctree), NULL,
674                              (GtkCTreeFunc) set_pixtext,
675                              GINT_TO_POINTER (TRUE));
676 }
677 
678 
679 static void
cb_change_to_icon_button(GtkButton * button,GimvDuplWin * sw)680 cb_change_to_icon_button (GtkButton *button, GimvDuplWin *sw)
681 {
682    g_return_if_fail (sw);
683 
684    gtk_clist_set_row_height (GTK_CLIST (sw->ctree), ICON_SIZE);
685    gtk_ctree_post_recursive (GTK_CTREE (sw->ctree), NULL,
686                              (GtkCTreeFunc) set_pixtext,
687                              GINT_TO_POINTER (FALSE));
688 }
689 
690 
691 static void
cb_ctree_select_row(GtkCTree * ctree,GList * node,gint column,GimvDuplWin * sw)692 cb_ctree_select_row (GtkCTree *ctree, GList *node, gint column, GimvDuplWin *sw)
693 {
694    GimvThumb *thumb;
695 
696    g_return_if_fail (ctree);
697    g_return_if_fail (node);
698    g_return_if_fail (sw);
699 
700    if (!sw->tv) return;
701 
702    thumb = gtk_ctree_node_get_row_data (ctree, GTK_CTREE_NODE (node));
703    g_return_if_fail (GIMV_IS_THUMB (thumb));
704 
705    cb_select_thumb (thumb, sw);
706 }
707 
708 
709 static GtkCTreeNode *
insert_node(GimvDuplWin * sw,GtkCTreeNode * parent,GimvThumb * thumb,gfloat similar)710 insert_node (GimvDuplWin *sw,
711              GtkCTreeNode *parent,
712              GimvThumb *thumb,
713              gfloat similar)
714 {
715    GtkCTreeNode *node;
716    GdkPixmap *pixmap;
717    GdkBitmap *mask;
718    gchar *text[32], accuracy[32], *tmpstr;
719 
720    g_return_val_if_fail (GIMV_IS_THUMB (thumb), NULL);
721 
722    if (GTK_TOGGLE_BUTTON (sw->radio_thumb)->active)
723       gimv_thumb_get_thumb (thumb, &pixmap, &mask);
724    else
725       gimv_thumb_get_icon (thumb, &pixmap, &mask);
726 
727    text[0] = (gchar *) gimv_image_info_get_path (thumb->info);
728    text[0] = charset_to_internal (text[0],
729                                   conf.charset_filename,
730                                   conf.charset_auto_detect_fn,
731                                   conf.charset_filename_mode);
732 
733    if (similar > 0) {
734       g_snprintf (accuracy, 32, "%2.1f%%", similar * 100);
735       text[1] = accuracy;
736    } else {
737       text[1] = NULL;
738    }
739 
740    tmpstr  = fileutil_size2str (thumb->info->st.st_size, FALSE);
741    text[2] = charset_locale_to_internal (tmpstr);
742    g_free (tmpstr);
743 
744    tmpstr  = fileutil_time2str (thumb->info->st.st_mtime);
745    text[3] = charset_locale_to_internal (tmpstr);
746    g_free (tmpstr);
747 
748    node = gtk_ctree_insert_node (GTK_CTREE (sw->ctree),
749                                  parent, NULL, text, 4,
750                                  pixmap, mask,
751                                  pixmap, mask,
752                                  FALSE, FALSE);
753    gtk_object_ref (GTK_OBJECT(thumb));
754    sw->priv->thumb_list = g_list_append (sw->priv->thumb_list, thumb);
755    gtk_ctree_node_set_row_data (GTK_CTREE (sw->ctree), node, thumb);
756 
757    g_free (text[0]);
758    g_free (text[2]);
759    g_free (text[3]);
760 
761    return node;
762 }
763 
764 #endif /* ENABLE_TREEVIEW */
765 
766 
767 
768 /******************************************************************************
769  *
770  *   Public Functions.
771  *
772  ******************************************************************************/
773 GimvDuplWin *
gimv_dupl_win_new(gint thumbnail_size)774 gimv_dupl_win_new (gint thumbnail_size)
775 {
776    GimvDuplWin *sw;
777 
778    sw = GIMV_DUPL_WIN (gtk_type_new (gimv_dupl_win_get_type ()));
779 
780    /* FIXME */
781    sw->priv->thumbnail_size = thumbnail_size;
782    gtk_widget_show_all (GTK_WIDGET (sw));
783    gimv_icon_stock_set_window_icon (GTK_WIDGET (sw)->window, "gimv_icon");
784    /* END FIXME */
785 
786    return sw;
787 }
788 
789 
790 void
gimv_dupl_win_set_relation(GimvDuplWin * sw,GimvThumbView * tv)791 gimv_dupl_win_set_relation (GimvDuplWin *sw, GimvThumbView *tv)
792 {
793    g_return_if_fail (sw);
794    g_return_if_fail (tv);
795 
796    sw->tv = tv;
797 
798    gtk_widget_set_sensitive (sw->select_button, TRUE);
799 }
800 
801 
802 void
gimv_dupl_win_unset_relation(GimvDuplWin * sw)803 gimv_dupl_win_unset_relation (GimvDuplWin *sw)
804 {
805    g_return_if_fail (sw);
806 
807    sw->tv = NULL;
808 
809    gtk_widget_set_sensitive (sw->select_button, FALSE);
810 }
811 
812 
813 void
gimv_dupl_win_set_thumb(GimvDuplWin * sw,GimvThumb * thumb1,GimvThumb * thumb2,gfloat similar)814 gimv_dupl_win_set_thumb (GimvDuplWin *sw,
815                          GimvThumb *thumb1,
816                          GimvThumb*thumb2,
817                          gfloat     similar)
818 {
819    g_return_if_fail (sw);
820    g_return_if_fail (GIMV_IS_THUMB (thumb1));
821    g_return_if_fail (GIMV_IS_THUMB (thumb2));
822 
823 #ifdef ENABLE_TREEVIEW
824 {
825    GtkTreeIter parent_iter, iter;
826    gboolean success;
827 
828    success = find_row (sw, thumb1, &parent_iter, NULL);
829    if (!success) {
830       GtkTreeView *treeview = GTK_TREE_VIEW (sw->ctree);
831       GtkTreeModel *model;
832       GtkTreeSelection *selection;
833 
834       success = insert_node (sw, &parent_iter, NULL, thumb1, -1);
835 
836       selection = gtk_tree_view_get_selection (treeview);
837       if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
838          if (find_row (sw, thumb1, &iter, NULL)) {
839             GtkTreePath *treepath = gtk_tree_model_get_path (model, &parent_iter);
840 
841             g_signal_handlers_block_by_func (sw->ctree,
842                                            cb_tree_cursor_changed,
843                                            sw);
844             gtk_tree_view_set_cursor (treeview, treepath, NULL, FALSE);
845             g_signal_handlers_unblock_by_func (sw->ctree,
846                                              cb_tree_cursor_changed,
847                                              sw);
848             gtk_tree_path_free (treepath);
849          }
850       }
851    }
852    if (success) {
853       GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (sw->ctree));
854       GtkTreePath *treepath = gtk_tree_model_get_path (model, &parent_iter);
855 
856       success = insert_node (sw, &iter, &parent_iter, thumb2, similar);
857       gtk_tree_view_expand_row (GTK_TREE_VIEW (sw->ctree), treepath, FALSE);
858       gtk_tree_path_free (treepath);
859    }
860 }
861 #else /* ENABLE_TREEVIEW */
862 {
863    GtkCTreeNode *parent, *node;
864 
865    node = gtk_ctree_find_by_row_data (GTK_CTREE (sw->ctree), NULL, thumb1);
866    if (node)
867       parent = node;
868    else
869       parent = insert_node (sw, node, thumb1, -1);
870 
871    node = insert_node (sw, parent, thumb2, similar);
872    gtk_ctree_expand (GTK_CTREE (sw->ctree), parent);
873 }
874 #endif /* ENABLE_TREEVIEW */
875 }
876