abfa1ed5666b9f23420f591ddcbd5cb812ff5d69
[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
39 #ifndef WIN32
40 #include <unistd.h>
41 #else
42 #include <io.h>
43 #include <direct.h>
44 #endif   
45 #include "MEM_guardedalloc.h"
46
47 #include "BLI_blenlib.h"
48 #include "BLI_fileops_types.h"
49 #include "BLI_linklist.h"
50 #include "BLI_utildefines.h"
51
52 #ifdef WIN32
53 #  include "BLI_winstuff.h"
54 #endif
55
56 #include "BKE_context.h"
57 #include "BKE_global.h"
58 #include "BKE_library.h"
59 #include "BKE_icons.h"
60 #include "BKE_main.h"
61 #include "BKE_report.h"
62 #include "BLO_readfile.h"
63 #include "BKE_idcode.h"
64
65 #include "DNA_space_types.h"
66
67 #include "ED_datafiles.h"
68 #include "ED_fileselect.h"
69
70 #include "IMB_imbuf.h"
71 #include "IMB_imbuf_types.h"
72 #include "IMB_thumbs.h"
73
74 #include "PIL_time.h"
75
76 #include "WM_api.h"
77 #include "WM_types.h"
78
79 #include "UI_resources.h"
80
81 #include "filelist.h"
82
83
84 /* ----------------- FOLDERLIST (previous/next) -------------- */
85
86 typedef struct FolderList {
87         struct FolderList *next, *prev;
88         char *foldername;
89 } FolderList;
90
91 ListBase *folderlist_new(void)
92 {
93         ListBase *p = MEM_callocN(sizeof(ListBase), "folderlist");
94         return p;
95 }
96
97 void folderlist_popdir(struct ListBase *folderlist, char *dir)
98 {
99         const char *prev_dir;
100         struct FolderList *folder;
101         folder = folderlist->last;
102
103         if (folder) {
104                 /* remove the current directory */
105                 MEM_freeN(folder->foldername);
106                 BLI_freelinkN(folderlist, folder);
107
108                 folder = folderlist->last;
109                 if (folder) {
110                         prev_dir = folder->foldername;
111                         BLI_strncpy(dir, prev_dir, FILE_MAXDIR);
112                 }
113         }
114         /* delete the folder next or use setdir directly before PREVIOUS OP */
115 }
116
117 void folderlist_pushdir(ListBase *folderlist, const char *dir)
118 {
119         struct FolderList *folder, *previous_folder;
120         previous_folder = folderlist->last;
121
122         /* check if already exists */
123         if (previous_folder && previous_folder->foldername) {
124                 if (BLI_path_cmp(previous_folder->foldername, dir) == 0) {
125                         return;
126                 }
127         }
128
129         /* create next folder element */
130         folder = (FolderList *)MEM_mallocN(sizeof(FolderList), "FolderList");
131         folder->foldername = BLI_strdup(dir);
132
133         /* add it to the end of the list */
134         BLI_addtail(folderlist, folder);
135 }
136
137 const char *folderlist_peeklastdir(ListBase *folderlist)
138 {
139         struct FolderList *folder;
140
141         if (!folderlist->last)
142                 return NULL;
143
144         folder = folderlist->last;
145         return folder->foldername;
146 }
147
148 int folderlist_clear_next(struct SpaceFile *sfile)
149 {
150         struct FolderList *folder;
151
152         /* if there is no folder_next there is nothing we can clear */
153         if (!sfile->folders_next)
154                 return 0;
155
156         /* if previous_folder, next_folder or refresh_folder operators are executed it doesn't clear folder_next */
157         folder = sfile->folders_prev->last;
158         if ((!folder) || (BLI_path_cmp(folder->foldername, sfile->params->dir) == 0))
159                 return 0;
160
161         /* eventually clear flist->folders_next */
162         return 1;
163 }
164
165 /* not listbase itself */
166 void folderlist_free(ListBase *folderlist)
167 {
168         if (folderlist) {
169                 FolderList *folder;
170                 for (folder = folderlist->first; folder; folder = folder->next)
171                         MEM_freeN(folder->foldername);
172                 BLI_freelistN(folderlist);
173         }
174 }
175
176 ListBase *folderlist_duplicate(ListBase *folderlist)
177 {
178         
179         if (folderlist) {
180                 ListBase *folderlistn = MEM_callocN(sizeof(ListBase), "copy folderlist");
181                 FolderList *folder;
182                 
183                 BLI_duplicatelist(folderlistn, folderlist);
184                 
185                 for (folder = folderlistn->first; folder; folder = folder->next) {
186                         folder->foldername = MEM_dupallocN(folder->foldername);
187                 }
188                 return folderlistn;
189         }
190         return NULL;
191 }
192
193
194 /* ------------------FILELIST------------------------ */
195
196 struct FileList;
197
198 typedef struct FileImage {
199         struct FileImage *next, *prev;
200         char path[FILE_MAX];
201         unsigned int flags;
202         int index;
203         short done;
204         ImBuf *img;
205 } FileImage;
206
207 typedef struct FileListFilter {
208         bool hide_dot;
209         bool hide_parent;
210         unsigned int filter;
211         char filter_glob[64];
212 } FileListFilter;
213
214 typedef struct FileList {
215         struct direntry *filelist;
216         int numfiles;
217         char dir[FILE_MAX];
218         short prv_w;
219         short prv_h;
220
221         bool changed;
222
223         short sort;
224         bool need_sorting;
225
226         FileListFilter filter_data;
227         int *fidx;  /* Also used to detect when we need to filter! */
228         int numfiltered;
229
230         bool need_thumbnails;
231
232         struct BlendHandle *libfiledata;
233
234         void (*readf)(struct FileList *);
235         bool (*filterf)(struct direntry *, const char *, FileListFilter *);
236 } FileList;
237
238 #define FILENAME_IS_BREADCRUMBS(_n) \
239         (((_n)[0] == '.' && (_n)[1] == '\0') || ((_n)[0] == '.' && (_n)[1] == '.' && (_n)[2] == '\0'))
240
241 #define SPECIAL_IMG_SIZE 48
242 #define SPECIAL_IMG_ROWS 4
243 #define SPECIAL_IMG_COLS 4
244
245 #define SPECIAL_IMG_FOLDER 0
246 #define SPECIAL_IMG_PARENT 1
247 #define SPECIAL_IMG_REFRESH 2
248 #define SPECIAL_IMG_BLENDFILE 3
249 #define SPECIAL_IMG_SOUNDFILE 4
250 #define SPECIAL_IMG_MOVIEFILE 5
251 #define SPECIAL_IMG_PYTHONFILE 6
252 #define SPECIAL_IMG_TEXTFILE 7
253 #define SPECIAL_IMG_FONTFILE 8
254 #define SPECIAL_IMG_UNKNOWNFILE 9
255 #define SPECIAL_IMG_LOADING 10
256 #define SPECIAL_IMG_BACKUP 11
257 #define SPECIAL_IMG_MAX SPECIAL_IMG_BACKUP + 1
258
259 static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
260
261
262 static void filelist_from_main(struct FileList *filelist);
263 static void filelist_from_library(struct FileList *filelist);
264
265 static void filelist_read_main(struct FileList *filelist);
266 static void filelist_read_library(struct FileList *filelist);
267 static void filelist_read_dir(struct FileList *filelist);
268
269 static void filelist_filter_clear(FileList *filelist);
270
271 /* ********** Sort helpers ********** */
272
273 static bool compare_is_directory(const struct direntry *entry)
274 {
275         /* for library browse .blend files may be treated as directories, but
276          * for sorting purposes they should be considered regular files */
277         if (S_ISDIR(entry->type))
278                 return !(entry->flags & (BLENDERFILE | BLENDERFILE_BACKUP));
279         
280         return false;
281 }
282
283 static int compare_name(const void *a1, const void *a2)
284 {
285         const struct direntry *entry1 = a1, *entry2 = a2;
286
287         /* type is equal to stat.st_mode */
288
289         if (compare_is_directory(entry1)) {
290                 if (compare_is_directory(entry2) == 0) return (-1);
291         }
292         else {
293                 if (compare_is_directory(entry2)) return (1);
294         }
295         if (S_ISREG(entry1->type)) {
296                 if (S_ISREG(entry2->type) == 0) return (-1);
297         }
298         else {
299                 if (S_ISREG(entry2->type)) return (1);
300         }
301         if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return (-1);
302         if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return (1);
303         
304         /* make sure "." and ".." are always first */
305         if (strcmp(entry1->relname, ".") == 0) return (-1);
306         if (strcmp(entry2->relname, ".") == 0) return (1);
307         if (strcmp(entry1->relname, "..") == 0) return (-1);
308         if (strcmp(entry2->relname, "..") == 0) return (1);
309         
310         return (BLI_natstrcmp(entry1->relname, entry2->relname));
311 }
312
313 static int compare_date(const void *a1, const void *a2) 
314 {
315         const struct direntry *entry1 = a1, *entry2 = a2;
316         
317         /* type is equal to stat.st_mode */
318
319         if (compare_is_directory(entry1)) {
320                 if (compare_is_directory(entry2) == 0) return (-1);
321         }
322         else {
323                 if (compare_is_directory(entry2)) return (1);
324         }
325         if (S_ISREG(entry1->type)) {
326                 if (S_ISREG(entry2->type) == 0) return (-1);
327         }
328         else {
329                 if (S_ISREG(entry2->type)) return (1);
330         }
331         if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return (-1);
332         if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return (1);
333
334         /* make sure "." and ".." are always first */
335         if (strcmp(entry1->relname, ".") == 0) return (-1);
336         if (strcmp(entry2->relname, ".") == 0) return (1);
337         if (strcmp(entry1->relname, "..") == 0) return (-1);
338         if (strcmp(entry2->relname, "..") == 0) return (1);
339         
340         if (entry1->s.st_mtime < entry2->s.st_mtime) return 1;
341         if (entry1->s.st_mtime > entry2->s.st_mtime) return -1;
342         
343         else return BLI_natstrcmp(entry1->relname, entry2->relname);
344 }
345
346 static int compare_size(const void *a1, const void *a2) 
347 {
348         const struct direntry *entry1 = a1, *entry2 = a2;
349
350         /* type is equal to stat.st_mode */
351
352         if (compare_is_directory(entry1)) {
353                 if (compare_is_directory(entry2) == 0) return (-1);
354         }
355         else {
356                 if (compare_is_directory(entry2)) return (1);
357         }
358         if (S_ISREG(entry1->type)) {
359                 if (S_ISREG(entry2->type) == 0) return (-1);
360         }
361         else {
362                 if (S_ISREG(entry2->type)) return (1);
363         }
364         if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return (-1);
365         if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return (1);
366
367         /* make sure "." and ".." are always first */
368         if (strcmp(entry1->relname, ".") == 0) return (-1);
369         if (strcmp(entry2->relname, ".") == 0) return (1);
370         if (strcmp(entry1->relname, "..") == 0) return (-1);
371         if (strcmp(entry2->relname, "..") == 0) return (1);
372         
373         if (entry1->s.st_size < entry2->s.st_size) return 1;
374         if (entry1->s.st_size > entry2->s.st_size) return -1;
375         else return BLI_natstrcmp(entry1->relname, entry2->relname);
376 }
377
378 static int compare_extension(const void *a1, const void *a2)
379 {
380         const struct direntry *entry1 = a1, *entry2 = a2;
381         const char *sufix1, *sufix2;
382         const char *nil = "";
383
384         if (!(sufix1 = strstr(entry1->relname, ".blend.gz")))
385                 sufix1 = strrchr(entry1->relname, '.');
386         if (!(sufix2 = strstr(entry2->relname, ".blend.gz")))
387                 sufix2 = strrchr(entry2->relname, '.');
388         if (!sufix1) sufix1 = nil;
389         if (!sufix2) sufix2 = nil;
390
391         /* type is equal to stat.st_mode */
392
393         if (compare_is_directory(entry1)) {
394                 if (compare_is_directory(entry2) == 0) return (-1);
395         }
396         else {
397                 if (compare_is_directory(entry2)) return (1);
398         }
399         if (S_ISREG(entry1->type)) {
400                 if (S_ISREG(entry2->type) == 0) return (-1);
401         }
402         else {
403                 if (S_ISREG(entry2->type)) return (1);
404         }
405         if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return (-1);
406         if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return (1);
407         
408         /* make sure "." and ".." are always first */
409         if (strcmp(entry1->relname, ".") == 0) return (-1);
410         if (strcmp(entry2->relname, ".") == 0) return (1);
411         if (strcmp(entry1->relname, "..") == 0) return (-1);
412         if (strcmp(entry2->relname, "..") == 0) return (1);
413         
414         return (BLI_strcasecmp(sufix1, sufix2));
415 }
416
417 bool filelist_need_sorting(struct FileList *filelist)
418 {
419         return filelist->need_sorting && (filelist->sort != FILE_SORT_NONE);
420 }
421
422 void filelist_sort(struct FileList *filelist)
423 {
424         if (filelist_need_sorting(filelist)) {
425                 filelist->need_sorting = false;
426
427                 switch (filelist->sort) {
428                         case FILE_SORT_ALPHA:
429                                 qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_name);
430                                 break;
431                         case FILE_SORT_TIME:
432                                 qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_date);
433                                 break;
434                         case FILE_SORT_SIZE:
435                                 qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_size);
436                                 break;
437                         case FILE_SORT_EXTENSION:
438                                 qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_extension);
439                                 break;
440                         case FILE_SORT_NONE:  /* Should never reach this point! */
441                         default:
442                                 BLI_assert(0);
443                                 return;
444                 }
445
446                 filelist_filter_clear(filelist);
447         }
448 }
449
450 void filelist_setsorting(struct FileList *filelist, const short sort)
451 {
452         if (filelist->sort != sort) {
453                 filelist->sort = sort;
454                 filelist->need_sorting = true;
455         }
456 }
457
458 /* ********** Filter helpers ********** */
459
460 static bool is_hidden_file(const char *filename, FileListFilter *filter)
461 {
462         bool is_hidden = false;
463
464         if (filter->hide_dot) {
465                 if (filename[0] == '.' && filename[1] != '.' && filename[1] != '\0') {
466                         is_hidden = true; /* ignore .file */
467                 }
468                 else {
469                         int len = strlen(filename);
470                         if ((len > 0) && (filename[len - 1] == '~')) {
471                                 is_hidden = true;  /* ignore file~ */
472                         }
473                 }
474         }
475         if (!is_hidden && filter->hide_parent) {
476                 if (filename[0] == '.' && filename[1] == '.' && filename[2] == '\0') {
477                         is_hidden = true; /* ignore .. */
478                 }
479         }
480         if (!is_hidden && ((filename[0] == '.') && (filename[1] == '\0'))) {
481                 is_hidden = true; /* ignore . */
482         }
483         return is_hidden;
484 }
485
486 static bool is_filtered_file(struct direntry *file, const char *UNUSED(root), FileListFilter *filter)
487 {
488         bool is_filtered = !is_hidden_file(file->relname, filter);
489
490         if (is_filtered && filter->filter && !FILENAME_IS_BREADCRUMBS(file->relname)) {
491                 if ((file->type & S_IFDIR) && !(filter->filter & FOLDERFILE)) {
492                         is_filtered = false;
493                 }
494                 if (!(file->type & S_IFDIR) && !(file->flags & filter->filter)) {
495                         is_filtered = false;
496                 }
497         }
498
499         return is_filtered;
500 }
501
502 static bool is_filtered_lib(struct direntry *file, const char *root, FileListFilter *filter)
503 {
504         bool is_filtered = !is_hidden_file(file->relname, filter);
505         char dir[FILE_MAXDIR], group[BLO_GROUP_MAX];
506
507         if (BLO_is_a_library(root, dir, group)) {
508                 is_filtered = !is_hidden_file(file->relname, filter);
509         }
510         else {
511                 is_filtered = is_filtered_file(file, root, filter);
512         }
513
514         return is_filtered;
515 }
516
517 static bool is_filtered_main(struct direntry *file, const char *UNUSED(dir), FileListFilter *filter)
518 {
519         return !is_hidden_file(file->relname, filter);
520 }
521
522 static void filelist_filter_clear(FileList *filelist)
523 {
524         MEM_SAFE_FREE(filelist->fidx);
525         filelist->numfiltered = 0;
526 }
527
528 void filelist_filter(FileList *filelist)
529 {
530         int num_filtered = 0;
531         int *fidx_tmp;
532         int i;
533
534         if (!filelist->filelist) {
535                 return;
536         }
537
538         if (filelist->fidx) {
539                 /* Assume it has already been filtered, nothing else to do! */
540                 return;
541         }
542
543         fidx_tmp = MEM_mallocN(sizeof(*fidx_tmp) * (size_t)filelist->numfiles, __func__);
544
545         /* Filter remap & count how many files are left after filter in a single loop. */
546         for (i = 0; i < filelist->numfiles; ++i) {
547                 struct direntry *file = &filelist->filelist[i];
548
549                 if (filelist->filterf(file, filelist->dir, &filelist->filter_data)) {
550                         fidx_tmp[num_filtered++] = i;
551                 }
552         }
553
554         /* Note: maybe we could even accept filelist->fidx to be filelist->numfiles -len allocated? */
555         filelist->fidx = (int *)MEM_mallocN(sizeof(*filelist->fidx) * (size_t)num_filtered, __func__);
556         memcpy(filelist->fidx, fidx_tmp, sizeof(*filelist->fidx) * (size_t)num_filtered);
557         filelist->numfiltered = num_filtered;
558
559         MEM_freeN(fidx_tmp);
560 }
561
562 void filelist_setfilter_options(FileList *filelist, const bool hide_dot, const bool hide_parent,
563                                 const unsigned int filter, const char *filter_glob)
564 {
565         if ((filelist->filter_data.hide_dot != hide_dot) ||
566             (filelist->filter_data.hide_parent != hide_parent) ||
567             (filelist->filter_data.filter != filter) ||
568             (!STREQ(filelist->filter_data.filter_glob, filter_glob)))
569         {
570                 filelist->filter_data.hide_dot = hide_dot;
571                 filelist->filter_data.hide_parent = hide_parent;
572
573                 filelist->filter_data.filter = filter;
574                 BLI_strncpy(filelist->filter_data.filter_glob, filter_glob, sizeof(filelist->filter_data.filter_glob));
575
576                 filelist_filter_clear(filelist);
577         }
578 }
579
580 /* ********** Icon/image helpers ********** */
581
582 void filelist_init_icons(void)
583 {
584         short x, y, k;
585         ImBuf *bbuf;
586         ImBuf *ibuf;
587
588         BLI_assert(G.background == false);
589
590 #ifdef WITH_HEADLESS
591         bbuf = NULL;
592 #else
593         bbuf = IMB_ibImageFromMemory((unsigned char *)datatoc_prvicons_png, datatoc_prvicons_png_size, IB_rect, NULL, "<splash>");
594 #endif
595         if (bbuf) {
596                 for (y = 0; y < SPECIAL_IMG_ROWS; y++) {
597                         for (x = 0; x < SPECIAL_IMG_COLS; x++) {
598                                 int tile = SPECIAL_IMG_COLS * y + x;
599                                 if (tile < SPECIAL_IMG_MAX) {
600                                         ibuf = IMB_allocImBuf(SPECIAL_IMG_SIZE, SPECIAL_IMG_SIZE, 32, IB_rect);
601                                         for (k = 0; k < SPECIAL_IMG_SIZE; k++) {
602                                                 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));
603                                         }
604                                         gSpecialFileImages[tile] = ibuf;
605                                 }
606                         }
607                 }
608                 IMB_freeImBuf(bbuf);
609         }
610 }
611
612 void filelist_free_icons(void)
613 {
614         int i;
615
616         BLI_assert(G.background == false);
617
618         for (i = 0; i < SPECIAL_IMG_MAX; ++i) {
619                 IMB_freeImBuf(gSpecialFileImages[i]);
620                 gSpecialFileImages[i] = NULL;
621         }
622 }
623
624 void filelist_imgsize(struct FileList *filelist, short w, short h)
625 {
626         filelist->prv_w = w;
627         filelist->prv_h = h;
628 }
629
630 ImBuf *filelist_getimage(struct FileList *filelist, int index)
631 {
632         ImBuf *ibuf = NULL;
633         int fidx = 0;
634
635         BLI_assert(G.background == false);
636
637         if ((index < 0) || (index >= filelist->numfiltered)) {
638                 return NULL;
639         }
640         fidx = filelist->fidx[index];
641         ibuf = filelist->filelist[fidx].image;
642
643         return ibuf;
644 }
645
646 ImBuf *filelist_geticon(struct FileList *filelist, int index)
647 {
648         ImBuf *ibuf = NULL;
649         struct direntry *file = NULL;
650         int fidx = 0;
651
652         BLI_assert(G.background == false);
653
654         if ((index < 0) || (index >= filelist->numfiltered)) {
655                 return NULL;
656         }
657         fidx = filelist->fidx[index];
658         file = &filelist->filelist[fidx];
659         if (file->type & S_IFDIR) {
660                 if (strcmp(filelist->filelist[fidx].relname, "..") == 0) {
661                         ibuf = gSpecialFileImages[SPECIAL_IMG_PARENT];
662                 }
663                 else if (strcmp(filelist->filelist[fidx].relname, ".") == 0) {
664                         ibuf = gSpecialFileImages[SPECIAL_IMG_REFRESH];
665                 }
666                 else {
667                         ibuf = gSpecialFileImages[SPECIAL_IMG_FOLDER];
668                 }
669         }
670         else {
671                 ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE];
672         }
673
674         if (file->flags & BLENDERFILE) {
675                 ibuf = gSpecialFileImages[SPECIAL_IMG_BLENDFILE];
676         }
677         else if ((file->flags & MOVIEFILE) || (file->flags & MOVIEFILE_ICON)) {
678                 ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE];
679         }
680         else if (file->flags & SOUNDFILE) {
681                 ibuf = gSpecialFileImages[SPECIAL_IMG_SOUNDFILE];
682         }
683         else if (file->flags & PYSCRIPTFILE) {
684                 ibuf = gSpecialFileImages[SPECIAL_IMG_PYTHONFILE];
685         }
686         else if (file->flags & FTFONTFILE) {
687                 ibuf = gSpecialFileImages[SPECIAL_IMG_FONTFILE];
688         }
689         else if (file->flags & TEXTFILE) {
690                 ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE];
691         }
692         else if (file->flags & IMAGEFILE) {
693                 ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING];
694         }
695         else if (file->flags & BLENDERFILE_BACKUP) {
696                 ibuf = gSpecialFileImages[SPECIAL_IMG_BACKUP];
697         }
698
699         return ibuf;
700 }
701
702 /* ********** Main ********** */
703
704 FileList *filelist_new(short type)
705 {
706         FileList *p = MEM_callocN(sizeof(FileList), "filelist");
707         switch (type) {
708                 case FILE_MAIN:
709                         p->readf = filelist_read_main;
710                         p->filterf = is_filtered_main;
711                         break;
712                 case FILE_LOADLIB:
713                         p->readf = filelist_read_library;
714                         p->filterf = is_filtered_lib;
715                         break;
716                 default:
717                         p->readf = filelist_read_dir;
718                         p->filterf = is_filtered_file;
719                         break;
720
721         }
722         return p;
723 }
724
725 void filelist_free(struct FileList *filelist)
726 {
727         if (!filelist) {
728                 printf("Attempting to delete empty filelist.\n");
729                 return;
730         }
731         
732         MEM_SAFE_FREE(filelist->fidx);
733         filelist->numfiltered = 0;
734         memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
735
736         filelist->need_sorting = false;
737         filelist->sort = FILE_SORT_NONE;
738
739         BLI_filelist_free(filelist->filelist, filelist->numfiles, NULL);
740         filelist->numfiles = 0;
741         filelist->filelist = NULL;
742 }
743
744 void filelist_freelib(struct FileList *filelist)
745 {
746         if (filelist->libfiledata)
747                 BLO_blendhandle_close(filelist->libfiledata);
748         filelist->libfiledata = NULL;
749 }
750
751 BlendHandle *filelist_lib(struct FileList *filelist)
752 {
753         return filelist->libfiledata;
754 }
755
756 int filelist_numfiles(struct FileList *filelist)
757 {
758         return filelist->numfiltered;
759 }
760
761 const char *filelist_dir(struct FileList *filelist)
762 {
763         return filelist->dir;
764 }
765
766 void filelist_setdir(struct FileList *filelist, const char *dir)
767 {
768         BLI_strncpy(filelist->dir, dir, sizeof(filelist->dir));
769 }
770
771 short filelist_changed(struct FileList *filelist)
772 {
773         return filelist->changed;
774 }
775
776 struct direntry *filelist_file(struct FileList *filelist, int index)
777 {
778         int fidx = 0;
779         
780         if ((index < 0) || (index >= filelist->numfiltered)) {
781                 return NULL;
782         }
783         fidx = filelist->fidx[index];
784
785         return &filelist->filelist[fidx];
786 }
787
788 int filelist_find(struct FileList *filelist, const char *filename)
789 {
790         int index = -1;
791         int i;
792         int fidx = -1;
793         
794         if (!filelist->fidx) 
795                 return fidx;
796
797         
798         for (i = 0; i < filelist->numfiles; ++i) {
799                 if (strcmp(filelist->filelist[i].relname, filename) == 0) {  /* not dealing with user input so don't need BLI_path_cmp */
800                         index = i;
801                         break;
802                 }
803         }
804
805         for (i = 0; i < filelist->numfiltered; ++i) {
806                 if (filelist->fidx[i] == index) {
807                         fidx = i;
808                         break;
809                 }
810         }
811         return fidx;
812 }
813
814 /* would recognize .blend as well */
815 static bool file_is_blend_backup(const char *str)
816 {
817         const size_t a = strlen(str);
818         size_t b = 7;
819         bool retval = 0;
820
821         if (a == 0 || b >= a) {
822                 /* pass */
823         }
824         else {
825                 const char *loc;
826                 
827                 if (a > b + 1)
828                         b++;
829                 
830                 /* allow .blend1 .blend2 .blend32 */
831                 loc = BLI_strcasestr(str + a - b, ".blend");
832                 
833                 if (loc)
834                         retval = 1;
835         }
836         
837         return (retval);
838 }
839
840 static int path_extension_type(const char *path)
841 {
842         if (BLO_has_bfile_extension(path)) {
843                 return BLENDERFILE;
844         }
845         else if (file_is_blend_backup(path)) {
846                 return BLENDERFILE_BACKUP;
847         }
848         else if (BLI_testextensie(path, ".app")) {
849                 return APPLICATIONBUNDLE;
850         }
851         else if (BLI_testextensie(path, ".py")) {
852                 return PYSCRIPTFILE;
853         }
854         else if (BLI_testextensie_n(path, ".txt", ".glsl", ".osl", ".data", NULL)) {
855                 return TEXTFILE;
856         }
857         else if (BLI_testextensie_n(path, ".ttf", ".ttc", ".pfb", ".otf", ".otc", NULL)) {
858                 return FTFONTFILE;
859         }
860         else if (BLI_testextensie(path, ".btx")) {
861                 return BTXFILE;
862         }
863         else if (BLI_testextensie(path, ".dae")) {
864                 return COLLADAFILE;
865         }
866         else if (BLI_testextensie_array(path, imb_ext_image) ||
867                  (G.have_quicktime && BLI_testextensie_array(path, imb_ext_image_qt)))
868         {
869                 return IMAGEFILE;
870         }
871         else if (BLI_testextensie(path, ".ogg")) {
872                 if (IMB_isanim(path)) {
873                         return MOVIEFILE;
874                 }
875                 else {
876                         return SOUNDFILE;
877                 }
878         }
879         else if (BLI_testextensie_array(path, imb_ext_movie)) {
880                 return MOVIEFILE;
881         }
882         else if (BLI_testextensie_array(path, imb_ext_audio)) {
883                 return SOUNDFILE;
884         }
885         return 0;
886 }
887
888 static int file_extension_type(const char *dir, const char *relname)
889 {
890         char path[FILE_MAX];
891         BLI_join_dirfile(path, sizeof(path), dir, relname);
892         return path_extension_type(path);
893 }
894
895 int ED_file_extension_icon(const char *path)
896 {
897         int type = path_extension_type(path);
898         
899         if (type == BLENDERFILE)
900                 return ICON_FILE_BLEND;
901         else if (type == BLENDERFILE_BACKUP)
902                 return ICON_FILE_BACKUP;
903         else if (type == IMAGEFILE)
904                 return ICON_FILE_IMAGE;
905         else if (type == MOVIEFILE)
906                 return ICON_FILE_MOVIE;
907         else if (type == PYSCRIPTFILE)
908                 return ICON_FILE_SCRIPT;
909         else if (type == SOUNDFILE)
910                 return ICON_FILE_SOUND;
911         else if (type == FTFONTFILE)
912                 return ICON_FILE_FONT;
913         else if (type == BTXFILE)
914                 return ICON_FILE_BLANK;
915         else if (type == COLLADAFILE)
916                 return ICON_FILE_BLANK;
917         else if (type == TEXTFILE)
918                 return ICON_FILE_TEXT;
919         
920         return ICON_FILE_BLANK;
921 }
922
923 static void filelist_setfiletypes(struct FileList *filelist)
924 {
925         struct direntry *file;
926         int num;
927         
928         file = filelist->filelist;
929         
930         for (num = 0; num < filelist->numfiles; num++, file++) {
931                 file->type = file->s.st_mode;  /* restore the mess below */
932 #ifndef __APPLE__
933                 /* Don't check extensions for directories, allow in OSX cause bundles have extensions*/
934                 if (file->type & S_IFDIR) {
935                         continue;
936                 }
937 #endif
938                 file->flags = file_extension_type(filelist->dir, file->relname);
939                 
940                 if (filelist->filter_data.filter_glob[0] &&
941                     BLI_testextensie_glob(file->relname, filelist->filter_data.filter_glob))
942                 {
943                         file->flags = OPERATORFILE;
944                 }
945         }
946 }
947
948 static void filelist_read_dir(struct FileList *filelist)
949 {
950         if (!filelist) return;
951
952         filelist->fidx = NULL;
953         filelist->filelist = NULL;
954
955         BLI_cleanup_dir(G.main->name, filelist->dir);
956         filelist->numfiles = BLI_filelist_dir_contents(filelist->dir, &(filelist->filelist));
957
958         filelist_setfiletypes(filelist);
959 }
960
961 static void filelist_read_main(struct FileList *filelist)
962 {
963         if (!filelist) return;
964         filelist_from_main(filelist);
965 }
966
967 static void filelist_read_library(struct FileList *filelist)
968 {
969         if (!filelist) return;
970         BLI_cleanup_dir(G.main->name, filelist->dir);
971         filelist_from_library(filelist);
972         if (!filelist->libfiledata) {
973                 int num;
974                 struct direntry *file;
975
976                 BLI_make_exist(filelist->dir);
977                 filelist_read_dir(filelist);
978                 file = filelist->filelist;
979                 for (num = 0; num < filelist->numfiles; num++, file++) {
980                         if (BLO_has_bfile_extension(file->relname)) {
981                                 char name[FILE_MAX];
982
983                                 BLI_join_dirfile(name, sizeof(name), filelist->dir, file->relname);
984
985                                 /* prevent current file being used as acceptable dir */
986                                 if (BLI_path_cmp(G.main->name, name) != 0) {
987                                         file->type &= ~S_IFMT;
988                                         file->type |= S_IFDIR;
989                                 }
990                         }
991                 }
992         }
993 }
994
995 void filelist_readdir(struct FileList *filelist)
996 {
997         filelist->readf(filelist);
998
999         filelist->need_sorting = true;
1000         filelist->need_thumbnails = true;
1001         filelist_filter_clear(filelist);
1002 }
1003
1004 int filelist_empty(struct FileList *filelist)
1005 {
1006         return filelist->filelist == NULL;
1007 }
1008
1009 void filelist_select_file(struct FileList *filelist, int index, FileSelType select, unsigned int flag, FileCheckType check)
1010 {
1011         struct direntry *file = filelist_file(filelist, index);
1012         if (file != NULL) {
1013                 int check_ok = 0; 
1014                 switch (check) {
1015                         case CHECK_DIRS:
1016                                 check_ok = S_ISDIR(file->type);
1017                                 break;
1018                         case CHECK_ALL:
1019                                 check_ok = 1;
1020                                 break;
1021                         case CHECK_FILES:
1022                         default:
1023                                 check_ok = !S_ISDIR(file->type);
1024                                 break;
1025                 }
1026                 if (check_ok) {
1027                         switch (select) {
1028                                 case FILE_SEL_REMOVE:
1029                                         file->selflag &= ~flag;
1030                                         break;
1031                                 case FILE_SEL_ADD:
1032                                         file->selflag |= flag;
1033                                         break;
1034                                 case FILE_SEL_TOGGLE:
1035                                         file->selflag ^= flag;
1036                                         break;
1037                         }
1038                 }
1039         }
1040 }
1041
1042 void filelist_select(struct FileList *filelist, FileSelection *sel, FileSelType select, unsigned int flag, FileCheckType check)
1043 {
1044         /* select all valid files between first and last indicated */
1045         if ((sel->first >= 0) && (sel->first < filelist->numfiltered) && (sel->last >= 0) && (sel->last < filelist->numfiltered)) {
1046                 int current_file;
1047                 for (current_file = sel->first; current_file <= sel->last; current_file++) {
1048                         filelist_select_file(filelist, current_file, select, flag, check);
1049                 }
1050         }
1051 }
1052
1053 bool filelist_is_selected(struct FileList *filelist, int index, FileCheckType check)
1054 {
1055         struct direntry *file = filelist_file(filelist, index);
1056         if (!file) {
1057                 return 0;
1058         }
1059         switch (check) {
1060                 case CHECK_DIRS:
1061                         return S_ISDIR(file->type) && (file->selflag & SELECTED_FILE);
1062                 case CHECK_FILES:
1063                         return S_ISREG(file->type) && (file->selflag & SELECTED_FILE);
1064                 case CHECK_ALL:
1065                 default:
1066                         return (file->selflag & SELECTED_FILE) != 0;
1067         }
1068 }
1069
1070
1071 bool filelist_islibrary(struct FileList *filelist, char *dir, char *group)
1072 {
1073         return BLO_is_a_library(filelist->dir, dir, group);
1074 }
1075
1076 static int groupname_to_code(const char *group)
1077 {
1078         char buf[BLO_GROUP_MAX];
1079         char *lslash;
1080         
1081         BLI_strncpy(buf, group, sizeof(buf));
1082         lslash = (char *)BLI_last_slash(buf);
1083         if (lslash)
1084                 lslash[0] = '\0';
1085
1086         return buf[0] ? BKE_idcode_from_name(buf) : 0;
1087 }
1088
1089 static void filelist_from_library(struct FileList *filelist)
1090 {
1091         LinkNode *l, *names, *previews;
1092         struct ImBuf *ima;
1093         int ok, i, nprevs, nnames, idcode;
1094         char filename[FILE_MAX];
1095         char dir[FILE_MAX], group[BLO_GROUP_MAX];
1096         
1097         /* name test */
1098         ok = filelist_islibrary(filelist, dir, group);
1099         if (!ok) {
1100                 /* free */
1101                 if (filelist->libfiledata) BLO_blendhandle_close(filelist->libfiledata);
1102                 filelist->libfiledata = NULL;
1103                 return;
1104         }
1105         
1106         BLI_strncpy(filename, G.main->name, sizeof(filename));
1107
1108         /* there we go */
1109         /* for the time being only read filedata when libfiledata==0 */
1110         if (filelist->libfiledata == NULL) {
1111                 filelist->libfiledata = BLO_blendhandle_from_file(dir, NULL);
1112                 if (filelist->libfiledata == NULL) return;
1113         }
1114         
1115         idcode = groupname_to_code(group);
1116
1117         /* memory for strings is passed into filelist[i].relname
1118          * and freed in freefilelist */
1119         if (idcode) {
1120                 previews = BLO_blendhandle_get_previews(filelist->libfiledata, idcode, &nprevs);
1121                 names = BLO_blendhandle_get_datablock_names(filelist->libfiledata, idcode, &nnames);
1122                 /* ugh, no rewind, need to reopen */
1123                 BLO_blendhandle_close(filelist->libfiledata);
1124                 filelist->libfiledata = BLO_blendhandle_from_file(dir, NULL);
1125                 
1126         }
1127         else {
1128                 previews = NULL;
1129                 nprevs = 0;
1130                 names = BLO_blendhandle_get_linkable_groups(filelist->libfiledata);
1131                 nnames = BLI_linklist_length(names);
1132         }
1133
1134         filelist->numfiles = nnames + 1;
1135         filelist->filelist = malloc(filelist->numfiles * sizeof(*filelist->filelist));
1136         memset(filelist->filelist, 0, filelist->numfiles * sizeof(*filelist->filelist));
1137
1138         filelist->filelist[0].relname = BLI_strdup("..");
1139         filelist->filelist[0].type |= S_IFDIR;
1140                 
1141         for (i = 0, l = names; i < nnames; i++, l = l->next) {
1142                 const char *blockname = l->link;
1143
1144                 filelist->filelist[i + 1].relname = BLI_strdup(blockname);
1145                 if (idcode) {
1146                         filelist->filelist[i + 1].type |= S_IFREG;
1147                 }
1148                 else {
1149                         filelist->filelist[i + 1].type |= S_IFDIR;
1150                 }
1151         }
1152         
1153         if (previews && (nnames != nprevs)) {
1154                 printf("filelist_from_library: error, found %d items, %d previews\n", nnames, nprevs);
1155         }
1156         else if (previews) {
1157                 for (i = 0, l = previews; i < nnames; i++, l = l->next) {
1158                         PreviewImage *img = l->link;
1159                         
1160                         if (img) {
1161                                 unsigned int w = img->w[ICON_SIZE_PREVIEW];
1162                                 unsigned int h = img->h[ICON_SIZE_PREVIEW];
1163                                 unsigned int *rect = img->rect[ICON_SIZE_PREVIEW];
1164
1165                                 /* first allocate imbuf for copying preview into it */
1166                                 if (w > 0 && h > 0 && rect) {
1167                                         ima = IMB_allocImBuf(w, h, 32, IB_rect);
1168                                         memcpy(ima->rect, rect, w * h * sizeof(unsigned int));
1169                                         filelist->filelist[i + 1].image = ima;
1170                                         filelist->filelist[i + 1].flags = IMAGEFILE;
1171                                 }
1172                         }
1173                 }
1174         }
1175
1176         BLI_linklist_free(names, free);
1177         if (previews) BLI_linklist_free(previews, BKE_previewimg_freefunc);
1178
1179         BLI_strncpy(G.main->name, filename, sizeof(filename));  /* prevent G.main->name to change */
1180 }
1181
1182 static void filelist_from_main(struct FileList *filelist)
1183 {
1184         ID *id;
1185         struct direntry *files, *firstlib = NULL;
1186         ListBase *lb;
1187         int a, fake, idcode, ok, totlib, totbl;
1188         
1189         // filelist->type = FILE_MAIN; // XXXXX TODO: add modes to filebrowser
1190
1191         if (filelist->dir[0] == '/') filelist->dir[0] = 0;
1192         
1193         if (filelist->dir[0]) {
1194                 idcode = groupname_to_code(filelist->dir);
1195                 if (idcode == 0) filelist->dir[0] = 0;
1196         }
1197         
1198         if (filelist->dir[0] == 0) {
1199                 
1200                 /* make directories */
1201 #ifdef WITH_FREESTYLE
1202                 filelist->numfiles = 24;
1203 #else
1204                 filelist->numfiles = 23;
1205 #endif
1206                 filelist->filelist = (struct direntry *)malloc(filelist->numfiles * sizeof(struct direntry));
1207                 
1208                 for (a = 0; a < filelist->numfiles; a++) {
1209                         memset(&(filelist->filelist[a]), 0, sizeof(struct direntry));
1210                         filelist->filelist[a].type |= S_IFDIR;
1211                 }
1212                 
1213                 filelist->filelist[0].relname = BLI_strdup("..");
1214                 filelist->filelist[1].relname = BLI_strdup("Scene");
1215                 filelist->filelist[2].relname = BLI_strdup("Object");
1216                 filelist->filelist[3].relname = BLI_strdup("Mesh");
1217                 filelist->filelist[4].relname = BLI_strdup("Curve");
1218                 filelist->filelist[5].relname = BLI_strdup("Metaball");
1219                 filelist->filelist[6].relname = BLI_strdup("Material");
1220                 filelist->filelist[7].relname = BLI_strdup("Texture");
1221                 filelist->filelist[8].relname = BLI_strdup("Image");
1222                 filelist->filelist[9].relname = BLI_strdup("Ika");
1223                 filelist->filelist[10].relname = BLI_strdup("Wave");
1224                 filelist->filelist[11].relname = BLI_strdup("Lattice");
1225                 filelist->filelist[12].relname = BLI_strdup("Lamp");
1226                 filelist->filelist[13].relname = BLI_strdup("Camera");
1227                 filelist->filelist[14].relname = BLI_strdup("Ipo");
1228                 filelist->filelist[15].relname = BLI_strdup("World");
1229                 filelist->filelist[16].relname = BLI_strdup("Screen");
1230                 filelist->filelist[17].relname = BLI_strdup("VFont");
1231                 filelist->filelist[18].relname = BLI_strdup("Text");
1232                 filelist->filelist[19].relname = BLI_strdup("Armature");
1233                 filelist->filelist[20].relname = BLI_strdup("Action");
1234                 filelist->filelist[21].relname = BLI_strdup("NodeTree");
1235                 filelist->filelist[22].relname = BLI_strdup("Speaker");
1236 #ifdef WITH_FREESTYLE
1237                 filelist->filelist[23].relname = BLI_strdup("FreestyleLineStyle");
1238 #endif
1239         }
1240         else {
1241
1242                 /* make files */
1243                 idcode = groupname_to_code(filelist->dir);
1244                 
1245                 lb = which_libbase(G.main, idcode);
1246                 if (lb == NULL) return;
1247                 
1248                 id = lb->first;
1249                 filelist->numfiles = 0;
1250                 while (id) {
1251                         if (!filelist->filter_data.hide_dot || id->name[2] != '.') {
1252                                 filelist->numfiles++;
1253                         }
1254                         
1255                         id = id->next;
1256                 }
1257                 
1258                 /* XXXXX TODO: if databrowse F4 or append/link filelist->hide_parent has to be set */
1259                 if (!filelist->filter_data.hide_parent) filelist->numfiles += 1;
1260                 filelist->filelist = filelist->numfiles > 0 ? (struct direntry *)malloc(filelist->numfiles * sizeof(struct direntry)) : NULL;
1261
1262                 files = filelist->filelist;
1263                 
1264                 if (!filelist->filter_data.hide_parent) {
1265                         memset(&(filelist->filelist[0]), 0, sizeof(struct direntry));
1266                         filelist->filelist[0].relname = BLI_strdup("..");
1267                         filelist->filelist[0].type |= S_IFDIR;
1268                 
1269                         files++;
1270                 }
1271                 
1272                 id = lb->first;
1273                 totlib = totbl = 0;
1274                 
1275                 while (id) {
1276                         ok = 1;
1277                         if (ok) {
1278                                 if (!filelist->filter_data.hide_dot || id->name[2] != '.') {
1279                                         memset(files, 0, sizeof(struct direntry));
1280                                         if (id->lib == NULL) {
1281                                                 files->relname = BLI_strdup(id->name + 2);
1282                                         }
1283                                         else {
1284                                                 files->relname = MEM_mallocN(FILE_MAX + (MAX_ID_NAME - 2),     "filename for lib");
1285                                                 BLI_snprintf(files->relname, FILE_MAX + (MAX_ID_NAME - 2) + 3, "%s | %s", id->lib->name, id->name + 2);
1286                                         }
1287                                         files->type |= S_IFREG;
1288 #if 0               /* XXXXX TODO show the selection status of the objects */
1289                                         if (!filelist->has_func) { /* F4 DATA BROWSE */
1290                                                 if (idcode == ID_OB) {
1291                                                         if ( ((Object *)id)->flag & SELECT) files->selflag |= SELECTED_FILE;
1292                                                 }
1293                                                 else if (idcode == ID_SCE) {
1294                                                         if ( ((Scene *)id)->r.scemode & R_BG_RENDER) files->selflag |= SELECTED_FILE;
1295                                                 }
1296                                         }
1297 #endif
1298                                         files->nr = totbl + 1;
1299                                         files->poin = id;
1300                                         fake = id->flag & LIB_FAKEUSER;
1301                                         if (idcode == ID_MA || idcode == ID_TE || idcode == ID_LA || idcode == ID_WO || idcode == ID_IM) {
1302                                                 files->flags |= IMAGEFILE;
1303                                         }
1304                                         if      (id->lib && fake) BLI_snprintf(files->extra, sizeof(files->extra), "LF %d",    id->us);
1305                                         else if (id->lib)         BLI_snprintf(files->extra, sizeof(files->extra), "L    %d",  id->us);
1306                                         else if (fake)            BLI_snprintf(files->extra, sizeof(files->extra), "F    %d",  id->us);
1307                                         else                      BLI_snprintf(files->extra, sizeof(files->extra), "      %d", id->us);
1308                                         
1309                                         if (id->lib) {
1310                                                 if (totlib == 0) firstlib = files;
1311                                                 totlib++;
1312                                         }
1313                                         
1314                                         files++;
1315                                 }
1316                                 totbl++;
1317                         }
1318                         
1319                         id = id->next;
1320                 }
1321                 
1322                 /* only qsort of library blocks */
1323                 if (totlib > 1) {
1324                         qsort(firstlib, totlib, sizeof(struct direntry), compare_name);
1325                 }
1326         }
1327 }
1328
1329 /* ********** Thumbnails job ********** */
1330
1331 typedef struct ThumbnailJob {
1332         ListBase loadimages;
1333         ImBuf *static_icons_buffers[BIFICONID_LAST];
1334         const short *stop;
1335         const short *do_update;
1336         struct FileList *filelist;
1337         ReportList reports;
1338 } ThumbnailJob;
1339
1340 bool filelist_need_thumbnails(FileList *filelist)
1341 {
1342         return filelist->need_thumbnails;
1343 }
1344
1345 static void thumbnail_joblist_free(ThumbnailJob *tj)
1346 {
1347         FileImage *limg = tj->loadimages.first;
1348         
1349         /* free the images not yet copied to the filelist -> these will get freed with the filelist */
1350         for (; limg; limg = limg->next) {
1351                 if ((limg->img) && (!limg->done)) {
1352                         IMB_freeImBuf(limg->img);
1353                 }
1354         }
1355         BLI_freelistN(&tj->loadimages);
1356 }
1357
1358 static void thumbnails_startjob(void *tjv, short *stop, short *do_update, float *UNUSED(progress))
1359 {
1360         ThumbnailJob *tj = tjv;
1361         FileImage *limg = tj->loadimages.first;
1362
1363         tj->stop = stop;
1364         tj->do_update = do_update;
1365
1366         while ((*stop == 0) && (limg)) {
1367                 if (limg->flags & IMAGEFILE) {
1368                         limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_IMAGE);
1369                 }
1370                 else if (limg->flags & (BLENDERFILE | BLENDERFILE_BACKUP)) {
1371                         limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_BLEND);
1372                 }
1373                 else if (limg->flags & MOVIEFILE) {
1374                         limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_MOVIE);
1375                         if (!limg->img) {
1376                                 /* remember that file can't be loaded via IMB_open_anim */
1377                                 limg->flags &= ~MOVIEFILE;
1378                                 limg->flags |= MOVIEFILE_ICON;
1379                         }
1380                 }
1381                 *do_update = true;
1382                 PIL_sleep_ms(10);
1383                 limg = limg->next;
1384         }
1385 }
1386
1387 static void thumbnails_update(void *tjv)
1388 {
1389         ThumbnailJob *tj = tjv;
1390
1391         if (tj->filelist && tj->filelist->filelist) {
1392                 FileImage *limg = tj->loadimages.first;
1393                 while (limg) {
1394                         if (!limg->done && limg->img) {
1395                                 tj->filelist->filelist[limg->index].image = limg->img;
1396                                 /* update flag for movie files where thumbnail can't be created */
1397                                 if (limg->flags & MOVIEFILE_ICON) {
1398                                         tj->filelist->filelist[limg->index].flags &= ~MOVIEFILE;
1399                                         tj->filelist->filelist[limg->index].flags |= MOVIEFILE_ICON;
1400                                 }
1401                                 limg->done = true;
1402                         }
1403                         limg = limg->next;
1404                 }
1405         }
1406 }
1407
1408 static void thumbnails_endjob(void *tjv)
1409 {
1410         ThumbnailJob *tj = tjv;
1411
1412         if (!*tj->stop) {
1413                 tj->filelist->need_thumbnails = false;
1414         }
1415 }
1416
1417 static void thumbnails_free(void *tjv)
1418 {
1419         ThumbnailJob *tj = tjv;
1420         thumbnail_joblist_free(tj);
1421         MEM_freeN(tj);
1422 }
1423
1424
1425 void thumbnails_start(FileList *filelist, const bContext *C)
1426 {
1427         wmJob *wm_job;
1428         ThumbnailJob *tj;
1429         int idx;
1430         
1431         /* prepare job data */
1432         tj = MEM_callocN(sizeof(ThumbnailJob), "thumbnails\n");
1433         tj->filelist = filelist;
1434         for (idx = 0; idx < filelist->numfiles; idx++) {
1435                 if (!filelist->filelist[idx].image) {
1436                         if ((filelist->filelist[idx].flags & (IMAGEFILE | MOVIEFILE | BLENDERFILE | BLENDERFILE_BACKUP))) {
1437                                 FileImage *limg = MEM_callocN(sizeof(FileImage), "loadimage");
1438                                 BLI_strncpy(limg->path, filelist->filelist[idx].path, FILE_MAX);
1439                                 limg->index = idx;
1440                                 limg->flags = filelist->filelist[idx].flags;
1441                                 BLI_addtail(&tj->loadimages, limg);
1442                         }
1443                 }
1444         }
1445
1446         BKE_reports_init(&tj->reports, RPT_PRINT);
1447
1448         /* setup job */
1449         wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), filelist, "Thumbnails",
1450                              0, WM_JOB_TYPE_FILESEL_THUMBNAIL);
1451         WM_jobs_customdata_set(wm_job, tj, thumbnails_free);
1452         WM_jobs_timer(wm_job, 0.5, NC_WINDOW, NC_WINDOW);
1453         WM_jobs_callbacks(wm_job, thumbnails_startjob, NULL, thumbnails_update, thumbnails_endjob);
1454
1455         /* start the job */
1456         WM_jobs_start(CTX_wm_manager(C), wm_job);
1457 }
1458
1459 void thumbnails_stop(wmWindowManager *wm, FileList *filelist)
1460 {
1461         WM_jobs_kill_type(wm, filelist, WM_JOB_TYPE_FILESEL_THUMBNAIL);
1462 }
1463
1464 int thumbnails_running(wmWindowManager *wm, FileList *filelist)
1465 {
1466         return WM_jobs_test(wm, filelist, WM_JOB_TYPE_FILESEL_THUMBNAIL);
1467 }