2 * Copyright (C) 2004 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
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.
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.
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.
22 #include "collect-io.h"
27 #include <gdk-pixbuf/gdk-pixbuf.h>
29 #include <glib-object.h>
38 #include "layout-util.h"
39 #include "main-defines.h"
41 #include "secure-save.h"
43 #include "ui-fileops.h"
44 #include "ui-utildlg.h"
46 #define GQ_COLLECTION_MARKER "#" GQ_APPNAME
49 GQ_COLLECTION_FAIL_MIN = 300,
50 GQ_COLLECTION_FAIL_PERCENT = 98,
51 GQ_COLLECTION_READ_BUFSIZE = 4096
54 struct CollectManagerEntry;
56 static void collection_load_thumb_step(CollectionData *cd);
57 static gboolean collection_save_private(CollectionData *cd, const gchar *path);
59 static CollectManagerEntry *collect_manager_get_entry(const gchar *path);
60 static void collect_manager_entry_reset(CollectManagerEntry *entry);
61 static gint collect_manager_process_action(CollectManagerEntry *entry, gchar **path_ptr);
66 gboolean scan_geometry(gchar *buffer, GdkRectangle &window)
73 if (sscanf(buffer, "%d %d %d %d", &nx, &ny, &nw, &nh) != 4) return FALSE;
85 static gboolean collection_load_private(CollectionData *cd, const gchar *path, CollectionLoadFlags flags)
87 gchar s_buf[GQ_COLLECTION_READ_BUFSIZE];
90 gboolean limit_failures = TRUE;
91 gboolean success = TRUE;
92 gboolean has_official_header = FALSE;
93 gboolean has_geometry_header = FALSE;
94 gboolean has_gqview_header = FALSE;
95 gboolean need_header = TRUE;
98 gboolean changed = FALSE;
99 CollectManagerEntry *entry = nullptr;
100 guint flush = !!(flags & COLLECTION_LOAD_FLUSH);
101 guint append = !!(flags & COLLECTION_LOAD_APPEND);
102 guint only_geometry = !!(flags & COLLECTION_LOAD_GEOMETRY);
103 gboolean reading_extended_filename = FALSE;
108 collection_load_stop(cd);
111 collect_manager_flush();
113 entry = collect_manager_get_entry(path);
117 g_list_free_full(cd->list, reinterpret_cast<GDestroyNotify>(collection_info_free));
122 if (!path && !cd->path) return FALSE;
124 if (!path) path = cd->path;
126 pathl = path_from_utf8(path);
128 DEBUG_1("collection load: append=%d flush=%d only_geometry=%d path=%s", append, flush, only_geometry, pathl);
131 f = fopen(pathl, "r");
135 log_printf("Failed to open collection file: \"%s\"\n", path);
139 GString *extended_filename_buffer = g_string_new(nullptr);
140 while (fgets(s_buf, sizeof(s_buf), f))
145 if (!reading_extended_filename)
147 /* Skip whitespaces and empty lines */
148 while (*p && g_ascii_isspace(*p)) p++;
149 if (*p == '\n' || *p == '\r') continue;
154 if (!need_header) continue;
155 if (g_ascii_strncasecmp(p, GQ_COLLECTION_MARKER, strlen(GQ_COLLECTION_MARKER)) == 0)
157 /* Looks like an official collection, allow unchecked input.
158 * All this does is allow adding files that may not exist,
159 * which is needed for the collection manager to work.
160 * Also unofficial files abort after too many invalid entries.
162 has_official_header = TRUE;
163 limit_failures = FALSE;
165 else if (strncmp(p, "#geometry:", 10 ) == 0 && scan_geometry(p + 10, cd->window))
167 has_geometry_header = TRUE;
168 cd->window_read = TRUE;
169 if (only_geometry) break;
171 else if (g_ascii_strncasecmp(p, "#GQview collection", strlen("#GQview collection")) == 0)
173 /* As 2008/04/15 there is no difference between our collection file format
174 * and GQview 2.1.5 collection file format so ignore failures as well. */
175 has_gqview_header = TRUE;
176 limit_failures = FALSE;
178 need_header = (!has_official_header && !has_gqview_header) || !has_geometry_header;
182 if (only_geometry) continue;
186 /** @todo This is not safe! */
187 /* Updated: anything within double quotes is considered a filename */
188 if (!reading_extended_filename)
190 /* not yet reading filename */
191 while (*p && *p != '"') p++;
193 /* start of filename read */
195 while (*p && *p != '"') p++;
198 /* first part of extended filename */
199 g_string_append(extended_filename_buffer, buf);
200 reading_extended_filename = TRUE;
207 while (*p && *p != '"') p++;
210 /* end of extended filename still not found */
211 g_string_append(extended_filename_buffer, buf);
215 /* end of extended filename found */
216 g_string_append_len(extended_filename_buffer, buf, p - buf);
217 reading_extended_filename = FALSE;
220 if (extended_filename_buffer->len > 0)
222 buffer2 = g_strdup(extended_filename_buffer->str);
223 g_string_erase(extended_filename_buffer, 0, -1);
228 buffer2 = g_strdup(buf);
236 changed |= collect_manager_process_action(entry, &buffer2);
238 valid = (buffer2[0] == G_DIR_SEPARATOR && collection_add_check(cd, file_data_new_simple(buffer2), FALSE, TRUE));
241 log_printf("Warning: Collection: %s Invalid file: %s", cd->name, buffer2);
242 DEBUG_1("collection invalid file: %s", buffer2);
248 /* If the file path has the prefix home, tmp or usr it was on the local file system and has been deleted. Ignore it. */
249 if (!g_str_has_prefix(buffer2, "/home") && !g_str_has_prefix(buffer2, "/tmp") && !g_str_has_prefix(buffer2, "/usr"))
251 /* The file was on a mounted drive and either has been deleted or the drive is not mounted */
252 struct mntent *mount_entry;
254 gboolean found = FALSE;
256 mount_entries = setmntent("/proc/mounts", "r");
257 if (mount_entries == nullptr)
259 /* It is assumed this will never fail */
264 while (nullptr != (mount_entry = getmntent(mount_entries)))
266 if (g_strcmp0(mount_entry->mnt_dir, G_DIR_SEPARATOR_S) != 0)
268 if (g_str_has_prefix(buffer2, mount_entry->mnt_dir))
270 log_printf("%s was a file on a mounted filesystem but has been deleted: %s", buffer2, cd->name);
276 endmntent(mount_entries);
280 log_printf("%s is a file on an unmounted filesystem: %s", buffer2, cd->path);
281 gchar *text = g_strdup_printf(_("This Collection cannot be opened because it contains a link to a file on a drive which is not yet mounted.\n\nCollection: %s\nFile: %s\n"), cd->path, buffer2);
282 warning_dialog(_("Cannot open Collection"), text, GQ_ICON_DIALOG_WARNING, nullptr);
285 collection_window_close_by_collection(cd);
292 log_printf("%s was a file on local filesystem but has been deleted: %s", buffer2, cd->name);
296 if (limit_failures && fail > GQ_COLLECTION_FAIL_MIN && fail * 100 / total > GQ_COLLECTION_FAIL_PERCENT)
298 log_printf("%d invalid filenames in unofficial collection file, closing: %s\n", fail, path);
307 g_string_free(extended_filename_buffer, TRUE);
309 DEBUG_1("collection files: total = %d fail = %d official=%d gqview=%d geometry=%d", total, fail, has_official_header, has_gqview_header, has_geometry_header);
312 if (only_geometry) return has_geometry_header;
316 gchar *buf = nullptr;
317 while (collect_manager_process_action(entry, &buf))
319 collection_add_check(cd, file_data_new_group(buf), FALSE, TRUE);
326 cd->list = collection_list_sort(cd->list, cd->sort_method);
328 if (!flush && changed && success)
329 collection_save_private(cd, path);
332 collect_manager_entry_reset(entry);
334 if (!append) cd->changed = FALSE;
339 gboolean collection_load(CollectionData *cd, const gchar *path, CollectionLoadFlags flags)
341 if (collection_load_private(cd, path, static_cast<CollectionLoadFlags>(flags | COLLECTION_LOAD_FLUSH)))
343 layout_recent_add_path(cd->path);
350 static void collection_load_thumb_do(CollectionData *cd)
354 if (!cd->thumb_loader || !g_list_find(cd->list, cd->thumb_info)) return;
356 pixbuf = thumb_loader_get_pixbuf(cd->thumb_loader);
357 collection_info_set_thumb(cd->thumb_info, pixbuf);
358 g_object_unref(pixbuf);
360 if (cd->info_updated_func) cd->info_updated_func(cd, cd->thumb_info, cd->info_updated_data);
363 static void collection_load_thumb_error_cb(ThumbLoader *, gpointer data)
365 auto cd = static_cast<CollectionData *>(data);
367 collection_load_thumb_do(cd);
368 collection_load_thumb_step(cd);
371 static void collection_load_thumb_done_cb(ThumbLoader *, gpointer data)
373 auto cd = static_cast<CollectionData *>(data);
375 collection_load_thumb_do(cd);
376 collection_load_thumb_step(cd);
379 static void collection_load_thumb_step(CollectionData *cd)
386 collection_load_stop(cd);
391 ci = static_cast<CollectInfo *>(work->data);
393 /* find first unloaded thumb */
394 while (work && ci->pixbuf)
396 ci = static_cast<CollectInfo *>(work->data);
400 if (!ci || ci->pixbuf)
403 collection_load_stop(cd);
405 /* send a NULL CollectInfo to notify end */
406 if (cd->info_updated_func) cd->info_updated_func(cd, nullptr, cd->info_updated_data);
411 /* setup loader and call it */
413 thumb_loader_free(cd->thumb_loader);
414 cd->thumb_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height);
415 thumb_loader_set_callbacks(cd->thumb_loader,
416 collection_load_thumb_done_cb,
417 collection_load_thumb_error_cb,
422 if (!thumb_loader_start(cd->thumb_loader, ci->fd))
424 /* error, handle it, do next */
425 DEBUG_1("error loading thumb for %s", ci->fd->path);
426 collection_load_thumb_do(cd);
427 collection_load_thumb_step(cd);
431 void collection_load_thumb_idle(CollectionData *cd)
433 if (!cd->thumb_loader) collection_load_thumb_step(cd);
436 gboolean collection_load_begin(CollectionData *cd, const gchar *path, CollectionLoadFlags flags)
438 if (!collection_load(cd, path, flags)) return FALSE;
440 collection_load_thumb_idle(cd);
445 void collection_load_stop(CollectionData *cd)
447 if (!cd->thumb_loader) return;
449 thumb_loader_free(cd->thumb_loader);
450 cd->thumb_loader = nullptr;
453 static gboolean collection_save_private(CollectionData *cd, const gchar *path)
459 if (!path && !cd->path) return FALSE;
467 pathl = path_from_utf8(path);
468 ssi = secure_open(pathl);
472 log_printf(_("failed to open collection (write) \"%s\"\n"), path);
476 secure_fprintf(ssi, "%s collection\n", GQ_COLLECTION_MARKER);
477 secure_fprintf(ssi, "#created with %s version %s\n", GQ_APPNAME, VERSION);
479 collection_update_geometry(cd);
482 secure_fprintf(ssi, "#geometry: %d %d %d %d\n", cd->window.x, cd->window.y, cd->window.width, cd->window.height);
486 while (work && secsave_errno == SS_ERR_NONE)
488 auto ci = static_cast<CollectInfo *>(work->data);
489 secure_fprintf(ssi, "\"%s\"\n", ci->fd->path);
493 secure_fprintf(ssi, "#end\n");
495 if (secure_close(ssi))
497 log_printf(_("error saving collection file: %s\nerror: %s\n"), path,
498 secsave_strerror(secsave_errno));
502 if (!cd->path || strcmp(path, cd->path) != 0)
504 gchar *buf = cd->path;
505 cd->path = g_strdup(path);
510 cd->name = g_strdup(filename_from_path(cd->path));
512 collection_path_changed(cd);
520 gboolean collection_save(CollectionData *cd, const gchar *path)
522 if (collection_save_private(cd, path))
524 layout_recent_add_path(cd->path);
531 gboolean collection_load_only_geometry(CollectionData *cd, const gchar *path)
533 return collection_load(cd, path, COLLECTION_LOAD_GEOMETRY);
538 *-------------------------------------------------------------------
540 *-------------------------------------------------------------------
544 COLLECT_MANAGER_ACTIONS_PER_IDLE = 1000,
545 COLLECT_MANAGER_FLUSH_DELAY = 10000
548 struct CollectManagerEntry
552 GHashTable *oldpath_hash;
553 GHashTable *newpath_hash;
557 enum CollectManagerType {
558 COLLECTION_MANAGER_UPDATE,
559 COLLECTION_MANAGER_ADD,
560 COLLECTION_MANAGER_REMOVE
563 struct CollectManagerAction
568 CollectManagerType type;
574 static GList *collection_manager_entry_list = nullptr;
575 static GList *collection_manager_action_list = nullptr;
576 static GList *collection_manager_action_tail = nullptr;
577 static guint collection_manager_timer_id = 0; /* event source id */
580 static CollectManagerAction *collect_manager_action_new(const gchar *oldpath, const gchar *newpath,
581 CollectManagerType type)
583 CollectManagerAction *action;
585 action = g_new0(CollectManagerAction, 1);
588 action->oldpath = g_strdup(oldpath);
589 action->newpath = g_strdup(newpath);
596 static void collect_manager_action_ref(CollectManagerAction *action)
601 static void collect_manager_action_unref(CollectManagerAction *action)
605 if (action->ref > 0) return;
607 g_free(action->oldpath);
608 g_free(action->newpath);
612 static void collect_manager_entry_free_data(CollectManagerEntry *entry)
614 g_list_free_full(entry->add_list, reinterpret_cast<GDestroyNotify>(collect_manager_action_unref));
615 if (g_hash_table_size(entry->oldpath_hash) > 0)
616 g_hash_table_destroy(entry->oldpath_hash);
618 g_hash_table_unref(entry->oldpath_hash);
619 if (g_hash_table_size(entry->newpath_hash) > 0)
620 g_hash_table_destroy(entry->newpath_hash);
622 g_hash_table_unref(entry->newpath_hash);
625 static void collect_manager_entry_init_data(CollectManagerEntry *entry)
627 entry->add_list = nullptr;
628 entry->oldpath_hash = g_hash_table_new_full(g_str_hash, g_str_equal, nullptr, reinterpret_cast<GDestroyNotify>(collect_manager_action_unref));
629 entry->newpath_hash = g_hash_table_new(g_str_hash, g_str_equal);
634 static CollectManagerEntry *collect_manager_entry_new(const gchar *path)
636 CollectManagerEntry *entry;
638 entry = g_new0(CollectManagerEntry, 1);
639 entry->path = g_strdup(path);
640 collect_manager_entry_init_data(entry);
642 collection_manager_entry_list = g_list_append(collection_manager_entry_list, entry);
648 static void collect_manager_entry_free(CollectManagerEntry *entry)
650 collection_manager_entry_list = g_list_remove(collection_manager_entry_list, entry);
652 collect_manager_entry_free_data(entry);
658 static void collect_manager_entry_reset(CollectManagerEntry *entry)
660 collect_manager_entry_free_data(entry);
661 collect_manager_entry_init_data(entry);
664 static CollectManagerEntry *collect_manager_get_entry(const gchar *path)
668 work = collection_manager_entry_list;
671 CollectManagerEntry *entry;
673 entry = static_cast<CollectManagerEntry *>(work->data);
675 if (strcmp(entry->path, path) == 0)
684 static void collect_manager_entry_add_action(CollectManagerEntry *entry, CollectManagerAction *action)
687 CollectManagerAction *orig_action;
689 entry->empty = FALSE;
691 if (action->oldpath == nullptr)
694 if (action->newpath == nullptr)
699 orig_action = static_cast<CollectManagerAction *>(g_hash_table_lookup(entry->newpath_hash, action->newpath));
702 /* target already exists */
703 log_printf("collection manager failed to add another action for target %s in collection %s\n",
704 action->newpath, entry->path);
707 entry->add_list = g_list_append(entry->add_list, action);
708 g_hash_table_insert(entry->newpath_hash, action->newpath, action);
709 collect_manager_action_ref(action);
713 orig_action = static_cast<CollectManagerAction *>(g_hash_table_lookup(entry->newpath_hash, action->oldpath));
716 /* new action with the same file */
717 CollectManagerAction *new_action = collect_manager_action_new(orig_action->oldpath, action->newpath, action->type);
719 if (new_action->oldpath)
721 g_hash_table_steal(entry->oldpath_hash, orig_action->oldpath);
722 g_hash_table_insert(entry->oldpath_hash, new_action->oldpath, new_action);
726 GList *work = g_list_find(entry->add_list, orig_action);
727 work->data = new_action;
730 g_hash_table_steal(entry->newpath_hash, orig_action->newpath);
731 if (new_action->newpath)
733 g_hash_table_insert(entry->newpath_hash, new_action->newpath, new_action);
735 collect_manager_action_unref(orig_action);
740 orig_action = static_cast<CollectManagerAction *>(g_hash_table_lookup(entry->oldpath_hash, action->oldpath));
743 /* another action for the same source, ignore */
744 log_printf("collection manager failed to add another action for source %s in collection %s\n",
745 action->oldpath, entry->path);
749 g_hash_table_insert(entry->oldpath_hash, action->oldpath, action);
752 g_hash_table_insert(entry->newpath_hash, action->newpath, action);
754 collect_manager_action_ref(action);
757 static gboolean collect_manager_process_action(CollectManagerEntry *entry, gchar **path_ptr)
759 gchar *path = *path_ptr;
760 CollectManagerAction *action;
767 action = static_cast<CollectManagerAction *>(entry->add_list->data);
768 g_assert(action->oldpath == nullptr);
769 entry->add_list = g_list_remove(entry->add_list, action);
770 path = g_strdup(action->newpath);
771 g_hash_table_remove(entry->newpath_hash, path);
772 collect_manager_action_unref(action);
775 return (path != nullptr);
778 action = static_cast<CollectManagerAction *>(g_hash_table_lookup(entry->oldpath_hash, path));
782 strcpy(*path_ptr, action->newpath);
786 return FALSE; /* no change */
789 static void collect_manager_refresh()
795 dir_fd = file_data_new_dir(get_collections_dir());
796 filelist_read(dir_fd, &list, nullptr);
797 file_data_unref(dir_fd);
799 work = collection_manager_entry_list;
802 CollectManagerEntry *entry;
805 entry = static_cast<CollectManagerEntry *>(work->data);
809 while (list_step && entry)
813 fd = static_cast<FileData *>(list_step->data);
814 list_step = list_step->next;
816 if (strcmp(fd->path, entry->path) == 0)
818 list = g_list_remove(list, fd);
825 collect_manager_entry_free(entry);
837 fd = static_cast<FileData *>(work->data);
840 collect_manager_entry_new(fd->path);
846 static void collect_manager_process_actions(gint max)
848 if (collection_manager_action_list) DEBUG_1("collection manager processing actions");
850 while (collection_manager_action_list != nullptr && max > 0)
852 CollectManagerAction *action;
855 action = static_cast<CollectManagerAction *>(collection_manager_action_list->data);
856 work = collection_manager_entry_list;
859 CollectManagerEntry *entry;
861 entry = static_cast<CollectManagerEntry *>(work->data);
864 if (action->type == COLLECTION_MANAGER_UPDATE)
866 collect_manager_entry_add_action(entry, action);
868 else if (action->oldpath && action->newpath &&
869 strcmp(action->newpath, entry->path) == 0)
871 /* convert action to standard add format */
872 g_free(action->newpath);
873 if (action->type == COLLECTION_MANAGER_ADD)
875 action->newpath = action->oldpath;
876 action->oldpath = nullptr;
878 else if (action->type == COLLECTION_MANAGER_REMOVE)
880 action->newpath = nullptr;
882 collect_manager_entry_add_action(entry, action);
888 if (action->type != COLLECTION_MANAGER_UPDATE &&
889 action->oldpath && action->newpath)
891 log_printf("collection manager failed to %s %s for collection %s\n",
892 (action->type == COLLECTION_MANAGER_ADD) ? "add" : "remove",
893 action->oldpath, action->newpath);
896 if (collection_manager_action_tail == collection_manager_action_list)
898 collection_manager_action_tail = nullptr;
900 collection_manager_action_list = g_list_remove(collection_manager_action_list, action);
901 collect_manager_action_unref(action);
905 static gboolean collect_manager_process_entry(CollectManagerEntry *entry)
909 if (entry->empty) return FALSE;
911 cd = collection_new(entry->path);
912 (void) collection_load_private(cd, entry->path, COLLECTION_LOAD_NONE);
914 collection_unref(cd);
919 static gboolean collect_manager_process_entry_list()
923 work = collection_manager_entry_list;
926 CollectManagerEntry *entry;
928 entry = static_cast<CollectManagerEntry *>(work->data);
930 if (collect_manager_process_entry(entry)) return TRUE;
938 static gboolean collect_manager_process_cb(gpointer)
940 if (collection_manager_action_list) collect_manager_refresh();
941 collect_manager_process_actions(COLLECT_MANAGER_ACTIONS_PER_IDLE);
942 if (collection_manager_action_list) return G_SOURCE_CONTINUE;
944 if (collect_manager_process_entry_list()) return G_SOURCE_CONTINUE;
946 DEBUG_1("collection manager is up to date");
947 return G_SOURCE_REMOVE;
950 static gboolean collect_manager_timer_cb(gpointer)
952 DEBUG_1("collection manager timer expired");
954 g_idle_add_full(G_PRIORITY_LOW, collect_manager_process_cb, nullptr, nullptr);
956 collection_manager_timer_id = 0;
960 static void collect_manager_timer_push(gint stop)
962 if (collection_manager_timer_id)
966 g_source_remove(collection_manager_timer_id);
967 collection_manager_timer_id = 0;
972 collection_manager_timer_id = g_timeout_add(COLLECT_MANAGER_FLUSH_DELAY,
973 collect_manager_timer_cb, nullptr);
974 DEBUG_1("collection manager timer started");
978 static void collect_manager_add_action(CollectManagerAction *action)
982 /* we keep track of the list's tail to keep this a n(1) operation */
984 if (collection_manager_action_tail)
986 collection_manager_action_tail = g_list_append(collection_manager_action_tail, action);
987 collection_manager_action_tail = collection_manager_action_tail->next;
991 collection_manager_action_list = g_list_append(collection_manager_action_list, action);
992 collection_manager_action_tail = collection_manager_action_list;
995 collect_manager_timer_push(FALSE);
998 void collect_manager_moved(FileData *fd)
1000 CollectManagerAction *action;
1001 const gchar *oldpath = fd->change->source;
1002 const gchar *newpath = fd->change->dest;
1004 action = collect_manager_action_new(oldpath, newpath, COLLECTION_MANAGER_UPDATE);
1005 collect_manager_add_action(action);
1008 void collect_manager_add(FileData *fd, const gchar *collection)
1010 CollectManagerAction *action;
1013 if (!fd || !collection) return;
1015 cw = collection_window_find_by_path(collection);
1018 if (collection_list_find_fd(cw->cd->list, fd) == nullptr)
1020 collection_add(cw->cd, fd, FALSE);
1025 action = collect_manager_action_new(fd->path, collection, COLLECTION_MANAGER_ADD);
1026 collect_manager_add_action(action);
1029 void collect_manager_remove(FileData *fd, const gchar *collection)
1031 CollectManagerAction *action;
1034 if (!fd || !collection) return;
1036 cw = collection_window_find_by_path(collection);
1039 while (collection_remove(cw->cd, fd));
1043 action = collect_manager_action_new(fd->path, collection, COLLECTION_MANAGER_REMOVE);
1044 collect_manager_add_action(action);
1047 void collect_manager_flush()
1049 collect_manager_timer_push(TRUE);
1051 DEBUG_1("collection manager flushing");
1052 while (collect_manager_process_cb(nullptr));
1055 void collect_manager_notify_cb(FileData *fd, NotifyType type, gpointer)
1057 if (!(type & NOTIFY_CHANGE) || !fd->change) return;
1059 DEBUG_1("Notify collect_manager: %s %04x", fd->path, type);
1060 switch (fd->change->type)
1062 case FILEDATA_CHANGE_MOVE:
1063 collect_manager_moved(fd);
1065 case FILEDATA_CHANGE_COPY:
1067 case FILEDATA_CHANGE_RENAME:
1068 collect_manager_moved(fd);
1070 case FILEDATA_CHANGE_DELETE:
1071 case FILEDATA_CHANGE_UNSPECIFIED:
1072 case FILEDATA_CHANGE_WRITE_METADATA:
1077 static gint collection_manager_sort_cb(gconstpointer a, gconstpointer b)
1079 auto char_a = static_cast<const gchar *>(a);
1080 auto char_b = static_cast<const gchar *>(b);
1082 return g_strcmp0(char_a, char_b);
1086 * @brief Creates sorted list of collections
1087 * @param[out] names_exc sorted list of collections names excluding extension
1088 * @param[out] names_inc sorted list of collections names including extension
1089 * @param[out] paths sorted list of collection paths
1091 * Lists of type gchar.
1092 * Used lists must be freed with string_list_free()
1094 void collect_manager_list(GList **names_exc, GList **names_inc, GList **paths)
1097 GList *list = nullptr;
1102 if (names_exc == nullptr && names_inc == nullptr && paths == nullptr)
1107 dir_fd = file_data_new_dir((get_collections_dir()));
1109 filelist_read(dir_fd, &list, nullptr);
1113 fd = static_cast<FileData *>(list->data);
1114 filename = g_strdup(filename_from_path(fd->path));
1116 if (file_extension_match(filename, GQ_COLLECTION_EXT))
1118 name = remove_extension_from_path(filename);
1120 if (names_exc != nullptr)
1122 *names_exc = g_list_insert_sorted(*names_exc, g_strdup(name),
1123 collection_manager_sort_cb);
1124 *names_exc = g_list_first(*names_exc);
1126 if (names_inc != nullptr)
1128 *names_inc = g_list_insert_sorted(*names_inc,filename,
1129 collection_manager_sort_cb);
1130 *names_inc = g_list_first(*names_inc);
1132 if (paths != nullptr)
1134 *paths = g_list_insert_sorted(*paths, g_strdup(fd->path),
1135 collection_manager_sort_cb);
1136 *paths = g_list_first(*paths);
1144 filelist_free(list);
1146 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */