This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/space_file/filelist.c
Campbell Barton 4ca67869cc Code cleanup: remove unused includes
Opted to keep includes if they are used indirectly (even if removing is possible).
2014-05-01 04:47:51 +10:00

1413 lines
36 KiB
C

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2007 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/editors/space_file/filelist.c
* \ingroup spfile
*/
/* global includes */
#include <stdlib.h>
#include <math.h>
#include <string.h>
#ifndef WIN32
#include <unistd.h>
#else
#include <io.h>
#include <direct.h>
#endif
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_linklist.h"
#include "BLI_utildefines.h"
#include "BLI_fileops_types.h"
#ifdef WIN32
# include "BLI_winstuff.h"
#endif
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_library.h"
#include "BKE_icons.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BLO_readfile.h"
#include "BKE_idcode.h"
#include "DNA_space_types.h"
#include "ED_fileselect.h"
#include "ED_datafiles.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_thumbs.h"
#include "PIL_time.h"
#include "WM_api.h"
#include "WM_types.h"
#include "UI_resources.h"
#include "filelist.h"
struct FileList;
typedef struct FileImage {
struct FileImage *next, *prev;
char path[FILE_MAX];
unsigned int flags;
int index;
short done;
ImBuf *img;
} FileImage;
typedef struct ThumbnailJob {
ListBase loadimages;
const short *stop;
const short *do_update;
struct FileList *filelist;
ReportList reports;
} ThumbnailJob;
typedef struct FileList {
struct direntry *filelist;
int *fidx;
int numfiles;
int numfiltered;
char dir[FILE_MAX];
short prv_w;
short prv_h;
short hide_dot;
unsigned int filter;
char filter_glob[64];
short changed;
struct BlendHandle *libfiledata;
short hide_parent;
void (*readf)(struct FileList *);
bool (*filterf)(struct direntry *file, const char *dir, unsigned int filter, short hide_dot);
} FileList;
typedef struct FolderList {
struct FolderList *next, *prev;
char *foldername;
} FolderList;
#define SPECIAL_IMG_SIZE 48
#define SPECIAL_IMG_ROWS 4
#define SPECIAL_IMG_COLS 4
#define SPECIAL_IMG_FOLDER 0
#define SPECIAL_IMG_PARENT 1
#define SPECIAL_IMG_REFRESH 2
#define SPECIAL_IMG_BLENDFILE 3
#define SPECIAL_IMG_SOUNDFILE 4
#define SPECIAL_IMG_MOVIEFILE 5
#define SPECIAL_IMG_PYTHONFILE 6
#define SPECIAL_IMG_TEXTFILE 7
#define SPECIAL_IMG_FONTFILE 8
#define SPECIAL_IMG_UNKNOWNFILE 9
#define SPECIAL_IMG_LOADING 10
#define SPECIAL_IMG_BACKUP 11
#define SPECIAL_IMG_MAX SPECIAL_IMG_BACKUP + 1
static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
/* ******************* SORT ******************* */
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 & (BLENDERFILE | BLENDERFILE_BACKUP));
return false;
}
static int compare_name(const void *a1, const void *a2)
{
const struct direntry *entry1 = a1, *entry2 = a2;
/* type is equal to stat.st_mode */
if (compare_is_directory(entry1)) {
if (compare_is_directory(entry2) == 0) return (-1);
}
else {
if (compare_is_directory(entry2)) return (1);
}
if (S_ISREG(entry1->type)) {
if (S_ISREG(entry2->type) == 0) 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 (strcmp(entry1->relname, ".") == 0) return (-1);
if (strcmp(entry2->relname, ".") == 0) return (1);
if (strcmp(entry1->relname, "..") == 0) return (-1);
if (strcmp(entry2->relname, "..") == 0) return (1);
return (BLI_natstrcmp(entry1->relname, entry2->relname));
}
static int compare_date(const void *a1, const void *a2)
{
const struct direntry *entry1 = a1, *entry2 = a2;
/* type is equal to stat.st_mode */
if (compare_is_directory(entry1)) {
if (compare_is_directory(entry2) == 0) return (-1);
}
else {
if (compare_is_directory(entry2)) return (1);
}
if (S_ISREG(entry1->type)) {
if (S_ISREG(entry2->type) == 0) 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 (strcmp(entry1->relname, ".") == 0) return (-1);
if (strcmp(entry2->relname, ".") == 0) return (1);
if (strcmp(entry1->relname, "..") == 0) return (-1);
if (strcmp(entry2->relname, "..") == 0) return (1);
if (entry1->s.st_mtime < entry2->s.st_mtime) return 1;
if (entry1->s.st_mtime > entry2->s.st_mtime) return -1;
else return BLI_natstrcmp(entry1->relname, entry2->relname);
}
static int compare_size(const void *a1, const void *a2)
{
const struct direntry *entry1 = a1, *entry2 = a2;
/* type is equal to stat.st_mode */
if (compare_is_directory(entry1)) {
if (compare_is_directory(entry2) == 0) return (-1);
}
else {
if (compare_is_directory(entry2)) return (1);
}
if (S_ISREG(entry1->type)) {
if (S_ISREG(entry2->type) == 0) 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 (strcmp(entry1->relname, ".") == 0) return (-1);
if (strcmp(entry2->relname, ".") == 0) return (1);
if (strcmp(entry1->relname, "..") == 0) return (-1);
if (strcmp(entry2->relname, "..") == 0) return (1);
if (entry1->s.st_size < entry2->s.st_size) return 1;
if (entry1->s.st_size > entry2->s.st_size) return -1;
else return BLI_natstrcmp(entry1->relname, entry2->relname);
}
static int compare_extension(const void *a1, const void *a2)
{
const struct direntry *entry1 = a1, *entry2 = a2;
const char *sufix1, *sufix2;
const char *nil = "";
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;
/* type is equal to stat.st_mode */
if (compare_is_directory(entry1)) {
if (compare_is_directory(entry2) == 0) return (-1);
}
else {
if (compare_is_directory(entry2)) return (1);
}
if (S_ISREG(entry1->type)) {
if (S_ISREG(entry2->type) == 0) 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 (strcmp(entry1->relname, ".") == 0) return (-1);
if (strcmp(entry2->relname, ".") == 0) return (1);
if (strcmp(entry1->relname, "..") == 0) return (-1);
if (strcmp(entry2->relname, "..") == 0) return (1);
return (BLI_strcasecmp(sufix1, sufix2));
}
static bool is_hidden_file(const char *filename, short hide_dot)
{
bool is_hidden = false;
if (hide_dot) {
if (filename[0] == '.' && filename[1] != '.' && filename[1] != 0) {
is_hidden = 1; /* ignore .file */
}
else if (((filename[0] == '.') && (filename[1] == 0))) {
is_hidden = 1; /* ignore . */
}
else {
int len = strlen(filename);
if ((len > 0) && (filename[len - 1] == '~')) {
is_hidden = 1; /* ignore file~ */
}
}
}
else {
if (((filename[0] == '.') && (filename[1] == 0))) {
is_hidden = 1; /* ignore . */
}
}
return is_hidden;
}
static bool is_filtered_file(struct direntry *file, const char *UNUSED(dir), unsigned int filter, short hide_dot)
{
bool is_filtered = false;
if (filter) {
if (file->flags & filter) {
is_filtered = 1;
}
else if (file->type & S_IFDIR) {
if (filter & FOLDERFILE) {
is_filtered = 1;
}
}
}
else {
is_filtered = 1;
}
return is_filtered && !is_hidden_file(file->relname, hide_dot);
}
static bool is_filtered_lib(struct direntry *file, const char *dir, unsigned int filter, short hide_dot)
{
bool is_filtered = false;
char tdir[FILE_MAX], tgroup[BLO_GROUP_MAX];
if (BLO_is_a_library(dir, tdir, tgroup)) {
is_filtered = !is_hidden_file(file->relname, hide_dot);
}
else {
is_filtered = is_filtered_file(file, dir, filter, hide_dot);
}
return is_filtered;
}
static bool is_filtered_main(struct direntry *file, const char *UNUSED(dir), unsigned int UNUSED(filter), short hide_dot)
{
return !is_hidden_file(file->relname, hide_dot);
}
void filelist_filter(FileList *filelist)
{
int num_filtered = 0;
int i, j;
if (!filelist->filelist)
return;
/* How many files are left after filter ? */
for (i = 0; i < filelist->numfiles; ++i) {
struct direntry *file = &filelist->filelist[i];
if (filelist->filterf(file, filelist->dir, filelist->filter, filelist->hide_dot)) {
num_filtered++;
}
}
if (filelist->fidx) {
MEM_freeN(filelist->fidx);
filelist->fidx = NULL;
}
filelist->fidx = (int *)MEM_callocN(num_filtered * sizeof(int), "filteridx");
filelist->numfiltered = num_filtered;
for (i = 0, j = 0; i < filelist->numfiles; ++i) {
struct direntry *file = &filelist->filelist[i];
if (filelist->filterf(file, filelist->dir, filelist->filter, filelist->hide_dot)) {
filelist->fidx[j++] = i;
}
}
}
void filelist_init_icons(void)
{
short x, y, k;
ImBuf *bbuf;
ImBuf *ibuf;
BLI_assert(G.background == false);
#ifdef WITH_HEADLESS
bbuf = NULL;
#else
bbuf = IMB_ibImageFromMemory((unsigned char *)datatoc_prvicons_png, datatoc_prvicons_png_size, IB_rect, NULL, "<splash>");
#endif
if (bbuf) {
for (y = 0; y < SPECIAL_IMG_ROWS; y++) {
for (x = 0; x < SPECIAL_IMG_COLS; x++) {
int tile = SPECIAL_IMG_COLS * y + x;
if (tile < SPECIAL_IMG_MAX) {
ibuf = IMB_allocImBuf(SPECIAL_IMG_SIZE, SPECIAL_IMG_SIZE, 32, IB_rect);
for (k = 0; k < SPECIAL_IMG_SIZE; k++) {
memcpy(&ibuf->rect[k * SPECIAL_IMG_SIZE], &bbuf->rect[(k + y * SPECIAL_IMG_SIZE) * SPECIAL_IMG_SIZE * SPECIAL_IMG_COLS + x * SPECIAL_IMG_SIZE], SPECIAL_IMG_SIZE * sizeof(int));
}
gSpecialFileImages[tile] = ibuf;
}
}
}
IMB_freeImBuf(bbuf);
}
}
void filelist_free_icons(void)
{
int i;
BLI_assert(G.background == false);
for (i = 0; i < SPECIAL_IMG_MAX; ++i) {
IMB_freeImBuf(gSpecialFileImages[i]);
gSpecialFileImages[i] = NULL;
}
}
/* -----------------FOLDERLIST (previous/next) -------------- */
ListBase *folderlist_new(void)
{
ListBase *p = MEM_callocN(sizeof(ListBase), "folderlist");
return p;
}
void folderlist_popdir(struct ListBase *folderlist, char *dir)
{
const char *prev_dir;
struct FolderList *folder;
folder = folderlist->last;
if (folder) {
/* remove the current directory */
MEM_freeN(folder->foldername);
BLI_freelinkN(folderlist, folder);
folder = folderlist->last;
if (folder) {
prev_dir = folder->foldername;
BLI_strncpy(dir, prev_dir, FILE_MAXDIR);
}
}
/* delete the folder next or use setdir directly before PREVIOUS OP */
}
void folderlist_pushdir(ListBase *folderlist, const char *dir)
{
struct FolderList *folder, *previous_folder;
previous_folder = folderlist->last;
/* check if already exists */
if (previous_folder && previous_folder->foldername) {
if (BLI_path_cmp(previous_folder->foldername, dir) == 0) {
return;
}
}
/* create next folder element */
folder = (FolderList *)MEM_mallocN(sizeof(FolderList), "FolderList");
folder->foldername = BLI_strdup(dir);
/* add it to the end of the list */
BLI_addtail(folderlist, folder);
}
const char *folderlist_peeklastdir(ListBase *folderlist)
{
struct FolderList *folder;
if (!folderlist->last)
return NULL;
folder = folderlist->last;
return folder->foldername;
}
int folderlist_clear_next(struct SpaceFile *sfile)
{
struct FolderList *folder;
/* if there is no folder_next there is nothing we can clear */
if (!sfile->folders_next)
return 0;
/* if previous_folder, next_folder or refresh_folder operators are executed it doesn't clear folder_next */
folder = sfile->folders_prev->last;
if ((!folder) || (BLI_path_cmp(folder->foldername, sfile->params->dir) == 0))
return 0;
/* eventually clear flist->folders_next */
return 1;
}
/* not listbase itself */
void folderlist_free(ListBase *folderlist)
{
if (folderlist) {
FolderList *folder;
for (folder = folderlist->first; folder; folder = folder->next)
MEM_freeN(folder->foldername);
BLI_freelistN(folderlist);
}
}
ListBase *folderlist_duplicate(ListBase *folderlist)
{
if (folderlist) {
ListBase *folderlistn = MEM_callocN(sizeof(ListBase), "copy folderlist");
FolderList *folder;
BLI_duplicatelist(folderlistn, folderlist);
for (folder = folderlistn->first; folder; folder = folder->next) {
folder->foldername = MEM_dupallocN(folder->foldername);
}
return folderlistn;
}
return NULL;
}
static void filelist_read_main(struct FileList *filelist);
static void filelist_read_library(struct FileList *filelist);
static void filelist_read_dir(struct FileList *filelist);
/* ------------------FILELIST------------------------ */
FileList *filelist_new(short type)
{
FileList *p = MEM_callocN(sizeof(FileList), "filelist");
switch (type) {
case FILE_MAIN:
p->readf = filelist_read_main;
p->filterf = is_filtered_main;
break;
case FILE_LOADLIB:
p->readf = filelist_read_library;
p->filterf = is_filtered_lib;
break;
default:
p->readf = filelist_read_dir;
p->filterf = is_filtered_file;
break;
}
return p;
}
void filelist_free(struct FileList *filelist)
{
if (!filelist) {
printf("Attempting to delete empty filelist.\n");
return;
}
if (filelist->fidx) {
MEM_freeN(filelist->fidx);
filelist->fidx = NULL;
}
BLI_free_filelist(filelist->filelist, filelist->numfiles);
filelist->numfiles = 0;
filelist->filelist = NULL;
filelist->filter = 0;
filelist->filter_glob[0] = '\0';
filelist->numfiltered = 0;
filelist->hide_dot = 0;
}
void filelist_freelib(struct FileList *filelist)
{
if (filelist->libfiledata)
BLO_blendhandle_close(filelist->libfiledata);
filelist->libfiledata = NULL;
}
BlendHandle *filelist_lib(struct FileList *filelist)
{
return filelist->libfiledata;
}
int filelist_numfiles(struct FileList *filelist)
{
return filelist->numfiltered;
}
const char *filelist_dir(struct FileList *filelist)
{
return filelist->dir;
}
void filelist_setdir(struct FileList *filelist, const char *dir)
{
BLI_strncpy(filelist->dir, dir, sizeof(filelist->dir));
}
void filelist_imgsize(struct FileList *filelist, short w, short h)
{
filelist->prv_w = w;
filelist->prv_h = h;
}
short filelist_changed(struct FileList *filelist)
{
return filelist->changed;
}
ImBuf *filelist_getimage(struct FileList *filelist, 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;
}
ImBuf *filelist_geticon(struct FileList *filelist, int index)
{
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 (strcmp(filelist->filelist[fidx].relname, "..") == 0) {
ibuf = gSpecialFileImages[SPECIAL_IMG_PARENT];
}
else if (strcmp(filelist->filelist[fidx].relname, ".") == 0) {
ibuf = gSpecialFileImages[SPECIAL_IMG_REFRESH];
}
else {
ibuf = gSpecialFileImages[SPECIAL_IMG_FOLDER];
}
}
else {
ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE];
}
if (file->flags & BLENDERFILE) {
ibuf = gSpecialFileImages[SPECIAL_IMG_BLENDFILE];
}
else if ((file->flags & MOVIEFILE) || (file->flags & MOVIEFILE_ICON)) {
ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE];
}
else if (file->flags & SOUNDFILE) {
ibuf = gSpecialFileImages[SPECIAL_IMG_SOUNDFILE];
}
else if (file->flags & PYSCRIPTFILE) {
ibuf = gSpecialFileImages[SPECIAL_IMG_PYTHONFILE];
}
else if (file->flags & FTFONTFILE) {
ibuf = gSpecialFileImages[SPECIAL_IMG_FONTFILE];
}
else if (file->flags & TEXTFILE) {
ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE];
}
else if (file->flags & IMAGEFILE) {
ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING];
}
else if (file->flags & BLENDERFILE_BACKUP) {
ibuf = gSpecialFileImages[SPECIAL_IMG_BACKUP];
}
return ibuf;
}
struct direntry *filelist_file(struct FileList *filelist, int index)
{
int fidx = 0;
if ((index < 0) || (index >= filelist->numfiltered)) {
return NULL;
}
fidx = filelist->fidx[index];
return &filelist->filelist[fidx];
}
int filelist_find(struct FileList *filelist, const char *filename)
{
int index = -1;
int i;
int fidx = -1;
if (!filelist->fidx)
return fidx;
for (i = 0; i < filelist->numfiles; ++i) {
if (strcmp(filelist->filelist[i].relname, filename) == 0) { /* not dealing with user input so don't need BLI_path_cmp */
index = i;
break;
}
}
for (i = 0; i < filelist->numfiltered; ++i) {
if (filelist->fidx[i] == index) {
fidx = i;
break;
}
}
return fidx;
}
void filelist_hidedot(struct FileList *filelist, short hide)
{
filelist->hide_dot = hide;
}
void filelist_setfilter(struct FileList *filelist, unsigned int filter)
{
filelist->filter = filter;
}
void filelist_setfilter_types(struct FileList *filelist, const char *filter_glob)
{
BLI_strncpy(filelist->filter_glob, filter_glob, sizeof(filelist->filter_glob));
}
/* would recognize .blend as well */
static bool file_is_blend_backup(const char *str)
{
const size_t a = strlen(str);
size_t b = 7;
bool retval = 0;
if (a == 0 || b >= a) {
/* pass */
}
else {
const char *loc;
if (a > b + 1)
b++;
/* allow .blend1 .blend2 .blend32 */
loc = BLI_strcasestr(str + a - b, ".blend");
if (loc)
retval = 1;
}
return (retval);
}
static int path_extension_type(const char *path)
{
if (BLO_has_bfile_extension(path)) {
return BLENDERFILE;
}
else if (file_is_blend_backup(path)) {
return BLENDERFILE_BACKUP;
}
else if (BLI_testextensie(path, ".app")) {
return APPLICATIONBUNDLE;
}
else if (BLI_testextensie(path, ".py")) {
return PYSCRIPTFILE;
}
else if (BLI_testextensie_n(path, ".txt", ".glsl", ".osl", ".data", NULL)) {
return TEXTFILE;
}
else if (BLI_testextensie_n(path, ".ttf", ".ttc", ".pfb", ".otf", ".otc", NULL)) {
return FTFONTFILE;
}
else if (BLI_testextensie(path, ".btx")) {
return BTXFILE;
}
else if (BLI_testextensie(path, ".dae")) {
return COLLADAFILE;
}
else if (BLI_testextensie_array(path, imb_ext_image) ||
(G.have_quicktime && BLI_testextensie_array(path, imb_ext_image_qt)))
{
return IMAGEFILE;
}
else if (BLI_testextensie(path, ".ogg")) {
if (IMB_isanim(path)) {
return MOVIEFILE;
}
else {
return SOUNDFILE;
}
}
else if (BLI_testextensie_array(path, imb_ext_movie)) {
return MOVIEFILE;
}
else if (BLI_testextensie_array(path, imb_ext_audio)) {
return SOUNDFILE;
}
return 0;
}
static int file_extension_type(const char *dir, const char *relname)
{
char path[FILE_MAX];
BLI_join_dirfile(path, sizeof(path), dir, relname);
return path_extension_type(path);
}
int ED_file_extension_icon(const char *path)
{
int type = path_extension_type(path);
if (type == BLENDERFILE)
return ICON_FILE_BLEND;
else if (type == BLENDERFILE_BACKUP)
return ICON_FILE_BACKUP;
else if (type == IMAGEFILE)
return ICON_FILE_IMAGE;
else if (type == MOVIEFILE)
return ICON_FILE_MOVIE;
else if (type == PYSCRIPTFILE)
return ICON_FILE_SCRIPT;
else if (type == SOUNDFILE)
return ICON_FILE_SOUND;
else if (type == FTFONTFILE)
return ICON_FILE_FONT;
else if (type == BTXFILE)
return ICON_FILE_BLANK;
else if (type == COLLADAFILE)
return ICON_FILE_BLANK;
else if (type == TEXTFILE)
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++) {
file->type = file->s.st_mode; /* restore the mess below */
#ifndef __APPLE__
/* Don't check extensions for directories, allow in OSX cause bundles have extensions*/
if (file->type & S_IFDIR) {
continue;
}
#endif
file->flags = file_extension_type(filelist->dir, file->relname);
if (filelist->filter_glob[0] &&
BLI_testextensie_glob(file->relname, filelist->filter_glob))
{
file->flags = OPERATORFILE;
}
}
}
static void filelist_read_dir(struct FileList *filelist)
{
if (!filelist) return;
filelist->fidx = NULL;
filelist->filelist = NULL;
BLI_cleanup_dir(G.main->name, filelist->dir);
filelist->numfiles = BLI_dir_contents(filelist->dir, &(filelist->filelist));
filelist_setfiletypes(filelist);
filelist_filter(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;
BLI_make_exist(filelist->dir);
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);
}
int filelist_empty(struct FileList *filelist)
{
return filelist->filelist == NULL;
}
void filelist_parent(struct FileList *filelist)
{
BLI_parent_dir(filelist->dir);
BLI_make_exist(filelist->dir);
filelist_readdir(filelist);
}
void filelist_select_file(struct FileList *filelist, int index, 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);
break;
case CHECK_ALL:
check_ok = 1;
break;
case CHECK_FILES:
default:
check_ok = !S_ISDIR(file->type);
break;
}
if (check_ok) {
switch (select) {
case FILE_SEL_REMOVE:
file->selflag &= ~flag;
break;
case FILE_SEL_ADD:
file->selflag |= flag;
break;
case FILE_SEL_TOGGLE:
file->selflag ^= flag;
break;
}
}
}
}
void filelist_select(struct FileList *filelist, FileSelection *sel, FileSelType select, unsigned int flag, FileCheckType check)
{
/* select all valid files between first and last indicated */
if ((sel->first >= 0) && (sel->first < filelist->numfiltered) && (sel->last >= 0) && (sel->last < filelist->numfiltered)) {
int current_file;
for (current_file = sel->first; current_file <= sel->last; current_file++) {
filelist_select_file(filelist, current_file, select, flag, check);
}
}
}
bool filelist_is_selected(struct FileList *filelist, int index, 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 & SELECTED_FILE);
case CHECK_FILES:
return S_ISREG(file->type) && (file->selflag & SELECTED_FILE);
case CHECK_ALL:
default:
return (file->selflag & SELECTED_FILE) != 0;
}
}
void filelist_sort(struct FileList *filelist, short sort)
{
switch (sort) {
case FILE_SORT_ALPHA:
qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_name);
break;
case FILE_SORT_TIME:
qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_date);
break;
case FILE_SORT_SIZE:
qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_size);
break;
case FILE_SORT_EXTENSION:
qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_extension);
break;
}
filelist_filter(filelist);
}
bool filelist_islibrary(struct FileList *filelist, char *dir, char *group)
{
return BLO_is_a_library(filelist->dir, dir, group);
}
static int groupname_to_code(const char *group)
{
char buf[BLO_GROUP_MAX];
char *lslash;
BLI_strncpy(buf, group, sizeof(buf));
lslash = (char *)BLI_last_slash(buf);
if (lslash)
lslash[0] = '\0';
return buf[0] ? BKE_idcode_from_name(buf) : 0;
}
void filelist_from_library(struct FileList *filelist)
{
LinkNode *l, *names, *previews;
struct ImBuf *ima;
int ok, i, nprevs, nnames, idcode;
char filename[FILE_MAX];
char dir[FILE_MAX], group[BLO_GROUP_MAX];
/* name test */
ok = filelist_islibrary(filelist, dir, group);
if (!ok) {
/* free */
if (filelist->libfiledata) BLO_blendhandle_close(filelist->libfiledata);
filelist->libfiledata = NULL;
return;
}
BLI_strncpy(filename, G.main->name, sizeof(filename));
/* 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;
}
idcode = groupname_to_code(group);
/* 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);
}
else {
previews = NULL;
nprevs = 0;
names = BLO_blendhandle_get_linkable_groups(filelist->libfiledata);
nnames = BLI_linklist_length(names);
}
filelist->numfiles = nnames + 1;
filelist->filelist = malloc(filelist->numfiles * sizeof(*filelist->filelist));
memset(filelist->filelist, 0, filelist->numfiles * sizeof(*filelist->filelist));
filelist->filelist[0].relname = BLI_strdup("..");
filelist->filelist[0].type |= S_IFDIR;
for (i = 0, l = names; i < nnames; i++, l = l->next) {
const char *blockname = l->link;
filelist->filelist[i + 1].relname = BLI_strdup(blockname);
if (idcode) {
filelist->filelist[i + 1].type |= S_IFREG;
}
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 = IMAGEFILE;
}
}
}
}
BLI_linklist_free(names, free);
if (previews) BLI_linklist_free(previews, BKE_previewimg_freefunc);
filelist_sort(filelist, FILE_SORT_ALPHA);
BLI_strncpy(G.main->name, filename, sizeof(filename)); /* prevent G.main->name to change */
filelist->filter = 0;
filelist_filter(filelist);
}
void filelist_hideparent(struct FileList *filelist, short hide)
{
filelist->hide_parent = hide;
}
void filelist_from_main(struct FileList *filelist)
{
ID *id;
struct direntry *files, *firstlib = NULL;
ListBase *lb;
int a, fake, idcode, ok, totlib, totbl;
// filelist->type = FILE_MAIN; // XXXXX TODO: add modes to filebrowser
if (filelist->dir[0] == '/') filelist->dir[0] = 0;
if (filelist->dir[0]) {
idcode = groupname_to_code(filelist->dir);
if (idcode == 0) filelist->dir[0] = 0;
}
if (filelist->dir[0] == 0) {
/* make directories */
#ifdef WITH_FREESTYLE
filelist->numfiles = 25;
#else
filelist->numfiles = 24;
#endif
filelist->filelist = (struct direntry *)malloc(filelist->numfiles * sizeof(struct direntry));
for (a = 0; a < filelist->numfiles; a++) {
memset(&(filelist->filelist[a]), 0, sizeof(struct direntry));
filelist->filelist[a].type |= S_IFDIR;
}
filelist->filelist[0].relname = BLI_strdup("..");
filelist->filelist[2].relname = BLI_strdup("Scene");
filelist->filelist[3].relname = BLI_strdup("Object");
filelist->filelist[4].relname = BLI_strdup("Mesh");
filelist->filelist[5].relname = BLI_strdup("Curve");
filelist->filelist[6].relname = BLI_strdup("Metaball");
filelist->filelist[7].relname = BLI_strdup("Material");
filelist->filelist[8].relname = BLI_strdup("Texture");
filelist->filelist[9].relname = BLI_strdup("Image");
filelist->filelist[10].relname = BLI_strdup("Ika");
filelist->filelist[11].relname = BLI_strdup("Wave");
filelist->filelist[12].relname = BLI_strdup("Lattice");
filelist->filelist[13].relname = BLI_strdup("Lamp");
filelist->filelist[14].relname = BLI_strdup("Camera");
filelist->filelist[15].relname = BLI_strdup("Ipo");
filelist->filelist[16].relname = BLI_strdup("World");
filelist->filelist[17].relname = BLI_strdup("Screen");
filelist->filelist[18].relname = BLI_strdup("VFont");
filelist->filelist[19].relname = BLI_strdup("Text");
filelist->filelist[20].relname = BLI_strdup("Armature");
filelist->filelist[21].relname = BLI_strdup("Action");
filelist->filelist[22].relname = BLI_strdup("NodeTree");
filelist->filelist[23].relname = BLI_strdup("Speaker");
#ifdef WITH_FREESTYLE
filelist->filelist[24].relname = BLI_strdup("FreestyleLineStyle");
#endif
filelist_sort(filelist, FILE_SORT_ALPHA);
}
else {
/* make files */
idcode = groupname_to_code(filelist->dir);
lb = which_libbase(G.main, idcode);
if (lb == NULL) return;
id = lb->first;
filelist->numfiles = 0;
while (id) {
if (!filelist->hide_dot || id->name[2] != '.') {
filelist->numfiles++;
}
id = id->next;
}
/* XXXXX TODO: if databrowse F4 or append/link filelist->hide_parent has to be set */
if (!filelist->hide_parent) filelist->numfiles += 1;
filelist->filelist = filelist->numfiles > 0 ? (struct direntry *)malloc(filelist->numfiles * sizeof(struct direntry)) : NULL;
files = filelist->filelist;
if (!filelist->hide_parent) {
memset(&(filelist->filelist[0]), 0, sizeof(struct direntry));
filelist->filelist[0].relname = BLI_strdup("..");
filelist->filelist[0].type |= S_IFDIR;
files++;
}
id = lb->first;
totlib = totbl = 0;
while (id) {
ok = 1;
if (ok) {
if (!filelist->hide_dot || id->name[2] != '.') {
memset(files, 0, sizeof(struct direntry));
if (id->lib == NULL) {
files->relname = BLI_strdup(id->name + 2);
}
else {
files->relname = MEM_mallocN(FILE_MAX + (MAX_ID_NAME - 2), "filename for lib");
BLI_snprintf(files->relname, FILE_MAX + (MAX_ID_NAME - 2) + 3, "%s | %s", id->lib->name, id->name + 2);
}
files->type |= S_IFREG;
#if 0 /* XXXXX 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 |= SELECTED_FILE;
}
else if (idcode == ID_SCE) {
if ( ((Scene *)id)->r.scemode & R_BG_RENDER) files->selflag |= SELECTED_FILE;
}
}
#endif
files->nr = totbl + 1;
files->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 |= IMAGEFILE;
}
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) {
if (totlib == 0) firstlib = files;
totlib++;
}
files++;
}
totbl++;
}
id = id->next;
}
/* only qsort of library blocks */
if (totlib > 1) {
qsort(firstlib, totlib, sizeof(struct direntry), compare_name);
}
}
filelist->filter = 0;
filelist_filter(filelist);
}
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_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)) {
if (limg->flags & IMAGEFILE) {
limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_IMAGE);
}
else if (limg->flags & (BLENDERFILE | BLENDERFILE_BACKUP)) {
limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_BLEND);
}
else if (limg->flags & MOVIEFILE) {
limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_MOVIE);
if (!limg->img) {
/* remember that file can't be loaded via IMB_open_anim */
limg->flags &= ~MOVIEFILE;
limg->flags |= MOVIEFILE_ICON;
}
}
*do_update = true;
PIL_sleep_ms(10);
limg = limg->next;
}
}
static void thumbnails_update(void *tjv)
{
ThumbnailJob *tj = tjv;
if (tj->filelist && tj->filelist->filelist) {
FileImage *limg = tj->loadimages.first;
while (limg) {
if (!limg->done && limg->img) {
tj->filelist->filelist[limg->index].image = limg->img;
/* update flag for movie files where thumbnail can't be created */
if (limg->flags & MOVIEFILE_ICON) {
tj->filelist->filelist[limg->index].flags &= ~MOVIEFILE;
tj->filelist->filelist[limg->index].flags |= MOVIEFILE_ICON;
}
limg->done = true;
}
limg = limg->next;
}
}
}
static void thumbnails_free(void *tjv)
{
ThumbnailJob *tj = tjv;
thumbnail_joblist_free(tj);
MEM_freeN(tj);
}
void thumbnails_start(FileList *filelist, const bContext *C)
{
wmJob *wm_job;
ThumbnailJob *tj;
int idx;
/* prepare job data */
tj = MEM_callocN(sizeof(ThumbnailJob), "thumbnails\n");
tj->filelist = filelist;
for (idx = 0; idx < filelist->numfiles; idx++) {
if (!filelist->filelist[idx].image) {
if ((filelist->filelist[idx].flags & (IMAGEFILE | MOVIEFILE | BLENDERFILE | BLENDERFILE_BACKUP))) {
FileImage *limg = MEM_callocN(sizeof(FileImage), "loadimage");
BLI_strncpy(limg->path, filelist->filelist[idx].path, FILE_MAX);
limg->index = idx;
limg->flags = filelist->filelist[idx].flags;
BLI_addtail(&tj->loadimages, limg);
}
}
}
BKE_reports_init(&tj->reports, RPT_PRINT);
/* 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, NULL);
/* start the job */
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
void thumbnails_stop(wmWindowManager *wm, FileList *filelist)
{
WM_jobs_kill(wm, filelist, NULL);
}
int thumbnails_running(wmWindowManager *wm, FileList *filelist)
{
return WM_jobs_test(wm, filelist, WM_JOB_TYPE_FILESEL_THUMBNAIL);
}