Trim trailing white spaces on empty lines.
[geeqie.git] / src / cache_maint.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  * Copyright (C) 2008 - 2012 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13 #include "main.h"
14 #include "cache_maint.h"
15
16 #include "cache.h"
17 #include "filedata.h"
18 #include "layout.h"
19 #include "thumb.h"
20 #include "thumb_standard.h"
21 #include "ui_fileops.h"
22 #include "ui_misc.h"
23 #include "ui_spinner.h"
24 #include "ui_tabcomp.h"
25 #include "ui_utildlg.h"
26
27
28 typedef struct _CMData CMData;
29 struct _CMData
30 {
31         GList *list;
32         GList *done_list;
33         guint idle_id; /* event source id */
34         GenericDialog *gd;
35         GtkWidget *entry;
36         GtkWidget *spinner;
37         GtkWidget *button_stop;
38         GtkWidget *button_close;
39         gboolean clear;
40         gboolean metadata;
41 };
42
43 #define PURGE_DIALOG_WIDTH 400
44
45
46 /*
47  *-------------------------------------------------------------------
48  * cache maintenance
49  *-------------------------------------------------------------------
50  */
51
52 static gchar *extension_find_dot(gchar *path)
53 {
54         gchar *dot = NULL;
55
56         if (!path) return NULL;
57
58         while (*path != '\0')
59                 {
60                 if (*path == '.') dot = path;
61                 path++;
62                 }
63
64         return dot;
65 }
66
67 static gboolean isempty(const gchar *path)
68 {
69         DIR *dp;
70         struct dirent *dir;
71         gchar *pathl;
72
73         pathl = path_from_utf8(path);
74         dp = opendir(pathl);
75         g_free(pathl);
76         if (!dp) return FALSE;
77
78         while ((dir = readdir(dp)) != NULL)
79                 {
80                 gchar *name = dir->d_name;
81
82                 if (!(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) )
83                         {
84                         closedir(dp);
85                         return FALSE;
86                         }
87                 }
88
89         closedir(dp);
90         return TRUE;
91 }
92
93 static void cache_maintain_home_close(CMData *cm)
94 {
95         if (cm->idle_id) g_source_remove(cm->idle_id);
96         if (cm->gd) generic_dialog_close(cm->gd);
97         filelist_free(cm->list);
98         g_list_free(cm->done_list);
99         g_free(cm);
100 }
101
102 static void cache_maintain_home_stop(CMData *cm)
103 {
104         if (cm->idle_id)
105                 {
106                 g_source_remove(cm->idle_id);
107                 cm->idle_id = 0;
108                 }
109
110         gtk_entry_set_text(GTK_ENTRY(cm->entry), _("done"));
111         spinner_set_interval(cm->spinner, -1);
112
113         gtk_widget_set_sensitive(cm->button_stop, FALSE);
114         gtk_widget_set_sensitive(cm->button_close, TRUE);
115 }
116
117 static gboolean cache_maintain_home_cb(gpointer data)
118 {
119         CMData *cm = data;
120         GList *dlist = NULL;
121         GList *list = NULL;
122         FileData *fd;
123         gboolean just_done = FALSE;
124         gboolean still_have_a_file = TRUE;
125         gsize base_length;
126         const gchar *cache_folder;
127
128         if (cm->metadata)
129                 {
130                 cache_folder = get_metadata_cache_dir();
131                 }
132         else
133                 {
134                 cache_folder = get_thumbnails_cache_dir();
135                 }
136
137         base_length = strlen(cache_folder);
138
139         if (!cm->list)
140                 {
141                 DEBUG_1("purge chk done.");
142                 cm->idle_id = 0;
143                 cache_maintain_home_stop(cm);
144                 return FALSE;
145                 }
146
147         fd = cm->list->data;
148
149         DEBUG_1("purge chk (%d) \"%s\"", (cm->clear && !cm->metadata), fd->path);
150
151         if (g_list_find(cm->done_list, fd) == NULL)
152                 {
153                 cm->done_list = g_list_prepend(cm->done_list, fd);
154
155                 if (filelist_read(fd, &list, &dlist))
156                         {
157                         GList *work;
158
159                         just_done = TRUE;
160                         still_have_a_file = FALSE;
161
162                         work = list;
163                         while (work)
164                                 {
165                                 FileData *fd_list = work->data;
166                                 gchar *path_buf = g_strdup(fd_list->path);
167                                 gchar *dot;
168
169                                 dot = extension_find_dot(path_buf);
170
171                                 if (dot) *dot = '\0';
172                                 if ((!cm->metadata && cm->clear) ||
173                                     (strlen(path_buf) > base_length && !isfile(path_buf + base_length)) )
174                                         {
175                                         if (dot) *dot = '.';
176                                         if (!unlink_file(path_buf)) log_printf("failed to delete:%s\n", path_buf);
177                                         }
178                                 else
179                                         {
180                                         still_have_a_file = TRUE;
181                                         }
182                                 g_free(path_buf);
183                                 work = work->next;
184                                 }
185                         }
186                 }
187         filelist_free(list);
188
189         cm->list = g_list_concat(dlist, cm->list);
190
191         if (cm->list && g_list_find(cm->done_list, cm->list->data) != NULL)
192                 {
193                 /* check if the dir is empty */
194
195                 if (cm->list->data == fd && just_done)
196                         {
197                         if (!still_have_a_file && !dlist && cm->list->next && !rmdir_utf8(fd->path))
198                                 {
199                                 log_printf("Unable to delete dir: %s\n", fd->path);
200                                 }
201                         }
202                 else
203                         {
204                         /* must re-check for an empty dir */
205                         if (isempty(fd->path) && cm->list->next && !rmdir_utf8(fd->path))
206                                 {
207                                 log_printf("Unable to delete dir: %s\n", fd->path);
208                                 }
209                         }
210
211                 fd = cm->list->data;
212                 cm->done_list = g_list_remove(cm->done_list, fd);
213                 cm->list = g_list_remove(cm->list, fd);
214                 file_data_unref(fd);
215                 }
216
217         if (cm->list)
218                 {
219                 const gchar *buf;
220
221                 fd = cm->list->data;
222                 if (strlen(fd->path) > base_length)
223                         {
224                         buf = fd->path + base_length;
225                         }
226                 else
227                         {
228                         buf = "...";
229                         }
230                 gtk_entry_set_text(GTK_ENTRY(cm->entry), buf);
231                 }
232
233         return TRUE;
234 }
235
236 static void cache_maintain_home_close_cb(GenericDialog *gd, gpointer data)
237 {
238         CMData *cm = data;
239
240         if (!gtk_widget_get_sensitive(cm->button_close)) return;
241
242         cache_maintain_home_close(cm);
243 }
244
245 static void cache_maintain_home_stop_cb(GenericDialog *gd, gpointer data)
246 {
247         CMData *cm = data;
248
249         cache_maintain_home_stop(cm);
250 }
251
252 /* sorry for complexity (cm->done_list), but need it to remove empty dirs */
253 void cache_maintain_home(gboolean metadata, gboolean clear, GtkWidget *parent)
254 {
255         CMData *cm;
256         GList *dlist;
257         FileData *dir_fd;
258         const gchar *msg;
259         const gchar *cache_folder;
260         GtkWidget *hbox;
261
262         if (metadata)
263                 {
264                 cache_folder = get_metadata_cache_dir();
265                 }
266         else
267                 {
268                 cache_folder = get_thumbnails_cache_dir();
269                 }
270
271         dir_fd = file_data_new_dir(cache_folder);
272         if (!filelist_read(dir_fd, NULL, &dlist))
273                 {
274                 file_data_unref(dir_fd);
275                 return;
276                 }
277
278         dlist = g_list_append(dlist, dir_fd);
279
280         cm = g_new0(CMData, 1);
281         cm->list = dlist;
282         cm->done_list = NULL;
283         cm->clear = clear;
284         cm->metadata = metadata;
285
286         if (metadata)
287                 {
288                 msg = _("Removing old metadata...");
289                 }
290         else if (clear)
291                 {
292                 msg = _("Clearing cached thumbnails...");
293                 }
294         else
295                 {
296                 msg = _("Removing old thumbnails...");
297                 }
298
299         cm->gd = generic_dialog_new(_("Maintenance"),
300                                     "main_maintenance",
301                                     parent, FALSE,
302                                     NULL, cm);
303         cm->gd->cancel_cb = cache_maintain_home_close_cb;
304         cm->button_close = generic_dialog_add_button(cm->gd, GTK_STOCK_CLOSE, NULL,
305                                                      cache_maintain_home_close_cb, FALSE);
306         gtk_widget_set_sensitive(cm->button_close, FALSE);
307         cm->button_stop = generic_dialog_add_button(cm->gd, GTK_STOCK_STOP, NULL,
308                                                     cache_maintain_home_stop_cb, FALSE);
309
310         generic_dialog_add_message(cm->gd, NULL, msg, NULL);
311         gtk_window_set_default_size(GTK_WINDOW(cm->gd->dialog), PURGE_DIALOG_WIDTH, -1);
312
313         hbox = gtk_hbox_new(FALSE, 0);
314         gtk_box_pack_start(GTK_BOX(cm->gd->vbox), hbox, FALSE, FALSE, 5);
315         gtk_widget_show(hbox);
316
317         cm->entry = gtk_entry_new();
318         gtk_widget_set_can_focus(cm->entry, FALSE);
319         gtk_editable_set_editable(GTK_EDITABLE(cm->entry), FALSE);
320         gtk_box_pack_start(GTK_BOX(hbox), cm->entry, TRUE, TRUE, 0);
321         gtk_widget_show(cm->entry);
322
323         cm->spinner = spinner_new(NULL, SPINNER_SPEED);
324         gtk_box_pack_start(GTK_BOX(hbox), cm->spinner, FALSE, FALSE, 0);
325         gtk_widget_show(cm->spinner);
326
327         gtk_widget_show(cm->gd->dialog);
328
329         cm->idle_id = g_idle_add(cache_maintain_home_cb, cm);
330 }
331
332 static void cache_file_move(const gchar *src, const gchar *dest)
333 {
334         if (!dest || !src || !isfile(src)) return;
335
336         if (!move_file(src, dest))
337                 {
338                 DEBUG_1("Failed to move cache file \"%s\" to \"%s\"", src, dest);
339                 /* we remove it anyway - it's stale */
340                 unlink_file(src);
341                 }
342 }
343
344 static void cache_maint_moved(FileData *fd)
345 {
346         gchar *base;
347         mode_t mode = 0755;
348         const gchar *src = fd->change->source;
349         const gchar *dest = fd->change->dest;
350
351         if (!src || !dest) return;
352
353         base = cache_get_location(CACHE_TYPE_THUMB, dest, FALSE, &mode);
354         if (recursive_mkdir_if_not_exists(base, mode))
355                 {
356                 gchar *buf;
357                 gchar *d;
358
359                 buf = cache_find_location(CACHE_TYPE_THUMB, src);
360                 d = cache_get_location(CACHE_TYPE_THUMB, dest, TRUE, NULL);
361                 cache_file_move(buf, d);
362                 g_free(d);
363                 g_free(buf);
364
365                 buf = cache_find_location(CACHE_TYPE_SIM, src);
366                 d = cache_get_location(CACHE_TYPE_SIM, dest, TRUE, NULL);
367                 cache_file_move(buf, d);
368                 g_free(d);
369                 g_free(buf);
370                 }
371         else
372                 {
373                 log_printf("Failed to create cache dir for move %s\n", base);
374                 }
375         g_free(base);
376
377         base = cache_get_location(CACHE_TYPE_METADATA, dest, FALSE, &mode);
378         if (recursive_mkdir_if_not_exists(base, mode))
379                 {
380                 gchar *buf;
381                 gchar *d;
382
383                 buf = cache_find_location(CACHE_TYPE_METADATA, src);
384                 d = cache_get_location(CACHE_TYPE_METADATA, dest, TRUE, NULL);
385                 cache_file_move(buf, d);
386                 g_free(d);
387                 g_free(buf);
388                 }
389         g_free(base);
390
391         if (options->thumbnails.enable_caching && options->thumbnails.spec_standard)
392                 thumb_std_maint_moved(src, dest);
393 }
394
395 static void cache_file_remove(const gchar *path)
396 {
397         if (path && isfile(path) && !unlink_file(path))
398                 {
399                 DEBUG_1("Failed to remove cache file %s", path);
400                 }
401 }
402
403 static void cache_maint_removed(FileData *fd)
404 {
405         gchar *buf;
406
407         buf = cache_find_location(CACHE_TYPE_THUMB, fd->path);
408         cache_file_remove(buf);
409         g_free(buf);
410
411         buf = cache_find_location(CACHE_TYPE_SIM, fd->path);
412         cache_file_remove(buf);
413         g_free(buf);
414
415         buf = cache_find_location(CACHE_TYPE_METADATA, fd->path);
416         cache_file_remove(buf);
417         g_free(buf);
418
419         if (options->thumbnails.enable_caching && options->thumbnails.spec_standard)
420                 thumb_std_maint_removed(fd->path);
421 }
422
423 static void cache_maint_copied(FileData *fd)
424 {
425         gchar *dest_base;
426         gchar *src_cache;
427         mode_t mode = 0755;
428
429         src_cache = cache_find_location(CACHE_TYPE_METADATA, fd->change->source);
430         if (!src_cache) return;
431
432         dest_base = cache_get_location(CACHE_TYPE_METADATA, fd->change->dest, FALSE, &mode);
433         if (recursive_mkdir_if_not_exists(dest_base, mode))
434                 {
435                 gchar *path;
436
437                 path = cache_get_location(CACHE_TYPE_METADATA, fd->change->dest, TRUE, NULL);
438                 if (!copy_file(src_cache, path))
439                         {
440                         DEBUG_1("failed to copy metadata %s to %s", src_cache, path);
441                         }
442                 g_free(path);
443                 }
444
445         g_free(dest_base);
446         g_free(src_cache);
447 }
448
449 void cache_notify_cb(FileData *fd, NotifyType type, gpointer data)
450 {
451         if (!(type & NOTIFY_CHANGE) || !fd->change) return;
452
453         DEBUG_1("Notify cache_maint: %s %04x", fd->path, type);
454         switch (fd->change->type)
455                 {
456                 case FILEDATA_CHANGE_MOVE:
457                 case FILEDATA_CHANGE_RENAME:
458                         cache_maint_moved(fd);
459                         break;
460                 case FILEDATA_CHANGE_COPY:
461                         cache_maint_copied(fd);
462                         break;
463                 case FILEDATA_CHANGE_DELETE:
464                         cache_maint_removed(fd);
465                         break;
466                 case FILEDATA_CHANGE_UNSPECIFIED:
467                 case FILEDATA_CHANGE_WRITE_METADATA:
468                         break;
469                 }
470 }
471
472
473 /*
474  *-------------------------------------------------------------------
475  * new cache maintenance utilities
476  *-------------------------------------------------------------------
477  */
478
479 typedef struct _CacheManager CacheManager;
480 struct _CacheManager
481 {
482         GenericDialog *dialog;
483         GtkWidget *folder_entry;
484         GtkWidget *progress;
485
486         GList *list_todo;
487
488         gint count_total;
489         gint count_done;
490 };
491
492 typedef struct _CleanData CleanData;
493 struct _CleanData
494 {
495         GenericDialog *gd;
496         ThumbLoaderStd *tl;
497
498         GList *list;
499         GList *list_dir;
500
501         gint days;
502         gboolean clear;
503
504         GtkWidget *button_close;
505         GtkWidget *button_stop;
506         GtkWidget *button_start;
507         GtkWidget *progress;
508         GtkWidget *spinner;
509
510         GtkWidget *group;
511         GtkWidget *entry;
512
513         gint count_total;
514         gint count_done;
515
516         gboolean local;
517         gboolean recurse;
518
519         guint idle_id; /* event source id */
520 };
521
522 static void cache_manager_render_reset(CleanData *cd)
523 {
524         filelist_free(cd->list);
525         cd->list = NULL;
526
527         filelist_free(cd->list_dir);
528         cd->list_dir = NULL;
529
530         thumb_loader_free((ThumbLoader *)cd->tl);
531         cd->tl = NULL;
532 }
533
534 static void cache_manager_render_close_cb(GenericDialog *fd, gpointer data)
535 {
536         CleanData *cd = data;
537
538         if (!gtk_widget_get_sensitive(cd->button_close)) return;
539
540         cache_manager_render_reset(cd);
541         generic_dialog_close(cd->gd);
542         g_free(cd);
543 }
544
545 static void cache_manager_render_finish(CleanData *cd)
546 {
547         cache_manager_render_reset(cd);
548
549         gtk_entry_set_text(GTK_ENTRY(cd->progress), _("done"));
550         spinner_set_interval(cd->spinner, -1);
551
552         gtk_widget_set_sensitive(cd->group, TRUE);
553         gtk_widget_set_sensitive(cd->button_start, TRUE);
554         gtk_widget_set_sensitive(cd->button_stop, FALSE);
555         gtk_widget_set_sensitive(cd->button_close, TRUE);
556 }
557
558 static void cache_manager_render_stop_cb(GenericDialog *fd, gpointer data)
559 {
560         CleanData *cd = data;
561
562         cache_manager_render_finish(cd);
563 }
564
565 static void cache_manager_render_folder(CleanData *cd, FileData *dir_fd)
566 {
567         GList *list_d = NULL;
568         GList *list_f = NULL;
569
570         if (cd->recurse)
571                 {
572                 filelist_read(dir_fd, &list_f, &list_d);
573                 }
574         else
575                 {
576                 filelist_read(dir_fd, &list_f, NULL);
577                 }
578
579         list_f = filelist_filter(list_f, FALSE);
580         list_d = filelist_filter(list_d, TRUE);
581
582         cd->list = g_list_concat(list_f, cd->list);
583         cd->list_dir = g_list_concat(list_d, cd->list_dir);
584 }
585
586 static gboolean cache_manager_render_file(CleanData *cd);
587
588 static void cache_manager_render_thumb_done_cb(ThumbLoader *tl, gpointer data)
589 {
590         CleanData *cd = data;
591
592         thumb_loader_free((ThumbLoader *)cd->tl);
593         cd->tl = NULL;
594
595         while (cache_manager_render_file(cd));
596 }
597
598 static gboolean cache_manager_render_file(CleanData *cd)
599 {
600         if (cd->list)
601                 {
602                 FileData *fd;
603                 gint success;
604
605                 fd = cd->list->data;
606                 cd->list = g_list_remove(cd->list, fd);
607
608                 cd->tl = (ThumbLoaderStd *)thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
609                 thumb_loader_set_callbacks((ThumbLoader *)cd->tl,
610                                            cache_manager_render_thumb_done_cb,
611                                            cache_manager_render_thumb_done_cb,
612                                            NULL, cd);
613                 thumb_loader_set_cache((ThumbLoader *)cd->tl, TRUE, cd->local, TRUE);
614                 success = thumb_loader_start((ThumbLoader *)cd->tl, fd);
615                 if (success)
616                         {
617                         gtk_entry_set_text(GTK_ENTRY(cd->progress), fd->path);
618                         }
619                 else
620                         {
621                         thumb_loader_free((ThumbLoader *)cd->tl);
622                         cd->tl = NULL;
623                         }
624
625                 file_data_unref(fd);
626
627                 return (!success);
628                 }
629         else if (cd->list_dir)
630                 {
631                 FileData *fd;
632
633                 fd = cd->list_dir->data;
634                 cd->list_dir = g_list_remove(cd->list_dir, fd);
635
636                 cache_manager_render_folder(cd, fd);
637
638                 file_data_unref(fd);
639
640                 return TRUE;
641                 }
642
643         cache_manager_render_finish(cd);
644
645         return FALSE;
646 }
647
648 static void cache_manager_render_start_cb(GenericDialog *fd, gpointer data)
649 {
650         CleanData *cd = data;
651         gchar *path;
652
653         if (cd->list || !gtk_widget_get_sensitive(cd->button_start)) return;
654
655         path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(cd->entry))));
656         parse_out_relatives(path);
657
658         if (!isdir(path))
659                 {
660                 warning_dialog(_("Invalid folder"),
661                                 _("The specified folder can not be found."),
662                                GTK_STOCK_DIALOG_WARNING, cd->gd->dialog);
663                 }
664         else
665                 {
666                 FileData *dir_fd;
667                 gtk_widget_set_sensitive(cd->group, FALSE);
668                 gtk_widget_set_sensitive(cd->button_start, FALSE);
669                 gtk_widget_set_sensitive(cd->button_stop, TRUE);
670                 gtk_widget_set_sensitive(cd->button_close, FALSE);
671
672                 spinner_set_interval(cd->spinner, SPINNER_SPEED);
673
674                 dir_fd = file_data_new_dir(path);
675                 cache_manager_render_folder(cd, dir_fd);
676                 file_data_unref(dir_fd);
677                 while (cache_manager_render_file(cd));
678                 }
679
680         g_free(path);
681 }
682
683 static void cache_manager_render_dialog(GtkWidget *widget, const gchar *path)
684 {
685         CleanData *cd;
686         GtkWidget *hbox;
687         GtkWidget *label;
688         GtkWidget *button;
689
690         cd = g_new0(CleanData, 1);
691
692         cd->gd = generic_dialog_new(_("Create thumbnails"),
693                                     "create_thumbnails",
694                                     widget, FALSE,
695                                     NULL, cd);
696         gtk_window_set_default_size(GTK_WINDOW(cd->gd->dialog), PURGE_DIALOG_WIDTH, -1);
697         cd->gd->cancel_cb = cache_manager_render_close_cb;
698         cd->button_close = generic_dialog_add_button(cd->gd, GTK_STOCK_CLOSE, NULL,
699                                                      cache_manager_render_close_cb, FALSE);
700         cd->button_start = generic_dialog_add_button(cd->gd, GTK_STOCK_OK, _("S_tart"),
701                                                      cache_manager_render_start_cb, FALSE);
702         cd->button_stop = generic_dialog_add_button(cd->gd, GTK_STOCK_STOP, NULL,
703                                                     cache_manager_render_stop_cb, FALSE);
704         gtk_widget_set_sensitive(cd->button_stop, FALSE);
705
706         generic_dialog_add_message(cd->gd, NULL, _("Create thumbnails"), NULL);
707
708         hbox = pref_box_new(cd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
709         pref_spacer(hbox, PREF_PAD_INDENT);
710         cd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
711
712         hbox = pref_box_new(cd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
713         pref_label_new(hbox, _("Folder:"));
714
715         label = tab_completion_new(&cd->entry, path, NULL, NULL);
716         tab_completion_add_select_button(cd->entry,_("Select folder") , TRUE);
717         gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
718         gtk_widget_show(label);
719
720         pref_checkbox_new_int(cd->group, _("Include subfolders"), FALSE, &cd->recurse);
721         button = pref_checkbox_new_int(cd->group, _("Store thumbnails local to source images"), FALSE, &cd->local);
722         gtk_widget_set_sensitive(button, options->thumbnails.spec_standard);
723
724         pref_line(cd->gd->vbox, PREF_PAD_SPACE);
725         hbox = pref_box_new(cd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
726
727         cd->progress = gtk_entry_new();
728         gtk_widget_set_can_focus(cd->progress, FALSE);
729         gtk_editable_set_editable(GTK_EDITABLE(cd->progress), FALSE);
730         gtk_entry_set_text(GTK_ENTRY(cd->progress), _("click start to begin"));
731         gtk_box_pack_start(GTK_BOX(hbox), cd->progress, TRUE, TRUE, 0);
732         gtk_widget_show(cd->progress);
733
734         cd->spinner = spinner_new(NULL, -1);
735         gtk_box_pack_start(GTK_BOX(hbox), cd->spinner, FALSE, FALSE, 0);
736         gtk_widget_show(cd->spinner);
737
738         cd->list = NULL;
739
740         gtk_widget_show(cd->gd->dialog);
741 }
742
743
744
745
746 static void cache_manager_standard_clean_close_cb(GenericDialog *gd, gpointer data)
747 {
748         CleanData *cd = data;
749
750         if (!gtk_widget_get_sensitive(cd->button_close)) return;
751
752         generic_dialog_close(cd->gd);
753
754         thumb_loader_std_thumb_file_validate_cancel(cd->tl);
755         filelist_free(cd->list);
756         g_free(cd);
757 }
758
759 static void cache_manager_standard_clean_done(CleanData *cd)
760 {
761         gtk_widget_set_sensitive(cd->button_stop, FALSE);
762         gtk_widget_set_sensitive(cd->button_close, TRUE);
763
764         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(cd->progress), 1.0);
765         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(cd->progress), _("done"));
766
767         if (cd->idle_id)
768                 {
769                 g_source_remove(cd->idle_id);
770                 cd->idle_id = 0;
771                 }
772
773         thumb_loader_std_thumb_file_validate_cancel(cd->tl);
774         cd->tl = NULL;
775
776         filelist_free(cd->list);
777         cd->list = NULL;
778 }
779
780 static void cache_manager_standard_clean_stop_cb(GenericDialog *gd, gpointer data)
781 {
782         CleanData *cd = data;
783
784         cache_manager_standard_clean_done(cd);
785 }
786
787 static gint cache_manager_standard_clean_clear_cb(gpointer data)
788 {
789         CleanData *cd = data;
790
791         if (cd->list)
792                 {
793                 FileData *next_fd;
794
795                 next_fd = cd->list->data;
796                 cd->list = g_list_remove(cd->list, next_fd);
797
798                 DEBUG_1("thumb removed: %s", next_fd->path);
799
800                 unlink_file(next_fd->path);
801                 file_data_unref(next_fd);
802
803                 cd->count_done++;
804                 if (cd->count_total != 0)
805                         {
806                         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(cd->progress),
807                                                       (gdouble)cd->count_done / cd->count_total);
808                         }
809
810                 return TRUE;
811                 }
812
813         cd->idle_id = 0;
814         cache_manager_standard_clean_done(cd);
815         return FALSE;
816 }
817
818 static void cache_manager_standard_clean_valid_cb(const gchar *path, gboolean valid, gpointer data)
819 {
820         CleanData *cd = data;
821
822         if (path)
823                 {
824                 if (!valid)
825                         {
826                         DEBUG_1("thumb cleaned: %s", path);
827                         unlink_file(path);
828                         }
829
830                 cd->count_done++;
831                 if (cd->count_total != 0)
832                         {
833                         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(cd->progress),
834                                                       (gdouble)cd->count_done / cd->count_total);
835                         }
836                 }
837
838         cd->tl = NULL;
839         if (cd->list)
840                 {
841                 FileData *next_fd;
842
843                 next_fd = cd->list->data;
844                 cd->list = g_list_remove(cd->list, next_fd);
845
846                 cd->tl = thumb_loader_std_thumb_file_validate(next_fd->path, cd->days,
847                                                               cache_manager_standard_clean_valid_cb, cd);
848                 file_data_unref(next_fd);
849                 }
850         else
851                 {
852                 cache_manager_standard_clean_done(cd);
853                 }
854 }
855
856 static void cache_manager_standard_clean_start_cb(GenericDialog *gd, gpointer data)
857 {
858         CleanData *cd = data;
859         GList *list;
860         gchar *path;
861         FileData *dir_fd;
862
863         if (cd->list || !gtk_widget_get_sensitive(cd->button_start)) return;
864
865         gtk_widget_set_sensitive(cd->button_start, FALSE);
866         gtk_widget_set_sensitive(cd->button_stop, TRUE);
867         gtk_widget_set_sensitive(cd->button_close, FALSE);
868
869         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(cd->progress), _("running..."));
870
871         path = g_build_filename(homedir(), THUMB_FOLDER_GLOBAL, THUMB_FOLDER_NORMAL, NULL);
872         dir_fd = file_data_new_dir(path);
873         filelist_read(dir_fd, &list, NULL);
874         cd->list = list;
875         file_data_unref(dir_fd);
876         g_free(path);
877
878         path = g_build_filename(homedir(), THUMB_FOLDER_GLOBAL, THUMB_FOLDER_LARGE, NULL);
879         dir_fd = file_data_new_dir(path);
880         filelist_read(dir_fd, &list, NULL);
881         cd->list = g_list_concat(cd->list, list);
882         file_data_unref(dir_fd);
883         g_free(path);
884
885         path = g_build_filename(homedir(), THUMB_FOLDER_GLOBAL, THUMB_FOLDER_FAIL, NULL);
886         dir_fd = file_data_new_dir(path);
887         filelist_read(dir_fd, &list, NULL);
888         cd->list = g_list_concat(cd->list, list);
889         file_data_unref(dir_fd);
890         g_free(path);
891
892         cd->count_total = g_list_length(cd->list);
893         cd->count_done = 0;
894
895         /* start iterating */
896         if (cd->clear)
897                 {
898                 cd->idle_id = g_idle_add(cache_manager_standard_clean_clear_cb, cd);
899                 }
900         else
901                 {
902                 cache_manager_standard_clean_valid_cb(NULL, TRUE, cd);
903                 }
904 }
905
906 static void cache_manager_standard_process(GtkWidget *widget, gboolean clear)
907 {
908         CleanData *cd;
909         const gchar *stock_id;
910         const gchar *msg;
911
912         cd = g_new0(CleanData, 1);
913         cd->clear = clear;
914
915         if (clear)
916                 {
917                 stock_id = GTK_STOCK_DELETE;
918                 msg = _("Clearing thumbnails...");
919                 }
920         else
921                 {
922                 stock_id = GTK_STOCK_CLEAR;
923                 msg = _("Removing old thumbnails...");
924                 }
925
926         cd->gd = generic_dialog_new(_("Maintenance"),
927                                     "standard_maintenance",
928                                     widget, FALSE,
929                                     NULL, cd);
930         cd->gd->cancel_cb = cache_manager_standard_clean_close_cb;
931         cd->button_close = generic_dialog_add_button(cd->gd, GTK_STOCK_CLOSE, NULL,
932                                                      cache_manager_standard_clean_close_cb, FALSE);
933         cd->button_start = generic_dialog_add_button(cd->gd, GTK_STOCK_OK, _("S_tart"),
934                                                      cache_manager_standard_clean_start_cb, FALSE);
935         cd->button_stop = generic_dialog_add_button(cd->gd, GTK_STOCK_STOP, NULL,
936                                                     cache_manager_standard_clean_stop_cb, FALSE);
937         gtk_widget_set_sensitive(cd->button_stop, FALSE);
938
939         generic_dialog_add_message(cd->gd, stock_id, msg, NULL);
940
941         cd->progress = gtk_progress_bar_new();
942         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(cd->progress), _("click start to begin"));
943         gtk_box_pack_start(GTK_BOX(cd->gd->vbox), cd->progress, FALSE, FALSE, 0);
944         gtk_widget_show(cd->progress);
945
946         cd->days = 30;
947         cd->tl = NULL;
948         cd->idle_id = 0;
949
950         gtk_widget_show(cd->gd->dialog);
951 }
952
953 static void cache_manager_standard_clean_cb(GtkWidget *widget, gpointer data)
954 {
955         cache_manager_standard_process(widget, FALSE);
956 }
957
958 static void cache_manager_standard_clear_cb(GtkWidget *widget, gpointer data)
959 {
960         cache_manager_standard_process(widget, TRUE);
961 }
962
963
964 static void cache_manager_main_clean_cb(GtkWidget *widget, gpointer data)
965 {
966         cache_maintain_home(FALSE, FALSE, widget);
967 }
968
969
970 static void dummy_cancel_cb(GenericDialog *gd, gpointer data)
971 {
972         /* no op, only so cancel button appears */
973 }
974
975 static void cache_manager_main_clear_ok_cb(GenericDialog *gd, gpointer data)
976 {
977         cache_maintain_home(FALSE, TRUE, NULL);
978 }
979
980 void cache_manager_main_clear_confirm(GtkWidget *parent)
981 {
982         GenericDialog *gd;
983
984         gd = generic_dialog_new(_("Clear cache"),
985                                 "clear_cache", parent, TRUE,
986                                 dummy_cancel_cb, NULL);
987         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Clear cache"),
988                                    _("This will remove all thumbnails that have\nbeen saved to disk, continue?"));
989         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, cache_manager_main_clear_ok_cb, TRUE);
990
991         gtk_widget_show(gd->dialog);
992 }
993
994 static void cache_manager_main_clear_cb(GtkWidget *widget, gpointer data)
995 {
996         cache_manager_main_clear_confirm(widget);
997 }
998
999 static void cache_manager_render_cb(GtkWidget *widget, gpointer data)
1000 {
1001         const gchar *path = layout_get_path(NULL);
1002
1003         if (!path || !*path) path = homedir();
1004         cache_manager_render_dialog(widget, path);
1005 }
1006
1007 static void cache_manager_metadata_clean_cb(GtkWidget *widget, gpointer data)
1008 {
1009         cache_maintain_home(TRUE, FALSE, widget);
1010 }
1011
1012
1013 static CacheManager *cache_manager = NULL;
1014
1015 static void cache_manager_close_cb(GenericDialog *gd, gpointer data)
1016 {
1017         generic_dialog_close(gd);
1018
1019         g_free(cache_manager);
1020         cache_manager = NULL;
1021 }
1022
1023 static GtkWidget *cache_manager_location_label(GtkWidget *group, const gchar *subdir)
1024 {
1025         GtkWidget *label;
1026         gchar *buf;
1027         gchar *path;
1028
1029         path = g_build_filename(homedir(), subdir, NULL);
1030         buf = g_strdup_printf(_("Location: %s"), path);
1031         g_free(path);
1032         label = pref_label_new(group, buf);
1033         g_free(buf);
1034         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1035
1036         return label;
1037 }
1038
1039 void cache_manager_show(void)
1040 {
1041         GenericDialog *gd;
1042         GtkWidget *group;
1043         GtkWidget *button;
1044         GtkWidget *table;
1045         GtkSizeGroup *sizegroup;
1046
1047         if (cache_manager)
1048                 {
1049                 gtk_window_present(GTK_WINDOW(cache_manager->dialog->dialog));
1050                 return;
1051                 }
1052
1053         cache_manager = g_new0(CacheManager, 1);
1054
1055         cache_manager->dialog = generic_dialog_new(_("Cache Maintenance"),
1056                                                    "cache_manager",
1057                                                    NULL, FALSE,
1058                                                    NULL, cache_manager);
1059         gd = cache_manager->dialog;
1060
1061         gd->cancel_cb = cache_manager_close_cb;
1062         generic_dialog_add_button(gd, GTK_STOCK_CLOSE, NULL,
1063                                   cache_manager_close_cb, FALSE);
1064
1065         generic_dialog_add_message(gd, NULL, _("Cache and Data Maintenance"), NULL);
1066
1067         sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1068
1069         group = pref_group_new(gd->vbox, FALSE, _("Thumbnail cache"), GTK_ORIENTATION_VERTICAL);
1070
1071         cache_manager_location_label(group, get_thumbnails_cache_dir());
1072
1073         table = pref_table_new(group, 2, 2, FALSE, FALSE);
1074
1075         button = pref_table_button(table, 0, 0, GTK_STOCK_CLEAR, _("Clean up"), FALSE,
1076                                    G_CALLBACK(cache_manager_main_clean_cb), cache_manager);
1077         gtk_size_group_add_widget(sizegroup, button);
1078         pref_table_label(table, 1, 0, _("Remove orphaned or outdated thumbnails."), 0.0);
1079
1080         button = pref_table_button(table, 0, 1, GTK_STOCK_DELETE, _("Clear cache"), FALSE,
1081                                    G_CALLBACK(cache_manager_main_clear_cb), cache_manager);
1082         gtk_size_group_add_widget(sizegroup, button);
1083         pref_table_label(table, 1, 1, _("Delete all cached thumbnails."), 0.0);
1084
1085
1086         group = pref_group_new(gd->vbox, FALSE, _("Shared thumbnail cache"), GTK_ORIENTATION_VERTICAL);
1087
1088         cache_manager_location_label(group, THUMB_FOLDER_GLOBAL);
1089
1090         table = pref_table_new(group, 2, 2, FALSE, FALSE);
1091
1092         button = pref_table_button(table, 0, 0, GTK_STOCK_CLEAR, _("Clean up"), FALSE,
1093                                    G_CALLBACK(cache_manager_standard_clean_cb), cache_manager);
1094         gtk_size_group_add_widget(sizegroup, button);
1095         pref_table_label(table, 1, 0, _("Remove orphaned or outdated thumbnails."), 0.0);
1096
1097         button = pref_table_button(table, 0, 1, GTK_STOCK_DELETE, _("Clear cache"), FALSE,
1098                                    G_CALLBACK(cache_manager_standard_clear_cb), cache_manager);
1099         gtk_size_group_add_widget(sizegroup, button);
1100         pref_table_label(table, 1, 1, _("Delete all cached thumbnails."), 0.0);
1101
1102         group = pref_group_new(gd->vbox, FALSE, _("Create thumbnails"), GTK_ORIENTATION_VERTICAL);
1103
1104         table = pref_table_new(group, 2, 1, FALSE, FALSE);
1105
1106         button = pref_table_button(table, 0, 1, GTK_STOCK_EXECUTE, _("Render"), FALSE,
1107                                    G_CALLBACK(cache_manager_render_cb), cache_manager);
1108         gtk_size_group_add_widget(sizegroup, button);
1109         pref_table_label(table, 1, 1, _("Render thumbnails for a specific folder."), 0.0);
1110
1111         group = pref_group_new(gd->vbox, FALSE, _("Metadata"), GTK_ORIENTATION_VERTICAL);
1112
1113         cache_manager_location_label(group, get_metadata_cache_dir());
1114
1115         table = pref_table_new(group, 2, 1, FALSE, FALSE);
1116
1117         button = pref_table_button(table, 0, 0, GTK_STOCK_CLEAR, _("Clean up"), FALSE,
1118                                    G_CALLBACK(cache_manager_metadata_clean_cb), cache_manager);
1119         gtk_size_group_add_widget(sizegroup, button);
1120         pref_table_label(table, 1, 0, _("Remove orphaned keywords and comments."), 0.0);
1121
1122         gtk_widget_show(cache_manager->dialog->dialog);
1123 }
1124 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */