Blender V5.0
filelist.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2007 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9/* global includes */
10
11#include <algorithm>
12#include <cstdlib>
13#include <cstring>
14#include <ctime>
15#include <memory>
16#include <optional>
17#include <sys/stat.h>
18
19#ifndef WIN32
20# include <unistd.h>
21#else
22# include <direct.h>
23# include <io.h>
24#endif
25
26#include "AS_asset_library.hh"
28
29#include "MEM_guardedalloc.h"
30
31#include "BLF_api.hh"
32
33#include "BLI_fileops.h"
34#include "BLI_fileops_types.h"
35#include "BLI_ghash.h"
36#include "BLI_linklist.h"
37#include "BLI_listbase.h"
38#include "BLI_math_vector.h"
39#include "BLI_path_utils.hh"
40#include "BLI_stack.h"
41#include "BLI_string.h"
42#include "BLI_string_utils.hh"
43#include "BLI_task.h"
44#include "BLI_threads.h"
45#include "BLI_utildefines.h"
46
47#ifdef WIN32
48# include "BLI_winstuff.h"
49#endif
50
51#include "BKE_asset.hh"
52#include "BKE_blendfile.hh"
53#include "BKE_context.hh"
54#include "BKE_global.hh"
55#include "BKE_icons.h"
56#include "BKE_idtype.hh"
57#include "BKE_main.hh"
58#include "BKE_preferences.h"
59#include "BKE_preview_image.hh"
60
61#include "DNA_asset_types.h"
62#include "DNA_space_types.h"
63#include "DNA_userdef_types.h"
64
65#include "ED_fileselect.hh"
66
67#include "IMB_imbuf.hh"
68#include "IMB_imbuf_types.hh"
69#include "IMB_thumbs.hh"
70
71#include "MOV_util.hh"
72
73#include "WM_api.hh"
74#include "WM_types.hh"
75
76#include "UI_interface_icons.hh"
77#include "UI_resources.hh"
78
79#include "atomic_ops.h"
80
81#include "../file_indexer.hh"
82#include "../file_intern.hh"
83#include "../filelist.hh"
84#include "filelist_intern.hh"
85
86using namespace blender;
87
89
90static void filelist_readjob_main(FileListReadJob *job_params,
91 bool *stop,
92 bool *do_update,
93 float *progress);
94static void filelist_readjob_lib(FileListReadJob *job_params,
95 bool *stop,
96 bool *do_update,
97 float *progress);
98static void filelist_readjob_dir(FileListReadJob *job_params,
99 bool *stop,
100 bool *do_update,
101 float *progress);
102static void filelist_readjob_asset_library(FileListReadJob *job_params,
103 bool *stop,
104 bool *do_update,
105 float *progress);
106static void filelist_readjob_main_assets(FileListReadJob *job_params,
107 bool *stop,
108 bool *do_update,
109 float *progress);
111 bool *stop,
112 bool *do_update,
113 float *progress);
114
115/* helper, could probably go in BKE actually? */
116static int groupname_to_code(const char *group);
117
118void filelist_setindexer(FileList *filelist, const FileIndexerType *indexer)
119{
120 BLI_assert(filelist);
121 BLI_assert(indexer);
122
123 filelist->indexer = indexer;
124}
125
127 FileList *filelist,
129 const ::bUUID *catalog_id)
130{
131 if (!filelist->filter_data.asset_catalog_filter) {
132 /* There's no filter data yet. */
135 }
136
137 const bool needs_update = file_set_asset_catalog_filter_settings(
138 filelist->filter_data.asset_catalog_filter, catalog_visibility, *catalog_id);
139
140 if (needs_update) {
142 }
143}
144
150 const AssetLibraryReference *library_b)
151{
152 if (library_a->type != library_b->type) {
153 return false;
154 }
155 if (library_a->type == ASSET_LIBRARY_CUSTOM) {
156 /* Don't only check the index, also check that it's valid. */
158 &U, library_a->custom_library_index);
159 return (library_ptr_a != nullptr) &&
160 (library_a->custom_library_index == library_b->custom_library_index);
161 }
162
163 return true;
164}
165
166void filelist_setlibrary(FileList *filelist, const AssetLibraryReference *asset_library_ref)
167{
168 /* Unset if needed. */
169 if (!asset_library_ref) {
170 if (filelist->asset_library_ref) {
172 filelist->flags |= FL_FORCE_RESET;
173 }
174 return;
175 }
176
177 if (!filelist->asset_library_ref) {
178 filelist->asset_library_ref = MEM_callocN<AssetLibraryReference>("filelist asset library");
179 *filelist->asset_library_ref = *asset_library_ref;
180
181 filelist->flags |= FL_FORCE_RESET;
182 }
183 else if (!filelist_compare_asset_libraries(filelist->asset_library_ref, asset_library_ref)) {
184 *filelist->asset_library_ref = *asset_library_ref;
185 filelist->flags |= FL_FORCE_RESET;
186 }
187}
188
189/* ********** Icon/image helpers ********** */
190
192{
193 BLI_assert(G.background == false);
194
195 for (int i = 0; i < int(SpecialFileImages::_Max); i++) {
197 gSpecialFileImages[i] = nullptr;
198 }
199}
200
202 const FileDirEntry *file,
203 char r_filepath[FILE_MAX_LIBEXTRA])
204{
205 if (file->asset) {
206 const std::string asset_path = file->asset->full_path();
207 BLI_strncpy(r_filepath, asset_path.c_str(), FILE_MAX_LIBEXTRA);
208 return;
209 }
210
211 const char *root = filelist_dir(filelist);
212 BLI_path_join(r_filepath, FILE_MAX_LIBEXTRA, root, file->relpath);
213}
214
216{
217 /* Actual preview loading is only started after the filelist is loaded, so the file isn't flagged
218 * with #FILE_ENTRY_PREVIEW_LOADING yet. */
219 const bool filelist_ready = filelist_is_ready(filelist);
220 return !filelist_ready || file->flags & FILE_ENTRY_PREVIEW_LOADING;
221}
222
223static FileDirEntry *filelist_geticon_get_file(FileList *filelist, const int index)
224{
225 BLI_assert(G.background == false);
226
227 return filelist_file(filelist, index);
228}
229
230ImBuf *filelist_get_preview_image(FileList *filelist, const int index)
231{
232 FileDirEntry *file = filelist_geticon_get_file(filelist, index);
233
234 return file->preview_icon_id ? BKE_icon_imbuf_get_buffer(file->preview_icon_id) : nullptr;
235}
236
241
243{
244 ImBuf *ibuf = gSpecialFileImages[int(image)];
245 if (ibuf) {
246 return ibuf;
247 }
248 return gSpecialFileImages[int(image)] = UI_svg_icon_bitmap(icon, 256.0f, false);
249}
250
252{
253 ImBuf *ibuf = nullptr;
254
255 if (file->typeflag & FILE_TYPE_DIR) {
256 if (FILENAME_IS_PARENT(file->relpath)) {
258 }
259 else {
261 }
262 }
263 else {
265 }
266
267 return ibuf;
268}
269
271{
272 FileDirEntry *file = filelist_geticon_get_file(filelist, index);
274}
275
276static int filelist_geticon_file_type_ex(const FileList *filelist,
277 const FileDirEntry *file,
278 const bool is_main,
279 const bool ignore_libdir)
280{
281 const eFileSel_File_Types typeflag = (eFileSel_File_Types)file->typeflag;
282
283 if ((typeflag & FILE_TYPE_DIR) &&
284 !(ignore_libdir && (typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER))))
285 {
286 if (FILENAME_IS_PARENT(file->relpath)) {
287 return is_main ? ICON_FILE_PARENT : ICON_NONE;
288 }
289 if (typeflag & FILE_TYPE_BUNDLE) {
290 return ICON_UGLYPACKAGE;
291 }
292 if (typeflag & FILE_TYPE_BLENDER) {
293 return ICON_FILE_BLEND;
294 }
295 if (is_main) {
296 /* Do not return icon for folders if icons are not 'main' draw type
297 * (e.g. when used over previews). */
298 return (file->attributes & FILE_ATTR_ANY_LINK) ? ICON_FOLDER_REDIRECT : ICON_FILE_FOLDER;
299 }
300
301 /* If this path is in System list or path cache then use that icon. */
302 FSMenu *fsmenu = ED_fsmenu_get();
303 FSMenuCategory categories[] = {
307 };
308
309 for (int i = 0; i < ARRAY_SIZE(categories); i++) {
310 FSMenuEntry *tfsm = ED_fsmenu_get_category(fsmenu, categories[i]);
311 char fullpath[FILE_MAX_LIBEXTRA];
312 char *target = fullpath;
313 if (file->redirection_path) {
314 target = file->redirection_path;
315 }
316 else if (filelist) {
317 filelist_file_get_full_path(filelist, file, fullpath);
318 BLI_path_slash_ensure(fullpath, sizeof(fullpath));
319 }
320 for (; tfsm; tfsm = tfsm->next) {
321 if (STREQ(tfsm->path, target)) {
322 /* Never want a little folder inside a large one. */
323 return (tfsm->icon == ICON_FILE_FOLDER) ? ICON_NONE : tfsm->icon;
324 }
325 }
326 }
327
328 if (file->attributes & FILE_ATTR_OFFLINE) {
329 return ICON_ERROR;
330 }
331 if (file->attributes & FILE_ATTR_TEMPORARY) {
332 return ICON_FILE_CACHE;
333 }
334 if (file->attributes & FILE_ATTR_SYSTEM) {
335 return ICON_SYSTEM;
336 }
337 }
338
339 if (typeflag & FILE_TYPE_BLENDER) {
340 return (is_main || file->preview_icon_id) ? ICON_FILE_BLEND : ICON_BLENDER;
341 }
342 if (typeflag & FILE_TYPE_BLENDER_BACKUP) {
343 return ICON_FILE_BACKUP;
344 }
345 if (typeflag & FILE_TYPE_IMAGE) {
346 return ICON_FILE_IMAGE;
347 }
348 if (typeflag & FILE_TYPE_MOVIE) {
349 return ICON_FILE_MOVIE;
350 }
351 if (typeflag & FILE_TYPE_PYSCRIPT) {
352 return ICON_FILE_SCRIPT;
353 }
354 if (typeflag & FILE_TYPE_SOUND) {
355 return ICON_FILE_SOUND;
356 }
357 if (typeflag & FILE_TYPE_FTFONT) {
358 return ICON_FILE_FONT;
359 }
360 if (typeflag & FILE_TYPE_BTX) {
361 return ICON_FILE_BLANK;
362 }
363 if (typeflag & FILE_TYPE_ALEMBIC) {
364 return ICON_FILE_3D;
365 }
366 if (typeflag & FILE_TYPE_USD) {
367 return ICON_FILE_3D;
368 }
369 if (typeflag & FILE_TYPE_VOLUME) {
370 return ICON_FILE_VOLUME;
371 }
372 if (typeflag & FILE_TYPE_OBJECT_IO) {
373 return ICON_FILE_3D;
374 }
375 if (typeflag & FILE_TYPE_TEXT) {
376 return ICON_FILE_TEXT;
377 }
378 if (typeflag & FILE_TYPE_ARCHIVE) {
379 return ICON_FILE_ARCHIVE;
380 }
381 if (typeflag & FILE_TYPE_BLENDERLIB) {
382 const int ret = UI_icon_from_idcode(file->blentype);
383 if (ret != ICON_NONE) {
384 return ret;
385 }
386 }
387 return is_main ? ICON_FILE_BLANK : ICON_NONE;
388}
389
390int filelist_geticon_file_type(FileList *filelist, const int index, const bool is_main)
391{
392 FileDirEntry *file = filelist_geticon_get_file(filelist, index);
393
394 return filelist_geticon_file_type_ex(filelist, file, is_main, false);
395}
396
398{
399 return file->preview_icon_id ? file->preview_icon_id :
400 filelist_geticon_file_type_ex(nullptr, file, false, false);
401}
402
404{
405 return intern_entry->local_data.id != nullptr;
406}
407
408/* ********** Main ********** */
409
411{
412 /* Only allow absolute paths as CWD relative doesn't make sense from the UI. */
414 return;
415 }
416
417#ifdef WIN32
419#else
420 ARRAY_SET_ITEMS(dir, '/', '\0');
421#endif
422}
423
424static bool filelist_checkdir_dir(const FileList * /*filelist*/,
425 char dirpath[FILE_MAX_LIBEXTRA],
426 const bool do_change)
427{
428 bool is_valid;
429 if (do_change) {
431 is_valid = true;
432 }
433 else {
434 is_valid = BLI_path_is_abs_from_cwd(dirpath) && BLI_is_dir(dirpath);
435 }
436 return is_valid;
437}
438
439static bool filelist_checkdir_lib(const FileList * /*filelist*/,
440 char dirpath[FILE_MAX_LIBEXTRA],
441 const bool do_change)
442{
443 char tdir[FILE_MAX_LIBEXTRA];
444 char *name;
445
446 const bool is_valid = (BLI_is_dir(dirpath) ||
447 (BKE_blendfile_library_path_explode(dirpath, tdir, nullptr, &name) &&
448 BLI_is_file(tdir) && !name));
449
450 if (do_change && !is_valid) {
451 /* if not a valid library, we need it to be a valid directory! */
453 return true;
454 }
455 return is_valid;
456}
457
458static bool filelist_checkdir_main(const FileList *filelist,
459 char dirpath[FILE_MAX_LIBEXTRA],
460 const bool do_change)
461{
462 /* TODO */
463 return filelist_checkdir_lib(filelist, dirpath, do_change);
464}
465
466static bool filelist_checkdir_return_always_valid(const FileList * /*filelist*/,
467 char /*dirpath*/[FILE_MAX_LIBEXTRA],
468 const bool /*do_change*/)
469{
470 return true;
471}
472
474{
475 if (entry->name && ((entry->flags & FILE_ENTRY_NAME_FREE) != 0)) {
476 MEM_freeN((char *)entry->name);
477 }
478 if (entry->relpath) {
479 MEM_freeN(entry->relpath);
480 }
481 if (entry->redirection_path) {
483 }
484 if (entry->preview_icon_id) {
486 entry->preview_icon_id = 0;
487 }
488}
489
491{
493 MEM_freeN(entry);
494}
495
497{
498#if 0
499 FileDirEntry *entry, *entry_next;
500
501 for (entry = array->entries.first; entry; entry = entry_next) {
502 entry_next = entry->next;
503 filelist_entry_free(entry);
504 }
505 BLI_listbase_clear(&array->entries);
506#else
508#endif
509 array->entries_num = FILEDIR_NBR_ENTRIES_UNSET;
510 array->entries_filtered_num = FILEDIR_NBR_ENTRIES_UNSET;
511}
512
514{
515 if (auto asset_ptr = entry->asset.lock()) {
516 BLI_assert(filelist->asset_library);
517 filelist->asset_library->remove_asset(*asset_ptr);
518 }
519
520 if (entry->relpath) {
521 MEM_freeN(entry->relpath);
522 }
523 if (entry->redirection_path) {
525 }
526 if (entry->name && entry->free_name) {
527 MEM_freeN((char *)entry->name);
528 }
529 MEM_delete(entry);
530}
531
532static void filelist_intern_free(FileList *filelist)
533{
534 FileListIntern *filelist_intern = &filelist->filelist_intern;
535 LISTBASE_FOREACH_MUTABLE (FileListInternEntry *, entry, &filelist_intern->entries) {
536 filelist_intern_entry_free(filelist, entry);
537 }
538 BLI_listbase_clear(&filelist_intern->entries);
539
540 MEM_SAFE_FREE(filelist_intern->filtered);
541}
542
547{
548 FileListIntern *filelist_intern = &filelist->filelist_intern;
549 int removed_counter = 0;
550 LISTBASE_FOREACH_MUTABLE (FileListInternEntry *, entry, &filelist_intern->entries) {
552 continue;
553 }
554
555 BLI_remlink(&filelist_intern->entries, entry);
556 filelist_intern_entry_free(filelist, entry);
557 removed_counter++;
558 }
559
560 if (removed_counter > 0) {
561 MEM_SAFE_FREE(filelist_intern->filtered);
562 }
563 return removed_counter;
564}
565
566static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdata)
567{
569 FileListEntryPreviewTaskData *preview_taskdata = static_cast<FileListEntryPreviewTaskData *>(
570 taskdata);
571 FileListEntryPreview *preview = preview_taskdata->preview;
572
573 /* XXX #THB_SOURCE_IMAGE for "historic" reasons. The case of an undefined source should be
574 * handled better. */
576
577 // printf("%s: Start (%d)...\n", __func__, threadid);
578
579 // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
580 BLI_assert(preview->flags &
583
584 if (preview->flags & FILE_TYPE_IMAGE) {
585 source = THB_SOURCE_IMAGE;
586 }
588 {
589 source = THB_SOURCE_BLEND;
590 }
591 else if (preview->flags & FILE_TYPE_MOVIE) {
592 source = THB_SOURCE_MOVIE;
593 }
594 else if (preview->flags & FILE_TYPE_FTFONT) {
595 source = THB_SOURCE_FONT;
596 }
597 else if (preview->flags & FILE_TYPE_OBJECT_IO) {
598 source = THB_SOURCE_OBJECT_IO;
599 }
600
602 /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate
603 * in case user switch to a bigger preview size. */
604 ImBuf *imbuf = IMB_thumb_manage(preview->filepath, THB_LARGE, source);
606 if (imbuf) {
608 }
609
610 /* Move ownership to the done queue. */
611 preview_taskdata->preview = nullptr;
612
614
615 // printf("%s: End (%d)...\n", __func__, threadid);
616}
617
618static void filelist_cache_preview_freef(TaskPool *__restrict /*pool*/, void *taskdata)
619{
620 FileListEntryPreviewTaskData *preview_taskdata = static_cast<FileListEntryPreviewTaskData *>(
621 taskdata);
622
623 /* In case the preview wasn't moved to the "done" queue yet. */
624 if (preview_taskdata->preview) {
625 MEM_freeN(preview_taskdata->preview);
626 }
627
628 MEM_freeN(preview_taskdata);
629}
630
641
643{
644 if (cache->previews_pool) {
646
647 LISTBASE_FOREACH (FileDirEntry *, entry, &cache->cached_entries) {
648 entry->flags &= ~FILE_ENTRY_PREVIEW_LOADING;
649 }
650
651 FileListEntryPreview *preview;
652 while ((preview = static_cast<FileListEntryPreview *>(
654 {
655 // printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path,
656 // preview->img);
657 if (preview->icon_id) {
658 BKE_icon_delete(preview->icon_id);
659 }
660 MEM_freeN(preview);
661 }
662 cache->previews_todo_count = 0;
663 }
664}
665
667{
668 if (cache->previews_pool) {
670
672
675 cache->previews_pool = nullptr;
676 cache->previews_done = nullptr;
677 cache->previews_todo_count = 0;
678
680 }
681
682 cache->flags &= ~FLC_PREVIEWS_ACTIVE;
683}
684
691{
693 return false;
694 }
695
696 if (!(entry->typeflag &
699 {
700 return false;
701 }
702
703 /* If we know this is an external ID without a preview, skip loading the preview. Can save quite
704 * some time in heavy files, because otherwise for each missing preview and for each preview
705 * reload, we'd reopen the .blend to look for the preview. */
706 if ((entry->typeflag & FILE_TYPE_BLENDERLIB) &&
708 {
709 return false;
710 }
711
712 /* External ID that is also a directory is never previewed. */
713 if ((entry->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR)) ==
715 {
716 return false;
717 }
718
719 return true;
720}
721
726static bool filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry, const int index)
727{
728 FileListEntryCache *cache = filelist->filelist_cache;
729
731
732 if (entry->preview_icon_id) {
733 return false;
734 }
735
737 return false;
738 }
739
740 FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index];
741 const PreviewImage *preview_in_memory = intern_entry->local_data.preview_image;
742 if (preview_in_memory && !BKE_previewimg_is_finished(preview_in_memory, ICON_SIZE_PREVIEW)) {
743 /* Nothing to set yet. Wait for next call. */
744 return false;
745 }
746
749
751 preview->index = index;
752 preview->flags = entry->typeflag;
753 preview->icon_id = 0;
754
755 if (preview_in_memory) {
756 /* TODO(mano-wii): No need to use the thread API here. */
758 preview->filepath[0] = '\0';
760 if (imbuf) {
762 }
764 }
765 else {
766 if (entry->redirection_path) {
768 }
769 else {
770 filelist_file_get_full_path(filelist, entry, preview->filepath);
771 }
772 // printf("%s: %d - %s\n", __func__, preview->index, preview->filepath);
773
775 __func__);
776 preview_taskdata->preview = preview;
779 preview_taskdata,
780 true,
782 }
783 cache->previews_todo_count++;
784
785 return true;
786}
787
789{
790 block_entries = static_cast<FileDirEntry **>(
791 MEM_mallocN(sizeof(*this->block_entries) * this->size, __func__));
792
793 this->misc_entries = BLI_ghash_ptr_new_ex(__func__, this->size);
794 this->misc_entries_indices = MEM_malloc_arrayN<int>(this->size, __func__);
795 copy_vn_i(this->misc_entries_indices, this->size, -1);
796
797 this->uids = BLI_ghash_new_ex(
798 BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__, this->size * 2);
799}
800
802{
804
806
807 BLI_ghash_free(this->misc_entries, nullptr, nullptr);
809
810 BLI_ghash_free(this->uids, nullptr, nullptr);
811
813 filelist_entry_free(entry);
814 }
815}
816
817void filelist_cache_clear(FileListEntryCache *cache, size_t new_size)
818{
820
821 cache->block_cursor = cache->block_start_index = cache->block_center_index =
822 cache->block_end_index = 0;
823 if (new_size != cache->size) {
824 cache->block_entries = static_cast<FileDirEntry **>(
825 MEM_reallocN(cache->block_entries, sizeof(*cache->block_entries) * new_size));
826 }
827
828 BLI_ghash_clear_ex(cache->misc_entries, nullptr, nullptr, new_size);
829 if (new_size != cache->size) {
830 cache->misc_entries_indices = static_cast<int *>(MEM_reallocN(
831 cache->misc_entries_indices, sizeof(*cache->misc_entries_indices) * new_size));
832 }
833 copy_vn_i(cache->misc_entries_indices, new_size, -1);
834
835 BLI_ghash_clear_ex(cache->uids, nullptr, nullptr, new_size * 2);
836
837 cache->size = new_size;
838
840 filelist_entry_free(entry);
841 }
843}
844
846{
847 FileList *p = MEM_callocN<FileList>(__func__);
848
849 p->filelist_cache = MEM_new<FileListEntryCache>("FileListEntryCache");
850
853 filelist_settype(p, type);
854
855 return p;
856}
857
858void filelist_settype(FileList *filelist, short type)
859{
860 if (filelist->type == type) {
861 return;
862 }
863
864 filelist->type = (eFileSelectType)type;
865 filelist->tags = 0;
866 filelist->indexer = &file_indexer_noop;
867 switch (filelist->type) {
868 case FILE_MAIN:
871 filelist->prepare_filter_fn = nullptr;
872 filelist->filter_fn = is_filtered_main;
873 break;
874 case FILE_LOADLIB:
877 filelist->prepare_filter_fn = nullptr;
878 filelist->filter_fn = is_filtered_lib;
879 break;
886 break;
887 case FILE_MAIN_ASSET:
893 break;
900 break;
901 default:
904 filelist->prepare_filter_fn = nullptr;
905 filelist->filter_fn = is_filtered_file;
906 break;
907 }
908
909 filelist->flags |= FL_FORCE_RESET;
910}
911
913{
914 /* The AssetLibraryService owns the AssetLibrary pointer, so no need for us to free it. */
915 filelist->asset_library = nullptr;
916 file_delete_asset_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter);
917}
918
920 const bool do_asset_library,
921 const bool do_cache,
922 const bool do_selection)
923{
924 if (!filelist) {
925 return;
926 }
927
929
930 if (do_cache) {
932 }
933
934 filelist_intern_free(filelist);
935
937
938 if (do_selection && filelist->selection_state) {
939 BLI_ghash_clear(filelist->selection_state, nullptr, nullptr);
940 }
941
942 if (do_asset_library) {
944 }
945}
946
948 const bool do_asset_library,
949 const bool do_cache,
950 const bool do_selection)
951{
952 if (!filelist || !(filelist->tags & FILELIST_TAGS_USES_MAIN_DATA)) {
953 return;
954 }
956 return;
957 }
958 const int removed_files = filelist_intern_free_main_files(filelist);
959 /* File list contains no main files to clear. */
960 if (removed_files == 0) {
961 return;
962 }
963
965
966 if (do_cache) {
968 }
969
970 filelist->filelist.entries_num -= removed_files;
973
974 if (do_selection && filelist->selection_state) {
975 BLI_ghash_clear(filelist->selection_state, nullptr, nullptr);
976 }
977
978 if (do_asset_library) {
980 }
981}
982
984{
985 filelist_clear_ex(filelist, true, true, true);
986}
987
989{
990 /* Do a full clear if needed. */
991 if (filelist->flags & FL_FORCE_RESET) {
992 filelist_clear(filelist);
993 return;
994 }
995
996 if (filelist->flags & FL_FORCE_RESET_MAIN_FILES) {
997 filelist_clear_main_files(filelist, false, true, false);
998 return;
999 }
1000}
1001
1003{
1004 if (!filelist) {
1005 printf("Attempting to delete empty filelist.\n");
1006 return;
1007 }
1008
1009 /* No need to clear cache & selection_state, we free them anyway. */
1010 filelist_clear_ex(filelist, true, false, false);
1011 MEM_delete(filelist->filelist_cache);
1012
1013 if (filelist->selection_state) {
1014 BLI_ghash_free(filelist->selection_state, nullptr, nullptr);
1015 filelist->selection_state = nullptr;
1016 }
1017
1019
1020 memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
1021
1022 filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING);
1023
1024 MEM_freeN(filelist);
1025}
1026
1031
1033{
1034 if (filelist->libfiledata) {
1036 }
1037 filelist->libfiledata = nullptr;
1038}
1039
1040BlendHandle *filelist_lib(FileList *filelist)
1041{
1042 return filelist->libfiledata;
1043}
1044
1046{
1047 return filelist->filelist.entries_num;
1048}
1049
1050static const char *fileentry_uiname(const char *root, FileListInternEntry *entry, char *buff)
1051{
1052 if (asset_system::AssetRepresentation *asset = entry->get_asset()) {
1053 const StringRefNull asset_name = asset->get_name();
1054 return BLI_strdupn(asset_name.c_str(), asset_name.size());
1055 }
1056
1057 const char *relpath = entry->relpath;
1058 const eFileSel_File_Types typeflag = entry->typeflag;
1059 char *name = nullptr;
1060
1061 if (typeflag & FILE_TYPE_FTFONT && !(typeflag & FILE_TYPE_BLENDERLIB)) {
1062 char abspath[FILE_MAX_LIBEXTRA];
1063 BLI_path_join(abspath, sizeof(abspath), root, relpath);
1065 if (name) {
1066 /* Allocated string, so no need to #BLI_strdup. */
1067 return name;
1068 }
1069 }
1070
1071 if (typeflag & FILE_TYPE_BLENDERLIB) {
1072 char abspath[FILE_MAX_LIBEXTRA];
1073 char *group;
1074
1075 BLI_path_join(abspath, sizeof(abspath), root, relpath);
1076 BKE_blendfile_library_path_explode(abspath, buff, &group, &name);
1077 if (!name) {
1078 name = group;
1079 }
1080 }
1081 /* Depending on platforms, 'my_file.blend/..' might be viewed as dir or not... */
1082 if (!name) {
1083 if (typeflag & FILE_TYPE_DIR) {
1084 name = (char *)relpath;
1085 }
1086 else {
1087 name = (char *)BLI_path_basename(relpath);
1088 }
1089 }
1091
1092 return BLI_strdup(name);
1093}
1094
1095const char *filelist_dir(const FileList *filelist)
1096{
1097 return filelist->filelist.root;
1098}
1099
1100bool filelist_is_dir(const FileList *filelist, const char *path)
1101{
1102 return filelist->check_dir_fn(filelist, (char *)path, false);
1103}
1104
1105void filelist_setdir(FileList *filelist, char dirpath[FILE_MAX_LIBEXTRA])
1106{
1107 const bool allow_invalid = filelist->asset_library_ref != nullptr;
1108 BLI_assert(strlen(dirpath) < FILE_MAX_LIBEXTRA);
1109
1112 const bool is_valid_path = filelist->check_dir_fn(filelist, dirpath, !allow_invalid);
1113 BLI_assert(is_valid_path || allow_invalid);
1114 UNUSED_VARS_NDEBUG(is_valid_path);
1115
1116 if (!STREQ(filelist->filelist.root, dirpath)) {
1117 STRNCPY(filelist->filelist.root, dirpath);
1118 filelist->flags |= FL_FORCE_RESET;
1119 }
1120}
1121
1122void filelist_setrecursion(FileList *filelist, const int recursion_level)
1123{
1124 if (filelist->max_recursion != recursion_level) {
1125 filelist->max_recursion = recursion_level;
1126 filelist->flags |= FL_FORCE_RESET;
1127 }
1128}
1129
1131{
1132 return (filelist->flags & (FL_FORCE_RESET | FL_FORCE_RESET_MAIN_FILES)) != 0;
1133}
1134
1136{
1137 filelist->flags |= FL_FORCE_RESET;
1138}
1139
1141{
1142 if (!(filelist->tags & FILELIST_TAGS_USES_MAIN_DATA)) {
1143 return;
1144 }
1145 filelist->flags |= FL_FORCE_RESET_MAIN_FILES;
1146}
1147
1149{
1150 filelist->flags |= FL_RELOAD_ASSET_LIBRARY;
1151}
1152
1153bool filelist_is_ready(const FileList *filelist)
1154{
1155 return (filelist->flags & FL_IS_READY) != 0;
1156}
1157
1158bool filelist_pending(const FileList *filelist)
1159{
1160 return (filelist->flags & FL_IS_PENDING) != 0;
1161}
1162
1164{
1165 return (filelist->tags & FILELIST_TAGS_USES_MAIN_DATA) != 0;
1166}
1167
1169{
1170 if (!filelist_needs_force_reset(filelist) || !filelist_needs_reading(filelist)) {
1171 filelist_sort(filelist);
1172 filelist_filter(filelist);
1173 }
1174
1175 return filelist->filelist.entries_filtered_num;
1176}
1177
1178static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int index)
1179{
1180 FileListInternEntry *entry = filelist->filelist_intern.filtered[index];
1181 FileListEntryCache *cache = filelist->filelist_cache;
1183
1184 ret = MEM_callocN<FileDirEntry>(__func__);
1185
1186 ret->size = uint64_t(entry->st.st_size);
1187 ret->time = int64_t(entry->st.st_mtime);
1188
1189 ret->relpath = BLI_strdup(entry->relpath);
1190 if (entry->free_name) {
1191 ret->name = BLI_strdup(entry->name);
1192 ret->flags |= FILE_ENTRY_NAME_FREE;
1193 }
1194 else {
1195 ret->name = entry->name;
1196 }
1197 ret->uid = entry->uid;
1198 ret->blentype = entry->blentype;
1199 ret->typeflag = entry->typeflag;
1200 ret->attributes = entry->attributes;
1201 if (entry->redirection_path) {
1202 ret->redirection_path = BLI_strdup(entry->redirection_path);
1203 }
1204 ret->id = entry->local_data.id;
1205 ret->asset = entry->get_asset();
1206 /* For some file types the preview is already available. */
1207 if (entry->local_data.preview_image &&
1209 {
1211 if (ibuf) {
1212 ret->preview_icon_id = BKE_icon_imbuf_create(ibuf);
1213 }
1214 }
1215 if (entry->blenderlib_has_no_preview) {
1217 }
1218 BLI_addtail(&cache->cached_entries, ret);
1219 return ret;
1220}
1221
1223{
1224 BLI_remlink(&filelist->filelist_cache->cached_entries, entry);
1225 filelist_entry_free(entry);
1226}
1227
1229{
1230 /* If the file is cached, we can get it from either the block or the misc entry storage. */
1231
1232 if (index >= cache->block_start_index && index < cache->block_end_index) {
1233 const int idx = (index - cache->block_start_index + cache->block_cursor) % cache->size;
1234 return cache->block_entries[idx];
1235 }
1236
1237 return static_cast<FileDirEntry *>(
1239}
1240
1241FileDirEntry *filelist_file_ex(FileList *filelist, const int index, const bool use_request)
1242{
1243 FileDirEntry *ret = nullptr, *old;
1244 FileListEntryCache *cache = filelist->filelist_cache;
1245 int old_index;
1246
1247 if ((index < 0) || (index >= filelist->filelist.entries_filtered_num)) {
1248 return ret;
1249 }
1250
1251 if ((ret = filelist_cache_file_lookup(cache, index))) {
1252 return ret;
1253 }
1254
1255 if (!use_request) {
1256 return nullptr;
1257 }
1258
1259 // printf("requesting file %d (not yet cached)\n", index);
1260
1261 /* Else, we have to add new entry to 'misc' cache - and possibly make room for it first! */
1262 ret = filelist_file_create_entry(filelist, index);
1263 old_index = cache->misc_entries_indices[cache->misc_cursor];
1264 if ((old = static_cast<FileDirEntry *>(
1265 BLI_ghash_popkey(cache->misc_entries, POINTER_FROM_INT(old_index), nullptr))))
1266 {
1267 BLI_ghash_remove(cache->uids, POINTER_FROM_UINT(old->uid), nullptr, nullptr);
1268 filelist_file_release_entry(filelist, old);
1269 }
1272
1273 cache->misc_entries_indices[cache->misc_cursor] = index;
1274 cache->misc_cursor = (cache->misc_cursor + 1) % cache->size;
1275
1276#if 0 /* Actually no, only block cached entries should have preview IMHO. */
1277 if (cache->previews_pool) {
1278 filelist_cache_previews_push(filelist, ret, index);
1279 }
1280#endif
1281
1282 return ret;
1283}
1284
1286{
1287 return filelist_file_ex(filelist, index, true);
1288}
1289
1290int filelist_file_find_path(FileList *filelist, const char *filename)
1291{
1293 return -1;
1294 }
1295
1296 /* XXX TODO: Cache could probably use a ghash on paths too? Not really urgent though.
1297 * This is only used to find again renamed entry,
1298 * annoying but looks hairy to get rid of it currently. */
1299
1300 for (int fidx = 0; fidx < filelist->filelist.entries_filtered_num; fidx++) {
1301 FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx];
1302 if (STREQ(entry->relpath, filename)) {
1303 return fidx;
1304 }
1305 }
1306
1307 return -1;
1308}
1309
1310int filelist_file_find_id(const FileList *filelist, const ID *id)
1311{
1313 return -1;
1314 }
1315
1316 for (int fidx = 0; fidx < filelist->filelist.entries_filtered_num; fidx++) {
1317 FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx];
1318 if (entry->local_data.id == id) {
1319 return fidx;
1320 }
1321 }
1322
1323 return -1;
1324}
1325
1326static FileListInternEntry *filelist_entry_intern_get(const FileList *filelist, const int index)
1327{
1328 BLI_assert(index >= 0 && index < filelist->filelist.entries_filtered_num);
1329 return filelist->filelist_intern.filtered[index];
1330}
1331
1332ID *filelist_entry_get_id(const FileList *filelist, const int index)
1333{
1334 const FileListInternEntry *intern_entry = filelist_entry_intern_get(filelist, index);
1335 return intern_entry->local_data.id;
1336}
1337
1339 const FileList *filelist, const int index)
1340{
1341 const FileListInternEntry *intern_entry = filelist_entry_intern_get(filelist, index);
1342 return intern_entry->get_asset();
1343}
1344
1346{
1347 return file->id;
1348}
1349
1350const char *filelist_entry_get_relpath(const FileList *filelist, int index)
1351{
1352 const FileListInternEntry *intern_entry = filelist_entry_intern_get(filelist, index);
1353 return intern_entry->relpath;
1354}
1355
1356#define FILE_UID_UNSET 0
1357
1359{
1360 /* Using an atomic operation to avoid having to lock thread...
1361 * Note that we do not really need this here currently, since there is a single listing thread,
1362 * but better remain consistent about threading! */
1364}
1365
1367{
1368 FileUID unset_uid;
1369 filelist_uid_unset(&unset_uid);
1370 return unset_uid != uid;
1371}
1372
1374{
1375 *r_uid = FILE_UID_UNSET;
1376}
1377
1378void filelist_file_cache_slidingwindow_set(FileList *filelist, size_t window_size)
1379{
1380 /* Always keep it power of 2, in [256, 8192] range for now,
1381 * cache being app. twice bigger than requested window. */
1382 size_t size = 256;
1383 window_size *= 2;
1384
1385 while (size < window_size && size < 8192) {
1386 size *= 2;
1387 }
1388
1389 if (size != filelist->filelist_cache->size) {
1391 }
1392}
1393
1394/* Helpers, low-level, they assume cursor + size <= cache_size */
1396 const int start_index,
1397 const int size,
1398 int cursor)
1399{
1400 FileListEntryCache *cache = filelist->filelist_cache;
1401
1402 int i, idx;
1403
1404 for (i = 0, idx = start_index; i < size; i++, idx++, cursor++) {
1405 FileDirEntry *entry;
1406
1407 /* That entry might have already been requested and stored in misc cache... */
1408 if ((entry = static_cast<FileDirEntry *>(
1409 BLI_ghash_popkey(cache->misc_entries, POINTER_FROM_INT(idx), nullptr))) == nullptr)
1410 {
1411 entry = filelist_file_create_entry(filelist, idx);
1412 BLI_ghash_insert(cache->uids, POINTER_FROM_UINT(entry->uid), entry);
1413 }
1414 cache->block_entries[cursor] = entry;
1415 }
1416 return true;
1417}
1418
1419static void filelist_file_cache_block_release(FileList *filelist, const int size, int cursor)
1420{
1421 FileListEntryCache *cache = filelist->filelist_cache;
1422
1423 int i;
1424
1425 for (i = 0; i < size; i++, cursor++) {
1426 FileDirEntry *entry = cache->block_entries[cursor];
1427#if 0
1428 printf("%s: release cacheidx %d (%%p %%s)\n",
1429 __func__,
1430 cursor /*, cache->block_entries[cursor], cache->block_entries[cursor]->relpath*/);
1431#endif
1432 BLI_ghash_remove(cache->uids, POINTER_FROM_UINT(entry->uid), nullptr, nullptr);
1433 filelist_file_release_entry(filelist, entry);
1434#ifndef NDEBUG
1435 cache->block_entries[cursor] = nullptr;
1436#endif
1437 }
1438}
1439
1440bool filelist_file_cache_block(FileList *filelist, const int index)
1441{
1442 FileListEntryCache *cache = filelist->filelist_cache;
1443 const size_t cache_size = cache->size;
1444
1445 const int entries_num = filelist->filelist.entries_filtered_num;
1446 int start_index = max_ii(0, index - (cache_size / 2));
1447 int end_index = min_ii(entries_num, index + (cache_size / 2));
1448 int i;
1449 const bool full_refresh = (filelist->flags & FL_IS_READY) == 0;
1450
1451 if ((index < 0) || (index >= entries_num)) {
1452 // printf("Wrong index %d ([%d:%d])", index, 0, entries_num);
1453 return false;
1454 }
1455
1456 /* Maximize cached range! */
1457 if ((end_index - start_index) < cache_size) {
1458 if (start_index == 0) {
1459 end_index = min_ii(entries_num, start_index + cache_size);
1460 }
1461 else if (end_index == entries_num) {
1462 start_index = max_ii(0, end_index - cache_size);
1463 }
1464 }
1465
1466 BLI_assert((end_index - start_index) <= cache_size);
1467
1468 // printf("%s: [%d:%d] around index %d (current cache: [%d:%d])\n", __func__,
1469 // start_index, end_index, index, cache->block_start_index, cache->block_end_index);
1470
1471 /* If we have something to (re)cache... */
1472 if (full_refresh || (start_index != cache->block_start_index) ||
1473 (end_index != cache->block_end_index))
1474 {
1475 if (full_refresh || (start_index >= cache->block_end_index) ||
1476 (end_index <= cache->block_start_index))
1477 {
1478 int size1 = cache->block_end_index - cache->block_start_index;
1479 int size2 = 0;
1480 int idx1 = cache->block_cursor, idx2 = 0;
1481
1482 // printf("Full Recaching!\n");
1483
1484 if (cache->flags & FLC_PREVIEWS_ACTIVE) {
1486 }
1487
1488 if (idx1 + size1 > cache_size) {
1489 size2 = idx1 + size1 - cache_size;
1490 size1 -= size2;
1491 filelist_file_cache_block_release(filelist, size2, idx2);
1492 }
1493 filelist_file_cache_block_release(filelist, size1, idx1);
1494
1495 cache->block_start_index = cache->block_end_index = cache->block_cursor = 0;
1496
1497 /* New cached block does not overlap existing one, simple. */
1498 if (!filelist_file_cache_block_create(filelist, start_index, end_index - start_index, 0)) {
1499 return false;
1500 }
1501
1502 cache->block_start_index = start_index;
1503 cache->block_end_index = end_index;
1504 }
1505 else {
1506 // printf("Partial Recaching!\n");
1507
1508 /* At this point, we know we keep part of currently cached entries, so update previews
1509 * if needed, and remove everything from working queue - we'll add all newly needed
1510 * entries at the end. */
1511 if (cache->flags & FLC_PREVIEWS_ACTIVE) {
1514 }
1515
1516 // printf("\tpreview cleaned up...\n");
1517
1518 if (start_index > cache->block_start_index) {
1519 int size1 = start_index - cache->block_start_index;
1520 int size2 = 0;
1521 int idx1 = cache->block_cursor, idx2 = 0;
1522
1523 // printf("\tcache releasing: [%d:%d] (%d, %d)\n",
1524 // cache->block_start_index, cache->block_start_index + size1,
1525 // cache->block_cursor, size1);
1526
1527 if (idx1 + size1 > cache_size) {
1528 size2 = idx1 + size1 - cache_size;
1529 size1 -= size2;
1530 filelist_file_cache_block_release(filelist, size2, idx2);
1531 }
1532 filelist_file_cache_block_release(filelist, size1, idx1);
1533
1534 cache->block_cursor = (idx1 + size1 + size2) % cache_size;
1535 cache->block_start_index = start_index;
1536 }
1537 if (end_index < cache->block_end_index) {
1538 int size1 = cache->block_end_index - end_index;
1539 int size2 = 0;
1540 int idx1, idx2 = 0;
1541
1542#if 0
1543 printf("\tcache releasing: [%d:%d] (%d)\n",
1544 cache->block_end_index - size1,
1545 cache->block_end_index,
1546 cache->block_cursor);
1547#endif
1548
1549 idx1 = (cache->block_cursor + end_index - cache->block_start_index) % cache_size;
1550 if (idx1 + size1 > cache_size) {
1551 size2 = idx1 + size1 - cache_size;
1552 size1 -= size2;
1553 filelist_file_cache_block_release(filelist, size2, idx2);
1554 }
1555 filelist_file_cache_block_release(filelist, size1, idx1);
1556
1557 cache->block_end_index = end_index;
1558 }
1559
1560 // printf("\tcache cleaned up...\n");
1561
1562 if (start_index < cache->block_start_index) {
1563 /* Add (request) needed entries before already cached ones. */
1564 /* NOTE: We need some index black magic to wrap around (cycle)
1565 * inside our cache_size array... */
1566 int size1 = cache->block_start_index - start_index;
1567 int size2 = 0;
1568 int idx1, idx2;
1569
1570 if (size1 > cache->block_cursor) {
1571 size2 = size1;
1572 size1 -= cache->block_cursor;
1573 size2 -= size1;
1574 idx2 = 0;
1575 idx1 = cache_size - size1;
1576 }
1577 else {
1578 idx1 = cache->block_cursor - size1;
1579 }
1580
1581 if (size2) {
1582 if (!filelist_file_cache_block_create(filelist, start_index + size1, size2, idx2)) {
1583 return false;
1584 }
1585 }
1586 if (!filelist_file_cache_block_create(filelist, start_index, size1, idx1)) {
1587 return false;
1588 }
1589
1590 cache->block_cursor = idx1;
1591 cache->block_start_index = start_index;
1592 }
1593 // printf("\tstart-extended...\n");
1594 if (end_index > cache->block_end_index) {
1595 /* Add (request) needed entries after already cached ones. */
1596 /* NOTE: We need some index black magic to wrap around (cycle)
1597 * inside our cache_size array... */
1598 int size1 = end_index - cache->block_end_index;
1599 int size2 = 0;
1600 int idx1, idx2;
1601
1602 idx1 = (cache->block_cursor + end_index - cache->block_start_index - size1) % cache_size;
1603 if ((idx1 + size1) > cache_size) {
1604 size2 = size1;
1605 size1 = cache_size - idx1;
1606 size2 -= size1;
1607 idx2 = 0;
1608 }
1609
1610 if (size2) {
1611 if (!filelist_file_cache_block_create(filelist, end_index - size2, size2, idx2)) {
1612 return false;
1613 }
1614 }
1615 if (!filelist_file_cache_block_create(filelist, end_index - size1 - size2, size1, idx1)) {
1616 return false;
1617 }
1618
1619 cache->block_end_index = end_index;
1620 }
1621
1622 // printf("\tend-extended...\n");
1623 }
1624 }
1625 else if ((cache->block_center_index != index) && (cache->flags & FLC_PREVIEWS_ACTIVE)) {
1626 /* We try to always preview visible entries first, so 'restart' preview background task. */
1629 }
1630
1631 // printf("Re-queueing previews...\n");
1632
1633 if (cache->flags & FLC_PREVIEWS_ACTIVE) {
1634 /* Note we try to preview first images around given index - i.e. assumed visible ones. */
1635 int block_index = cache->block_cursor + (index - start_index);
1636 int offs_max = max_ii(end_index - index, index - start_index);
1637 for (i = 0; i <= offs_max; i++) {
1638 int offs = i;
1639 do {
1640 int offs_idx = index + offs;
1641 if (start_index <= offs_idx && offs_idx < end_index) {
1642 int offs_block_idx = (block_index + offs) % int(cache_size);
1643 filelist_cache_previews_push(filelist, cache->block_entries[offs_block_idx], offs_idx);
1644 }
1645 } while ((offs = -offs) < 0); /* Switch between negative and positive offset. */
1646 }
1647 }
1648
1649 cache->block_center_index = index;
1650
1651 // printf("%s Finished!\n", __func__);
1652
1653 return true;
1654}
1655
1656void filelist_cache_previews_set(FileList *filelist, const bool use_previews)
1657{
1658 FileListEntryCache *cache = filelist->filelist_cache;
1659
1660 if (use_previews == ((cache->flags & FLC_PREVIEWS_ACTIVE) != 0)) {
1661 return;
1662 }
1663 /* Do not start preview work while listing, gives nasty flickering! */
1664 if (use_previews && (filelist->flags & FL_IS_READY)) {
1665 cache->flags |= FLC_PREVIEWS_ACTIVE;
1666
1667 BLI_assert((cache->previews_pool == nullptr) && (cache->previews_done == nullptr) &&
1668 (cache->previews_todo_count == 0));
1669
1670 // printf("%s: Init Previews...\n", __func__);
1671
1672 /* No need to populate preview queue here, filelist_file_cache_block() handles this. */
1673 }
1674 else {
1675 // printf("%s: Clear Previews...\n", __func__);
1676
1678 }
1679}
1680
1682{
1683 FileListEntryCache *cache = filelist->filelist_cache;
1684 TaskPool *pool = cache->previews_pool;
1685 bool changed = false;
1686
1687 if (!pool) {
1688 return changed;
1689 }
1690
1691 // printf("%s: Update Previews...\n", __func__);
1692
1693 while (!BLI_thread_queue_is_empty(cache->previews_done)) {
1694 FileListEntryPreview *preview = static_cast<FileListEntryPreview *>(
1696 FileDirEntry *entry;
1697
1698 /* Paranoid (should never happen currently
1699 * since we consume this queue from a single thread), but... */
1700 if (!preview) {
1701 continue;
1702 }
1703 /* entry might have been removed from cache in the mean time,
1704 * we do not want to cache it again here. */
1705 entry = filelist_file_ex(filelist, preview->index, false);
1706
1707 // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->filepath, preview->img);
1708
1709 if (entry) {
1710 if (preview->icon_id) {
1711 /* The FILE_ENTRY_PREVIEW_LOADING flag should have prevented any other asynchronous
1712 * process from trying to generate the same preview icon. */
1713 BLI_assert_msg(!entry->preview_icon_id, "Preview icon should not have been generated yet");
1714
1715 /* Move ownership over icon. */
1716 entry->preview_icon_id = preview->icon_id;
1717 preview->icon_id = 0;
1718 }
1719 else {
1720 /* We want to avoid re-processing this entry continuously!
1721 * Note that, since entries only live in cache,
1722 * preview will be retried quite often anyway. */
1724 }
1726 changed = true;
1727 }
1728 else {
1729 BKE_icon_delete(preview->icon_id);
1730 }
1731
1732 MEM_freeN(preview);
1733 cache->previews_todo_count--;
1734 }
1735
1736 return changed;
1737}
1738
1740{
1741 FileListEntryCache *cache = filelist->filelist_cache;
1742
1743 return (cache->previews_pool != nullptr);
1744}
1745
1747{
1748 FileListEntryCache *cache = filelist->filelist_cache;
1749 if ((cache->flags & FLC_PREVIEWS_ACTIVE) == 0) {
1750 /* There are no previews. */
1751 return false;
1752 }
1753
1754 return (cache->previews_pool == nullptr) || (cache->previews_done == nullptr) ||
1755 (cache->previews_todo_count == 0);
1756}
1757
1758/* would recognize .blend as well */
1759static bool file_is_blend_backup(const char *str)
1760{
1761 const size_t a = strlen(str);
1762 size_t b = 7;
1763 bool retval = false;
1764
1765 if (a == 0 || b >= a) {
1766 /* pass */
1767 }
1768 else {
1769 const char *loc;
1770
1771 if (a > b + 1) {
1772 b++;
1773 }
1774
1775 /* allow .blend1 .blend2 .blend32 */
1776 loc = BLI_strcasestr(str + a - b, ".blend");
1777
1778 if (loc) {
1779 retval = true;
1780 }
1781 }
1782
1783 return retval;
1784}
1785
1786int ED_path_extension_type(const char *path)
1787{
1788 /* ATTENTION: Never return OR'ed bit-flags here, always return a single enum value! Some code
1789 * using this may do `ELEM()`-like checks. */
1790
1792 return FILE_TYPE_BLENDER;
1793 }
1794 if (file_is_blend_backup(path)) {
1796 }
1797#ifdef __APPLE__
1799 /* Application bundle */
1800 ".app",
1801 /* Safari in-progress/paused download */
1802 ".download",
1803 nullptr))
1804 {
1805 return FILE_TYPE_BUNDLE;
1806 }
1807#endif
1808 if (BLI_path_extension_check(path, ".py")) {
1809 return FILE_TYPE_PYSCRIPT;
1810 }
1812 ".txt",
1813 ".glsl",
1814 ".osl",
1815 ".data",
1816 ".pov",
1817 ".ini",
1818 ".mcr",
1819 ".inc",
1820 ".fountain",
1821 nullptr))
1822 {
1823 return FILE_TYPE_TEXT;
1824 }
1825
1826 /* NOTE: While `.ttc` & `.otc` files can be loaded, only a single "face" is supported,
1827 * users will have to extract bold/italic etc manually for Blender to use them, see #44254. */
1828 if (BLI_path_extension_check_n(path, ".ttf", ".pfb", ".otf", ".woff", ".woff2", nullptr)) {
1829 return FILE_TYPE_FTFONT;
1830 }
1831 if (BLI_path_extension_check(path, ".btx")) {
1832 return FILE_TYPE_BTX;
1833 }
1834 if (BLI_path_extension_check(path, ".abc")) {
1835 return FILE_TYPE_ALEMBIC;
1836 }
1837 if (BLI_path_extension_check_n(path, ".usd", ".usda", ".usdc", ".usdz", nullptr)) {
1838 return FILE_TYPE_USD;
1839 }
1840 if (BLI_path_extension_check(path, ".vdb")) {
1841 return FILE_TYPE_VOLUME;
1842 }
1843 if (BLI_path_extension_check(path, ".zip")) {
1844 return FILE_TYPE_ARCHIVE;
1845 }
1847 path, ".obj", ".mtl", ".3ds", ".fbx", ".glb", ".gltf", ".svg", ".ply", ".stl", nullptr))
1848 {
1849 return FILE_TYPE_OBJECT_IO;
1850 }
1852 return FILE_TYPE_IMAGE;
1853 }
1854 if (BLI_path_extension_check(path, ".ogg")) {
1855 if (MOV_is_movie_file(path)) {
1856 return FILE_TYPE_MOVIE;
1857 }
1858 return FILE_TYPE_SOUND;
1859 }
1861 return FILE_TYPE_MOVIE;
1862 }
1864 return FILE_TYPE_SOUND;
1865 }
1866 return 0;
1867}
1868
1869int ED_file_extension_icon(const char *path)
1870{
1871 const int type = ED_path_extension_type(path);
1872
1873 switch (type) {
1874 case FILE_TYPE_BLENDER:
1875 return ICON_FILE_BLEND;
1877 return ICON_FILE_BACKUP;
1878 case FILE_TYPE_IMAGE:
1879 return ICON_FILE_IMAGE;
1880 case FILE_TYPE_MOVIE:
1881 return ICON_FILE_MOVIE;
1882 case FILE_TYPE_PYSCRIPT:
1883 return ICON_FILE_SCRIPT;
1884 case FILE_TYPE_SOUND:
1885 return ICON_FILE_SOUND;
1886 case FILE_TYPE_FTFONT:
1887 return ICON_FILE_FONT;
1888 case FILE_TYPE_BTX:
1889 return ICON_FILE_BLANK;
1890 case FILE_TYPE_ALEMBIC:
1892 return ICON_FILE_3D;
1893 case FILE_TYPE_TEXT:
1894 return ICON_FILE_TEXT;
1895 case FILE_TYPE_ARCHIVE:
1896 return ICON_FILE_ARCHIVE;
1897 case FILE_TYPE_VOLUME:
1898 return ICON_FILE_VOLUME;
1899 default:
1900 return ICON_FILE_BLANK;
1901 }
1902}
1903
1905{
1906 return (filelist->filelist.entries_num == FILEDIR_NBR_ENTRIES_UNSET) ||
1908}
1909
1911 const FileDirEntry *entry,
1914 FileCheckType check)
1915{
1916 /* Default nullptr pointer if not found is fine here! */
1917 void **es_p = BLI_ghash_lookup_p(filelist->selection_state, POINTER_FROM_UINT(entry->uid));
1918 eDirEntry_SelectFlag entry_flag = eDirEntry_SelectFlag(es_p ? POINTER_AS_UINT(*es_p) : 0);
1919 const eDirEntry_SelectFlag org_entry_flag = entry_flag;
1920
1921 BLI_assert(entry);
1923
1924 if ((check == CHECK_ALL) || ((check == CHECK_DIRS) && (entry->typeflag & FILE_TYPE_DIR)) ||
1925 ((check == CHECK_FILES) && !(entry->typeflag & FILE_TYPE_DIR)))
1926 {
1927 switch (select) {
1928 case FILE_SEL_REMOVE:
1929 entry_flag &= ~flag;
1930 break;
1931 case FILE_SEL_ADD:
1932 entry_flag |= flag;
1933 break;
1934 case FILE_SEL_TOGGLE:
1935 entry_flag ^= flag;
1936 break;
1937 }
1938 }
1939
1940 if (entry_flag != org_entry_flag) {
1941 if (es_p) {
1942 if (entry_flag) {
1943 *es_p = POINTER_FROM_UINT(entry_flag);
1944 }
1945 else {
1947 filelist->selection_state, POINTER_FROM_UINT(entry->uid), nullptr, nullptr);
1948 }
1949 }
1950 else if (entry_flag) {
1952 filelist->selection_state, POINTER_FROM_UINT(entry->uid), POINTER_FROM_UINT(entry_flag));
1953 }
1954 }
1955
1956 return entry_flag;
1957}
1958
1960 const int index,
1963 FileCheckType check)
1964{
1965 FileDirEntry *entry = filelist_file(filelist, index);
1966
1967 if (entry) {
1968 filelist_entry_select_set(filelist, entry, select, flag, check);
1969 }
1970}
1971
1973 FileSelection *sel,
1976 FileCheckType check)
1977{
1978 /* select all valid files between first and last indicated */
1979 if ((sel->first >= 0) && (sel->first < filelist->filelist.entries_filtered_num) &&
1980 (sel->last >= 0) && (sel->last < filelist->filelist.entries_filtered_num))
1981 {
1982 int current_file;
1983 for (current_file = sel->first; current_file <= sel->last; current_file++) {
1984 filelist_entry_select_index_set(filelist, current_file, select, flag, check);
1985 }
1986 }
1987}
1988
1990 FileDirEntry *entry,
1991 FileCheckType check)
1992{
1993 BLI_assert(entry);
1995
1996 if ((check == CHECK_ALL) || ((check == CHECK_DIRS) && (entry->typeflag & FILE_TYPE_DIR)) ||
1997 ((check == CHECK_FILES) && !(entry->typeflag & FILE_TYPE_DIR)))
1998 {
1999 /* Default nullptr pointer if not found is fine here! */
2002 }
2003
2004 return eDirEntry_SelectFlag(0);
2005}
2006
2008 const int index,
2009 FileCheckType check)
2010{
2011 FileDirEntry *entry = filelist_file(filelist, index);
2012
2013 if (entry) {
2014 return filelist_entry_select_get(filelist, entry, check);
2015 }
2016
2017 return eDirEntry_SelectFlag(0);
2018}
2019
2020bool filelist_entry_is_selected(FileList *filelist, const int index)
2021{
2022 BLI_assert(index >= 0 && index < filelist->filelist.entries_filtered_num);
2023 FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index];
2024
2025 /* BLI_ghash_lookup returns nullptr if not found, which gets mapped to 0, which gets mapped to
2026 * "not selected". */
2028 BLI_ghash_lookup(filelist->selection_state, POINTER_FROM_UINT(intern_entry->uid))));
2029
2030 return selection_state != 0;
2031}
2032
2036 FileCheckType check)
2037{
2038 if ((filelist->filter_data.flags & FLF_HIDE_PARENT) == 0) {
2039 filelist_entry_select_index_set(filelist, 0, select, flag, check);
2040 }
2041}
2042
2043bool filelist_islibrary(FileList *filelist, char *dir, char **r_group)
2044{
2045 if (filelist->asset_library) {
2046 return true;
2047 }
2048 return BKE_blendfile_library_path_explode(filelist->filelist.root, dir, r_group, nullptr);
2049}
2050
2051static int groupname_to_code(const char *group)
2052{
2053 char buf[BLO_GROUP_MAX];
2054 char *lslash;
2055
2056 BLI_assert(group);
2057
2058 STRNCPY(buf, group);
2059 lslash = (char *)BLI_path_slash_rfind(buf);
2060 if (lslash) {
2061 lslash[0] = '\0';
2062 }
2063
2064 return buf[0] ? BKE_idtype_idcode_from_name(buf) : 0;
2065}
2066
2072struct TodoDir {
2074 char *dir;
2075};
2076
2116
2122static char *current_relpath_append(const FileListReadJob *job_params, const char *filename)
2123{
2124 const char *relbase = job_params->cur_relbase;
2125
2126 /* Early exit, nothing to join. */
2127 if (!relbase[0]) {
2128 return BLI_strdup(filename);
2129 }
2130
2131 BLI_assert(ELEM(relbase[strlen(relbase) - 1], SEP, ALTSEP));
2132 BLI_assert(BLI_path_is_rel(relbase));
2133
2134 char relpath[FILE_MAX_LIBEXTRA];
2135 /* Using #BLI_path_join works but isn't needed as `rel_subdir` has a trailing slash. */
2136 BLI_string_join(relpath,
2137 sizeof(relpath),
2138 /* + 2 to remove "//" relative path prefix. */
2139 relbase + 2,
2140 filename);
2141
2142 return BLI_strdup(relpath);
2143}
2144
2146 const char *root,
2147 ListBase *entries,
2148 const char *filter_glob,
2149 const bool do_lib,
2150 const char *main_filepath,
2151 const bool skip_currpar)
2152{
2153 direntry *files;
2154 int entries_num = 0;
2155 /* Full path of the item. */
2156 char full_path[FILE_MAX];
2157
2158 const int files_num = BLI_filelist_dir_contents(root, &files);
2159 if (files) {
2160 int i = files_num;
2161 while (i--) {
2162 FileListInternEntry *entry;
2163
2164 if (skip_currpar && FILENAME_IS_CURRPAR(files[i].relname)) {
2165 continue;
2166 }
2167
2168 entry = MEM_new<FileListInternEntry>(__func__);
2169 entry->relpath = current_relpath_append(job_params, files[i].relname);
2170 entry->st = files[i].s;
2171
2172 BLI_path_join(full_path, FILE_MAX, root, files[i].relname);
2173 char *target = full_path;
2174
2175 /* Set initial file type and attributes. */
2176 entry->attributes = BLI_file_attributes(full_path);
2177 if (S_ISDIR(files[i].s.st_mode)
2178#ifdef __APPLE__
2179 && !(ED_path_extension_type(full_path) & FILE_TYPE_BUNDLE)
2180#endif
2181 )
2182 {
2183 entry->typeflag = FILE_TYPE_DIR;
2184 }
2185
2186 /* Is this a file that points to another file? */
2187 if (entry->attributes & FILE_ATTR_ALIAS) {
2189 if (BLI_file_alias_target(full_path, entry->redirection_path)) {
2190 if (BLI_is_dir(entry->redirection_path)) {
2191 entry->typeflag = FILE_TYPE_DIR;
2193 }
2194 else {
2196 }
2197 target = entry->redirection_path;
2198#ifdef WIN32
2199 /* On Windows don't show `.lnk` extension for valid shortcuts. */
2201#endif
2202 }
2203 else {
2205 entry->redirection_path = nullptr;
2206 entry->attributes |= FILE_ATTR_HIDDEN;
2207 }
2208 }
2209
2210 if (!(entry->typeflag & FILE_TYPE_DIR)) {
2211 if (do_lib && BKE_blendfile_extension_check(target)) {
2212 /* If we are considering .blend files as libraries, promote them to directory status. */
2213 entry->typeflag = FILE_TYPE_BLENDER;
2214 /* prevent current file being used as acceptable dir */
2215 if (BLI_path_cmp(main_filepath, target) != 0) {
2216 entry->typeflag |= FILE_TYPE_DIR;
2217 }
2218 }
2219 else {
2221 if (filter_glob[0] && BLI_path_extension_check_glob(target, filter_glob)) {
2222 entry->typeflag |= FILE_TYPE_OPERATOR;
2223 }
2224 }
2225 }
2226
2227#ifndef WIN32
2228 /* Set linux-style dot files hidden too. */
2230 entry->attributes |= FILE_ATTR_HIDDEN;
2231 }
2232#endif
2233
2234 BLI_addtail(entries, entry);
2235 entries_num++;
2236 }
2237 BLI_filelist_free(files, files_num);
2238 }
2239 return entries_num;
2240}
2241
2244
2245 /* Will read both the groups + actual ids from the library. Reduces the amount of times that
2246 * a library needs to be opened. */
2248
2249 /* Will only list assets. */
2251
2252 /* Add given root as result. */
2254};
2256
2258 const FileListReadJob *job_params, const int idcode, const char *group_name)
2259{
2260 FileListInternEntry *entry = MEM_new<FileListInternEntry>(__func__);
2261 entry->relpath = current_relpath_append(job_params, group_name);
2263 entry->blentype = idcode;
2264 return entry;
2265}
2266
2272 ListBase *entries,
2273 BLODataBlockInfo *datablock_info,
2274 const bool prefix_relpath_with_group_name,
2275 const int idcode,
2276 const char *group_name)
2277{
2278 FileListInternEntry *entry = MEM_new<FileListInternEntry>(__func__);
2279 if (prefix_relpath_with_group_name) {
2280 std::string datablock_path = StringRef(group_name) + SEP_STR + datablock_info->name;
2281 entry->relpath = current_relpath_append(job_params, datablock_path.c_str());
2282 }
2283 else {
2284 entry->relpath = current_relpath_append(job_params, datablock_info->name);
2285 }
2287 if (datablock_info) {
2288 entry->blenderlib_has_no_preview = datablock_info->no_preview_found;
2289
2290 if (datablock_info->name[0] == '.') {
2291 entry->attributes |= FILE_ATTR_HIDDEN;
2292 }
2293
2294 if (datablock_info->asset_data) {
2295 entry->typeflag |= FILE_TYPE_ASSET;
2296
2297 if (job_params->load_asset_library) {
2298 /* Take ownership over the asset data (shallow copies into unique_ptr managed memory) to
2299 * pass it on to the asset system. */
2300 std::unique_ptr metadata = std::make_unique<AssetMetaData>(
2301 std::move(*datablock_info->asset_data));
2302 MEM_delete(datablock_info->asset_data);
2303 /* Give back a non-owning pointer, because the data-block info is still needed (e.g. to
2304 * update the asset index). */
2305 datablock_info->asset_data = metadata.get();
2306 datablock_info->free_asset_data = false;
2307
2308 entry->asset = job_params->load_asset_library->add_external_asset(
2309 entry->relpath, datablock_info->name, idcode, std::move(metadata));
2310 }
2311 }
2312 }
2313 entry->blentype = idcode;
2314 BLI_addtail(entries, entry);
2315}
2316
2318 ListBase *entries,
2319 LinkNode *datablock_infos,
2320 const bool prefix_relpath_with_group_name,
2321 const int idcode,
2322 const char *group_name)
2323{
2324 for (LinkNode *ln = datablock_infos; ln; ln = ln->next) {
2325 BLODataBlockInfo *datablock_info = static_cast<BLODataBlockInfo *>(ln->link);
2327 job_params, entries, datablock_info, prefix_relpath_with_group_name, idcode, group_name);
2328 }
2329}
2330
2332 FileListReadJob *job_params,
2333 ListBase *entries,
2334 const FileIndexerEntries *indexer_entries,
2335 const bool prefix_relpath_with_group_name)
2336{
2337 for (const LinkNode *ln = indexer_entries->entries; ln; ln = ln->next) {
2338 FileIndexerEntry *indexer_entry = static_cast<FileIndexerEntry *>(ln->link);
2339 const char *group_name = BKE_idtype_idcode_to_name(indexer_entry->idcode);
2341 entries,
2342 &indexer_entry->datablock_info,
2343 prefix_relpath_with_group_name,
2344 indexer_entry->idcode,
2345 group_name);
2346 }
2347}
2348
2350 const FileListReadJob *job_params)
2351{
2352 FileListInternEntry *entry = MEM_new<FileListInternEntry>(__func__);
2353 entry->relpath = current_relpath_append(job_params, FILENAME_PARENT);
2355 return entry;
2356}
2357
2363
2368};
2369
2371 ListBase *entries,
2372 const ListLibOptions options,
2373 const int read_from_index,
2374 const FileIndexerEntries *indexer_entries)
2375{
2376 int navigate_to_parent_len = 0;
2379 job_params);
2380 BLI_addtail(entries, entry);
2381 navigate_to_parent_len = 1;
2382 }
2383
2384 filelist_readjob_list_lib_add_from_indexer_entries(job_params, entries, indexer_entries, true);
2385 return read_from_index + navigate_to_parent_len;
2386}
2387
2392static std::optional<int> filelist_readjob_list_lib(FileListReadJob *job_params,
2393 const char *root,
2394 ListBase *entries,
2395 const ListLibOptions options,
2396 FileIndexer *indexer_runtime)
2397{
2398 BLI_assert(indexer_runtime);
2399
2400 char dir[FILE_MAX_LIBEXTRA], *group;
2401
2402 BlendHandle *libfiledata = nullptr;
2403
2404 /* Check if the given root is actually a library. All folders are passed to
2405 * `filelist_readjob_list_lib` and based on the number of found entries `filelist_readjob_do`
2406 * will do a dir listing only when this function does not return any entries. */
2407 /* TODO(jbakker): We should consider introducing its own function to detect if it is a lib and
2408 * call it directly from `filelist_readjob_do` to increase readability. */
2409 const bool is_lib = BKE_blendfile_library_path_explode(root, dir, &group, nullptr);
2410 if (!is_lib) {
2411 return std::nullopt;
2412 }
2413
2414 /* The root path contains an ID group (e.g. "Materials" or "Objects"). */
2415 const bool has_group = group != nullptr;
2416
2417 /* Try read from indexer_runtime. */
2418 /* Indexing returns all entries in a blend file. We should ignore the index when listing a group
2419 * inside a blend file, so the `entries` isn't filled with undesired entries.
2420 * This happens when linking or appending data-blocks, where you can navigate into a group (ie
2421 * Materials/Objects) where you only want to work with partial indexes.
2422 *
2423 * Adding support for partial reading/updating indexes would increase the complexity.
2424 */
2425 const bool use_indexer = !has_group;
2426 FileIndexerEntries indexer_entries = {nullptr};
2427 if (use_indexer) {
2428 int read_from_index = 0;
2429 eFileIndexerResult indexer_result = indexer_runtime->callbacks->read_index(
2430 dir, &indexer_entries, &read_from_index, indexer_runtime->user_data);
2431 if (indexer_result == FILE_INDEXER_ENTRIES_LOADED) {
2433 job_params, entries, options, read_from_index, &indexer_entries);
2434 ED_file_indexer_entries_clear(&indexer_entries);
2435 return entries_read;
2436 }
2437 }
2438
2439 /* Open the library file. */
2440 BlendFileReadReport bf_reports{};
2441 libfiledata = BLO_blendhandle_from_file(dir, &bf_reports);
2442 if (libfiledata == nullptr) {
2443 return std::nullopt;
2444 }
2445
2446 /* Add current parent when requested. */
2447 /* Is the navigate to previous level added to the list of entries. When added the return value
2448 * should be increased to match the actual number of entries added. It is introduced to keep
2449 * the code clean and readable and not counting in a single variable. */
2450 int navigate_to_parent_len = 0;
2453 job_params);
2454 BLI_addtail(entries, entry);
2455 navigate_to_parent_len = 1;
2456 }
2457
2458 int group_len = 0;
2459 int datablock_len = 0;
2460 /* Read only the datablocks from this group. */
2461 if (has_group) {
2462 const int idcode = groupname_to_code(group);
2464 libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &datablock_len);
2466 job_params, entries, datablock_infos, false, idcode, group);
2467 BLO_datablock_info_linklist_free(datablock_infos);
2468 }
2469 /* Read all datablocks from all groups. */
2470 else {
2471 LinkNode *groups = BLO_blendhandle_get_linkable_groups(libfiledata);
2472 group_len = BLI_linklist_count(groups);
2473
2474 for (LinkNode *ln = groups; ln; ln = ln->next) {
2475 const char *group_name = static_cast<char *>(ln->link);
2476 const int idcode = groupname_to_code(group_name);
2478 job_params, idcode, group_name);
2479 BLI_addtail(entries, group_entry);
2480
2482 int group_datablock_len;
2483 LinkNode *group_datablock_infos = BLO_blendhandle_get_datablock_info(
2484 libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &group_datablock_len);
2486 job_params, entries, group_datablock_infos, true, idcode, group_name);
2487 if (use_indexer) {
2489 &indexer_entries, group_datablock_infos, idcode);
2490 }
2491 BLO_datablock_info_linklist_free(group_datablock_infos);
2492 datablock_len += group_datablock_len;
2493 }
2494 }
2495
2496 BLI_linklist_freeN(groups);
2497 }
2498
2499 BLO_blendhandle_close(libfiledata);
2500
2501 /* Update the index. */
2502 if (use_indexer) {
2503 indexer_runtime->callbacks->update_index(dir, &indexer_entries, indexer_runtime->user_data);
2504 ED_file_indexer_entries_clear(&indexer_entries);
2505 }
2506
2507 /* Return the number of items added to entries. */
2508 int added_entries_len = group_len + datablock_len + navigate_to_parent_len;
2509 return added_entries_len;
2510}
2511
2512#if 0
2513/* Kept for reference here, in case we want to add back that feature later.
2514 * We do not need it currently. */
2515/* Code ***NOT*** updated for job stuff! */
2516static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
2517{
2518 ID *id;
2519 FileDirEntry *files, *firstlib = nullptr;
2520 ListBase *lb;
2521 int a, fake, idcode, ok, totlib, totbl;
2522
2523 // filelist->type = FILE_MAIN; /* XXX TODO: add modes to file-browser */
2524
2525 BLI_assert(filelist->filelist.entries == nullptr);
2526
2527 if (filelist->filelist.root[0] == '/') {
2528 filelist->filelist.root[0] = '\0';
2529 }
2530
2531 if (filelist->filelist.root[0]) {
2532 idcode = groupname_to_code(filelist->filelist.root);
2533 if (idcode == 0) {
2534 filelist->filelist.root[0] = '\0';
2535 }
2536 }
2537
2538 if (filelist->dir[0] == 0) {
2539 /* make directories */
2540# ifdef WITH_FREESTYLE
2541 filelist->filelist.entries_num = 27;
2542# else
2543 filelist->filelist.entries_num = 26;
2544# endif
2545 filelist_resize(filelist, filelist->filelist.entries_num);
2546
2547 for (a = 0; a < filelist->filelist.entries_num; a++) {
2548 filelist->filelist.entries[a].typeflag |= FILE_TYPE_DIR;
2549 }
2550
2551 filelist->filelist.entries[0].entry->relpath = BLI_strdup(FILENAME_PARENT);
2552 filelist->filelist.entries[1].entry->relpath = BLI_strdup("Scene");
2553 filelist->filelist.entries[2].entry->relpath = BLI_strdup("Object");
2554 filelist->filelist.entries[3].entry->relpath = BLI_strdup("Mesh");
2555 filelist->filelist.entries[4].entry->relpath = BLI_strdup("Curve");
2556 filelist->filelist.entries[5].entry->relpath = BLI_strdup("Metaball");
2557 filelist->filelist.entries[6].entry->relpath = BLI_strdup("Material");
2558 filelist->filelist.entries[7].entry->relpath = BLI_strdup("Texture");
2559 filelist->filelist.entries[8].entry->relpath = BLI_strdup("Image");
2560 filelist->filelist.entries[9].entry->relpath = BLI_strdup("Ika");
2561 filelist->filelist.entries[10].entry->relpath = BLI_strdup("Wave");
2562 filelist->filelist.entries[11].entry->relpath = BLI_strdup("Lattice");
2563 filelist->filelist.entries[12].entry->relpath = BLI_strdup("Light");
2564 filelist->filelist.entries[13].entry->relpath = BLI_strdup("Camera");
2565 filelist->filelist.entries[14].entry->relpath = BLI_strdup("Ipo");
2566 filelist->filelist.entries[15].entry->relpath = BLI_strdup("World");
2567 filelist->filelist.entries[16].entry->relpath = BLI_strdup("Screen");
2568 filelist->filelist.entries[17].entry->relpath = BLI_strdup("VFont");
2569 filelist->filelist.entries[18].entry->relpath = BLI_strdup("Text");
2570 filelist->filelist.entries[19].entry->relpath = BLI_strdup("Armature");
2571 filelist->filelist.entries[20].entry->relpath = BLI_strdup("Action");
2572 filelist->filelist.entries[21].entry->relpath = BLI_strdup("NodeTree");
2573 filelist->filelist.entries[22].entry->relpath = BLI_strdup("Speaker");
2574 filelist->filelist.entries[23].entry->relpath = BLI_strdup("Curves");
2575 filelist->filelist.entries[24].entry->relpath = BLI_strdup("Point Cloud");
2576 filelist->filelist.entries[25].entry->relpath = BLI_strdup("Volume");
2577# ifdef WITH_FREESTYLE
2578 filelist->filelist.entries[26].entry->relpath = BLI_strdup("FreestyleLineStyle");
2579# endif
2580 }
2581 else {
2582 /* make files */
2583 idcode = groupname_to_code(filelist->filelist.root);
2584
2585 lb = which_libbase(bmain, idcode);
2586 if (lb == nullptr) {
2587 return;
2588 }
2589
2590 filelist->filelist.entries_num = 0;
2591 for (id = lb->first; id; id = id->next) {
2592 if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') {
2593 filelist->filelist.entries_num++;
2594 }
2595 }
2596
2597 /* XXX TODO: if data-browse or append/link #FLF_HIDE_PARENT has to be set. */
2598 if (!(filelist->filter_data.flags & FLF_HIDE_PARENT)) {
2599 filelist->filelist.entries_num++;
2600 }
2601
2602 if (filelist->filelist.entries_num > 0) {
2603 filelist_resize(filelist, filelist->filelist.entries_num);
2604 }
2605
2606 files = filelist->filelist.entries;
2607
2608 if (!(filelist->filter_data.flags & FLF_HIDE_PARENT)) {
2609 files->entry->relpath = BLI_strdup(FILENAME_PARENT);
2610 files->typeflag |= FILE_TYPE_DIR;
2611
2612 files++;
2613 }
2614
2615 totlib = totbl = 0;
2616 for (id = lb->first; id; id = id->next) {
2617 ok = 1;
2618 if (ok) {
2619 if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') {
2620 if (!ID_IS_LINKED(id)) {
2621 files->entry->relpath = BLI_strdup(id->name + 2);
2622 }
2623 else {
2624 char relname[FILE_MAX + (MAX_ID_NAME - 2) + 3];
2625 SNPRINTF(relname, "%s | %s", id->lib->filepath, id->name + 2);
2626 files->entry->relpath = BLI_strdup(relname);
2627 }
2628// files->type |= S_IFREG;
2629# if 0 /* XXX TODO: show the selection status of the objects. */
2630 if (!filelist->has_func) { /* F4 DATA BROWSE */
2631 if (idcode == ID_OB) {
2632 if (((Object *)id)->flag & SELECT) {
2633 files->entry->selflag |= FILE_SEL_SELECTED;
2634 }
2635 }
2636 else if (idcode == ID_SCE) {
2637 if (((Scene *)id)->r.scemode & R_BG_RENDER) {
2638 files->entry->selflag |= FILE_SEL_SELECTED;
2639 }
2640 }
2641 }
2642# endif
2643 // files->entry->nr = totbl + 1;
2644 files->entry->poin = id;
2645 fake = id->flag & ID_FLAG_FAKEUSER;
2646 if (ELEM(idcode, ID_MA, ID_TE, ID_LA, ID_WO, ID_IM)) {
2647 files->typeflag |= FILE_TYPE_IMAGE;
2648 }
2649# if 0
2650 if (id->lib && fake) {
2651 SNPRINTF_UTF8(files->extra, "LF %d", id->us);
2652 }
2653 else if (id->lib) {
2654 SNPRINTF_UTF8(files->extra, "L %d", id->us);
2655 }
2656 else if (fake) {
2657 SNPRINTF_UTF8(files->extra, "F %d", id->us);
2658 }
2659 else {
2660 SNPRINTF_UTF8(files->extra, " %d", id->us);
2661 }
2662# endif
2663
2664 if (id->lib) {
2665 if (totlib == 0) {
2666 firstlib = files;
2667 }
2668 totlib++;
2669 }
2670
2671 files++;
2672 }
2673 totbl++;
2674 }
2675 }
2676
2677 /* only qsort of library blocks */
2678 if (totlib > 1) {
2679 qsort(firstlib, totlib, sizeof(*files), compare_name);
2680 }
2681 }
2682}
2683#endif
2684
2689 ListBase *from_entries,
2690 int from_entries_num)
2691{
2692 BLI_assert(BLI_listbase_count(from_entries) == from_entries_num);
2693 if (from_entries_num <= 0) {
2694 return false;
2695 }
2696
2697 FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
2698 std::scoped_lock lock(job_params->lock);
2699 BLI_movelisttolist(&filelist->filelist.entries, from_entries);
2700 filelist->filelist.entries_num += from_entries_num;
2701
2702 return true;
2703}
2704
2705static bool filelist_readjob_should_recurse_into_entry(const int max_recursion,
2706 const bool is_lib,
2707 const int current_recursion_level,
2708 FileListInternEntry *entry)
2709{
2710 if (max_recursion == 0) {
2711 /* Recursive loading is disabled. */
2712 return false;
2713 }
2714 if (!is_lib && current_recursion_level > max_recursion) {
2715 /* No more levels of recursion left. */
2716 return false;
2717 }
2718 /* Show entries when recursion is set to `Blend file` even when `current_recursion_level`
2719 * exceeds `max_recursion`. */
2720 if (!is_lib && (current_recursion_level >= max_recursion) &&
2722 {
2723 return false;
2724 }
2725 if (entry->typeflag & FILE_TYPE_BLENDERLIB) {
2726 /* Libraries are already loaded recursively when recursive loaded is used. No need to add
2727 * them another time. This loading is done with the `LIST_LIB_RECURSIVE` option. */
2728 return false;
2729 }
2730 if (!(entry->typeflag & FILE_TYPE_DIR)) {
2731 /* Cannot recurse into regular file entries. */
2732 return false;
2733 }
2734 if (FILENAME_IS_CURRPAR(entry->relpath)) {
2735 /* Don't schedule go to parent entry, (`..`) */
2736 return false;
2737 }
2738
2739 return true;
2740}
2741
2742static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
2743 FileListReadJob *job_params,
2744 const bool *stop,
2745 bool *do_update,
2746 float *progress)
2747{
2748 FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
2749 ListBase entries = {nullptr};
2750 BLI_Stack *todo_dirs;
2751 TodoDir *td_dir;
2752 char dir[FILE_MAX_LIBEXTRA];
2753 char filter_glob[FILE_MAXFILE];
2754 const char *root = filelist->filelist.root;
2755 const int max_recursion = filelist->max_recursion;
2756 int dirs_done_count = 0, dirs_todo_count = 1;
2757
2758 todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__);
2759 td_dir = static_cast<TodoDir *>(BLI_stack_push_r(todo_dirs));
2760 td_dir->level = 1;
2761
2762 STRNCPY(dir, filelist->filelist.root);
2763 STRNCPY(filter_glob, filelist->filter_data.filter_glob);
2764
2765 BLI_path_abs(dir, job_params->main_filepath);
2766 BLI_path_normalize_dir(dir, sizeof(dir));
2767 td_dir->dir = BLI_strdup(dir);
2768
2769 /* Init the file indexer. */
2770 FileIndexer indexer_runtime{};
2771 indexer_runtime.callbacks = filelist->indexer;
2772 if (indexer_runtime.callbacks->init_user_data) {
2773 indexer_runtime.user_data = indexer_runtime.callbacks->init_user_data(dir, sizeof(dir));
2774 }
2775
2776 while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) {
2777 int entries_num = 0;
2778
2779 char *subdir;
2780 char rel_subdir[FILE_MAX_LIBEXTRA];
2781 int recursion_level;
2782 bool skip_currpar;
2783
2784 td_dir = static_cast<TodoDir *>(BLI_stack_peek(todo_dirs));
2785 subdir = td_dir->dir;
2786 recursion_level = td_dir->level;
2787 skip_currpar = (recursion_level > 1);
2788
2789 BLI_stack_discard(todo_dirs);
2790
2791 /* ARRRG! We have to be very careful *not to use* common `BLI_path_utils.hh` helpers over
2792 * entry->relpath itself (nor any path containing it), since it may actually be a datablock
2793 * name inside .blend file, which can have slashes and backslashes! See #46827.
2794 * Note that in the end, this means we 'cache' valid relative subdir once here,
2795 * this is actually better. */
2796 STRNCPY(rel_subdir, subdir);
2797 BLI_path_abs(rel_subdir, root);
2798 BLI_path_normalize_dir(rel_subdir, sizeof(rel_subdir));
2799 BLI_path_rel(rel_subdir, root);
2800
2801 /* Update the current relative base path within the filelist root. */
2802 STRNCPY(job_params->cur_relbase, rel_subdir);
2803
2804 bool is_lib = false;
2805 if (do_lib) {
2806 ListLibOptions list_lib_options = LIST_LIB_OPTION_NONE;
2807 if (!skip_currpar) {
2808 list_lib_options |= LIST_LIB_ADD_PARENT;
2809 }
2810
2811 /* Libraries are loaded recursively when max_recursion is set. It doesn't check if there is
2812 * still a recursion level over. */
2813 if (max_recursion > 0) {
2814 list_lib_options |= LIST_LIB_RECURSIVE;
2815 }
2816 /* Only load assets when browsing an asset library. For normal file browsing we return all
2817 * entries. `FLF_ASSETS_ONLY` filter can be enabled/disabled by the user. */
2818 if (job_params->load_asset_library) {
2819 list_lib_options |= LIST_LIB_ASSETS_ONLY;
2820 }
2821 std::optional<int> lib_entries_num = filelist_readjob_list_lib(
2822 job_params, subdir, &entries, list_lib_options, &indexer_runtime);
2823 if (lib_entries_num) {
2824 is_lib = true;
2825 entries_num += *lib_entries_num;
2826 }
2827 }
2828
2829 if (!is_lib && BLI_is_dir(subdir)) {
2830 entries_num = filelist_readjob_list_dir(job_params,
2831 subdir,
2832 &entries,
2833 filter_glob,
2834 do_lib,
2835 job_params->main_filepath,
2836 skip_currpar);
2837 }
2838
2839 LISTBASE_FOREACH (FileListInternEntry *, entry, &entries) {
2840 entry->uid = filelist_uid_generate(filelist);
2841 entry->name = fileentry_uiname(root, entry, dir);
2842 entry->free_name = true;
2843
2845 max_recursion, is_lib, recursion_level, entry))
2846 {
2847 /* We have a directory we want to list, add it to todo list!
2848 * Using #BLI_path_join works but isn't needed as `root` has a trailing slash. */
2849 BLI_string_join(dir, sizeof(dir), root, entry->relpath);
2850 BLI_path_abs(dir, job_params->main_filepath);
2851 BLI_path_normalize_dir(dir, sizeof(dir));
2852 td_dir = static_cast<TodoDir *>(BLI_stack_push_r(todo_dirs));
2853 td_dir->level = recursion_level + 1;
2854 td_dir->dir = BLI_strdup(dir);
2855 dirs_todo_count++;
2856 }
2857 }
2858
2859 if (filelist_readjob_append_entries(job_params, &entries, entries_num)) {
2860 *do_update = true;
2861 }
2862
2863 dirs_done_count++;
2864 *progress = float(dirs_done_count) / float(dirs_todo_count);
2865 MEM_freeN(subdir);
2866 }
2867
2868 /* Finalize and free indexer. */
2869 if (indexer_runtime.callbacks->filelist_finished && BLI_stack_is_empty(todo_dirs)) {
2870 indexer_runtime.callbacks->filelist_finished(indexer_runtime.user_data);
2871 }
2872 if (indexer_runtime.callbacks->free_user_data && indexer_runtime.user_data) {
2873 indexer_runtime.callbacks->free_user_data(indexer_runtime.user_data);
2874 indexer_runtime.user_data = nullptr;
2875 }
2876
2877 /* If we were interrupted by stop, stack may not be empty and we need to free
2878 * pending dir paths. */
2879 while (!BLI_stack_is_empty(todo_dirs)) {
2880 td_dir = static_cast<TodoDir *>(BLI_stack_peek(todo_dirs));
2881 MEM_freeN(td_dir->dir);
2882 BLI_stack_discard(todo_dirs);
2883 }
2884 BLI_stack_free(todo_dirs);
2885}
2886
2887static void filelist_readjob_do(const bool do_lib,
2888 FileListReadJob *job_params,
2889 const bool *stop,
2890 bool *do_update,
2891 float *progress)
2892{
2893 FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
2894
2895 // BLI_assert(filelist->filtered == nullptr);
2898
2899 /* A valid, but empty directory from now. */
2900 filelist->filelist.entries_num = 0;
2901
2902 filelist_readjob_recursive_dir_add_items(do_lib, job_params, stop, do_update, progress);
2903}
2904
2906 bool *stop,
2907 bool *do_update,
2908 float *progress)
2909{
2910 filelist_readjob_do(false, job_params, stop, do_update, progress);
2911}
2912
2914 bool *stop,
2915 bool *do_update,
2916 float *progress)
2917{
2918 filelist_readjob_do(true, job_params, stop, do_update, progress);
2919}
2920
2924static void filelist_readjob_load_asset_library_data(FileListReadJob *job_params, bool *do_update)
2925{
2926 FileList *tmp_filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
2927
2928 *do_update = false;
2929
2930 if (job_params->filelist->asset_library_ref == nullptr) {
2931 return;
2932 }
2933 if (tmp_filelist->asset_library != nullptr && job_params->reload_asset_library == false) {
2934 /* Asset library itself is already loaded. Load assets into this. */
2935 job_params->load_asset_library = tmp_filelist->asset_library;
2936 return;
2937 }
2938
2939 /* Load asset catalogs, into the temp filelist for thread-safety.
2940 * #filelist_readjob_endjob() will move it into the real filelist. */
2941 tmp_filelist->asset_library = AS_asset_library_load(job_params->current_main,
2942 *job_params->filelist->asset_library_ref);
2943 /* Set asset library to load (may be overridden later for loading nested ones). */
2944 job_params->load_asset_library = tmp_filelist->asset_library;
2945 *do_update = true;
2946}
2947
2949 bool * /*stop*/,
2950 bool *do_update,
2951 float * /*progress*/)
2952{
2953 FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
2954
2955 FileListInternEntry *entry;
2956 ListBase tmp_entries = {nullptr};
2957 ID *id_iter;
2958 int entries_num = 0;
2959
2960 /* Make sure no IDs are added/removed/reallocated in the main thread while this is running in
2961 * parallel. */
2962 BKE_main_lock(job_params->current_main);
2963
2964 FOREACH_MAIN_ID_BEGIN (job_params->current_main, id_iter) {
2965 if (!id_iter->asset_data || ID_IS_LINKED(id_iter)) {
2966 continue;
2967 }
2968
2969 const char *id_code_name = BKE_idtype_idcode_to_name(GS(id_iter->name));
2970
2971 entry = MEM_new<FileListInternEntry>(__func__);
2972 std::string datablock_path = StringRef(id_code_name) + SEP_STR + (id_iter->name + 2);
2973 entry->relpath = current_relpath_append(job_params, datablock_path.c_str());
2974 entry->name = id_iter->name + 2;
2975 entry->free_name = false;
2977 entry->blentype = GS(id_iter->name);
2978 entry->uid = filelist_uid_generate(filelist);
2980 id_iter);
2981 entry->local_data.id = id_iter;
2982 if (job_params->load_asset_library) {
2983 entry->asset = job_params->load_asset_library->add_local_id_asset(*id_iter);
2984 }
2985 entries_num++;
2986 BLI_addtail(&tmp_entries, entry);
2987 }
2989
2990 BKE_main_unlock(job_params->current_main);
2991
2992 if (entries_num) {
2993 *do_update = true;
2994
2995 BLI_movelisttolist(&filelist->filelist.entries, &tmp_entries);
2996 filelist->filelist.entries_num += entries_num;
2997 filelist->filelist.entries_filtered_num = -1;
2998 }
2999}
3000
3008static bool filelist_contains_main(const FileList *filelist, const Main *bmain)
3009{
3010 if (filelist->asset_library_ref && (filelist->asset_library_ref->type == ASSET_LIBRARY_ALL)) {
3011 return true;
3012 }
3013
3014 const char *blendfile_path = BKE_main_blendfile_path(bmain);
3015 return blendfile_path[0] && BLI_path_contains(filelist->filelist.root, blendfile_path);
3016}
3017
3019 bool *stop,
3020 bool *do_update,
3021 float *progress)
3022{
3023 FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
3024
3027
3028 /* A valid, but empty file-list from now. */
3029 filelist->filelist.entries_num = 0;
3030
3031 BLI_assert(job_params->filelist->asset_library_ref != nullptr);
3032
3033 /* NOP if already read. */
3034 filelist_readjob_load_asset_library_data(job_params, do_update);
3035
3036 if (filelist_contains_main(filelist, job_params->current_main)) {
3037 asset_system::AssetLibrary *original_file_library = job_params->load_asset_library;
3040 filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress);
3041 job_params->load_asset_library = original_file_library;
3042 }
3043 if (!job_params->only_main_data) {
3044 filelist_readjob_recursive_dir_add_items(true, job_params, stop, do_update, progress);
3045 }
3046}
3047
3049 bool *stop,
3050 bool *do_update,
3051 float *progress)
3052{
3053 /* TODO! */
3054 filelist_readjob_dir(job_params, stop, do_update, progress);
3055}
3056
3058 bool *stop,
3059 bool *do_update,
3060 float *progress)
3061{
3062 FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
3065
3066 filelist_readjob_load_asset_library_data(job_params, do_update);
3067
3068 /* A valid, but empty file-list from now. */
3069 filelist->filelist.entries_num = 0;
3070
3071 filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress);
3072}
3073
3075 bool *stop,
3076 bool *do_update,
3077 float *progress)
3078{
3079 FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
3082
3083 filelist_readjob_load_asset_library_data(job_params, do_update);
3084
3085 /* A valid, but empty file-list from now. */
3086 filelist->filelist.entries_num = 0;
3087
3088 asset_system::AssetLibrary *current_file_library;
3089 {
3090 AssetLibraryReference library_ref{};
3091 library_ref.custom_library_index = -1;
3092 library_ref.type = ASSET_LIBRARY_LOCAL;
3093
3094 current_file_library = AS_asset_library_load(job_params->current_main, library_ref);
3095 }
3096
3097 job_params->load_asset_library = current_file_library;
3098 filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress);
3099
3100 /* When only doing partially reload for main data, we're done. */
3101 if (job_params->only_main_data) {
3102 return;
3103 }
3104
3105 /* Count how many asset libraries need to be loaded, for progress reporting. Not very precise. */
3106 int library_count = 0;
3107 asset_system::AssetLibrary::foreach_loaded([&library_count](auto &) { library_count++; }, false);
3108
3109 BLI_assert(filelist->asset_library != nullptr);
3110
3111 int libraries_done_count = 0;
3112 /* The "All" asset library was loaded, which means all other asset libraries are also loaded.
3113 * Load their assets from disk into the "All" library. */
3115 [&](asset_system::AssetLibrary &nested_library) {
3116 StringRefNull root_path = nested_library.root_path();
3117 if (root_path.is_empty()) {
3118 return;
3119 }
3120 if (&nested_library == current_file_library) {
3121 /* Skip the "Current File" library, it's already loaded above. */
3122 return;
3123 }
3124
3125 /* Override library info to read this library. */
3126 job_params->load_asset_library = &nested_library;
3127 STRNCPY(filelist->filelist.root, root_path.c_str());
3128
3129 float progress_this = 0.0f;
3131 true, job_params, stop, do_update, &progress_this);
3132
3133 libraries_done_count++;
3134 *progress = float(libraries_done_count) / library_count;
3135 },
3136 false);
3137}
3138
3143{
3144 return read_job->only_main_data;
3145}
3146
3152static void filelist_readjob_startjob(void *flrjv, wmJobWorkerStatus *worker_status)
3153{
3154 FileListReadJob *flrj = static_cast<FileListReadJob *>(flrjv);
3155
3156 // printf("START filelist reading (%d files, main thread: %d)\n",
3157 // flrj->filelist->filelist.entries_num, BLI_thread_is_main());
3158
3159 {
3160 std::scoped_lock lock(flrj->lock);
3161 BLI_assert((flrj->tmp_filelist == nullptr) && flrj->filelist);
3162
3163 flrj->tmp_filelist = static_cast<FileList *>(MEM_dupallocN(flrj->filelist));
3164
3167
3168 flrj->tmp_filelist->filelist_intern.filtered = nullptr;
3171 /* Don't unset the current UID on partial read, would give duplicates otherwise. */
3172 }
3173 else {
3175 }
3176
3177 flrj->tmp_filelist->libfiledata = nullptr;
3178 flrj->tmp_filelist->filelist_cache = nullptr;
3179 flrj->tmp_filelist->selection_state = nullptr;
3180 flrj->tmp_filelist->asset_library_ref = nullptr;
3182 }
3183
3185 flrj, &worker_status->stop, &worker_status->do_update, &worker_status->progress);
3186}
3187
3193static void filelist_readjob_update(void *flrjv)
3194{
3195 FileListReadJob *flrj = static_cast<FileListReadJob *>(flrjv);
3196 FileListIntern *fl_intern = &flrj->filelist->filelist_intern;
3197 ListBase new_entries = {nullptr};
3198 int entries_num, new_entries_num = 0;
3199
3200 BLI_movelisttolist(&new_entries, &fl_intern->entries);
3201 entries_num = flrj->filelist->filelist.entries_num;
3202
3203 {
3204 std::scoped_lock lock(flrj->lock);
3205 if (flrj->tmp_filelist->filelist.entries_num > 0) {
3206 /* We just move everything out of 'thread context' into final list. */
3207 new_entries_num = flrj->tmp_filelist->filelist.entries_num;
3208 BLI_movelisttolist(&new_entries, &flrj->tmp_filelist->filelist.entries);
3210 }
3211
3212 if (flrj->tmp_filelist->asset_library) {
3214 }
3215
3216 /* Important for partial reads: Copy increased UID counter back to the real list. */
3217 fl_intern->curr_uid = std::max(flrj->tmp_filelist->filelist_intern.curr_uid,
3218 fl_intern->curr_uid);
3219 }
3220
3221 if (new_entries_num) {
3222 /* Do not clear selection cache, we can assume already 'selected' UIDs are still valid! Keep
3223 * the asset library data we just read. */
3224 filelist_clear_ex(flrj->filelist, false, true, false);
3225
3227 }
3228
3229 /* if no new_entries_num, this is NOP */
3230 BLI_movelisttolist(&fl_intern->entries, &new_entries);
3231 flrj->filelist->filelist.entries_num = std::max(entries_num, 0) + new_entries_num;
3232}
3233
3234static void filelist_readjob_endjob(void *flrjv)
3235{
3236 FileListReadJob *flrj = static_cast<FileListReadJob *>(flrjv);
3237
3238 /* In case there would be some dangling update... */
3240
3241 flrj->filelist->flags &= ~FL_IS_PENDING;
3242 flrj->filelist->flags |= FL_IS_READY;
3243}
3244
3245static void filelist_readjob_free(void *flrjv)
3246{
3247 FileListReadJob *flrj = static_cast<FileListReadJob *>(flrjv);
3248
3249 // printf("END filelist reading (%d files)\n", flrj->filelist->filelist.entries_num);
3250
3251 if (flrj->tmp_filelist) {
3252 /* tmp_filelist shall never ever be filtered! */
3255
3258 }
3259
3260 MEM_delete(flrj);
3261}
3262
3264{
3265 if (filelist->asset_library_ref) {
3267 }
3269}
3270
3271/* TODO(Julian): This is temporary, because currently the job system identifies jobs to suspend by
3272 * the startjob callback, rather than the type. See PR #123033. */
3273static void assetlibrary_readjob_startjob(void *flrjv, wmJobWorkerStatus *worker_status)
3274{
3275 filelist_readjob_startjob(flrjv, worker_status);
3276}
3277
3279 const int space_notifier,
3280 const bContext *C,
3281 const bool force_blocking_read)
3282{
3283 Main *bmain = CTX_data_main(C);
3284 wmJob *wm_job;
3285 FileListReadJob *flrj;
3286
3287 if (!filelist_is_dir(filelist, filelist->filelist.root)) {
3288 return;
3289 }
3290
3291 /* prepare job data */
3292 flrj = MEM_new<FileListReadJob>(__func__);
3293 flrj->filelist = filelist;
3294 flrj->current_main = bmain;
3296 if ((filelist->flags & FL_FORCE_RESET_MAIN_FILES) && !(filelist->flags & FL_FORCE_RESET) &&
3298 {
3299 flrj->only_main_data = true;
3300 }
3301 if (filelist->flags & FL_RELOAD_ASSET_LIBRARY) {
3302 flrj->reload_asset_library = true;
3303 }
3304
3306 FL_IS_READY);
3307 filelist->flags |= FL_IS_PENDING;
3308
3309 /* The file list type may not support threading so execute immediately. Same when only rereading
3310 * #Main data (which we do quite often on changes to #Main, since it's the easiest and safest way
3311 * to ensure the displayed data is up to date), because some operations executing right after
3312 * main data changed may need access to the ID files (see #93691). */
3313 const bool no_threads = (filelist->tags & FILELIST_TAGS_NO_THREADS) || flrj->only_main_data;
3314
3315 if (force_blocking_read || no_threads) {
3316 /* Single threaded execution. Just directly call the callbacks. */
3317 wmJobWorkerStatus worker_status = {};
3318 filelist_readjob_startjob(flrj, &worker_status);
3321
3322 WM_event_add_notifier(C, space_notifier | NA_JOB_FINISHED, nullptr);
3323 return;
3324 }
3325
3326 /* setup job */
3327 wm_job = WM_jobs_get(CTX_wm_manager(C),
3329 filelist,
3330 "Listing directories...",
3332 filelist_jobtype_get(filelist));
3334 WM_jobs_timer(wm_job, 0.01, space_notifier, space_notifier | NA_JOB_FINISHED);
3335 WM_jobs_callbacks(wm_job,
3338 nullptr,
3341
3342 /* start the job */
3343 WM_jobs_start(CTX_wm_manager(C), wm_job);
3344}
3345
3346void filelist_readjob_start(FileList *filelist, const int space_notifier, const bContext *C)
3347{
3348 filelist_readjob_start_ex(filelist, space_notifier, C, false);
3349}
3350
3351void filelist_readjob_blocking_run(FileList *filelist, int space_notifier, const bContext *C)
3352{
3353 filelist_readjob_start_ex(filelist, space_notifier, C, true);
3354}
3355
3357{
3358 WM_jobs_kill_type(wm, filelist, filelist_jobtype_get(filelist));
3359}
3360
3362{
3363 return WM_jobs_test(wm, filelist, filelist_jobtype_get(filelist));
3364}
blender::asset_system::AssetLibrary * AS_asset_library_load(const Main *bmain, const AssetLibraryReference &library_reference)
Main runtime representation of an asset.
PreviewImage * BKE_asset_metadata_preview_get_from_id(const AssetMetaData *asset_data, const ID *owner_id)
Definition asset.cc:189
bool BKE_blendfile_library_path_explode(const char *path, char *r_dir, char **r_group, char **r_name)
Definition blendfile.cc:92
bool BKE_blendfile_extension_check(const char *str)
Definition blendfile.cc:86
wmWindow * CTX_wm_window(const bContext *C)
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
int BKE_icon_imbuf_create(struct ImBuf *ibuf) ATTR_WARN_UNUSED_RESULT
Definition icons.cc:375
struct ImBuf * BKE_icon_imbuf_get_buffer(int icon_id) ATTR_WARN_UNUSED_RESULT
Definition icons.cc:385
bool BKE_icon_delete(int icon_id)
Definition icons.cc:455
const char * BKE_idtype_idcode_to_name(short idcode)
Definition idtype.cc:164
short BKE_idtype_idcode_from_name(const char *idtype_name)
Definition idtype.cc:185
#define FOREACH_MAIN_ID_END
Definition BKE_main.hh:583
ListBase * which_libbase(Main *bmain, short type)
Definition main.cc:902
void BKE_main_lock(Main *bmain)
Definition main.cc:486
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition BKE_main.hh:577
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:887
void BKE_main_unlock(Main *bmain)
Definition main.cc:491
const char * BKE_main_blendfile_path_from_global()
Definition main.cc:892
struct bUserAssetLibrary * BKE_preferences_asset_library_find_index(const struct UserDef *userdef, int index) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
ImBuf * BKE_previewimg_to_imbuf(const PreviewImage *prv, int size)
bool BKE_previewimg_is_finished(const PreviewImage *prv, int size)
char * BLF_display_name_from_file(const char *filepath) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition blf.cc:1073
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
File and directory operations.
eFileAttributes BLI_file_attributes(const char *path)
Definition storage.cc:237
#define FILE_ATTR_ANY_LINK
unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_filelist)
bool BLI_is_file(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:448
bool BLI_file_alias_target(const char *filepath, char r_targetpath[768]) ATTR_WARN_UNUSED_RESULT
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:443
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries)
@ FILE_ATTR_ALIAS
@ FILE_ATTR_TEMPORARY
@ FILE_ATTR_HIDDEN
@ FILE_ATTR_SYSTEM
@ FILE_ATTR_OFFLINE
Some types for dealing with directories.
void BLI_ghash_clear(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:855
GHash * BLI_ghash_new(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:686
void ** BLI_ghash_lookup_p(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:745
void * BLI_ghash_popkey(GHash *gh, const void *key, GHashKeyFreeFP keyfreefp) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:802
GHash * BLI_ghash_ptr_new_ex(const char *info, unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
bool BLI_ghashutil_intcmp(const void *a, const void *b)
GHash * BLI_ghash_new_ex(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info, unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:678
unsigned int BLI_ghashutil_inthash_p(const void *ptr)
bool BLI_ghash_remove(GHash *gh, const void *key, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:787
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.cc:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
void BLI_ghash_clear_ex(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp, unsigned int nentries_reserve)
Definition BLI_ghash.cc:842
void void void BLI_movelisttolist(ListBase *dst, ListBase *src) ATTR_NONNULL(1
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
void copy_vn_i(int *array_tar, int size, int val)
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
bool BLI_path_parent_dir_until_exists(char *path) ATTR_NONNULL(1)
#define ALTSEP
bool bool BLI_path_extension_strip(char *path) ATTR_NONNULL(1)
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
int BLI_path_normalize_dir(char *dir, size_t dir_maxncpy) ATTR_NONNULL(1)
bool BLI_path_has_hidden_component(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILE_MAXFILE
bool BLI_path_extension_check_array(const char *path, const char **ext_array) ATTR_NONNULL(1
#define FILE_MAX
bool BLI_path_contains(const char *container_path, const char *containee_path) ATTR_NONNULL(1
#define BLI_path_join(...)
bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILENAME_IS_CURRPAR(_n)
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define SEP
bool BLI_path_extension_check(const char *path, const char *ext) ATTR_NONNULL(1
#define FILENAME_PARENT
bool void BLI_path_rel(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1)
bool BLI_path_extension_check_n(const char *path,...) ATTR_NONNULL(1) ATTR_SENTINEL(0)
int BLI_path_slash_ensure(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
const char * BLI_path_slash_rfind(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILE_MAXDIR
bool BLI_path_extension_check_glob(const char *path, const char *ext_fnmatch) ATTR_NONNULL(1
#define BLI_path_cmp
#define FILENAME_IS_PARENT(_n)
bool BLI_stack_is_empty(const BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.cc:250
void BLI_stack_free(BLI_Stack *stack) ATTR_NONNULL()
Definition stack.cc:96
void * BLI_stack_peek(BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.cc:169
void * BLI_stack_push_r(BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.cc:103
void BLI_stack_discard(BLI_Stack *stack) ATTR_NONNULL()
Definition stack.cc:176
#define BLI_stack_new(esize, descr)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:604
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
int char * BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define SNPRINTF_UTF8(dst, format,...)
#define BLI_string_join(...)
unsigned int uint
@ TASK_PRIORITY_LOW
Definition BLI_task.h:52
void * BLI_task_pool_user_data(TaskPool *pool)
Definition task_pool.cc:550
void BLI_task_pool_cancel(TaskPool *pool)
Definition task_pool.cc:540
TaskPool * BLI_task_pool_create_background(void *userdata, eTaskPriority priority)
Definition task_pool.cc:489
void BLI_task_pool_free(TaskPool *pool)
Definition task_pool.cc:521
void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskFreeFunction freedata)
Definition task_pool.cc:526
@ BLI_THREAD_QUEUE_WORK_PRIORITY_NORMAL
void * BLI_thread_queue_pop(ThreadQueue *queue)
Definition threads.cc:712
ThreadQueue * BLI_thread_queue_init(void)
Definition threads.cc:624
void BLI_thread_queue_free(ThreadQueue *queue)
Definition threads.cc:635
bool BLI_thread_queue_is_empty(ThreadQueue *queue)
Definition threads.cc:839
uint64_t BLI_thread_queue_push(ThreadQueue *queue, void *work, ThreadQueueWorkPriority priority)
Definition threads.cc:645
void BLI_thread_queue_nowait(ThreadQueue *queue)
Definition threads.cc:851
void * BLI_thread_queue_pop_timeout(ThreadQueue *queue, int ms)
Definition threads.cc:782
#define ARRAY_SIZE(arr)
#define ARRAY_SET_ITEMS(...)
#define ENUM_OPERATORS(_type, _max)
#define POINTER_FROM_INT(i)
#define POINTER_AS_UINT(i)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define POINTER_FROM_UINT(i)
#define STREQ(a, b)
Compatibility-like things for windows.
void BLI_windows_get_default_root_dir(char root_dir[4])
#define S_ISDIR(x)
void BLO_datablock_info_linklist_free(LinkNode *datablock_infos)
LinkNode * BLO_blendhandle_get_datablock_info(BlendHandle *bh, int ofblocktype, bool use_assets_only, int *r_tot_info_items)
#define BLO_GROUP_MAX
BlendHandle * BLO_blendhandle_from_file(const char *filepath, BlendFileReadReport *reports)
void BLO_blendhandle_close(BlendHandle *bh) ATTR_NONNULL(1)
LinkNode * BLO_blendhandle_get_linkable_groups(BlendHandle *bh)
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
#define MAX_ID_NAME
Definition DNA_ID.h:373
@ ID_FLAG_FAKEUSER
Definition DNA_ID.h:769
@ ICON_SIZE_PREVIEW
@ ID_TE
@ ID_IM
@ ID_LA
@ ID_SCE
@ ID_WO
@ ID_MA
@ ID_OB
@ ASSET_LIBRARY_CUSTOM
@ ASSET_LIBRARY_LOCAL
@ ASSET_LIBRARY_ALL
@ R_BG_RENDER
eFileSelectType
@ FILE_LOADLIB
@ FILE_ASSET_LIBRARY
@ FILE_MAIN_ASSET
@ FILE_MAIN
@ FILE_ASSET_LIBRARY_ALL
eFileSel_File_Types
@ FILE_TYPE_BTX
@ FILE_TYPE_BLENDER
@ FILE_TYPE_ASSET
@ FILE_TYPE_BUNDLE
@ FILE_TYPE_ALEMBIC
@ FILE_TYPE_ARCHIVE
@ FILE_TYPE_TEXT
@ FILE_TYPE_PYSCRIPT
@ FILE_TYPE_BLENDER_BACKUP
@ FILE_TYPE_VOLUME
@ FILE_TYPE_MOVIE
@ FILE_TYPE_SOUND
@ FILE_TYPE_OBJECT_IO
@ FILE_TYPE_FTFONT
@ FILE_TYPE_BLENDERLIB
@ FILE_TYPE_OPERATOR
@ FILE_TYPE_USD
@ FILE_TYPE_IMAGE
@ FILE_TYPE_DIR
eFileSel_Params_AssetCatalogVisibility
eDirEntry_SelectFlag
@ FILE_SEL_SELECTED
@ FILE_ENTRY_BLENDERLIB_NO_PREVIEW
@ FILE_ENTRY_PREVIEW_LOADING
@ FILE_ENTRY_NAME_FREE
@ FILE_ENTRY_INVALID_PREVIEW
#define FILE_MAX_LIBEXTRA
eFileIndexerResult
@ FILE_INDEXER_ENTRIES_LOADED
void ED_file_indexer_entries_clear(FileIndexerEntries *indexer_entries)
void ED_file_indexer_entries_extend_from_datablock_infos(FileIndexerEntries *indexer_entries, LinkNode *datablock_infos, int idcode)
FSMenu * ED_fsmenu_get()
Definition fsmenu.cc:46
FSMenuCategory
@ FS_CATEGORY_SYSTEM_BOOKMARKS
@ FS_CATEGORY_OTHER
@ FS_CATEGORY_SYSTEM
FSMenuEntry * ED_fsmenu_get_category(FSMenu *fsmenu, FSMenuCategory category)
Definition fsmenu.cc:54
int ED_path_extension_type(const char *path)
Definition filelist.cc:1786
void IMB_freeImBuf(ImBuf *ibuf)
const char * imb_ext_movie[]
const char * imb_ext_audio[]
const char * imb_ext_image[]
@ THB_LARGE
Definition IMB_thumbs.hh:23
void IMB_thumb_path_unlock(const char *path)
Definition thumbs.cc:723
ThumbSource
Definition IMB_thumbs.hh:27
@ THB_SOURCE_IMAGE
Definition IMB_thumbs.hh:28
@ THB_SOURCE_FONT
Definition IMB_thumbs.hh:31
@ THB_SOURCE_BLEND
Definition IMB_thumbs.hh:30
@ THB_SOURCE_MOVIE
Definition IMB_thumbs.hh:29
@ THB_SOURCE_OBJECT_IO
Definition IMB_thumbs.hh:32
void IMB_thumb_locks_acquire()
Definition thumbs.cc:676
ImBuf * IMB_thumb_manage(const char *file_or_lib_path, ThumbSize size, ThumbSource source)
Definition thumbs.cc:538
void IMB_thumb_path_lock(const char *path)
Definition thumbs.cc:707
void IMB_thumb_locks_release()
Definition thumbs.cc:692
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
#define C
Definition RandGen.cpp:29
int UI_icon_from_idcode(int idcode)
ImBuf * UI_svg_icon_bitmap(uint icon_id, float size, bool multicolor=false)
eWM_JobType
Definition WM_api.hh:1773
@ WM_JOB_TYPE_ASSET_LIBRARY_LOAD
Definition WM_api.hh:1787
@ WM_JOB_TYPE_FILESEL_READDIR
Definition WM_api.hh:1786
@ WM_JOB_PROGRESS
Definition WM_api.hh:1766
#define NA_JOB_FINISHED
Definition WM_types.hh:592
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x)
volatile int lock
#define U
long long int int64_t
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr bool is_empty() const
constexpr int64_t size() const
constexpr const char * c_str() const
std::weak_ptr< AssetRepresentation > add_local_id_asset(ID &id)
static void foreach_loaded(FunctionRef< void(AssetLibrary &)> fn, bool include_all_library)
bool remove_asset(AssetRepresentation &asset)
std::weak_ptr< AssetRepresentation > add_external_asset(StringRef relative_asset_path, StringRef name, int id_type, std::unique_ptr< AssetMetaData > metadata)
nullptr float
#define SELECT
CCL_NAMESPACE_BEGIN struct Options options
#define str(s)
const FileIndexerType file_indexer_noop
eDirEntry_SelectFlag filelist_entry_select_index_get(FileList *filelist, const int index, FileCheckType check)
Definition filelist.cc:2007
int ED_file_icon(const FileDirEntry *file)
Definition filelist.cc:397
static char * current_relpath_append(const FileListReadJob *job_params, const char *filename)
Definition filelist.cc:2122
static bool filelist_readjob_should_recurse_into_entry(const int max_recursion, const bool is_lib, const int current_recursion_level, FileListInternEntry *entry)
Definition filelist.cc:2705
void filelist_freelib(FileList *filelist)
Definition filelist.cc:1032
void filelist_entry_parent_select_set(FileList *filelist, FileSelType select, const eDirEntry_SelectFlag flag, FileCheckType check)
Definition filelist.cc:2033
void filelist_tag_force_reset(FileList *filelist)
Definition filelist.cc:1135
int filelist_files_num_entries(FileList *filelist)
Definition filelist.cc:1045
static eWM_JobType filelist_jobtype_get(const FileList *filelist)
Definition filelist.cc:3263
static std::optional< int > filelist_readjob_list_lib(FileListReadJob *job_params, const char *root, ListBase *entries, const ListLibOptions options, FileIndexer *indexer_runtime)
Definition filelist.cc:2392
bool filelist_islibrary(FileList *filelist, char *dir, char **r_group)
Definition filelist.cc:2043
void filelist_entries_select_index_range_set(FileList *filelist, FileSelection *sel, FileSelType select, const eDirEntry_SelectFlag flag, FileCheckType check)
Definition filelist.cc:1972
void filelist_cache_clear(FileListEntryCache *cache, size_t new_size)
Definition filelist.cc:817
static void filelist_readjob_list_lib_add_datablocks(FileListReadJob *job_params, ListBase *entries, LinkNode *datablock_infos, const bool prefix_relpath_with_group_name, const int idcode, const char *group_name)
Definition filelist.cc:2317
const char * filelist_dir(const FileList *filelist)
Definition filelist.cc:1095
BlendHandle * filelist_lib(FileList *filelist)
Definition filelist.cc:1040
static void filelist_cache_previews_clear(FileListEntryCache *cache)
Definition filelist.cc:642
void filelist_cache_previews_set(FileList *filelist, const bool use_previews)
Definition filelist.cc:1656
static bool filelist_readjob_append_entries(FileListReadJob *job_params, ListBase *from_entries, int from_entries_num)
Definition filelist.cc:2688
static void assetlibrary_readjob_startjob(void *flrjv, wmJobWorkerStatus *worker_status)
Definition filelist.cc:3273
static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdata)
Definition filelist.cc:566
static bool filelist_checkdir_main(const FileList *filelist, char dirpath[FILE_MAX_LIBEXTRA], const bool do_change)
Definition filelist.cc:458
static FileListInternEntry * filelist_entry_intern_get(const FileList *filelist, const int index)
Definition filelist.cc:1326
static void filelist_file_cache_block_release(FileList *filelist, const int size, int cursor)
Definition filelist.cc:1419
static void filelist_intern_free(FileList *filelist)
Definition filelist.cc:532
static void filelist_cache_preview_freef(TaskPool *__restrict, void *taskdata)
Definition filelist.cc:618
static void filelist_clear_main_files(FileList *filelist, const bool do_asset_library, const bool do_cache, const bool do_selection)
Definition filelist.cc:947
blender::asset_system::AssetLibrary * filelist_asset_library(FileList *filelist)
Definition filelist.cc:1027
bool filelist_needs_force_reset(const FileList *filelist)
Definition filelist.cc:1130
static void filelist_readjob_lib(FileListReadJob *job_params, bool *stop, bool *do_update, float *progress)
Definition filelist.cc:2913
void filelist_readjob_blocking_run(FileList *filelist, int space_notifier, const bContext *C)
Definition filelist.cc:3351
bool filelist_file_is_preview_pending(const FileList *filelist, const FileDirEntry *file)
Definition filelist.cc:215
static int filelist_readjob_list_dir(FileListReadJob *job_params, const char *root, ListBase *entries, const char *filter_glob, const bool do_lib, const char *main_filepath, const bool skip_currpar)
Definition filelist.cc:2145
static void filelist_readjob_start_ex(FileList *filelist, const int space_notifier, const bContext *C, const bool force_blocking_read)
Definition filelist.cc:3278
static FileDirEntry * filelist_file_create_entry(FileList *filelist, const int index)
Definition filelist.cc:1178
void filelist_clear_from_reset_tag(FileList *filelist)
Definition filelist.cc:988
static void filelist_readjob_startjob(void *flrjv, wmJobWorkerStatus *worker_status)
Definition filelist.cc:3152
bool filelist_pending(const FileList *filelist)
Definition filelist.cc:1158
static void filelist_direntryarr_free(FileDirEntryArr *array)
Definition filelist.cc:496
bool filelist_cache_previews_running(FileList *filelist)
Definition filelist.cc:1739
static FileDirEntry * filelist_cache_file_lookup(FileListEntryCache *cache, const int index)
Definition filelist.cc:1228
static void parent_dir_until_exists_or_default_root(char *dir)
Definition filelist.cc:410
static bool filelist_checkdir_dir(const FileList *, char dirpath[FILE_MAX_LIBEXTRA], const bool do_change)
Definition filelist.cc:424
static void filelist_readjob_do(const bool do_lib, FileListReadJob *job_params, const bool *stop, bool *do_update, float *progress)
Definition filelist.cc:2887
void filelist_set_asset_catalog_filter_options(FileList *filelist, eFileSel_Params_AssetCatalogVisibility catalog_visibility, const ::bUUID *catalog_id)
Definition filelist.cc:126
void filelist_free(FileList *filelist)
Definition filelist.cc:1002
static void filelist_readjob_all_asset_library(FileListReadJob *job_params, bool *stop, bool *do_update, float *progress)
Definition filelist.cc:3074
void filelist_setlibrary(FileList *filelist, const AssetLibraryReference *asset_library_ref)
Definition filelist.cc:166
static void filelist_cache_previews_free(FileListEntryCache *cache)
Definition filelist.cc:666
bool filelist_file_cache_block(FileList *filelist, const int index)
Definition filelist.cc:1440
asset_system::AssetRepresentation * filelist_entry_get_asset_representation(const FileList *filelist, const int index)
Definition filelist.cc:1338
bool filelist_cache_previews_done(FileList *filelist)
Definition filelist.cc:1746
static int groupname_to_code(const char *group)
Definition filelist.cc:2051
static void filelist_readjob_dir(FileListReadJob *job_params, bool *stop, bool *do_update, float *progress)
Definition filelist.cc:2905
void filelist_clear(FileList *filelist)
Definition filelist.cc:983
static void filelist_readjob_main_assets(FileListReadJob *job_params, bool *stop, bool *do_update, float *progress)
Definition filelist.cc:3057
ImBuf * filelist_get_preview_image(FileList *filelist, const int index)
Definition filelist.cc:230
static void filelist_readjob_endjob(void *flrjv)
Definition filelist.cc:3234
void filelist_clear_ex(FileList *filelist, const bool do_asset_library, const bool do_cache, const bool do_selection)
Definition filelist.cc:919
void filelist_free_icons()
Definition filelist.cc:191
static void filelist_file_release_entry(FileList *filelist, FileDirEntry *entry)
Definition filelist.cc:1222
static bool filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry, const int index)
Definition filelist.cc:726
const char * filelist_entry_get_relpath(const FileList *filelist, int index)
Definition filelist.cc:1350
void filelist_setdir(FileList *filelist, char dirpath[FILE_MAX_LIBEXTRA])
Definition filelist.cc:1105
void filelist_tag_reload_asset_library(FileList *filelist)
Definition filelist.cc:1148
ImBuf * filelist_geticon_special_file_image_ex(const FileDirEntry *file)
Definition filelist.cc:251
int ED_file_extension_icon(const char *path)
Definition filelist.cc:1869
void filelist_setrecursion(FileList *filelist, const int recursion_level)
Definition filelist.cc:1122
#define FILE_UID_UNSET
Definition filelist.cc:1356
ID * filelist_file_get_id(const FileDirEntry *file)
Definition filelist.cc:1345
bool filelist_cache_previews_update(FileList *filelist)
Definition filelist.cc:1681
int filelist_file_find_path(FileList *filelist, const char *filename)
Definition filelist.cc:1290
static FileListInternEntry * filelist_readjob_list_lib_group_create(const FileListReadJob *job_params, const int idcode, const char *group_name)
Definition filelist.cc:2257
static FileDirEntry * filelist_geticon_get_file(FileList *filelist, const int index)
Definition filelist.cc:223
static void filelist_entry_free(FileDirEntry *entry)
Definition filelist.cc:490
void filelist_settype(FileList *filelist, short type)
Definition filelist.cc:858
static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params, bool *, bool *do_update, float *)
Definition filelist.cc:2948
FileList * filelist_new(short type)
Definition filelist.cc:845
void filelist_entry_select_index_set(FileList *filelist, const int index, FileSelType select, const eDirEntry_SelectFlag flag, FileCheckType check)
Definition filelist.cc:1959
int filelist_file_find_id(const FileList *filelist, const ID *id)
Definition filelist.cc:1310
static bool filelist_contains_main(const FileList *filelist, const Main *bmain)
Definition filelist.cc:3008
static void filelist_clear_asset_library(FileList *filelist)
Definition filelist.cc:912
static FileListInternEntry * filelist_readjob_list_lib_navigate_to_parent_entry_create(const FileListReadJob *job_params)
Definition filelist.cc:2349
FileDirEntry * filelist_file_ex(FileList *filelist, const int index, const bool use_request)
Definition filelist.cc:1241
bool filelist_uid_is_set(const FileUID uid)
Definition filelist.cc:1366
void filelist_readjob_stop(FileList *filelist, wmWindowManager *wm)
Definition filelist.cc:3356
static int filelist_readjob_list_lib_populate_from_index(FileListReadJob *job_params, ListBase *entries, const ListLibOptions options, const int read_from_index, const FileIndexerEntries *indexer_entries)
Definition filelist.cc:2370
static void filelist_readjob_update(void *flrjv)
Definition filelist.cc:3193
static void filelist_readjob_list_lib_add_from_indexer_entries(FileListReadJob *job_params, ListBase *entries, const FileIndexerEntries *indexer_entries, const bool prefix_relpath_with_group_name)
Definition filelist.cc:2331
int filelist_geticon_file_type(FileList *filelist, const int index, const bool is_main)
Definition filelist.cc:390
static bool filelist_compare_asset_libraries(const AssetLibraryReference *library_a, const AssetLibraryReference *library_b)
Definition filelist.cc:149
static void filelist_cache_preview_ensure_running(FileListEntryCache *cache)
Definition filelist.cc:631
int filelist_needs_reading(const FileList *filelist)
Definition filelist.cc:1904
static void filelist_readjob_recursive_dir_add_items(const bool do_lib, FileListReadJob *job_params, const bool *stop, bool *do_update, float *progress)
Definition filelist.cc:2742
ID * filelist_entry_get_id(const FileList *filelist, const int index)
Definition filelist.cc:1332
static void filelist_readjob_load_asset_library_data(FileListReadJob *job_params, bool *do_update)
Definition filelist.cc:2924
void filelist_file_cache_slidingwindow_set(FileList *filelist, size_t window_size)
Definition filelist.cc:1378
static bool filelist_file_cache_block_create(FileList *filelist, const int start_index, const int size, int cursor)
Definition filelist.cc:1395
static void filelist_intern_entry_free(FileList *filelist, FileListInternEntry *entry)
Definition filelist.cc:513
int ED_path_extension_type(const char *path)
Definition filelist.cc:1786
int filelist_readjob_running(FileList *filelist, wmWindowManager *wm)
Definition filelist.cc:3361
ImBuf * filelist_file_get_preview_image(const FileDirEntry *file)
Definition filelist.cc:237
void filelist_tag_force_reset_mainfiles(FileList *filelist)
Definition filelist.cc:1140
bool filelist_intern_entry_is_main_file(const FileListInternEntry *intern_entry)
Definition filelist.cc:403
static void filelist_entry_clear(FileDirEntry *entry)
Definition filelist.cc:473
bool filelist_is_dir(const FileList *filelist, const char *path)
Definition filelist.cc:1100
static int filelist_intern_free_main_files(FileList *filelist)
Definition filelist.cc:546
bool filelist_needs_reset_on_main_changes(const FileList *filelist)
Definition filelist.cc:1163
int filelist_files_ensure(FileList *filelist)
Definition filelist.cc:1168
static void filelist_readjob_main(FileListReadJob *job_params, bool *stop, bool *do_update, float *progress)
Definition filelist.cc:3048
static const char * fileentry_uiname(const char *root, FileListInternEntry *entry, char *buff)
Definition filelist.cc:1050
static int filelist_geticon_file_type_ex(const FileList *filelist, const FileDirEntry *file, const bool is_main, const bool ignore_libdir)
Definition filelist.cc:276
bool filelist_entry_is_selected(FileList *filelist, const int index)
Definition filelist.cc:2020
void filelist_setindexer(FileList *filelist, const FileIndexerType *indexer)
Definition filelist.cc:118
void filelist_file_get_full_path(const FileList *filelist, const FileDirEntry *file, char r_filepath[FILE_MAX_LIBEXTRA])
Definition filelist.cc:201
FileDirEntry * filelist_file(FileList *filelist, int index)
Definition filelist.cc:1285
bool filelist_is_ready(const FileList *filelist)
Definition filelist.cc:1153
uint filelist_entry_select_set(const FileList *filelist, const FileDirEntry *entry, FileSelType select, const eDirEntry_SelectFlag flag, FileCheckType check)
Definition filelist.cc:1910
ListLibOptions
Definition filelist.cc:2242
@ LIST_LIB_ASSETS_ONLY
Definition filelist.cc:2250
@ LIST_LIB_OPTION_NONE
Definition filelist.cc:2243
@ LIST_LIB_ADD_PARENT
Definition filelist.cc:2253
@ LIST_LIB_RECURSIVE
Definition filelist.cc:2247
static bool filelist_checkdir_return_always_valid(const FileList *, char[FILE_MAX_LIBEXTRA], const bool)
Definition filelist.cc:466
static bool filelist_readjob_is_partial_read(const FileListReadJob *read_job)
Definition filelist.cc:3142
ImBuf * filelist_geticon_special_file_image(FileList *filelist, const int index)
Definition filelist.cc:270
void filelist_readjob_start(FileList *filelist, const int space_notifier, const bContext *C)
Definition filelist.cc:3346
static void filelist_readjob_free(void *flrjv)
Definition filelist.cc:3245
eDirEntry_SelectFlag filelist_entry_select_get(FileList *filelist, FileDirEntry *entry, FileCheckType check)
Definition filelist.cc:1989
static void filelist_readjob_list_lib_add_datablock(FileListReadJob *job_params, ListBase *entries, BLODataBlockInfo *datablock_info, const bool prefix_relpath_with_group_name, const int idcode, const char *group_name)
Definition filelist.cc:2271
static bool filelist_checkdir_lib(const FileList *, char dirpath[FILE_MAX_LIBEXTRA], const bool do_change)
Definition filelist.cc:439
static ImBuf * filelist_ensure_special_file_image(SpecialFileImages image, int icon)
Definition filelist.cc:242
void filelist_uid_unset(FileUID *r_uid)
Definition filelist.cc:1373
static bool file_is_blend_backup(const char *str)
Definition filelist.cc:1759
static FileUID filelist_uid_generate(FileList *filelist)
Definition filelist.cc:1358
static ImBuf * gSpecialFileImages[int(SpecialFileImages::_Max)]
Definition filelist.cc:88
static void filelist_readjob_asset_library(FileListReadJob *job_params, bool *stop, bool *do_update, float *progress)
Definition filelist.cc:3018
static bool filelist_file_preview_load_poll(const FileDirEntry *entry)
Definition filelist.cc:690
void filelist_tag_needs_filtering(FileList *filelist)
uint32_t FileUID
Definition filelist.hh:33
FileSelType
Definition filelist.hh:35
@ FILE_SEL_REMOVE
Definition filelist.hh:36
@ FILE_SEL_ADD
Definition filelist.hh:37
@ FILE_SEL_TOGGLE
Definition filelist.hh:38
FileCheckType
Definition filelist.hh:41
@ CHECK_FILES
Definition filelist.hh:43
@ CHECK_DIRS
Definition filelist.hh:42
@ CHECK_ALL
Definition filelist.hh:44
void filelist_filter(FileList *filelist)
void filelist_sort(FileList *filelist)
bool is_filtered_asset_library(FileListInternEntry *file, const char *root, FileListFilter *filter)
bool is_filtered_main_assets(FileListInternEntry *file, const char *, FileListFilter *filter)
bool is_filtered_file(FileListInternEntry *file, const char *, FileListFilter *filter)
bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
void prepare_filter_asset_library(const FileList *filelist, FileListFilter *filter)
bool is_filtered_main(FileListInternEntry *file, const char *, FileListFilter *filter)
#define FILELIST_ENTRYCACHESIZE_DEFAULT
#define FILEDIR_NBR_ENTRIES_UNSET
@ FILELIST_TAGS_USES_MAIN_DATA
@ FILELIST_TAGS_NO_THREADS
@ FLF_HIDE_PARENT
@ FLF_HIDE_DOT
@ FL_NEED_SORTING
@ FL_IS_PENDING
@ FL_IS_READY
@ FL_FORCE_RESET_MAIN_FILES
@ FL_NEED_FILTERING
@ FL_FORCE_RESET
@ FL_RELOAD_ASSET_LIBRARY
SpecialFileImages
@ FLC_PREVIEWS_ACTIVE
static int compare_name(void *user_data, const void *a1, const void *a2)
#define GS(x)
#define printf(...)
#define select(A, B, C)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
bool MOV_is_movie_file(const char *filepath)
AssetLibraryReference current_file_library_reference()
AssetCatalogFilterSettings * file_create_asset_catalog_filter_settings()
std::mutex Mutex
Definition BLI_mutex.hh:47
const char * name
return ret
AssetMetaData * asset_data
FSMenuEntry * next
struct FileDirEntry * next
AssetRepresentationHandle * asset
char * redirection_path
const char * name
BLODataBlockInfo datablock_info
FileIndexerUpdateIndexFunc update_index
FileIndexerFinishedFunc filelist_finished
FileIndexerReadIndexFunc read_index
FileIndexerFreeUserDataFunc free_user_data
FileIndexerInitUserDataFunc init_user_data
void * user_data
Definition filelist.cc:2367
const FileIndexerType * callbacks
Definition filelist.cc:2362
ThreadQueue * previews_done
FileDirEntry ** block_entries
FileListEntryPreview * preview
char filepath[FILE_MAX_LIBEXTRA]
char filter_glob[FILE_MAXFILE]
blender::ed::asset_browser::AssetCatalogFilterSettings * asset_catalog_filter
struct FileListInternEntry::@364374371172256277004264162343271025307331240316 local_data
PreviewImage * preview_image
eFileAttributes attributes
eFileSel_File_Types typeflag
std::weak_ptr< blender::asset_system::AssetRepresentation > asset
blender::asset_system::AssetRepresentation * get_asset() const
FileListInternEntry ** filtered
asset_system::AssetLibrary * load_asset_library
Definition filelist.cc:2096
char cur_relbase[FILE_MAX_LIBEXTRA]
Definition filelist.cc:2090
Main * current_main
Definition filelist.cc:2080
blender::Mutex lock
Definition filelist.cc:2078
FileList * tmp_filelist
Definition filelist.cc:2114
FileList * filelist
Definition filelist.cc:2081
char main_filepath[FILE_MAX]
Definition filelist.cc:2079
bool reload_asset_library
Definition filelist.cc:2102
void(* prepare_filter_fn)(const FileList *filelist, FileListFilter *filter)
bool(* filter_fn)(FileListInternEntry *file, const char *root, FileListFilter *filter)
FileDirEntryArr filelist
bool(* check_dir_fn)(const FileList *filelist, char dirpath[FILE_MAX_LIBEXTRA], const bool do_change)
FileListEntryCache * filelist_cache
AssetLibraryReference * asset_library_ref
void(* read_job_fn)(FileListReadJob *job_params, bool *stop, bool *do_update, float *progress)
GHash * selection_state
short max_recursion
FileListIntern filelist_intern
eFileSelectType type
BlendHandle * libfiledata
FileListFilter filter_data
const FileIndexerType * indexer
blender::asset_system::AssetLibrary * asset_library
Definition DNA_ID.h:414
struct AssetMetaData * asset_data
Definition DNA_ID.h:423
struct Library * lib
Definition DNA_ID.h:420
char name[258]
Definition DNA_ID.h:432
int us
Definition DNA_ID.h:443
short flag
Definition DNA_ID.h:438
void * next
Definition DNA_ID.h:417
char filepath[1024]
Definition DNA_ID.h:552
struct LinkNode * next
void * first
int level
Definition filelist.cc:2073
char * dir
Definition filelist.cc:2074
struct stat s
i
Definition text_draw.cc:230
#define SEP_STR
Definition unit.cc:39
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:376
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:479
void WM_jobs_kill_type(wmWindowManager *wm, const void *owner, int job_type)
Definition wm_jobs.cc:623
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
Definition wm_jobs.cc:211
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition wm_jobs.cc:388
bool WM_jobs_test(const wmWindowManager *wm, const void *owner, int job_type)
Definition wm_jobs.cc:247
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:360
uint8_t flag
Definition wm_window.cc:145