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/fsmenu.c
Yevgeny Makarov 5c5f2bd221 Fix file browser not excluding AirDrop from the System list on macOS
There is already code for this, it just wasn't working. Now add the
slash after checking for an empty string.

Differential Revision: https://developer.blender.org/D6568
2020-01-20 14:50:18 +01:00

931 lines
27 KiB
C

/*
* 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) 2001-2002 by NaN Holding BV.
* All rights reserved.
*/
/** \file
* \ingroup spfile
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_blenlib.h"
#include "BKE_appdir.h"
#include "ED_fileselect.h"
#ifdef WIN32
/* Need to include windows.h so _WIN32_IE is defined. */
# include <windows.h>
/* For SHGetSpecialFolderPath, has to be done before BLI_winstuff
* because 'near' is disabled through BLI_windstuff. */
# include <shlobj.h>
# include "BLI_winstuff.h"
#endif
#include "WM_api.h"
#include "WM_types.h"
#include "UI_interface_icons.h"
#include "UI_resources.h"
#ifdef __APPLE__
# include <Carbon/Carbon.h>
#endif /* __APPLE__ */
#ifdef __linux__
# include <mntent.h>
# include "BLI_fileops_types.h"
#endif
#include "fsmenu.h" /* include ourselves */
/* FSMENU HANDLING */
typedef struct FSMenu {
FSMenuEntry *fsmenu_system;
FSMenuEntry *fsmenu_system_bookmarks;
FSMenuEntry *fsmenu_bookmarks;
FSMenuEntry *fsmenu_recent;
} FSMenu;
static FSMenu *g_fsmenu = NULL;
FSMenu *ED_fsmenu_get(void)
{
if (!g_fsmenu) {
g_fsmenu = MEM_callocN(sizeof(struct FSMenu), "fsmenu");
}
return g_fsmenu;
}
struct FSMenuEntry *ED_fsmenu_get_category(struct FSMenu *fsmenu, FSMenuCategory category)
{
FSMenuEntry *fsm_head = NULL;
switch (category) {
case FS_CATEGORY_SYSTEM:
fsm_head = fsmenu->fsmenu_system;
break;
case FS_CATEGORY_SYSTEM_BOOKMARKS:
fsm_head = fsmenu->fsmenu_system_bookmarks;
break;
case FS_CATEGORY_BOOKMARKS:
fsm_head = fsmenu->fsmenu_bookmarks;
break;
case FS_CATEGORY_RECENT:
fsm_head = fsmenu->fsmenu_recent;
break;
}
return fsm_head;
}
void ED_fsmenu_set_category(struct FSMenu *fsmenu, FSMenuCategory category, FSMenuEntry *fsm_head)
{
switch (category) {
case FS_CATEGORY_SYSTEM:
fsmenu->fsmenu_system = fsm_head;
break;
case FS_CATEGORY_SYSTEM_BOOKMARKS:
fsmenu->fsmenu_system_bookmarks = fsm_head;
break;
case FS_CATEGORY_BOOKMARKS:
fsmenu->fsmenu_bookmarks = fsm_head;
break;
case FS_CATEGORY_RECENT:
fsmenu->fsmenu_recent = fsm_head;
break;
}
}
int ED_fsmenu_get_nentries(struct FSMenu *fsmenu, FSMenuCategory category)
{
FSMenuEntry *fsm_iter;
int count = 0;
for (fsm_iter = ED_fsmenu_get_category(fsmenu, category); fsm_iter; fsm_iter = fsm_iter->next) {
count++;
}
return count;
}
FSMenuEntry *ED_fsmenu_get_entry(struct FSMenu *fsmenu, FSMenuCategory category, int index)
{
FSMenuEntry *fsm_iter;
for (fsm_iter = ED_fsmenu_get_category(fsmenu, category); fsm_iter && index;
fsm_iter = fsm_iter->next) {
index--;
}
return fsm_iter;
}
char *ED_fsmenu_entry_get_path(struct FSMenuEntry *fsentry)
{
return fsentry->path;
}
void ED_fsmenu_entry_set_path(struct FSMenuEntry *fsentry, const char *path)
{
if ((!fsentry->path || !path || !STREQ(path, fsentry->path)) && (fsentry->path != path)) {
char tmp_name[FILE_MAXFILE];
MEM_SAFE_FREE(fsentry->path);
fsentry->path = (path && path[0]) ? BLI_strdup(path) : NULL;
BLI_make_file_string("/",
tmp_name,
BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL),
BLENDER_BOOKMARK_FILE);
fsmenu_write_file(ED_fsmenu_get(), tmp_name);
}
}
int ED_fsmenu_entry_get_icon(struct FSMenuEntry *fsentry)
{
return (fsentry->icon) ? fsentry->icon : ICON_FILE_FOLDER;
}
void ED_fsmenu_entry_set_icon(struct FSMenuEntry *fsentry, const int icon)
{
fsentry->icon = icon;
}
static void fsmenu_entry_generate_name(struct FSMenuEntry *fsentry, char *name, size_t name_size)
{
int offset = 0;
int len = name_size;
if (BLI_path_name_at_index(fsentry->path, -1, &offset, &len)) {
/* use as size */
len += 1;
}
BLI_strncpy(name, &fsentry->path[offset], MIN2(len, name_size));
if (!name[0]) {
name[0] = '/';
name[1] = '\0';
}
}
char *ED_fsmenu_entry_get_name(struct FSMenuEntry *fsentry)
{
if (fsentry->name[0]) {
return fsentry->name;
}
else {
/* Here we abuse fsm_iter->name, keeping first char NULL. */
char *name = fsentry->name + 1;
size_t name_size = sizeof(fsentry->name) - 1;
fsmenu_entry_generate_name(fsentry, name, name_size);
return name;
}
}
void ED_fsmenu_entry_set_name(struct FSMenuEntry *fsentry, const char *name)
{
if (!STREQ(name, fsentry->name)) {
char tmp_name[FILE_MAXFILE];
size_t tmp_name_size = sizeof(tmp_name);
fsmenu_entry_generate_name(fsentry, tmp_name, tmp_name_size);
if (!name[0] || STREQ(tmp_name, name)) {
/* reset name to default behavior. */
fsentry->name[0] = '\0';
}
else {
BLI_strncpy(fsentry->name, name, sizeof(fsentry->name));
}
BLI_make_file_string("/",
tmp_name,
BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL),
BLENDER_BOOKMARK_FILE);
fsmenu_write_file(ED_fsmenu_get(), tmp_name);
}
}
void fsmenu_entry_refresh_valid(struct FSMenuEntry *fsentry)
{
if (fsentry->path && fsentry->path[0]) {
#ifdef WIN32
/* XXX Special case, always consider those as valid.
* Thanks to Windows, which can spend five seconds to perform a mere stat() call on those paths
* See T43684. */
const char *exceptions[] = {"A:\\", "B:\\", NULL};
const size_t exceptions_len[] = {strlen(exceptions[0]), strlen(exceptions[1]), 0};
int i;
for (i = 0; exceptions[i]; i++) {
if (STRCASEEQLEN(fsentry->path, exceptions[i], exceptions_len[i])) {
fsentry->valid = true;
return;
}
}
#endif
fsentry->valid = BLI_is_dir(fsentry->path);
}
else {
fsentry->valid = false;
}
}
short fsmenu_can_save(struct FSMenu *fsmenu, FSMenuCategory category, int idx)
{
FSMenuEntry *fsm_iter;
for (fsm_iter = ED_fsmenu_get_category(fsmenu, category); fsm_iter && idx;
fsm_iter = fsm_iter->next) {
idx--;
}
return fsm_iter ? fsm_iter->save : 0;
}
void fsmenu_insert_entry(struct FSMenu *fsmenu,
FSMenuCategory category,
const char *path,
const char *name,
const int icon,
FSMenuInsert flag)
{
FSMenuEntry *fsm_prev;
FSMenuEntry *fsm_iter;
FSMenuEntry *fsm_head;
fsm_head = ED_fsmenu_get_category(fsmenu, category);
fsm_prev = fsm_head; /* this is odd and not really correct? */
for (fsm_iter = fsm_head; fsm_iter; fsm_prev = fsm_iter, fsm_iter = fsm_iter->next) {
if (fsm_iter->path) {
const int cmp_ret = BLI_path_cmp(path, fsm_iter->path);
if (cmp_ret == 0) {
if (flag & FS_INSERT_FIRST) {
if (fsm_iter != fsm_head) {
fsm_prev->next = fsm_iter->next;
fsm_iter->next = fsm_head;
ED_fsmenu_set_category(fsmenu, category, fsm_iter);
}
}
return;
}
else if ((flag & FS_INSERT_SORTED) && cmp_ret < 0) {
break;
}
}
else {
/* if we're bookmarking this, file should come
* before the last separator, only automatically added
* current dir go after the last sep. */
if (flag & FS_INSERT_SAVE) {
break;
}
}
}
fsm_iter = MEM_mallocN(sizeof(*fsm_iter), "fsme");
fsm_iter->path = BLI_strdup(path);
fsm_iter->save = (flag & FS_INSERT_SAVE) != 0;
if ((category == FS_CATEGORY_RECENT) && (!name || !name[0])) {
/* Special handling when adding new recent entry - check if dir exists in
* some other categories, and try to use name from there if so. */
FSMenuCategory cats[] = {
FS_CATEGORY_SYSTEM, FS_CATEGORY_SYSTEM_BOOKMARKS, FS_CATEGORY_BOOKMARKS};
int i = ARRAY_SIZE(cats);
while (i--) {
FSMenuEntry *tfsm = ED_fsmenu_get_category(fsmenu, cats[i]);
for (; tfsm; tfsm = tfsm->next) {
if (STREQ(tfsm->path, fsm_iter->path)) {
if (tfsm->name[0]) {
name = tfsm->name;
}
break;
}
}
if (tfsm) {
break;
}
}
}
if (name && name[0]) {
BLI_strncpy(fsm_iter->name, name, sizeof(fsm_iter->name));
}
else {
fsm_iter->name[0] = '\0';
}
ED_fsmenu_entry_set_icon(fsm_iter, icon);
fsmenu_entry_refresh_valid(fsm_iter);
if (fsm_prev) {
if (flag & FS_INSERT_FIRST) {
fsm_iter->next = fsm_head;
ED_fsmenu_set_category(fsmenu, category, fsm_iter);
}
else {
fsm_iter->next = fsm_prev->next;
fsm_prev->next = fsm_iter;
}
}
else {
fsm_iter->next = fsm_head;
ED_fsmenu_set_category(fsmenu, category, fsm_iter);
}
}
void fsmenu_remove_entry(struct FSMenu *fsmenu, FSMenuCategory category, int idx)
{
FSMenuEntry *fsm_prev = NULL;
FSMenuEntry *fsm_iter;
FSMenuEntry *fsm_head;
fsm_head = ED_fsmenu_get_category(fsmenu, category);
for (fsm_iter = fsm_head; fsm_iter && idx; fsm_prev = fsm_iter, fsm_iter = fsm_iter->next) {
idx--;
}
if (fsm_iter) {
/* you should only be able to remove entries that were
* not added by default, like windows drives.
* also separators (where path == NULL) shouldn't be removed */
if (fsm_iter->save && fsm_iter->path) {
/* remove fsme from list */
if (fsm_prev) {
fsm_prev->next = fsm_iter->next;
}
else {
fsm_head = fsm_iter->next;
ED_fsmenu_set_category(fsmenu, category, fsm_head);
}
/* free entry */
MEM_freeN(fsm_iter->path);
MEM_freeN(fsm_iter);
}
}
}
void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename)
{
FSMenuEntry *fsm_iter = NULL;
char fsm_name[FILE_MAX];
int nwritten = 0;
FILE *fp = BLI_fopen(filename, "w");
if (!fp) {
return;
}
fprintf(fp, "[Bookmarks]\n");
for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS); fsm_iter;
fsm_iter = fsm_iter->next) {
if (fsm_iter->path && fsm_iter->save) {
fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name));
if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) {
fprintf(fp, "!%s\n", fsm_iter->name);
}
fprintf(fp, "%s\n", fsm_iter->path);
}
}
fprintf(fp, "[Recent]\n");
for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_RECENT);
fsm_iter && (nwritten < FSMENU_RECENT_MAX);
fsm_iter = fsm_iter->next, nwritten++) {
if (fsm_iter->path && fsm_iter->save) {
fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name));
if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) {
fprintf(fp, "!%s\n", fsm_iter->name);
}
fprintf(fp, "%s\n", fsm_iter->path);
}
}
fclose(fp);
}
void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename)
{
char line[FILE_MAXDIR];
char name[FILE_MAXFILE];
FSMenuCategory category = FS_CATEGORY_BOOKMARKS;
FILE *fp;
fp = BLI_fopen(filename, "r");
if (!fp) {
return;
}
name[0] = '\0';
while (fgets(line, sizeof(line), fp) != NULL) { /* read a line */
if (STREQLEN(line, "[Bookmarks]", 11)) {
category = FS_CATEGORY_BOOKMARKS;
}
else if (STREQLEN(line, "[Recent]", 8)) {
category = FS_CATEGORY_RECENT;
}
else if (line[0] == '!') {
int len = strlen(line);
if (len > 0) {
if (line[len - 1] == '\n') {
line[len - 1] = '\0';
}
BLI_strncpy(name, line + 1, sizeof(name));
}
}
else {
int len = strlen(line);
if (len > 0) {
if (line[len - 1] == '\n') {
line[len - 1] = '\0';
}
/* don't do this because it can be slow on network drives,
* having a bookmark from a drive that's ejected or so isn't
* all _that_ bad */
#if 0
if (BLI_exists(line))
#endif
{
fsmenu_insert_entry(fsmenu, category, line, name, ICON_FILE_FOLDER, FS_INSERT_SAVE);
}
}
/* always reset name. */
name[0] = '\0';
}
}
fclose(fp);
}
void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
{
char line[FILE_MAXDIR];
#ifdef WIN32
/* Add the drive names to the listing */
{
wchar_t wline[FILE_MAXDIR];
__int64 tmp;
char tmps[4], *name;
int i;
tmp = GetLogicalDrives();
for (i = 0; i < 26; i++) {
if ((tmp >> i) & 1) {
tmps[0] = 'A' + i;
tmps[1] = ':';
tmps[2] = '\\';
tmps[3] = '\0';
name = NULL;
/* Flee from horrible win querying hover floppy drives! */
if (i > 1) {
/* Try to get a friendly drive description. */
SHFILEINFOW shFile = {0};
BLI_strncpy_wchar_from_utf8(wline, tmps, 4);
if (SHGetFileInfoW(wline, 0, &shFile, sizeof(SHFILEINFOW), SHGFI_DISPLAYNAME)) {
BLI_strncpy_wchar_as_utf8(line, shFile.szDisplayName, FILE_MAXDIR);
name = line;
}
}
if (name == NULL) {
name = tmps;
}
int icon = ICON_DISK_DRIVE;
switch (GetDriveType(tmps)) {
case DRIVE_REMOVABLE:
icon = ICON_EXTERNAL_DRIVE;
break;
case DRIVE_CDROM:
icon = ICON_DISC;
break;
case DRIVE_FIXED:
case DRIVE_RAMDISK:
icon = ICON_DISK_DRIVE;
break;
case DRIVE_REMOTE:
icon = ICON_NETWORK_DRIVE;
break;
}
fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, tmps, name, icon, FS_INSERT_SORTED);
}
}
/* Adding Desktop and My Documents */
if (read_bookmarks) {
SHGetSpecialFolderPathW(0, wline, CSIDL_PERSONAL, 0);
BLI_strncpy_wchar_as_utf8(line, wline, FILE_MAXDIR);
fsmenu_insert_entry(
fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, ICON_DOCUMENTS, FS_INSERT_SORTED);
SHGetSpecialFolderPathW(0, wline, CSIDL_DESKTOPDIRECTORY, 0);
BLI_strncpy_wchar_as_utf8(line, wline, FILE_MAXDIR);
fsmenu_insert_entry(
fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, ICON_DESKTOP, FS_INSERT_SORTED);
}
}
#else
# ifdef __APPLE__
{
/* Get mounted volumes better method OSX 10.6 and higher, see:
* https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFURLRef/Reference/reference.html
*/
/* We get all volumes sorted including network and do not relay
* on user-defined finder visibility, less confusing. */
CFURLRef cfURL = NULL;
CFURLEnumeratorResult result = kCFURLEnumeratorSuccess;
CFURLEnumeratorRef volEnum = CFURLEnumeratorCreateForMountedVolumes(
NULL, kCFURLEnumeratorSkipInvisibles, NULL);
while (result != kCFURLEnumeratorEnd) {
char defPath[FILE_MAX];
result = CFURLEnumeratorGetNextURL(volEnum, &cfURL, NULL);
if (result != kCFURLEnumeratorSuccess) {
continue;
}
CFURLGetFileSystemRepresentation(cfURL, false, (UInt8 *)defPath, FILE_MAX);
/* Add end slash for consistency with other platforms */
BLI_add_slash(defPath);
fsmenu_insert_entry(
fsmenu, FS_CATEGORY_SYSTEM, defPath, NULL, ICON_DISK_DRIVE, FS_INSERT_SORTED);
}
CFRelease(volEnum);
/* Finally get user favorite places */
if (read_bookmarks) {
UInt32 seed;
LSSharedFileListRef list = LSSharedFileListCreate(
NULL, kLSSharedFileListFavoriteItems, NULL);
CFArrayRef pathesArray = LSSharedFileListCopySnapshot(list, &seed);
CFIndex pathesCount = CFArrayGetCount(pathesArray);
for (CFIndex i = 0; i < pathesCount; i++) {
LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(
pathesArray, i);
CFURLRef cfURL = NULL;
OSErr err = LSSharedFileListItemResolve(itemRef,
kLSSharedFileListNoUserInteraction |
kLSSharedFileListDoNotMountVolumes,
&cfURL,
NULL);
if (err != noErr || !cfURL) {
continue;
}
CFStringRef pathString = CFURLCopyFileSystemPath(cfURL, kCFURLPOSIXPathStyle);
if (pathString == NULL ||
!CFStringGetCString(pathString, line, sizeof(line), kCFStringEncodingUTF8)) {
continue;
}
/* Exclude "all my files" as it makes no sense in blender fileselector */
/* Exclude "airdrop" if wlan not active as it would show "" ) */
if (!strstr(line, "myDocuments.cannedSearch") && (*line != '\0')) {
/* Add end slash for consistency with other platforms */
BLI_add_slash(line);
fsmenu_insert_entry(
fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, ICON_FILE_FOLDER, FS_INSERT_LAST);
}
CFRelease(pathString);
CFRelease(cfURL);
}
CFRelease(pathesArray);
CFRelease(list);
}
}
# else
/* unix */
{
const char *home = BLI_getenv("HOME");
if (read_bookmarks && home) {
BLI_snprintf(line, sizeof(line), "%s/", home);
fsmenu_insert_entry(
fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, ICON_HOME, FS_INSERT_SORTED);
BLI_snprintf(line, sizeof(line), "%s/Desktop/", home);
if (BLI_exists(line)) {
fsmenu_insert_entry(
fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL, ICON_DESKTOP, FS_INSERT_SORTED);
}
}
{
int found = 0;
# ifdef __linux__
/* loop over mount points */
struct mntent *mnt;
int len;
FILE *fp;
fp = setmntent(MOUNTED, "r");
if (fp == NULL) {
fprintf(stderr, "could not get a list of mounted filesystems\n");
}
else {
while ((mnt = getmntent(fp))) {
if (STRPREFIX(mnt->mnt_dir, "/boot")) {
/* Hide share not usable to the user. */
continue;
}
else if (!STRPREFIX(mnt->mnt_fsname, "/dev")) {
continue;
}
else if (STRPREFIX(mnt->mnt_fsname, "/dev/loop")) {
/* The dev/loop* entries are SNAPS used by desktop environment
* (Gnome) no need for them to show up in the list. */
continue;
}
len = strlen(mnt->mnt_dir);
if (len && mnt->mnt_dir[len - 1] != '/') {
BLI_snprintf(line, sizeof(line), "%s/", mnt->mnt_dir);
fsmenu_insert_entry(
fsmenu, FS_CATEGORY_SYSTEM, line, NULL, ICON_DISK_DRIVE, FS_INSERT_SORTED);
}
else {
fsmenu_insert_entry(
fsmenu, FS_CATEGORY_SYSTEM, mnt->mnt_dir, NULL, ICON_DISK_DRIVE, FS_INSERT_SORTED);
}
found = 1;
}
if (endmntent(fp) == 0) {
fprintf(stderr, "could not close the list of mounted filesystems\n");
}
}
/* Check gvfs shares. */
const char *const xdg_runtime_dir = BLI_getenv("XDG_RUNTIME_DIR");
if (xdg_runtime_dir != NULL) {
struct direntry *dir;
char name[FILE_MAX];
BLI_join_dirfile(name, sizeof(name), xdg_runtime_dir, "gvfs/");
const uint dir_len = BLI_filelist_dir_contents(name, &dir);
for (uint i = 0; i < dir_len; i++) {
if ((dir[i].type & S_IFDIR)) {
const char *dirname = dir[i].relname;
if (dirname[0] != '.') {
/* Dir names contain a lot of unwanted text.
* Assuming every entry ends with the share name */
const char *label = strstr(dirname, "share=");
if (label != NULL) {
/* Move pointer so "share=" is trimmed off
* or use full dirname as label. */
const char *label_test = label + 6;
label = *label_test ? label_test : dirname;
}
BLI_snprintf(line, sizeof(line), "%s%s/", name, dirname);
fsmenu_insert_entry(
fsmenu, FS_CATEGORY_SYSTEM, line, label, ICON_NETWORK_DRIVE, FS_INSERT_SORTED);
found = 1;
}
}
}
BLI_filelist_free(dir, dir_len);
}
# endif
/* fallback */
if (!found) {
fsmenu_insert_entry(
fsmenu, FS_CATEGORY_SYSTEM, "/", NULL, ICON_DISK_DRIVE, FS_INSERT_SORTED);
}
}
}
# endif
#endif
}
static void fsmenu_free_category(struct FSMenu *fsmenu, FSMenuCategory category)
{
FSMenuEntry *fsm_iter = ED_fsmenu_get_category(fsmenu, category);
while (fsm_iter) {
FSMenuEntry *fsm_next = fsm_iter->next;
if (fsm_iter->path) {
MEM_freeN(fsm_iter->path);
}
MEM_freeN(fsm_iter);
fsm_iter = fsm_next;
}
}
void fsmenu_refresh_system_category(struct FSMenu *fsmenu)
{
fsmenu_free_category(fsmenu, FS_CATEGORY_SYSTEM);
ED_fsmenu_set_category(fsmenu, FS_CATEGORY_SYSTEM, NULL);
fsmenu_free_category(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS);
ED_fsmenu_set_category(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, NULL);
/* Add all entries to system category */
fsmenu_read_system(fsmenu, true);
}
static void fsmenu_free_ex(FSMenu **fsmenu)
{
if (*fsmenu != NULL) {
fsmenu_free_category(*fsmenu, FS_CATEGORY_SYSTEM);
fsmenu_free_category(*fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS);
fsmenu_free_category(*fsmenu, FS_CATEGORY_BOOKMARKS);
fsmenu_free_category(*fsmenu, FS_CATEGORY_RECENT);
MEM_freeN(*fsmenu);
}
*fsmenu = NULL;
}
void fsmenu_free(void)
{
fsmenu_free_ex(&g_fsmenu);
}
static void fsmenu_copy_category(struct FSMenu *fsmenu_dst,
struct FSMenu *fsmenu_src,
const FSMenuCategory category)
{
FSMenuEntry *fsm_dst_prev = NULL, *fsm_dst_head = NULL;
FSMenuEntry *fsm_src_iter = ED_fsmenu_get_category(fsmenu_src, category);
for (; fsm_src_iter != NULL; fsm_src_iter = fsm_src_iter->next) {
FSMenuEntry *fsm_dst = MEM_dupallocN(fsm_src_iter);
if (fsm_dst->path != NULL) {
fsm_dst->path = MEM_dupallocN(fsm_dst->path);
}
if (fsm_dst_prev != NULL) {
fsm_dst_prev->next = fsm_dst;
}
else {
fsm_dst_head = fsm_dst;
}
fsm_dst_prev = fsm_dst;
}
ED_fsmenu_set_category(fsmenu_dst, category, fsm_dst_head);
}
static FSMenu *fsmenu_copy(FSMenu *fsmenu)
{
FSMenu *fsmenu_copy = MEM_dupallocN(fsmenu);
fsmenu_copy_category(fsmenu_copy, fsmenu_copy, FS_CATEGORY_SYSTEM);
fsmenu_copy_category(fsmenu_copy, fsmenu_copy, FS_CATEGORY_SYSTEM_BOOKMARKS);
fsmenu_copy_category(fsmenu_copy, fsmenu_copy, FS_CATEGORY_BOOKMARKS);
fsmenu_copy_category(fsmenu_copy, fsmenu_copy, FS_CATEGORY_RECENT);
return fsmenu_copy;
}
int fsmenu_get_active_indices(struct FSMenu *fsmenu, enum FSMenuCategory category, const char *dir)
{
FSMenuEntry *fsm_iter = ED_fsmenu_get_category(fsmenu, category);
int i;
for (i = 0; fsm_iter; fsm_iter = fsm_iter->next, i++) {
if (BLI_path_cmp(dir, fsm_iter->path) == 0) {
return i;
}
}
return -1;
}
/* Thanks to some bookmarks sometimes being network drives that can have tens of seconds of delay
* before being defined as unreachable by the OS, we need to validate the bookmarks in an async
* job...
*/
static void fsmenu_bookmark_validate_job_startjob(void *fsmenuv,
short *stop,
short *do_update,
float *UNUSED(progress))
{
FSMenu *fsmenu = fsmenuv;
int categories[] = {
FS_CATEGORY_SYSTEM, FS_CATEGORY_SYSTEM_BOOKMARKS, FS_CATEGORY_BOOKMARKS, FS_CATEGORY_RECENT};
for (size_t i = ARRAY_SIZE(categories); i--;) {
FSMenuEntry *fsm_iter = ED_fsmenu_get_category(fsmenu, categories[i]);
for (; fsm_iter; fsm_iter = fsm_iter->next) {
if (*stop) {
return;
}
/* Note that we do not really need atomics primitives or thread locks here, since this only
* sets one short, which is assumed to be 'atomic'-enough for us here. */
fsmenu_entry_refresh_valid(fsm_iter);
*do_update = true;
}
}
}
static void fsmenu_bookmark_validate_job_update(void *fsmenuv)
{
FSMenu *fsmenu_job = fsmenuv;
int categories[] = {
FS_CATEGORY_SYSTEM, FS_CATEGORY_SYSTEM_BOOKMARKS, FS_CATEGORY_BOOKMARKS, FS_CATEGORY_RECENT};
for (size_t i = ARRAY_SIZE(categories); i--;) {
FSMenuEntry *fsm_iter_src = ED_fsmenu_get_category(fsmenu_job, categories[i]);
FSMenuEntry *fsm_iter_dst = ED_fsmenu_get_category(ED_fsmenu_get(), categories[i]);
for (; fsm_iter_dst != NULL; fsm_iter_dst = fsm_iter_dst->next) {
while (fsm_iter_src != NULL && !STREQ(fsm_iter_dst->path, fsm_iter_src->path)) {
fsm_iter_src = fsm_iter_src->next;
}
if (fsm_iter_src == NULL) {
return;
}
fsm_iter_dst->valid = fsm_iter_src->valid;
}
}
}
static void fsmenu_bookmark_validate_job_end(void *fsmenuv)
{
/* In case there would be some dangling update... */
fsmenu_bookmark_validate_job_update(fsmenuv);
}
static void fsmenu_bookmark_validate_job_free(void *fsmenuv)
{
FSMenu *fsmenu = fsmenuv;
fsmenu_free_ex(&fsmenu);
}
static void fsmenu_bookmark_validate_job_start(wmWindowManager *wm)
{
wmJob *wm_job;
FSMenu *fsmenu_job = fsmenu_copy(g_fsmenu);
/* setup job */
wm_job = WM_jobs_get(
wm, wm->winactive, wm, "Validating Bookmarks...", 0, WM_JOB_TYPE_FSMENU_BOOKMARK_VALIDATE);
WM_jobs_customdata_set(wm_job, fsmenu_job, fsmenu_bookmark_validate_job_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,
fsmenu_bookmark_validate_job_startjob,
NULL,
fsmenu_bookmark_validate_job_update,
fsmenu_bookmark_validate_job_end);
/* start the job */
WM_jobs_start(wm, wm_job);
}
static void fsmenu_bookmark_validate_job_stop(wmWindowManager *wm)
{
WM_jobs_kill_type(wm, wm, WM_JOB_TYPE_FSMENU_BOOKMARK_VALIDATE);
}
void fsmenu_refresh_bookmarks_status(wmWindowManager *wm, FSMenu *fsmenu)
{
BLI_assert(fsmenu == ED_fsmenu_get());
UNUSED_VARS_NDEBUG(fsmenu);
fsmenu_bookmark_validate_job_stop(wm);
fsmenu_bookmark_validate_job_start(wm);
}