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:
@@ -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__)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -31,6 +31,7 @@ set(INC
|
||||
../../makesrna
|
||||
../../render/extern/include
|
||||
../../windowmanager
|
||||
../../../../intern/atomic
|
||||
../../../../intern/guardedalloc
|
||||
../../../../intern/glew-mx
|
||||
)
|
||||
|
||||
@@ -29,6 +29,7 @@ Import ('env')
|
||||
|
||||
sources = env.Glob('*.c')
|
||||
incs = [
|
||||
'#/intern/atomic',
|
||||
'#/intern/guardedalloc',
|
||||
env['BF_GLEW_INC'],
|
||||
'#/intern/glew-mx',
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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", "");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user