2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2007 Blender Foundation.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): none yet.
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file blender/editors/space_file/filelist.c
45 #include "MEM_guardedalloc.h"
47 #include "BLI_blenlib.h"
48 #include "BLI_fileops_types.h"
49 #include "BLI_linklist.h"
50 #include "BLI_utildefines.h"
53 # include "BLI_winstuff.h"
56 #include "BKE_context.h"
57 #include "BKE_global.h"
58 #include "BKE_library.h"
59 #include "BKE_icons.h"
61 #include "BKE_report.h"
62 #include "BLO_readfile.h"
63 #include "BKE_idcode.h"
65 #include "DNA_space_types.h"
67 #include "ED_datafiles.h"
68 #include "ED_fileselect.h"
70 #include "IMB_imbuf.h"
71 #include "IMB_imbuf_types.h"
72 #include "IMB_thumbs.h"
79 #include "UI_resources.h"
84 /* ----------------- FOLDERLIST (previous/next) -------------- */
86 typedef struct FolderList {
87 struct FolderList *next, *prev;
91 ListBase *folderlist_new(void)
93 ListBase *p = MEM_callocN(sizeof(ListBase), "folderlist");
97 void folderlist_popdir(struct ListBase *folderlist, char *dir)
100 struct FolderList *folder;
101 folder = folderlist->last;
104 /* remove the current directory */
105 MEM_freeN(folder->foldername);
106 BLI_freelinkN(folderlist, folder);
108 folder = folderlist->last;
110 prev_dir = folder->foldername;
111 BLI_strncpy(dir, prev_dir, FILE_MAXDIR);
114 /* delete the folder next or use setdir directly before PREVIOUS OP */
117 void folderlist_pushdir(ListBase *folderlist, const char *dir)
119 struct FolderList *folder, *previous_folder;
120 previous_folder = folderlist->last;
122 /* check if already exists */
123 if (previous_folder && previous_folder->foldername) {
124 if (BLI_path_cmp(previous_folder->foldername, dir) == 0) {
129 /* create next folder element */
130 folder = (FolderList *)MEM_mallocN(sizeof(FolderList), "FolderList");
131 folder->foldername = BLI_strdup(dir);
133 /* add it to the end of the list */
134 BLI_addtail(folderlist, folder);
137 const char *folderlist_peeklastdir(ListBase *folderlist)
139 struct FolderList *folder;
141 if (!folderlist->last)
144 folder = folderlist->last;
145 return folder->foldername;
148 int folderlist_clear_next(struct SpaceFile *sfile)
150 struct FolderList *folder;
152 /* if there is no folder_next there is nothing we can clear */
153 if (!sfile->folders_next)
156 /* if previous_folder, next_folder or refresh_folder operators are executed it doesn't clear folder_next */
157 folder = sfile->folders_prev->last;
158 if ((!folder) || (BLI_path_cmp(folder->foldername, sfile->params->dir) == 0))
161 /* eventually clear flist->folders_next */
165 /* not listbase itself */
166 void folderlist_free(ListBase *folderlist)
170 for (folder = folderlist->first; folder; folder = folder->next)
171 MEM_freeN(folder->foldername);
172 BLI_freelistN(folderlist);
176 ListBase *folderlist_duplicate(ListBase *folderlist)
180 ListBase *folderlistn = MEM_callocN(sizeof(ListBase), "copy folderlist");
183 BLI_duplicatelist(folderlistn, folderlist);
185 for (folder = folderlistn->first; folder; folder = folder->next) {
186 folder->foldername = MEM_dupallocN(folder->foldername);
194 /* ------------------FILELIST------------------------ */
198 typedef struct FileImage {
199 struct FileImage *next, *prev;
207 typedef struct FileListFilter {
211 char filter_glob[64];
214 typedef struct FileList {
215 struct direntry *filelist;
226 FileListFilter filter_data;
227 int *fidx; /* Also used to detect when we need to filter! */
230 bool need_thumbnails;
232 struct BlendHandle *libfiledata;
234 void (*readf)(struct FileList *);
235 bool (*filterf)(struct direntry *, const char *, FileListFilter *);
238 #define FILENAME_IS_BREADCRUMBS(_n) \
239 (((_n)[0] == '.' && (_n)[1] == '\0') || ((_n)[0] == '.' && (_n)[1] == '.' && (_n)[2] == '\0'))
241 #define SPECIAL_IMG_SIZE 48
242 #define SPECIAL_IMG_ROWS 4
243 #define SPECIAL_IMG_COLS 4
245 #define SPECIAL_IMG_FOLDER 0
246 #define SPECIAL_IMG_PARENT 1
247 #define SPECIAL_IMG_REFRESH 2
248 #define SPECIAL_IMG_BLENDFILE 3
249 #define SPECIAL_IMG_SOUNDFILE 4
250 #define SPECIAL_IMG_MOVIEFILE 5
251 #define SPECIAL_IMG_PYTHONFILE 6
252 #define SPECIAL_IMG_TEXTFILE 7
253 #define SPECIAL_IMG_FONTFILE 8
254 #define SPECIAL_IMG_UNKNOWNFILE 9
255 #define SPECIAL_IMG_LOADING 10
256 #define SPECIAL_IMG_BACKUP 11
257 #define SPECIAL_IMG_MAX SPECIAL_IMG_BACKUP + 1
259 static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
262 static void filelist_from_main(struct FileList *filelist);
263 static void filelist_from_library(struct FileList *filelist);
265 static void filelist_read_main(struct FileList *filelist);
266 static void filelist_read_library(struct FileList *filelist);
267 static void filelist_read_dir(struct FileList *filelist);
269 static void filelist_filter_clear(FileList *filelist);
271 /* ********** Sort helpers ********** */
273 static bool compare_is_directory(const struct direntry *entry)
275 /* for library browse .blend files may be treated as directories, but
276 * for sorting purposes they should be considered regular files */
277 if (S_ISDIR(entry->type))
278 return !(entry->flags & (BLENDERFILE | BLENDERFILE_BACKUP));
283 static int compare_name(const void *a1, const void *a2)
285 const struct direntry *entry1 = a1, *entry2 = a2;
287 /* type is equal to stat.st_mode */
289 if (compare_is_directory(entry1)) {
290 if (compare_is_directory(entry2) == 0) return (-1);
293 if (compare_is_directory(entry2)) return (1);
295 if (S_ISREG(entry1->type)) {
296 if (S_ISREG(entry2->type) == 0) return (-1);
299 if (S_ISREG(entry2->type)) return (1);
301 if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return (-1);
302 if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return (1);
304 /* make sure "." and ".." are always first */
305 if (strcmp(entry1->relname, ".") == 0) return (-1);
306 if (strcmp(entry2->relname, ".") == 0) return (1);
307 if (strcmp(entry1->relname, "..") == 0) return (-1);
308 if (strcmp(entry2->relname, "..") == 0) return (1);
310 return (BLI_natstrcmp(entry1->relname, entry2->relname));
313 static int compare_date(const void *a1, const void *a2)
315 const struct direntry *entry1 = a1, *entry2 = a2;
317 /* type is equal to stat.st_mode */
319 if (compare_is_directory(entry1)) {
320 if (compare_is_directory(entry2) == 0) return (-1);
323 if (compare_is_directory(entry2)) return (1);
325 if (S_ISREG(entry1->type)) {
326 if (S_ISREG(entry2->type) == 0) return (-1);
329 if (S_ISREG(entry2->type)) return (1);
331 if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return (-1);
332 if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return (1);
334 /* make sure "." and ".." are always first */
335 if (strcmp(entry1->relname, ".") == 0) return (-1);
336 if (strcmp(entry2->relname, ".") == 0) return (1);
337 if (strcmp(entry1->relname, "..") == 0) return (-1);
338 if (strcmp(entry2->relname, "..") == 0) return (1);
340 if (entry1->s.st_mtime < entry2->s.st_mtime) return 1;
341 if (entry1->s.st_mtime > entry2->s.st_mtime) return -1;
343 else return BLI_natstrcmp(entry1->relname, entry2->relname);
346 static int compare_size(const void *a1, const void *a2)
348 const struct direntry *entry1 = a1, *entry2 = a2;
350 /* type is equal to stat.st_mode */
352 if (compare_is_directory(entry1)) {
353 if (compare_is_directory(entry2) == 0) return (-1);
356 if (compare_is_directory(entry2)) return (1);
358 if (S_ISREG(entry1->type)) {
359 if (S_ISREG(entry2->type) == 0) return (-1);
362 if (S_ISREG(entry2->type)) return (1);
364 if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return (-1);
365 if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return (1);
367 /* make sure "." and ".." are always first */
368 if (strcmp(entry1->relname, ".") == 0) return (-1);
369 if (strcmp(entry2->relname, ".") == 0) return (1);
370 if (strcmp(entry1->relname, "..") == 0) return (-1);
371 if (strcmp(entry2->relname, "..") == 0) return (1);
373 if (entry1->s.st_size < entry2->s.st_size) return 1;
374 if (entry1->s.st_size > entry2->s.st_size) return -1;
375 else return BLI_natstrcmp(entry1->relname, entry2->relname);
378 static int compare_extension(const void *a1, const void *a2)
380 const struct direntry *entry1 = a1, *entry2 = a2;
381 const char *sufix1, *sufix2;
382 const char *nil = "";
384 if (!(sufix1 = strstr(entry1->relname, ".blend.gz")))
385 sufix1 = strrchr(entry1->relname, '.');
386 if (!(sufix2 = strstr(entry2->relname, ".blend.gz")))
387 sufix2 = strrchr(entry2->relname, '.');
388 if (!sufix1) sufix1 = nil;
389 if (!sufix2) sufix2 = nil;
391 /* type is equal to stat.st_mode */
393 if (compare_is_directory(entry1)) {
394 if (compare_is_directory(entry2) == 0) return (-1);
397 if (compare_is_directory(entry2)) return (1);
399 if (S_ISREG(entry1->type)) {
400 if (S_ISREG(entry2->type) == 0) return (-1);
403 if (S_ISREG(entry2->type)) return (1);
405 if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return (-1);
406 if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return (1);
408 /* make sure "." and ".." are always first */
409 if (strcmp(entry1->relname, ".") == 0) return (-1);
410 if (strcmp(entry2->relname, ".") == 0) return (1);
411 if (strcmp(entry1->relname, "..") == 0) return (-1);
412 if (strcmp(entry2->relname, "..") == 0) return (1);
414 return (BLI_strcasecmp(sufix1, sufix2));
417 bool filelist_need_sorting(struct FileList *filelist)
419 return filelist->need_sorting && (filelist->sort != FILE_SORT_NONE);
422 void filelist_sort(struct FileList *filelist)
424 if (filelist_need_sorting(filelist)) {
425 filelist->need_sorting = false;
427 switch (filelist->sort) {
428 case FILE_SORT_ALPHA:
429 qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_name);
432 qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_date);
435 qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_size);
437 case FILE_SORT_EXTENSION:
438 qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_extension);
440 case FILE_SORT_NONE: /* Should never reach this point! */
446 filelist_filter_clear(filelist);
450 void filelist_setsorting(struct FileList *filelist, const short sort)
452 if (filelist->sort != sort) {
453 filelist->sort = sort;
454 filelist->need_sorting = true;
458 /* ********** Filter helpers ********** */
460 static bool is_hidden_file(const char *filename, FileListFilter *filter)
462 bool is_hidden = false;
464 if (filter->hide_dot) {
465 if (filename[0] == '.' && filename[1] != '.' && filename[1] != '\0') {
466 is_hidden = true; /* ignore .file */
469 int len = strlen(filename);
470 if ((len > 0) && (filename[len - 1] == '~')) {
471 is_hidden = true; /* ignore file~ */
475 if (!is_hidden && filter->hide_parent) {
476 if (filename[0] == '.' && filename[1] == '.' && filename[2] == '\0') {
477 is_hidden = true; /* ignore .. */
480 if (!is_hidden && ((filename[0] == '.') && (filename[1] == '\0'))) {
481 is_hidden = true; /* ignore . */
486 static bool is_filtered_file(struct direntry *file, const char *UNUSED(root), FileListFilter *filter)
488 bool is_filtered = !is_hidden_file(file->relname, filter);
490 if (is_filtered && filter->filter && !FILENAME_IS_BREADCRUMBS(file->relname)) {
491 if ((file->type & S_IFDIR) && !(filter->filter & FOLDERFILE)) {
494 if (!(file->type & S_IFDIR) && !(file->flags & filter->filter)) {
502 static bool is_filtered_lib(struct direntry *file, const char *root, FileListFilter *filter)
504 bool is_filtered = !is_hidden_file(file->relname, filter);
505 char dir[FILE_MAXDIR], group[BLO_GROUP_MAX];
507 if (BLO_is_a_library(root, dir, group)) {
508 is_filtered = !is_hidden_file(file->relname, filter);
511 is_filtered = is_filtered_file(file, root, filter);
517 static bool is_filtered_main(struct direntry *file, const char *UNUSED(dir), FileListFilter *filter)
519 return !is_hidden_file(file->relname, filter);
522 static void filelist_filter_clear(FileList *filelist)
524 MEM_SAFE_FREE(filelist->fidx);
525 filelist->numfiltered = 0;
528 void filelist_filter(FileList *filelist)
530 int num_filtered = 0;
534 if (!filelist->filelist) {
538 if (filelist->fidx) {
539 /* Assume it has already been filtered, nothing else to do! */
543 fidx_tmp = MEM_mallocN(sizeof(*fidx_tmp) * (size_t)filelist->numfiles, __func__);
545 /* Filter remap & count how many files are left after filter in a single loop. */
546 for (i = 0; i < filelist->numfiles; ++i) {
547 struct direntry *file = &filelist->filelist[i];
549 if (filelist->filterf(file, filelist->dir, &filelist->filter_data)) {
550 fidx_tmp[num_filtered++] = i;
554 /* Note: maybe we could even accept filelist->fidx to be filelist->numfiles -len allocated? */
555 filelist->fidx = (int *)MEM_mallocN(sizeof(*filelist->fidx) * (size_t)num_filtered, __func__);
556 memcpy(filelist->fidx, fidx_tmp, sizeof(*filelist->fidx) * (size_t)num_filtered);
557 filelist->numfiltered = num_filtered;
562 void filelist_setfilter_options(FileList *filelist, const bool hide_dot, const bool hide_parent,
563 const unsigned int filter, const char *filter_glob)
565 if ((filelist->filter_data.hide_dot != hide_dot) ||
566 (filelist->filter_data.hide_parent != hide_parent) ||
567 (filelist->filter_data.filter != filter) ||
568 (!STREQ(filelist->filter_data.filter_glob, filter_glob)))
570 filelist->filter_data.hide_dot = hide_dot;
571 filelist->filter_data.hide_parent = hide_parent;
573 filelist->filter_data.filter = filter;
574 BLI_strncpy(filelist->filter_data.filter_glob, filter_glob, sizeof(filelist->filter_data.filter_glob));
576 filelist_filter_clear(filelist);
580 /* ********** Icon/image helpers ********** */
582 void filelist_init_icons(void)
588 BLI_assert(G.background == false);
593 bbuf = IMB_ibImageFromMemory((unsigned char *)datatoc_prvicons_png, datatoc_prvicons_png_size, IB_rect, NULL, "<splash>");
596 for (y = 0; y < SPECIAL_IMG_ROWS; y++) {
597 for (x = 0; x < SPECIAL_IMG_COLS; x++) {
598 int tile = SPECIAL_IMG_COLS * y + x;
599 if (tile < SPECIAL_IMG_MAX) {
600 ibuf = IMB_allocImBuf(SPECIAL_IMG_SIZE, SPECIAL_IMG_SIZE, 32, IB_rect);
601 for (k = 0; k < SPECIAL_IMG_SIZE; k++) {
602 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));
604 gSpecialFileImages[tile] = ibuf;
612 void filelist_free_icons(void)
616 BLI_assert(G.background == false);
618 for (i = 0; i < SPECIAL_IMG_MAX; ++i) {
619 IMB_freeImBuf(gSpecialFileImages[i]);
620 gSpecialFileImages[i] = NULL;
624 void filelist_imgsize(struct FileList *filelist, short w, short h)
630 ImBuf *filelist_getimage(struct FileList *filelist, int index)
635 BLI_assert(G.background == false);
637 if ((index < 0) || (index >= filelist->numfiltered)) {
640 fidx = filelist->fidx[index];
641 ibuf = filelist->filelist[fidx].image;
646 ImBuf *filelist_geticon(struct FileList *filelist, int index)
649 struct direntry *file = NULL;
652 BLI_assert(G.background == false);
654 if ((index < 0) || (index >= filelist->numfiltered)) {
657 fidx = filelist->fidx[index];
658 file = &filelist->filelist[fidx];
659 if (file->type & S_IFDIR) {
660 if (strcmp(filelist->filelist[fidx].relname, "..") == 0) {
661 ibuf = gSpecialFileImages[SPECIAL_IMG_PARENT];
663 else if (strcmp(filelist->filelist[fidx].relname, ".") == 0) {
664 ibuf = gSpecialFileImages[SPECIAL_IMG_REFRESH];
667 ibuf = gSpecialFileImages[SPECIAL_IMG_FOLDER];
671 ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE];
674 if (file->flags & BLENDERFILE) {
675 ibuf = gSpecialFileImages[SPECIAL_IMG_BLENDFILE];
677 else if ((file->flags & MOVIEFILE) || (file->flags & MOVIEFILE_ICON)) {
678 ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE];
680 else if (file->flags & SOUNDFILE) {
681 ibuf = gSpecialFileImages[SPECIAL_IMG_SOUNDFILE];
683 else if (file->flags & PYSCRIPTFILE) {
684 ibuf = gSpecialFileImages[SPECIAL_IMG_PYTHONFILE];
686 else if (file->flags & FTFONTFILE) {
687 ibuf = gSpecialFileImages[SPECIAL_IMG_FONTFILE];
689 else if (file->flags & TEXTFILE) {
690 ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE];
692 else if (file->flags & IMAGEFILE) {
693 ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING];
695 else if (file->flags & BLENDERFILE_BACKUP) {
696 ibuf = gSpecialFileImages[SPECIAL_IMG_BACKUP];
702 /* ********** Main ********** */
704 FileList *filelist_new(short type)
706 FileList *p = MEM_callocN(sizeof(FileList), "filelist");
709 p->readf = filelist_read_main;
710 p->filterf = is_filtered_main;
713 p->readf = filelist_read_library;
714 p->filterf = is_filtered_lib;
717 p->readf = filelist_read_dir;
718 p->filterf = is_filtered_file;
725 void filelist_free(struct FileList *filelist)
728 printf("Attempting to delete empty filelist.\n");
732 MEM_SAFE_FREE(filelist->fidx);
733 filelist->numfiltered = 0;
734 memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
736 filelist->need_sorting = false;
737 filelist->sort = FILE_SORT_NONE;
739 BLI_filelist_free(filelist->filelist, filelist->numfiles, NULL);
740 filelist->numfiles = 0;
741 filelist->filelist = NULL;
744 void filelist_freelib(struct FileList *filelist)
746 if (filelist->libfiledata)
747 BLO_blendhandle_close(filelist->libfiledata);
748 filelist->libfiledata = NULL;
751 BlendHandle *filelist_lib(struct FileList *filelist)
753 return filelist->libfiledata;
756 int filelist_numfiles(struct FileList *filelist)
758 return filelist->numfiltered;
761 const char *filelist_dir(struct FileList *filelist)
763 return filelist->dir;
766 void filelist_setdir(struct FileList *filelist, const char *dir)
768 BLI_strncpy(filelist->dir, dir, sizeof(filelist->dir));
771 short filelist_changed(struct FileList *filelist)
773 return filelist->changed;
776 struct direntry *filelist_file(struct FileList *filelist, int index)
780 if ((index < 0) || (index >= filelist->numfiltered)) {
783 fidx = filelist->fidx[index];
785 return &filelist->filelist[fidx];
788 int filelist_find(struct FileList *filelist, const char *filename)
798 for (i = 0; i < filelist->numfiles; ++i) {
799 if (strcmp(filelist->filelist[i].relname, filename) == 0) { /* not dealing with user input so don't need BLI_path_cmp */
805 for (i = 0; i < filelist->numfiltered; ++i) {
806 if (filelist->fidx[i] == index) {
814 /* would recognize .blend as well */
815 static bool file_is_blend_backup(const char *str)
817 const size_t a = strlen(str);
821 if (a == 0 || b >= a) {
830 /* allow .blend1 .blend2 .blend32 */
831 loc = BLI_strcasestr(str + a - b, ".blend");
840 static int path_extension_type(const char *path)
842 if (BLO_has_bfile_extension(path)) {
845 else if (file_is_blend_backup(path)) {
846 return BLENDERFILE_BACKUP;
848 else if (BLI_testextensie(path, ".app")) {
849 return APPLICATIONBUNDLE;
851 else if (BLI_testextensie(path, ".py")) {
854 else if (BLI_testextensie_n(path, ".txt", ".glsl", ".osl", ".data", NULL)) {
857 else if (BLI_testextensie_n(path, ".ttf", ".ttc", ".pfb", ".otf", ".otc", NULL)) {
860 else if (BLI_testextensie(path, ".btx")) {
863 else if (BLI_testextensie(path, ".dae")) {
866 else if (BLI_testextensie_array(path, imb_ext_image) ||
867 (G.have_quicktime && BLI_testextensie_array(path, imb_ext_image_qt)))
871 else if (BLI_testextensie(path, ".ogg")) {
872 if (IMB_isanim(path)) {
879 else if (BLI_testextensie_array(path, imb_ext_movie)) {
882 else if (BLI_testextensie_array(path, imb_ext_audio)) {
888 static int file_extension_type(const char *dir, const char *relname)
891 BLI_join_dirfile(path, sizeof(path), dir, relname);
892 return path_extension_type(path);
895 int ED_file_extension_icon(const char *path)
897 int type = path_extension_type(path);
899 if (type == BLENDERFILE)
900 return ICON_FILE_BLEND;
901 else if (type == BLENDERFILE_BACKUP)
902 return ICON_FILE_BACKUP;
903 else if (type == IMAGEFILE)
904 return ICON_FILE_IMAGE;
905 else if (type == MOVIEFILE)
906 return ICON_FILE_MOVIE;
907 else if (type == PYSCRIPTFILE)
908 return ICON_FILE_SCRIPT;
909 else if (type == SOUNDFILE)
910 return ICON_FILE_SOUND;
911 else if (type == FTFONTFILE)
912 return ICON_FILE_FONT;
913 else if (type == BTXFILE)
914 return ICON_FILE_BLANK;
915 else if (type == COLLADAFILE)
916 return ICON_FILE_BLANK;
917 else if (type == TEXTFILE)
918 return ICON_FILE_TEXT;
920 return ICON_FILE_BLANK;
923 static void filelist_setfiletypes(struct FileList *filelist)
925 struct direntry *file;
928 file = filelist->filelist;
930 for (num = 0; num < filelist->numfiles; num++, file++) {
931 file->type = file->s.st_mode; /* restore the mess below */
933 /* Don't check extensions for directories, allow in OSX cause bundles have extensions*/
934 if (file->type & S_IFDIR) {
938 file->flags = file_extension_type(filelist->dir, file->relname);
940 if (filelist->filter_data.filter_glob[0] &&
941 BLI_testextensie_glob(file->relname, filelist->filter_data.filter_glob))
943 file->flags = OPERATORFILE;
948 static void filelist_read_dir(struct FileList *filelist)
950 if (!filelist) return;
952 filelist->fidx = NULL;
953 filelist->filelist = NULL;
955 BLI_cleanup_dir(G.main->name, filelist->dir);
956 filelist->numfiles = BLI_filelist_dir_contents(filelist->dir, &(filelist->filelist));
958 filelist_setfiletypes(filelist);
961 static void filelist_read_main(struct FileList *filelist)
963 if (!filelist) return;
964 filelist_from_main(filelist);
967 static void filelist_read_library(struct FileList *filelist)
969 if (!filelist) return;
970 BLI_cleanup_dir(G.main->name, filelist->dir);
971 filelist_from_library(filelist);
972 if (!filelist->libfiledata) {
974 struct direntry *file;
976 BLI_make_exist(filelist->dir);
977 filelist_read_dir(filelist);
978 file = filelist->filelist;
979 for (num = 0; num < filelist->numfiles; num++, file++) {
980 if (BLO_has_bfile_extension(file->relname)) {
983 BLI_join_dirfile(name, sizeof(name), filelist->dir, file->relname);
985 /* prevent current file being used as acceptable dir */
986 if (BLI_path_cmp(G.main->name, name) != 0) {
987 file->type &= ~S_IFMT;
988 file->type |= S_IFDIR;
995 void filelist_readdir(struct FileList *filelist)
997 filelist->readf(filelist);
999 filelist->need_sorting = true;
1000 filelist->need_thumbnails = true;
1001 filelist_filter_clear(filelist);
1004 int filelist_empty(struct FileList *filelist)
1006 return filelist->filelist == NULL;
1009 void filelist_select_file(struct FileList *filelist, int index, FileSelType select, unsigned int flag, FileCheckType check)
1011 struct direntry *file = filelist_file(filelist, index);
1016 check_ok = S_ISDIR(file->type);
1023 check_ok = !S_ISDIR(file->type);
1028 case FILE_SEL_REMOVE:
1029 file->selflag &= ~flag;
1032 file->selflag |= flag;
1034 case FILE_SEL_TOGGLE:
1035 file->selflag ^= flag;
1042 void filelist_select(struct FileList *filelist, FileSelection *sel, FileSelType select, unsigned int flag, FileCheckType check)
1044 /* select all valid files between first and last indicated */
1045 if ((sel->first >= 0) && (sel->first < filelist->numfiltered) && (sel->last >= 0) && (sel->last < filelist->numfiltered)) {
1047 for (current_file = sel->first; current_file <= sel->last; current_file++) {
1048 filelist_select_file(filelist, current_file, select, flag, check);
1053 bool filelist_is_selected(struct FileList *filelist, int index, FileCheckType check)
1055 struct direntry *file = filelist_file(filelist, index);
1061 return S_ISDIR(file->type) && (file->selflag & SELECTED_FILE);
1063 return S_ISREG(file->type) && (file->selflag & SELECTED_FILE);
1066 return (file->selflag & SELECTED_FILE) != 0;
1071 bool filelist_islibrary(struct FileList *filelist, char *dir, char *group)
1073 return BLO_is_a_library(filelist->dir, dir, group);
1076 static int groupname_to_code(const char *group)
1078 char buf[BLO_GROUP_MAX];
1081 BLI_strncpy(buf, group, sizeof(buf));
1082 lslash = (char *)BLI_last_slash(buf);
1086 return buf[0] ? BKE_idcode_from_name(buf) : 0;
1089 static void filelist_from_library(struct FileList *filelist)
1091 LinkNode *l, *names, *previews;
1093 int ok, i, nprevs, nnames, idcode;
1094 char filename[FILE_MAX];
1095 char dir[FILE_MAX], group[BLO_GROUP_MAX];
1098 ok = filelist_islibrary(filelist, dir, group);
1101 if (filelist->libfiledata) BLO_blendhandle_close(filelist->libfiledata);
1102 filelist->libfiledata = NULL;
1106 BLI_strncpy(filename, G.main->name, sizeof(filename));
1109 /* for the time being only read filedata when libfiledata==0 */
1110 if (filelist->libfiledata == NULL) {
1111 filelist->libfiledata = BLO_blendhandle_from_file(dir, NULL);
1112 if (filelist->libfiledata == NULL) return;
1115 idcode = groupname_to_code(group);
1117 /* memory for strings is passed into filelist[i].relname
1118 * and freed in freefilelist */
1120 previews = BLO_blendhandle_get_previews(filelist->libfiledata, idcode, &nprevs);
1121 names = BLO_blendhandle_get_datablock_names(filelist->libfiledata, idcode, &nnames);
1122 /* ugh, no rewind, need to reopen */
1123 BLO_blendhandle_close(filelist->libfiledata);
1124 filelist->libfiledata = BLO_blendhandle_from_file(dir, NULL);
1130 names = BLO_blendhandle_get_linkable_groups(filelist->libfiledata);
1131 nnames = BLI_linklist_length(names);
1134 filelist->numfiles = nnames + 1;
1135 filelist->filelist = malloc(filelist->numfiles * sizeof(*filelist->filelist));
1136 memset(filelist->filelist, 0, filelist->numfiles * sizeof(*filelist->filelist));
1138 filelist->filelist[0].relname = BLI_strdup("..");
1139 filelist->filelist[0].type |= S_IFDIR;
1141 for (i = 0, l = names; i < nnames; i++, l = l->next) {
1142 const char *blockname = l->link;
1144 filelist->filelist[i + 1].relname = BLI_strdup(blockname);
1146 filelist->filelist[i + 1].type |= S_IFREG;
1149 filelist->filelist[i + 1].type |= S_IFDIR;
1153 if (previews && (nnames != nprevs)) {
1154 printf("filelist_from_library: error, found %d items, %d previews\n", nnames, nprevs);
1156 else if (previews) {
1157 for (i = 0, l = previews; i < nnames; i++, l = l->next) {
1158 PreviewImage *img = l->link;
1161 unsigned int w = img->w[ICON_SIZE_PREVIEW];
1162 unsigned int h = img->h[ICON_SIZE_PREVIEW];
1163 unsigned int *rect = img->rect[ICON_SIZE_PREVIEW];
1165 /* first allocate imbuf for copying preview into it */
1166 if (w > 0 && h > 0 && rect) {
1167 ima = IMB_allocImBuf(w, h, 32, IB_rect);
1168 memcpy(ima->rect, rect, w * h * sizeof(unsigned int));
1169 filelist->filelist[i + 1].image = ima;
1170 filelist->filelist[i + 1].flags = IMAGEFILE;
1176 BLI_linklist_free(names, free);
1177 if (previews) BLI_linklist_free(previews, BKE_previewimg_freefunc);
1179 BLI_strncpy(G.main->name, filename, sizeof(filename)); /* prevent G.main->name to change */
1182 static void filelist_from_main(struct FileList *filelist)
1185 struct direntry *files, *firstlib = NULL;
1187 int a, fake, idcode, ok, totlib, totbl;
1189 // filelist->type = FILE_MAIN; // XXXXX TODO: add modes to filebrowser
1191 if (filelist->dir[0] == '/') filelist->dir[0] = 0;
1193 if (filelist->dir[0]) {
1194 idcode = groupname_to_code(filelist->dir);
1195 if (idcode == 0) filelist->dir[0] = 0;
1198 if (filelist->dir[0] == 0) {
1200 /* make directories */
1201 #ifdef WITH_FREESTYLE
1202 filelist->numfiles = 24;
1204 filelist->numfiles = 23;
1206 filelist->filelist = (struct direntry *)malloc(filelist->numfiles * sizeof(struct direntry));
1208 for (a = 0; a < filelist->numfiles; a++) {
1209 memset(&(filelist->filelist[a]), 0, sizeof(struct direntry));
1210 filelist->filelist[a].type |= S_IFDIR;
1213 filelist->filelist[0].relname = BLI_strdup("..");
1214 filelist->filelist[1].relname = BLI_strdup("Scene");
1215 filelist->filelist[2].relname = BLI_strdup("Object");
1216 filelist->filelist[3].relname = BLI_strdup("Mesh");
1217 filelist->filelist[4].relname = BLI_strdup("Curve");
1218 filelist->filelist[5].relname = BLI_strdup("Metaball");
1219 filelist->filelist[6].relname = BLI_strdup("Material");
1220 filelist->filelist[7].relname = BLI_strdup("Texture");
1221 filelist->filelist[8].relname = BLI_strdup("Image");
1222 filelist->filelist[9].relname = BLI_strdup("Ika");
1223 filelist->filelist[10].relname = BLI_strdup("Wave");
1224 filelist->filelist[11].relname = BLI_strdup("Lattice");
1225 filelist->filelist[12].relname = BLI_strdup("Lamp");
1226 filelist->filelist[13].relname = BLI_strdup("Camera");
1227 filelist->filelist[14].relname = BLI_strdup("Ipo");
1228 filelist->filelist[15].relname = BLI_strdup("World");
1229 filelist->filelist[16].relname = BLI_strdup("Screen");
1230 filelist->filelist[17].relname = BLI_strdup("VFont");
1231 filelist->filelist[18].relname = BLI_strdup("Text");
1232 filelist->filelist[19].relname = BLI_strdup("Armature");
1233 filelist->filelist[20].relname = BLI_strdup("Action");
1234 filelist->filelist[21].relname = BLI_strdup("NodeTree");
1235 filelist->filelist[22].relname = BLI_strdup("Speaker");
1236 #ifdef WITH_FREESTYLE
1237 filelist->filelist[23].relname = BLI_strdup("FreestyleLineStyle");
1243 idcode = groupname_to_code(filelist->dir);
1245 lb = which_libbase(G.main, idcode);
1246 if (lb == NULL) return;
1249 filelist->numfiles = 0;
1251 if (!filelist->filter_data.hide_dot || id->name[2] != '.') {
1252 filelist->numfiles++;
1258 /* XXXXX TODO: if databrowse F4 or append/link filelist->hide_parent has to be set */
1259 if (!filelist->filter_data.hide_parent) filelist->numfiles += 1;
1260 filelist->filelist = filelist->numfiles > 0 ? (struct direntry *)malloc(filelist->numfiles * sizeof(struct direntry)) : NULL;
1262 files = filelist->filelist;
1264 if (!filelist->filter_data.hide_parent) {
1265 memset(&(filelist->filelist[0]), 0, sizeof(struct direntry));
1266 filelist->filelist[0].relname = BLI_strdup("..");
1267 filelist->filelist[0].type |= S_IFDIR;
1278 if (!filelist->filter_data.hide_dot || id->name[2] != '.') {
1279 memset(files, 0, sizeof(struct direntry));
1280 if (id->lib == NULL) {
1281 files->relname = BLI_strdup(id->name + 2);
1284 files->relname = MEM_mallocN(FILE_MAX + (MAX_ID_NAME - 2), "filename for lib");
1285 BLI_snprintf(files->relname, FILE_MAX + (MAX_ID_NAME - 2) + 3, "%s | %s", id->lib->name, id->name + 2);
1287 files->type |= S_IFREG;
1288 #if 0 /* XXXXX TODO show the selection status of the objects */
1289 if (!filelist->has_func) { /* F4 DATA BROWSE */
1290 if (idcode == ID_OB) {
1291 if ( ((Object *)id)->flag & SELECT) files->selflag |= SELECTED_FILE;
1293 else if (idcode == ID_SCE) {
1294 if ( ((Scene *)id)->r.scemode & R_BG_RENDER) files->selflag |= SELECTED_FILE;
1298 files->nr = totbl + 1;
1300 fake = id->flag & LIB_FAKEUSER;
1301 if (idcode == ID_MA || idcode == ID_TE || idcode == ID_LA || idcode == ID_WO || idcode == ID_IM) {
1302 files->flags |= IMAGEFILE;
1304 if (id->lib && fake) BLI_snprintf(files->extra, sizeof(files->extra), "LF %d", id->us);
1305 else if (id->lib) BLI_snprintf(files->extra, sizeof(files->extra), "L %d", id->us);
1306 else if (fake) BLI_snprintf(files->extra, sizeof(files->extra), "F %d", id->us);
1307 else BLI_snprintf(files->extra, sizeof(files->extra), " %d", id->us);
1310 if (totlib == 0) firstlib = files;
1322 /* only qsort of library blocks */
1324 qsort(firstlib, totlib, sizeof(struct direntry), compare_name);
1329 /* ********** Thumbnails job ********** */
1331 typedef struct ThumbnailJob {
1332 ListBase loadimages;
1333 ImBuf *static_icons_buffers[BIFICONID_LAST];
1335 const short *do_update;
1336 struct FileList *filelist;
1340 bool filelist_need_thumbnails(FileList *filelist)
1342 return filelist->need_thumbnails;
1345 static void thumbnail_joblist_free(ThumbnailJob *tj)
1347 FileImage *limg = tj->loadimages.first;
1349 /* free the images not yet copied to the filelist -> these will get freed with the filelist */
1350 for (; limg; limg = limg->next) {
1351 if ((limg->img) && (!limg->done)) {
1352 IMB_freeImBuf(limg->img);
1355 BLI_freelistN(&tj->loadimages);
1358 static void thumbnails_startjob(void *tjv, short *stop, short *do_update, float *UNUSED(progress))
1360 ThumbnailJob *tj = tjv;
1361 FileImage *limg = tj->loadimages.first;
1364 tj->do_update = do_update;
1366 while ((*stop == 0) && (limg)) {
1367 if (limg->flags & IMAGEFILE) {
1368 limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_IMAGE);
1370 else if (limg->flags & (BLENDERFILE | BLENDERFILE_BACKUP)) {
1371 limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_BLEND);
1373 else if (limg->flags & MOVIEFILE) {
1374 limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_MOVIE);
1376 /* remember that file can't be loaded via IMB_open_anim */
1377 limg->flags &= ~MOVIEFILE;
1378 limg->flags |= MOVIEFILE_ICON;
1387 static void thumbnails_update(void *tjv)
1389 ThumbnailJob *tj = tjv;
1391 if (tj->filelist && tj->filelist->filelist) {
1392 FileImage *limg = tj->loadimages.first;
1394 if (!limg->done && limg->img) {
1395 tj->filelist->filelist[limg->index].image = limg->img;
1396 /* update flag for movie files where thumbnail can't be created */
1397 if (limg->flags & MOVIEFILE_ICON) {
1398 tj->filelist->filelist[limg->index].flags &= ~MOVIEFILE;
1399 tj->filelist->filelist[limg->index].flags |= MOVIEFILE_ICON;
1408 static void thumbnails_endjob(void *tjv)
1410 ThumbnailJob *tj = tjv;
1413 tj->filelist->need_thumbnails = false;
1417 static void thumbnails_free(void *tjv)
1419 ThumbnailJob *tj = tjv;
1420 thumbnail_joblist_free(tj);
1425 void thumbnails_start(FileList *filelist, const bContext *C)
1431 /* prepare job data */
1432 tj = MEM_callocN(sizeof(ThumbnailJob), "thumbnails\n");
1433 tj->filelist = filelist;
1434 for (idx = 0; idx < filelist->numfiles; idx++) {
1435 if (!filelist->filelist[idx].image) {
1436 if ((filelist->filelist[idx].flags & (IMAGEFILE | MOVIEFILE | BLENDERFILE | BLENDERFILE_BACKUP))) {
1437 FileImage *limg = MEM_callocN(sizeof(FileImage), "loadimage");
1438 BLI_strncpy(limg->path, filelist->filelist[idx].path, FILE_MAX);
1440 limg->flags = filelist->filelist[idx].flags;
1441 BLI_addtail(&tj->loadimages, limg);
1446 BKE_reports_init(&tj->reports, RPT_PRINT);
1449 wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), filelist, "Thumbnails",
1450 0, WM_JOB_TYPE_FILESEL_THUMBNAIL);
1451 WM_jobs_customdata_set(wm_job, tj, thumbnails_free);
1452 WM_jobs_timer(wm_job, 0.5, NC_WINDOW, NC_WINDOW);
1453 WM_jobs_callbacks(wm_job, thumbnails_startjob, NULL, thumbnails_update, thumbnails_endjob);
1456 WM_jobs_start(CTX_wm_manager(C), wm_job);
1459 void thumbnails_stop(wmWindowManager *wm, FileList *filelist)
1461 WM_jobs_kill_type(wm, filelist, WM_JOB_TYPE_FILESEL_THUMBNAIL);
1464 int thumbnails_running(wmWindowManager *wm, FileList *filelist)
1466 return WM_jobs_test(wm, filelist, WM_JOB_TYPE_FILESEL_THUMBNAIL);