Final 'FileBrowser First Stage' merge.

It basically rewrites most of filelist.c, with some more limited changes in other areas of filebrowser.

From user perspective, it:
* Removes some info in 'long' drawing mode (owner, permissions) - OS-specific data that do not really matter in Blender!
* Makes short/long display 'fixed' size (among four choices, like thumbnails mode).
* Allows to list several layers of dirtree at once, in a flat way (inside .blend files and/or real directories).
* Consequently, adds datablocks types filtering.
* Uses way less RAM when listing big directories, especially in thumbnail mode (we are talking of several hundred of MiB spared).
* Generates thumbnails way faster.

From code perspective, it:
* Is ready for asset engine needs (on data structure level in filebrowser's listing).
* Simplifies and makes 'generic' file listing much lighter.
* Separates file listing in three different aspects:
** 'generic' filelisting (in BLI), which becomes a shallow wrapper around stat struct.
** 'filebrowser drawing' filelisting, which only contains current visible subset of the whole list (sliding window), with extra drawing data (strings for size, date/time, preview, etc.).
** 'asset-ready' filelisting, which is used for operations common to 'basic' filehandling and future asset-related one.
* Uses uuid's to handle file selection/state in the browser, instead of using flags in filelisting items.
* Uses much lighter BLI_task handling for previews, instead of heavy 'job' system (using the new 'notifier' timer to handle UI refresh, in similar way to jobs).
* Moves .blend datablocks preview handling to IMB_thumbnail (necessary to avoid storing all datablock previews at once, and gives better consistency and performances too).

Revision: https://developer.blender.org/D1316

Thanks to Campbell & Sergey for the reviews. :)
This commit is contained in:
Bastien Montagne
2015-08-19 22:41:39 +02:00
parent 5c659574e6
commit f69e9681fa
21 changed files with 2776 additions and 1100 deletions

View File

@@ -40,22 +40,23 @@ class FILEBROWSER_HT_header(Header):
row.operator("file.parent", text="", icon='FILE_PARENT')
row.operator("file.refresh", text="", icon='FILE_REFRESH')
row = layout.row()
row.separator()
row = layout.row(align=True)
layout.separator()
layout.operator_context = 'EXEC_DEFAULT'
row.operator("file.directory_new", icon='NEWFOLDER')
layout.operator("file.directory_new", icon='NEWFOLDER', text="")
layout.separator()
layout.operator_context = 'INVOKE_DEFAULT'
params = st.params
# can be None when save/reload with a file selector open
if params:
is_lib_browser = params.use_library_browsing
layout.prop(params, "recursion_level", text="")
layout.prop(params, "display_type", expand=True, text="")
if params.display_type == 'FILE_IMGDISPLAY':
layout.prop(params, "thumbnail_size", text="")
layout.prop(params, "thumbnail_size", text="")
layout.prop(params, "sort_method", expand=True, text="")
@@ -81,9 +82,17 @@ class FILEBROWSER_HT_header(Header):
row.prop(params, "use_filter_sound", text="")
row.prop(params, "use_filter_text", text="")
if is_lib_browser:
row.prop(params, "use_filter_blendid", text="")
if params.use_filter_blendid:
row.separator()
row.prop(params, "filter_id_category", text="")
row.separator()
row.prop(params, "filter_search", text="", icon='VIEWZOOM')
layout.template_running_jobs()
class FILEBROWSER_UL_dir(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
@@ -214,5 +223,24 @@ class FILEBROWSER_PT_recent_folders(Panel):
col.operator("file.reset_recent", icon='X', text="")
class FILEBROWSER_PT_advanced_filter(Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOLS'
bl_category = "Filter"
bl_label = "Advanced Filter"
def draw(self, context):
layout = self.layout
space = context.space_data
params = space.params
if params and params.use_library_browsing:
layout.prop(params, "use_filter_blendid")
if params.use_filter_blendid:
layout.separator()
col = layout.column()
col.prop(params, "filter_id")
if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)

View File

@@ -91,11 +91,19 @@ char *BLI_current_working_dir(char *dir, const size_t maxlen) ATTR_NONNULL();
/* Filelist */
unsigned int BLI_filelist_dir_contents(const char *dir, struct direntry **filelist);
unsigned int BLI_filelist_dir_contents(const char *dir, struct direntry **r_filelist);
void BLI_filelist_entry_duplicate(struct direntry *dst, const struct direntry *src);
void BLI_filelist_duplicate(
struct direntry **dest_filelist, struct direntry *src_filelist, unsigned int nrentries,
void *(*dup_poin)(void *));
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries, void (*free_poin)(void *));
struct direntry **dest_filelist, struct direntry * const src_filelist, const unsigned int nrentries);
void BLI_filelist_entry_free(struct direntry *entry);
void BLI_filelist_free(struct direntry *filelist, const unsigned int nrentries);
void BLI_filelist_entry_size_to_string(const struct stat *st, const uint64_t sz, const bool compact, char r_size[]);
void BLI_filelist_entry_mode_to_string(
const struct stat *st, const bool compact, char r_mode1[], char r_mode2[], char r_mode3[]);
void BLI_filelist_entry_owner_to_string(const struct stat *st, const bool compact, char r_owner[]);
void BLI_filelist_entry_datetime_to_string(
const struct stat *st, const int64_t ts, const bool compact, char r_time[], char r_date[]);
/* Files */

View File

@@ -39,7 +39,11 @@
typedef unsigned int mode_t;
#endif
struct ImBuf;
#define FILELIST_DIRENTRY_SIZE_LEN 16
#define FILELIST_DIRENTRY_MODE_LEN 4
#define FILELIST_DIRENTRY_OWNER_LEN 16
#define FILELIST_DIRENTRY_TIME_LEN 8
#define FILELIST_DIRENTRY_DATE_LEN 16
struct direntry {
mode_t type;
@@ -56,19 +60,6 @@ struct direntry {
#else
struct stat s;
#endif
unsigned int flags;
char size[16];
char mode1[4];
char mode2[4];
char mode3[4];
char owner[16];
char time[8];
char date[16];
char extra[16];
void *poin;
int nr;
struct ImBuf *image;
unsigned int selflag; /* selection flag */
};
struct dirlink {

View File

@@ -182,7 +182,6 @@ static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname)
/* Hack around for UNC paths on windows - does not support stat on '\\SERVER\foo\..', sigh... */
file->type |= S_IFDIR;
}
file->flags = 0;
dir_ctx->nrfiles++;
file++;
dlink = dlink->next;
@@ -209,112 +208,13 @@ static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname)
}
}
/**
* Fills in the "mode[123]", "size" and "string" fields in the elements of the files
* array with descriptive details about each item. "string" will have a format similar to "ls -l".
*/
static void bli_adddirstrings(struct BuildDirCtx *dir_ctx)
{
const char *types[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
/* symbolic display, indexed by mode field value */
int num;
double size;
struct direntry *file;
struct tm *tm;
time_t zero = 0;
#ifndef WIN32
int mode;
#endif
for (num = 0, file = dir_ctx->files; num < dir_ctx->nrfiles; num++, file++) {
/* Mode */
#ifdef WIN32
BLI_strncpy(file->mode1, types[0], sizeof(file->mode1));
BLI_strncpy(file->mode2, types[0], sizeof(file->mode2));
BLI_strncpy(file->mode3, types[0], sizeof(file->mode3));
#else
mode = file->s.st_mode;
BLI_strncpy(file->mode1, types[(mode & 0700) >> 6], sizeof(file->mode1));
BLI_strncpy(file->mode2, types[(mode & 0070) >> 3], sizeof(file->mode2));
BLI_strncpy(file->mode3, types[(mode & 0007)], sizeof(file->mode3));
if (((mode & S_ISGID) == S_ISGID) && (file->mode2[2] == '-')) file->mode2[2] = 'l';
if (mode & (S_ISUID | S_ISGID)) {
if (file->mode1[2] == 'x') file->mode1[2] = 's';
else file->mode1[2] = 'S';
if (file->mode2[2] == 'x') file->mode2[2] = 's';
}
if (mode & S_ISVTX) {
if (file->mode3[2] == 'x') file->mode3[2] = 't';
else file->mode3[2] = 'T';
}
#endif
/* User */
#ifdef WIN32
strcpy(file->owner, "user");
#else
{
struct passwd *pwuser;
pwuser = getpwuid(file->s.st_uid);
if (pwuser) {
BLI_strncpy(file->owner, pwuser->pw_name, sizeof(file->owner));
}
else {
BLI_snprintf(file->owner, sizeof(file->owner), "%u", file->s.st_uid);
}
}
#endif
/* Time */
tm = localtime(&file->s.st_mtime);
// prevent impossible dates in windows
if (tm == NULL) tm = localtime(&zero);
strftime(file->time, sizeof(file->time), "%H:%M", tm);
strftime(file->date, sizeof(file->date), "%d-%b-%y", tm);
/* Size */
/*
* Seems st_size is signed 32-bit value in *nix and Windows. This
* will buy us some time until files get bigger than 4GB or until
* everyone starts using __USE_FILE_OFFSET64 or equivalent.
*/
size = (double)file->s.st_size;
if (size > 1024.0 * 1024.0 * 1024.0 * 1024.0) {
BLI_snprintf(file->size, sizeof(file->size), "%.1f TiB", size / (1024.0 * 1024.0 * 1024.0 * 1024.0));
}
else if (size > 1024.0 * 1024.0 * 1024.0) {
BLI_snprintf(file->size, sizeof(file->size), "%.1f GiB", size / (1024.0 * 1024.0 * 1024.0));
}
else if (size > 1024.0 * 1024.0) {
BLI_snprintf(file->size, sizeof(file->size), "%.1f MiB", size / (1024.0 * 1024.0));
}
else if (size > 1024.0) {
BLI_snprintf(file->size, sizeof(file->size), "%.1f KiB", size / 1024.0);
}
else {
BLI_snprintf(file->size, sizeof(file->size), "%d B", (int)size);
}
}
}
/**
* Scans the contents of the directory named *dirname, and allocates and fills in an
* array of entries describing them in *filelist.
*
* \return The length of filelist array.
*/
unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **filelist)
unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_filelist)
{
struct BuildDirCtx dir_ctx;
@@ -322,68 +222,188 @@ unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **f
dir_ctx.files = NULL;
bli_builddir(&dir_ctx, dirname);
bli_adddirstrings(&dir_ctx);
if (dir_ctx.files) {
*filelist = dir_ctx.files;
*r_filelist = dir_ctx.files;
}
else {
// keep blender happy. Blender stores this in a variable
// where 0 has special meaning.....
*filelist = MEM_mallocN(sizeof(**filelist), __func__);
*r_filelist = MEM_mallocN(sizeof(**r_filelist), __func__);
}
return dir_ctx.nrfiles;
}
/**
* Convert given entry's size into human-readable strings.
*
*/
void BLI_filelist_entry_size_to_string(
const struct stat *st, const uint64_t sz, const bool compact, char r_size[FILELIST_DIRENTRY_SIZE_LEN])
{
double size;
const char *fmt;
const char *units[] = {"KiB", "MiB", "GiB", "TiB", NULL};
const char *units_compact[] = {"K", "M", "G", "T", NULL};
const char *unit = "B";
/*
* Seems st_size is signed 32-bit value in *nix and Windows. This
* will buy us some time until files get bigger than 4GB or until
* everyone starts using __USE_FILE_OFFSET64 or equivalent.
*/
size = (double)(st ? st->st_size : sz);
if (size > 1024.0) {
const char **u;
for (u = compact ? units_compact : units, size /= 1024.0; size > 1024.0 && *(u + 1); u++, size /= 1024.0);
fmt = size > 100.0 ? "%.0f %s" : (size > 10.0 ? "%.1f %s" : "%.2f %s");
unit = *u;
}
else {
fmt = "%.0f %s";
}
BLI_snprintf(r_size, sizeof(*r_size) * FILELIST_DIRENTRY_SIZE_LEN, fmt, size, unit);
}
/**
* Convert given entry's modes into human-readable strings.
*
*/
void BLI_filelist_entry_mode_to_string(
const struct stat *st, const bool UNUSED(compact), char r_mode1[FILELIST_DIRENTRY_MODE_LEN],
char r_mode2[FILELIST_DIRENTRY_MODE_LEN], char r_mode3[FILELIST_DIRENTRY_MODE_LEN])
{
const char *types[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
#ifdef WIN32
BLI_strncpy(r_mode1, types[0], sizeof(*r_mode1) * FILELIST_DIRENTRY_MODE_LEN);
BLI_strncpy(r_mode2, types[0], sizeof(*r_mode2) * FILELIST_DIRENTRY_MODE_LEN);
BLI_strncpy(r_mode3, types[0], sizeof(*r_mode3) * FILELIST_DIRENTRY_MODE_LEN);
#else
const int mode = st->st_mode;
BLI_strncpy(r_mode1, types[(mode & 0700) >> 6], sizeof(*r_mode1) * FILELIST_DIRENTRY_MODE_LEN);
BLI_strncpy(r_mode2, types[(mode & 0070) >> 3], sizeof(*r_mode2) * FILELIST_DIRENTRY_MODE_LEN);
BLI_strncpy(r_mode3, types[(mode & 0007)], sizeof(*r_mode3) * FILELIST_DIRENTRY_MODE_LEN);
if (((mode & S_ISGID) == S_ISGID) && (r_mode2[2] == '-')) r_mode2[2] = 'l';
if (mode & (S_ISUID | S_ISGID)) {
if (r_mode1[2] == 'x') r_mode1[2] = 's';
else r_mode1[2] = 'S';
if (r_mode2[2] == 'x') r_mode2[2] = 's';
}
if (mode & S_ISVTX) {
if (r_mode3[2] == 'x') r_mode3[2] = 't';
else r_mode3[2] = 'T';
}
#endif
}
/**
* Convert given entry's owner into human-readable strings.
*
*/
void BLI_filelist_entry_owner_to_string(
const struct stat *st, const bool UNUSED(compact), char r_owner[FILELIST_DIRENTRY_OWNER_LEN])
{
#ifdef WIN32
strcpy(r_owner, "unknown");
#else
struct passwd *pwuser = getpwuid(st->st_uid);
if (pwuser) {
BLI_strncpy(r_owner, pwuser->pw_name, sizeof(*r_owner) * FILELIST_DIRENTRY_OWNER_LEN);
}
else {
BLI_snprintf(r_owner, sizeof(*r_owner) * FILELIST_DIRENTRY_OWNER_LEN, "%u", st->st_uid);
}
#endif
}
/**
* Convert given entry's time into human-readable strings.
*/
void BLI_filelist_entry_datetime_to_string(
const struct stat *st, const int64_t ts, const bool compact,
char r_time[FILELIST_DIRENTRY_TIME_LEN], char r_date[FILELIST_DIRENTRY_DATE_LEN])
{
const struct tm *tm = localtime(st ? &st->st_mtime : &ts);
const time_t zero = 0;
/* Prevent impossible dates in windows. */
if (tm == NULL) {
tm = localtime(&zero);
}
if (r_time) {
strftime(r_time, sizeof(*r_time) * FILELIST_DIRENTRY_TIME_LEN, "%H:%M", tm);
}
if (r_date) {
strftime(r_date, sizeof(*r_date) * FILELIST_DIRENTRY_DATE_LEN, compact ? "%d/%m/%y" : "%d-%b-%y", tm);
}
}
/**
* Deep-duplicate of a single direntry.
*
* \param dup_poin If given, called for each non-NULL direntry->poin. Otherwise, pointer is always simply copied over.
*/
void BLI_filelist_entry_duplicate(struct direntry *dst, const struct direntry *src)
{
*dst = *src;
if (dst->relname) {
dst->relname = MEM_dupallocN(src->relname);
}
if (dst->path) {
dst->path = MEM_dupallocN(src->path);
}
}
/**
* Deep-duplicate of an array of direntries, including the array itself.
*
* \param dup_poin If given, called for each non-NULL direntry->poin. Otherwise, pointer is always simply copied over.
*/
void BLI_filelist_duplicate(
struct direntry **dest_filelist, struct direntry *src_filelist, unsigned int nrentries,
void *(*dup_poin)(void *))
struct direntry **dest_filelist, struct direntry * const src_filelist, const unsigned int nrentries)
{
unsigned int i;
*dest_filelist = MEM_mallocN(sizeof(**dest_filelist) * (size_t)(nrentries), __func__);
for (i = 0; i < nrentries; ++i) {
struct direntry * const src = &src_filelist[i];
struct direntry *dest = &(*dest_filelist)[i];
*dest = *src;
if (dest->image) {
dest->image = IMB_dupImBuf(src->image);
}
if (dest->relname) {
dest->relname = MEM_dupallocN(src->relname);
}
if (dest->path) {
dest->path = MEM_dupallocN(src->path);
}
if (dest->poin && dup_poin) {
dest->poin = dup_poin(src->poin);
}
struct direntry *dst = &(*dest_filelist)[i];
BLI_filelist_entry_duplicate(dst, src);
}
}
/**
* frees storage for a single direntry, not the direntry itself.
*/
void BLI_filelist_entry_free(struct direntry *entry)
{
if (entry->relname) {
MEM_freeN((void *)entry->relname);
}
if (entry->path) {
MEM_freeN((void *)entry->path);
}
}
/**
* frees storage for an array of direntries, including the array itself.
*/
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries, void (*free_poin)(void *))
void BLI_filelist_free(struct direntry *filelist, const unsigned int nrentries)
{
unsigned int i;
for (i = 0; i < nrentries; ++i) {
struct direntry *entry = filelist + i;
if (entry->image) {
IMB_freeImBuf(entry->image);
}
if (entry->relname)
MEM_freeN((void *)entry->relname);
if (entry->path)
MEM_freeN((void *)entry->path);
if (entry->poin && free_poin)
free_poin(entry->poin);
BLI_filelist_entry_free(&filelist[i]);
}
if (filelist != NULL) {

View File

@@ -33,6 +33,7 @@
struct ARegion;
struct FileSelectParams;
struct ScrArea;
struct SpaceFile;
struct bContext;
struct wmWindowManager;
@@ -40,17 +41,13 @@ struct wmWindowManager;
#define FILE_LAYOUT_HOR 1
#define FILE_LAYOUT_VER 2
#define MAX_FILE_COLUMN 8
#define MAX_FILE_COLUMN 4
typedef enum FileListColumns {
COLUMN_NAME = 0,
COLUMN_DATE,
COLUMN_TIME,
COLUMN_SIZE,
COLUMN_MODE1,
COLUMN_MODE2,
COLUMN_MODE3,
COLUMN_OWNER
} FileListColumns;
typedef struct FileLayout {
@@ -71,6 +68,9 @@ typedef struct FileLayout {
int dirty;
int textheight;
float column_widths[MAX_FILE_COLUMN];
/* When we change display size, we may have to update static strings like size of files... */
short curr_size;
} FileLayout;
typedef struct FileSelection {
@@ -100,9 +100,9 @@ void ED_fileselect_layout_tilepos(FileLayout *layout, int tile, int *x, int *y);
void ED_operatormacros_file(void);
void ED_fileselect_clear(struct wmWindowManager *wm, struct SpaceFile *sfile);
void ED_fileselect_clear(struct wmWindowManager *wm, struct ScrArea *sa, struct SpaceFile *sfile);
void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile);
void ED_fileselect_exit(struct wmWindowManager *wm, struct ScrArea *sa, struct SpaceFile *sfile);
int ED_file_extension_icon(const char *relname);

View File

@@ -757,7 +757,7 @@ static void init_iconfile_list(struct ListBase *list)
}
}
BLI_filelist_free(dir, totfile, NULL);
BLI_filelist_free(dir, totfile);
dir = NULL;
}

View File

@@ -3343,7 +3343,8 @@ void uiTemplateOperatorSearch(uiLayout *layout)
#define B_STOPCOMPO 4
#define B_STOPSEQ 5
#define B_STOPCLIP 6
#define B_STOPOTHER 7
#define B_STOPFILE 7
#define B_STOPOTHER 8
static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
{
@@ -3366,6 +3367,9 @@ static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
case B_STOPCLIP:
WM_jobs_stop(CTX_wm_manager(C), CTX_wm_area(C), NULL);
break;
case B_STOPFILE:
WM_jobs_stop(CTX_wm_manager(C), CTX_wm_area(C), NULL);
break;
case B_STOPOTHER:
G.is_break = true;
break;
@@ -3396,6 +3400,12 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C)
owner = sa;
handle_event = B_STOPCLIP;
}
else if (sa->spacetype == SPACE_FILE) {
if (WM_jobs_test(wm, sa, WM_JOB_TYPE_FILESEL_READDIR)) {
owner = sa;
}
handle_event = B_STOPFILE;
}
else {
Scene *scene;
/* another scene can be rendering too, for example via compositor */

View File

@@ -31,6 +31,7 @@ set(INC
../../makesrna
../../render/extern/include
../../windowmanager
../../../../intern/atomic
../../../../intern/guardedalloc
../../../../intern/glew-mx
)

View File

@@ -29,6 +29,7 @@ Import ('env')
sources = env.Glob('*.c')
incs = [
'#/intern/atomic',
'#/intern/guardedalloc',
env['BF_GLEW_INC'],
'#/intern/glew-mx',

View File

@@ -35,6 +35,7 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLI_fileops_types.h"
#include "BLI_math.h"
#ifdef WIN32
# include "BLI_winstuff.h"
@@ -47,6 +48,8 @@
#include "BKE_global.h"
#include "BKE_main.h"
#include "BLO_readfile.h"
#include "BLT_translation.h"
#include "IMB_imbuf_types.h"
@@ -64,12 +67,20 @@
#include "UI_resources.h"
#include "UI_view2d.h"
#include "WM_api.h"
#include "WM_types.h"
#include "filelist.h"
#include "file_intern.h" // own include
/* Dummy helper - we need dynamic tooltips here. */
static char *file_draw_tooltip_func(bContext *UNUSED(C), void *argN, const char *UNUSED(tip))
{
char *dyn_tooltip = argN;
return BLI_strdup(dyn_tooltip);
}
/* Note: This function uses pixelspace (0, 0, winx, winy), not view2d.
* The controls are laid out as follows:
*
@@ -157,9 +168,9 @@ void file_draw_buttons(const bContext *C, ARegion *ar)
/* Text input fields for directory and file. */
if (available_w > 0) {
const struct direntry *file = sfile->files ? filelist_file(sfile->files, params->active_file) : NULL;
const struct FileDirEntry *file = sfile->files ? filelist_file(sfile->files, params->active_file) : NULL;
int overwrite_alert = file_draw_check_exists(sfile);
const bool is_active_dir = file && file->path && BLI_is_dir(file->path);
const bool is_active_dir = file && (file->typeflag & FILE_TYPE_FOLDER);
/* callbacks for operator check functions */
UI_block_func_set(block, file_draw_check_cb, NULL, NULL);
@@ -220,8 +231,8 @@ void file_draw_buttons(const bContext *C, ARegion *ar)
/* Execute / cancel buttons. */
if (loadbutton) {
const struct direntry *file = filelist_file(sfile->files, params->active_file);
const char *str_exec = (file && file->path && BLI_is_dir(file->path)) ?
const struct FileDirEntry *file = sfile->files ? filelist_file(sfile->files, params->active_file) : NULL;
const char *str_exec = (file && (file->typeflag & FILE_TYPE_FOLDER)) ?
/* params->title is already translated! */
IFACE_("Open Directory") : params->title;
@@ -244,44 +255,6 @@ static void draw_tile(int sx, int sy, int width, int height, int colorid, int sh
}
static int get_file_icon(struct direntry *file)
{
if (file->type & S_IFDIR) {
if (FILENAME_IS_PARENT(file->relname)) {
return ICON_FILE_PARENT;
}
if (file->flags & FILE_TYPE_APPLICATIONBUNDLE) {
return ICON_UGLYPACKAGE;
}
if (file->flags & FILE_TYPE_BLENDER) {
return ICON_FILE_BLEND;
}
return ICON_FILE_FOLDER;
}
else if (file->flags & FILE_TYPE_BLENDER)
return ICON_FILE_BLEND;
else if (file->flags & FILE_TYPE_BLENDER_BACKUP)
return ICON_FILE_BACKUP;
else if (file->flags & FILE_TYPE_IMAGE)
return ICON_FILE_IMAGE;
else if (file->flags & FILE_TYPE_MOVIE)
return ICON_FILE_MOVIE;
else if (file->flags & FILE_TYPE_PYSCRIPT)
return ICON_FILE_SCRIPT;
else if (file->flags & FILE_TYPE_SOUND)
return ICON_FILE_SOUND;
else if (file->flags & FILE_TYPE_FTFONT)
return ICON_FILE_FONT;
else if (file->flags & FILE_TYPE_BTX)
return ICON_FILE_BLANK;
else if (file->flags & FILE_TYPE_COLLADA)
return ICON_FILE_BLANK;
else if (file->flags & FILE_TYPE_TEXT)
return ICON_FILE_TEXT;
else
return ICON_FILE_BLANK;
}
static void file_draw_icon(uiBlock *block, const char *path, int sx, int sy, int icon, int width, int height, bool drag)
{
uiBut *but;
@@ -293,10 +266,12 @@ static void file_draw_icon(uiBlock *block, const char *path, int sx, int sy, int
/*if (icon == ICON_FILE_BLANK) alpha = 0.375f;*/
but = uiDefIconBut(block, UI_BTYPE_LABEL, 0, icon, x, y, width, height, NULL, 0.0f, 0.0f, 0.0f, 0.0f, "");
but = uiDefIconBut(block, UI_BTYPE_LABEL, 0, icon, x, y, width, height, NULL, 0.0f, 0.0f, 0.0f, 0.0f, NULL);
UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path));
if (drag) {
UI_but_drag_set_path(but, path, false);
/* path is no more static, cannot give it directly to but... */
UI_but_drag_set_path(but, BLI_strdup(path), true);
}
}
@@ -338,7 +313,9 @@ void file_calc_previews(const bContext *C, ARegion *ar)
UI_view2d_totRect_set(v2d, sfile->layout->width, sfile->layout->height);
}
static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int sy, ImBuf *imb, FileLayout *layout, bool is_icon, bool drag)
static void file_draw_preview(
uiBlock *block, const char *path, int sx, int sy, const float icon_aspect,
ImBuf *imb, const int icon, FileLayout *layout, const bool is_icon, const int typeflags, const bool drag)
{
uiBut *but;
float fx, fy;
@@ -348,7 +325,7 @@ static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int
float scaledx, scaledy;
float scale;
int ex, ey;
bool use_dropshadow = !is_icon && (file->flags & FILE_TYPE_IMAGE);
bool use_dropshadow = !is_icon && (typeflags & FILE_TYPE_IMAGE);
BLI_assert(imb != NULL);
@@ -394,7 +371,7 @@ static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int
glEnable(GL_BLEND);
/* the image */
if (!is_icon && file->flags & FILE_TYPE_FTFONT) {
if (!is_icon && typeflags & FILE_TYPE_FTFONT) {
UI_ThemeColor(TH_TEXT);
}
else {
@@ -402,16 +379,23 @@ static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int
}
glaDrawPixelsTexScaled((float)xco, (float)yco, imb->x, imb->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, imb->rect, scale, scale);
if (icon) {
UI_icon_draw_aspect((float)xco, (float)yco, icon, icon_aspect, 1.0f);
}
/* border */
if (use_dropshadow) {
glColor4f(0.0f, 0.0f, 0.0f, 0.4f);
fdrawbox((float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey));
}
but = uiDefBut(block, UI_BTYPE_LABEL, 0, "", xco, yco, ex, ey, NULL, 0.0, 0.0, 0, 0, NULL);
UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path));
/* dragregion */
if (drag) {
but = uiDefBut(block, UI_BTYPE_LABEL, 0, "", xco, yco, ex, ey, NULL, 0.0, 0.0, 0, 0, "");
UI_but_drag_set_image(but, file->path, get_file_icon(file), imb, scale, false);
/* path is no more static, cannot give it directly to but... */
UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true);
}
glDisable(GL_BLEND);
@@ -424,6 +408,7 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname)
char filename[FILE_MAX + 12];
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
BLI_make_file_string(G.main->name, orgname, sfile->params->dir, oldname);
@@ -435,7 +420,7 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname)
if (!BLI_exists(newname)) {
BLI_rename(orgname, newname);
/* to make sure we show what is on disk */
ED_fileselect_clear(wm, sfile);
ED_fileselect_clear(wm, sa, sfile);
}
ED_region_tag_redraw(ar);
@@ -500,7 +485,8 @@ void file_draw_list(const bContext *C, ARegion *ar)
FileLayout *layout = ED_fileselect_get_layout(sfile, ar);
View2D *v2d = &ar->v2d;
struct FileList *files = sfile->files;
struct direntry *file;
struct FileDirEntry *file;
const char *root = filelist_dir(files);
ImBuf *imb;
uiBlock *block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
int numfiles;
@@ -513,8 +499,11 @@ void file_draw_list(const bContext *C, ARegion *ar)
short align;
bool do_drag;
int column_space = 0.6f * UI_UNIT_X;
const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size);
const bool update_stat_strings = small_size != SMALL_SIZE_CHECK(layout->curr_size);
const float thumb_icon_aspect = sqrtf(64.0f / (float)(params->thumbnail_size));
numfiles = filelist_numfiles(files);
numfiles = filelist_files_ensure(files);
if (params->display != FILE_IMGDISPLAY) {
@@ -536,27 +525,61 @@ void file_draw_list(const bContext *C, ARegion *ar)
numfiles_layout += layout->columns;
}
filelist_file_cache_slidingwindow_set(files, numfiles_layout);
textwidth = (FILE_IMGDISPLAY == params->display) ? layout->tile_w : (int)layout->column_widths[COLUMN_NAME];
textheight = (int)(layout->textheight * 3.0 / 2.0 + 0.5);
align = (FILE_IMGDISPLAY == params->display) ? UI_STYLE_TEXT_CENTER : UI_STYLE_TEXT_LEFT;
if (numfiles > 0) {
const bool success = filelist_file_cache_block(files, min_ii(offset + (numfiles_layout / 2), numfiles - 1));
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
filelist_cache_previews_update(files);
/* Handle preview timer here, since it's filelist_file_cache_block() and filelist_cache_previews_update()
* which controlls previews task. */
{
const bool previews_running = filelist_cache_previews_running(files);
// printf("%s: preview task: %d\n", __func__, previews_running);
if (previews_running && !sfile->previews_timer) {
sfile->previews_timer = WM_event_add_timer_notifier(CTX_wm_manager(C), CTX_wm_window(C),
NC_SPACE | ND_SPACE_FILE_PREVIEW, 0.01);
}
if (!previews_running && sfile->previews_timer) {
/* Preview is not running, no need to keep generating update events! */
// printf("%s: Inactive preview task, sleeping!\n", __func__);
WM_event_remove_timer_notifier(CTX_wm_manager(C), CTX_wm_window(C), sfile->previews_timer);
sfile->previews_timer = NULL;
}
}
}
for (i = offset; (i < numfiles) && (i < offset + numfiles_layout); i++) {
unsigned int file_selflag;
char path[FILE_MAX_LIBEXTRA];
ED_fileselect_layout_tilepos(layout, i, &sx, &sy);
sx += (int)(v2d->tot.xmin + 0.1f * UI_UNIT_X);
sy = (int)(v2d->tot.ymax - sy);
file = filelist_file(files, i);
file_selflag = filelist_entry_select_get(sfile->files, file, CHECK_ALL);
BLI_join_dirfile(path, sizeof(path), root, file->relpath);
UI_ThemeColor4(TH_TEXT);
if (!(file->selflag & FILE_SEL_EDITING)) {
if ((params->highlight_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) || (file->selflag & FILE_SEL_SELECTED)) {
int colorid = (file->selflag & FILE_SEL_SELECTED) ? TH_HILITE : TH_BACK;
int shade = (params->highlight_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) ? 35 : 0;
if (!(file_selflag & FILE_SEL_EDITING)) {
if ((params->highlight_file == i) || (file_selflag & FILE_SEL_HIGHLIGHTED) ||
(file_selflag & FILE_SEL_SELECTED))
{
int colorid = (file_selflag & FILE_SEL_SELECTED) ? TH_HILITE : TH_BACK;
int shade = (params->highlight_file == i) || (file_selflag & FILE_SEL_HIGHLIGHTED) ? 35 : 0;
BLI_assert(i > 0 || FILENAME_IS_CURRPAR(file->relname));
BLI_assert(i > 0 || FILENAME_IS_CURRPAR(file->relpath));
draw_tile(sx, sy - 1, layout->tile_w + 4, sfile->layout->tile_h + layout->tile_border_y, colorid, shade);
}
@@ -564,26 +587,29 @@ void file_draw_list(const bContext *C, ARegion *ar)
UI_draw_roundbox_corner_set(UI_CNR_NONE);
/* don't drag parent or refresh items */
do_drag = !(FILENAME_IS_CURRPAR(file->relname));
do_drag = !(FILENAME_IS_CURRPAR(file->relpath));
if (FILE_IMGDISPLAY == params->display) {
const int icon = filelist_geticon(files, i, false);
is_icon = 0;
imb = filelist_getimage(files, i);
if (!imb) {
imb = filelist_geticon(files, i);
imb = filelist_geticon_image(files, i);
is_icon = 1;
}
file_draw_preview(block, file, sx, sy, imb, layout, is_icon, do_drag);
file_draw_preview(block, path, sx, sy, thumb_icon_aspect,
imb, icon, layout, is_icon, file->typeflag, do_drag);
}
else {
file_draw_icon(block, file->path, sx, sy - (UI_UNIT_Y / 6), get_file_icon(file), ICON_DEFAULT_WIDTH_SCALE, ICON_DEFAULT_HEIGHT_SCALE, do_drag);
file_draw_icon(block, path, sx, sy - (UI_UNIT_Y / 6), filelist_geticon(files, i, true),
ICON_DEFAULT_WIDTH_SCALE, ICON_DEFAULT_HEIGHT_SCALE, do_drag);
sx += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X;
}
UI_ThemeColor4(TH_TEXT);
if (file->selflag & FILE_SEL_EDITING) {
if (file_selflag & FILE_SEL_EDITING) {
uiBut *but;
short width;
@@ -591,9 +617,7 @@ void file_draw_list(const bContext *C, ARegion *ar)
width = layout->tile_w - (ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X);
}
else if (params->display == FILE_LONGDISPLAY) {
width = layout->column_widths[COLUMN_NAME] + layout->column_widths[COLUMN_MODE1] +
layout->column_widths[COLUMN_MODE2] + layout->column_widths[COLUMN_MODE3] +
(column_space * 3.5f);
width = layout->column_widths[COLUMN_NAME] + (column_space * 3.5f);
}
else {
BLI_assert(params->display == FILE_IMGDISPLAY);
@@ -601,53 +625,51 @@ void file_draw_list(const bContext *C, ARegion *ar)
}
but = uiDefBut(block, UI_BTYPE_TEXT, 1, "", sx, sy - layout->tile_h - 0.15f * UI_UNIT_X,
width, textheight, sfile->params->renameedit, 1.0f, (float)sizeof(sfile->params->renameedit), 0, 0, "");
width, textheight, sfile->params->renameedit, 1.0f,
(float)sizeof(sfile->params->renameedit), 0, 0, "");
UI_but_func_rename_set(but, renamebutton_cb, file);
UI_but_flag_enable(but, UI_BUT_NO_UTF8); /* allow non utf8 names */
UI_but_flag_disable(but, UI_BUT_UNDO);
if (false == UI_but_active_only(C, ar, block, but)) {
file->selflag &= ~FILE_SEL_EDITING;
file_selflag = filelist_entry_select_set(
sfile->files, file, FILE_SEL_REMOVE, FILE_SEL_EDITING, CHECK_ALL);
}
}
if (!(file->selflag & FILE_SEL_EDITING)) {
if (!(file_selflag& FILE_SEL_EDITING)) {
int tpos = (FILE_IMGDISPLAY == params->display) ? sy - layout->tile_h + layout->textheight : sy;
file_draw_string(sx + 1, tpos, file->relname, (float)textwidth, textheight, align);
file_draw_string(sx + 1, tpos, file->name, (float)textwidth, textheight, align);
}
if (params->display == FILE_SHORTDISPLAY) {
sx += (int)layout->column_widths[COLUMN_NAME] + column_space;
if (!(file->type & S_IFDIR)) {
file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
if (!(file->typeflag & FILE_TYPE_DIR)) {
if ((file->entry->size_str[0] == '\0') || update_stat_strings) {
BLI_filelist_entry_size_to_string(NULL, file->entry->size, small_size, file->entry->size_str);
}
file_draw_string(
sx, sy, file->entry->size_str, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
sx += (int)layout->column_widths[COLUMN_SIZE] + column_space;
}
}
else if (params->display == FILE_LONGDISPLAY) {
sx += (int)layout->column_widths[COLUMN_NAME] + column_space;
#ifndef WIN32
/* rwx rwx rwx */
file_draw_string(sx, sy, file->mode1, layout->column_widths[COLUMN_MODE1], layout->tile_h, align);
sx += layout->column_widths[COLUMN_MODE1] + column_space;
file_draw_string(sx, sy, file->mode2, layout->column_widths[COLUMN_MODE2], layout->tile_h, align);
sx += layout->column_widths[COLUMN_MODE2] + column_space;
file_draw_string(sx, sy, file->mode3, layout->column_widths[COLUMN_MODE3], layout->tile_h, align);
sx += layout->column_widths[COLUMN_MODE3] + column_space;
file_draw_string(sx, sy, file->owner, layout->column_widths[COLUMN_OWNER], layout->tile_h, align);
sx += layout->column_widths[COLUMN_OWNER] + column_space;
#endif
file_draw_string(sx, sy, file->date, layout->column_widths[COLUMN_DATE], layout->tile_h, align);
if ((file->entry->date_str[0] == '\0') || update_stat_strings) {
BLI_filelist_entry_datetime_to_string(
NULL, file->entry->time, small_size, file->entry->time_str, file->entry->date_str);
}
file_draw_string(sx, sy, file->entry->date_str, layout->column_widths[COLUMN_DATE], layout->tile_h, align);
sx += (int)layout->column_widths[COLUMN_DATE] + column_space;
file_draw_string(sx, sy, file->time, layout->column_widths[COLUMN_TIME], layout->tile_h, align);
file_draw_string(sx, sy, file->entry->time_str, layout->column_widths[COLUMN_TIME], layout->tile_h, align);
sx += (int)layout->column_widths[COLUMN_TIME] + column_space;
if (!(file->type & S_IFDIR)) {
file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
if (!(file->typeflag & FILE_TYPE_DIR)) {
if ((file->entry->size_str[0] == '\0') || update_stat_strings) {
BLI_filelist_entry_size_to_string(NULL, file->entry->size, small_size, file->entry->size_str);
}
file_draw_string(
sx, sy, file->entry->size_str, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
sx += (int)layout->column_widths[COLUMN_SIZE] + column_space;
}
}
@@ -656,4 +678,5 @@ void file_draw_list(const bContext *C, ARegion *ar)
UI_block_end(C, block);
UI_block_draw(C, block);
layout->curr_size = params->thumbnail_size;
}

View File

@@ -48,6 +48,8 @@ struct ARegion *file_tools_region(struct ScrArea *sa);
#define IMASEL_BUTTONS_HEIGHT (UI_UNIT_Y * 2)
#define IMASEL_BUTTONS_MARGIN (UI_UNIT_Y / 6)
#define SMALL_SIZE_CHECK(_size) ((_size) < 64) /* Related to FileSelectParams.thumbnail_size. */
void file_draw_buttons(const bContext *C, ARegion *ar);
void file_calc_previews(const bContext *C, ARegion *ar);
void file_draw_list(const bContext *C, ARegion *ar);

View File

@@ -99,9 +99,9 @@ static void file_deselect_all(SpaceFile *sfile, unsigned int flag)
{
FileSelection sel;
sel.first = 0;
sel.last = filelist_numfiles(sfile->files) - 1;
sel.last = filelist_files_ensure(sfile->files) - 1;
filelist_select(sfile->files, &sel, FILE_SEL_REMOVE, flag, CHECK_ALL);
filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_REMOVE, flag, CHECK_ALL);
}
typedef enum FileSelect {
@@ -139,7 +139,7 @@ static FileSelection file_selection_get(bContext *C, const rcti *rect, bool fill
{
ARegion *ar = CTX_wm_region(C);
SpaceFile *sfile = CTX_wm_space_file(C);
int numfiles = filelist_numfiles(sfile->files);
int numfiles = filelist_files_ensure(sfile->files);
FileSelection sel;
sel = find_file_mouse_rect(sfile, ar, rect);
@@ -152,7 +152,7 @@ static FileSelection file_selection_get(bContext *C, const rcti *rect, bool fill
if (fill && (sel.last >= 0) && (sel.last < numfiles) ) {
int f = sel.last;
while (f >= 0) {
if (filelist_is_selected(sfile->files, f, CHECK_ALL) )
if (filelist_entry_select_index_get(sfile->files, f, CHECK_ALL) )
break;
f--;
}
@@ -168,8 +168,8 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
FileSelect retval = FILE_SELECT_NOTHING;
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_params(sfile);
int numfiles = filelist_numfiles(sfile->files);
struct direntry *file;
int numfiles = filelist_files_ensure(sfile->files);
const FileDirEntry *file;
/* make the selected file active */
if ((selected_idx >= 0) &&
@@ -177,27 +177,33 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
(file = filelist_file(sfile->files, selected_idx)))
{
params->highlight_file = selected_idx;
sfile->params->active_file = selected_idx;
params->active_file = selected_idx;
if (S_ISDIR(file->type)) {
const bool is_parent_dir = FILENAME_IS_PARENT(file->relname);
if (file->typeflag & FILE_TYPE_DIR) {
const bool is_parent_dir = FILENAME_IS_PARENT(file->relpath);
if (do_diropen == false) {
params->file[0] = '\0';
retval = FILE_SELECT_DIR;
}
/* the path is too long and we are not going up! */
else if (!is_parent_dir && strlen(params->dir) + strlen(file->relname) >= FILE_MAX) {
else if (!is_parent_dir && strlen(params->dir) + strlen(file->relpath) >= FILE_MAX) {
// XXX error("Path too long, cannot enter this directory");
}
else {
if (is_parent_dir) {
/* avoids /../../ */
BLI_parent_dir(params->dir);
if (params->recursion_level > 1) {
/* Disable 'dirtree' recursion when going up in tree. */
params->recursion_level = 0;
filelist_setrecursion(sfile->files, params->recursion_level);
}
}
else {
BLI_cleanup_dir(G.main->name, params->dir);
strcat(params->dir, file->relname);
strcat(params->dir, file->relpath);
BLI_add_slash(params->dir);
}
@@ -218,11 +224,12 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
*/
static bool file_is_any_selected(struct FileList *files)
{
const int numfiles = filelist_numfiles(files);
const int numfiles = filelist_files_ensure(files);
int i;
/* Is any file selected ? */
for (i = 0; i < numfiles; ++i) {
if (filelist_is_selected(files, i, CHECK_ALL)) {
if (filelist_entry_select_index_get(files, i, CHECK_ALL)) {
return true;
}
}
@@ -239,7 +246,7 @@ static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select,
const FileCheckType check_type = (sfile->params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_ALL;
/* flag the files as selected in the filelist */
filelist_select(sfile->files, &sel, select, FILE_SEL_SELECTED, check_type);
filelist_entries_select_index_range_set(sfile->files, &sel, select, FILE_SEL_SELECTED, check_type);
/* Don't act on multiple selected files */
if (sel.first != sel.last) select = 0;
@@ -247,7 +254,7 @@ static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select,
/* Do we have a valid selection and are we actually selecting */
if ((sel.last >= 0) && (select != FILE_SEL_REMOVE)) {
/* Check last selection, if selected, act on the file or dir */
if (filelist_is_selected(sfile->files, sel.last, check_type)) {
if (filelist_entry_select_index_get(sfile->files, sel.last, check_type)) {
retval = file_select_do(C, sel.last, do_diropen);
}
}
@@ -305,25 +312,24 @@ static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent *
result = WM_border_select_modal(C, op, event);
if (result == OPERATOR_RUNNING_MODAL) {
WM_operator_properties_border_to_rcti(op, &rect);
BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect);
sel = file_selection_get(C, &rect, 0);
if ( (sel.first != params->sel_first) || (sel.last != params->sel_last) ) {
if ((sel.first != params->sel_first) || (sel.last != params->sel_last)) {
int idx;
file_deselect_all(sfile, FILE_SEL_HIGHLIGHTED);
filelist_select(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_HIGHLIGHTED, CHECK_ALL);
filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_HIGHLIGHTED, CHECK_ALL);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
for (idx = sel.last; idx >= 0; idx--) {
struct direntry *file = filelist_file(sfile->files, idx);
const FileDirEntry *file = filelist_file(sfile->files, idx);
/* dont highlight readonly file (".." or ".") on border select */
if (FILENAME_IS_CURRPAR(file->relname)) {
file->selflag &= ~FILE_SEL_HIGHLIGHTED;
if (FILENAME_IS_CURRPAR(file->relpath)) {
filelist_entry_select_set(sfile->files, file, FILE_SEL_REMOVE, FILE_SEL_HIGHLIGHTED, CHECK_ALL);
}
/* make sure highlight_file is no readonly file */
@@ -366,7 +372,7 @@ static int file_border_select_exec(bContext *C, wmOperator *op)
ret = file_select(C, &rect, select ? FILE_SEL_ADD : FILE_SEL_REMOVE, false, false);
/* unselect '..' parent entry - it's not supposed to be selected if more than one file is selected */
filelist_select_file(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
filelist_entry_select_index_set(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
if (FILE_SELECT_DIR == ret) {
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
@@ -416,8 +422,9 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (sfile && sfile->params) {
int idx = sfile->params->highlight_file;
int numfiles = filelist_files_ensure(sfile->files);
if (idx >= 0) {
if ((idx >= 0) && (idx < numfiles)) {
/* single select, deselect all selected first */
if (!extend) {
file_deselect_all(sfile, FILE_SEL_SELECTED);
@@ -429,7 +436,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (extend) {
/* unselect '..' parent entry - it's not supposed to be selected if more than one file is selected */
filelist_select_file(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
filelist_entry_select_index_set(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
}
if (FILE_SELECT_DIR == ret)
@@ -484,12 +491,13 @@ static bool file_walk_select_selection_set(
if (has_selection) {
if (extend &&
filelist_is_selected(files, active_old, FILE_SEL_SELECTED) &&
filelist_is_selected(files, active_new, FILE_SEL_SELECTED))
filelist_entry_select_index_get(files, active_old, FILE_SEL_SELECTED) &&
filelist_entry_select_index_get(files, active_new, FILE_SEL_SELECTED))
{
/* conditions for deselecting: initial file is selected, new file is
* selected and either other_side isn't selected/found or we use fill */
deselect = (fill || other_site == -1 || !filelist_is_selected(files, other_site, FILE_SEL_SELECTED));
deselect = (fill || other_site == -1 ||
!filelist_entry_select_index_get(files, other_site, FILE_SEL_SELECTED));
/* don't change highlight_file here since we either want to deselect active or we want to
* walk through a block of selected files without selecting/deselecting anything */
@@ -527,7 +535,7 @@ static bool file_walk_select_selection_set(
params->highlight_file = params->active_file;
/* unselect '..' parent entry - it's not supposed to be selected if more than one file is selected */
filelist_select_file(files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
filelist_entry_select_index_set(files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
}
else {
/* deselect all first */
@@ -548,15 +556,15 @@ static bool file_walk_select_selection_set(
}
/* fill selection between last and first selected file */
filelist_select(
filelist_entries_select_index_range_set(
files, &sel, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
/* entire sel is cleared here, so select active again */
if (deselect) {
filelist_select_file(files, active, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
filelist_entry_select_index_set(files, active, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
}
}
else {
filelist_select_file(
filelist_entry_select_index_set(
files, active, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
}
@@ -576,7 +584,7 @@ static bool file_walk_select_do(
const bool extend, const bool fill)
{
struct FileList *files = sfile->files;
const int numfiles = filelist_numfiles(files);
const int numfiles = filelist_files_ensure(files);
const bool has_selection = file_is_any_selected(files);
const int active_old = params->active_file;
int active_new = -1;
@@ -692,7 +700,7 @@ static int file_select_all_exec(bContext *C, wmOperator *UNUSED(op))
ScrArea *sa = CTX_wm_area(C);
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelection sel;
const int numfiles = filelist_numfiles(sfile->files);
const int numfiles = filelist_files_ensure(sfile->files);
const bool has_selection = file_is_any_selected(sfile->files);
sel.first = 0;
@@ -700,18 +708,18 @@ static int file_select_all_exec(bContext *C, wmOperator *UNUSED(op))
/* select all only if previously no file was selected */
if (has_selection) {
filelist_select(sfile->files, &sel, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
sfile->params->active_file = -1;
}
else {
const FileCheckType check_type = (sfile->params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_FILES;
int i;
filelist_select(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_SELECTED, check_type);
filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_SELECTED, check_type);
/* set active_file to first selected */
for (i = 0; i < numfiles; i++) {
if (filelist_is_selected(sfile->files, i, check_type)) {
if (filelist_entry_select_index_get(sfile->files, i, check_type)) {
sfile->params->active_file = i;
break;
}
@@ -1029,7 +1037,7 @@ int file_highlight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
if (sfile == NULL || sfile->files == NULL) return 0;
numfiles = filelist_numfiles(sfile->files);
numfiles = filelist_files_ensure(sfile->files);
params = ED_fileselect_get_params(sfile);
origfile = params->highlight_file;
@@ -1143,17 +1151,17 @@ void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath)
/* this is called on operators check() so clear collections first since
* they may be already set. */
{
int i, numfiles = filelist_numfiles(sfile->files);
int i, numfiles = filelist_files_ensure(sfile->files);
if ((prop = RNA_struct_find_property(op->ptr, "files"))) {
PointerRNA itemptr;
int num_files = 0;
RNA_property_collection_clear(op->ptr, prop);
for (i = 0; i < numfiles; i++) {
if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
struct direntry *file = filelist_file(sfile->files, i);
if (filelist_entry_select_index_get(sfile->files, i, CHECK_FILES)) {
FileDirEntry *file = filelist_file(sfile->files, i);
RNA_property_collection_add(op->ptr, prop, &itemptr);
RNA_string_set(&itemptr, "name", file->relname);
RNA_string_set(&itemptr, "name", file->relpath);
num_files++;
}
}
@@ -1169,10 +1177,10 @@ void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath)
int num_dirs = 0;
RNA_property_collection_clear(op->ptr, prop);
for (i = 0; i < numfiles; i++) {
if (filelist_is_selected(sfile->files, i, CHECK_DIRS)) {
struct direntry *file = filelist_file(sfile->files, i);
if (filelist_entry_select_index_get(sfile->files, i, CHECK_DIRS)) {
FileDirEntry *file = filelist_file(sfile->files, i);
RNA_property_collection_add(op->ptr, prop, &itemptr);
RNA_string_set(&itemptr, "name", file->relname);
RNA_string_set(&itemptr, "name", file->relpath);
num_dirs++;
}
}
@@ -1262,19 +1270,17 @@ int file_exec(bContext *C, wmOperator *exec_op)
{
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
const struct direntry *file = filelist_file(sfile->files, sfile->params->active_file);
const struct FileDirEntry *file = filelist_file(sfile->files, sfile->params->active_file);
char filepath[FILE_MAX];
/* directory change */
if (file && S_ISDIR(file->type)) {
BLI_assert(file->path == NULL || STRPREFIX(file->path, sfile->params->dir));
if (FILENAME_IS_PARENT(file->relname)) {
if (file && (file->typeflag & FILE_TYPE_DIR)) {
if (FILENAME_IS_PARENT(file->relpath)) {
BLI_parent_dir(sfile->params->dir);
}
else if (file->relname) {
else if (file->relpath) {
BLI_cleanup_dir(G.main->name, sfile->params->dir);
strcat(sfile->params->dir, file->relname);
strcat(sfile->params->dir, file->relpath);
BLI_add_slash(sfile->params->dir);
}
@@ -1287,10 +1293,11 @@ int file_exec(bContext *C, wmOperator *exec_op)
/* when used as a macro, for doubleclick,
* to prevent closing when doubleclicking on .. item */
if (RNA_boolean_get(exec_op->ptr, "need_active")) {
const int numfiles = filelist_files_ensure(sfile->files);
int i, active = 0;
for (i = 0; i < filelist_numfiles(sfile->files); i++) {
if (filelist_is_selected(sfile->files, i, CHECK_ALL)) {
for (i = 0; i < numfiles; i++) {
if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) {
active = 1;
break;
}
@@ -1358,6 +1365,11 @@ int file_parent_exec(bContext *C, wmOperator *UNUSED(unused))
else {
ED_file_change_dir(C, true);
}
if (sfile->params->recursion_level > 1) {
/* Disable 'dirtree' recursion when going up in tree. */
sfile->params->recursion_level = 0;
filelist_setrecursion(sfile->files, sfile->params->recursion_level);
}
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
}
@@ -1384,9 +1396,10 @@ static int file_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
{
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
ScrArea *sa = CTX_wm_area(C);
struct FSMenu *fsmenu = ED_fsmenu_get();
ED_fileselect_clear(wm, sfile);
ED_fileselect_clear(wm, sa, sfile);
/* refresh system directory menu */
fsmenu_refresh_system_category(fsmenu);
@@ -1476,11 +1489,11 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w
if (sfile->smoothscroll_timer == NULL || sfile->smoothscroll_timer != event->customdata)
return OPERATOR_PASS_THROUGH;
numfiles = filelist_numfiles(sfile->files);
numfiles = filelist_files_ensure(sfile->files);
/* check if we are editing a name */
for (i = 0; i < numfiles; ++i) {
if (filelist_is_selected(sfile->files, i, CHECK_ALL) ) {
if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL) ) {
edit_idx = i;
break;
}
@@ -1603,6 +1616,7 @@ int file_directory_new_exec(bContext *C, wmOperator *op)
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
ScrArea *sa = CTX_wm_area(C);
if (!sfile->params) {
BKE_report(op->reports, RPT_WARNING, "No parent directory given");
@@ -1655,7 +1669,7 @@ int file_directory_new_exec(bContext *C, wmOperator *op)
sfile->scroll_offset = 0;
/* reload dir to make sure we're seeing what's in the directory */
ED_fileselect_clear(wm, sfile);
ED_fileselect_clear(wm, sa, sfile);
if (RNA_boolean_get(op->ptr, "open")) {
BLI_strncpy(sfile->params->dir, path, sizeof(sfile->params->dir));
@@ -1876,10 +1890,11 @@ static int file_hidedot_exec(bContext *C, wmOperator *UNUSED(unused))
{
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
ScrArea *sa = CTX_wm_area(C);
if (sfile->params) {
sfile->params->flag ^= FILE_HIDE_DOT;
ED_fileselect_clear(wm, sfile);
ED_fileselect_clear(wm, sa, sfile);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
@@ -1989,11 +2004,11 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
if (sfile->params) {
int idx = sfile->params->highlight_file;
int numfiles = filelist_numfiles(sfile->files);
int numfiles = filelist_files_ensure(sfile->files);
if ((0 <= idx) && (idx < numfiles)) {
struct direntry *file = filelist_file(sfile->files, idx);
filelist_select_file(sfile->files, idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL);
BLI_strncpy(sfile->params->renameedit, file->relname, FILE_MAXFILE);
FileDirEntry *file = filelist_file(sfile->files, idx);
filelist_entry_select_index_set(sfile->files, idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL);
BLI_strncpy(sfile->params->renameedit, file->relpath, FILE_MAXFILE);
sfile->params->renamefile[0] = '\0';
}
ED_area_tag_redraw(sa);
@@ -2005,29 +2020,34 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
static int file_rename_poll(bContext *C)
{
int poll = ED_operator_file_active(C);
bool poll = ED_operator_file_active(C);
SpaceFile *sfile = CTX_wm_space_file(C);
if (sfile && sfile->params) {
int idx = sfile->params->highlight_file;
int numfiles = filelist_files_ensure(sfile->files);
if (idx >= 0) {
struct direntry *file = filelist_file(sfile->files, idx);
if (FILENAME_IS_CURRPAR(file->relname)) {
poll = 0;
if ((0 <= idx) && (idx < numfiles)) {
FileDirEntry *file = filelist_file(sfile->files, idx);
if (FILENAME_IS_CURRPAR(file->relpath)) {
poll = false;
}
}
if (sfile->params->highlight_file < 0) {
poll = 0;
poll = false;
}
else {
char dir[FILE_MAX];
if (filelist_islibrary(sfile->files, dir, NULL)) poll = 0;
if (filelist_islibrary(sfile->files, dir, NULL)) {
poll = false;
}
}
}
else
poll = 0;
else {
poll = false;
}
return poll;
}
@@ -2051,13 +2071,13 @@ static int file_delete_poll(bContext *C)
if (sfile && sfile->params) {
char dir[FILE_MAX];
int numfiles = filelist_numfiles(sfile->files);
int numfiles = filelist_files_ensure(sfile->files);
int i;
int num_selected = 0;
if (filelist_islibrary(sfile->files, dir, NULL)) poll = 0;
for (i = 0; i < numfiles; i++) {
if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
if (filelist_entry_select_index_get(sfile->files, i, CHECK_FILES)) {
num_selected++;
}
}
@@ -2076,19 +2096,20 @@ int file_delete_exec(bContext *C, wmOperator *UNUSED(op))
char str[FILE_MAX];
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
struct direntry *file;
int numfiles = filelist_numfiles(sfile->files);
ScrArea *sa = CTX_wm_area(C);
FileDirEntry *file;
int numfiles = filelist_files_ensure(sfile->files);
int i;
for (i = 0; i < numfiles; i++) {
if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
if (filelist_entry_select_index_get(sfile->files, i, CHECK_FILES)) {
file = filelist_file(sfile->files, i);
BLI_make_file_string(G.main->name, str, sfile->params->dir, file->relname);
BLI_make_file_string(G.main->name, str, sfile->params->dir, file->relpath);
BLI_delete(str, false, false);
}
}
ED_fileselect_clear(wm, sfile);
ED_fileselect_clear(wm, sa, sfile);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
return OPERATOR_FINISHED;

View File

@@ -35,6 +35,8 @@
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#ifndef WIN32
# include <unistd.h>
@@ -45,9 +47,16 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_fileops.h"
#include "BLI_fileops_types.h"
#include "BLI_fnmatch.h"
#include "BLI_ghash.h"
#include "BLI_hash_md5.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"
#ifdef WIN32
@@ -60,13 +69,13 @@
#include "BKE_icons.h"
#include "BKE_idcode.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BLO_readfile.h"
#include "DNA_space_types.h"
#include "ED_datafiles.h"
#include "ED_fileselect.h"
#include "ED_screen.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -78,6 +87,9 @@
#include "WM_types.h"
#include "UI_resources.h"
#include "UI_interface_icons.h"
#include "atomic_ops.h"
#include "filelist.h"
@@ -194,49 +206,128 @@ ListBase *folderlist_duplicate(ListBase *folderlist)
/* ------------------FILELIST------------------------ */
struct FileList;
typedef struct FileListInternEntry {
struct FileListInternEntry *next, *prev;
typedef struct FileImage {
struct FileImage *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;
/* Allows to quickly get a cached entry from its UUID. */
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];
unsigned int flags;
int index;
short done;
ImBuf *img;
} FileImage;
} FileListEntryPreview;
typedef struct FileListFilter {
bool hide_dot;
bool hide_parent;
unsigned int filter;
unsigned int filter_id;
char filter_glob[64];
char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */
short flags;
} FileListFilter;
/* FileListFilter.flags */
enum {
FLF_HIDE_DOT = 1 << 0,
FLF_HIDE_PARENT = 1 << 1,
FLF_HIDE_LIB_DIR = 1 << 2,
};
typedef struct FileList {
struct direntry *filelist;
int numfiles;
char dir[FILE_MAX];
FileDirEntryArr filelist;
short prv_w;
short prv_h;
bool changed;
short flags;
short sort;
bool need_sorting;
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;
void (*readf)(struct FileList *);
bool (*filterf)(struct direntry *, const char *, FileListFilter *);
/* Set given path as root directory, may change given string in place to a valid value. */
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.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_ROWS 4
#define SPECIAL_IMG_COLS 4
@@ -260,153 +351,187 @@ enum {
static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
static void filelist_from_main(struct FileList *filelist);
static void filelist_from_library(struct FileList *filelist);
static void filelist_readjob_main(struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
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);
static void filelist_read_dir(struct FileList *filelist);
/* helper, could probably go in BKE actually? */
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_cache_clear(FileListEntryCache *cache, size_t new_size);
/* ********** Sort helpers ********** */
static bool compare_is_directory(const struct direntry *entry)
{
/* 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)
static int compare_direntry_generic(const FileListInternEntry *entry1, const FileListInternEntry *entry2)
{
/* type is equal to stat.st_mode */
if (compare_is_directory(entry1)) {
if (compare_is_directory(entry2) == 0) {
if (entry1->typeflag & FILE_TYPE_DIR) {
if (entry2->typeflag & FILE_TYPE_DIR) {
/* 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 (entry1->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
if (!(entry2->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
return 1;
}
}
else if (entry2->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
return -1;
}
}
else {
return -1;
}
}
else if (compare_is_directory(entry2)) {
return 1;
else if (entry2->typeflag & FILE_TYPE_DIR) {
return 1;
}
if (S_ISREG(entry1->type)) {
if (!S_ISREG(entry2->type)) {
return -1;
}
}
else if (S_ISREG(entry2->type)) {
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 */
if (FILENAME_IS_CURRENT(entry1->relname)) return -1;
if (FILENAME_IS_CURRENT(entry2->relname)) return 1;
if (FILENAME_IS_PARENT(entry1->relname)) return -1;
if (FILENAME_IS_PARENT(entry2->relname)) return 1;
if (FILENAME_IS_CURRENT(entry1->relpath)) return -1;
if (FILENAME_IS_CURRENT(entry2->relpath)) return 1;
if (FILENAME_IS_PARENT(entry1->relpath)) return -1;
if (FILENAME_IS_PARENT(entry2->relpath)) return 1;
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;
if ((ret = compare_direntry_generic(entry1, entry2))) {
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;
if ((ret = compare_direntry_generic(entry1, entry2))) {
return ret;
}
if (entry1->s.st_mtime < entry2->s.st_mtime) return 1;
if (entry1->s.st_mtime > entry2->s.st_mtime) return -1;
time1 = (int64_t)entry1->st.st_mtime;
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;
if ((ret = compare_direntry_generic(entry1, entry2))) {
return ret;
}
if (entry1->s.st_size < entry2->s.st_size) return 1;
if (entry1->s.st_size > entry2->s.st_size) return -1;
size1 = entry1->st.st_size;
size2 = entry2->st.st_size;
if (size1 < size2) return 1;
if (size1 > size2) return -1;
return BLI_natstrcmp(entry1->relname, entry2->relname);
name1 = entry1->name;
name2 = entry2->name;
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 char *sufix1, *sufix2;
const char *nil = "";
const FileListInternEntry *entry1 = a1;
const FileListInternEntry *entry2 = a2;
char *name1, *name2;
int ret;
if ((ret = compare_direntry_generic(entry1, entry2))) {
return ret;
}
if (!(sufix1 = strstr(entry1->relname, ".blend.gz")))
sufix1 = strrchr(entry1->relname, '.');
if (!(sufix2 = strstr(entry2->relname, ".blend.gz")))
sufix2 = strrchr(entry2->relname, '.');
if (!sufix1) sufix1 = nil;
if (!sufix2) sufix2 = nil;
if ((entry1->typeflag & FILE_TYPE_BLENDERLIB) && !(entry2->typeflag & FILE_TYPE_BLENDERLIB)) return -1;
if (!(entry1->typeflag & FILE_TYPE_BLENDERLIB) && (entry2->typeflag & FILE_TYPE_BLENDERLIB)) return 1;
if ((entry1->typeflag & FILE_TYPE_BLENDERLIB) && (entry2->typeflag & FILE_TYPE_BLENDERLIB)) {
if ((entry1->typeflag & FILE_TYPE_DIR) && !(entry2->typeflag & FILE_TYPE_DIR)) return 1;
if (!(entry1->typeflag & FILE_TYPE_DIR) && (entry2->typeflag & FILE_TYPE_DIR)) return -1;
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 = "";
bool filelist_need_sorting(struct FileList *filelist)
{
return filelist->need_sorting && (filelist->sort != FILE_SORT_NONE);
if ((ret = BLI_strcasecmp(sufix1, sufix2))) {
return ret;
}
}
name1 = entry1->name;
name2 = entry2->name;
return BLI_natstrcmp(name1, name2);
}
void filelist_sort(struct FileList *filelist)
{
if (filelist_need_sorting(filelist)) {
filelist->need_sorting = false;
if ((filelist->flags & FL_NEED_SORTING) && (filelist->sort != FILE_SORT_NONE)) {
switch (filelist->sort) {
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;
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;
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;
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;
case FILE_SORT_NONE: /* Should never reach this point! */
default:
BLI_assert(0);
return;
}
filelist_filter_clear(filelist);
filelist->flags &= ~FL_NEED_SORTING;
}
}
@@ -414,7 +539,7 @@ void filelist_setsorting(struct FileList *filelist, const short sort)
{
if (filelist->sort != sort) {
filelist->sort = sort;
filelist->need_sorting = true;
filelist->flags |= FL_NEED_SORTING;
}
}
@@ -422,9 +547,10 @@ void filelist_setsorting(struct FileList *filelist, const short sort)
static bool is_hidden_file(const char *filename, FileListFilter *filter)
{
char *sep = (char *)BLI_last_slash(filename);
bool is_hidden = false;
if (filter->hide_dot) {
if (filter->flags & FLF_HIDE_DOT) {
if (filename[0] == '.' && filename[1] != '.' && filename[1] != '\0') {
is_hidden = true; /* ignore .file */
}
@@ -435,7 +561,7 @@ static bool is_hidden_file(const char *filename, FileListFilter *filter)
}
}
}
if (!is_hidden && filter->hide_parent) {
if (!is_hidden && (filter->flags & FLF_HIDE_PARENT)) {
if (filename[0] == '.' && filename[1] == '.' && filename[2] == '\0') {
is_hidden = true; /* ignore .. */
}
@@ -443,22 +569,49 @@ static bool is_hidden_file(const char *filename, FileListFilter *filter)
if (!is_hidden && ((filename[0] == '.') && (filename[1] == '\0'))) {
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;
}
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 ((file->type & S_IFDIR) && !(filter->filter & FILE_TYPE_FOLDER)) {
is_filtered = false;
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 (!(file->type & S_IFDIR) && !(file->flags & filter->filter)) {
is_filtered = false;
else {
if (!(file->typeflag & filter->filter)) {
is_filtered = false;
}
}
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;
}
}
@@ -467,16 +620,41 @@ static bool is_filtered_file(struct direntry *file, const char *UNUSED(root), Fi
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);
char dir[FILE_MAXDIR];
bool is_filtered;
char path[FILE_MAX_LIBEXTRA], dir[FILE_MAXDIR], *group, *name;
if (BLO_library_path_explode(root, dir, NULL, NULL)) {
is_filtered = !is_hidden_file(file->relname, filter);
if (is_filtered && filter->filter && !FILENAME_IS_CURRPAR(file->relname)) {
BLI_join_dirfile(path, sizeof(path), root, file->relpath);
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 (fnmatch(filter->filter_search, file->relname, FNM_CASEFOLD) != 0) {
if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
is_filtered = false;
}
}
@@ -489,70 +667,97 @@ static bool is_filtered_lib(struct direntry *file, const char *root, FileListFil
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)
{
MEM_SAFE_FREE(filelist->fidx);
filelist->numfiltered = 0;
filelist->flags |= FL_NEED_FILTERING;
}
void filelist_filter(FileList *filelist)
{
int num_filtered = 0;
int *fidx_tmp;
int i;
const int num_files = filelist->filelist.nbr_entries;
FileListInternEntry **filtered_tmp, *file;
if (!filelist->filelist) {
if (filelist->filelist.nbr_entries == 0) {
return;
}
if (filelist->fidx) {
if (!(filelist->flags & FL_NEED_FILTERING)) {
/* Assume it has already been filtered, nothing else to do! */
return;
}
fidx_tmp = MEM_mallocN(sizeof(*fidx_tmp) * (size_t)filelist->numfiles, __func__);
/* Filter remap & count how many files are left after filter in a single loop. */
for (i = 0; i < filelist->numfiles; ++i) {
struct direntry *file = &filelist->filelist[i];
if (filelist->filterf(file, filelist->dir, &filelist->filter_data)) {
fidx_tmp[num_filtered++] = i;
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;
}
}
/* Note: maybe we could even accept filelist->fidx to be filelist->numfiles -len allocated? */
filelist->fidx = MEM_mallocN(sizeof(*filelist->fidx) * (size_t)num_filtered, __func__);
memcpy(filelist->fidx, fidx_tmp, sizeof(*filelist->fidx) * (size_t)num_filtered);
filelist->numfiltered = num_filtered;
filtered_tmp = MEM_mallocN(sizeof(*filtered_tmp) * (size_t)num_files, __func__);
MEM_freeN(fidx_tmp);
/* Filter remap & count how many files are left after filter in a single loop. */
for (file = filelist->filelist_intern.entries.first; file; file = file->next) {
if (filelist->filterf(file, filelist->filelist.root, &filelist->filter_data)) {
filtered_tmp[num_filtered++] = file;
}
}
if (filelist->filelist_intern.filtered) {
MEM_freeN(filelist->filelist_intern.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(filtered_tmp);
}
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)
{
if ((filelist->filter_data.hide_dot != hide_dot) ||
(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;
bool update = false;
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_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));
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, '*',
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);
}
}
@@ -607,101 +812,489 @@ void filelist_imgsize(struct FileList *filelist, short w, short 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);
if ((index < 0) || (index >= filelist->numfiltered)) {
return NULL;
}
fidx = filelist->fidx[index];
ibuf = filelist->filelist[fidx].image;
return ibuf;
return filelist_file(filelist, index);
}
ImBuf *filelist_geticon(struct FileList *filelist, const int index)
ImBuf *filelist_getimage(struct FileList *filelist, const int index)
{
FileDirEntry *file = filelist_geticon_get_file(filelist, index);
return file->image;
}
static ImBuf *filelist_geticon_image_ex(const unsigned int typeflag, const char *relpath)
{
ImBuf *ibuf = NULL;
struct direntry *file = NULL;
int fidx = 0;
BLI_assert(G.background == false);
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)) {
if (typeflag & FILE_TYPE_DIR) {
if (FILENAME_IS_PARENT(relpath)) {
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];
}
else {
ibuf = gSpecialFileImages[SPECIAL_IMG_FOLDER];
}
}
else {
ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE];
}
if (file->flags & FILE_TYPE_BLENDER) {
else if (typeflag & FILE_TYPE_BLENDER) {
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];
}
else if (file->flags & FILE_TYPE_SOUND) {
else if (typeflag & FILE_TYPE_SOUND) {
ibuf = gSpecialFileImages[SPECIAL_IMG_SOUNDFILE];
}
else if (file->flags & FILE_TYPE_PYSCRIPT) {
else if (typeflag & FILE_TYPE_PYSCRIPT) {
ibuf = gSpecialFileImages[SPECIAL_IMG_PYTHONFILE];
}
else if (file->flags & FILE_TYPE_FTFONT) {
else if (typeflag & FILE_TYPE_FTFONT) {
ibuf = gSpecialFileImages[SPECIAL_IMG_FONTFILE];
}
else if (file->flags & FILE_TYPE_TEXT) {
else if (typeflag & FILE_TYPE_TEXT) {
ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE];
}
else if (file->flags & FILE_TYPE_IMAGE) {
else if (typeflag & FILE_TYPE_IMAGE) {
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];
}
else {
ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE];
}
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 ********** */
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) {
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 UNUSED(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_path_lock(preview->path);
preview->img = IMB_thumb_manage(preview->path, THB_LARGE, source);
IMB_thumb_path_unlock(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 = max_ii(2, BLI_system_thread_count() / 2);
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 *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) {
case FILE_MAIN:
p->readf = filelist_read_main;
p->checkdirf = filelist_checkdir_main;
p->read_jobf = filelist_readjob_main;
p->filterf = is_filtered_main;
break;
case FILE_LOADLIB:
p->readf = filelist_read_library;
p->checkdirf = filelist_checkdir_lib;
p->read_jobf = filelist_readjob_lib;
p->filterf = is_filtered_lib;
break;
default:
p->readf = filelist_read_dir;
p->checkdirf = filelist_checkdir_dir;
p->read_jobf = filelist_readjob_dir;
p->filterf = is_filtered_file;
break;
}
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)
{
if (!filelist) {
@@ -709,16 +1302,18 @@ void filelist_free(struct FileList *filelist)
return;
}
MEM_SAFE_FREE(filelist->fidx);
filelist->numfiltered = 0;
filelist_clear_ex(filelist, false, false); /* No need to clear cache & selection_state, we free them anyway. */
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));
filelist->need_sorting = false;
filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING);
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)
@@ -733,49 +1328,194 @@ BlendHandle *filelist_lib(struct FileList *filelist)
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)
{
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;
}
}
short filelist_changed(struct FileList *filelist)
void filelist_setrecursion(struct FileList *filelist, const int recursion_level)
{
return filelist->changed;
if (filelist->max_recursion != recursion_level) {
filelist->max_recursion = recursion_level;
filelist->flags |= FL_FORCE_RESET;
}
}
struct direntry *filelist_file(struct FileList *filelist, int index)
bool filelist_force_reset(struct FileList *filelist)
{
int fidx = 0;
if ((index < 0) || (index >= filelist->numfiltered)) {
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;
}
static void filelist_file_release_entry(FileList *filelist, FileDirEntry *entry)
{
BLI_remlink(&filelist->filelist.entries, entry);
filelist_entry_free(entry);
}
static FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const bool use_request)
{
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 ((ret = BLI_ghash_lookup(cache->misc_entries, SET_INT_IN_POINTER(index)))) {
return ret;
}
if (!use_request) {
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;
}
int filelist_find(struct FileList *filelist, const char *filename)
FileDirEntry *filelist_file(struct FileList *filelist, int index)
{
return filelist_file_ex(filelist, index, true);
}
int filelist_file_findpath(struct FileList *filelist, const char *filename)
{
int fidx = -1;
if (!filelist->fidx)
if (filelist->filelist.nbr_entries_filtered < 0) {
return fidx;
}
for (fidx = 0; fidx < filelist->numfiltered; fidx++) {
int index = filelist->fidx[fidx];
/* XXX TODO Cache could probably use a ghash on paths too? Not really urgent though.
* 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;
}
}
@@ -783,6 +1523,375 @@ int filelist_find(struct FileList *filelist, const char *filename)
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;
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 */
static bool file_is_blend_backup(const char *str)
{
@@ -857,10 +1966,10 @@ static int path_extension_type(const char *path)
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];
BLI_join_dirfile(path, sizeof(path), dir, relname);
BLI_join_dirfile(path, sizeof(path), dir, relpath);
return path_extension_type(path);
}
@@ -868,185 +1977,137 @@ int ED_file_extension_icon(const char *path)
{
int type = path_extension_type(path);
if (type == FILE_TYPE_BLENDER)
return ICON_FILE_BLEND;
else if (type == FILE_TYPE_BLENDER_BACKUP)
return ICON_FILE_BACKUP;
else if (type == FILE_TYPE_IMAGE)
return ICON_FILE_IMAGE;
else if (type == FILE_TYPE_MOVIE)
return ICON_FILE_MOVIE;
else if (type == FILE_TYPE_PYSCRIPT)
return ICON_FILE_SCRIPT;
else if (type == FILE_TYPE_SOUND)
return ICON_FILE_SOUND;
else if (type == FILE_TYPE_FTFONT)
return ICON_FILE_FONT;
else if (type == FILE_TYPE_BTX)
return ICON_FILE_BLANK;
else if (type == FILE_TYPE_COLLADA)
return ICON_FILE_BLANK;
else if (type == FILE_TYPE_TEXT)
return ICON_FILE_TEXT;
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] &&
BLI_testextensie_glob(file->relname, filelist->filter_data.filter_glob))
{
file->flags = FILE_TYPE_OPERATOR;
}
else {
file->flags = file_extension_type(filelist->dir, file->relname);
}
switch (type) {
case FILE_TYPE_BLENDER:
return ICON_FILE_BLEND;
case FILE_TYPE_BLENDER_BACKUP:
return ICON_FILE_BACKUP;
case FILE_TYPE_IMAGE:
return ICON_FILE_IMAGE;
case FILE_TYPE_MOVIE:
return ICON_FILE_MOVIE;
case FILE_TYPE_PYSCRIPT:
return ICON_FILE_SCRIPT;
case FILE_TYPE_SOUND:
return ICON_FILE_SOUND;
case FILE_TYPE_FTFONT:
return ICON_FILE_FONT;
case FILE_TYPE_BTX:
return ICON_FILE_BLANK;
case FILE_TYPE_COLLADA:
return ICON_FILE_BLANK;
case FILE_TYPE_TEXT:
return ICON_FILE_TEXT;
default:
return ICON_FILE_BLANK;
}
}
static void filelist_read_dir(struct FileList *filelist)
{
if (!filelist) return;
filelist->fidx = NULL;
filelist->filelist = NULL;
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
* (ensured by BLI_make_exist()). So we expect at the very least the parent '..' entry. */
BLI_assert(filelist->numfiles != 0);
filelist_setfiletypes(filelist);
}
static void filelist_read_main(struct FileList *filelist)
{
if (!filelist) return;
filelist_from_main(filelist);
}
static void filelist_read_library(struct FileList *filelist)
{
if (!filelist) return;
BLI_cleanup_dir(G.main->name, filelist->dir);
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;
}
}
}
}
}
void filelist_readdir(struct FileList *filelist)
{
filelist->readf(filelist);
filelist->need_sorting = true;
filelist->need_thumbnails = true;
filelist_filter_clear(filelist);
}
int filelist_empty(struct FileList *filelist)
{
return filelist->filelist == NULL;
return (filelist->filelist.nbr_entries == 0);
}
void filelist_select_file(struct FileList *filelist, int index, FileSelType select, unsigned int flag, FileCheckType check)
unsigned int filelist_entry_select_set(
const FileList *filelist, const FileDirEntry *entry, FileSelType select, unsigned int flag, FileCheckType 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);
/* Default NULL pointer if not found is fine here! */
void **es_p = BLI_ghash_lookup_p(filelist->selection_state, entry->uuid);
unsigned int entry_flag = es_p ? GET_UINT_FROM_POINTER(*es_p) : 0;
const unsigned int org_entry_flag = entry_flag;
BLI_assert(entry);
BLI_assert(ELEM(check, CHECK_DIRS, CHECK_FILES, CHECK_ALL));
if (((check == CHECK_ALL)) ||
((check == CHECK_DIRS) && (entry->typeflag & FILE_TYPE_DIR)) ||
((check == CHECK_FILES) && !(entry->typeflag & FILE_TYPE_DIR)))
{
switch (select) {
case FILE_SEL_REMOVE:
entry_flag &= ~flag;
break;
case CHECK_ALL:
check_ok = 1;
case FILE_SEL_ADD:
entry_flag |= flag;
break;
case CHECK_FILES:
default:
check_ok = !S_ISDIR(file->type);
case FILE_SEL_TOGGLE:
entry_flag ^= flag;
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;
}
if (entry_flag != org_entry_flag) {
if (es_p) {
if (entry_flag) {
*es_p = SET_UINT_IN_POINTER(entry_flag);
}
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));
}
}
return entry_flag;
}
void filelist_entry_select_index_set(FileList *filelist, const int index, FileSelType select, unsigned int flag, FileCheckType check)
{
FileDirEntry *entry = filelist_file(filelist, index);
if (entry) {
filelist_entry_select_set(filelist, entry, select, flag, check);
}
}
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 */
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;
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);
if (!file) {
return 0;
}
switch (check) {
case CHECK_DIRS:
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;
BLI_assert(entry);
BLI_assert(ELEM(check, CHECK_DIRS, CHECK_FILES, CHECK_ALL));
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));
}
return 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)
{
return BLO_library_path_explode(filelist->dir, dir, group, NULL);
return BLO_library_path_explode(filelist->filelist.root, dir, group, NULL);
}
static int groupname_to_code(const char *group)
@@ -1054,6 +2115,8 @@ static int groupname_to_code(const char *group)
char buf[BLO_GROUP_MAX];
char *lslash;
BLI_assert(group);
BLI_strncpy(buf, group, sizeof(buf));
lslash = (char *)BLI_last_slash(buf);
if (lslash)
@@ -1062,182 +2125,231 @@ static int groupname_to_code(const char *group)
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;
struct ImBuf *ima;
int ok, i, nprevs, nnames, idcode;
char filename[FILE_MAX];
int id_code = groupname_to_code(group);
return BKE_idcode_to_idfilter(id_code);
}
/*
* 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).
*/
typedef struct TodoDir {
int level;
char *dir;
} TodoDir;
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;
}
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];
entry->typeflag = FILE_TYPE_BLENDER;
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 {
entry->typeflag = file_extension_type(root, entry->relpath);
}
}
BLI_addtail(entries, entry);
nbr_entries++;
}
BLI_filelist_free(files, nbr_files);
}
return nbr_entries;
}
static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar)
{
FileListInternEntry *entry;
LinkNode *ln, *names;
int i, nnames, idcode = 0, nbr_entries = 0;
char dir[FILE_MAX], *group;
bool ok;
struct BlendHandle *libfiledata = NULL;
/* name test */
ok = filelist_islibrary(filelist, dir, &group);
ok = BLO_library_path_explode(root, dir, &group, NULL);
if (!ok) {
/* free */
if (filelist->libfiledata) BLO_blendhandle_close(filelist->libfiledata);
filelist->libfiledata = NULL;
return;
return nbr_entries;
}
BLI_strncpy(filename, G.main->name, sizeof(filename));
/* there we go */
/* for the time being only read filedata when libfiledata==0 */
if (filelist->libfiledata == NULL) {
filelist->libfiledata = BLO_blendhandle_from_file(dir, NULL);
if (filelist->libfiledata == NULL) return;
libfiledata = BLO_blendhandle_from_file(dir, NULL);
if (libfiledata == NULL) {
return nbr_entries;
}
idcode = group ? groupname_to_code(group) : 0;
/* memory for strings is passed into filelist[i].relname
* 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);
/* memory for strings is passed into filelist[i].entry->relpath and freed in filelist_entry_free. */
if (group) {
idcode = groupname_to_code(group);
names = BLO_blendhandle_get_datablock_names(libfiledata, idcode, &nnames);
}
else {
previews = NULL;
nprevs = 0;
names = BLO_blendhandle_get_linkable_groups(filelist->libfiledata);
names = BLO_blendhandle_get_linkable_groups(libfiledata);
nnames = BLI_linklist_count(names);
}
filelist->numfiles = nnames + 1;
filelist->filelist = MEM_mallocN(filelist->numfiles * sizeof(*filelist->filelist), __func__);
memset(filelist->filelist, 0, filelist->numfiles * sizeof(*filelist->filelist));
BLO_blendhandle_close(libfiledata);
filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT);
filelist->filelist[0].type |= S_IFDIR;
for (i = 0, l = names; i < nnames; i++, l = l->next) {
const char *blockname = l->link;
if (!skip_currpar) {
entry = MEM_callocN(sizeof(*entry), __func__);
entry->relpath = BLI_strdup(FILENAME_PARENT);
entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR);
BLI_addtail(entries, entry);
nbr_entries++;
}
filelist->filelist[i + 1].relname = BLI_strdup(blockname);
if (idcode) {
filelist->filelist[i + 1].type |= S_IFREG;
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 {
filelist->filelist[i + 1].type |= S_IFDIR;
}
}
if (previews && (nnames != nprevs)) {
printf("filelist_from_library: error, found %d items, %d previews\n", nnames, nprevs);
}
else if (previews) {
for (i = 0, l = previews; i < nnames; i++, l = l->next) {
PreviewImage *img = l->link;
if (img) {
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 (w > 0 && h > 0 && rect) {
ima = IMB_allocImBuf(w, h, 32, IB_rect);
memcpy(ima->rect, rect, w * h * sizeof(unsigned int));
filelist->filelist[i + 1].image = ima;
filelist->filelist[i + 1].flags = FILE_TYPE_IMAGE;
}
}
entry->blentype = idcode;
}
BLI_addtail(entries, entry);
nbr_entries++;
}
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;
struct direntry *files, *firstlib = NULL;
FileDirEntry *files, *firstlib = NULL;
ListBase *lb;
int a, fake, idcode, ok, totlib, totbl;
// filelist->type = FILE_MAIN; // XXX TODO: add modes to filebrowser
// filelist->type = FILE_MAIN; // XXXXX 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]) {
idcode = groupname_to_code(filelist->dir);
if (idcode == 0) filelist->dir[0] = 0;
if (filelist->filelist.root[0]) {
idcode = groupname_to_code(filelist->filelist.root);
if (idcode == 0) filelist->filelist.root[0] = '\0';
}
if (filelist->dir[0] == 0) {
/* make directories */
#ifdef WITH_FREESTYLE
filelist->numfiles = 24;
filelist->filelist.nbr_entries = 24;
#else
filelist->numfiles = 23;
filelist->filelist.nbr_entries = 23;
#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++) {
memset(&(filelist->filelist[a]), 0, sizeof(struct direntry));
filelist->filelist[a].type |= S_IFDIR;
for (a = 0; a < filelist->filelist.nbr_entries; a++) {
filelist->filelist.entries[a].typeflag |= FILE_TYPE_DIR;
}
filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT);
filelist->filelist[1].relname = BLI_strdup("Scene");
filelist->filelist[2].relname = BLI_strdup("Object");
filelist->filelist[3].relname = BLI_strdup("Mesh");
filelist->filelist[4].relname = BLI_strdup("Curve");
filelist->filelist[5].relname = BLI_strdup("Metaball");
filelist->filelist[6].relname = BLI_strdup("Material");
filelist->filelist[7].relname = BLI_strdup("Texture");
filelist->filelist[8].relname = BLI_strdup("Image");
filelist->filelist[9].relname = BLI_strdup("Ika");
filelist->filelist[10].relname = BLI_strdup("Wave");
filelist->filelist[11].relname = BLI_strdup("Lattice");
filelist->filelist[12].relname = BLI_strdup("Lamp");
filelist->filelist[13].relname = BLI_strdup("Camera");
filelist->filelist[14].relname = BLI_strdup("Ipo");
filelist->filelist[15].relname = BLI_strdup("World");
filelist->filelist[16].relname = BLI_strdup("Screen");
filelist->filelist[17].relname = BLI_strdup("VFont");
filelist->filelist[18].relname = BLI_strdup("Text");
filelist->filelist[19].relname = BLI_strdup("Armature");
filelist->filelist[20].relname = BLI_strdup("Action");
filelist->filelist[21].relname = BLI_strdup("NodeTree");
filelist->filelist[22].relname = BLI_strdup("Speaker");
filelist->filelist.entries[0].entry->relpath = BLI_strdup(FILENAME_PARENT);
filelist->filelist.entries[1].entry->relpath = BLI_strdup("Scene");
filelist->filelist.entries[2].entry->relpath = BLI_strdup("Object");
filelist->filelist.entries[3].entry->relpath = BLI_strdup("Mesh");
filelist->filelist.entries[4].entry->relpath = BLI_strdup("Curve");
filelist->filelist.entries[5].entry->relpath = BLI_strdup("Metaball");
filelist->filelist.entries[6].entry->relpath = BLI_strdup("Material");
filelist->filelist.entries[7].entry->relpath = BLI_strdup("Texture");
filelist->filelist.entries[8].entry->relpath = BLI_strdup("Image");
filelist->filelist.entries[9].entry->relpath = BLI_strdup("Ika");
filelist->filelist.entries[10].entry->relpath = BLI_strdup("Wave");
filelist->filelist.entries[11].entry->relpath = BLI_strdup("Lattice");
filelist->filelist.entries[12].entry->relpath = BLI_strdup("Lamp");
filelist->filelist.entries[13].entry->relpath = BLI_strdup("Camera");
filelist->filelist.entries[14].entry->relpath = BLI_strdup("Ipo");
filelist->filelist.entries[15].entry->relpath = BLI_strdup("World");
filelist->filelist.entries[16].entry->relpath = BLI_strdup("Screen");
filelist->filelist.entries[17].entry->relpath = BLI_strdup("VFont");
filelist->filelist.entries[18].entry->relpath = BLI_strdup("Text");
filelist->filelist.entries[19].entry->relpath = BLI_strdup("Armature");
filelist->filelist.entries[20].entry->relpath = BLI_strdup("Action");
filelist->filelist.entries[21].entry->relpath = BLI_strdup("NodeTree");
filelist->filelist.entries[22].entry->relpath = BLI_strdup("Speaker");
#ifdef WITH_FREESTYLE
filelist->filelist[23].relname = BLI_strdup("FreestyleLineStyle");
filelist->filelist.entries[23].entry->relpath = BLI_strdup("FreestyleLineStyle");
#endif
}
else {
/* make files */
idcode = groupname_to_code(filelist->dir);
idcode = groupname_to_code(filelist->filelist.root);
lb = which_libbase(G.main, idcode);
if (lb == NULL) return;
filelist->numfiles = 0;
filelist->filelist.nbr_entries = 0;
for (id = lb->first; id; id = id->next) {
if (!filelist->filter_data.hide_dot || id->name[2] != '.') {
filelist->numfiles++;
if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') {
filelist->filelist.nbr_entries++;
}
}
/* XXXXX TODO: if databrowse F4 or append/link filelist->hide_parent has to be set */
if (!filelist->filter_data.hide_parent) filelist->numfiles += 1;
filelist->filelist = filelist->numfiles > 0 ? MEM_mallocN(sizeof(*filelist->filelist) * filelist->numfiles, __func__) : NULL;
/* XXX TODO: if databrowse F4 or append/link filelist->flags & FLF_HIDE_PARENT has to be set */
if (!(filelist->filter_data.flags & FLF_HIDE_PARENT))
filelist->filelist.nbr_entries++;
files = filelist->filelist;
if (filelist->filelist.nbr_entries > 0) {
filelist_resize(filelist, filelist->filelist.nbr_entries);
}
if (files && !filelist->filter_data.hide_parent) {
memset(&(filelist->filelist[0]), 0, sizeof(struct direntry));
filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT);
filelist->filelist[0].type |= S_IFDIR;
files = filelist->filelist.entries;
if (!(filelist->filter_data.flags & FLF_HIDE_PARENT)) {
files->entry->relpath = BLI_strdup(FILENAME_PARENT);
files->typeflag |= FILE_TYPE_DIR;
files++;
}
@@ -1246,37 +2358,36 @@ static void filelist_from_main(struct FileList *filelist)
for (id = lb->first; id; id = id->next) {
ok = 1;
if (ok) {
if (files && (!filelist->filter_data.hide_dot || id->name[2] != '.')) {
memset(files, 0, sizeof(struct direntry));
if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') {
if (id->lib == NULL) {
files->relname = BLI_strdup(id->name + 2);
files->entry->relpath = BLI_strdup(id->name + 2);
}
else {
char relname[FILE_MAX + (MAX_ID_NAME - 2) + 3];
char relname[FILE_MAX + (MAX_ID_NAME - 2) + 3];
BLI_snprintf(relname, sizeof(relname), "%s | %s", id->lib->name, id->name + 2);
files->relname = BLI_strdup(relname);
files->entry->relpath = BLI_strdup(relname);
}
files->type |= S_IFREG;
#if 0 /* XXXXX TODO show the selection status of the objects */
// files->type |= S_IFREG;
#if 0 /* XXX TODO show the selection status of the objects */
if (!filelist->has_func) { /* F4 DATA BROWSE */
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) {
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
files->nr = totbl + 1;
files->poin = id;
// files->entry->nr = totbl + 1;
files->entry->poin = id;
fake = id->flag & LIB_FAKEUSER;
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);
else if (id->lib) BLI_snprintf(files->extra, sizeof(files->extra), "L %d", id->us);
else if (fake) BLI_snprintf(files->extra, sizeof(files->extra), "F %d", id->us);
else BLI_snprintf(files->extra, sizeof(files->extra), " %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->entry->extra), "L %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->entry->extra), " %d", id->us);
if (id->lib) {
if (totlib == 0) firstlib = files;
@@ -1291,152 +2402,279 @@ static void filelist_from_main(struct FileList *filelist)
/* only qsort of library blocks */
if (totlib > 1) {
qsort(firstlib, totlib, sizeof(struct direntry), compare_name);
qsort(firstlib, totlib, sizeof(*files), compare_name);
}
}
}
#endif
/* ********** Thumbnails job ********** */
typedef struct ThumbnailJob {
ListBase loadimages;
ImBuf *static_icons_buffers[BIFICONID_LAST];
const short *stop;
const short *do_update;
struct FileList *filelist;
ReportList reports;
} ThumbnailJob;
bool filelist_need_thumbnails(FileList *filelist)
static void filelist_readjob_do(
const bool do_lib,
FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock)
{
return filelist->need_thumbnails;
}
ListBase entries = {0};
BLI_Stack *todo_dirs;
TodoDir *td_dir;
char dir[FILE_MAX_LIBEXTRA];
char filter_glob[64]; /* TODO should be define! */
const char *root = filelist->filelist.root;
const int max_recursion = filelist->max_recursion;
int nbr_done_dirs = 0, nbr_todo_dirs = 1;
static void thumbnail_joblist_free(ThumbnailJob *tj)
{
FileImage *limg = tj->loadimages.first;
/* free the images not yet copied to the filelist -> these will get freed with the filelist */
for (; limg; limg = limg->next) {
if ((limg->img) && (!limg->done)) {
IMB_freeImBuf(limg->img);
// 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);
}
}
BLI_freelistN(&tj->loadimages);
}
static void thumbnails_startjob(void *tjv, short *stop, short *do_update, float *UNUSED(progress))
{
ThumbnailJob *tj = tjv;
FileImage *limg = tj->loadimages.first;
tj->stop = stop;
tj->do_update = do_update;
while ((*stop == 0) && (limg)) {
ThumbSource source = 0;
BLI_assert(limg->flags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP));
if (limg->flags & FILE_TYPE_IMAGE) {
source = THB_SOURCE_IMAGE;
if (!nbr_entries) {
is_lib = false;
nbr_entries = filelist_readjob_list_dir(subdir, &entries, filter_glob, do_lib, main_name, skip_currpar);
}
else if (limg->flags & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
source = THB_SOURCE_BLEND;
}
else if (limg->flags & FILE_TYPE_MOVIE) {
source = THB_SOURCE_MOVIE;
}
else if (limg->flags & FILE_TYPE_FTFONT) {
source = THB_SOURCE_FONT;
}
limg->img = IMB_thumb_manage(limg->path, THB_LARGE, source);
*do_update = true;
PIL_sleep_ms(10);
limg = limg->next;
}
}
static void thumbnails_update(void *tjv)
{
ThumbnailJob *tj = tjv;
for (entry = entries.first; entry; entry = entry->next) {
BLI_join_dirfile(dir, sizeof(dir), subdir, entry->relpath);
BLI_cleanup_file(root, dir);
if (tj->filelist && tj->filelist->filelist) {
FileImage *limg = tj->loadimages.first;
while (limg) {
if (!limg->done && limg->img) {
tj->filelist->filelist[limg->index].image = IMB_dupImBuf(limg->img);
limg->done = true;
IMB_freeImBuf(limg->img);
limg->img = NULL;
/* 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) &&
((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0))
{
/* Do not recurse in real directories in this case, only in .blend libs. */
}
else {
/* We have a directory we want to list, add it to todo list! */
BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath);
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++;
}
}
limg = limg->next;
}
if (nbr_entries) {
BLI_mutex_lock(lock);
*do_update = true;
BLI_movelisttolist(&filelist->filelist.entries, &entries);
filelist->filelist.nbr_entries += nbr_entries;
BLI_mutex_unlock(lock);
}
nbr_done_dirs++;
*progress = (float)nbr_done_dirs / (float)nbr_todo_dirs;
MEM_freeN(subdir);
}
}
static void thumbnails_endjob(void *tjv)
{
ThumbnailJob *tj = tjv;
if (!*tj->stop) {
tj->filelist->need_thumbnails = false;
/* 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);
}
BLI_stack_free(todo_dirs);
}
static void thumbnails_free(void *tjv)
static void filelist_readjob_dir(
FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock)
{
ThumbnailJob *tj = tjv;
thumbnail_joblist_free(tj);
MEM_freeN(tj);
filelist_readjob_do(false, filelist, main_name, stop, do_update, progress, lock);
}
static void filelist_readjob_lib(
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 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);
}
void thumbnails_start(FileList *filelist, const bContext *C)
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)
{
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;
flrj->tmp_filelist->filelist_intern.filtered = NULL;
BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries);
memset(flrj->tmp_filelist->filelist_intern.curr_uuid, 0, sizeof(flrj->tmp_filelist->filelist_intern.curr_uuid));
flrj->tmp_filelist->libfiledata = NULL;
memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache));
flrj->tmp_filelist->selection_state = NULL;
BLI_mutex_unlock(&flrj->lock);
flrj->tmp_filelist->read_jobf(flrj->tmp_filelist, flrj->main_name, stop, do_update, progress, &flrj->lock);
}
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 filelist_readjob_endjob(void *flrjv)
{
FileListReadJob *flrj = flrjv;
/* In case there would be some dangling update... */
filelist_readjob_update(flrjv);
flrj->filelist->flags &= ~FL_IS_PENDING;
flrj->filelist->flags |= FL_IS_READY;
}
static void filelist_readjob_free(void *flrjv)
{
FileListReadJob *flrj = flrjv;
// 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 filelist_readjob_start(FileList *filelist, const bContext *C)
{
wmJob *wm_job;
ThumbnailJob *tj;
int idx;
FileListReadJob *flrj;
/* prepare job data */
tj = MEM_callocN(sizeof(*tj), __func__);
tj->filelist = filelist;
for (idx = 0; idx < filelist->numfiles; idx++) {
if (!filelist->filelist[idx].path) {
continue;
}
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);
}
}
}
flrj = MEM_callocN(sizeof(*flrj), __func__);
flrj->filelist = filelist;
BLI_strncpy(flrj->main_name, G.main->name, sizeof(flrj->main_name));
BKE_reports_init(&tj->reports, RPT_PRINT);
filelist->flags &= ~(FL_FORCE_RESET | FL_IS_READY);
filelist->flags |= FL_IS_PENDING;
BLI_mutex_init(&flrj->lock);
/* setup job */
wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), filelist, "Thumbnails",
0, WM_JOB_TYPE_FILESEL_THUMBNAIL);
WM_jobs_customdata_set(wm_job, tj, thumbnails_free);
WM_jobs_timer(wm_job, 0.5, NC_WINDOW, NC_WINDOW);
WM_jobs_callbacks(wm_job, thumbnails_startjob, NULL, thumbnails_update, thumbnails_endjob);
wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_wm_area(C), "Listing Dirs...",
WM_JOB_PROGRESS, WM_JOB_TYPE_FILESEL_READDIR);
WM_jobs_customdata_set(wm_job, flrj, filelist_readjob_free);
WM_jobs_timer(wm_job, 0.01, NC_SPACE | ND_SPACE_FILE_LIST, NC_SPACE | ND_SPACE_FILE_LIST);
WM_jobs_callbacks(wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob);
/* start the 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);
}

View File

@@ -40,9 +40,10 @@ extern "C" {
struct BlendHandle;
struct FileList;
struct FileSelection;
struct direntry;
struct wmWindowManager;
struct FileDirEntry;
typedef enum FileSelType {
FILE_SEL_REMOVE = 0,
FILE_SEL_ADD = 1,
@@ -65,11 +66,10 @@ int folderlist_clear_next(struct SpaceFile *sfile);
void filelist_setsorting(struct FileList *filelist, const short sort);
bool filelist_need_sorting(struct FileList *filelist);
void filelist_sort(struct FileList *filelist);
void filelist_setfilter_options(struct 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);
void filelist_filter(struct FileList *filelist);
@@ -77,34 +77,48 @@ void filelist_init_icons(void);
void filelist_free_icons(void);
void filelist_imgsize(struct FileList *filelist, short w, short h);
struct ImBuf * filelist_getimage(struct FileList *filelist, const int index);
struct ImBuf * filelist_geticon(struct FileList *filelist, const int index);
struct ImBuf * filelist_geticon_image(struct FileList *filelist, const int index);
int filelist_geticon(struct FileList *filelist, const int index, const bool is_main);
struct FileList * filelist_new(short type);
void filelist_clear(struct FileList *filelist);
void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection);
void filelist_free(struct FileList *filelist);
const char * filelist_dir(struct FileList *filelist);
void filelist_readdir(struct FileList *filelist);
void filelist_setdir(struct FileList *filelist, const char *dir);
void filelist_setdir(struct FileList *filelist, char *r_dir);
int filelist_files_ensure(struct FileList *filelist);
int filelist_empty(struct FileList *filelist);
int filelist_numfiles(struct FileList *filelist);
struct direntry * filelist_file(struct FileList *filelist, int index);
int filelist_find(struct FileList *filelist, const char *file);
FileDirEntry * filelist_file(struct FileList *filelist, int index);
int filelist_file_findpath(struct FileList *filelist, const char *file);
FileDirEntry * filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4]);
void filelist_file_cache_slidingwindow_set(struct FileList *filelist, size_t window_size);
bool filelist_file_cache_block(struct FileList *filelist, const int index);
short filelist_changed(struct FileList *filelist);
bool filelist_force_reset(struct FileList *filelist);
bool filelist_pending(struct FileList *filelist);
bool filelist_is_ready(struct FileList *filelist);
void filelist_select(struct FileList *filelist, FileSelection *sel, FileSelType select, unsigned int flag, FileCheckType check);
void filelist_select_file(struct FileList *filelist, int index, FileSelType select, unsigned int flag, FileCheckType check);
bool filelist_is_selected(struct FileList *filelist, int index, FileCheckType check);
unsigned int filelist_entry_select_set(const struct FileList *filelist, const struct FileDirEntry *entry, FileSelType select, unsigned int flag, FileCheckType check);
void filelist_entry_select_index_set(struct FileList *filelist, const int index, FileSelType select, unsigned int flag, FileCheckType check);
void filelist_entries_select_index_range_set(struct FileList *filelist, FileSelection *sel, FileSelType select, unsigned int flag, FileCheckType check);
unsigned int filelist_entry_select_get(struct FileList *filelist, struct FileDirEntry *entry, FileCheckType check);
unsigned int filelist_entry_select_index_get(struct FileList *filelist, const int index, FileCheckType check);
void filelist_setrecursion(struct FileList *filelist, const int recursion_level);
struct BlendHandle *filelist_lib(struct FileList *filelist);
bool filelist_islibrary(struct FileList *filelist, char *dir, char **group);
void filelist_freelib(struct FileList *filelist);
bool filelist_need_thumbnails(struct FileList *filelist);
void thumbnails_start(struct FileList *filelist, const struct bContext *C);
void thumbnails_stop(struct wmWindowManager *wm, struct FileList *filelist);
int thumbnails_running(struct wmWindowManager *wm, struct FileList *filelist);
void filelist_readjob_start(struct FileList *filelist, const struct bContext *C);
void filelist_readjob_stop(struct wmWindowManager *wm, struct ScrArea *sa);
int filelist_readjob_running(struct wmWindowManager *wm, struct ScrArea *sa);
bool filelist_cache_previews_update(struct FileList *filelist);
void filelist_cache_previews_set(struct FileList *filelist, const bool use_previews);
bool filelist_cache_previews_running(struct FileList *filelist);
#ifdef __cplusplus
}

View File

@@ -163,6 +163,8 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->filter = 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_blender")))
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BLENDER : 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_blenlib")))
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BLENDERLIB : 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_backup")))
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BLENDER_BACKUP : 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_image")))
@@ -200,6 +202,13 @@ short ED_fileselect_set_params(SpaceFile *sfile)
}
}
/* For now, always init filterid to 'all true' */
params->filter_id = FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU | FILTER_ID_GD |
FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | FILTER_ID_MA |
FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB |
FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO | FILTER_ID_TE |
FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO;
if (U.uiflag & USER_HIDE_DOT) {
params->flag |= FILE_HIDE_DOT;
}
@@ -292,9 +301,9 @@ void ED_fileselect_reset_params(SpaceFile *sfile)
*/
void fileselect_file_set(SpaceFile *sfile, const int index)
{
const struct direntry *file = filelist_file(sfile->files, index);
if (file && file->relname[0] && file->path && !BLI_is_dir(file->path)) {
BLI_strncpy(sfile->params->file, file->relname, FILE_MAXFILE);
const struct FileDirEntry *file = filelist_file(sfile->files, index);
if (file && file->relpath && file->relpath[0] && !(file->typeflag & FILE_TYPE_FOLDER)) {
BLI_strncpy(sfile->params->file, file->relpath, FILE_MAXFILE);
}
}
@@ -446,37 +455,20 @@ float file_font_pointsize(void)
#endif
}
static void column_widths(struct FileList *files, struct FileLayout *layout)
static void column_widths(FileSelectParams *params, struct FileLayout *layout)
{
int i;
int numfiles = filelist_numfiles(files);
const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size);
for (i = 0; i < MAX_FILE_COLUMN; ++i) {
layout->column_widths[i] = 0;
}
for (i = 0; (i < numfiles); ++i) {
struct direntry *file = filelist_file(files, i);
if (file) {
float len;
len = file_string_width(file->relname);
if (len > layout->column_widths[COLUMN_NAME]) layout->column_widths[COLUMN_NAME] = len;
len = file_string_width(file->date);
if (len > layout->column_widths[COLUMN_DATE]) layout->column_widths[COLUMN_DATE] = len;
len = file_string_width(file->time);
if (len > layout->column_widths[COLUMN_TIME]) layout->column_widths[COLUMN_TIME] = len;
len = file_string_width(file->size);
if (len > layout->column_widths[COLUMN_SIZE]) layout->column_widths[COLUMN_SIZE] = len;
len = file_string_width(file->mode1);
if (len > layout->column_widths[COLUMN_MODE1]) layout->column_widths[COLUMN_MODE1] = len;
len = file_string_width(file->mode2);
if (len > layout->column_widths[COLUMN_MODE2]) layout->column_widths[COLUMN_MODE2] = len;
len = file_string_width(file->mode3);
if (len > layout->column_widths[COLUMN_MODE3]) layout->column_widths[COLUMN_MODE3] = len;
len = file_string_width(file->owner);
if (len > layout->column_widths[COLUMN_OWNER]) layout->column_widths[COLUMN_OWNER] = len;
}
}
layout->column_widths[COLUMN_NAME] = ((float)params->thumbnail_size / 8.0f) * UI_UNIT_X;;
/* Biggest possible reasonable values... */
layout->column_widths[COLUMN_DATE] = file_string_width(small_size ? "23/08/89" : "23-Dec-89");
layout->column_widths[COLUMN_TIME] = file_string_width("23:59");
layout->column_widths[COLUMN_SIZE] = file_string_width(small_size ? "98.7 M" : "98.7 MiB");
}
void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
@@ -496,7 +488,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
return;
}
numfiles = filelist_numfiles(sfile->files);
numfiles = filelist_files_ensure(sfile->files);
textheight = (int)file_font_pointsize();
layout = sfile->layout;
layout->textheight = textheight;
@@ -535,7 +527,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
layout->height = (int)(BLI_rctf_size_y(&v2d->cur) - 2 * layout->tile_border_y);
layout->rows = layout->height / (layout->tile_h + 2 * layout->tile_border_y);
column_widths(sfile->files, layout);
column_widths(params, layout);
if (params->display == FILE_SHORTDISPLAY) {
maxlen = ICON_DEFAULT_WIDTH_SCALE + column_icon_space +
@@ -545,12 +537,6 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
else {
maxlen = ICON_DEFAULT_WIDTH_SCALE + column_icon_space +
(int)layout->column_widths[COLUMN_NAME] + column_space +
#ifndef WIN32
(int)layout->column_widths[COLUMN_MODE1] + column_space +
(int)layout->column_widths[COLUMN_MODE2] + column_space +
(int)layout->column_widths[COLUMN_MODE3] + column_space +
(int)layout->column_widths[COLUMN_OWNER] + column_space +
#endif
(int)layout->column_widths[COLUMN_DATE] + column_space +
(int)layout->column_widths[COLUMN_TIME] + column_space +
(int)layout->column_widths[COLUMN_SIZE] + column_space;
@@ -581,9 +567,10 @@ void ED_file_change_dir(bContext *C, const bool checkdir)
{
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
ScrArea *sa = CTX_wm_area(C);
if (sfile->params) {
ED_fileselect_clear(wm, sfile);
ED_fileselect_clear(wm, sa, sfile);
/* Clear search string, it is very rare to want to keep that filter while changing dir,
* and usually very annoying to keep it actually! */
@@ -610,8 +597,8 @@ int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matche
int match = 0;
int i;
struct direntry *file;
int n = filelist_numfiles(sfile->files);
FileDirEntry *file;
int n = filelist_files_ensure(sfile->files);
/* select any file that matches the pattern, this includes exact match
* if the user selects a single file by entering the filename
@@ -619,10 +606,10 @@ int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matche
for (i = 0; i < n; i++) {
file = filelist_file(sfile->files, i);
/* Do not check wether file is a file or dir here! Causes T44243 (we do accept dirs at this stage). */
if (fnmatch(pattern, file->relname, 0) == 0) {
file->selflag |= FILE_SEL_SELECTED;
if (fnmatch(pattern, file->relpath, 0) == 0) {
filelist_entry_select_set(sfile->files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
if (!match) {
BLI_strncpy(matched_file, file->relname, FILE_MAX);
BLI_strncpy(matched_file, file->relpath, FILE_MAX);
}
match++;
}
@@ -687,14 +674,12 @@ int autocomplete_file(struct bContext *C, char *str, void *UNUSED(arg_v))
/* search if str matches the beginning of name */
if (str[0] && sfile->files) {
AutoComplete *autocpl = UI_autocomplete_begin(str, FILE_MAX);
int nentries = filelist_numfiles(sfile->files);
int nentries = filelist_files_ensure(sfile->files);
int i;
for (i = 0; i < nentries; ++i) {
struct direntry *file = filelist_file(sfile->files, i);
if (file && (S_ISREG(file->type) || S_ISDIR(file->type))) {
UI_autocomplete_update_name(autocpl, file->relname);
}
FileDirEntry *file = filelist_file(sfile->files, i);
UI_autocomplete_update_name(autocpl, file->relpath);
}
match = UI_autocomplete_end(autocpl, str);
}
@@ -702,20 +687,20 @@ int autocomplete_file(struct bContext *C, char *str, void *UNUSED(arg_v))
return match;
}
void ED_fileselect_clear(struct wmWindowManager *wm, struct SpaceFile *sfile)
void ED_fileselect_clear(wmWindowManager *wm, ScrArea *sa, SpaceFile *sfile)
{
/* only NULL in rare cases - [#29734] */
if (sfile->files) {
thumbnails_stop(wm, sfile->files);
filelist_readjob_stop(wm, sa);
filelist_freelib(sfile->files);
filelist_free(sfile->files);
filelist_clear(sfile->files);
}
sfile->params->highlight_file = -1;
WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile)
void ED_fileselect_exit(wmWindowManager *wm, ScrArea *sa, SpaceFile *sfile)
{
if (!sfile) return;
if (sfile->op) {
@@ -727,7 +712,8 @@ void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile)
folderlist_free(sfile->folders_next);
if (sfile->files) {
ED_fileselect_clear(wm, sfile);
ED_fileselect_clear(wm, sa, sfile);
filelist_free(sfile->files);
MEM_freeN(sfile->files);
sfile->files = NULL;
}

View File

@@ -117,6 +117,8 @@ static void file_free(SpaceLink *sl)
{
SpaceFile *sfile = (SpaceFile *) sl;
BLI_assert(sfile->previews_timer == NULL);
if (sfile->files) {
// XXXXX would need to do thumbnails_stop here, but no context available
filelist_freelib(sfile->files);
@@ -170,7 +172,12 @@ static void file_exit(wmWindowManager *wm, ScrArea *sa)
{
SpaceFile *sfile = (SpaceFile *)sa->spacedata.first;
ED_fileselect_exit(wm, sfile);
if (sfile->previews_timer) {
WM_event_remove_timer_notifier(wm, NULL, sfile->previews_timer);
sfile->previews_timer = NULL;
}
ED_fileselect_exit(wm, sa, sfile);
}
static SpaceLink *file_duplicate(SpaceLink *sl)
@@ -211,13 +218,15 @@ static void file_refresh(const bContext *C, ScrArea *sa)
}
if (!sfile->files) {
sfile->files = filelist_new(params->type);
filelist_setdir(sfile->files, params->dir);
params->highlight_file = -1; /* added this so it opens nicer (ton) */
}
filelist_setdir(sfile->files, params->dir);
filelist_setrecursion(sfile->files, params->recursion_level);
filelist_setsorting(sfile->files, params->sort);
filelist_setfilter_options(sfile->files, params->flag & FILE_HIDE_DOT,
filelist_setfilter_options(sfile->files, (params->flag & FILE_HIDE_DOT) != 0,
false, /* TODO hide_parent, should be controllable? */
params->flag & FILE_FILTER ? params->filter : 0,
params->filter_id,
params->filter_glob,
params->filter_search);
@@ -227,39 +236,45 @@ static void file_refresh(const bContext *C, ScrArea *sa)
sfile->bookmarknr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir);
sfile->recentnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_RECENT, params->dir);
if (filelist_empty(sfile->files)) {
thumbnails_stop(wm, sfile->files);
filelist_readdir(sfile->files);
filelist_sort(sfile->files);
BLI_strncpy(params->dir, filelist_dir(sfile->files), FILE_MAX);
}
else if (filelist_need_sorting(sfile->files)) {
thumbnails_stop(wm, sfile->files);
filelist_sort(sfile->files);
if (filelist_force_reset(sfile->files)) {
filelist_readjob_stop(wm, sa);
filelist_clear(sfile->files);
}
if ((params->display == FILE_IMGDISPLAY) && filelist_need_thumbnails(sfile->files)) {
if (!thumbnails_running(wm, sfile->files)) {
thumbnails_start(sfile->files, C);
if (filelist_empty(sfile->files)) {
if (!filelist_pending(sfile->files)) {
filelist_readjob_start(sfile->files, C);
}
}
else {
/* stop any running thumbnail jobs if we're not displaying them - speedup for NFS */
thumbnails_stop(wm, sfile->files);
}
filelist_sort(sfile->files);
filelist_filter(sfile->files);
if (params->display == FILE_IMGDISPLAY) {
filelist_cache_previews_set(sfile->files, true);
}
else {
filelist_cache_previews_set(sfile->files, false);
if (sfile->previews_timer) {
WM_event_remove_timer_notifier(wm, CTX_wm_window(C), sfile->previews_timer);
sfile->previews_timer = NULL;
}
}
if (params->renamefile[0] != '\0') {
int idx = filelist_find(sfile->files, params->renamefile);
int idx = filelist_file_findpath(sfile->files, params->renamefile);
if (idx >= 0) {
struct direntry *file = filelist_file(sfile->files, idx);
FileDirEntry *file = filelist_file(sfile->files, idx);
if (file) {
file->selflag |= FILE_SEL_EDITING;
filelist_entry_select_set(sfile->files, file, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL);
}
}
BLI_strncpy(sfile->params->renameedit, sfile->params->renamefile, sizeof(sfile->params->renameedit));
params->renamefile[0] = '\0';
/* File listing is now async, do not clear renamefile if matching entry not found
* and dirlist is not finished! */
if (idx >= 0 || filelist_is_ready(sfile->files)) {
params->renamefile[0] = '\0';
}
}
if (sfile->layout) {
@@ -278,7 +293,7 @@ static void file_refresh(const bContext *C, ScrArea *sa)
static void file_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn)
{
/* SpaceFile *sfile = (SpaceFile *)sa->spacedata.first; */
SpaceFile *sfile = (SpaceFile *)sa->spacedata.first;
/* context changes */
switch (wmn->category) {
@@ -292,6 +307,12 @@ static void file_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn)
ED_area_tag_refresh(sa);
ED_area_tag_redraw(sa);
break;
case ND_SPACE_FILE_PREVIEW:
if (filelist_cache_previews_update(sfile->files)) {
ED_area_tag_refresh(sa);
ED_area_tag_redraw(sa);
}
break;
}
break;
}

View File

@@ -590,6 +590,7 @@ typedef struct FileSelectParams {
char filter_glob[64]; /* list of filetypes to filter */
char filter_search[64]; /* text items' name must match to be shown. */
int filter_id; /* same as filter, but for ID types (aka library groups). */
int active_file; /* active file used for keyboard navigation */
int highlight_file; /* file under cursor */
@@ -603,7 +604,9 @@ typedef struct FileSelectParams {
short flag; /* settings for filter, hiding dots files,... */
short sort; /* sort order */
short display; /* display mode flag */
short filter; /* filter when (flags & FILE_FILTER) is true */
int filter; /* filter when (flags & FILE_FILTER) is true */
short recursion_level; /* max number of levels in dirtree to show at once, 0 to disable recursion. */
/* XXX --- still unused -- */
short f_fp; /* show font preview */
@@ -635,6 +638,7 @@ typedef struct SpaceFile {
struct wmOperator *op;
struct wmTimer *smoothscroll_timer;
struct wmTimer *previews_timer;
struct FileLayout *layout;
@@ -709,7 +713,10 @@ typedef enum eFileSel_Params_Flag {
} eFileSel_Params_Flag;
/* files in filesel list: file types */
/* files in filesel list: file types
* Note we could use mere values (instead of bitflags) for file types themselves,
* but since we do not lack of bytes currently...
*/
typedef enum eFileSel_File_Types {
FILE_TYPE_BLENDER = (1 << 2),
FILE_TYPE_BLENDER_BACKUP = (1 << 3),
@@ -725,6 +732,9 @@ typedef enum eFileSel_File_Types {
FILE_TYPE_COLLADA = (1 << 13),
FILE_TYPE_OPERATOR = (1 << 14), /* from filter_glob operator property */
FILE_TYPE_APPLICATIONBUNDLE = (1 << 15),
FILE_TYPE_DIR = (1 << 30), /* An FS directory (i.e. S_ISDIR on its path is true). */
FILE_TYPE_BLENDERLIB = (1 << 31),
} eFileSel_File_Types;
/* Selection Flags in filesel: struct direntry, unsigned char selflag */
@@ -735,6 +745,117 @@ typedef enum eDirEntry_SelectFlag {
FILE_SEL_EDITING = (1 << 4),
} eDirEntry_SelectFlag;
#define FILE_LIST_MAX_RECURSION 4
/* ***** Related to file browser, but never saved in DNA, only here to help with RNA. ***** */
/* About Unique identifier.
* Stored in a CustomProps once imported.
* Each engine is free to use it as it likes - it will be the only thing passed to it by blender to identify
* asset/variant/version (concatenating the three into a single 48 bytes one).
* Assumed to be 128bits, handled as four integers due to lack of real bytes proptype in RNA :|.
*/
#define ASSET_UUID_LENGTH 16
/* Used to communicate with asset engines outside of 'import' context. */
typedef struct AssetUUID {
int uuid_asset[4];
int uuid_variant[4];
int uuid_revision[4];
} AssetUUID;
typedef struct AssetUUIDList {
AssetUUID *uuids;
int nbr_uuids, pad;
} AssetUUIDList;
/* Container for a revision, only relevant in asset context. */
typedef struct FileDirEntryRevision {
struct FileDirEntryRevision *next, *prev;
int uuid[4];
char *comment;
uint64_t size;
int64_t time;
/* Temp caching of UI-generated strings... */
char size_str[16];
char time_str[8];
char date_str[16];
} FileDirEntryRevision;
/* Container for a variant, only relevant in asset context.
* In case there are no variants, a single one shall exist, with NULL name/description. */
typedef struct FileDirEntryVariant {
struct FileDirEntryVariant *next, *prev;
int uuid[4];
char *name;
char *description;
ListBase revisions;
int nbr_revisions;
int act_revision;
} FileDirEntryVariant;
/* Container for mere direntry, with additional asset-related data. */
typedef struct FileDirEntry {
struct FileDirEntry *next, *prev;
int uuid[4];
char *name;
char *description;
/* Either point to active variant/revision if available, or own entry (in mere filebrowser case). */
FileDirEntryRevision *entry;
int typeflag; /* eFileSel_File_Types */
int blentype; /* ID type, in case typeflag has FILE_TYPE_BLENDERLIB set. */
char *relpath;
void *poin; /* TODO: make this a real ID pointer? */
struct ImBuf *image;
/* Tags are for info only, most of filtering is done in asset engine. */
char **tags;
int nbr_tags;
short status;
short flags;
ListBase variants;
int nbr_variants;
int act_variant;
} FileDirEntry;
/* Array of direntries. */
/* This struct is used in various, different contexts.
* In Filebrowser UI, it stores the total number of available entries, the number of visible (filtered) entries,
* and a subset of those in 'entries' ListBase, from idx_start (included) to idx_end (excluded).
* In AssetEngine context (i.e. outside of 'browsing' context), entries contain all needed data, there is no filtering,
* so nbr_entries_filtered, entry_idx_start and entry_idx_end should all be set to -1.
*/
typedef struct FileDirEntryArr {
ListBase entries;
int nbr_entries;
int nbr_entries_filtered;
int entry_idx_start, entry_idx_end;
char root[1024]; /* FILE_MAX */
} FileDirEntryArr;
/* FileDirEntry.status */
enum {
ASSET_STATUS_LOCAL = 1 << 0, /* If active uuid is available localy/immediately. */
ASSET_STATUS_LATEST = 1 << 1, /* If active uuid is latest available version. */
};
/* FileDirEntry.flags */
enum {
FILE_ENTRY_INVALID_PREVIEW = 1 << 0, /* The preview for this entry could not be generated. */
};
/* Image/UV Editor ======================================== */
/* Image/UV Editor */

View File

@@ -219,6 +219,16 @@ static EnumPropertyItem buttons_texture_context_items[] = {
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem fileselectparams_recursion_level_items[] = {
{0, "NONE", 0, "None", "Only list current directory's content, with no recursion"},
{1, "BLEND", 0, "Blend File", "List .blend files' content"},
{2, "ALL_1", 0, "One Level", "List all sub-directories' content, one level of recursion"},
{3, "ALL_2", 0, "Two Levels", "List all sub-directories' content, two levels of recursion"},
{4, "ALL_3", 0, "Three Levels", "List all sub-directories' content, three levels of recursion"},
{0, NULL, 0, NULL, NULL}
};
#ifdef RNA_RUNTIME
#include "DNA_anim_types.h"
@@ -1522,6 +1532,37 @@ static void rna_SpaceClipEditor_view_type_update(Main *UNUSED(bmain), Scene *UNU
/* File browser. */
static int rna_FileSelectParams_use_lib_get(PointerRNA *ptr)
{
FileSelectParams *params = ptr->data;
return params && (params->type == FILE_LOADLIB);
}
static EnumPropertyItem *rna_FileSelectParams_recursion_level_itemf(
bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
FileSelectParams *params = ptr->data;
if (params && params->type != FILE_LOADLIB) {
EnumPropertyItem *item = NULL;
int totitem = 0;
RNA_enum_items_add_value(&item, &totitem, fileselectparams_recursion_level_items, 0);
RNA_enum_items_add_value(&item, &totitem, fileselectparams_recursion_level_items, 2);
RNA_enum_items_add_value(&item, &totitem, fileselectparams_recursion_level_items, 3);
RNA_enum_items_add_value(&item, &totitem, fileselectparams_recursion_level_items, 4);
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
*r_free = false;
return fileselectparams_recursion_level_items;
}
static void rna_FileBrowser_FSMenuEntry_path_get(PointerRNA *ptr, char *value)
{
char *path = ED_fsmenu_entry_get_path(ptr->data);
@@ -3710,6 +3751,58 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem file_filter_idtypes_items[] = {
{FILTER_ID_AC, "ACTION", ICON_ANIM_DATA, "Actions", "Show/hide Action datablocks"},
{FILTER_ID_AR, "ARMATURE", ICON_ARMATURE_DATA, "Armatures", "Show/hide Armature datablocks"},
{FILTER_ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brushes", "Show/hide Brushes datablocks"},
{FILTER_ID_CA, "CAMERA", ICON_CAMERA_DATA, "Cameras", "Show/hide Camera datablocks"},
{FILTER_ID_CU, "CURVE", ICON_CURVE_DATA, "Curves", "Show/hide Curve datablocks"},
{FILTER_ID_GD, "GREASE_PENCIL", ICON_GREASEPENCIL, "Grease Pencil", "Show/hide Grease pencil datablocks"},
{FILTER_ID_GR, "GROUP", ICON_GROUP, "Groups", "Show/hide Group datablocks"},
{FILTER_ID_IM, "IMAGE", ICON_IMAGE_DATA, "Images", "Show/hide Image datablocks"},
{FILTER_ID_LA, "LAMP", ICON_LAMP_DATA, "Lamps", "Show/hide Lamp datablocks"},
{FILTER_ID_LS, "LINESTYLE", ICON_LINE_DATA, "Freestyle Linestyles", "Show/hide Freestyle's Line Style datablocks"},
{FILTER_ID_LT, "LATTICE", ICON_LATTICE_DATA, "Lattices", "Show/hide Lattice datablocks"},
{FILTER_ID_MA, "MATERIAL", ICON_MATERIAL_DATA, "Materials", "Show/hide Material datablocks"},
{FILTER_ID_MB, "METABALL", ICON_META_DATA, "Metaballs", "Show/hide Mateball datablocks"},
{FILTER_ID_MC, "MOVIE_CLIP", ICON_CLIP, "Movie Clips", "Show/hide Movie Clip datablocks"},
{FILTER_ID_ME, "MESH", ICON_MESH_DATA, "Meshes", "Show/hide Mesh datablocks"},
{FILTER_ID_MSK, "MASK", ICON_MOD_MASK, "Masks", "Show/hide Mask datablocks"},
{FILTER_ID_NT, "NODE_TREE", ICON_NODETREE, "Node Trees", "Show/hide Node Tree datablocks"},
{FILTER_ID_OB, "OBJECT", ICON_OBJECT_DATA, "Objects", "Show/hide Object datablocks"},
{FILTER_ID_PAL, "PALETTE", ICON_COLOR, "Palettes", "Show/hide Palette datablocks"},
{FILTER_ID_PC, "PAINT_CURVE", ICON_CURVE_BEZCURVE, "Paint Curves", "Show/hide Paint Curve datablocks"},
{FILTER_ID_SCE, "SCENE", ICON_SCENE_DATA, "Scenes", "Show/hide Scene datablocks"},
{FILTER_ID_SPK, "SPEAKER", ICON_SPEAKER, "Speakers", "Show/hide Speaker datablocks"},
{FILTER_ID_SO, "SOUND", ICON_SOUND, "Sounds", "Show/hide Sound datablocks"},
{FILTER_ID_TE, "TEXTURE", ICON_TEXTURE_DATA, "Textures", "Show/hide Texture datablocks"},
{FILTER_ID_TXT, "TEXT", ICON_TEXT, "Texts", "Show/hide Text datablocks"},
{FILTER_ID_VF, "FONT", ICON_FONT_DATA, "Fonts", "Show/hide Font datablocks"},
{FILTER_ID_WO, "WORLD", ICON_WORLD_DATA, "Worlds", "Show/hide World datablocks"},
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem file_filter_idcategories_items[] = {
{FILTER_ID_SCE,
"SCENE", ICON_SCENE_DATA, "Scenes", "Show/hide scenes"},
{FILTER_ID_AC,
"ANIMATION", ICON_ANIM_DATA, "Animations", "Show/hide animation data"},
{FILTER_ID_OB | FILTER_ID_GR,
"OBJECT", ICON_GROUP, "Objects & Groups", "Show/hide objects and groups"},
{FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME,
"GEOMETRY", ICON_MESH_DATA, "Geometry", "Show/hide meshes, curves, lattice, armatures and metaballs data"},
{FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE,
"SHADING", ICON_MATERIAL_DATA, "Shading",
"Show/hide materials, nodetrees, textures and Freestyle's linestyles"},
{FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO,
"IMAGE", ICON_IMAGE_DATA, "Images & Sounds", "Show/hide images, movie clips, sounds and masks"},
{FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_SPK | FILTER_ID_WO,
"ENVIRONMENT", ICON_WORLD_DATA, "Environment", "Show/hide worlds, lamps, cameras and speakers"},
{FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF,
"MISC", ICON_GREASEPENCIL, "Miscellaneous", "Show/hide other data types"},
{0, NULL, 0, NULL, NULL}
};
srna = RNA_def_struct(brna, "FileSelectParams", NULL);
RNA_def_struct_ui_text(srna, "File Select Parameters", "File Select Parameters");
@@ -3728,12 +3821,23 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "File Name", "Active file in the file browser");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "use_library_browsing", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_ui_text(prop, "Library Browser", "Whether we may browse blender files' content or not");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_funcs(prop, "rna_FileSelectParams_use_lib_get", NULL);
prop = RNA_def_property(srna, "display_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "display");
RNA_def_property_enum_items(prop, file_display_items);
RNA_def_property_ui_text(prop, "Display Mode", "Display mode for the file list");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "recursion_level", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, fileselectparams_recursion_level_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_FileSelectParams_recursion_level_itemf");
RNA_def_property_ui_text(prop, "Recursion", "Numbers of dirtree levels to show simultaneously");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "use_filter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FILE_FILTER);
RNA_def_property_ui_text(prop, "Filter Files", "Enable filtering of files");
@@ -3803,7 +3907,27 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Filter Folder", "Show folders");
RNA_def_property_ui_icon(prop, ICON_FILE_FOLDER, 0);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "use_filter_blendid", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "filter", FILE_TYPE_BLENDERLIB);
RNA_def_property_ui_text(prop, "Filter Blender IDs", "Show .blend files items (objects, materials, etc.)");
RNA_def_property_ui_icon(prop, ICON_BLENDER, 0);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "filter_id", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "filter_id");
RNA_def_property_enum_items(prop, file_filter_idtypes_items);
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
RNA_def_property_ui_text(prop, "Filter ID types", "Which ID types to show/hide, when browsing a library");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "filter_id_category", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "filter_id");
RNA_def_property_enum_items(prop, file_filter_idcategories_items);
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
RNA_def_property_ui_text(prop, "Filter ID categories", "Which ID categories to show/hide, when browsing a library");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "filter_glob", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "filter_glob");
RNA_def_property_ui_text(prop, "Extension Filter", "");

View File

@@ -417,7 +417,7 @@ enum {
WM_JOB_TYPE_OBJECT_SIM_FLUID,
WM_JOB_TYPE_OBJECT_BAKE_TEXTURE,
WM_JOB_TYPE_OBJECT_BAKE,
WM_JOB_TYPE_FILESEL_THUMBNAIL,
WM_JOB_TYPE_FILESEL_READDIR,
WM_JOB_TYPE_CLIP_BUILD_PROXY,
WM_JOB_TYPE_CLIP_TRACK_MARKERS,
WM_JOB_TYPE_CLIP_SOLVE_CAMERA,

View File

@@ -361,6 +361,7 @@ typedef struct wmNotifier {
#define ND_SPACE_NODE_VIEW (17<<16)
#define ND_SPACE_CHANGED (18<<16) /*sent to a new editor type after it's replaced an old one*/
#define ND_SPACE_CLIP (19<<16)
#define ND_SPACE_FILE_PREVIEW (20<<16)
/* subtype, 256 entries too */
#define NOTE_SUBTYPE 0x0000FF00

View File

@@ -1262,6 +1262,8 @@ void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type,
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_boolean(ot->srna, "filter_folder", (filter & FILE_TYPE_FOLDER) != 0, "Filter folders", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_boolean(ot->srna, "filter_blenlib", (filter & FILE_TYPE_BLENDERLIB) != 0, "Filter Blender IDs", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_int(ot->srna, "filemode", type, FILE_LOADLIB, FILE_SPECIAL,
"File Browser Mode", "The setting for the file browser mode to load a .blend file, a library or a special file",
@@ -2594,86 +2596,34 @@ static short wm_link_append_flag(wmOperator *op)
return flag;
}
static int wm_link_append_exec(bContext *C, wmOperator *op)
/* Helper.
* if `name` is non-NULL, we assume a single-item link/append.
* else if `*todo_libraries` is NULL we assume first-run.
*/
static void wm_link_append_do_libgroup(
bContext *C, wmOperator *op, const char *root, const char *libname, char *group, char *name,
const short flag, GSet **todo_libraries)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Main *mainl = NULL;
Main *mainl;
BlendHandle *bh;
Library *lib;
PropertyRNA *prop;
char name[FILE_MAX], dir[FILE_MAX], libname[FILE_MAX];
char *group;
int idcode, totfiles = 0;
short flag;
RNA_string_get(op->ptr, "filename", name);
RNA_string_get(op->ptr, "directory", dir);
char path[FILE_MAX_LIBEXTRA], relname[FILE_MAX];
int idcode;
const bool is_first_run = (*todo_libraries == NULL);
/* test if we have a valid data */
if (BLO_library_path_explode(dir, libname, &group, NULL) == 0) {
BKE_report(op->reports, RPT_ERROR, "Not a library");
return OPERATOR_CANCELLED;
}
else if ((group == NULL) || (group[0] == '\0')) {
BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
return OPERATOR_CANCELLED;
}
else if (BLI_path_cmp(bmain->name, libname) == 0) {
BKE_report(op->reports, RPT_ERROR, "Cannot use current file as library");
return OPERATOR_CANCELLED;
}
/* check if something is indicated for append/link */
prop = RNA_struct_find_property(op->ptr, "files");
if (prop) {
totfiles = RNA_property_collection_length(op->ptr, prop);
if (totfiles == 0) {
if (name[0] == '\0') {
BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
return OPERATOR_CANCELLED;
}
}
}
else if (name[0] == '\0') {
BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
return OPERATOR_CANCELLED;
}
BLI_assert(group);
idcode = BKE_idcode_from_name(group);
bh = BLO_blendhandle_from_file(libname, op->reports);
if (bh == NULL) {
/* unlikely since we just browsed it, but possible
* error reports will have been made by BLO_blendhandle_from_file() */
return OPERATOR_CANCELLED;
return;
}
/* from here down, no error returns */
idcode = BKE_idcode_from_name(group);
/* now we have or selected, or an indicated file */
if (RNA_boolean_get(op->ptr, "autoselect"))
BKE_scene_base_deselect_all(scene);
flag = wm_link_append_flag(op);
/* sanity checks for flag */
if (scene->id.lib && (flag & FILE_GROUP_INSTANCE)) {
/* TODO, user never gets this message */
BKE_reportf(op->reports, RPT_WARNING, "Scene '%s' is linked, group instance disabled", scene->id.name + 2);
flag &= ~FILE_GROUP_INSTANCE;
}
/* tag everything, all untagged data can be made local
* its also generally useful to know what is new
*
* take extra care BKE_main_id_flag_all(LIB_LINK_TAG, false) is called after! */
BKE_main_id_flag_all(bmain, LIB_PRE_EXISTING, 1);
/* here appending/linking starts */
mainl = BLO_library_append_begin(bmain, &bh, libname);
lib = mainl->curlib;
@@ -2686,19 +2636,47 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
mainl->versionfile, mainl->subversionfile);
}
if (totfiles == 0) {
if (name) {
BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
}
else {
if (is_first_run) {
*todo_libraries = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__);
}
RNA_BEGIN (op->ptr, itemptr, "files")
{
RNA_string_get(&itemptr, "name", name);
BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
char curr_libname[FILE_MAX];
int curr_idcode;
RNA_string_get(&itemptr, "name", relname);
BLI_join_dirfile(path, sizeof(path), root, relname);
if (BLO_library_path_explode(path, curr_libname, &group, &name)) {
if (!group || !name) {
continue;
}
curr_idcode = BKE_idcode_from_name(group);
if ((idcode == curr_idcode) && (BLI_path_cmp(curr_libname, libname) == 0)) {
BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
}
else if (is_first_run) {
BLI_join_dirfile(path, sizeof(path), curr_libname, group);
if (!BLI_gset_haskey(*todo_libraries, path)) {
BLI_gset_insert(*todo_libraries, BLI_strdup(path));
}
}
}
}
RNA_END;
}
BLO_library_append_end(C, mainl, &bh, idcode, flag);
BLO_blendhandle_close(bh);
/* mark all library linked objects to be updated */
BKE_main_lib_objects_recalc_all(bmain);
IMB_colormanagement_check_file_config(bmain);
@@ -2708,6 +2686,95 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
BLI_assert(BLI_findindex(&bmain->library, lib) != -1);
BKE_library_make_local(bmain, lib, true);
}
}
static int wm_link_append_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
PropertyRNA *prop;
char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX];
char *group, *name;
int totfiles = 0;
short flag;
GSet *todo_libraries = NULL;
RNA_string_get(op->ptr, "filename", relname);
RNA_string_get(op->ptr, "directory", root);
BLI_join_dirfile(path, sizeof(path), root, relname);
/* test if we have a valid data */
if (!BLO_library_path_explode(path, libname, &group, &name)) {
BKE_report(op->reports, RPT_ERROR, "Not a library");
return OPERATOR_CANCELLED;
}
else if (!group) {
BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
return OPERATOR_CANCELLED;
}
else if (BLI_path_cmp(bmain->name, libname) == 0) {
BKE_report(op->reports, RPT_ERROR, "Cannot use current file as library");
return OPERATOR_CANCELLED;
}
/* check if something is indicated for append/link */
prop = RNA_struct_find_property(op->ptr, "files");
if (prop) {
totfiles = RNA_property_collection_length(op->ptr, prop);
if (totfiles == 0) {
if (!name) {
BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
return OPERATOR_CANCELLED;
}
}
}
else if (!name) {
BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
return OPERATOR_CANCELLED;
}
flag = wm_link_append_flag(op);
/* sanity checks for flag */
if (scene->id.lib && (flag & FILE_GROUP_INSTANCE)) {
/* TODO, user never gets this message */
BKE_reportf(op->reports, RPT_WARNING, "Scene '%s' is linked, group instance disabled", scene->id.name + 2);
flag &= ~FILE_GROUP_INSTANCE;
}
/* from here down, no error returns */
/* now we have or selected, or an indicated file */
if (RNA_boolean_get(op->ptr, "autoselect"))
BKE_scene_base_deselect_all(scene);
/* tag everything, all untagged data can be made local
* its also generally useful to know what is new
*
* take extra care BKE_main_id_flag_all(LIB_LINK_TAG, false) is called after! */
BKE_main_id_flag_all(bmain, LIB_PRE_EXISTING, 1);
if (totfiles != 0) {
name = NULL;
}
wm_link_append_do_libgroup(C, op, root, libname, group, name, flag, &todo_libraries);
if (todo_libraries) {
GSetIterator libs_it;
GSET_ITER(libs_it, todo_libraries) {
char *libpath = (char *)BLI_gsetIterator_getKey(&libs_it);
BLO_library_path_explode(libpath, libname, &group, NULL);
wm_link_append_do_libgroup(C, op, root, libname, group, NULL, flag, &todo_libraries);
}
BLI_gset_free(todo_libraries, MEM_freeN);
}
/* important we unset, otherwise these object wont
* link into other scenes from this blend file */
@@ -2718,10 +2785,9 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
/* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */
GPU_materials_free();
BLO_blendhandle_close(bh);
/* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */
BLI_strncpy(G.lib, dir, FILE_MAX);
BLI_strncpy(G.lib, root, FILE_MAX);
WM_event_add_notifier(C, NC_WINDOW, NULL);
@@ -2761,7 +2827,7 @@ static void WM_OT_link(wmOperatorType *ot)
ot->flag |= OPTYPE_UNDO;
WM_operator_properties_filesel(
ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_LOADLIB, FILE_OPENFILE,
ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE,
WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES,
FILE_DEFAULTDISPLAY);
@@ -2781,7 +2847,7 @@ static void WM_OT_append(wmOperatorType *ot)
ot->flag |= OPTYPE_UNDO;
WM_operator_properties_filesel(
ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_LOADLIB, FILE_OPENFILE,
ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE,
WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES,
FILE_DEFAULTDISPLAY);