diff --git a/CMakeLists.txt b/CMakeLists.txt
index 25aff34d46..a6e6e2c8ea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -41,8 +41,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_ASAN "Enable ASan on Clang and GCC" 0)
option(SLIC3R_UBSAN "Enable UBSan on Clang and GCC" 0)
-option(SLIC3R_ENABLE_FORMAT_STEP "Enable compilation of STEP file support" ON)
-# If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable.
+option(SLIC3R_ENABLE_FORMAT_STEP "Enable compilation of STEP file support" 1)
+# If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, otherwise variable.
CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0)
set(OPENVDB_FIND_MODULE_PATH "" CACHE PATH "Path to OpenVDB installation's find modules.")
@@ -606,6 +606,13 @@ function(prusaslicer_copy_dlls target)
COMMENT "Copy mpfr runtime to build tree"
VERBATIM)
+ if(DEFINED DESTDIR)
+ add_custom_command(TARGET ${target} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${DESTDIR}/usr/local/bin/WebView2Loader.dll ${_out_dir}
+ COMMENT "Copy WebView2Loader runtime to build tree"
+ VERBATIM)
+ endif ()
+
endfunction()
diff --git a/deps/+WebView2/WebView2.cmake b/deps/+WebView2/WebView2.cmake
new file mode 100644
index 0000000000..dff90d5936
--- /dev/null
+++ b/deps/+WebView2/WebView2.cmake
@@ -0,0 +1,60 @@
+
+if (MSVC)
+
+ # Update the following variables if updating WebView2 SDK
+ set(WEBVIEW2_VERSION "1.0.705.50")
+ set(WEBVIEW2_URL "https://www.nuget.org/api/v2/package/Microsoft.Web.WebView2/${WEBVIEW2_VERSION}")
+ set(WEBVIEW2_SHA256 "6a34bb553e18cfac7297b4031f3eac2558e439f8d16a45945c22945ac404105d")
+
+ set(WEBVIEW2_DEFAULT_PACKAGE_DIR "${CMAKE_CURRENT_BINARY_DIR}/dep_WebView2-prefix/packages/Microsoft.Web.WebView2.${WEBVIEW2_VERSION}")
+ set(WEBVIEW2_DOWNLOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/dep_WebView2-prefix/download")
+
+ #message(STATUS "WEBVIEW2_DEFAULT_PACKAGE_DIR = ${WEBVIEW2_DEFAULT_PACKAGE_DIR}")
+
+ if(NOT EXISTS ${WEBVIEW2_PACKAGE_DIR})
+ unset(WEBVIEW2_PACKAGE_DIR CACHE)
+ endif()
+
+ set(WEBVIEW2_PACKAGE_DIR ${WEBVIEW2_DEFAULT_PACKAGE_DIR} CACHE PATH "WebView2 SDK PATH" FORCE)
+
+ #file(MAKE_DIRECTORY ${DEP_DOWNLOAD_DIR}/WebView2)
+
+ message(STATUS "WEBVIEW2_URL = ${WEBVIEW2_URL}")
+ message(STATUS "WEBVIEW2_DOWNLOAD_DIR = ${WEBVIEW2_DOWNLOAD_DIR}")
+ file(DOWNLOAD
+ ${WEBVIEW2_URL}
+ ${WEBVIEW2_DOWNLOAD_DIR}/WebView2.nuget
+ EXPECTED_HASH SHA256=${WEBVIEW2_SHA256})
+
+ file(MAKE_DIRECTORY ${WEBVIEW2_PACKAGE_DIR})
+
+ execute_process(COMMAND
+ ${CMAKE_COMMAND} -E tar x ${WEBVIEW2_DOWNLOAD_DIR}/WebView2.nuget
+ WORKING_DIRECTORY ${WEBVIEW2_PACKAGE_DIR}
+ )
+
+ set(_srcdir ${WEBVIEW2_PACKAGE_DIR}/build/native)
+ set(_dstdir ${${PROJECT_NAME}_DEP_INSTALL_PREFIX})
+
+ set(_output ${_dstdir}/include/WebView2.h
+ ${_dstdir}/bin/WebView2Loader.dll)
+
+ if(NOT EXISTS ${_dstdir}/include)
+ file(MAKE_DIRECTORY ${_dstdir}/include)
+ endif()
+
+ if(NOT EXISTS ${_dstdir}/bin)
+ file(MAKE_DIRECTORY ${_dstdir}/bin)
+ endif()
+
+ add_custom_command(
+ OUTPUT ${_output}
+ COMMAND ${CMAKE_COMMAND} -E copy ${_srcdir}/include/WebView2.h ${_dstdir}/include/
+ COMMAND ${CMAKE_COMMAND} -E copy ${_srcdir}/x${DEPS_BITS}/WebView2Loader.dll ${_dstdir}/bin/
+ )
+
+ add_custom_target(dep_WebView2 SOURCES ${_output})
+
+ set(WEBVIEW2_PACKAGE_DIR ${WEBVIEW2_PACKAGE_DIR} CACHE INTERNAL "" FORCE)
+
+endif ()
\ No newline at end of file
diff --git a/deps/+wxWidgets/wxWidgets.cmake b/deps/+wxWidgets/wxWidgets.cmake
index ac15a48d0e..b621bb4973 100644
--- a/deps/+wxWidgets/wxWidgets.cmake
+++ b/deps/+wxWidgets/wxWidgets.cmake
@@ -14,6 +14,18 @@ if (UNIX AND NOT APPLE) # wxWidgets will not use char as the underlying type for
set (_unicode_utf8 ON)
endif()
+if (MSVC)
+ set(_wx_webview "-DwxUSE_WEBVIEW_EDGE=ON")
+else ()
+ set(_wx_webview "-DwxUSE_WEBVIEW=ON")
+endif ()
+
+if (UNIX AND NOT APPLE)
+ set(_wx_secretstore "-DwxUSE_SECRETSTORE=OFF")
+else ()
+ set(_wx_secretstore "-DwxUSE_SECRETSTORE=ON")
+endif ()
+
add_cmake_project(wxWidgets
URL https://github.com/prusa3d/wxWidgets/archive/78aa2dc0ea7ce99dc19adc1140f74c3e2e3f3a26.zip
URL_HASH SHA256=94b7d972373503e380e5a8b0ca63b1ccb956da4006402298dd89a0c5c7041b1e
@@ -39,6 +51,8 @@ add_cmake_project(wxWidgets
-DwxUSE_XTEST=OFF
-DwxUSE_GLCANVAS_EGL=OFF
-DwxUSE_WEBREQUEST=OFF
+ ${_wx_webview}
+ ${_wx_secretstore}
)
set(DEP_wxWidgets_DEPENDS ZLIB PNG EXPAT TIFF JPEG NanoSVG)
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index 175b64a619..04c8c8e1d2 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -180,7 +180,7 @@ foreach (pkg ${FOUND_PACKAGES})
if (${pkg} IN_LIST SYSTEM_PROVIDED_PACKAGES)
check_system_package(${pkg} _checked_list)
- else ()
+ elseif (TARGET dep_${pkg})
get_target_property(_is_excluded_from_all dep_${pkg} EXCLUDE_FROM_ALL)
if (NOT _is_excluded_from_all)
list(APPEND DEPS_TO_BUILD ${pkg})
@@ -189,6 +189,12 @@ foreach (pkg ${FOUND_PACKAGES})
endif ()
endforeach()
+# This ugly append ensures that WebView2 was appended no matter what EXCLUDE_FROM_ALL is.
+# (Webview2 is not added by add_cmake_project)
+if (MSVC)
+ list(APPEND DEPS_TO_BUILD WebView2)
+endif()
+
# Establish dependency graph
foreach (pkg ${SUPPORTED_PACKAGES})
if (${pkg} IN_LIST DEPS_TO_BUILD)
diff --git a/resources/icons/connect_gcode.svg b/resources/icons/connect_gcode.svg
new file mode 100644
index 0000000000..fb57be9c46
--- /dev/null
+++ b/resources/icons/connect_gcode.svg
@@ -0,0 +1,65 @@
+
+
diff --git a/resources/icons/login.svg b/resources/icons/login.svg
new file mode 100644
index 0000000000..0b01b66613
--- /dev/null
+++ b/resources/icons/login.svg
@@ -0,0 +1,18 @@
+
+
+
\ No newline at end of file
diff --git a/resources/icons/logout.svg b/resources/icons/logout.svg
new file mode 100644
index 0000000000..b2aa004057
--- /dev/null
+++ b/resources/icons/logout.svg
@@ -0,0 +1,16 @@
+
+
+
\ No newline at end of file
diff --git a/resources/icons/printer_available.svg b/resources/icons/printer_available.svg
new file mode 100644
index 0000000000..12e295098b
--- /dev/null
+++ b/resources/icons/printer_available.svg
@@ -0,0 +1,16 @@
+
+
+
diff --git a/resources/icons/printer_busy.svg b/resources/icons/printer_busy.svg
new file mode 100644
index 0000000000..32c8149174
--- /dev/null
+++ b/resources/icons/printer_busy.svg
@@ -0,0 +1,16 @@
+
+
+
diff --git a/resources/icons/printer_offline.svg b/resources/icons/printer_offline.svg
new file mode 100644
index 0000000000..c56326470f
--- /dev/null
+++ b/resources/icons/printer_offline.svg
@@ -0,0 +1,16 @@
+
+
+
diff --git a/resources/icons/sla_printer_available.svg b/resources/icons/sla_printer_available.svg
new file mode 100644
index 0000000000..47db35e290
--- /dev/null
+++ b/resources/icons/sla_printer_available.svg
@@ -0,0 +1,16 @@
+
+
+
diff --git a/resources/icons/sla_printer_busy.svg b/resources/icons/sla_printer_busy.svg
new file mode 100644
index 0000000000..fc4dcf3bc6
--- /dev/null
+++ b/resources/icons/sla_printer_busy.svg
@@ -0,0 +1,16 @@
+
+
+
diff --git a/resources/icons/sla_printer_offline.svg b/resources/icons/sla_printer_offline.svg
new file mode 100644
index 0000000000..eb91f6fe5c
--- /dev/null
+++ b/resources/icons/sla_printer_offline.svg
@@ -0,0 +1,16 @@
+
+
+
diff --git a/resources/icons/user.svg b/resources/icons/user.svg
new file mode 100644
index 0000000000..b750021fc4
--- /dev/null
+++ b/resources/icons/user.svg
@@ -0,0 +1,18 @@
+
+
+
\ No newline at end of file
diff --git a/resources/web/connection_failed.html b/resources/web/connection_failed.html
new file mode 100644
index 0000000000..72cb686232
--- /dev/null
+++ b/resources/web/connection_failed.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+ Connection failed
+
+
+
+
+
Connection failed
+
Something went wrong.
+
+
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9bbddbff90..dd5b70c5a5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -48,7 +48,7 @@ if (SLIC3R_GUI)
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
set (wxWidgets_CONFIG_OPTIONS "--toolkit=gtk${SLIC3R_GTK}")
endif ()
- find_package(wxWidgets 3.2 MODULE REQUIRED COMPONENTS base core adv html gl)
+ find_package(wxWidgets 3.2 MODULE REQUIRED COMPONENTS base core adv html gl webview)
include(${wxWidgets_USE_FILE})
diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp
index 114ac7489a..82900f4ab8 100644
--- a/src/libslic3r/AppConfig.cpp
+++ b/src/libslic3r/AppConfig.cpp
@@ -215,6 +215,12 @@ void AppConfig::set_defaults()
if (get("wifi_config_dialog_declined").empty())
set("wifi_config_dialog_declined", "0");
+ if (get("connect_polling").empty())
+ set("connect_polling", "1");
+
+ if (get("auth_login_dialog_confirmed").empty())
+ set("auth_login_dialog_confirmed", "0");
+
#ifdef _WIN32
if (get("use_legacy_3DConnexion").empty())
set("use_legacy_3DConnexion", "0");
diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp
index c68588032d..1ad7e44c72 100644
--- a/src/libslic3r/Config.cpp
+++ b/src/libslic3r/Config.cpp
@@ -39,6 +39,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp
index 24bbe7a5b1..46126a621c 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -1990,6 +1990,8 @@ public:
one_string,
// Close parameter, string value could be one of the list values.
select_close,
+ // Password, string vaule is hidden by asterisk.
+ password,
};
static bool is_gui_type_enum_open(const GUIType gui_type)
{ return gui_type == ConfigOptionDef::GUIType::i_enum_open || gui_type == ConfigOptionDef::GUIType::f_enum_open || gui_type == ConfigOptionDef::GUIType::select_open; }
diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index bc428a73f2..3f26b3f35c 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -1211,6 +1211,12 @@ Preset* PresetCollection::find_preset(const std::string &name, bool first_visibl
first_visible_if_not_found ? &this->first_visible() : nullptr;
}
+size_t PresetCollection::get_preset_idx_by_name(const std::string name) const
+{
+ auto it = this->find_preset_internal(name);
+ return it != m_presets.end() ? it - m_presets.begin() : size_t(-1);
+}
+
// Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible.
size_t PresetCollection::first_visible_idx() const
{
diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp
index 533c693812..13f1185031 100644
--- a/src/libslic3r/Preset.hpp
+++ b/src/libslic3r/Preset.hpp
@@ -125,6 +125,7 @@ public:
TYPE_PHYSICAL_PRINTER,
// This type is here to support search through the Preferences
TYPE_PREFERENCES,
+ TYPE_WEBVIEW,
};
Type type = TYPE_INVALID;
@@ -436,6 +437,8 @@ public:
const Preset* find_preset(const std::string &name, bool first_visible_if_not_found = false, bool respect_active_preset = true) const
{ return const_cast(this)->find_preset(name, first_visible_if_not_found, respect_active_preset); }
+ size_t get_preset_idx_by_name(const std::string preset_name) const;
+
size_t first_visible_idx() const;
// Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible.
// If one of the prefered_alternates is compatible, select it.
diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp
index b4fb5ce27d..b7d241abcf 100644
--- a/src/libslic3r/PresetBundle.cpp
+++ b/src/libslic3r/PresetBundle.cpp
@@ -1947,7 +1947,7 @@ void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_pri
}
}
-void PresetBundle::export_configbundle(const std::string &path, bool export_system_settings, bool export_physical_printers/* = false*/)
+void PresetBundle::export_configbundle(const std::string &path, bool export_system_settings, bool export_physical_printers/* = false*/, std::function secret_callback)
{
boost::nowide::ofstream c;
c.open(path, std::ios::out | std::ios::trunc);
@@ -1974,8 +1974,14 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst
if (export_physical_printers) {
for (const PhysicalPrinter& ph_printer : this->physical_printers) {
c << std::endl << "[physical_printer:" << ph_printer.name << "]" << std::endl;
- for (const std::string& opt_key : ph_printer.config.keys())
- c << opt_key << " = " << ph_printer.config.opt_serialize(opt_key) << std::endl;
+ for (const std::string& opt_key : ph_printer.config.keys()) {
+ std::string opt_val = ph_printer.config.opt_serialize(opt_key);
+ if (opt_val == "stored") {
+ secret_callback(ph_printer.name, opt_key, opt_val);
+ }
+
+ c << opt_key << " = " << opt_val << std::endl;
+ }
}
}
diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp
index 1e6aa1caa1..ddd22f348a 100644
--- a/src/libslic3r/PresetBundle.hpp
+++ b/src/libslic3r/PresetBundle.hpp
@@ -129,7 +129,7 @@ public:
const std::string &path, LoadConfigBundleAttributes flags, ForwardCompatibilitySubstitutionRule compatibility_rule);
// Export a config bundle file containing all the presets and the names of the active presets.
- void export_configbundle(const std::string &path, bool export_system_settings = false, bool export_physical_printers = false);
+ void export_configbundle(const std::string &path, bool export_system_settings = false, bool export_physical_printers = false, std::function secret_callback = nullptr);
// Enable / disable the "- default -" preset.
void set_default_suppressed(bool default_suppressed);
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 391db4309e..8f57c70f80 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -102,7 +102,9 @@ static const t_config_enum_values s_keys_map_PrintHostType {
{ "flashair", htFlashAir },
{ "astrobox", htAstroBox },
{ "repetier", htRepetier },
- { "mks", htMKS }
+ { "mks", htMKS },
+ { "prusaconnectnew", htPrusaConnectNew },
+
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrintHostType)
@@ -393,6 +395,7 @@ void PrintConfigDef::init_common_params()
def = this->add("printhost_password", coString);
def->label = L("Password");
// def->tooltip = L("");
+ def->gui_type = ConfigOptionDef::GUIType::password;
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index c14230d4f4..c48a8d6bb9 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -65,7 +65,7 @@ enum class MachineLimitsUsage {
};
enum PrintHostType {
- htPrusaLink, htPrusaConnect, htOctoPrint, htMoonraker, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS
+ htPrusaLink, htPrusaConnect, htOctoPrint, htMoonraker, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htPrusaConnectNew
};
enum AuthorizationType {
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index fea794c943..479144dab9 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -19,6 +19,16 @@ set(SLIC3R_GUI_SOURCES
GUI/AboutDialog.hpp
GUI/ArrangeSettingsDialogImgui.hpp
GUI/ArrangeSettingsDialogImgui.cpp
+ GUI/UserAccountCommunication.cpp
+ GUI/UserAccountCommunication.hpp
+ GUI/UserAccountSession.cpp
+ GUI/UserAccountSession.hpp
+ GUI/UserAccount.cpp
+ GUI/UserAccount.hpp
+ GUI/WebViewDialog.cpp
+ GUI/WebViewDialog.hpp
+ GUI/WebView.cpp
+ GUI/WebView.hpp
GUI/SysInfoDialog.cpp
GUI/SysInfoDialog.hpp
GUI/KBShortcutsDialog.cpp
@@ -242,6 +252,8 @@ set(SLIC3R_GUI_SOURCES
GUI/DoubleSlider.hpp
GUI/Notebook.cpp
GUI/Notebook.hpp
+ GUI/TopBar.cpp
+ GUI/TopBar.hpp
GUI/ObjectDataViewModel.cpp
GUI/ObjectDataViewModel.hpp
GUI/InstanceCheck.cpp
@@ -289,6 +301,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Downloader.hpp
GUI/DownloaderFileGet.cpp
GUI/DownloaderFileGet.hpp
+ GUI/LoginDialog.cpp
+ GUI/LoginDialog.hpp
Utils/AppUpdater.cpp
Utils/AppUpdater.hpp
Utils/Http.cpp
@@ -335,6 +349,10 @@ set(SLIC3R_GUI_SOURCES
Utils/WxFontUtils.hpp
Utils/WifiScanner.hpp
Utils/WifiScanner.cpp
+ Utils/Secrets.hpp
+ Utils/Secrets.cpp
+ Utils/PrusaConnect.hpp
+ Utils/PrusaConnect.cpp
)
find_package(NanoSVG REQUIRED)
diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp
index 596fa2b697..26e1ddbe5e 100644
--- a/src/slic3r/GUI/ConfigWizard.cpp
+++ b/src/slic3r/GUI/ConfigWizard.cpp
@@ -588,7 +588,7 @@ PageWelcome::PageWelcome(ConfigWizard *parent)
{
welcome_text->Hide();
cbox_reset->Hide();
- cbox_integrate->Hide();
+ cbox_integrate->Hide();
}
void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason)
@@ -1536,11 +1536,13 @@ bool PageDownloader::on_finish_downloader() const
return m_downloader->on_finish();
}
-bool DownloaderUtils::Worker::perform_register(const std::string& path_override/* = {}*/)
+#ifdef __linux__
+bool DownloaderUtils::Worker::perform_registration_linux = false;
+#endif // __linux__
+
+bool DownloaderUtils::Worker::perform_register(const std::string& path)
{
- boost::filesystem::path aux_dest (GUI::into_u8(path_name()));
- if (!path_override.empty())
- aux_dest = boost::filesystem::path(path_override);
+ boost::filesystem::path aux_dest (path);
boost::system::error_code ec;
boost::filesystem::path chosen_dest = boost::filesystem::absolute(aux_dest, ec);
if(ec)
@@ -1549,7 +1551,7 @@ bool DownloaderUtils::Worker::perform_register(const std::string& path_override/
if (chosen_dest.empty() || !boost::filesystem::is_directory(chosen_dest, ec) || ec) {
std::string err_msg = GUI::format("%1%\n\n%2%",_L("Chosen directory for downloads does not exist.") ,chosen_dest.string());
BOOST_LOG_TRIVIAL(error) << err_msg;
- show_error(m_parent, err_msg);
+ show_error(/*m_parent*/ nullptr, err_msg);
return false;
}
BOOST_LOG_TRIVIAL(info) << "Downloader registration: Directory for downloads: " << chosen_dest.string();
@@ -1613,12 +1615,12 @@ bool DownloaderUtils::Worker::on_finish() {
BOOST_LOG_TRIVIAL(debug) << "PageDownloader::on_finish_downloader ac_value " << ac_value << " downloader_checked " << downloader_checked;
if (ac_value && downloader_checked) {
// already registered but we need to do it again
- if (!perform_register())
+ if (!perform_register(GUI::into_u8(path_name())))
return false;
app_config->set("downloader_url_registered", "1");
} else if (!ac_value && downloader_checked) {
// register
- if (!perform_register())
+ if (!perform_register(GUI::into_u8(path_name())))
return false;
app_config->set("downloader_url_registered", "1");
} else if (ac_value && !downloader_checked) {
@@ -2883,7 +2885,6 @@ bool ConfigWizard::priv::check_and_install_missing_materials(Technology technolo
const auto ask_and_select_default_materials = [this](const wxString &message, const std::set &printer_models, Technology technology)
{
- //wxMessageDialog msg(q, message, _L("Notice"), wxYES_NO);
MessageDialog msg(q, message, _L("Notice"), wxYES_NO);
if (msg.ShowModal() == wxID_YES)
select_default_materials_for_printer_models(technology, printer_models);
@@ -3065,10 +3066,10 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
#ifdef __linux__
// Desktop integration on Linux
- BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << page_downloader->m_downloader->get_perform_registration_linux();
+ BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << DownloaderUtils::Worker::perform_registration_linux;
if (page_welcome->integrate_desktop())
DesktopIntegrationDialog::perform_desktop_integration();
- if (page_downloader->m_downloader->get_perform_registration_linux())
+ if (DownloaderUtils::Worker::perform_registration_linux)
DesktopIntegrationDialog::perform_downloader_desktop_integration();
#endif
diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp
index 43e3b103af..1425b7cfa4 100644
--- a/src/slic3r/GUI/ConfigWizard.hpp
+++ b/src/slic3r/GUI/ConfigWizard.hpp
@@ -31,9 +31,6 @@ namespace DownloaderUtils {
wxWindow* m_parent{ nullptr };
wxTextCtrl* m_input_path{ nullptr };
bool downloader_checked{ false };
-#ifdef __linux__
- bool perform_registration_linux{ false };
-#endif // __linux__
void deregister();
@@ -49,9 +46,9 @@ namespace DownloaderUtils {
void set_path_name(const std::string& name);
bool on_finish();
- bool perform_register(const std::string& path_override = {});
+ static bool perform_register(const std::string& path);
#ifdef __linux__
- bool get_perform_registration_linux() { return perform_registration_linux; }
+ static bool perform_registration_linux;
#endif // __linux__
};
}
diff --git a/src/slic3r/GUI/DownloaderFileGet.cpp b/src/slic3r/GUI/DownloaderFileGet.cpp
index ef9b0256e0..7a7c7044c2 100644
--- a/src/slic3r/GUI/DownloaderFileGet.cpp
+++ b/src/slic3r/GUI/DownloaderFileGet.cpp
@@ -170,7 +170,7 @@ void FileGet::priv::get_perform()
m_tmp_path = m_dest_folder / (m_filename + "." + std::to_string(get_current_pid()) + ".download");
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_NAME_CHANGE);
- evt->SetString(boost::nowide::widen(m_filename));
+ evt->SetString(from_u8(m_filename));
evt->SetInt(m_id);
m_evt_handler->QueueEvent(evt);
}
diff --git a/src/slic3r/GUI/FrequentlyChangedParameters.hpp b/src/slic3r/GUI/FrequentlyChangedParameters.hpp
index 08d07afa05..0be8fb0415 100644
--- a/src/slic3r/GUI/FrequentlyChangedParameters.hpp
+++ b/src/slic3r/GUI/FrequentlyChangedParameters.hpp
@@ -17,6 +17,8 @@
#ifndef slic3r_FreqChangedParams_hpp_
#define slic3r_FreqChangedParams_hpp_
+#include
+
#include "Event.hpp"
class wxButton;
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 6bbe209f32..0c129803a5 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -2858,7 +2858,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
int keyCode = evt.GetKeyCode();
int ctrlMask = wxMOD_CONTROL;
int shiftMask = wxMOD_SHIFT;
- if (keyCode == WXK_ESCAPE && (_deactivate_undo_redo_toolbar_items() || _deactivate_search_toolbar_item() || _deactivate_arrange_menu()))
+ if (keyCode == WXK_ESCAPE && (_deactivate_undo_redo_toolbar_items() || _deactivate_arrange_menu()))
return;
if (m_gizmos.on_char(evt)) {
@@ -2890,14 +2890,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
#endif /* __APPLE__ */
post_event(SimpleEvent(EVT_GLTOOLBAR_COPY));
break;
-#ifdef __APPLE__
- case 'f':
- case 'F':
-#else /* __APPLE__ */
- case WXK_CONTROL_F:
-#endif /* __APPLE__ */
- _activate_search_toolbar_item();
- break;
#ifdef __APPLE__
case 'm':
case 'M':
@@ -3354,10 +3346,9 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
}
}
- // If the Search window or Undo/Redo list is opened,
+ // If Undo/Redo list is opened,
// update them according to the event
- if (m_main_toolbar.is_item_pressed("search") ||
- m_undoredo_toolbar.is_item_pressed("undo") ||
+ if (m_undoredo_toolbar.is_item_pressed("undo") ||
m_undoredo_toolbar.is_item_pressed("redo")) {
m_mouse_wheel = int((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta());
return;
@@ -3664,7 +3655,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_dirty = true;
}
else if (evt.LeftDown() || evt.RightDown() || evt.MiddleDown()) {
- if (_deactivate_undo_redo_toolbar_items() || _deactivate_search_toolbar_item() || _deactivate_arrange_menu())
+ if (_deactivate_undo_redo_toolbar_items() || _deactivate_arrange_menu())
return;
// If user pressed left or right button we first check whether this happened
@@ -4765,73 +4756,6 @@ bool GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x)
return action_taken;
}
-// Getter for the const char*[] for the search list
-static bool search_string_getter(int idx, const char** label, const char** tooltip)
-{
- const Search::OptionsSearcher& search_list = wxGetApp().searcher();
- if (0 <= idx && (size_t)idx < search_list.size()) {
- search_list[idx].get_marked_label_and_tooltip(label, tooltip);
- return true;
- }
- return false;
-}
-
-bool GLCanvas3D::_render_search_list(float pos_x)
-{
- bool action_taken = false;
- ImGuiWrapper* imgui = wxGetApp().imgui();
-
- imgui->set_next_window_pos(pos_x, m_main_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f);
- std::string title = L("Search");
- imgui->begin(_(title), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
-
- int selected = -1;
- bool edited = false;
- float em = static_cast(wxGetApp().em_unit());
-#if ENABLE_RETINA_GL
- em *= m_retina_helper->get_scale_factor();
-#endif // ENABLE_RETINA_GL
-
- // update searcher before show imGui search dialog on the plater, if printer technology or mode was changed
- wxGetApp().check_and_update_searcher(wxGetApp().get_mode());
- Search::OptionsSearcher& searcher = wxGetApp().searcher();
-
- std::string& search_line = searcher.search_string();
- char *s = new char[255];
- strcpy(s, search_line.empty() ? _u8L("Enter a search term").c_str() : search_line.c_str());
-
- imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s,
- wxGetApp().searcher().view_params,
- selected, edited, m_mouse_wheel, wxGetApp().is_localized());
-
- search_line = s;
- delete [] s;
- if (search_line == _u8L("Enter a search term"))
- search_line.clear();
-
- if (edited)
- searcher.search();
-
- if (selected >= 0) {
- // selected == 9999 means that Esc kye was pressed
- /*// revert commit https://github.com/prusa3d/PrusaSlicer/commit/91897589928789b261ca0dc735ffd46f2b0b99f2
- if (selected == 9999)
- action_taken = true;
- else
- sidebar.jump_to_option(selected);*/
- if (selected != 9999) {
- imgui->end(); // end imgui before the jump to option
- wxGetApp().jump_to_option(selected);
- return true;
- }
- action_taken = true;
- }
-
- imgui->end();
-
- return action_taken;
-}
-
bool GLCanvas3D::_render_arrange_menu(float pos_x)
{
m_arrange_settings_dialog.render(pos_x, m_main_toolbar.get_height());
@@ -5395,30 +5319,6 @@ bool GLCanvas3D::_init_main_toolbar()
if (!m_main_toolbar.add_item(item))
return false;
- /*
- if (!m_main_toolbar.add_separator())
- return false;
- */
-
- item.name = "search";
- item.icon_filename = "search_.svg";
- item.tooltip = _u8L("Search") + " [" + GUI::shortkey_ctrl_prefix() + "F]";
- item.sprite_id = 11;
- item.left.toggable = true;
- item.left.render_callback = [this](float left, float right, float, float) {
- if (m_canvas != nullptr) {
- if (!m_canvas->HasFocus())
- m_canvas->SetFocus();
- if (_render_search_list(0.5f * (left + right)))
- _deactivate_search_toolbar_item();
- }
- };
- item.left.action_callback = GLToolbarItem::Default_Action_Callback;
- item.visibility_callback = GLToolbarItem::Default_Visibility_Callback;
- item.enabling_callback = [this]()->bool { return m_gizmos.get_current_type() == GLGizmosManager::Undefined; };
- if (!m_main_toolbar.add_item(item))
- return false;
-
if (!m_main_toolbar.add_separator())
return false;
@@ -7653,11 +7553,6 @@ bool GLCanvas3D::_deactivate_undo_redo_toolbar_items()
return false;
}
-bool GLCanvas3D::is_search_pressed() const
-{
- return m_main_toolbar.is_item_pressed("search");
-}
-
bool GLCanvas3D::_deactivate_arrange_menu()
{
if (m_main_toolbar.is_item_pressed("arrange")) {
@@ -7668,26 +7563,6 @@ bool GLCanvas3D::_deactivate_arrange_menu()
return false;
}
-bool GLCanvas3D::_deactivate_search_toolbar_item()
-{
- if (is_search_pressed()) {
- m_main_toolbar.force_left_action(m_main_toolbar.get_item_id("search"), *this);
- return true;
- }
-
- return false;
-}
-
-bool GLCanvas3D::_activate_search_toolbar_item()
-{
- if (!m_main_toolbar.is_item_pressed("search")) {
- m_main_toolbar.force_left_action(m_main_toolbar.get_item_id("search"), *this);
- return true;
- }
-
- return false;
-}
-
bool GLCanvas3D::_deactivate_collapse_toolbar_items()
{
GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar();
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 0660ca7cc9..5f714aae30 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -780,7 +780,6 @@ public:
bool is_layers_editing_enabled() const;
bool is_layers_editing_allowed() const;
- bool is_search_pressed() const;
void reset_layer_height_profile();
void adaptive_layer_height_profile(float quality_factor);
@@ -1064,7 +1063,6 @@ private:
void _render_sla_slices();
void _render_selection_sidebar_hints();
bool _render_undo_redo_stack(const bool is_undo, float pos_x);
- bool _render_search_list(float pos_x);
bool _render_arrange_menu(float pos_x);
void _render_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, const GLVolumeCollection& volumes, Camera::EType camera_type);
// render thumbnail using an off-screen framebuffer
@@ -1113,8 +1111,6 @@ private:
void _update_selection_from_hover();
bool _deactivate_undo_redo_toolbar_items();
- bool _deactivate_search_toolbar_item();
- bool _activate_search_toolbar_item();
bool _deactivate_collapse_toolbar_items();
bool _deactivate_arrange_menu();
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index a5646bb52e..115eba8351 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -98,9 +98,13 @@
#include "Downloader.hpp"
#include "PhysicalPrinterDialog.hpp"
#include "WifiConfigDialog.hpp"
+#include "UserAccount.hpp"
+#include "WebViewDialog.hpp"
+#include "LoginDialog.hpp"
#include "BitmapCache.hpp"
-#include "Notebook.hpp"
+//#include "Notebook.hpp"
+#include "TopBar.hpp"
#ifdef __WXMSW__
#include
@@ -1109,6 +1113,10 @@ void GUI_App::jump_to_option(const std::string& composite_key)
void GUI_App::show_search_dialog()
{
+ // To avoid endless loop caused by mutual lose focuses from serch_input and search_dialog
+ // invoke killFocus for serch_input by set focus to tab_panel
+ GUI::wxGetApp().tab_panel()->SetFocus();
+
check_and_update_searcher(get_mode());
m_searcher->show_dialog();
}
@@ -1401,6 +1409,8 @@ bool GUI_App::on_init_inner()
update_mode(); // update view mode after fix of the object_list size
+ show_printer_webview_tab();
+
#ifdef __APPLE__
other_instance_message_handler()->bring_instance_forward();
#endif //__APPLE__
@@ -1516,16 +1526,16 @@ void GUI_App::init_ui_colours()
m_mode_palette = get_mode_default_palette();
bool is_dark_mode = dark_mode();
-#ifdef _WIN32
+//#ifdef _WIN32
m_color_label_default = is_dark_mode ? wxColour(250, 250, 250): wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
m_color_highlight_label_default = is_dark_mode ? wxColour(230, 230, 230): wxSystemSettings::GetColour(/*wxSYS_COLOUR_HIGHLIGHTTEXT*/wxSYS_COLOUR_WINDOWTEXT);
m_color_highlight_default = is_dark_mode ? wxColour(78, 78, 78) : wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
m_color_hovered_btn_label = is_dark_mode ? wxColour(253, 111, 40) : wxColour(252, 77, 1);
m_color_default_btn_label = is_dark_mode ? wxColour(255, 181, 100): wxColour(203, 61, 0);
m_color_selected_btn_bg = is_dark_mode ? wxColour(95, 73, 62) : wxColour(228, 220, 216);
-#else
- m_color_label_default = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
-#endif
+//#else
+// m_color_label_default = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
+//#endif
m_color_window_default = is_dark_mode ? wxColour(43, 43, 43) : wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
}
@@ -1561,12 +1571,13 @@ void GUI_App::update_label_colours()
tab->update_label_colours();
}
-#ifdef _WIN32
+#if 0//def _WIN32
static bool is_focused(HWND hWnd)
{
HWND hFocusedWnd = ::GetFocus();
return hFocusedWnd && hWnd == hFocusedWnd;
}
+#endif
static bool is_default(wxWindow* win)
{
@@ -1576,7 +1587,6 @@ static bool is_default(wxWindow* win)
return win == tlw->GetDefaultItem();
}
-#endif
void GUI_App::UpdateDarkUI(wxWindow* window, bool highlited/* = false*/, bool just_font/* = false*/)
{
@@ -1589,6 +1599,7 @@ void GUI_App::UpdateDarkUI(wxWindow* window, bool highlited/* = false*/, bool ju
highlited = true;
}
// button marking
+ if (!dynamic_cast(window->GetParent())) // don't marking the button if it is from TopBar
{
auto mark_button = [this, btn, highlited](const bool mark) {
if (btn->GetLabel().IsEmpty())
@@ -1601,12 +1612,12 @@ void GUI_App::UpdateDarkUI(wxWindow* window, bool highlited/* = false*/, bool ju
// hovering
btn->Bind(wxEVT_ENTER_WINDOW, [mark_button](wxMouseEvent& event) { mark_button(true); event.Skip(); });
- btn->Bind(wxEVT_LEAVE_WINDOW, [mark_button, btn](wxMouseEvent& event) { mark_button(is_focused(btn->GetHWND())); event.Skip(); });
+ btn->Bind(wxEVT_LEAVE_WINDOW, [mark_button, btn](wxMouseEvent& event) { mark_button(btn->HasFocus()); event.Skip(); });
// focusing
btn->Bind(wxEVT_SET_FOCUS, [mark_button](wxFocusEvent& event) { mark_button(true); event.Skip(); });
btn->Bind(wxEVT_KILL_FOCUS, [mark_button](wxFocusEvent& event) { mark_button(false); event.Skip(); });
- is_focused_button = is_focused(btn->GetHWND());
+ is_focused_button = btn->HasFocus();// is_focused(btn->GetHWND());
is_default_button = is_default(btn);
if (is_focused_button || is_default_button)
mark_button(is_focused_button);
@@ -1824,7 +1835,7 @@ bool GUI_App::suppress_round_corners() const
wxSize GUI_App::get_min_size(wxWindow* display_win) const
{
- wxSize min_size(76*m_em_unit, 49 * m_em_unit);
+ wxSize min_size(120 * m_em_unit, 49 * m_em_unit);
const wxDisplay display = wxDisplay(display_win);
wxRect display_rect = display.GetGeometry();
@@ -2468,10 +2479,8 @@ void GUI_App::update_mode()
{
sidebar().update_mode();
-#ifdef _WIN32 //_MSW_DARK_MODE
if (!wxGetApp().tabs_as_menu())
- dynamic_cast(mainframe->m_tabpanel)->UpdateMode();
-#endif
+ dynamic_cast(mainframe->m_tabpanel)->UpdateMode();
for (auto tab : tabs_list)
tab->update_mode();
@@ -2480,7 +2489,7 @@ void GUI_App::update_mode()
plater()->canvas3D()->update_gizmos_on_off_state();
}
-void GUI_App::add_config_menu(wxMenuBar *menu)
+wxMenu* GUI_App::get_config_menu()
{
auto local_menu = new wxMenu();
wxWindowID config_id_base = wxWindow::NewControlId(int(ConfigMenuCnt));
@@ -2507,20 +2516,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
"\tCtrl+P",
#endif
_L("Application preferences"));
- wxMenu* mode_menu = nullptr;
- if (is_editor()) {
- local_menu->AppendSeparator();
- mode_menu = new wxMenu();
- mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _L("Simple"), _L("Simple View Mode"));
-// mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _L("Advanced"), _L("Advanced View Mode"));
- mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _CTX("Advanced", "Mode"), _L("Advanced View Mode"));
- mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert, _L("Expert"), _L("Expert View Mode"));
- Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if (get_mode() == comSimple) evt.Check(true); }, config_id_base + ConfigMenuModeSimple);
- Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if (get_mode() == comAdvanced) evt.Check(true); }, config_id_base + ConfigMenuModeAdvanced);
- Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if (get_mode() == comExpert) evt.Check(true); }, config_id_base + ConfigMenuModeExpert);
- local_menu->AppendSubMenu(mode_menu, _L("Mode"), wxString::Format(_L("%s View Mode"), SLIC3R_APP_NAME));
- }
local_menu->AppendSeparator();
local_menu->Append(config_id_base + ConfigMenuLanguage, _L("&Language"));
if (is_editor()) {
@@ -2646,16 +2642,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
}
});
- using std::placeholders::_1;
-
- if (mode_menu != nullptr) {
- auto modfn = [this](int mode, wxCommandEvent&) { if (get_mode() != mode) save_mode(mode); };
- mode_menu->Bind(wxEVT_MENU, std::bind(modfn, comSimple, _1), config_id_base + ConfigMenuModeSimple);
- mode_menu->Bind(wxEVT_MENU, std::bind(modfn, comAdvanced, _1), config_id_base + ConfigMenuModeAdvanced);
- mode_menu->Bind(wxEVT_MENU, std::bind(modfn, comExpert, _1), config_id_base + ConfigMenuModeExpert);
- }
-
- menu->Append(local_menu, _L("&Configuration"));
+ return local_menu;
}
void GUI_App::open_preferences(const std::string& highlight_option /*= std::string()*/, const std::string& tab_name/*= std::string()*/)
@@ -2690,7 +2677,7 @@ void GUI_App::open_preferences(const std::string& highlight_option /*= std::stri
if (mainframe->preferences_dialog->settings_layout_changed()) {
// hide full main_sizer for mainFrame
mainframe->GetSizer()->Show(false);
- mainframe->update_layout();
+ mainframe->update_layout();
mainframe->select_tab(size_t(0));
}
}
@@ -3016,7 +3003,15 @@ void GUI_App::MacOpenURL(const wxString& url)
BOOST_LOG_TRIVIAL(error) << "Recieved command to open URL, but it is not allowed in app configuration. URL: " << url;
return;
}
- start_download(into_u8(url));
+
+ std::string narrow_url = into_u8(url);
+ if (boost::starts_with(narrow_url, "prusaslicer://open?file=")) {
+ start_download(std::move(narrow_url));
+ } else if (boost::starts_with(narrow_url, "prusaslicer://login")) {
+ plater()->get_user_account()->on_login_code_recieved(std::move(narrow_url));
+ } else {
+ BOOST_LOG_TRIVIAL(error) << "MacOpenURL recieved improper URL: " << url;
+ }
}
#endif /* __APPLE */
@@ -3147,6 +3142,15 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage
{
wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null");
+ if (!plater()->get_user_account()->is_logged()) {
+ m_login_dialog = std::make_unique(mainframe, plater()->get_user_account());
+ m_login_dialog->ShowModal();
+ mainframe->RemoveChild(m_login_dialog.get());
+ m_login_dialog->Destroy();
+ // Destructor does not call Destroy
+ m_login_dialog.reset();
+ }
+
if (reason == ConfigWizard::RR_USER) {
// Cancel sync before starting wizard to prevent two downloads at same time
preset_updater->cancel_sync();
@@ -3176,6 +3180,14 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage
return res;
}
+void GUI_App::update_login_dialog()
+{
+ if (!m_login_dialog) {
+ return;
+ }
+ m_login_dialog->update_account();
+}
+
void GUI_App::show_desktop_integration_dialog()
{
#ifdef __linux__
@@ -3199,7 +3211,7 @@ void GUI_App::show_downloader_registration_dialog()
auto downloader_worker = new DownloaderUtils::Worker(nullptr);
downloader_worker->perform_register(app_config->get("url_downloader_dest"));
#ifdef __linux__
- if (downloader_worker->get_perform_registration_linux())
+ if (DownloaderUtils::Worker::perform_registration_linux)
DesktopIntegrationDialog::perform_downloader_desktop_integration();
#endif // __linux__
} else {
@@ -3431,6 +3443,18 @@ bool GUI_App::open_browser_with_warning_dialog(const wxString& url, wxWindow* pa
return launch && wxLaunchDefaultBrowser(url, flags);
}
+bool GUI_App::open_login_browser_with_dialog(const wxString& url, wxWindow* parent/* = nullptr*/, int flags/* = 0*/)
+{
+ bool auth_login_dialog_confirmed = app_config->get_bool("auth_login_dialog_confirmed");
+ if (!auth_login_dialog_confirmed) {
+ RichMessageDialog dialog(parent, _L("Open default browser with Prusa Account Log in page?\n(On Yes, You will not be asked again.)"), _L("PrusaSlicer: Open Log in page"), wxICON_QUESTION | wxYES_NO);
+ if (dialog.ShowModal() != wxID_YES)
+ return false;
+ app_config->set("auth_login_dialog_confirmed", "1");
+ }
+ return wxLaunchDefaultBrowser(url, flags);
+}
+
// static method accepting a wxWindow object as first parameter
// void warning_catcher{
// my($self, $message_dialog) = @_;
@@ -3626,5 +3650,92 @@ void GUI_App::open_wifi_config_dialog(bool forced, const wxString& drive_path/*
m_wifi_config_dialog_shown = false;
}
+bool GUI_App::select_printer_from_connect(const Preset* preset)
+{
+ assert(preset);
+
+ bool is_installed{ false };
+
+ // When physical printer is selected, it somehow remains selected in printer tab
+ // TabPresetComboBox::update() looks at physical_printers and if some has selected = true, it overrides the selection.
+ // This might be, because OnSelect event callback is not triggered
+ if(preset_bundle->physical_printers.get_selected_printer_config()) {
+ preset_bundle->physical_printers.unselect_printer();
+ }
+
+ if (!preset->is_visible) {
+ size_t preset_id = preset_bundle->printers.get_preset_idx_by_name(preset->name);
+ assert(preset_id != size_t(-1));
+ preset_bundle->printers.select_preset(preset_id);
+ is_installed = true;
+ }
+
+ get_tab(Preset::Type::TYPE_PRINTER)->select_preset(preset->name);
+ return is_installed;
+}
+
+void GUI_App::handle_connect_request_printer_pick(std::string msg)
+{
+ BOOST_LOG_TRIVIAL(error) << "Handling web request: " << msg;
+ // return to plater
+ this->mainframe->select_tab(size_t(0));
+ // parse message
+ std::vector compatible_printers;
+ plater()->get_user_account()->fill_compatible_printers_from_json(msg, compatible_printers);
+ std::string model_name;
+ if (compatible_printers.empty()) {
+ // TODO: This should go away when compatible printers gives right information.
+ model_name = plater()->get_user_account()->get_model_from_json(msg);
+ } else {
+ model_name = compatible_printers.front();
+ }
+ std::string nozzle = plater()->get_user_account()->get_nozzle_from_json(msg);
+ assert(!model_name.empty());
+ if (model_name.empty())
+ return;
+
+ // select printer
+ const Preset* preset = preset_bundle->printers.find_system_preset_by_model_and_variant(model_name, nozzle);
+ bool is_installed = preset && select_printer_from_connect(preset);
+ // notification
+ std::string out = preset ?
+ (is_installed ? GUI::format(_L("Installed and Select Printer:\n%1%"), preset->name) :
+ GUI::format(_L("Select Printer:\n%1%"), preset->name) ):
+ GUI::format(_L("Printer not found:\n%1%"), model_name);
+ this->plater()->get_notification_manager()->close_notification_of_type(NotificationType::UserAccountID);
+ this->plater()->get_notification_manager()->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, out);
+}
+
+void GUI_App::show_printer_webview_tab()
+{
+ //bool show, const DynamicPrintConfig& dpc
+
+ if (DynamicPrintConfig* dpc = preset_bundle->physical_printers.get_selected_printer_config(); dpc == nullptr) {
+ this->mainframe->select_tab(size_t(0));
+ mainframe->remove_printer_webview_tab();
+ } else {
+ std::string url = dpc->opt_string("print_host");
+
+ if (url.find("http://") != 0 && url.find("https://") != 0) {
+ url = "http://" + url;
+ }
+
+ // set password / api key
+ if (dynamic_cast*>(dpc->option("printhost_authorization_type"))->value == AuthorizationType::atKeyPassword) {
+ mainframe->set_printer_webview_api_key(dpc->opt_string("printhost_apikey"));
+ }
+#if 0 // The user password authentication is not working in prusa link as of now.
+ else {
+ mainframe->set_printer_webview_credentials(dpc->opt_string("printhost_user"), dpc->opt_string("printhost_password"));
+ }
+#endif // 0
+ // add printer or change url
+ if (mainframe->get_printer_webview_tab_added()) {
+ mainframe->set_printer_webview_tab_url(from_u8(url));
+ } else {
+ mainframe->add_printer_webview_tab(from_u8(url));
+ }
+ }
+}
} // GUI
} //Slic3r
diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp
index 6d14123af3..50c8c25773 100644
--- a/src/slic3r/GUI/GUI_App.hpp
+++ b/src/slic3r/GUI/GUI_App.hpp
@@ -59,7 +59,7 @@ class NotificationManager;
class Downloader;
struct GUI_InitParams;
class GalleryDialog;
-
+class LoginDialog;
enum FileType
@@ -142,12 +142,13 @@ private:
wxColour m_color_label_sys;
wxColour m_color_label_default;
wxColour m_color_window_default;
-#ifdef _WIN32
+//#ifdef _WIN32
wxColour m_color_highlight_label_default;
wxColour m_color_hovered_btn_label;
wxColour m_color_default_btn_label;
wxColour m_color_highlight_default;
wxColour m_color_selected_btn_bg;
+#ifdef _WIN32
bool m_force_colors_update { false };
#endif
std::vector m_mode_palette;
@@ -247,7 +248,7 @@ public:
std::vector get_mode_palette();
void set_mode_palette(const std::vector &palette);
-#ifdef _WIN32
+//#ifdef _WIN32
const wxColour& get_label_highlight_clr() { return m_color_highlight_label_default; }
const wxColour& get_highlight_default_clr() { return m_color_highlight_default; }
const wxColour& get_color_hovered_btn_label() { return m_color_hovered_btn_label; }
@@ -256,7 +257,7 @@ public:
#ifdef _MSW_DARK_MODE
void force_menu_update();
#endif //_MSW_DARK_MODE
-#endif
+//#endif
const wxFont& small_font() { return m_small_font; }
const wxFont& bold_font() { return m_bold_font; }
@@ -293,7 +294,7 @@ public:
bool save_mode(const /*ConfigOptionMode*/int mode) ;
void update_mode();
- void add_config_menu(wxMenuBar *menu);
+ wxMenu* get_config_menu();
bool has_unsaved_preset_changes() const;
bool has_current_preset_changes() const;
void update_saved_preset_from_current_preset();
@@ -317,6 +318,7 @@ public:
// Calls wxLaunchDefaultBrowser if user confirms in dialog.
// Add "Rememeber my choice" checkbox to question dialog, when it is forced or a "suppress_hyperlinks" option has empty value
bool open_browser_with_warning_dialog(const wxString& url, wxWindow* parent = nullptr, bool force_remember_choice = true, int flags = 0);
+ bool open_login_browser_with_dialog(const wxString& url, wxWindow* parent = nullptr, int flags = 0);
#ifdef __APPLE__
void OSXStoreOpenFiles(const wxArrayString &files) override;
// wxWidgets override to get an event on open files.
@@ -369,6 +371,7 @@ public:
void open_web_page_localized(const std::string &http_address);
bool may_switch_to_SLA_preset(const wxString& caption);
bool run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page = ConfigWizard::SP_WELCOME);
+ void update_login_dialog();
void show_desktop_integration_dialog();
void show_downloader_registration_dialog();
@@ -397,6 +400,26 @@ public:
void open_wifi_config_dialog(bool forced, const wxString& drive_path = {});
bool get_wifi_config_dialog_shown() const { return m_wifi_config_dialog_shown; }
+
+ void request_login(bool show_user_info = false) {}
+ bool check_login() { return false; }
+ void get_login_info() {}
+ bool is_user_login() { return true; }
+
+ void request_user_login(int online_login) {}
+ void request_user_logout() {}
+ int request_user_unbind(std::string dev_id) { return 0; }
+ void handle_connect_request_printer_pick(std::string cmd);
+ void show_printer_webview_tab();
+ // return true if preset vas invisible and we have to installed it to make it selectable
+ bool select_printer_from_connect(const Preset* printer_preset);
+ void handle_script_message(std::string msg) {}
+ void request_model_download(std::string import_json) {}
+ void download_project(std::string project_id) {}
+ void request_project_download(std::string project_id) {}
+ void request_open_project(std::string project_id) {}
+ void request_remove_project(std::string project_id) {}
+
private:
bool on_init_inner();
void init_app_config();
@@ -419,6 +442,12 @@ private:
void app_version_check(bool from_user);
bool m_wifi_config_dialog_shown { false };
+ bool m_wifi_config_dialog_was_declined { false };
+ // change to vector of items when adding more items that require update
+ //wxMenuItem* m_login_config_menu_item { nullptr };
+ std::map< ConfigMenuIDs, wxMenuItem*> m_config_menu_updatable_items;
+
+ std::unique_ptr m_login_dialog;
};
DECLARE_APP(GUI_App)
diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp
index 45381e8f75..bed6c6368e 100644
--- a/src/slic3r/GUI/GUI_Factories.cpp
+++ b/src/slic3r/GUI/GUI_Factories.cpp
@@ -1463,6 +1463,8 @@ void MenuFactory::sys_color_changed()
void MenuFactory::sys_color_changed(wxMenuBar* menubar)
{
+ if (!menubar)
+ return;
for (size_t id = 0; id < menubar->GetMenuCount(); id++) {
wxMenu* menu = menubar->GetMenu(id);
sys_color_changed_menu(menu);
diff --git a/src/slic3r/GUI/InstanceCheck.cpp b/src/slic3r/GUI/InstanceCheck.cpp
index a8f8adfdc9..595b71fd1a 100644
--- a/src/slic3r/GUI/InstanceCheck.cpp
+++ b/src/slic3r/GUI/InstanceCheck.cpp
@@ -378,6 +378,7 @@ namespace GUI {
wxDEFINE_EVENT(EVT_LOAD_MODEL_OTHER_INSTANCE, LoadFromOtherInstanceEvent);
wxDEFINE_EVENT(EVT_START_DOWNLOAD_OTHER_INSTANCE, StartDownloadOtherInstanceEvent);
+wxDEFINE_EVENT(EVT_LOGIN_OTHER_INSTANCE, LoginOtherInstanceEvent);
wxDEFINE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent);
void OtherInstanceMessageHandler::init(wxEvtHandler* callback_evt_handler)
@@ -520,6 +521,9 @@ void OtherInstanceMessageHandler::handle_message(const std::string& message)
else if (it->rfind("prusaslicer://open?file=", 0) == 0)
#endif
downloads.emplace_back(*it);
+ else if (it->rfind("prusaslicer://login", 0) == 0) {
+ wxPostEvent(m_callback_evt_handler, LoginOtherInstanceEvent(GUI::EVT_LOGIN_OTHER_INSTANCE, std::string(*it)));
+ }
}
if (! paths.empty()) {
//wxEvtHandler* evt_handler = wxGetApp().plater(); //assert here?
diff --git a/src/slic3r/GUI/InstanceCheck.hpp b/src/slic3r/GUI/InstanceCheck.hpp
index 3d9d2e0fb2..d70f4a90a5 100644
--- a/src/slic3r/GUI/InstanceCheck.hpp
+++ b/src/slic3r/GUI/InstanceCheck.hpp
@@ -48,8 +48,10 @@ class MainFrame;
using LoadFromOtherInstanceEvent = Event>;
using StartDownloadOtherInstanceEvent = Event>;
+using LoginOtherInstanceEvent = Event;
wxDECLARE_EVENT(EVT_LOAD_MODEL_OTHER_INSTANCE, LoadFromOtherInstanceEvent);
wxDECLARE_EVENT(EVT_START_DOWNLOAD_OTHER_INSTANCE, StartDownloadOtherInstanceEvent);
+wxDECLARE_EVENT(EVT_LOGIN_OTHER_INSTANCE, LoginOtherInstanceEvent);
using InstanceGoToFrontEvent = SimpleEvent;
wxDECLARE_EVENT(EVT_INSTANCE_GO_TO_FRONT, InstanceGoToFrontEvent);
diff --git a/src/slic3r/GUI/LoginDialog.cpp b/src/slic3r/GUI/LoginDialog.cpp
new file mode 100644
index 0000000000..f106ddd61f
--- /dev/null
+++ b/src/slic3r/GUI/LoginDialog.cpp
@@ -0,0 +1,95 @@
+#include "LoginDialog.hpp"
+
+#include "GUI_App.hpp"
+#include "wxExtensions.hpp"
+#include "GUI.hpp"
+#include "I18N.hpp"
+#include "format.hpp"
+
+namespace Slic3r {
+namespace GUI {
+
+LoginDialog::LoginDialog(wxWindow* parent, UserAccount* user_account)
+ // TRN: This is the dialog title.
+ : DPIDialog(parent, wxID_ANY, _L("Prusa Account"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+ , p_user_account(user_account)
+{
+ const int em = wxGetApp().em_unit();
+ bool logged = p_user_account->is_logged();
+ wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
+ // sizer with black border
+ wxStaticBoxSizer* static_box_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Log into your Prusa Account"));
+ static_box_sizer->SetMinSize(wxSize(em * 30, em * 15));
+ // avatar
+ boost::filesystem::path path = p_user_account->get_avatar_path(logged);
+ ScalableBitmap logo(this, path, wxSize(em * 10, em * 10));
+ m_avatar_bitmap = new wxStaticBitmap(this, wxID_ANY, logo.bmp(), wxDefaultPosition, wxDefaultSize);
+ static_box_sizer->Add(m_avatar_bitmap, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 10);
+ // username
+ const wxString username = GUI::format_wxstr("%1%", logged ? from_u8(p_user_account->get_username()) : _L("Anonymous"));
+ m_username_label = new wxStaticText(this, wxID_ANY, username, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
+ m_username_label->SetFont(m_username_label->GetFont().Bold());
+ static_box_sizer->Add(m_username_label, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
+ // login button
+ m_login_button_id = NewControlId();
+ m_login_button = new wxButton(this, m_login_button_id, logged ? _L("Log out") : _L("Log in"));
+ static_box_sizer->Add(m_login_button, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 10);
+ // TODO: why is m_login_button always hovered?
+ main_sizer->Add(static_box_sizer, 1, wxEXPAND | wxALL, 10);
+ // continue button
+ m_continue_button = new wxButton(this, wxID_OK, logged ? _L("Continue") : _L("Continue without Prusa Account"));
+ main_sizer->Add(m_continue_button, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 10);
+
+ SetSizerAndFit(main_sizer);
+
+ m_login_button->Bind(wxEVT_BUTTON, [user_account = p_user_account](wxCommandEvent& event) {
+ if (!user_account->is_logged())
+ user_account->do_login();
+ else
+ user_account->do_logout();
+ });
+
+ wxGetApp().UpdateDlgDarkUI(this);
+ SetFocus();
+}
+
+LoginDialog::~LoginDialog()
+{
+}
+
+void LoginDialog::update_account()
+{
+ bool logged = p_user_account->is_logged();
+
+ const wxString username = GUI::format_wxstr("%1%", logged ? from_u8(p_user_account->get_username()) : _L("Anonymous"));
+ m_username_label->SetLabel(username);
+
+ boost::filesystem::path path = p_user_account->get_avatar_path(logged);
+ if (boost::filesystem::exists(path)) {
+ const int em = wxGetApp().em_unit();
+ ScalableBitmap logo(this, path, wxSize(em * 10, em * 10));
+ m_avatar_bitmap->SetBitmap(logo.bmp());
+ }
+
+ m_login_button->SetLabel(logged ? _L("Log out") : _L("Log in"));
+ m_continue_button->SetLabel(logged ? _L("Continue") : _L("Continue without Prusa Account"));
+ // TODO: resize correctly m_continue_button
+ //m_continue_button->Fit();
+
+ Fit();
+ Refresh();
+}
+
+void LoginDialog::on_dpi_changed(const wxRect& suggested_rect)
+{
+
+ SetFont(wxGetApp().normal_font());
+
+ const int em = em_unit();
+ msw_buttons_rescale(this, em, { wxID_OK, m_login_button_id});
+
+ Fit();
+ Refresh();
+
+}
+}}// Slicer::GUI
\ No newline at end of file
diff --git a/src/slic3r/GUI/LoginDialog.hpp b/src/slic3r/GUI/LoginDialog.hpp
new file mode 100644
index 0000000000..debd0c4418
--- /dev/null
+++ b/src/slic3r/GUI/LoginDialog.hpp
@@ -0,0 +1,38 @@
+#ifndef slic3r_LoginDialog_hpp_
+#define slic3r_LoginDialog_hpp_
+
+#include "UserAccount.hpp"
+
+#include "GUI_Utils.hpp"
+
+#include
+#include
+#include
+#include
+
+namespace Slic3r {
+namespace GUI {
+
+class RemovableDriveManager;
+class LoginDialog : public DPIDialog
+{
+public:
+ LoginDialog(wxWindow* parent, UserAccount* user_account);
+ ~LoginDialog();
+
+ void update_account();
+private:
+ UserAccount* p_user_account;
+
+ wxStaticText* m_username_label;
+ wxStaticBitmap* m_avatar_bitmap;
+ wxButton* m_login_button;
+ int m_login_button_id{ wxID_ANY };
+ wxButton* m_continue_button;
+protected:
+ void on_dpi_changed(const wxRect& suggested_rect) override;
+ void on_sys_color_changed() override {}
+};
+
+}} // Slicer::GUI
+#endif
\ No newline at end of file
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 7c28b9eb36..ea28670ec6 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -20,9 +20,13 @@
#include
#include
#include
+#include
//#include
#include
#include
+#if wxUSE_SECRETSTORE
+#include
+#endif
#include
#include
@@ -53,12 +57,14 @@
#include "GUI_App.hpp"
#include "UnsavedChangesDialog.hpp"
#include "MsgDialog.hpp"
-#include "Notebook.hpp"
+//#include "Notebook.hpp"
+#include "TopBar.hpp"
#include "GUI_Factories.hpp"
#include "GUI_ObjectList.hpp"
#include "GalleryDialog.hpp"
#include "NotificationManager.hpp"
#include "Preferences.hpp"
+#include "WebViewDialog.hpp"
#ifdef _WIN32
#include
@@ -175,19 +181,34 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
else
init_menubar_as_editor();
+#ifndef __APPLE__
+ std::vector& entries_cache = accelerator_entries_cache();
+ assert(entries_cache.size() + 6 < 100);
+ wxAcceleratorEntry entries[100];
+
+ int id = 0;
+ for (const auto* entry : entries_cache)
+ entries[id++].Set(entry->GetFlags(), entry->GetKeyCode(), entry->GetMenuItem()->GetId());
+
#if _WIN32
// This is needed on Windows to fake the CTRL+# of the window menu when using the numpad
- wxAcceleratorEntry entries[6];
- entries[0].Set(wxACCEL_CTRL, WXK_NUMPAD1, wxID_HIGHEST + 1);
- entries[1].Set(wxACCEL_CTRL, WXK_NUMPAD2, wxID_HIGHEST + 2);
- entries[2].Set(wxACCEL_CTRL, WXK_NUMPAD3, wxID_HIGHEST + 3);
- entries[3].Set(wxACCEL_CTRL, WXK_NUMPAD4, wxID_HIGHEST + 4);
- entries[4].Set(wxACCEL_CTRL, WXK_NUMPAD5, wxID_HIGHEST + 5);
- entries[5].Set(wxACCEL_CTRL, WXK_NUMPAD6, wxID_HIGHEST + 6);
- wxAcceleratorTable accel(6, entries);
- SetAcceleratorTable(accel);
+ entries[id++].Set(wxACCEL_CTRL, WXK_NUMPAD1, wxID_HIGHEST + 1);
+ entries[id++].Set(wxACCEL_CTRL, WXK_NUMPAD2, wxID_HIGHEST + 2);
+ entries[id++].Set(wxACCEL_CTRL, WXK_NUMPAD3, wxID_HIGHEST + 3);
+ entries[id++].Set(wxACCEL_CTRL, WXK_NUMPAD4, wxID_HIGHEST + 4);
+ entries[id++].Set(wxACCEL_CTRL, WXK_NUMPAD5, wxID_HIGHEST + 5);
+ entries[id++].Set(wxACCEL_CTRL, WXK_NUMPAD6, wxID_HIGHEST + 6);
#endif // _WIN32
+ wxAcceleratorTable accel(id, entries);
+ SetAcceleratorTable(accel);
+
+ // clear cache with wxAcceleratorEntry, because it's no need anymore
+ for (auto entry : entries_cache)
+ delete entry;
+ entries_cache.clear();
+#endif
+
// set default tooltip timer in msec
// SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values
// (SetAutoPop is not available on GTK.)
@@ -360,6 +381,8 @@ static void add_tabs_as_menu(wxMenuBar* bar, MainFrame* main_frame, wxWindow* ba
void MainFrame::show_tabs_menu(bool show)
{
+ if (!m_menubar)
+ return;
if (show)
append_tab_menu_items_to_menubar(m_menubar, plater() ? plater()->printer_technology() : ptFFF, true);
else
@@ -451,13 +474,15 @@ void MainFrame::update_layout()
case ESettingsLayout::Old:
{
m_plater->Reparent(m_tabpanel);
-#ifdef _MSW_DARK_MODE
m_plater->Layout();
+#ifdef _WIN32
if (!wxGetApp().tabs_as_menu())
- dynamic_cast(m_tabpanel)->InsertPage(0, m_plater, _L("Plater"), std::string("plater"), true);
- else
#endif
+ dynamic_cast(m_tabpanel)->InsertPage(0, m_plater, _L("Plater"), std::string("plater"), true);
+#ifdef _WIN32
+ else
m_tabpanel->InsertPage(0, m_plater, _L("Plater"));
+#endif
m_main_sizer->Add(m_tabpanel, 1, wxEXPAND | wxTOP, 1);
m_plater->Show();
m_tabpanel->Show();
@@ -477,12 +502,14 @@ void MainFrame::update_layout()
m_tabpanel->Hide();
m_main_sizer->Add(m_tabpanel, 1, wxEXPAND);
m_plater_page = new wxPanel(m_tabpanel);
-#ifdef _MSW_DARK_MODE
+#ifdef _WIN32
if (!wxGetApp().tabs_as_menu())
- dynamic_cast(m_tabpanel)->InsertPage(0, m_plater_page, _L("Plater"), std::string("plater"), true);
- else
#endif
+ dynamic_cast(m_tabpanel)->InsertPage(0, m_plater_page, _L("Plater"), std::string("plater"), true);
+#ifdef _WIN32
+ else
m_tabpanel->InsertPage(0, m_plater_page, _L("Plater")); // empty panel just for Plater tab */
+#endif
m_plater->Show();
break;
}
@@ -494,7 +521,7 @@ void MainFrame::update_layout()
m_tabpanel->Show();
m_plater->Show();
-#ifdef _MSW_DARK_MODE
+#ifdef _WIN32
if (wxGetApp().tabs_as_menu())
show_tabs_menu(false);
#endif
@@ -511,11 +538,6 @@ void MainFrame::update_layout()
}
}
-#ifdef _MSW_DARK_MODE
- // Sizer with buttons for mode changing
- m_plater->sidebar().show_mode_sizer(wxGetApp().tabs_as_menu() || m_layout != ESettingsLayout::Old);
-#endif
-
#ifdef __WXMSW__
if (update_scaling_state != State::noUpdate)
{
@@ -559,10 +581,6 @@ void MainFrame::update_layout()
// m_tabpanel->SetMinSize(size);
// }
//#endif
-
-#ifdef __APPLE__
- m_plater->sidebar().change_top_border_for_mode_sizer(m_layout != ESettingsLayout::Old);
-#endif
Layout();
Thaw();
@@ -691,22 +709,14 @@ void MainFrame::init_tabpanel()
// wxGetApp().UpdateDarkUI(m_tabpanel);
}
else
- m_tabpanel = new Notebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME, true);
-#else
- m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME);
#endif
-
- wxGetApp().UpdateDarkUI(m_tabpanel);
+ m_tabpanel = new TopBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME);
m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font());
m_tabpanel->Hide();
m_settings_dialog.set_tabpanel(m_tabpanel);
-#ifdef __WXMSW__
m_tabpanel->Bind(wxEVT_BOOKCTRL_PAGE_CHANGED, [this](wxBookCtrlEvent& e) {
-#else
- m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxBookCtrlEvent& e) {
-#endif
if (int old_selection = e.GetOldSelection();
old_selection != wxNOT_FOUND && old_selection < static_cast(m_tabpanel->GetPageCount())) {
Tab* old_tab = dynamic_cast(m_tabpanel->GetPage(old_selection));
@@ -721,6 +731,10 @@ void MainFrame::init_tabpanel()
if (panel == nullptr || (tab != nullptr && !tab->supports_printer_technology(m_plater->printer_technology())))
return;
+ // temporary fix - WebViewPanel is not inheriting from Tab -> would jump to select Plater
+ if (panel && !tab)
+ return;
+
auto& tabs_list = wxGetApp().tabs_list;
if (tab && std::find(tabs_list.begin(), tabs_list.end(), tab) != tabs_list.end()) {
// On GTK, the wxEVT_NOTEBOOK_PAGE_CHANGED event is triggered
@@ -736,6 +750,13 @@ void MainFrame::init_tabpanel()
select_tab(size_t(0)); // select Plater
});
+ if (wxGetApp().is_editor()) {
+
+ //m_webview = new WebViewPanel(m_tabpanel);
+ //m_tabpanel->AddPage(m_webview, "web", "cog"/*, "tab_home_active"*/);
+ //m_param_panel = new ParamsPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
+ }
+
m_plater = new Plater(this, this);
m_plater->Hide();
@@ -820,6 +841,94 @@ void MainFrame::create_preset_tabs()
add_created_tab(new TabSLAPrint(m_tabpanel), "cog");
add_created_tab(new TabSLAMaterial(m_tabpanel), "resin");
add_created_tab(new TabPrinter(m_tabpanel), wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF ? "printer" : "sla_printer");
+
+ m_connect_webview = new ConnectWebViewPanel(m_tabpanel);
+ m_printer_webview = new PrinterWebViewPanel(m_tabpanel, L"");
+ // new created tabs have to be hidden by default
+ m_connect_webview->Hide();
+ m_printer_webview->Hide();
+
+}
+
+void MainFrame::add_connect_webview_tab()
+{
+ if (m_connect_webview_added) {
+ return;
+ } // parameters of InsertPage (to prevent ambigous overloaded function)
+ // insert to positon 4, if physical printer is already added, it moves to 5
+ // order of tabs: Plater - Print Settings - Filaments - Printers - Prusa Connect - Prusa Link
+ size_t n = 4;
+ wxWindow* page = m_connect_webview;
+ const wxString text(L"Prusa Connect");
+ const std::string bmp_name = "";
+ bool bSelect = false;
+ dynamic_cast(m_tabpanel)->InsertPage(n, page, text, bmp_name, bSelect);
+ m_connect_webview->load_default_url_delayed();
+ m_connect_webview_added = true;
+}
+void MainFrame::remove_connect_webview_tab()
+{
+ if (!m_connect_webview_added) {
+ return;
+ }
+ // connect tab should always be at position 4
+ if (m_tabpanel->GetSelection() == 4)
+ m_tabpanel->SetSelection(0);
+ dynamic_cast(m_tabpanel)->RemovePage(4);
+ m_connect_webview_added = false;
+ m_connect_webview->logout();
+}
+
+void MainFrame::add_printer_webview_tab(const wxString& url)
+{
+ if (m_printer_webview_added) {
+ set_printer_webview_tab_url(url);
+ return;
+ }
+ m_printer_webview_added = true;
+ // add as the last (rightmost) panel
+ dynamic_cast(m_tabpanel)->AddPage(m_printer_webview, L"Physical Printer", "", false);
+ m_printer_webview->set_default_url(url);
+ m_printer_webview->load_default_url_delayed();
+}
+void MainFrame::remove_printer_webview_tab()
+{
+ if (!m_printer_webview_added) {
+ return;
+ }
+ m_printer_webview_added = false;
+ m_printer_webview->Hide();
+ // always remove the last tab
+ dynamic_cast(m_tabpanel)->RemovePage(m_tabpanel->GetPageCount() - 1);
+}
+void MainFrame::set_printer_webview_tab_url(const wxString& url)
+{
+ if (!m_printer_webview_added) {
+ add_printer_webview_tab(url);
+ return;
+ }
+ m_printer_webview->clear();
+ m_printer_webview->set_default_url(url);
+ if (m_tabpanel->GetSelection() == m_tabpanel->GetPageCount() - 1) {
+ m_printer_webview->load_url(url);
+ } else {
+ m_printer_webview->load_default_url_delayed();
+ }
+}
+
+void MainFrame::set_printer_webview_api_key(const std::string& key)
+{
+ m_printer_webview->set_api_key(key);
+}
+void MainFrame::set_printer_webview_credentials(const std::string& usr, const std::string& psk)
+{
+ m_printer_webview->set_credentials(usr, psk);
+}
+
+void Slic3r::GUI::MainFrame::refresh_account_menu(bool avatar/* = false */)
+{
+ // Update User name in TopBar
+ dynamic_cast(m_tabpanel)->GetTopBarItemsCtrl()->UpdateAccountMenu(avatar);
}
void MainFrame::add_created_tab(Tab* panel, const std::string& bmp_name /*= ""*/)
@@ -829,12 +938,14 @@ void MainFrame::add_created_tab(Tab* panel, const std::string& bmp_name /*= ""*
const auto printer_tech = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
if (panel->supports_printer_technology(printer_tech))
-#ifdef _MSW_DARK_MODE
+#ifdef _WIN32
if (!wxGetApp().tabs_as_menu())
- dynamic_cast(m_tabpanel)->AddPage(panel, panel->title(), bmp_name);
- else
#endif
+ dynamic_cast(m_tabpanel)->AddPage(panel, panel->title(), bmp_name);
+#ifdef _WIN32
+ else
m_tabpanel->AddPage(panel, panel->title());
+#endif
}
bool MainFrame::is_active_and_shown_tab(Tab* tab)
@@ -1017,10 +1128,10 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect)
wxGetApp().update_fonts(this);
this->SetFont(this->normal_font());
-#ifdef _MSW_DARK_MODE
+#ifdef _WIN32
// update common mode sizer
if (!wxGetApp().tabs_as_menu())
- dynamic_cast(m_tabpanel)->Rescale();
+ dynamic_cast(m_tabpanel)->Rescale();
#endif
// update Plater
@@ -1065,12 +1176,9 @@ void MainFrame::on_sys_color_changed()
wxGetApp().update_ui_colours_from_appconfig();
#ifdef __WXMSW__
wxGetApp().UpdateDarkUI(m_tabpanel);
-#ifdef _MSW_DARK_MODE
- // update common mode sizer
if (!wxGetApp().tabs_as_menu())
- dynamic_cast(m_tabpanel)->OnColorsChanged();
-#endif
#endif
+ dynamic_cast(m_tabpanel)->OnColorsChanged();
// update Plater
wxGetApp().plater()->sys_color_changed();
@@ -1079,6 +1187,9 @@ void MainFrame::on_sys_color_changed()
for (auto tab : wxGetApp().tabs_list)
tab->sys_color_changed();
+ m_connect_webview->sys_color_changed();
+ m_printer_webview->sys_color_changed();
+
MenuFactory::sys_color_changed(m_menubar);
this->Refresh();
@@ -1088,16 +1199,9 @@ void MainFrame::on_sys_color_changed()
void MainFrame::update_mode_markers()
{
-#ifdef __WXMSW__
-#ifdef _MSW_DARK_MODE
// update markers in common mode sizer
if (!wxGetApp().tabs_as_menu())
- dynamic_cast(m_tabpanel)->UpdateModeMarkers();
-#endif
-#endif
-
- // update mode markers on side_bar
- wxGetApp().sidebar().update_mode_markers();
+ dynamic_cast(m_tabpanel)->UpdateModeMarkers();
// update mode markers in tabs
for (auto tab : wxGetApp().tabs_list)
@@ -1434,7 +1538,7 @@ void MainFrame::init_menubar_as_editor()
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _L("Searc&h") + "\tCtrl+F",
- _L("Search in settings"), [this](wxCommandEvent&) { m_plater->IsShown() ? m_plater->search() : wxGetApp().show_search_dialog(); },
+ _L("Search in settings"), [](wxCommandEvent&) { wxGetApp().show_search_dialog(); },
"search", nullptr, []() {return true; }, this);
}
@@ -1520,6 +1624,28 @@ void MainFrame::init_menubar_as_editor()
// Help menu
auto helpMenu = generate_help_menu();
+#ifndef __APPLE__
+ // append menus for Menu button from TopBar
+
+ TopBar* top_bar = dynamic_cast(m_tabpanel);
+ top_bar->AppendMenuItem(fileMenu, _L("&File"));
+ if (editMenu)
+ top_bar->AppendMenuItem(editMenu, _L("&Edit"));
+
+ top_bar->AppendMenuSeparaorItem();
+
+ top_bar->AppendMenuItem(windowMenu, _L("&Window"));
+ if (viewMenu)
+ top_bar->AppendMenuItem(viewMenu, _L("&View"));
+
+ top_bar->AppendMenuItem(wxGetApp().get_config_menu(), _L("&Configuration"));
+
+ top_bar->AppendMenuSeparaorItem();
+
+ top_bar->AppendMenuItem(helpMenu, _L("&Help"));
+
+#else
+
// menubar
// assign menubar to frame after appending items, otherwise special items
// will not be handled correctly
@@ -1530,25 +1656,13 @@ void MainFrame::init_menubar_as_editor()
m_menubar->Append(windowMenu, _L("&Window"));
if (viewMenu) m_menubar->Append(viewMenu, _L("&View"));
// Add additional menus from C++
- wxGetApp().add_config_menu(m_menubar);
+ m_menubar->Append(wxGetApp().get_config_menu(), _L("&Configuration"));
m_menubar->Append(helpMenu, _L("&Help"));
-#ifdef _MSW_DARK_MODE
- if (wxGetApp().tabs_as_menu()) {
- // Add separator
- m_menubar->Append(new wxMenu(), " ");
- add_tabs_as_menu(m_menubar, this, this);
- }
-#endif
SetMenuBar(m_menubar);
-#ifdef _MSW_DARK_MODE
- if (wxGetApp().tabs_as_menu())
- m_menubar->EnableTop(6, false);
-#endif
-
-#ifdef __APPLE__
init_macos_application_menu(m_menubar, this);
+
#endif // __APPLE__
if (plater()->printer_technology() == ptSLA)
@@ -1635,7 +1749,7 @@ void MainFrame::init_menubar_as_gcodeviewer()
m_menubar->Append(fileMenu, _L("&File"));
if (viewMenu != nullptr) m_menubar->Append(viewMenu, _L("&View"));
// Add additional menus from C++
- wxGetApp().add_config_menu(m_menubar);
+// wxGetApp().add_config_menu(m_menubar);
m_menubar->Append(helpMenu, _L("&Help"));
SetMenuBar(m_menubar);
@@ -1789,9 +1903,54 @@ void MainFrame::export_configbundle(bool export_physical_printers /*= false*/)
file = dlg.GetPath();
if (!file.IsEmpty()) {
// Export the config bundle.
+
+ bool passwords_to_plain = false;
+ bool passwords_dialog_shown = false;
+ // callback function thats going to be passed to preset bundle (so preset bundle doesnt have to include WX secret lib)
+ std::function load_password = [&](const std::string& printer_id, const std::string& opt, std::string& out_psswd)->bool{
+ out_psswd = std::string();
+#if wxUSE_SECRETSTORE
+ // First password prompts user with dialog
+ if (!passwords_dialog_shown) {
+ //wxString msg = GUI::format_wxstr(L"%1%\n%2%", _L("Some of the exported printers contains passwords, which are stored in the system password store."), _L("Do you wish to include the passwords to the exported file in the plain text form?"));
+ wxString msg = _L("Some of the exported printers contains passwords, which are stored in the system password store."
+ " Do you wish to include the passwords in the plain text form to the exported file?");
+ MessageDialog dlg_psswd(this, msg, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION);
+ if (dlg_psswd.ShowModal() == wxID_YES)
+ passwords_to_plain = true;
+ passwords_dialog_shown = true;
+ }
+ if (!passwords_to_plain)
+ return false;
+ wxSecretStore store = wxSecretStore::GetDefault();
+ wxString errmsg;
+ if (!store.IsOk(&errmsg)) {
+ std::string msg = GUI::format("%1% (%2%).", _u8L("Failed to load credentials from the system secret store."), errmsg);
+ BOOST_LOG_TRIVIAL(error) << msg;
+ show_error(nullptr, msg);
+ // Do not try again. System store is not reachable.
+ passwords_to_plain = false;
+ return false;
+ }
+ const wxString service = GUI::format_wxstr(L"%1%/PhysicalPrinter/%2%/%3%", SLIC3R_APP_NAME, printer_id, opt);
+ wxString username;
+ wxSecretValue password;
+ if (!store.Load(service, username, password)) {
+ std::string msg = GUI::format(_u8L("Failed to load credentials from the system secret store for the printer %1%."), printer_id);
+ BOOST_LOG_TRIVIAL(error) << msg;
+ show_error(nullptr, msg);
+ return false;
+ }
+ out_psswd = into_u8(password.GetAsString());
+ return true;
+#else
+ return false;
+#endif // wxUSE_SECRETSTORE
+ };
+
wxGetApp().app_config->update_config_dir(get_dir_name(file));
try {
- wxGetApp().preset_bundle->export_configbundle(file.ToUTF8().data(), false, export_physical_printers);
+ wxGetApp().preset_bundle->export_configbundle(file.ToUTF8().data(), false, export_physical_printers, load_password);
} catch (const std::exception &ex) {
show_error(this, ex.what());
}
@@ -1887,6 +2046,8 @@ void MainFrame::select_tab(Tab* tab)
void MainFrame::select_tab(size_t tab/* = size_t(-1)*/)
{
+ if (!wxGetApp().is_editor())
+ return;
bool tabpanel_was_hidden = false;
// Controls on page are created on active page of active tab now.
@@ -1918,9 +2079,6 @@ void MainFrame::select_tab(size_t tab/* = size_t(-1)*/)
if (tab==0) {
if (m_settings_dialog.IsShown())
this->SetFocus();
- // plater should be focused for correct navigation inside search window
- if (m_plater->canvas3D()->is_search_pressed())
- m_plater->SetFocus();
return;
}
// Show/Activate Settings Dialog
@@ -2038,6 +2196,8 @@ void MainFrame::add_to_recent_projects(const wxString& filename)
void MainFrame::technology_changed()
{
+ if (!m_menubar)
+ return;
// update menu titles
PrinterTechnology pt = plater()->printer_technology();
if (int id = m_menubar->FindMenu(pt == ptFFF ? _L("Material Settings") : _L("Filament Settings")); id != wxNOT_FOUND)
@@ -2171,10 +2331,10 @@ void SettingsDialog::on_dpi_changed(const wxRect& suggested_rect)
const int& em = em_unit();
const wxSize& size = wxSize(85 * em, 50 * em);
-#ifdef _MSW_DARK_MODE
+#ifdef _WIN32
// update common mode sizer
if (!wxGetApp().tabs_as_menu())
- dynamic_cast(m_tabpanel)->Rescale();
+ dynamic_cast(m_tabpanel)->Rescale();
#endif
// update Tabs
diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp
index 37fcfc3ab2..4206c4496e 100644
--- a/src/slic3r/GUI/MainFrame.hpp
+++ b/src/slic3r/GUI/MainFrame.hpp
@@ -43,6 +43,8 @@ class Plater;
class MainFrame;
class PreferencesDialog;
class GalleryDialog;
+class ConnectWebViewPanel;
+class PrinterWebViewPanel;
enum QuickSlice
{
@@ -96,6 +98,11 @@ class MainFrame : public DPIFrame
size_t m_last_selected_tab;
Search::OptionsSearcher m_searcher;
+ ConnectWebViewPanel* m_connect_webview{ nullptr };
+ bool m_connect_webview_added{ false };
+ PrinterWebViewPanel* m_printer_webview{ nullptr };
+ bool m_printer_webview_added{ false };
+
std::string get_base_name(const wxString &full_name, const char *extension = nullptr) const;
std::string get_dir_name(const wxString &full_name) const;
@@ -124,6 +131,7 @@ class MainFrame : public DPIFrame
miSend, // Send G-code Send to print
miMaterialTab, // Filament Settings Material Settings
miPrinterTab, // Different bitmap for Printer Settings
+ miLogin,
};
// vector of a MenuBar items changeable in respect to printer technology
@@ -207,6 +215,18 @@ public:
void add_to_recent_projects(const wxString& filename);
void technology_changed();
+ void add_connect_webview_tab();
+ void remove_connect_webview_tab();
+
+ void add_printer_webview_tab(const wxString& url);
+ void remove_printer_webview_tab();
+ void set_printer_webview_tab_url(const wxString& url);
+ bool get_printer_webview_tab_added() const { return m_printer_webview_added; }
+ void set_printer_webview_api_key(const std::string& key);
+ void set_printer_webview_credentials(const std::string& usr, const std::string& psk);
+
+ void refresh_account_menu(bool avatar = false);
+
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
Plater* m_plater { nullptr };
@@ -218,7 +238,7 @@ public:
PreferencesDialog* preferences_dialog { nullptr };
PrintHostQueueDialog* m_printhost_queue_dlg;
GalleryDialog* m_gallery_dialog{ nullptr };
-
+
#ifdef __APPLE__
std::unique_ptr m_taskbar_icon;
#endif // __APPLE__
diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
index d74b28af60..f95a367545 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -129,7 +129,11 @@ enum class NotificationType
// MacOS specific - PS comes forward even when downloader is not allowed
URLNotRegistered,
// Config file was detected during startup, open wifi config dialog via hypertext
- WifiConfigFileDetected
+ WifiConfigFileDetected,
+ // Info abouty successful login or logout
+ UserAccountID,
+ // Debug notification for connect communication
+ PrusaConnectPrinters,
};
class NotificationManager
diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp
index 69902545a3..8d599efd8c 100644
--- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp
+++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
#include
@@ -16,6 +17,10 @@
#include
#include
#include
+#if wxUSE_SECRETSTORE
+#include
+#endif
+#include
#include "libslic3r/libslic3r.h"
#include "libslic3r/PrintConfig.hpp"
@@ -153,6 +158,78 @@ void PresetForPrinter::on_sys_color_changed()
m_delete_preset_btn->sys_color_changed();
}
+namespace {
+
+bool is_secret_store_ok()
+{
+#if wxUSE_SECRETSTORE
+ wxSecretStore store = wxSecretStore::GetDefault();
+ wxString errmsg;
+ if (!store.IsOk(&errmsg)) {
+ BOOST_LOG_TRIVIAL(warning) << "wxSecretStore is not supported: " << errmsg;
+ return false;
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+bool save_secret(const std::string& id, const std::string& opt, const std::string& usr, const std::string& psswd)
+{
+#if wxUSE_SECRETSTORE
+ wxSecretStore store = wxSecretStore::GetDefault();
+ wxString errmsg;
+ if (!store.IsOk(&errmsg)) {
+ std::string msg = GUI::format("%1% (%2%).", _u8L("This system doesn't support storing passwords securely"), errmsg);
+ BOOST_LOG_TRIVIAL(error) << msg;
+ show_error(nullptr, msg);
+ return false;
+ }
+ const wxString service = GUI::format_wxstr(L"%1%/PhysicalPrinter/%2%/%3%", SLIC3R_APP_NAME, id, opt);
+ const wxString username = boost::nowide::widen(usr);
+ const wxSecretValue password(boost::nowide::widen(psswd));
+ if (!store.Save(service, username, password)) {
+ std::string msg(_u8L("Failed to save credentials to the system secret store."));
+ BOOST_LOG_TRIVIAL(error) << msg;
+ show_error(nullptr, msg);
+ return false;
+ }
+ return true;
+#else
+ BOOST_LOG_TRIVIAL(error) << "wxUSE_SECRETSTORE not supported. Cannot save password to the system store.";
+ return false;
+#endif // wxUSE_SECRETSTORE
+}
+bool load_secret(const std::string& id, const std::string& opt, std::string& usr, std::string& psswd)
+{
+#if wxUSE_SECRETSTORE
+ wxSecretStore store = wxSecretStore::GetDefault();
+ wxString errmsg;
+ if (!store.IsOk(&errmsg)) {
+ std::string msg = GUI::format("%1% (%2%).", _u8L("This system doesn't support storing passwords securely"), errmsg);
+ BOOST_LOG_TRIVIAL(error) << msg;
+ show_error(nullptr, msg);
+ return false;
+ }
+ const wxString service = GUI::format_wxstr(L"%1%/PhysicalPrinter/%2%/%3%", SLIC3R_APP_NAME, id, opt);
+ wxString username;
+ wxSecretValue password;
+ if (!store.Load(service, username, password)) {
+ std::string msg(_u8L("Failed to load credentials from the system secret store."));
+ BOOST_LOG_TRIVIAL(error) << msg;
+ show_error(nullptr, msg);
+ return false;
+ }
+ usr = into_u8(username);
+ psswd = into_u8(password.GetAsString());
+ return true;
+#else
+ BOOST_LOG_TRIVIAL(error) << "wxUSE_SECRETSTORE not supported. Cannot load password from the system store.";
+ return false;
+#endif // wxUSE_SECRETSTORE
+}
+}
+
//------------------------------------------
// PhysicalPrinterDialog
@@ -202,6 +279,20 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent, wxString printer_
const std::set& preset_names = printer->get_preset_names();
for (const std::string& preset_name : preset_names)
m_presets.emplace_back(new PresetForPrinter(this, preset_name));
+ // "stored" indicates data are stored secretly, load them from store.
+ if (m_printer.config.opt_string("printhost_password") == "stored" && m_printer.config.opt_string("printhost_password") == "stored") {
+ std::string username;
+ std::string password;
+ if (load_secret(m_printer.name, "printhost_password", username, password)) {
+ if (!username.empty())
+ m_printer.config.opt_string("printhost_user") = username;
+ if (!password.empty())
+ m_printer.config.opt_string("printhost_password") = password;
+ } else {
+ m_printer.config.opt_string("printhost_user") = std::string();
+ m_printer.config.opt_string("printhost_password") = std::string();
+ }
+ }
}
if (m_presets.size() == 1)
@@ -348,6 +439,20 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
return sizer;
};
+ auto api_key_copy = [=](wxWindow* parent) {
+ auto sizer = create_sizer_with_btn(parent, &m_api_key_copy_btn, "copy", _L("Copy"));
+ m_api_key_copy_btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent& e) {
+ if (Field* apikey_field = m_optgroup->get_field("printhost_apikey"); apikey_field) {
+ if (wxTextCtrl* temp = dynamic_cast(apikey_field->getWindow()); temp ) {
+ wxTheClipboard->Open();
+ wxTheClipboard->SetData(new wxTextDataObject(temp->GetValue()));
+ wxTheClipboard->Close();
+ }
+ }
+ });
+ return sizer;
+ };
+
// Set a wider width for a better alignment
Option option = m_optgroup->get_option("print_host");
option.opt.width = Field::def_width_wider();
@@ -360,7 +465,9 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
option = m_optgroup->get_option("printhost_apikey");
option.opt.width = Field::def_width_wider();
- m_optgroup->append_single_option_line(option);
+ Line apikey_line = m_optgroup->create_single_option_line(option);
+ apikey_line.append_widget(api_key_copy);
+ m_optgroup->append_line(apikey_line);
option = m_optgroup->get_option("printhost_port");
option.opt.width = Field::def_width_wider();
@@ -422,6 +529,38 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
m_optgroup->append_line(line);
}
+ // Text line with info how passwords and api keys are stored
+ if (is_secret_store_ok()) {
+ Line line{ "", "" };
+ line.full_width = 1;
+ line.widget = [ca_file_hint](wxWindow* parent) {
+ wxString info = GUI::format_wxstr(L"%1%:\n\t%2%\n"
+ , _L("Storing passwords")
+ , GUI::format_wxstr(_L("On this system, %1% uses the system password store to safely store and read passwords and API keys."), SLIC3R_APP_NAME));
+ auto txt = new wxStaticText(parent, wxID_ANY, info);
+ txt->SetFont(wxGetApp().normal_font());
+ auto sizer = new wxBoxSizer(wxHORIZONTAL);
+ sizer->Add(txt, 1, wxEXPAND);
+ return sizer;
+ };
+ m_optgroup->append_line(line);
+ } else {
+ Line line{ "", "" };
+ line.full_width = 1;
+ line.widget = [ca_file_hint](wxWindow* parent) {
+ wxString info = GUI::format_wxstr(L"%1%:\n\t%2%\n\t%3%\n"
+ , _L("Storing passwords")
+ , GUI::format_wxstr(_L("On this system, %1% cannot access the system password store."), SLIC3R_APP_NAME)
+ , _L("Passwords and API keys are stored in plain text."));
+ auto txt = new wxStaticText(parent, wxID_ANY, info);
+ txt->SetFont(wxGetApp().normal_font());
+ auto sizer = new wxBoxSizer(wxHORIZONTAL);
+ sizer->Add(txt, 1, wxEXPAND);
+ return sizer;
+ };
+ m_optgroup->append_line(line);
+ }
+
for (const std::string& opt_key : std::vector{ "printhost_user", "printhost_password" }) {
option = m_optgroup->get_option(opt_key);
option.opt.width = Field::def_width_wider();
@@ -810,6 +949,13 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event)
//update printer name, if it was changed
m_printer.set_name(into_u8(printer_name));
+ // save access data secretly
+ if (!m_printer.config.opt_string("printhost_password").empty()) {
+ if (save_secret(m_printer.name, "printhost_password", m_printer.config.opt_string("printhost_user"), m_printer.config.opt_string("printhost_password"))) {
+ m_printer.config.opt_string("printhost_password", false) = "stored";
+ }
+ }
+
// save new physical printer
printers.save_printer(m_printer, renamed_from);
diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.hpp b/src/slic3r/GUI/PhysicalPrinterDialog.hpp
index 0de8df235f..2a26e1bf1e 100644
--- a/src/slic3r/GUI/PhysicalPrinterDialog.hpp
+++ b/src/slic3r/GUI/PhysicalPrinterDialog.hpp
@@ -76,6 +76,7 @@ class PhysicalPrinterDialog : public DPIDialog
ScalableButton* m_printhost_test_btn {nullptr};
ScalableButton* m_printhost_cafile_browse_btn {nullptr};
ScalableButton* m_printhost_port_browse_btn {nullptr};
+ ScalableButton* m_api_key_copy_btn {nullptr};
wxBoxSizer* m_presets_sizer {nullptr};
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index bbfbc57088..cd446795b3 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -35,6 +35,8 @@
#include
#include
#include
+#include
+#include
#include
#include
@@ -50,6 +52,9 @@
#include
#include
#include
+#if wxUSE_SECRETSTORE
+#include
+#endif
#include
@@ -116,6 +121,9 @@
#include "Gizmos/GLGizmoSVG.hpp" // Drop SVG file
#include "Gizmos/GLGizmoCut.hpp"
#include "FileArchiveDialog.hpp"
+#include "UserAccount.hpp"
+#include "DesktopIntegrationDialog.hpp"
+#include "WebViewDialog.hpp"
#ifdef __APPLE__
#include "Gizmos/GLGizmosManager.hpp"
@@ -259,9 +267,10 @@ struct Plater::priv
GLToolbar collapse_toolbar;
Preview *preview;
std::unique_ptr notification_manager;
+ std::unique_ptr user_account;
ProjectDirtyStateManager dirty_state;
-
+
BackgroundSlicingProcess background_process;
bool suppressed_backround_processing_update { false };
@@ -605,6 +614,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
}))
, sidebar(new Sidebar(q))
, notification_manager(std::make_unique(q))
+ , user_account(std::make_unique(q, wxGetApp().app_config, wxGetApp().get_instance_hash_string()))
, m_worker{q, std::make_unique(notification_manager.get()), "ui_worker"}
, m_sla_import_dlg{new SLAImportDialog{q}}
, delayed_scene_refresh(false)
@@ -850,11 +860,118 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
wxGetApp().start_download(evt.data[i]);
}
+ });
+ this->q->Bind(EVT_LOGIN_OTHER_INSTANCE, [this](LoginOtherInstanceEvent& evt) {
+ BOOST_LOG_TRIVIAL(trace) << "Received login from other instance event.";
+ user_account->on_login_code_recieved(evt.data);
});
this->q->Bind(EVT_INSTANCE_GO_TO_FRONT, [this](InstanceGoToFrontEvent &) {
bring_instance_forward();
});
+
+ this->q->Bind(EVT_OPEN_PRUSAAUTH, [](OpenPrusaAuthEvent& evt) {
+ BOOST_LOG_TRIVIAL(info) << "open browser: " << evt.data;
+ // first register url to be sure to get the code back
+ //auto downloader_worker = new DownloaderUtils::Worker(nullptr);
+ DownloaderUtils::Worker::perform_register(wxGetApp().app_config->get("url_downloader_dest"));
+#ifdef __linux__
+ if (DownloaderUtils::Worker::perform_registration_linux)
+ DesktopIntegrationDialog::perform_downloader_desktop_integration();
+#endif // __linux__
+ // than open url
+ wxGetApp().open_login_browser_with_dialog(evt.data);
+ });
+
+ this->q->Bind(EVT_UA_LOGGEDOUT, [this](UserAccountSuccessEvent& evt) {
+ user_account->clear();
+ std::string text = _u8L("Logged out from Prusa Account.");
+ this->notification_manager->close_notification_of_type(NotificationType::UserAccountID);
+ this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, text);
+ this->main_frame->remove_connect_webview_tab();
+ this->main_frame->refresh_account_menu(true);
+ // Update sidebar printer status
+ sidebar->update_printer_presets_combobox();
+ wxGetApp().update_login_dialog();
+ this->show_action_buttons(this->ready_to_slice);
+ });
+
+ this->q->Bind(EVT_UA_ID_USER_SUCCESS, [this](UserAccountSuccessEvent& evt) {
+ std::string username;
+ if (user_account->on_user_id_success(evt.data, username)) {
+ // login notification
+ std::string text = format(_u8L("Logged to Prusa Account as %1%."), username);
+ this->notification_manager->close_notification_of_type(NotificationType::UserAccountID);
+ this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, text);
+ // show connect tab
+ this->main_frame->add_connect_webview_tab();
+ // Update User name in TopBar
+ this->main_frame->refresh_account_menu();
+ wxGetApp().update_login_dialog();
+ this->show_action_buttons(this->ready_to_slice);
+ } else {
+ // data were corrupt and username was not retrieved
+ // procced as if EVT_UA_RESET was recieved
+ BOOST_LOG_TRIVIAL(error) << "Reseting Prusa Account communication. Recieved data were corrupt.";
+ user_account->clear();
+ this->notification_manager->close_notification_of_type(NotificationType::UserAccountID);
+ this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::WarningNotificationLevel, _u8L("Failed to connect to Prusa Account."));
+ this->main_frame->remove_connect_webview_tab();
+ // Update User name in TopBar
+ this->main_frame->refresh_account_menu(true);
+ // Update sidebar printer status
+ sidebar->update_printer_presets_combobox();
+ }
+
+ });
+ this->q->Bind(EVT_UA_RESET, [this](UserAccountFailEvent& evt) {
+ BOOST_LOG_TRIVIAL(error) << "Reseting Prusa Account communication. Error message: " << evt.data;
+ user_account->clear();
+ this->notification_manager->close_notification_of_type(NotificationType::UserAccountID);
+ this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::WarningNotificationLevel, _u8L("Failed to connect to Prusa Account."));
+ this->main_frame->remove_connect_webview_tab();
+ // Update User name in TopBar
+ this->main_frame->refresh_account_menu(true);
+ // Update sidebar printer status
+ sidebar->update_printer_presets_combobox();
+ });
+ this->q->Bind(EVT_UA_FAIL, [this](UserAccountFailEvent& evt) {
+ BOOST_LOG_TRIVIAL(error) << "Failed communication with Prusa Account: " << evt.data;
+ user_account->on_communication_fail();
+ });
+#if 0
+ // for debug purposes only
+ this->q->Bind(EVT_UA_SUCCESS, [this](UserAccountSuccessEvent& evt) {
+ this->notification_manager->close_notification_of_type(NotificationType::UserAccountID);
+ this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, evt.data);
+ });
+ this->q->Bind(EVT_UA_CONNECT_USER_DATA_SUCCESS, [this](UserAccountSuccessEvent& evt) {
+ BOOST_LOG_TRIVIAL(error) << evt.data;
+ user_account->on_connect_user_data_success(evt.data);
+ });
+#endif // 0
+ this->q->Bind(EVT_UA_PRUSACONNECT_PRINTERS_SUCCESS, [this](UserAccountSuccessEvent& evt) {
+ std::string text;
+ bool printers_changed = false;
+ if (user_account->on_connect_printers_success(evt.data, wxGetApp().app_config, printers_changed)) {
+ if (printers_changed) {
+ sidebar->update_printer_presets_combobox();
+ }
+ } else {
+ // message was corrupt, procceed like EVT_UA_FAIL
+ user_account->on_communication_fail();
+ }
+ });
+ this->q->Bind(EVT_UA_AVATAR_SUCCESS, [this](UserAccountSuccessEvent& evt) {
+ boost::filesystem::path path = user_account->get_avatar_path(true);
+ FILE* file;
+ file = fopen(path.string().c_str(), "wb");
+ fwrite(evt.data.c_str(), 1, evt.data.size(), file);
+ fclose(file);
+ this->main_frame->refresh_account_menu(true);
+ wxGetApp().update_login_dialog();
+ });
+
wxGetApp().other_instance_message_handler()->init(this->q);
// collapse sidebar according to saved value
@@ -3408,7 +3525,7 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice_) const
DynamicPrintConfig* selected_printer_config = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config();
const auto print_host_opt = selected_printer_config ? selected_printer_config->option("print_host") : nullptr;
const bool send_gcode_shown = print_host_opt != nullptr && !print_host_opt->value.empty();
-
+ const bool connect_gcode_shown = print_host_opt == nullptr && user_account->is_logged();
// when a background processing is ON, export_btn and/or send_btn are showing
if (get_config_bool("background_processing"))
{
@@ -3416,6 +3533,7 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice_) const
if (sidebar->show_reslice(false) |
sidebar->show_export(true) |
sidebar->show_send(send_gcode_shown) |
+ sidebar->show_connect(connect_gcode_shown) |
sidebar->show_export_removable(removable_media_status.has_removable_drives))
sidebar->Layout();
}
@@ -3427,6 +3545,7 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice_) const
if (sidebar->show_reslice(ready_to_slice) |
sidebar->show_export(!ready_to_slice) |
sidebar->show_send(send_gcode_shown && !ready_to_slice) |
+ sidebar->show_connect(connect_gcode_shown && !ready_to_slice) |
sidebar->show_export_removable(!ready_to_slice && removable_media_status.has_removable_drives))
sidebar->Layout();
}
@@ -5700,6 +5819,166 @@ void Plater::reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &
this->reslice_until_step_inner(SLAPrintObjectStep(step), object, postpone_error_messages);
}
+namespace {
+bool load_secret(const std::string& id, const std::string& opt, std::string& usr, std::string& psswd)
+{
+#if wxUSE_SECRETSTORE
+ wxSecretStore store = wxSecretStore::GetDefault();
+ wxString errmsg;
+ if (!store.IsOk(&errmsg)) {
+ std::string msg = GUI::format("%1% (%2%).", _u8L("This system doesn't support storing passwords securely"), errmsg);
+ BOOST_LOG_TRIVIAL(error) << msg;
+ show_error(nullptr, msg);
+ return false;
+ }
+ const wxString service = GUI::format_wxstr(L"%1%/PhysicalPrinter/%2%/%3%", SLIC3R_APP_NAME, id, opt);
+ wxString username;
+ wxSecretValue password;
+ if (!store.Load(service, username, password)) {
+ std::string msg(_u8L("Failed to load credentials from the system secret store."));
+ BOOST_LOG_TRIVIAL(error) << msg;
+ show_error(nullptr, msg);
+ return false;
+ }
+ usr = into_u8(username);
+ psswd = into_u8(password.GetAsString());
+ return true;
+#else
+ BOOST_LOG_TRIVIAL(error) << "wxUSE_SECRETSTORE not supported. Cannot load password from the system store.";
+ return false;
+#endif // wxUSE_SECRETSTORE
+}
+}
+void Plater::connect_gcode()
+{
+ assert(p->user_account->is_logged());
+ std::string dialog_msg;
+ if(PrinterPickWebViewDialog(this, dialog_msg).ShowModal() != wxID_OK) {
+ return;
+ }
+ if (dialog_msg.empty()) {
+ show_error(this, _L("Failed to select a printer. PrusaConnect did not return a value."));
+ return;
+ }
+ BOOST_LOG_TRIVIAL(debug) << "Message from Printer pick webview: " << dialog_msg;
+
+ PresetBundle* preset_bundle = wxGetApp().preset_bundle;
+ // Connect data
+ std::vector compatible_printers;
+ p->user_account->fill_compatible_printers_from_json(dialog_msg, compatible_printers);
+ std::string connect_nozzle = p->user_account->get_nozzle_from_json(dialog_msg);
+ std::string connect_filament = p->user_account->get_keyword_from_json(dialog_msg, "filament_type");
+ std::vector compatible_printer_presets;
+ for (const std::string& cp : compatible_printers) {
+ compatible_printer_presets.emplace_back(preset_bundle->printers.find_system_preset_by_model_and_variant(cp, connect_nozzle));
+ }
+ // Selected profiles
+ const Preset* selected_printer_preset = &preset_bundle->printers.get_selected_preset();
+ const Preset* selected_filament_preset = &preset_bundle->filaments.get_selected_preset();
+ const std::string selected_nozzle_serialized = dynamic_cast(selected_printer_preset->config.option("nozzle_diameter"))->serialize();
+ const std::string selected_filament_serialized = selected_filament_preset->config.option("filament_type")->serialize();
+ const std::string selected_printer_model_serialized = selected_printer_preset->config.option("printer_model")->serialize();
+
+ bool is_first = compatible_printer_presets.front()->name == selected_printer_preset->name;
+ bool found = false;
+ for (const Preset* connect_preset : compatible_printer_presets) {
+ if (!connect_preset) {
+ continue;
+ }
+ if (selected_printer_preset->name == connect_preset->name) {
+ found = true;
+ break;
+ }
+ }
+ //
+ if (!found) {
+ wxString line1 = _L("The printer profile you've selected for upload is not compatible with profiles selected for slicing.");
+ wxString line2 = GUI::format_wxstr(_L("PrusaSlicer Profile:\n%1%"), selected_printer_preset->name);
+ wxString line3 = _L("Known profiles compatible with printer selected for upload:");
+ wxString printers_line;
+ for (const Preset* connect_preset : compatible_printer_presets) {
+ if (!connect_preset) {
+ continue;
+ }
+ printers_line += GUI::format_wxstr(_L("\n%1%"), connect_preset->name);
+ }
+ wxString line4 = _L("Do you still wish to upload?");
+ wxString message = GUI::format_wxstr("%1%\n\n%2%\n\n%3%%4%\n\n%5%", line1, line2, line3, printers_line,line4);
+ MessageDialog msg_dialog(this, message, _L("Do you wish to upload?"), wxYES_NO);
+ auto modal_res = msg_dialog.ShowModal();
+ if (modal_res != wxID_YES) {
+ return;
+ }
+ } else if (!is_first) {
+ wxString line1 = _L("The printer profile you've selected for upload might not be compatible with profiles selected for slicing.");
+ wxString line2 = GUI::format_wxstr(_L("PrusaSlicer Profile:\n%1%"), selected_printer_preset->name);
+ wxString line3 = _L("Known profiles compatible with printer selected for upload:");
+ wxString printers_line;
+ for (const Preset* connect_preset : compatible_printer_presets) {
+ if (!connect_preset) {
+ continue;
+ }
+ printers_line += GUI::format_wxstr(_L("\n%1%"), connect_preset->name);
+ }
+ wxString line4 = _L("Do you still wish to upload?");
+ wxString message = GUI::format_wxstr("%1%\n\n%2%\n\n%3%%4%\n\n%5%", line1, line2, line3, printers_line, line4);
+ MessageDialog msg_dialog(this, message, _L("Do you wish to upload?"), wxYES_NO);
+ auto modal_res = msg_dialog.ShowModal();
+ if (modal_res != wxID_YES) {
+ return;
+ }
+ }
+ // Commented code with selecting printers in plater
+ /*
+ // if selected (in connect) preset is not visible, make it visible and selected
+ if (!connect_printer_preset->is_visible) {
+ size_t preset_id = preset_bundle->printers.get_preset_idx_by_name(connect_printer_preset->name);
+ assert(preset_id != size_t(-1));
+ preset_bundle->printers.select_preset(preset_id);
+ wxGetApp().get_tab(Preset::Type::TYPE_PRINTER)->select_preset(connect_printer_preset->name);
+ p->notification_manager->close_notification_of_type(NotificationType::PrusaConnectPrinters);
+ p->notification_manager->push_notification(NotificationType::PrusaConnectPrinters, NotificationManager::NotificationLevel::ImportantNotificationLevel, format(_u8L("Changed Printer to %1%."), connect_printer_preset->name));
+ select_view_3D("3D");
+ }
+ // if selected (in connect) preset is not selected in slicer, select it
+ if (preset_bundle->printers.get_selected_preset_name() != connect_printer_preset->name) {
+ size_t preset_id = preset_bundle->printers.get_preset_idx_by_name(connect_printer_preset->name);
+ assert(preset_id != size_t(-1));
+ preset_bundle->printers.select_preset(preset_id);
+ wxGetApp().get_tab(Preset::Type::TYPE_PRINTER)->select_preset(connect_printer_preset->name);
+ p->notification_manager->close_notification_of_type(NotificationType::PrusaConnectPrinters);
+ p->notification_manager->push_notification(NotificationType::PrusaConnectPrinters, NotificationManager::NotificationLevel::ImportantNotificationLevel, format(_u8L("Changed Printer to %1%."), connect_printer_preset->name));
+ select_view_3D("3D");
+ }
+ */
+
+ const std::string connect_state = p->user_account->get_keyword_from_json(dialog_msg, "connect_state");
+ const std::string printer_state = p->user_account->get_keyword_from_json(dialog_msg, "printer_state");
+ const std::map& printer_state_table = p->user_account->get_printer_state_table();
+ const auto state = printer_state_table.find(connect_state);
+ assert(state != printer_state_table.end());
+ // TODO: all states that does not allow to upload
+ if (state->second == ConnectPrinterState::CONNECT_PRINTER_OFFLINE) {
+ show_error(this, _L("Failed to select a printer. Chosen printer is in offline state."));
+ return;
+ }
+
+ const std::string uuid = p->user_account->get_keyword_from_json(dialog_msg, "uuid");
+ const std::string team_id = p->user_account->get_keyword_from_json(dialog_msg, "team_id");
+ if (uuid.empty() || team_id.empty()) {
+ show_error(this, _L("Failed to select a printer. Missing data (uuid and team id) for chosen printer."));
+ return;
+ }
+ PhysicalPrinter ph_printer("connect_temp_printer", wxGetApp().preset_bundle->physical_printers.default_config(), /**connect_printer_preset*/*selected_printer_preset);
+ ph_printer.config.set_key_value("host_type", new ConfigOptionEnum(htPrusaConnectNew));
+ // use existing structures to pass data
+ ph_printer.config.opt_string("printhost_apikey") = team_id;
+ ph_printer.config.opt_string("print_host") = uuid;
+ DynamicPrintConfig* physical_printer_config = &ph_printer.config;
+
+ send_gcode_inner(physical_printer_config);
+}
+
void Plater::send_gcode()
{
// if physical_printer is selected, send gcode for this printer
@@ -5707,6 +5986,38 @@ void Plater::send_gcode()
if (! physical_printer_config || p->model.objects.empty())
return;
+ // Passwords and API keys
+ // "stored" indicates data are stored secretly, load them from store.
+ std::string printer_name = wxGetApp().preset_bundle->physical_printers.get_selected_printer().name;
+ if (physical_printer_config->opt_string("printhost_password") == "stored" && physical_printer_config->opt_string("printhost_password") == "stored") {
+ std::string username;
+ std::string password;
+ if (load_secret(printer_name, "printhost_password", username, password)) {
+ if (!username.empty())
+ physical_printer_config->opt_string("printhost_user") = username;
+ if (!password.empty())
+ physical_printer_config->opt_string("printhost_password") = password;
+ }
+ else {
+ physical_printer_config->opt_string("printhost_user") = std::string();
+ physical_printer_config->opt_string("printhost_password") = std::string();
+ }
+ }
+ /*
+ if (physical_printer_config->opt_string("printhost_apikey") == "stored") {
+ std::string username;
+ std::string password;
+ if (load_secret(printer_name, "printhost_apikey", username, password) && !password.empty())
+ physical_printer_config->opt_string("printhost_apikey") = password;
+ else
+ physical_printer_config->opt_string("printhost_apikey") = std::string();
+ }
+ */
+ send_gcode_inner(physical_printer_config);
+}
+
+void Plater::send_gcode_inner(DynamicPrintConfig* physical_printer_config)
+{
PrintHostJob upload_job(physical_printer_config);
if (upload_job.empty())
return;
@@ -5782,6 +6093,7 @@ void Plater::send_gcode()
p->export_gcode(fs::path(), false, std::move(upload_job));
}
+
}
// Called when the Eject button is pressed.
@@ -6371,23 +6683,6 @@ void Plater::paste_from_clipboard()
p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
}
-void Plater::search()
-{
- if (is_preview_shown())
- return;
- // plater should be focused for correct navigation inside search window
- this->SetFocus();
-
- wxKeyEvent evt;
-#ifdef __APPLE__
- evt.m_keyCode = 'f';
-#else /* __APPLE__ */
- evt.m_keyCode = WXK_CONTROL_F;
-#endif /* __APPLE__ */
- evt.SetControlDown(true);
- canvas3D()->on_char(evt);
-}
-
void Plater::msw_rescale()
{
p->preview->msw_rescale();
@@ -6519,6 +6814,16 @@ const NotificationManager * Plater::get_notification_manager() const
return p->notification_manager.get();
}
+UserAccount* Plater::get_user_account()
+{
+ return p->user_account.get();
+}
+
+const UserAccount* Plater::get_user_account() const
+{
+ return p->user_account.get();
+}
+
void Plater::init_notification_manager()
{
p->init_notification_manager();
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index ac596a9dcb..7455227597 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -62,6 +62,7 @@ class Mouse3DController;
class NotificationManager;
struct Camera;
class GLToolbar;
+class UserAccount;
class Plater: public wxPanel
{
@@ -223,7 +224,9 @@ public:
bool is_background_process_update_scheduled() const;
void suppress_background_process(const bool stop_background_process) ;
void send_gcode();
+ void send_gcode_inner(DynamicPrintConfig* physical_printer_config);
void eject_drive();
+ void connect_gcode();
void take_snapshot(const std::string &snapshot_name);
void take_snapshot(const wxString &snapshot_name);
@@ -282,7 +285,6 @@ public:
void copy_selection_to_clipboard();
void paste_from_clipboard();
- void search();
void mirror(Axis axis);
void split_object();
void split_volume();
@@ -347,8 +349,11 @@ public:
void set_bed_shape(const Pointfs& shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const;
void set_default_bed_shape() const;
- NotificationManager * get_notification_manager();
- const NotificationManager * get_notification_manager() const;
+ NotificationManager* get_notification_manager();
+ const NotificationManager* get_notification_manager() const;
+
+ UserAccount* get_user_account();
+ const UserAccount* get_user_account() const;
void init_notification_manager();
diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp
index ee4c06f4a1..04c3f04da6 100644
--- a/src/slic3r/GUI/Preferences.cpp
+++ b/src/slic3r/GUI/Preferences.cpp
@@ -761,7 +761,7 @@ void PreferencesDialog::accept(wxEvent&)
if (!downloader->on_finish())
return;
#ifdef __linux__
- if( downloader->get_perform_registration_linux())
+ if(DownloaderUtils::Worker::perform_registration_linux)
DesktopIntegrationDialog::perform_downloader_desktop_integration();
#endif // __linux__
}
diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp
index c40978b03d..bcd79ed69e 100644
--- a/src/slic3r/GUI/PresetComboBoxes.cpp
+++ b/src/slic3r/GUI/PresetComboBoxes.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#ifdef _WIN32
#include
@@ -44,6 +45,7 @@
#include "BitmapCache.hpp"
#include "PhysicalPrinterDialog.hpp"
#include "MsgDialog.hpp"
+#include "UserAccount.hpp"
#include "Widgets/ComboBox.hpp"
@@ -618,6 +620,12 @@ bool PresetComboBox::selection_is_changed_according_to_physical_printers()
// *** PlaterPresetComboBox ***
// ---------------------------------
+static bool is_active_connect()
+{
+ auto user_account = wxGetApp().plater()->get_user_account();
+ return user_account && user_account->is_logged();
+}
+
PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset_type) :
PresetComboBox(parent, preset_type, wxSize(15 * wxGetApp().em_unit(), -1))
{
@@ -657,6 +665,9 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset
else
switch_to_tab();
});
+
+ if (m_type == Preset::TYPE_PRINTER)
+ connect_info = new wxGenericStaticText(parent, wxID_ANY, /*"Info about Connect for printer preset"*/ "");
}
PlaterPresetComboBox::~PlaterPresetComboBox()
@@ -832,6 +843,86 @@ wxString PlaterPresetComboBox::get_preset_name(const Preset& preset)
return from_u8(name + suffix(preset));
}
+
+struct PrinterStatesCount
+{
+ size_t offline_cnt { 0 };
+ size_t busy_cnt { 0 };
+ size_t available_cnt { 0 };
+ size_t total { 0 };
+};
+
+static PrinterStatesCount get_printe_states_count(const std::vector& states)
+{
+ PrinterStatesCount states_cnt;
+
+ for (size_t i = 0; i < states.size(); i++) {
+ if (states[i] == 0)
+ continue;
+
+ ConnectPrinterState state = static_cast(i);
+
+ if (state == ConnectPrinterState::CONNECT_PRINTER_OFFLINE)
+ states_cnt.offline_cnt += states[i];
+ else if (state == ConnectPrinterState::CONNECT_PRINTER_PAUSED ||
+ state == ConnectPrinterState::CONNECT_PRINTER_STOPPED ||
+ state == ConnectPrinterState::CONNECT_PRINTER_PRINTING ||
+ state == ConnectPrinterState::CONNECT_PRINTER_BUSY ||
+ state == ConnectPrinterState::CONNECT_PRINTER_ATTENTION ||
+ state == ConnectPrinterState::CONNECT_PRINTER_ERROR)
+ states_cnt.busy_cnt += states[i];
+ else
+ states_cnt.available_cnt += states[i];
+ }
+ states_cnt.total = states_cnt.offline_cnt + states_cnt.busy_cnt + states_cnt.available_cnt;
+
+ return states_cnt;
+}
+
+static std::string get_connect_state_suffix_for_printer(const Preset& printer_preset)
+{
+ // process real data from Connect
+ if (auto printer_state_map = wxGetApp().plater()->get_user_account()->get_printer_state_map();
+ !printer_state_map.empty()) {
+
+ for (const auto& [printer_model_id, states] : printer_state_map) {
+ if (printer_model_id == printer_preset.config.opt_string("printer_model")) {
+
+ PrinterStatesCount states_cnt = get_printe_states_count(states);
+
+ if (states_cnt.available_cnt > 0)
+ return "_available";
+ if (states_cnt.busy_cnt > 0)
+ return "_busy";
+ return "_offline";
+ }
+ }
+ }
+
+ return "";
+}
+
+static wxString get_connect_info_line(const Preset& printer_preset)
+{
+ if (auto printer_state_map = wxGetApp().plater()->get_user_account()->get_printer_state_map();
+ !printer_state_map.empty()) {
+
+ for (const auto& [printer_model_id, states] : printer_state_map) {
+ if (printer_model_id == printer_preset.config.opt_string("printer_model")) {
+
+ PrinterStatesCount states_cnt = get_printe_states_count(states);
+
+ return format_wxstr(_L("Available: %1%, Offline: %2%, Busy: %3%. Total: %4% printers"),
+ format("%1%" , states_cnt.available_cnt),
+ format("%1%" , states_cnt.offline_cnt),
+ format("%1%", states_cnt.busy_cnt),
+ format("%1%", states_cnt.total));
+ }
+ }
+ }
+ return " "; // to correct update of strinh height don't use empty string
+}
+
// Only the compatible presets are shown.
// If an incompatible preset is selected, it is shown as well.
void PlaterPresetComboBox::update()
@@ -904,6 +995,12 @@ void PlaterPresetComboBox::update()
std::string bitmap_key, filament_rgb, extruder_rgb, material_rgb;
std::string bitmap_type_name = bitmap_key = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name;
+ if (m_type == Preset::TYPE_PRINTER) {
+ bitmap_type_name = bitmap_key += get_connect_state_suffix_for_printer(preset);
+ if (is_selected)
+ connect_info->SetLabelMarkup(get_connect_info_line(preset));
+ }
+
bool single_bar = false;
if (m_type == Preset::TYPE_FILAMENT)
{
@@ -1032,6 +1129,8 @@ void PlaterPresetComboBox::update()
validate_selection(data.selected);
}
}
+
+ connect_info->Show(is_active_connect());
}
if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_FILAMENT || m_type == Preset::TYPE_SLA_MATERIAL) {
diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp
index 421d96d481..5a9821b7de 100644
--- a/src/slic3r/GUI/PresetComboBoxes.hpp
+++ b/src/slic3r/GUI/PresetComboBoxes.hpp
@@ -16,6 +16,7 @@
class wxString;
class wxTextCtrl;
class wxStaticText;
+class wxGenericStaticText;
class ScalableButton;
class wxBoxSizer;
class wxComboBox;
@@ -153,6 +154,7 @@ public:
~PlaterPresetComboBox();
ScalableButton* edit_btn { nullptr };
+ wxGenericStaticText* connect_info { nullptr };
void set_extruder_idx(const int extr_idx) { m_extruder_idx = extr_idx; }
int get_extruder_idx() const { return m_extruder_idx; }
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index 4fcd2d0f9d..a35388a88a 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -16,6 +16,8 @@
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "GUI_App.hpp"
+#include "I18N.hpp"
+#include "format.hpp"
#include "MainFrame.hpp"
#include "Tab.hpp"
@@ -306,6 +308,7 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
OptionsSearcher::OptionsSearcher()
{
+ default_string = _L("Enter a search term");
}
OptionsSearcher::~OptionsSearcher()
@@ -421,20 +424,60 @@ Option OptionsSearcher::get_option(const std::string& opt_key, const wxString& l
return create_option(opt_key, label, type, gc);
}
-void OptionsSearcher::show_dialog()
+static bool has_focus(wxWindow* win)
{
- if (!search_dialog) {
- search_dialog = new SearchDialog(this);
+ if (win->HasFocus())
+ return true;
- auto parent = search_dialog->GetParent();
- wxPoint pos = parent->ClientToScreen(wxPoint(0, 0));
- pos.x += em_unit(parent) * 40;
- pos.y += em_unit(parent) * 4;
-
- search_dialog->SetPosition(pos);
+ auto children = win->GetChildren();
+ for (auto child : children) {
+ if (has_focus(child))
+ return true;
}
+ return false;
+}
+
+void OptionsSearcher::update_dialog_position()
+{
+ if (search_dialog) {
+ search_dialog->CenterOnParent(wxHORIZONTAL);
+ search_dialog->SetPosition({ search_dialog->GetPosition().x, search_input->GetScreenPosition().y + search_input->GetSize().y });
+ }
+}
+
+void OptionsSearcher::show_dialog(bool show /*= true*/)
+{
+ if (search_dialog && !show) {
+ search_dialog->EndModal(wxID_CLOSE);
+ return;
+ }
+
+ if (!search_dialog) {
+ search_dialog = new SearchDialog(this, search_input);
+ update_dialog_position();
+
+ search_dialog->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e)
+ {
+ if (search_dialog->IsShown() && !search_input->HasFocus())
+ show_dialog(false);
+ e.Skip();
+ });
+
+ search_input->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e)
+ {
+ e.Skip();
+ if (search_dialog->IsShown() && !has_focus(search_dialog))
+ show_dialog(false);
+ });
+ }
+
+ search_string();
+ search_input->SetSelection(-1,-1);
+
search_dialog->Popup();
+ if (!search_input->HasFocus())
+ search_input->SetFocus();
}
void OptionsSearcher::dlg_sys_color_changed()
@@ -449,6 +492,57 @@ void OptionsSearcher::dlg_msw_rescale()
search_dialog->msw_rescale();
}
+void OptionsSearcher::set_search_input(TextInput* input_ctrl)
+{
+ search_input = input_ctrl;
+
+ search_input->Bind(wxEVT_TEXT, [this](wxEvent& e)
+ {
+ if (search_dialog) {
+ search_dialog->input_text(search_input->GetValue());
+ if (!search_dialog->IsShown())
+ search_dialog->Popup();
+ }
+ else
+ GUI::wxGetApp().show_search_dialog();
+ });
+
+ wxTextCtrl* ctrl = search_input->GetTextCtrl();
+ ctrl->SetToolTip(GUI::format_wxstr(_L("Search in settings [%1%]"), "Ctrl+F"));
+
+ ctrl->Bind(wxEVT_KEY_DOWN, [this](wxKeyEvent& e)
+ {
+ int key = e.GetKeyCode();
+ if (key == WXK_TAB)
+ search_input->Navigate(e.ShiftDown() ? wxNavigationKeyEvent::IsBackward : wxNavigationKeyEvent::IsForward);
+ else if (key == WXK_ESCAPE)
+ search_dialog->EndModal(wxID_CLOSE);
+ else if (search_dialog && (key == WXK_UP || key == WXK_DOWN || key == WXK_NUMPAD_ENTER || key == WXK_RETURN))
+ search_dialog->KeyDown(e);
+ e.Skip();
+ });
+
+ ctrl->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent& event)
+ {
+ if (search_input->GetValue() == default_string)
+ search_input->SetValue("");
+ event.Skip();
+ });
+
+ ctrl->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent& event) {
+ GUI::wxGetApp().show_search_dialog();
+ event.Skip();
+ });
+
+ search_input->Bind(wxEVT_MOVE, [this](wxMoveEvent& event)
+ {
+ event.Skip();
+ update_dialog_position();
+ });
+
+ search_input->SetOnDropDownIcon([](){ GUI::wxGetApp().show_search_dialog(); });
+}
+
void OptionsSearcher::add_key(const std::string& opt_key, Preset::Type type, const wxString& group, const wxString& category)
{
groups_and_categories[get_key(opt_key, type)] = GroupAndCategory{group, category};
@@ -468,8 +562,8 @@ static const std::map icon_idxs = {
{ImGui::PreferencesButton , 5},
};
-SearchDialog::SearchDialog(OptionsSearcher* searcher)
- : GUI::DPIDialog(GUI::wxGetApp().tab_panel(), wxID_ANY, _L("Search"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
+SearchDialog::SearchDialog(OptionsSearcher* searcher, wxWindow* parent)
+ : GUI::DPIDialog(parent ? parent : GUI::wxGetApp().tab_panel(), wxID_ANY, _L("Search"), wxDefaultPosition, wxDefaultSize, wxSTAY_ON_TOP | wxRESIZE_BORDER),
searcher(searcher)
{
SetFont(GUI::wxGetApp().normal_font());
@@ -479,13 +573,9 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher)
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#endif
- default_string = _L("Enter a search term");
int border = 10;
int em = em_unit();
- search_line = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
- GUI::wxGetApp().UpdateDarkUI(search_line);
-
search_list = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 40, em * 30), wxDV_NO_HEADER | wxDV_SINGLE
#ifdef _WIN32
| wxBORDER_SIMPLE
@@ -531,15 +621,9 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher)
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
- topSizer->Add(search_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
topSizer->Add(search_list, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
topSizer->Add(check_sizer, 0, wxEXPAND | wxALL, border);
- search_line->Bind(wxEVT_TEXT, &SearchDialog::OnInputText, this);
- search_line->Bind(wxEVT_LEFT_UP, &SearchDialog::OnLeftUpInTextCtrl, this);
- // process wxEVT_KEY_DOWN to navigate inside search_list, if ArrowUp/Down was pressed
- search_line->Bind(wxEVT_KEY_DOWN,&SearchDialog::OnKeyDown, this);
-
search_list->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &SearchDialog::OnSelect, this);
search_list->Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, &SearchDialog::OnActivate, this);
#ifdef __WXMSW__
@@ -559,7 +643,7 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher)
check_english ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this);
// Bind(wxEVT_MOTION, &SearchDialog::OnMotion, this);
- Bind(wxEVT_LEFT_DOWN, &SearchDialog::OnLeftDown, this);
+// Bind(wxEVT_LEFT_DOWN, &SearchDialog::OnLeftDown, this);
SetSizer(topSizer);
topSizer->SetSizeHints(this);
@@ -573,11 +657,6 @@ SearchDialog::~SearchDialog()
void SearchDialog::Popup(wxPoint position /*= wxDefaultPosition*/)
{
- const std::string& line = searcher->search_string();
- search_line->SetValue(line.empty() ? default_string : from_u8(line));
- search_line->SetFocus();
- search_line->SelectAll();
-
update_list();
const OptionViewParameters& params = searcher->view_params;
@@ -587,7 +666,11 @@ void SearchDialog::Popup(wxPoint position /*= wxDefaultPosition*/)
if (position != wxDefaultPosition)
this->SetPosition(position);
- this->ShowModal();
+#ifdef __APPLE__
+ this->ShowWithoutActivating();
+#else
+ this->Show();
+#endif
}
void SearchDialog::ProcessSelection(wxDataViewItem selection)
@@ -606,10 +689,9 @@ void SearchDialog::ProcessSelection(wxDataViewItem selection)
wxPostEvent(GUI::wxGetApp().mainframe, event);
}
-void SearchDialog::OnInputText(wxCommandEvent&)
+void SearchDialog::input_text(wxString input_string)
{
- wxString input_string = search_line->GetValue();
- if (input_string == default_string)
+ if (input_string == searcher->default_string)
input_string.Clear();
searcher->search(into_u8(input_string));
@@ -617,14 +699,6 @@ void SearchDialog::OnInputText(wxCommandEvent&)
update_list();
}
-void SearchDialog::OnLeftUpInTextCtrl(wxEvent& event)
-{
- if (search_line->GetValue() == default_string)
- search_line->SetValue("");
-
- event.Skip();
-}
-
void SearchDialog::OnKeyDown(wxKeyEvent& event)
{
int key = event.GetKeyCode();
@@ -749,7 +823,7 @@ void SearchDialog::on_sys_color_changed()
#ifdef _WIN32
GUI::wxGetApp().UpdateAllStaticTextDarkUI(this);
GUI::wxGetApp().UpdateDarkUI(static_cast(this->FindWindowById(wxID_CANCEL, this)), true);
- for (wxWindow* win : std::vector {search_line, search_list, check_category, check_english})
+ for (wxWindow* win : std::vector {search_list, check_category, check_english})
if (win) GUI::wxGetApp().UpdateDarkUI(win);
#endif
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index 571448d492..87187247f3 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -27,6 +27,7 @@
#include "Widgets/CheckBox.hpp"
class CheckBox;
+class TextInput;
namespace Slic3r {
@@ -91,6 +92,8 @@ class OptionsSearcher
std::map groups_and_categories;
PrinterTechnology printer_technology {ptAny};
ConfigOptionMode mode{ comUndef };
+ TextInput* search_input { nullptr };
+ SearchDialog* search_dialog { nullptr };
std::vector