Changeset View
Standalone View
source/blender/editors/space_file/filelist.c
| Show All 29 Lines | |||||
| */ | */ | ||||
| /* global includes */ | /* global includes */ | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <math.h> | #include <math.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include <sys/stat.h> | |||||
| #include <time.h> | |||||
| #ifndef WIN32 | #ifndef WIN32 | ||||
| # include <unistd.h> | # include <unistd.h> | ||||
| #else | #else | ||||
| # include <io.h> | # include <io.h> | ||||
| # include <direct.h> | # include <direct.h> | ||||
| #endif | #endif | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "BLI_blenlib.h" | #include "BLI_blenlib.h" | ||||
| #include "BLI_fileops.h" | |||||
| #include "BLI_fileops_types.h" | #include "BLI_fileops_types.h" | ||||
| #include "BLI_fnmatch.h" | #include "BLI_fnmatch.h" | ||||
| #include "BLI_ghash.h" | |||||
| #include "BLI_hash_md5.h" | |||||
| #include "BLI_linklist.h" | #include "BLI_linklist.h" | ||||
| #include "BLI_math.h" | |||||
| #include "BLI_stack.h" | |||||
| #include "BLI_task.h" | |||||
| #include "BLI_threads.h" | |||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #ifdef WIN32 | #ifdef WIN32 | ||||
| # include "BLI_winstuff.h" | # include "BLI_winstuff.h" | ||||
| #endif | #endif | ||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| #include "BKE_library.h" | #include "BKE_library.h" | ||||
| #include "BKE_icons.h" | #include "BKE_icons.h" | ||||
| #include "BKE_idcode.h" | #include "BKE_idcode.h" | ||||
| #include "BKE_main.h" | #include "BKE_main.h" | ||||
| #include "BKE_report.h" | |||||
| #include "BLO_readfile.h" | #include "BLO_readfile.h" | ||||
| #include "DNA_space_types.h" | #include "DNA_space_types.h" | ||||
| #include "ED_datafiles.h" | #include "ED_datafiles.h" | ||||
| #include "ED_fileselect.h" | #include "ED_fileselect.h" | ||||
| #include "ED_screen.h" | |||||
| #include "IMB_imbuf.h" | #include "IMB_imbuf.h" | ||||
| #include "IMB_imbuf_types.h" | #include "IMB_imbuf_types.h" | ||||
| #include "IMB_thumbs.h" | #include "IMB_thumbs.h" | ||||
| #include "PIL_time.h" | #include "PIL_time.h" | ||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "WM_types.h" | #include "WM_types.h" | ||||
| #include "UI_resources.h" | #include "UI_resources.h" | ||||
| #include "UI_interface_icons.h" | |||||
| #include "atomic_ops.h" | |||||
| #include "filelist.h" | #include "filelist.h" | ||||
| /* ----------------- FOLDERLIST (previous/next) -------------- */ | /* ----------------- FOLDERLIST (previous/next) -------------- */ | ||||
| typedef struct FolderList { | typedef struct FolderList { | ||||
| struct FolderList *next, *prev; | struct FolderList *next, *prev; | ||||
| ▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | if (folderlist) { | ||||
| return folderlistn; | return folderlistn; | ||||
| } | } | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* ------------------FILELIST------------------------ */ | /* ------------------FILELIST------------------------ */ | ||||
| struct FileList; | typedef struct FileListInternEntry { | ||||
| struct FileListInternEntry *next, *prev; | |||||
| char uuid[16]; /* ASSET_UUID_LENGTH */ | |||||
| int typeflag; /* eFileSel_File_Types */ | |||||
| int blentype; /* ID type, in case typeflag has FILE_TYPE_BLENDERLIB set. */ | |||||
| char *relpath; | |||||
| char *name; /* not striclty needed, but used during sorting, avoids to have to recompute it there... */ | |||||
| BLI_stat_t st; | |||||
| } FileListInternEntry; | |||||
| typedef struct FileListIntern { | |||||
| ListBase entries; /* FileListInternEntry items. */ | |||||
| FileListInternEntry **filtered; | |||||
| char curr_uuid[16]; /* Used to generate uuid during internal listing. */ | |||||
| } FileListIntern; | |||||
| #define FILELIST_ENTRYCACHESIZE_DEFAULT 1024 /* Keep it a power of two! */ | |||||
| typedef struct FileListEntryCache { | |||||
| size_t size; /* The size of the cache... */ | |||||
| int flags; | |||||
| /* Block cache: all entries between start and end index. used for part of the list on diplay. */ | |||||
| FileDirEntry **block_entries; | |||||
| int block_start_index, block_end_index, block_center_index, block_cursor; | |||||
| /* Misc cache: random indices, FIFO behavior. | |||||
| * Note: Not 100% sure we actually need that, time will say. */ | |||||
| int misc_cursor; | |||||
| int *misc_entries_indices; | |||||
| GHash *misc_entries; | |||||
| typedef struct FileImage { | /* Allows to quickly get a cached entry from its UUID. */ | ||||
| struct FileImage *next, *prev; | GHash *uuids; | ||||
| /* Previews handling. */ | |||||
| TaskPool *previews_pool; | |||||
| ThreadQueue *previews_todo; | |||||
| ThreadQueue *previews_done; | |||||
| double previews_timestamp; | |||||
| int previews_pending; | |||||
| } FileListEntryCache; | |||||
| /* FileListCache.flags */ | |||||
| enum { | |||||
| FLC_IS_INIT = 1 << 0, | |||||
| FLC_PREVIEWS_ACTIVE = 1 << 1, | |||||
| }; | |||||
| typedef struct FileListEntryPreview { | |||||
| char path[FILE_MAX]; | char path[FILE_MAX]; | ||||
| unsigned int flags; | unsigned int flags; | ||||
| int index; | int index; | ||||
| short done; | |||||
| ImBuf *img; | ImBuf *img; | ||||
| } FileImage; | } FileListEntryPreview; | ||||
| typedef struct FileListFilter { | typedef struct FileListFilter { | ||||
| bool hide_dot; | |||||
| bool hide_parent; | |||||
| unsigned int filter; | unsigned int filter; | ||||
| unsigned int filter_id; | |||||
| char filter_glob[64]; | char filter_glob[64]; | ||||
| char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */ | char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */ | ||||
| short flags; | |||||
| } FileListFilter; | } FileListFilter; | ||||
| /* FileListFilter.flags */ | |||||
| enum { | |||||
| FLF_HIDE_DOT = 1 << 0, | |||||
| FLF_HIDE_PARENT = 1 << 1, | |||||
| FLF_HIDE_LIB_DIR = 1 << 2, | |||||
| }; | |||||
| typedef struct FileList { | typedef struct FileList { | ||||
| struct direntry *filelist; | FileDirEntryArr filelist; | ||||
| int numfiles; | |||||
| char dir[FILE_MAX]; | |||||
| short prv_w; | short prv_w; | ||||
| short prv_h; | short prv_h; | ||||
| bool changed; | short flags; | ||||
| short sort; | short sort; | ||||
| bool need_sorting; | |||||
| FileListFilter filter_data; | FileListFilter filter_data; | ||||
| int *fidx; /* Also used to detect when we need to filter! */ | |||||
| int numfiltered; | |||||
| bool need_thumbnails; | struct FileListIntern filelist_intern; | ||||
| struct FileListEntryCache filelist_cache; | |||||
| /* We need to keep those info outside of actual filelist items, because those are no more persistent | |||||
| * (only generated on demand, and freed as soon as possible). | |||||
| * Persistent part (mere list of paths + stat info) is kept as small as possible, and filebrowser-agnostic. | |||||
| */ | |||||
| GHash *selection_state; | |||||
| short max_recursion; | |||||
| short recursion_level; | |||||
| struct BlendHandle *libfiledata; | struct BlendHandle *libfiledata; | ||||
| void (*readf)(struct FileList *); | /* Set given path as root directory, may change given string in place to a valid value. */ | ||||
| bool (*filterf)(struct direntry *, const char *, FileListFilter *); | void (*checkdirf)(struct FileList *, char *); | ||||
| /* Fill filelist (to be called by read job). */ | |||||
| void (*read_jobf)(struct FileList *, const char *, short *, short *, float *, ThreadMutex *); | |||||
| /* Filter an entry of current filelist. */ | |||||
| bool (*filterf)(struct FileListInternEntry *, const char *, FileListFilter *); | |||||
| } FileList; | } FileList; | ||||
| /* FileList.flags */ | |||||
| enum { | |||||
| FL_FORCE_RESET = 1 << 0, | |||||
| FL_IS_READY = 1 << 1, | |||||
| FL_IS_PENDING = 1 << 2, | |||||
| FL_NEED_SORTING = 1 << 3, | |||||
| FL_NEED_FILTERING = 1 << 4, | |||||
| }; | |||||
| #define SPECIAL_IMG_SIZE 48 | #define SPECIAL_IMG_SIZE 48 | ||||
| #define SPECIAL_IMG_ROWS 4 | #define SPECIAL_IMG_ROWS 4 | ||||
| #define SPECIAL_IMG_COLS 4 | #define SPECIAL_IMG_COLS 4 | ||||
| enum { | enum { | ||||
| SPECIAL_IMG_FOLDER = 0, | SPECIAL_IMG_FOLDER = 0, | ||||
| SPECIAL_IMG_PARENT = 1, | SPECIAL_IMG_PARENT = 1, | ||||
| SPECIAL_IMG_REFRESH = 2, | SPECIAL_IMG_REFRESH = 2, | ||||
| SPECIAL_IMG_BLENDFILE = 3, | SPECIAL_IMG_BLENDFILE = 3, | ||||
| SPECIAL_IMG_SOUNDFILE = 4, | SPECIAL_IMG_SOUNDFILE = 4, | ||||
| SPECIAL_IMG_MOVIEFILE = 5, | SPECIAL_IMG_MOVIEFILE = 5, | ||||
| SPECIAL_IMG_PYTHONFILE = 6, | SPECIAL_IMG_PYTHONFILE = 6, | ||||
| SPECIAL_IMG_TEXTFILE = 7, | SPECIAL_IMG_TEXTFILE = 7, | ||||
| SPECIAL_IMG_FONTFILE = 8, | SPECIAL_IMG_FONTFILE = 8, | ||||
| SPECIAL_IMG_UNKNOWNFILE = 9, | SPECIAL_IMG_UNKNOWNFILE = 9, | ||||
| SPECIAL_IMG_LOADING = 10, | SPECIAL_IMG_LOADING = 10, | ||||
| SPECIAL_IMG_BACKUP = 11, | SPECIAL_IMG_BACKUP = 11, | ||||
| SPECIAL_IMG_MAX | SPECIAL_IMG_MAX | ||||
| }; | }; | ||||
| static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX]; | static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX]; | ||||
| static void filelist_from_main(struct FileList *filelist); | static void filelist_readjob_main(struct FileList *, const char *, short *, short *, float *, ThreadMutex *); | ||||
| static void filelist_from_library(struct FileList *filelist); | static void filelist_readjob_lib(struct FileList *, const char *, short *, short *, float *, ThreadMutex *); | ||||
| static void filelist_readjob_dir(struct FileList *, const char *, short *, short *, float *, ThreadMutex *); | |||||
| static void filelist_read_main(struct FileList *filelist); | |||||
| static void filelist_read_library(struct FileList *filelist); | /* helper, could probably go in BKE actually? */ | ||||
| static void filelist_read_dir(struct FileList *filelist); | static int groupname_to_code(const char *group); | ||||
| static unsigned int groupname_to_filter_id(const char *group); | |||||
| static void filelist_filter_clear(FileList *filelist); | static void filelist_filter_clear(FileList *filelist); | ||||
| static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size); | |||||
| /* ********** Sort helpers ********** */ | /* ********** Sort helpers ********** */ | ||||
| static bool compare_is_directory(const struct direntry *entry) | static int compare_direntry_generic(const FileListInternEntry *entry1, const FileListInternEntry *entry2) | ||||
| { | |||||
| /* for library browse .blend files may be treated as directories, but | |||||
| * for sorting purposes they should be considered regular files */ | |||||
| if (S_ISDIR(entry->type)) | |||||
| return !(entry->flags & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)); | |||||
| return false; | |||||
| } | |||||
| static int compare_direntry_generic(const struct direntry *entry1, const struct direntry *entry2) | |||||
| { | { | ||||
| /* type is equal to stat.st_mode */ | /* type is equal to stat.st_mode */ | ||||
| if (compare_is_directory(entry1)) { | if (entry1->typeflag & FILE_TYPE_DIR) { | ||||
| if (compare_is_directory(entry2) == 0) { | if (entry2->typeflag & FILE_TYPE_DIR) { | ||||
| return -1; | /* If both entries are tagged as dirs, we make a 'sub filter' that shows first the real dirs, | ||||
| * then libs (.blend files), then categories in libs. */ | |||||
| if (entry1->typeflag & FILE_TYPE_BLENDERLIB) { | |||||
| if (!(entry2->typeflag & FILE_TYPE_BLENDERLIB)) { | |||||
| return 1; | |||||
| } | |||||
| } | } | ||||
| else if (entry2->typeflag & FILE_TYPE_BLENDERLIB) { | |||||
| return -1; | |||||
| } | } | ||||
| else if (compare_is_directory(entry2)) { | else if (entry1->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { | ||||
| if (!(entry2->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| } | |||||
| if (S_ISREG(entry1->type)) { | else if (entry2->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { | ||||
| if (!S_ISREG(entry2->type)) { | return -1; | ||||
| } | |||||
| } | |||||
| else { | |||||
| return -1; | return -1; | ||||
| } | } | ||||
| } | } | ||||
| else if (S_ISREG(entry2->type)) { | else if (entry2->typeflag & FILE_TYPE_DIR) { | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return -1; | |||||
| if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return 1; | |||||
| /* make sure "." and ".." are always first */ | /* make sure "." and ".." are always first */ | ||||
| if (FILENAME_IS_CURRENT(entry1->relname)) return -1; | if (FILENAME_IS_CURRENT(entry1->relpath)) return -1; | ||||
| if (FILENAME_IS_CURRENT(entry2->relname)) return 1; | if (FILENAME_IS_CURRENT(entry2->relpath)) return 1; | ||||
| if (FILENAME_IS_PARENT(entry1->relname)) return -1; | if (FILENAME_IS_PARENT(entry1->relpath)) return -1; | ||||
| if (FILENAME_IS_PARENT(entry2->relname)) return 1; | if (FILENAME_IS_PARENT(entry2->relpath)) return 1; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int compare_name(const void *a1, const void *a2) | static int compare_name(void *UNUSED(user_data), const void *a1, const void *a2) | ||||
| { | { | ||||
| const struct direntry *entry1 = a1, *entry2 = a2; | const FileListInternEntry *entry1 = a1; | ||||
| const FileListInternEntry *entry2 = a2; | |||||
| char *name1, *name2; | |||||
| int ret; | int ret; | ||||
| if ((ret = compare_direntry_generic(entry1, entry2))) { | if ((ret = compare_direntry_generic(entry1, entry2))) { | ||||
| return ret; | return ret; | ||||
| } | } | ||||
| return (BLI_natstrcmp(entry1->relname, entry2->relname)); | name1 = entry1->name; | ||||
| name2 = entry2->name; | |||||
| return BLI_natstrcmp(name1, name2); | |||||
| } | } | ||||
| static int compare_date(const void *a1, const void *a2) | static int compare_date(void *UNUSED(user_data), const void *a1, const void *a2) | ||||
| { | { | ||||
| const struct direntry *entry1 = a1, *entry2 = a2; | const FileListInternEntry *entry1 = a1; | ||||
| const FileListInternEntry *entry2 = a2; | |||||
| char *name1, *name2; | |||||
| int64_t time1, time2; | |||||
| int ret; | int ret; | ||||
| if ((ret = compare_direntry_generic(entry1, entry2))) { | if ((ret = compare_direntry_generic(entry1, entry2))) { | ||||
| return ret; | return ret; | ||||
| } | } | ||||
| if (entry1->s.st_mtime < entry2->s.st_mtime) return 1; | time1 = (int64_t)entry1->st.st_mtime; | ||||
| if (entry1->s.st_mtime > entry2->s.st_mtime) return -1; | time2 = (int64_t)entry2->st.st_mtime; | ||||
| if (time1 < time2) return 1; | |||||
| if (time1 > time2) return -1; | |||||
| return BLI_natstrcmp(entry1->relname, entry2->relname); | name1 = entry1->name; | ||||
| name2 = entry2->name; | |||||
| return BLI_natstrcmp(name1, name2); | |||||
| } | } | ||||
| static int compare_size(const void *a1, const void *a2) | static int compare_size(void *UNUSED(user_data), const void *a1, const void *a2) | ||||
| { | { | ||||
| const struct direntry *entry1 = a1, *entry2 = a2; | const FileListInternEntry *entry1 = a1; | ||||
| const FileListInternEntry *entry2 = a2; | |||||
| char *name1, *name2; | |||||
| uint64_t size1, size2; | |||||
| int ret; | int ret; | ||||
| if ((ret = compare_direntry_generic(entry1, entry2))) { | if ((ret = compare_direntry_generic(entry1, entry2))) { | ||||
| return ret; | return ret; | ||||
| } | } | ||||
| if (entry1->s.st_size < entry2->s.st_size) return 1; | size1 = entry1->st.st_size; | ||||
| if (entry1->s.st_size > entry2->s.st_size) return -1; | size2 = entry2->st.st_size; | ||||
| if (size1 < size2) return 1; | |||||
| if (size1 > size2) return -1; | |||||
| name1 = entry1->name; | |||||
| name2 = entry2->name; | |||||
| return BLI_natstrcmp(entry1->relname, entry2->relname); | return BLI_natstrcmp(name1, name2); | ||||
| } | } | ||||
| static int compare_extension(const void *a1, const void *a2) | static int compare_extension(void *UNUSED(user_data), const void *a1, const void *a2) | ||||
| { | { | ||||
| const struct direntry *entry1 = a1, *entry2 = a2; | const FileListInternEntry *entry1 = a1; | ||||
| const char *sufix1, *sufix2; | const FileListInternEntry *entry2 = a2; | ||||
| const char *nil = ""; | char *name1, *name2; | ||||
| int ret; | int ret; | ||||
| if ((ret = compare_direntry_generic(entry1, entry2))) { | if ((ret = compare_direntry_generic(entry1, entry2))) { | ||||
| return ret; | return ret; | ||||
| } | } | ||||
| if (!(sufix1 = strstr(entry1->relname, ".blend.gz"))) | if ((entry1->typeflag & FILE_TYPE_BLENDERLIB) && !(entry2->typeflag & FILE_TYPE_BLENDERLIB)) return -1; | ||||
| sufix1 = strrchr(entry1->relname, '.'); | if (!(entry1->typeflag & FILE_TYPE_BLENDERLIB) && (entry2->typeflag & FILE_TYPE_BLENDERLIB)) return 1; | ||||
| if (!(sufix2 = strstr(entry2->relname, ".blend.gz"))) | if ((entry1->typeflag & FILE_TYPE_BLENDERLIB) && (entry2->typeflag & FILE_TYPE_BLENDERLIB)) { | ||||
| sufix2 = strrchr(entry2->relname, '.'); | if ((entry1->typeflag & FILE_TYPE_DIR) && !(entry2->typeflag & FILE_TYPE_DIR)) return 1; | ||||
| if (!sufix1) sufix1 = nil; | if (!(entry1->typeflag & FILE_TYPE_DIR) && (entry2->typeflag & FILE_TYPE_DIR)) return -1; | ||||
| if (!sufix2) sufix2 = nil; | if (entry1->blentype < entry2->blentype) return -1; | ||||
| if (entry1->blentype > entry2->blentype) return 1; | |||||
| } | |||||
| else { | |||||
| const char *sufix1, *sufix2; | |||||
| return BLI_strcasecmp(sufix1, sufix2); | if (!(sufix1 = strstr(entry1->relpath, ".blend.gz"))) | ||||
| sufix1 = strrchr(entry1->relpath, '.'); | |||||
| if (!(sufix2 = strstr(entry2->relpath, ".blend.gz"))) | |||||
| sufix2 = strrchr(entry2->relpath, '.'); | |||||
| if (!sufix1) sufix1 = ""; | |||||
| if (!sufix2) sufix2 = ""; | |||||
| if ((ret = BLI_strcasecmp(sufix1, sufix2))) { | |||||
| return ret; | |||||
| } | |||||
| } | } | ||||
| bool filelist_need_sorting(struct FileList *filelist) | name1 = entry1->name; | ||||
| { | name2 = entry2->name; | ||||
| return filelist->need_sorting && (filelist->sort != FILE_SORT_NONE); | |||||
| return BLI_natstrcmp(name1, name2); | |||||
| } | } | ||||
| void filelist_sort(struct FileList *filelist) | void filelist_sort(struct FileList *filelist) | ||||
| { | { | ||||
| if (filelist_need_sorting(filelist)) { | if ((filelist->flags & FL_NEED_SORTING) && (filelist->sort != FILE_SORT_NONE)) { | ||||
| filelist->need_sorting = false; | |||||
| switch (filelist->sort) { | switch (filelist->sort) { | ||||
| case FILE_SORT_ALPHA: | case FILE_SORT_ALPHA: | ||||
| qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_name); | BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_name, NULL); | ||||
| break; | break; | ||||
| case FILE_SORT_TIME: | case FILE_SORT_TIME: | ||||
| qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_date); | BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_date, NULL); | ||||
| break; | break; | ||||
| case FILE_SORT_SIZE: | case FILE_SORT_SIZE: | ||||
| qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_size); | BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_size, NULL); | ||||
| break; | break; | ||||
| case FILE_SORT_EXTENSION: | case FILE_SORT_EXTENSION: | ||||
| qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_extension); | BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_extension, NULL); | ||||
| break; | break; | ||||
| case FILE_SORT_NONE: /* Should never reach this point! */ | case FILE_SORT_NONE: /* Should never reach this point! */ | ||||
| default: | default: | ||||
| BLI_assert(0); | BLI_assert(0); | ||||
| return; | |||||
| } | } | ||||
| filelist_filter_clear(filelist); | filelist_filter_clear(filelist); | ||||
| filelist->flags &= ~FL_NEED_SORTING; | |||||
| } | } | ||||
| } | } | ||||
| void filelist_setsorting(struct FileList *filelist, const short sort) | void filelist_setsorting(struct FileList *filelist, const short sort) | ||||
| { | { | ||||
| if (filelist->sort != sort) { | if (filelist->sort != sort) { | ||||
| filelist->sort = sort; | filelist->sort = sort; | ||||
| filelist->need_sorting = true; | filelist->flags |= FL_NEED_SORTING; | ||||
| } | } | ||||
| } | } | ||||
| /* ********** Filter helpers ********** */ | /* ********** Filter helpers ********** */ | ||||
| static bool is_hidden_file(const char *filename, FileListFilter *filter) | static bool is_hidden_file(const char *filename, FileListFilter *filter) | ||||
| { | { | ||||
| char *sep = (char *)BLI_last_slash(filename); | |||||
| bool is_hidden = false; | bool is_hidden = false; | ||||
| if (filter->hide_dot) { | if (filter->flags & FLF_HIDE_DOT) { | ||||
| if (filename[0] == '.' && filename[1] != '.' && filename[1] != '\0') { | if (filename[0] == '.' && filename[1] != '.' && filename[1] != '\0') { | ||||
| is_hidden = true; /* ignore .file */ | is_hidden = true; /* ignore .file */ | ||||
| } | } | ||||
| else { | else { | ||||
| int len = strlen(filename); | int len = strlen(filename); | ||||
| if ((len > 0) && (filename[len - 1] == '~')) { | if ((len > 0) && (filename[len - 1] == '~')) { | ||||
| is_hidden = true; /* ignore file~ */ | is_hidden = true; /* ignore file~ */ | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if (!is_hidden && filter->hide_parent) { | if (!is_hidden && (filter->flags & FLF_HIDE_PARENT)) { | ||||
| if (filename[0] == '.' && filename[1] == '.' && filename[2] == '\0') { | if (filename[0] == '.' && filename[1] == '.' && filename[2] == '\0') { | ||||
| is_hidden = true; /* ignore .. */ | is_hidden = true; /* ignore .. */ | ||||
| } | } | ||||
| } | } | ||||
| if (!is_hidden && ((filename[0] == '.') && (filename[1] == '\0'))) { | if (!is_hidden && ((filename[0] == '.') && (filename[1] == '\0'))) { | ||||
| is_hidden = true; /* ignore . */ | is_hidden = true; /* ignore . */ | ||||
| } | } | ||||
| /* filename might actually be a piece of path, in which case we have to check all its parts. */ | |||||
| if (!is_hidden && sep) { | |||||
| char tmp_filename[FILE_MAX_LIBEXTRA]; | |||||
| BLI_strncpy(tmp_filename, filename, sizeof(tmp_filename)); | |||||
| sep = tmp_filename + (sep - filename); | |||||
| while (sep) { | |||||
| BLI_assert(sep[1] != '\0'); | |||||
| if (is_hidden_file(sep + 1, filter)) { | |||||
| is_hidden = true; | |||||
| break; | |||||
| } | |||||
| *sep = '\0'; | |||||
| sep = (char *)BLI_last_slash(tmp_filename); | |||||
| } | |||||
| } | |||||
| return is_hidden; | return is_hidden; | ||||
| } | } | ||||
| static bool is_filtered_file(struct direntry *file, const char *UNUSED(root), FileListFilter *filter) | static bool is_filtered_file(FileListInternEntry *file, const char *UNUSED(root), FileListFilter *filter) | ||||
| { | { | ||||
| bool is_filtered = !is_hidden_file(file->relname, filter); | bool is_filtered = !is_hidden_file(file->relpath, filter); | ||||
| if (is_filtered && filter->filter && !FILENAME_IS_CURRPAR(file->relname)) { | if (is_filtered && filter->filter && !FILENAME_IS_CURRPAR(file->relpath)) { | ||||
| if ((file->type & S_IFDIR) && !(filter->filter & FILE_TYPE_FOLDER)) { | if (file->typeflag & FILE_TYPE_DIR) { | ||||
| if (file->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { | |||||
| if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { | |||||
| is_filtered = false; | is_filtered = false; | ||||
| } | } | ||||
| if (!(file->type & S_IFDIR) && !(file->flags & filter->filter)) { | } | ||||
| else { | |||||
| if (!(filter->filter & FILE_TYPE_FOLDER)) { | |||||
| is_filtered = false; | is_filtered = false; | ||||
| } | } | ||||
| } | |||||
| } | |||||
| else { | |||||
| if (!(file->typeflag & filter->filter)) { | |||||
| is_filtered = false; | |||||
| } | |||||
| } | |||||
| if (is_filtered && (filter->filter_search[0] != '\0')) { | if (is_filtered && (filter->filter_search[0] != '\0')) { | ||||
| if (fnmatch(filter->filter_search, file->relname, FNM_CASEFOLD) != 0) { | if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { | ||||
| is_filtered = false; | is_filtered = false; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return is_filtered; | return is_filtered; | ||||
| } | } | ||||
| static bool is_filtered_lib(struct direntry *file, const char *root, FileListFilter *filter) | static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter) | ||||
| { | { | ||||
| bool is_filtered = !is_hidden_file(file->relname, filter); | bool is_filtered; | ||||
| char dir[FILE_MAXDIR], group[BLO_GROUP_MAX]; | char path[FILE_MAX_LIBEXTRA], dir[FILE_MAXDIR], *group, *name; | ||||
| if (BLO_is_a_library(root, dir, group)) { | BLI_join_dirfile(path, sizeof(path), root, file->relpath); | ||||
| is_filtered = !is_hidden_file(file->relname, filter); | |||||
| if (is_filtered && filter->filter && !FILENAME_IS_CURRPAR(file->relname)) { | if (BLO_library_path_explode(path, dir, &group, &name)) { | ||||
| is_filtered = !is_hidden_file(file->relpath, filter); | |||||
| if (is_filtered && filter->filter && !FILENAME_IS_CURRPAR(file->relpath)) { | |||||
| if (file->typeflag & FILE_TYPE_DIR) { | |||||
| if (file->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { | |||||
| if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { | |||||
| is_filtered = false; | |||||
| } | |||||
| } | |||||
| else { | |||||
| if (!(filter->filter & FILE_TYPE_FOLDER)) { | |||||
| is_filtered = false; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (is_filtered && group) { | |||||
| if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) { | |||||
| is_filtered = false; | |||||
| } | |||||
| else { | |||||
| unsigned int filter_id = groupname_to_filter_id(group); | |||||
| if (!(filter_id & filter->filter_id)) { | |||||
| is_filtered = false; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (is_filtered && (filter->filter_search[0] != '\0')) { | if (is_filtered && (filter->filter_search[0] != '\0')) { | ||||
| if (fnmatch(filter->filter_search, file->relname, FNM_CASEFOLD) != 0) { | if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { | ||||
| is_filtered = false; | is_filtered = false; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| is_filtered = is_filtered_file(file, root, filter); | is_filtered = is_filtered_file(file, root, filter); | ||||
| } | } | ||||
| return is_filtered; | return is_filtered; | ||||
| } | } | ||||
| static bool is_filtered_main(struct direntry *file, const char *UNUSED(dir), FileListFilter *filter) | static bool is_filtered_main(FileListInternEntry *file, const char *UNUSED(dir), FileListFilter *filter) | ||||
| { | { | ||||
| return !is_hidden_file(file->relname, filter); | return !is_hidden_file(file->relpath, filter); | ||||
| } | } | ||||
| static void filelist_filter_clear(FileList *filelist) | static void filelist_filter_clear(FileList *filelist) | ||||
| { | { | ||||
| MEM_SAFE_FREE(filelist->fidx); | filelist->flags |= FL_NEED_FILTERING; | ||||
| filelist->numfiltered = 0; | |||||
| } | } | ||||
| void filelist_filter(FileList *filelist) | void filelist_filter(FileList *filelist) | ||||
| { | { | ||||
| int num_filtered = 0; | int num_filtered = 0; | ||||
| int *fidx_tmp; | const int num_files = filelist->filelist.nbr_entries; | ||||
| int i; | FileListInternEntry **filtered_tmp, *file; | ||||
| if (!filelist->filelist) { | if (filelist->filelist.nbr_entries == 0) { | ||||
| return; | return; | ||||
| } | } | ||||
| if (filelist->fidx) { | if (!(filelist->flags & FL_NEED_FILTERING)) { | ||||
| /* Assume it has already been filtered, nothing else to do! */ | /* Assume it has already been filtered, nothing else to do! */ | ||||
| return; | return; | ||||
| } | } | ||||
| fidx_tmp = MEM_mallocN(sizeof(*fidx_tmp) * (size_t)filelist->numfiles, __func__); | filelist->filter_data.flags &= ~FLF_HIDE_LIB_DIR; | ||||
| if (filelist->max_recursion) { | |||||
| /* Never show lib ID 'categories' directories when we are in 'flat' mode, unless | |||||
| * root path is a blend file. */ | |||||
| char dir[FILE_MAXDIR]; | |||||
| if (!filelist_islibrary(filelist, dir, NULL)) { | |||||
| filelist->filter_data.flags |= FLF_HIDE_LIB_DIR; | |||||
| } | |||||
| } | |||||
| filtered_tmp = MEM_mallocN(sizeof(*filtered_tmp) * (size_t)num_files, __func__); | |||||
| /* Filter remap & count how many files are left after filter in a single loop. */ | /* Filter remap & count how many files are left after filter in a single loop. */ | ||||
| for (i = 0; i < filelist->numfiles; ++i) { | for (file = filelist->filelist_intern.entries.first; file; file = file->next) { | ||||
| struct direntry *file = &filelist->filelist[i]; | if (filelist->filterf(file, filelist->filelist.root, &filelist->filter_data)) { | ||||
| filtered_tmp[num_filtered++] = file; | |||||
| if (filelist->filterf(file, filelist->dir, &filelist->filter_data)) { | |||||
| fidx_tmp[num_filtered++] = i; | |||||
| } | } | ||||
| } | } | ||||
| /* Note: maybe we could even accept filelist->fidx to be filelist->numfiles -len allocated? */ | if (filelist->filelist_intern.filtered) { | ||||
| filelist->fidx = MEM_mallocN(sizeof(*filelist->fidx) * (size_t)num_filtered, __func__); | MEM_freeN(filelist->filelist_intern.filtered); | ||||
| memcpy(filelist->fidx, fidx_tmp, sizeof(*filelist->fidx) * (size_t)num_filtered); | } | ||||
| filelist->numfiltered = num_filtered; | filelist->filelist_intern.filtered = MEM_mallocN(sizeof(*filelist->filelist_intern.filtered) * (size_t)num_filtered, | ||||
| __func__); | |||||
| memcpy(filelist->filelist_intern.filtered, filtered_tmp, | |||||
| sizeof(*filelist->filelist_intern.filtered) * (size_t)num_filtered); | |||||
| filelist->filelist.nbr_entries_filtered = num_filtered; | |||||
| // printf("Filetered: %d over %d entries\n", num_filtered, filelist->filelist.nbr_entries); | |||||
| filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size); | |||||
| filelist->flags &= ~FL_NEED_FILTERING; | |||||
| MEM_freeN(fidx_tmp); | MEM_freeN(filtered_tmp); | ||||
| } | } | ||||
| void filelist_setfilter_options(FileList *filelist, const bool hide_dot, const bool hide_parent, | void filelist_setfilter_options(FileList *filelist, const bool hide_dot, const bool hide_parent, | ||||
| const unsigned int filter, | const unsigned int filter, const unsigned int filter_id, | ||||
| const char *filter_glob, const char *filter_search) | const char *filter_glob, const char *filter_search) | ||||
| { | { | ||||
| if ((filelist->filter_data.hide_dot != hide_dot) || | bool update = false; | ||||
| (filelist->filter_data.hide_parent != hide_parent) || | |||||
| (filelist->filter_data.filter != filter) || | |||||
| !STREQ(filelist->filter_data.filter_glob, filter_glob) || | |||||
| (BLI_strcmp_ignore_pad(filelist->filter_data.filter_search, filter_search, '*') != 0)) | |||||
| { | |||||
| filelist->filter_data.hide_dot = hide_dot; | |||||
| filelist->filter_data.hide_parent = hide_parent; | |||||
| if (((filelist->filter_data.flags & FLF_HIDE_DOT) != 0) != (hide_dot != 0)) { | |||||
| filelist->filter_data.flags ^= FLF_HIDE_DOT; | |||||
| update = true; | |||||
| } | |||||
| if (((filelist->filter_data.flags & FLF_HIDE_PARENT) != 0) != (hide_parent != 0)) { | |||||
| filelist->filter_data.flags ^= FLF_HIDE_PARENT; | |||||
| update = true; | |||||
| } | |||||
| if ((filelist->filter_data.filter != filter) || (filelist->filter_data.filter_id != filter_id)) { | |||||
| filelist->filter_data.filter = filter; | filelist->filter_data.filter = filter; | ||||
| filelist->filter_data.filter_id = filter_id; | |||||
| update = true; | |||||
| } | |||||
| if (!STREQ(filelist->filter_data.filter_glob, filter_glob)) { | |||||
| BLI_strncpy(filelist->filter_data.filter_glob, filter_glob, sizeof(filelist->filter_data.filter_glob)); | BLI_strncpy(filelist->filter_data.filter_glob, filter_glob, sizeof(filelist->filter_data.filter_glob)); | ||||
| update = true; | |||||
| } | |||||
| if ((BLI_strcmp_ignore_pad(filelist->filter_data.filter_search, filter_search, '*') != 0)) { | |||||
| BLI_strncpy_ensure_pad(filelist->filter_data.filter_search, filter_search, '*', | BLI_strncpy_ensure_pad(filelist->filter_data.filter_search, filter_search, '*', | ||||
| sizeof(filelist->filter_data.filter_search)); | sizeof(filelist->filter_data.filter_search)); | ||||
| update = true; | |||||
| } | |||||
| /* And now, free filtered data so that we now we have to filter again. */ | if (update) { | ||||
| /* And now, free filtered data so that we know we have to filter again. */ | |||||
| filelist_filter_clear(filelist); | filelist_filter_clear(filelist); | ||||
| } | } | ||||
| } | } | ||||
| /* ********** Icon/image helpers ********** */ | /* ********** Icon/image helpers ********** */ | ||||
| void filelist_init_icons(void) | void filelist_init_icons(void) | ||||
| { | { | ||||
| Show All 38 Lines | |||||
| } | } | ||||
| void filelist_imgsize(struct FileList *filelist, short w, short h) | void filelist_imgsize(struct FileList *filelist, short w, short h) | ||||
| { | { | ||||
| filelist->prv_w = w; | filelist->prv_w = w; | ||||
| filelist->prv_h = h; | filelist->prv_h = h; | ||||
| } | } | ||||
| ImBuf *filelist_getimage(struct FileList *filelist, const int index) | static FileDirEntry *filelist_geticon_get_file(struct FileList *filelist, const int index) | ||||
| { | { | ||||
| ImBuf *ibuf = NULL; | |||||
| int fidx = 0; | |||||
| BLI_assert(G.background == false); | BLI_assert(G.background == false); | ||||
| if ((index < 0) || (index >= filelist->numfiltered)) { | return filelist_file(filelist, index); | ||||
| return NULL; | |||||
| } | } | ||||
| fidx = filelist->fidx[index]; | |||||
| ibuf = filelist->filelist[fidx].image; | |||||
| return ibuf; | ImBuf *filelist_getimage(struct FileList *filelist, const int index) | ||||
| { | |||||
| FileDirEntry *file = filelist_geticon_get_file(filelist, index); | |||||
| return file->image; | |||||
| } | } | ||||
| ImBuf *filelist_geticon(struct FileList *filelist, const int index) | static ImBuf *filelist_geticon_image_ex(const unsigned int typeflag, const char *relpath) | ||||
| { | { | ||||
| ImBuf *ibuf = NULL; | ImBuf *ibuf = NULL; | ||||
| struct direntry *file = NULL; | |||||
| int fidx = 0; | |||||
| BLI_assert(G.background == false); | if (typeflag & FILE_TYPE_DIR) { | ||||
| if (FILENAME_IS_PARENT(relpath)) { | |||||
| if ((index < 0) || (index >= filelist->numfiltered)) { | |||||
| return NULL; | |||||
| } | |||||
| fidx = filelist->fidx[index]; | |||||
| file = &filelist->filelist[fidx]; | |||||
| if (file->type & S_IFDIR) { | |||||
| if (FILENAME_IS_PARENT(filelist->filelist[fidx].relname)) { | |||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_PARENT]; | ibuf = gSpecialFileImages[SPECIAL_IMG_PARENT]; | ||||
| } | } | ||||
| else if (FILENAME_IS_CURRENT(filelist->filelist[fidx].relname)) { | else if (FILENAME_IS_CURRENT(relpath)) { | ||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_REFRESH]; | ibuf = gSpecialFileImages[SPECIAL_IMG_REFRESH]; | ||||
| } | } | ||||
| else { | else { | ||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_FOLDER]; | ibuf = gSpecialFileImages[SPECIAL_IMG_FOLDER]; | ||||
| } | } | ||||
| } | } | ||||
| else { | else if (typeflag & FILE_TYPE_BLENDER) { | ||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE]; | |||||
| } | |||||
| if (file->flags & FILE_TYPE_BLENDER) { | |||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_BLENDFILE]; | ibuf = gSpecialFileImages[SPECIAL_IMG_BLENDFILE]; | ||||
| } | } | ||||
| else if (file->flags & FILE_TYPE_MOVIE) { | else if (typeflag & FILE_TYPE_BLENDERLIB) { | ||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE]; | |||||
| } | |||||
| else if (typeflag & (FILE_TYPE_MOVIE)) { | |||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE]; | ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE]; | ||||
| } | } | ||||
| else if (file->flags & FILE_TYPE_SOUND) { | else if (typeflag & FILE_TYPE_SOUND) { | ||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_SOUNDFILE]; | ibuf = gSpecialFileImages[SPECIAL_IMG_SOUNDFILE]; | ||||
| } | } | ||||
| else if (file->flags & FILE_TYPE_PYSCRIPT) { | else if (typeflag & FILE_TYPE_PYSCRIPT) { | ||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_PYTHONFILE]; | ibuf = gSpecialFileImages[SPECIAL_IMG_PYTHONFILE]; | ||||
| } | } | ||||
| else if (file->flags & FILE_TYPE_FTFONT) { | else if (typeflag & FILE_TYPE_FTFONT) { | ||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_FONTFILE]; | ibuf = gSpecialFileImages[SPECIAL_IMG_FONTFILE]; | ||||
| } | } | ||||
| else if (file->flags & FILE_TYPE_TEXT) { | else if (typeflag & FILE_TYPE_TEXT) { | ||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE]; | ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE]; | ||||
| } | } | ||||
| else if (file->flags & FILE_TYPE_IMAGE) { | else if (typeflag & FILE_TYPE_IMAGE) { | ||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING]; | ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING]; | ||||
| } | } | ||||
| else if (file->flags & FILE_TYPE_BLENDER_BACKUP) { | else if (typeflag & FILE_TYPE_BLENDER_BACKUP) { | ||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_BACKUP]; | ibuf = gSpecialFileImages[SPECIAL_IMG_BACKUP]; | ||||
| } | } | ||||
| else { | |||||
| ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE]; | |||||
| } | |||||
| return ibuf; | return ibuf; | ||||
| } | } | ||||
| ImBuf *filelist_geticon_image(struct FileList *filelist, const int index) | |||||
| { | |||||
| FileDirEntry *file = filelist_geticon_get_file(filelist, index); | |||||
| return filelist_geticon_image_ex(file->typeflag, file->relpath); | |||||
| } | |||||
| static int filelist_geticon_ex( | |||||
| const int typeflag, const int blentype, const char *relpath, const bool is_main, const bool ignore_libdir) | |||||
| { | |||||
| if ((typeflag & FILE_TYPE_DIR) && !(ignore_libdir && (typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER)))) { | |||||
| if (FILENAME_IS_PARENT(relpath)) { | |||||
| return is_main ? ICON_FILE_PARENT : ICON_NONE; | |||||
| } | |||||
| else if (typeflag & FILE_TYPE_APPLICATIONBUNDLE) { | |||||
| return ICON_UGLYPACKAGE; | |||||
| } | |||||
| else if (typeflag & FILE_TYPE_BLENDER) { | |||||
| return ICON_FILE_BLEND; | |||||
| } | |||||
| else if (is_main) { | |||||
| /* Do not return icon for folders if icons are not 'main' draw type (e.g. when used over previews). */ | |||||
| return ICON_FILE_FOLDER; | |||||
| } | |||||
| } | |||||
| if (typeflag & FILE_TYPE_BLENDER) | |||||
| return ICON_FILE_BLEND; | |||||
| else if (typeflag & FILE_TYPE_BLENDER_BACKUP) | |||||
| return ICON_FILE_BACKUP; | |||||
| else if (typeflag & FILE_TYPE_IMAGE) | |||||
| return ICON_FILE_IMAGE; | |||||
| else if (typeflag & FILE_TYPE_MOVIE) | |||||
| return ICON_FILE_MOVIE; | |||||
| else if (typeflag & FILE_TYPE_PYSCRIPT) | |||||
| return ICON_FILE_SCRIPT; | |||||
| else if (typeflag & FILE_TYPE_SOUND) | |||||
| return ICON_FILE_SOUND; | |||||
| else if (typeflag & FILE_TYPE_FTFONT) | |||||
| return ICON_FILE_FONT; | |||||
| else if (typeflag & FILE_TYPE_BTX) | |||||
| return ICON_FILE_BLANK; | |||||
| else if (typeflag & FILE_TYPE_COLLADA) | |||||
| return ICON_FILE_BLANK; | |||||
| else if (typeflag & FILE_TYPE_TEXT) | |||||
| return ICON_FILE_TEXT; | |||||
| else if (typeflag & FILE_TYPE_BLENDERLIB) { | |||||
| const int ret = UI_idcode_icon_get(blentype); | |||||
| if (ret != ICON_NONE) { | |||||
| return ret; | |||||
| } | |||||
| } | |||||
| return is_main ? ICON_FILE_BLANK : ICON_NONE; | |||||
| } | |||||
| int filelist_geticon(struct FileList *filelist, const int index, const bool is_main) | |||||
| { | |||||
| FileDirEntry *file = filelist_geticon_get_file(filelist, index); | |||||
| return filelist_geticon_ex(file->typeflag, file->blentype, file->relpath, is_main, false); | |||||
| } | |||||
| /* ********** Main ********** */ | /* ********** Main ********** */ | ||||
| static void filelist_checkdir_dir(struct FileList *UNUSED(filelist), char *r_dir) | |||||
| { | |||||
| BLI_make_exist(r_dir); | |||||
| } | |||||
| static void filelist_checkdir_lib(struct FileList *UNUSED(filelist), char *r_dir) | |||||
| { | |||||
| char dir[FILE_MAXDIR]; | |||||
| if (!BLO_library_path_explode(r_dir, dir, NULL, NULL)) { | |||||
| /* if not a valid library, we need it to be a valid directory! */ | |||||
| BLI_make_exist(r_dir); | |||||
| } | |||||
| } | |||||
| static void filelist_checkdir_main(struct FileList *filelist, char *r_dir) | |||||
| { | |||||
| /* TODO */ | |||||
| filelist_checkdir_lib(filelist, r_dir); | |||||
| } | |||||
| static void filelist_entry_clear(FileDirEntry *entry) | |||||
| { | |||||
| if (entry->name) { | |||||
| MEM_freeN(entry->name); | |||||
| } | |||||
| if (entry->description) { | |||||
| MEM_freeN(entry->description); | |||||
| } | |||||
| if (entry->relpath) { | |||||
| MEM_freeN(entry->relpath); | |||||
| } | |||||
| if (entry->image) { | |||||
| IMB_freeImBuf(entry->image); | |||||
| } | |||||
| /* For now, consider FileDirEntryRevision::poin as not owned here, so no need to do anything about it */ | |||||
| if (!BLI_listbase_is_empty(&entry->variants)) { | |||||
| FileDirEntryVariant *var; | |||||
| for (var = entry->variants.first; var; var = var->next) { | |||||
campbellbarton: Would make this into a `UI_` function. | |||||
| if (var->name) { | |||||
| MEM_freeN(var->name); | |||||
| } | |||||
| if (var->description) { | |||||
| MEM_freeN(var->description); | |||||
| } | |||||
| if (!BLI_listbase_is_empty(&var->revisions)) { | |||||
| FileDirEntryRevision *rev; | |||||
| for (rev = var->revisions.first; rev; rev = rev->next) { | |||||
| if (rev->comment) { | |||||
| MEM_freeN(rev->comment); | |||||
| } | |||||
| } | |||||
| BLI_freelistN(&var->revisions); | |||||
| } | |||||
| } | |||||
| /* TODO: tags! */ | |||||
| BLI_freelistN(&entry->variants); | |||||
| } | |||||
| else if (entry->entry){ | |||||
| MEM_freeN(entry->entry); | |||||
| } | |||||
| } | |||||
| static void filelist_entry_free(FileDirEntry *entry) | |||||
| { | |||||
| filelist_entry_clear(entry); | |||||
| MEM_freeN(entry); | |||||
| } | |||||
| static void filelist_direntryarr_free(FileDirEntryArr *array) | |||||
| { | |||||
| FileDirEntry *entry, *entry_next; | |||||
| for (entry = array->entries.first; entry; entry = entry_next) { | |||||
| entry_next = entry->next; | |||||
| filelist_entry_free(entry); | |||||
| } | |||||
| BLI_listbase_clear(&array->entries); | |||||
| array->nbr_entries = 0; | |||||
| array->nbr_entries_filtered = -1; | |||||
| array->entry_idx_start = -1; | |||||
| array->entry_idx_end = -1; | |||||
| } | |||||
| static void filelist_intern_entry_free(FileListInternEntry *entry) | |||||
| { | |||||
| if (entry->relpath) { | |||||
| MEM_freeN(entry->relpath); | |||||
| } | |||||
| if (entry->name) { | |||||
| MEM_freeN(entry->name); | |||||
| } | |||||
| MEM_freeN(entry); | |||||
| } | |||||
| static void filelist_intern_free(FileListIntern *filelist_intern) | |||||
| { | |||||
| FileListInternEntry *entry, *entry_next; | |||||
| for (entry = filelist_intern->entries.first; entry; entry = entry_next) { | |||||
| entry_next = entry->next; | |||||
| filelist_intern_entry_free(entry); | |||||
| } | |||||
| BLI_listbase_clear(&filelist_intern->entries); | |||||
| MEM_SAFE_FREE(filelist_intern->filtered); | |||||
| } | |||||
| static void filelist_cache_previewf(TaskPool *pool, void *taskdata, int threadid) | |||||
| { | |||||
| FileListEntryCache *cache = taskdata; | |||||
| FileListEntryPreview *preview; | |||||
| printf("%s: Start (%d)...\n", __func__, threadid); | |||||
| /* Note we wait on queue here. */ | |||||
| while (!BLI_task_pool_canceled(pool) && (preview = BLI_thread_queue_pop(cache->previews_todo))) { | |||||
| ThumbSource source = 0; | |||||
| // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); | |||||
| BLI_assert(preview->flags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | | |||||
| FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)); | |||||
| if (preview->flags & FILE_TYPE_IMAGE) { | |||||
| source = THB_SOURCE_IMAGE; | |||||
| } | |||||
| else if (preview->flags & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)) { | |||||
| source = THB_SOURCE_BLEND; | |||||
| } | |||||
| else if (preview->flags & FILE_TYPE_MOVIE) { | |||||
| source = THB_SOURCE_MOVIE; | |||||
| } | |||||
| else if (preview->flags & FILE_TYPE_FTFONT) { | |||||
| source = THB_SOURCE_FONT; | |||||
| } | |||||
| IMB_thumb_lock_path(preview->path); | |||||
| preview->img = IMB_thumb_manage(preview->path, THB_LARGE, source); | |||||
| IMB_thumb_unlock_path(preview->path); | |||||
| BLI_thread_queue_push(cache->previews_done, preview); | |||||
| } | |||||
| printf("%s: End (%d)...\n", __func__, threadid); | |||||
| } | |||||
| static void filelist_cache_preview_ensure_running(FileListEntryCache *cache) | |||||
| { | |||||
| if (!cache->previews_pool) { | |||||
| TaskScheduler *scheduler = BLI_task_scheduler_get(); | |||||
| TaskPool *pool; | |||||
| int num_tasks = 4; | |||||
| pool = cache->previews_pool = BLI_task_pool_create(scheduler, NULL); | |||||
| cache->previews_todo = BLI_thread_queue_init(); | |||||
| cache->previews_done = BLI_thread_queue_init(); | |||||
| while (num_tasks--) { | |||||
| BLI_task_pool_push(pool, filelist_cache_previewf, cache, false, TASK_PRIORITY_HIGH); | |||||
| } | |||||
| IMB_thumb_locks_acquire(); | |||||
| } | |||||
| cache->previews_timestamp = 0.0; | |||||
| } | |||||
| static void filelist_cache_previews_clear(FileListEntryCache *cache) | |||||
| { | |||||
| FileListEntryPreview *preview; | |||||
| if (cache->previews_pool) { | |||||
| while ((preview = BLI_thread_queue_pop_timeout(cache->previews_todo, 0))) { | |||||
| // printf("%s: TODO %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); | |||||
| MEM_freeN(preview); | |||||
| cache->previews_pending--; | |||||
| } | |||||
| while ((preview = BLI_thread_queue_pop_timeout(cache->previews_done, 0))) { | |||||
| // printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); | |||||
| if (preview->img) { | |||||
| IMB_freeImBuf(preview->img); | |||||
| } | |||||
| MEM_freeN(preview); | |||||
| cache->previews_pending--; | |||||
| } | |||||
| } | |||||
| // printf("%s: remaining pending previews: %d\n", __func__, cache->previews_pending); | |||||
| } | |||||
| static void filelist_cache_previews_free(FileListEntryCache *cache, const bool set_inactive) | |||||
| { | |||||
| if (cache->previews_pool) { | |||||
| BLI_thread_queue_nowait(cache->previews_todo); | |||||
| BLI_thread_queue_nowait(cache->previews_done); | |||||
| BLI_task_pool_cancel(cache->previews_pool); | |||||
| filelist_cache_previews_clear(cache); | |||||
| BLI_thread_queue_free(cache->previews_done); | |||||
| BLI_thread_queue_free(cache->previews_todo); | |||||
| BLI_task_pool_free(cache->previews_pool); | |||||
| cache->previews_pool = NULL; | |||||
| cache->previews_todo = NULL; | |||||
| cache->previews_done = NULL; | |||||
| IMB_thumb_locks_release(); | |||||
| } | |||||
| if (set_inactive) { | |||||
| cache->flags &= ~FLC_PREVIEWS_ACTIVE; | |||||
| } | |||||
| BLI_assert(cache->previews_pending == 0); | |||||
| cache->previews_timestamp = 0.0; | |||||
| } | |||||
| static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry, const int index) | |||||
| { | |||||
| FileListEntryCache *cache = &filelist->filelist_cache; | |||||
| BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE); | |||||
| if (!entry->image && | |||||
| !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) && | |||||
| (entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | | |||||
| FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) | |||||
| { | |||||
| FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__); | |||||
| BLI_join_dirfile(preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath); | |||||
| preview->index = index; | |||||
| preview->flags = entry->typeflag; | |||||
| preview->img = NULL; | |||||
| // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); | |||||
| filelist_cache_preview_ensure_running(cache); | |||||
| BLI_thread_queue_push(cache->previews_todo, preview); | |||||
| cache->previews_pending++; | |||||
| } | |||||
| } | |||||
| static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size) | |||||
| { | |||||
| cache->block_cursor = cache->block_start_index = cache->block_center_index = cache->block_end_index = 0; | |||||
| cache->block_entries = MEM_mallocN(sizeof(*cache->block_entries) * cache_size, __func__); | |||||
| cache->misc_entries = BLI_ghash_ptr_new_ex(__func__, cache_size); | |||||
| cache->misc_entries_indices = MEM_mallocN(sizeof(*cache->misc_entries_indices) * cache_size, __func__); | |||||
| copy_vn_i(cache->misc_entries_indices, cache_size, -1); | |||||
| cache->misc_cursor = 0; | |||||
| /* XXX This assumes uint is 32 bits and uuid is 128 bits (char[16]), be careful! */ | |||||
| cache->uuids = BLI_ghash_new_ex( | |||||
| BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__, cache_size * 2); | |||||
| cache->size = cache_size; | |||||
| cache->flags = FLC_IS_INIT; | |||||
| } | |||||
| static void filelist_cache_free(FileListEntryCache *cache) | |||||
| { | |||||
| if (!(cache->flags & FLC_IS_INIT)) { | |||||
| return; | |||||
| } | |||||
| filelist_cache_previews_free(cache, true); | |||||
| /* Note we nearly have nothing to do here, entries are just 'borrowed', not owned by cache... */ | |||||
| MEM_freeN(cache->block_entries); | |||||
| BLI_ghash_free(cache->misc_entries, NULL, NULL); | |||||
| MEM_freeN(cache->misc_entries_indices); | |||||
| BLI_ghash_free(cache->uuids, NULL, NULL); | |||||
| } | |||||
| static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size) | |||||
| { | |||||
| if (!(cache->flags & FLC_IS_INIT)) { | |||||
| return; | |||||
| } | |||||
| filelist_cache_previews_clear(cache); | |||||
| /* Note we nearly have nothing to do here, entries are just 'borrowed', not owned by cache... */ | |||||
| cache->block_cursor = cache->block_start_index = cache->block_center_index = cache->block_end_index = 0; | |||||
| if (new_size != cache->size) { | |||||
| cache->block_entries = MEM_reallocN(cache->block_entries, sizeof(*cache->block_entries) * new_size); | |||||
| } | |||||
| BLI_ghash_clear_ex(cache->misc_entries, NULL, NULL, new_size); | |||||
| if (new_size != cache->size) { | |||||
| cache->misc_entries_indices = MEM_reallocN(cache->misc_entries_indices, | |||||
| sizeof(*cache->misc_entries_indices) * new_size); | |||||
| } | |||||
| copy_vn_i(cache->misc_entries_indices, new_size, -1); | |||||
| BLI_ghash_clear_ex(cache->uuids, NULL, NULL, new_size * 2); | |||||
| cache->size = new_size; | |||||
| } | |||||
| FileList *filelist_new(short type) | FileList *filelist_new(short type) | ||||
| { | { | ||||
| FileList *p = MEM_callocN(sizeof(*p), __func__); | FileList *p = MEM_callocN(sizeof(*p), __func__); | ||||
| filelist_cache_init(&p->filelist_cache, FILELIST_ENTRYCACHESIZE_DEFAULT); | |||||
| p->selection_state = BLI_ghash_new(BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__); | |||||
| switch (type) { | switch (type) { | ||||
| case FILE_MAIN: | case FILE_MAIN: | ||||
| p->readf = filelist_read_main; | p->checkdirf = filelist_checkdir_main; | ||||
| p->read_jobf = filelist_readjob_main; | |||||
| p->filterf = is_filtered_main; | p->filterf = is_filtered_main; | ||||
| break; | break; | ||||
| case FILE_LOADLIB: | case FILE_LOADLIB: | ||||
| p->readf = filelist_read_library; | p->checkdirf = filelist_checkdir_lib; | ||||
| p->read_jobf = filelist_readjob_lib; | |||||
| p->filterf = is_filtered_lib; | p->filterf = is_filtered_lib; | ||||
| break; | break; | ||||
| default: | default: | ||||
| p->readf = filelist_read_dir; | p->checkdirf = filelist_checkdir_dir; | ||||
| p->read_jobf = filelist_readjob_dir; | |||||
| p->filterf = is_filtered_file; | p->filterf = is_filtered_file; | ||||
| break; | break; | ||||
| } | } | ||||
| return p; | return p; | ||||
| } | } | ||||
| void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection) | |||||
| { | |||||
| if (!filelist) { | |||||
| return; | |||||
| } | |||||
| filelist_filter_clear(filelist); | |||||
| if (do_cache) { | |||||
| filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size); | |||||
| } | |||||
| filelist_intern_free(&filelist->filelist_intern); | |||||
| filelist_direntryarr_free(&filelist->filelist); | |||||
| if (do_selection && filelist->selection_state) { | |||||
| BLI_ghash_clear(filelist->selection_state, MEM_freeN, NULL); | |||||
| } | |||||
| } | |||||
| void filelist_clear(struct FileList *filelist) | |||||
| { | |||||
| filelist_clear_ex(filelist, true, true); | |||||
| } | |||||
| void filelist_free(struct FileList *filelist) | void filelist_free(struct FileList *filelist) | ||||
| { | { | ||||
| if (!filelist) { | if (!filelist) { | ||||
| printf("Attempting to delete empty filelist.\n"); | printf("Attempting to delete empty filelist.\n"); | ||||
| return; | return; | ||||
| } | } | ||||
| MEM_SAFE_FREE(filelist->fidx); | filelist_clear_ex(filelist, false, false); /* No need to clear cache & selection_state, we free them anyway. */ | ||||
| filelist->numfiltered = 0; | filelist_cache_free(&filelist->filelist_cache); | ||||
| if (filelist->selection_state) { | |||||
| BLI_ghash_free(filelist->selection_state, MEM_freeN, NULL); | |||||
| filelist->selection_state = NULL; | |||||
| } | |||||
| memset(&filelist->filter_data, 0, sizeof(filelist->filter_data)); | memset(&filelist->filter_data, 0, sizeof(filelist->filter_data)); | ||||
| filelist->need_sorting = false; | filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING); | ||||
| filelist->sort = FILE_SORT_NONE; | filelist->sort = FILE_SORT_NONE; | ||||
| BLI_filelist_free(filelist->filelist, filelist->numfiles, NULL); | |||||
| filelist->numfiles = 0; | |||||
| filelist->filelist = NULL; | |||||
| } | } | ||||
| void filelist_freelib(struct FileList *filelist) | void filelist_freelib(struct FileList *filelist) | ||||
| { | { | ||||
| if (filelist->libfiledata) | if (filelist->libfiledata) | ||||
| BLO_blendhandle_close(filelist->libfiledata); | BLO_blendhandle_close(filelist->libfiledata); | ||||
| filelist->libfiledata = NULL; | filelist->libfiledata = NULL; | ||||
| } | } | ||||
| BlendHandle *filelist_lib(struct FileList *filelist) | BlendHandle *filelist_lib(struct FileList *filelist) | ||||
| { | { | ||||
| return filelist->libfiledata; | return filelist->libfiledata; | ||||
| } | } | ||||
| int filelist_numfiles(struct FileList *filelist) | static const char *fileentry_uiname(const char *root, const char *relpath, const int typeflag, char *buff) | ||||
| { | { | ||||
| return filelist->numfiltered; | char *name = NULL; | ||||
| if (typeflag & FILE_TYPE_BLENDERLIB) { | |||||
| char abspath[FILE_MAX_LIBEXTRA]; | |||||
| char *group; | |||||
| BLI_join_dirfile(abspath, sizeof(abspath), root, relpath); | |||||
| BLO_library_path_explode(abspath, buff, &group, &name); | |||||
| if (!name) { | |||||
| name = group; | |||||
| } | |||||
| } | |||||
| /* Depending on platforms, 'my_file.blend/..' might be viewed as dir or not... */ | |||||
| if (!name) { | |||||
| if (typeflag & FILE_TYPE_DIR) { | |||||
| name = (char *)relpath; | |||||
| } | |||||
| else { | |||||
| name = (char *)BLI_path_basename(relpath); | |||||
| } | |||||
| } | |||||
| BLI_assert(name); | |||||
| return name; | |||||
| } | } | ||||
| const char *filelist_dir(struct FileList *filelist) | const char *filelist_dir(struct FileList *filelist) | ||||
| { | { | ||||
| return filelist->dir; | return filelist->filelist.root; | ||||
| } | } | ||||
| void filelist_setdir(struct FileList *filelist, const char *dir) | /** | ||||
| * May modify in place given r_dir, which is expected to be FILE_MAX_LIBEXTRA length. | |||||
| */ | |||||
| void filelist_setdir(struct FileList *filelist, char *r_dir) | |||||
| { | { | ||||
| BLI_strncpy(filelist->dir, dir, sizeof(filelist->dir)); | #ifndef NDEBUG | ||||
| size_t len = strlen(r_dir); | |||||
| BLI_assert((len < FILE_MAX_LIBEXTRA) && ELEM(r_dir[len - 1], SEP, ALTSEP)); | |||||
| #endif | |||||
| BLI_cleanup_dir(G.main->name, r_dir); | |||||
| BLI_add_slash(r_dir); | |||||
| filelist->checkdirf(filelist, r_dir); | |||||
| if (!STREQ(filelist->filelist.root, r_dir)) { | |||||
| BLI_strncpy(filelist->filelist.root, r_dir, sizeof(filelist->filelist.root)); | |||||
| filelist->flags |= FL_FORCE_RESET; | |||||
| } | |||||
| } | |||||
| void filelist_setrecursion(struct FileList *filelist, const int recursion_level) | |||||
| { | |||||
| if (filelist->max_recursion != recursion_level) { | |||||
| filelist->max_recursion = recursion_level; | |||||
| filelist->flags |= FL_FORCE_RESET; | |||||
| } | |||||
| } | |||||
| bool filelist_force_reset(struct FileList *filelist) | |||||
| { | |||||
| return (filelist->flags & FL_FORCE_RESET) != 0; | |||||
| } | |||||
| bool filelist_is_ready(struct FileList *filelist) | |||||
| { | |||||
| return (filelist->flags & FL_IS_READY) != 0; | |||||
| } | |||||
| bool filelist_pending(struct FileList *filelist) | |||||
| { | |||||
| return (filelist->flags & FL_IS_PENDING) != 0; | |||||
| } | |||||
| /** | |||||
| * Limited version of full update done by space_file's file_refresh(), to be used by operators and such. | |||||
| * Ensures given filelist is ready to be used (i.e. it is filtered and sorted), unless it is tagged for a full refresh. | |||||
| */ | |||||
| int filelist_files_ensure(FileList *filelist) | |||||
| { | |||||
| if (!filelist_force_reset(filelist) || !filelist_empty(filelist)) { | |||||
| filelist_sort(filelist); | |||||
| filelist_filter(filelist); | |||||
| } | |||||
| return filelist->filelist.nbr_entries_filtered;; | |||||
| } | |||||
| static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int index) | |||||
| { | |||||
| FileListInternEntry *entry = filelist->filelist_intern.filtered[index]; | |||||
| FileDirEntry *ret; | |||||
| FileDirEntryRevision *rev; | |||||
| ret = MEM_callocN(sizeof(*ret), __func__); | |||||
| rev = MEM_callocN(sizeof(*rev), __func__); | |||||
| rev->size = (uint64_t)entry->st.st_size; | |||||
| rev->time = (int64_t)entry->st.st_mtime; | |||||
| ret->entry = rev; | |||||
| ret->relpath = BLI_strdup(entry->relpath); | |||||
| ret->name = BLI_strdup(entry->name); | |||||
| ret->description = BLI_strdupcat(filelist->filelist.root, entry->relpath); | |||||
| memcpy(ret->uuid, entry->uuid, sizeof(ret->uuid)); | |||||
| ret->blentype = entry->blentype; | |||||
| ret->typeflag = entry->typeflag; | |||||
| BLI_addtail(&filelist->filelist.entries, ret); | |||||
| return ret; | |||||
| } | } | ||||
| short filelist_changed(struct FileList *filelist) | static void filelist_file_release_entry(FileList *filelist, FileDirEntry *entry) | ||||
| { | { | ||||
| return filelist->changed; | BLI_remlink(&filelist->filelist.entries, entry); | ||||
| filelist_entry_free(entry); | |||||
| } | } | ||||
| struct direntry *filelist_file(struct FileList *filelist, int index) | static FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const bool use_request) | ||||
| { | { | ||||
| int fidx = 0; | FileDirEntry *ret = NULL, *old; | ||||
| FileListEntryCache *cache = &filelist->filelist_cache; | |||||
| const size_t cache_size = cache->size; | |||||
| int old_index; | |||||
| if ((index < 0) || (index >= filelist->filelist.nbr_entries_filtered)) { | |||||
| return ret; | |||||
| } | |||||
| if (index >= cache->block_start_index && index < cache->block_end_index) { | |||||
| const int idx = (index - cache->block_start_index + cache->block_cursor) % cache_size; | |||||
| return cache->block_entries[idx]; | |||||
| } | |||||
| if ((index < 0) || (index >= filelist->numfiltered)) { | if ((ret = BLI_ghash_lookup(cache->misc_entries, SET_INT_IN_POINTER(index)))) { | ||||
| return ret; | |||||
| } | |||||
| if (!use_request) { | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| fidx = filelist->fidx[index]; | |||||
| return &filelist->filelist[fidx]; | // printf("requesting file %d (not yet cached)\n", index); | ||||
| /* Else, we have to add new entry to 'misc' cache - and possibly make room for it first! */ | |||||
| ret = filelist_file_create_entry(filelist, index); | |||||
| old_index = cache->misc_entries_indices[cache->misc_cursor]; | |||||
| if ((old = BLI_ghash_popkey(cache->misc_entries, SET_INT_IN_POINTER(old_index), NULL))) { | |||||
| BLI_ghash_remove(cache->uuids, old->uuid, NULL, NULL); | |||||
| filelist_file_release_entry(filelist, old); | |||||
| } | |||||
| BLI_ghash_insert(cache->misc_entries, SET_INT_IN_POINTER(index), ret); | |||||
| BLI_ghash_insert(cache->uuids, ret->uuid, ret); | |||||
| cache->misc_entries_indices[cache->misc_cursor] = index; | |||||
| cache->misc_cursor = (cache->misc_cursor + 1) % cache_size; | |||||
| #if 0 /* Actually no, only block cached entries should have preview imho. */ | |||||
| if (cache->previews_pool) { | |||||
| filelist_cache_previews_push(filelist, ret, index); | |||||
| } | |||||
| #endif | |||||
| return ret; | |||||
| } | |||||
| FileDirEntry *filelist_file(struct FileList *filelist, int index) | |||||
| { | |||||
| return filelist_file_ex(filelist, index, true); | |||||
| } | } | ||||
| int filelist_find(struct FileList *filelist, const char *filename) | int filelist_file_findpath(struct FileList *filelist, const char *filename) | ||||
| { | { | ||||
| int fidx = -1; | int fidx = -1; | ||||
| if (!filelist->fidx) | if (filelist->filelist.nbr_entries_filtered < 0) { | ||||
| return fidx; | return fidx; | ||||
| } | |||||
| for (fidx = 0; fidx < filelist->numfiltered; fidx++) { | /* XXX TODO Cache could probably use a ghash on paths too? Not really urgent though. | ||||
| int index = filelist->fidx[fidx]; | * This is only used to find again renamed entry, annoying but looks hairy to get rid of it currently. */ | ||||
| if (STREQ(filelist->filelist[index].relname, filename)) { | for (fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) { | ||||
| FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx]; | |||||
| if (STREQ(entry->relpath, filename)) { | |||||
| return fidx; | return fidx; | ||||
| } | } | ||||
| } | } | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4]) | |||||
| { | |||||
| if (filelist->filelist.nbr_entries_filtered < 0) { | |||||
| return NULL; | |||||
| } | |||||
| if (filelist->filelist_cache.uuids) { | |||||
| FileDirEntry *entry = BLI_ghash_lookup(filelist->filelist_cache.uuids, uuid); | |||||
| if (entry) { | |||||
| return entry; | |||||
| } | |||||
| } | |||||
| { | |||||
| int fidx; | |||||
| for (fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) { | |||||
| FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx]; | |||||
| if (memcmp(entry->uuid, uuid, sizeof(entry->uuid)) == 0) { | |||||
| return filelist_file(filelist, fidx); | |||||
| } | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| void filelist_file_cache_slidingwindow_set(FileList *filelist, size_t window_size) | |||||
| { | |||||
| /* Always keep it power of 2, in [256, 8192] range for now, cache being app. twice bigger than requested window. */ | |||||
| size_t size = 256; | |||||
| window_size *= 2; | |||||
| while (size < window_size && size < 8192) { | |||||
| size *= 2; | |||||
| } | |||||
| if (size != filelist->filelist_cache.size) { | |||||
| filelist_cache_clear(&filelist->filelist_cache, size); | |||||
| } | |||||
| } | |||||
| /* Helpers, low-level, they assume cursor + size <= cache_size */ | |||||
| static bool filelist_file_cache_block_create(FileList *filelist, const int start_index, const int size, int cursor) | |||||
| { | |||||
| FileListEntryCache *cache = &filelist->filelist_cache; | |||||
| { | |||||
| int i, idx; | |||||
| for (i = 0, idx = start_index; i < size; i++, idx++, cursor++) { | |||||
| FileDirEntry *entry = filelist_file_create_entry(filelist, idx); | |||||
| cache->block_entries[cursor] = entry; | |||||
| BLI_ghash_insert(cache->uuids, entry->uuid, entry); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| static void filelist_file_cache_block_release(struct FileList *filelist, const int size, int cursor) | |||||
| { | |||||
| FileListEntryCache *cache = &filelist->filelist_cache; | |||||
| { | |||||
| int i; | |||||
| for (i = 0; i < size; i++, cursor++) { | |||||
| FileDirEntry *entry = cache->block_entries[cursor]; | |||||
| // printf("%s: release cacheidx %d (%%p %%s)\n", __func__, cursor/*, cache->block_entries[cursor], cache->block_entries[cursor]->relpath*/); | |||||
| BLI_ghash_remove(cache->uuids, entry->uuid, NULL, NULL); | |||||
| filelist_file_release_entry(filelist, entry); | |||||
| #ifndef NDEBUG | |||||
| cache->block_entries[cursor] = NULL; | |||||
| #endif | |||||
| } | |||||
| } | |||||
| } | |||||
| /* Load in cache all entries "around" given index (as much as block cache may hold). */ | |||||
| bool filelist_file_cache_block(struct FileList *filelist, const int index) | |||||
| { | |||||
| FileListEntryCache *cache = &filelist->filelist_cache; | |||||
| const size_t cache_size = cache->size; | |||||
| const int nbr_entries = filelist->filelist.nbr_entries_filtered; | |||||
| int start_index = max_ii(0, index - (cache_size / 2)); | |||||
| int end_index = min_ii(nbr_entries, index + (cache_size / 2)); | |||||
| int i; | |||||
| if ((index < 0) || (index >= nbr_entries)) { | |||||
| // printf("Wrong index %d ([%d:%d])", index, 0, nbr_entries); | |||||
| return false; | |||||
| } | |||||
| /* Maximize cached range! */ | |||||
| if ((end_index - start_index) < cache_size) { | |||||
| if (start_index == 0) { | |||||
| end_index = min_ii(nbr_entries, start_index + cache_size); | |||||
| } | |||||
| else if (end_index == nbr_entries) { | |||||
| start_index = max_ii(0, end_index - cache_size); | |||||
| } | |||||
| } | |||||
| BLI_assert((end_index - start_index) <= cache_size) ; | |||||
| // printf("%s: [%d:%d] around index %d (current cache: [%d:%d])\n", __func__, | |||||
| // start_index, end_index, index, cache->block_start_index, cache->block_end_index); | |||||
| /* If we have something to (re)cache... */ | |||||
| if ((start_index != cache->block_start_index) || (end_index != cache->block_end_index)) { | |||||
| if ((start_index >= cache->block_end_index) || (end_index <= cache->block_start_index)) { | |||||
| int size1 = cache->block_end_index - cache->block_start_index; | |||||
| int size2 = 0; | |||||
| int idx1 = cache->block_cursor, idx2 = 0; | |||||
| // printf("Full Recaching!\n"); | |||||
| if (cache->flags & FLC_PREVIEWS_ACTIVE) { | |||||
| filelist_cache_previews_clear(cache); | |||||
| } | |||||
| if (idx1 + size1 > cache_size) { | |||||
| size2 = idx1 + size1 - cache_size; | |||||
| size1 -= size2; | |||||
| filelist_file_cache_block_release(filelist, size2, idx2); | |||||
| } | |||||
| filelist_file_cache_block_release(filelist, size1, idx1); | |||||
| cache->block_start_index = cache->block_end_index = cache->block_cursor = 0; | |||||
| /* New cached block does not overlap existing one, simple. */ | |||||
| if (!filelist_file_cache_block_create(filelist, start_index, end_index - start_index, 0)) { | |||||
| return false; | |||||
| } | |||||
| cache->block_start_index = start_index; | |||||
| cache->block_end_index = end_index; | |||||
| } | |||||
| else { | |||||
| // printf("Partial Recaching!\n"); | |||||
| /* At this point, we know we keep part of currently cached entries, so update previews if needed, | |||||
| * and remove everything from working queue - we'll add all newly needed entries at the end. */ | |||||
| if (cache->flags & FLC_PREVIEWS_ACTIVE) { | |||||
| filelist_cache_previews_update(filelist); | |||||
| filelist_cache_previews_clear(cache); | |||||
| } | |||||
| // printf("\tpreview cleaned up...\n"); | |||||
| if (start_index > cache->block_start_index) { | |||||
| int size1 = start_index - cache->block_start_index; | |||||
| int size2 = 0; | |||||
| int idx1 = cache->block_cursor, idx2 = 0; | |||||
| // printf("\tcache releasing: [%d:%d] (%d, %d)\n", cache->block_start_index, cache->block_start_index + size1, cache->block_cursor, size1); | |||||
| if (idx1 + size1 > cache_size) { | |||||
| size2 = idx1 + size1 - cache_size; | |||||
| size1 -= size2; | |||||
| filelist_file_cache_block_release(filelist, size2, idx2); | |||||
| } | |||||
| filelist_file_cache_block_release(filelist, size1, idx1); | |||||
| cache->block_cursor = (idx1 + size1 + size2) % cache_size; | |||||
| cache->block_start_index = start_index; | |||||
| } | |||||
| if (end_index < cache->block_end_index) { | |||||
| int size1 = cache->block_end_index - end_index; | |||||
| int size2 = 0; | |||||
| int idx1, idx2 = 0; | |||||
| // printf("\tcache releasing: [%d:%d] (%d)\n", cache->block_end_index - size1, cache->block_end_index, cache->block_cursor); | |||||
| idx1 = (cache->block_cursor + end_index - cache->block_start_index) % cache_size; | |||||
| if (idx1 + size1 > cache_size) { | |||||
| size2 = idx1 + size1 - cache_size; | |||||
| size1 -= size2; | |||||
| filelist_file_cache_block_release(filelist, size2, idx2); | |||||
| } | |||||
| filelist_file_cache_block_release(filelist, size1, idx1); | |||||
| cache->block_end_index = end_index; | |||||
| } | |||||
| // printf("\tcache cleaned up...\n"); | |||||
| if (start_index < cache->block_start_index) { | |||||
| /* Add (request) needed entries before already cached ones. */ | |||||
| /* Note: We need some index black magic to wrap around (cycle) inside our cache_size array... */ | |||||
| int size1 = cache->block_start_index - start_index; | |||||
| int size2 = 0; | |||||
| int idx1, idx2; | |||||
| if (size1 > cache->block_cursor) { | |||||
| size2 = size1; | |||||
| size1 -= cache->block_cursor; | |||||
| size2 -= size1; | |||||
| idx2 = 0; | |||||
| idx1 = cache_size - size1; | |||||
| } | |||||
| else { | |||||
| idx1 = cache->block_cursor - size1; | |||||
| } | |||||
| if (size2) { | |||||
| if (!filelist_file_cache_block_create(filelist, start_index + size1, size2, idx2)) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| if (!filelist_file_cache_block_create(filelist, start_index, size1, idx1)) { | |||||
| return false; | |||||
| } | |||||
| cache->block_cursor = idx1; | |||||
| cache->block_start_index = start_index; | |||||
| } | |||||
| // printf("\tstart-extended...\n"); | |||||
| if (end_index > cache->block_end_index) { | |||||
| /* Add (request) needed entries after already cached ones. */ | |||||
| /* Note: We need some index black magic to wrap around (cycle) inside our cache_size array... */ | |||||
| int size1 = end_index - cache->block_end_index; | |||||
| int size2 = 0; | |||||
| int idx1, idx2; | |||||
| idx1 = (cache->block_cursor + end_index - cache->block_start_index - size1) % cache_size; | |||||
| if ((idx1 + size1) > cache_size) { | |||||
| size2 = size1; | |||||
| size1 = cache_size - idx1; | |||||
| size2 -= size1; | |||||
| idx2 = 0; | |||||
| } | |||||
| if (size2) { | |||||
| if (!filelist_file_cache_block_create(filelist, end_index - size2, size2, idx2)) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| if (!filelist_file_cache_block_create(filelist, end_index - size1 - size2, size1, idx1)) { | |||||
| return false; | |||||
| } | |||||
| cache->block_end_index = end_index; | |||||
| } | |||||
| // printf("\tend-extended...\n"); | |||||
| } | |||||
| } | |||||
| else if ((cache->block_center_index != index) && (cache->flags & FLC_PREVIEWS_ACTIVE)) { | |||||
| /* We try to always preview visible entries first, so 'restart' preview background task. */ | |||||
| filelist_cache_previews_update(filelist); | |||||
| filelist_cache_previews_clear(cache); | |||||
| } | |||||
| // printf("Re-queueing previews...\n"); | |||||
| /* Note we try to preview first images around given index - i.e. assumed visible ones. */ | |||||
| if (cache->flags & FLC_PREVIEWS_ACTIVE) { | |||||
| for (i = 0; ((index + i) < end_index) || ((index - i) >= start_index); i++) { | |||||
| if ((index - i) >= start_index) { | |||||
| const int idx = (cache->block_cursor + (index - start_index) - i) % cache_size; | |||||
| filelist_cache_previews_push(filelist, cache->block_entries[idx], index - i); | |||||
| } | |||||
| if ((index + i) < end_index) { | |||||
| const int idx = (cache->block_cursor + (index - start_index) + i) % cache_size; | |||||
| filelist_cache_previews_push(filelist, cache->block_entries[idx], index + i); | |||||
| } | |||||
| } | |||||
| } | |||||
| cache->block_center_index = index; | |||||
| // printf("%s Finished!\n", __func__); | |||||
| return true; | |||||
| } | |||||
| void filelist_cache_previews_set(FileList *filelist, const bool use_previews) | |||||
| { | |||||
| FileListEntryCache *cache = &filelist->filelist_cache; | |||||
Done Inline ActionsThis does a full linked list count, suggest to add BLI_thread_queue_is_empty to master, and call that. campbellbarton: This does a full linked list count, suggest to add `BLI_thread_queue_is_empty` to master, and… | |||||
| if (use_previews == ((cache->flags & FLC_PREVIEWS_ACTIVE) != 0)) { | |||||
| return; | |||||
| } | |||||
| /* Do not start preview work while listing, gives nasty flickering! */ | |||||
| else if (use_previews && (filelist->flags & FL_IS_READY)) { | |||||
| cache->flags |= FLC_PREVIEWS_ACTIVE; | |||||
| BLI_assert((cache->previews_pool == NULL) && (cache->previews_todo == NULL) && (cache->previews_done == NULL)); | |||||
| // printf("%s: Init Previews...\n", __func__); | |||||
| /* No need to populate preview queue here, filelist_file_cache_block() handles this. */ | |||||
| } | |||||
| else { | |||||
| // printf("%s: Clear Previews...\n", __func__); | |||||
| filelist_cache_previews_free(cache, true); | |||||
| } | |||||
| } | |||||
| bool filelist_cache_previews_update(FileList *filelist) | |||||
| { | |||||
| FileListEntryCache *cache = &filelist->filelist_cache; | |||||
| TaskPool *pool = cache->previews_pool; | |||||
| bool changed = false; | |||||
| if (!pool) { | |||||
| return changed; | |||||
| } | |||||
| // printf("%s: Update Previews...\n", __func__); | |||||
| while (!BLI_thread_queue_is_empty(cache->previews_done)) { | |||||
| FileListEntryPreview *preview = BLI_thread_queue_pop(cache->previews_done); | |||||
| /* entry might have been removed from cache in the mean while, we do not want to cache it again here. */ | |||||
| FileDirEntry *entry = filelist_file_ex(filelist, preview->index, false); | |||||
| // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); | |||||
| if (preview->img) { | |||||
| /* Due to asynchronous process, a preview for a given image may be generated several times, i.e. | |||||
| * entry->image may already be set at this point. */ | |||||
| if (entry && !entry->image) { | |||||
| entry->image = preview->img; | |||||
| changed = true; | |||||
| } | |||||
| else { | |||||
| IMB_freeImBuf(preview->img); | |||||
| } | |||||
| } | |||||
| else if (entry) { | |||||
| /* We want to avoid re-processing this entry continuously! | |||||
| * Note that, since entries only live in cache, preview will be retried quite often anyway. */ | |||||
| entry->flags |= FILE_ENTRY_INVALID_PREVIEW; | |||||
| } | |||||
| MEM_freeN(preview); | |||||
| cache->previews_pending--; | |||||
| BLI_assert(cache->previews_pending >= 0); | |||||
| } | |||||
| // printf("%s: Previews pending: %d\n", __func__, cache->previews_pending); | |||||
| if (cache->previews_pending == 0) { | |||||
| if (cache->previews_timestamp == 0.0) { | |||||
| cache->previews_timestamp = PIL_check_seconds_timer(); | |||||
| } | |||||
| else if (PIL_check_seconds_timer() - cache->previews_timestamp > 1.0) { | |||||
| /* Preview task is IDLE since more than (approximatively) 1000ms, | |||||
| * kill workers & task for now - they will be automatically restarted when needed. */ | |||||
| // printf("%s: Inactive preview task, sleeping (%f vs %f)!\n", __func__, PIL_check_seconds_timer(), cache->previews_timestamp); | |||||
| filelist_cache_previews_free(cache, false); | |||||
| } | |||||
| } | |||||
| return changed; | |||||
| } | |||||
| bool filelist_cache_previews_running(FileList *filelist) | |||||
| { | |||||
| FileListEntryCache *cache = &filelist->filelist_cache; | |||||
| return (cache->previews_pool != NULL); | |||||
| } | |||||
| /* would recognize .blend as well */ | /* would recognize .blend as well */ | ||||
| static bool file_is_blend_backup(const char *str) | static bool file_is_blend_backup(const char *str) | ||||
| { | { | ||||
| const size_t a = strlen(str); | const size_t a = strlen(str); | ||||
| size_t b = 7; | size_t b = 7; | ||||
| bool retval = 0; | bool retval = 0; | ||||
| if (a == 0 || b >= a) { | if (a == 0 || b >= a) { | ||||
| ▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | else if (BLI_testextensie_array(path, imb_ext_movie)) { | ||||
| return FILE_TYPE_MOVIE; | return FILE_TYPE_MOVIE; | ||||
| } | } | ||||
| else if (BLI_testextensie_array(path, imb_ext_audio)) { | else if (BLI_testextensie_array(path, imb_ext_audio)) { | ||||
| return FILE_TYPE_SOUND; | return FILE_TYPE_SOUND; | ||||
| } | } | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int file_extension_type(const char *dir, const char *relname) | static int file_extension_type(const char *dir, const char *relpath) | ||||
| { | { | ||||
| char path[FILE_MAX]; | char path[FILE_MAX]; | ||||
| BLI_join_dirfile(path, sizeof(path), dir, relname); | BLI_join_dirfile(path, sizeof(path), dir, relpath); | ||||
| return path_extension_type(path); | return path_extension_type(path); | ||||
| } | } | ||||
| int ED_file_extension_icon(const char *path) | int ED_file_extension_icon(const char *path) | ||||
| { | { | ||||
| int type = path_extension_type(path); | int type = path_extension_type(path); | ||||
| if (type == FILE_TYPE_BLENDER) | switch (type) { | ||||
Done Inline ActionsUse switch-case which will be compiled into a jump table. Not as if it's some bottleneck part of code, but still nice to avoid sloppyness when it's simple to do :) sergey: Use `switch-case` which will be compiled into a jump table. Not as if it's some bottleneck part… | |||||
| case FILE_TYPE_BLENDER: | |||||
| return ICON_FILE_BLEND; | return ICON_FILE_BLEND; | ||||
| else if (type == FILE_TYPE_BLENDER_BACKUP) | case FILE_TYPE_BLENDER_BACKUP: | ||||
| return ICON_FILE_BACKUP; | return ICON_FILE_BACKUP; | ||||
| else if (type == FILE_TYPE_IMAGE) | case FILE_TYPE_IMAGE: | ||||
| return ICON_FILE_IMAGE; | return ICON_FILE_IMAGE; | ||||
| else if (type == FILE_TYPE_MOVIE) | case FILE_TYPE_MOVIE: | ||||
| return ICON_FILE_MOVIE; | return ICON_FILE_MOVIE; | ||||
| else if (type == FILE_TYPE_PYSCRIPT) | case FILE_TYPE_PYSCRIPT: | ||||
| return ICON_FILE_SCRIPT; | return ICON_FILE_SCRIPT; | ||||
| else if (type == FILE_TYPE_SOUND) | case FILE_TYPE_SOUND: | ||||
| return ICON_FILE_SOUND; | return ICON_FILE_SOUND; | ||||
| else if (type == FILE_TYPE_FTFONT) | case FILE_TYPE_FTFONT: | ||||
| return ICON_FILE_FONT; | return ICON_FILE_FONT; | ||||
| else if (type == FILE_TYPE_BTX) | case FILE_TYPE_BTX: | ||||
| return ICON_FILE_BLANK; | return ICON_FILE_BLANK; | ||||
| else if (type == FILE_TYPE_COLLADA) | case FILE_TYPE_COLLADA: | ||||
| return ICON_FILE_BLANK; | return ICON_FILE_BLANK; | ||||
| else if (type == FILE_TYPE_TEXT) | case FILE_TYPE_TEXT: | ||||
| return ICON_FILE_TEXT; | return ICON_FILE_TEXT; | ||||
| default: | |||||
| return ICON_FILE_BLANK; | return ICON_FILE_BLANK; | ||||
| } | } | ||||
| static void filelist_setfiletypes(struct FileList *filelist) | |||||
| { | |||||
| struct direntry *file; | |||||
| int num; | |||||
| file = filelist->filelist; | |||||
| for (num = 0; num < filelist->numfiles; num++, file++) { | |||||
| #ifndef __APPLE__ | |||||
| /* Don't check extensions for directories, allow in OSX cause bundles have extensions*/ | |||||
| if (file->type & S_IFDIR) { | |||||
| continue; | |||||
| } | } | ||||
| #endif | |||||
| if (filelist->filter_data.filter_glob[0] && | int filelist_empty(struct FileList *filelist) | ||||
| BLI_testextensie_glob(file->relname, filelist->filter_data.filter_glob)) | |||||
| { | { | ||||
| file->flags = FILE_TYPE_OPERATOR; | return (filelist->filelist.nbr_entries == 0); | ||||
| } | |||||
| else { | |||||
| file->flags = file_extension_type(filelist->dir, file->relname); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| static void filelist_read_dir(struct FileList *filelist) | unsigned int filelist_entry_select_set( | ||||
| const FileList *filelist, const FileDirEntry *entry, FileSelType select, unsigned int flag, FileCheckType check) | |||||
| { | { | ||||
| if (!filelist) return; | /* Default NULL pointer if not found is fine here! */ | ||||
| void **es_p = BLI_ghash_lookup_p(filelist->selection_state, entry->uuid); | |||||
| filelist->fidx = NULL; | unsigned int entry_flag = es_p ? GET_UINT_FROM_POINTER(*es_p) : 0; | ||||
| filelist->filelist = NULL; | const unsigned int org_entry_flag = entry_flag; | ||||
| BLI_make_exist(filelist->dir); | |||||
| BLI_cleanup_dir(G.main->name, filelist->dir); | |||||
| filelist->numfiles = BLI_filelist_dir_contents(filelist->dir, &(filelist->filelist)); | |||||
| /* We shall *never* get an empty list here, since we now the dir exists and is readable | BLI_assert(entry); | ||||
| * (ensured by BLI_make_exist()). So we expect at the very least the parent '..' entry. */ | BLI_assert(ELEM(check, CHECK_DIRS, CHECK_FILES, CHECK_ALL)); | ||||
| BLI_assert(filelist->numfiles != 0); | |||||
| filelist_setfiletypes(filelist); | if (((check == CHECK_ALL)) || | ||||
| } | ((check == CHECK_DIRS) && (entry->typeflag & FILE_TYPE_DIR)) || | ||||
| ((check == CHECK_FILES) && !(entry->typeflag & FILE_TYPE_DIR))) | |||||
| static void filelist_read_main(struct FileList *filelist) | |||||
| { | { | ||||
| if (!filelist) return; | switch (select) { | ||||
| filelist_from_main(filelist); | case FILE_SEL_REMOVE: | ||||
| entry_flag &= ~flag; | |||||
| break; | |||||
| case FILE_SEL_ADD: | |||||
| entry_flag |= flag; | |||||
| break; | |||||
| case FILE_SEL_TOGGLE: | |||||
| entry_flag ^= flag; | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| static void filelist_read_library(struct FileList *filelist) | if (entry_flag != org_entry_flag) { | ||||
| { | if (es_p) { | ||||
| if (!filelist) return; | if (entry_flag) { | ||||
| BLI_cleanup_dir(G.main->name, filelist->dir); | *es_p = SET_UINT_IN_POINTER(entry_flag); | ||||
| filelist_from_library(filelist); | |||||
| if (!filelist->libfiledata) { | |||||
| int num; | |||||
| struct direntry *file; | |||||
| filelist_read_dir(filelist); | |||||
| file = filelist->filelist; | |||||
| for (num = 0; num < filelist->numfiles; num++, file++) { | |||||
| if (BLO_has_bfile_extension(file->relname)) { | |||||
| char name[FILE_MAX]; | |||||
| BLI_join_dirfile(name, sizeof(name), filelist->dir, file->relname); | |||||
| /* prevent current file being used as acceptable dir */ | |||||
| if (BLI_path_cmp(G.main->name, name) != 0) { | |||||
| file->type &= ~S_IFMT; | |||||
| file->type |= S_IFDIR; | |||||
| } | } | ||||
| else { | |||||
| BLI_ghash_remove(filelist->selection_state, entry->uuid, MEM_freeN, NULL); | |||||
| } | } | ||||
| } | } | ||||
| else if (entry_flag) { | |||||
| void *key = MEM_mallocN(sizeof(entry->uuid), __func__); | |||||
| memcpy(key, entry->uuid, sizeof(entry->uuid)); | |||||
| BLI_ghash_insert(filelist->selection_state, key, SET_UINT_IN_POINTER(entry_flag)); | |||||
| } | } | ||||
| } | } | ||||
| void filelist_readdir(struct FileList *filelist) | return entry_flag; | ||||
| { | |||||
| filelist->readf(filelist); | |||||
| filelist->need_sorting = true; | |||||
| filelist->need_thumbnails = true; | |||||
| filelist_filter_clear(filelist); | |||||
| } | } | ||||
| int filelist_empty(struct FileList *filelist) | void filelist_entry_select_index_set(FileList *filelist, const int index, FileSelType select, unsigned int flag, FileCheckType check) | ||||
| { | { | ||||
| return filelist->filelist == NULL; | FileDirEntry *entry = filelist_file(filelist, index); | ||||
| } | |||||
| void filelist_select_file(struct FileList *filelist, int index, FileSelType select, unsigned int flag, FileCheckType check) | if (entry) { | ||||
| { | filelist_entry_select_set(filelist, entry, select, flag, check); | ||||
| struct direntry *file = filelist_file(filelist, index); | |||||
| if (file != NULL) { | |||||
| int check_ok = 0; | |||||
| switch (check) { | |||||
| case CHECK_DIRS: | |||||
| check_ok = S_ISDIR(file->type); | |||||
| break; | |||||
| case CHECK_ALL: | |||||
| check_ok = 1; | |||||
| break; | |||||
| case CHECK_FILES: | |||||
| default: | |||||
| check_ok = !S_ISDIR(file->type); | |||||
| break; | |||||
| } | |||||
| if (check_ok) { | |||||
| switch (select) { | |||||
| case FILE_SEL_REMOVE: | |||||
| file->selflag &= ~flag; | |||||
| break; | |||||
| case FILE_SEL_ADD: | |||||
| file->selflag |= flag; | |||||
| break; | |||||
| case FILE_SEL_TOGGLE: | |||||
| file->selflag ^= flag; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| void filelist_select(struct FileList *filelist, FileSelection *sel, FileSelType select, unsigned int flag, FileCheckType check) | void filelist_entries_select_index_range_set( | ||||
| FileList *filelist, FileSelection *sel, FileSelType select, unsigned int flag, FileCheckType check) | |||||
| { | { | ||||
| /* select all valid files between first and last indicated */ | /* select all valid files between first and last indicated */ | ||||
| if ((sel->first >= 0) && (sel->first < filelist->numfiltered) && (sel->last >= 0) && (sel->last < filelist->numfiltered)) { | if ((sel->first >= 0) && (sel->first < filelist->filelist.nbr_entries_filtered) && | ||||
| (sel->last >= 0) && (sel->last < filelist->filelist.nbr_entries_filtered)) | |||||
| { | |||||
| int current_file; | int current_file; | ||||
| for (current_file = sel->first; current_file <= sel->last; current_file++) { | for (current_file = sel->first; current_file <= sel->last; current_file++) { | ||||
| filelist_select_file(filelist, current_file, select, flag, check); | filelist_entry_select_index_set(filelist, current_file, select, flag, check); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| bool filelist_is_selected(struct FileList *filelist, int index, FileCheckType check) | unsigned int filelist_entry_select_get(FileList *filelist, FileDirEntry *entry, FileCheckType check) | ||||
| { | { | ||||
| struct direntry *file = filelist_file(filelist, index); | BLI_assert(entry); | ||||
| if (!file) { | BLI_assert(ELEM(check, CHECK_DIRS, CHECK_FILES, CHECK_ALL)); | ||||
| return 0; | |||||
| if (((check == CHECK_ALL)) || | |||||
| ((check == CHECK_DIRS) && (entry->typeflag & FILE_TYPE_DIR)) || | |||||
| ((check == CHECK_FILES) && !(entry->typeflag & FILE_TYPE_DIR))) | |||||
| { | |||||
| /* Default NULL pointer if not found is fine here! */ | |||||
| return GET_UINT_FROM_POINTER(BLI_ghash_lookup(filelist->selection_state, entry->uuid)); | |||||
| } | } | ||||
| switch (check) { | |||||
| case CHECK_DIRS: | return 0; | ||||
| return S_ISDIR(file->type) && (file->selflag & FILE_SEL_SELECTED); | |||||
| case CHECK_FILES: | |||||
| return S_ISREG(file->type) && (file->selflag & FILE_SEL_SELECTED); | |||||
| case CHECK_ALL: | |||||
| default: | |||||
| return (file->selflag & FILE_SEL_SELECTED) != 0; | |||||
| } | } | ||||
| unsigned int filelist_entry_select_index_get(FileList *filelist, const int index, FileCheckType check) | |||||
| { | |||||
| FileDirEntry *entry = filelist_file(filelist, index); | |||||
| if (entry) { | |||||
| return filelist_entry_select_get(filelist, entry, check); | |||||
| } | } | ||||
| return 0; | |||||
| } | |||||
| bool filelist_islibrary(struct FileList *filelist, char *dir, char *group) | bool filelist_islibrary(struct FileList *filelist, char *dir, char **group) | ||||
| { | { | ||||
| return BLO_is_a_library(filelist->dir, dir, group); | return BLO_library_path_explode(filelist->filelist.root, dir, group, NULL); | ||||
| } | } | ||||
| static int groupname_to_code(const char *group) | static int groupname_to_code(const char *group) | ||||
| { | { | ||||
| char buf[BLO_GROUP_MAX]; | char buf[BLO_GROUP_MAX]; | ||||
| char *lslash; | char *lslash; | ||||
| BLI_assert(group); | |||||
| BLI_strncpy(buf, group, sizeof(buf)); | BLI_strncpy(buf, group, sizeof(buf)); | ||||
| lslash = (char *)BLI_last_slash(buf); | lslash = (char *)BLI_last_slash(buf); | ||||
| if (lslash) | if (lslash) | ||||
| lslash[0] = '\0'; | lslash[0] = '\0'; | ||||
| return buf[0] ? BKE_idcode_from_name(buf) : 0; | return buf[0] ? BKE_idcode_from_name(buf) : 0; | ||||
| } | } | ||||
| static void filelist_from_library(struct FileList *filelist) | static unsigned int groupname_to_filter_id(const char *group) | ||||
| { | { | ||||
| LinkNode *l, *names, *previews; | int id_code = groupname_to_code(group); | ||||
| struct ImBuf *ima; | |||||
| int ok, i, nprevs, nnames, idcode; | |||||
| char filename[FILE_MAX]; | |||||
| char dir[FILE_MAX], group[BLO_GROUP_MAX]; | |||||
| /* name test */ | return BKE_idcode_to_idfilter(id_code); | ||||
| ok = filelist_islibrary(filelist, dir, group); | |||||
| if (!ok) { | |||||
| /* free */ | |||||
| if (filelist->libfiledata) BLO_blendhandle_close(filelist->libfiledata); | |||||
| filelist->libfiledata = NULL; | |||||
| return; | |||||
| } | } | ||||
| BLI_strncpy(filename, G.main->name, sizeof(filename)); | /* | ||||
| * From here, we are in 'Job Context', i.e. have to be careful about sharing stuff between bacground working thread | |||||
| * and main one (used by UI among other things). | |||||
| */ | |||||
| /* there we go */ | typedef struct TodoDir { | ||||
| /* for the time being only read filedata when libfiledata==0 */ | int level; | ||||
| if (filelist->libfiledata == NULL) { | char *dir; | ||||
| filelist->libfiledata = BLO_blendhandle_from_file(dir, NULL); | } TodoDir; | ||||
| if (filelist->libfiledata == NULL) return; | |||||
| static int filelist_readjob_list_dir( | |||||
| const char *root, ListBase *entries, const char *filter_glob, | |||||
| const bool do_lib, const char *main_name, const bool skip_currpar) | |||||
| { | |||||
| struct direntry *files; | |||||
| int nbr_files, nbr_entries = 0; | |||||
| nbr_files = BLI_filelist_dir_contents(root, &files); | |||||
| if (files) { | |||||
| int i = nbr_files; | |||||
| while (i--) { | |||||
| FileListInternEntry *entry; | |||||
| if (skip_currpar && FILENAME_IS_CURRPAR(files[i].relname)) { | |||||
| continue; | |||||
| } | } | ||||
| idcode = groupname_to_code(group); | entry = MEM_callocN(sizeof(*entry), __func__); | ||||
| entry->relpath = MEM_dupallocN(files[i].relname); | |||||
| if (S_ISDIR(files[i].s.st_mode)) { | |||||
| entry->typeflag |= FILE_TYPE_DIR; | |||||
| } | |||||
| entry->st = files[i].s; | |||||
| /* Set file type. */ | |||||
| /* If we are considering .blend files as libs, promote them to directory status! */ | |||||
| if (do_lib && BLO_has_bfile_extension(entry->relpath)) { | |||||
| char name[FILE_MAX]; | |||||
| /* memory for strings is passed into filelist[i].relname | entry->typeflag = FILE_TYPE_BLENDER; | ||||
| * and freed in freefilelist */ | |||||
| if (idcode) { | |||||
| previews = BLO_blendhandle_get_previews(filelist->libfiledata, idcode, &nprevs); | |||||
| names = BLO_blendhandle_get_datablock_names(filelist->libfiledata, idcode, &nnames); | |||||
| /* ugh, no rewind, need to reopen */ | |||||
| BLO_blendhandle_close(filelist->libfiledata); | |||||
| filelist->libfiledata = BLO_blendhandle_from_file(dir, NULL); | |||||
| BLI_join_dirfile(name, sizeof(name), root, entry->relpath); | |||||
| /* prevent current file being used as acceptable dir */ | |||||
| if (BLI_path_cmp(main_name, name) != 0) { | |||||
| entry->typeflag |= FILE_TYPE_DIR; | |||||
| } | |||||
| } | |||||
| /* Otherwise, do not check extensions for directories! */ | |||||
| else if (!(entry->typeflag & FILE_TYPE_DIR)) { | |||||
| if (filter_glob[0] && BLI_testextensie_glob(entry->relpath, filter_glob)) { | |||||
| entry->typeflag = FILE_TYPE_OPERATOR; | |||||
| } | } | ||||
| else { | else { | ||||
| previews = NULL; | entry->typeflag = file_extension_type(root, entry->relpath); | ||||
| nprevs = 0; | } | ||||
| names = BLO_blendhandle_get_linkable_groups(filelist->libfiledata); | |||||
| nnames = BLI_linklist_count(names); | |||||
| } | } | ||||
| filelist->numfiles = nnames + 1; | BLI_addtail(entries, entry); | ||||
| filelist->filelist = MEM_mallocN(filelist->numfiles * sizeof(*filelist->filelist), __func__); | nbr_entries++; | ||||
| memset(filelist->filelist, 0, filelist->numfiles * sizeof(*filelist->filelist)); | } | ||||
| BLI_filelist_free(files, nbr_files); | |||||
| } | |||||
| return nbr_entries; | |||||
| } | |||||
| filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT); | static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar) | ||||
| filelist->filelist[0].type |= S_IFDIR; | { | ||||
| FileListInternEntry *entry; | |||||
| LinkNode *ln, *names; | |||||
| int i, nnames, idcode = 0, nbr_entries = 0; | |||||
| char dir[FILE_MAX], *group; | |||||
| bool ok; | |||||
| for (i = 0, l = names; i < nnames; i++, l = l->next) { | struct BlendHandle *libfiledata = NULL; | ||||
| const char *blockname = l->link; | |||||
| filelist->filelist[i + 1].relname = BLI_strdup(blockname); | /* name test */ | ||||
| if (idcode) { | ok = BLO_library_path_explode(root, dir, &group, NULL); | ||||
| filelist->filelist[i + 1].type |= S_IFREG; | if (!ok) { | ||||
| } | return nbr_entries; | ||||
| else { | |||||
| filelist->filelist[i + 1].type |= S_IFDIR; | |||||
| } | } | ||||
| /* there we go */ | |||||
| libfiledata = BLO_blendhandle_from_file(dir, NULL); | |||||
| if (libfiledata == NULL) { | |||||
| return nbr_entries; | |||||
| } | } | ||||
| if (previews && (nnames != nprevs)) { | /* memory for strings is passed into filelist[i].entry->relpath and freed in filelist_entry_free. */ | ||||
| printf("filelist_from_library: error, found %d items, %d previews\n", nnames, nprevs); | if (group) { | ||||
| idcode = groupname_to_code(group); | |||||
| names = BLO_blendhandle_get_datablock_names(libfiledata, idcode, &nnames); | |||||
| } | |||||
| else { | |||||
| names = BLO_blendhandle_get_linkable_groups(libfiledata); | |||||
| nnames = BLI_linklist_count(names); | |||||
| } | } | ||||
| else if (previews) { | |||||
| for (i = 0, l = previews; i < nnames; i++, l = l->next) { | |||||
| PreviewImage *img = l->link; | |||||
| if (img) { | BLO_blendhandle_close(libfiledata); | ||||
| unsigned int w = img->w[ICON_SIZE_PREVIEW]; | |||||
| unsigned int h = img->h[ICON_SIZE_PREVIEW]; | |||||
| unsigned int *rect = img->rect[ICON_SIZE_PREVIEW]; | |||||
| /* first allocate imbuf for copying preview into it */ | if (!skip_currpar) { | ||||
| if (w > 0 && h > 0 && rect) { | entry = MEM_callocN(sizeof(*entry), __func__); | ||||
| ima = IMB_allocImBuf(w, h, 32, IB_rect); | entry->relpath = BLI_strdup(FILENAME_PARENT); | ||||
| memcpy(ima->rect, rect, w * h * sizeof(unsigned int)); | entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR); | ||||
| filelist->filelist[i + 1].image = ima; | BLI_addtail(entries, entry); | ||||
| filelist->filelist[i + 1].flags = FILE_TYPE_IMAGE; | nbr_entries++; | ||||
| } | } | ||||
| for (i = 0, ln = names; i < nnames; i++, ln = ln->next) { | |||||
| const char *blockname = ln->link; | |||||
| entry = MEM_callocN(sizeof(*entry), __func__); | |||||
| entry->relpath = BLI_strdup(blockname); | |||||
| entry->typeflag |= FILE_TYPE_BLENDERLIB; | |||||
| if (!(group && idcode)) { | |||||
| entry->typeflag |= FILE_TYPE_DIR; | |||||
| entry->blentype = groupname_to_code(blockname); | |||||
| } | } | ||||
| else { | |||||
| entry->blentype = idcode; | |||||
| } | } | ||||
| BLI_addtail(entries, entry); | |||||
| nbr_entries++; | |||||
| } | } | ||||
| BLI_linklist_free(names, free); | BLI_linklist_free(names, free); | ||||
| if (previews) { | |||||
| BLI_linklist_free(previews, BKE_previewimg_freefunc); | |||||
| } | |||||
| BLI_strncpy(G.main->name, filename, sizeof(filename)); /* prevent G.main->name to change */ | return nbr_entries; | ||||
| } | } | ||||
| static void filelist_from_main(struct FileList *filelist) | #if 0 | ||||
| /* Kept for reference here, in case we want to add back that feature later. We do not need it currently. */ | |||||
| /* Code ***NOT*** updated for job stuff! */ | |||||
| static void filelist_readjob_main_rec(struct FileList *filelist) | |||||
| { | { | ||||
| ID *id; | ID *id; | ||||
| struct direntry *files, *firstlib = NULL; | FileDirEntry *files, *firstlib = NULL; | ||||
| ListBase *lb; | ListBase *lb; | ||||
| int a, fake, idcode, ok, totlib, totbl; | int a, fake, idcode, ok, totlib, totbl; | ||||
| // filelist->type = FILE_MAIN; // XXXXX TODO: add modes to filebrowser | // filelist->type = FILE_MAIN; // XXX TODO: add modes to filebrowser | ||||
| BLI_assert(filelist->filelist.entries == NULL); | |||||
| if (filelist->dir[0] == '/') filelist->dir[0] = 0; | if (filelist->filelist.root[0] == '/') filelist->filelist.root[0] = '\0'; | ||||
| if (filelist->dir[0]) { | if (filelist->filelist.root[0]) { | ||||
| idcode = groupname_to_code(filelist->dir); | idcode = groupname_to_code(filelist->filelist.root); | ||||
| if (idcode == 0) filelist->dir[0] = 0; | if (idcode == 0) filelist->filelist.root[0] = '\0'; | ||||
| } | } | ||||
| if (filelist->dir[0] == 0) { | if (filelist->dir[0] == 0) { | ||||
| /* make directories */ | /* make directories */ | ||||
| #ifdef WITH_FREESTYLE | #ifdef WITH_FREESTYLE | ||||
| filelist->numfiles = 24; | filelist->filelist.nbr_entries = 24; | ||||
| #else | #else | ||||
| filelist->numfiles = 23; | filelist->filelist.nbr_entries = 23; | ||||
| #endif | #endif | ||||
| filelist->filelist = MEM_mallocN(sizeof(*filelist->filelist) * filelist->numfiles, __func__); | filelist_resize(filelist, filelist->filelist.nbr_entries); | ||||
| for (a = 0; a < filelist->numfiles; a++) { | for (a = 0; a < filelist->filelist.nbr_entries; a++) { | ||||
| memset(&(filelist->filelist[a]), 0, sizeof(struct direntry)); | filelist->filelist.entries[a].typeflag |= FILE_TYPE_DIR; | ||||
| filelist->filelist[a].type |= S_IFDIR; | } | ||||
| } | |||||
| filelist->filelist.entries[0].entry->relpath = BLI_strdup(FILENAME_PARENT); | |||||
| filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT); | filelist->filelist.entries[1].entry->relpath = BLI_strdup("Scene"); | ||||
| filelist->filelist[1].relname = BLI_strdup("Scene"); | filelist->filelist.entries[2].entry->relpath = BLI_strdup("Object"); | ||||
| filelist->filelist[2].relname = BLI_strdup("Object"); | filelist->filelist.entries[3].entry->relpath = BLI_strdup("Mesh"); | ||||
| filelist->filelist[3].relname = BLI_strdup("Mesh"); | filelist->filelist.entries[4].entry->relpath = BLI_strdup("Curve"); | ||||
| filelist->filelist[4].relname = BLI_strdup("Curve"); | filelist->filelist.entries[5].entry->relpath = BLI_strdup("Metaball"); | ||||
| filelist->filelist[5].relname = BLI_strdup("Metaball"); | filelist->filelist.entries[6].entry->relpath = BLI_strdup("Material"); | ||||
| filelist->filelist[6].relname = BLI_strdup("Material"); | filelist->filelist.entries[7].entry->relpath = BLI_strdup("Texture"); | ||||
| filelist->filelist[7].relname = BLI_strdup("Texture"); | filelist->filelist.entries[8].entry->relpath = BLI_strdup("Image"); | ||||
| filelist->filelist[8].relname = BLI_strdup("Image"); | filelist->filelist.entries[9].entry->relpath = BLI_strdup("Ika"); | ||||
| filelist->filelist[9].relname = BLI_strdup("Ika"); | filelist->filelist.entries[10].entry->relpath = BLI_strdup("Wave"); | ||||
| filelist->filelist[10].relname = BLI_strdup("Wave"); | filelist->filelist.entries[11].entry->relpath = BLI_strdup("Lattice"); | ||||
| filelist->filelist[11].relname = BLI_strdup("Lattice"); | filelist->filelist.entries[12].entry->relpath = BLI_strdup("Lamp"); | ||||
| filelist->filelist[12].relname = BLI_strdup("Lamp"); | filelist->filelist.entries[13].entry->relpath = BLI_strdup("Camera"); | ||||
| filelist->filelist[13].relname = BLI_strdup("Camera"); | filelist->filelist.entries[14].entry->relpath = BLI_strdup("Ipo"); | ||||
| filelist->filelist[14].relname = BLI_strdup("Ipo"); | filelist->filelist.entries[15].entry->relpath = BLI_strdup("World"); | ||||
| filelist->filelist[15].relname = BLI_strdup("World"); | filelist->filelist.entries[16].entry->relpath = BLI_strdup("Screen"); | ||||
| filelist->filelist[16].relname = BLI_strdup("Screen"); | filelist->filelist.entries[17].entry->relpath = BLI_strdup("VFont"); | ||||
| filelist->filelist[17].relname = BLI_strdup("VFont"); | filelist->filelist.entries[18].entry->relpath = BLI_strdup("Text"); | ||||
| filelist->filelist[18].relname = BLI_strdup("Text"); | filelist->filelist.entries[19].entry->relpath = BLI_strdup("Armature"); | ||||
| filelist->filelist[19].relname = BLI_strdup("Armature"); | filelist->filelist.entries[20].entry->relpath = BLI_strdup("Action"); | ||||
| filelist->filelist[20].relname = BLI_strdup("Action"); | filelist->filelist.entries[21].entry->relpath = BLI_strdup("NodeTree"); | ||||
| filelist->filelist[21].relname = BLI_strdup("NodeTree"); | filelist->filelist.entries[22].entry->relpath = BLI_strdup("Speaker"); | ||||
| filelist->filelist[22].relname = BLI_strdup("Speaker"); | |||||
| #ifdef WITH_FREESTYLE | #ifdef WITH_FREESTYLE | ||||
| filelist->filelist[23].relname = BLI_strdup("FreestyleLineStyle"); | filelist->filelist.entries[23].entry->relpath = BLI_strdup("FreestyleLineStyle"); | ||||
| #endif | #endif | ||||
| } | } | ||||
| else { | else { | ||||
| /* make files */ | /* make files */ | ||||
| idcode = groupname_to_code(filelist->dir); | idcode = groupname_to_code(filelist->filelist.root); | ||||
| lb = which_libbase(G.main, idcode); | lb = which_libbase(G.main, idcode); | ||||
| if (lb == NULL) return; | if (lb == NULL) return; | ||||
| filelist->numfiles = 0; | filelist->filelist.nbr_entries = 0; | ||||
| for (id = lb->first; id; id = id->next) { | for (id = lb->first; id; id = id->next) { | ||||
| if (!filelist->filter_data.hide_dot || id->name[2] != '.') { | if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') { | ||||
| filelist->numfiles++; | filelist->filelist.nbr_entries++; | ||||
| } | } | ||||
| } | } | ||||
| /* XXXXX TODO: if databrowse F4 or append/link filelist->hide_parent has to be set */ | /* XXX TODO: if databrowse F4 or append/link filelist->flags & FLF_HIDE_PARENT has to be set */ | ||||
| if (!filelist->filter_data.hide_parent) filelist->numfiles += 1; | if (!(filelist->filter_data.flags & FLF_HIDE_PARENT)) | ||||
| filelist->filelist = filelist->numfiles > 0 ? MEM_mallocN(sizeof(*filelist->filelist) * filelist->numfiles, __func__) : NULL; | filelist->filelist.nbr_entries++; | ||||
| if (filelist->filelist.nbr_entries > 0) { | |||||
| filelist_resize(filelist, filelist->filelist.nbr_entries); | |||||
| } | |||||
| files = filelist->filelist; | files = filelist->filelist.entries; | ||||
| if (files && !filelist->filter_data.hide_parent) { | if (!(filelist->filter_data.flags & FLF_HIDE_PARENT)) { | ||||
| memset(&(filelist->filelist[0]), 0, sizeof(struct direntry)); | files->entry->relpath = BLI_strdup(FILENAME_PARENT); | ||||
| filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT); | files->typeflag |= FILE_TYPE_DIR; | ||||
| filelist->filelist[0].type |= S_IFDIR; | |||||
| files++; | files++; | ||||
| } | } | ||||
| totlib = totbl = 0; | totlib = totbl = 0; | ||||
| for (id = lb->first; id; id = id->next) { | for (id = lb->first; id; id = id->next) { | ||||
| ok = 1; | ok = 1; | ||||
| if (ok) { | if (ok) { | ||||
| if (files && (!filelist->filter_data.hide_dot || id->name[2] != '.')) { | if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') { | ||||
| memset(files, 0, sizeof(struct direntry)); | |||||
| if (id->lib == NULL) { | if (id->lib == NULL) { | ||||
| files->relname = BLI_strdup(id->name + 2); | files->entry->relpath = BLI_strdup(id->name + 2); | ||||
| } | } | ||||
| else { | else { | ||||
| files->relname = MEM_mallocN(sizeof(*files->relname) * (FILE_MAX + (MAX_ID_NAME - 2)), __func__); | files->entry->relpath = MEM_mallocN(sizeof(*files->relpath) * (FILE_MAX + (MAX_ID_NAME - 2)), __func__); | ||||
| BLI_snprintf(files->relname, FILE_MAX + (MAX_ID_NAME - 2) + 3, "%s | %s", id->lib->name, id->name + 2); | BLI_snprintf(files->entry->relpath, FILE_MAX + (MAX_ID_NAME - 2) + 3, "%s | %s", id->lib->name, id->name + 2); | ||||
| } | } | ||||
| files->type |= S_IFREG; | // files->type |= S_IFREG; | ||||
| #if 0 /* XXXXX TODO show the selection status of the objects */ | #if 0 /* XXX TODO show the selection status of the objects */ | ||||
| if (!filelist->has_func) { /* F4 DATA BROWSE */ | if (!filelist->has_func) { /* F4 DATA BROWSE */ | ||||
| if (idcode == ID_OB) { | if (idcode == ID_OB) { | ||||
| if ( ((Object *)id)->flag & SELECT) files->selflag |= FILE_SEL_SELECTED; | if ( ((Object *)id)->flag & SELECT) files->entry->selflag |= FILE_SEL_SELECTED; | ||||
| } | } | ||||
| else if (idcode == ID_SCE) { | else if (idcode == ID_SCE) { | ||||
| if ( ((Scene *)id)->r.scemode & R_BG_RENDER) files->selflag |= FILE_SEL_SELECTED; | if ( ((Scene *)id)->r.scemode & R_BG_RENDER) files->entry->selflag |= FILE_SEL_SELECTED; | ||||
| } | } | ||||
| } | } | ||||
| #endif | #endif | ||||
| files->nr = totbl + 1; | // files->entry->nr = totbl + 1; | ||||
| files->poin = id; | files->entry->poin = id; | ||||
| fake = id->flag & LIB_FAKEUSER; | fake = id->flag & LIB_FAKEUSER; | ||||
| if (idcode == ID_MA || idcode == ID_TE || idcode == ID_LA || idcode == ID_WO || idcode == ID_IM) { | if (idcode == ID_MA || idcode == ID_TE || idcode == ID_LA || idcode == ID_WO || idcode == ID_IM) { | ||||
| files->flags |= FILE_TYPE_IMAGE; | files->typeflag |= FILE_TYPE_IMAGE; | ||||
| } | } | ||||
| if (id->lib && fake) BLI_snprintf(files->extra, sizeof(files->extra), "LF %d", id->us); | // if (id->lib && fake) BLI_snprintf(files->extra, sizeof(files->entry->extra), "LF %d", id->us); | ||||
| else if (id->lib) BLI_snprintf(files->extra, sizeof(files->extra), "L %d", id->us); | // else if (id->lib) BLI_snprintf(files->extra, sizeof(files->entry->extra), "L %d", id->us); | ||||
| else if (fake) BLI_snprintf(files->extra, sizeof(files->extra), "F %d", id->us); | // else if (fake) BLI_snprintf(files->extra, sizeof(files->entry->extra), "F %d", id->us); | ||||
| else BLI_snprintf(files->extra, sizeof(files->extra), " %d", id->us); | // else BLI_snprintf(files->extra, sizeof(files->entry->extra), " %d", id->us); | ||||
Not Done Inline ActionsDead code is forbidden. sergey: Dead code is forbidden. | |||||
Not Done Inline ActionsThat whole func is dead anyway, not used anywhere. Just kept it (more or less) in sync with other changes I made, in case I need this zombie later… mont29: That whole func is dead anyway, not used anywhere. Just kept it (more or less) in sync with… | |||||
Not Done Inline ActionsBut do we need the function? :) sergey: But do we need the function? :) | |||||
Not Done Inline ActionsGood question, to which I do not have an answer yet. Thing is, we may need it again for asset engines (more precisely, to the 'create/edit assets' part, which is not fully defined yet). So… would rather keep it for now, easy to clean it up later in case we definitively do not need 'current' .blend file listing in filebrowser. mont29: Good question, to which I do not have an answer yet. Thing is, we may need it again for asset… | |||||
| if (id->lib) { | if (id->lib) { | ||||
| if (totlib == 0) firstlib = files; | if (totlib == 0) firstlib = files; | ||||
| totlib++; | totlib++; | ||||
| } | } | ||||
| files++; | files++; | ||||
| } | } | ||||
| totbl++; | totbl++; | ||||
| } | } | ||||
| } | } | ||||
| /* only qsort of library blocks */ | /* only qsort of library blocks */ | ||||
| if (totlib > 1) { | if (totlib > 1) { | ||||
| qsort(firstlib, totlib, sizeof(struct direntry), compare_name); | qsort(firstlib, totlib, sizeof(*files), compare_name); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| #endif | |||||
| /* ********** Thumbnails job ********** */ | static void filelist_readjob_do( | ||||
| const bool do_lib, | |||||
| typedef struct ThumbnailJob { | FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock) | ||||
| ListBase loadimages; | { | ||||
| ImBuf *static_icons_buffers[BIFICONID_LAST]; | ListBase entries = {0}; | ||||
| const short *stop; | BLI_Stack *todo_dirs; | ||||
| const short *do_update; | TodoDir *td_dir; | ||||
| struct FileList *filelist; | char dir[FILE_MAX_LIBEXTRA]; | ||||
| ReportList reports; | char filter_glob[64]; /* TODO should be define! */ | ||||
| } ThumbnailJob; | const char *root = filelist->filelist.root; | ||||
| const int max_recursion = filelist->max_recursion; | |||||
| bool filelist_need_thumbnails(FileList *filelist) | int nbr_done_dirs = 0, nbr_todo_dirs = 1; | ||||
| { | |||||
| return filelist->need_thumbnails; | // BLI_assert(filelist->filtered == NULL); | ||||
| BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && (filelist->filelist.nbr_entries == 0)); | |||||
| todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__); | |||||
| td_dir = BLI_stack_push_r(todo_dirs); | |||||
| td_dir->level = 1; | |||||
| BLI_strncpy(dir, filelist->filelist.root, sizeof(dir)); | |||||
| BLI_strncpy(filter_glob, filelist->filter_data.filter_glob, sizeof(filter_glob)); | |||||
| BLI_cleanup_dir(main_name, dir); | |||||
| td_dir->dir = BLI_strdup(dir); | |||||
| while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) { | |||||
| FileListInternEntry *entry; | |||||
| int nbr_entries = 0; | |||||
| bool is_lib = do_lib; | |||||
| char *subdir; | |||||
| int recursion_level; | |||||
| bool skip_currpar; | |||||
| td_dir = BLI_stack_peek(todo_dirs); | |||||
| subdir = td_dir->dir; | |||||
| recursion_level = td_dir->level; | |||||
| skip_currpar = (recursion_level > 1); | |||||
| BLI_stack_discard(todo_dirs); | |||||
| if (do_lib) { | |||||
| nbr_entries = filelist_readjob_list_lib(subdir, &entries, skip_currpar); | |||||
| } | |||||
| if (!nbr_entries) { | |||||
| is_lib = false; | |||||
| nbr_entries = filelist_readjob_list_dir(subdir, &entries, filter_glob, do_lib, main_name, skip_currpar); | |||||
| } | |||||
| for (entry = entries.first; entry; entry = entry->next) { | |||||
| BLI_join_dirfile(dir, sizeof(dir), subdir, entry->relpath); | |||||
| BLI_cleanup_file(root, dir); | |||||
| /* Generate our entry uuid. Abusing uuid as an uint64, shall be more than enough here, | |||||
| * things would crash way before we overflow that counter! | |||||
| * Using an atomic operation to avoid having to lock thread... | |||||
| * Note that we do not really need this here currently, since there is a single listing thread, but better | |||||
| * remain consistent about threading! */ | |||||
| *((uint64_t *)entry->uuid) = atomic_add_uint64((uint64_t *)filelist->filelist_intern.curr_uuid, 1); | |||||
| BLI_path_rel(dir, root); | |||||
| /* Only thing we change in direntry here, so we need to free it first. */ | |||||
| MEM_freeN(entry->relpath); | |||||
| entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//' added by BLI_path_rel */ | |||||
| entry->name = BLI_strdup(fileentry_uiname(root, entry->relpath, entry->typeflag, dir)); | |||||
| /* Here we decide whether current filedirentry is to be listed too, or not. */ | |||||
| if (max_recursion && (is_lib || (recursion_level <= max_recursion))) { | |||||
| if (((entry->typeflag & FILE_TYPE_DIR) == 0) || FILENAME_IS_CURRPAR(entry->relpath)) { | |||||
| /* Skip... */ | |||||
| } | } | ||||
| else if (!is_lib && (recursion_level >= max_recursion) && | |||||
| static void thumbnail_joblist_free(ThumbnailJob *tj) | ((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0)) | ||||
| { | { | ||||
| FileImage *limg = tj->loadimages.first; | /* Do not recurse in real directories in this case, only in .blend libs. */ | ||||
| } | |||||
| /* free the images not yet copied to the filelist -> these will get freed with the filelist */ | else { | ||||
| for (; limg; limg = limg->next) { | /* We have a directory we want to list, add it to todo list! */ | ||||
| if ((limg->img) && (!limg->done)) { | BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath); | ||||
| IMB_freeImBuf(limg->img); | BLI_cleanup_dir(main_name, dir); | ||||
| td_dir = BLI_stack_push_r(todo_dirs); | |||||
| td_dir->level = recursion_level + 1; | |||||
| td_dir->dir = BLI_strdup(dir); | |||||
| nbr_todo_dirs++; | |||||
| } | } | ||||
| } | } | ||||
| BLI_freelistN(&tj->loadimages); | |||||
| } | } | ||||
| static void thumbnails_startjob(void *tjv, short *stop, short *do_update, float *UNUSED(progress)) | if (nbr_entries) { | ||||
| { | BLI_mutex_lock(lock); | ||||
| ThumbnailJob *tj = tjv; | |||||
| FileImage *limg = tj->loadimages.first; | |||||
| tj->stop = stop; | *do_update = true; | ||||
| tj->do_update = do_update; | |||||
| while ((*stop == 0) && (limg)) { | BLI_movelisttolist(&filelist->filelist.entries, &entries); | ||||
| ThumbSource source = 0; | filelist->filelist.nbr_entries += nbr_entries; | ||||
| BLI_assert(limg->flags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | | BLI_mutex_unlock(lock); | ||||
| FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)); | |||||
| if (limg->flags & FILE_TYPE_IMAGE) { | |||||
| source = THB_SOURCE_IMAGE; | |||||
| } | } | ||||
| else if (limg->flags & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { | |||||
| source = THB_SOURCE_BLEND; | nbr_done_dirs++; | ||||
| *progress = (float)nbr_done_dirs / (float)nbr_todo_dirs; | |||||
| MEM_freeN(subdir); | |||||
| } | } | ||||
| else if (limg->flags & FILE_TYPE_MOVIE) { | |||||
| source = THB_SOURCE_MOVIE; | /* If we were interrupted by stop, stack may not be empty and we need to free pending dir paths. */ | ||||
| while (!BLI_stack_is_empty(todo_dirs)) { | |||||
| td_dir = BLI_stack_peek(todo_dirs); | |||||
| MEM_freeN(td_dir->dir); | |||||
| BLI_stack_discard(todo_dirs); | |||||
| } | } | ||||
| else if (limg->flags & FILE_TYPE_FTFONT) { | BLI_stack_free(todo_dirs); | ||||
| source = THB_SOURCE_FONT; | |||||
| } | } | ||||
| limg->img = IMB_thumb_manage(limg->path, THB_LARGE, source); | |||||
| *do_update = true; | static void filelist_readjob_dir( | ||||
| PIL_sleep_ms(10); | FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock) | ||||
| limg = limg->next; | { | ||||
Not Done Inline ActionsIs there a good reason to use queue instead of BLI_task? sergey: Is there a good reason to use queue instead of `BLI_task`? | |||||
Not Done Inline ActionsFirst, I’m not sure it is worth creating a task for each and every preview? Would mean potentially creating thousands of tasks in a very short amount of time… But main reason is memory management. If we create one task per preview, then we have to pass FileListEntryCache *cache as pool userdata, and a single FileListEntryPreview *preview to each task's userdata - with free_taskdata set to true. But then, since preview will be systematically freed after filelist_cache_previewf is done, I’d need to duplicate it to add it to done queue… So think using input and output queue, with limited number of working tasks, is more efficient here? mont29: First, I’m not sure it is worth creating a task for each and every preview? Would mean… | |||||
| filelist_readjob_do(false, filelist, main_name, stop, do_update, progress, lock); | |||||
| } | } | ||||
| static void filelist_readjob_lib( | |||||
Not Done Inline ActionsOk. Still would love to de-duplicate all the threading blocks we've got. Thinking of similar thing we did for parallel for, could do the same here. But wouldn't consider this is to be changed prior the merge. sergey: Ok. Still would love to de-duplicate all the threading blocks we've got. Thinking of similar… | |||||
Not Done Inline Actionsyes, see the point, but indeed not in scope of this patch. :) mont29: yes, see the point, but indeed not in scope of this patch. :) | |||||
| FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock) | |||||
| { | |||||
| filelist_readjob_do(true, filelist, main_name, stop, do_update, progress, lock); | |||||
| } | } | ||||
| static void thumbnails_update(void *tjv) | static void filelist_readjob_main( | ||||
| FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock) | |||||
| { | |||||
| /* TODO! */ | |||||
| filelist_readjob_dir(filelist, main_name, stop, do_update, progress, lock); | |||||
| } | |||||
| typedef struct FileListReadJob { | |||||
| ThreadMutex lock; | |||||
| char main_name[FILE_MAX]; | |||||
| struct FileList *filelist; | |||||
| struct FileList *tmp_filelist; /* XXX We may use a simpler struct here... just a linked list and root path? */ | |||||
| } FileListReadJob; | |||||
| static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update, float *progress) | |||||
| { | { | ||||
| ThumbnailJob *tj = tjv; | FileListReadJob *flrj = flrjv; | ||||
| printf("START filelist reading (%d files, main thread: %d)\n", | |||||
| flrj->filelist->filelist.nbr_entries, BLI_thread_is_main()); | |||||
| BLI_mutex_lock(&flrj->lock); | |||||
| BLI_assert((flrj->tmp_filelist == NULL) && flrj->filelist); | |||||
| flrj->tmp_filelist = MEM_dupallocN(flrj->filelist); | |||||
| BLI_listbase_clear(&flrj->tmp_filelist->filelist.entries); | |||||
| flrj->tmp_filelist->filelist.nbr_entries = 0; | |||||
| if (tj->filelist && tj->filelist->filelist) { | flrj->tmp_filelist->filelist_intern.filtered = NULL; | ||||
| FileImage *limg = tj->loadimages.first; | BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries); | ||||
| while (limg) { | memset(flrj->tmp_filelist->filelist_intern.curr_uuid, 0, sizeof(flrj->tmp_filelist->filelist_intern.curr_uuid)); | ||||
| if (!limg->done && limg->img) { | |||||
| tj->filelist->filelist[limg->index].image = IMB_dupImBuf(limg->img); | flrj->tmp_filelist->libfiledata = NULL; | ||||
| limg->done = true; | memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache)); | ||||
| IMB_freeImBuf(limg->img); | flrj->tmp_filelist->selection_state = NULL; | ||||
| limg->img = NULL; | |||||
| BLI_mutex_unlock(&flrj->lock); | |||||
| flrj->tmp_filelist->read_jobf(flrj->tmp_filelist, flrj->main_name, stop, do_update, progress, &flrj->lock); | |||||
| } | } | ||||
| limg = limg->next; | |||||
| static void filelist_readjob_update(void *flrjv) | |||||
| { | |||||
| FileListReadJob *flrj = flrjv; | |||||
| FileListIntern *fl_intern = &flrj->filelist->filelist_intern; | |||||
| ListBase new_entries = {NULL}; | |||||
| int nbr_entries, new_nbr_entries = 0; | |||||
| BLI_movelisttolist(&new_entries, &fl_intern->entries); | |||||
| nbr_entries = flrj->filelist->filelist.nbr_entries; | |||||
| BLI_mutex_lock(&flrj->lock); | |||||
| if (flrj->tmp_filelist->filelist.nbr_entries) { | |||||
| /* We just move everything out of 'thread context' into final list. */ | |||||
| new_nbr_entries = flrj->tmp_filelist->filelist.nbr_entries; | |||||
| BLI_movelisttolist(&new_entries, &flrj->tmp_filelist->filelist.entries); | |||||
| flrj->tmp_filelist->filelist.nbr_entries = 0; | |||||
| } | } | ||||
| BLI_mutex_unlock(&flrj->lock); | |||||
| if (new_nbr_entries) { | |||||
| /* Do not clear selection cache, we can assume already 'selected' uuids are still valid! */ | |||||
| filelist_clear_ex(flrj->filelist, true, false); | |||||
| flrj->filelist->flags |= (FL_NEED_SORTING | FL_NEED_FILTERING); | |||||
| } | } | ||||
| /* if no new_nbr_entries, this is NOP */ | |||||
| BLI_movelisttolist(&fl_intern->entries, &new_entries); | |||||
| flrj->filelist->filelist.nbr_entries = nbr_entries + new_nbr_entries; | |||||
| } | } | ||||
| static void thumbnails_endjob(void *tjv) | static void filelist_readjob_endjob(void *flrjv) | ||||
| { | { | ||||
| ThumbnailJob *tj = tjv; | FileListReadJob *flrj = flrjv; | ||||
| if (!*tj->stop) { | /* In case there would be some dangling update... */ | ||||
| tj->filelist->need_thumbnails = false; | filelist_readjob_update(flrjv); | ||||
| } | |||||
| flrj->filelist->flags &= ~FL_IS_PENDING; | |||||
| flrj->filelist->flags |= FL_IS_READY; | |||||
| } | } | ||||
| static void thumbnails_free(void *tjv) | static void filelist_readjob_free(void *flrjv) | ||||
| { | { | ||||
| ThumbnailJob *tj = tjv; | FileListReadJob *flrj = flrjv; | ||||
| thumbnail_joblist_free(tj); | |||||
| MEM_freeN(tj); | printf("END filelist reading (%d files)\n", flrj->filelist->filelist.nbr_entries); | ||||
| if (flrj->tmp_filelist) { | |||||
| /* tmp_filelist shall never ever be filtered! */ | |||||
| BLI_assert(flrj->tmp_filelist->filelist.nbr_entries == 0); | |||||
| BLI_assert(BLI_listbase_is_empty(&flrj->tmp_filelist->filelist.entries)); | |||||
| filelist_freelib(flrj->tmp_filelist); | |||||
| filelist_free(flrj->tmp_filelist); | |||||
| MEM_freeN(flrj->tmp_filelist); | |||||
| } | } | ||||
| BLI_mutex_end(&flrj->lock); | |||||
| MEM_freeN(flrj); | |||||
| } | |||||
| void thumbnails_start(FileList *filelist, const bContext *C) | void filelist_readjob_start(FileList *filelist, const bContext *C) | ||||
| { | { | ||||
| wmJob *wm_job; | wmJob *wm_job; | ||||
| ThumbnailJob *tj; | FileListReadJob *flrj; | ||||
| int idx; | |||||
| /* prepare job data */ | /* prepare job data */ | ||||
| tj = MEM_callocN(sizeof(*tj), __func__); | flrj = MEM_callocN(sizeof(*flrj), __func__); | ||||
| tj->filelist = filelist; | flrj->filelist = filelist; | ||||
| for (idx = 0; idx < filelist->numfiles; idx++) { | BLI_strncpy(flrj->main_name, G.main->name, sizeof(flrj->main_name)); | ||||
| if (!filelist->filelist[idx].path) { | |||||
| continue; | filelist->flags &= ~(FL_FORCE_RESET | FL_IS_READY); | ||||
| } | filelist->flags |= FL_IS_PENDING; | ||||
| if (!filelist->filelist[idx].image) { | |||||
| if (filelist->filelist[idx].flags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | | |||||
| FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) | |||||
| { | |||||
| FileImage *limg = MEM_callocN(sizeof(*limg), __func__); | |||||
| BLI_strncpy(limg->path, filelist->filelist[idx].path, sizeof(limg->path)); | |||||
| limg->index = idx; | |||||
| limg->flags = filelist->filelist[idx].flags; | |||||
| BLI_addtail(&tj->loadimages, limg); | |||||
| } | |||||
| } | |||||
| } | |||||
| BKE_reports_init(&tj->reports, RPT_PRINT); | BLI_mutex_init(&flrj->lock); | ||||
Done Inline ActionsIt's a bit weird code. And more weird is why we don't need it anymore? sergey: It's a bit weird code. And more weird is why we don't need it anymore? | |||||
Not Done Inline ActionsThat comes from the *thumbnail previewing* job, which has been removed now. Just remaining 'dirt', removed. mont29: That comes from the *thumbnail previewing* job, which has been removed now. Just remaining… | |||||
| /* setup job */ | /* setup job */ | ||||
| wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), filelist, "Thumbnails", | wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_wm_area(C), "Listing Dirs...", | ||||
| 0, WM_JOB_TYPE_FILESEL_THUMBNAIL); | WM_JOB_PROGRESS, WM_JOB_TYPE_FILESEL_READDIR); | ||||
| WM_jobs_customdata_set(wm_job, tj, thumbnails_free); | WM_jobs_customdata_set(wm_job, flrj, filelist_readjob_free); | ||||
| WM_jobs_timer(wm_job, 0.5, NC_WINDOW, NC_WINDOW); | WM_jobs_timer(wm_job, 0.01, NC_SPACE | ND_SPACE_FILE_LIST, NC_SPACE | ND_SPACE_FILE_LIST); | ||||
| WM_jobs_callbacks(wm_job, thumbnails_startjob, NULL, thumbnails_update, thumbnails_endjob); | WM_jobs_callbacks(wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob); | ||||
| /* start the job */ | /* start the job */ | ||||
| WM_jobs_start(CTX_wm_manager(C), wm_job); | WM_jobs_start(CTX_wm_manager(C), wm_job); | ||||
| } | } | ||||
| void thumbnails_stop(wmWindowManager *wm, FileList *filelist) | void filelist_readjob_stop(wmWindowManager *wm, ScrArea *sa) | ||||
| { | { | ||||
| WM_jobs_kill_type(wm, filelist, WM_JOB_TYPE_FILESEL_THUMBNAIL); | WM_jobs_kill_type(wm, sa, WM_JOB_TYPE_FILESEL_READDIR); | ||||
| } | } | ||||
| int thumbnails_running(wmWindowManager *wm, FileList *filelist) | int filelist_readjob_running(wmWindowManager *wm, ScrArea *sa) | ||||
| { | { | ||||
| return WM_jobs_test(wm, filelist, WM_JOB_TYPE_FILESEL_THUMBNAIL); | return WM_jobs_test(wm, sa, WM_JOB_TYPE_FILESEL_READDIR); | ||||
| } | } | ||||
Would make this into a UI_ function.