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"
17 #include "layout_util.h"
20 #include "ui_fileops.h"
23 #define GQVIEW_COLLECTION_MARKER "#GQview"
25 #define GQVIEW_COLLECTION_FAIL_MIN 300
26 #define GQVIEW_COLLECTION_FAIL_PERCENT 98
29 static void collection_load_thumb_step(CollectionData *cd);
32 static gint scan_geometry(gchar *buffer, gint *x, gint *y, gint *w, gint *h)
36 if(sscanf(buffer, "%d %d %d %d", &nx, &ny, &nw, &nh) != 4) return FALSE;
46 static gint collection_load_private(CollectionData *cd, const gchar *path, gint append, gint flush)
51 gint official = FALSE;
56 collection_load_stop(cd);
58 if (flush) collect_manager_flush();
62 collection_list_free(cd->list);
66 if (!path && !cd->path) return FALSE;
68 if (!path) path = cd->path;
71 pathl = path_from_utf8(path);
72 f = fopen(pathl, "r");
76 printf("Failed to open collection file: \"%s\"\n", path);
80 while (fgets(s_buf, sizeof(s_buf), f))
85 if (strncasecmp(s_buf, GQVIEW_COLLECTION_MARKER, strlen(GQVIEW_COLLECTION_MARKER)) == 0)
87 /* Looks like an official collection, allow unchecked input.
88 * All this does is allow adding files that may not exist,
89 * which is needed for the collection manager to work.
90 * Also unofficial files abort after too many invalid entries.
94 else if (strncmp(s_buf, "#geometry:", 10 ) == 0 &&
95 scan_geometry(s_buf + 10, &cd->window_x, &cd->window_y, &cd->window_w, &cd->window_h) )
97 cd->window_read = TRUE;
101 if (s_buf[0]=='\n') continue;
103 buf = quoted_value(s_buf);
108 valid = (buf[0] == '/' && collection_add_check(cd, buf, FALSE, flush));
112 if (!valid && !official)
115 if (fail > GQVIEW_COLLECTION_FAIL_MIN &&
116 fail * 100 / total > GQVIEW_COLLECTION_FAIL_PERCENT)
118 printf("Too many invalid filenames in unoffical collection file, closing: %s\n", path);
128 cd->list = collection_list_sort(cd->list, cd->sort_method);
129 if (!append) cd->changed = FALSE;
134 gint collection_load(CollectionData *cd, const gchar *path, gint append)
136 if (collection_load_private(cd, path, append, TRUE))
138 layout_recent_add_path(cd->path);
145 static void collection_load_thumb_do(CollectionData *cd)
149 if (!cd->thumb_loader || !g_list_find(cd->list, cd->thumb_info)) return;
151 pixbuf = thumb_loader_get_pixbuf(cd->thumb_loader, TRUE);
152 collection_info_set_thumb(cd->thumb_info, pixbuf);
153 g_object_unref(pixbuf);
155 if (cd->info_updated_func) cd->info_updated_func(cd, cd->thumb_info, cd->info_updated_data);
158 static void collection_load_thumb_error_cb(ThumbLoader *tl, gpointer data)
160 CollectionData *cd = data;
162 collection_load_thumb_do(cd);
163 collection_load_thumb_step(cd);
166 static void collection_load_thumb_done_cb(ThumbLoader *tl, gpointer data)
168 CollectionData *cd = data;
170 collection_load_thumb_do(cd);
171 collection_load_thumb_step(cd);
174 static void collection_load_thumb_step(CollectionData *cd)
181 collection_load_stop(cd);
188 /* find first unloaded thumb */
189 while (work && ci->pixbuf)
195 if (!ci || ci->pixbuf)
198 collection_load_stop(cd);
200 /* send a NULL CollectInfo to notify end */
201 if (cd->info_updated_func) cd->info_updated_func(cd, NULL, cd->info_updated_data);
206 /* setup loader and call it */
208 thumb_loader_free(cd->thumb_loader);
209 cd->thumb_loader = thumb_loader_new(thumb_max_width, thumb_max_height);
210 thumb_loader_set_callbacks(cd->thumb_loader,
211 collection_load_thumb_done_cb,
212 collection_load_thumb_error_cb,
217 if (!thumb_loader_start(cd->thumb_loader, ci->path))
219 /* error, handle it, do next */
220 if (debug) printf("error loading thumb for %s\n", ci->path);
221 collection_load_thumb_do(cd);
222 collection_load_thumb_step(cd);
226 void collection_load_thumb_idle(CollectionData *cd)
228 if (!cd->thumb_loader) collection_load_thumb_step(cd);
231 gint collection_load_begin(CollectionData *cd, const gchar *path, gint append)
233 if (!collection_load(cd, path, append)) return FALSE;
235 collection_load_thumb_idle(cd);
240 void collection_load_stop(CollectionData *cd)
242 if (!cd->thumb_loader) return;
244 thumb_loader_free(cd->thumb_loader);
245 cd->thumb_loader = NULL;
248 static gint collection_save_private(CollectionData *cd, const gchar *path)
256 if (!path && !cd->path) return FALSE;
263 tmp_path = unique_filename(path, ".tmp", "_", 3);
264 if (!tmp_path) return FALSE;
266 pathl = path_from_utf8(tmp_path);
267 save_mask = umask(0077);
268 f = fopen(pathl, "w");
274 /* file open failed */
275 printf("failed to open collection (write) \"%s\"\n", tmp_path);
280 fprintf(f, "%s collection\n", GQVIEW_COLLECTION_MARKER);
281 fprintf(f, "#created with GQview version %s\n", VERSION);
283 collection_update_geometry(cd);
286 fprintf(f, "#geometry: %d %d %d %d\n", cd->window_x, cd->window_y, cd->window_w, cd->window_h);
292 CollectInfo *ci = work->data;
293 if (fprintf(f, "\"%s\"\n", ci->path) < 0)
296 printf("Error writing to %s\n", tmp_path);
297 unlink_file(tmp_path);
304 fprintf(f, "#end\n");
308 copy_file_attributes(path, tmp_path, TRUE, FALSE);
309 if (!rename_file(tmp_path, path))
311 printf("collection save unable to rename %s to %s\n", tmp_path, path);
312 unlink_file(tmp_path);
319 if (!cd->path || strcmp(path, cd->path) != 0)
321 gchar *buf = cd->path;
322 cd->path = g_strdup(path);
327 cd->name = g_strdup(filename_from_path(cd->path));
329 collection_path_changed(cd);
337 gint collection_save(CollectionData *cd, const gchar *path)
339 if (collection_save_private(cd, path))
341 layout_recent_add_path(cd->path);
348 gint collection_load_only_geometry(CollectionData *cd, const gchar *path)
354 if (!path && !cd->path) return FALSE;
356 if (!path) path = cd->path;
359 pathl = path_from_utf8(path);
360 f = fopen(pathl, "r");
362 if (!f) return FALSE;
364 while (fgets(s_buf, sizeof(s_buf), f))
367 strncmp(s_buf, "#geometry:", 10 ) == 0 &&
368 scan_geometry(s_buf + 10, &cd->window_x, &cd->window_y, &cd->window_w, &cd->window_h) )
370 cd->window_read = TRUE;
381 *-------------------------------------------------------------------
383 *-------------------------------------------------------------------
386 #define COLLECT_MANAGER_ACTIONS_PER_IDLE 1000
387 #define COLLECT_MANAGER_FLUSH_DELAY 10000
389 typedef struct _CollectManagerEntry CollectManagerEntry;
390 struct _CollectManagerEntry
397 COLLECTION_MANAGER_UPDATE,
398 COLLECTION_MANAGER_ADD,
399 COLLECTION_MANAGER_REMOVE
400 } CollectManagerType;
402 typedef struct _CollectManagerAction CollectManagerAction;
403 struct _CollectManagerAction
408 CollectManagerType type;
414 static GList *collection_manager_entry_list = NULL;
415 static GList *collection_manager_action_list = NULL;
416 static GList *collection_manager_action_tail = NULL;
417 static gint collection_manager_timer_id = -1;
420 static CollectManagerAction *collect_manager_action_new(const gchar *oldpath, const gchar *newpath,
421 CollectManagerType type)
423 CollectManagerAction *action;
425 action = g_new0(CollectManagerAction, 1);
428 action->oldpath = g_strdup(oldpath);
429 action->newpath = g_strdup(newpath);
436 static void collect_manager_action_ref(CollectManagerAction *action)
441 static void collect_manager_action_unref(CollectManagerAction *action)
445 if (action->ref > 0) return;
447 g_free(action->oldpath);
448 g_free(action->newpath);
452 static CollectManagerEntry *collect_manager_entry_new(const gchar *path)
454 CollectManagerEntry *entry;
456 entry = g_new0(CollectManagerEntry, 1);
457 entry->path = g_strdup(path);
458 entry->action_list = NULL;
460 collection_manager_entry_list = g_list_append(collection_manager_entry_list, entry);
465 static void collect_manager_entry_free(CollectManagerEntry *entry)
469 collection_manager_entry_list = g_list_remove(collection_manager_entry_list, entry);
471 work = entry->action_list;
474 CollectManagerAction *action;
479 collect_manager_action_unref(action);
481 g_list_free(entry->action_list);
487 static void collect_manager_refresh(void)
493 base = g_strconcat(homedir(), "/", GQVIEW_RC_DIR_COLLECTIONS, NULL);
494 path_list(base, &list, NULL);
497 work = collection_manager_entry_list;
500 CollectManagerEntry *entry;
507 while (list_step && entry)
511 path = list_step->data;
512 list_step = list_step->next;
514 if (strcmp(path, entry->path) == 0)
516 list = g_list_remove(list, path);
523 collect_manager_entry_free(entry);
536 collect_manager_entry_new(path);
543 static void collect_manager_process_actions(gint max)
545 if (debug && collection_manager_action_list)
547 printf("collection manager processing actions\n");
550 while (collection_manager_action_list != NULL && max > 0)
552 CollectManagerAction *action;
555 action = collection_manager_action_list->data;
556 work = collection_manager_entry_list;
559 CollectManagerEntry *entry;
564 if (action->type == COLLECTION_MANAGER_UPDATE)
566 entry->action_list = g_list_prepend(entry->action_list, action);
567 collect_manager_action_ref(action);
569 else if (action->oldpath && action->newpath &&
570 strcmp(action->newpath, entry->path) == 0)
572 /* convert action to standard add format */
573 g_free(action->newpath);
574 if (action->type == COLLECTION_MANAGER_ADD)
576 action->newpath = action->oldpath;
577 action->oldpath = NULL;
579 else if (action->type == COLLECTION_MANAGER_REMOVE)
581 action->newpath = NULL;
584 entry->action_list = g_list_prepend(entry->action_list, action);
585 collect_manager_action_ref(action);
591 if (action->type != COLLECTION_MANAGER_UPDATE &&
592 action->oldpath && action->newpath)
594 printf("collection manager failed to %s %s for collection %s\n",
595 (action->type == COLLECTION_MANAGER_ADD) ? "add" : "remove",
596 action->oldpath, action->newpath);
599 if (collection_manager_action_tail == collection_manager_action_list)
601 collection_manager_action_tail = NULL;
603 collection_manager_action_list = g_list_remove(collection_manager_action_list, action);
604 collect_manager_action_unref(action);
608 static gint collect_manager_process_entry(CollectManagerEntry *entry)
614 if (!entry->action_list) return FALSE;
616 cd = collection_new(entry->path);
617 success = collection_load_private(cd, entry->path, FALSE, FALSE);
619 work = g_list_last(entry->action_list);
622 CollectManagerAction *action;
627 if (!action->oldpath)
630 if (collection_list_find(cd->list, action->newpath) == NULL)
632 collection_add_check(cd, action->newpath, FALSE, FALSE);
635 else if (action->newpath)
638 while (collection_rename(cd, action->oldpath, action->newpath));
643 while (collection_remove(cd, action->oldpath));
645 collect_manager_action_unref(action);
648 if (success && cd->changed)
650 collection_save_private(cd, entry->path);
651 if (debug) printf("collection manager updated: %s\n", entry->path);
653 collection_unref(cd);
655 g_list_free(entry->action_list);
656 entry->action_list = NULL;
661 static gint collect_manager_process_entry_list(void)
665 work = collection_manager_entry_list;
668 CollectManagerEntry *entry;
672 if (collect_manager_process_entry(entry)) return TRUE;
678 static gint collect_manager_process_cb(gpointer data)
680 if (collection_manager_action_list) collect_manager_refresh();
681 collect_manager_process_actions(COLLECT_MANAGER_ACTIONS_PER_IDLE);
682 if (collection_manager_action_list) return TRUE;
684 if (collect_manager_process_entry_list()) return TRUE;
686 if (debug) printf("collection manager is up to date\n");
690 static gint collect_manager_timer_cb(gpointer data)
692 if (debug) printf("collection manager timer expired\n");
694 g_idle_add_full(G_PRIORITY_LOW, collect_manager_process_cb, NULL, NULL);
696 collection_manager_timer_id = -1;
700 static void collect_manager_timer_push(gint stop)
702 if (collection_manager_timer_id != -1)
706 g_source_remove(collection_manager_timer_id);
707 collection_manager_timer_id = -1;
712 collection_manager_timer_id = g_timeout_add(COLLECT_MANAGER_FLUSH_DELAY,
713 collect_manager_timer_cb, NULL);
714 if (debug) printf("collection manager timer started\n");
718 static void collect_manager_add_action(CollectManagerAction *action)
722 /* we keep track of the list's tail to keep this a n(1) operation */
724 if (collection_manager_action_tail)
726 g_list_append(collection_manager_action_tail, action);
727 collection_manager_action_tail = collection_manager_action_tail->next;
731 collection_manager_action_list = g_list_append(collection_manager_action_list, action);
732 collection_manager_action_tail = collection_manager_action_list;
735 collect_manager_timer_push(FALSE);
738 void collect_manager_moved(const gchar *oldpath, const gchar *newpath)
740 CollectManagerAction *action;
742 action = collect_manager_action_new(oldpath, newpath, COLLECTION_MANAGER_UPDATE);
743 collect_manager_add_action(action);
746 void collect_manager_add(const gchar *path, const gchar *collection)
748 CollectManagerAction *action;
751 if (!path || !collection) return;
753 cw = collection_window_find_by_path(collection);
756 if (collection_list_find(cw->cd->list, path) == NULL)
758 collection_add(cw->cd, path, FALSE);
763 action = collect_manager_action_new(path, collection, COLLECTION_MANAGER_ADD);
764 collect_manager_add_action(action);
767 void collect_manager_remove(const gchar *path, const gchar *collection)
769 CollectManagerAction *action;
772 if (!path || !collection) return;
774 cw = collection_window_find_by_path(collection);
777 while (collection_remove(cw->cd, path));
781 action = collect_manager_action_new(path, collection, COLLECTION_MANAGER_REMOVE);
782 collect_manager_add_action(action);
785 void collect_manager_flush(void)
787 collect_manager_timer_push(TRUE);
789 if (debug) printf("collection manager flushing\n");
790 while (collect_manager_process_cb(NULL));