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