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