Create/Update file similarity cache
[geeqie.git] / src / cache_maint.c
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
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 along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "cache_maint.h"
24
25 #include "cache.h"
26 #include "cache-loader.h"
27 #include "filedata.h"
28 #include "layout.h"
29 #include "thumb.h"
30 #include "thumb_standard.h"
31 #include "ui_fileops.h"
32 #include "ui_misc.h"
33 #include "ui_spinner.h"
34 #include "ui_tabcomp.h"
35 #include "ui_utildlg.h"
36 #include "window.h"
37
38
39 typedef struct _CMData CMData;
40 struct _CMData
41 {
42         GList *list;
43         GList *done_list;
44         guint idle_id; /* event source id */
45         GenericDialog *gd;
46         GtkWidget *entry;
47         GtkWidget *spinner;
48         GtkWidget *button_stop;
49         GtkWidget *button_close;
50         gboolean clear;
51         gboolean metadata;
52         gboolean remote;
53 };
54
55 #define PURGE_DIALOG_WIDTH 400
56
57
58 /*
59  *-------------------------------------------------------------------
60  * cache maintenance
61  *-------------------------------------------------------------------
62  */
63
64 static gchar *extension_find_dot(gchar *path)
65 {
66         gchar *dot = NULL;
67
68         if (!path) return NULL;
69
70         while (*path != '\0')
71                 {
72                 if (*path == '.') dot = path;
73                 path++;
74                 }
75
76         return dot;
77 }
78
79 static gboolean isempty(const gchar *path)
80 {
81         DIR *dp;
82         struct dirent *dir;
83         gchar *pathl;
84
85         pathl = path_from_utf8(path);
86         dp = opendir(pathl);
87         g_free(pathl);
88         if (!dp) return FALSE;
89
90         while ((dir = readdir(dp)) != NULL)
91                 {
92                 gchar *name = dir->d_name;
93
94                 if (!(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) )
95                         {
96                         closedir(dp);
97                         return FALSE;
98                         }
99                 }
100
101         closedir(dp);
102         return TRUE;
103 }
104
105 static void cache_maintain_home_close(CMData *cm)
106 {
107         if (cm->idle_id) g_source_remove(cm->idle_id);
108         if (cm->gd) generic_dialog_close(cm->gd);
109         filelist_free(cm->list);
110         g_list_free(cm->done_list);
111         g_free(cm);
112 }
113
114 static void cache_maintain_home_stop(CMData *cm)
115 {
116         if (cm->idle_id)
117                 {
118                 g_source_remove(cm->idle_id);
119                 cm->idle_id = 0;
120                 }
121
122         if (!cm->remote)
123                 {
124                 gtk_entry_set_text(GTK_ENTRY(cm->entry), _("done"));
125                 spinner_set_interval(cm->spinner, -1);
126
127                 gtk_widget_set_sensitive(cm->button_stop, FALSE);
128                 gtk_widget_set_sensitive(cm->button_close, TRUE);
129                 }
130 }
131
132 static gboolean cache_maintain_home_cb(gpointer data)
133 {
134         CMData *cm = data;
135         GList *dlist = NULL;
136         GList *list = NULL;
137         FileData *fd;
138         gboolean just_done = FALSE;
139         gboolean still_have_a_file = TRUE;
140         gsize base_length;
141         const gchar *cache_folder;
142         gboolean filter_disable;
143
144         if (cm->metadata)
145                 {
146                 cache_folder = get_metadata_cache_dir();
147                 }
148         else
149                 {
150                 cache_folder = get_thumbnails_cache_dir();
151                 }
152
153         base_length = strlen(cache_folder);
154
155         if (!cm->list)
156                 {
157                 DEBUG_1("purge chk done.");
158                 cm->idle_id = 0;
159                 cache_maintain_home_stop(cm);
160                 return FALSE;
161                 }
162
163         fd = cm->list->data;
164
165         DEBUG_1("purge chk (%d) \"%s\"", (cm->clear && !cm->metadata), fd->path);
166
167 /**
168  * It is necessary to disable the file filter when clearing the cache,
169  * otherwise the .sim (file similarity) files are not deleted.
170  */
171         filter_disable = options->file_filter.disable;
172         options->file_filter.disable = TRUE;
173
174         if (g_list_find(cm->done_list, fd) == NULL)
175                 {
176                 cm->done_list = g_list_prepend(cm->done_list, fd);
177
178                 if (filelist_read(fd, &list, &dlist))
179                         {
180                         GList *work;
181
182                         just_done = TRUE;
183                         still_have_a_file = FALSE;
184
185                         work = list;
186                         while (work)
187                                 {
188                                 FileData *fd_list = work->data;
189                                 gchar *path_buf = g_strdup(fd_list->path);
190                                 gchar *dot;
191
192                                 dot = extension_find_dot(path_buf);
193
194                                 if (dot) *dot = '\0';
195                                 if ((!cm->metadata && cm->clear) ||
196                                     (strlen(path_buf) > base_length && !isfile(path_buf + base_length)) )
197                                         {
198                                         if (dot) *dot = '.';
199                                         if (!unlink_file(path_buf)) log_printf("failed to delete:%s\n", path_buf);
200                                         }
201                                 else
202                                         {
203                                         still_have_a_file = TRUE;
204                                         }
205                                 g_free(path_buf);
206                                 work = work->next;
207                                 }
208                         }
209                 }
210         options->file_filter.disable = filter_disable;
211
212         filelist_free(list);
213
214         cm->list = g_list_concat(dlist, cm->list);
215
216         if (cm->list && g_list_find(cm->done_list, cm->list->data) != NULL)
217                 {
218                 /* check if the dir is empty */
219
220                 if (cm->list->data == fd && just_done)
221                         {
222                         if (!still_have_a_file && !dlist && cm->list->next && !rmdir_utf8(fd->path))
223                                 {
224                                 log_printf("Unable to delete dir: %s\n", fd->path);
225                                 }
226                         }
227                 else
228                         {
229                         /* must re-check for an empty dir */
230                         if (isempty(fd->path) && cm->list->next && !rmdir_utf8(fd->path))
231                                 {
232                                 log_printf("Unable to delete dir: %s\n", fd->path);
233                                 }
234                         }
235
236                 fd = cm->list->data;
237                 cm->done_list = g_list_remove(cm->done_list, fd);
238                 cm->list = g_list_remove(cm->list, fd);
239                 file_data_unref(fd);
240                 }
241
242         if (cm->list && !cm->remote)
243                 {
244                 const gchar *buf;
245
246                 fd = cm->list->data;
247                 if (strlen(fd->path) > base_length)
248                         {
249                         buf = fd->path + base_length;
250                         }
251                 else
252                         {
253                         buf = "...";
254                         }
255                 gtk_entry_set_text(GTK_ENTRY(cm->entry), buf);
256                 }
257
258         return TRUE;
259 }
260
261 static void cache_maintain_home_close_cb(GenericDialog *gd, gpointer data)
262 {
263         CMData *cm = data;
264
265         if (!gtk_widget_get_sensitive(cm->button_close)) return;
266
267         cache_maintain_home_close(cm);
268 }
269
270 static void cache_maintain_home_stop_cb(GenericDialog *gd, gpointer data)
271 {
272         CMData *cm = data;
273
274         cache_maintain_home_stop(cm);
275 }
276
277 /* sorry for complexity (cm->done_list), but need it to remove empty dirs */
278 void cache_maintain_home(gboolean metadata, gboolean clear, GtkWidget *parent)
279 {
280         CMData *cm;
281         GList *dlist;
282         FileData *dir_fd;
283         const gchar *msg;
284         const gchar *cache_folder;
285         GtkWidget *hbox;
286
287         if (metadata)
288                 {
289                 cache_folder = get_metadata_cache_dir();
290                 }
291         else
292                 {
293                 cache_folder = get_thumbnails_cache_dir();
294                 }
295
296         dir_fd = file_data_new_dir(cache_folder);
297         if (!filelist_read(dir_fd, NULL, &dlist))
298                 {
299                 file_data_unref(dir_fd);
300                 return;
301                 }
302
303         dlist = g_list_append(dlist, dir_fd);
304
305         cm = g_new0(CMData, 1);
306         cm->list = dlist;
307         cm->done_list = NULL;
308         cm->clear = clear;
309         cm->metadata = metadata;
310         cm->remote = FALSE;
311
312         if (metadata)
313                 {
314                 msg = _("Removing old metadata...");
315                 }
316         else if (clear)
317                 {
318                 msg = _("Clearing cached thumbnails...");
319                 }
320         else
321                 {
322                 msg = _("Removing old thumbnails...");
323                 }
324
325         cm->gd = generic_dialog_new(_("Maintenance"),
326                                     "main_maintenance",
327                                     parent, FALSE,
328                                     NULL, cm);
329         cm->gd->cancel_cb = cache_maintain_home_close_cb;
330         cm->button_close = generic_dialog_add_button(cm->gd, GTK_STOCK_CLOSE, NULL,
331                                                      cache_maintain_home_close_cb, FALSE);
332         gtk_widget_set_sensitive(cm->button_close, FALSE);
333         cm->button_stop = generic_dialog_add_button(cm->gd, GTK_STOCK_STOP, NULL,
334                                                     cache_maintain_home_stop_cb, FALSE);
335
336         generic_dialog_add_message(cm->gd, NULL, msg, NULL, FALSE);
337         gtk_window_set_default_size(GTK_WINDOW(cm->gd->dialog), PURGE_DIALOG_WIDTH, -1);
338
339         hbox = gtk_hbox_new(FALSE, 0);
340         gtk_box_pack_start(GTK_BOX(cm->gd->vbox), hbox, FALSE, FALSE, 5);
341         gtk_widget_show(hbox);
342
343         cm->entry = gtk_entry_new();
344         gtk_widget_set_can_focus(cm->entry, FALSE);
345         gtk_editable_set_editable(GTK_EDITABLE(cm->entry), FALSE);
346         gtk_box_pack_start(GTK_BOX(hbox), cm->entry, TRUE, TRUE, 0);
347         gtk_widget_show(cm->entry);
348
349         cm->spinner = spinner_new(NULL, SPINNER_SPEED);
350         gtk_box_pack_start(GTK_BOX(hbox), cm->spinner, FALSE, FALSE, 0);
351         gtk_widget_show(cm->spinner);
352
353         gtk_widget_show(cm->gd->dialog);
354
355         cm->idle_id = g_idle_add(cache_maintain_home_cb, cm);
356 }
357
358 void cache_maintain_home_remote(gboolean metadata, gboolean clear)
359 {
360         CMData *cm;
361         GList *dlist;
362         FileData *dir_fd;
363         const gchar *cache_folder;
364
365         if (metadata)
366                 {
367                 cache_folder = get_metadata_cache_dir();
368                 }
369         else
370                 {
371                 cache_folder = get_thumbnails_cache_dir();
372                 }
373
374         dir_fd = file_data_new_dir(cache_folder);
375         if (!filelist_read(dir_fd, NULL, &dlist))
376                 {
377                 file_data_unref(dir_fd);
378                 return;
379                 }
380
381         dlist = g_list_append(dlist, dir_fd);
382
383         cm = g_new0(CMData, 1);
384         cm->list = dlist;
385         cm->done_list = NULL;
386         cm->clear = clear;
387         cm->metadata = metadata;
388         cm->remote = TRUE;
389
390         cm->idle_id = g_idle_add(cache_maintain_home_cb, cm);
391 }
392
393 static void cache_file_move(const gchar *src, const gchar *dest)
394 {
395         if (!dest || !src || !isfile(src)) return;
396
397         if (!move_file(src, dest))
398                 {
399                 DEBUG_1("Failed to move cache file \"%s\" to \"%s\"", src, dest);
400                 /* we remove it anyway - it's stale */
401                 unlink_file(src);
402                 }
403 }
404
405 static void cache_maint_moved(FileData *fd)
406 {
407         gchar *base;
408         mode_t mode = 0755;
409         const gchar *src = fd->change->source;
410         const gchar *dest = fd->change->dest;
411
412         if (!src || !dest) return;
413
414         base = cache_get_location(CACHE_TYPE_THUMB, dest, FALSE, &mode);
415         if (recursive_mkdir_if_not_exists(base, mode))
416                 {
417                 gchar *buf;
418                 gchar *d;
419
420                 buf = cache_find_location(CACHE_TYPE_THUMB, src);
421                 d = cache_get_location(CACHE_TYPE_THUMB, dest, TRUE, NULL);
422                 cache_file_move(buf, d);
423                 g_free(d);
424                 g_free(buf);
425
426                 buf = cache_find_location(CACHE_TYPE_SIM, src);
427                 d = cache_get_location(CACHE_TYPE_SIM, dest, TRUE, NULL);
428                 cache_file_move(buf, d);
429                 g_free(d);
430                 g_free(buf);
431                 }
432         else
433                 {
434                 log_printf("Failed to create cache dir for move %s\n", base);
435                 }
436         g_free(base);
437
438         base = cache_get_location(CACHE_TYPE_METADATA, dest, FALSE, &mode);
439         if (recursive_mkdir_if_not_exists(base, mode))
440                 {
441                 gchar *buf;
442                 gchar *d;
443
444                 buf = cache_find_location(CACHE_TYPE_METADATA, src);
445                 d = cache_get_location(CACHE_TYPE_METADATA, dest, TRUE, NULL);
446                 cache_file_move(buf, d);
447                 g_free(d);
448                 g_free(buf);
449                 }
450         g_free(base);
451
452         if (options->thumbnails.enable_caching && options->thumbnails.spec_standard)
453                 thumb_std_maint_moved(src, dest);
454 }
455
456 static void cache_file_remove(const gchar *path)
457 {
458         if (path && isfile(path) && !unlink_file(path))
459                 {
460                 DEBUG_1("Failed to remove cache file %s", path);
461                 }
462 }
463
464 static void cache_maint_removed(FileData *fd)
465 {
466         gchar *buf;
467
468         buf = cache_find_location(CACHE_TYPE_THUMB, fd->path);
469         cache_file_remove(buf);
470         g_free(buf);
471
472         buf = cache_find_location(CACHE_TYPE_SIM, fd->path);
473         cache_file_remove(buf);
474         g_free(buf);
475
476         buf = cache_find_location(CACHE_TYPE_METADATA, fd->path);
477         cache_file_remove(buf);
478         g_free(buf);
479
480         if (options->thumbnails.enable_caching && options->thumbnails.spec_standard)
481                 thumb_std_maint_removed(fd->path);
482 }
483
484 static void cache_maint_copied(FileData *fd)
485 {
486         gchar *dest_base;
487         gchar *src_cache;
488         mode_t mode = 0755;
489
490         src_cache = cache_find_location(CACHE_TYPE_METADATA, fd->change->source);
491         if (!src_cache) return;
492
493         dest_base = cache_get_location(CACHE_TYPE_METADATA, fd->change->dest, FALSE, &mode);
494         if (recursive_mkdir_if_not_exists(dest_base, mode))
495                 {
496                 gchar *path;
497
498                 path = cache_get_location(CACHE_TYPE_METADATA, fd->change->dest, TRUE, NULL);
499                 if (!copy_file(src_cache, path))
500                         {
501                         DEBUG_1("failed to copy metadata %s to %s", src_cache, path);
502                         }
503                 g_free(path);
504                 }
505
506         g_free(dest_base);
507         g_free(src_cache);
508 }
509
510 void cache_notify_cb(FileData *fd, NotifyType type, gpointer data)
511 {
512         if (!(type & NOTIFY_CHANGE) || !fd->change) return;
513
514         DEBUG_1("Notify cache_maint: %s %04x", fd->path, type);
515         switch (fd->change->type)
516                 {
517                 case FILEDATA_CHANGE_MOVE:
518                 case FILEDATA_CHANGE_RENAME:
519                         cache_maint_moved(fd);
520                         break;
521                 case FILEDATA_CHANGE_COPY:
522                         cache_maint_copied(fd);
523                         break;
524                 case FILEDATA_CHANGE_DELETE:
525                         cache_maint_removed(fd);
526                         break;
527                 case FILEDATA_CHANGE_UNSPECIFIED:
528                 case FILEDATA_CHANGE_WRITE_METADATA:
529                         break;
530                 }
531 }
532
533
534 /*
535  *-------------------------------------------------------------------
536  * new cache maintenance utilities
537  *-------------------------------------------------------------------
538  */
539
540 typedef struct _CacheManager CacheManager;
541 struct _CacheManager
542 {
543         GenericDialog *dialog;
544         GtkWidget *folder_entry;
545         GtkWidget *progress;
546
547         GList *list_todo;
548
549         gint count_total;
550         gint count_done;
551 };
552
553 typedef struct _CacheOpsData CacheOpsData;
554 struct _CacheOpsData
555 {
556         GenericDialog *gd;
557         ThumbLoaderStd *tl;
558         CacheLoader *cl;
559
560         GList *list;
561         GList *list_dir;
562
563         gint days;
564         gboolean clear;
565
566         GtkWidget *button_close;
567         GtkWidget *button_stop;
568         GtkWidget *button_start;
569         GtkWidget *progress;
570         GtkWidget *spinner;
571
572         GtkWidget *group;
573         GtkWidget *entry;
574
575         gint count_total;
576         gint count_done;
577
578         gboolean local;
579         gboolean recurse;
580
581         gboolean remote;
582
583         guint idle_id; /* event source id */
584 };
585
586 static void cache_manager_render_reset(CacheOpsData *cd)
587 {
588         filelist_free(cd->list);
589         cd->list = NULL;
590
591         filelist_free(cd->list_dir);
592         cd->list_dir = NULL;
593
594         thumb_loader_free((ThumbLoader *)cd->tl);
595         cd->tl = NULL;
596 }
597
598 static void cache_manager_render_close_cb(GenericDialog *fd, gpointer data)
599 {
600         CacheOpsData *cd = data;
601
602         if (!gtk_widget_get_sensitive(cd->button_close)) return;
603
604         cache_manager_render_reset(cd);
605         generic_dialog_close(cd->gd);
606         g_free(cd);
607 }
608
609 static void cache_manager_render_finish(CacheOpsData *cd)
610 {
611         cache_manager_render_reset(cd);
612         if (!cd->remote)
613                 {
614                 gtk_entry_set_text(GTK_ENTRY(cd->progress), _("done"));
615                 spinner_set_interval(cd->spinner, -1);
616
617                 gtk_widget_set_sensitive(cd->group, TRUE);
618                 gtk_widget_set_sensitive(cd->button_start, TRUE);
619                 gtk_widget_set_sensitive(cd->button_stop, FALSE);
620                 gtk_widget_set_sensitive(cd->button_close, TRUE);
621                 }
622 }
623
624 static void cache_manager_render_stop_cb(GenericDialog *fd, gpointer data)
625 {
626         CacheOpsData *cd = data;
627
628         cache_manager_render_finish(cd);
629 }
630
631 static void cache_manager_render_folder(CacheOpsData *cd, FileData *dir_fd)
632 {
633         GList *list_d = NULL;
634         GList *list_f = NULL;
635
636         if (cd->recurse)
637                 {
638                 filelist_read(dir_fd, &list_f, &list_d);
639                 }
640         else
641                 {
642                 filelist_read(dir_fd, &list_f, NULL);
643                 }
644
645         list_f = filelist_filter(list_f, FALSE);
646         list_d = filelist_filter(list_d, TRUE);
647
648         cd->list = g_list_concat(list_f, cd->list);
649         cd->list_dir = g_list_concat(list_d, cd->list_dir);
650 }
651
652 static gboolean cache_manager_render_file(CacheOpsData *cd);
653
654 static void cache_manager_render_thumb_done_cb(ThumbLoader *tl, gpointer data)
655 {
656         CacheOpsData *cd = data;
657
658         thumb_loader_free((ThumbLoader *)cd->tl);
659         cd->tl = NULL;
660
661         while (cache_manager_render_file(cd));
662 }
663
664 static gboolean cache_manager_render_file(CacheOpsData *cd)
665 {
666         if (cd->list)
667                 {
668                 FileData *fd;
669                 gint success;
670
671                 fd = cd->list->data;
672                 cd->list = g_list_remove(cd->list, fd);
673
674                 cd->tl = (ThumbLoaderStd *)thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
675                 thumb_loader_set_callbacks((ThumbLoader *)cd->tl,
676                                            cache_manager_render_thumb_done_cb,
677                                            cache_manager_render_thumb_done_cb,
678                                            NULL, cd);
679                 thumb_loader_set_cache((ThumbLoader *)cd->tl, TRUE, cd->local, TRUE);
680                 success = thumb_loader_start((ThumbLoader *)cd->tl, fd);
681                 if (success)
682                         {
683                         if (!cd->remote)
684                                 {
685                                 gtk_entry_set_text(GTK_ENTRY(cd->progress), fd->path);
686                                 }
687                         }
688                 else
689                         {
690                         thumb_loader_free((ThumbLoader *)cd->tl);
691                         cd->tl = NULL;
692                         }
693
694                 file_data_unref(fd);
695
696                 return (!success);
697                 }
698         else if (cd->list_dir)
699                 {
700                 FileData *fd;
701
702                 fd = cd->list_dir->data;
703                 cd->list_dir = g_list_remove(cd->list_dir, fd);
704
705                 cache_manager_render_folder(cd, fd);
706
707                 file_data_unref(fd);
708
709                 return TRUE;
710                 }
711
712         cache_manager_render_finish(cd);
713
714         return FALSE;
715 }
716
717 static void cache_manager_render_start_cb(GenericDialog *fd, gpointer data)
718 {
719         CacheOpsData *cd = data;
720         gchar *path;
721
722         if(!cd->remote)
723                 {
724                 if (cd->list || !gtk_widget_get_sensitive(cd->button_start)) return;
725                 }
726
727         path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(cd->entry))));
728         parse_out_relatives(path);
729
730         if (!isdir(path))
731                 {
732                 if (!cd->remote)
733                         {
734                         warning_dialog(_("Invalid folder"),
735                         _("The specified folder can not be found."),
736                         GTK_STOCK_DIALOG_WARNING, cd->gd->dialog);
737                         }
738                 else
739                         {
740                         log_printf("The specified folder can not be found: %s\n", path);
741                         }
742                 }
743         else
744                 {
745                 FileData *dir_fd;
746                 if(!cd->remote)
747                         {
748                         gtk_widget_set_sensitive(cd->group, FALSE);
749                         gtk_widget_set_sensitive(cd->button_start, FALSE);
750                         gtk_widget_set_sensitive(cd->button_stop, TRUE);
751                         gtk_widget_set_sensitive(cd->button_close, FALSE);
752
753                         spinner_set_interval(cd->spinner, SPINNER_SPEED);
754                         }
755                 dir_fd = file_data_new_dir(path);
756                 cache_manager_render_folder(cd, dir_fd);
757                 file_data_unref(dir_fd);
758                 while (cache_manager_render_file(cd));
759                 }
760
761         g_free(path);
762 }
763
764 static void cache_manager_render_start_render_remote(CacheOpsData *cd, const gchar *user_path)
765 {
766         gchar *path;
767
768         path = remove_trailing_slash(user_path);
769         parse_out_relatives(path);
770
771         if (!isdir(path))
772                 {
773                 log_printf("The specified folder can not be found: %s\n", path);
774                 }
775         else
776                 {
777                 FileData *dir_fd;
778
779                 dir_fd = file_data_new_dir(path);
780                 cache_manager_render_folder(cd, dir_fd);
781                 file_data_unref(dir_fd);
782                 while (cache_manager_render_file(cd));
783                 }
784
785         g_free(path);
786 }
787
788 static void cache_manager_render_dialog(GtkWidget *widget, const gchar *path)
789 {
790         CacheOpsData *cd;
791         GtkWidget *hbox;
792         GtkWidget *label;
793         GtkWidget *button;
794
795         cd = g_new0(CacheOpsData, 1);
796         cd->remote = FALSE;
797
798         cd->gd = generic_dialog_new(_("Create thumbnails"),
799                                     "create_thumbnails",
800                                     widget, FALSE,
801                                     NULL, cd);
802         gtk_window_set_default_size(GTK_WINDOW(cd->gd->dialog), PURGE_DIALOG_WIDTH, -1);
803         cd->gd->cancel_cb = cache_manager_render_close_cb;
804         cd->button_close = generic_dialog_add_button(cd->gd, GTK_STOCK_CLOSE, NULL,
805                                                      cache_manager_render_close_cb, FALSE);
806         cd->button_start = generic_dialog_add_button(cd->gd, GTK_STOCK_OK, _("S_tart"),
807                                                      cache_manager_render_start_cb, FALSE);
808         cd->button_stop = generic_dialog_add_button(cd->gd, GTK_STOCK_STOP, NULL,
809                                                     cache_manager_render_stop_cb, FALSE);
810         gtk_widget_set_sensitive(cd->button_stop, FALSE);
811
812         generic_dialog_add_message(cd->gd, NULL, _("Create thumbnails"), NULL, FALSE);
813
814         hbox = pref_box_new(cd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
815         pref_spacer(hbox, PREF_PAD_INDENT);
816         cd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
817
818         hbox = pref_box_new(cd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
819         pref_label_new(hbox, _("Folder:"));
820
821         label = tab_completion_new(&cd->entry, path, NULL, NULL, NULL, NULL);
822         tab_completion_add_select_button(cd->entry,_("Select folder") , TRUE);
823         gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
824         gtk_widget_show(label);
825
826         pref_checkbox_new_int(cd->group, _("Include subfolders"), FALSE, &cd->recurse);
827         button = pref_checkbox_new_int(cd->group, _("Store thumbnails local to source images"), FALSE, &cd->local);
828         gtk_widget_set_sensitive(button, options->thumbnails.spec_standard);
829
830         pref_line(cd->gd->vbox, PREF_PAD_SPACE);
831         hbox = pref_box_new(cd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
832
833         cd->progress = gtk_entry_new();
834         gtk_widget_set_can_focus(cd->progress, FALSE);
835         gtk_editable_set_editable(GTK_EDITABLE(cd->progress), FALSE);
836         gtk_entry_set_text(GTK_ENTRY(cd->progress), _("click start to begin"));
837         gtk_box_pack_start(GTK_BOX(hbox), cd->progress, TRUE, TRUE, 0);
838         gtk_widget_show(cd->progress);
839
840         cd->spinner = spinner_new(NULL, -1);
841         gtk_box_pack_start(GTK_BOX(hbox), cd->spinner, FALSE, FALSE, 0);
842         gtk_widget_show(cd->spinner);
843
844         cd->list = NULL;
845
846         gtk_widget_show(cd->gd->dialog);
847 }
848
849 void cache_manager_render_remote(const gchar *path, gboolean recurse, gboolean local)
850 {
851         CacheOpsData *cd;
852
853         cd = g_new0(CacheOpsData, 1);
854         cd->recurse = recurse;
855         cd->local = local;
856         cd->remote = TRUE;
857
858         cache_manager_render_start_render_remote(cd, path);
859 }
860
861 static void cache_manager_standard_clean_close_cb(GenericDialog *gd, gpointer data)
862 {
863         CacheOpsData *cd = data;
864
865         if (!gtk_widget_get_sensitive(cd->button_close)) return;
866
867         generic_dialog_close(cd->gd);
868
869         thumb_loader_std_thumb_file_validate_cancel(cd->tl);
870         filelist_free(cd->list);
871         g_free(cd);
872 }
873
874 static void cache_manager_standard_clean_done(CacheOpsData *cd)
875 {
876         if (!cd->remote)
877                 {
878                 gtk_widget_set_sensitive(cd->button_stop, FALSE);
879                 gtk_widget_set_sensitive(cd->button_close, TRUE);
880
881                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(cd->progress), 1.0);
882                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(cd->progress), _("done"));
883                 }
884         if (cd->idle_id)
885                 {
886                 g_source_remove(cd->idle_id);
887                 cd->idle_id = 0;
888                 }
889
890         thumb_loader_std_thumb_file_validate_cancel(cd->tl);
891         cd->tl = NULL;
892
893         filelist_free(cd->list);
894         cd->list = NULL;
895 }
896
897 static void cache_manager_standard_clean_stop_cb(GenericDialog *gd, gpointer data)
898 {
899         CacheOpsData *cd = data;
900
901         cache_manager_standard_clean_done(cd);
902 }
903
904 static gint cache_manager_standard_clean_clear_cb(gpointer data)
905 {
906         CacheOpsData *cd = data;
907
908         if (cd->list)
909                 {
910                 FileData *next_fd;
911
912                 next_fd = cd->list->data;
913                 cd->list = g_list_remove(cd->list, next_fd);
914
915                 DEBUG_1("thumb removed: %s", next_fd->path);
916
917                 unlink_file(next_fd->path);
918                 file_data_unref(next_fd);
919
920                 cd->count_done++;
921                 if (!cd->remote)
922                         {
923                         if (cd->count_total != 0)
924                                 {
925                                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(cd->progress),
926                                                               (gdouble)cd->count_done / cd->count_total);
927                                 }
928                         }
929
930                 return TRUE;
931                 }
932
933         cd->idle_id = 0;
934         cache_manager_standard_clean_done(cd);
935         return FALSE;
936 }
937
938 static void cache_manager_standard_clean_valid_cb(const gchar *path, gboolean valid, gpointer data)
939 {
940         CacheOpsData *cd = data;
941
942         if (path)
943                 {
944                 if (!valid)
945                         {
946                         DEBUG_1("thumb cleaned: %s", path);
947                         unlink_file(path);
948                         }
949
950                 cd->count_done++;
951                 if (!cd->remote)
952                         {
953                         if (cd->count_total != 0)
954                                 {
955                                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(cd->progress),
956                                                               (gdouble)cd->count_done / cd->count_total);
957                                 }
958                         }
959                 }
960
961         cd->tl = NULL;
962         if (cd->list)
963                 {
964                 FileData *next_fd;
965
966                 next_fd = cd->list->data;
967                 cd->list = g_list_remove(cd->list, next_fd);
968
969                 cd->tl = thumb_loader_std_thumb_file_validate(next_fd->path, cd->days,
970                                                               cache_manager_standard_clean_valid_cb, cd);
971                 file_data_unref(next_fd);
972                 }
973         else
974                 {
975                 cache_manager_standard_clean_done(cd);
976                 }
977 }
978
979 static void cache_manager_standard_clean_start(GenericDialog *gd, gpointer data)
980 {
981         CacheOpsData *cd = data;
982         GList *list;
983         gchar *path;
984         FileData *dir_fd;
985
986         if (!cd->remote)
987         {
988                 if (cd->list || !gtk_widget_get_sensitive(cd->button_start)) return;
989
990                 gtk_widget_set_sensitive(cd->button_start, FALSE);
991                 gtk_widget_set_sensitive(cd->button_stop, TRUE);
992                 gtk_widget_set_sensitive(cd->button_close, FALSE);
993
994                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(cd->progress), _("running..."));
995         }
996
997         path = g_build_filename(get_thumbnails_standard_cache_dir(), THUMB_FOLDER_NORMAL, NULL);
998         dir_fd = file_data_new_dir(path);
999         filelist_read(dir_fd, &list, NULL);
1000         cd->list = list;
1001         file_data_unref(dir_fd);
1002         g_free(path);
1003
1004         path = g_build_filename(get_thumbnails_standard_cache_dir(), THUMB_FOLDER_LARGE, NULL);
1005         dir_fd = file_data_new_dir(path);
1006         filelist_read(dir_fd, &list, NULL);
1007         cd->list = g_list_concat(cd->list, list);
1008         file_data_unref(dir_fd);
1009         g_free(path);
1010
1011         path = g_build_filename(get_thumbnails_standard_cache_dir(), THUMB_FOLDER_FAIL, NULL);
1012         dir_fd = file_data_new_dir(path);
1013         filelist_read(dir_fd, &list, NULL);
1014         cd->list = g_list_concat(cd->list, list);
1015         file_data_unref(dir_fd);
1016         g_free(path);
1017
1018         cd->count_total = g_list_length(cd->list);
1019         cd->count_done = 0;
1020
1021         /* start iterating */
1022         if (cd->clear)
1023                 {
1024                 cd->idle_id = g_idle_add(cache_manager_standard_clean_clear_cb, cd);
1025                 }
1026         else
1027                 {
1028                 cache_manager_standard_clean_valid_cb(NULL, TRUE, cd);
1029                 }
1030 }
1031
1032 static void cache_manager_standard_clean_start_cb(GenericDialog *gd, gpointer data)
1033 {
1034         cache_manager_standard_clean_start(gd, data);
1035 }
1036
1037 static void cache_manager_standard_process(GtkWidget *widget, gboolean clear)
1038 {
1039         CacheOpsData *cd;
1040         const gchar *stock_id;
1041         const gchar *msg;
1042
1043         cd = g_new0(CacheOpsData, 1);
1044         cd->clear = clear;
1045         cd->remote = FALSE;
1046
1047         if (clear)
1048                 {
1049                 stock_id = GTK_STOCK_DELETE;
1050                 msg = _("Clearing thumbnails...");
1051                 }
1052         else
1053                 {
1054                 stock_id = GTK_STOCK_CLEAR;
1055                 msg = _("Removing old thumbnails...");
1056                 }
1057
1058         cd->gd = generic_dialog_new(_("Maintenance"),
1059                                     "standard_maintenance",
1060                                     widget, FALSE,
1061                                     NULL, cd);
1062         cd->gd->cancel_cb = cache_manager_standard_clean_close_cb;
1063         cd->button_close = generic_dialog_add_button(cd->gd, GTK_STOCK_CLOSE, NULL,
1064                                                      cache_manager_standard_clean_close_cb, FALSE);
1065         cd->button_start = generic_dialog_add_button(cd->gd, GTK_STOCK_OK, _("S_tart"),
1066                                                      cache_manager_standard_clean_start_cb, FALSE);
1067         cd->button_stop = generic_dialog_add_button(cd->gd, GTK_STOCK_STOP, NULL,
1068                                                     cache_manager_standard_clean_stop_cb, FALSE);
1069         gtk_widget_set_sensitive(cd->button_stop, FALSE);
1070
1071         generic_dialog_add_message(cd->gd, stock_id, msg, NULL, FALSE);
1072
1073         cd->progress = gtk_progress_bar_new();
1074         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(cd->progress), _("click start to begin"));
1075 #if GTK_CHECK_VERSION(3,0,0)
1076         gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(cd->progress), TRUE);
1077 #endif
1078         gtk_box_pack_start(GTK_BOX(cd->gd->vbox), cd->progress, FALSE, FALSE, 0);
1079         gtk_widget_show(cd->progress);
1080
1081         cd->days = 30;
1082         cd->tl = NULL;
1083         cd->idle_id = 0;
1084
1085         gtk_widget_show(cd->gd->dialog);
1086 }
1087
1088 void cache_manager_standard_process_remote(gboolean clear)
1089 {
1090         CacheOpsData *cd;
1091
1092         cd = g_new0(CacheOpsData, 1);
1093         cd->clear = clear;
1094         cd->days = 30;
1095         cd->tl = NULL;
1096         cd->idle_id = 0;
1097         cd->remote = TRUE;
1098
1099         cache_manager_standard_clean_start(NULL, cd);
1100 }
1101
1102 static void cache_manager_standard_clean_cb(GtkWidget *widget, gpointer data)
1103 {
1104         cache_manager_standard_process(widget, FALSE);
1105 }
1106
1107 static void cache_manager_standard_clear_cb(GtkWidget *widget, gpointer data)
1108 {
1109         cache_manager_standard_process(widget, TRUE);
1110 }
1111
1112
1113 static void cache_manager_main_clean_cb(GtkWidget *widget, gpointer data)
1114 {
1115         cache_maintain_home(FALSE, FALSE, widget);
1116 }
1117
1118
1119 static void dummy_cancel_cb(GenericDialog *gd, gpointer data)
1120 {
1121         /* no op, only so cancel button appears */
1122 }
1123
1124 static void cache_manager_main_clear_ok_cb(GenericDialog *gd, gpointer data)
1125 {
1126         cache_maintain_home(FALSE, TRUE, NULL);
1127 }
1128
1129 void cache_manager_main_clear_confirm(GtkWidget *parent)
1130 {
1131         GenericDialog *gd;
1132
1133         gd = generic_dialog_new(_("Clear cache"),
1134                                 "clear_cache", parent, TRUE,
1135                                 dummy_cancel_cb, NULL);
1136         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Clear cache"),
1137                                    _("This will remove all thumbnails and sim. files\nthat have been saved to disk, continue?"), TRUE);
1138         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, cache_manager_main_clear_ok_cb, TRUE);
1139
1140         gtk_widget_show(gd->dialog);
1141 }
1142
1143 static void cache_manager_main_clear_cb(GtkWidget *widget, gpointer data)
1144 {
1145         cache_manager_main_clear_confirm(widget);
1146 }
1147
1148 static void cache_manager_render_cb(GtkWidget *widget, gpointer data)
1149 {
1150         const gchar *path = layout_get_path(NULL);
1151
1152         if (!path || !*path) path = homedir();
1153         cache_manager_render_dialog(widget, path);
1154 }
1155
1156 static void cache_manager_metadata_clean_cb(GtkWidget *widget, gpointer data)
1157 {
1158         cache_maintain_home(TRUE, FALSE, widget);
1159 }
1160
1161
1162 static CacheManager *cache_manager = NULL;
1163
1164 static void cache_manager_close_cb(GenericDialog *gd, gpointer data)
1165 {
1166         generic_dialog_close(gd);
1167
1168         g_free(cache_manager);
1169         cache_manager = NULL;
1170 }
1171
1172 static void cache_manager_help_cb(GenericDialog *gd, gpointer data)
1173 {
1174         help_window_show("GuideReferenceManagement.html");
1175 }
1176
1177 static GtkWidget *cache_manager_location_label(GtkWidget *group, const gchar *subdir)
1178 {
1179         GtkWidget *label;
1180         gchar *buf;
1181
1182         buf = g_strdup_printf(_("Location: %s"), subdir);
1183         label = pref_label_new(group, buf);
1184         g_free(buf);
1185         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1186
1187         return label;
1188 }
1189
1190 static gboolean cache_manager_sim_file(CacheOpsData *cd);
1191
1192 static void cache_manager_sim_reset(CacheOpsData *cd)
1193 {
1194         filelist_free(cd->list);
1195         cd->list = NULL;
1196
1197         filelist_free(cd->list_dir);
1198         cd->list_dir = NULL;
1199
1200         cache_loader_free((CacheLoader *)cd->cl);
1201         cd->cl = NULL;
1202 }
1203
1204 static void cache_manager_sim_close_cb(GenericDialog *fd, gpointer data)
1205 {
1206         CacheOpsData *cd = data;
1207
1208         if (!gtk_widget_get_sensitive(cd->button_close)) return;
1209
1210         cache_manager_sim_reset(cd);
1211         generic_dialog_close(cd->gd);
1212         g_free(cd);
1213 }
1214
1215 static void cache_manager_sim_finish(CacheOpsData *cd)
1216 {
1217         cache_manager_sim_reset(cd);
1218         if (!cd->remote)
1219                 {
1220                 gtk_entry_set_text(GTK_ENTRY(cd->progress), _("done"));
1221                 spinner_set_interval(cd->spinner, -1);
1222
1223                 gtk_widget_set_sensitive(cd->group, TRUE);
1224                 gtk_widget_set_sensitive(cd->button_start, TRUE);
1225                 gtk_widget_set_sensitive(cd->button_stop, FALSE);
1226                 gtk_widget_set_sensitive(cd->button_close, TRUE);
1227                 }
1228 }
1229
1230 static void cache_manager_sim_stop_cb(GenericDialog *fd, gpointer data)
1231 {
1232         CacheOpsData *cd = data;
1233
1234         cache_manager_sim_finish(cd);
1235 }
1236
1237 static void cache_manager_sim_folder(CacheOpsData *cd, FileData *dir_fd)
1238 {
1239         GList *list_d = NULL;
1240         GList *list_f = NULL;
1241
1242         if (cd->recurse)
1243                 {
1244                 filelist_read(dir_fd, &list_f, &list_d);
1245                 }
1246         else
1247                 {
1248                 filelist_read(dir_fd, &list_f, NULL);
1249                 }
1250
1251         list_f = filelist_filter(list_f, FALSE);
1252         list_d = filelist_filter(list_d, TRUE);
1253
1254         cd->list = g_list_concat(list_f, cd->list);
1255         cd->list_dir = g_list_concat(list_d, cd->list_dir);
1256 }
1257
1258 static void cache_manager_sim_file_done_cb(CacheLoader *cl, gint error, gpointer data)
1259 {
1260         CacheOpsData *cd = data;
1261
1262         cache_loader_free((CacheLoader *)cd->cl);
1263         cd->cl = NULL;
1264
1265         while (cache_manager_sim_file(cd));
1266 }
1267
1268 static gboolean cache_manager_sim_file(CacheOpsData *cd)
1269 {
1270         CacheDataType load_mask;
1271
1272         if (cd->list)
1273                 {
1274                 FileData *fd;
1275                 fd = cd->list->data;
1276                 cd->list = g_list_remove(cd->list, fd);
1277
1278                 load_mask = CACHE_LOADER_DIMENSIONS | CACHE_LOADER_DATE | CACHE_LOADER_MD5SUM | CACHE_LOADER_SIMILARITY;
1279                 cd->cl = (CacheLoader *)cache_loader_new(fd, load_mask, (cache_manager_sim_file_done_cb), cd);
1280
1281                 gtk_entry_set_text(GTK_ENTRY(cd->progress), fd->path);
1282
1283                 file_data_unref(fd);
1284
1285                 return FALSE;
1286                 }
1287         else if (cd->list_dir)
1288                 {
1289                 FileData *fd;
1290
1291                 fd = cd->list_dir->data;
1292                 cd->list_dir = g_list_remove(cd->list_dir, fd);
1293
1294                 cache_manager_sim_folder((CacheOpsData *)cd, fd);
1295                 file_data_unref(fd);
1296
1297                 return TRUE;
1298                 }
1299
1300         cache_manager_sim_finish((CacheOpsData *)cd);
1301
1302         return FALSE;
1303 }
1304
1305 static void cache_manager_sim_start_cb(GenericDialog *fd, gpointer data)
1306 {
1307         CacheOpsData *cd = data;
1308         gchar *path;
1309
1310         if (!cd->remote)
1311                 {
1312                 if (cd->list || !gtk_widget_get_sensitive(cd->button_start)) return;
1313                 }
1314
1315         path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(cd->entry))));
1316         parse_out_relatives(path);
1317
1318         if (!isdir(path))
1319                 {
1320                 if (!cd->remote)
1321                         {
1322                         warning_dialog(_("Invalid folder"),
1323                         _("The specified folder can not be found."),
1324                         GTK_STOCK_DIALOG_WARNING, cd->gd->dialog);
1325                         }
1326                 else
1327                         {
1328                         log_printf("The specified folder can not be found: %s\n", path);
1329                         }
1330                 }
1331         else
1332                 {
1333                 FileData *dir_fd;
1334                 if(!cd->remote)
1335                         {
1336                         gtk_widget_set_sensitive(cd->group, FALSE);
1337                         gtk_widget_set_sensitive(cd->button_start, FALSE);
1338                         gtk_widget_set_sensitive(cd->button_stop, TRUE);
1339                         gtk_widget_set_sensitive(cd->button_close, FALSE);
1340
1341                         spinner_set_interval(cd->spinner, SPINNER_SPEED);
1342                         }
1343                 dir_fd = file_data_new_dir(path);
1344                 cache_manager_sim_folder(cd, dir_fd);
1345                 file_data_unref(dir_fd);
1346
1347                 while (cache_manager_sim_file((CacheOpsData *)cd));
1348                 }
1349
1350         g_free(path);
1351 }
1352
1353 static void cache_manager_sim_load_dialog(GtkWidget *widget, const gchar *path)
1354 {
1355         CacheOpsData *cd;
1356         GtkWidget *hbox;
1357         GtkWidget *label;
1358
1359         cd = g_new0(CacheOpsData, 1);
1360         cd->remote = FALSE;
1361         cd->recurse = TRUE;
1362
1363         cd->gd = generic_dialog_new(_("Create sim. files"), "create_sim_files", widget, FALSE, NULL, cd);
1364         gtk_window_set_default_size(GTK_WINDOW(cd->gd->dialog), PURGE_DIALOG_WIDTH, -1);
1365         cd->gd->cancel_cb = cache_manager_sim_close_cb;
1366         cd->button_close = generic_dialog_add_button(cd->gd, GTK_STOCK_CLOSE, NULL,
1367                                                      cache_manager_sim_close_cb, FALSE);
1368         cd->button_start = generic_dialog_add_button(cd->gd, GTK_STOCK_OK, _("S_tart"),
1369                                                      cache_manager_sim_start_cb, FALSE);
1370         cd->button_stop = generic_dialog_add_button(cd->gd, GTK_STOCK_STOP, NULL,
1371                                                     cache_manager_sim_stop_cb, FALSE);
1372         gtk_widget_set_sensitive(cd->button_stop, FALSE);
1373
1374         generic_dialog_add_message(cd->gd, NULL, _("Create sim. files recursively"), NULL, FALSE);
1375
1376         hbox = pref_box_new(cd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
1377         pref_spacer(hbox, PREF_PAD_INDENT);
1378         cd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1379
1380         hbox = pref_box_new(cd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1381         pref_label_new(hbox, _("Folder:"));
1382
1383         label = tab_completion_new(&cd->entry, path, NULL, NULL, NULL, NULL);
1384         tab_completion_add_select_button(cd->entry,_("Select folder") , TRUE);
1385         gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
1386         gtk_widget_show(label);
1387
1388         pref_line(cd->gd->vbox, PREF_PAD_SPACE);
1389         hbox = pref_box_new(cd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1390
1391         cd->progress = gtk_entry_new();
1392         gtk_widget_set_can_focus(cd->progress, FALSE);
1393         gtk_editable_set_editable(GTK_EDITABLE(cd->progress), FALSE);
1394         gtk_entry_set_text(GTK_ENTRY(cd->progress), _("click start to begin"));
1395         gtk_box_pack_start(GTK_BOX(hbox), cd->progress, TRUE, TRUE, 0);
1396         gtk_widget_show(cd->progress);
1397
1398         cd->spinner = spinner_new(NULL, -1);
1399         gtk_box_pack_start(GTK_BOX(hbox), cd->spinner, FALSE, FALSE, 0);
1400         gtk_widget_show(cd->spinner);
1401
1402         cd->list = NULL;
1403
1404         gtk_widget_show(cd->gd->dialog);
1405 }
1406
1407 static void cache_manager_sim_clear_ok_cb(GenericDialog *gd, gpointer data)
1408 {
1409         cache_maintain_home(FALSE, TRUE, NULL);
1410 }
1411
1412 static void cache_manager_sim_load_cb(GtkWidget *widget, gpointer data)
1413 {
1414         const gchar *path = layout_get_path(NULL);
1415
1416         if (!path || !*path) path = homedir();
1417         cache_manager_sim_load_dialog(widget, path);
1418 }
1419
1420 void cache_manager_show(void)
1421 {
1422         GenericDialog *gd;
1423         GtkWidget *group;
1424         GtkWidget *button;
1425         GtkWidget *table;
1426         GtkSizeGroup *sizegroup;
1427         gchar *path;
1428
1429         if (cache_manager)
1430                 {
1431                 gtk_window_present(GTK_WINDOW(cache_manager->dialog->dialog));
1432                 return;
1433                 }
1434
1435         cache_manager = g_new0(CacheManager, 1);
1436
1437         cache_manager->dialog = generic_dialog_new(_("Cache Maintenance"),
1438                                                    "cache_manager",
1439                                                    NULL, FALSE,
1440                                                    NULL, cache_manager);
1441         gd = cache_manager->dialog;
1442
1443         gd->cancel_cb = cache_manager_close_cb;
1444         generic_dialog_add_button(gd, GTK_STOCK_CLOSE, NULL,
1445                                   cache_manager_close_cb, FALSE);
1446         generic_dialog_add_button(gd, GTK_STOCK_HELP, NULL,
1447                                   cache_manager_help_cb, FALSE);
1448
1449         generic_dialog_add_message(gd, NULL, _("Cache and Data Maintenance"), NULL, FALSE);
1450
1451         sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1452
1453         group = pref_group_new(gd->vbox, FALSE, _("Geeqie thumbnail and sim. cache"), GTK_ORIENTATION_VERTICAL);
1454
1455         cache_manager_location_label(group, get_thumbnails_cache_dir());
1456
1457         table = pref_table_new(group, 2, 2, FALSE, FALSE);
1458
1459         button = pref_table_button(table, 0, 0, GTK_STOCK_CLEAR, _("Clean up"), FALSE,
1460                                    G_CALLBACK(cache_manager_main_clean_cb), cache_manager);
1461         gtk_size_group_add_widget(sizegroup, button);
1462         pref_table_label(table, 1, 0, _("Remove orphaned or outdated thumbnails and sim. files."), 0.0);
1463
1464         button = pref_table_button(table, 0, 1, GTK_STOCK_DELETE, _("Clear cache"), FALSE,
1465                                    G_CALLBACK(cache_manager_main_clear_cb), cache_manager);
1466         gtk_size_group_add_widget(sizegroup, button);
1467         pref_table_label(table, 1, 1, _("Delete all cached data."), 0.0);
1468
1469
1470         group = pref_group_new(gd->vbox, FALSE, _("Shared thumbnail cache"), GTK_ORIENTATION_VERTICAL);
1471
1472         path = g_build_filename(get_thumbnails_standard_cache_dir(), NULL);
1473         cache_manager_location_label(group, path);
1474         g_free(path);
1475
1476         table = pref_table_new(group, 2, 2, FALSE, FALSE);
1477
1478         button = pref_table_button(table, 0, 0, GTK_STOCK_CLEAR, _("Clean up"), FALSE,
1479                                    G_CALLBACK(cache_manager_standard_clean_cb), cache_manager);
1480         gtk_size_group_add_widget(sizegroup, button);
1481         pref_table_label(table, 1, 0, _("Remove orphaned or outdated thumbnails."), 0.0);
1482
1483         button = pref_table_button(table, 0, 1, GTK_STOCK_DELETE, _("Clear cache"), FALSE,
1484                                    G_CALLBACK(cache_manager_standard_clear_cb), cache_manager);
1485         gtk_size_group_add_widget(sizegroup, button);
1486         pref_table_label(table, 1, 1, _("Delete all cached thumbnails."), 0.0);
1487
1488         group = pref_group_new(gd->vbox, FALSE, _("Create thumbnails"), GTK_ORIENTATION_VERTICAL);
1489
1490         table = pref_table_new(group, 2, 1, FALSE, FALSE);
1491
1492         button = pref_table_button(table, 0, 1, GTK_STOCK_EXECUTE, _("Render"), FALSE,
1493                                    G_CALLBACK(cache_manager_render_cb), cache_manager);
1494         gtk_size_group_add_widget(sizegroup, button);
1495         pref_table_label(table, 1, 1, _("Render thumbnails for a specific folder."), 0.0);
1496         gtk_widget_set_sensitive(group, options->thumbnails.enable_caching);
1497
1498         group = pref_group_new(gd->vbox, FALSE, _("File similarity cache"), GTK_ORIENTATION_VERTICAL);
1499
1500         table = pref_table_new(group, 3, 2, FALSE, FALSE);
1501
1502         button = pref_table_button(table, 0, 0, GTK_STOCK_EXECUTE, _("Create"), FALSE,
1503                                    G_CALLBACK(cache_manager_sim_load_cb), cache_manager);
1504         gtk_size_group_add_widget(sizegroup, button);
1505         pref_table_label(table, 1, 0, _("Create sim. files recursively."), 0.0);
1506         gtk_widget_set_sensitive(group, options->thumbnails.enable_caching);
1507
1508         group = pref_group_new(gd->vbox, FALSE, _("Metadata"), GTK_ORIENTATION_VERTICAL);
1509
1510         cache_manager_location_label(group, get_metadata_cache_dir());
1511
1512         table = pref_table_new(group, 2, 1, FALSE, FALSE);
1513
1514         button = pref_table_button(table, 0, 0, GTK_STOCK_CLEAR, _("Clean up"), FALSE,
1515                                    G_CALLBACK(cache_manager_metadata_clean_cb), cache_manager);
1516         gtk_size_group_add_widget(sizegroup, button);
1517         pref_table_label(table, 1, 0, _("Remove orphaned keywords and comments."), 0.0);
1518
1519         gtk_widget_show(cache_manager->dialog->dialog);
1520 }
1521 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */