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