UI: Linux platform support for 'user-dirs.dirs'

Originally D6826 by @a.monti with parsing rewritten.
This commit is contained in:
Campbell Barton
2020-02-26 18:07:05 +11:00
parent 5f83495e4c
commit 48816d462e

View File

@@ -30,6 +30,7 @@
#include "BLI_utildefines.h"
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
#include "BLT_translation.h"
@@ -102,6 +103,111 @@ struct FSMenuEntry *ED_fsmenu_get_category(struct FSMenu *fsmenu, FSMenuCategory
return fsm_head;
}
/* -------------------------------------------------------------------- */
/** \name XDG User Directory Support (Unix)
*
* Generic Unix, Use XDG when available, otherwise fallback to the home directory.
* \{ */
/**
* Look for `user-dirs.dirs`, where localized or custom user folders are defined,
* and store their paths in a GHash.
*/
static GHash *fsmenu_xdg_user_dirs_parse(const char *home)
{
size_t data_len;
char *data;
/* Check if the config file exists. */
{
char filepath[FILE_MAX];
const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
if (xdg_config_home != NULL) {
BLI_path_join(filepath, sizeof(filepath), xdg_config_home, "user-dirs.dirs", NULL);
}
else {
BLI_path_join(filepath, sizeof(filepath), home, ".config", "user-dirs.dirs", NULL);
}
data = BLI_file_read_text_as_mem_with_newline_as_nil(filepath, true, 1, &data_len);
if (data == NULL) {
return NULL;
}
}
/* By default there are 8 paths. */
GHash *xdg_map = BLI_ghash_str_new_ex(__func__, 8);
char *data_end = data + data_len;
uint l_len;
for (char *l = data; l != data_end; l += l_len + 1) {
l_len = strlen(l);
/* Avoid inserting invalid values. */
if (STRPREFIX(l, "XDG_")) {
char *l_value = strchr(l, '=');
if (l_value != NULL) {
*l_value = '\0';
l_value++;
if (*l_value == '"' && l[l_len - 1] == '"') {
l_value++;
l[l_len - 1] = '\0';
char l_value_expanded[FILE_MAX];
char *l_value_final = l_value;
/* This is currently the only variable used.
* Based on the 'user-dirs.dirs' man page,
* there is no need to resolve arbitrary environment variables. */
if (STRPREFIX(l_value, "$HOME" SEP_STR)) {
BLI_path_join(l_value_expanded, sizeof(l_value_expanded), home, l_value + 6, NULL);
l_value_final = l_value_expanded;
}
BLI_ghash_insert(xdg_map, BLI_strdup(l), BLI_strdup(l_value_final));
printf("'%s' = '%s'\n", l, l_value_final);
}
}
}
}
MEM_freeN(data);
return xdg_map;
}
static void fsmenu_xdg_user_dirs_free(GHash *xdg_map)
{
if (xdg_map != NULL) {
BLI_ghash_free(xdg_map, MEM_freeN, MEM_freeN);
}
}
/**
* Add fsmenu entry for system folders on linux.
* - Check if a path is stored in the GHash generated from user-dirs.dirs
* - If not, check for a default path in $HOME
*
* \param key: Use `user-dirs.dirs` format "XDG_EXAMPLE_DIR"
* \param default_path: Directory name to check in $HOME, also used for the menu entry name.
*/
static void fsmenu_xdg_insert_entry(GHash *xdg_map,
struct FSMenu *fsmenu,
const char *key,
const char *default_path,
int icon,
const char *home)
{
char dirpath[FILE_MAXDIR];
char *xdg_path = xdg_map ? BLI_ghash_lookup(xdg_map, key) : NULL;
if (xdg_path == NULL) {
BLI_path_join(dirpath, sizeof(dirpath), home, default_path, NULL);
}
else {
BLI_snprintf(dirpath, sizeof(dirpath), xdg_path);
}
fsmenu_insert_entry(
fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, dirpath, IFACE_(default_path), icon, FS_INSERT_LAST);
}
/** \} */
void ED_fsmenu_set_category(struct FSMenu *fsmenu, FSMenuCategory category, FSMenuEntry *fsm_head)
{
switch (category) {
@@ -710,68 +816,27 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
}
/* Follow the XDG spec, check if these are available. */
GHash *xdg_map = fsmenu_xdg_user_dirs_parse(home);
/* TODO: parse "$XDG_CONFIG_HOME/user-dirs.dirs" for localized paths. */
struct {
const char *key;
const char *default_path;
BIFIconID icon;
} xdg_items[] = {
{"XDG_DESKTOP_DIR", "Desktop", ICON_DESKTOP},
{"XDG_DOCUMENTS_DIR", "Documents", ICON_DOCUMENTS},
{"XDG_DOWNLOAD_DIR", "Downloads", ICON_IMPORT},
{"XDG_VIDEOS_DIR", "Videos", ICON_FILE_MOVIE},
{"XDG_PICTURES_DIR", "Pictures", ICON_FILE_IMAGE},
{"XDG_MUSIC_DIR", "Music", ICON_FILE_SOUND},
};
BLI_snprintf(line, sizeof(line), "%s/Desktop/", home);
if (BLI_exists(line)) {
fsmenu_insert_entry(fsmenu,
FS_CATEGORY_SYSTEM_BOOKMARKS,
line,
IFACE_("Desktop"),
ICON_DESKTOP,
FS_INSERT_LAST);
for (int i = 0; i < ARRAY_SIZE(xdg_items); i++) {
fsmenu_xdg_insert_entry(
xdg_map, fsmenu, xdg_items[i].key, xdg_items[i].default_path, xdg_items[i].icon, home);
}
BLI_snprintf(line, sizeof(line), "%s/Documents/", home);
if (BLI_exists(line)) {
fsmenu_insert_entry(fsmenu,
FS_CATEGORY_SYSTEM_BOOKMARKS,
line,
IFACE_("Documents"),
ICON_DOCUMENTS,
FS_INSERT_LAST);
}
BLI_snprintf(line, sizeof(line), "%s/Downloads/", home);
if (BLI_exists(line)) {
fsmenu_insert_entry(fsmenu,
FS_CATEGORY_SYSTEM_BOOKMARKS,
line,
IFACE_("Downloads"),
ICON_IMPORT,
FS_INSERT_LAST);
}
BLI_snprintf(line, sizeof(line), "%s/Videos/", home);
if (BLI_exists(line)) {
fsmenu_insert_entry(fsmenu,
FS_CATEGORY_SYSTEM_BOOKMARKS,
line,
IFACE_("Videos"),
ICON_FILE_MOVIE,
FS_INSERT_LAST);
}
BLI_snprintf(line, sizeof(line), "%s/Pictures/", home);
if (BLI_exists(line)) {
fsmenu_insert_entry(fsmenu,
FS_CATEGORY_SYSTEM_BOOKMARKS,
line,
IFACE_("Pictures"),
ICON_FILE_IMAGE,
FS_INSERT_LAST);
}
BLI_snprintf(line, sizeof(line), "%s/Music/", home);
if (BLI_exists(line)) {
fsmenu_insert_entry(fsmenu,
FS_CATEGORY_SYSTEM_BOOKMARKS,
line,
IFACE_("Music"),
ICON_FILE_SOUND,
FS_INSERT_LAST);
}
fsmenu_xdg_user_dirs_free(xdg_map);
}
{
@@ -858,6 +923,11 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
}
# endif
#endif
#if defined(WIN32) || defined(__APPLE__)
/* Quiet warnings. */
UNUSED_VARS(fsmenu_xdg_insert_entry, fsmenu_xdg_user_dirs_parse, fsmenu_xdg_user_dirs_free);
#endif
}
static void fsmenu_free_category(struct FSMenu *fsmenu, FSMenuCategory category)