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