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