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