7 * This software is released under the GNU General Public License (GNU GPL).
8 * Please read the included file COPYING for more information.
9 * This software comes with no warranty of any kind, use at your own risk!
14 #include "collect-io.h"
18 #include "layout_util.h"
20 #include "secure_save.h"
22 #include "ui_fileops.h"
25 #define GQ_COLLECTION_MARKER "#" GQ_APPNAME
27 #define GQ_COLLECTION_FAIL_MIN 300
28 #define GQ_COLLECTION_FAIL_PERCENT 98
30 typedef struct _CollectManagerEntry CollectManagerEntry;
32 static void collection_load_thumb_step(CollectionData *cd);
33 static gint collection_save_private(CollectionData *cd, const gchar *path);
35 static CollectManagerEntry *collect_manager_get_entry(const gchar *path);
36 static void collect_manager_entry_reset(CollectManagerEntry *entry);
37 static gint collect_manager_process_action(CollectManagerEntry *entry, gchar **path_ptr);
40 static gint scan_geometry(gchar *buffer, gint *x, gint *y, gint *w, gint *h)
44 if(sscanf(buffer, "%d %d %d %d", &nx, &ny, &nw, &nh) != 4) return FALSE;
54 static gint collection_load_private(CollectionData *cd, const gchar *path, CollectionLoadFlags flags)
59 gint official = FALSE;
63 gboolean changed = FALSE;
64 CollectManagerEntry *entry = NULL;
65 guint flush = flags & COLLECTION_LOAD_FLUSH;
66 guint append = flags & COLLECTION_LOAD_APPEND;
67 guint only_geometry = flags & COLLECTION_LOAD_GEOMETRY;
71 collection_load_stop(cd);
74 collect_manager_flush();
76 entry = collect_manager_get_entry(path);
80 collection_list_free(cd->list);
85 if (!path && !cd->path) return FALSE;
87 if (!path) path = cd->path;
90 pathl = path_from_utf8(path);
91 f = fopen(pathl, "r");
95 printf("Failed to open collection file: \"%s\"\n", path);
99 while (fgets(s_buf, sizeof(s_buf), f))
104 if (strncasecmp(s_buf, GQ_COLLECTION_MARKER, strlen(GQ_COLLECTION_MARKER)) == 0)
106 /* Looks like an official collection, allow unchecked input.
107 * All this does is allow adding files that may not exist,
108 * which is needed for the collection manager to work.
109 * Also unofficial files abort after too many invalid entries.
113 else if (strncmp(s_buf, "#geometry:", 10 ) == 0 &&
114 scan_geometry(s_buf + 10, &cd->window_x, &cd->window_y, &cd->window_w, &cd->window_h) )
116 cd->window_read = TRUE;
125 if (s_buf[0]=='\n') continue;
127 buf = quoted_value(s_buf, NULL);
133 changed |= collect_manager_process_action(entry, &buf);
135 valid = (buf[0] == '/' && collection_add_check(cd, file_data_new_simple(buf), FALSE, TRUE));
139 if (!valid && !official)
142 if (fail > GQ_COLLECTION_FAIL_MIN &&
143 fail * 100 / total > GQ_COLLECTION_FAIL_PERCENT)
145 printf("Too many invalid filenames in unoffical collection file, closing: %s\n", path);
154 if (only_geometry) return FALSE;
159 while (collect_manager_process_action(entry, &buf))
161 collection_add_check(cd, file_data_new_simple(buf), FALSE, TRUE);
167 cd->list = collection_list_sort(cd->list, cd->sort_method);
169 if (!flush && changed && success)
170 collection_save_private(cd, path);
173 collect_manager_entry_reset(entry);
175 if (!append) cd->changed = FALSE;
180 gint collection_load(CollectionData *cd, const gchar *path, CollectionLoadFlags flags)
182 if (collection_load_private(cd, path, flags | COLLECTION_LOAD_FLUSH))
184 layout_recent_add_path(cd->path);
191 static void collection_load_thumb_do(CollectionData *cd)
195 if (!cd->thumb_loader || !g_list_find(cd->list, cd->thumb_info)) return;
197 pixbuf = thumb_loader_get_pixbuf(cd->thumb_loader, TRUE);
198 collection_info_set_thumb(cd->thumb_info, pixbuf);
199 g_object_unref(pixbuf);
201 if (cd->info_updated_func) cd->info_updated_func(cd, cd->thumb_info, cd->info_updated_data);
204 static void collection_load_thumb_error_cb(ThumbLoader *tl, gpointer data)
206 CollectionData *cd = data;
208 collection_load_thumb_do(cd);
209 collection_load_thumb_step(cd);
212 static void collection_load_thumb_done_cb(ThumbLoader *tl, gpointer data)
214 CollectionData *cd = data;
216 collection_load_thumb_do(cd);
217 collection_load_thumb_step(cd);
220 static void collection_load_thumb_step(CollectionData *cd)
227 collection_load_stop(cd);
234 /* find first unloaded thumb */
235 while (work && ci->pixbuf)
241 if (!ci || ci->pixbuf)
244 collection_load_stop(cd);
246 /* send a NULL CollectInfo to notify end */
247 if (cd->info_updated_func) cd->info_updated_func(cd, NULL, cd->info_updated_data);
252 /* setup loader and call it */
254 thumb_loader_free(cd->thumb_loader);
255 cd->thumb_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
256 thumb_loader_set_callbacks(cd->thumb_loader,
257 collection_load_thumb_done_cb,
258 collection_load_thumb_error_cb,
263 if (!thumb_loader_start(cd->thumb_loader, ci->fd->path))
265 /* error, handle it, do next */
266 if (debug) printf("error loading thumb for %s\n", ci->fd->path);
267 collection_load_thumb_do(cd);
268 collection_load_thumb_step(cd);
272 void collection_load_thumb_idle(CollectionData *cd)
274 if (!cd->thumb_loader) collection_load_thumb_step(cd);
277 gint collection_load_begin(CollectionData *cd, const gchar *path, CollectionLoadFlags flags)
279 if (!collection_load(cd, path, flags)) return FALSE;
281 collection_load_thumb_idle(cd);
286 void collection_load_stop(CollectionData *cd)
288 if (!cd->thumb_loader) return;
290 thumb_loader_free(cd->thumb_loader);
291 cd->thumb_loader = NULL;
294 static gint collection_save_private(CollectionData *cd, const gchar *path)
300 if (!path && !cd->path) return FALSE;
308 pathl = path_from_utf8(path);
309 ssi = secure_open(pathl);
315 buf = g_strdup_printf(_("failed to open collection (write) \"%s\"\n"), path);
321 secure_fprintf(ssi, "%s collection\n", GQ_COLLECTION_MARKER);
322 secure_fprintf(ssi, "#created with %s version %s\n", GQ_APPNAME, VERSION);
324 collection_update_geometry(cd);
327 secure_fprintf(ssi, "#geometry: %d %d %d %d\n", cd->window_x, cd->window_y, cd->window_w, cd->window_h);
331 while (work && secsave_errno == SS_ERR_NONE)
333 CollectInfo *ci = work->data;
334 secure_fprintf(ssi, "\"%s\"\n", ci->fd->path);
338 secure_fprintf(ssi, "#end\n");
340 if (secure_close(ssi))
344 buf = g_strdup_printf(_("error saving collection file: %s\nerror: %s\n"), path,
345 secsave_strerror(secsave_errno));
351 if (!cd->path || strcmp(path, cd->path) != 0)
353 gchar *buf = cd->path;
354 cd->path = g_strdup(path);
359 cd->name = g_strdup(filename_from_path(cd->path));
361 collection_path_changed(cd);
369 gint collection_save(CollectionData *cd, const gchar *path)
371 if (collection_save_private(cd, path))
373 layout_recent_add_path(cd->path);
380 gint collection_load_only_geometry(CollectionData *cd, const gchar *path)
382 return collection_load(cd, path, COLLECTION_LOAD_GEOMETRY);
387 *-------------------------------------------------------------------
389 *-------------------------------------------------------------------
392 #define COLLECT_MANAGER_ACTIONS_PER_IDLE 1000
393 #define COLLECT_MANAGER_FLUSH_DELAY 10000
395 struct _CollectManagerEntry
399 GHashTable *oldpath_hash;
400 GHashTable *newpath_hash;
405 COLLECTION_MANAGER_UPDATE,
406 COLLECTION_MANAGER_ADD,
407 COLLECTION_MANAGER_REMOVE
408 } CollectManagerType;
410 typedef struct _CollectManagerAction CollectManagerAction;
411 struct _CollectManagerAction
416 CollectManagerType type;
422 static GList *collection_manager_entry_list = NULL;
423 static GList *collection_manager_action_list = NULL;
424 static GList *collection_manager_action_tail = NULL;
425 static gint collection_manager_timer_id = -1;
428 static CollectManagerAction *collect_manager_action_new(const gchar *oldpath, const gchar *newpath,
429 CollectManagerType type)
431 CollectManagerAction *action;
433 action = g_new0(CollectManagerAction, 1);
436 action->oldpath = g_strdup(oldpath);
437 action->newpath = g_strdup(newpath);
444 static void collect_manager_action_ref(CollectManagerAction *action)
449 static void collect_manager_action_unref(CollectManagerAction *action)
453 if (action->ref > 0) return;
455 g_free(action->oldpath);
456 g_free(action->newpath);
460 static void collect_manager_entry_free_data(CollectManagerEntry *entry)
464 work = entry->add_list;
467 CollectManagerAction *action;
472 collect_manager_action_unref(action);
474 g_list_free(entry->add_list);
475 g_hash_table_destroy(entry->oldpath_hash);
476 g_hash_table_destroy(entry->newpath_hash);
479 static void collect_manager_entry_init_data(CollectManagerEntry *entry)
481 entry->add_list = NULL;
482 entry->oldpath_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify) collect_manager_action_unref);
483 entry->newpath_hash = g_hash_table_new(g_str_hash, g_str_equal);
488 static CollectManagerEntry *collect_manager_entry_new(const gchar *path)
490 CollectManagerEntry *entry;
492 entry = g_new0(CollectManagerEntry, 1);
493 entry->path = g_strdup(path);
494 collect_manager_entry_init_data(entry);
496 collection_manager_entry_list = g_list_append(collection_manager_entry_list, entry);
502 static void collect_manager_entry_free(CollectManagerEntry *entry)
504 collection_manager_entry_list = g_list_remove(collection_manager_entry_list, entry);
506 collect_manager_entry_free_data(entry);
512 static void collect_manager_entry_reset(CollectManagerEntry *entry)
514 collect_manager_entry_free_data(entry);
515 collect_manager_entry_init_data(entry);
518 static CollectManagerEntry *collect_manager_get_entry(const gchar *path)
522 work = collection_manager_entry_list;
525 CollectManagerEntry *entry;
529 if (strcmp(entry->path, path) == 0)
538 static void collect_manager_entry_add_action(CollectManagerEntry *entry, CollectManagerAction *action)
541 CollectManagerAction *orig_action;
543 entry->empty = FALSE;
545 if (action->oldpath == NULL)
548 if (action->newpath == NULL)
553 orig_action = g_hash_table_lookup(entry->newpath_hash, action->newpath);
556 /* target already exists */
557 printf("collection manager failed to add another action for target %s in collection %s\n",
558 action->newpath, entry->path);
561 entry->add_list = g_list_append(entry->add_list, action);
562 g_hash_table_insert(entry->newpath_hash, action->newpath, action);
563 collect_manager_action_ref(action);
567 orig_action = g_hash_table_lookup(entry->newpath_hash, action->oldpath);
570 /* new action with the same file */
571 CollectManagerAction *new_action = collect_manager_action_new(orig_action->oldpath, action->newpath, action->type);
573 if (new_action->oldpath)
575 g_hash_table_steal(entry->oldpath_hash, orig_action->oldpath);
576 g_hash_table_insert(entry->oldpath_hash, new_action->oldpath, new_action);
580 GList *work = g_list_find(entry->add_list, orig_action);
581 work->data = new_action;
584 g_hash_table_steal(entry->newpath_hash, orig_action->newpath);
585 if (new_action->newpath)
587 g_hash_table_insert(entry->newpath_hash, new_action->newpath, new_action);
589 collect_manager_action_unref(orig_action);
594 orig_action = g_hash_table_lookup(entry->oldpath_hash, action->oldpath);
597 /* another action for the same source, ignore */
598 printf("collection manager failed to add another action for source %s in collection %s\n",
599 action->oldpath, entry->path);
603 g_hash_table_insert(entry->oldpath_hash, action->oldpath, action);
606 g_hash_table_insert(entry->newpath_hash, action->newpath, action);
608 collect_manager_action_ref(action);
611 static gint collect_manager_process_action(CollectManagerEntry *entry, gchar **path_ptr)
613 gchar *path = *path_ptr;
614 CollectManagerAction *action;
621 action = entry->add_list->data;
622 g_assert(action->oldpath == NULL);
623 entry->add_list = g_list_remove(entry->add_list, action);
624 path = g_strdup(action->newpath);
625 g_hash_table_remove(entry->newpath_hash, path);
626 collect_manager_action_unref(action);
629 return (path != NULL);
632 action = g_hash_table_lookup(entry->oldpath_hash, path);
637 path = g_strdup(action->newpath);
642 return FALSE; /* no change */
645 static void collect_manager_refresh(void)
651 base = g_strconcat(homedir(), "/", GQ_RC_DIR_COLLECTIONS, NULL);
652 path_list(base, &list, NULL);
655 work = collection_manager_entry_list;
658 CollectManagerEntry *entry;
665 while (list_step && entry)
669 path = list_step->data;
670 list_step = list_step->next;
672 if (strcmp(path, entry->path) == 0)
674 list = g_list_remove(list, path);
681 collect_manager_entry_free(entry);
694 collect_manager_entry_new(path);
701 static void collect_manager_process_actions(gint max)
703 if (debug && collection_manager_action_list)
705 printf("collection manager processing actions\n");
708 while (collection_manager_action_list != NULL && max > 0)
710 CollectManagerAction *action;
713 action = collection_manager_action_list->data;
714 work = collection_manager_entry_list;
717 CollectManagerEntry *entry;
722 if (action->type == COLLECTION_MANAGER_UPDATE)
724 collect_manager_entry_add_action(entry, action);
726 else if (action->oldpath && action->newpath &&
727 strcmp(action->newpath, entry->path) == 0)
729 /* convert action to standard add format */
730 g_free(action->newpath);
731 if (action->type == COLLECTION_MANAGER_ADD)
733 action->newpath = action->oldpath;
734 action->oldpath = NULL;
736 else if (action->type == COLLECTION_MANAGER_REMOVE)
738 action->newpath = NULL;
740 collect_manager_entry_add_action(entry, action);
746 if (action->type != COLLECTION_MANAGER_UPDATE &&
747 action->oldpath && action->newpath)
749 printf("collection manager failed to %s %s for collection %s\n",
750 (action->type == COLLECTION_MANAGER_ADD) ? "add" : "remove",
751 action->oldpath, action->newpath);
754 if (collection_manager_action_tail == collection_manager_action_list)
756 collection_manager_action_tail = NULL;
758 collection_manager_action_list = g_list_remove(collection_manager_action_list, action);
759 collect_manager_action_unref(action);
763 static gint collect_manager_process_entry(CollectManagerEntry *entry)
768 if (entry->empty) return FALSE;
770 cd = collection_new(entry->path);
771 success = collection_load_private(cd, entry->path, COLLECTION_LOAD_NONE);
773 collection_unref(cd);
778 static gint collect_manager_process_entry_list(void)
782 work = collection_manager_entry_list;
785 CollectManagerEntry *entry;
789 if (collect_manager_process_entry(entry)) return TRUE;
797 static gint collect_manager_process_cb(gpointer data)
799 if (collection_manager_action_list) collect_manager_refresh();
800 collect_manager_process_actions(COLLECT_MANAGER_ACTIONS_PER_IDLE);
801 if (collection_manager_action_list) return TRUE;
803 if (collect_manager_process_entry_list()) return TRUE;
805 if (debug) printf("collection manager is up to date\n");
809 static gint collect_manager_timer_cb(gpointer data)
811 if (debug) printf("collection manager timer expired\n");
813 g_idle_add_full(G_PRIORITY_LOW, collect_manager_process_cb, NULL, NULL);
815 collection_manager_timer_id = -1;
819 static void collect_manager_timer_push(gint stop)
821 if (collection_manager_timer_id != -1)
825 g_source_remove(collection_manager_timer_id);
826 collection_manager_timer_id = -1;
831 collection_manager_timer_id = g_timeout_add(COLLECT_MANAGER_FLUSH_DELAY,
832 collect_manager_timer_cb, NULL);
833 if (debug) printf("collection manager timer started\n");
837 static void collect_manager_add_action(CollectManagerAction *action)
841 /* we keep track of the list's tail to keep this a n(1) operation */
843 if (collection_manager_action_tail)
845 collection_manager_action_tail = g_list_append(collection_manager_action_tail, action);
846 collection_manager_action_tail = collection_manager_action_tail->next;
850 collection_manager_action_list = g_list_append(collection_manager_action_list, action);
851 collection_manager_action_tail = collection_manager_action_list;
854 collect_manager_timer_push(FALSE);
857 void collect_manager_moved(FileData *fd)
859 CollectManagerAction *action;
860 const gchar *oldpath = fd->change->source;
861 const gchar *newpath = fd->change->dest;
863 action = collect_manager_action_new(oldpath, newpath, COLLECTION_MANAGER_UPDATE);
864 collect_manager_add_action(action);
867 void collect_manager_add(FileData *fd, const gchar *collection)
869 CollectManagerAction *action;
872 if (!fd || !collection) return;
874 cw = collection_window_find_by_path(collection);
877 if (collection_list_find(cw->cd->list, fd->path) == NULL)
879 collection_add(cw->cd, fd, FALSE);
884 action = collect_manager_action_new(fd->path, collection, COLLECTION_MANAGER_ADD);
885 collect_manager_add_action(action);
888 void collect_manager_remove(FileData *fd, const gchar *collection)
890 CollectManagerAction *action;
893 if (!fd || !collection) return;
895 cw = collection_window_find_by_path(collection);
898 while (collection_remove(cw->cd, fd));
902 action = collect_manager_action_new(fd->path, collection, COLLECTION_MANAGER_REMOVE);
903 collect_manager_add_action(action);
906 void collect_manager_flush(void)
908 collect_manager_timer_push(TRUE);
910 if (debug) printf("collection manager flushing\n");
911 while (collect_manager_process_cb(NULL));