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