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