diff --git a/src/libslic3r/Utils/DirectoriesUtils.cpp b/src/libslic3r/Utils/DirectoriesUtils.cpp index d0ab335271..68425735a5 100644 --- a/src/libslic3r/Utils/DirectoriesUtils.cpp +++ b/src/libslic3r/Utils/DirectoriesUtils.cpp @@ -59,10 +59,10 @@ std::optional get_env(std::string_view key) { return std::string{result}; } -namespace Slic3r { -std::optional get_home_config_dir() { +namespace { +std::optional get_home_dir(const std::string& subfolder) { if (auto result{get_env("HOME")}) { - return *result + "/.config"; + return *result + subfolder; } else { std::optional user_name{get_env("USER")}; if (!user_name) { @@ -78,13 +78,23 @@ std::optional get_home_config_dir() { who = getpwuid(getuid()); } if (who) { - return std::string{who->pw_dir} + "/.config"; + return std::string{who->pw_dir} + subfolder; } } return std::nullopt; } } +namespace Slic3r { +std::optional get_home_config_dir() { + return get_home_dir("/.config"); +} + +std::optional get_home_local_dir() { + return get_home_dir("/.local"); +} +} + std::string GetDataDir() { if (auto result{get_env("XDG_CONFIG_HOME")}) { diff --git a/src/libslic3r/Utils/DirectoriesUtils.hpp b/src/libslic3r/Utils/DirectoriesUtils.hpp index f6ded98630..81dccd66f9 100644 --- a/src/libslic3r/Utils/DirectoriesUtils.hpp +++ b/src/libslic3r/Utils/DirectoriesUtils.hpp @@ -14,6 +14,7 @@ namespace Slic3r { // Only defined on linux. std::optional get_home_config_dir(); +std::optional get_home_local_dir(); std::string get_default_datadir(); diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.cpp b/src/slic3r/GUI/DesktopIntegrationDialog.cpp index b05bf31241..32d2aa8fcd 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.cpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.cpp @@ -14,10 +14,10 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/Platform.hpp" #include "libslic3r/Config.hpp" +#include "libslic3r/Utils/DirectoriesUtils.hpp" #include // IWYU pragma: keep #include -#include #include #include #include @@ -224,6 +224,7 @@ bool create_desktop_file(const std::string& path, const std::string& data) // methods that actually do / undo desktop integration. Static to be accesible from anywhere. bool DesktopIntegrationDialog::is_integrated() { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__; const AppConfig *app_config = wxGetApp().app_config; std::string path(app_config->get("desktop_integration_app_path")); BOOST_LOG_TRIVIAL(debug) << "Desktop integration desktop file path: " << path; @@ -237,10 +238,12 @@ bool DesktopIntegrationDialog::is_integrated() } bool DesktopIntegrationDialog::integration_possible() { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__; return true; } void DesktopIntegrationDialog::perform_desktop_integration() { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__; BOOST_LOG_TRIVIAL(debug) << "performing desktop integration."; // Path to appimage const char *appimage_env = std::getenv("APPIMAGE"); @@ -446,8 +449,9 @@ void DesktopIntegrationDialog::perform_desktop_integration() } wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess); } -void DesktopIntegrationDialog::undo_desktop_intgration() +void DesktopIntegrationDialog::undo_desktop_integration() { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__; const AppConfig *app_config = wxGetApp().app_config; // slicer .desktop std::string path = std::string(app_config->get("desktop_integration_app_path")); @@ -634,6 +638,7 @@ void DesktopIntegrationDialog::perform_downloader_desktop_integration() } void DesktopIntegrationDialog::undo_downloader_registration() { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__; const AppConfig *app_config = wxGetApp().app_config; std::string path = std::string(app_config->get("desktop_integration_URL_path")); if (!path.empty()) { @@ -644,6 +649,7 @@ void DesktopIntegrationDialog::undo_downloader_registration() } void DesktopIntegrationDialog::undo_downloader_registration_rigid() { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__; // Try ro find any PrusaSlicerURLProtocol.desktop files including alpha and beta and get rid of them // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. @@ -655,7 +661,7 @@ void DesktopIntegrationDialog::undo_downloader_registration_rigid() target_candidates.emplace_back(GUI::into_u8(wxFileName::GetHomeDir()) + "/.local/share"); resolve_path_from_var("XDG_DATA_HOME", target_candidates); resolve_path_from_var("XDG_DATA_DIRS", target_candidates); - for (const std::string cand : target_candidates) { + for (const std::string& cand : target_candidates) { boost::filesystem::path apps_path = get_existing_dir(cand, "applications"); if (apps_path.empty()) { continue; @@ -675,6 +681,57 @@ void DesktopIntegrationDialog::undo_downloader_registration_rigid() } } +void DesktopIntegrationDialog::find_all_desktop_files(std::vector& results) +{ + // Try ro find any PrusaSlicer.desktop and PrusaSlicerGcodeViewer.desktop and PrusaSlicerURLProtocol.desktop files including alpha and beta + + // For regular apps (f.e. appimage) this is true: + // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. + // If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used. + // $XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data files in addition to the $XDG_DATA_HOME base directory. + // The directories in $XDG_DATA_DIRS should be seperated with a colon ':'. + // If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used. + + // But flatpak resets XDG_DATA_HOME and XDG_DATA_DIRS, so we do not look into them + // Lets look into $HOME/.local/share, /usr/local/share/, /usr/share/ + std::vector target_candidates; + if (auto home_config_dir = Slic3r::get_home_local_dir(); home_config_dir) { + target_candidates.emplace_back((*home_config_dir).string() + "/share"); + } + target_candidates.emplace_back("usr/local/share/"); + target_candidates.emplace_back("usr/share/"); + for (const std::string& cand : target_candidates) { + boost::filesystem::path apps_path = get_existing_dir(cand, "applications"); + if (apps_path.empty()) { + continue; + } + for (const std::string& filename : {"PrusaSlicer","PrusaSlicerGcodeViewer","PrusaSlicerURLProtocol"}) { + for (const std::string& suffix : {"" , "-beta", "-alpha", "_beta", "_alpha"}) { + boost::filesystem::path file_path = apps_path / GUI::format("%1%%2%.desktop", filename, suffix); + boost::system::error_code ec; + if (!boost::filesystem::exists(file_path, ec) || ec) { + continue; + } + BOOST_LOG_TRIVIAL(debug) << "Desktop File found: " << file_path; + results.emplace_back(std::move(file_path)); + } + } + } +} + +void DesktopIntegrationDialog::remove_desktop_file_list(const std::vector& list, std::vector& fails) +{ + for (const boost::filesystem::path& entry : list) { + boost::system::error_code ec; + if (!boost::filesystem::remove(entry, ec) || ec) { + BOOST_LOG_TRIVIAL(error) << "Failed to remove file " << entry << " ec: " << ec.message(); + fails.emplace_back(entry); + continue; + } + BOOST_LOG_TRIVIAL(info) << "Desktop File removed: " << entry; + } +} + DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent) : wxDialog(parent, wxID_ANY, _(L("Desktop Integration")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) { @@ -705,7 +762,7 @@ DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent) if (can_undo){ wxButton *btn_undo = new wxButton(this, wxID_ANY, _L("Undo")); btn_szr->Add(btn_undo, 0, wxALL, 10); - btn_undo->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::undo_desktop_intgration(); EndModal(wxID_ANY); }); + btn_undo->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::undo_desktop_integration(); EndModal(wxID_ANY); }); } wxButton *btn_cancel = new wxButton(this, wxID_ANY, _L("Cancel")); btn_szr->Add(btn_cancel, 0, wxALL, 10); @@ -718,7 +775,6 @@ DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent) DesktopIntegrationDialog::~DesktopIntegrationDialog() { - } } // namespace GUI diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.hpp b/src/slic3r/GUI/DesktopIntegrationDialog.hpp index c26ad6b8d4..145d4e8ade 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.hpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.hpp @@ -7,6 +7,8 @@ #define slic3r_DesktopIntegrationDialog_hpp_ #include +#include + namespace Slic3r { namespace GUI { @@ -35,13 +37,13 @@ public: // Regiters PrusaSlicer to start on prusaslicer:// URL static void perform_desktop_integration(); // Deletes Desktop files and icons for both PrusaSlicer and GcodeViewer at paths stored in App Config. - static void undo_desktop_intgration(); + static void undo_desktop_integration(); static void perform_downloader_desktop_integration(); static void undo_downloader_registration(); static void undo_downloader_registration_rigid(); -private: - + static void find_all_desktop_files(std::vector& results); + static void remove_desktop_file_list(const std::vector& list, std::vector& fails); }; } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 4213acd872..18a360c37f 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1311,6 +1311,31 @@ static int get_app_font_pt_size(const AppConfig* app_config) return (font_pt_size > max_font_pt_size) ? max_font_pt_size : font_pt_size; } +#if defined(__linux__) && !defined(SLIC3R_DESKTOP_INTEGRATION) +void GUI_App::remove_desktop_files_dialog() +{ + // Find all old existing desktop file + std::vector found_desktop_files; + DesktopIntegrationDialog::find_all_desktop_files(found_desktop_files); + if(found_desktop_files.empty()) { + return; + } + // Delete files. + std::vector fails; + DesktopIntegrationDialog::remove_desktop_file_list(found_desktop_files, fails); + if (fails.empty()) { + return; + } + // Inform about fails. + std::string text = "Failed to remove desktop files:"; + text += "\n"; + for (const boost::filesystem::path& entry : fails) { + text += GUI::format("%1%\n",entry.string()); + } + BOOST_LOG_TRIVIAL(error) << text; +} +#endif //(__linux__) && !defined(SLIC3R_DESKTOP_INTEGRATION) + bool GUI_App::on_init_inner() { // TODO: remove this when all asserts are gone. @@ -1569,6 +1594,10 @@ bool GUI_App::on_init_inner() // Call this check only after appconfig was loaded to mainframe, otherwise there will be duplicity error. legacy_app_config_vendor_check(); +#if defined(__linux__) && !defined(SLIC3R_DESKTOP_INTEGRATION) + remove_desktop_files_dialog(); +#endif //(__linux__) && !defined(SLIC3R_DESKTOP_INTEGRATION) + sidebar().obj_list()->init_objects(); // propagate model objects to object list update_mode(); // mode sizer doesn't exist anymore, so we came update mode here, before load_current_presets SetTopWindow(mainframe); @@ -2723,7 +2752,7 @@ wxMenu* GUI_App::get_config_menu(MainFrame* main_frame) #ifdef __linux__ case ConfigMenuDesktopIntegration: show_desktop_integration_dialog(); - break; + break; #endif case ConfigMenuTakeSnapshot: // Take a configuration snapshot. diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 5dde60ea54..a5c98a35fc 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -453,6 +453,9 @@ private: void app_updater(bool from_user); // inititate read of version file online in separate thread void app_version_check(bool from_user); +#if defined(__linux__) && !defined(SLIC3R_DESKTOP_INTEGRATION) + void remove_desktop_files_dialog(); +#endif //(__linux__) && !defined(SLIC3R_DESKTOP_INTEGRATION) bool m_wifi_config_dialog_shown { false }; bool m_wifi_config_dialog_was_declined { false }; diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 4cc5e58d3a..0818d816b0 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -637,7 +637,7 @@ void PreferencesDialog::build() L("Show \"Log in\" button in application top bar"), L("If enabled, PrusaSlicer will show up \"Log in\" button in application top bar."), app_config->get_bool("show_login_button")); - + append_bool_option(m_optgroup_other, "downloader_url_registered", L("Allow downloads from Printables.com"), L("If enabled, PrusaSlicer will be allowed to download from Printables.com"),