Blender V5.0
fsmenu.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cstdio>
11#include <cstdlib>
12#include <cstring>
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_fileops.h"
17#include "BLI_path_utils.hh"
18#include "BLI_string.h"
19#include "BLI_string_utils.hh"
20#include "BLI_utildefines.h"
21
22#include "BLT_translation.hh"
23
24#include "BKE_appdir.hh"
25
26#include "ED_fileselect.hh"
27
28#include "UI_resources.hh"
29#include "WM_api.hh"
30#include "WM_types.hh"
31
32#include "fsmenu.h" /* include ourselves */
33
34/* FSMENU HANDLING */
35
43
44static FSMenu *g_fsmenu = nullptr;
45
47{
48 if (!g_fsmenu) {
49 g_fsmenu = MEM_callocN<FSMenu>(__func__);
50 }
51 return g_fsmenu;
52}
53
55{
56 FSMenuEntry *fsm_head = nullptr;
57
58 switch (category) {
60 fsm_head = fsmenu->fsmenu_system;
61 break;
63 fsm_head = fsmenu->fsmenu_system_bookmarks;
64 break;
66 fsm_head = fsmenu->fsmenu_bookmarks;
67 break;
69 fsm_head = fsmenu->fsmenu_recent;
70 break;
72 fsm_head = fsmenu->fsmenu_other;
73 break;
74 }
75 return fsm_head;
76}
77
78void ED_fsmenu_set_category(FSMenu *fsmenu, FSMenuCategory category, FSMenuEntry *fsm_head)
79{
80 switch (category) {
82 fsmenu->fsmenu_system = fsm_head;
83 break;
85 fsmenu->fsmenu_system_bookmarks = fsm_head;
86 break;
88 fsmenu->fsmenu_bookmarks = fsm_head;
89 break;
91 fsmenu->fsmenu_recent = fsm_head;
92 break;
94 fsmenu->fsmenu_other = fsm_head;
95 break;
96 }
97}
98
100{
101 FSMenuEntry *fsm_iter;
102 int count = 0;
103
104 for (fsm_iter = ED_fsmenu_get_category(fsmenu, category); fsm_iter; fsm_iter = fsm_iter->next) {
105 count++;
106 }
107
108 return count;
109}
110
112{
113 FSMenuEntry *fsm_iter;
114
115 for (fsm_iter = ED_fsmenu_get_category(fsmenu, category); fsm_iter && idx;
116 fsm_iter = fsm_iter->next)
117 {
118 idx--;
119 }
120
121 return fsm_iter;
122}
123
125{
126 return fsentry->path;
127}
128
129void ED_fsmenu_entry_set_path(FSMenuEntry *fsentry, const char *path)
130{
131 if ((!fsentry->path || !path || !STREQ(path, fsentry->path)) && (fsentry->path != path)) {
132 char tmp_name[FILE_MAXFILE];
133
134 MEM_SAFE_FREE(fsentry->path);
135
136 fsentry->path = (path && path[0]) ? BLI_strdup(path) : nullptr;
137
138 const std::optional<std::string> user_config_dir = BKE_appdir_folder_id_create(
139 BLENDER_USER_CONFIG, nullptr);
140
141 if (user_config_dir.has_value()) {
142 BLI_path_join(tmp_name, sizeof(tmp_name), user_config_dir->c_str(), BLENDER_BOOKMARK_FILE);
143 fsmenu_write_file(ED_fsmenu_get(), tmp_name);
144 }
145 }
146}
147
149{
150 return (fsentry->icon) ? fsentry->icon : ICON_FILE_FOLDER;
151}
152
153void ED_fsmenu_entry_set_icon(FSMenuEntry *fsentry, const int icon)
154{
155 fsentry->icon = icon;
156}
157
158static void fsmenu_entry_generate_name(FSMenuEntry *fsentry, char *name, size_t name_size)
159{
160 int offset = 0;
161 int len = name_size;
162
163 if (BLI_path_name_at_index(fsentry->path, -1, &offset, &len)) {
164 /* use as size */
165 len += 1;
166 }
167
168 BLI_strncpy(name, &fsentry->path[offset], std::min(size_t(len), name_size));
169 if (!name[0]) {
170 name[0] = '/';
171 name[1] = '\0';
172 }
173}
174
176{
177 if (fsentry->name[0]) {
178 return fsentry->name;
179 }
180
181 /* Here we abuse fsm_iter->name, keeping first char nullptr. */
182 char *name = fsentry->name + 1;
183 size_t name_size = sizeof(fsentry->name) - 1;
184
185 fsmenu_entry_generate_name(fsentry, name, name_size);
186 return name;
187}
188
189void ED_fsmenu_entry_set_name(FSMenuEntry *fsentry, const char *name)
190{
191 if (!STREQ(name, fsentry->name)) {
192 char tmp_name[FILE_MAXFILE];
193 size_t tmp_name_size = sizeof(tmp_name);
194
195 fsmenu_entry_generate_name(fsentry, tmp_name, tmp_name_size);
196 if (!name[0] || STREQ(tmp_name, name)) {
197 /* reset name to default behavior. */
198 fsentry->name[0] = '\0';
199 }
200 else {
201 STRNCPY(fsentry->name, name);
202 }
203
204 const std::optional<std::string> user_config_dir = BKE_appdir_folder_id_create(
205 BLENDER_USER_CONFIG, nullptr);
206
207 if (user_config_dir.has_value()) {
208 BLI_path_join(tmp_name, sizeof(tmp_name), user_config_dir->c_str(), BLENDER_BOOKMARK_FILE);
209 fsmenu_write_file(ED_fsmenu_get(), tmp_name);
210 }
211 }
212}
213
214short fsmenu_can_save(FSMenu *fsmenu, FSMenuCategory category, int idx)
215{
216 FSMenuEntry *fsm_iter;
217
218 for (fsm_iter = ED_fsmenu_get_category(fsmenu, category); fsm_iter && idx;
219 fsm_iter = fsm_iter->next)
220 {
221 idx--;
222 }
223
224 return fsm_iter ? fsm_iter->save : 0;
225}
226
228 FSMenuCategory category,
229 const char *path,
230 const char *name,
231 int icon,
233{
234 /* NOTE: this function must *not* perform file-system checks on `path`,
235 * although the path may be inspected as a literal string.
236 *
237 * This is important because accessing the file system may reference drives
238 * which are offline, network drives requiring an internet connection,
239 * external drives that aren't plugged in, etc.
240 * Delays in any file-system checks can causes hanging on startup.
241 * See !138218 for details. */
242
243 const uint path_len = strlen(path);
244 BLI_assert(path_len > 0);
245 if (path_len == 0) {
246 return;
247 }
249 const bool has_trailing_slash = (path[path_len - 1] == SEP);
250 FSMenuEntry *fsm_prev;
251 FSMenuEntry *fsm_iter;
252 FSMenuEntry *fsm_head;
253
254 fsm_head = ED_fsmenu_get_category(fsmenu, category);
255 fsm_prev = fsm_head; /* this is odd and not really correct? */
256
257 for (fsm_iter = fsm_head; fsm_iter; fsm_prev = fsm_iter, fsm_iter = fsm_iter->next) {
258 if (fsm_iter->path) {
259 /* Compare, with/without the trailing slash in 'path'. */
260 const int cmp_ret = BLI_path_ncmp(path, fsm_iter->path, path_len);
261 if (cmp_ret == 0 && STREQ(fsm_iter->path + path_len, has_trailing_slash ? "" : SEP_STR)) {
262 if (flag & FS_INSERT_FIRST) {
263 if (fsm_iter != fsm_head) {
264 fsm_prev->next = fsm_iter->next;
265 fsm_iter->next = fsm_head;
266 ED_fsmenu_set_category(fsmenu, category, fsm_iter);
267 }
268 }
269 return;
270 }
271 if ((flag & FS_INSERT_SORTED) && cmp_ret < 0) {
272 break;
273 }
274 }
275 else {
276 /* if we're bookmarking this, file should come
277 * before the last separator, only automatically added
278 * current dir go after the last separator. */
279 if (flag & FS_INSERT_SAVE) {
280 break;
281 }
282 }
283 }
284
285 fsm_iter = MEM_mallocN<FSMenuEntry>("fsme");
286 fsm_iter->path = has_trailing_slash ? BLI_strdup(path) : BLI_string_joinN(path, SEP_STR);
287 fsm_iter->save = (flag & FS_INSERT_SAVE) != 0;
288
289 /* If entry is also in another list, use that icon and maybe name. */
290 /* On macOS we get icons and names for System Bookmarks from the FS_CATEGORY_OTHER list. */
292
293 const FSMenuCategory cats[] = {
298 };
299 int i = ARRAY_SIZE(cats);
300 if (category == FS_CATEGORY_BOOKMARKS) {
301 i--;
302 }
303
304 while (i--) {
305 FSMenuEntry *tfsm = ED_fsmenu_get_category(fsmenu, cats[i]);
306 for (; tfsm; tfsm = tfsm->next) {
307 if (STREQ(tfsm->path, fsm_iter->path)) {
308 icon = tfsm->icon;
309 if (tfsm->name[0] && (!name || !name[0])) {
310 name = DATA_(tfsm->name);
311 }
312 break;
313 }
314 }
315 if (tfsm) {
316 break;
317 }
318 }
319 }
320
321 if (name && name[0]) {
322 STRNCPY(fsm_iter->name, name);
323 }
324 else {
325 fsm_iter->name[0] = '\0';
326 }
327
328 ED_fsmenu_entry_set_icon(fsm_iter, icon);
329
330 if (fsm_prev) {
331 if (flag & FS_INSERT_FIRST) {
332 fsm_iter->next = fsm_head;
333 ED_fsmenu_set_category(fsmenu, category, fsm_iter);
334 }
335 else {
336 fsm_iter->next = fsm_prev->next;
337 fsm_prev->next = fsm_iter;
338 }
339 }
340 else {
341 fsm_iter->next = fsm_head;
342 ED_fsmenu_set_category(fsmenu, category, fsm_iter);
343 }
344}
345
346void fsmenu_remove_entry(FSMenu *fsmenu, FSMenuCategory category, int idx)
347{
348 FSMenuEntry *fsm_prev = nullptr;
349 FSMenuEntry *fsm_iter;
350 FSMenuEntry *fsm_head;
351
352 fsm_head = ED_fsmenu_get_category(fsmenu, category);
353
354 for (fsm_iter = fsm_head; fsm_iter && idx; fsm_prev = fsm_iter, fsm_iter = fsm_iter->next) {
355 idx--;
356 }
357
358 if (fsm_iter) {
359 /* you should only be able to remove entries that were
360 * not added by default, like windows drives.
361 * also separators (where path == nullptr) shouldn't be removed */
362 if (fsm_iter->save && fsm_iter->path) {
363
364 /* remove fsme from list */
365 if (fsm_prev) {
366 fsm_prev->next = fsm_iter->next;
367 }
368 else {
369 fsm_head = fsm_iter->next;
370 ED_fsmenu_set_category(fsmenu, category, fsm_head);
371 }
372 /* free entry */
373 MEM_freeN(fsm_iter->path);
374 MEM_freeN(fsm_iter);
375 }
376 }
377}
378
379bool fsmenu_write_file(FSMenu *fsmenu, const char *filepath)
380{
381 FSMenuEntry *fsm_iter = nullptr;
382 char fsm_name[FILE_MAX];
383 int nwritten = 0;
384
385 FILE *fp = BLI_fopen(filepath, "w");
386 if (!fp) {
387 return false;
388 }
389
390 bool has_error = false;
391 has_error |= (fprintf(fp, "[Bookmarks]\n") < 0);
392 for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS); fsm_iter;
393 fsm_iter = fsm_iter->next)
394 {
395 if (fsm_iter->path && fsm_iter->save) {
396 fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name));
397 if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) {
398 has_error |= (fprintf(fp, "!%s\n", fsm_iter->name) < 0);
399 }
400 has_error |= (fprintf(fp, "%s\n", fsm_iter->path) < 0);
401 }
402 }
403 has_error = (fprintf(fp, "[Recent]\n") < 0);
404 for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_RECENT);
405 fsm_iter && (nwritten < FSMENU_RECENT_MAX);
406 fsm_iter = fsm_iter->next, nwritten++)
407 {
408 if (fsm_iter->path && fsm_iter->save) {
409 fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name));
410 if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) {
411 has_error |= (fprintf(fp, "!%s\n", fsm_iter->name) < 0);
412 }
413 has_error |= (fprintf(fp, "%s\n", fsm_iter->path) < 0);
414 }
415 }
416 fclose(fp);
417
418 return !has_error;
419}
420
421void fsmenu_read_bookmarks(FSMenu *fsmenu, const char *filepath)
422{
423 char line[FILE_MAXDIR];
424 char name[FILE_MAXFILE];
426 FILE *fp;
427
428 fp = BLI_fopen(filepath, "r");
429 if (!fp) {
430 return;
431 }
432
433 name[0] = '\0';
434
435 while (fgets(line, sizeof(line), fp) != nullptr) { /* read a line */
436 if (STRPREFIX(line, "[Bookmarks]")) {
437 category = FS_CATEGORY_BOOKMARKS;
438 }
439 else if (STRPREFIX(line, "[Recent]")) {
440 category = FS_CATEGORY_RECENT;
441 }
442 else if (line[0] == '!') {
443 int len = strlen(line);
444 if (len > 0) {
445 if (line[len - 1] == '\n') {
446 line[len - 1] = '\0';
447 }
448 STRNCPY(name, line + 1);
449 }
450 }
451 else {
452 int len = strlen(line);
453 if (len > 0) {
454 if (line[len - 1] == '\n') {
455 line[len - 1] = '\0';
456 }
457 /* Don't check if the path exists before adding because it can be slow on network drives,
458 * having a bookmark from a drive that's ejected or so isn't all that bad.
459 * See !138218 for details. */
460 fsmenu_insert_entry(fsmenu, category, line, name, ICON_FILE_FOLDER, FS_INSERT_SAVE);
461 }
462 /* always reset name. */
463 name[0] = '\0';
464 }
465 }
466 fclose(fp);
467}
468
469static void fsmenu_free_category(FSMenu *fsmenu, FSMenuCategory category)
470{
471 FSMenuEntry *fsm_iter = ED_fsmenu_get_category(fsmenu, category);
472
473 while (fsm_iter) {
474 FSMenuEntry *fsm_next = fsm_iter->next;
475
476 if (fsm_iter->path) {
477 MEM_freeN(fsm_iter->path);
478 }
479 MEM_freeN(fsm_iter);
480
481 fsm_iter = fsm_next;
482 }
483}
484
486{
489
492
493 /* Add all entries to system category */
494 fsmenu_read_system(fsmenu, true);
495}
496
497static void fsmenu_free_ex(FSMenu **fsmenu)
498{
499 if (*fsmenu != nullptr) {
505 MEM_freeN(*fsmenu);
506 }
507
508 *fsmenu = nullptr;
509}
510
512{
514}
515
516int fsmenu_get_active_indices(FSMenu *fsmenu, enum FSMenuCategory category, const char *dir)
517{
518 FSMenuEntry *fsm_iter = ED_fsmenu_get_category(fsmenu, category);
519 int i;
520
521 for (i = 0; fsm_iter; fsm_iter = fsm_iter->next, i++) {
522 if (BLI_path_cmp(dir, fsm_iter->path) == 0) {
523 return i;
524 }
525 }
526
527 return -1;
528}
#define BLENDER_BOOKMARK_FILE
@ BLENDER_USER_CONFIG
std::optional< std::string > BKE_appdir_folder_id_create(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT
Definition appdir.cc:781
#define BLI_assert(a)
Definition BLI_assert.h:46
File and directory operations.
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define BLI_path_ncmp
#define FILE_MAXFILE
#define FILE_MAX
#define BLI_path_join(...)
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define SEP
#define FILE_MAXDIR
bool BLI_path_name_at_index(const char *__restrict path, int index, int *__restrict r_offset, int *__restrict r_len) ATTR_NONNULL(1
#define BLI_path_cmp
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define BLI_string_joinN(...)
unsigned int uint
#define STRPREFIX(a, b)
#define ARRAY_SIZE(arr)
#define ELEM(...)
#define STREQ(a, b)
#define DATA_(msgid)
FSMenuCategory
@ FS_CATEGORY_RECENT
@ FS_CATEGORY_BOOKMARKS
@ FS_CATEGORY_SYSTEM_BOOKMARKS
@ FS_CATEGORY_OTHER
@ FS_CATEGORY_SYSTEM
FSMenuInsert
@ FS_INSERT_SAVE
@ FS_INSERT_FIRST
@ FS_INSERT_SORTED
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
FSMenuEntry * ED_fsmenu_get_entry(FSMenu *fsmenu, FSMenuCategory category, int idx)
Definition fsmenu.cc:111
int ED_fsmenu_get_nentries(FSMenu *fsmenu, FSMenuCategory category)
Definition fsmenu.cc:99
FSMenu * ED_fsmenu_get()
Definition fsmenu.cc:46
static void fsmenu_free_ex(FSMenu **fsmenu)
Definition fsmenu.cc:497
short fsmenu_can_save(FSMenu *fsmenu, FSMenuCategory category, int idx)
Definition fsmenu.cc:214
void ED_fsmenu_entry_set_name(FSMenuEntry *fsentry, const char *name)
Definition fsmenu.cc:189
char * ED_fsmenu_entry_get_name(FSMenuEntry *fsentry)
Definition fsmenu.cc:175
int fsmenu_get_active_indices(FSMenu *fsmenu, enum FSMenuCategory category, const char *dir)
Definition fsmenu.cc:516
void ED_fsmenu_set_category(FSMenu *fsmenu, FSMenuCategory category, FSMenuEntry *fsm_head)
Definition fsmenu.cc:78
char * ED_fsmenu_entry_get_path(FSMenuEntry *fsentry)
Definition fsmenu.cc:124
int ED_fsmenu_entry_get_icon(FSMenuEntry *fsentry)
Definition fsmenu.cc:148
static FSMenu * g_fsmenu
Definition fsmenu.cc:44
static void fsmenu_free_category(FSMenu *fsmenu, FSMenuCategory category)
Definition fsmenu.cc:469
void fsmenu_remove_entry(FSMenu *fsmenu, FSMenuCategory category, int idx)
Definition fsmenu.cc:346
FSMenuEntry * ED_fsmenu_get_category(FSMenu *fsmenu, FSMenuCategory category)
Definition fsmenu.cc:54
void fsmenu_read_bookmarks(FSMenu *fsmenu, const char *filepath)
Definition fsmenu.cc:421
void ED_fsmenu_entry_set_path(FSMenuEntry *fsentry, const char *path)
Definition fsmenu.cc:129
void fsmenu_refresh_system_category(FSMenu *fsmenu)
Definition fsmenu.cc:485
void fsmenu_free()
Definition fsmenu.cc:511
static void fsmenu_entry_generate_name(FSMenuEntry *fsentry, char *name, size_t name_size)
Definition fsmenu.cc:158
void ED_fsmenu_entry_set_icon(FSMenuEntry *fsentry, const int icon)
Definition fsmenu.cc:153
void fsmenu_insert_entry(FSMenu *fsmenu, FSMenuCategory category, const char *path, const char *name, int icon, FSMenuInsert flag)
Definition fsmenu.cc:227
bool fsmenu_write_file(FSMenu *fsmenu, const char *filepath)
Definition fsmenu.cc:379
#define FSMENU_RECENT_MAX
Definition fsmenu.h:14
void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
int count
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
const char * name
char name[256]
FSMenuEntry * next
FSMenuEntry * fsmenu_bookmarks
Definition fsmenu.cc:39
FSMenuEntry * fsmenu_other
Definition fsmenu.cc:41
FSMenuEntry * fsmenu_system_bookmarks
Definition fsmenu.cc:38
FSMenuEntry * fsmenu_recent
Definition fsmenu.cc:40
FSMenuEntry * fsmenu_system
Definition fsmenu.cc:37
i
Definition text_draw.cc:230
#define SEP_STR
Definition unit.cc:39
uint len
uint8_t flag
Definition wm_window.cc:145