From 48816d462eedc254d439608431fde78d74f2f518 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 26 Feb 2020 18:07:05 +1100 Subject: UI: Linux platform support for 'user-dirs.dirs' Originally D6826 by @a.monti with parsing rewritten. --- source/blender/editors/space_file/fsmenu.c | 190 ++++++++++++++++++++--------- 1 file changed, 130 insertions(+), 60 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index eaabd907f23..80ea0c190e2 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -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. */ - - /* TODO: parse "$XDG_CONFIG_HOME/user-dirs.dirs" for localized paths. */ - - 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); - } - - 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); + GHash *xdg_map = fsmenu_xdg_user_dirs_parse(home); + + 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}, + }; + + 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/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) -- cgit v1.2.3