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