Desktop integration changes

empty catch block fix
internal namespace fix
wrong app config var
Desktop integration for regular executables
cmake option SLIC3R_DESKTOP_INTEGRATION
cmake dependent option
escape executable path in desktop file by adding /'
Error messages instead of notifications.
This commit is contained in:
David Kocik 2021-08-19 10:15:42 +02:00
parent a3b089eceb
commit 12328a74f7
3 changed files with 58 additions and 51 deletions

View File

@ -3,6 +3,7 @@ project(PrusaSlicer)
include("version.inc") include("version.inc")
include(GNUInstallDirs) include(GNUInstallDirs)
include(CMakeDependentOption)
set(SLIC3R_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/resources") set(SLIC3R_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/resources")
file(TO_NATIVE_PATH "${SLIC3R_RESOURCES_DIR}" SLIC3R_RESOURCES_DIR_WIN) file(TO_NATIVE_PATH "${SLIC3R_RESOURCES_DIR}" SLIC3R_RESOURCES_DIR_WIN)
@ -32,6 +33,8 @@ option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1)
option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1) option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0) option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0)
option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0) option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0)
# If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable.
CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 0 "NOT SLIC3R_FHS" 0)
set(OPENVDB_FIND_MODULE_PATH "" CACHE PATH "Path to OpenVDB installation's find modules.") set(OPENVDB_FIND_MODULE_PATH "" CACHE PATH "Path to OpenVDB installation's find modules.")
@ -71,6 +74,10 @@ if (SLIC3R_GUI)
add_definitions(-DSLIC3R_GUI) add_definitions(-DSLIC3R_GUI)
endif () endif ()
if(SLIC3R_DESKTOP_INTEGRATION)
add_definitions(-DSLIC3R_DESKTOP_INTEGRATION)
endif ()
if (MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) if (MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(IS_CLANG_CL TRUE) set(IS_CLANG_CL TRUE)

View File

@ -1,15 +1,18 @@
#ifdef __linux__ #ifdef __linux__
#include "DesktopIntegrationDialog.hpp" #include "DesktopIntegrationDialog.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "GUI.hpp"
#include "format.hpp" #include "format.hpp"
#include "I18N.hpp" #include "I18N.hpp"
#include "NotificationManager.hpp" #include "NotificationManager.hpp"
#include "libslic3r/AppConfig.hpp" #include "libslic3r/AppConfig.hpp"
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include "libslic3r/Platform.hpp" #include "libslic3r/Platform.hpp"
#include "libslic3r/Config.hpp"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <boost/dll/runtime_symbol_info.hpp>
#include <wx/filename.h> #include <wx/filename.h>
#include <wx/stattext.h> #include <wx/stattext.h>
@ -17,9 +20,9 @@
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
namespace integrate_desktop_internal{ namespace {
// Disects path strings stored in system variable divided by ':' and adds into vector // Disects path strings stored in system variable divided by ':' and adds into vector
static void resolve_path_from_var(const std::string& var, std::vector<std::string>& paths) void resolve_path_from_var(const std::string& var, std::vector<std::string>& paths)
{ {
wxString wxdirs; wxString wxdirs;
if (! wxGetEnv(boost::nowide::widen(var), &wxdirs) || wxdirs.empty() ) if (! wxGetEnv(boost::nowide::widen(var), &wxdirs) || wxdirs.empty() )
@ -34,7 +37,7 @@ static void resolve_path_from_var(const std::string& var, std::vector<std::strin
paths.push_back(dirs); paths.push_back(dirs);
} }
// Return true if directory in path p+dir_name exists // Return true if directory in path p+dir_name exists
static bool contains_path_dir(const std::string& p, const std::string& dir_name) bool contains_path_dir(const std::string& p, const std::string& dir_name)
{ {
if (p.empty() || dir_name.empty()) if (p.empty() || dir_name.empty())
return false; return false;
@ -47,7 +50,7 @@ static bool contains_path_dir(const std::string& p, const std::string& dir_name)
return false; return false;
} }
// Creates directory in path if not exists yet // Creates directory in path if not exists yet
static void create_dir(const boost::filesystem::path& path) void create_dir(const boost::filesystem::path& path)
{ {
if (boost::filesystem::exists(path)) if (boost::filesystem::exists(path))
return; return;
@ -58,7 +61,7 @@ static void create_dir(const boost::filesystem::path& path)
BOOST_LOG_TRIVIAL(error)<< "create directory failed: " << ec.message(); BOOST_LOG_TRIVIAL(error)<< "create directory failed: " << ec.message();
} }
// Starts at basic_path (excluded) and creates all directories in dir_path // Starts at basic_path (excluded) and creates all directories in dir_path
static void create_path(const std::string& basic_path, const std::string& dir_path) void create_path(const std::string& basic_path, const std::string& dir_path)
{ {
if (basic_path.empty() || dir_path.empty()) if (basic_path.empty() || dir_path.empty())
return; return;
@ -76,7 +79,7 @@ static void create_path(const std::string& basic_path, const std::string& dir_pa
create_dir(path); create_dir(path);
} }
// Calls our internal copy_file function to copy file at icon_path to dest_path // Calls our internal copy_file function to copy file at icon_path to dest_path
static bool copy_icon(const std::string& icon_path, const std::string& dest_path) bool copy_icon(const std::string& icon_path, const std::string& dest_path)
{ {
BOOST_LOG_TRIVIAL(debug) <<"icon from "<< icon_path; BOOST_LOG_TRIVIAL(debug) <<"icon from "<< icon_path;
BOOST_LOG_TRIVIAL(debug) <<"icon to "<< dest_path; BOOST_LOG_TRIVIAL(debug) <<"icon to "<< dest_path;
@ -90,8 +93,8 @@ static bool copy_icon(const std::string& icon_path, const std::string& dest_path
return true; return true;
} }
// Creates new file filled with data. // Creates new file filled with data.
static bool create_desktop_file(const std::string& path, const std::string& data) bool create_desktop_file(const std::string& path, const std::string& data)
{ {
BOOST_LOG_TRIVIAL(debug) <<".desktop to "<< path; BOOST_LOG_TRIVIAL(debug) <<".desktop to "<< path;
std::ofstream output(path); std::ofstream output(path);
output << data; output << data;
@ -109,10 +112,6 @@ static 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. // methods that actually do / undo desktop integration. Static to be accesible from anywhere.
bool DesktopIntegrationDialog::is_integrated() bool DesktopIntegrationDialog::is_integrated()
{ {
const char *appimage_env = std::getenv("APPIMAGE");
if (!appimage_env)
return false;
const AppConfig *app_config = wxGetApp().app_config; const AppConfig *app_config = wxGetApp().app_config;
std::string path(app_config->get("desktop_integration_app_path")); std::string path(app_config->get("desktop_integration_app_path"));
BOOST_LOG_TRIVIAL(debug) << "Desktop integration desktop file path: " << path; BOOST_LOG_TRIVIAL(debug) << "Desktop integration desktop file path: " << path;
@ -126,10 +125,6 @@ bool DesktopIntegrationDialog::is_integrated()
} }
bool DesktopIntegrationDialog::integration_possible() bool DesktopIntegrationDialog::integration_possible()
{ {
const char *appimage_env = std::getenv("APPIMAGE");
if (!appimage_env)
return false;
return true; return true;
} }
void DesktopIntegrationDialog::perform_desktop_integration() void DesktopIntegrationDialog::perform_desktop_integration()
@ -143,14 +138,26 @@ void DesktopIntegrationDialog::perform_desktop_integration()
try { try {
appimage_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string(); appimage_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string();
} catch (std::exception &) { } catch (std::exception &) {
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - boost::filesystem::canonical did not return appimage path.";
show_error(nullptr, _L("Performing desktop integration failed - boost::filesystem::canonical did not return appimage path."));
return;
} }
} else { } else {
// not appimage - not performing // not appimage - find executable
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - not Appimage executable."; appimage_path = boost::dll::program_location().string();
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail); //appimage_path = wxStandardPaths::Get().GetExecutablePath().string();
return; BOOST_LOG_TRIVIAL(debug) << "non-appimage path to executable: " << appimage_path;
if (appimage_path.empty())
{
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - no executable found.";
show_error(nullptr, _L("Performing desktop integration failed - Could not find executable."));
return;
}
} }
// Escape ' characters in appimage, other special symbols will be esacaped in desktop file by 'appimage_path'
//appimage_path = std::regex_replace(appimage_path, std::regex("\'"), "\\\'");
// Find directories icons and applications // Find directories icons and applications
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. // $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. // If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
@ -158,8 +165,8 @@ void DesktopIntegrationDialog::perform_desktop_integration()
// The directories in $XDG_DATA_DIRS should be seperated with a colon ':'. // 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. // If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
std::vector<std::string>target_candidates; std::vector<std::string>target_candidates;
integrate_desktop_internal::resolve_path_from_var("XDG_DATA_HOME", target_candidates); resolve_path_from_var("XDG_DATA_HOME", target_candidates);
integrate_desktop_internal::resolve_path_from_var("XDG_DATA_DIRS", target_candidates); resolve_path_from_var("XDG_DATA_DIRS", target_candidates);
AppConfig *app_config = wxGetApp().app_config; AppConfig *app_config = wxGetApp().app_config;
// suffix string to create different desktop file for alpha, beta. // suffix string to create different desktop file for alpha, beta.
@ -186,7 +193,6 @@ void DesktopIntegrationDialog::perform_desktop_integration()
icon_theme_dirs = "/hicolor/96x96/apps"; icon_theme_dirs = "/hicolor/96x96/apps";
} }
std::string target_dir_icons; std::string target_dir_icons;
std::string target_dir_desktop; std::string target_dir_desktop;
@ -194,24 +200,24 @@ void DesktopIntegrationDialog::perform_desktop_integration()
// iterate thru target_candidates to find icons folder // iterate thru target_candidates to find icons folder
for (size_t i = 0; i < target_candidates.size(); ++i) { for (size_t i = 0; i < target_candidates.size(); ++i) {
// Copy icon PrusaSlicer.png from resources_dir()/icons to target_dir_icons/icons/ // Copy icon PrusaSlicer.png from resources_dir()/icons to target_dir_icons/icons/
if (integrate_desktop_internal::contains_path_dir(target_candidates[i], "icons")) { if (contains_path_dir(target_candidates[i], "icons")) {
target_dir_icons = target_candidates[i]; target_dir_icons = target_candidates[i];
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir()); std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir());
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix); std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
if (integrate_desktop_internal::copy_icon(icon_path, dest_path)) if (copy_icon(icon_path, dest_path))
break; // success break; // success
else else
target_dir_icons.clear(); // copying failed target_dir_icons.clear(); // copying failed
// if all failed - try creating default home folder // if all failed - try creating default home folder
if (i == target_candidates.size() - 1) { if (i == target_candidates.size() - 1) {
// create $HOME/.local/share // create $HOME/.local/share
integrate_desktop_internal::create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs); create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs);
// copy icon // copy icon
target_dir_icons = GUI::format("%1%/.local/share",wxFileName::GetHomeDir()); target_dir_icons = GUI::format("%1%/.local/share",wxFileName::GetHomeDir());
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir()); std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir());
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix); std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
if (!integrate_desktop_internal::contains_path_dir(target_dir_icons, "icons") if (!contains_path_dir(target_dir_icons, "icons")
|| !integrate_desktop_internal::copy_icon(icon_path, dest_path)) { || !copy_icon(icon_path, dest_path)) {
// every attempt failed - icon wont be present // every attempt failed - icon wont be present
target_dir_icons.clear(); target_dir_icons.clear();
} }
@ -228,7 +234,7 @@ void DesktopIntegrationDialog::perform_desktop_integration()
// iterate thru target_candidates to find applications folder // iterate thru target_candidates to find applications folder
for (size_t i = 0; i < target_candidates.size(); ++i) for (size_t i = 0; i < target_candidates.size(); ++i)
{ {
if (integrate_desktop_internal::contains_path_dir(target_candidates[i], "applications")) { if (contains_path_dir(target_candidates[i], "applications")) {
target_dir_desktop = target_candidates[i]; target_dir_desktop = target_candidates[i];
// Write slicer desktop file // Write slicer desktop file
std::string desktop_file = GUI::format( std::string desktop_file = GUI::format(
@ -236,7 +242,7 @@ void DesktopIntegrationDialog::perform_desktop_integration()
"Name=PrusaSlicer%1%\n" "Name=PrusaSlicer%1%\n"
"GenericName=3D Printing Software\n" "GenericName=3D Printing Software\n"
"Icon=PrusaSlicer%2%\n" "Icon=PrusaSlicer%2%\n"
"Exec=%3% %%F\n" "Exec=\'%3%\' %%F\n"
"Terminal=false\n" "Terminal=false\n"
"Type=Application\n" "Type=Application\n"
"MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n" "MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n"
@ -246,23 +252,23 @@ void DesktopIntegrationDialog::perform_desktop_integration()
"StartupWMClass=prusa-slicer", name_suffix, version_suffix, appimage_path); "StartupWMClass=prusa-slicer", name_suffix, version_suffix, appimage_path);
std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix); std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix);
if (integrate_desktop_internal::create_desktop_file(path, desktop_file)){ if (create_desktop_file(path, desktop_file)){
BOOST_LOG_TRIVIAL(debug) << "PrusaSlicer.desktop file installation success."; BOOST_LOG_TRIVIAL(debug) << "PrusaSlicer.desktop file installation success.";
break; break;
} else { } else {
// write failed - try another path // write failed - try another path
BOOST_LOG_TRIVIAL(error) << "PrusaSlicer.desktop file installation failed."; BOOST_LOG_TRIVIAL(debug) << "Attempt to PrusaSlicer.desktop file installation failed. failed path: " << target_candidates[i];
target_dir_desktop.clear(); target_dir_desktop.clear();
} }
// if all failed - try creating default home folder // if all failed - try creating default home folder
if (i == target_candidates.size() - 1) { if (i == target_candidates.size() - 1) {
// create $HOME/.local/share // create $HOME/.local/share
integrate_desktop_internal::create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications"); create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications");
// create desktop file // create desktop file
target_dir_desktop = GUI::format("%1%/.local/share",wxFileName::GetHomeDir()); target_dir_desktop = GUI::format("%1%/.local/share",wxFileName::GetHomeDir());
std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix); std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix);
if (integrate_desktop_internal::contains_path_dir(target_dir_desktop, "applications")) { if (contains_path_dir(target_dir_desktop, "applications")) {
if (!integrate_desktop_internal::create_desktop_file(path, desktop_file)) { if (!create_desktop_file(path, desktop_file)) {
// Desktop file not written - end desktop integration // Desktop file not written - end desktop integration
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create desktop file"; BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create desktop file";
return; return;
@ -278,7 +284,7 @@ void DesktopIntegrationDialog::perform_desktop_integration()
if(target_dir_desktop.empty()) { if(target_dir_desktop.empty()) {
// Desktop file not written - end desktop integration // Desktop file not written - end desktop integration
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not find applications directory"; BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not find applications directory";
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail); show_error(nullptr, _L("Performing desktop integration failed - could not find applications directory."));
return; return;
} }
// save path to desktop file // save path to desktop file
@ -290,7 +296,7 @@ void DesktopIntegrationDialog::perform_desktop_integration()
{ {
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir()); std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir());
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix); std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
if (integrate_desktop_internal::copy_icon(icon_path, dest_path)) if (copy_icon(icon_path, dest_path))
// save path to icon // save path to icon
app_config->set("desktop_integration_icon_viewer_path", dest_path); app_config->set("desktop_integration_icon_viewer_path", dest_path);
else else
@ -303,7 +309,7 @@ void DesktopIntegrationDialog::perform_desktop_integration()
"Name=Prusa Gcode Viewer%1%\n" "Name=Prusa Gcode Viewer%1%\n"
"GenericName=3D Printing Software\n" "GenericName=3D Printing Software\n"
"Icon=PrusaSlicer-gcodeviewer%2%\n" "Icon=PrusaSlicer-gcodeviewer%2%\n"
"Exec=%3% --gcodeviwer %%F\n" "Exec=\'%3%\' --gcodeviwer %%F\n"
"Terminal=false\n" "Terminal=false\n"
"Type=Application\n" "Type=Application\n"
"MimeType=text/x.gcode;\n" "MimeType=text/x.gcode;\n"
@ -312,23 +318,17 @@ void DesktopIntegrationDialog::perform_desktop_integration()
"StartupNotify=false", name_suffix, version_suffix, appimage_path); "StartupNotify=false", name_suffix, version_suffix, appimage_path);
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix); std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix);
if (integrate_desktop_internal::create_desktop_file(desktop_path, desktop_file)) if (create_desktop_file(desktop_path, desktop_file))
// save path to desktop file // save path to desktop file
app_config->set("desktop_integration_app_viewer_path", desktop_path); app_config->set("desktop_integration_app_viewer_path", desktop_path);
else { else {
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could create gcode viewer desktop file"; BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create Gcodeviewer desktop file";
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail); show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully."));
} }
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess); wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
} }
void DesktopIntegrationDialog::undo_desktop_intgration() void DesktopIntegrationDialog::undo_desktop_intgration()
{ {
const char *appimage_env = std::getenv("APPIMAGE");
if (!appimage_env) {
BOOST_LOG_TRIVIAL(error) << "Undo desktop integration failed - not Appimage executable.";
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationFail);
return;
}
const AppConfig *app_config = wxGetApp().app_config; const AppConfig *app_config = wxGetApp().app_config;
// slicer .desktop // slicer .desktop
std::string path = std::string(app_config->get("desktop_integration_app_path")); std::string path = std::string(app_config->get("desktop_integration_app_path"));

View File

@ -1806,10 +1806,10 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
local_menu->Append(config_id_base + ConfigMenuSnapshots, _L("&Configuration Snapshots") + dots, _L("Inspect / activate configuration snapshots")); local_menu->Append(config_id_base + ConfigMenuSnapshots, _L("&Configuration Snapshots") + dots, _L("Inspect / activate configuration snapshots"));
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _L("Take Configuration &Snapshot"), _L("Capture a configuration snapshot")); local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _L("Take Configuration &Snapshot"), _L("Capture a configuration snapshot"));
local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for updates"), _L("Check for configuration updates")); local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for updates"), _L("Check for configuration updates"));
#ifdef __linux__ #if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION)
if (DesktopIntegrationDialog::integration_possible()) //if (DesktopIntegrationDialog::integration_possible())
local_menu->Append(config_id_base + ConfigMenuDesktopIntegration, _L("Desktop Integration"), _L("Desktop Integration")); local_menu->Append(config_id_base + ConfigMenuDesktopIntegration, _L("Desktop Integration"), _L("Desktop Integration"));
#endif #endif //(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION)
local_menu->AppendSeparator(); local_menu->AppendSeparator();
} }
local_menu->Append(config_id_base + ConfigMenuPreferences, _L("&Preferences") + dots + local_menu->Append(config_id_base + ConfigMenuPreferences, _L("&Preferences") + dots +