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