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