From 664f5ce9605b580b9500547fff1e54eac553f295 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 19 Dec 2020 13:27:15 +0000 Subject: [PATCH] Add open user data button to main menu (#10579) --- .../net/minetest/minetest/GameActivity.java | 4 +-- builtin/mainmenu/tab_credits.lua | 20 +++++++++-- doc/menu_lua_api.txt | 7 ++++ src/porting.cpp | 35 ++++++++++++++----- src/porting.h | 20 ++++++++++- src/porting_android.cpp | 6 ++-- src/porting_android.h | 2 +- src/script/lua_api/l_mainmenu.cpp | 21 ++++++++++- src/script/lua_api/l_mainmenu.h | 4 +++ 9 files changed, 100 insertions(+), 19 deletions(-) diff --git a/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java b/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java index db126a2b9..38a388230 100644 --- a/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java +++ b/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java @@ -142,8 +142,8 @@ public class GameActivity extends NativeActivity { return getResources().getDisplayMetrics().widthPixels; } - public void openURL(String url) { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + public void openURI(String uri) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri)); startActivity(browserIntent); } } diff --git a/builtin/mainmenu/tab_credits.lua b/builtin/mainmenu/tab_credits.lua index c2b7e503a..075274798 100644 --- a/builtin/mainmenu/tab_credits.lua +++ b/builtin/mainmenu/tab_credits.lua @@ -100,9 +100,10 @@ return { cbf_formspec = function(tabview, name, tabdata) local logofile = defaulttexturedir .. "logo.png" local version = core.get_version() - return "image[0.5,1;" .. core.formspec_escape(logofile) .. "]" .. - "label[0.5,2.8;" .. version.project .. " " .. version.string .. "]" .. - "button[0.5,3;2,2;homepage;minetest.net]" .. + local fs = "image[0.75,0.5;2.2,2.2;" .. core.formspec_escape(logofile) .. "]" .. + "style[label_button;border=false]" .. + "button[0.5,2;2.5,2;label_button;" .. version.project .. " " .. version.string .. "]" .. + "button[0.75,2.75;2,2;homepage;minetest.net]" .. "tablecolumns[color;text]" .. "tableoptions[background=#00000000;highlight=#00000000;border=false]" .. "table[3.5,-0.25;8.5,6.05;list_credits;" .. @@ -115,10 +116,23 @@ return { "#FFFF00," .. fgettext("Previous Contributors") .. ",," .. buildCreditList(previous_contributors) .. "," .. ";1]" + + if PLATFORM ~= "Android" then + fs = fs .. "tooltip[userdata;" .. + fgettext("Opens the directory that contains user-provided worlds, games, mods,\n" .. + "and texture packs in a file manager / explorer.") .. "]" + fs = fs .. "button[0,4.75;3.5,1;userdata;" .. fgettext("Open User Data Directory") .. "]" + end + + return fs end, cbf_button_handler = function(this, fields, name, tabdata) if fields.homepage then core.open_url("https://www.minetest.net") end + + if fields.userdata then + core.open_dir(core.get_user_path()) + end end, } diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt index 76bebe08b..8908552d5 100644 --- a/doc/menu_lua_api.txt +++ b/doc/menu_lua_api.txt @@ -43,10 +43,14 @@ core.get_max_supp_proto() core.open_url(url) ^ opens the URL in a web browser, returns false on failure. ^ Must begin with http:// or https:// +core.open_dir(path) +^ opens the path in the system file browser/explorer, returns false on failure. +^ Must be an existing directory. core.get_version() (possible in async calls) ^ returns current core version + Filesystem ---------- @@ -207,6 +211,9 @@ Content and Packages Content - an installed mod, modpack, game, or texture pack (txt) Package - content which is downloadable from the content db, may or may not be installed. +* core.get_user_path() (possible in async calls) + * returns path to global user data, + the directory that contains user-provided mods, worlds, games, and texture packs. * core.get_modpath() (possible in async calls) * returns path to global modpath * core.get_clientmodpath() (possible in async calls) diff --git a/src/porting.cpp b/src/porting.cpp index e7ed4e090..4c87bddee 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -719,29 +719,48 @@ int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...) return c; } -bool openURL(const std::string &url) +static bool open_uri(const std::string &uri) { - if ((url.substr(0, 7) != "http://" && url.substr(0, 8) != "https://") || - url.find_first_of("\r\n") != std::string::npos) { - errorstream << "Invalid url: " << url << std::endl; + if (uri.find_first_of("\r\n") != std::string::npos) { + errorstream << "Unable to open URI as it is invalid, contains new line: " << uri << std::endl; return false; } #if defined(_WIN32) - return (intptr_t)ShellExecuteA(NULL, NULL, url.c_str(), NULL, NULL, SW_SHOWNORMAL) > 32; + return (intptr_t)ShellExecuteA(NULL, NULL, uri.c_str(), NULL, NULL, SW_SHOWNORMAL) > 32; #elif defined(__ANDROID__) - openURLAndroid(url); + openURIAndroid(uri); return true; #elif defined(__APPLE__) - const char *argv[] = {"open", url.c_str(), NULL}; + const char *argv[] = {"open", uri.c_str(), NULL}; return posix_spawnp(NULL, "open", NULL, NULL, (char**)argv, (*_NSGetEnviron())) == 0; #else - const char *argv[] = {"xdg-open", url.c_str(), NULL}; + const char *argv[] = {"xdg-open", uri.c_str(), NULL}; return posix_spawnp(NULL, "xdg-open", NULL, NULL, (char**)argv, environ) == 0; #endif } +bool open_url(const std::string &url) +{ + if (url.substr(0, 7) != "http://" && url.substr(0, 8) != "https://") { + errorstream << "Unable to open browser as URL is missing schema: " << url << std::endl; + return false; + } + + return open_uri(url); +} + +bool open_directory(const std::string &path) +{ + if (!fs::IsDir(path)) { + errorstream << "Unable to open directory as it does not exist: " << path << std::endl; + return false; + } + + return open_uri(path); +} + // Load performance counter frequency only once at startup #ifdef _WIN32 diff --git a/src/porting.h b/src/porting.h index c7adf12a2..e4ebe36fd 100644 --- a/src/porting.h +++ b/src/porting.h @@ -332,7 +332,25 @@ void attachOrCreateConsole(); int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...); -bool openURL(const std::string &url); +/** + * Opens URL in default web browser + * + * Must begin with http:// or https://, and not contain any new lines + * + * @param url The URL + * @return true on success, false on failure + */ +bool open_url(const std::string &url); + +/** + * Opens a directory in the default file manager + * + * The directory must exist. + * + * @param path Path to directory + * @return true on success, false on failure + */ +bool open_directory(const std::string &path); } // namespace porting diff --git a/src/porting_android.cpp b/src/porting_android.cpp index 41b521ec2..f5870c174 100644 --- a/src/porting_android.cpp +++ b/src/porting_android.cpp @@ -213,13 +213,13 @@ void showInputDialog(const std::string &acceptButton, const std::string &hint, jacceptButton, jhint, jcurrent, jeditType); } -void openURLAndroid(const std::string &url) +void openURIAndroid(const std::string &url) { - jmethodID url_open = jnienv->GetMethodID(nativeActivity, "openURL", + jmethodID url_open = jnienv->GetMethodID(nativeActivity, "openURI", "(Ljava/lang/String;)V"); FATAL_ERROR_IF(url_open == nullptr, - "porting::openURLAndroid unable to find java openURL method"); + "porting::openURIAndroid unable to find java openURI method"); jstring jurl = jnienv->NewStringUTF(url.c_str()); jnienv->CallVoidMethod(app_global->activity->clazz, url_open, jurl); diff --git a/src/porting_android.h b/src/porting_android.h index 6eb054041..239815922 100644 --- a/src/porting_android.h +++ b/src/porting_android.h @@ -58,7 +58,7 @@ void initializePathsAndroid(); void showInputDialog(const std::string &acceptButton, const std::string &hint, const std::string ¤t, int editType); -void openURLAndroid(const std::string &url); +void openURIAndroid(const std::string &url); /** * WORKAROUND for not working callbacks from java -> c++ diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 0aa2760e9..0b0b2de3b 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -686,6 +686,14 @@ int ModApiMainMenu::l_get_mapgen_names(lua_State *L) } +/******************************************************************************/ +int ModApiMainMenu::l_get_user_path(lua_State *L) +{ + std::string path = fs::RemoveRelativePathComponents(porting::path_user); + lua_pushstring(L, path.c_str()); + return 1; +} + /******************************************************************************/ int ModApiMainMenu::l_get_modpath(lua_State *L) { @@ -1067,7 +1075,15 @@ int ModApiMainMenu::l_get_max_supp_proto(lua_State *L) int ModApiMainMenu::l_open_url(lua_State *L) { std::string url = luaL_checkstring(L, 1); - lua_pushboolean(L, porting::openURL(url)); + lua_pushboolean(L, porting::open_url(url)); + return 1; +} + +/******************************************************************************/ +int ModApiMainMenu::l_open_dir(lua_State *L) +{ + std::string path = luaL_checkstring(L, 1); + lua_pushboolean(L, porting::open_directory(path)); return 1; } @@ -1113,6 +1129,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top) API_FCT(set_background); API_FCT(set_topleft_text); API_FCT(get_mapgen_names); + API_FCT(get_user_path); API_FCT(get_modpath); API_FCT(get_clientmodpath); API_FCT(get_gamepath); @@ -1134,6 +1151,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top) API_FCT(get_min_supp_proto); API_FCT(get_max_supp_proto); API_FCT(open_url); + API_FCT(open_dir); API_FCT(do_async_callback); } @@ -1144,6 +1162,7 @@ void ModApiMainMenu::InitializeAsync(lua_State *L, int top) API_FCT(get_games); API_FCT(get_favorites); API_FCT(get_mapgen_names); + API_FCT(get_user_path); API_FCT(get_modpath); API_FCT(get_clientmodpath); API_FCT(get_gamepath); diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h index 5a16b3bfe..faa2bf273 100644 --- a/src/script/lua_api/l_mainmenu.h +++ b/src/script/lua_api/l_mainmenu.h @@ -112,6 +112,8 @@ private: static int l_get_mainmenu_path(lua_State *L); + static int l_get_user_path(lua_State *L); + static int l_get_modpath(lua_State *L); static int l_get_clientmodpath(lua_State *L); @@ -148,6 +150,8 @@ private: // other static int l_open_url(lua_State *L); + static int l_open_dir(lua_State *L); + // async static int l_do_async_callback(lua_State *L);