1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2007 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup spfile
22 */
23
24 /* global includes */
25
26 #include <math.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <time.h>
31
32 #ifndef WIN32
33 # include <unistd.h>
34 #else
35 # include <direct.h>
36 # include <io.h>
37 #endif
38 #include "MEM_guardedalloc.h"
39
40 #include "BLI_blenlib.h"
41 #include "BLI_fileops.h"
42 #include "BLI_fileops_types.h"
43 #include "BLI_fnmatch.h"
44 #include "BLI_ghash.h"
45 #include "BLI_linklist.h"
46 #include "BLI_math.h"
47 #include "BLI_stack.h"
48 #include "BLI_task.h"
49 #include "BLI_threads.h"
50 #include "BLI_utildefines.h"
51
52 #ifdef WIN32
53 # include "BLI_winstuff.h"
54 #endif
55
56 #include "BKE_context.h"
57 #include "BKE_global.h"
58 #include "BKE_icons.h"
59 #include "BKE_idtype.h"
60 #include "BKE_main.h"
61 #include "BLO_readfile.h"
62
63 #include "DNA_space_types.h"
64
65 #include "ED_datafiles.h"
66 #include "ED_fileselect.h"
67 #include "ED_screen.h"
68
69 #include "IMB_imbuf.h"
70 #include "IMB_imbuf_types.h"
71 #include "IMB_thumbs.h"
72
73 #include "PIL_time.h"
74
75 #include "WM_api.h"
76 #include "WM_types.h"
77
78 #include "UI_interface_icons.h"
79 #include "UI_resources.h"
80
81 #include "atomic_ops.h"
82
83 #include "filelist.h"
84
85 /* ----------------- FOLDERLIST (previous/next) -------------- */
86
87 typedef struct FolderList {
88 struct FolderList *next, *prev;
89 char *foldername;
90 } FolderList;
91
folderlist_new(void)92 ListBase *folderlist_new(void)
93 {
94 ListBase *p = MEM_callocN(sizeof(*p), __func__);
95 return p;
96 }
97
folderlist_popdir(struct ListBase * folderlist,char * dir)98 void folderlist_popdir(struct ListBase *folderlist, char *dir)
99 {
100 const char *prev_dir;
101 struct FolderList *folder;
102 folder = folderlist->last;
103
104 if (folder) {
105 /* remove the current directory */
106 MEM_freeN(folder->foldername);
107 BLI_freelinkN(folderlist, folder);
108
109 folder = folderlist->last;
110 if (folder) {
111 prev_dir = folder->foldername;
112 BLI_strncpy(dir, prev_dir, FILE_MAXDIR);
113 }
114 }
115 /* delete the folder next or use setdir directly before PREVIOUS OP */
116 }
117
folderlist_pushdir(ListBase * folderlist,const char * dir)118 void folderlist_pushdir(ListBase *folderlist, const char *dir)
119 {
120 struct FolderList *folder, *previous_folder;
121 previous_folder = folderlist->last;
122
123 /* check if already exists */
124 if (previous_folder && previous_folder->foldername) {
125 if (BLI_path_cmp(previous_folder->foldername, dir) == 0) {
126 return;
127 }
128 }
129
130 /* create next folder element */
131 folder = MEM_mallocN(sizeof(*folder), __func__);
132 folder->foldername = BLI_strdup(dir);
133
134 /* add it to the end of the list */
135 BLI_addtail(folderlist, folder);
136 }
137
folderlist_peeklastdir(ListBase * folderlist)138 const char *folderlist_peeklastdir(ListBase *folderlist)
139 {
140 struct FolderList *folder;
141
142 if (!folderlist->last) {
143 return NULL;
144 }
145
146 folder = folderlist->last;
147 return folder->foldername;
148 }
149
folderlist_clear_next(struct SpaceFile * sfile)150 int folderlist_clear_next(struct SpaceFile *sfile)
151 {
152 struct FolderList *folder;
153
154 /* if there is no folder_next there is nothing we can clear */
155 if (!sfile->folders_next) {
156 return 0;
157 }
158
159 /* if previous_folder, next_folder or refresh_folder operators are executed
160 * it doesn't clear folder_next */
161 folder = sfile->folders_prev->last;
162 if ((!folder) || (BLI_path_cmp(folder->foldername, sfile->params->dir) == 0)) {
163 return 0;
164 }
165
166 /* eventually clear flist->folders_next */
167 return 1;
168 }
169
170 /* not listbase itself */
folderlist_free(ListBase * folderlist)171 void folderlist_free(ListBase *folderlist)
172 {
173 if (folderlist) {
174 FolderList *folder;
175 for (folder = folderlist->first; folder; folder = folder->next) {
176 MEM_freeN(folder->foldername);
177 }
178 BLI_freelistN(folderlist);
179 }
180 }
181
folderlist_duplicate(ListBase * folderlist)182 ListBase *folderlist_duplicate(ListBase *folderlist)
183 {
184
185 if (folderlist) {
186 ListBase *folderlistn = MEM_callocN(sizeof(*folderlistn), __func__);
187 FolderList *folder;
188
189 BLI_duplicatelist(folderlistn, folderlist);
190
191 for (folder = folderlistn->first; folder; folder = folder->next) {
192 folder->foldername = MEM_dupallocN(folder->foldername);
193 }
194 return folderlistn;
195 }
196 return NULL;
197 }
198
199 /* ------------------FILELIST------------------------ */
200
201 typedef struct FileListInternEntry {
202 struct FileListInternEntry *next, *prev;
203
204 /** ASSET_UUID_LENGTH */
205 char uuid[16];
206
207 /** eFileSel_File_Types */
208 int typeflag;
209 /** ID type, in case typeflag has FILE_TYPE_BLENDERLIB set. */
210 int blentype;
211
212 char *relpath;
213 /** Optional argument for shortcuts, aliases etc. */
214 char *redirection_path;
215 /** not strictly needed, but used during sorting, avoids to have to recompute it there... */
216 char *name;
217
218 /** Defined in BLI_fileops.h */
219 eFileAttributes attributes;
220 BLI_stat_t st;
221 } FileListInternEntry;
222
223 typedef struct FileListIntern {
224 /** FileListInternEntry items. */
225 ListBase entries;
226 FileListInternEntry **filtered;
227
228 char curr_uuid[16]; /* Used to generate uuid during internal listing. */
229 } FileListIntern;
230
231 #define FILELIST_ENTRYCACHESIZE_DEFAULT 1024 /* Keep it a power of two! */
232 typedef struct FileListEntryCache {
233 size_t size; /* The size of the cache... */
234
235 int flags;
236
237 /* This one gathers all entries from both block and misc caches. Used for easy bulk-freing. */
238 ListBase cached_entries;
239
240 /* Block cache: all entries between start and end index.
241 * used for part of the list on display. */
242 FileDirEntry **block_entries;
243 int block_start_index, block_end_index, block_center_index, block_cursor;
244
245 /* Misc cache: random indices, FIFO behavior.
246 * Note: Not 100% sure we actually need that, time will say. */
247 int misc_cursor;
248 int *misc_entries_indices;
249 GHash *misc_entries;
250
251 /* Allows to quickly get a cached entry from its UUID. */
252 GHash *uuids;
253
254 /* Previews handling. */
255 TaskPool *previews_pool;
256 ThreadQueue *previews_done;
257 } FileListEntryCache;
258
259 /* FileListCache.flags */
260 enum {
261 FLC_IS_INIT = 1 << 0,
262 FLC_PREVIEWS_ACTIVE = 1 << 1,
263 };
264
265 typedef struct FileListEntryPreview {
266 char path[FILE_MAX];
267 uint flags;
268 int index;
269 ImBuf *img;
270 } FileListEntryPreview;
271
272 /* Dummy wrapper around FileListEntryPreview to ensure we do not access freed memory when freeing
273 * tasks' data (see T74609). */
274 typedef struct FileListEntryPreviewTaskData {
275 FileListEntryPreview *preview;
276 } FileListEntryPreviewTaskData;
277
278 typedef struct FileListFilter {
279 uint64_t filter;
280 uint64_t filter_id;
281 char filter_glob[FILE_MAXFILE];
282 char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */
283 short flags;
284 } FileListFilter;
285
286 /* FileListFilter.flags */
287 enum {
288 FLF_DO_FILTER = 1 << 0,
289 FLF_HIDE_DOT = 1 << 1,
290 FLF_HIDE_PARENT = 1 << 2,
291 FLF_HIDE_LIB_DIR = 1 << 3,
292 };
293
294 typedef struct FileList {
295 FileDirEntryArr filelist;
296
297 short prv_w;
298 short prv_h;
299
300 short flags;
301
302 short sort;
303
304 FileListFilter filter_data;
305
306 struct FileListIntern filelist_intern;
307
308 struct FileListEntryCache filelist_cache;
309
310 /* We need to keep those info outside of actual filelist items,
311 * because those are no more persistent
312 * (only generated on demand, and freed as soon as possible).
313 * Persistent part (mere list of paths + stat info)
314 * is kept as small as possible, and filebrowser-agnostic.
315 */
316 GHash *selection_state;
317
318 short max_recursion;
319 short recursion_level;
320
321 struct BlendHandle *libfiledata;
322
323 /* Set given path as root directory,
324 * if last bool is true may change given string in place to a valid value.
325 * Returns True if valid dir. */
326 bool (*checkdirf)(struct FileList *, char *, const bool);
327
328 /* Fill filelist (to be called by read job). */
329 void (*read_jobf)(struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
330
331 /* Filter an entry of current filelist. */
332 bool (*filterf)(struct FileListInternEntry *, const char *, FileListFilter *);
333 } FileList;
334
335 /* FileList.flags */
336 enum {
337 FL_FORCE_RESET = 1 << 0,
338 FL_IS_READY = 1 << 1,
339 FL_IS_PENDING = 1 << 2,
340 FL_NEED_SORTING = 1 << 3,
341 FL_NEED_FILTERING = 1 << 4,
342 FL_SORT_INVERT = 1 << 5,
343 };
344
345 #define SPECIAL_IMG_SIZE 256
346 #define SPECIAL_IMG_ROWS 1
347 #define SPECIAL_IMG_COLS 7
348
349 enum {
350 SPECIAL_IMG_DOCUMENT = 0,
351 SPECIAL_IMG_DRIVE_DISC = 1,
352 SPECIAL_IMG_FOLDER = 2,
353 SPECIAL_IMG_PARENT = 3,
354 SPECIAL_IMG_DRIVE_FIXED = 4,
355 SPECIAL_IMG_DRIVE_ATTACHED = 5,
356 SPECIAL_IMG_DRIVE_REMOTE = 6,
357 SPECIAL_IMG_MAX,
358 };
359
360 static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
361
362 static void filelist_readjob_main(FileList *filelist,
363 const char *main_name,
364 short *stop,
365 short *do_update,
366 float *progress,
367 ThreadMutex *lock);
368 static void filelist_readjob_lib(FileList *filelist,
369 const char *main_name,
370 short *stop,
371 short *do_update,
372 float *progress,
373 ThreadMutex *lock);
374 static void filelist_readjob_dir(FileList *filelist,
375 const char *main_name,
376 short *stop,
377 short *do_update,
378 float *progress,
379 ThreadMutex *lock);
380
381 /* helper, could probably go in BKE actually? */
382 static int groupname_to_code(const char *group);
383 static uint64_t groupname_to_filter_id(const char *group);
384
385 static void filelist_filter_clear(FileList *filelist);
386 static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size);
387
388 /* ********** Sort helpers ********** */
389
390 struct FileSortData {
391 bool inverted;
392 };
393
compare_apply_inverted(int val,const struct FileSortData * sort_data)394 static int compare_apply_inverted(int val, const struct FileSortData *sort_data)
395 {
396 return sort_data->inverted ? -val : val;
397 }
398
399 /**
400 * Handles inverted sorting itself (currently there's nothing to invert), so if this returns non-0,
401 * it should be used as-is and not inverted.
402 */
compare_direntry_generic(const FileListInternEntry * entry1,const FileListInternEntry * entry2)403 static int compare_direntry_generic(const FileListInternEntry *entry1,
404 const FileListInternEntry *entry2)
405 {
406 /* type is equal to stat.st_mode */
407
408 if (entry1->typeflag & FILE_TYPE_DIR) {
409 if (entry2->typeflag & FILE_TYPE_DIR) {
410 /* If both entries are tagged as dirs, we make a 'sub filter' that shows first the real dirs,
411 * then libs (.blend files), then categories in libs. */
412 if (entry1->typeflag & FILE_TYPE_BLENDERLIB) {
413 if (!(entry2->typeflag & FILE_TYPE_BLENDERLIB)) {
414 return 1;
415 }
416 }
417 else if (entry2->typeflag & FILE_TYPE_BLENDERLIB) {
418 return -1;
419 }
420 else if (entry1->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
421 if (!(entry2->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
422 return 1;
423 }
424 }
425 else if (entry2->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
426 return -1;
427 }
428 }
429 else {
430 return -1;
431 }
432 }
433 else if (entry2->typeflag & FILE_TYPE_DIR) {
434 return 1;
435 }
436
437 /* make sure "." and ".." are always first */
438 if (FILENAME_IS_CURRENT(entry1->relpath)) {
439 return -1;
440 }
441 if (FILENAME_IS_CURRENT(entry2->relpath)) {
442 return 1;
443 }
444 if (FILENAME_IS_PARENT(entry1->relpath)) {
445 return -1;
446 }
447 if (FILENAME_IS_PARENT(entry2->relpath)) {
448 return 1;
449 }
450
451 return 0;
452 }
453
compare_name(void * user_data,const void * a1,const void * a2)454 static int compare_name(void *user_data, const void *a1, const void *a2)
455 {
456 const FileListInternEntry *entry1 = a1;
457 const FileListInternEntry *entry2 = a2;
458 const struct FileSortData *sort_data = user_data;
459 char *name1, *name2;
460 int ret;
461
462 if ((ret = compare_direntry_generic(entry1, entry2))) {
463 return ret;
464 }
465
466 name1 = entry1->name;
467 name2 = entry2->name;
468
469 return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data);
470 }
471
compare_date(void * user_data,const void * a1,const void * a2)472 static int compare_date(void *user_data, const void *a1, const void *a2)
473 {
474 const FileListInternEntry *entry1 = a1;
475 const FileListInternEntry *entry2 = a2;
476 const struct FileSortData *sort_data = user_data;
477 char *name1, *name2;
478 int64_t time1, time2;
479 int ret;
480
481 if ((ret = compare_direntry_generic(entry1, entry2))) {
482 return ret;
483 }
484
485 time1 = (int64_t)entry1->st.st_mtime;
486 time2 = (int64_t)entry2->st.st_mtime;
487 if (time1 < time2) {
488 return compare_apply_inverted(1, sort_data);
489 }
490 if (time1 > time2) {
491 return compare_apply_inverted(-1, sort_data);
492 }
493
494 name1 = entry1->name;
495 name2 = entry2->name;
496
497 return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data);
498 }
499
compare_size(void * user_data,const void * a1,const void * a2)500 static int compare_size(void *user_data, const void *a1, const void *a2)
501 {
502 const FileListInternEntry *entry1 = a1;
503 const FileListInternEntry *entry2 = a2;
504 const struct FileSortData *sort_data = user_data;
505 char *name1, *name2;
506 uint64_t size1, size2;
507 int ret;
508
509 if ((ret = compare_direntry_generic(entry1, entry2))) {
510 return ret;
511 }
512
513 size1 = entry1->st.st_size;
514 size2 = entry2->st.st_size;
515 if (size1 < size2) {
516 return compare_apply_inverted(1, sort_data);
517 }
518 if (size1 > size2) {
519 return compare_apply_inverted(-1, sort_data);
520 }
521
522 name1 = entry1->name;
523 name2 = entry2->name;
524
525 return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data);
526 }
527
compare_extension(void * user_data,const void * a1,const void * a2)528 static int compare_extension(void *user_data, const void *a1, const void *a2)
529 {
530 const FileListInternEntry *entry1 = a1;
531 const FileListInternEntry *entry2 = a2;
532 const struct FileSortData *sort_data = user_data;
533 char *name1, *name2;
534 int ret;
535
536 if ((ret = compare_direntry_generic(entry1, entry2))) {
537 return ret;
538 }
539
540 if ((entry1->typeflag & FILE_TYPE_BLENDERLIB) && !(entry2->typeflag & FILE_TYPE_BLENDERLIB)) {
541 return -1;
542 }
543 if (!(entry1->typeflag & FILE_TYPE_BLENDERLIB) && (entry2->typeflag & FILE_TYPE_BLENDERLIB)) {
544 return 1;
545 }
546 if ((entry1->typeflag & FILE_TYPE_BLENDERLIB) && (entry2->typeflag & FILE_TYPE_BLENDERLIB)) {
547 if ((entry1->typeflag & FILE_TYPE_DIR) && !(entry2->typeflag & FILE_TYPE_DIR)) {
548 return 1;
549 }
550 if (!(entry1->typeflag & FILE_TYPE_DIR) && (entry2->typeflag & FILE_TYPE_DIR)) {
551 return -1;
552 }
553 if (entry1->blentype < entry2->blentype) {
554 return compare_apply_inverted(-1, sort_data);
555 }
556 if (entry1->blentype > entry2->blentype) {
557 return compare_apply_inverted(1, sort_data);
558 }
559 }
560 else {
561 const char *sufix1, *sufix2;
562
563 if (!(sufix1 = strstr(entry1->relpath, ".blend.gz"))) {
564 sufix1 = strrchr(entry1->relpath, '.');
565 }
566 if (!(sufix2 = strstr(entry2->relpath, ".blend.gz"))) {
567 sufix2 = strrchr(entry2->relpath, '.');
568 }
569 if (!sufix1) {
570 sufix1 = "";
571 }
572 if (!sufix2) {
573 sufix2 = "";
574 }
575
576 if ((ret = BLI_strcasecmp(sufix1, sufix2))) {
577 return compare_apply_inverted(ret, sort_data);
578 }
579 }
580
581 name1 = entry1->name;
582 name2 = entry2->name;
583
584 return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data);
585 }
586
filelist_sort(struct FileList * filelist)587 void filelist_sort(struct FileList *filelist)
588 {
589 if ((filelist->flags & FL_NEED_SORTING) && (filelist->sort != FILE_SORT_NONE)) {
590 void *sort_cb = NULL;
591
592 switch (filelist->sort) {
593 case FILE_SORT_ALPHA:
594 sort_cb = compare_name;
595 break;
596 case FILE_SORT_TIME:
597 sort_cb = compare_date;
598 break;
599 case FILE_SORT_SIZE:
600 sort_cb = compare_size;
601 break;
602 case FILE_SORT_EXTENSION:
603 sort_cb = compare_extension;
604 break;
605 case FILE_SORT_NONE: /* Should never reach this point! */
606 default:
607 BLI_assert(0);
608 break;
609 }
610 BLI_listbase_sort_r(
611 &filelist->filelist_intern.entries,
612 sort_cb,
613 &(struct FileSortData){.inverted = (filelist->flags & FL_SORT_INVERT) != 0});
614
615 filelist_filter_clear(filelist);
616 filelist->flags &= ~FL_NEED_SORTING;
617 }
618 }
619
filelist_setsorting(struct FileList * filelist,const short sort,bool invert_sort)620 void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort)
621 {
622 const bool was_invert_sort = filelist->flags & FL_SORT_INVERT;
623
624 if ((filelist->sort != sort) || (was_invert_sort != invert_sort)) {
625 filelist->sort = sort;
626 filelist->flags |= FL_NEED_SORTING;
627 filelist->flags = invert_sort ? (filelist->flags | FL_SORT_INVERT) :
628 (filelist->flags & ~FL_SORT_INVERT);
629 }
630 }
631
632 /* ********** Filter helpers ********** */
633
634 /* True if filename is meant to be hidden, eg. starting with period. */
is_hidden_dot_filename(const char * filename,FileListInternEntry * file)635 static bool is_hidden_dot_filename(const char *filename, FileListInternEntry *file)
636 {
637 if (filename[0] == '.' && !ELEM(filename[1], '.', '\0')) {
638 return true; /* ignore .file */
639 }
640
641 int len = strlen(filename);
642 if ((len > 0) && (filename[len - 1] == '~')) {
643 return true; /* ignore file~ */
644 }
645
646 /* filename might actually be a piece of path, in which case we have to check all its parts. */
647
648 bool hidden = false;
649 char *sep = (char *)BLI_path_slash_rfind(filename);
650
651 if (!hidden && sep) {
652 char tmp_filename[FILE_MAX_LIBEXTRA];
653
654 BLI_strncpy(tmp_filename, filename, sizeof(tmp_filename));
655 sep = tmp_filename + (sep - filename);
656 while (sep) {
657 /* This happens when a path contains 'ALTSEP', '\' on Unix for e.g.
658 * Supporting alternate slashes in paths is a bigger task involving changes
659 * in many parts of the code, for now just prevent an assert, see T74579. */
660 #if 0
661 BLI_assert(sep[1] != '\0');
662 #endif
663 if (is_hidden_dot_filename(sep + 1, file)) {
664 hidden = true;
665 break;
666 }
667 *sep = '\0';
668 sep = (char *)BLI_path_slash_rfind(tmp_filename);
669 }
670 }
671 return hidden;
672 }
673
674 /* True if should be hidden, based on current filtering. */
is_filtered_hidden(const char * filename,FileListFilter * filter,FileListInternEntry * file)675 static bool is_filtered_hidden(const char *filename,
676 FileListFilter *filter,
677 FileListInternEntry *file)
678 {
679 if ((filename[0] == '.') && (filename[1] == '\0')) {
680 return true; /* Ignore . */
681 }
682
683 if (filter->flags & FLF_HIDE_PARENT) {
684 if (filename[0] == '.' && filename[1] == '.' && filename[2] == '\0') {
685 return true; /* Ignore .. */
686 }
687 }
688
689 if ((filter->flags & FLF_HIDE_DOT) && (file->attributes & FILE_ATTR_HIDDEN)) {
690 return true; /* Ignore files with Hidden attribute. */
691 }
692
693 #ifndef WIN32
694 /* Check for unix-style names starting with period. */
695 if ((filter->flags & FLF_HIDE_DOT) && is_hidden_dot_filename(filename, file)) {
696 return true;
697 }
698 #endif
699
700 return false;
701 }
702
is_filtered_file(FileListInternEntry * file,const char * UNUSED (root),FileListFilter * filter)703 static bool is_filtered_file(FileListInternEntry *file,
704 const char *UNUSED(root),
705 FileListFilter *filter)
706 {
707 bool is_filtered = !is_filtered_hidden(file->relpath, filter, file);
708
709 if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
710 /* We only check for types if some type are enabled in filtering. */
711 if (filter->filter && (filter->flags & FLF_DO_FILTER)) {
712 if (file->typeflag & FILE_TYPE_DIR) {
713 if (file->typeflag &
714 (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
715 if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
716 is_filtered = false;
717 }
718 }
719 else {
720 if (!(filter->filter & FILE_TYPE_FOLDER)) {
721 is_filtered = false;
722 }
723 }
724 }
725 else {
726 if (!(file->typeflag & filter->filter)) {
727 is_filtered = false;
728 }
729 }
730 }
731 /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
732 if (is_filtered && (filter->filter_search[0] != '\0')) {
733 if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
734 is_filtered = false;
735 }
736 }
737 }
738
739 return is_filtered;
740 }
741
is_filtered_lib(FileListInternEntry * file,const char * root,FileListFilter * filter)742 static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
743 {
744 bool is_filtered;
745 char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name;
746
747 BLI_join_dirfile(path, sizeof(path), root, file->relpath);
748
749 if (BLO_library_path_explode(path, dir, &group, &name)) {
750 is_filtered = !is_filtered_hidden(file->relpath, filter, file);
751 if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
752 /* We only check for types if some type are enabled in filtering. */
753 if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
754 if (file->typeflag & FILE_TYPE_DIR) {
755 if (file->typeflag &
756 (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
757 if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
758 is_filtered = false;
759 }
760 }
761 else {
762 if (!(filter->filter & FILE_TYPE_FOLDER)) {
763 is_filtered = false;
764 }
765 }
766 }
767 if (is_filtered && group) {
768 if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) {
769 is_filtered = false;
770 }
771 else {
772 uint64_t filter_id = groupname_to_filter_id(group);
773 if (!(filter_id & filter->filter_id)) {
774 is_filtered = false;
775 }
776 }
777 }
778 }
779 /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
780 if (is_filtered && (filter->filter_search[0] != '\0')) {
781 if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
782 is_filtered = false;
783 }
784 }
785 }
786 }
787 else {
788 is_filtered = is_filtered_file(file, root, filter);
789 }
790
791 return is_filtered;
792 }
793
is_filtered_main(FileListInternEntry * file,const char * UNUSED (dir),FileListFilter * filter)794 static bool is_filtered_main(FileListInternEntry *file,
795 const char *UNUSED(dir),
796 FileListFilter *filter)
797 {
798 return !is_filtered_hidden(file->relpath, filter, file);
799 }
800
filelist_filter_clear(FileList * filelist)801 static void filelist_filter_clear(FileList *filelist)
802 {
803 filelist->flags |= FL_NEED_FILTERING;
804 }
805
filelist_filter(FileList * filelist)806 void filelist_filter(FileList *filelist)
807 {
808 int num_filtered = 0;
809 const int num_files = filelist->filelist.nbr_entries;
810 FileListInternEntry **filtered_tmp, *file;
811
812 if (filelist->filelist.nbr_entries == 0) {
813 return;
814 }
815
816 if (!(filelist->flags & FL_NEED_FILTERING)) {
817 /* Assume it has already been filtered, nothing else to do! */
818 return;
819 }
820
821 filelist->filter_data.flags &= ~FLF_HIDE_LIB_DIR;
822 if (filelist->max_recursion) {
823 /* Never show lib ID 'categories' directories when we are in 'flat' mode, unless
824 * root path is a blend file. */
825 char dir[FILE_MAX_LIBEXTRA];
826 if (!filelist_islibrary(filelist, dir, NULL)) {
827 filelist->filter_data.flags |= FLF_HIDE_LIB_DIR;
828 }
829 }
830
831 filtered_tmp = MEM_mallocN(sizeof(*filtered_tmp) * (size_t)num_files, __func__);
832
833 /* Filter remap & count how many files are left after filter in a single loop. */
834 for (file = filelist->filelist_intern.entries.first; file; file = file->next) {
835 if (filelist->filterf(file, filelist->filelist.root, &filelist->filter_data)) {
836 filtered_tmp[num_filtered++] = file;
837 }
838 }
839
840 if (filelist->filelist_intern.filtered) {
841 MEM_freeN(filelist->filelist_intern.filtered);
842 }
843 filelist->filelist_intern.filtered = MEM_mallocN(
844 sizeof(*filelist->filelist_intern.filtered) * (size_t)num_filtered, __func__);
845 memcpy(filelist->filelist_intern.filtered,
846 filtered_tmp,
847 sizeof(*filelist->filelist_intern.filtered) * (size_t)num_filtered);
848 filelist->filelist.nbr_entries_filtered = num_filtered;
849 // printf("Filetered: %d over %d entries\n", num_filtered, filelist->filelist.nbr_entries);
850
851 filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size);
852 filelist->flags &= ~FL_NEED_FILTERING;
853
854 MEM_freeN(filtered_tmp);
855 }
856
filelist_setfilter_options(FileList * filelist,const bool do_filter,const bool hide_dot,const bool hide_parent,const uint64_t filter,const uint64_t filter_id,const char * filter_glob,const char * filter_search)857 void filelist_setfilter_options(FileList *filelist,
858 const bool do_filter,
859 const bool hide_dot,
860 const bool hide_parent,
861 const uint64_t filter,
862 const uint64_t filter_id,
863 const char *filter_glob,
864 const char *filter_search)
865 {
866 bool update = false;
867
868 if (((filelist->filter_data.flags & FLF_DO_FILTER) != 0) != (do_filter != 0)) {
869 filelist->filter_data.flags ^= FLF_DO_FILTER;
870 update = true;
871 }
872 if (((filelist->filter_data.flags & FLF_HIDE_DOT) != 0) != (hide_dot != 0)) {
873 filelist->filter_data.flags ^= FLF_HIDE_DOT;
874 update = true;
875 }
876 if (((filelist->filter_data.flags & FLF_HIDE_PARENT) != 0) != (hide_parent != 0)) {
877 filelist->filter_data.flags ^= FLF_HIDE_PARENT;
878 update = true;
879 }
880 if (filelist->filter_data.filter != filter) {
881 filelist->filter_data.filter = filter;
882 update = true;
883 }
884 const uint64_t new_filter_id = (filter & FILE_TYPE_BLENDERLIB) ? filter_id : FILTER_ID_ALL;
885 if (filelist->filter_data.filter_id != new_filter_id) {
886 filelist->filter_data.filter_id = new_filter_id;
887 update = true;
888 }
889 if (!STREQ(filelist->filter_data.filter_glob, filter_glob)) {
890 BLI_strncpy(
891 filelist->filter_data.filter_glob, filter_glob, sizeof(filelist->filter_data.filter_glob));
892 update = true;
893 }
894 if ((BLI_strcmp_ignore_pad(filelist->filter_data.filter_search, filter_search, '*') != 0)) {
895 BLI_strncpy_ensure_pad(filelist->filter_data.filter_search,
896 filter_search,
897 '*',
898 sizeof(filelist->filter_data.filter_search));
899 update = true;
900 }
901
902 if (update) {
903 /* And now, free filtered data so that we know we have to filter again. */
904 filelist_filter_clear(filelist);
905 }
906 }
907
908 /* ********** Icon/image helpers ********** */
909
filelist_init_icons(void)910 void filelist_init_icons(void)
911 {
912 short x, y, k;
913 ImBuf *bbuf;
914 ImBuf *ibuf;
915
916 BLI_assert(G.background == false);
917
918 #ifdef WITH_HEADLESS
919 bbuf = NULL;
920 #else
921 bbuf = IMB_ibImageFromMemory(
922 (const uchar *)datatoc_prvicons_png, datatoc_prvicons_png_size, IB_rect, NULL, "<splash>");
923 #endif
924 if (bbuf) {
925 for (y = 0; y < SPECIAL_IMG_ROWS; y++) {
926 for (x = 0; x < SPECIAL_IMG_COLS; x++) {
927 int tile = SPECIAL_IMG_COLS * y + x;
928 if (tile < SPECIAL_IMG_MAX) {
929 ibuf = IMB_allocImBuf(SPECIAL_IMG_SIZE, SPECIAL_IMG_SIZE, 32, IB_rect);
930 for (k = 0; k < SPECIAL_IMG_SIZE; k++) {
931 memcpy(&ibuf->rect[k * SPECIAL_IMG_SIZE],
932 &bbuf->rect[(k + y * SPECIAL_IMG_SIZE) * SPECIAL_IMG_SIZE * SPECIAL_IMG_COLS +
933 x * SPECIAL_IMG_SIZE],
934 SPECIAL_IMG_SIZE * sizeof(int));
935 }
936 gSpecialFileImages[tile] = ibuf;
937 }
938 }
939 }
940 IMB_freeImBuf(bbuf);
941 }
942 }
943
filelist_free_icons(void)944 void filelist_free_icons(void)
945 {
946 BLI_assert(G.background == false);
947
948 for (int i = 0; i < SPECIAL_IMG_MAX; i++) {
949 IMB_freeImBuf(gSpecialFileImages[i]);
950 gSpecialFileImages[i] = NULL;
951 }
952 }
953
filelist_imgsize(struct FileList * filelist,short w,short h)954 void filelist_imgsize(struct FileList *filelist, short w, short h)
955 {
956 filelist->prv_w = w;
957 filelist->prv_h = h;
958 }
959
filelist_geticon_get_file(struct FileList * filelist,const int index)960 static FileDirEntry *filelist_geticon_get_file(struct FileList *filelist, const int index)
961 {
962 BLI_assert(G.background == false);
963
964 return filelist_file(filelist, index);
965 }
966
filelist_getimage(struct FileList * filelist,const int index)967 ImBuf *filelist_getimage(struct FileList *filelist, const int index)
968 {
969 FileDirEntry *file = filelist_geticon_get_file(filelist, index);
970
971 return file->image;
972 }
973
filelist_geticon_image_ex(FileDirEntry * file)974 static ImBuf *filelist_geticon_image_ex(FileDirEntry *file)
975 {
976 ImBuf *ibuf = NULL;
977
978 if (file->typeflag & FILE_TYPE_DIR) {
979 if (FILENAME_IS_PARENT(file->relpath)) {
980 ibuf = gSpecialFileImages[SPECIAL_IMG_PARENT];
981 }
982 else {
983 ibuf = gSpecialFileImages[SPECIAL_IMG_FOLDER];
984 }
985 }
986 else {
987 ibuf = gSpecialFileImages[SPECIAL_IMG_DOCUMENT];
988 }
989
990 return ibuf;
991 }
992
filelist_geticon_image(struct FileList * filelist,const int index)993 ImBuf *filelist_geticon_image(struct FileList *filelist, const int index)
994 {
995 FileDirEntry *file = filelist_geticon_get_file(filelist, index);
996 return filelist_geticon_image_ex(file);
997 }
998
filelist_geticon_ex(FileDirEntry * file,const char * root,const bool is_main,const bool ignore_libdir)999 static int filelist_geticon_ex(FileDirEntry *file,
1000 const char *root,
1001 const bool is_main,
1002 const bool ignore_libdir)
1003 {
1004 const int typeflag = file->typeflag;
1005
1006 if ((typeflag & FILE_TYPE_DIR) &&
1007 !(ignore_libdir && (typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER)))) {
1008 if (FILENAME_IS_PARENT(file->relpath)) {
1009 return is_main ? ICON_FILE_PARENT : ICON_NONE;
1010 }
1011 if (typeflag & FILE_TYPE_APPLICATIONBUNDLE) {
1012 return ICON_UGLYPACKAGE;
1013 }
1014 if (typeflag & FILE_TYPE_BLENDER) {
1015 return ICON_FILE_BLEND;
1016 }
1017 if (is_main) {
1018 /* Do not return icon for folders if icons are not 'main' draw type
1019 * (e.g. when used over previews). */
1020 return (file->attributes & FILE_ATTR_ANY_LINK) ? ICON_FOLDER_REDIRECT : ICON_FILE_FOLDER;
1021 }
1022
1023 /* If this path is in System list or path cache then use that icon. */
1024 struct FSMenu *fsmenu = ED_fsmenu_get();
1025 FSMenuCategory categories[] = {
1026 FS_CATEGORY_SYSTEM,
1027 FS_CATEGORY_SYSTEM_BOOKMARKS,
1028 FS_CATEGORY_OTHER,
1029 };
1030
1031 for (int i = 0; i < ARRAY_SIZE(categories); i++) {
1032 FSMenuEntry *tfsm = ED_fsmenu_get_category(fsmenu, categories[i]);
1033 char fullpath[FILE_MAX_LIBEXTRA];
1034 char *target = fullpath;
1035 if (file->redirection_path) {
1036 target = file->redirection_path;
1037 }
1038 else {
1039 BLI_join_dirfile(fullpath, sizeof(fullpath), root, file->relpath);
1040 BLI_path_slash_ensure(fullpath);
1041 }
1042 for (; tfsm; tfsm = tfsm->next) {
1043 if (STREQ(tfsm->path, target)) {
1044 /* Never want a little folder inside a large one. */
1045 return (tfsm->icon == ICON_FILE_FOLDER) ? ICON_NONE : tfsm->icon;
1046 }
1047 }
1048 }
1049
1050 if (file->attributes & FILE_ATTR_OFFLINE) {
1051 return ICON_ERROR;
1052 }
1053 if (file->attributes & FILE_ATTR_TEMPORARY) {
1054 return ICON_FILE_CACHE;
1055 }
1056 if (file->attributes & FILE_ATTR_SYSTEM) {
1057 return ICON_SYSTEM;
1058 }
1059 }
1060
1061 if (typeflag & FILE_TYPE_BLENDER) {
1062 return ICON_FILE_BLEND;
1063 }
1064 if (typeflag & FILE_TYPE_BLENDER_BACKUP) {
1065 return ICON_FILE_BACKUP;
1066 }
1067 if (typeflag & FILE_TYPE_IMAGE) {
1068 return ICON_FILE_IMAGE;
1069 }
1070 if (typeflag & FILE_TYPE_MOVIE) {
1071 return ICON_FILE_MOVIE;
1072 }
1073 if (typeflag & FILE_TYPE_PYSCRIPT) {
1074 return ICON_FILE_SCRIPT;
1075 }
1076 if (typeflag & FILE_TYPE_SOUND) {
1077 return ICON_FILE_SOUND;
1078 }
1079 if (typeflag & FILE_TYPE_FTFONT) {
1080 return ICON_FILE_FONT;
1081 }
1082 if (typeflag & FILE_TYPE_BTX) {
1083 return ICON_FILE_BLANK;
1084 }
1085 if (typeflag & FILE_TYPE_COLLADA) {
1086 return ICON_FILE_3D;
1087 }
1088 if (typeflag & FILE_TYPE_ALEMBIC) {
1089 return ICON_FILE_3D;
1090 }
1091 if (typeflag & FILE_TYPE_USD) {
1092 return ICON_FILE_3D;
1093 }
1094 if (typeflag & FILE_TYPE_VOLUME) {
1095 return ICON_FILE_VOLUME;
1096 }
1097 if (typeflag & FILE_TYPE_OBJECT_IO) {
1098 return ICON_FILE_3D;
1099 }
1100 if (typeflag & FILE_TYPE_TEXT) {
1101 return ICON_FILE_TEXT;
1102 }
1103 if (typeflag & FILE_TYPE_ARCHIVE) {
1104 return ICON_FILE_ARCHIVE;
1105 }
1106 if (typeflag & FILE_TYPE_BLENDERLIB) {
1107 const int ret = UI_icon_from_idcode(file->blentype);
1108 if (ret != ICON_NONE) {
1109 return ret;
1110 }
1111 }
1112 return is_main ? ICON_FILE_BLANK : ICON_NONE;
1113 }
1114
filelist_geticon(struct FileList * filelist,const int index,const bool is_main)1115 int filelist_geticon(struct FileList *filelist, const int index, const bool is_main)
1116 {
1117 FileDirEntry *file = filelist_geticon_get_file(filelist, index);
1118
1119 return filelist_geticon_ex(file, filelist->filelist.root, is_main, false);
1120 }
1121
1122 /* ********** Main ********** */
1123
parent_dir_until_exists_or_default_root(char * dir)1124 static void parent_dir_until_exists_or_default_root(char *dir)
1125 {
1126 if (!BLI_path_parent_dir_until_exists(dir)) {
1127 #ifdef WIN32
1128 BLI_windows_get_default_root_dir(dir);
1129 #else
1130 strcpy(dir, "/");
1131 #endif
1132 }
1133 }
1134
filelist_checkdir_dir(struct FileList * UNUSED (filelist),char * r_dir,const bool do_change)1135 static bool filelist_checkdir_dir(struct FileList *UNUSED(filelist),
1136 char *r_dir,
1137 const bool do_change)
1138 {
1139 if (do_change) {
1140 parent_dir_until_exists_or_default_root(r_dir);
1141 return true;
1142 }
1143 return BLI_is_dir(r_dir);
1144 }
1145
filelist_checkdir_lib(struct FileList * UNUSED (filelist),char * r_dir,const bool do_change)1146 static bool filelist_checkdir_lib(struct FileList *UNUSED(filelist),
1147 char *r_dir,
1148 const bool do_change)
1149 {
1150 char tdir[FILE_MAX_LIBEXTRA];
1151 char *name;
1152
1153 const bool is_valid = (BLI_is_dir(r_dir) ||
1154 (BLO_library_path_explode(r_dir, tdir, NULL, &name) &&
1155 BLI_is_file(tdir) && !name));
1156
1157 if (do_change && !is_valid) {
1158 /* if not a valid library, we need it to be a valid directory! */
1159 parent_dir_until_exists_or_default_root(r_dir);
1160 return true;
1161 }
1162 return is_valid;
1163 }
1164
filelist_checkdir_main(struct FileList * filelist,char * r_dir,const bool do_change)1165 static bool filelist_checkdir_main(struct FileList *filelist, char *r_dir, const bool do_change)
1166 {
1167 /* TODO */
1168 return filelist_checkdir_lib(filelist, r_dir, do_change);
1169 }
1170
filelist_entry_clear(FileDirEntry * entry)1171 static void filelist_entry_clear(FileDirEntry *entry)
1172 {
1173 if (entry->name) {
1174 MEM_freeN(entry->name);
1175 }
1176 if (entry->description) {
1177 MEM_freeN(entry->description);
1178 }
1179 if (entry->relpath) {
1180 MEM_freeN(entry->relpath);
1181 }
1182 if (entry->redirection_path) {
1183 MEM_freeN(entry->redirection_path);
1184 }
1185 if (entry->image) {
1186 IMB_freeImBuf(entry->image);
1187 }
1188 /* For now, consider FileDirEntryRevision::poin as not owned here,
1189 * so no need to do anything about it */
1190
1191 if (!BLI_listbase_is_empty(&entry->variants)) {
1192 FileDirEntryVariant *var;
1193
1194 for (var = entry->variants.first; var; var = var->next) {
1195 if (var->name) {
1196 MEM_freeN(var->name);
1197 }
1198 if (var->description) {
1199 MEM_freeN(var->description);
1200 }
1201
1202 if (!BLI_listbase_is_empty(&var->revisions)) {
1203 FileDirEntryRevision *rev;
1204
1205 for (rev = var->revisions.first; rev; rev = rev->next) {
1206 if (rev->comment) {
1207 MEM_freeN(rev->comment);
1208 }
1209 }
1210
1211 BLI_freelistN(&var->revisions);
1212 }
1213 }
1214
1215 /* TODO: tags! */
1216
1217 BLI_freelistN(&entry->variants);
1218 }
1219 else if (entry->entry) {
1220 MEM_freeN(entry->entry);
1221 }
1222 }
1223
filelist_entry_free(FileDirEntry * entry)1224 static void filelist_entry_free(FileDirEntry *entry)
1225 {
1226 filelist_entry_clear(entry);
1227 MEM_freeN(entry);
1228 }
1229
filelist_direntryarr_free(FileDirEntryArr * array)1230 static void filelist_direntryarr_free(FileDirEntryArr *array)
1231 {
1232 #if 0
1233 FileDirEntry *entry, *entry_next;
1234
1235 for (entry = array->entries.first; entry; entry = entry_next) {
1236 entry_next = entry->next;
1237 filelist_entry_free(entry);
1238 }
1239 BLI_listbase_clear(&array->entries);
1240 #else
1241 BLI_assert(BLI_listbase_is_empty(&array->entries));
1242 #endif
1243 array->nbr_entries = 0;
1244 array->nbr_entries_filtered = -1;
1245 array->entry_idx_start = -1;
1246 array->entry_idx_end = -1;
1247 }
1248
filelist_intern_entry_free(FileListInternEntry * entry)1249 static void filelist_intern_entry_free(FileListInternEntry *entry)
1250 {
1251 if (entry->relpath) {
1252 MEM_freeN(entry->relpath);
1253 }
1254 if (entry->redirection_path) {
1255 MEM_freeN(entry->redirection_path);
1256 }
1257 if (entry->name) {
1258 MEM_freeN(entry->name);
1259 }
1260 MEM_freeN(entry);
1261 }
1262
filelist_intern_free(FileListIntern * filelist_intern)1263 static void filelist_intern_free(FileListIntern *filelist_intern)
1264 {
1265 FileListInternEntry *entry, *entry_next;
1266
1267 for (entry = filelist_intern->entries.first; entry; entry = entry_next) {
1268 entry_next = entry->next;
1269 filelist_intern_entry_free(entry);
1270 }
1271 BLI_listbase_clear(&filelist_intern->entries);
1272
1273 MEM_SAFE_FREE(filelist_intern->filtered);
1274 }
1275
filelist_cache_preview_runf(TaskPool * __restrict pool,void * taskdata)1276 static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdata)
1277 {
1278 FileListEntryCache *cache = BLI_task_pool_user_data(pool);
1279 FileListEntryPreviewTaskData *preview_taskdata = taskdata;
1280 FileListEntryPreview *preview = preview_taskdata->preview;
1281
1282 ThumbSource source = 0;
1283
1284 // printf("%s: Start (%d)...\n", __func__, threadid);
1285
1286 // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
1287 BLI_assert(preview->flags &
1288 (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | FILE_TYPE_BLENDER |
1289 FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB));
1290
1291 if (preview->flags & FILE_TYPE_IMAGE) {
1292 source = THB_SOURCE_IMAGE;
1293 }
1294 else if (preview->flags &
1295 (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)) {
1296 source = THB_SOURCE_BLEND;
1297 }
1298 else if (preview->flags & FILE_TYPE_MOVIE) {
1299 source = THB_SOURCE_MOVIE;
1300 }
1301 else if (preview->flags & FILE_TYPE_FTFONT) {
1302 source = THB_SOURCE_FONT;
1303 }
1304
1305 IMB_thumb_path_lock(preview->path);
1306 preview->img = IMB_thumb_manage(preview->path, THB_LARGE, source);
1307 IMB_thumb_path_unlock(preview->path);
1308
1309 /* That way task freeing function won't free th preview, since it does not own it anymore. */
1310 atomic_cas_ptr((void **)&preview_taskdata->preview, preview, NULL);
1311 BLI_thread_queue_push(cache->previews_done, preview);
1312
1313 // printf("%s: End (%d)...\n", __func__, threadid);
1314 }
1315
filelist_cache_preview_freef(TaskPool * __restrict UNUSED (pool),void * taskdata)1316 static void filelist_cache_preview_freef(TaskPool *__restrict UNUSED(pool), void *taskdata)
1317 {
1318 FileListEntryPreviewTaskData *preview_taskdata = taskdata;
1319 FileListEntryPreview *preview = preview_taskdata->preview;
1320
1321 /* preview_taskdata->preview is atomically set to NULL once preview has been processed and sent
1322 * to previews_done queue. */
1323 if (preview != NULL) {
1324 if (preview->img) {
1325 IMB_freeImBuf(preview->img);
1326 }
1327 MEM_freeN(preview);
1328 }
1329 MEM_freeN(preview_taskdata);
1330 }
1331
filelist_cache_preview_ensure_running(FileListEntryCache * cache)1332 static void filelist_cache_preview_ensure_running(FileListEntryCache *cache)
1333 {
1334 if (!cache->previews_pool) {
1335 cache->previews_pool = BLI_task_pool_create_background(cache, TASK_PRIORITY_LOW);
1336 cache->previews_done = BLI_thread_queue_init();
1337
1338 IMB_thumb_locks_acquire();
1339 }
1340 }
1341
filelist_cache_previews_clear(FileListEntryCache * cache)1342 static void filelist_cache_previews_clear(FileListEntryCache *cache)
1343 {
1344 if (cache->previews_pool) {
1345 BLI_task_pool_cancel(cache->previews_pool);
1346
1347 FileListEntryPreview *preview;
1348 while ((preview = BLI_thread_queue_pop_timeout(cache->previews_done, 0))) {
1349 // printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path,
1350 // preview->img);
1351 if (preview->img) {
1352 IMB_freeImBuf(preview->img);
1353 }
1354 MEM_freeN(preview);
1355 }
1356 }
1357 }
1358
filelist_cache_previews_free(FileListEntryCache * cache)1359 static void filelist_cache_previews_free(FileListEntryCache *cache)
1360 {
1361 if (cache->previews_pool) {
1362 BLI_thread_queue_nowait(cache->previews_done);
1363
1364 filelist_cache_previews_clear(cache);
1365
1366 BLI_thread_queue_free(cache->previews_done);
1367 BLI_task_pool_free(cache->previews_pool);
1368 cache->previews_pool = NULL;
1369 cache->previews_done = NULL;
1370
1371 IMB_thumb_locks_release();
1372 }
1373
1374 cache->flags &= ~FLC_PREVIEWS_ACTIVE;
1375 }
1376
filelist_cache_previews_push(FileList * filelist,FileDirEntry * entry,const int index)1377 static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry, const int index)
1378 {
1379 FileListEntryCache *cache = &filelist->filelist_cache;
1380
1381 BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE);
1382
1383 if (!entry->image && !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) &&
1384 (entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
1385 FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) {
1386 FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__);
1387
1388 if (entry->redirection_path) {
1389 BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR);
1390 }
1391 else {
1392 BLI_join_dirfile(
1393 preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath);
1394 }
1395
1396 preview->index = index;
1397 preview->flags = entry->typeflag;
1398 preview->img = NULL;
1399 // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
1400
1401 filelist_cache_preview_ensure_running(cache);
1402
1403 FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata),
1404 __func__);
1405 preview_taskdata->preview = preview;
1406 BLI_task_pool_push(cache->previews_pool,
1407 filelist_cache_preview_runf,
1408 preview_taskdata,
1409 true,
1410 filelist_cache_preview_freef);
1411 }
1412 }
1413
filelist_cache_init(FileListEntryCache * cache,size_t cache_size)1414 static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size)
1415 {
1416 BLI_listbase_clear(&cache->cached_entries);
1417
1418 cache->block_cursor = cache->block_start_index = cache->block_center_index =
1419 cache->block_end_index = 0;
1420 cache->block_entries = MEM_mallocN(sizeof(*cache->block_entries) * cache_size, __func__);
1421
1422 cache->misc_entries = BLI_ghash_ptr_new_ex(__func__, cache_size);
1423 cache->misc_entries_indices = MEM_mallocN(sizeof(*cache->misc_entries_indices) * cache_size,
1424 __func__);
1425 copy_vn_i(cache->misc_entries_indices, cache_size, -1);
1426 cache->misc_cursor = 0;
1427
1428 /* XXX This assumes uint is 32 bits and uuid is 128 bits (char[16]), be careful! */
1429 cache->uuids = BLI_ghash_new_ex(
1430 BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__, cache_size * 2);
1431
1432 cache->size = cache_size;
1433 cache->flags = FLC_IS_INIT;
1434
1435 /* We cannot translate from non-main thread, so init translated strings once from here. */
1436 IMB_thumb_ensure_translations();
1437 }
1438
filelist_cache_free(FileListEntryCache * cache)1439 static void filelist_cache_free(FileListEntryCache *cache)
1440 {
1441 FileDirEntry *entry, *entry_next;
1442
1443 if (!(cache->flags & FLC_IS_INIT)) {
1444 return;
1445 }
1446
1447 filelist_cache_previews_free(cache);
1448
1449 MEM_freeN(cache->block_entries);
1450
1451 BLI_ghash_free(cache->misc_entries, NULL, NULL);
1452 MEM_freeN(cache->misc_entries_indices);
1453
1454 BLI_ghash_free(cache->uuids, NULL, NULL);
1455
1456 for (entry = cache->cached_entries.first; entry; entry = entry_next) {
1457 entry_next = entry->next;
1458 filelist_entry_free(entry);
1459 }
1460 BLI_listbase_clear(&cache->cached_entries);
1461 }
1462
filelist_cache_clear(FileListEntryCache * cache,size_t new_size)1463 static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size)
1464 {
1465 FileDirEntry *entry, *entry_next;
1466
1467 if (!(cache->flags & FLC_IS_INIT)) {
1468 return;
1469 }
1470
1471 filelist_cache_previews_clear(cache);
1472
1473 cache->block_cursor = cache->block_start_index = cache->block_center_index =
1474 cache->block_end_index = 0;
1475 if (new_size != cache->size) {
1476 cache->block_entries = MEM_reallocN(cache->block_entries,
1477 sizeof(*cache->block_entries) * new_size);
1478 }
1479
1480 BLI_ghash_clear_ex(cache->misc_entries, NULL, NULL, new_size);
1481 if (new_size != cache->size) {
1482 cache->misc_entries_indices = MEM_reallocN(cache->misc_entries_indices,
1483 sizeof(*cache->misc_entries_indices) * new_size);
1484 }
1485 copy_vn_i(cache->misc_entries_indices, new_size, -1);
1486
1487 BLI_ghash_clear_ex(cache->uuids, NULL, NULL, new_size * 2);
1488
1489 cache->size = new_size;
1490
1491 for (entry = cache->cached_entries.first; entry; entry = entry_next) {
1492 entry_next = entry->next;
1493 filelist_entry_free(entry);
1494 }
1495 BLI_listbase_clear(&cache->cached_entries);
1496 }
1497
filelist_new(short type)1498 FileList *filelist_new(short type)
1499 {
1500 FileList *p = MEM_callocN(sizeof(*p), __func__);
1501
1502 filelist_cache_init(&p->filelist_cache, FILELIST_ENTRYCACHESIZE_DEFAULT);
1503
1504 p->selection_state = BLI_ghash_new(
1505 BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__);
1506
1507 switch (type) {
1508 case FILE_MAIN:
1509 p->checkdirf = filelist_checkdir_main;
1510 p->read_jobf = filelist_readjob_main;
1511 p->filterf = is_filtered_main;
1512 break;
1513 case FILE_LOADLIB:
1514 p->checkdirf = filelist_checkdir_lib;
1515 p->read_jobf = filelist_readjob_lib;
1516 p->filterf = is_filtered_lib;
1517 break;
1518 default:
1519 p->checkdirf = filelist_checkdir_dir;
1520 p->read_jobf = filelist_readjob_dir;
1521 p->filterf = is_filtered_file;
1522 break;
1523 }
1524 return p;
1525 }
1526
filelist_clear_ex(struct FileList * filelist,const bool do_cache,const bool do_selection)1527 void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection)
1528 {
1529 if (!filelist) {
1530 return;
1531 }
1532
1533 filelist_filter_clear(filelist);
1534
1535 if (do_cache) {
1536 filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size);
1537 }
1538
1539 filelist_intern_free(&filelist->filelist_intern);
1540
1541 filelist_direntryarr_free(&filelist->filelist);
1542
1543 if (do_selection && filelist->selection_state) {
1544 BLI_ghash_clear(filelist->selection_state, MEM_freeN, NULL);
1545 }
1546 }
1547
filelist_clear(struct FileList * filelist)1548 void filelist_clear(struct FileList *filelist)
1549 {
1550 filelist_clear_ex(filelist, true, true);
1551 }
1552
filelist_free(struct FileList * filelist)1553 void filelist_free(struct FileList *filelist)
1554 {
1555 if (!filelist) {
1556 printf("Attempting to delete empty filelist.\n");
1557 return;
1558 }
1559
1560 /* No need to clear cache & selection_state, we free them anyway. */
1561 filelist_clear_ex(filelist, false, false);
1562 filelist_cache_free(&filelist->filelist_cache);
1563
1564 if (filelist->selection_state) {
1565 BLI_ghash_free(filelist->selection_state, MEM_freeN, NULL);
1566 filelist->selection_state = NULL;
1567 }
1568
1569 memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
1570
1571 filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING);
1572 filelist->sort = FILE_SORT_NONE;
1573 }
1574
filelist_freelib(struct FileList * filelist)1575 void filelist_freelib(struct FileList *filelist)
1576 {
1577 if (filelist->libfiledata) {
1578 BLO_blendhandle_close(filelist->libfiledata);
1579 }
1580 filelist->libfiledata = NULL;
1581 }
1582
filelist_lib(struct FileList * filelist)1583 BlendHandle *filelist_lib(struct FileList *filelist)
1584 {
1585 return filelist->libfiledata;
1586 }
1587
fileentry_uiname(const char * root,const char * relpath,const int typeflag,char * buff)1588 static const char *fileentry_uiname(const char *root,
1589 const char *relpath,
1590 const int typeflag,
1591 char *buff)
1592 {
1593 char *name = NULL;
1594
1595 if (typeflag & FILE_TYPE_BLENDERLIB) {
1596 char abspath[FILE_MAX_LIBEXTRA];
1597 char *group;
1598
1599 BLI_join_dirfile(abspath, sizeof(abspath), root, relpath);
1600 BLO_library_path_explode(abspath, buff, &group, &name);
1601 if (!name) {
1602 name = group;
1603 }
1604 }
1605 /* Depending on platforms, 'my_file.blend/..' might be viewed as dir or not... */
1606 if (!name) {
1607 if (typeflag & FILE_TYPE_DIR) {
1608 name = (char *)relpath;
1609 }
1610 else {
1611 name = (char *)BLI_path_basename(relpath);
1612 }
1613 }
1614 BLI_assert(name);
1615
1616 return name;
1617 }
1618
filelist_dir(struct FileList * filelist)1619 const char *filelist_dir(struct FileList *filelist)
1620 {
1621 return filelist->filelist.root;
1622 }
1623
filelist_is_dir(struct FileList * filelist,const char * path)1624 bool filelist_is_dir(struct FileList *filelist, const char *path)
1625 {
1626 return filelist->checkdirf(filelist, (char *)path, false);
1627 }
1628
1629 /**
1630 * May modify in place given r_dir, which is expected to be FILE_MAX_LIBEXTRA length.
1631 */
filelist_setdir(struct FileList * filelist,char * r_dir)1632 void filelist_setdir(struct FileList *filelist, char *r_dir)
1633 {
1634 BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA);
1635
1636 BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir);
1637 const bool is_valid_path = filelist->checkdirf(filelist, r_dir, true);
1638 BLI_assert(is_valid_path);
1639 UNUSED_VARS_NDEBUG(is_valid_path);
1640
1641 if (!STREQ(filelist->filelist.root, r_dir)) {
1642 BLI_strncpy(filelist->filelist.root, r_dir, sizeof(filelist->filelist.root));
1643 filelist->flags |= FL_FORCE_RESET;
1644 }
1645 }
1646
filelist_setrecursion(struct FileList * filelist,const int recursion_level)1647 void filelist_setrecursion(struct FileList *filelist, const int recursion_level)
1648 {
1649 if (filelist->max_recursion != recursion_level) {
1650 filelist->max_recursion = recursion_level;
1651 filelist->flags |= FL_FORCE_RESET;
1652 }
1653 }
1654
filelist_force_reset(struct FileList * filelist)1655 bool filelist_force_reset(struct FileList *filelist)
1656 {
1657 return (filelist->flags & FL_FORCE_RESET) != 0;
1658 }
1659
filelist_is_ready(struct FileList * filelist)1660 bool filelist_is_ready(struct FileList *filelist)
1661 {
1662 return (filelist->flags & FL_IS_READY) != 0;
1663 }
1664
filelist_pending(struct FileList * filelist)1665 bool filelist_pending(struct FileList *filelist)
1666 {
1667 return (filelist->flags & FL_IS_PENDING) != 0;
1668 }
1669
1670 /**
1671 * Limited version of full update done by space_file's file_refresh(),
1672 * to be used by operators and such.
1673 * Ensures given filelist is ready to be used (i.e. it is filtered and sorted),
1674 * unless it is tagged for a full refresh.
1675 */
filelist_files_ensure(FileList * filelist)1676 int filelist_files_ensure(FileList *filelist)
1677 {
1678 if (!filelist_force_reset(filelist) || !filelist_empty(filelist)) {
1679 filelist_sort(filelist);
1680 filelist_filter(filelist);
1681 }
1682
1683 return filelist->filelist.nbr_entries_filtered;
1684 }
1685
filelist_file_create_entry(FileList * filelist,const int index)1686 static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int index)
1687 {
1688 FileListInternEntry *entry = filelist->filelist_intern.filtered[index];
1689 FileListEntryCache *cache = &filelist->filelist_cache;
1690 FileDirEntry *ret;
1691 FileDirEntryRevision *rev;
1692
1693 ret = MEM_callocN(sizeof(*ret), __func__);
1694 rev = MEM_callocN(sizeof(*rev), __func__);
1695
1696 rev->size = (uint64_t)entry->st.st_size;
1697
1698 rev->time = (int64_t)entry->st.st_mtime;
1699
1700 ret->entry = rev;
1701 ret->relpath = BLI_strdup(entry->relpath);
1702 ret->name = BLI_strdup(entry->name);
1703 ret->description = BLI_strdupcat(filelist->filelist.root, entry->relpath);
1704 memcpy(ret->uuid, entry->uuid, sizeof(ret->uuid));
1705 ret->blentype = entry->blentype;
1706 ret->typeflag = entry->typeflag;
1707 ret->attributes = entry->attributes;
1708 if (entry->redirection_path) {
1709 ret->redirection_path = BLI_strdup(entry->redirection_path);
1710 }
1711 BLI_addtail(&cache->cached_entries, ret);
1712 return ret;
1713 }
1714
filelist_file_release_entry(FileList * filelist,FileDirEntry * entry)1715 static void filelist_file_release_entry(FileList *filelist, FileDirEntry *entry)
1716 {
1717 BLI_remlink(&filelist->filelist_cache.cached_entries, entry);
1718 filelist_entry_free(entry);
1719 }
1720
filelist_file_ex(struct FileList * filelist,const int index,const bool use_request)1721 static FileDirEntry *filelist_file_ex(struct FileList *filelist,
1722 const int index,
1723 const bool use_request)
1724 {
1725 FileDirEntry *ret = NULL, *old;
1726 FileListEntryCache *cache = &filelist->filelist_cache;
1727 const size_t cache_size = cache->size;
1728 int old_index;
1729
1730 if ((index < 0) || (index >= filelist->filelist.nbr_entries_filtered)) {
1731 return ret;
1732 }
1733
1734 if (index >= cache->block_start_index && index < cache->block_end_index) {
1735 const int idx = (index - cache->block_start_index + cache->block_cursor) % cache_size;
1736 return cache->block_entries[idx];
1737 }
1738
1739 if ((ret = BLI_ghash_lookup(cache->misc_entries, POINTER_FROM_INT(index)))) {
1740 return ret;
1741 }
1742
1743 if (!use_request) {
1744 return NULL;
1745 }
1746
1747 // printf("requesting file %d (not yet cached)\n", index);
1748
1749 /* Else, we have to add new entry to 'misc' cache - and possibly make room for it first! */
1750 ret = filelist_file_create_entry(filelist, index);
1751 old_index = cache->misc_entries_indices[cache->misc_cursor];
1752 if ((old = BLI_ghash_popkey(cache->misc_entries, POINTER_FROM_INT(old_index), NULL))) {
1753 BLI_ghash_remove(cache->uuids, old->uuid, NULL, NULL);
1754 filelist_file_release_entry(filelist, old);
1755 }
1756 BLI_ghash_insert(cache->misc_entries, POINTER_FROM_INT(index), ret);
1757 BLI_ghash_insert(cache->uuids, ret->uuid, ret);
1758
1759 cache->misc_entries_indices[cache->misc_cursor] = index;
1760 cache->misc_cursor = (cache->misc_cursor + 1) % cache_size;
1761
1762 #if 0 /* Actually no, only block cached entries should have preview imho. */
1763 if (cache->previews_pool) {
1764 filelist_cache_previews_push(filelist, ret, index);
1765 }
1766 #endif
1767
1768 return ret;
1769 }
1770
filelist_file(struct FileList * filelist,int index)1771 FileDirEntry *filelist_file(struct FileList *filelist, int index)
1772 {
1773 return filelist_file_ex(filelist, index, true);
1774 }
1775
filelist_file_findpath(struct FileList * filelist,const char * filename)1776 int filelist_file_findpath(struct FileList *filelist, const char *filename)
1777 {
1778 int fidx = -1;
1779
1780 if (filelist->filelist.nbr_entries_filtered < 0) {
1781 return fidx;
1782 }
1783
1784 /* XXX TODO Cache could probably use a ghash on paths too? Not really urgent though.
1785 * This is only used to find again renamed entry,
1786 * annoying but looks hairy to get rid of it currently. */
1787
1788 for (fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) {
1789 FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx];
1790 if (STREQ(entry->relpath, filename)) {
1791 return fidx;
1792 }
1793 }
1794
1795 return -1;
1796 }
1797
filelist_entry_find_uuid(struct FileList * filelist,const int uuid[4])1798 FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4])
1799 {
1800 if (filelist->filelist.nbr_entries_filtered < 0) {
1801 return NULL;
1802 }
1803
1804 if (filelist->filelist_cache.uuids) {
1805 FileDirEntry *entry = BLI_ghash_lookup(filelist->filelist_cache.uuids, uuid);
1806 if (entry) {
1807 return entry;
1808 }
1809 }
1810
1811 {
1812 int fidx;
1813
1814 for (fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) {
1815 FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx];
1816 if (memcmp(entry->uuid, uuid, sizeof(entry->uuid)) == 0) {
1817 return filelist_file(filelist, fidx);
1818 }
1819 }
1820 }
1821
1822 return NULL;
1823 }
1824
filelist_file_cache_slidingwindow_set(FileList * filelist,size_t window_size)1825 void filelist_file_cache_slidingwindow_set(FileList *filelist, size_t window_size)
1826 {
1827 /* Always keep it power of 2, in [256, 8192] range for now,
1828 * cache being app. twice bigger than requested window. */
1829 size_t size = 256;
1830 window_size *= 2;
1831
1832 while (size < window_size && size < 8192) {
1833 size *= 2;
1834 }
1835
1836 if (size != filelist->filelist_cache.size) {
1837 filelist_cache_clear(&filelist->filelist_cache, size);
1838 }
1839 }
1840
1841 /* Helpers, low-level, they assume cursor + size <= cache_size */
filelist_file_cache_block_create(FileList * filelist,const int start_index,const int size,int cursor)1842 static bool filelist_file_cache_block_create(FileList *filelist,
1843 const int start_index,
1844 const int size,
1845 int cursor)
1846 {
1847 FileListEntryCache *cache = &filelist->filelist_cache;
1848
1849 {
1850 int i, idx;
1851
1852 for (i = 0, idx = start_index; i < size; i++, idx++, cursor++) {
1853 FileDirEntry *entry;
1854
1855 /* That entry might have already been requested and stored in misc cache... */
1856 if ((entry = BLI_ghash_popkey(cache->misc_entries, POINTER_FROM_INT(idx), NULL)) == NULL) {
1857 entry = filelist_file_create_entry(filelist, idx);
1858 BLI_ghash_insert(cache->uuids, entry->uuid, entry);
1859 }
1860 cache->block_entries[cursor] = entry;
1861 }
1862 return true;
1863 }
1864
1865 return false;
1866 }
1867
filelist_file_cache_block_release(struct FileList * filelist,const int size,int cursor)1868 static void filelist_file_cache_block_release(struct FileList *filelist,
1869 const int size,
1870 int cursor)
1871 {
1872 FileListEntryCache *cache = &filelist->filelist_cache;
1873
1874 {
1875 int i;
1876
1877 for (i = 0; i < size; i++, cursor++) {
1878 FileDirEntry *entry = cache->block_entries[cursor];
1879 #if 0
1880 printf("%s: release cacheidx %d (%%p %%s)\n",
1881 __func__,
1882 cursor /*, cache->block_entries[cursor], cache->block_entries[cursor]->relpath*/);
1883 #endif
1884 BLI_ghash_remove(cache->uuids, entry->uuid, NULL, NULL);
1885 filelist_file_release_entry(filelist, entry);
1886 #ifndef NDEBUG
1887 cache->block_entries[cursor] = NULL;
1888 #endif
1889 }
1890 }
1891 }
1892
1893 /* Load in cache all entries "around" given index (as much as block cache may hold). */
filelist_file_cache_block(struct FileList * filelist,const int index)1894 bool filelist_file_cache_block(struct FileList *filelist, const int index)
1895 {
1896 FileListEntryCache *cache = &filelist->filelist_cache;
1897 const size_t cache_size = cache->size;
1898
1899 const int nbr_entries = filelist->filelist.nbr_entries_filtered;
1900 int start_index = max_ii(0, index - (cache_size / 2));
1901 int end_index = min_ii(nbr_entries, index + (cache_size / 2));
1902 int i;
1903 const bool full_refresh = (filelist->flags & FL_IS_READY) == 0;
1904
1905 if ((index < 0) || (index >= nbr_entries)) {
1906 // printf("Wrong index %d ([%d:%d])", index, 0, nbr_entries);
1907 return false;
1908 }
1909
1910 /* Maximize cached range! */
1911 if ((end_index - start_index) < cache_size) {
1912 if (start_index == 0) {
1913 end_index = min_ii(nbr_entries, start_index + cache_size);
1914 }
1915 else if (end_index == nbr_entries) {
1916 start_index = max_ii(0, end_index - cache_size);
1917 }
1918 }
1919
1920 BLI_assert((end_index - start_index) <= cache_size);
1921
1922 // printf("%s: [%d:%d] around index %d (current cache: [%d:%d])\n", __func__,
1923 // start_index, end_index, index, cache->block_start_index, cache->block_end_index);
1924
1925 /* If we have something to (re)cache... */
1926 if (full_refresh || (start_index != cache->block_start_index) ||
1927 (end_index != cache->block_end_index)) {
1928 if (full_refresh || (start_index >= cache->block_end_index) ||
1929 (end_index <= cache->block_start_index)) {
1930 int size1 = cache->block_end_index - cache->block_start_index;
1931 int size2 = 0;
1932 int idx1 = cache->block_cursor, idx2 = 0;
1933
1934 // printf("Full Recaching!\n");
1935
1936 if (cache->flags & FLC_PREVIEWS_ACTIVE) {
1937 filelist_cache_previews_clear(cache);
1938 }
1939
1940 if (idx1 + size1 > cache_size) {
1941 size2 = idx1 + size1 - cache_size;
1942 size1 -= size2;
1943 filelist_file_cache_block_release(filelist, size2, idx2);
1944 }
1945 filelist_file_cache_block_release(filelist, size1, idx1);
1946
1947 cache->block_start_index = cache->block_end_index = cache->block_cursor = 0;
1948
1949 /* New cached block does not overlap existing one, simple. */
1950 if (!filelist_file_cache_block_create(filelist, start_index, end_index - start_index, 0)) {
1951 return false;
1952 }
1953
1954 cache->block_start_index = start_index;
1955 cache->block_end_index = end_index;
1956 }
1957 else {
1958 // printf("Partial Recaching!\n");
1959
1960 /* At this point, we know we keep part of currently cached entries, so update previews
1961 * if needed, and remove everything from working queue - we'll add all newly needed
1962 * entries at the end. */
1963 if (cache->flags & FLC_PREVIEWS_ACTIVE) {
1964 filelist_cache_previews_update(filelist);
1965 filelist_cache_previews_clear(cache);
1966 }
1967
1968 // printf("\tpreview cleaned up...\n");
1969
1970 if (start_index > cache->block_start_index) {
1971 int size1 = start_index - cache->block_start_index;
1972 int size2 = 0;
1973 int idx1 = cache->block_cursor, idx2 = 0;
1974
1975 // printf("\tcache releasing: [%d:%d] (%d, %d)\n",
1976 // cache->block_start_index, cache->block_start_index + size1,
1977 // cache->block_cursor, size1);
1978
1979 if (idx1 + size1 > cache_size) {
1980 size2 = idx1 + size1 - cache_size;
1981 size1 -= size2;
1982 filelist_file_cache_block_release(filelist, size2, idx2);
1983 }
1984 filelist_file_cache_block_release(filelist, size1, idx1);
1985
1986 cache->block_cursor = (idx1 + size1 + size2) % cache_size;
1987 cache->block_start_index = start_index;
1988 }
1989 if (end_index < cache->block_end_index) {
1990 int size1 = cache->block_end_index - end_index;
1991 int size2 = 0;
1992 int idx1, idx2 = 0;
1993
1994 #if 0
1995 printf("\tcache releasing: [%d:%d] (%d)\n",
1996 cache->block_end_index - size1,
1997 cache->block_end_index,
1998 cache->block_cursor);
1999 #endif
2000
2001 idx1 = (cache->block_cursor + end_index - cache->block_start_index) % cache_size;
2002 if (idx1 + size1 > cache_size) {
2003 size2 = idx1 + size1 - cache_size;
2004 size1 -= size2;
2005 filelist_file_cache_block_release(filelist, size2, idx2);
2006 }
2007 filelist_file_cache_block_release(filelist, size1, idx1);
2008
2009 cache->block_end_index = end_index;
2010 }
2011
2012 // printf("\tcache cleaned up...\n");
2013
2014 if (start_index < cache->block_start_index) {
2015 /* Add (request) needed entries before already cached ones. */
2016 /* Note: We need some index black magic to wrap around (cycle)
2017 * inside our cache_size array... */
2018 int size1 = cache->block_start_index - start_index;
2019 int size2 = 0;
2020 int idx1, idx2;
2021
2022 if (size1 > cache->block_cursor) {
2023 size2 = size1;
2024 size1 -= cache->block_cursor;
2025 size2 -= size1;
2026 idx2 = 0;
2027 idx1 = cache_size - size1;
2028 }
2029 else {
2030 idx1 = cache->block_cursor - size1;
2031 }
2032
2033 if (size2) {
2034 if (!filelist_file_cache_block_create(filelist, start_index + size1, size2, idx2)) {
2035 return false;
2036 }
2037 }
2038 if (!filelist_file_cache_block_create(filelist, start_index, size1, idx1)) {
2039 return false;
2040 }
2041
2042 cache->block_cursor = idx1;
2043 cache->block_start_index = start_index;
2044 }
2045 // printf("\tstart-extended...\n");
2046 if (end_index > cache->block_end_index) {
2047 /* Add (request) needed entries after already cached ones. */
2048 /* Note: We need some index black magic to wrap around (cycle)
2049 * inside our cache_size array... */
2050 int size1 = end_index - cache->block_end_index;
2051 int size2 = 0;
2052 int idx1, idx2;
2053
2054 idx1 = (cache->block_cursor + end_index - cache->block_start_index - size1) % cache_size;
2055 if ((idx1 + size1) > cache_size) {
2056 size2 = size1;
2057 size1 = cache_size - idx1;
2058 size2 -= size1;
2059 idx2 = 0;
2060 }
2061
2062 if (size2) {
2063 if (!filelist_file_cache_block_create(filelist, end_index - size2, size2, idx2)) {
2064 return false;
2065 }
2066 }
2067 if (!filelist_file_cache_block_create(filelist, end_index - size1 - size2, size1, idx1)) {
2068 return false;
2069 }
2070
2071 cache->block_end_index = end_index;
2072 }
2073
2074 // printf("\tend-extended...\n");
2075 }
2076 }
2077 else if ((cache->block_center_index != index) && (cache->flags & FLC_PREVIEWS_ACTIVE)) {
2078 /* We try to always preview visible entries first, so 'restart' preview background task. */
2079 filelist_cache_previews_update(filelist);
2080 filelist_cache_previews_clear(cache);
2081 }
2082
2083 // printf("Re-queueing previews...\n");
2084
2085 /* Note we try to preview first images around given index - i.e. assumed visible ones. */
2086 if (cache->flags & FLC_PREVIEWS_ACTIVE) {
2087 for (i = 0; ((index + i) < end_index) || ((index - i) >= start_index); i++) {
2088 if ((index - i) >= start_index) {
2089 const int idx = (cache->block_cursor + (index - start_index) - i) % cache_size;
2090 filelist_cache_previews_push(filelist, cache->block_entries[idx], index - i);
2091 }
2092 if ((index + i) < end_index) {
2093 const int idx = (cache->block_cursor + (index - start_index) + i) % cache_size;
2094 filelist_cache_previews_push(filelist, cache->block_entries[idx], index + i);
2095 }
2096 }
2097 }
2098
2099 cache->block_center_index = index;
2100
2101 // printf("%s Finished!\n", __func__);
2102
2103 return true;
2104 }
2105
filelist_cache_previews_set(FileList * filelist,const bool use_previews)2106 void filelist_cache_previews_set(FileList *filelist, const bool use_previews)
2107 {
2108 FileListEntryCache *cache = &filelist->filelist_cache;
2109
2110 if (use_previews == ((cache->flags & FLC_PREVIEWS_ACTIVE) != 0)) {
2111 return;
2112 }
2113 /* Do not start preview work while listing, gives nasty flickering! */
2114 if (use_previews && (filelist->flags & FL_IS_READY)) {
2115 cache->flags |= FLC_PREVIEWS_ACTIVE;
2116
2117 BLI_assert((cache->previews_pool == NULL) && (cache->previews_done == NULL));
2118
2119 // printf("%s: Init Previews...\n", __func__);
2120
2121 /* No need to populate preview queue here, filelist_file_cache_block() handles this. */
2122 }
2123 else {
2124 // printf("%s: Clear Previews...\n", __func__);
2125
2126 filelist_cache_previews_free(cache);
2127 }
2128 }
2129
filelist_cache_previews_update(FileList * filelist)2130 bool filelist_cache_previews_update(FileList *filelist)
2131 {
2132 FileListEntryCache *cache = &filelist->filelist_cache;
2133 TaskPool *pool = cache->previews_pool;
2134 bool changed = false;
2135
2136 if (!pool) {
2137 return changed;
2138 }
2139
2140 // printf("%s: Update Previews...\n", __func__);
2141
2142 while (!BLI_thread_queue_is_empty(cache->previews_done)) {
2143 FileListEntryPreview *preview = BLI_thread_queue_pop(cache->previews_done);
2144 FileDirEntry *entry;
2145
2146 /* Paranoid (should never happen currently
2147 * since we consume this queue from a single thread), but... */
2148 if (!preview) {
2149 continue;
2150 }
2151 /* entry might have been removed from cache in the mean time,
2152 * we do not want to cache it again here. */
2153 entry = filelist_file_ex(filelist, preview->index, false);
2154
2155 // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
2156
2157 if (preview->img) {
2158 /* Due to asynchronous process, a preview for a given image may be generated several times,
2159 * i.e. entry->image may already be set at this point. */
2160 if (entry && !entry->image) {
2161 entry->image = preview->img;
2162 changed = true;
2163 }
2164 else {
2165 IMB_freeImBuf(preview->img);
2166 }
2167 }
2168 else if (entry) {
2169 /* We want to avoid re-processing this entry continuously!
2170 * Note that, since entries only live in cache,
2171 * preview will be retried quite often anyway. */
2172 entry->flags |= FILE_ENTRY_INVALID_PREVIEW;
2173 }
2174
2175 MEM_freeN(preview);
2176 }
2177
2178 return changed;
2179 }
2180
filelist_cache_previews_running(FileList * filelist)2181 bool filelist_cache_previews_running(FileList *filelist)
2182 {
2183 FileListEntryCache *cache = &filelist->filelist_cache;
2184
2185 return (cache->previews_pool != NULL);
2186 }
2187
2188 /* would recognize .blend as well */
file_is_blend_backup(const char * str)2189 static bool file_is_blend_backup(const char *str)
2190 {
2191 const size_t a = strlen(str);
2192 size_t b = 7;
2193 bool retval = 0;
2194
2195 if (a == 0 || b >= a) {
2196 /* pass */
2197 }
2198 else {
2199 const char *loc;
2200
2201 if (a > b + 1) {
2202 b++;
2203 }
2204
2205 /* allow .blend1 .blend2 .blend32 */
2206 loc = BLI_strcasestr(str + a - b, ".blend");
2207
2208 if (loc) {
2209 retval = 1;
2210 }
2211 }
2212
2213 return retval;
2214 }
2215
2216 /* TODO: Maybe we should move this to BLI?
2217 * On the other hand, it's using defines from space-file area, so not sure... */
ED_path_extension_type(const char * path)2218 int ED_path_extension_type(const char *path)
2219 {
2220 if (BLO_has_bfile_extension(path)) {
2221 return FILE_TYPE_BLENDER;
2222 }
2223 if (file_is_blend_backup(path)) {
2224 return FILE_TYPE_BLENDER_BACKUP;
2225 }
2226 if (BLI_path_extension_check(path, ".app")) {
2227 return FILE_TYPE_APPLICATIONBUNDLE;
2228 }
2229 if (BLI_path_extension_check(path, ".py")) {
2230 return FILE_TYPE_PYSCRIPT;
2231 }
2232 if (BLI_path_extension_check_n(path,
2233 ".txt",
2234 ".glsl",
2235 ".osl",
2236 ".data",
2237 ".pov",
2238 ".ini",
2239 ".mcr",
2240 ".inc",
2241 ".fountain",
2242 NULL)) {
2243 return FILE_TYPE_TEXT;
2244 }
2245 if (BLI_path_extension_check_n(path, ".ttf", ".ttc", ".pfb", ".otf", ".otc", NULL)) {
2246 return FILE_TYPE_FTFONT;
2247 }
2248 if (BLI_path_extension_check(path, ".btx")) {
2249 return FILE_TYPE_BTX;
2250 }
2251 if (BLI_path_extension_check(path, ".dae")) {
2252 return FILE_TYPE_COLLADA;
2253 }
2254 if (BLI_path_extension_check(path, ".abc")) {
2255 return FILE_TYPE_ALEMBIC;
2256 }
2257 if (BLI_path_extension_check_n(path, ".usd", ".usda", ".usdc", NULL)) {
2258 return FILE_TYPE_USD;
2259 }
2260 if (BLI_path_extension_check(path, ".vdb")) {
2261 return FILE_TYPE_VOLUME;
2262 }
2263 if (BLI_path_extension_check(path, ".zip")) {
2264 return FILE_TYPE_ARCHIVE;
2265 }
2266 if (BLI_path_extension_check_n(path, ".obj", ".3ds", ".fbx", ".glb", ".gltf", NULL)) {
2267 return FILE_TYPE_OBJECT_IO;
2268 }
2269 if (BLI_path_extension_check_array(path, imb_ext_image)) {
2270 return FILE_TYPE_IMAGE;
2271 }
2272 if (BLI_path_extension_check(path, ".ogg")) {
2273 if (IMB_isanim(path)) {
2274 return FILE_TYPE_MOVIE;
2275 }
2276 return FILE_TYPE_SOUND;
2277 }
2278 if (BLI_path_extension_check_array(path, imb_ext_movie)) {
2279 return FILE_TYPE_MOVIE;
2280 }
2281 if (BLI_path_extension_check_array(path, imb_ext_audio)) {
2282 return FILE_TYPE_SOUND;
2283 }
2284 return 0;
2285 }
2286
ED_file_extension_icon(const char * path)2287 int ED_file_extension_icon(const char *path)
2288 {
2289 const int type = ED_path_extension_type(path);
2290
2291 switch (type) {
2292 case FILE_TYPE_BLENDER:
2293 return ICON_FILE_BLEND;
2294 case FILE_TYPE_BLENDER_BACKUP:
2295 return ICON_FILE_BACKUP;
2296 case FILE_TYPE_IMAGE:
2297 return ICON_FILE_IMAGE;
2298 case FILE_TYPE_MOVIE:
2299 return ICON_FILE_MOVIE;
2300 case FILE_TYPE_PYSCRIPT:
2301 return ICON_FILE_SCRIPT;
2302 case FILE_TYPE_SOUND:
2303 return ICON_FILE_SOUND;
2304 case FILE_TYPE_FTFONT:
2305 return ICON_FILE_FONT;
2306 case FILE_TYPE_BTX:
2307 return ICON_FILE_BLANK;
2308 case FILE_TYPE_COLLADA:
2309 case FILE_TYPE_ALEMBIC:
2310 case FILE_TYPE_OBJECT_IO:
2311 return ICON_FILE_3D;
2312 case FILE_TYPE_TEXT:
2313 return ICON_FILE_TEXT;
2314 case FILE_TYPE_ARCHIVE:
2315 return ICON_FILE_ARCHIVE;
2316 case FILE_TYPE_VOLUME:
2317 return ICON_FILE_VOLUME;
2318 default:
2319 return ICON_FILE_BLANK;
2320 }
2321 }
2322
filelist_empty(struct FileList * filelist)2323 int filelist_empty(struct FileList *filelist)
2324 {
2325 return (filelist->filelist.nbr_entries == 0);
2326 }
2327
filelist_entry_select_set(const FileList * filelist,const FileDirEntry * entry,FileSelType select,uint flag,FileCheckType check)2328 uint filelist_entry_select_set(const FileList *filelist,
2329 const FileDirEntry *entry,
2330 FileSelType select,
2331 uint flag,
2332 FileCheckType check)
2333 {
2334 /* Default NULL pointer if not found is fine here! */
2335 void **es_p = BLI_ghash_lookup_p(filelist->selection_state, entry->uuid);
2336 uint entry_flag = es_p ? POINTER_AS_UINT(*es_p) : 0;
2337 const uint org_entry_flag = entry_flag;
2338
2339 BLI_assert(entry);
2340 BLI_assert(ELEM(check, CHECK_DIRS, CHECK_FILES, CHECK_ALL));
2341
2342 if (((check == CHECK_ALL)) || ((check == CHECK_DIRS) && (entry->typeflag & FILE_TYPE_DIR)) ||
2343 ((check == CHECK_FILES) && !(entry->typeflag & FILE_TYPE_DIR))) {
2344 switch (select) {
2345 case FILE_SEL_REMOVE:
2346 entry_flag &= ~flag;
2347 break;
2348 case FILE_SEL_ADD:
2349 entry_flag |= flag;
2350 break;
2351 case FILE_SEL_TOGGLE:
2352 entry_flag ^= flag;
2353 break;
2354 }
2355 }
2356
2357 if (entry_flag != org_entry_flag) {
2358 if (es_p) {
2359 if (entry_flag) {
2360 *es_p = POINTER_FROM_UINT(entry_flag);
2361 }
2362 else {
2363 BLI_ghash_remove(filelist->selection_state, entry->uuid, MEM_freeN, NULL);
2364 }
2365 }
2366 else if (entry_flag) {
2367 void *key = MEM_mallocN(sizeof(entry->uuid), __func__);
2368 memcpy(key, entry->uuid, sizeof(entry->uuid));
2369 BLI_ghash_insert(filelist->selection_state, key, POINTER_FROM_UINT(entry_flag));
2370 }
2371 }
2372
2373 return entry_flag;
2374 }
2375
filelist_entry_select_index_set(FileList * filelist,const int index,FileSelType select,uint flag,FileCheckType check)2376 void filelist_entry_select_index_set(
2377 FileList *filelist, const int index, FileSelType select, uint flag, FileCheckType check)
2378 {
2379 FileDirEntry *entry = filelist_file(filelist, index);
2380
2381 if (entry) {
2382 filelist_entry_select_set(filelist, entry, select, flag, check);
2383 }
2384 }
2385
filelist_entries_select_index_range_set(FileList * filelist,FileSelection * sel,FileSelType select,uint flag,FileCheckType check)2386 void filelist_entries_select_index_range_set(
2387 FileList *filelist, FileSelection *sel, FileSelType select, uint flag, FileCheckType check)
2388 {
2389 /* select all valid files between first and last indicated */
2390 if ((sel->first >= 0) && (sel->first < filelist->filelist.nbr_entries_filtered) &&
2391 (sel->last >= 0) && (sel->last < filelist->filelist.nbr_entries_filtered)) {
2392 int current_file;
2393 for (current_file = sel->first; current_file <= sel->last; current_file++) {
2394 filelist_entry_select_index_set(filelist, current_file, select, flag, check);
2395 }
2396 }
2397 }
2398
filelist_entry_select_get(FileList * filelist,FileDirEntry * entry,FileCheckType check)2399 uint filelist_entry_select_get(FileList *filelist, FileDirEntry *entry, FileCheckType check)
2400 {
2401 BLI_assert(entry);
2402 BLI_assert(ELEM(check, CHECK_DIRS, CHECK_FILES, CHECK_ALL));
2403
2404 if (((check == CHECK_ALL)) || ((check == CHECK_DIRS) && (entry->typeflag & FILE_TYPE_DIR)) ||
2405 ((check == CHECK_FILES) && !(entry->typeflag & FILE_TYPE_DIR))) {
2406 /* Default NULL pointer if not found is fine here! */
2407 return POINTER_AS_UINT(BLI_ghash_lookup(filelist->selection_state, entry->uuid));
2408 }
2409
2410 return 0;
2411 }
2412
filelist_entry_select_index_get(FileList * filelist,const int index,FileCheckType check)2413 uint filelist_entry_select_index_get(FileList *filelist, const int index, FileCheckType check)
2414 {
2415 FileDirEntry *entry = filelist_file(filelist, index);
2416
2417 if (entry) {
2418 return filelist_entry_select_get(filelist, entry, check);
2419 }
2420
2421 return 0;
2422 }
2423
2424 /**
2425 * Set selection of the '..' parent entry, but only if it's actually visible.
2426 */
filelist_entry_parent_select_set(FileList * filelist,FileSelType select,uint flag,FileCheckType check)2427 void filelist_entry_parent_select_set(FileList *filelist,
2428 FileSelType select,
2429 uint flag,
2430 FileCheckType check)
2431 {
2432 if ((filelist->filter_data.flags & FLF_HIDE_PARENT) == 0) {
2433 filelist_entry_select_index_set(filelist, 0, select, flag, check);
2434 }
2435 }
2436
2437 /* WARNING! dir must be FILE_MAX_LIBEXTRA long! */
filelist_islibrary(struct FileList * filelist,char * dir,char ** r_group)2438 bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group)
2439 {
2440 return BLO_library_path_explode(filelist->filelist.root, dir, r_group, NULL);
2441 }
2442
groupname_to_code(const char * group)2443 static int groupname_to_code(const char *group)
2444 {
2445 char buf[BLO_GROUP_MAX];
2446 char *lslash;
2447
2448 BLI_assert(group);
2449
2450 BLI_strncpy(buf, group, sizeof(buf));
2451 lslash = (char *)BLI_path_slash_rfind(buf);
2452 if (lslash) {
2453 lslash[0] = '\0';
2454 }
2455
2456 return buf[0] ? BKE_idtype_idcode_from_name(buf) : 0;
2457 }
2458
groupname_to_filter_id(const char * group)2459 static uint64_t groupname_to_filter_id(const char *group)
2460 {
2461 int id_code = groupname_to_code(group);
2462
2463 return BKE_idtype_idcode_to_idfilter(id_code);
2464 }
2465
2466 /**
2467 * From here, we are in 'Job Context',
2468 * i.e. have to be careful about sharing stuff between background working thread.
2469 * and main one (used by UI among other things).
2470 */
2471 typedef struct TodoDir {
2472 int level;
2473 char *dir;
2474 } TodoDir;
2475
filelist_readjob_list_dir(const char * root,ListBase * entries,const char * filter_glob,const bool do_lib,const char * main_name,const bool skip_currpar)2476 static int filelist_readjob_list_dir(const char *root,
2477 ListBase *entries,
2478 const char *filter_glob,
2479 const bool do_lib,
2480 const char *main_name,
2481 const bool skip_currpar)
2482 {
2483 struct direntry *files;
2484 int nbr_files, nbr_entries = 0;
2485 /* Full path of the item. */
2486 char full_path[FILE_MAX];
2487
2488 nbr_files = BLI_filelist_dir_contents(root, &files);
2489 if (files) {
2490 int i = nbr_files;
2491 while (i--) {
2492 FileListInternEntry *entry;
2493
2494 if (skip_currpar && FILENAME_IS_CURRPAR(files[i].relname)) {
2495 continue;
2496 }
2497
2498 entry = MEM_callocN(sizeof(*entry), __func__);
2499 entry->relpath = MEM_dupallocN(files[i].relname);
2500 entry->st = files[i].s;
2501
2502 BLI_join_dirfile(full_path, FILE_MAX, root, entry->relpath);
2503 char *target = full_path;
2504
2505 /* Set initial file type and attributes. */
2506 entry->attributes = BLI_file_attributes(full_path);
2507 if (S_ISDIR(files[i].s.st_mode)
2508 #ifdef __APPLE__
2509 && !(ED_path_extension_type(full_path) & FILE_TYPE_APPLICATIONBUNDLE)
2510 #endif
2511 ) {
2512 entry->typeflag = FILE_TYPE_DIR;
2513 }
2514
2515 /* Is this a file that points to another file? */
2516 if (entry->attributes & FILE_ATTR_ALIAS) {
2517 entry->redirection_path = MEM_callocN(FILE_MAXDIR, __func__);
2518 if (BLI_file_alias_target(full_path, entry->redirection_path)) {
2519 if (BLI_is_dir(entry->redirection_path)) {
2520 entry->typeflag = FILE_TYPE_DIR;
2521 BLI_path_slash_ensure(entry->redirection_path);
2522 }
2523 else {
2524 entry->typeflag = ED_path_extension_type(entry->redirection_path);
2525 }
2526 target = entry->redirection_path;
2527 #ifdef WIN32
2528 /* On Windows don't show ".lnk" extension for valid shortcuts. */
2529 BLI_path_extension_replace(entry->relpath, FILE_MAXDIR, "");
2530 #endif
2531 }
2532 else {
2533 MEM_freeN(entry->redirection_path);
2534 entry->redirection_path = NULL;
2535 entry->attributes |= FILE_ATTR_HIDDEN;
2536 }
2537 }
2538
2539 if (!(entry->typeflag & FILE_TYPE_DIR)) {
2540 if (do_lib && BLO_has_bfile_extension(target)) {
2541 /* If we are considering .blend files as libs, promote them to directory status. */
2542 entry->typeflag = FILE_TYPE_BLENDER;
2543 /* prevent current file being used as acceptable dir */
2544 if (BLI_path_cmp(main_name, target) != 0) {
2545 entry->typeflag |= FILE_TYPE_DIR;
2546 }
2547 }
2548 else {
2549 entry->typeflag = ED_path_extension_type(target);
2550 if (filter_glob[0] && BLI_path_extension_check_glob(target, filter_glob)) {
2551 entry->typeflag |= FILE_TYPE_OPERATOR;
2552 }
2553 }
2554 }
2555
2556 #ifndef WIN32
2557 /* Set linux-style dot files hidden too. */
2558 if (is_hidden_dot_filename(entry->relpath, entry)) {
2559 entry->attributes |= FILE_ATTR_HIDDEN;
2560 }
2561 #endif
2562
2563 BLI_addtail(entries, entry);
2564 nbr_entries++;
2565 }
2566 BLI_filelist_free(files, nbr_files);
2567 }
2568 return nbr_entries;
2569 }
2570
filelist_readjob_list_lib(const char * root,ListBase * entries,const bool skip_currpar)2571 static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar)
2572 {
2573 FileListInternEntry *entry;
2574 LinkNode *ln, *names;
2575 int i, nnames, idcode = 0, nbr_entries = 0;
2576 char dir[FILE_MAX_LIBEXTRA], *group;
2577 bool ok;
2578
2579 struct BlendHandle *libfiledata = NULL;
2580
2581 /* name test */
2582 ok = BLO_library_path_explode(root, dir, &group, NULL);
2583 if (!ok) {
2584 return nbr_entries;
2585 }
2586
2587 /* there we go */
2588 libfiledata = BLO_blendhandle_from_file(dir, NULL);
2589 if (libfiledata == NULL) {
2590 return nbr_entries;
2591 }
2592
2593 /* memory for strings is passed into filelist[i].entry->relpath
2594 * and freed in filelist_entry_free. */
2595 if (group) {
2596 idcode = groupname_to_code(group);
2597 names = BLO_blendhandle_get_datablock_names(libfiledata, idcode, &nnames);
2598 }
2599 else {
2600 names = BLO_blendhandle_get_linkable_groups(libfiledata);
2601 nnames = BLI_linklist_count(names);
2602 }
2603
2604 BLO_blendhandle_close(libfiledata);
2605
2606 if (!skip_currpar) {
2607 entry = MEM_callocN(sizeof(*entry), __func__);
2608 entry->relpath = BLI_strdup(FILENAME_PARENT);
2609 entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR);
2610 BLI_addtail(entries, entry);
2611 nbr_entries++;
2612 }
2613
2614 for (i = 0, ln = names; i < nnames; i++, ln = ln->next) {
2615 const char *blockname = ln->link;
2616
2617 entry = MEM_callocN(sizeof(*entry), __func__);
2618 entry->relpath = BLI_strdup(blockname);
2619 entry->typeflag |= FILE_TYPE_BLENDERLIB;
2620 if (!(group && idcode)) {
2621 entry->typeflag |= FILE_TYPE_DIR;
2622 entry->blentype = groupname_to_code(blockname);
2623 }
2624 else {
2625 entry->blentype = idcode;
2626 }
2627 BLI_addtail(entries, entry);
2628 nbr_entries++;
2629 }
2630
2631 BLI_linklist_free(names, free);
2632
2633 return nbr_entries;
2634 }
2635
2636 #if 0
2637 /* Kept for reference here, in case we want to add back that feature later.
2638 * We do not need it currently. */
2639 /* Code ***NOT*** updated for job stuff! */
2640 static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
2641 {
2642 ID *id;
2643 FileDirEntry *files, *firstlib = NULL;
2644 ListBase *lb;
2645 int a, fake, idcode, ok, totlib, totbl;
2646
2647 // filelist->type = FILE_MAIN; /* XXX TODO: add modes to filebrowser */
2648
2649 BLI_assert(filelist->filelist.entries == NULL);
2650
2651 if (filelist->filelist.root[0] == '/') {
2652 filelist->filelist.root[0] = '\0';
2653 }
2654
2655 if (filelist->filelist.root[0]) {
2656 idcode = groupname_to_code(filelist->filelist.root);
2657 if (idcode == 0) {
2658 filelist->filelist.root[0] = '\0';
2659 }
2660 }
2661
2662 if (filelist->dir[0] == 0) {
2663 /* make directories */
2664 # ifdef WITH_FREESTYLE
2665 filelist->filelist.nbr_entries = 27;
2666 # else
2667 filelist->filelist.nbr_entries = 26;
2668 # endif
2669 filelist_resize(filelist, filelist->filelist.nbr_entries);
2670
2671 for (a = 0; a < filelist->filelist.nbr_entries; a++) {
2672 filelist->filelist.entries[a].typeflag |= FILE_TYPE_DIR;
2673 }
2674
2675 filelist->filelist.entries[0].entry->relpath = BLI_strdup(FILENAME_PARENT);
2676 filelist->filelist.entries[1].entry->relpath = BLI_strdup("Scene");
2677 filelist->filelist.entries[2].entry->relpath = BLI_strdup("Object");
2678 filelist->filelist.entries[3].entry->relpath = BLI_strdup("Mesh");
2679 filelist->filelist.entries[4].entry->relpath = BLI_strdup("Curve");
2680 filelist->filelist.entries[5].entry->relpath = BLI_strdup("Metaball");
2681 filelist->filelist.entries[6].entry->relpath = BLI_strdup("Material");
2682 filelist->filelist.entries[7].entry->relpath = BLI_strdup("Texture");
2683 filelist->filelist.entries[8].entry->relpath = BLI_strdup("Image");
2684 filelist->filelist.entries[9].entry->relpath = BLI_strdup("Ika");
2685 filelist->filelist.entries[10].entry->relpath = BLI_strdup("Wave");
2686 filelist->filelist.entries[11].entry->relpath = BLI_strdup("Lattice");
2687 filelist->filelist.entries[12].entry->relpath = BLI_strdup("Light");
2688 filelist->filelist.entries[13].entry->relpath = BLI_strdup("Camera");
2689 filelist->filelist.entries[14].entry->relpath = BLI_strdup("Ipo");
2690 filelist->filelist.entries[15].entry->relpath = BLI_strdup("World");
2691 filelist->filelist.entries[16].entry->relpath = BLI_strdup("Screen");
2692 filelist->filelist.entries[17].entry->relpath = BLI_strdup("VFont");
2693 filelist->filelist.entries[18].entry->relpath = BLI_strdup("Text");
2694 filelist->filelist.entries[19].entry->relpath = BLI_strdup("Armature");
2695 filelist->filelist.entries[20].entry->relpath = BLI_strdup("Action");
2696 filelist->filelist.entries[21].entry->relpath = BLI_strdup("NodeTree");
2697 filelist->filelist.entries[22].entry->relpath = BLI_strdup("Speaker");
2698 filelist->filelist.entries[23].entry->relpath = BLI_strdup("Hair");
2699 filelist->filelist.entries[24].entry->relpath = BLI_strdup("Point Cloud");
2700 filelist->filelist.entries[25].entry->relpath = BLI_strdup("Volume");
2701 # ifdef WITH_FREESTYLE
2702 filelist->filelist.entries[26].entry->relpath = BLI_strdup("FreestyleLineStyle");
2703 # endif
2704 }
2705 else {
2706 /* make files */
2707 idcode = groupname_to_code(filelist->filelist.root);
2708
2709 lb = which_libbase(bmain, idcode);
2710 if (lb == NULL) {
2711 return;
2712 }
2713
2714 filelist->filelist.nbr_entries = 0;
2715 for (id = lb->first; id; id = id->next) {
2716 if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') {
2717 filelist->filelist.nbr_entries++;
2718 }
2719 }
2720
2721 /* XXX TODO: if databrowse F4 or append/link
2722 * filelist->flags & FLF_HIDE_PARENT has to be set */
2723 if (!(filelist->filter_data.flags & FLF_HIDE_PARENT)) {
2724 filelist->filelist.nbr_entries++;
2725 }
2726
2727 if (filelist->filelist.nbr_entries > 0) {
2728 filelist_resize(filelist, filelist->filelist.nbr_entries);
2729 }
2730
2731 files = filelist->filelist.entries;
2732
2733 if (!(filelist->filter_data.flags & FLF_HIDE_PARENT)) {
2734 files->entry->relpath = BLI_strdup(FILENAME_PARENT);
2735 files->typeflag |= FILE_TYPE_DIR;
2736
2737 files++;
2738 }
2739
2740 totlib = totbl = 0;
2741 for (id = lb->first; id; id = id->next) {
2742 ok = 1;
2743 if (ok) {
2744 if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') {
2745 if (id->lib == NULL) {
2746 files->entry->relpath = BLI_strdup(id->name + 2);
2747 }
2748 else {
2749 char relname[FILE_MAX + (MAX_ID_NAME - 2) + 3];
2750 BLI_snprintf(relname, sizeof(relname), "%s | %s", id->lib->filepath, id->name + 2);
2751 files->entry->relpath = BLI_strdup(relname);
2752 }
2753 // files->type |= S_IFREG;
2754 # if 0 /* XXX TODO show the selection status of the objects */
2755 if (!filelist->has_func) { /* F4 DATA BROWSE */
2756 if (idcode == ID_OB) {
2757 if ( ((Object *)id)->flag & SELECT) {
2758 files->entry->selflag |= FILE_SEL_SELECTED;
2759 }
2760 }
2761 else if (idcode == ID_SCE) {
2762 if ( ((Scene *)id)->r.scemode & R_BG_RENDER) {
2763 files->entry->selflag |= FILE_SEL_SELECTED;
2764 }
2765 }
2766 }
2767 # endif
2768 // files->entry->nr = totbl + 1;
2769 files->entry->poin = id;
2770 fake = id->flag & LIB_FAKEUSER;
2771 if (idcode == ID_MA || idcode == ID_TE || idcode == ID_LA || idcode == ID_WO ||
2772 idcode == ID_IM) {
2773 files->typeflag |= FILE_TYPE_IMAGE;
2774 }
2775 # if 0
2776 if (id->lib && fake) {
2777 BLI_snprintf(files->extra, sizeof(files->entry->extra), "LF %d", id->us);
2778 }
2779 else if (id->lib) {
2780 BLI_snprintf(files->extra, sizeof(files->entry->extra), "L %d", id->us);
2781 }
2782 else if (fake) {
2783 BLI_snprintf(files->extra, sizeof(files->entry->extra), "F %d", id->us);
2784 }
2785 else {
2786 BLI_snprintf(files->extra, sizeof(files->entry->extra), " %d", id->us);
2787 }
2788 # endif
2789
2790 if (id->lib) {
2791 if (totlib == 0) {
2792 firstlib = files;
2793 }
2794 totlib++;
2795 }
2796
2797 files++;
2798 }
2799 totbl++;
2800 }
2801 }
2802
2803 /* only qsort of library blocks */
2804 if (totlib > 1) {
2805 qsort(firstlib, totlib, sizeof(*files), compare_name);
2806 }
2807 }
2808 }
2809 #endif
2810
filelist_readjob_do(const bool do_lib,FileList * filelist,const char * main_name,const short * stop,short * do_update,float * progress,ThreadMutex * lock)2811 static void filelist_readjob_do(const bool do_lib,
2812 FileList *filelist,
2813 const char *main_name,
2814 const short *stop,
2815 short *do_update,
2816 float *progress,
2817 ThreadMutex *lock)
2818 {
2819 ListBase entries = {0};
2820 BLI_Stack *todo_dirs;
2821 TodoDir *td_dir;
2822 char dir[FILE_MAX_LIBEXTRA];
2823 char filter_glob[FILE_MAXFILE];
2824 const char *root = filelist->filelist.root;
2825 const int max_recursion = filelist->max_recursion;
2826 int nbr_done_dirs = 0, nbr_todo_dirs = 1;
2827
2828 // BLI_assert(filelist->filtered == NULL);
2829 BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
2830 (filelist->filelist.nbr_entries == 0));
2831
2832 todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__);
2833 td_dir = BLI_stack_push_r(todo_dirs);
2834 td_dir->level = 1;
2835
2836 BLI_strncpy(dir, filelist->filelist.root, sizeof(dir));
2837 BLI_strncpy(filter_glob, filelist->filter_data.filter_glob, sizeof(filter_glob));
2838
2839 BLI_path_normalize_dir(main_name, dir);
2840 td_dir->dir = BLI_strdup(dir);
2841
2842 while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) {
2843 FileListInternEntry *entry;
2844 int nbr_entries = 0;
2845 bool is_lib = do_lib;
2846
2847 char *subdir;
2848 char rel_subdir[FILE_MAX_LIBEXTRA];
2849 int recursion_level;
2850 bool skip_currpar;
2851
2852 td_dir = BLI_stack_peek(todo_dirs);
2853 subdir = td_dir->dir;
2854 recursion_level = td_dir->level;
2855 skip_currpar = (recursion_level > 1);
2856
2857 BLI_stack_discard(todo_dirs);
2858
2859 /* ARRRG! We have to be very careful *not to use* common BLI_path_util helpers over
2860 * entry->relpath itself (nor any path containing it), since it may actually be a datablock
2861 * name inside .blend file, which can have slashes and backslashes! See T46827.
2862 * Note that in the end, this means we 'cache' valid relative subdir once here,
2863 * this is actually better. */
2864 BLI_strncpy(rel_subdir, subdir, sizeof(rel_subdir));
2865 BLI_path_normalize_dir(root, rel_subdir);
2866 BLI_path_rel(rel_subdir, root);
2867
2868 if (do_lib) {
2869 nbr_entries = filelist_readjob_list_lib(subdir, &entries, skip_currpar);
2870 }
2871 if (!nbr_entries) {
2872 is_lib = false;
2873 nbr_entries = filelist_readjob_list_dir(
2874 subdir, &entries, filter_glob, do_lib, main_name, skip_currpar);
2875 }
2876
2877 for (entry = entries.first; entry; entry = entry->next) {
2878 BLI_join_dirfile(dir, sizeof(dir), rel_subdir, entry->relpath);
2879
2880 /* Generate our entry uuid. Abusing uuid as an uint32, shall be more than enough here,
2881 * things would crash way before we overflow that counter!
2882 * Using an atomic operation to avoid having to lock thread...
2883 * Note that we do not really need this here currently,
2884 * since there is a single listing thread, but better
2885 * remain consistent about threading! */
2886 *((uint32_t *)entry->uuid) = atomic_add_and_fetch_uint32(
2887 (uint32_t *)filelist->filelist_intern.curr_uuid, 1);
2888
2889 /* Only thing we change in direntry here, so we need to free it first. */
2890 MEM_freeN(entry->relpath);
2891 entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//'
2892 * added by BLI_path_rel to rel_subdir. */
2893 entry->name = BLI_strdup(fileentry_uiname(root, entry->relpath, entry->typeflag, dir));
2894
2895 /* Here we decide whether current filedirentry is to be listed too, or not. */
2896 if (max_recursion && (is_lib || (recursion_level <= max_recursion))) {
2897 if (((entry->typeflag & FILE_TYPE_DIR) == 0) || FILENAME_IS_CURRPAR(entry->relpath)) {
2898 /* Skip... */
2899 }
2900 else if (!is_lib && (recursion_level >= max_recursion) &&
2901 ((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0)) {
2902 /* Do not recurse in real directories in this case, only in .blend libs. */
2903 }
2904 else {
2905 /* We have a directory we want to list, add it to todo list! */
2906 BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath);
2907 BLI_path_normalize_dir(main_name, dir);
2908 td_dir = BLI_stack_push_r(todo_dirs);
2909 td_dir->level = recursion_level + 1;
2910 td_dir->dir = BLI_strdup(dir);
2911 nbr_todo_dirs++;
2912 }
2913 }
2914 }
2915
2916 if (nbr_entries) {
2917 BLI_mutex_lock(lock);
2918
2919 *do_update = true;
2920
2921 BLI_movelisttolist(&filelist->filelist.entries, &entries);
2922 filelist->filelist.nbr_entries += nbr_entries;
2923
2924 BLI_mutex_unlock(lock);
2925 }
2926
2927 nbr_done_dirs++;
2928 *progress = (float)nbr_done_dirs / (float)nbr_todo_dirs;
2929 MEM_freeN(subdir);
2930 }
2931
2932 /* If we were interrupted by stop, stack may not be empty and we need to free
2933 * pending dir paths. */
2934 while (!BLI_stack_is_empty(todo_dirs)) {
2935 td_dir = BLI_stack_peek(todo_dirs);
2936 MEM_freeN(td_dir->dir);
2937 BLI_stack_discard(todo_dirs);
2938 }
2939 BLI_stack_free(todo_dirs);
2940 }
2941
filelist_readjob_dir(FileList * filelist,const char * main_name,short * stop,short * do_update,float * progress,ThreadMutex * lock)2942 static void filelist_readjob_dir(FileList *filelist,
2943 const char *main_name,
2944 short *stop,
2945 short *do_update,
2946 float *progress,
2947 ThreadMutex *lock)
2948 {
2949 filelist_readjob_do(false, filelist, main_name, stop, do_update, progress, lock);
2950 }
2951
filelist_readjob_lib(FileList * filelist,const char * main_name,short * stop,short * do_update,float * progress,ThreadMutex * lock)2952 static void filelist_readjob_lib(FileList *filelist,
2953 const char *main_name,
2954 short *stop,
2955 short *do_update,
2956 float *progress,
2957 ThreadMutex *lock)
2958 {
2959 filelist_readjob_do(true, filelist, main_name, stop, do_update, progress, lock);
2960 }
2961
filelist_readjob_main(FileList * filelist,const char * main_name,short * stop,short * do_update,float * progress,ThreadMutex * lock)2962 static void filelist_readjob_main(FileList *filelist,
2963 const char *main_name,
2964 short *stop,
2965 short *do_update,
2966 float *progress,
2967 ThreadMutex *lock)
2968 {
2969 /* TODO! */
2970 filelist_readjob_dir(filelist, main_name, stop, do_update, progress, lock);
2971 }
2972
2973 typedef struct FileListReadJob {
2974 ThreadMutex lock;
2975 char main_name[FILE_MAX];
2976 struct FileList *filelist;
2977 /** XXX We may use a simpler struct here... just a linked list and root path? */
2978 struct FileList *tmp_filelist;
2979 } FileListReadJob;
2980
filelist_readjob_startjob(void * flrjv,short * stop,short * do_update,float * progress)2981 static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update, float *progress)
2982 {
2983 FileListReadJob *flrj = flrjv;
2984
2985 // printf("START filelist reading (%d files, main thread: %d)\n",
2986 // flrj->filelist->filelist.nbr_entries, BLI_thread_is_main());
2987
2988 BLI_mutex_lock(&flrj->lock);
2989
2990 BLI_assert((flrj->tmp_filelist == NULL) && flrj->filelist);
2991
2992 flrj->tmp_filelist = MEM_dupallocN(flrj->filelist);
2993
2994 BLI_listbase_clear(&flrj->tmp_filelist->filelist.entries);
2995 flrj->tmp_filelist->filelist.nbr_entries = 0;
2996
2997 flrj->tmp_filelist->filelist_intern.filtered = NULL;
2998 BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries);
2999 memset(flrj->tmp_filelist->filelist_intern.curr_uuid,
3000 0,
3001 sizeof(flrj->tmp_filelist->filelist_intern.curr_uuid));
3002
3003 flrj->tmp_filelist->libfiledata = NULL;
3004 memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache));
3005 flrj->tmp_filelist->selection_state = NULL;
3006
3007 BLI_mutex_unlock(&flrj->lock);
3008
3009 flrj->tmp_filelist->read_jobf(
3010 flrj->tmp_filelist, flrj->main_name, stop, do_update, progress, &flrj->lock);
3011 }
3012
filelist_readjob_update(void * flrjv)3013 static void filelist_readjob_update(void *flrjv)
3014 {
3015 FileListReadJob *flrj = flrjv;
3016 FileListIntern *fl_intern = &flrj->filelist->filelist_intern;
3017 ListBase new_entries = {NULL};
3018 int nbr_entries, new_nbr_entries = 0;
3019
3020 BLI_movelisttolist(&new_entries, &fl_intern->entries);
3021 nbr_entries = flrj->filelist->filelist.nbr_entries;
3022
3023 BLI_mutex_lock(&flrj->lock);
3024
3025 if (flrj->tmp_filelist->filelist.nbr_entries) {
3026 /* We just move everything out of 'thread context' into final list. */
3027 new_nbr_entries = flrj->tmp_filelist->filelist.nbr_entries;
3028 BLI_movelisttolist(&new_entries, &flrj->tmp_filelist->filelist.entries);
3029 flrj->tmp_filelist->filelist.nbr_entries = 0;
3030 }
3031
3032 BLI_mutex_unlock(&flrj->lock);
3033
3034 if (new_nbr_entries) {
3035 /* Do not clear selection cache, we can assume already 'selected' uuids are still valid! */
3036 filelist_clear_ex(flrj->filelist, true, false);
3037
3038 flrj->filelist->flags |= (FL_NEED_SORTING | FL_NEED_FILTERING);
3039 }
3040
3041 /* if no new_nbr_entries, this is NOP */
3042 BLI_movelisttolist(&fl_intern->entries, &new_entries);
3043 flrj->filelist->filelist.nbr_entries = nbr_entries + new_nbr_entries;
3044 }
3045
filelist_readjob_endjob(void * flrjv)3046 static void filelist_readjob_endjob(void *flrjv)
3047 {
3048 FileListReadJob *flrj = flrjv;
3049
3050 /* In case there would be some dangling update... */
3051 filelist_readjob_update(flrjv);
3052
3053 flrj->filelist->flags &= ~FL_IS_PENDING;
3054 flrj->filelist->flags |= FL_IS_READY;
3055 }
3056
filelist_readjob_free(void * flrjv)3057 static void filelist_readjob_free(void *flrjv)
3058 {
3059 FileListReadJob *flrj = flrjv;
3060
3061 // printf("END filelist reading (%d files)\n", flrj->filelist->filelist.nbr_entries);
3062
3063 if (flrj->tmp_filelist) {
3064 /* tmp_filelist shall never ever be filtered! */
3065 BLI_assert(flrj->tmp_filelist->filelist.nbr_entries == 0);
3066 BLI_assert(BLI_listbase_is_empty(&flrj->tmp_filelist->filelist.entries));
3067
3068 filelist_freelib(flrj->tmp_filelist);
3069 filelist_free(flrj->tmp_filelist);
3070 MEM_freeN(flrj->tmp_filelist);
3071 }
3072
3073 BLI_mutex_end(&flrj->lock);
3074
3075 MEM_freeN(flrj);
3076 }
3077
filelist_readjob_start(FileList * filelist,const bContext * C)3078 void filelist_readjob_start(FileList *filelist, const bContext *C)
3079 {
3080 Main *bmain = CTX_data_main(C);
3081 wmJob *wm_job;
3082 FileListReadJob *flrj;
3083
3084 /* prepare job data */
3085 flrj = MEM_callocN(sizeof(*flrj), __func__);
3086 flrj->filelist = filelist;
3087 BLI_strncpy(flrj->main_name, BKE_main_blendfile_path(bmain), sizeof(flrj->main_name));
3088
3089 filelist->flags &= ~(FL_FORCE_RESET | FL_IS_READY);
3090 filelist->flags |= FL_IS_PENDING;
3091
3092 BLI_mutex_init(&flrj->lock);
3093
3094 /* setup job */
3095 wm_job = WM_jobs_get(CTX_wm_manager(C),
3096 CTX_wm_window(C),
3097 CTX_data_scene(C),
3098 "Listing Dirs...",
3099 WM_JOB_PROGRESS,
3100 WM_JOB_TYPE_FILESEL_READDIR);
3101 WM_jobs_customdata_set(wm_job, flrj, filelist_readjob_free);
3102 WM_jobs_timer(wm_job, 0.01, NC_SPACE | ND_SPACE_FILE_LIST, NC_SPACE | ND_SPACE_FILE_LIST);
3103 WM_jobs_callbacks(
3104 wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob);
3105
3106 /* start the job */
3107 WM_jobs_start(CTX_wm_manager(C), wm_job);
3108 }
3109
filelist_readjob_stop(wmWindowManager * wm,Scene * owner_scene)3110 void filelist_readjob_stop(wmWindowManager *wm, Scene *owner_scene)
3111 {
3112 WM_jobs_kill_type(wm, owner_scene, WM_JOB_TYPE_FILESEL_READDIR);
3113 }
3114
filelist_readjob_running(wmWindowManager * wm,Scene * owner_scene)3115 int filelist_readjob_running(wmWindowManager *wm, Scene *owner_scene)
3116 {
3117 return WM_jobs_test(wm, owner_scene, WM_JOB_TYPE_FILESEL_READDIR);
3118 }
3119