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