1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2 
3 /*
4  * GImageView
5  * Copyright (C) 2001 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_thumb_view.c,v 1.17 2004/12/20 11:07:31 makeinu Exp $
22  */
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 
30 #include "gimageview.h"
31 
32 #include "charset.h"
33 #include "dirview.h"
34 #include "dnd.h"
35 #include "fileload.h"
36 #include "fileutil.h"
37 #include "gfileutil.h"
38 #include "gtk2-compat.h"
39 #include "gtk_prop.h"
40 #include "gtkutils.h"
41 #include "gimv_comment_view.h"
42 #include "gimv_dupl_finder.h"
43 #include "gimv_dupl_win.h"
44 #include "gimv_image.h"
45 #include "gimv_image_win.h"
46 #include "gimv_scrolled.h"
47 #include "gimv_thumb.h"
48 #include "gimv_thumb_cache.h"
49 #include "gimv_thumb_view.h"
50 #include "gimv_thumb_win.h"
51 #include "menu.h"
52 #include "prefs.h"
53 
54 #ifdef ENABLE_EXIF
55 #   include "exif_view.h"
56 #endif /* ENABLE_EXIF */
57 
58 
59 typedef enum
60 {
61    ACTION_NONE,
62    ACTION_POPUP,
63    ACTION_OPEN_AUTO,
64    ACTION_OPEN_AUTO_FORCE,
65    ACTION_OPEN_IN_PREVIEW,
66    ACTION_OPEN_IN_PREVIEW_FORCE,
67    ACTION_OPEN_IN_NEW_WIN,
68    ACTION_OPEN_IN_SHARED_WIN,
69    ACTION_OPEN_IN_SHARED_WIN_FORCE
70 } ButtonActionType;
71 
72 
73 struct GimvThumbViewPriv_Tag
74 {
75    gchar               *dirname;   /* store dir name if mode is dirmode */
76    FRArchive           *archive;
77 
78    /* list of relation to other object */
79    GList               *related_image_view;
80    GList               *related_dupl_win;
81 
82    /* for mouse event */
83    gint                 button_2pressed_queue; /* attach an action to
84                                                   button release event */
85 };
86 
87 
88 static void gimv_thumb_view_class_init (GimvThumbViewClass *klass);
89 static void gimv_thumb_view_init       (GimvThumbView *tv);
90 static void gimv_thumb_view_destroy    (GtkObject     *object);
91 
92 
93 /* callback functions */
94 static void   cb_open_image            (GimvThumbView  *tv,
95                                         GimvThumbViewOpenImageType action,
96                                         GtkWidget      *menuitem);
97 static void   cb_open_image_by_external(GtkWidget      *menuitem,
98                                         GimvThumbView  *tv);
99 static void   cb_open_image_by_script  (GtkWidget      *menuitem,
100                                         GimvThumbView  *tv);
101 static void   cb_recreate_thumbnail    (GimvThumbView  *tv,
102                                         guint           action,
103                                         GtkWidget      *menuitem);
104 static void   cb_remove_thumbnail      (GimvThumbView  *tv,
105                                         guint           action,
106                                         GtkWidget      *menuitem);
107 static void   cb_file_property         (GimvThumbView  *tv,
108                                         guint           action,
109                                         GtkWidget      *menuitem);
110 #ifdef ENABLE_EXIF
111 static void   cb_exif                  (GimvThumbView  *tv,
112                                         guint           action,
113                                         GtkWidget      *menuitem);
114 #endif /* ENABLE_EXIF */
115 static void   cb_edit_comment          (GimvThumbView  *tv,
116                                         guint           action,
117                                         GtkWidget      *menuitem);
118 static void   cb_file_operate          (GimvThumbView  *tv,
119                                         FileOperateType type,
120                                         GtkWidget      *widget);
121 static void   cb_rename_file           (GimvThumbView  *tv,
122                                         guint           action,
123                                         GtkWidget      *menuitem);
124 static void   cb_remove_file           (GimvThumbView  *tv,
125                                         guint           action,
126                                         GtkWidget      *menuitem);
127 static void   cb_dupl_win_destroy      (GtkWidget      *window,
128                                         GimvThumbView  *tv);
129 static void   cb_thumbview_scrollbar_value_changed
130                                        (GtkWidget      *widget,
131                                         GimvThumbView  *tv);
132 
133 
134 /* compare functions */
135 static int comp_func_spel  (gconstpointer data1,
136                             gconstpointer data2);
137 static int comp_func_size  (gconstpointer data1,
138                             gconstpointer data2);
139 static int comp_func_atime (gconstpointer data1,
140                             gconstpointer data2);
141 static int comp_func_mtime (gconstpointer data1,
142                             gconstpointer data2);
143 static int comp_func_ctime (gconstpointer data1,
144                             gconstpointer data2);
145 static int comp_func_type  (gconstpointer data1,
146                             gconstpointer data2);
147 static int comp_func_width (gconstpointer data1,
148                             gconstpointer data2);
149 static int comp_func_height(gconstpointer data1,
150                             gconstpointer data2);
151 static int comp_func_area  (gconstpointer data1,
152                             gconstpointer data2);
153 
154 /* other private functions */
155 static void       gimv_thumb_view_append_thumb_data(GimvThumbView  *tv,
156                                                     GimvThumb      *thumb);
157 static void       gimv_thumb_view_remove_thumb_data(GimvThumbView  *tv,
158                                                     GimvThumb      *thumb);
159 static gchar     *get_uri_list                     (GList          *thumblist);
160 static void       gimv_thumb_view_button_action    (GimvThumbView  *tv,
161                                                     GimvThumb      *thumb,
162                                                     GdkEventButton *event,
163                                                     gint            num);
164 static GtkWidget *create_progs_submenu             (GimvThumbView  *tv);
165 static GtkWidget *create_scripts_submenu           (GimvThumbView  *tv);
166 static void       gimv_thumb_view_reset_load_priority
167                                                    (GimvThumbView  *tv);
168 static void       gimv_thumb_view_set_scrollbar_callback
169                                                    (GimvThumbView *tv);
170 static void       gimv_thumb_view_remove_scrollbar_callback
171                                                    (GimvThumbView *tv);
172 static GCompareFunc
173                   gimv_thumb_view_get_compare_func(GimvThumbView *tv,
174                                                     gboolean      *reverse);
175 
176 
177 /* reference popup menu for each thumbnail */
178 static GtkItemFactoryEntry thumb_button_popup_items [] =
179 {
180    {N_("/_Open"),                     NULL,  cb_open_image,    GIMV_THUMB_VIEW_OPEN_IMAGE_AUTO,        NULL},
181    {N_("/Open in New _Window"),       NULL,  cb_open_image,    GIMV_THUMB_VIEW_OPEN_IMAGE_NEW_WIN,     NULL},
182    {N_("/Open in S_hared Window"),    NULL,  cb_open_image,    GIMV_THUMB_VIEW_OPEN_IMAGE_SHARED_WIN,  NULL},
183    {N_("/Open in E_xternal Program"), NULL,  NULL,             0,           "<Branch>"},
184    {N_("/_Scripts"),                  NULL,  NULL,             0,           "<Branch>"},
185    {N_("/---"),                       NULL,  NULL,             0,           "<Separator>"},
186    {N_("/_Update Thumbnail"),         NULL,  cb_recreate_thumbnail,  0,     NULL},
187    {N_("/Remo_ve from List"),         NULL,  cb_remove_thumbnail,    0,     NULL},
188    {N_("/---"),                       NULL,  NULL,             0,           "<Separator>"},
189    {N_("/_Property..."),              NULL,  cb_file_property, 0,           NULL},
190 #ifdef ENABLE_EXIF
191    {N_("/Scan E_XIF Data..."),        NULL,  cb_exif,          0,           NULL},
192 #endif
193    {N_("/_Edit Comment..."),          NULL,  cb_edit_comment,  0,           NULL},
194    {N_("/---"),                       NULL,  NULL,             0,           "<Separator>"},
195    {N_("/Re_name..."),                NULL,  cb_rename_file,   0,           NULL},
196    {N_("/_Copy Files To..."),         NULL,  cb_file_operate,  FILE_COPY,   NULL},
197    {N_("/_Move Files To..."),         NULL,  cb_file_operate,  FILE_MOVE,   NULL},
198    {N_("/_Link Files To..."),         NULL,  cb_file_operate,  FILE_LINK,   NULL},
199    {N_("/_Remove file..."),           NULL,  cb_remove_file,   0,           NULL},
200    {NULL, NULL, NULL, 0, NULL},
201 };
202 
203 
204 static GList   *GimvThumbViewList = NULL;
205 
206 static GtkObjectClass *parent_class = NULL;
207 
208 static guint    total_tab_count = 0;
209 
210 static guint    button = 0;
211 static gboolean pressed = FALSE;
212 static gboolean dragging = FALSE;
213 static gint     drag_start_x = 0;
214 static gint     drag_start_y = 0;
215 
216 
217 
218 /****************************************************************************
219  *
220  *  Plugin Management
221  *
222  ****************************************************************************/
223 static GList                *plugin_list = NULL;
224 static GHashTable           *modes_table = NULL;
225 static gchar               **mode_labels = NULL;
226 extern GimvThumbViewPlugin   thumbalbum_modes[];
227 extern gint                  thumbalbum_modes_num;
228 
229 gint
gimv_thumb_view_label_to_num(const gchar * label)230 gimv_thumb_view_label_to_num(const gchar *label)
231 {
232    GimvThumbViewPlugin *view, *default_view;
233    gint pos;
234 
235    g_return_val_if_fail (modes_table, 0);
236 
237    if (!label || !*label) return 0;
238 
239    view = g_hash_table_lookup (modes_table, label);
240 
241    if (!view) {
242       default_view = g_hash_table_lookup (modes_table,
243                                           GIMV_THUMB_VIEW_DEFAULT_SUMMARY_MODE);
244       g_return_val_if_fail (view && default_view, 0);
245       pos = g_list_index (plugin_list, view);
246    } else {
247       pos = g_list_index (plugin_list, view);
248    }
249 
250    if (pos < 0)
251       return 0;
252    else
253       return pos;
254 }
255 
256 
257 const gchar *
gimv_thumb_view_num_to_label(gint num)258 gimv_thumb_view_num_to_label (gint num)
259 {
260    GimvThumbViewPlugin *view;
261 
262    view = g_list_nth_data (plugin_list, num);
263    g_return_val_if_fail (view, NULL);
264 
265    return view->label;
266 }
267 
268 
269 GList *
gimv_thumb_view_get_summary_mode_list(void)270 gimv_thumb_view_get_summary_mode_list (void)
271 {
272    gint i;
273 
274    if (!modes_table)
275       modes_table = g_hash_table_new (g_str_hash, g_str_equal);
276 
277    if (!plugin_list) {
278       for (i = 0; i < thumbalbum_modes_num; i++) {
279          g_hash_table_insert (modes_table,
280                               (gpointer) thumbalbum_modes[i].label,
281                               &thumbalbum_modes[i]);
282          plugin_list = g_list_append (plugin_list, &thumbalbum_modes[i]);
283       }
284    }
285 
286    return plugin_list;
287 }
288 
289 
290 gchar **
gimv_thumb_view_get_summary_mode_labels(gint * length_ret)291 gimv_thumb_view_get_summary_mode_labels (gint *length_ret)
292 {
293    gint i, num;
294    gchar **labels;
295    GList *list;
296 
297    gimv_thumb_view_get_summary_mode_list ();
298    g_return_val_if_fail (plugin_list, NULL);
299 
300    if (mode_labels) {
301       *length_ret = sizeof (mode_labels) / sizeof (gchar *);
302       return mode_labels;
303    }
304 
305    num = *length_ret = g_list_length (plugin_list);
306    g_return_val_if_fail (num > 0, NULL);
307 
308    mode_labels = labels = g_new0 (gchar *, num + 1);
309    list = plugin_list;
310    for (i = 0; list && i < num; i++) {
311       GimvThumbViewPlugin *view = list->data;
312       if (!view) {
313          g_free (labels);
314          return NULL;
315       }
316       labels[i] = g_strdup (view->label);
317       list = g_list_next (list);
318    }
319    labels[num] = NULL;
320 
321    return labels;
322 }
323 
324 
325 static gint
comp_func_priority(GimvThumbViewPlugin * plugin1,GimvThumbViewPlugin * plugin2)326 comp_func_priority (GimvThumbViewPlugin *plugin1,
327                     GimvThumbViewPlugin *plugin2)
328 {
329    g_return_val_if_fail (plugin1, 1);
330    g_return_val_if_fail (plugin2, -1);
331 
332    return plugin1->priority_hint - plugin2->priority_hint;
333 }
334 
335 
336 gboolean
gimv_thumb_view_plugin_regist(const gchar * plugin_name,const gchar * module_name,gpointer impl,gint size)337 gimv_thumb_view_plugin_regist (const gchar *plugin_name,
338                                const gchar *module_name,
339                                gpointer impl,
340                                gint     size)
341 {
342    GimvThumbViewPlugin *plugin = impl;
343 
344    g_return_val_if_fail (module_name, FALSE);
345    g_return_val_if_fail (plugin, FALSE);
346    g_return_val_if_fail (size > 0, FALSE);
347    g_return_val_if_fail (plugin->if_version == GIMV_THUMBNAIL_VIEW_IF_VERSION,
348                          FALSE);
349    g_return_val_if_fail (plugin->label, FALSE);
350 
351    if (!plugin_list)
352       gimv_thumb_view_get_summary_mode_list();
353 
354    g_hash_table_insert (modes_table,
355                         (gpointer) plugin->label,
356                         plugin);
357    plugin_list = g_list_append (plugin_list, plugin);
358    plugin_list = g_list_sort (plugin_list,
359                               (GCompareFunc) comp_func_priority);
360 
361    return TRUE;
362 }
363 
364 
365 /******************************************************************************
366  *
367  *   Callback functions.
368  *
369  ******************************************************************************/
370 static void
cb_open_image(GimvThumbView * tv,GimvThumbViewOpenImageType action,GtkWidget * menuitem)371 cb_open_image (GimvThumbView *tv, GimvThumbViewOpenImageType action,
372                GtkWidget *menuitem)
373 {
374    GimvThumb *thumb;
375    GList *thumblist, *node;
376    gint listnum;
377 
378    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
379 
380    thumblist = gimv_thumb_view_get_selection_list (tv);
381    if (!thumblist) return;
382 
383    listnum = g_list_length (thumblist);
384 
385    if (action == GIMV_THUMB_VIEW_OPEN_IMAGE_SHARED_WIN) {
386       thumb = thumblist->data;
387       gimv_thumb_view_open_image (tv, thumb, action);
388    } else {
389       FilesLoader *files;
390 
391       files = files_loader_new ();
392       if (listnum > 2)
393          files_loader_create_progress_window (files);
394       node = thumblist;
395 
396       while (node) {
397          gint pos;
398          gfloat progress;
399          gchar buf[32];
400 
401          thumb = node->data;
402          node = g_list_next (node);
403 
404          if (files->status == CANCEL || files->status == STOP) break;
405 
406          gimv_thumb_view_open_image (tv, thumb, action);
407 
408          pos = g_list_position (thumblist, node) + 1;
409          progress = (gfloat) pos / (gfloat) listnum;
410          g_snprintf (buf, 32, "%d/%d files", pos, listnum);
411          files_loader_progress_update (files, progress, buf);
412       }
413 
414       files_loader_destroy_progress_window (files);
415       files_loader_delete (files);
416    }
417 
418    g_list_free (thumblist);
419 }
420 
421 
422 static void
cb_open_image_by_external(GtkWidget * menuitem,GimvThumbView * tv)423 cb_open_image_by_external (GtkWidget *menuitem, GimvThumbView *tv)
424 {
425    GimvThumb *thumb;
426    GList *thumblist, *node;
427    guint action;
428    gchar *user_cmd, *cmd = NULL, *tmpstr = NULL, **pair;
429    gboolean show_dialog = FALSE;
430 
431    g_return_if_fail (menuitem && GIMV_IS_THUMB_VIEW (tv));
432 
433    thumblist = node = gimv_thumb_view_get_selection_list (tv);
434    if (!thumblist) return;
435 
436    action = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (menuitem), "num"));
437 
438    /* find command */
439    if (action < sizeof (conf.progs) / sizeof (conf.progs[0])) {
440       pair = g_strsplit (conf.progs[action], ",", 3);
441       if (!pair[1]) {
442          g_strfreev (pair);
443          return;
444       } else {
445          cmd = g_strdup (pair[1]);
446       }
447       if (pair[2] && !g_strcasecmp (pair[2], "TRUE"))
448          show_dialog = TRUE;
449       g_strfreev (pair);
450    } else {
451       return;
452    }
453 
454    /* create command string */
455    while (node) {
456       thumb = node->data;
457       tmpstr = g_strconcat (cmd, " ",
458                             "\"", gimv_image_info_get_path (thumb->info), "\"",
459                             NULL);
460       g_free (cmd);
461       cmd = tmpstr;
462       node = g_list_next (node);
463    }
464    tmpstr = g_strconcat (cmd, " &", NULL);
465    g_free (cmd);
466    cmd = tmpstr;
467    tmpstr = NULL;
468 
469    tmpstr = cmd;
470    cmd = charset_to_internal (cmd,
471                               conf.charset_filename,
472                               conf.charset_auto_detect_fn,
473                               conf.charset_filename_mode);
474    g_free (tmpstr);
475    tmpstr = NULL;
476 
477    if (show_dialog) {
478       user_cmd = gtkutil_popup_textentry (_("Execute command"),
479                                           _("Please enter options:"),
480                                           cmd, NULL, 400,
481                                           TEXT_ENTRY_AUTOCOMP_PATH
482                                           | TEXT_ENTRY_WRAP_ENTRY
483                                           | TEXT_ENTRY_CURSOR_TOP,
484                                           GTK_WINDOW (tv->tw));
485    } else {
486       user_cmd = g_strdup (cmd);
487    }
488    g_free (cmd);
489    cmd = NULL;
490 
491    /* exec command */
492    if (user_cmd) {
493       tmpstr = user_cmd;
494       user_cmd = charset_internal_to_locale (user_cmd);
495       g_free (tmpstr);
496       tmpstr = NULL;
497       system (user_cmd);
498       g_free (user_cmd);
499    }
500 
501    g_list_free (thumblist);
502 }
503 
504 
505 static void
cb_open_image_by_script(GtkWidget * menuitem,GimvThumbView * tv)506 cb_open_image_by_script (GtkWidget *menuitem, GimvThumbView *tv)
507 {
508    GimvThumb *thumb;
509    GList *thumblist, *node;
510    gchar *script, *cmd = NULL, *tmpstr = NULL, *user_cmd;
511 
512    g_return_if_fail (menuitem && GIMV_IS_THUMB_VIEW (tv));
513 
514    thumblist = node = gimv_thumb_view_get_selection_list (tv);
515    if (!thumblist) return;
516 
517    script = gtk_object_get_data (GTK_OBJECT (menuitem), "script");
518    if (!script || !script || !isexecutable (script)) goto ERROR;
519 
520    cmd = g_strdup (script);
521 
522    /* create command string */
523    while (node) {
524       thumb = node->data;
525       tmpstr = g_strconcat (cmd, " ",
526                             "\"", gimv_image_info_get_path (thumb->info), "\"",
527                             NULL);
528       g_free (cmd);
529       cmd = tmpstr;
530       node = g_list_next (node);
531    }
532    tmpstr = g_strconcat (cmd, " &", NULL);
533    g_free (cmd);
534    cmd = tmpstr;
535    tmpstr = NULL;
536 
537    {
538       tmpstr = cmd;
539       cmd = charset_to_internal (cmd,
540                                  conf.charset_filename,
541                                  conf.charset_auto_detect_fn,
542                                  conf.charset_filename_mode);
543       g_free (tmpstr);
544       tmpstr = NULL;
545 
546       if (conf.scripts_show_dialog) {
547          user_cmd = gtkutil_popup_textentry (_("Execute script"),
548                                              _("Please enter options:"),
549                                              cmd, NULL, 400,
550                                              TEXT_ENTRY_AUTOCOMP_PATH
551                                              | TEXT_ENTRY_WRAP_ENTRY
552                                              | TEXT_ENTRY_CURSOR_TOP,
553                                              GTK_WINDOW (tv->tw));
554       } else {
555          user_cmd = g_strdup (cmd);
556       }
557       g_free (cmd);
558       cmd = NULL;
559 
560       tmpstr = user_cmd;
561       user_cmd = charset_internal_to_locale (user_cmd);
562       g_free (tmpstr);
563       tmpstr = NULL;
564    }
565 
566    /* exec command */
567    if (user_cmd) {
568       {   /********** convert charset **********/
569          tmpstr = user_cmd;
570          user_cmd = charset_internal_to_locale (user_cmd);
571          g_free (tmpstr);
572          tmpstr = NULL;
573       }
574       system (user_cmd);
575       g_free (user_cmd);
576       user_cmd = NULL;
577    }
578 
579 ERROR:
580    g_list_free (thumblist);
581 }
582 
583 
584 /* FIXME: context of progress should be displayed */
585 static void
cb_recreate_thumbnail(GimvThumbView * tv,guint action,GtkWidget * menuitem)586 cb_recreate_thumbnail (GimvThumbView *tv, guint action, GtkWidget *menuitem)
587 {
588    GimvThumb *thumb;
589    GList *thumblist, *node;
590 
591    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
592 
593    thumblist = gimv_thumb_view_get_selection_list (tv);
594    if (!thumblist) return;
595 
596    for (node = thumblist; node; node = g_list_next (node)) {
597       thumb = node->data;
598       if (!thumb) continue;
599 
600       gimv_thumb_view_refresh_thumbnail (tv, thumb, CREATE_THUMB);
601    }
602 
603    g_list_free (thumblist);
604 }
605 /* END FIXME */
606 
607 
608 static void
cb_remove_thumbnail(GimvThumbView * tv,guint action,GtkWidget * menuitem)609 cb_remove_thumbnail (GimvThumbView *tv, guint action, GtkWidget *menuitem)
610 {
611    GimvThumb *thumb;
612    GList *thumblist, *node;
613 
614    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
615    g_return_if_fail (tv->vfuncs);
616 
617    thumblist = gimv_thumb_view_get_selection_list (tv);
618    if (!thumblist) return;
619 
620    if (tv->vfuncs->freeze)
621       tv->vfuncs->freeze (tv);
622 
623    node = thumblist;
624    while (node) {
625       thumb = node->data;
626       node = g_list_next (node);
627       if (!GIMV_IS_THUMB (thumb)) continue;
628 
629       tv->vfuncs->remove_thumb (tv, thumb);
630       gimv_thumb_view_remove_thumb_data (tv, thumb);
631       gtk_object_unref (GTK_OBJECT(thumb));
632    }
633 
634    if (tv->vfuncs->thaw)
635       tv->vfuncs->thaw (tv);
636 
637    gimv_thumb_win_set_statusbar_page_info (tv->tw,
638                                            GIMV_THUMB_WIN_CURRENT_PAGE);
639 
640    g_list_free (thumblist);
641 }
642 
643 
644 static void
cb_file_property(GimvThumbView * tv,guint action,GtkWidget * menuitem)645 cb_file_property (GimvThumbView *tv, guint action, GtkWidget *menuitem)
646 {
647    GimvThumb *thumb;
648    GList *thumblist;
649    GimvImageInfo *info;
650    gint flags = 0;
651 
652    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
653 
654    thumblist = gimv_thumb_view_get_selection_list (tv);
655    if (!thumblist || g_list_length (thumblist) > 1) return;
656 
657    thumb = thumblist->data;
658    if (!thumb) return;
659 
660    info = gimv_image_info_ref (thumb->info);
661    if (!info) return;
662 
663    if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) {
664       flags |= GTK_PROP_EDITABLE | GTK_PROP_NOT_DETECT_TYPE;
665    }
666 
667    if (dlg_prop_from_image_info (info, flags))
668       gimv_thumb_view_refresh_list (tv);
669 
670    gimv_image_info_unref (info);
671 }
672 
673 
674 #ifdef ENABLE_EXIF
675 static void
cb_exif(GimvThumbView * tv,guint action,GtkWidget * menuitem)676 cb_exif (GimvThumbView *tv, guint action, GtkWidget *menuitem)
677 {
678    GimvThumb *thumb;
679    GList *thumblist;
680 
681    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
682 
683    thumblist = gimv_thumb_view_get_selection_list (tv);
684    if (!thumblist || g_list_length (thumblist) > 1) return;
685 
686    thumb = thumblist->data;
687    if (!thumb) return;
688 
689    exif_view_create_window (gimv_image_info_get_path (thumb->info),
690                             GTK_WINDOW (tv->tw));
691 }
692 #endif /* ENABLE_EXIF */
693 
694 
695 static void
cb_edit_comment(GimvThumbView * tv,guint action,GtkWidget * menuitem)696 cb_edit_comment (GimvThumbView *tv, guint action, GtkWidget *menuitem)
697 {
698    GList *thumblist, *node;
699 
700    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
701 
702    node = thumblist = gimv_thumb_view_get_selection_list (tv);
703    if (!thumblist) return;
704 
705    while (node) {
706       GimvThumb *thumb = node->data;
707       node = g_list_next (node);
708 
709       if (!thumb) continue;
710 
711       gimv_comment_view_create_window (thumb->info);
712    }
713 }
714 
715 
716 static gchar *previous_dir = NULL;
717 
718 
719 static void
cb_file_operate(GimvThumbView * tv,FileOperateType type,GtkWidget * widget)720 cb_file_operate (GimvThumbView *tv, FileOperateType type, GtkWidget *widget)
721 {
722    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
723    gimv_thumb_view_file_operate (tv, type);
724 }
725 
726 
727 static void
cb_rename_file(GimvThumbView * tv,guint action,GtkWidget * menuitem)728 cb_rename_file (GimvThumbView *tv, guint action, GtkWidget *menuitem)
729 {
730    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
731    gimv_thumb_view_rename_file (tv);
732 }
733 
734 
735 static void
cb_remove_file(GimvThumbView * tv,guint action,GtkWidget * menuitem)736 cb_remove_file (GimvThumbView *tv, guint action, GtkWidget *menuitem)
737 {
738    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
739    gimv_thumb_view_delete_files (tv);
740 }
741 
742 
743 static void
cb_dupl_win_destroy(GtkWidget * window,GimvThumbView * tv)744 cb_dupl_win_destroy (GtkWidget *window, GimvThumbView *tv)
745 {
746    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
747 
748    if (!tv->priv) return;
749 
750    tv->priv->related_dupl_win
751       = g_list_remove (tv->priv->related_dupl_win, window);
752 }
753 
754 
755 static void
cb_thumbview_scrollbar_value_changed(GtkWidget * widget,GimvThumbView * tv)756 cb_thumbview_scrollbar_value_changed (GtkWidget *widget,
757                                       GimvThumbView *tv)
758 {
759    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
760 
761    gimv_thumb_view_reset_load_priority (tv);
762 }
763 
764 
765 typedef struct GimvThumbViewButtonAction_Tag
766 {
767    GimvThumbView *tv;
768    GimvThumb *thumb;
769    GdkEventButton event;
770    gint action;
771 } GimvThumbViewButtonAction;
772 
773 
774 static gint
idle_button_action(gpointer data)775 idle_button_action (gpointer data)
776 {
777    GimvThumbViewButtonAction *act = data;
778    g_return_val_if_fail (act, 0);
779    gimv_thumb_view_button_action (act->tv, act->thumb,
780                                   &act->event, act->action);
781    return 0;
782 }
783 
784 
785 gboolean
gimv_thumb_view_thumb_button_press_cb(GtkWidget * widget,GdkEventButton * event,GimvThumb * thumb)786 gimv_thumb_view_thumb_button_press_cb (GtkWidget *widget,
787                                        GdkEventButton *event,
788                                        GimvThumb *thumb)
789 {
790    GimvThumbView *tv;
791    GimvThumbWin *tw;
792    gint num;
793 
794    g_return_val_if_fail (event, FALSE);
795 
796    button = event->button;
797    pressed = TRUE;
798    drag_start_x = event->x;
799    drag_start_y = event->y;
800 
801    if (!thumb) goto ERROR;
802    g_return_val_if_fail (GIMV_IS_THUMB (thumb), FALSE);
803 
804    tv = gimv_thumb_get_parent_thumbview (thumb);
805    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), FALSE);
806 
807    tw = tv->tw;
808    g_return_val_if_fail (GIMV_IS_THUMB_WIN (tw), FALSE);
809 
810    gimv_thumb_win_notebook_drag_src_unset (tw);   /* FIXMEEEEEEEE!! */
811 
812    /* reset selection */
813    if (event->type == GDK_BUTTON_PRESS
814        && (event->button == 2 || event->button == 3))
815    {
816       if (!thumb->selected) {
817          gimv_thumb_view_set_selection_all (tv, FALSE);
818          gimv_thumb_view_set_selection (thumb, TRUE);
819       }
820    }
821 
822    while (gtk_events_pending()) gtk_main_iteration();
823 
824    num = prefs_mouse_get_num_from_event (event, conf.thumbview_mouse_button);
825    if (event->type == GDK_2BUTTON_PRESS) {
826       tv->priv->button_2pressed_queue = num;
827    } else if (num > 0) {
828       GimvThumbViewButtonAction *act = g_new0 (GimvThumbViewButtonAction, 1);
829 
830       act->tv = tv;
831       act->thumb = thumb;
832       act->event = *event;
833       act->action = num;
834       gtk_idle_add_full (GTK_PRIORITY_REDRAW,
835                          idle_button_action, NULL, act,
836                          (GtkDestroyNotify) g_free);
837    }
838 
839  ERROR:
840    if (event->button == 3) /* for avoiding notebook's event */
841       return TRUE;
842    else
843       return FALSE;
844 }
845 
846 
847 gboolean
gimv_thumb_view_thumb_button_release_cb(GtkWidget * widget,GdkEventButton * event,GimvThumb * thumb)848 gimv_thumb_view_thumb_button_release_cb (GtkWidget *widget,
849                                          GdkEventButton *event,
850                                          GimvThumb *thumb)
851 {
852    GimvThumbView *tv;
853    GimvThumbWin *tw;
854    gint num;
855 
856    g_return_val_if_fail (event, FALSE);
857 
858    if (!thumb) goto ERROR;
859    g_return_val_if_fail (GIMV_IS_THUMB (thumb), FALSE);
860 
861    tv = gimv_thumb_get_parent_thumbview (thumb);
862    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), FALSE);
863 
864    tw = tv->tw;
865    g_return_val_if_fail (GIMV_IS_THUMB_WIN (tw), FALSE);
866 
867    gimv_thumb_win_notebook_drag_src_reset (tw);   /* FIXMEEEEEEEEE!!! */
868 
869    if(pressed && !dragging) {
870       if (tv->priv->button_2pressed_queue) {
871          num = tv->priv->button_2pressed_queue;
872          if (num > 0)
873             num = 0 - num;
874          tv->priv->button_2pressed_queue = 0;
875       } else {
876          num = prefs_mouse_get_num_from_event (event,
877                                                conf.thumbview_mouse_button);
878       }
879       if (num < 0) {
880          GimvThumbViewButtonAction *act = g_new0 (GimvThumbViewButtonAction, 1);
881          act->tv = tv;
882          act->thumb = thumb;
883          act->event = *event;
884          act->action = num;
885          gtk_idle_add_full (GTK_PRIORITY_REDRAW,
886                             idle_button_action, NULL, act,
887                             (GtkDestroyNotify) g_free);
888       }
889    }
890 
891  ERROR:
892    button   = 0;
893    pressed  = FALSE;
894    dragging = FALSE;
895 
896    if (event->button == 3)   /* for avoiding notebook's callback */
897       return TRUE;
898    else
899       return FALSE;
900 }
901 
902 
903 gboolean
gimv_thumb_view_thumb_key_press_cb(GtkWidget * widget,GdkEventKey * event,GimvThumb * thumb)904 gimv_thumb_view_thumb_key_press_cb (GtkWidget *widget,
905                                     GdkEventKey *event,
906                                     GimvThumb *thumb)
907 {
908    guint keyval, popup_key = 0;
909    GdkModifierType modval, popup_mod = 0;
910 
911    g_return_val_if_fail (event, FALSE);
912 
913    keyval = event->keyval;
914    modval = event->state;
915 
916    if (akey.common_popup_menu || *akey.common_popup_menu)
917       gtk_accelerator_parse (akey.common_popup_menu, &popup_key, &popup_mod);
918 
919    if (keyval == popup_key && (!popup_mod || (modval & popup_mod))) {
920       GimvThumbView *tv = gimv_thumb_get_parent_thumbview (thumb);
921       g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), FALSE);
922       gimv_thumb_view_popup_menu (tv, NULL, NULL);
923       return TRUE;
924    }
925 
926    return FALSE;
927 }
928 
929 
930 gboolean
gimv_thumb_view_thumb_key_release_cb(GtkWidget * widget,GdkEventKey * event,GimvThumb * thumb)931 gimv_thumb_view_thumb_key_release_cb (GtkWidget *widget,
932                                       GdkEventKey *event,
933                                       GimvThumb *thumb)
934 {
935    g_return_val_if_fail (event, FALSE);
936    return FALSE;
937 }
938 
939 
940 gboolean
gimv_thumb_view_motion_notify_cb(GtkWidget * widget,GdkEventMotion * event,GimvThumb * thumb)941 gimv_thumb_view_motion_notify_cb (GtkWidget *widget,
942                                   GdkEventMotion *event,
943                                   GimvThumb *thumb)
944 {
945    GimvThumbView *tv;
946    GimvThumbWin *tw;
947    gint dx, dy;
948 
949    g_return_val_if_fail (event, FALSE);
950 
951    if (thumb) {
952       tv = gimv_thumb_get_parent_thumbview (thumb);
953       g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), FALSE);
954 
955       tw = tv->tw;
956       g_return_val_if_fail (GIMV_IS_THUMB_WIN (tw), FALSE);
957    }
958 
959    if (!pressed)
960       return FALSE;
961 
962    dx = event->x - drag_start_x;
963    dy = event->y - drag_start_y;
964 
965    if (!dragging && (abs (dx) > 2 || abs (dy) > 2))
966       dragging = TRUE;
967 
968    /* return TRUE; */
969    return FALSE;
970 }
971 
972 
973 void
gimv_thumb_view_drag_begin_cb(GtkWidget * widget,GdkDragContext * context,gpointer data)974 gimv_thumb_view_drag_begin_cb (GtkWidget *widget,
975                                GdkDragContext *context,
976                                gpointer data)
977 {
978    GimvThumbView *tv = data;
979    GimvThumb *thumb;
980    GList *thumblist;
981    GdkPixmap *pixmap;
982    GdkBitmap *mask;
983    GdkColormap *colormap;
984 
985    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv) && widget);
986 
987    thumblist = gimv_thumb_view_get_selection_list (tv);
988    if (!thumblist) return;
989 
990    thumb = thumblist->data;
991    gimv_thumb_get_icon (thumb, &pixmap, &mask);
992    if (g_list_length (thumblist) == 1 && pixmap) {
993       colormap = gdk_colormap_get_system ();
994       gtk_drag_set_icon_pixmap (context, colormap, pixmap, mask, -7, -7);
995    }
996 }
997 
998 
999 void
gimv_thumb_view_drag_data_get_cb(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * seldata,guint info,guint time,gpointer data)1000 gimv_thumb_view_drag_data_get_cb (GtkWidget *widget,
1001                                   GdkDragContext *context,
1002                                   GtkSelectionData *seldata,
1003                                   guint info,
1004                                   guint time,
1005                                   gpointer data)
1006 {
1007    GimvThumbView *tv = data;
1008    GList *thumblist;
1009    gchar *uri_list;
1010 
1011    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv) && widget);
1012 
1013    thumblist = gimv_thumb_view_get_selection_list (tv);
1014 
1015    if (!thumblist) {
1016       gtkutil_message_dialog (_("Error!!"), _("No files specified!!"),
1017                               GTK_WINDOW (tv->tw));
1018       return;
1019    }
1020 
1021    switch (info) {
1022    case TARGET_URI_LIST:
1023       if (!thumblist) return;
1024       uri_list = get_uri_list (thumblist);
1025       gtk_selection_data_set(seldata, seldata->target,
1026                              8, uri_list, strlen(uri_list));
1027       g_free (uri_list);
1028       break;
1029    default:
1030       break;
1031    }
1032 }
1033 
1034 
1035 void
gimv_thumb_view_drag_data_received_cb(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * seldata,guint info,guint time,gpointer data)1036 gimv_thumb_view_drag_data_received_cb (GtkWidget *widget,
1037                                        GdkDragContext *context,
1038                                        gint x, gint y,
1039                                        GtkSelectionData *seldata,
1040                                        guint info,
1041                                        guint time,
1042                                        gpointer data)
1043 {
1044    GimvThumbView *tv = data;
1045    FilesLoader *files;
1046    GList *list;
1047    GtkWidget *src_widget;
1048    GimvThumbView *src_tab = NULL, *dest_tab = NULL;
1049 
1050    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv) && widget);
1051 
1052    src_widget = gtk_drag_get_source_widget (context);
1053    if (src_widget == widget) return;
1054 
1055    if (src_widget)
1056       src_tab  = gtk_object_get_data (GTK_OBJECT (src_widget), "gimv-tab");
1057    dest_tab = gtk_object_get_data (GTK_OBJECT (widget), "gimv-tab");
1058    if (src_tab == dest_tab) return;
1059 
1060    if (tv->mode == GIMV_THUMB_VIEW_MODE_DIR) {
1061       gchar *dirname;
1062 
1063       if (tv->dnd_destdir)
1064          dirname = tv->dnd_destdir;
1065       else
1066          dirname = tv->priv->dirname;
1067 
1068       if (iswritable (dirname)) {
1069          dnd_file_operation (dirname, context, seldata,
1070                              time, tv->tw);
1071       } else {
1072          gchar error_message[BUF_SIZE], *dir_internal;
1073 
1074          dir_internal = charset_to_internal (dirname,
1075                                              conf.charset_filename,
1076                                              conf.charset_auto_detect_fn,
1077                                              conf.charset_filename_mode);
1078          g_snprintf (error_message, BUF_SIZE,
1079                      _("Permission denied: %s"),
1080                      dir_internal);
1081          gtkutil_message_dialog (_("Error!!"), error_message,
1082                                  GTK_WINDOW (tv->tw));
1083 
1084          g_free (dir_internal);
1085       }
1086       tv->dnd_destdir = NULL;
1087 
1088    } else if (tv->mode == GIMV_THUMB_VIEW_MODE_COLLECTION) {
1089       list = dnd_get_file_list (seldata->data, seldata->length);
1090       files = files_loader_new ();
1091       files->filelist = list;
1092       gimv_thumb_view_append_thumbnail (tv, files, FALSE);
1093       files_loader_delete (files);
1094    }
1095 }
1096 
1097 
1098 void
gimv_thumb_view_drag_end_cb(GtkWidget * widget,GdkDragContext * drag_context,gpointer data)1099 gimv_thumb_view_drag_end_cb (GtkWidget *widget, GdkDragContext *drag_context,
1100                              gpointer data)
1101 {
1102    GimvThumbView *tv = data;
1103 
1104    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
1105 
1106    if (conf.dnd_refresh_list_always) {
1107       /* gimv_thumb_view_refresh_list (tv); */
1108       /* to avoid gtk's bug, exec redraw after exit this callback function */
1109       gtk_idle_add (gimv_thumb_view_refresh_list_idle, tv);
1110    }
1111 }
1112 
1113 
1114 void
gimv_thumb_view_drag_data_delete_cb(GtkWidget * widget,GdkDragContext * drag_context,gpointer data)1115 gimv_thumb_view_drag_data_delete_cb (GtkWidget *widget,
1116                                      GdkDragContext *drag_context,
1117                                      gpointer data)
1118 {
1119    GimvThumbView *tv = data;
1120 
1121    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
1122 
1123    if (!conf.dnd_refresh_list_always)
1124       gimv_thumb_view_refresh_list (tv);
1125 }
1126 
1127 
1128 
1129 /******************************************************************************
1130  *
1131  *   Compare functions.
1132  *
1133  ******************************************************************************/
1134 /* sort by spel */
1135 static int
comp_func_spel(gconstpointer data1,gconstpointer data2)1136 comp_func_spel (gconstpointer data1, gconstpointer data2)
1137 {
1138    GimvThumb *thumb1, *thumb2;
1139    GimvThumbView *tv;
1140    const gchar *filename1, *filename2;
1141    gboolean ignore_dir;
1142    gint comp;
1143    GimvSortItem item;
1144    GimvSortFlag flags;
1145 
1146    thumb1 = (GimvThumb *) data1;
1147    thumb2 = (GimvThumb *) data2;
1148 
1149    filename1 = gimv_image_info_get_path (thumb1->info);
1150    filename2 = gimv_image_info_get_path (thumb2->info);
1151 
1152    tv = gimv_thumb_get_parent_thumbview (thumb1);
1153    g_return_val_if_fail(GIMV_IS_THUMB_VIEW (tv), 0);
1154    item = gimv_thumb_win_get_sort_type (tv->tw, &flags);
1155    ignore_dir = flags & GIMV_SORT_DIR_INSENSITIVE;
1156 
1157    if (!filename1 || !*filename1)
1158       return -1;
1159    else if (!filename2 || !*filename2)
1160       return 1;
1161 
1162    if (filename1 && !strcmp ("..", g_basename (filename1))) {
1163       comp = -1;
1164    } else if (filename2 && !strcmp ("..", g_basename (filename2))) {
1165       comp = 1;
1166    } else if (!ignore_dir && isdir (filename1) && !isdir (filename2)) {
1167       comp = -1;
1168    } else if (!ignore_dir && !isdir (filename1) && isdir (filename2)) {
1169       comp = 1;
1170    } else {
1171       if (flags & GIMV_SORT_CASE_INSENSITIVE)
1172          comp = g_strcasecmp ((gchar *) filename1, (gchar *) filename2);
1173       else
1174          comp = strcmp ((gchar *) filename1, (gchar *) filename2);
1175    }
1176 
1177    return comp;
1178 }
1179 
1180 
1181 /* sort by file size */
1182 static int
comp_func_size(gconstpointer data1,gconstpointer data2)1183 comp_func_size (gconstpointer data1, gconstpointer data2)
1184 {
1185    GimvThumb *thumb1, *thumb2;
1186    int retval;
1187 
1188    thumb1 = (GimvThumb *) data1;
1189    thumb2 = (GimvThumb *) data2;
1190 
1191    if (!thumb1)
1192       return -1;
1193    else if (!thumb2)
1194       return 1;
1195 
1196    retval = thumb1->info->st.st_size - thumb2->info->st.st_size;
1197 
1198    if (retval == 0)
1199       return comp_func_spel (data1, data2);
1200    else
1201       return retval;
1202 }
1203 
1204 
1205 /* sort by time of las access */
1206 static int
comp_func_atime(gconstpointer data1,gconstpointer data2)1207 comp_func_atime (gconstpointer data1, gconstpointer data2)
1208 {
1209    GimvThumb *thumb1, *thumb2;
1210    int retval;
1211 
1212    thumb1 = (GimvThumb *) data1;
1213    thumb2 = (GimvThumb *) data2;
1214 
1215    if (!thumb1)
1216       return -1;
1217    else if (!thumb2)
1218       return 1;
1219 
1220    retval = thumb1->info->st.st_atime - thumb2->info->st.st_atime;
1221 
1222    if (retval == 0)
1223       return comp_func_spel (data1, data2);
1224    else
1225       return retval;
1226 }
1227 
1228 
1229 /* sort by time of last modification */
1230 static int
comp_func_mtime(gconstpointer data1,gconstpointer data2)1231 comp_func_mtime (gconstpointer data1, gconstpointer data2)
1232 {
1233    GimvThumb *thumb1, *thumb2;
1234    int retval;
1235 
1236    thumb1 = (GimvThumb *) data1;
1237    thumb2 = (GimvThumb *) data2;
1238 
1239    if (!thumb1)
1240       return -1;
1241    else if (!thumb2)
1242       return 1;
1243 
1244    retval = thumb1->info->st.st_mtime - thumb2->info->st.st_mtime;
1245 
1246    if (retval == 0)
1247       return comp_func_spel (data1, data2);
1248    else
1249       return retval;
1250 }
1251 
1252 
1253 /* sort by time of last change */
1254 static int
comp_func_ctime(gconstpointer data1,gconstpointer data2)1255 comp_func_ctime (gconstpointer data1, gconstpointer data2)
1256 {
1257    GimvThumb *thumb1, *thumb2;
1258    int retval;
1259 
1260    thumb1 = (GimvThumb *) data1;
1261    thumb2 = (GimvThumb *) data2;
1262 
1263    if (!thumb1)
1264       return -1;
1265    else if (!thumb2)
1266       return 1;
1267 
1268    retval = thumb1->info->st.st_ctime - thumb2->info->st.st_ctime;
1269 
1270    if (retval == 0)
1271       return comp_func_spel (data1, data2);
1272    else
1273       return retval;
1274 }
1275 
1276 
1277 /* sort by time of file name extension */
1278 static int
comp_func_type(gconstpointer data1,gconstpointer data2)1279 comp_func_type (gconstpointer data1, gconstpointer data2)
1280 {
1281    GimvThumb *thumb1, *thumb2;
1282    const gchar *file1, *file2, *ext1, *ext2;
1283    int retval;
1284 
1285    thumb1 = (GimvThumb *) data1;
1286    thumb2 = (GimvThumb *) data2;
1287 
1288    if (!thumb1)
1289       return -1;
1290    else if (!thumb2)
1291       return 1;
1292 
1293    file1 = gimv_image_info_get_path (thumb1->info);
1294    file2 = gimv_image_info_get_path (thumb2->info);
1295 
1296    if (!file1 || !*file1)
1297       return -1;
1298    else if (!file2 || !*file2)
1299       return 1;
1300 
1301    ext1 = strrchr (file1, '.');
1302    if (ext1 && *ext1)
1303       ext1++;
1304    else
1305       ext1 = NULL;
1306 
1307    ext2 = strrchr (file2, '.');
1308    if (ext2 && *ext2)
1309       ext2++;
1310    else
1311       ext2 = NULL;
1312 
1313    if ((!ext1 || !*ext1) && (!ext2 || !*ext2)) {
1314       return g_strcasecmp (file1, file2);
1315    } else if (!ext1 || !*ext1) {
1316       return -1;
1317    } else if (!ext2 || !*ext2) {
1318       return 1;
1319    }
1320 
1321    retval = g_strcasecmp (ext1, ext2);
1322 
1323    if (retval == 0)
1324       return comp_func_spel (data1, data2);
1325    else
1326       return retval;
1327 }
1328 
1329 
1330 /* sort by image width */
1331 static int
comp_func_width(gconstpointer data1,gconstpointer data2)1332 comp_func_width (gconstpointer data1, gconstpointer data2)
1333 {
1334    GimvThumb *thumb1, *thumb2;
1335    int retval;
1336 
1337    thumb1 = (GimvThumb *) data1;
1338    thumb2 = (GimvThumb *) data2;
1339 
1340    if (!thumb1)
1341       return -1;
1342    else if (!thumb2)
1343       return 1;
1344 
1345    retval = thumb1->info->width - thumb2->info->width;
1346 
1347    if (retval == 0)
1348       return comp_func_spel (data1, data2);
1349    else
1350       return retval;
1351 }
1352 
1353 
1354 /* sort by image height */
1355 static int
comp_func_height(gconstpointer data1,gconstpointer data2)1356 comp_func_height (gconstpointer data1, gconstpointer data2)
1357 {
1358    GimvThumb *thumb1, *thumb2;
1359    int retval;
1360 
1361    thumb1 = (GimvThumb *) data1;
1362    thumb2 = (GimvThumb *) data2;
1363 
1364    if (!thumb1)
1365       return -1;
1366    else if (!thumb2)
1367       return 1;
1368 
1369    retval = thumb1->info->height - thumb2->info->height;
1370 
1371    if (retval == 0)
1372       return comp_func_spel (data1, data2);
1373    else
1374       return retval;
1375 }
1376 
1377 
1378 /* sort by image width */
1379 static int
comp_func_area(gconstpointer data1,gconstpointer data2)1380 comp_func_area (gconstpointer data1, gconstpointer data2)
1381 {
1382    GimvThumb *thumb1, *thumb2;
1383    int retval, area1, area2;
1384 
1385    thumb1 = (GimvThumb *) data1;
1386    thumb2 = (GimvThumb *) data2;
1387 
1388    if (!thumb1)
1389       return -1;
1390    else if (!thumb2)
1391       return 1;
1392 
1393    area1 = thumb1->info->width * thumb1->info->height;
1394    if (thumb1->info->width < 0 && thumb1->info->height < 0)
1395       area1 = 0 - area1;
1396    area2 = thumb2->info->width * thumb2->info->height;
1397    if (thumb2->info->width < 0 && thumb2->info->height < 0)
1398       area2 = 0 - area2;
1399 
1400    retval = area1 - area2;
1401 
1402    if (retval == 0)
1403       return comp_func_spel (data1, data2);
1404    else
1405       return retval;
1406 }
1407 
1408 
1409 
1410 /******************************************************************************
1411  *
1412  *   Other Private Functions.
1413  *
1414  ******************************************************************************/
1415 static gint
progress_timeout(gpointer data)1416 progress_timeout (gpointer data)
1417 {
1418    gfloat new_val;
1419    GtkAdjustment *adj;
1420 
1421    adj = GTK_PROGRESS (data)->adjustment;
1422 
1423    new_val = adj->value + 1;
1424    if (new_val > adj->upper)
1425       new_val = adj->lower;
1426 
1427    gtk_progress_set_value (GTK_PROGRESS (data), new_val);
1428 
1429    return (TRUE);
1430 }
1431 
1432 
1433 static gboolean
gimv_thumb_view_extract_archive_file(GimvThumb * thumb)1434 gimv_thumb_view_extract_archive_file (GimvThumb *thumb)
1435 {
1436    GimvThumbWin *tw;
1437    GimvThumbView *tv;
1438    FilesLoader *files;
1439    guint timer;
1440    gboolean success;
1441 
1442    g_return_val_if_fail (GIMV_IS_THUMB (thumb), FALSE);
1443 
1444    tv = gimv_thumb_get_parent_thumbview (thumb);
1445    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), FALSE);
1446 
1447    if (tv->mode != GIMV_THUMB_VIEW_MODE_ARCHIVE) return FALSE;
1448 
1449    tw = tv->tw;
1450    g_return_val_if_fail (tw, FALSE);
1451 
1452    files = files_loader_new ();
1453    tv->status = GIMV_THUMB_VIEW_STATUS_LOADING;
1454    files->archive = gimv_image_info_get_archive (thumb->info);
1455    if (files->archive)
1456       fr_archive_ref (files->archive);
1457 
1458    /* set progress bar */
1459    gtk_progress_set_activity_mode (GTK_PROGRESS (tw->progressbar), TRUE);
1460    timer = gtk_timeout_add (50, (GtkFunction)progress_timeout, tw->progressbar);
1461 
1462    /* extract */
1463    success = gimv_image_info_extract_archive (thumb->info);
1464 
1465    /* unset progress bar */
1466    gtk_timeout_remove (timer);
1467    gtk_progress_set_activity_mode (GTK_PROGRESS (tw->progressbar), FALSE);
1468    gtk_progress_bar_update (GTK_PROGRESS_BAR(tw->progressbar), 0.0);
1469 
1470    tv->status = GIMV_THUMB_VIEW_STATUS_NORMAL;
1471    files_loader_delete (files);
1472 
1473    return success;
1474 }
1475 
1476 
1477 static void
gimv_thumb_view_append_thumb_data(GimvThumbView * tv,GimvThumb * thumb)1478 gimv_thumb_view_append_thumb_data (GimvThumbView *tv, GimvThumb *thumb)
1479 {
1480    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
1481 
1482    tv->thumblist = g_list_append (tv->thumblist, thumb);
1483 
1484    /* update file num info */
1485    tv->tw->filenum++;
1486    tv->tw->filesize += thumb->info->st.st_size;
1487    tv->filenum++;
1488    tv->filesize += thumb->info->st.st_size;
1489 }
1490 
1491 
1492 static void
gimv_thumb_view_remove_thumb_data(GimvThumbView * tv,GimvThumb * thumb)1493 gimv_thumb_view_remove_thumb_data (GimvThumbView *tv, GimvThumb *thumb)
1494 {
1495    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
1496    g_return_if_fail (g_list_find (tv->thumblist, thumb));
1497 
1498    /* update file num info */
1499    tv->tw->filenum--;
1500    tv->tw->filesize -= thumb->info->st.st_size;
1501    tv->filenum--;
1502    tv->filesize -= thumb->info->st.st_size;
1503 
1504    tv->thumblist = g_list_remove (tv->thumblist, thumb);
1505 }
1506 
1507 
1508 static void
gimv_thumb_view_insert_thumb_frames(GimvThumbView * tv,GList * filelist)1509 gimv_thumb_view_insert_thumb_frames (GimvThumbView *tv, GList *filelist)
1510 {
1511    GimvThumb  *thumb;
1512    GList *node, *new_thumb_list = NULL;
1513 
1514    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
1515    if (!filelist) return;
1516    g_return_if_fail (tv->vfuncs);
1517 
1518    for (node = filelist; node; node = g_list_next (node)) {
1519       gchar *filename = node->data;
1520 
1521       if (file_exists(node->data)) {
1522          GimvImageInfo *info = gimv_image_info_get (filename);
1523 
1524          thumb = gimv_thumb_new (info);
1525          gimv_image_info_unref (info);
1526          gimv_thumb_set_parent_thumbview (thumb, tv);
1527 
1528          gimv_thumb_view_append_thumb_data (tv, thumb);
1529 
1530          new_thumb_list = g_list_append (new_thumb_list, thumb);
1531       }
1532    }
1533 
1534    gimv_thumb_view_sort_data (tv);
1535 
1536    for (node = new_thumb_list; node; node = g_list_next (node))
1537       tv->vfuncs->insert_thumb (tv, node->data, tv->summary_mode);
1538    g_list_free(new_thumb_list);
1539 }
1540 
1541 
1542 static void
gimv_thumb_view_insert_thumb_frames_from_archive(GimvThumbView * tv,FRArchive * archive)1543 gimv_thumb_view_insert_thumb_frames_from_archive (GimvThumbView *tv,
1544                                                   FRArchive *archive)
1545 {
1546    GimvThumb  *thumb;
1547    FRCommand *command;
1548    GList *node, *new_thumb_list = NULL;
1549 
1550    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
1551    if (!archive) return;
1552    g_return_if_fail (tv->vfuncs);
1553 
1554    command = archive->command;
1555    g_return_if_fail (command);
1556 
1557    for (node = command->file_list; node; node = g_list_next (node)) {
1558       GimvImageInfo *info = node->data;
1559 
1560       if (!info) continue;
1561 
1562       /* detect filename extension */
1563       if (conf.detect_filetype_by_ext
1564           && !gimv_image_detect_type_by_ext (info->filename)
1565           && !fr_archive_utils_get_file_name_ext (info->filename))
1566       {
1567          continue;
1568       }
1569 
1570       thumb = gimv_thumb_new (info);
1571       gimv_thumb_set_parent_thumbview (thumb, tv);
1572 
1573       gimv_thumb_view_append_thumb_data (tv, thumb);
1574 
1575       new_thumb_list = g_list_append (new_thumb_list, thumb);
1576    }
1577 
1578    gimv_thumb_view_sort_data (tv);
1579 
1580    for (node = new_thumb_list; node; node = g_list_next (node))
1581       tv->vfuncs->insert_thumb (tv, node->data, tv->summary_mode);
1582    g_list_free(new_thumb_list);
1583 }
1584 
1585 
1586 static gchar *
get_uri_list(GList * thumblist)1587 get_uri_list (GList *thumblist)
1588 {
1589    gchar *path;
1590    gchar *uri;
1591    GList *node;
1592    GimvThumb *thumb;
1593 
1594    if (!thumblist) return NULL;
1595 
1596    uri = g_strdup ("");
1597    node = thumblist;
1598    while (node) {
1599       gchar *filename = NULL;
1600 
1601       thumb = node->data;
1602 
1603       filename = gimv_image_info_get_path_with_archive (thumb->info);
1604       path = g_strconcat (uri, "file://", filename, "\r\n", NULL);
1605       g_free (filename);
1606       g_free (uri);
1607       uri = g_strdup (path);
1608       g_free (path);
1609 
1610       node = g_list_next (node);
1611    }
1612 
1613    return uri;
1614 }
1615 
1616 
1617 typedef struct ChangeImageData_Tag
1618 {
1619    GimvThumbWin  *tw;
1620    GimvImageWin  *iw;
1621    GimvImageView *iv;
1622    GimvThumb     *thumb;
1623 } ChangeImageData;
1624 
1625 
1626 static gboolean
change_image_idle(gpointer user_data)1627 change_image_idle (gpointer user_data)
1628 {
1629    ChangeImageData *data = user_data;
1630 
1631    if (data->iw) {
1632       gimv_image_win_change_image (data->iw, data->thumb->info);
1633    } else {
1634       gimv_image_view_change_image (data->iv, data->thumb->info);
1635    }
1636 
1637    return FALSE;
1638 }
1639 
1640 
1641 static GList *
next_image(GimvImageView * iv,gpointer list_owner,GList * current,gpointer data)1642 next_image (GimvImageView *iv, gpointer list_owner,
1643             GList *current, gpointer data)
1644 {
1645    GimvImageWin *iw = data;
1646    GList *next, *node;
1647    GimvThumb *thumb;
1648    GimvThumbView *tv = list_owner;
1649    GimvThumbWin *tw;
1650    ChangeImageData *change_data;
1651 
1652    g_return_val_if_fail (GIMV_IS_IMAGE_VIEW (iv), NULL);
1653    g_return_val_if_fail (current, NULL);
1654 
1655    node = g_list_find (GimvThumbViewList, tv);
1656    g_return_val_if_fail (node, NULL);
1657 
1658    node = g_list_find (tv->thumblist, current->data);
1659    g_return_val_if_fail (node, NULL);
1660 
1661    next = g_list_next (current);
1662    if (!next) next = g_list_first (current);
1663    g_return_val_if_fail (next, NULL);
1664 
1665    while (next && next != current) {
1666       GimvThumb *nthumb = next->data;
1667       GList *temp;
1668 
1669       if (!gimv_image_info_is_dir (nthumb->info)
1670           && !gimv_image_info_is_archive (nthumb->info))
1671       {
1672          break;
1673       }
1674 
1675       temp = g_list_next (next);
1676       if (!temp)
1677          next = g_list_first (next);
1678       else
1679          next = temp;
1680    }
1681    if (!next) next = current;
1682 
1683    thumb = next->data;
1684    g_return_val_if_fail (GIMV_IS_THUMB (thumb), NULL);
1685 
1686    tw = tv->tw;
1687    g_return_val_if_fail (GIMV_IS_THUMB_WIN (tw), NULL);
1688 
1689    gimv_thumb_view_set_selection_all (tv, FALSE);
1690    gimv_thumb_view_set_selection (thumb, TRUE);
1691    gimv_thumb_view_adjust (tv, thumb);
1692 
1693    if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) {
1694       gboolean success =  gimv_thumb_view_extract_archive_file (thumb);
1695       g_return_val_if_fail (success, next);
1696    }
1697 
1698    change_data = g_new0 (ChangeImageData, 1);
1699    change_data->tw = tw;
1700    change_data->iw = iw;
1701    change_data->iv = iv;
1702    change_data->thumb = thumb;
1703    gtk_idle_add_full (GTK_PRIORITY_DEFAULT,
1704                       change_image_idle,
1705                       NULL,
1706                       change_data,
1707                       (GtkDestroyNotify) g_free);
1708 
1709    if (!iw)
1710       gimv_comment_view_change_file (tw->cv, thumb->info);
1711 
1712    return next;
1713 }
1714 
1715 
1716 static GList *
prev_image(GimvImageView * iv,gpointer list_owner,GList * current,gpointer data)1717 prev_image (GimvImageView *iv, gpointer list_owner,
1718             GList *current, gpointer data)
1719 {
1720    GimvImageWin *iw = data;
1721    GList *prev, *node;
1722    GimvThumb *thumb;
1723    GimvThumbView *tv = list_owner;
1724    GimvThumbWin *tw;
1725    ChangeImageData *change_data;
1726 
1727    g_return_val_if_fail (GIMV_IS_IMAGE_VIEW (iv), NULL);
1728    g_return_val_if_fail (current, NULL);
1729 
1730    node = g_list_find (GimvThumbViewList, tv);
1731    g_return_val_if_fail (node, NULL);
1732 
1733    node = g_list_find (tv->thumblist, current->data);
1734    g_return_val_if_fail (node, NULL);
1735 
1736    prev = g_list_previous (current);
1737    if (!prev) prev = g_list_last (current);
1738    g_return_val_if_fail (prev, NULL);
1739 
1740    while (prev && prev != current) {
1741       GimvThumb *nthumb = prev->data;
1742       GList *temp;
1743 
1744       if (!gimv_image_info_is_dir (nthumb->info)
1745           && !gimv_image_info_is_archive (nthumb->info))
1746       {
1747          break;
1748       }
1749 
1750       temp = g_list_previous (prev);
1751       if (!temp)
1752          prev = g_list_last (prev);
1753       else
1754          prev = temp;
1755    }
1756    if (!prev) prev = current;
1757 
1758    thumb = prev->data;
1759    g_return_val_if_fail (GIMV_IS_THUMB (thumb), NULL);
1760 
1761    tw = tv->tw;
1762    g_return_val_if_fail (GIMV_IS_THUMB_WIN (tw), NULL);
1763 
1764    gimv_thumb_view_set_selection_all (tv, FALSE);
1765    gimv_thumb_view_set_selection (thumb, TRUE);
1766    gimv_thumb_view_adjust (tv, thumb);
1767 
1768    if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) {
1769       gboolean success = gimv_thumb_view_extract_archive_file (thumb);
1770       g_return_val_if_fail (success, prev);
1771    }
1772 
1773    change_data = g_new0 (ChangeImageData, 1);
1774    change_data->tw = tw;
1775    change_data->iw = iw;
1776    change_data->iv = iv;
1777    change_data->thumb = thumb;
1778    gtk_idle_add_full (GTK_PRIORITY_DEFAULT,
1779                       change_image_idle,
1780                       NULL,
1781                       change_data,
1782                       (GtkDestroyNotify) g_free);
1783 
1784    if (!iw)
1785       gimv_comment_view_change_file (tw->cv, thumb->info);
1786 
1787    return prev;
1788 }
1789 
1790 
1791 static GList *
nth_image(GimvImageView * iv,gpointer list_owner,GList * current,guint nth,gpointer data)1792 nth_image (GimvImageView *iv,
1793            gpointer list_owner,
1794            GList *current,
1795            guint nth,
1796            gpointer data)
1797 {
1798    GimvImageWin *iw = data;
1799    GList *node;
1800    GimvThumb *thumb;
1801    GimvThumbView *tv = list_owner;
1802    GimvThumbWin *tw;
1803    ChangeImageData *change_data;
1804 
1805    /* check check chek ... */
1806    g_return_val_if_fail (GIMV_IS_IMAGE_VIEW (iv), NULL);
1807    g_return_val_if_fail (current, NULL);
1808 
1809    node = g_list_find (GimvThumbViewList, tv);
1810    g_return_val_if_fail (node, NULL);
1811 
1812    node = g_list_find (tv->thumblist, current->data);
1813    g_return_val_if_fail (node, NULL);
1814 
1815    tw = tv->tw;
1816    g_return_val_if_fail (tw, NULL);
1817 
1818    /* get nth of list */
1819    node = g_list_nth (tv->thumblist, nth);
1820    g_return_val_if_fail (node, NULL);
1821 
1822    thumb = node->data;
1823    g_return_val_if_fail (GIMV_IS_THUMB (thumb), NULL);
1824 
1825    gimv_thumb_view_set_selection_all (tv, FALSE);
1826    gimv_thumb_view_set_selection (thumb, TRUE);
1827    gimv_thumb_view_adjust (tv, thumb);
1828 
1829    /* show image */
1830    if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) {
1831       gboolean success = gimv_thumb_view_extract_archive_file (thumb);
1832       g_return_val_if_fail (success, node);
1833    }
1834 
1835    change_data = g_new0 (ChangeImageData, 1);
1836    change_data->tw = tw;
1837    change_data->iw = iw;
1838    change_data->iv = iv;
1839    change_data->thumb = thumb;
1840    gtk_idle_add_full (GTK_PRIORITY_DEFAULT,
1841                       change_image_idle,
1842                       NULL,
1843                       change_data,
1844                       (GtkDestroyNotify) g_free);
1845 
1846    if (!iw)
1847       gimv_comment_view_change_file (tw->cv, thumb->info);
1848 
1849    return node;
1850 }
1851 
1852 
1853 static void
cb_imageview_thumbnail_created(GimvImageView * iv,GimvImageInfo * info,GimvThumbView * tv)1854 cb_imageview_thumbnail_created (GimvImageView *iv,
1855                                 GimvImageInfo *info,
1856                                 GimvThumbView *tv)
1857 {
1858    GList *node;
1859    GimvThumb *thumb = NULL;
1860 
1861    g_return_if_fail (GIMV_IS_IMAGE_VIEW (iv));
1862    g_return_if_fail (info);
1863    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
1864 
1865    node = tv->thumblist;
1866    while (node) {
1867       thumb = node->data;
1868       node = g_list_next (node);
1869 
1870       if (gimv_image_info_is_same (thumb->info, info))
1871          break;
1872       else
1873          thumb = NULL;
1874    }
1875 
1876    if (thumb)
1877       gimv_thumb_view_refresh_thumbnail (tv, thumb, LOAD_CACHE);
1878 }
1879 
1880 
1881 static void
remove_list(GimvImageView * iv,gpointer list_owner,gpointer data)1882 remove_list (GimvImageView *iv, gpointer list_owner, gpointer data)
1883 {
1884    GimvThumbView *tv = list_owner;
1885    GList *node;
1886 
1887    g_return_if_fail (GIMV_IS_IMAGE_VIEW (iv));
1888    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
1889 
1890    gtk_signal_disconnect_by_func (
1891       GTK_OBJECT (iv),
1892       GTK_SIGNAL_FUNC (cb_imageview_thumbnail_created),
1893       tv);
1894 
1895    node = g_list_find (GimvThumbViewList, tv);
1896    if (!node) return;
1897 
1898    if (tv->priv)
1899       tv->priv->related_image_view
1900          = g_list_remove (tv->priv->related_image_view, iv);
1901 }
1902 
1903 
1904 static void
gimv_thumb_view_button_action(GimvThumbView * tv,GimvThumb * thumb,GdkEventButton * event,gint num)1905 gimv_thumb_view_button_action (GimvThumbView *tv,
1906                                GimvThumb *thumb,
1907                                GdkEventButton *event,
1908                                gint num)
1909 {
1910    GimvThumbWin *tw;
1911 
1912    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
1913 
1914    tw = tv->tw;
1915    g_return_if_fail (GIMV_IS_THUMB_WIN (tw));
1916 
1917    if (gimv_image_info_is_dir (thumb->info)
1918        || gimv_image_info_is_archive (thumb->info))
1919    {
1920       switch (abs (num)) {
1921       case ACTION_OPEN_AUTO:
1922          num = ACTION_OPEN_AUTO_FORCE;
1923          break;
1924       case ACTION_OPEN_IN_PREVIEW:
1925          num = ACTION_OPEN_IN_PREVIEW_FORCE;
1926          break;
1927       case ACTION_OPEN_IN_SHARED_WIN:
1928          num = ACTION_OPEN_IN_SHARED_WIN_FORCE;
1929          break;
1930       default:
1931          break;
1932       }
1933    }
1934 
1935    switch (abs (num)) {
1936    case ACTION_POPUP:
1937       gimv_thumb_view_popup_menu (tv, thumb, event);
1938 
1939       /* FIXME */
1940       if (GIMV_IS_SCROLLED (GTK_BIN (tv->container)->child))
1941          gimv_scrolled_stop_auto_scroll (GIMV_SCROLLED (GTK_BIN (tv->container)->child));
1942 
1943       break;
1944 
1945    case ACTION_OPEN_AUTO:
1946       if (tw->show_preview || gimv_image_win_get_shared_window())
1947          gimv_thumb_view_open_image (tv, thumb,
1948                                      GIMV_THUMB_VIEW_OPEN_IMAGE_AUTO);
1949       break;
1950 
1951    case ACTION_OPEN_AUTO_FORCE:
1952       gimv_thumb_view_open_image (tv, thumb,
1953                                   GIMV_THUMB_VIEW_OPEN_IMAGE_AUTO);
1954       break;
1955 
1956    case ACTION_OPEN_IN_PREVIEW:
1957       if (tw->show_preview)
1958          gimv_thumb_view_open_image (tv, thumb,
1959                                      GIMV_THUMB_VIEW_OPEN_IMAGE_PREVIEW);
1960       break;
1961 
1962    case ACTION_OPEN_IN_PREVIEW_FORCE:
1963       if (!tw->show_preview)
1964          gimv_thumb_win_open_preview (tw);
1965       gimv_thumb_view_open_image (tv, thumb,
1966                                   GIMV_THUMB_VIEW_OPEN_IMAGE_PREVIEW);
1967       break;
1968 
1969    case ACTION_OPEN_IN_NEW_WIN:
1970       gimv_thumb_view_open_image (tv, thumb,
1971                                   GIMV_THUMB_VIEW_OPEN_IMAGE_NEW_WIN);
1972       break;
1973 
1974    case ACTION_OPEN_IN_SHARED_WIN:
1975       if (gimv_image_win_get_shared_window())
1976          gimv_thumb_view_open_image (tv, thumb,
1977                                      GIMV_THUMB_VIEW_OPEN_IMAGE_SHARED_WIN);
1978       break;
1979 
1980    case ACTION_OPEN_IN_SHARED_WIN_FORCE:
1981       gimv_thumb_view_open_image (tv, thumb,
1982                                   GIMV_THUMB_VIEW_OPEN_IMAGE_SHARED_WIN);
1983       break;
1984 
1985    default:
1986       break;
1987    }
1988 }
1989 
1990 
1991 static GtkWidget *
create_progs_submenu(GimvThumbView * tv)1992 create_progs_submenu (GimvThumbView *tv)
1993 {
1994    GtkWidget *menu;
1995    GtkWidget *menu_item;
1996    gint i, conf_num = sizeof (conf.progs) / sizeof (conf.progs[0]);
1997    gchar **pair;
1998 
1999    menu = gtk_menu_new();
2000 
2001    /* count items num */
2002    for (i = 0; i < conf_num; i++) {
2003       if (!conf.progs[i]) continue;
2004 
2005       pair = g_strsplit (conf.progs[i], ",", 3);
2006 
2007       if (pair[0] && pair[1]) {
2008          gchar *label;
2009 
2010          if (pair[2] && !strcasecmp (pair[2], "TRUE"))
2011             label = g_strconcat (pair[0], "...", NULL);
2012          else
2013             label = g_strdup (pair[0]);
2014 
2015          menu_item = gtk_menu_item_new_with_label (label);
2016          gtk_object_set_data (GTK_OBJECT (menu_item), "num", GINT_TO_POINTER (i));
2017          gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
2018                              GTK_SIGNAL_FUNC (cb_open_image_by_external), tv);
2019          gtk_menu_append (GTK_MENU (menu), menu_item);
2020          gtk_widget_show (menu_item);
2021 
2022          g_free (label);
2023       }
2024 
2025       g_strfreev (pair);
2026    }
2027 
2028    return menu;
2029 }
2030 
2031 
2032 void
gimv_thumb_view_open_image(GimvThumbView * tv,GimvThumb * thumb,gint type)2033 gimv_thumb_view_open_image (GimvThumbView *tv, GimvThumb *thumb, gint type)
2034 {
2035    GimvThumbWin *tw;
2036    GimvImageWin *iw = NULL;
2037    GimvImageView   *iv = NULL;
2038    GList *current, *node;
2039    const gchar *image_name, *ext;
2040    gchar *filename, *tmpstr;
2041 
2042    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv) && thumb);
2043 
2044    tw = tv->tw;
2045    g_return_if_fail (GIMV_IS_THUMB_WIN (tw));
2046 
2047    image_name = gimv_image_info_get_path (thumb->info);
2048    g_return_if_fail (image_name && *image_name);
2049    filename = g_strdup (image_name);
2050 
2051    if (!strcmp ("..", g_basename (filename))) {
2052       tmpstr = filename;
2053       filename = g_dirname (filename);
2054       g_free (tmpstr);
2055       if (filename) {
2056          tmpstr = filename;
2057          filename = g_dirname (filename);
2058          g_free (tmpstr);
2059       }
2060       tmpstr = NULL;
2061    }
2062 
2063    /* open directory */
2064    if (isdir (filename)) {
2065 #warning FIXME!!!! Use this tab?
2066       open_dir_images (filename, tw, NULL, LOAD_CACHE, conf.scan_dir_recursive);
2067       g_free (filename);
2068       return;
2069    }
2070 
2071    /* extract image in archive */
2072    if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) {
2073       gboolean success = gimv_thumb_view_extract_archive_file (thumb);
2074       if (!success) {
2075          g_free (filename);
2076          return;
2077       }
2078    }
2079 
2080    /* open archive */
2081    ext = fr_archive_utils_get_file_name_ext (filename);
2082    if (ext) {
2083       open_archive_images (filename, tw, NULL, LOAD_CACHE);
2084       g_free (filename);
2085       return;
2086    }
2087 
2088    /* open the image */
2089    if ((type == GIMV_THUMB_VIEW_OPEN_IMAGE_AUTO && tw->show_preview)
2090        || type == GIMV_THUMB_VIEW_OPEN_IMAGE_PREVIEW)
2091    {
2092       iv = tw->iv;
2093       gimv_comment_view_change_file (tw->cv, thumb->info);
2094       gimv_image_view_change_image (iv, thumb->info);
2095 
2096    } else if (gimv_image_win_get_shared_window() &&
2097               ((type == GIMV_THUMB_VIEW_OPEN_IMAGE_AUTO && !conf.imgwin_open_new_win)
2098                ||type == GIMV_THUMB_VIEW_OPEN_IMAGE_SHARED_WIN))
2099    {
2100       iw = gimv_image_win_open_shared_window (thumb->info);
2101       if (iw)
2102          iv = iw->iv;
2103 
2104    } else {
2105       if (type == GIMV_THUMB_VIEW_OPEN_IMAGE_SHARED_WIN) {
2106          iw = gimv_image_win_open_shared_window (thumb->info);
2107       } else if (type == GIMV_THUMB_VIEW_OPEN_IMAGE_AUTO) {
2108          iw = gimv_image_win_open_window_auto (thumb->info);
2109       } else {
2110          iw = gimv_image_win_open_window (thumb->info);
2111       }
2112       if (iw)
2113          iv = iw->iv;
2114    }
2115 
2116    if (iv && g_list_find (gimv_image_view_get_list(), iv)) {
2117       current = g_list_find (tv->thumblist, thumb);
2118       gimv_image_view_set_list (iv, tv->thumblist, current,
2119                                 (gpointer) tv,
2120                                 next_image,
2121                                 prev_image,
2122                                 nth_image,
2123                                 remove_list,
2124                                 iw);
2125       gtk_signal_connect (GTK_OBJECT (iv), "thumbnail_created",
2126                           GTK_SIGNAL_FUNC (cb_imageview_thumbnail_created), tv);
2127       node = g_list_find (tv->priv->related_image_view, iv);
2128       if (!node) {
2129          gint num;
2130          tv->priv->related_image_view
2131             = g_list_append (tv->priv->related_image_view, iv);
2132          num = g_list_length (tv->priv->related_image_view);
2133       }
2134    }
2135 
2136    g_free (filename);
2137 }
2138 
2139 
2140 void
gimv_thumb_view_popup_menu(GimvThumbView * tv,GimvThumb * thumb,GdkEventButton * event)2141 gimv_thumb_view_popup_menu (GimvThumbView *tv, GimvThumb *thumb,
2142                             GdkEventButton *event)
2143 {
2144    GtkWidget *popup_menu, *progs_submenu, *scripts_submenu;
2145    GList *thumblist = NULL, *node;
2146    guint n_menu_items;
2147    GtkItemFactory *ifactory;
2148    GtkWidget *menuitem;
2149    gchar *dirname;
2150    guint button;
2151    guint32 time;
2152    GtkMenuPositionFunc pos_fn = NULL;
2153 
2154    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
2155 
2156    if (event) {
2157       button = event->button;
2158       time = gdk_event_get_time ((GdkEvent *) event);
2159    } else {
2160       button = 0;
2161       time = GDK_CURRENT_TIME;
2162       pos_fn = menu_calc_popup_position;
2163    }
2164 
2165    if (tv->popup_menu) {
2166       gtk_widget_unref (tv->popup_menu);
2167       tv->popup_menu = NULL;
2168    }
2169 
2170    thumblist = node = gimv_thumb_view_get_selection_list (tv);
2171    if (!thumblist) return;
2172 
2173    if (!thumb) thumb = thumblist->data;
2174    g_return_if_fail (GIMV_IS_THUMB (thumb));
2175 
2176    /* create popup menu */
2177    n_menu_items = sizeof(thumb_button_popup_items)
2178       / sizeof(thumb_button_popup_items[0]) - 1;
2179    popup_menu = menu_create_items(GTK_WIDGET (tv->tw),
2180                                   thumb_button_popup_items, n_menu_items,
2181                                   "<ThumbnailButtonPop>", tv);
2182 
2183    /* set sensitive */
2184    ifactory = gtk_item_factory_from_widget (popup_menu);
2185 
2186    menuitem = gtk_item_factory_get_item (ifactory, "/Open");
2187    if (!gimv_image_info_is_dir (thumb->info)
2188        && !gimv_image_info_is_archive (thumb->info))
2189    {
2190       gtk_widget_hide (menuitem);
2191    }
2192 
2193    menuitem = gtk_item_factory_get_item (ifactory, "/Open in New Window");
2194    if (gimv_image_info_is_dir (thumb->info)
2195        || gimv_image_info_is_archive (thumb->info))
2196    {
2197       gtk_widget_hide (menuitem);
2198    }
2199 
2200    menuitem = gtk_item_factory_get_item (ifactory, "/Open in Shared Window");
2201    if (gimv_image_info_is_dir (thumb->info)
2202        || gimv_image_info_is_archive (thumb->info))
2203    {
2204       gtk_widget_hide (menuitem);
2205    } else if (g_list_length (thumblist) > 1) {
2206       gtk_widget_set_sensitive (menuitem, FALSE);
2207    }
2208 
2209    menuitem = gtk_item_factory_get_item (ifactory, "/Open in External Program");
2210    if (/* thumbnail_is_dir (thumb) || thumbnail_is_archive (thumb) */
2211       gimv_image_info_is_in_archive (thumb->info))
2212    {
2213       gtk_widget_hide (menuitem);
2214    } else {
2215       progs_submenu = create_progs_submenu (tv);
2216       menu_set_submenu (popup_menu, "/Open in External Program", progs_submenu);
2217    }
2218 
2219    menuitem = gtk_item_factory_get_item (ifactory, "/Scripts");
2220    if (/* thumbnail_is_dir (thumb) || thumbnail_is_archive (thumb) */
2221       gimv_image_info_is_in_archive (thumb->info))
2222    {
2223       gtk_widget_hide (menuitem);
2224    } else {
2225       scripts_submenu = create_scripts_submenu (tv);
2226       menu_set_submenu (popup_menu, "/Scripts", scripts_submenu);
2227    }
2228 
2229 #warning FIXME!!
2230    menuitem = gtk_item_factory_get_item (ifactory, "/Update Thumbnail");
2231    if (gimv_image_info_is_dir (thumb->info)
2232        || gimv_image_info_is_archive (thumb->info)
2233        || gimv_image_info_is_movie (thumb->info))
2234    {
2235       gtk_widget_set_sensitive (menuitem, FALSE);
2236    }
2237 
2238    menuitem = gtk_item_factory_get_item (ifactory, "/Remove from List");
2239    if (tv->progress || tv->mode != GIMV_THUMB_VIEW_MODE_COLLECTION) {
2240       gtk_widget_set_sensitive (menuitem, FALSE);
2241    }
2242 
2243    dirname = g_dirname (gimv_image_info_get_path (thumb->info));
2244    if (g_list_length (thumblist) < 1 || !iswritable (dirname)
2245        || tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE)
2246    {
2247       if (g_list_length (thumblist) < 1) {
2248          menuitem = gtk_item_factory_get_item (ifactory, "/Property...");
2249          gtk_widget_set_sensitive (menuitem, FALSE);
2250       }
2251       menuitem = gtk_item_factory_get_item (ifactory, "/Move Files To...");
2252       gtk_widget_set_sensitive (menuitem, FALSE);
2253       menuitem = gtk_item_factory_get_item (ifactory, "/Rename...");
2254       gtk_widget_set_sensitive (menuitem, FALSE);
2255       menuitem = gtk_item_factory_get_item (ifactory, "/Remove file...");
2256       gtk_widget_set_sensitive (menuitem, FALSE);
2257    }
2258    g_free (dirname);
2259 
2260    if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) {
2261       menuitem = gtk_item_factory_get_item (ifactory, "/Copy Files To...");
2262       gtk_widget_set_sensitive (menuitem, FALSE);
2263       menuitem = gtk_item_factory_get_item (ifactory, "/Link Files To...");
2264       gtk_widget_set_sensitive (menuitem, FALSE);
2265    }
2266 
2267    if (g_list_length (thumblist) > 1) {
2268       menuitem = gtk_item_factory_get_item (ifactory, "/Property...");
2269       gtk_widget_set_sensitive (menuitem, FALSE);
2270       menuitem = gtk_item_factory_get_item (ifactory, "/Rename...");
2271       gtk_widget_set_sensitive (menuitem, FALSE);
2272    }
2273 
2274 #ifdef ENABLE_EXIF
2275    {
2276       const gchar *img_name = gimv_image_info_get_path (thumb->info);
2277       const gchar *format = gimv_image_detect_type_by_ext (img_name);
2278       menuitem = gtk_item_factory_get_item (ifactory, "/Scan EXIF Data...");
2279 #warning FIXME!!
2280       if (!format || !*format || g_strcasecmp(format, "image/jpeg")) {
2281          gtk_widget_hide (menuitem);
2282       }
2283       if (g_list_length (thumblist) > 1
2284           || tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE)
2285       {
2286          gtk_widget_set_sensitive (menuitem, FALSE);
2287       }
2288    }
2289 #endif /* ENABLE_EXIF */
2290 
2291    /* popup */
2292    gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL,
2293                   NULL, NULL, button, time);
2294 
2295    tv->popup_menu = popup_menu;
2296 #ifdef USE_GTK2
2297    gtk_object_ref (GTK_OBJECT (tv->popup_menu));
2298    gtk_object_sink (GTK_OBJECT (tv->popup_menu));
2299 #endif
2300 
2301    g_list_free (thumblist);
2302 }
2303 
2304 
2305 void
gimv_thumb_view_file_operate(GimvThumbView * tv,FileOperateType type)2306 gimv_thumb_view_file_operate (GimvThumbView *tv, FileOperateType type)
2307 {
2308    GList *list = NULL;
2309    gboolean success = FALSE;
2310 
2311    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
2312 
2313    /* FIXME!! */
2314    /* not implemented yet */
2315    if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) return;
2316 
2317    list = gimv_thumb_view_get_selected_file_list (tv);
2318    if (!list) return;
2319 
2320    if (!previous_dir && tv->mode == GIMV_THUMB_VIEW_MODE_DIR) {
2321       previous_dir = g_strdup (tv->priv->dirname);
2322    }
2323 
2324    success = files2dir_with_dialog (list, &previous_dir, type,
2325                                     GTK_WINDOW (tv->tw));
2326 
2327    if (success && type == FILE_MOVE) {
2328       gimv_thumb_view_refresh_list (tv);
2329    }
2330 
2331    /* update dest side file list */
2332    tv = gimv_thumb_view_find_opened_dir (previous_dir);
2333    if (tv) {
2334       gimv_thumb_view_refresh_list (tv);
2335    }
2336 
2337    g_list_foreach (list, (GFunc) g_free, NULL);
2338    g_list_free (list);
2339 }
2340 
2341 
2342 void
gimv_thumb_view_rename_file(GimvThumbView * tv)2343 gimv_thumb_view_rename_file (GimvThumbView *tv)
2344 {
2345    GimvThumb *thumb;
2346    const gchar *cache_type;
2347    GList *thumblist;
2348    const gchar *src_file;
2349    gchar *dest_file, *dest_path;
2350    gchar *src_cache_path, *dest_cache_path;
2351    gchar *src_comment, *dest_comment;
2352    gchar message[BUF_SIZE], *dirname;
2353    ConfirmType confirm;
2354    gboolean exist;
2355    struct stat dest_st;
2356 
2357    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
2358 
2359    /* FIXME!! */
2360    /* not implemented yet */
2361    if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) return;
2362 
2363    thumblist = gimv_thumb_view_get_selection_list (tv);
2364    if (!thumblist || g_list_length (thumblist) > 1) return;
2365 
2366    thumb = thumblist->data;
2367    if (!thumb) return;
2368 
2369    {   /********** convert charset **********/
2370       gchar *tmpstr, *src_file_internal;
2371 
2372       src_file = g_basename(gimv_image_info_get_path (thumb->info));
2373       src_file_internal = charset_to_internal (src_file,
2374                                                conf.charset_filename,
2375                                                conf.charset_auto_detect_fn,
2376                                                conf.charset_filename_mode);
2377 
2378       tmpstr = gtkutil_popup_textentry (_("Rename a file"),
2379                                         _("New file name: "),
2380                                         src_file_internal, NULL, -1, 0,
2381                                         GTK_WINDOW (tv->tw));
2382       g_free (src_file_internal);
2383       src_file_internal = NULL;
2384 
2385       if (!tmpstr) return;
2386 
2387       dest_file = charset_internal_to_locale (tmpstr);
2388       g_free (tmpstr);
2389    }
2390 
2391    if (!strcmp (src_file, dest_file)) goto ERROR0;
2392 
2393    dirname = g_dirname (gimv_image_info_get_path (thumb->info));
2394    dest_path = g_strconcat (dirname, "/", g_basename (dest_file), NULL);
2395    g_free (dirname);
2396    exist = !lstat(dest_path, &dest_st);
2397    if (exist) {
2398       {   /********** convert charset **********/
2399          gchar *tmpstr;
2400 
2401          tmpstr = charset_to_internal (src_file,
2402                                        conf.charset_filename,
2403                                        conf.charset_auto_detect_fn,
2404                                        conf.charset_filename_mode);
2405 
2406          g_snprintf (message, BUF_SIZE,
2407                      _("File exist : %s\n\n"
2408                        "Overwrite?"), tmpstr);
2409 
2410          g_free (tmpstr);
2411       }
2412 
2413       confirm = gtkutil_confirm_dialog (_("File exist!!"), message, 0,
2414                                         GTK_WINDOW (tv->tw));
2415       if (confirm == CONFIRM_NO) goto ERROR1;
2416    }
2417 
2418    /* rename file!! */
2419    if (rename (gimv_image_info_get_path (thumb->info), dest_path) < 0) {
2420       {   /********** convert charset **********/
2421          gchar *tmpstr;
2422 
2423          tmpstr = charset_to_internal (gimv_image_info_get_path (thumb->info),
2424                                        conf.charset_filename,
2425                                        conf.charset_auto_detect_fn,
2426                                        conf.charset_filename_mode);
2427 
2428          g_snprintf (message, BUF_SIZE,
2429                      _("Faild to rename file :\n%s"),
2430                      tmpstr);
2431 
2432          g_free (tmpstr);
2433       }
2434 
2435       gtkutil_message_dialog (_("Error!!"), message,
2436                               GTK_WINDOW (tv->tw));
2437    }
2438 
2439    /* rename cache */
2440    cache_type = gimv_thumb_get_cache_type (thumb);
2441    if (cache_type) {
2442       src_cache_path
2443          = gimv_thumb_cache_get_path (gimv_image_info_get_path (thumb->info),
2444                                       cache_type);
2445       dest_cache_path
2446          = gimv_thumb_cache_get_path (dest_path, cache_type);
2447       if (rename (src_cache_path, dest_cache_path) < 0)
2448          g_print (_("Faild to rename cache file :%s\n"), dest_path);
2449       g_free (src_cache_path);
2450       g_free (dest_cache_path);
2451    }
2452 
2453    /* rename comment */
2454    src_comment = gimv_comment_find_file (gimv_image_info_get_path (thumb->info));
2455    if (src_comment) {
2456       dest_comment = gimv_comment_get_path (dest_path);
2457       if (rename (src_comment, dest_comment) < 0)
2458          g_print (_("Faild to rename comment file :%s\n"), dest_comment);
2459       g_free (src_comment);
2460       g_free (dest_comment);
2461    }
2462 
2463    gimv_thumb_view_refresh_list (tv);
2464 
2465 ERROR1:
2466    g_free (dest_path);
2467 ERROR0:
2468    g_free (dest_file);
2469 }
2470 
2471 
2472 gboolean
gimv_thumb_view_delete_files(GimvThumbView * tv)2473 gimv_thumb_view_delete_files (GimvThumbView *tv)
2474 {
2475    GimvThumbWin *tw;
2476    GList *selection;
2477    GList *filelist = NULL, *list;
2478    gboolean retval;
2479 
2480    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), FALSE);
2481 
2482    /* FIXME!! */
2483    /* not implemented yet */
2484    if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) return FALSE;
2485 
2486    tw = tv->tw;
2487    g_return_val_if_fail (GIMV_IS_THUMB_WIN (tw), FALSE);
2488 
2489    selection = gimv_thumb_view_get_selection_list (tv);
2490    if (!selection) return FALSE;
2491 
2492    /* convert to filename list */
2493    for (list = selection; list; list = g_list_next (list)) {
2494       GimvThumb *thumb = list->data;
2495       const gchar *filename;
2496 
2497       if (!thumb) continue;
2498       filename = gimv_image_info_get_path (thumb->info);
2499       if (filename && *filename)
2500          filelist = g_list_append (filelist, (gpointer) filename);
2501    }
2502 
2503    /* do delete */
2504    retval = delete_files (filelist, CONFIRM_ASK,
2505                           GTK_WINDOW (tv->tw));
2506 
2507    if (retval) {
2508       gimv_thumb_view_refresh_list (tv);
2509       if (tw->show_dirview)
2510          dirview_refresh_list (tw->dv);
2511    }
2512 
2513    g_list_free (filelist);
2514    g_list_free (selection);
2515 
2516    return retval;
2517 }
2518 
2519 
2520 static GtkWidget *
create_scripts_submenu(GimvThumbView * tv)2521 create_scripts_submenu (GimvThumbView *tv)
2522 {
2523    GtkWidget *menu;
2524    GtkWidget *menu_item;
2525    GList *tmplist = NULL, *filelist = NULL, *list;
2526    const gchar *dirlist;
2527    gchar **dirs;
2528    gint i, flags;
2529 
2530    menu = gtk_menu_new();
2531 
2532    if (conf.scripts_use_default_search_dir_list)
2533       dirlist = SCRIPTS_DEFAULT_SEARCH_DIR_LIST;
2534    else
2535       dirlist = conf.scripts_search_dir_list;
2536 
2537    if (!dirlist || !*dirlist) return NULL;
2538 
2539    dirs = g_strsplit (dirlist, ",", -1);
2540    if (!dirs) return NULL;
2541 
2542    flags = 0 | GETDIR_FOLLOW_SYMLINK;
2543    for (i = 0; dirs[i]; i++) {
2544       if (!*dirs || !isdir (dirs[i])) continue;
2545       get_dir (dirs[i], flags, &tmplist, NULL);
2546       filelist = g_list_concat (filelist, tmplist);
2547    }
2548    g_strfreev (dirs);
2549 
2550    for (list = filelist; list; list = g_list_next (list)) {
2551       gchar *filename = list->data;
2552       gchar *label;
2553 
2554       if (!filename || !*filename || !isexecutable(filename)) continue;
2555 
2556       if (conf.scripts_show_dialog)
2557          label = g_strconcat (g_basename (filename), "...", NULL);
2558       else
2559          label = g_strdup (g_basename (filename));
2560 
2561       menu_item = gtk_menu_item_new_with_label (label);
2562       gtk_object_set_data_full (GTK_OBJECT (menu_item),
2563                                 "script",
2564                                 g_strdup (filename),
2565                                 (GtkDestroyNotify) g_free);
2566       gtk_signal_connect (GTK_OBJECT (menu_item),
2567                           "activate",
2568                           GTK_SIGNAL_FUNC (cb_open_image_by_script),
2569                           tv);
2570       gtk_menu_append (GTK_MENU (menu), menu_item);
2571       gtk_widget_show (menu_item);
2572 
2573       g_free (label);
2574    }
2575 
2576    g_list_foreach (filelist, (GFunc) g_free, NULL);
2577    g_list_free (filelist);
2578 
2579    return menu;
2580 }
2581 
2582 
2583 static void
gimv_thumb_view_reset_load_priority(GimvThumbView * tv)2584 gimv_thumb_view_reset_load_priority (GimvThumbView *tv)
2585 {
2586    GList *node, *tmp_list = NULL;
2587    gboolean in_view;
2588 
2589    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
2590 
2591    if (!tv->load_list) return;
2592    node = g_list_last (tv->load_list);
2593    g_return_if_fail (tv->vfuncs);
2594 
2595    if (!tv->vfuncs->is_in_view) return;
2596 
2597    while (node) {
2598       GimvThumb *thumb = node->data;
2599       node = g_list_previous (node);
2600 
2601       in_view = tv->vfuncs->is_in_view (tv, thumb);
2602       if (in_view) {
2603          tv->load_list = g_list_remove (tv->load_list, thumb);
2604          tmp_list = g_list_prepend (tmp_list, thumb);
2605       }
2606    }
2607 
2608    if (tmp_list)
2609       tv->load_list = g_list_concat (tmp_list, tv->load_list);
2610 }
2611 
2612 
2613 static void
gimv_thumb_view_set_scrollbar_callback(GimvThumbView * tv)2614 gimv_thumb_view_set_scrollbar_callback (GimvThumbView *tv)
2615 {
2616    GtkAdjustment *hadj, *vadj;
2617 
2618    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
2619 
2620    hadj = gtk_scrolled_window_get_hadjustment (
2621                GTK_SCROLLED_WINDOW (tv->container));
2622    vadj = gtk_scrolled_window_get_vadjustment (
2623                GTK_SCROLLED_WINDOW (tv->container));
2624 
2625    gtk_signal_connect (GTK_OBJECT (hadj),
2626                        "value_changed",
2627                        GTK_SIGNAL_FUNC (cb_thumbview_scrollbar_value_changed),
2628                        tv);
2629    gtk_signal_connect (GTK_OBJECT (vadj),
2630                        "value_changed",
2631                        GTK_SIGNAL_FUNC (cb_thumbview_scrollbar_value_changed),
2632                        tv);
2633 }
2634 
2635 
2636 static void
gimv_thumb_view_remove_scrollbar_callback(GimvThumbView * tv)2637 gimv_thumb_view_remove_scrollbar_callback (GimvThumbView *tv)
2638 {
2639    GtkAdjustment *hadj, *vadj;
2640 
2641    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
2642 
2643    hadj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (tv->container));
2644    vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (tv->container));
2645 
2646    gtk_signal_disconnect_by_func (GTK_OBJECT (hadj),
2647                                   GTK_SIGNAL_FUNC (cb_thumbview_scrollbar_value_changed),
2648                                   tv);
2649    gtk_signal_disconnect_by_func (GTK_OBJECT (vadj),
2650                                   GTK_SIGNAL_FUNC (cb_thumbview_scrollbar_value_changed),
2651                                   tv);
2652 }
2653 
2654 
2655 static GCompareFunc
gimv_thumb_view_get_compare_func(GimvThumbView * tv,gboolean * reverse)2656 gimv_thumb_view_get_compare_func (GimvThumbView *tv, gboolean *reverse)
2657 {
2658    GimvSortItem item;
2659    GimvSortFlag flags;
2660    GCompareFunc func = NULL;
2661 
2662    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), NULL);
2663 
2664    item = gimv_thumb_win_get_sort_type (tv->tw, &flags);
2665 
2666    /* sort thumbnail */
2667    if (item == GIMV_SORT_NAME)
2668       func = comp_func_spel;
2669    else if (item == GIMV_SORT_SIZE)
2670       func = comp_func_size;
2671    else if (item == GIMV_SORT_ATIME)
2672       func = comp_func_atime;
2673    else if (item == GIMV_SORT_MTIME)
2674       func = comp_func_mtime;
2675    else if (item == GIMV_SORT_CTIME)
2676       func = comp_func_ctime;
2677    else if (item == GIMV_SORT_TYPE)
2678       func = comp_func_type;
2679    else if (item == GIMV_SORT_WIDTH)
2680       func = comp_func_width;
2681    else if (item == GIMV_SORT_HEIGHT)
2682       func = comp_func_height;
2683    else if (item == GIMV_SORT_AREA)
2684       func = comp_func_area;
2685 
2686    if (reverse) {
2687       if ((flags & GIMV_SORT_REVERSE))
2688          *reverse = TRUE;
2689       else
2690          *reverse = FALSE;
2691    }
2692 
2693    return func;
2694 }
2695 
2696 
2697 
2698 /******************************************************************************
2699  *
2700  *   Public Functions.
2701  *
2702  ******************************************************************************/
2703 GList *
gimv_thumb_view_get_list(void)2704 gimv_thumb_view_get_list (void)
2705 {
2706    return GimvThumbViewList;
2707 }
2708 
2709 
2710 static void
gimv_thumb_view_destroy_dupl_win_relation(GimvDuplWin * sw,GimvThumbView * tv)2711 gimv_thumb_view_destroy_dupl_win_relation (GimvDuplWin *sw,GimvThumbView *tv)
2712 {
2713    g_return_if_fail (GIMV_IS_DUPL_WIN (sw));
2714    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
2715 
2716    gimv_dupl_win_unset_relation (sw);
2717    gtk_signal_disconnect_by_func (GTK_OBJECT (sw),
2718                                   GTK_SIGNAL_FUNC (cb_dupl_win_destroy),
2719                                   tv);
2720 }
2721 
2722 
2723 const gchar *
gimv_thumb_view_get_path(GimvThumbView * tv)2724 gimv_thumb_view_get_path (GimvThumbView *tv)
2725 {
2726    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), NULL);
2727 
2728    if (tv->mode == GIMV_THUMB_VIEW_MODE_DIR) {
2729       return tv->priv->dirname;
2730    } else if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) {
2731       return tv->priv->archive->filename;
2732    }
2733 
2734    return NULL;
2735 }
2736 
2737 
2738 GimvThumbView *
gimv_thumb_view_find_opened_dir(const gchar * path)2739 gimv_thumb_view_find_opened_dir (const gchar *path)
2740 {
2741    GList *node;
2742 
2743    if (!path) return NULL;
2744 
2745    for (node = GimvThumbViewList; node; node = g_list_next (node)) {
2746       GimvThumbView *tv  = node->data;
2747 
2748       if (tv->mode == GIMV_THUMB_VIEW_MODE_DIR &&
2749           tv->priv && tv->priv->dirname &&
2750           !strcmp (path, tv->priv->dirname))
2751       {
2752          return tv;
2753       }
2754    }
2755 
2756    return NULL;
2757 }
2758 
2759 
2760 GimvThumbView *
gimv_thumb_view_find_opened_archive(const gchar * path)2761 gimv_thumb_view_find_opened_archive (const gchar *path)
2762 {
2763    GimvThumbView *tv;
2764    GList *node;
2765 
2766    if (!path) return NULL;
2767 
2768    node = g_list_first (GimvThumbViewList);
2769    while (node) {
2770       tv = node->data;
2771       if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE
2772           && tv->priv->archive
2773           && !strcmp (path, tv->priv->archive->filename))
2774       {
2775          return tv;
2776       }
2777       node = g_list_next (node);
2778    }
2779 
2780    return NULL;
2781 }
2782 
2783 
2784 void
gimv_thumb_view_sort_data(GimvThumbView * tv)2785 gimv_thumb_view_sort_data (GimvThumbView *tv)
2786 {
2787    GCompareFunc func;
2788    gboolean reverse = FALSE;
2789 
2790    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
2791 
2792    func = gimv_thumb_view_get_compare_func (tv, &reverse);
2793    g_return_if_fail (func);
2794 
2795    tv->thumblist = g_list_sort (tv->thumblist, func);
2796    if (reverse)
2797       tv->thumblist = g_list_reverse (tv->thumblist);
2798 }
2799 
2800 
2801 /* #define ENABLE_THUMB_LOADER_TIMER 0 */
2802 gboolean
gimv_thumb_view_load_thumbnails(GimvThumbView * tv,GList * loadlist,const gchar * dest_mode)2803 gimv_thumb_view_load_thumbnails (GimvThumbView *tv, GList *loadlist,
2804                                  const gchar *dest_mode)
2805 {
2806 #ifdef ENABLE_THUMB_LOADER_TIMER
2807    GTimer *timer;
2808    gdouble etime;
2809    gulong mtime;
2810 #endif /* ENABLE_THUMB_LOADER_TIMER */
2811    FilesLoader *files = tv->progress;
2812    gint pos;
2813 
2814    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv) && loadlist, FALSE);
2815    g_return_val_if_fail (plugin_list, FALSE);
2816 
2817 #ifdef ENABLE_THUMB_LOADER_TIMER
2818    timer = g_timer_new ();
2819    g_timer_start (timer);
2820    g_print ("timer started\n");
2821 #endif /* ENABLE_THUMB_LOADER_TIMER */
2822 
2823    g_return_val_if_fail (tv->vfuncs, FALSE);
2824 
2825    /* load thumbnail!! */
2826    tv->load_list = g_list_copy (loadlist);
2827    gimv_thumb_view_reset_load_priority (tv);
2828 
2829    tv->progress->window = GTK_WIDGET (tv->tw);
2830    tv->progress->progressbar = tv->tw->progressbar;
2831    tv->progress->num = g_list_length (loadlist);
2832    tv->progress->pos = 0;
2833    gimv_thumb_win_set_statusbar_page_info (tv->tw,
2834                                            GIMV_THUMB_WIN_CURRENT_PAGE);
2835 
2836    for (pos = 1; tv->load_list && files->status < CANCEL; pos++) {
2837       GimvThumb *thumb;
2838 
2839       if (tv->progress->pos % conf.thumbwin_redraw_interval == 0)
2840          while (gtk_events_pending()) gtk_main_iteration();
2841 
2842       thumb = tv->load_list->data;
2843 
2844       gimv_thumb_load (thumb, tv->thumb_size, files->thumb_load_type);
2845 
2846       if (tv->vfuncs->update_thumb)
2847          tv->vfuncs->update_thumb (tv, thumb, dest_mode);
2848 
2849       if(files->status < 0) {
2850          GimvThumbViewList = g_list_remove (GimvThumbViewList, tv);
2851          gtk_object_unref (GTK_OBJECT (tv));
2852          break;
2853       } else {
2854          /* update progress info */
2855          tv->progress->now_file = gimv_image_info_get_path (thumb->info);
2856          tv->progress->pos = pos;
2857          gimv_thumb_win_loading_update_progress (tv->tw,
2858                                                  GIMV_THUMB_WIN_CURRENT_PAGE);
2859       }
2860 
2861       tv->load_list = g_list_remove (tv->load_list, thumb);
2862    }
2863 
2864    if (tv->load_list) {
2865       g_list_free (tv->load_list);
2866       tv->load_list = NULL;
2867    }
2868 
2869    gtk_progress_set_show_text(GTK_PROGRESS(files->progressbar), FALSE);
2870    gtk_progress_bar_update (GTK_PROGRESS_BAR(files->progressbar), 0.0);
2871 
2872 #ifdef ENABLE_THUMB_LOADER_TIMER
2873    g_timer_stop (timer);
2874    g_print ("timer stopped\n");
2875    etime = g_timer_elapsed (timer, &mtime);
2876    g_print ("elapsed time = %f sec\n", etime);
2877    g_timer_destroy (timer);
2878 #endif /* ENABLE_THUMB_LOADER_TIMER */
2879 
2880    return TRUE;
2881 }
2882 
2883 
2884 gboolean
gimv_thumb_view_append_thumbnail(GimvThumbView * tv,FilesLoader * files,gboolean force)2885 gimv_thumb_view_append_thumbnail (GimvThumbView *tv, FilesLoader *files,
2886                                   gboolean force)
2887 {
2888    GList *loadlist = NULL;
2889 
2890    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv) && files, FALSE);
2891    g_return_val_if_fail (tv->vfuncs, FALSE);
2892 
2893    if (!tv || !files || (tv->mode == GIMV_THUMB_VIEW_MODE_DIR && !force)) {
2894       return FALSE;
2895    }
2896 
2897    tv->progress = files;
2898 
2899    gimv_thumb_view_insert_thumb_frames (tv, files->filelist);
2900 
2901    if (tv->vfuncs->get_load_list)
2902       loadlist = tv->vfuncs->get_load_list (tv);
2903 
2904    if (loadlist) {
2905       gimv_thumb_view_load_thumbnails (tv, loadlist, tv->summary_mode);
2906       g_list_free (loadlist);
2907    }
2908 
2909    tv->progress = NULL;
2910 
2911    if (files->status >= 0)
2912       gimv_thumb_win_set_statusbar_page_info (tv->tw,
2913                                               GIMV_THUMB_WIN_CURRENT_PAGE);
2914 
2915    return TRUE;
2916 }
2917 
2918 
2919 void
gimv_thumb_view_clear(GimvThumbView * tv)2920 gimv_thumb_view_clear (GimvThumbView *tv)
2921 {
2922    GimvThumb *thumb;
2923    GList *node;
2924 
2925    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
2926    g_return_if_fail (tv->vfuncs);
2927    g_return_if_fail (!tv->progress);
2928 
2929    if (tv->vfuncs->freeze)
2930       tv->vfuncs->freeze (tv);
2931 
2932    node = tv->thumblist;
2933    while (node) {
2934       thumb = node->data;
2935       node = g_list_next (node);
2936       if (!GIMV_IS_THUMB (thumb)) continue;
2937 
2938       tv->vfuncs->remove_thumb (tv, thumb);
2939       gimv_thumb_view_remove_thumb_data (tv, thumb);
2940       gtk_object_unref (GTK_OBJECT(thumb));
2941    }
2942 
2943    /* destroy relation */
2944    node = tv->priv->related_image_view;
2945    while (node) {
2946       GimvImageView *iv = node->data;
2947       node = g_list_next(node);
2948       gimv_image_view_remove_list (iv, tv);
2949    }
2950    g_list_free (tv->priv->related_image_view);
2951    tv->priv->related_image_view = NULL;
2952 
2953    g_list_foreach (tv->priv->related_dupl_win,
2954                    (GFunc) gimv_thumb_view_destroy_dupl_win_relation,
2955                    tv);
2956    g_list_free (tv->priv->related_dupl_win);
2957    tv->priv->related_dupl_win = NULL;
2958 
2959    if (tv->vfuncs->thaw)
2960       tv->vfuncs->thaw (tv);
2961 
2962    gimv_thumb_win_set_statusbar_page_info (tv->tw,
2963                                            GIMV_THUMB_WIN_CURRENT_PAGE);
2964 }
2965 
2966 
2967 void
gimv_thumb_view_refresh_thumbnail(GimvThumbView * tv,GimvThumb * thumb,ThumbLoadType type)2968 gimv_thumb_view_refresh_thumbnail (GimvThumbView *tv, GimvThumb *thumb,
2969                                    ThumbLoadType type)
2970 {
2971    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
2972    g_return_if_fail (GIMV_IS_THUMB (thumb));
2973    g_return_if_fail (tv->vfuncs);
2974 
2975    gimv_thumb_load (thumb, tv->thumb_size, type);
2976 
2977    if (tv->vfuncs->update_thumb)
2978       tv->vfuncs->update_thumb (tv, thumb, tv->summary_mode);
2979 }
2980 
2981 
2982 typedef struct _SetCursorData
2983 {
2984    GimvThumbView *tv;
2985    GimvThumb *cursor;
2986 } SetCursorData;
2987 
2988 
2989 static gboolean
idle_set_cursor(gpointer user_data)2990 idle_set_cursor(gpointer user_data)
2991 {
2992    SetCursorData *data = user_data;
2993 
2994    if (data->cursor) {
2995       gimv_thumb_view_set_focus (data->tv, data->cursor);
2996       gimv_thumb_view_adjust (data->tv, data->cursor);
2997    }
2998 
2999    g_free (data);
3000 
3001    return FALSE;
3002 }
3003 
3004 
3005 gboolean
gimv_thumb_view_refresh_list(GimvThumbView * tv)3006 gimv_thumb_view_refresh_list (GimvThumbView *tv)
3007 {
3008    FilesLoader *files;
3009    GList *selection, *thumbnode, *node;
3010    GimvThumb *thumb, *dest_cur[4];
3011    gchar *filename;
3012    gboolean exist = FALSE;
3013    struct stat st;
3014    gint i, flags, n_dest_cur;
3015 
3016    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), FALSE);
3017    g_return_val_if_fail (tv->vfuncs, FALSE);
3018 
3019    if (tv->progress)
3020       return FALSE;
3021 
3022    /* get dest cursor candiates */
3023    n_dest_cur = sizeof (dest_cur) / sizeof (GimvThumb *);
3024    for (i = 0; i < n_dest_cur; i++)
3025       dest_cur[i] = NULL;
3026 
3027    dest_cur[0] = gimv_thumb_view_get_focus (tv);
3028    if (dest_cur[0]) {
3029       node = g_list_find (tv->thumblist, dest_cur[0]);
3030       if (node) {
3031          if (g_list_next (node))
3032             dest_cur[1] = g_list_next (node)->data;
3033          else if (g_list_previous (node))
3034             dest_cur[1] = g_list_previous (node)->data;
3035       }
3036    }
3037 
3038    selection = gimv_thumb_view_get_selection_list (tv);
3039 
3040    if (selection) {
3041       node = g_list_find (tv->thumblist, selection->data);
3042       if (node) {
3043          dest_cur[2] = node->data;
3044          if (g_list_previous (node))
3045             dest_cur[3] = g_list_previous (node)->data;
3046       }
3047 
3048       for (; node; node = g_list_next (node)) {
3049          if (!g_list_find (selection, node->data)) {
3050             dest_cur[3] = node->data;
3051             break;
3052          }
3053       }
3054    }
3055 
3056    g_list_free (selection);
3057 
3058    /* search */
3059    files = files_loader_new ();
3060 
3061    if (tv->mode == GIMV_THUMB_VIEW_MODE_DIR) {
3062       if (tv->vfuncs->freeze)
3063          tv->vfuncs->freeze (tv);
3064 
3065       flags = GETDIR_FOLLOW_SYMLINK;
3066       if (conf.read_dotfile)
3067          flags = flags | GETDIR_READ_DOT;
3068       if (conf.detect_filetype_by_ext)
3069          flags = flags | GETDIR_DETECT_EXT;
3070       if (conf.thumbview_show_archive)
3071          flags = flags | GETDIR_GET_ARCHIVE;
3072 
3073       get_dir (tv->priv->dirname, flags, &files->filelist, &files->dirlist);
3074 
3075       if (tv->mode == GIMV_THUMB_VIEW_MODE_DIR && conf.thumbview_show_dir) {
3076          gchar *parent = g_strconcat (tv->priv->dirname, "..", NULL);
3077          files->filelist = g_list_concat (files->dirlist, files->filelist);
3078          files->filelist = g_list_prepend (files->filelist, parent);
3079          files->dirlist = NULL;
3080       }
3081 
3082       thumbnode = g_list_first (tv->thumblist);
3083       while (thumbnode) {
3084          thumb = thumbnode->data;
3085          thumbnode = g_list_next (thumbnode);
3086 
3087          /* remove same file from files->filelist */
3088          node = g_list_first (files->filelist);
3089          while (node) {
3090             filename = node->data;
3091             if (!strcmp (filename, gimv_image_info_get_path (thumb->info))) {
3092                /* check modification time */
3093                if (!stat (filename, &st) &&
3094                    ((thumb->info->st.st_mtime != st.st_mtime) ||
3095                     (thumb->info->st.st_ctime != st.st_ctime)))
3096                {
3097                   exist = FALSE;
3098                } else {
3099                   exist = TRUE;
3100                   files->filelist = g_list_remove (files->filelist, filename);
3101                   g_free (filename);
3102                }
3103                break;
3104             }
3105             node = g_list_next (node);
3106          }
3107 
3108          /* remove obsolete data */
3109          if (!exist) {
3110             tv->vfuncs->remove_thumb (tv, thumb);
3111             gimv_thumb_view_remove_thumb_data (tv, thumb);
3112             gtk_object_unref (GTK_OBJECT (thumb));
3113          }
3114 
3115          exist = FALSE;
3116       }
3117 
3118       if (tv->vfuncs->thaw)
3119          tv->vfuncs->thaw (tv);
3120 
3121       /* append new files */
3122       if (files->filelist)
3123          gimv_thumb_view_append_thumbnail (tv, files, TRUE);
3124 
3125    } else if (tv->mode == GIMV_THUMB_VIEW_MODE_COLLECTION) {
3126    }
3127 
3128    files_loader_delete (files);
3129 
3130    gimv_thumb_win_set_statusbar_page_info (tv->tw,
3131                                            GIMV_THUMB_WIN_CURRENT_PAGE);
3132 
3133    /* set cursor position */
3134    for (i = 0; i < n_dest_cur; i++) {
3135       thumb = dest_cur[i];
3136 
3137       if (thumb && g_list_find (tv->thumblist, thumb)) {
3138          SetCursorData *data = g_new0 (SetCursorData, 1);
3139          data->tv = tv;
3140          data->cursor = thumb;
3141          gtk_idle_add (idle_set_cursor, data);
3142          break;
3143       }
3144    }
3145 
3146    /* check relation */
3147    node = tv->priv->related_image_view;
3148    while (node) {
3149       GimvImageView *iv = node->data;
3150       GList *lnode;
3151 
3152       node = g_list_next (node);
3153 
3154       lnode = gimv_image_view_image_list_current (iv);
3155       if (!lnode) continue;
3156 
3157       if (!g_list_find (tv->thumblist, lnode->data)) {
3158          gimv_image_view_remove_list (iv, (gpointer) tv);
3159       }
3160    }
3161 
3162    return TRUE;
3163 }
3164 
3165 
3166 gint
gimv_thumb_view_refresh_list_idle(gpointer data)3167 gimv_thumb_view_refresh_list_idle (gpointer data)
3168 {
3169    GimvThumbView *tv = data;
3170    gimv_thumb_view_refresh_list (tv);
3171    return FALSE;
3172 }
3173 
3174 
3175 void
gimv_thumb_view_change_summary_mode(GimvThumbView * tv,const gchar * mode)3176 gimv_thumb_view_change_summary_mode (GimvThumbView *tv, const gchar *mode)
3177 {
3178    GimvThumbWin *tw;
3179    GList *node, *loadlist = NULL;
3180 
3181    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
3182 
3183    tw = tv->tw;
3184    g_return_if_fail (GIMV_IS_THUMB_WIN (tw));
3185    g_return_if_fail (tv->vfuncs);
3186 
3187    node = g_list_find (GimvThumbViewList, tv);
3188    if (!node) return;
3189 
3190    gimv_thumb_view_set_widget (tv, tw, tv->container, mode);
3191 
3192    if (tv->vfuncs->get_load_list)
3193    loadlist = tv->vfuncs->get_load_list (tv);
3194 
3195    /* reload images if needed */
3196    if (loadlist) {
3197       FilesLoader *files;
3198 
3199       files = files_loader_new ();
3200       tv->progress = files;
3201 
3202       gimv_thumb_view_load_thumbnails (tv, loadlist, tv->summary_mode);
3203 
3204       g_list_free (loadlist);
3205       files_loader_delete (files);
3206       tv->progress = NULL;
3207 
3208       gimv_thumb_win_set_statusbar_page_info (tw, GIMV_THUMB_WIN_CURRENT_PAGE);
3209    }
3210 }
3211 
3212 
3213 void
gimv_thumb_view_adjust(GimvThumbView * tv,GimvThumb * thumb)3214 gimv_thumb_view_adjust (GimvThumbView *tv, GimvThumb *thumb)
3215 {
3216    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
3217    g_return_if_fail (tv->vfuncs);
3218 
3219    if (tv->vfuncs->adjust_position)
3220       tv->vfuncs->adjust_position (tv, thumb);
3221 
3222    return;
3223 }
3224 
3225 
3226 GList *
gimv_thumb_view_get_selection_list(GimvThumbView * tv)3227 gimv_thumb_view_get_selection_list (GimvThumbView *tv)
3228 {
3229    GList *list = NULL, *node;
3230    GimvThumb *thumb;
3231 
3232    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), NULL);
3233 
3234    node = tv->thumblist;
3235    if (!node) return NULL;
3236 
3237    while (node) {
3238       thumb = node->data;
3239 
3240       if (thumb->selected)
3241          list = g_list_append (list, thumb);
3242 
3243       node = g_list_next (node);
3244    }
3245 
3246    return list;
3247 }
3248 
3249 
3250 GList *
gimv_thumb_view_get_selected_file_list(GimvThumbView * tv)3251 gimv_thumb_view_get_selected_file_list (GimvThumbView *tv)
3252 {
3253    GList *list = NULL, *filelist = NULL, *node;
3254 
3255    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), NULL);
3256 
3257    list = gimv_thumb_view_get_selection_list (tv);
3258    if (!list) return NULL;
3259 
3260    node = list;
3261    while (node) {
3262       GimvThumb *thumb = node->data;
3263 
3264       if (thumb->info)
3265          filelist
3266             = g_list_append (filelist,
3267                              g_strdup (gimv_image_info_get_path (thumb->info)));
3268 
3269       node = g_list_next (node);
3270    }
3271 
3272    return filelist;
3273 }
3274 
3275 
3276 gboolean
gimv_thumb_view_set_selection(GimvThumb * thumb,gboolean select)3277 gimv_thumb_view_set_selection (GimvThumb *thumb, gboolean select)
3278 {
3279    GimvThumbView *tv;
3280 
3281    g_return_val_if_fail (GIMV_IS_THUMB (thumb), FALSE);
3282 
3283    tv = gimv_thumb_get_parent_thumbview (thumb);
3284    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), FALSE);
3285 
3286    if ((thumb->selected && select) || (!thumb->selected && !select))
3287       return TRUE;
3288 
3289    g_return_val_if_fail (tv->vfuncs, FALSE);
3290 
3291    if (tv->vfuncs->set_selection)
3292       tv->vfuncs->set_selection (tv, thumb, select);
3293    else
3294       return FALSE;
3295 
3296    return TRUE;
3297 }
3298 
3299 
3300 gboolean
gimv_thumb_view_set_selection_all(GimvThumbView * tv,gboolean select)3301 gimv_thumb_view_set_selection_all (GimvThumbView *tv, gboolean select)
3302 {
3303    GimvThumb *thumb;
3304    GList *node;
3305 
3306    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), FALSE);
3307    g_return_val_if_fail (tv->thumblist, FALSE);
3308 
3309    node = tv->thumblist;
3310    while (node) {
3311       thumb = node->data;
3312       gimv_thumb_view_set_selection (thumb, select);
3313       node = g_list_next (node);
3314    }
3315 
3316    return TRUE;
3317 }
3318 
3319 
3320 void
gimv_thumb_view_set_focus(GimvThumbView * tv,GimvThumb * thumb)3321 gimv_thumb_view_set_focus (GimvThumbView *tv, GimvThumb *thumb)
3322 {
3323    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
3324    g_return_if_fail (tv->vfuncs);
3325 
3326    if (tv->vfuncs->set_focus)
3327       tv->vfuncs->set_focus (tv, thumb);
3328 }
3329 
3330 
3331 GimvThumb *
gimv_thumb_view_get_focus(GimvThumbView * tv)3332 gimv_thumb_view_get_focus (GimvThumbView *tv)
3333 {
3334    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), NULL);
3335    g_return_val_if_fail (tv->vfuncs, NULL);
3336 
3337    if (tv->vfuncs->get_focus)
3338       return tv->vfuncs->get_focus (tv);
3339 
3340    return NULL;
3341 }
3342 
3343 
3344 gboolean
gimv_thumb_view_set_selection_multiple(GimvThumbView * tv,GimvThumb * thumb,gboolean reverse,gboolean clear)3345 gimv_thumb_view_set_selection_multiple (GimvThumbView *tv, GimvThumb *thumb,
3346                                         gboolean reverse, gboolean clear)
3347 {
3348    GimvThumb *thumb_tmp;
3349    GList *node, *current_node;
3350    gboolean retval = FALSE;
3351 
3352    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), FALSE);
3353    g_return_val_if_fail (GIMV_IS_THUMB (thumb), FALSE);
3354 
3355    node = current_node = g_list_find (tv->thumblist, thumb);
3356    if (reverse)
3357       node = g_list_previous (node);
3358    else
3359       node = g_list_next (node);
3360 
3361    while (node) {
3362       thumb_tmp = node->data;
3363       if (thumb_tmp->selected) {
3364          if (clear)
3365             gimv_thumb_view_set_selection_all (tv, FALSE);
3366          while (TRUE) {
3367             thumb_tmp = node->data;
3368             gimv_thumb_view_set_selection (thumb_tmp, TRUE);
3369             if (node == current_node) break;
3370             if (reverse)
3371                node = g_list_next (node);
3372             else
3373                node = g_list_previous (node);
3374          }
3375          retval = TRUE;
3376          break;
3377       }
3378       if (reverse)
3379          node = g_list_previous (node);
3380       else
3381          node = g_list_next (node);
3382    }
3383 
3384    return retval;
3385 }
3386 
3387 
3388 void
gimv_thumb_view_grab_focus(GimvThumbView * tv)3389 gimv_thumb_view_grab_focus (GimvThumbView *tv)
3390 {
3391    g_return_if_fail(GIMV_IS_THUMB_VIEW (tv));
3392    g_return_if_fail(GTK_IS_BIN(tv->container));
3393    gtk_widget_grab_focus (GTK_BIN (tv->container)->child);
3394 }
3395 
3396 
3397 void
gimv_thumb_view_find_duplicates(GimvThumbView * tv,GimvThumb * thumb,const gchar * type)3398 gimv_thumb_view_find_duplicates (GimvThumbView *tv, GimvThumb *thumb,
3399                                  const gchar *type)
3400 {
3401    GimvDuplFinder *finder;
3402    GimvDuplWin *sw = NULL;
3403    GimvThumbWin *tw;
3404    GList *node;
3405 
3406    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
3407 
3408    tw = tv->tw;
3409    g_return_if_fail (GIMV_IS_THUMB_WIN (tw));
3410 
3411    /* create window */
3412    sw = gimv_dupl_win_new (tv->thumb_size);
3413    gtk_signal_connect (GTK_OBJECT (sw), "destroy",
3414                        GTK_SIGNAL_FUNC (cb_dupl_win_destroy),
3415                        tv);
3416    gimv_dupl_win_set_relation (sw, tv);
3417    tv->priv->related_dupl_win
3418       = g_list_append (tv->priv->related_dupl_win, sw);
3419 
3420    /* set finder */
3421    finder = sw->finder;
3422    gimv_dupl_finder_set_algol_type (finder, type);
3423 
3424    for (node = tv->thumblist; node; node = g_list_next (node)) {
3425       gimv_dupl_finder_append_dest (finder, node->data);
3426    }
3427 
3428    gimv_dupl_finder_start (finder);
3429 }
3430 
3431 
3432 void
gimv_thumb_view_reset_tab_label(GimvThumbView * tv,const gchar * title)3433 gimv_thumb_view_reset_tab_label (GimvThumbView *tv, const gchar *title)
3434 {
3435    gchar *tmpstr;
3436    const gchar *filename;
3437 
3438    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
3439 
3440    /* set tab title */
3441    if (tv->mode == GIMV_THUMB_VIEW_MODE_COLLECTION) {
3442       if (title) {
3443          g_free (tv->tabtitle);
3444          tv->tabtitle = g_strdup (title);
3445       }
3446 
3447    } else {
3448       if (tv->mode == GIMV_THUMB_VIEW_MODE_DIR) {
3449          g_return_if_fail (tv->priv->dirname && *tv->priv->dirname);
3450          filename = tv->priv->dirname;
3451       } else if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) {
3452          g_return_if_fail (tv->priv->archive);
3453          g_return_if_fail (tv->priv->archive->filename
3454                            && *tv->priv->archive->filename);
3455          filename = tv->priv->archive->filename;
3456       } else {
3457          return;
3458       }
3459 
3460       if (conf.thumbwin_tab_fullpath) {
3461          tmpstr = fileutil_home2tilde (filename);
3462       } else {
3463          if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) {
3464             tmpstr = g_strdup (g_basename (filename));
3465          } else {
3466             gchar *dirname = g_dirname (filename);
3467             tmpstr = fileutil_dir_basename (dirname);
3468             g_free (dirname);
3469          }
3470       }
3471 
3472       g_free (tv->tabtitle);
3473       tv->tabtitle = charset_to_internal (tmpstr,
3474                                           conf.charset_filename,
3475                                           conf.charset_auto_detect_fn,
3476                                           conf.charset_filename_mode);
3477 
3478       g_free (tmpstr);
3479 
3480    }
3481 
3482    gimv_thumb_win_set_tab_label_text (tv->container, tv->tabtitle);
3483 }
3484 
3485 
3486 GtkType
gimv_thumb_view_get_type(void)3487 gimv_thumb_view_get_type (void)
3488 {
3489    static GtkType gimv_thumb_view_type = 0;
3490 
3491    if (!gimv_thumb_view_type) {
3492       static const GtkTypeInfo gimv_thumb_view_info = {
3493          "GimvThumbView",
3494          sizeof (GimvThumbView),
3495          sizeof (GimvThumbViewClass),
3496          (GtkClassInitFunc) gimv_thumb_view_class_init,
3497          (GtkObjectInitFunc) gimv_thumb_view_init,
3498          NULL,
3499          NULL,
3500          (GtkClassInitFunc) NULL,
3501       };
3502 
3503       gimv_thumb_view_type = gtk_type_unique (gtk_object_get_type (),
3504                                               &gimv_thumb_view_info);
3505    }
3506 
3507    return gimv_thumb_view_type;
3508 }
3509 
3510 
3511 static void
gimv_thumb_view_class_init(GimvThumbViewClass * klass)3512 gimv_thumb_view_class_init (GimvThumbViewClass *klass)
3513 {
3514    GtkObjectClass *object_class;
3515 
3516    gimv_thumb_view_get_summary_mode_list ();
3517 
3518    object_class = (GtkObjectClass *) klass;
3519    parent_class = gtk_type_class (gtk_object_get_type ());
3520 
3521    object_class->destroy  = gimv_thumb_view_destroy;
3522 }
3523 
3524 
3525 static void
gimv_thumb_view_init(GimvThumbView * tv)3526 gimv_thumb_view_init (GimvThumbView *tv)
3527 {
3528    tv->thumblist          = NULL;
3529 
3530    tv->tw                 = NULL;
3531 
3532    tv->container          = NULL;
3533    tv->popup_menu         = NULL;
3534 
3535    tv->tabtitle           = NULL;
3536 
3537    tv->thumb_size         = 96;
3538 
3539    tv->mode               = 0;
3540 
3541    tv->filenum            = 0;
3542    tv->filesize           = 0;
3543 
3544    tv->summary_mode       = NULL;
3545    tv->vfuncs             = NULL;
3546 
3547    tv->dnd_destdir        = NULL;
3548 
3549    tv->status             = 0;
3550    tv->progress           = NULL;
3551    tv->load_list          = NULL;
3552 
3553    tv->priv = g_new0 (GimvThumbViewPriv, 1);
3554    tv->priv->dirname               = NULL;
3555    tv->priv->archive               = NULL;
3556    tv->priv->related_image_view    = NULL;
3557    tv->priv->related_dupl_win      = NULL;
3558    tv->priv->button_2pressed_queue = 0;
3559 
3560 #ifdef USE_GTK2
3561    gtk_object_ref (GTK_OBJECT (tv));
3562    gtk_object_sink (GTK_OBJECT (tv));
3563 #endif
3564 
3565    GimvThumbViewList = g_list_append (GimvThumbViewList, tv);
3566    total_tab_count++;
3567 }
3568 
3569 
3570 GimvThumbView *
gimv_thumb_view_new(void)3571 gimv_thumb_view_new (void)
3572 {
3573    GimvThumbView *tv;
3574 
3575    tv = GIMV_THUMB_VIEW (gtk_type_new (gimv_thumb_view_get_type ()));
3576 
3577    return tv;
3578 }
3579 
3580 
3581 static void
gimv_thumb_view_redraw(GimvThumbView * tv,const gchar * mode,GtkWidget * scroll_win)3582 gimv_thumb_view_redraw (GimvThumbView *tv,
3583                         const gchar *mode,
3584                         GtkWidget *scroll_win)
3585 {
3586    GList *node;
3587    GtkAdjustment *hadj, *vadj;
3588    GtkWidget *widget;
3589 
3590    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
3591 
3592    node = g_list_find (GimvThumbViewList, tv);
3593    if (!node) return;
3594 
3595    if (!scroll_win)
3596       scroll_win = tv->container;
3597 
3598    gimv_thumb_view_remove_scrollbar_callback (tv);
3599 
3600    hadj = gtk_scrolled_window_get_hadjustment (
3601                GTK_SCROLLED_WINDOW (tv->container));
3602    vadj = gtk_scrolled_window_get_vadjustment (
3603                GTK_SCROLLED_WINDOW (tv->container));
3604    hadj->value = 0.0;
3605    vadj->value = 0.0;
3606    gtk_signal_emit_by_name (GTK_OBJECT(hadj), "value_changed");
3607    gtk_signal_emit_by_name (GTK_OBJECT(vadj), "value_changed");
3608 
3609    /* sort thumbnail list */
3610    gimv_thumb_view_sort_data (tv);
3611 
3612    /* remove current widget */
3613    if (GTK_BIN (tv->container)->child)
3614       gtk_widget_destroy (GTK_BIN (tv->container)->child);
3615 
3616    /* create new widget */
3617    tv->vfuncs = g_hash_table_lookup (modes_table, mode);
3618    tv->summary_mode = mode;
3619    g_return_if_fail (tv->vfuncs);
3620 
3621    widget = tv->vfuncs->create (tv, mode);
3622 
3623    gtk_container_add (GTK_CONTAINER (scroll_win), widget);
3624    tv->container = scroll_win;
3625 
3626    gimv_thumb_view_set_scrollbar_callback (tv);
3627 
3628    gimv_thumb_view_reset_load_priority (tv);
3629 
3630    gtk_widget_grab_focus (GTK_BIN (tv->container)->child);
3631 }
3632 
3633 
3634 gboolean
gimv_thumb_view_set_widget(GimvThumbView * tv,GimvThumbWin * tw,GtkWidget * container,const gchar * summary_mode)3635 gimv_thumb_view_set_widget (GimvThumbView *tv, GimvThumbWin *tw,
3636                             GtkWidget *container, const gchar *summary_mode)
3637 {
3638    gint page;
3639    gboolean is_newtab = FALSE;
3640 
3641    g_return_val_if_fail (GIMV_IS_THUMB_VIEW (tv), FALSE);
3642    g_return_val_if_fail (container || tv->container, FALSE);
3643 
3644    /* check parent widget */
3645    page = gtk_notebook_page_num (GTK_NOTEBOOK (tw->notebook), container);
3646    if (page < 0)
3647       return FALSE;
3648 
3649    if (!tv->container)
3650       is_newtab = TRUE;
3651 
3652    if (is_newtab) {
3653       gchar buf[BUF_SIZE];
3654 
3655       tv->tw = tw;
3656       tv->container = container;
3657       tv->summary_mode = summary_mode;
3658       tv->mode = GIMV_THUMB_VIEW_MODE_COLLECTION;
3659       g_snprintf (buf, BUF_SIZE, _("Collection %d"), total_tab_count);
3660       gimv_thumb_view_reset_tab_label (tv, buf);
3661       gimv_thumb_view_set_scrollbar_callback (tv);
3662       gimv_thumb_view_redraw (tv, tv->summary_mode, tv->container);
3663    } else {
3664       gimv_thumb_view_redraw (tv, summary_mode, container);
3665       tv->tw = tw;
3666    }
3667 
3668    return TRUE;
3669 }
3670 
3671 
3672 void
gimv_thumb_view_reload(GimvThumbView * tv,FilesLoader * files,GimvThumbViewMode mode)3673 gimv_thumb_view_reload (GimvThumbView *tv, FilesLoader *files, GimvThumbViewMode mode)
3674 {
3675    gint current_page, this_page;
3676    GList *loadlist = NULL;
3677    gchar buf[BUF_SIZE];
3678 
3679    g_return_if_fail (GIMV_IS_THUMB_VIEW (tv));
3680    g_return_if_fail (files);
3681    g_return_if_fail ((mode == GIMV_THUMB_VIEW_MODE_COLLECTION) ||
3682                      (mode == GIMV_THUMB_VIEW_MODE_DIR && files->dirname) ||
3683                      (mode == GIMV_THUMB_VIEW_MODE_RECURSIVE_DIR && files->dirname) ||
3684                      (mode == GIMV_THUMB_VIEW_MODE_ARCHIVE && files->archive));
3685    g_return_if_fail (!tv->progress);
3686 
3687    gimv_thumb_view_clear (tv);
3688    g_free (tv->priv->dirname);
3689    tv->priv->dirname = NULL;
3690    if (tv->priv->archive)
3691       fr_archive_unref (tv->priv->archive);
3692    tv->priv->archive = NULL;
3693 
3694    /* set mode specific data */
3695    if (mode == GIMV_THUMB_VIEW_MODE_DIR ||
3696        mode == GIMV_THUMB_VIEW_MODE_RECURSIVE_DIR)
3697    {
3698       if (files->dirname[strlen (files->dirname) - 1] != '/')
3699          tv->priv->dirname = g_strconcat (files->dirname, "/", NULL);
3700       else
3701          tv->priv->dirname = g_strdup (files->dirname);
3702 
3703    } else if (mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) {
3704       fr_archive_ref (FR_ARCHIVE (files->archive));
3705       tv->priv->archive = files->archive;
3706 
3707    } else if (mode == GIMV_THUMB_VIEW_MODE_COLLECTION) {
3708    }
3709 
3710    /* set struct member */
3711    tv->progress = files;
3712 
3713    if (mode == GIMV_THUMB_VIEW_MODE_RECURSIVE_DIR)
3714       tv->mode = GIMV_THUMB_VIEW_MODE_COLLECTION;
3715    else
3716       tv->mode = mode;
3717 
3718    tv->thumb_size =
3719       gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(tv->tw->button.size_spin));
3720 
3721    /* set tab label */
3722    if (mode == GIMV_THUMB_VIEW_MODE_RECURSIVE_DIR) {
3723       gchar *tmpstr, *dirname_internal;
3724       tmpstr = fileutil_home2tilde (tv->priv->dirname);
3725       dirname_internal = charset_to_internal (tmpstr,
3726                                               conf.charset_filename,
3727                                               conf.charset_auto_detect_fn,
3728                                               conf.charset_filename_mode);
3729       g_snprintf (buf, BUF_SIZE, _("%s (Collection)"), dirname_internal);
3730       g_free (dirname_internal);
3731       g_free (tmpstr);
3732       gimv_thumb_view_reset_tab_label (tv, buf);
3733    } else if (mode == GIMV_THUMB_VIEW_MODE_COLLECTION) {
3734       if (!tv->tabtitle) {
3735          g_snprintf (buf, BUF_SIZE, _("Collection %d"), total_tab_count);
3736          gimv_thumb_view_reset_tab_label (tv, buf);
3737       }
3738    } else {
3739       gimv_thumb_view_reset_tab_label (tv, NULL);
3740    }
3741    gimv_thumb_win_set_tab_label_state (tv->container, GTK_STATE_SELECTED);
3742 
3743    gimv_thumb_win_location_entry_set_text (tv->tw, NULL);
3744 
3745    /* fetch infomation about image files */
3746    if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE) {
3747       gimv_thumb_view_insert_thumb_frames_from_archive (tv, files->archive);
3748    } else {
3749       if (tv->mode == GIMV_THUMB_VIEW_MODE_DIR && conf.thumbview_show_dir) {
3750          gchar *parent = g_strconcat (tv->priv->dirname, "..", NULL);
3751          GList *dirlist = g_list_copy (files->dirlist);
3752 
3753          dirlist = g_list_prepend (dirlist, parent);
3754          gimv_thumb_view_insert_thumb_frames (tv, dirlist);
3755          g_list_free(dirlist);
3756          g_free (parent);
3757       }
3758       gimv_thumb_view_insert_thumb_frames (tv, files->filelist);
3759    }
3760 
3761    /* set window status */
3762    if (tv->mode == GIMV_THUMB_VIEW_MODE_DIR && tv->priv->dirname)
3763       dirview_set_opened_mark (tv->tw->dv, tv->priv->dirname);
3764 
3765    current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (tv->tw->notebook));
3766    this_page = gtk_notebook_page_num (GTK_NOTEBOOK (tv->tw->notebook),
3767                                       tv->container);
3768 
3769    if (current_page == this_page) {
3770       tv->tw->status = GIMV_THUMB_WIN_STATUS_LOADING;
3771       gimv_thumb_win_set_sensitive (tv->tw, GIMV_THUMB_WIN_STATUS_LOADING);
3772       if (tv->mode == GIMV_THUMB_VIEW_MODE_DIR && tv->priv->dirname)
3773          dirview_change_dir (tv->tw->dv, tv->priv->dirname);
3774 
3775    } else {
3776       tv->tw->status = GIMV_THUMB_WIN_STATUS_LOADING_BG;
3777       gimv_thumb_win_set_sensitive (tv->tw, GIMV_THUMB_WIN_STATUS_LOADING_BG);
3778    }
3779 
3780    /* load thumbnails */
3781    if (tv->vfuncs->get_load_list)
3782       loadlist = tv->vfuncs->get_load_list (tv);
3783    if (loadlist) {
3784       gimv_thumb_view_load_thumbnails (tv, loadlist, tv->summary_mode);
3785       g_list_free (loadlist);
3786    }
3787 
3788    /* reset window status */
3789    if (files->status > 0 && files->status < CANCEL) {
3790       files->status = THUMB_LOAD_DONE;
3791    }
3792 
3793    if (files->status > 0) {
3794       tv->progress = NULL;
3795       /* reset status bar and tab label */
3796       tv->tw->status = GIMV_THUMB_WIN_STATUS_NORMAL;
3797       gimv_thumb_win_set_statusbar_page_info (tv->tw, GIMV_THUMB_WIN_CURRENT_PAGE);
3798    }
3799 
3800    gimv_thumb_win_set_tab_label_state (tv->container, GTK_STATE_NORMAL);
3801 }
3802 
3803 
3804 static void
gimv_thumb_view_destroy(GtkObject * object)3805 gimv_thumb_view_destroy (GtkObject *object)
3806 {
3807    GimvThumbView *tv;
3808    GList *node;
3809 
3810    g_return_if_fail (GIMV_IS_THUMB_VIEW (object));
3811 
3812    tv = GIMV_THUMB_VIEW (object);
3813 
3814    gimv_thumb_view_remove_scrollbar_callback (tv);
3815 
3816    GimvThumbViewList = g_list_remove (GimvThumbViewList, tv);
3817 
3818    if (GTK_BIN (tv->container)->child) {
3819       gtk_widget_destroy (GTK_BIN (tv->container)->child);
3820       GTK_BIN (tv->container)->child = NULL;
3821    }
3822 
3823    if (tv->priv) {
3824       if (tv->tw && tv->tw->dv &&
3825           tv->mode == GIMV_THUMB_VIEW_MODE_DIR && tv->priv->dirname) {
3826          dirview_unset_opened_mark (tv->tw->dv, tv->priv->dirname);
3827       }
3828 
3829       g_free (tv->priv->dirname);
3830       tv->priv->dirname = NULL;
3831 
3832       /* remove archive */
3833       if (tv->mode == GIMV_THUMB_VIEW_MODE_ARCHIVE && tv->priv->archive) {
3834          fr_archive_unref (FR_ARCHIVE (tv->priv->archive));
3835          tv->priv->archive = NULL;
3836       }
3837 
3838       /* destroy relation */
3839       node = tv->priv->related_image_view;
3840       while (node) {
3841          GimvImageView *iv = node->data;
3842          node = g_list_next(node);
3843          gimv_image_view_remove_list(iv, tv);
3844       }
3845       g_list_free (tv->priv->related_image_view);
3846       tv->priv->related_image_view = NULL;
3847 
3848       g_list_foreach (tv->priv->related_dupl_win,
3849                       (GFunc) gimv_thumb_view_destroy_dupl_win_relation,
3850                       tv);
3851       g_list_free (tv->priv->related_dupl_win);
3852       tv->priv->related_dupl_win = NULL;
3853 
3854       g_free (tv->priv);
3855       tv->priv = NULL;
3856    }
3857 
3858    if (tv->tw) {
3859       tv->tw->filenum -= tv->filenum;
3860       tv->tw->filesize -= tv->filesize;
3861       tv->tw = NULL;
3862    }
3863 
3864    if (tv->progress && tv->progress->status != WINDOW_DESTROYED) {
3865       tv->progress->status = CONTAINER_DESTROYED;
3866       tv->progress = NULL;
3867    }
3868 
3869    /* remove thumbnails */
3870    g_list_foreach (tv->thumblist, (GFunc) gtk_object_unref, NULL);
3871    g_list_free(tv->thumblist);
3872    tv->thumblist = NULL;
3873 
3874    /* remove popup menu */
3875    if (tv->popup_menu) {
3876       gtk_widget_unref (tv->popup_menu);
3877       tv->popup_menu = NULL;
3878    }
3879 
3880    g_free (tv->tabtitle);
3881    tv->tabtitle = NULL;
3882 }
3883