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