692 lines
19 KiB
C++
692 lines
19 KiB
C++
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup spfile
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_string_utils.hh"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BLT_translation.hh"
|
|
|
|
#include "BKE_appdir.hh"
|
|
|
|
#include "ED_fileselect.hh"
|
|
|
|
#include "UI_resources.hh"
|
|
#include "WM_api.hh"
|
|
#include "WM_types.hh"
|
|
|
|
#include "fsmenu.h" /* include ourselves */
|
|
|
|
/* FSMENU HANDLING */
|
|
|
|
struct FSMenu {
|
|
FSMenuEntry *fsmenu_system;
|
|
FSMenuEntry *fsmenu_system_bookmarks;
|
|
FSMenuEntry *fsmenu_bookmarks;
|
|
FSMenuEntry *fsmenu_recent;
|
|
FSMenuEntry *fsmenu_other;
|
|
};
|
|
|
|
static FSMenu *g_fsmenu = nullptr;
|
|
|
|
FSMenu *ED_fsmenu_get()
|
|
{
|
|
if (!g_fsmenu) {
|
|
g_fsmenu = MEM_cnew<FSMenu>(__func__);
|
|
}
|
|
return g_fsmenu;
|
|
}
|
|
|
|
FSMenuEntry *ED_fsmenu_get_category(FSMenu *fsmenu, FSMenuCategory category)
|
|
{
|
|
FSMenuEntry *fsm_head = nullptr;
|
|
|
|
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;
|
|
case FS_CATEGORY_OTHER:
|
|
fsm_head = fsmenu->fsmenu_other;
|
|
break;
|
|
}
|
|
return fsm_head;
|
|
}
|
|
|
|
void ED_fsmenu_set_category(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;
|
|
case FS_CATEGORY_OTHER:
|
|
fsmenu->fsmenu_other = fsm_head;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int ED_fsmenu_get_nentries(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(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;
|
|
}
|
|
|
|
char *ED_fsmenu_entry_get_path(FSMenuEntry *fsentry)
|
|
{
|
|
return fsentry->path;
|
|
}
|
|
|
|
void ED_fsmenu_entry_set_path(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) : nullptr;
|
|
|
|
const std::optional<std::string> user_config_dir = BKE_appdir_folder_id_create(
|
|
BLENDER_USER_CONFIG, nullptr);
|
|
|
|
if (user_config_dir.has_value()) {
|
|
BLI_path_join(tmp_name, sizeof(tmp_name), user_config_dir->c_str(), BLENDER_BOOKMARK_FILE);
|
|
fsmenu_write_file(ED_fsmenu_get(), tmp_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
int ED_fsmenu_entry_get_icon(FSMenuEntry *fsentry)
|
|
{
|
|
return (fsentry->icon) ? fsentry->icon : ICON_FILE_FOLDER;
|
|
}
|
|
|
|
void ED_fsmenu_entry_set_icon(FSMenuEntry *fsentry, const int icon)
|
|
{
|
|
fsentry->icon = icon;
|
|
}
|
|
|
|
static void fsmenu_entry_generate_name(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], std::min(size_t(len), name_size));
|
|
if (!name[0]) {
|
|
name[0] = '/';
|
|
name[1] = '\0';
|
|
}
|
|
}
|
|
|
|
char *ED_fsmenu_entry_get_name(FSMenuEntry *fsentry)
|
|
{
|
|
if (fsentry->name[0]) {
|
|
return fsentry->name;
|
|
}
|
|
|
|
/* Here we abuse fsm_iter->name, keeping first char nullptr. */
|
|
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(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 {
|
|
STRNCPY(fsentry->name, name);
|
|
}
|
|
|
|
const std::optional<std::string> user_config_dir = BKE_appdir_folder_id_create(
|
|
BLENDER_USER_CONFIG, nullptr);
|
|
|
|
if (user_config_dir.has_value()) {
|
|
BLI_path_join(tmp_name, sizeof(tmp_name), user_config_dir->c_str(), BLENDER_BOOKMARK_FILE);
|
|
fsmenu_write_file(ED_fsmenu_get(), tmp_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void fsmenu_entry_refresh_valid(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 #43684. */
|
|
const char *exceptions[] = {"A:\\", "B:\\", nullptr};
|
|
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(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(FSMenu *fsmenu,
|
|
FSMenuCategory category,
|
|
const char *path,
|
|
const char *name,
|
|
int icon,
|
|
FSMenuInsert flag)
|
|
{
|
|
const uint path_len = strlen(path);
|
|
BLI_assert(path_len > 0);
|
|
if (path_len == 0) {
|
|
return;
|
|
}
|
|
const bool has_trailing_slash = (path[path_len - 1] == SEP);
|
|
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) {
|
|
/* Compare, with/without the trailing slash in 'path'. */
|
|
const int cmp_ret = BLI_path_ncmp(path, fsm_iter->path, path_len);
|
|
if (cmp_ret == 0 && STREQ(fsm_iter->path + path_len, has_trailing_slash ? "" : SEP_STR)) {
|
|
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;
|
|
}
|
|
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 separator. */
|
|
if (flag & FS_INSERT_SAVE) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
fsm_iter = static_cast<FSMenuEntry *>(MEM_mallocN(sizeof(*fsm_iter), "fsme"));
|
|
fsm_iter->path = has_trailing_slash ? BLI_strdup(path) : BLI_string_joinN(path, SEP_STR);
|
|
fsm_iter->save = (flag & FS_INSERT_SAVE) != 0;
|
|
|
|
/* If entry is also in another list, use that icon and maybe name. */
|
|
/* On macOS we get icons and names for System Bookmarks from the FS_CATEGORY_OTHER list. */
|
|
if (ELEM(category, FS_CATEGORY_SYSTEM_BOOKMARKS, FS_CATEGORY_BOOKMARKS, FS_CATEGORY_RECENT)) {
|
|
|
|
const FSMenuCategory cats[] = {
|
|
FS_CATEGORY_OTHER,
|
|
FS_CATEGORY_SYSTEM,
|
|
FS_CATEGORY_SYSTEM_BOOKMARKS,
|
|
FS_CATEGORY_BOOKMARKS,
|
|
};
|
|
int i = ARRAY_SIZE(cats);
|
|
if (category == FS_CATEGORY_BOOKMARKS) {
|
|
i--;
|
|
}
|
|
|
|
while (i--) {
|
|
FSMenuEntry *tfsm = ED_fsmenu_get_category(fsmenu, cats[i]);
|
|
for (; tfsm; tfsm = tfsm->next) {
|
|
if (STREQ(tfsm->path, fsm_iter->path)) {
|
|
icon = tfsm->icon;
|
|
if (tfsm->name[0] && (!name || !name[0])) {
|
|
name = DATA_(tfsm->name);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (tfsm) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (name && name[0]) {
|
|
STRNCPY(fsm_iter->name, name);
|
|
}
|
|
else {
|
|
fsm_iter->name[0] = '\0';
|
|
}
|
|
|
|
ED_fsmenu_entry_set_icon(fsm_iter, icon);
|
|
|
|
if (flag & FS_INSERT_NO_VALIDATE) {
|
|
fsm_iter->valid = true;
|
|
}
|
|
else {
|
|
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(FSMenu *fsmenu, FSMenuCategory category, int idx)
|
|
{
|
|
FSMenuEntry *fsm_prev = nullptr;
|
|
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 == nullptr) 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool fsmenu_write_file(FSMenu *fsmenu, const char *filepath)
|
|
{
|
|
FSMenuEntry *fsm_iter = nullptr;
|
|
char fsm_name[FILE_MAX];
|
|
int nwritten = 0;
|
|
|
|
FILE *fp = BLI_fopen(filepath, "w");
|
|
if (!fp) {
|
|
return false;
|
|
}
|
|
|
|
bool has_error = false;
|
|
has_error |= (fprintf(fp, "[Bookmarks]\n") < 0);
|
|
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)) {
|
|
has_error |= (fprintf(fp, "!%s\n", fsm_iter->name) < 0);
|
|
}
|
|
has_error |= (fprintf(fp, "%s\n", fsm_iter->path) < 0);
|
|
}
|
|
}
|
|
has_error = (fprintf(fp, "[Recent]\n") < 0);
|
|
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)) {
|
|
has_error |= (fprintf(fp, "!%s\n", fsm_iter->name) < 0);
|
|
}
|
|
has_error |= (fprintf(fp, "%s\n", fsm_iter->path) < 0);
|
|
}
|
|
}
|
|
fclose(fp);
|
|
|
|
return !has_error;
|
|
}
|
|
|
|
void fsmenu_read_bookmarks(FSMenu *fsmenu, const char *filepath)
|
|
{
|
|
char line[FILE_MAXDIR];
|
|
char name[FILE_MAXFILE];
|
|
FSMenuCategory category = FS_CATEGORY_BOOKMARKS;
|
|
FILE *fp;
|
|
|
|
fp = BLI_fopen(filepath, "r");
|
|
if (!fp) {
|
|
return;
|
|
}
|
|
|
|
name[0] = '\0';
|
|
|
|
while (fgets(line, sizeof(line), fp) != nullptr) { /* read a line */
|
|
if (STRPREFIX(line, "[Bookmarks]")) {
|
|
category = FS_CATEGORY_BOOKMARKS;
|
|
}
|
|
else if (STRPREFIX(line, "[Recent]")) {
|
|
category = FS_CATEGORY_RECENT;
|
|
}
|
|
else if (line[0] == '!') {
|
|
int len = strlen(line);
|
|
if (len > 0) {
|
|
if (line[len - 1] == '\n') {
|
|
line[len - 1] = '\0';
|
|
}
|
|
STRNCPY(name, line + 1);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
static void fsmenu_free_category(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(FSMenu *fsmenu)
|
|
{
|
|
fsmenu_free_category(fsmenu, FS_CATEGORY_SYSTEM);
|
|
ED_fsmenu_set_category(fsmenu, FS_CATEGORY_SYSTEM, nullptr);
|
|
|
|
fsmenu_free_category(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS);
|
|
ED_fsmenu_set_category(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, nullptr);
|
|
|
|
/* Add all entries to system category */
|
|
fsmenu_read_system(fsmenu, true);
|
|
}
|
|
|
|
static void fsmenu_free_ex(FSMenu **fsmenu)
|
|
{
|
|
if (*fsmenu != nullptr) {
|
|
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);
|
|
fsmenu_free_category(*fsmenu, FS_CATEGORY_OTHER);
|
|
MEM_freeN(*fsmenu);
|
|
}
|
|
|
|
*fsmenu = nullptr;
|
|
}
|
|
|
|
void fsmenu_free()
|
|
{
|
|
fsmenu_free_ex(&g_fsmenu);
|
|
}
|
|
|
|
static void fsmenu_copy_category(FSMenu *fsmenu_dst,
|
|
FSMenu *fsmenu_src,
|
|
const FSMenuCategory category)
|
|
{
|
|
FSMenuEntry *fsm_dst_prev = nullptr, *fsm_dst_head = nullptr;
|
|
FSMenuEntry *fsm_src_iter = ED_fsmenu_get_category(fsmenu_src, category);
|
|
|
|
for (; fsm_src_iter != nullptr; fsm_src_iter = fsm_src_iter->next) {
|
|
FSMenuEntry *fsm_dst = static_cast<FSMenuEntry *>(MEM_dupallocN(fsm_src_iter));
|
|
if (fsm_dst->path != nullptr) {
|
|
fsm_dst->path = static_cast<char *>(MEM_dupallocN(fsm_dst->path));
|
|
}
|
|
|
|
if (fsm_dst_prev != nullptr) {
|
|
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 = static_cast<FSMenu *>(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);
|
|
fsmenu_copy_category(fsmenu_copy, fsmenu_copy, FS_CATEGORY_OTHER);
|
|
|
|
return fsmenu_copy;
|
|
}
|
|
|
|
int fsmenu_get_active_indices(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
|
|
* asynchronous job.
|
|
*/
|
|
static void fsmenu_bookmark_validate_job_startjob(void *fsmenuv, wmJobWorkerStatus *worker_status)
|
|
{
|
|
FSMenu *fsmenu = static_cast<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, FSMenuCategory(categories[i]));
|
|
for (; fsm_iter; fsm_iter = fsm_iter->next) {
|
|
if (worker_status->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);
|
|
worker_status->do_update = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void fsmenu_bookmark_validate_job_update(void *fsmenuv)
|
|
{
|
|
FSMenu *fsmenu_job = static_cast<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_src = ED_fsmenu_get_category(fsmenu_job, FSMenuCategory(categories[i]));
|
|
FSMenuEntry *fsm_iter_dst = ED_fsmenu_get_category(ED_fsmenu_get(),
|
|
FSMenuCategory(categories[i]));
|
|
for (; fsm_iter_dst != nullptr; fsm_iter_dst = fsm_iter_dst->next) {
|
|
while (fsm_iter_src != nullptr && !STREQ(fsm_iter_dst->path, fsm_iter_src->path)) {
|
|
fsm_iter_src = fsm_iter_src->next;
|
|
}
|
|
if (fsm_iter_src == nullptr) {
|
|
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 = static_cast<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...",
|
|
eWM_JobFlag(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,
|
|
nullptr,
|
|
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);
|
|
}
|