mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 10:51:22 +00:00
Merge branch 'main' into zaa
This commit is contained in:
@@ -285,7 +285,7 @@ void PingCodeBindDialog::on_bind_printer(wxCommandEvent& event)
|
||||
}
|
||||
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (agent && agent->is_user_login() && ping_code.length() == PING_CODE_LENGTH) {
|
||||
if (agent && agent->is_user_login(wxGetApp().get_printer_cloud_provider()) && ping_code.length() == PING_CODE_LENGTH) {
|
||||
auto result = agent->ping_bind(ping_code.ToStdString());
|
||||
|
||||
if(result < 0){
|
||||
@@ -868,11 +868,12 @@ void BindMachineDialog::on_show(wxShowEvent &event)
|
||||
|
||||
m_printer_name->SetLabelText(from_u8(m_machine_info->get_dev_name()));
|
||||
|
||||
if (wxGetApp().is_user_login()) {
|
||||
wxString username_text = from_u8(wxGetApp().getAgent()->get_user_nickname());
|
||||
const std::string provider = wxGetApp().get_printer_cloud_provider();
|
||||
if (wxGetApp().is_user_login(provider)) {
|
||||
wxString username_text = from_u8(wxGetApp().getAgent()->get_user_nickname(provider));
|
||||
m_user_name->SetLabelText(username_text);
|
||||
|
||||
std::string avatar_url = wxGetApp().getAgent()->get_user_avatar();
|
||||
std::string avatar_url = wxGetApp().getAgent()->get_user_avatar(provider);
|
||||
Slic3r::Http http = Slic3r::Http::get(avatar_url);
|
||||
std::string suffix = avatar_url.substr(avatar_url.find_last_of(".") + 1);
|
||||
http.header("accept", "image/" + suffix)
|
||||
@@ -1017,7 +1018,7 @@ void UnBindMachineDialog::on_cancel(wxCommandEvent &event)
|
||||
|
||||
void UnBindMachineDialog::on_unbind_printer(wxCommandEvent &event)
|
||||
{
|
||||
if (!wxGetApp().is_user_login()) {
|
||||
if (!wxGetApp().is_user_login(wxGetApp().get_printer_cloud_provider())) {
|
||||
m_status_text->SetLabelText(_L("Please log in first."));
|
||||
return;
|
||||
}
|
||||
@@ -1073,11 +1074,12 @@ void UnBindMachineDialog::on_show(wxShowEvent &event)
|
||||
m_printer_name->SetLabelText(from_u8(m_machine_info->get_dev_name()));
|
||||
|
||||
|
||||
if (wxGetApp().is_user_login()) {
|
||||
wxString username_text = from_u8(wxGetApp().getAgent()->get_user_name());
|
||||
const std::string provider = wxGetApp().get_printer_cloud_provider();
|
||||
if (wxGetApp().is_user_login(provider)) {
|
||||
wxString username_text = from_u8(wxGetApp().getAgent()->get_user_name(provider));
|
||||
m_user_name->SetLabelText(username_text);
|
||||
|
||||
std::string avatar_url = wxGetApp().getAgent()->get_user_avatar();
|
||||
std::string avatar_url = wxGetApp().getAgent()->get_user_avatar(provider);
|
||||
Slic3r::Http http = Slic3r::Http::get(avatar_url);
|
||||
std::string suffix = avatar_url.substr(avatar_url.find_last_of(".") + 1);
|
||||
http.header("accept", "image/" + suffix)
|
||||
|
||||
@@ -244,13 +244,14 @@ void SelectMObjectPopup::Popup(wxWindow* WXUNUSED(focus))
|
||||
m_refresh_timer->Start(MACHINE_LIST_REFRESH_INTERVAL);
|
||||
}
|
||||
|
||||
if (wxGetApp().is_user_login()) {
|
||||
const std::string provider = wxGetApp().get_printer_cloud_provider();
|
||||
if (wxGetApp().is_user_login(provider)) {
|
||||
if (!get_print_info_thread) {
|
||||
get_print_info_thread = new boost::thread(Slic3r::create_thread([this, token = std::weak_ptr<int>(m_token)] {
|
||||
get_print_info_thread = new boost::thread(Slic3r::create_thread([this, token = std::weak_ptr<int>(m_token), provider] {
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
unsigned int http_code;
|
||||
std::string body;
|
||||
int result = agent->get_user_print_info(&http_code, &body);
|
||||
int result = agent->get_user_print_info(&http_code, &body, provider);
|
||||
|
||||
wxGetApp().CallAfter([token, this, result, body]() {
|
||||
if (token.expired()) {return;}
|
||||
@@ -585,7 +586,7 @@ void CalibrationPanel::update_all() {
|
||||
// only disconnected server in cloud mode
|
||||
if (obj->connection_type() != "lan") {
|
||||
if (m_agent) {
|
||||
server_status = m_agent->is_server_connected() ? 0 : (int)MONITOR_DISCONNECTED_SERVER;
|
||||
server_status = m_agent->is_server_connected(wxGetApp().get_printer_cloud_provider()) ? 0 : (int)MONITOR_DISCONNECTED_SERVER;
|
||||
}
|
||||
}
|
||||
show_status((int)MONITOR_DISCONNECTED + server_status);
|
||||
|
||||
@@ -1694,7 +1694,7 @@ void CalibrationPresetPage::update_show_status()
|
||||
|
||||
MachineObject* obj_ = dev->get_selected_machine();
|
||||
if (!obj_) {
|
||||
if (agent->is_user_login()) {
|
||||
if (agent->is_user_login(wxGetApp().get_printer_cloud_provider())) {
|
||||
show_status(CaliPresetPageStatus::CaliPresetStatusInvalidPrinter);
|
||||
}
|
||||
else {
|
||||
@@ -1704,7 +1704,7 @@ void CalibrationPresetPage::update_show_status()
|
||||
}
|
||||
|
||||
if (!obj_->is_lan_mode_printer()) {
|
||||
if (!agent->is_server_connected()) {
|
||||
if (!agent->is_server_connected(wxGetApp().get_printer_cloud_provider())) {
|
||||
show_status(CaliPresetPageStatus::CaliPresetStatusConnectingServer);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -248,8 +248,7 @@ static bool delete_filament_preset_by_name(std::string delete_preset_name, std::
|
||||
if (!need_delete_preset) BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" can't find delete preset and name: %1%") % delete_preset_name;
|
||||
if (!need_delete_preset->setting_id.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "delete preset = " << need_delete_preset->name << ", setting_id = " << need_delete_preset->setting_id;
|
||||
m_presets.set_sync_info_and_save(need_delete_preset->name, need_delete_preset->setting_id, "delete", 0);
|
||||
wxGetApp().delete_preset_from_cloud(need_delete_preset->setting_id);
|
||||
wxGetApp().delete_preset_from_cloud(need_delete_preset->setting_id, need_delete_preset->file);
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" can't preset setting id is empty and name: %1%") % delete_preset_name;
|
||||
}
|
||||
@@ -4348,7 +4347,7 @@ void ExportConfigsDialog::data_init()
|
||||
|
||||
const std::deque<Preset> &filament_presets = preset_bundle.filaments.get_presets();
|
||||
for (const Preset &filament_preset : filament_presets) {
|
||||
if (filament_preset.is_system || filament_preset.is_default || filament_preset.is_project_embedded) continue;
|
||||
if (!filament_preset.is_user()) continue;
|
||||
if (filament_preset.is_compatible) {
|
||||
Preset *new_filament_preset = new Preset(filament_preset);
|
||||
m_filament_presets[preset_name].push_back(new_filament_preset);
|
||||
@@ -4357,7 +4356,7 @@ void ExportConfigsDialog::data_init()
|
||||
|
||||
const std::deque<Preset> &process_presets = preset_bundle.prints.get_presets();
|
||||
for (const Preset &process_preset : process_presets) {
|
||||
if (process_preset.is_system || process_preset.is_default || process_preset.is_project_embedded) continue;
|
||||
if (!process_preset.is_user()) continue;
|
||||
if (process_preset.is_compatible) {
|
||||
Preset *new_prpcess_preset = new Preset(process_preset);
|
||||
m_process_presets[preset_name].push_back(new_prpcess_preset);
|
||||
@@ -4371,7 +4370,7 @@ void ExportConfigsDialog::data_init()
|
||||
}
|
||||
const std::deque<Preset> &filament_presets = preset_bundle.filaments.get_presets();
|
||||
for (const Preset &filament_preset : filament_presets) {
|
||||
if (filament_preset.is_system || filament_preset.is_default) continue;
|
||||
if (!filament_preset.can_overwrite()) continue;
|
||||
Preset *new_filament_preset = new Preset(filament_preset);
|
||||
const Preset *base_filament_preset = preset_bundle.filaments.get_preset_base(*new_filament_preset);
|
||||
|
||||
|
||||
@@ -18,6 +18,28 @@ namespace Slic3r
|
||||
{
|
||||
class MachineObject;
|
||||
|
||||
/**
|
||||
* DevAmsTray - Represents a single filament tray/slot in an AMS unit or virtual tray.
|
||||
*
|
||||
* Data Population:
|
||||
* ================
|
||||
* This class is used in two contexts with different population paths:
|
||||
*
|
||||
* 1. AMS Trays (within DevFilaSystem):
|
||||
* NetworkAgent → MachineObject::parse_json() → DevFilaSystemParser::ParseV1_0()
|
||||
*
|
||||
* 2. Virtual Trays (MachineObject::vt_slot for external/manual filament):
|
||||
* NetworkAgent → MachineObject::parse_json() → MachineObject::parse_vt_tray()
|
||||
*
|
||||
* Key Fields Used by build_filament_ams_list():
|
||||
* - setting_id: Filament preset identifier (tray_info_idx from printer)
|
||||
* - tag_uid: RFID tag unique ID for identifying filament spools
|
||||
* - m_fila_type: Filament material type (e.g., "PLA", "ABS", "PETG")
|
||||
* - color: Hex color string without '#' prefix (e.g., "FF0000")
|
||||
* - cols: Multi-color component list for gradient/multi-color filaments
|
||||
* - ctype: Color type indicator
|
||||
* - is_exists: Whether filament is currently loaded in the tray
|
||||
*/
|
||||
class DevAmsTray
|
||||
{
|
||||
public:
|
||||
@@ -83,6 +105,27 @@ public:
|
||||
static wxColour decode_color(const std::string& color);
|
||||
};
|
||||
|
||||
/**
|
||||
* DevAms - Represents a single AMS (Automatic Material System) unit.
|
||||
*
|
||||
* An AMS unit is a physical hardware component that holds multiple filament trays.
|
||||
* Different printer models support different AMS variants with varying slot counts
|
||||
* and capabilities.
|
||||
*
|
||||
* Data Population:
|
||||
* ================
|
||||
* Populated by DevFilaSystemParser from printer JSON messages received via NetworkAgent.
|
||||
*
|
||||
* Key Properties:
|
||||
* - m_ams_id: Unique identifier for this AMS unit (string, typically "0", "1", etc.)
|
||||
* - m_ext_id: Which extruder this AMS is connected to (for multi-extruder setups)
|
||||
* - m_trays: Map of tray IDs to DevAmsTray pointers containing filament data
|
||||
*
|
||||
* AMS Type Variants:
|
||||
* - AMS (type 1): Standard 4-slot AMS with humidity control
|
||||
* - AMS_LITE (type 2): Simplified version
|
||||
* - N3F/N3S (types 3,4): Newer variants with different humidity/drying support
|
||||
*/
|
||||
class DevAms
|
||||
{
|
||||
friend class DevFilaSystemParser;
|
||||
@@ -146,6 +189,36 @@ private:
|
||||
int m_left_dry_time = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* DevFilaSystem - Central manager for all AMS-related data on a printer.
|
||||
*
|
||||
* This class owns and manages the hierarchy of AMS units (DevAms) and their trays (DevAmsTray).
|
||||
* It provides the primary interface for querying filament/AMS state used by the GUI.
|
||||
*
|
||||
* Data Flow Architecture:
|
||||
* =======================
|
||||
* Printer Device (sends status via MQTT/LAN)
|
||||
* ↓
|
||||
* NetworkAgent (receives JSON, invokes registered callbacks)
|
||||
* ↓
|
||||
* MachineObject::parse_json() (delegates to DevFilaSystemParser)
|
||||
* ↓
|
||||
* DevFilaSystemParser::ParseV1_0() (populates this DevFilaSystem instance)
|
||||
* ↓
|
||||
* GUI functions like build_filament_ams_list() read from here
|
||||
*
|
||||
* Key Methods:
|
||||
* - GetAmsList(): Returns map of all AMS units (ams_id -> DevAms*)
|
||||
* - GetAmsTray(): Retrieves specific tray by AMS ID and tray ID
|
||||
* - HasAms(): Checks if any AMS units are connected
|
||||
*
|
||||
* Ownership:
|
||||
* - Owned by MachineObject (m_fila_system member)
|
||||
* - Owns all DevAms instances which in turn own DevAmsTray instances
|
||||
*
|
||||
* Note: This class does NOT directly communicate with NetworkAgent.
|
||||
* It is a passive data store populated by the parsing layer.
|
||||
*/
|
||||
class DevFilaSystem
|
||||
{
|
||||
friend class DevFilaSystemParser;
|
||||
@@ -200,6 +273,18 @@ private:
|
||||
};// class DevFilaSystem
|
||||
|
||||
|
||||
/**
|
||||
* DevFilaSystemParser - Parses printer JSON messages to populate DevFilaSystem.
|
||||
*
|
||||
* This is the bridge between NetworkAgent's raw JSON data and the structured
|
||||
* DevFilaSystem/DevAms/DevAmsTray hierarchy.
|
||||
*
|
||||
* Called from MachineObject::parse_json() when AMS-related fields are present
|
||||
* in printer status messages received via MQTT or LAN communication.
|
||||
*
|
||||
* @see MachineObject::parse_json() - Entry point for JSON parsing
|
||||
* @see DevFilaSystem - Target data structure
|
||||
*/
|
||||
class DevFilaSystemParser
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -363,7 +363,7 @@ namespace Slic3r
|
||||
return obj;
|
||||
}
|
||||
|
||||
int DeviceManager::query_bind_status(std::string& msg)
|
||||
int DeviceManager::query_bind_status(std::string& msg, const std::string& provider)
|
||||
{
|
||||
if (!m_agent)
|
||||
{
|
||||
@@ -381,7 +381,7 @@ namespace Slic3r
|
||||
|
||||
unsigned int http_code;
|
||||
std::string http_body;
|
||||
int result = m_agent->query_bind_status(query_list, &http_code, &http_body);
|
||||
int result = m_agent->query_bind_status(query_list, &http_code, &http_body, provider);
|
||||
|
||||
if (result < 0)
|
||||
{
|
||||
@@ -419,9 +419,9 @@ namespace Slic3r
|
||||
return result;
|
||||
}
|
||||
|
||||
MachineObject* DeviceManager::get_user_machine(std::string dev_id)
|
||||
MachineObject* DeviceManager::get_user_machine(std::string dev_id, const std::string& provider)
|
||||
{
|
||||
if (!m_agent || !m_agent->is_user_login())
|
||||
if (!m_agent || !m_agent->is_user_login(provider))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -442,14 +442,13 @@ namespace Slic3r
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DeviceManager::clean_user_info()
|
||||
void DeviceManager::clean_user_info(bool keep_local_selection)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "DeviceManager::clean_user_info";
|
||||
// reset selected_machine
|
||||
selected_machine = "";
|
||||
local_selected_machine = "";
|
||||
|
||||
OnSelectedMachineChanged(selected_machine, "");
|
||||
const std::string previous_selected_machine = selected_machine;
|
||||
const bool keep_selected_machine = keep_local_selection &&
|
||||
!selected_machine.empty() &&
|
||||
localMachineList.find(selected_machine) != localMachineList.end();
|
||||
|
||||
// clean user list
|
||||
for (auto it = userMachineList.begin(); it != userMachineList.end(); it++)
|
||||
@@ -462,6 +461,13 @@ namespace Slic3r
|
||||
}
|
||||
}
|
||||
userMachineList.clear();
|
||||
|
||||
if (!keep_selected_machine) {
|
||||
selected_machine = "";
|
||||
local_selected_machine = "";
|
||||
}
|
||||
|
||||
OnSelectedMachineChanged(previous_selected_machine, selected_machine);
|
||||
}
|
||||
|
||||
bool DeviceManager::set_selected_machine(std::string dev_id)
|
||||
@@ -566,7 +572,7 @@ namespace Slic3r
|
||||
{
|
||||
if (selected_machine.empty()) return nullptr;
|
||||
|
||||
MachineObject* obj = get_user_machine(selected_machine);
|
||||
MachineObject* obj = get_user_machine(selected_machine, GUI::wxGetApp().get_printer_cloud_provider());
|
||||
if (obj)
|
||||
return obj;
|
||||
|
||||
@@ -683,15 +689,15 @@ namespace Slic3r
|
||||
return "";
|
||||
}
|
||||
|
||||
void DeviceManager::modify_device_name(std::string dev_id, std::string dev_name)
|
||||
void DeviceManager::modify_device_name(std::string dev_id, std::string dev_name, const std::string& provider)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "modify_device_name";
|
||||
if (m_agent)
|
||||
{
|
||||
int result = m_agent->modify_printer_name(dev_id, dev_name);
|
||||
int result = m_agent->modify_printer_name(dev_id, dev_name, provider);
|
||||
if (result == 0)
|
||||
{
|
||||
update_user_machine_list_info();
|
||||
update_user_machine_list_info(provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -712,6 +718,7 @@ namespace Slic3r
|
||||
try
|
||||
{
|
||||
json j = json::parse(body);
|
||||
const std::string provider = GUI::wxGetApp().get_printer_cloud_provider();
|
||||
|
||||
#if !BBL_RELEASE_TO_PUBLIC
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": " << j;
|
||||
@@ -740,7 +747,7 @@ namespace Slic3r
|
||||
obj = new MachineObject(this, m_agent, "", "", "");
|
||||
if (m_agent)
|
||||
{
|
||||
obj->set_bind_status(m_agent->get_user_name());
|
||||
obj->set_bind_status(m_agent->get_user_name(provider));
|
||||
}
|
||||
|
||||
if (obj->get_dev_ip().empty())
|
||||
@@ -806,14 +813,14 @@ namespace Slic3r
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceManager::update_user_machine_list_info()
|
||||
void DeviceManager::update_user_machine_list_info(const std::string& provider)
|
||||
{
|
||||
if (!m_agent) return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "update_user_machine_list_info";
|
||||
unsigned int http_code;
|
||||
std::string body;
|
||||
int result = m_agent->get_user_print_info(&http_code, &body);
|
||||
int result = m_agent->get_user_print_info(&http_code, &body, provider);
|
||||
if (result == 0)
|
||||
{
|
||||
parse_user_print_info(body);
|
||||
@@ -906,7 +913,7 @@ namespace Slic3r
|
||||
}
|
||||
|
||||
// do some refresh
|
||||
if (Slic3r::GUI::wxGetApp().is_user_login())
|
||||
if (Slic3r::GUI::wxGetApp().is_user_login(Slic3r::GUI::wxGetApp().get_printer_cloud_provider()))
|
||||
{
|
||||
m_manager->check_pushing();
|
||||
try
|
||||
@@ -922,4 +929,4 @@ namespace Slic3r
|
||||
// certificate
|
||||
agent->install_device_cert(obj->get_dev_id(), obj->is_lan_mode_printer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,14 +65,14 @@ public:
|
||||
std::map<std::string, MachineObject*> get_user_machinelist() const { return userMachineList; }
|
||||
std::string get_first_online_user_machine() const;
|
||||
void erase_user_machine(std::string dev_id) { userMachineList.erase(dev_id); }
|
||||
void clean_user_info();
|
||||
void clean_user_info(bool keep_local_selection = false);
|
||||
|
||||
void load_last_machine();
|
||||
void update_user_machine_list_info();
|
||||
void update_user_machine_list_info(const std::string& provider);
|
||||
void parse_user_print_info(std::string body);
|
||||
void reload_printer_settings();
|
||||
|
||||
MachineObject* get_user_machine(std::string dev_id);
|
||||
MachineObject* get_user_machine(std::string dev_id, const std::string& provider);
|
||||
|
||||
// subscribe
|
||||
void add_user_subscribe();
|
||||
@@ -83,11 +83,11 @@ public:
|
||||
MachineObject* get_my_machine(std::string dev_id);
|
||||
std::map<std::string, MachineObject*> get_my_machine_list();
|
||||
std::map<std::string, MachineObject*> get_my_cloud_machine_list();
|
||||
void modify_device_name(std::string dev_id, std::string dev_name);
|
||||
void modify_device_name(std::string dev_id, std::string dev_name, const std::string& provider);
|
||||
|
||||
/* create machine or update machine properties */
|
||||
void on_machine_alive(std::string json_str);
|
||||
int query_bind_status(std::string& msg);
|
||||
int query_bind_status(std::string& msg, const std::string& provider);
|
||||
|
||||
// mutil-device
|
||||
void EnableMultiMachine(bool enable = true);
|
||||
|
||||
@@ -2430,7 +2430,7 @@ bool MachineObject::is_connected()
|
||||
if (!is_lan_mode_printer()) {
|
||||
NetworkAgent* m_agent = Slic3r::GUI::wxGetApp().getAgent();
|
||||
if (m_agent) {
|
||||
return m_agent->is_server_connected();
|
||||
return m_agent->is_server_connected(Slic3r::GUI::wxGetApp().get_printer_cloud_provider());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,553 @@
|
||||
#include "ExportPresetBundleDialog.hpp"
|
||||
#include <slic3r/GUI/Widgets/WebView.hpp>
|
||||
#include "GUI_App.hpp"
|
||||
#include "ConfigWizard.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include <libslic3r/Config.hpp>
|
||||
#include <wx/app.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/scrolwin.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <libslic3r/PresetBundle.hpp>
|
||||
#include <wx/string.h>
|
||||
#include <slic3r/GUI/Widgets/WebView.hpp>
|
||||
#include <miniz.h>
|
||||
#include <slic3r/GUI/MsgDialog.hpp>
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
ExportPresetBundleDialog::ExportPresetBundleDialog(
|
||||
wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style)
|
||||
: DPIDialog(parent, id, _L("ExportPresetBundle"), pos, size, style)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
SetMinSize(DESIGN_WINDOW_SIZE);
|
||||
Init();
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
}
|
||||
|
||||
ExportPresetBundleDialog::~ExportPresetBundleDialog()
|
||||
{
|
||||
for (std::pair<std::string, Preset*> printer_preset : m_printer_presets) {
|
||||
Preset* preset = printer_preset.second;
|
||||
if (preset) {
|
||||
delete preset;
|
||||
preset = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExportPresetBundleDialog::LoadUrl(wxString& url)
|
||||
{
|
||||
if (!m_browser)
|
||||
return;
|
||||
BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << " enter, url=" << url.ToStdString();
|
||||
WebView::LoadUrl(m_browser, url);
|
||||
m_browser->SetFocus();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " exit";
|
||||
}
|
||||
|
||||
void ExportPresetBundleDialog::on_dpi_changed(const wxRect& suggested_rect) { this->Refresh(); }
|
||||
|
||||
void ExportPresetBundleDialog::Init()
|
||||
{
|
||||
wxString TargetUrl = from_u8(
|
||||
(boost::filesystem::path(resources_dir()) / "web/dialog/ExportPresetDialog/index.html").make_preferred().string());
|
||||
wxString strlang = wxGetApp().current_language_code_safe();
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", strlang=%1%") % into_u8(strlang);
|
||||
if (strlang != "")
|
||||
TargetUrl = wxString::Format("%s?lang=%s", std::string(TargetUrl.mb_str()), strlang);
|
||||
TargetUrl = "file://" + TargetUrl;
|
||||
|
||||
// Create the webview
|
||||
m_browser = WebView::CreateWebView(this, TargetUrl);
|
||||
if (m_browser == nullptr) {
|
||||
wxLogError("Could not init m_browser");
|
||||
return;
|
||||
}
|
||||
|
||||
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
|
||||
SetTitle(_L("Export Preset Bundle"));
|
||||
SetSizer(topsizer);
|
||||
topsizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
||||
|
||||
// Set a more sensible size for web browsing
|
||||
wxSize pSize = FromDIP(wxSize(820, 660));
|
||||
SetSize(pSize);
|
||||
int screenheight = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y, NULL);
|
||||
int screenwidth = wxSystemSettings::GetMetric(wxSYS_SCREEN_X, NULL);
|
||||
int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0;
|
||||
wxPoint tmpPT((screenwidth - pSize.x) / 2, MaxY);
|
||||
Move(tmpPT);
|
||||
|
||||
Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &ExportPresetBundleDialog::OnScriptMessage, this, m_browser->GetId());
|
||||
|
||||
LoadUrl(TargetUrl);
|
||||
}
|
||||
|
||||
void ExportPresetBundleDialog::RunScript(const wxString& s)
|
||||
{
|
||||
if (!m_browser)
|
||||
return;
|
||||
|
||||
WebView::RunScript(m_browser, s);
|
||||
}
|
||||
|
||||
void ExportPresetBundleDialog::OnScriptMessage(wxWebViewEvent& e)
|
||||
{
|
||||
try {
|
||||
wxString strInput = e.GetString();
|
||||
BOOST_LOG_TRIVIAL(trace) << "ExportPresetBundleDialog::OnScriptMessage;OnRecv:" << strInput.c_str();
|
||||
json j = json::parse(strInput.utf8_string());
|
||||
|
||||
wxString strCmd = j["command"];
|
||||
BOOST_LOG_TRIVIAL(trace) << "ExportPresetBundleDialog::OnScriptMessage;Command:" << strCmd;
|
||||
|
||||
if (strCmd == "close_page") {
|
||||
this->EndModal(wxID_CANCEL);
|
||||
} else if (strCmd == "request_export_preset_profile") {
|
||||
InitExportData();
|
||||
OnRequestPresets();
|
||||
} else if (strCmd == "export_local") {
|
||||
wxFileDialog dlg(this, _L("Save preset bundle"), "", "export.orca_bundle", "Orca Preset Bundle (*.orca_bundle)|*.orca_bundle",
|
||||
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
wxString path;
|
||||
wxString name;
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
path = dlg.GetPath();
|
||||
wxFileName file_name(path);
|
||||
name = file_name.GetName();
|
||||
if (file_name.GetExt().empty()) {
|
||||
file_name.SetExt("orca_bundle");
|
||||
path = file_name.GetFullPath();
|
||||
}
|
||||
}
|
||||
OnExportData(path, name, j["data"]);
|
||||
}
|
||||
|
||||
} catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "ExportPresetBundleDialog::OnScriptMessage;Error:" << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
static std::string get_machine_name(const std::string& preset_name)
|
||||
{
|
||||
size_t index_at = preset_name.find_last_of("@");
|
||||
if (std::string::npos == index_at) {
|
||||
return "";
|
||||
} else {
|
||||
return preset_name.substr(index_at + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string get_filament_name(std::string& preset_name)
|
||||
{
|
||||
size_t index_at = preset_name.find_last_of("@");
|
||||
if (std::string::npos == index_at) {
|
||||
return preset_name;
|
||||
} else {
|
||||
return preset_name.substr(0, index_at - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string get_vendor_name(std::string& preset_name)
|
||||
{
|
||||
if (preset_name.empty())
|
||||
return "";
|
||||
std::string vendor_name = preset_name.substr(preset_name.find_first_not_of(' ')); // remove the name prefix space
|
||||
size_t index_at = vendor_name.find(" ");
|
||||
if (std::string::npos == index_at) {
|
||||
return vendor_name;
|
||||
} else {
|
||||
vendor_name = vendor_name.substr(0, index_at);
|
||||
return vendor_name;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string get_curr_time(const char* format = "%Y_%m_%d_%H_%M_%S")
|
||||
{
|
||||
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
||||
|
||||
std::time_t time = std::chrono::system_clock::to_time_t(now);
|
||||
|
||||
std::tm local_time = *std::localtime(&time);
|
||||
std::ostringstream time_stream;
|
||||
time_stream << std::put_time(&local_time, format);
|
||||
|
||||
std::string current_time = time_stream.str();
|
||||
return current_time;
|
||||
}
|
||||
|
||||
static mz_bool initial_zip_archive(mz_zip_archive& zip_archive, const std::string& file_path)
|
||||
{
|
||||
mz_zip_zero_struct(&zip_archive);
|
||||
mz_bool status;
|
||||
|
||||
// Initialize the ZIP file to write to the structure, using memory storage
|
||||
|
||||
std::string export_dir = encode_path(file_path.c_str());
|
||||
status = mz_zip_writer_init_file(&zip_archive, export_dir.c_str(), 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
void ExportPresetBundleDialog::InitExportData()
|
||||
{
|
||||
// Delete the Temp folder
|
||||
boost::filesystem::path folder(data_dir() + "/" + PRESET_USER_DIR + "/" + "Temp");
|
||||
if (boost::filesystem::exists(folder))
|
||||
boost::filesystem::remove_all(folder);
|
||||
|
||||
boost::system::error_code ec;
|
||||
boost::filesystem::path user_folder(data_dir() + "/" + PRESET_USER_DIR);
|
||||
bool temp_folder_exist = true;
|
||||
if (!boost::filesystem::exists(user_folder)) {
|
||||
if (!boost::filesystem::create_directories(user_folder, ec)) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " create directory failed: " << user_folder << " " << ec.message();
|
||||
temp_folder_exist = false;
|
||||
}
|
||||
}
|
||||
boost::filesystem::path temp_folder(user_folder / "Temp");
|
||||
if (!boost::filesystem::exists(temp_folder)) {
|
||||
if (!boost::filesystem::create_directories(temp_folder, ec)) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " create directory failed: " << temp_folder << " " << ec.message();
|
||||
temp_folder_exist = false;
|
||||
}
|
||||
}
|
||||
if (!temp_folder_exist) {
|
||||
MessageDialog dlg(this, _L("Failed to create temporary folder, please try Export Configs again."),
|
||||
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE);
|
||||
dlg.ShowModal();
|
||||
EndModal(wxCANCEL);
|
||||
}
|
||||
|
||||
PresetBundle preset_bundle(*wxGetApp().preset_bundle);
|
||||
|
||||
const std::deque<Preset>& printer_presets = preset_bundle.printers.get_presets();
|
||||
// for all the printers
|
||||
for (const Preset& printer_preset : printer_presets) {
|
||||
std::string preset_name = printer_preset.name;
|
||||
if (!printer_preset.is_visible || printer_preset.is_default || printer_preset.is_project_embedded)
|
||||
continue;
|
||||
if (preset_bundle.printers.select_preset_by_name(preset_name, true)) {
|
||||
preset_bundle.update_compatible(PresetSelectCompatibleType::Always);
|
||||
|
||||
const std::deque<Preset>& filament_presets = preset_bundle.filaments.get_presets();
|
||||
for (const Preset& filament_preset : filament_presets) {
|
||||
if (!filament_preset.is_user())
|
||||
continue;
|
||||
if (filament_preset.is_compatible) {
|
||||
Preset* new_filament_preset = new Preset(filament_preset);
|
||||
m_filament_presets[preset_name].push_back(new_filament_preset);
|
||||
}
|
||||
}
|
||||
|
||||
const std::deque<Preset>& process_presets = preset_bundle.prints.get_presets();
|
||||
for (const Preset& process_preset : process_presets) {
|
||||
if (!process_preset.is_user())
|
||||
continue;
|
||||
if (process_preset.is_compatible) {
|
||||
Preset* new_prpcess_preset = new Preset(process_preset);
|
||||
m_process_presets[preset_name].push_back(new_prpcess_preset);
|
||||
}
|
||||
}
|
||||
// make new and erase sensitive information
|
||||
Preset* new_printer_preset = new Preset(printer_preset);
|
||||
if (new_printer_preset->type == Preset::Type::TYPE_PRINTER) {
|
||||
boost::filesystem::path file_path(data_dir() + "/" + PRESET_USER_DIR + "/" + "Temp" + "/" +
|
||||
(new_printer_preset->name + ".json"));
|
||||
new_printer_preset->file = file_path.make_preferred().string();
|
||||
|
||||
DynamicPrintConfig& config = new_printer_preset->config;
|
||||
config.erase("print_host");
|
||||
config.erase("print_host_webui");
|
||||
config.erase("printhost_apikey");
|
||||
config.erase("printhost_cafile");
|
||||
config.erase("printhost_user");
|
||||
config.erase("printhost_password");
|
||||
config.erase("printhost_port");
|
||||
|
||||
new_printer_preset->save(nullptr);
|
||||
}
|
||||
m_printer_presets[preset_name] = new_printer_preset;
|
||||
}
|
||||
}
|
||||
const std::deque<Preset>& filament_presets = preset_bundle.filaments.get_presets();
|
||||
for (const Preset& filament_preset : filament_presets) {
|
||||
if (!filament_preset.can_overwrite())
|
||||
continue;
|
||||
Preset* new_filament_preset = new Preset(filament_preset);
|
||||
const Preset* base_filament_preset = preset_bundle.filaments.get_preset_base(*new_filament_preset);
|
||||
|
||||
if (base_filament_preset == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Failed to find base preset";
|
||||
continue;
|
||||
}
|
||||
std::string filament_preset_name = base_filament_preset->name;
|
||||
std::string machine_name = get_machine_name(filament_preset_name);
|
||||
m_filament_name_to_presets[get_filament_name(filament_preset_name)].push_back(
|
||||
std::make_pair(get_vendor_name(machine_name), new_filament_preset));
|
||||
}
|
||||
}
|
||||
|
||||
void ExportPresetBundleDialog::OnRequestPresets()
|
||||
{
|
||||
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
|
||||
json res;
|
||||
res["command"] = "response_export_preset_profile";
|
||||
res["sequence_id"] = "2000";
|
||||
res["data"] = json::object();
|
||||
|
||||
res["data"]["printers"] = json::array();
|
||||
res["data"]["filaments"] = json::array();
|
||||
res["data"]["process"] = json::array();
|
||||
|
||||
for (std::pair<std::string, Preset*> preset : m_printer_presets) {
|
||||
if (preset.second->is_system)
|
||||
continue;
|
||||
res["data"]["printers"].push_back(preset.first);
|
||||
}
|
||||
|
||||
for (std::pair<std::string, std::vector<std::pair<std::string, Preset*>>> filament_name_to_preset : m_filament_name_to_presets) {
|
||||
if (filament_name_to_preset.second.empty())
|
||||
continue;
|
||||
res["data"]["filaments"].push_back(filament_name_to_preset.first);
|
||||
}
|
||||
|
||||
for (std::pair<std::string, std::vector<const Preset*>> presets : m_process_presets) {
|
||||
Preset* printer_preset = preset_bundle->printers.find_preset(presets.first, false);
|
||||
if (!printer_preset)
|
||||
continue;
|
||||
if (!printer_preset->is_system)
|
||||
continue;
|
||||
if (preset_bundle->printers.get_preset_base(*printer_preset) != printer_preset)
|
||||
continue;
|
||||
for (const Preset* preset : presets.second) {
|
||||
if (!preset->is_system) {
|
||||
res["data"]["process"].push_back(preset->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wxString strJS = wxString::Format("HandleStudio(%s)", wxString::FromUTF8(res.dump(-1, ' ', false, json::error_handler_t::ignore)));
|
||||
wxGetApp().CallAfter([this, strJS] { RunScript(strJS); });
|
||||
}
|
||||
|
||||
void ExportPresetBundleDialog::OnExportData(const wxString& path, const wxString& filename, json data)
|
||||
{
|
||||
auto get_names = [&](const char* key) {
|
||||
std::vector<std::string> out;
|
||||
auto it = data.find(key);
|
||||
if (it == data.end() || !it->is_array())
|
||||
return out;
|
||||
for (const auto& v : *it) {
|
||||
if (v.is_string())
|
||||
out.push_back(v.get<std::string>());
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
// JS BuildResultPayload uses: machines / filaments / presets
|
||||
const auto machine_names = get_names("machines");
|
||||
const auto filament_names = get_names("filaments");
|
||||
const auto process_names = get_names("presets"); // or "process" if your JS sends that
|
||||
|
||||
std::vector<Preset*> selected_printers;
|
||||
for (const auto& name : machine_names) {
|
||||
auto it = m_printer_presets.find(name);
|
||||
if (it != m_printer_presets.end() && it->second)
|
||||
selected_printers.push_back(it->second);
|
||||
}
|
||||
|
||||
std::vector<Preset*> selected_filaments;
|
||||
for (const auto& name : filament_names) {
|
||||
auto it = m_filament_name_to_presets.find(name); // name -> vector<pair<vendor, Preset*>>
|
||||
if (it == m_filament_name_to_presets.end())
|
||||
continue;
|
||||
for (const auto& vp : it->second) {
|
||||
if (vp.second)
|
||||
selected_filaments.push_back(vp.second);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Preset*> selected_processes;
|
||||
for (const auto& name : process_names) {
|
||||
if (Preset* p = wxGetApp().preset_bundle->prints.find_preset(name, false))
|
||||
selected_processes.push_back(p);
|
||||
}
|
||||
|
||||
std::string export_path = into_u8(path);
|
||||
if (export_path.empty() || "initial_failed" == export_path)
|
||||
return;
|
||||
|
||||
boost::filesystem::path export_file_path = boost::filesystem::path(export_path).make_preferred();
|
||||
if (export_file_path.extension().empty())
|
||||
export_file_path += ".orca_bundle";
|
||||
|
||||
const boost::filesystem::path export_dir = export_file_path.parent_path();
|
||||
if (!export_dir.empty() && !boost::filesystem::exists(export_dir)) {
|
||||
boost::system::error_code ec;
|
||||
if (!boost::filesystem::create_directories(export_dir, ec)) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " create directory failed: " << export_dir << " " << ec.message();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " export file path: " << export_file_path.string();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Export printer preset bundle";
|
||||
|
||||
json bundle_structure;
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
std::string clock = get_curr_time();
|
||||
if (agent) {
|
||||
bundle_structure["version"] = agent->get_version();
|
||||
bundle_structure["bundle_id"] = agent->get_user_id() + "_" + std::string(filename.utf8_string()) + "_" + clock;
|
||||
} else {
|
||||
bundle_structure["version"] = "";
|
||||
std::string id;
|
||||
bundle_structure["bundle_id"] = id + "offline_" + "_" + clock;
|
||||
}
|
||||
bundle_structure["bundle_type"] = "printer config bundle";
|
||||
bundle_structure["printer_preset_name"] = "export";
|
||||
json printer_config = json::array();
|
||||
json filament_configs = json::array();
|
||||
json process_configs = json::array();
|
||||
mz_zip_archive zip_archive;
|
||||
mz_bool status = initial_zip_archive(zip_archive, export_file_path.string());
|
||||
if (MZ_FALSE == status) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Failed to initialize ZIP archive";
|
||||
show_export_result(ExportCase::INITIALIZE_FAIL);
|
||||
}
|
||||
for (auto& printer : selected_printers) {
|
||||
boost::filesystem::path printer_file_path = boost::filesystem::path(printer->file);
|
||||
std::string preset_path = printer_file_path.make_preferred().string();
|
||||
if (preset_path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Export printer preset: " << printer->name << " skip because of the preset file path is empty.";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add a file to the ZIP file
|
||||
std::string printer_config_file_name = "printer/" + printer_file_path.filename().string();
|
||||
status = mz_zip_writer_add_file(&zip_archive, printer_config_file_name.c_str(), encode_path(preset_path.c_str()).c_str(), NULL, 0,
|
||||
MZ_DEFAULT_COMPRESSION);
|
||||
if (MZ_FALSE == status) {
|
||||
BOOST_LOG_TRIVIAL(info) << printer->name << " Failed to add file to ZIP archive";
|
||||
mz_zip_writer_end(&zip_archive);
|
||||
show_export_result(ExportCase::ADD_FILE_FAIL);
|
||||
return;
|
||||
}
|
||||
printer_config.push_back(printer_config_file_name);
|
||||
BOOST_LOG_TRIVIAL(info) << "Printer preset json add successful: " << printer->name;
|
||||
}
|
||||
for (auto& filament : selected_filaments) {
|
||||
boost::filesystem::path filament_file_path = boost::filesystem::path(filament->file);
|
||||
std::string filament_preset_path = filament_file_path.make_preferred().string();
|
||||
if (filament_preset_path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Export filament preset: " << filament->name << " skip because of the preset file path is empty.";
|
||||
continue;
|
||||
}
|
||||
std::string filament_config_file_name = "filament/" + filament_file_path.filename().string();
|
||||
status = mz_zip_writer_add_file(&zip_archive, filament_config_file_name.c_str(), encode_path(filament_preset_path.c_str()).c_str(),
|
||||
NULL, 0, MZ_DEFAULT_COMPRESSION);
|
||||
if (MZ_FALSE == status) {
|
||||
BOOST_LOG_TRIVIAL(info) << filament->name << " Failed to add file to ZIP archive";
|
||||
mz_zip_writer_end(&zip_archive);
|
||||
show_export_result(ExportCase::ADD_FILE_FAIL);
|
||||
return;
|
||||
}
|
||||
filament_configs.push_back(filament_config_file_name);
|
||||
BOOST_LOG_TRIVIAL(info) << "Filament preset json add successful.";
|
||||
}
|
||||
|
||||
for (auto& process : selected_processes) {
|
||||
boost::filesystem::path process_file_path = boost::filesystem::path(process->file);
|
||||
std::string process_preset_path = process_file_path.make_preferred().string();
|
||||
if (process_preset_path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Export process preset: " << process->name << " skip because of the preset file path is empty.";
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string process_config_file_name = "process/" + process_file_path.filename().string();
|
||||
status = mz_zip_writer_add_file(&zip_archive, process_config_file_name.c_str(), encode_path(process_preset_path.c_str()).c_str(),
|
||||
NULL, 0, MZ_DEFAULT_COMPRESSION);
|
||||
if (MZ_FALSE == status) {
|
||||
BOOST_LOG_TRIVIAL(info) << process->name << " Failed to add file to ZIP archive";
|
||||
mz_zip_writer_end(&zip_archive);
|
||||
show_export_result(ExportCase::ADD_FILE_FAIL);
|
||||
return;
|
||||
}
|
||||
process_configs.push_back(process_config_file_name);
|
||||
BOOST_LOG_TRIVIAL(info) << "Process preset json add successful: ";
|
||||
}
|
||||
|
||||
bundle_structure["printer_config"] = printer_config;
|
||||
bundle_structure["filament_config"] = filament_configs;
|
||||
bundle_structure["process_config"] = process_configs;
|
||||
|
||||
std::string bundle_structure_str = bundle_structure.dump();
|
||||
status = mz_zip_writer_add_mem(&zip_archive, BUNDLE_STRUCTURE_JSON_NAME, bundle_structure_str.data(), bundle_structure_str.size(),
|
||||
MZ_DEFAULT_COMPRESSION);
|
||||
if (MZ_FALSE == status) {
|
||||
BOOST_LOG_TRIVIAL(info) << " Failed to add file: " << BUNDLE_STRUCTURE_JSON_NAME;
|
||||
mz_zip_writer_end(&zip_archive);
|
||||
show_export_result(ExportCase::ADD_BUNDLE_STRUCTURE_FAIL);
|
||||
return;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << " Success to add file: " << BUNDLE_STRUCTURE_JSON_NAME;
|
||||
|
||||
// Complete writing of ZIP file
|
||||
mz_bool s = mz_zip_writer_finalize_archive(&zip_archive);
|
||||
if (MZ_FALSE == s) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Failed to finalize ZIP archive";
|
||||
mz_zip_writer_end(&zip_archive);
|
||||
show_export_result(ExportCase::FINALIZE_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Release ZIP file to write structure and related resources
|
||||
mz_zip_writer_end(&zip_archive);
|
||||
// if (ExportCase::CASE_COUNT != save_result) return save_result;
|
||||
BOOST_LOG_TRIVIAL(info) << "ZIP archive created successfully";
|
||||
}
|
||||
|
||||
void ExportPresetBundleDialog::show_export_result(const ExportCase& export_case)
|
||||
{
|
||||
MessageDialog* msg_dlg = nullptr;
|
||||
switch (export_case) {
|
||||
case ExportCase::INITIALIZE_FAIL:
|
||||
msg_dlg = new MessageDialog(this, _L("initialize fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
|
||||
wxYES | wxYES_DEFAULT | wxCENTRE);
|
||||
break;
|
||||
case ExportCase::ADD_FILE_FAIL:
|
||||
msg_dlg = new MessageDialog(this, _L("add file fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
|
||||
wxYES | wxYES_DEFAULT | wxCENTRE);
|
||||
break;
|
||||
case ExportCase::ADD_BUNDLE_STRUCTURE_FAIL:
|
||||
msg_dlg = new MessageDialog(this, _L("add bundle structure file fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
|
||||
wxYES | wxYES_DEFAULT | wxCENTRE);
|
||||
break;
|
||||
case ExportCase::FINALIZE_FAIL:
|
||||
msg_dlg = new MessageDialog(this, _L("finalize fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
|
||||
wxYES | wxYES_DEFAULT | wxCENTRE);
|
||||
break;
|
||||
case ExportCase::OPEN_ZIP_WRITTEN_FILE:
|
||||
msg_dlg = new MessageDialog(this, _L("open zip written fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
|
||||
wxYES | wxYES_DEFAULT | wxCENTRE);
|
||||
break;
|
||||
case ExportCase::EXPORT_SUCCESS:
|
||||
msg_dlg = new MessageDialog(this, _L("Export successful"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
|
||||
wxYES | wxYES_DEFAULT | wxCENTRE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg_dlg) {
|
||||
msg_dlg->ShowModal();
|
||||
delete msg_dlg;
|
||||
msg_dlg = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,86 @@
|
||||
#ifndef slic3r_ExportPresetBundleDialog_hpp_
|
||||
#define slic3r_ExportPresetBundleDialog_hpp_
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include <slic3r/GUI/GUI.hpp>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/language.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/fswatcher.h>
|
||||
#include <wx/webview.h>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
#define DESIGN_GRAY900_COLOR wxColour("#363636") // Label color
|
||||
#define DESIGN_GRAY600_COLOR wxColour("#ACACAC") // Dimmed text color
|
||||
|
||||
#define DESIGN_WINDOW_SIZE wxSize(FromDIP(640), FromDIP(640))
|
||||
#define DESIGN_TITLE_SIZE wxSize(FromDIP(280), -1)
|
||||
#define DESIGN_COMBOBOX_SIZE wxSize(FromDIP(120), -1)
|
||||
#define DESIGN_LARGE_COMBOBOX_SIZE wxSize(FromDIP(120), -1)
|
||||
#define DESIGN_INPUT_SIZE wxSize(FromDIP(120), -1)
|
||||
#define DESIGN_LEFT_MARGIN 25
|
||||
#define VERTICAL_GAP_SIZE FromDIP(4)
|
||||
|
||||
enum ExportCase {
|
||||
INITIALIZE_FAIL = 0,
|
||||
ADD_FILE_FAIL,
|
||||
ADD_BUNDLE_STRUCTURE_FAIL,
|
||||
FINALIZE_FAIL,
|
||||
OPEN_ZIP_WRITTEN_FILE,
|
||||
EXPORT_CANCEL,
|
||||
EXPORT_SUCCESS,
|
||||
CASE_COUNT,
|
||||
};
|
||||
|
||||
class ExportPresetBundleDialog : public Slic3r::GUI::DPIDialog
|
||||
{
|
||||
public:
|
||||
ExportPresetBundleDialog(wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxString& title = wxT(""),
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxMAXIMIZE_BOX);
|
||||
|
||||
~ExportPresetBundleDialog();
|
||||
|
||||
// Utilities
|
||||
bool seq_top_layer_only_changed() const { return m_seq_top_layer_only_changed; }
|
||||
bool recreate_GUI() const { return m_recreate_GUI; }
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
void show_export_result(const ExportCase& e);
|
||||
|
||||
void Init();
|
||||
void InitExportData();
|
||||
|
||||
// Webview
|
||||
void LoadUrl(wxString& url);
|
||||
void OnScriptMessage(wxWebViewEvent& e);
|
||||
void RunScript(const wxString& s);
|
||||
void OnRequestPresets();
|
||||
void OnExportData(const wxString& path, const wxString& name, json data);
|
||||
|
||||
protected:
|
||||
bool m_seq_top_layer_only_changed{false};
|
||||
bool m_recreate_GUI{false};
|
||||
|
||||
// Webview
|
||||
wxWebView* m_browser{nullptr};
|
||||
|
||||
// Export Preset
|
||||
std::unordered_map<std::string, Preset*> m_printer_presets; // first: printer name, second: printer presets have same printer name
|
||||
std::unordered_map<std::string, std::vector<const Preset*>>
|
||||
m_filament_presets; // first: printer name, second: filament presets have same printer name
|
||||
std::unordered_map<std::string, std::vector<const Preset*>>
|
||||
m_process_presets; // first: printer name, second: filament presets have same printer name
|
||||
std::unordered_map<std::string, std::vector<std::pair<std::string, Preset*>>>
|
||||
m_filament_name_to_presets; // first: filament name, second presets have same filament name and printer name in vector
|
||||
};
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "ICloudServiceAgent.hpp"
|
||||
#include "format.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
@@ -524,15 +525,6 @@ void about()
|
||||
dlg.ShowModal();
|
||||
}
|
||||
|
||||
void login()
|
||||
{
|
||||
//LoginDialog dlg;
|
||||
//dlg.ShowModal();
|
||||
|
||||
ZUserLogin dlg;
|
||||
dlg.run();
|
||||
}
|
||||
|
||||
void desktop_open_datadir_folder()
|
||||
{
|
||||
// Execute command to open a file explorer, platform dependent.
|
||||
|
||||
@@ -79,8 +79,6 @@ boost::filesystem::path into_path(const wxString &str);
|
||||
|
||||
// Display an About dialog
|
||||
extern void about();
|
||||
// Display a Login dialog
|
||||
extern void login();
|
||||
// Ask the destop to open the datadir using the default file explorer.
|
||||
extern void desktop_open_datadir_folder();
|
||||
// Ask the destop to open one folder
|
||||
|
||||
+1097
-254
File diff suppressed because it is too large
Load Diff
+61
-25
@@ -6,11 +6,13 @@
|
||||
#include "ImGuiWrapper.hpp"
|
||||
#include "ConfigWizard.hpp"
|
||||
#include "OpenGLManager.hpp"
|
||||
#include "PresetBundleDialog.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
#include "slic3r/GUI/UserNotification.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
#include "slic3r/Utils/BBLCloudServiceAgent.hpp"
|
||||
#include "slic3r/GUI/WebViewDialog.hpp"
|
||||
#include "slic3r/GUI/WebUserLoginDialog.hpp"
|
||||
#include "slic3r/GUI/BindDialog.hpp"
|
||||
@@ -293,13 +295,21 @@ private:
|
||||
Slic3r::UserManager* m_user_manager { nullptr };
|
||||
Slic3r::TaskManager* m_task_manager { nullptr };
|
||||
NetworkAgent* m_agent { nullptr };
|
||||
std::vector<std::string> need_delete_presets; // store setting ids of preset
|
||||
std::map<std::string, std::string> need_delete_presets; // store setting ids of preset
|
||||
std::vector<bool> m_create_preset_blocked { false, false, false, false, false, false }; // excceed limit
|
||||
bool m_networking_compatible { false };
|
||||
bool m_networking_need_update { false };
|
||||
bool m_networking_cancel_update { false };
|
||||
std::shared_ptr<UpgradeNetworkJob> m_upgrade_network_job;
|
||||
|
||||
// ORCA: for installing vendors on the main thread when presets to be synced requires it
|
||||
// vendor structure is:
|
||||
// [vendor_name]: { model: variants, model: variants, ... }
|
||||
// filaments structure is:
|
||||
// [filament_name]: true/false, ...
|
||||
std::map<std::string, std::map<std::string, std::set<std::string>>> need_add_vendors;
|
||||
std::map<std::string, std::string> need_add_filaments;
|
||||
|
||||
// login widget
|
||||
ZUserLogin* login_dlg { nullptr };
|
||||
|
||||
@@ -460,27 +470,29 @@ public:
|
||||
wxString transition_tridid(int trid_id) const;
|
||||
void ShowUserGuide();
|
||||
void ShowDownNetPluginDlg();
|
||||
void ShowUserLogin(bool show = true);
|
||||
void ShowUserLogin(bool show = true, const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void ShowOnlyFilament();
|
||||
//BBS
|
||||
void request_login(bool show_user_info = false);
|
||||
bool check_login();
|
||||
void get_login_info();
|
||||
bool is_user_login();
|
||||
// Orca auth
|
||||
void request_login(bool show_user_info = false, const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
bool check_login(const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void get_login_info(const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
bool is_user_login(const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
const std::string& get_printer_cloud_provider() const;
|
||||
|
||||
void request_user_login(int online_login = 0);
|
||||
void request_user_handle(int online_login = 0);
|
||||
void request_user_logout();
|
||||
int request_user_unbind(std::string dev_id);
|
||||
void request_user_login(int online_login = 0, const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void request_user_handle(int online_login = 0, const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void request_user_logout(const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void post_logout_to_webview(const std::string& provider);
|
||||
int request_user_unbind(std::string dev_id, const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
std::string handle_web_request(std::string cmd);
|
||||
void handle_script_message(std::string msg);
|
||||
void handle_script_message(std::string msg, const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void request_model_download(wxString url);
|
||||
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);
|
||||
|
||||
void handle_http_error(unsigned int status, std::string body);
|
||||
void handle_http_error(unsigned int status, std::string body, const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void on_http_error(wxCommandEvent &evt);
|
||||
void on_update_machine_list(wxCommandEvent& evt);
|
||||
void on_user_login(wxCommandEvent &evt);
|
||||
@@ -507,19 +519,36 @@ public:
|
||||
void push_notification(const MachineObject* obj, wxString msg, wxString title = wxEmptyString, UserNotificationStyle style = UserNotificationStyle::UNS_NORMAL);
|
||||
void reload_settings();
|
||||
void remove_user_presets();
|
||||
|
||||
bool maybe_migrate_user_presets_on_login();
|
||||
|
||||
// ORCA: functions for loading unloaded vendors to allow for proper inheritance when syncing user presets/bundles
|
||||
bool check_preset_parent_available(const std::pair<std::string, std::map<std::string, std::string>>& preset_data);
|
||||
void add_pending_vendor_preset(const std::pair<std::string, std::map<std::string, std::string>>& preset_data);
|
||||
void load_pending_vendors();
|
||||
|
||||
void sync_preset(Preset* preset);
|
||||
void start_sync_user_preset(bool with_progress_dlg = false);
|
||||
void stop_sync_user_preset();
|
||||
void start_http_server();
|
||||
void start_http_server(int port);
|
||||
|
||||
// Bundle subscription sync
|
||||
void check_bundle_updates();
|
||||
void sync_bundle(std::string bundle_id, std::string version);
|
||||
bool unsubscribe_bundle(const std::string& id);
|
||||
void update_single_bundle(wxCommandEvent& evt);
|
||||
|
||||
PresetBundleDialog* m_preset_bundle_dlg{nullptr};
|
||||
|
||||
void start_http_server(const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void start_http_server(int port, const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void stop_http_server();
|
||||
void switch_staff_pick(bool on);
|
||||
|
||||
void on_show_check_privacy_dlg(int online_login = 0);
|
||||
void on_show_check_privacy_dlg(int online_login = 0, const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void show_check_privacy_dlg(wxCommandEvent& evt);
|
||||
void on_check_privacy_update(wxCommandEvent &evt);
|
||||
bool check_privacy_update();
|
||||
void check_privacy_version(int online_login = 0);
|
||||
void check_privacy_version(int online_login = 0, const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void check_track_enable();
|
||||
|
||||
static bool catch_error(std::function<void()> cb, const std::string& err);
|
||||
@@ -560,10 +589,13 @@ public:
|
||||
bool checked_tab(Tab* tab);
|
||||
//BBS: add preset combox re-active logic
|
||||
void load_current_presets(bool active_preset_combox = false, bool check_printer_presets = true);
|
||||
std::vector<std::string> &get_delete_cache_presets();
|
||||
std::vector<std::string> get_delete_cache_presets_lock();
|
||||
void delete_preset_from_cloud(std::string setting_id);
|
||||
std::map<std::string, std::string> &get_delete_cache_presets();
|
||||
std::map<std::string, std::string> get_delete_cache_presets_lock();
|
||||
void process_delete_presets();
|
||||
void delete_preset_from_cloud(std::string setting_id, std::string preset_file_path);
|
||||
void preset_deleted_from_cloud(std::string setting_id);
|
||||
void scan_orphaned_info_files();
|
||||
static std::string extract_setting_id_from_info(const std::string& info_file_path);
|
||||
|
||||
wxString filter_string(wxString str);
|
||||
wxString current_language_code() const { return m_active_language_code.empty() && m_wxLocale ? m_wxLocale->GetCanonicalName() : m_active_language_code; }
|
||||
@@ -572,7 +604,8 @@ public:
|
||||
bool is_localized() const { return m_wxLocale->GetLocale() != "English"; }
|
||||
|
||||
void open_preferences(size_t open_on_tab = 0, const std::string& highlight_option = std::string());
|
||||
|
||||
void open_presetbundledialog(size_t open_on_tab = 0, const std::string& highlight_option = std::string());
|
||||
void open_exportpresetbundledialog(size_t open_on_tab = 0, const std::string& highlight_option = std::string());
|
||||
virtual bool OnExceptionInMainLoop() override;
|
||||
// Calls wxLaunchDefaultBrowser if user confirms in dialog.
|
||||
bool open_browser_with_warning_dialog(const wxString& url, int flags = 0);
|
||||
@@ -707,6 +740,10 @@ public:
|
||||
bool hot_reload_network_plugin();
|
||||
std::string get_latest_network_version() const;
|
||||
bool has_network_update_available() const;
|
||||
// Orca: return the client version to report to Bambu servers. Pinned to
|
||||
// 01.10.01.50 when the legacy network plugin lacks get_my_token support
|
||||
// so the auth server stays on the ?access_token= redirect path.
|
||||
std::string get_bbl_client_version();
|
||||
|
||||
private:
|
||||
int updating_bambu_networking();
|
||||
@@ -718,10 +755,6 @@ private:
|
||||
void remove_old_networking_plugins();
|
||||
void drain_pending_events(int timeout_ms);
|
||||
bool wait_for_network_idle(int timeout_ms);
|
||||
//BBS set extra header for http request
|
||||
std::map<std::string, std::string> get_extra_header();
|
||||
void init_http_extra_header();
|
||||
void update_http_extra_header();
|
||||
bool check_older_app_config(Semver current_version, bool backup);
|
||||
void copy_older_config();
|
||||
void window_pos_save(wxTopLevelWindow* window, const std::string &name);
|
||||
@@ -747,6 +780,9 @@ private:
|
||||
|
||||
DECLARE_APP(GUI_App)
|
||||
wxDECLARE_EVENT(EVT_CONNECT_LAN_MODE_PRINT, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_UPDATE_PRESET_BUNDLE, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_UPDATE_BUNDLE_COMPLETE, wxCommandEvent);
|
||||
|
||||
|
||||
bool is_support_filament(int extruder_id, bool strict_check = true);
|
||||
bool is_soluble_filament(int extruder_id);
|
||||
|
||||
+140
-20
@@ -3,6 +3,7 @@
|
||||
#include "GUI_App.hpp"
|
||||
#include "slic3r/Utils/Http.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
#include "slic3r/Utils/BBLNetworkPlugin.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@@ -60,7 +61,7 @@ void session::read_body()
|
||||
int nbuffer = 1000;
|
||||
std::shared_ptr<std::vector<char>> bufptr = std::make_shared<std::vector<char>>(nbuffer);
|
||||
async_read(socket, boost::asio::buffer(*bufptr, nbuffer),
|
||||
[this, self](const boost::beast::error_code& e, std::size_t s) { server.stop(self); });
|
||||
[this, self, bufptr](const boost::beast::error_code& e, std::size_t s) { server.stop(self); });
|
||||
}
|
||||
|
||||
void session::read_next_line()
|
||||
@@ -145,18 +146,27 @@ void HttpServer::IOServer::stop_all()
|
||||
|
||||
HttpServer::HttpServer(boost::asio::ip::port_type port) : port(port) {}
|
||||
|
||||
HttpServer::~HttpServer()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void HttpServer::start()
|
||||
{
|
||||
if (start_http_server)
|
||||
return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "start_http_service...";
|
||||
start_http_server = true;
|
||||
m_http_server_thread = create_thread([this] {
|
||||
server_ = std::make_unique<IOServer>(*this);
|
||||
IOServer* io_server = server_.get();
|
||||
start_http_server = true;
|
||||
m_http_server_thread = create_thread([io_server] {
|
||||
set_current_thread_name("http_server");
|
||||
server_ = std::make_unique<IOServer>(*this);
|
||||
server_->acceptor.listen();
|
||||
io_server->acceptor.listen();
|
||||
|
||||
server_->do_accept();
|
||||
io_server->do_accept();
|
||||
|
||||
server_->io_service.run();
|
||||
io_server->io_service.run();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -164,9 +174,14 @@ void HttpServer::stop()
|
||||
{
|
||||
start_http_server = false;
|
||||
if (server_) {
|
||||
server_->acceptor.close();
|
||||
server_->stop_all();
|
||||
server_->io_service.stop();
|
||||
IOServer* io_server = server_.get();
|
||||
boost::asio::post(io_server->io_service, [io_server] {
|
||||
boost::system::error_code ec;
|
||||
io_server->acceptor.cancel(ec);
|
||||
io_server->acceptor.close(ec);
|
||||
io_server->stop_all();
|
||||
io_server->io_service.stop();
|
||||
});
|
||||
}
|
||||
if (m_http_server_thread.joinable())
|
||||
m_http_server_thread.join();
|
||||
@@ -179,12 +194,20 @@ void HttpServer::set_request_handler(const std::function<std::shared_ptr<Respons
|
||||
}
|
||||
|
||||
std::shared_ptr<HttpServer::Response> HttpServer::bbl_auth_handle_request(const std::string& url)
|
||||
{
|
||||
return auth_handle_request(url, BBL_CLOUD_PROVIDER);
|
||||
}
|
||||
|
||||
std::shared_ptr<HttpServer::Response> HttpServer::auth_handle_request(const std::string& url, const std::string& provider)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "thirdparty_login: get_response";
|
||||
|
||||
const std::string auth_code = url_get_param(url, "code");
|
||||
if (!auth_code.empty()) {
|
||||
std::string state = url_get_param(url, "state");
|
||||
std::string state = url_get_param(url, "orca_state");
|
||||
if (state.empty()) {
|
||||
state = url_get_param(url, "state"); // fallback
|
||||
}
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (!agent) {
|
||||
return std::make_shared<ResponseNotFound>();
|
||||
@@ -195,10 +218,10 @@ std::shared_ptr<HttpServer::Response> HttpServer::bbl_auth_handle_request(const
|
||||
payload["data"]["code"] = auth_code;
|
||||
payload["data"]["state"] = state;
|
||||
|
||||
agent->change_user(payload.dump());
|
||||
const bool login_ok = agent->is_user_login();
|
||||
agent->change_user(payload.dump(), provider);
|
||||
const bool login_ok = agent->is_user_login(provider);
|
||||
if (login_ok) {
|
||||
wxGetApp().request_user_login(1);
|
||||
wxGetApp().request_user_login(1, provider);
|
||||
GUI::wxGetApp().CallAfter([] { wxGetApp().ShowUserLogin(false); });
|
||||
}
|
||||
|
||||
@@ -228,7 +251,7 @@ std::shared_ptr<HttpServer::Response> HttpServer::bbl_auth_handle_request(const
|
||||
|
||||
unsigned int http_code;
|
||||
std::string http_body;
|
||||
int result = agent->get_my_profile(access_token, &http_code, &http_body);
|
||||
int result = agent->get_my_profile(access_token, &http_code, &http_body, provider);
|
||||
if (result == 0) {
|
||||
std::string user_id;
|
||||
std::string user_name;
|
||||
@@ -256,9 +279,9 @@ std::shared_ptr<HttpServer::Response> HttpServer::bbl_auth_handle_request(const
|
||||
j["data"]["user"]["name"] = user_name;
|
||||
j["data"]["user"]["account"] = user_account;
|
||||
j["data"]["user"]["avatar"] = user_avatar;
|
||||
agent->change_user(j.dump());
|
||||
if (agent->is_user_login()) {
|
||||
wxGetApp().request_user_login(1);
|
||||
agent->change_user(j.dump(), provider);
|
||||
if (agent->is_user_login(provider)) {
|
||||
wxGetApp().request_user_login(1, provider);
|
||||
}
|
||||
GUI::wxGetApp().CallAfter([] { wxGetApp().ShowUserLogin(false); });
|
||||
std::string location_str = (boost::format("%1%?result=success") % redirect_url).str();
|
||||
@@ -268,9 +291,106 @@ std::shared_ptr<HttpServer::Response> HttpServer::bbl_auth_handle_request(const
|
||||
std::string location_str = (boost::format("%1%?result=fail&error=%2%") % redirect_url % error_str).str();
|
||||
return std::make_shared<ResponseRedirect>(location_str);
|
||||
}
|
||||
} else {
|
||||
return std::make_shared<ResponseNotFound>();
|
||||
}
|
||||
|
||||
// Ticket-based redirect: Bambu Lab's auth server redirects here after a
|
||||
// third-party (Google) OAuth so that the access token never travels through
|
||||
// the URL. We exchange the ticket via the network plugin's get_my_token,
|
||||
// then run the same get_my_profile + change_user flow as access_token.
|
||||
// Skip entirely on legacy plugins missing bambu_network_get_my_token —
|
||||
// those clients pin X-BBL-Client-Version so the server stays on the legacy
|
||||
// ?access_token= redirect path and never sends ?ticket= here.
|
||||
const std::string ticket = url_get_param(url, "ticket");
|
||||
const std::string ticket_redirect_url = url_get_param(url, "redirect_url");
|
||||
if (!ticket.empty() && !ticket_redirect_url.empty() &&
|
||||
BBLNetworkPlugin::instance().get_get_my_token() != nullptr) {
|
||||
BOOST_LOG_TRIVIAL(info) << "thirdparty_login: ticket flow";
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (!agent) {
|
||||
std::string location_str = (boost::format("%1%?result=fail&error=no_agent") % ticket_redirect_url).str();
|
||||
return std::make_shared<ResponseRedirect>(location_str);
|
||||
}
|
||||
|
||||
auto fail_redirect = [&ticket_redirect_url](const std::string& reason) {
|
||||
std::string location_str = (boost::format("%1%?result=fail&error=%2%") % ticket_redirect_url % reason).str();
|
||||
return std::make_shared<ResponseRedirect>(location_str);
|
||||
};
|
||||
|
||||
unsigned int token_http_code = 0;
|
||||
std::string token_body;
|
||||
int token_result = agent->get_my_token(ticket, &token_http_code, &token_body, provider);
|
||||
if (token_result != 0) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "thirdparty_login: get_my_token failed, http_code=" << token_http_code;
|
||||
return fail_redirect("get_my_token_error_" + std::to_string(token_result));
|
||||
}
|
||||
|
||||
std::string access_token;
|
||||
std::string refresh_token;
|
||||
std::string expires_in_str;
|
||||
std::string refresh_expires_in_str;
|
||||
try {
|
||||
json token_j = json::parse(token_body);
|
||||
if (token_j.contains("accessToken"))
|
||||
access_token = token_j["accessToken"].get<std::string>();
|
||||
if (token_j.contains("refreshToken"))
|
||||
refresh_token = token_j["refreshToken"].get<std::string>();
|
||||
if (token_j.contains("expiresIn"))
|
||||
expires_in_str = std::to_string(token_j["expiresIn"].get<double>());
|
||||
if (token_j.contains("refreshExpiresIn"))
|
||||
refresh_expires_in_str = std::to_string(token_j["refreshExpiresIn"].get<double>());
|
||||
} catch (...) {
|
||||
return fail_redirect("token_parse_error");
|
||||
}
|
||||
|
||||
if (access_token.empty()) {
|
||||
return fail_redirect("token_missing");
|
||||
}
|
||||
|
||||
unsigned int profile_http_code = 0;
|
||||
std::string profile_body;
|
||||
int profile_result = agent->get_my_profile(access_token, &profile_http_code, &profile_body, provider);
|
||||
if (profile_result != 0) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "thirdparty_login: get_my_profile failed, http_code=" << profile_http_code;
|
||||
return fail_redirect("get_user_profile_error_" + std::to_string(profile_result));
|
||||
}
|
||||
|
||||
std::string user_id;
|
||||
std::string user_name;
|
||||
std::string user_account;
|
||||
std::string user_avatar;
|
||||
try {
|
||||
json user_j = json::parse(profile_body);
|
||||
if (user_j.contains("uidStr"))
|
||||
user_id = user_j["uidStr"].get<std::string>();
|
||||
if (user_j.contains("name"))
|
||||
user_name = user_j["name"].get<std::string>();
|
||||
if (user_j.contains("avatar"))
|
||||
user_avatar = user_j["avatar"].get<std::string>();
|
||||
if (user_j.contains("account"))
|
||||
user_account = user_j["account"].get<std::string>();
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "thirdparty_login: profile JSON parse failed";
|
||||
}
|
||||
|
||||
json j;
|
||||
j["data"]["refresh_token"] = refresh_token;
|
||||
j["data"]["token"] = access_token;
|
||||
j["data"]["expires_in"] = expires_in_str;
|
||||
j["data"]["refresh_expires_in"] = refresh_expires_in_str;
|
||||
j["data"]["user"]["uid"] = user_id;
|
||||
j["data"]["user"]["name"] = user_name;
|
||||
j["data"]["user"]["account"] = user_account;
|
||||
j["data"]["user"]["avatar"] = user_avatar;
|
||||
agent->change_user(j.dump(), provider);
|
||||
if (agent->is_user_login(provider)) {
|
||||
wxGetApp().request_user_login(1, provider);
|
||||
}
|
||||
GUI::wxGetApp().CallAfter([] { wxGetApp().ShowUserLogin(false); });
|
||||
std::string location_str = (boost::format("%1%?result=success") % ticket_redirect_url).str();
|
||||
return std::make_shared<ResponseRedirect>(location_str);
|
||||
}
|
||||
|
||||
return std::make_shared<ResponseNotFound>();
|
||||
}
|
||||
|
||||
void HttpServer::ResponseNotFound::write_response(std::stringstream& ssOut)
|
||||
|
||||
@@ -110,6 +110,7 @@ public:
|
||||
};
|
||||
|
||||
HttpServer(boost::asio::ip::port_type port = LOCALHOST_PORT);
|
||||
~HttpServer();
|
||||
|
||||
boost::thread m_http_server_thread;
|
||||
bool start_http_server = false;
|
||||
@@ -122,6 +123,7 @@ public:
|
||||
void set_request_handler(const std::function<std::shared_ptr<Response>(const std::string&)>& m_request_handler);
|
||||
|
||||
static std::shared_ptr<Response> bbl_auth_handle_request(const std::string& url);
|
||||
static std::shared_ptr<Response> auth_handle_request(const std::string& url, const std::string& provider);
|
||||
|
||||
private:
|
||||
class IOServer
|
||||
|
||||
@@ -125,7 +125,7 @@ void BindJob::process(Ctl &ctl)
|
||||
post_fail_event(result_code, result_info);
|
||||
return;
|
||||
}
|
||||
dev->update_user_machine_list_info();
|
||||
dev->update_user_machine_list_info(wxGetApp().get_printer_cloud_provider());
|
||||
|
||||
wxCommandEvent event(EVT_BIND_MACHINE_SUCCESS);
|
||||
event.SetEventObject(m_event_handle);
|
||||
|
||||
@@ -3243,6 +3243,15 @@ void MainFrame::init_menubar_as_editor()
|
||||
wxGetApp().open_preferences();
|
||||
},
|
||||
"", nullptr, []() { return true; }, this);
|
||||
|
||||
append_menu_item(
|
||||
m_topbar->GetTopMenu(), wxID_ANY, _L("Preset Bundle") + "\t", "",
|
||||
[this](wxCommandEvent &) {
|
||||
// Orca: Use GUI_App::open_preferences instead of direct call so windows associations are updated on exit
|
||||
wxGetApp().open_presetbundledialog();
|
||||
plater()->get_current_canvas3D()->force_set_focus();
|
||||
},
|
||||
"", nullptr, []() { return true; }, this);
|
||||
//m_topbar->AddDropDownMenuItem(preference_item);
|
||||
//m_topbar->AddDropDownMenuItem(printer_item);
|
||||
//m_topbar->AddDropDownMenuItem(language_item);
|
||||
@@ -3343,6 +3352,15 @@ void MainFrame::init_menubar_as_editor()
|
||||
{return m_plater->is_view3D_shown();; }, this);
|
||||
|
||||
#else
|
||||
// On Mac, the Apple menu ignores non-standard custom items, so add Preset Bundle to the File menu
|
||||
fileMenu->AppendSeparator();
|
||||
append_menu_item(
|
||||
fileMenu, wxID_ANY, _L("Preset Bundle"), "",
|
||||
[this](wxCommandEvent &) {
|
||||
wxGetApp().open_presetbundledialog();
|
||||
plater()->get_current_canvas3D()->force_set_focus();
|
||||
},
|
||||
"", nullptr, []() { return true; }, this);
|
||||
m_menubar->Append(fileMenu, wxString::Format("&%s", _L("File")));
|
||||
if (editMenu)
|
||||
m_menubar->Append(editMenu, wxString::Format("&%s", _L("Edit")));
|
||||
@@ -3645,7 +3663,7 @@ void MainFrame::load_config_file()
|
||||
// return;
|
||||
wxFileDialog dlg(this, _L("Select profile to load:"),
|
||||
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
|
||||
"config.json", "Config files (*.json;*.zip;*.orca_printer;*.orca_filament)|*.json;*.zip;*.orca_printer;*.orca_filament", wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
|
||||
"config.json", "Config files (*.json;*.zip;*.orca_printer;*.orca_bundle;*.orca_filament)|*.json;*.zip;*.orca_printer;*.orca_bundle;*.orca_filament", wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
|
||||
wxArrayString files;
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return;
|
||||
@@ -3662,11 +3680,16 @@ void MainFrame::load_config_file()
|
||||
int ids[]{wxID_NO, wxID_YES, wxID_NOTOALL, wxID_YESTOALL};
|
||||
return std::find(ids, ids + 4, res) - ids;
|
||||
},
|
||||
ForwardCompatibilitySubstitutionRule::Enable);
|
||||
ForwardCompatibilitySubstitutionRule::Enable,
|
||||
*wxGetApp().app_config);
|
||||
if (!cfiles.empty()) {
|
||||
wxGetApp().app_config->update_config_dir(get_dir_name(cfiles.back()));
|
||||
wxGetApp().load_current_presets();
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " presets has been import,and size is" << cfiles.size();
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (agent) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " user is: " << agent->get_user_id();
|
||||
}
|
||||
}
|
||||
wxGetApp().preset_bundle->update_compatible(PresetSelectCompatibleType::Always);
|
||||
update_side_preset_ui();
|
||||
@@ -4213,7 +4236,7 @@ void MainFrame::update_side_preset_ui()
|
||||
void MainFrame::on_select_default_preset(SimpleEvent& evt)
|
||||
{
|
||||
MessageDialog dialog(this,
|
||||
_L("Do you want to synchronize your personal data from Bambu Cloud?\n"
|
||||
_L("Do you want to synchronize your personal data from Orca Cloud?\n"
|
||||
"It contains the following information:\n"
|
||||
"1. The Process presets\n"
|
||||
"2. The Filament presets\n"
|
||||
|
||||
@@ -362,7 +362,7 @@ void MonitorPanel::update_all()
|
||||
// only disconnected server in cloud mode
|
||||
if (obj->connection_type() != "lan") {
|
||||
if (m_agent) {
|
||||
server_status = m_agent->is_server_connected() ? 0 : (int)MONITOR_DISCONNECTED_SERVER;
|
||||
server_status = m_agent->is_server_connected(wxGetApp().get_printer_cloud_provider()) ? 0 : (int)MONITOR_DISCONNECTED_SERVER;
|
||||
}
|
||||
}
|
||||
show_status((int) MONITOR_DISCONNECTED + server_status);
|
||||
|
||||
@@ -2231,6 +2231,138 @@ void NotificationManager::push_import_finished_notification(const std::string& p
|
||||
set_slicing_progress_hidden();
|
||||
}
|
||||
|
||||
// SharedProfilesNotification implementation
|
||||
|
||||
void NotificationManager::SharedProfilesNotification::init()
|
||||
{
|
||||
PopNotification::init();
|
||||
// Add one extra line for the hyperlink row ("Browse shared profiles" + "Don't show again")
|
||||
m_lines_count++;
|
||||
}
|
||||
|
||||
void NotificationManager::SharedProfilesNotification::render_text(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
float x_offset = m_left_indentation;
|
||||
float shift_y = m_line_height;
|
||||
float starting_y = m_line_height / 2;
|
||||
|
||||
// Render main text line(s)
|
||||
int last_end = 0;
|
||||
std::string line;
|
||||
for (size_t i = 0; i < m_endlines.size(); i++) {
|
||||
if (m_text1.size() >= m_endlines[i]) {
|
||||
line = m_text1.substr(last_end, m_endlines[i] - last_end);
|
||||
last_end = m_endlines[i];
|
||||
if (m_text1.size() > m_endlines[i])
|
||||
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
|
||||
ImGui::SetCursorPosX(x_offset);
|
||||
ImGui::SetCursorPosY(starting_y + i * shift_y);
|
||||
imgui.text(line.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Render "Browse shared profiles" hyperlink on the next line
|
||||
float hyper_y = starting_y + m_endlines.size() * shift_y;
|
||||
render_hypertext(imgui, x_offset, hyper_y, m_hypertext);
|
||||
|
||||
// Render "Don't show again" hyperlink after the browse link
|
||||
{
|
||||
float dont_show_x = x_offset + ImGui::CalcTextSize((m_hypertext + " ").c_str()).x;
|
||||
std::string dont_show_text = _u8L("Don't show again");
|
||||
ImVec2 part_size = ImGui::CalcTextSize(dont_show_text.c_str());
|
||||
|
||||
// Invisible button
|
||||
ImGui::SetCursorPosX(dont_show_x - 4);
|
||||
ImGui::SetCursorPosY(hyper_y - 5);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
if (imgui.button("##dont_show_btn", part_size.x + 6, part_size.y + 10)) {
|
||||
wxGetApp().app_config->set_bool("show_shared_profiles_notification", false);
|
||||
wxGetApp().app_config->save();
|
||||
close();
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
// Hover color
|
||||
ImVec4 color = m_HyperTextColor;
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly))
|
||||
color.y += 0.1f;
|
||||
|
||||
// Text
|
||||
push_style_color(ImGuiCol_Text, color, m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::SetCursorPosX(dont_show_x);
|
||||
ImGui::SetCursorPosY(hyper_y);
|
||||
imgui.text(dont_show_text.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
// Underline
|
||||
ImVec2 lineEnd = ImGui::GetItemRectMax();
|
||||
lineEnd.y -= 2;
|
||||
ImVec2 lineStart = lineEnd;
|
||||
lineStart.x = ImGui::GetItemRectMin().x;
|
||||
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd,
|
||||
IM_COL32((int)(color.x * 255), (int)(color.y * 255), (int)(color.z * 255),
|
||||
(int)(color.w * 255.f * (m_state == EState::FadingOut ? m_current_fade_opacity : 1.f))));
|
||||
}
|
||||
}
|
||||
|
||||
bool NotificationManager::SharedProfilesNotification::on_text_click()
|
||||
{
|
||||
wxLaunchDefaultBrowser(m_explore_url);
|
||||
return false;
|
||||
}
|
||||
|
||||
void NotificationManager::SharedProfilesNotification::render_hypertext(ImGuiWrapper& imgui,
|
||||
const float text_x, const float text_y, const std::string text, bool more)
|
||||
{
|
||||
// Invisible button
|
||||
ImVec2 part_size = ImGui::CalcTextSize(text.c_str());
|
||||
ImGui::SetCursorPosX(text_x - 4);
|
||||
ImGui::SetCursorPosY(text_y - 5);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
if (imgui.button("##browse_btn", part_size.x + 6, part_size.y + 10)) {
|
||||
if (on_text_click()) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
// Hover color
|
||||
ImVec4 HyperColor = m_HyperTextColor;
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly))
|
||||
HyperColor.y += 0.1f;
|
||||
|
||||
// Text
|
||||
push_style_color(ImGuiCol_Text, HyperColor, m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::SetCursorPosX(text_x);
|
||||
ImGui::SetCursorPosY(text_y);
|
||||
imgui.text(text.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
// Underline
|
||||
ImVec2 lineEnd = ImGui::GetItemRectMax();
|
||||
lineEnd.y -= 2;
|
||||
ImVec2 lineStart = lineEnd;
|
||||
lineStart.x = ImGui::GetItemRectMin().x;
|
||||
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd,
|
||||
IM_COL32((int)(HyperColor.x * 255), (int)(HyperColor.y * 255), (int)(HyperColor.z * 255),
|
||||
(int)(HyperColor.w * 255.f * (m_state == EState::FadingOut ? m_current_fade_opacity : 1.f))));
|
||||
}
|
||||
|
||||
void NotificationManager::push_shared_profiles_notification(const std::string& explore_url)
|
||||
{
|
||||
close_notification_of_type(NotificationType::OrcaSharedProfilesAvailable);
|
||||
NotificationData data{ NotificationType::OrcaSharedProfilesAvailable, NotificationLevel::RegularNotificationLevel, 0,
|
||||
_u8L("Shared profiles may be available for this printer."),
|
||||
_u8L("Browse shared profiles") };
|
||||
push_notification_data(std::make_unique<NotificationManager::SharedProfilesNotification>(data, m_id_provider, m_evt_handler, explore_url), 0);
|
||||
}
|
||||
|
||||
void NotificationManager::push_download_URL_progress_notification(size_t id, const std::string& text, std::function<bool(DownloaderUserAction, int)> user_action_callback)
|
||||
{
|
||||
// If already exists
|
||||
|
||||
@@ -161,6 +161,7 @@ enum class NotificationType
|
||||
BBLBedFilamentIncompatible,
|
||||
BBLMixUsePLAAndPETG,
|
||||
BBLNozzleFilamentIncompatible,
|
||||
OrcaSharedProfilesAvailable,
|
||||
NotificationTypeCount
|
||||
|
||||
};
|
||||
@@ -271,6 +272,9 @@ public:
|
||||
void push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable);
|
||||
void push_import_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable);
|
||||
|
||||
// Shared profiles available for selected printer
|
||||
void push_shared_profiles_notification(const std::string& explore_url);
|
||||
|
||||
// Download URL progress notif
|
||||
void push_download_URL_progress_notification(size_t id, const std::string& text, std::function<bool(DownloaderUserAction, int)> user_action_callback);
|
||||
void set_download_URL_progress(size_t id, float percentage);
|
||||
@@ -858,6 +862,30 @@ private:
|
||||
};
|
||||
|
||||
// in SlicingProgressNotification.hpp
|
||||
|
||||
class SharedProfilesNotification : public PopNotification
|
||||
{
|
||||
public:
|
||||
SharedProfilesNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler,
|
||||
const std::string& explore_url)
|
||||
: PopNotification(n, id_provider, evt_handler)
|
||||
, m_explore_url(explore_url)
|
||||
{
|
||||
m_multiline = true;
|
||||
}
|
||||
protected:
|
||||
void init() override;
|
||||
void render_text(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
bool on_text_click() override;
|
||||
void render_hypertext(ImGuiWrapper& imgui,
|
||||
const float text_x, const float text_y,
|
||||
const std::string text, bool more = false) override;
|
||||
|
||||
std::string m_explore_url;
|
||||
bool m_dont_show_clicked{ false };
|
||||
};
|
||||
class SlicingProgressNotification;
|
||||
|
||||
// in HintNotification.hpp
|
||||
|
||||
+69
-20
@@ -161,6 +161,8 @@
|
||||
#include "DailyTips.hpp"
|
||||
#include "CreatePresetsDialog.hpp"
|
||||
#include "FileArchiveDialog.hpp"
|
||||
#include "../Utils/Http.hpp"
|
||||
#include "../Utils/OrcaCloudServiceAgent.hpp"
|
||||
#include "StepMeshDialog.hpp"
|
||||
#include "FilamentMapDialog.hpp"
|
||||
#include "CloneDialog.hpp"
|
||||
@@ -5992,11 +5994,24 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
|
||||
// BBS: version check
|
||||
Semver app_version = *(Semver::parse(SoftFever_VERSION));
|
||||
const wxString load_3mf_title = _L("Load 3MF");
|
||||
const wxString newer_3mf_title = _L("Newer 3MF version");
|
||||
const wxString bambu_project_title = _L("BambuStudio Project");
|
||||
const wxString msg_unsupported_geometry = _L("The 3MF is not supported by OrcaSlicer, loading geometry data only.");
|
||||
const wxString msg_old_orca_geometry = _L("The 3MF file was generated by an old OrcaSlicer version, loading geometry data only.");
|
||||
const wxString msg_older_geometry = _L("The 3MF file was generated by an older version, loading geometry data only.");
|
||||
const wxString msg_bambu_geometry = _L("The 3MF file was generated by BambuStudio, loading geometry data only.");
|
||||
auto log_and_show_3mf_info = [&](const wxString& text, const wxString& title) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__
|
||||
<< " "
|
||||
<< boost::format("3MF import message [%1%]: %2% | file: %3%") % into_u8(title) % into_u8(text) % path.string();
|
||||
show_info(q, text, title);
|
||||
};
|
||||
if (en_3mf_file_type == En3mfType::From_Prusa) {
|
||||
// do not reset the model config
|
||||
load_config = false;
|
||||
if(load_type != LoadType::LoadGeometry)
|
||||
show_info(q, _L("The 3MF is not supported by OrcaSlicer, loading geometry data only."), _L("Load 3MF"));
|
||||
if (load_type != LoadType::LoadGeometry)
|
||||
log_and_show_3mf_info(msg_unsupported_geometry, load_3mf_title);
|
||||
}
|
||||
else if (en_3mf_file_type == En3mfType::From_Orca) {
|
||||
// OrcaSlicer file (has OrcaSlicer tag) - compare file_version with SoftFever_VERSION
|
||||
@@ -6032,7 +6047,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
wxString append = _L("You'd better upgrade your software.\n");
|
||||
context += "\n\n";
|
||||
context += append;
|
||||
show_info(q, context, _L("Newer 3MF version"));
|
||||
log_and_show_3mf_info(context, newer_3mf_title);
|
||||
}
|
||||
else {
|
||||
//if the minor version is not matched
|
||||
@@ -6040,13 +6055,13 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
wxString text = wxString::Format(_L("The 3MF file version %s is newer than %s's version %s, we suggest to upgrade your software."),
|
||||
file_version.to_string_sf(), std::string(SLIC3R_APP_FULL_NAME), app_version.to_string_sf());
|
||||
text += "\n";
|
||||
show_info(q, text, _L("Newer 3MF version"));
|
||||
log_and_show_3mf_info(text, newer_3mf_title);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (load_config && config_loaded.empty()) {
|
||||
load_config = false;
|
||||
show_info(q, _L("The 3MF file was generated by an old OrcaSlicer version, loading geometry data only."), _L("Load 3MF"));
|
||||
log_and_show_3mf_info(msg_old_orca_geometry, load_3mf_title);
|
||||
}
|
||||
}
|
||||
else if (en_3mf_file_type == En3mfType::From_BBS) {
|
||||
@@ -6081,7 +6096,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
}
|
||||
else if (load_config && config_loaded.empty()) {
|
||||
load_config = false;
|
||||
show_info(q, _L("The 3MF file was generated by an older version, loading geometry data only."), _L("Load 3MF"));
|
||||
log_and_show_3mf_info(msg_older_geometry, load_3mf_title);
|
||||
}
|
||||
} else {
|
||||
// BambuStudio project (version > 2.3.2 without OrcaSlicer tag)
|
||||
@@ -6089,7 +6104,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
Semver slic3r_version = *(Semver::parse(SLIC3R_VERSION));
|
||||
if (load_config && config_loaded.empty()) {
|
||||
load_config = false;
|
||||
show_info(q, _L("The 3MF file was generated by BambuStudio, loading geometry data only."), _L("Load 3MF"));
|
||||
log_and_show_3mf_info(msg_bambu_geometry, load_3mf_title);
|
||||
}
|
||||
else if (load_config && (file_version > slic3r_version)) {
|
||||
// BambuStudio file version is newer than our compatible SLIC3R_VERSION
|
||||
@@ -6101,23 +6116,32 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
wxString append = _L("You'd better upgrade your software.\n");
|
||||
context += "\n\n";
|
||||
context += append;
|
||||
show_info(q, context, _L("BambuStudio Project"));
|
||||
log_and_show_3mf_info(context, bambu_project_title);
|
||||
} else {
|
||||
wxString text = wxString::Format(_L("The 3MF was created by BambuStudio (version %s), which is newer than the compatible version %s. Some settings may not be fully compatible."),
|
||||
file_version.to_string(), slic3r_version.to_string());
|
||||
text += "\n";
|
||||
show_info(q, text, _L("BambuStudio Project"));
|
||||
log_and_show_3mf_info(text, bambu_project_title);
|
||||
}
|
||||
} else if (load_config) {
|
||||
// BambuStudio version is older or same as our SLIC3R_VERSION
|
||||
wxString text = _L("The 3MF was created by BambuStudio. Some settings may differ from OrcaSlicer.");
|
||||
show_info(q, text, _L("BambuStudio Project"));
|
||||
log_and_show_3mf_info(text, bambu_project_title);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (en_3mf_file_type == En3mfType::From_Other) {
|
||||
// Generic CAD/other 3MF without slicer metadata: import geometry silently.
|
||||
if (load_config && config_loaded.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__
|
||||
<< " "
|
||||
<< boost::format("3MF has no slicer metadata/project config, importing geometry only: %1%") % path.string();
|
||||
load_config = false;
|
||||
}
|
||||
}
|
||||
else if (load_config && config_loaded.empty()) {
|
||||
load_config = false;
|
||||
show_info(q, _L("The 3MF file was generated by an old OrcaSlicer version, loading geometry data only."), _L("Load 3MF"));
|
||||
log_and_show_3mf_info(msg_old_orca_geometry, load_3mf_title);
|
||||
}
|
||||
else if (!load_config) {
|
||||
// reset config except color
|
||||
@@ -6207,9 +6231,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
config += std::move(config_loaded);
|
||||
std::map<std::string, std::string> validity = config.validate();
|
||||
if (!validity.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("Param values in 3mf error: ");
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << " " << boost::format("Param values in 3mf error: ");
|
||||
for (std::map<std::string, std::string>::iterator it=validity.begin(); it!=validity.end(); ++it)
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("%1%: %2%")%it->first %it->second;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << " " << boost::format("%1%: %2%")%it->first %it->second;
|
||||
//
|
||||
NotificationManager *notify_manager = q->get_notification_manager();
|
||||
std::string error_message = L("Invalid values found in the 3MF:");
|
||||
@@ -6691,7 +6715,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
model_object->center_around_origin(false);
|
||||
|
||||
// BBS
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_LOAD_MODEL_OBJECTS \n");
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << " " << boost::format("import 3mf IMPORT_LOAD_MODEL_OBJECTS \n");
|
||||
wxString msg = wxString::Format("Loading file: %s", from_path(real_filename));
|
||||
model_idx++;
|
||||
dlg_cont = dlg.Update(progress_percent, msg);
|
||||
@@ -9401,12 +9425,17 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
|
||||
//! instead of
|
||||
//! combo->GetStringSelection().ToUTF8().data());
|
||||
|
||||
wxString wx_name = combo->GetString(selection);
|
||||
// if (preset_type == Preset::TYPE_PRINTER) {
|
||||
// wx_name = combo->get_preset_item_name(selection); }
|
||||
|
||||
std::string preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type,
|
||||
Preset::remove_suffix_modified(wx_name.ToUTF8().data()));
|
||||
// Prefer the internal preset name stored per combo item to avoid alias collisions
|
||||
// (e.g. user preset and bundle preset sharing the same display alias).
|
||||
wxString wx_stored_name = combo->GetItemAlias(selection);
|
||||
std::string preset_name;
|
||||
if (!wx_stored_name.empty()) {
|
||||
preset_name = Preset::remove_suffix_modified(wx_stored_name.ToUTF8().data());
|
||||
} else {
|
||||
wxString wx_name = combo->GetString(selection);
|
||||
preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type,
|
||||
Preset::remove_suffix_modified(wx_name.ToUTF8().data()));
|
||||
}
|
||||
|
||||
if (preset_type == Preset::TYPE_FILAMENT) {
|
||||
wxGetApp().preset_bundle->set_filament_preset(idx, preset_name);
|
||||
@@ -9518,6 +9547,26 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
|
||||
for (size_t idx = 0; idx < filament_size; ++idx)
|
||||
wxGetApp().plater()->sidebar().auto_calc_flushing_volumes(idx);
|
||||
#endif
|
||||
|
||||
// Show shared profiles notification for the newly selected printer
|
||||
if (wxGetApp().app_config->get_bool("show_shared_profiles_notification"))
|
||||
{
|
||||
std::string printer_name = wxGetApp().preset_bundle->printers.get_selected_preset_base().name;
|
||||
std::string encoded_name = Http::url_encode(printer_name);
|
||||
|
||||
std::string cloud_url;
|
||||
if (auto agent = wxGetApp().getAgent()) {
|
||||
if (auto orca_agent = std::dynamic_pointer_cast<OrcaCloudServiceAgent>(agent->get_cloud_agent())) {
|
||||
cloud_url = orca_agent->get_cloud_base_url();
|
||||
}
|
||||
}
|
||||
if (cloud_url.empty())
|
||||
cloud_url = "https://cloud.orcaslicer.com";
|
||||
|
||||
std::string explore_url = cloud_url + "/app/bundles/explore?printers=" + encoded_name;
|
||||
|
||||
wxGetApp().plater()->get_notification_manager()->push_shared_profiles_notification(explore_url);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __WXMSW__
|
||||
|
||||
@@ -1356,6 +1356,9 @@ void PreferencesDialog::create_items()
|
||||
auto item_show_splash_scr = create_item_checkbox(_L("Show splash screen"), _L("Show the splash screen during startup."), "show_splash_screen");
|
||||
g_sizer->Add(item_show_splash_scr);
|
||||
|
||||
auto item_shared_profiles = create_item_checkbox(_L("Show shared profiles notification"), _L("Show a notification with a link to browse shared profiles when the selected printer is changed."), "show_shared_profiles_notification");
|
||||
g_sizer->Add(item_shared_profiles);
|
||||
|
||||
#ifdef __linux__
|
||||
auto item_window_button_pos = create_item_checkbox(_L("Use window buttons on left side"), "", "window_buttons_on_left", _L("(Requires restart)"));
|
||||
g_sizer->Add(item_window_button_pos);
|
||||
@@ -1534,6 +1537,45 @@ void PreferencesDialog::create_items()
|
||||
});
|
||||
g_sizer->Add(item_network_test);
|
||||
|
||||
//// ONLINE > Cloud Providers
|
||||
g_sizer->Add(create_item_title(_L("Cloud Providers")), 1, wxEXPAND);
|
||||
|
||||
{
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->AddSpacer(FromDIP(DESIGN_LEFT_MARGIN));
|
||||
|
||||
auto text = new wxStaticText(m_parent, wxID_ANY, _L("Enable Bambu Cloud"),
|
||||
wxDefaultPosition, DESIGN_TITLE_SIZE, wxST_NO_AUTORESIZE);
|
||||
text->SetForegroundColour(DESIGN_GRAY900_COLOR);
|
||||
text->SetFont(::Label::Body_14);
|
||||
text->SetToolTip(_L("Allow logging into Bambu Cloud alongside Orca Cloud. When enabled, a Bambu login section appears on the homepage."));
|
||||
text->Wrap(DESIGN_TITLE_SIZE.x);
|
||||
|
||||
auto cb = new ::CheckBox(m_parent);
|
||||
cb->SetValue(app_config->has_cloud_provider(BBL_CLOUD_PROVIDER));
|
||||
cb->SetToolTip(text->GetToolTipText());
|
||||
|
||||
cb->Bind(wxEVT_TOGGLEBUTTON, [this, cb](wxCommandEvent &e) {
|
||||
e.Skip(); // let CheckBox::update() refresh the bitmap
|
||||
if (cb->GetValue()) {
|
||||
app_config->add_cloud_provider(BBL_CLOUD_PROVIDER);
|
||||
} else {
|
||||
app_config->remove_cloud_provider(BBL_CLOUD_PROVIDER);
|
||||
}
|
||||
app_config->save();
|
||||
|
||||
// Update homepage visibility immediately
|
||||
auto *mainframe = wxGetApp().mainframe;
|
||||
if (mainframe && mainframe->m_webview)
|
||||
mainframe->m_webview->SendCloudProvidersInfo();
|
||||
});
|
||||
|
||||
sizer->Add(text, 0, wxALIGN_CENTER | wxTOP | wxBOTTOM, FromDIP(3));
|
||||
sizer->Add(cb, 0, wxALIGN_CENTER | wxRIGHT | wxLEFT, FromDIP(5));
|
||||
|
||||
g_sizer->Add(sizer);
|
||||
}
|
||||
|
||||
//// ONLINE > Update & sync
|
||||
g_sizer->Add(create_item_title(_L("Update & sync")), 1, wxEXPAND);
|
||||
|
||||
|
||||
@@ -0,0 +1,472 @@
|
||||
#include "PresetBundleDialog.hpp"
|
||||
#include "ExportPresetBundleDialog.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include <libslic3r/Config.hpp>
|
||||
#include <libslic3r/Thread.hpp>
|
||||
#include <wx/app.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/scrolwin.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <libslic3r/PresetBundle.hpp>
|
||||
#include <wx/string.h>
|
||||
#include "MainFrame.hpp"
|
||||
#include <slic3r/GUI/Widgets/WebView.hpp>
|
||||
#include <miniz.h>
|
||||
#include <OrcaCloudServiceAgent.hpp>
|
||||
#include <wx/event.h>
|
||||
#include <wx/utils.h>
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
PresetBundleDialog::PresetBundleDialog(
|
||||
wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style)
|
||||
: DPIDialog(parent, id, _L("PresetBundle"), pos, size, style)
|
||||
{
|
||||
wxGetApp().preset_bundle->bundles.PauseRead(); // for the entirety of the preset bundle dialog, we want the update thread to yield.
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
SetMinSize(DESIGN_WINDOW_SIZE);
|
||||
create();
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
|
||||
m_watcher = new wxFileSystemWatcher();
|
||||
m_watcher->SetOwner(this);
|
||||
|
||||
Bind(wxEVT_FSWATCHER, &PresetBundleDialog::OnFSWatch, this);
|
||||
Bind(EVT_UPDATE_BUNDLE_COMPLETE, &PresetBundleDialog::OnBundleUpdate, this);
|
||||
|
||||
m_watcher->Add(wxFileName(wxGetApp().preset_bundle->dir_user_presets_local.c_str())); // _local
|
||||
m_watcher->Add(wxFileName(wxGetApp().preset_bundle->dir_user_presets_subscribed.c_str())); // _subscribed
|
||||
|
||||
RefreshBundleMap();
|
||||
|
||||
StartDialogWorker(); // start worker thread;
|
||||
}
|
||||
|
||||
PresetBundleDialog::~PresetBundleDialog()
|
||||
{
|
||||
StopDialogWorker();
|
||||
wxGetApp().preset_bundle->bundles.UnpauseRead(); // yield for update thread
|
||||
if (m_watcher) {
|
||||
m_watcher->RemoveAll();
|
||||
delete m_watcher;
|
||||
}
|
||||
}
|
||||
|
||||
void PresetBundleDialog::OnFSWatch(wxFileSystemWatcherEvent& e)
|
||||
{
|
||||
GUI::wxGetApp().preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem);
|
||||
wxGetApp().mainframe->update_side_preset_ui();
|
||||
|
||||
// ListBundles();
|
||||
m_update_bundles.store(true);
|
||||
e.Skip();
|
||||
}
|
||||
|
||||
void PresetBundleDialog::StartDialogWorker()
|
||||
{
|
||||
if (m_dialog_worker_token) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_dialog_worker_token = std::make_shared<int>(0);
|
||||
|
||||
m_dialog_worker_thread = Slic3r::create_thread([this, token = std::weak_ptr<int>(m_dialog_worker_token)] {
|
||||
while (!token.expired()) {
|
||||
// after comparing version with the cloud, if there is an update, we will update the rows to reflect
|
||||
|
||||
if (m_check_update_pending.exchange(false, std::memory_order_relaxed)) {
|
||||
if (CheckUpdateCloud()) {
|
||||
ListBundles();
|
||||
}
|
||||
}
|
||||
if (m_update_bundles.exchange(false, std::memory_order_relaxed)) {
|
||||
RefreshBundleMap();
|
||||
ListBundles();
|
||||
}
|
||||
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(500));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// true if b>a
|
||||
bool PresetBundleDialog::CompareVer(const std::string& a, const std::string& b)
|
||||
{
|
||||
// Compare versions using Semver
|
||||
auto local_version = Semver::parse(a);
|
||||
auto remote_version = Semver::parse(b);
|
||||
|
||||
if (!local_version || !remote_version) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (local_version < remote_version) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PresetBundleDialog::CheckUpdateCloud()
|
||||
{
|
||||
bool has_update = false;
|
||||
if (!wxGetApp().getAgent() || !wxGetApp().getAgent()->is_user_login())
|
||||
return false;
|
||||
auto orca_agent = std::dynamic_pointer_cast<OrcaCloudServiceAgent>(wxGetApp().getAgent()->get_cloud_agent());
|
||||
if (!orca_agent)
|
||||
return false;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Preset Bundle Dialog: checking for bundle updates";
|
||||
|
||||
// Fetch all subscribed bundles from cloud
|
||||
std::vector<std::pair<std::string, std::string>> subscribed_bundles;
|
||||
std::vector<std::string> notfound;
|
||||
std::vector<std::string> unauthorized;
|
||||
int result = orca_agent->get_subscribed_bundles(&subscribed_bundles, notfound, unauthorized);
|
||||
|
||||
if (result != 0) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Preset Bundle Dialog: failed to fetch subscribed bundles, result=" << result;
|
||||
return false;
|
||||
}
|
||||
|
||||
// if unauthorized or not found it should be a warning icon to the ui shit
|
||||
|
||||
// check bundle copy with the subscribed bundles
|
||||
for (auto& b : subscribed_bundles) {
|
||||
if (bundle_copy.find(b.first) != bundle_copy.end()) {
|
||||
if (CompareVer(bundle_copy[b.first].version, b.second)) {
|
||||
bundle_copy[b.first].update_available = true;
|
||||
bundle_copy[b.first].unauthorized = false;
|
||||
has_update = true;
|
||||
|
||||
} else {
|
||||
// we count it as an update to the UI if we need to update the unauthorized state
|
||||
if (bundle_copy[b.first].unauthorized) {
|
||||
bundle_copy[b.first].unauthorized = false;
|
||||
has_update = true;
|
||||
}
|
||||
bundle_copy[b.first].update_available = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& a : unauthorized) {
|
||||
if (bundle_copy.find(a) != bundle_copy.end()) {
|
||||
bundle_copy[a].unauthorized = true;
|
||||
has_update = true;
|
||||
}
|
||||
}
|
||||
return has_update;
|
||||
}
|
||||
|
||||
void PresetBundleDialog::StopDialogWorker()
|
||||
{
|
||||
if (!m_dialog_worker_token) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_dialog_worker_token.reset();
|
||||
|
||||
if (m_dialog_worker_thread.joinable()) {
|
||||
m_dialog_worker_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void PresetBundleDialog::OnBundleUpdate(wxCommandEvent& evt)
|
||||
{
|
||||
// const std::string bundle_id = evt.GetString().ToStdString();
|
||||
m_update_bundles.store(true);
|
||||
}
|
||||
|
||||
void PresetBundleDialog::RefreshBundleMap()
|
||||
{
|
||||
wxGetApp().preset_bundle->bundles.ReadLock();
|
||||
bundle_copy = wxGetApp().preset_bundle->bundles.m_bundles;
|
||||
wxGetApp().preset_bundle->bundles.ReadUnlock();
|
||||
}
|
||||
|
||||
void PresetBundleDialog::load_url(wxString& url)
|
||||
{
|
||||
if (!m_browser)
|
||||
return;
|
||||
BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << " enter, url=" << url.ToStdString();
|
||||
WebView::LoadUrl(m_browser, url);
|
||||
m_browser->SetFocus();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " exit";
|
||||
}
|
||||
|
||||
void PresetBundleDialog::create()
|
||||
{
|
||||
app_config = get_app_config();
|
||||
|
||||
wxString TargetUrl = from_u8(
|
||||
(boost::filesystem::path(resources_dir()) / "web/dialog/PresetBundleDialog/index.html").make_preferred().string());
|
||||
wxString strlang = wxGetApp().current_language_code_safe();
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", strlang=%1%") % into_u8(strlang);
|
||||
if (strlang != "")
|
||||
TargetUrl = wxString::Format("%s?lang=%s", std::string(TargetUrl.mb_str()), strlang);
|
||||
|
||||
TargetUrl = "file://" + TargetUrl;
|
||||
|
||||
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
|
||||
SetTitle(_L("Preset Bundle"));
|
||||
|
||||
m_browser = WebView::CreateWebView(this, TargetUrl);
|
||||
if (m_browser == nullptr) {
|
||||
wxLogError("Could not init m_browser");
|
||||
return;
|
||||
}
|
||||
|
||||
SetSizer(topsizer);
|
||||
topsizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
||||
|
||||
// Set a more sensible size for web browsing
|
||||
wxSize pSize = FromDIP(wxSize(820, 660));
|
||||
SetSize(pSize);
|
||||
|
||||
int screenheight = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y, NULL);
|
||||
int screenwidth = wxSystemSettings::GetMetric(wxSYS_SCREEN_X, NULL);
|
||||
int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0;
|
||||
wxPoint tmpPT((screenwidth - pSize.x) / 2, MaxY);
|
||||
Move(tmpPT);
|
||||
|
||||
Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &PresetBundleDialog::OnScriptMessage, this, m_browser->GetId());
|
||||
|
||||
load_url(TargetUrl);
|
||||
}
|
||||
|
||||
bool PresetBundleDialog::DeleteBundleById(const wxString& id)
|
||||
{
|
||||
auto* b = wxGetApp().preset_bundle;
|
||||
if (id.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string bundle_id = id.ToStdString();
|
||||
|
||||
wxGetApp().preset_bundle->bundles.ReadLock();
|
||||
auto it = b->bundles.m_bundles.find(bundle_id);
|
||||
if (it == b->bundles.m_bundles.end()) {
|
||||
wxGetApp().preset_bundle->bundles.ReadUnlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string metadata_path = it->second.path;
|
||||
const boost::filesystem::path bundle_dir = boost::filesystem::path(metadata_path).parent_path();
|
||||
|
||||
const BundleType bundle_type = it->second.bundle_type;
|
||||
wxGetApp().preset_bundle->bundles.ReadUnlock();
|
||||
|
||||
if (bundle_type == BundleType::Subscribed) {
|
||||
// do unsubscribe before deleting locally
|
||||
}
|
||||
|
||||
wxGetApp().preset_bundle->bundles.WriteLock();
|
||||
b->bundles.m_bundles.erase(it);
|
||||
wxGetApp().preset_bundle->bundles.WriteUnlock();
|
||||
|
||||
auto remove_from_collection = [&](PresetCollection& c) {
|
||||
std::vector<std::string> to_delete;
|
||||
for (const auto& p : c.get_presets()) {
|
||||
if (p.bundle_id == bundle_id)
|
||||
to_delete.push_back(p.name);
|
||||
}
|
||||
for (const auto& name : to_delete)
|
||||
c.delete_preset(name);
|
||||
};
|
||||
|
||||
remove_from_collection(b->prints);
|
||||
remove_from_collection(b->filaments);
|
||||
remove_from_collection(b->printers);
|
||||
|
||||
boost::system::error_code ec;
|
||||
if (!bundle_dir.empty() && boost::filesystem::exists(bundle_dir))
|
||||
boost::filesystem::remove_all(bundle_dir, ec);
|
||||
|
||||
wxGetApp().preset_bundle->update_compatible(PresetSelectCompatibleType::Always);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PresetBundleDialog::UnsubscribeBundleById(const std::string& id) { return wxGetApp().unsubscribe_bundle(id); }
|
||||
|
||||
void PresetBundleDialog::on_dpi_changed(const wxRect& suggested_rect) { this->Refresh(); }
|
||||
|
||||
void PresetBundleDialog::RunScript(const wxString& s)
|
||||
{
|
||||
if (!m_browser)
|
||||
return;
|
||||
|
||||
WebView::RunScript(m_browser, s);
|
||||
}
|
||||
|
||||
void PresetBundleDialog::OnScriptMessage(wxWebViewEvent& e)
|
||||
{
|
||||
try {
|
||||
wxString strInput = e.GetString();
|
||||
BOOST_LOG_TRIVIAL(trace) << "PresetBundleDialog::OnScriptMessage;OnRecv:" << strInput.c_str();
|
||||
json j = json::parse(strInput.utf8_string());
|
||||
|
||||
wxString strCmd = j["command"];
|
||||
BOOST_LOG_TRIVIAL(trace) << "PresetBundleDialog::OnScriptMessage;Command:" << strCmd;
|
||||
|
||||
if (strCmd == "request_bundles") {
|
||||
ListBundles();
|
||||
} else if (strCmd == "refresh_bundles") {
|
||||
// use the thread to check for updates.
|
||||
m_check_update_pending.store(true, std::memory_order_relaxed);
|
||||
} else if (strCmd == "update_bundle") {
|
||||
std::string id = j["bundle_id"];
|
||||
|
||||
auto* evt = new wxCommandEvent(EVT_UPDATE_PRESET_BUNDLE);
|
||||
evt->SetString(wxString::FromUTF8(id));
|
||||
wxQueueEvent(&wxGetApp(), evt); // dialog -> GUI_App
|
||||
} else if (strCmd == "set_auto_update") {
|
||||
bool enabled = j.value("enabled", false);
|
||||
|
||||
// Example persistence location. Adjust key name if you already have one.
|
||||
app_config->set_bool("preset_bundle_auto_update", enabled ? true : false);
|
||||
app_config->save();
|
||||
} else if (strCmd == "close_page") {
|
||||
this->EndModal(wxID_CANCEL);
|
||||
} else if (strCmd == "export_page") {
|
||||
wxGetApp().CallAfter([this]() {
|
||||
ExportPresetBundleDialog dlg(this);
|
||||
dlg.ShowModal();
|
||||
});
|
||||
} else if (strCmd == "top_row_menu_action") {
|
||||
if (j["action"] == "open_folder") {
|
||||
std::string id = j["bundle_id"];
|
||||
OpenFolder(id);
|
||||
} else if (j["action"] == "delete_bundle") {
|
||||
std::string id = j["bundle_id"];
|
||||
DeleteBundle(id);
|
||||
} else if (j["action"] == "unsubscribe_bundle") {
|
||||
std::string id = j["bundle_id"];
|
||||
UnsubscribeBundle(id);
|
||||
}
|
||||
} else if (strCmd == "open_bundle_on_cloud") {
|
||||
std::string bundle_id = j["bundle_id"];
|
||||
OpenBundleOnCloud(bundle_id);
|
||||
}
|
||||
|
||||
} catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "PresetBundleDialog::OnScriptMessage;Error:" << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
// call on dialog create to populate the js local store
|
||||
void PresetBundleDialog::ListBundles()
|
||||
{
|
||||
json res;
|
||||
res["command"] = "list_bundles";
|
||||
res["sequence_id"] = "2000";
|
||||
res["data"] = json::array();
|
||||
|
||||
const auto& all_bundles = bundle_copy;
|
||||
|
||||
auto strip_prefix = [](const std::vector<std::string>& names) {
|
||||
json arr = json::array();
|
||||
for (const auto& name : names)
|
||||
arr.push_back(boost::filesystem::path(name).filename().string());
|
||||
return arr;
|
||||
};
|
||||
|
||||
for (const auto& bundle : all_bundles) {
|
||||
const auto& metadata = bundle.second;
|
||||
json temp;
|
||||
temp["id"] = metadata.id;
|
||||
temp["name"] = metadata.name;
|
||||
temp["type"] = metadata.bundle_type == Subscribed ? "Subscribed" : metadata.bundle_type == Local ? "Local" : "Default";
|
||||
temp["author"] = metadata.author;
|
||||
temp["version"] = metadata.version;
|
||||
temp["description"] = metadata.description;
|
||||
temp["path"] = metadata.path;
|
||||
|
||||
temp["printers"] = strip_prefix(metadata.printer_presets);
|
||||
temp["filaments"] = strip_prefix(metadata.filament_presets);
|
||||
temp["presets"] = strip_prefix(metadata.print_presets);
|
||||
|
||||
temp["update_available"] = metadata.update_available;
|
||||
temp["unauthorized"] = metadata.unauthorized;
|
||||
res["auto_update_enabled"] = app_config->get_bool("preset_bundle_auto_update");
|
||||
|
||||
res["data"].push_back(std::move(temp));
|
||||
}
|
||||
|
||||
wxString strJS = wxString::Format("HandleStudio(%s)", wxString::FromUTF8(res.dump(-1, ' ', false, json::error_handler_t::ignore)));
|
||||
wxGetApp().CallAfter([this, strJS] { RunScript(strJS); });
|
||||
}
|
||||
|
||||
void PresetBundleDialog::OpenFolder(const std::string& id)
|
||||
{
|
||||
wxGetApp().preset_bundle->bundles.ReadLock();
|
||||
wxString target = _L(wxGetApp().preset_bundle->bundles.m_bundles.find(id)->second.path);
|
||||
wxGetApp().preset_bundle->bundles.ReadUnlock();
|
||||
wxFileName fn(target);
|
||||
if (fn.FileExists())
|
||||
target = fn.GetPath();
|
||||
|
||||
if (target.empty() || !wxFileName::DirExists(target)) {
|
||||
wxMessageBox(_L("Bundle folder does not exist."), _L("Open Folder"), wxOK | wxICON_WARNING, this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!wxLaunchDefaultApplication(target)) {
|
||||
wxMessageBox(_L("Failed to open folder."), _L("Open Folder"), wxOK | wxICON_ERROR, this);
|
||||
}
|
||||
}
|
||||
|
||||
void PresetBundleDialog::DeleteBundle(const std::string& id)
|
||||
{
|
||||
if (id.empty())
|
||||
return;
|
||||
|
||||
const int rc = wxMessageBox(_L("Delete selected bundle from folder and all presets loaded from it?"), _L("Delete Bundle"),
|
||||
wxYES_NO | wxNO_DEFAULT | wxICON_WARNING, this);
|
||||
|
||||
if (rc != wxYES)
|
||||
return;
|
||||
|
||||
if (!DeleteBundleById(id)) {
|
||||
wxMessageBox(_L("Failed to remove bundle."), _L("Remove Bundle"), wxOK | wxICON_ERROR, this);
|
||||
return;
|
||||
}
|
||||
wxGetApp().mainframe->update_side_preset_ui();
|
||||
}
|
||||
|
||||
void PresetBundleDialog::UnsubscribeBundle(const std::string& id)
|
||||
{
|
||||
if (id.empty())
|
||||
return;
|
||||
|
||||
const int rc = wxMessageBox(_L("Unsubscribe bundle?"), _L("UnsubscribeBundle"), wxYES_NO | wxNO_DEFAULT | wxICON_WARNING, this);
|
||||
|
||||
if (rc != wxYES)
|
||||
return;
|
||||
|
||||
if (!UnsubscribeBundleById(id)) {
|
||||
wxMessageBox(_L("Failed to unsubscribe bundle."), _L("Unsubscribe Bundle"), wxOK | wxICON_ERROR, this);
|
||||
return;
|
||||
}
|
||||
|
||||
wxGetApp().mainframe->update_side_preset_ui();
|
||||
}
|
||||
|
||||
void PresetBundleDialog::OpenBundleOnCloud(const std::string& id)
|
||||
{
|
||||
if (id.empty())
|
||||
return;
|
||||
|
||||
if (!wxGetApp().getAgent())
|
||||
return;
|
||||
|
||||
auto orca_agent = std::dynamic_pointer_cast<OrcaCloudServiceAgent>(wxGetApp().getAgent()->get_cloud_agent());
|
||||
if (!orca_agent)
|
||||
return;
|
||||
|
||||
wxLaunchDefaultBrowser(wxString::FromUTF8(orca_agent->get_bundle_url(id)));
|
||||
}
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,102 @@
|
||||
#ifndef slic3r_PresetBundleDialog_hpp_
|
||||
#define slic3r_PresetBundleDialog_hpp_
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include <boost/thread/detail/thread.hpp>
|
||||
#include <libslic3r/PresetBundle.hpp>
|
||||
#include <memory>
|
||||
#include <slic3r/GUI/GUI.hpp>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/language.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/fswatcher.h>
|
||||
#include <wx/webview.h>
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
#define DESIGN_GRAY900_COLOR wxColour("#363636") // Label color
|
||||
#define DESIGN_GRAY600_COLOR wxColour("#ACACAC") // Dimmed text color
|
||||
|
||||
#define DESIGN_WINDOW_SIZE wxSize(FromDIP(640), FromDIP(640))
|
||||
#define DESIGN_TITLE_SIZE wxSize(FromDIP(280), -1)
|
||||
#define DESIGN_COMBOBOX_SIZE wxSize(FromDIP(120), -1)
|
||||
#define DESIGN_LARGE_COMBOBOX_SIZE wxSize(FromDIP(120), -1)
|
||||
#define DESIGN_INPUT_SIZE wxSize(FromDIP(120), -1)
|
||||
#define DESIGN_LEFT_MARGIN 25
|
||||
#define VERTICAL_GAP_SIZE FromDIP(4)
|
||||
class PresetBundleDialog : public Slic3r::GUI::DPIDialog
|
||||
{
|
||||
public:
|
||||
PresetBundleDialog(wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxString& title = wxT(""),
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxMAXIMIZE_BOX);
|
||||
|
||||
~PresetBundleDialog();
|
||||
|
||||
void create();
|
||||
|
||||
bool DeleteBundleById(const wxString& id);
|
||||
bool UnsubscribeBundleById(const std::string& id);
|
||||
|
||||
bool seq_top_layer_only_changed() const { return m_seq_top_layer_only_changed; }
|
||||
bool recreate_GUI() const { return m_recreate_GUI; }
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
|
||||
// webview utilities
|
||||
void load_url(wxString& url);
|
||||
void ListBundles();
|
||||
void OpenFolder(const std::string& id);
|
||||
void DeleteBundle(const std::string& id);
|
||||
void UnsubscribeBundle(const std::string& id);
|
||||
void OpenBundleOnCloud(const std::string& id);
|
||||
|
||||
void OnPresetBundlePage();
|
||||
|
||||
// sends command to webview
|
||||
void RunScript(const wxString& s);
|
||||
|
||||
// webview events
|
||||
void OnScriptMessage(wxWebViewEvent& e);
|
||||
|
||||
void StartDialogWorker();
|
||||
void StopDialogWorker();
|
||||
|
||||
void RefreshBundleMap();
|
||||
|
||||
bool CheckUpdateCloud();
|
||||
|
||||
void OnBundleUpdate(wxCommandEvent& evt);
|
||||
|
||||
// true if b>a
|
||||
bool CompareVer(const std::string& a, const std::string& b);
|
||||
|
||||
protected:
|
||||
wxFileSystemWatcher* m_watcher = nullptr;
|
||||
|
||||
void OnFSWatch(wxFileSystemWatcherEvent& e);
|
||||
|
||||
bool m_seq_top_layer_only_changed{false};
|
||||
bool m_recreate_GUI{false};
|
||||
|
||||
// Webview
|
||||
wxWebView* m_browser{nullptr};
|
||||
std::unordered_map<std::string, BundleMetadata> bundle_copy;
|
||||
|
||||
boost::thread m_dialog_worker_thread;
|
||||
std::shared_ptr<int> m_dialog_worker_token;
|
||||
std::atomic<bool> m_check_update_pending{false};
|
||||
std::atomic<bool> m_update_bundles{false};
|
||||
|
||||
private:
|
||||
Slic3r::AppConfig* app_config;
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
#endif
|
||||
@@ -1133,16 +1133,21 @@ void PlaterPresetComboBox::update()
|
||||
std::map<wxString, wxBitmap*> nonsys_presets;
|
||||
//BBS: add project embedded presets logic
|
||||
std::map<wxString, wxBitmap*> project_embedded_presets;
|
||||
// ORCA: add bundle presets
|
||||
std::map<wxString, wxBitmap*> bundle_presets;
|
||||
std::map<wxString, wxBitmap *> system_presets;
|
||||
std::map<wxString, wxBitmap *> uncompatible_presets;
|
||||
std::unordered_set<std::string> system_printer_models;
|
||||
std::map<wxString, wxString> preset_descriptions;
|
||||
std::map<wxString, std::string> preset_filament_vendors;
|
||||
std::map<wxString, std::string> preset_filament_types;
|
||||
std::map<wxString, std::string> preset_filament_names; // ORCA
|
||||
std::map<wxString, std::string> preset_aliases; // ORCA
|
||||
std::map<wxString, std::string> preset_bundle_ids;
|
||||
std::map<wxString, std::string> preset_bundle_names;
|
||||
//BBS: move system to the end
|
||||
wxString selected_system_preset;
|
||||
wxString selected_user_preset;
|
||||
wxString selected_bundle_preset;
|
||||
wxString tooltip;
|
||||
const std::deque<Preset>& presets = m_collection->get_presets();
|
||||
|
||||
@@ -1170,7 +1175,21 @@ void PlaterPresetComboBox::update()
|
||||
}
|
||||
|
||||
bool single_bar = false;
|
||||
wxString name = get_preset_name(preset);
|
||||
wxString name = preset.name;
|
||||
preset_aliases[name] = get_preset_name(preset).ToStdString(); // ORCA
|
||||
|
||||
// Track bundle names for bundled presets
|
||||
if (preset.is_from_bundle()) {
|
||||
m_preset_bundle->bundles.ReadLock();
|
||||
auto bundle_it = m_preset_bundle->bundles.m_bundles.find(preset.bundle_id);
|
||||
if (bundle_it != m_preset_bundle->bundles.m_bundles.end()) {
|
||||
preset_bundle_ids[name] = bundle_it->second.id;
|
||||
preset_bundle_names[name] = bundle_it->second.name;
|
||||
}
|
||||
m_preset_bundle->bundles.ReadUnlock();
|
||||
|
||||
}
|
||||
|
||||
if (m_type == Preset::TYPE_FILAMENT)
|
||||
{
|
||||
#if 0
|
||||
@@ -1192,7 +1211,6 @@ void PlaterPresetComboBox::update()
|
||||
if (preset_filament_vendors[name] == "Bambu Lab")
|
||||
preset_filament_vendors[name] = "Bambu";
|
||||
preset_filament_types[name] = preset.config.option<ConfigOptionStrings>("filament_type")->values.at(0);
|
||||
preset_filament_names[name] = name.ToStdString(); // ORCA
|
||||
//}
|
||||
}
|
||||
wxBitmap* bmp = get_bmp(preset);
|
||||
@@ -1214,6 +1232,7 @@ void PlaterPresetComboBox::update()
|
||||
name = from_u8(is_selected && preset.is_dirty ? Preset::suffix_modified() + printer_model : printer_model);
|
||||
|
||||
if (system_printer_models.count(printer_model) == 0) {
|
||||
preset_aliases[name] = name.ToStdString(); // ORCA
|
||||
system_presets.emplace(name, bmp);
|
||||
system_printer_models.insert(printer_model);
|
||||
}
|
||||
@@ -1246,6 +1265,15 @@ void PlaterPresetComboBox::update()
|
||||
tooltip = wxString::FromUTF8(preset.name.c_str());
|
||||
}
|
||||
}
|
||||
// ORCA: add bundle presets
|
||||
else if (preset.is_from_bundle())
|
||||
{
|
||||
bundle_presets.emplace(name, bmp);
|
||||
if (is_selected) {
|
||||
selected_bundle_preset = name;
|
||||
tooltip = get_tooltip(preset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nonsys_presets.emplace(name, bmp);
|
||||
@@ -1271,11 +1299,11 @@ void PlaterPresetComboBox::update()
|
||||
"Bambu PLA Galaxy", "Bambu PLA Metal", "Bambu PLA Marble", "Bambu PETG-CF", "Bambu PETG Translucent", "Bambu ABS-GF"};
|
||||
std::vector<std::string> first_vendors = {"", "Bambu", "Generic"}; // Empty vendor for non-system presets
|
||||
std::vector<std::string> first_types = {"PLA", "PETG", "ABS", "TPU"};
|
||||
auto add_presets = [this, &preset_descriptions, &filament_orders, &preset_filament_vendors, &first_vendors, &preset_filament_types, &preset_filament_names, &first_types, &selected_in_ams]
|
||||
auto add_presets = [this, &preset_descriptions, &filament_orders, &preset_filament_vendors, &first_vendors, &preset_filament_types, &preset_aliases, &preset_bundle_ids, &preset_bundle_names, &first_types, &selected_in_ams]
|
||||
(std::map<wxString, wxBitmap *> const &presets, wxString const &selected, std::string const &group, wxString const &groupName) {
|
||||
if (!presets.empty()) {
|
||||
set_label_marker(Append(_L(group), wxNullBitmap, DD_ITEM_STYLE_SPLIT_ITEM));
|
||||
if (m_type == Preset::TYPE_FILAMENT) {
|
||||
if (m_type == Preset::TYPE_FILAMENT || m_type == Preset::TYPE_PRINTER) {
|
||||
std::vector<std::map<wxString, wxBitmap *>::value_type const*> list(presets.size(), nullptr);
|
||||
std::transform(presets.begin(), presets.end(), list.begin(), [](auto & pair) { return &pair; });
|
||||
bool groupByGroup = group != "System presets";
|
||||
@@ -1283,7 +1311,7 @@ void PlaterPresetComboBox::update()
|
||||
// if (GetCount() == 1) Clear();
|
||||
// else SetString(GetCount() - 1, "");
|
||||
//}
|
||||
if (group == "System presets" || group == "Unsupported presets")
|
||||
if (m_type == Preset::TYPE_FILAMENT && (group == "System presets" || group == "Unsupported presets"))
|
||||
std::sort(list.begin(), list.end(), [&filament_orders, &preset_filament_vendors, &first_vendors, &preset_filament_types, &first_types](auto *l, auto *r) {
|
||||
{ // Compare order
|
||||
auto iter1 = std::find(filament_orders.begin(), filament_orders.end(), l->first);
|
||||
@@ -1306,10 +1334,11 @@ void PlaterPresetComboBox::update()
|
||||
return l->first < r->first;
|
||||
});
|
||||
// ORCA add sorting support for vendor / type for user presets. also non grouped items
|
||||
if (groupName == "by_vendor" || groupName == "by_type" || groupName == ""){
|
||||
auto by = groupName == "by_vendor" ? preset_filament_vendors
|
||||
if (groupName == "by_bundle" || groupName == "by_vendor" || groupName == "by_type" || groupName == ""){
|
||||
auto by = groupName == "by_bundle" ? preset_bundle_names
|
||||
: groupName == "by_vendor" ? preset_filament_vendors
|
||||
: groupName == "by_type" ? preset_filament_types
|
||||
: preset_filament_names;
|
||||
: preset_aliases;
|
||||
std::sort(list.begin(), list.end(), [&by](auto *l, auto *r) {
|
||||
auto get_key = [&](auto* item) -> std::pair<bool, std::string> {
|
||||
std::string str = by.count(item->first) ? by.at(item->first) : "";
|
||||
@@ -1319,20 +1348,29 @@ void PlaterPresetComboBox::update()
|
||||
auto [l_valid, l_lower] = get_key(l);
|
||||
auto [r_valid, r_lower] = get_key(r);
|
||||
return (l_valid != r_valid) ? l_valid > r_valid
|
||||
: (l_lower != r_lower) ? l_lower < r_lower
|
||||
: (l_lower != r_lower) ? l_lower < r_lower
|
||||
: l->first < r->first;
|
||||
});
|
||||
}
|
||||
bool unsupported = group == "Unsupported presets";
|
||||
for (auto it : list) {
|
||||
// ORCA add sorting support for vendor / type for user presets
|
||||
auto groupName2 = groupName == "by_type" ? (preset_filament_types[it->first].empty() ? _L("Unspecified") : preset_filament_types[it->first])
|
||||
: groupName == "by_vendor" ? (preset_filament_vendors[it->first].empty() ? _L("Unspecified") : preset_filament_vendors[it->first])
|
||||
: groupByGroup ? groupName
|
||||
auto groupName2 = groupName == "by_bundle" ? (preset_bundle_names[it->first].empty() ? _L("Unspecified") : preset_bundle_names[it->first])
|
||||
: groupName == "by_type" ? (preset_filament_types[it->first].empty() ? _L("Unspecified") : preset_filament_types[it->first])
|
||||
: groupName == "by_vendor" ? (preset_filament_vendors[it->first].empty() ? _L("Unspecified") : preset_filament_vendors[it->first])
|
||||
: groupByGroup ? groupName
|
||||
: preset_filament_vendors[it->first];
|
||||
int index = Append(it->first, *it->second, groupName2, nullptr, unsupported ? DD_ITEM_STYLE_DISABLED : 0);
|
||||
int index = groupName == "by_bundle"
|
||||
? Append(preset_aliases[it->first], *it->second,
|
||||
from_u8(preset_bundle_ids[it->first]), groupName2, nullptr,
|
||||
unsupported ? DD_ITEM_STYLE_DISABLED : 0)
|
||||
: Append(preset_aliases[it->first], *it->second, groupName2, nullptr,
|
||||
unsupported ? DD_ITEM_STYLE_DISABLED : 0);
|
||||
SetItemAlias(index, it->first);
|
||||
if (unsupported)
|
||||
set_label_marker(index, LABEL_ITEM_DISABLED);
|
||||
else if (m_type == Preset::TYPE_PRINTER && group == "System presets" )
|
||||
set_label_marker(index, LABEL_ITEM_PRINTER_MODELS);
|
||||
SetItemTooltip(index, preset_descriptions[it->first]);
|
||||
bool is_selected = it->first == selected;
|
||||
validate_selection(is_selected);
|
||||
@@ -1342,7 +1380,9 @@ void PlaterPresetComboBox::update()
|
||||
}
|
||||
} else {
|
||||
for (std::map<wxString, wxBitmap *>::const_iterator it = presets.begin(); it != presets.end(); ++it) {
|
||||
SetItemTooltip(Append(it->first, *it->second), preset_descriptions[it->first]);
|
||||
int index = Append(preset_aliases[it->first], *it->second);
|
||||
SetItemAlias(index, it->first);
|
||||
SetItemTooltip(index, preset_descriptions[it->first]);
|
||||
if (group == "System presets")
|
||||
set_label_marker(GetCount() - 1, LABEL_ITEM_PRINTER_MODELS);
|
||||
validate_selection(it->first == selected);
|
||||
@@ -1360,6 +1400,9 @@ void PlaterPresetComboBox::update()
|
||||
: group_filament_presets == "3" ? "by_vendor" // Create sub menus with filament vendor
|
||||
: ""; // Use without sub menu
|
||||
add_presets(nonsys_presets, selected_user_preset, L("User presets"), group_filament_presets_by);
|
||||
// ORCA: add bundle presets with sub-dropdown grouping for filament and printer
|
||||
auto bundle_group_name = (m_type == Preset::TYPE_FILAMENT || m_type == Preset::TYPE_PRINTER) ? "by_bundle" : "";
|
||||
add_presets(bundle_presets, selected_bundle_preset, L("Bundle presets"), bundle_group_name);
|
||||
// BBS: move system to the end
|
||||
add_presets(system_presets, selected_system_preset, L("System presets"), _L("System"));
|
||||
add_presets(uncompatible_presets, {}, L("Unsupported presets"), _L("Unsupported") + " ");
|
||||
@@ -1579,7 +1622,10 @@ void TabPresetComboBox::OnSelect(wxCommandEvent &evt)
|
||||
|
||||
wxString TabPresetComboBox::get_preset_name(const Preset& preset)
|
||||
{
|
||||
return from_u8(preset.label(true));
|
||||
if (preset.is_from_bundle())
|
||||
return from_u8(preset.label(false));
|
||||
else
|
||||
return from_u8(preset.label(true));
|
||||
}
|
||||
|
||||
// Update the choice UI from the list of presets.
|
||||
@@ -1598,7 +1644,12 @@ void TabPresetComboBox::update()
|
||||
std::map<wxString, std::pair<wxBitmap*, bool>> project_embedded_presets;
|
||||
//BBS: move system to the end
|
||||
std::map<wxString, std::pair<wxBitmap*, bool>> system_presets;
|
||||
// ORCA: add bundle presets
|
||||
std::map<wxString, std::pair<wxBitmap*, bool>> bundle_presets;
|
||||
std::map<wxString, wxString> preset_descriptions;
|
||||
std::map<wxString, std::string> preset_aliases; // ORCA
|
||||
std::map<wxString, std::string> preset_bundle_ids;
|
||||
std::map<wxString, std::string> preset_bundle_names;
|
||||
|
||||
wxString selected = "";
|
||||
//BBS: move system to the end
|
||||
@@ -1625,10 +1676,23 @@ void TabPresetComboBox::update()
|
||||
wxBitmap* bmp = get_bmp(preset);
|
||||
assert(bmp);
|
||||
|
||||
const wxString name = get_preset_name(preset);
|
||||
const wxString name = preset.name;
|
||||
preset_aliases[name] = get_preset_name(preset).ToStdString();
|
||||
if (preset.is_system)
|
||||
preset_descriptions.emplace(name, from_u8(preset.description));
|
||||
|
||||
// ORCA: Track bundle names for bundled presets
|
||||
if (preset.is_from_bundle()) {
|
||||
m_preset_bundle->bundles.ReadLock();
|
||||
auto bundle_it = m_preset_bundle->bundles.m_bundles.find(preset.bundle_id);
|
||||
if (bundle_it != m_preset_bundle->bundles.m_bundles.end()) {
|
||||
preset_bundle_ids[name] = bundle_it->second.id;
|
||||
preset_bundle_names[name] = bundle_it->second.name;
|
||||
}
|
||||
m_preset_bundle->bundles.ReadUnlock();
|
||||
|
||||
}
|
||||
|
||||
if (preset.is_default || preset.is_system) {
|
||||
//BBS: move system to the end
|
||||
system_presets.emplace(name, std::pair<wxBitmap *, bool>(bmp, is_enabled));
|
||||
@@ -1647,6 +1711,13 @@ void TabPresetComboBox::update()
|
||||
if (i == idx_selected)
|
||||
selected = name;
|
||||
}
|
||||
// ORCA: add bundle presets
|
||||
else if (preset.is_from_bundle())
|
||||
{
|
||||
bundle_presets.emplace(name, std::pair<wxBitmap*, bool>(bmp, is_enabled));
|
||||
if (i == idx_selected)
|
||||
selected = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::pair<wxBitmap*, bool> pair(bmp, is_enabled);
|
||||
@@ -1680,6 +1751,27 @@ void TabPresetComboBox::update()
|
||||
set_label_marker(Append(_L("User presets"), wxNullBitmap, DD_ITEM_STYLE_SPLIT_ITEM));
|
||||
for (std::map<wxString, std::pair<wxBitmap*, bool>>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
|
||||
int item_id = Append(it->first, *it->second.first);
|
||||
SetItemAlias(item_id, it->first);
|
||||
SetItemTooltip(item_id, preset_descriptions[it->first]);
|
||||
bool is_enabled = it->second.second;
|
||||
if (!is_enabled)
|
||||
set_label_marker(item_id, LABEL_ITEM_DISABLED);
|
||||
validate_selection(it->first == selected);
|
||||
}
|
||||
}
|
||||
// ORCA: add bundle presets with sub-dropdown grouping
|
||||
if (!bundle_presets.empty())
|
||||
{
|
||||
set_label_marker(Append(_L("Bundle presets"), wxNullBitmap, DD_ITEM_STYLE_SPLIT_ITEM));
|
||||
for (std::map<wxString, std::pair<wxBitmap*, bool>>::iterator it = bundle_presets.begin(); it != bundle_presets.end(); ++it) {
|
||||
// Get bundle name for grouping
|
||||
wxString bundle_name = _L("Unspecified");
|
||||
if (preset_bundle_names.count(it->first) > 0 && !preset_bundle_names[it->first].empty()) {
|
||||
bundle_name = preset_bundle_names[it->first];
|
||||
}
|
||||
// Use Append with group parameter for sub-dropdown grouping
|
||||
int item_id = Append(preset_aliases[it->first], *it->second.first, from_u8(preset_bundle_ids[it->first]), bundle_name);
|
||||
SetItemAlias(item_id, it->first);
|
||||
SetItemTooltip(item_id, preset_descriptions[it->first]);
|
||||
bool is_enabled = it->second.second;
|
||||
if (!is_enabled)
|
||||
@@ -1693,6 +1785,7 @@ void TabPresetComboBox::update()
|
||||
set_label_marker(Append(_L("System presets"), wxNullBitmap, DD_ITEM_STYLE_SPLIT_ITEM));
|
||||
for (std::map<wxString, std::pair<wxBitmap*, bool>>::iterator it = system_presets.begin(); it != system_presets.end(); ++it) {
|
||||
int item_id = Append(it->first, *it->second.first);
|
||||
SetItemAlias(item_id, it->first);
|
||||
SetItemTooltip(item_id, preset_descriptions[it->first]);
|
||||
bool is_enabled = it->second.second;
|
||||
if (!is_enabled)
|
||||
|
||||
@@ -36,7 +36,7 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string &suffix, wxBox
|
||||
m_presets = tab->get_presets();
|
||||
|
||||
const Preset &sel_preset = m_presets->get_selected_preset();
|
||||
std::string preset_name = sel_preset.is_default ? "Untitled" : sel_preset.is_system ? (boost::format(("%1% - %2%")) % sel_preset.name % suffix).str() : sel_preset.name;
|
||||
std::string preset_name = sel_preset.is_default ? "Untitled" : sel_preset.is_system ? (boost::format(("%1% - %2%")) % sel_preset.name % suffix).str() : sel_preset.is_from_bundle() && !sel_preset.alias.empty() ? sel_preset.alias : sel_preset.name;
|
||||
|
||||
// if name contains extension
|
||||
if (boost::iends_with(preset_name, ".ini")) {
|
||||
@@ -162,7 +162,7 @@ void SavePresetDialog::Item::update()
|
||||
}
|
||||
|
||||
const Preset *existing = m_presets->find_preset(m_preset_name, false);
|
||||
if (m_valid_type == Valid && existing && (existing->is_default || existing->is_system)) {
|
||||
if (m_valid_type == Valid && existing && !existing->can_overwrite()) {
|
||||
info_line = _L("Overwriting a system profile is not allowed.");
|
||||
m_valid_type = NoValid;
|
||||
}
|
||||
@@ -230,13 +230,11 @@ void SavePresetDialog::Item::update_valid_bmp()
|
||||
void SavePresetDialog::Item::accept()
|
||||
{
|
||||
if (m_valid_type == Warning) {
|
||||
// BBS add sync info
|
||||
auto it = m_presets->find_preset(m_preset_name, false);
|
||||
Preset ¤t_preset = *it;
|
||||
current_preset.sync_info = "delete";
|
||||
if (!current_preset.setting_id.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "delete preset = " << current_preset.name << ", setting_id = " << current_preset.setting_id;
|
||||
wxGetApp().delete_preset_from_cloud(current_preset.setting_id);
|
||||
wxGetApp().delete_preset_from_cloud(current_preset.setting_id, current_preset.file);
|
||||
}
|
||||
m_presets->delete_preset(m_preset_name);
|
||||
}
|
||||
|
||||
@@ -2601,12 +2601,13 @@ void SelectMachineDialog::clear_ip_address_config(wxCommandEvent& e)
|
||||
void SelectMachineDialog::update_user_machine_list()
|
||||
{
|
||||
NetworkAgent* m_agent = wxGetApp().getAgent();
|
||||
if (m_agent && m_agent->is_user_login()) {
|
||||
boost::thread get_print_info_thread = Slic3r::create_thread([this, token = std::weak_ptr<int>(m_token)] {
|
||||
const std::string provider = wxGetApp().get_printer_cloud_provider();
|
||||
if (m_agent && m_agent->is_user_login(provider)) {
|
||||
boost::thread get_print_info_thread = Slic3r::create_thread([this, token = std::weak_ptr<int>(m_token), provider] {
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
unsigned int http_code;
|
||||
std::string body;
|
||||
int result = agent->get_user_print_info(&http_code, &body);
|
||||
int result = agent->get_user_print_info(&http_code, &body, provider);
|
||||
CallAfter([token, this, result, body] {
|
||||
if (token.expired()) {return;}
|
||||
if (result == 0) {
|
||||
@@ -3233,7 +3234,7 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_)
|
||||
|
||||
/** error check **/
|
||||
/* check cloud machine connections */
|
||||
if (!obj_->is_lan_mode_printer() && !agent->is_server_connected()) {
|
||||
if (!obj_->is_lan_mode_printer() && !agent->is_server_connected(wxGetApp().get_printer_cloud_provider())) {
|
||||
show_status(PrintDialogStatus::PrintStatusConnectingServer);
|
||||
reset_timeout();
|
||||
return;
|
||||
@@ -3770,7 +3771,7 @@ void SelectMachineDialog::set_default()
|
||||
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (agent) {
|
||||
if (agent->is_user_login()) {
|
||||
if (agent->is_user_login(wxGetApp().get_printer_cloud_provider())) {
|
||||
show_status(PrintDialogStatus::PrintStatusInit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ MachineObjectPanel::~MachineObjectPanel() {}
|
||||
|
||||
void MachineObjectPanel::show_bind_dialog()
|
||||
{
|
||||
if (wxGetApp().is_user_login()) {
|
||||
if (wxGetApp().is_user_login(wxGetApp().get_printer_cloud_provider())) {
|
||||
BindMachineDialog dlg;
|
||||
dlg.update_machine_info(m_info);
|
||||
dlg.ShowModal();
|
||||
@@ -387,13 +387,14 @@ void SelectMachinePopup::Popup(wxWindow *WXUNUSED(focus))
|
||||
m_refresh_timer->Start(MACHINE_LIST_REFRESH_INTERVAL);
|
||||
}
|
||||
|
||||
if (wxGetApp().is_user_login()) {
|
||||
const std::string provider = wxGetApp().get_printer_cloud_provider();
|
||||
if (wxGetApp().is_user_login(provider)) {
|
||||
if (!get_print_info_thread) {
|
||||
get_print_info_thread = new boost::thread(Slic3r::create_thread([this, token = std::weak_ptr<int>(m_token)] {
|
||||
get_print_info_thread = new boost::thread(Slic3r::create_thread([this, token = std::weak_ptr<int>(m_token), provider] {
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
unsigned int http_code;
|
||||
std::string body;
|
||||
int result = agent->get_user_print_info(&http_code, &body);
|
||||
int result = agent->get_user_print_info(&http_code, &body, provider);
|
||||
CallAfter([token, this, result, body]() {
|
||||
if (token.expired()) {return;}
|
||||
if (result == 0) {
|
||||
@@ -511,7 +512,7 @@ void SelectMachinePopup::update_other_devices()
|
||||
/* do not show printer bind state is empty */
|
||||
if (!mobj->is_avaliable()) continue;
|
||||
|
||||
if (!wxGetApp().is_user_login() && !mobj->is_lan_mode_printer())
|
||||
if (!wxGetApp().is_user_login(wxGetApp().get_printer_cloud_provider()) && !mobj->is_lan_mode_printer())
|
||||
continue;
|
||||
|
||||
/* do not show printer in my list */
|
||||
@@ -983,7 +984,7 @@ void EditDevNameDialog::on_edit_name(wxCommandEvent &e)
|
||||
auto utf8_str = new_dev_name.ToUTF8();
|
||||
auto name = std::string(utf8_str.data(), utf8_str.length());
|
||||
if (m_info)
|
||||
dev->modify_device_name(m_info->get_dev_id(), name);
|
||||
dev->modify_device_name(m_info->get_dev_id(), name, wxGetApp().get_printer_cloud_provider());
|
||||
}
|
||||
DPIDialog::EndModal(wxID_CLOSE);
|
||||
}
|
||||
|
||||
@@ -1013,12 +1013,13 @@ void SendToPrinterDialog::clear_ip_address_config(wxCommandEvent& e)
|
||||
void SendToPrinterDialog::update_user_machine_list()
|
||||
{
|
||||
NetworkAgent* m_agent = wxGetApp().getAgent();
|
||||
if (m_agent && m_agent->is_user_login()) {
|
||||
boost::thread get_print_info_thread = Slic3r::create_thread([this, token = std::weak_ptr<int>(m_token)] {
|
||||
const std::string provider = wxGetApp().get_printer_cloud_provider();
|
||||
if (m_agent && m_agent->is_user_login(provider)) {
|
||||
boost::thread get_print_info_thread = Slic3r::create_thread([this, token = std::weak_ptr<int>(m_token), provider] {
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
unsigned int http_code;
|
||||
std::string body;
|
||||
int result = agent->get_user_print_info(&http_code, &body);
|
||||
int result = agent->get_user_print_info(&http_code, &body, provider);
|
||||
CallAfter([token, this, result, body] {
|
||||
if (token.expired()) {return;}
|
||||
if (result == 0) {
|
||||
@@ -1224,11 +1225,12 @@ void SendToPrinterDialog::update_show_status()
|
||||
DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
|
||||
if (!agent) return;
|
||||
if (!dev) return;
|
||||
const std::string provider = wxGetApp().get_printer_cloud_provider();
|
||||
MachineObject* obj_ = dev->get_my_machine(m_printer_last_select);
|
||||
|
||||
if (!obj_) {
|
||||
if (agent) {
|
||||
if (agent->is_user_login()) {
|
||||
if (agent->is_user_login(provider)) {
|
||||
show_status(PrintDialogStatus::PrintStatusInvalidPrinter);
|
||||
}
|
||||
}
|
||||
@@ -1237,7 +1239,7 @@ void SendToPrinterDialog::update_show_status()
|
||||
|
||||
/* check cloud machine connections */
|
||||
if (!obj_->is_lan_mode_printer()) {
|
||||
if (!agent->is_server_connected()) {
|
||||
if (!agent->is_server_connected(provider)) {
|
||||
show_status(PrintDialogStatus::PrintStatusConnectingServer);
|
||||
reset_timeout();
|
||||
return;
|
||||
@@ -1564,7 +1566,7 @@ void SendToPrinterDialog::set_default()
|
||||
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (agent) {
|
||||
if (agent->is_user_login()) {
|
||||
if (agent->is_user_login(wxGetApp().get_printer_cloud_provider())) {
|
||||
show_status(PrintDialogStatus::PrintStatusInit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2054,12 +2054,13 @@ void SyncAmsInfoDialog::Enable_Auto_Refill(bool enable)
|
||||
void SyncAmsInfoDialog::update_user_machine_list()
|
||||
{
|
||||
NetworkAgent *m_agent = wxGetApp().getAgent();
|
||||
if (m_agent && m_agent->is_user_login()) {
|
||||
boost::thread get_print_info_thread = Slic3r::create_thread([this, token = std::weak_ptr(m_token)] {
|
||||
const std::string provider = wxGetApp().get_printer_cloud_provider();
|
||||
if (m_agent && m_agent->is_user_login(provider)) {
|
||||
boost::thread get_print_info_thread = Slic3r::create_thread([this, token = std::weak_ptr(m_token), provider] {
|
||||
NetworkAgent *agent = wxGetApp().getAgent();
|
||||
unsigned int http_code;
|
||||
std::string body;
|
||||
int result = agent->get_user_print_info(&http_code, &body);
|
||||
int result = agent->get_user_print_info(&http_code, &body, provider);
|
||||
CallAfter([token, this, result, body] {
|
||||
if (token.expired()) { return; }
|
||||
if (result == 0) {
|
||||
@@ -2307,13 +2308,14 @@ void SyncAmsInfoDialog::update_show_status()
|
||||
return;
|
||||
}
|
||||
if (!dev) return;
|
||||
const std::string provider = wxGetApp().get_printer_cloud_provider();
|
||||
|
||||
// blank plate has no valid gcode file
|
||||
if (is_must_finish_slice_then_connected_printer()) { return; }
|
||||
MachineObject * obj_ = dev->get_selected_machine();
|
||||
if (!obj_) {
|
||||
if (agent) {
|
||||
if (agent->is_user_login()) {
|
||||
if (agent->is_user_login(provider)) {
|
||||
show_status(PrintDialogStatus::PrintStatusInvalidPrinter);
|
||||
}
|
||||
}
|
||||
@@ -2322,7 +2324,7 @@ void SyncAmsInfoDialog::update_show_status()
|
||||
|
||||
/* check cloud machine connections */
|
||||
if (!obj_->is_lan_mode_printer()) {
|
||||
if (!agent->is_server_connected()) {
|
||||
if (!agent->is_server_connected(provider)) {
|
||||
show_status(PrintDialogStatus::PrintStatusConnectingServer);
|
||||
reset_timeout();
|
||||
return;
|
||||
@@ -2585,7 +2587,7 @@ void SyncAmsInfoDialog::set_default(bool hide_some)
|
||||
NetworkAgent *agent = wxGetApp().getAgent();
|
||||
if (agent) {
|
||||
if (!hide_some) {
|
||||
if (agent->is_user_login()) {
|
||||
if (agent->is_user_login(wxGetApp().get_printer_cloud_provider())) {
|
||||
show_status(PrintDialogStatus::PrintStatusInit);
|
||||
}
|
||||
}
|
||||
|
||||
+23
-11
@@ -33,6 +33,7 @@
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
@@ -229,9 +230,17 @@ void Tab::create_preset_tab()
|
||||
if (m_type == Preset::TYPE_PRINTER && !m_presets_choice->is_selected_physical_printer())
|
||||
m_preset_bundle->physical_printers.unselect_printer();
|
||||
|
||||
// select preset
|
||||
std::string preset_name = m_presets_choice->GetString(selection).ToUTF8().data();
|
||||
select_preset(Preset::remove_suffix_modified(preset_name));
|
||||
// select preset — prefer stored internal name to avoid alias collisions
|
||||
wxString stored_name = m_presets_choice->GetItemAlias(selection);
|
||||
std::string preset_name;
|
||||
if (!stored_name.empty()) {
|
||||
preset_name = Preset::remove_suffix_modified(stored_name.ToUTF8().data());
|
||||
} else {
|
||||
std::string selected_label = Preset::remove_suffix_modified(
|
||||
m_presets_choice->GetString(selection).ToUTF8().data());
|
||||
preset_name = m_preset_bundle->get_preset_name_by_alias(m_type, selected_label);
|
||||
}
|
||||
select_preset(preset_name);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2053,6 +2062,12 @@ void Tab::on_presets_changed()
|
||||
// Check if printer agent needs switching
|
||||
if (m_type == Preset::TYPE_PRINTER) {
|
||||
wxGetApp().switch_printer_agent();
|
||||
|
||||
// Trigger per-vendor preset update check
|
||||
const Preset& printer_preset = m_preset_bundle->printers.get_edited_preset();
|
||||
if (printer_preset.vendor) {
|
||||
wxGetApp().get_preset_updater()->check_vendor_update(printer_preset.vendor->id);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_bbl_vendor_preset = m_preset_bundle->is_bbl_vendor();
|
||||
@@ -5676,7 +5691,7 @@ void Tab::update_btns_enabling()
|
||||
// and any user preset
|
||||
const Preset& preset = m_presets->get_edited_preset();
|
||||
m_btn_delete_preset->Show((m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection())
|
||||
|| (!preset.is_default && !preset.is_system));
|
||||
|| preset.can_overwrite());
|
||||
|
||||
//if (m_btn_edit_ph_printer)
|
||||
// m_btn_edit_ph_printer->SetToolTip( m_preset_bundle->physical_printers.has_selection() ?
|
||||
@@ -5857,7 +5872,7 @@ bool Tab::select_preset(
|
||||
Preset ¤t_preset = m_presets->get_selected_preset();
|
||||
|
||||
// Obtain compatible filament and process presets for printers
|
||||
if (m_preset_bundle && m_presets->get_preset_base(current_preset) == ¤t_preset && printer_tab && !current_preset.is_system) {
|
||||
if (m_preset_bundle && m_presets->get_preset_base(current_preset) == ¤t_preset && printer_tab && !current_preset.is_system && !current_preset.is_from_bundle()) {
|
||||
delete_third_printer = true;
|
||||
for (const Preset &preset : m_preset_bundle->filaments.get_presets()) {
|
||||
if (preset.is_compatible && !preset.is_default) {
|
||||
@@ -5865,7 +5880,6 @@ bool Tab::select_preset(
|
||||
filament_presets.push_front(preset);
|
||||
else
|
||||
filament_presets.push_back(preset);
|
||||
if (!preset.setting_id.empty()) { m_preset_bundle->filaments.set_sync_info_and_save(preset.name, preset.setting_id, "delete", 0); }
|
||||
}
|
||||
}
|
||||
for (const Preset &preset : m_preset_bundle->prints.get_presets()) {
|
||||
@@ -5874,13 +5888,11 @@ bool Tab::select_preset(
|
||||
process_presets.push_front(preset);
|
||||
else
|
||||
process_presets.push_back(preset);
|
||||
if (!preset.setting_id.empty()) { m_preset_bundle->filaments.set_sync_info_and_save(preset.name, preset.setting_id, "delete", 0); }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!current_preset.setting_id.empty()) {
|
||||
m_presets->set_sync_info_and_save(current_preset.name, current_preset.setting_id, "delete", 0);
|
||||
wxGetApp().delete_preset_from_cloud(current_preset.setting_id);
|
||||
wxGetApp().delete_preset_from_cloud(current_preset.setting_id, current_preset.file);
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "delete preset = " << current_preset.name << ", setting_id = " << current_preset.setting_id;
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("will delete current preset...");
|
||||
@@ -5971,7 +5983,7 @@ bool Tab::select_preset(
|
||||
|
||||
for (const Preset &preset : filament_presets) {
|
||||
if (!preset.setting_id.empty()) {
|
||||
wxGetApp().delete_preset_from_cloud(preset.setting_id);
|
||||
wxGetApp().delete_preset_from_cloud(preset.setting_id, preset.file);
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "delete filament preset = " << preset.name << ", setting_id = " << preset.setting_id;
|
||||
preset_bundle->filaments.delete_preset(preset.name);
|
||||
@@ -5979,7 +5991,7 @@ bool Tab::select_preset(
|
||||
|
||||
for (const Preset &preset : process_presets) {
|
||||
if (!preset.setting_id.empty()) {
|
||||
wxGetApp().delete_preset_from_cloud(preset.setting_id);
|
||||
wxGetApp().delete_preset_from_cloud(preset.setting_id, preset.file);
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "delete print preset = " << preset.name << ", setting_id = " << preset.setting_id;
|
||||
preset_bundle->prints.delete_preset(preset.name);
|
||||
|
||||
@@ -437,6 +437,7 @@ protected:
|
||||
// return true if cancelled
|
||||
bool tree_sel_change_delayed(wxCommandEvent& event);
|
||||
void on_presets_changed();
|
||||
void update_printer_agent_if_needed();
|
||||
void build_preset_description_line(ConfigOptionsGroup* optgroup);
|
||||
void update_preset_description_line();
|
||||
void update_frequently_changed_parameters();
|
||||
|
||||
@@ -1087,7 +1087,7 @@ bool UnsavedChangesDialog::save(PresetCollection* dependent_presets, bool show_s
|
||||
// for system/default/external presets we should take an edited name
|
||||
//BBS: add project embedded preset logic and refine is_external
|
||||
bool save_to_project = false;
|
||||
if (preset.is_system || preset.is_default) {
|
||||
if (!preset.can_overwrite()) {
|
||||
//if (preset.is_system || preset.is_default || preset.is_external) {
|
||||
SavePresetDialog save_dlg(this, preset.type);
|
||||
if (save_dlg.ShowModal() != wxID_OK) {
|
||||
@@ -1114,7 +1114,7 @@ bool UnsavedChangesDialog::save(PresetCollection* dependent_presets, bool show_s
|
||||
if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) {
|
||||
const Preset& preset = tab->get_presets()->get_edited_preset();
|
||||
//BBS: add project embedded preset logic and refine is_external
|
||||
if (preset.is_system || preset.is_default)
|
||||
if (!preset.can_overwrite())
|
||||
//if (preset.is_system || preset.is_default || preset.is_external)
|
||||
types_for_save.emplace_back(preset.type);
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ int UserManager::parse_json(std::string payload)
|
||||
GUI::MessageDialog msgdialog(nullptr, _L("Log in successful."), "", wxAPPLY | wxOK);
|
||||
msgdialog.ShowModal();
|
||||
}
|
||||
dev->update_user_machine_list_info();
|
||||
dev->update_user_machine_list_info(GUI::wxGetApp().get_printer_cloud_provider());
|
||||
dev->set_selected_machine(dev_id);
|
||||
return 0;
|
||||
}
|
||||
@@ -75,4 +75,4 @@ int UserManager::parse_json(std::string payload)
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -509,7 +509,6 @@ void GuideFrame::OnScriptMessage(wxWebViewEvent &evt)
|
||||
SaveProfile();
|
||||
|
||||
std::string oldregion = m_ProfileJson["region"];
|
||||
bool bLogin = false;
|
||||
if (m_Region != oldregion) {
|
||||
AppConfig* config = GUI::wxGetApp().app_config;
|
||||
std::string country_code = config->get_country_code();
|
||||
@@ -517,7 +516,6 @@ void GuideFrame::OnScriptMessage(wxWebViewEvent &evt)
|
||||
if (agent) {
|
||||
agent->set_country_code(country_code);
|
||||
if (wxGetApp().is_user_login()) {
|
||||
bLogin = true;
|
||||
agent->user_logout();
|
||||
}
|
||||
}
|
||||
@@ -526,10 +524,7 @@ void GuideFrame::OnScriptMessage(wxWebViewEvent &evt)
|
||||
this->EndModal(wxID_OK);
|
||||
|
||||
if (InstallNetplugin)
|
||||
GUI::wxGetApp().CallAfter([this] { GUI::wxGetApp().ShowDownNetPluginDlg(); });
|
||||
|
||||
if (bLogin)
|
||||
GUI::wxGetApp().CallAfter([this] { login(); });
|
||||
GUI::wxGetApp().CallAfter([] { GUI::wxGetApp().ShowDownNetPluginDlg(); });
|
||||
}
|
||||
else if (strCmd == "user_guide_cancel") {
|
||||
this->EndModal(wxID_CANCEL);
|
||||
@@ -790,11 +785,9 @@ bool GuideFrame::apply_config(AppConfig *app_config, PresetBundle *preset_bundle
|
||||
!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
|
||||
if (install_bundles.size() > 0) {
|
||||
// Install bundles from resources.
|
||||
// Don't create snapshot - we've already done that above if applicable.
|
||||
if (! updater->install_bundles_rsrc(std::move(install_bundles), false))
|
||||
return false;
|
||||
// If there are bundles to install, they will be installed by apply_vendor_config
|
||||
if (!install_bundles.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Will install " << install_bundles.size() << " vendor bundles from resources";
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resource directory";
|
||||
}
|
||||
@@ -884,32 +877,82 @@ bool GuideFrame::apply_config(AppConfig *app_config, PresetBundle *preset_bundle
|
||||
// Not switch filament
|
||||
//get_first_added_material_preset(AppConfig::SECTION_FILAMENTS, first_added_filament);
|
||||
|
||||
//update the app_config
|
||||
app_config->set_section(AppConfig::SECTION_FILAMENTS, enabled_filaments);
|
||||
app_config->set_vendors(m_appconfig_new);
|
||||
// ORCA: functionality moved to PresetBundle::apply_vendor_config; keeping for future reference
|
||||
// // For each @System filament, check if a vendor-specific override exists
|
||||
// // in the loaded profiles. If so, replace the @System variant with the
|
||||
// // override (e.g. replace "Generic ABS @System" with BBL "Generic ABS").
|
||||
// // When printers from the default bundle are also selected, keep @System
|
||||
// // too since those printers need it.
|
||||
// static const std::string system_suffix = " @System";
|
||||
// auto it_default = enabled_vendors.find(PresetBundle::ORCA_DEFAULT_BUNDLE);
|
||||
// bool has_default_bundle_printer = it_default != enabled_vendors.end() && !it_default->second.empty();
|
||||
// bool has_filament_profiles = m_ProfileJson.contains("filament");
|
||||
|
||||
if (check_unsaved_preset_changes)
|
||||
preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::Enable,
|
||||
{preferred_model, preferred_variant, first_added_filament, std::string()});
|
||||
// // Check if any non-default vendor has selected printers
|
||||
// bool has_vendor_printer = false;
|
||||
// for (const auto& [vendor, models] : enabled_vendors) {
|
||||
// if (vendor != PresetBundle::ORCA_DEFAULT_BUNDLE && !models.empty()) {
|
||||
// has_vendor_printer = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// If the active filament is not in the wizard-selected filaments, switch to the first
|
||||
// compatible wizard-selected filament. This handles the first-run case where load_presets
|
||||
// falls back to "Generic PLA" even though the user selected a different filament.
|
||||
bool active_filament_selected = enabled_filaments.empty()
|
||||
|| enabled_filaments.count(preset_bundle->filament_presets.front()) > 0;
|
||||
if (!active_filament_selected) {
|
||||
for (const auto& [filament_name, _] : enabled_filaments) {
|
||||
const Preset* preset = preset_bundle->filaments.find_preset(filament_name);
|
||||
if (preset && preset->is_visible && preset->is_compatible) {
|
||||
preset_bundle->filaments.select_preset_by_name(filament_name, true);
|
||||
preset_bundle->filament_presets.front() = preset_bundle->filaments.get_selected_preset_name();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// std::map<std::string, std::string> supplemented_filaments;
|
||||
// for (const auto& [name, value] : enabled_filaments) {
|
||||
// if (name.size() > system_suffix.size() &&
|
||||
// name.compare(name.size() - system_suffix.size(), system_suffix.size(), system_suffix) == 0) {
|
||||
// std::string short_name = name.substr(0, name.size() - system_suffix.size());
|
||||
// if (has_vendor_printer && has_filament_profiles && m_ProfileJson["filament"].contains(short_name)) {
|
||||
// supplemented_filaments[short_name] = value;
|
||||
// BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Replacing @System filament: '" << name << "' -> '" << short_name << "'";
|
||||
// if (has_default_bundle_printer) {
|
||||
// supplemented_filaments[name] = value;
|
||||
// BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Also keeping '" << name << "' for default bundle printers";
|
||||
// }
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
// supplemented_filaments[name] = value;
|
||||
// }
|
||||
|
||||
// Update the selections from the compatibilty.
|
||||
preset_bundle->export_selections(*app_config);
|
||||
// //update the app_config
|
||||
// app_config->set_section(AppConfig::SECTION_FILAMENTS, supplemented_filaments);
|
||||
// app_config->set_vendors(m_appconfig_new);
|
||||
|
||||
// if (check_unsaved_preset_changes)
|
||||
// preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::Enable,
|
||||
// {preferred_model, preferred_variant, first_added_filament, std::string()});
|
||||
|
||||
// // If the active filament is not in the wizard-selected filaments, switch to the first
|
||||
// // compatible wizard-selected filament. This handles the first-run case where load_presets
|
||||
// // falls back to "Generic PLA" even though the user selected a different filament.
|
||||
// bool active_filament_selected = supplemented_filaments.empty()
|
||||
// || supplemented_filaments.count(preset_bundle->filament_presets.front()) > 0;
|
||||
// if (!active_filament_selected) {
|
||||
// for (const auto& [filament_name, _] : supplemented_filaments) {
|
||||
// const Preset* preset = preset_bundle->filaments.find_preset(filament_name);
|
||||
// if (preset && preset->is_visible && preset->is_compatible) {
|
||||
// preset_bundle->filaments.select_preset_by_name(filament_name, true);
|
||||
// preset_bundle->filament_presets.front() = preset_bundle->filaments.get_selected_preset_name();
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Update the selections from the compatibilty.
|
||||
// preset_bundle->export_selections(*app_config);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "calling apply_vendor_config from WebGuideDialog";
|
||||
// Call the Core library function to apply vendor configuration
|
||||
// This handles bundle installation, filament @System substitution, AppConfig updates, and preset loading
|
||||
if (!preset_bundle->apply_vendor_config(
|
||||
enabled_vendors,
|
||||
enabled_filaments,
|
||||
app_config,
|
||||
true,
|
||||
preferred_model,
|
||||
preferred_variant))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include <wx/wfstream.h>
|
||||
|
||||
#include <boost/cast.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
@@ -40,13 +42,78 @@ END_EVENT_TABLE()
|
||||
|
||||
int ZUserLogin::web_sequence_id = 20000;
|
||||
|
||||
ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_ANY, "OrcaSlicer")
|
||||
namespace {
|
||||
|
||||
int reserve_loopback_port()
|
||||
{
|
||||
try {
|
||||
boost::asio::io_service io_service;
|
||||
boost::asio::ip::tcp::acceptor acceptor(io_service, {boost::asio::ip::tcp::v4(), 0});
|
||||
return static_cast<int>(acceptor.local_endpoint().port());
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::string rewrite_loopback_url(std::string url, int port)
|
||||
{
|
||||
if (port <= 0) {
|
||||
return url;
|
||||
}
|
||||
|
||||
const std::string old_port = std::to_string(LOCALHOST_PORT);
|
||||
const std::string new_port = std::to_string(port);
|
||||
|
||||
boost::replace_all(url, std::string(LOCALHOST_URL) + old_port, std::string(LOCALHOST_URL) + new_port);
|
||||
boost::replace_all(url, "http://127.0.0.1:" + old_port, "http://127.0.0.1:" + new_port);
|
||||
boost::replace_all(url, "http%3A%2F%2Flocalhost%3A" + old_port, "http%3A%2F%2Flocalhost%3A" + new_port);
|
||||
boost::replace_all(url, "http%3A%2F%2F127.0.0.1%3A" + old_port, "http%3A%2F%2F127.0.0.1%3A" + new_port);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int ZUserLogin::ensure_loopback_port()
|
||||
{
|
||||
if (m_loopback_port <= 0) {
|
||||
m_loopback_port = reserve_loopback_port();
|
||||
}
|
||||
int port = m_loopback_port > 0 ? m_loopback_port : LOCALHOST_PORT;
|
||||
wxGetApp().start_http_server(port, m_cloud_agent->get_id());
|
||||
return port;
|
||||
}
|
||||
|
||||
ZUserLogin::ZUserLogin(std::shared_ptr<ICloudServiceAgent> cloud_agent)
|
||||
: wxDialog((wxWindow*) (wxGetApp().mainframe), wxID_ANY, "OrcaSlicer"), m_cloud_agent(cloud_agent)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
const auto bblnetwork_enabled =wxGetApp().app_config->get_bool("installed_networking");
|
||||
// Url
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (!agent && bblnetwork_enabled) {
|
||||
|
||||
if (!m_cloud_agent) {
|
||||
wxBoxSizer* m_sizer_main = new wxBoxSizer(wxVERTICAL);
|
||||
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1));
|
||||
m_line_top->SetBackgroundColour(wxColour(166, 169, 170));
|
||||
m_sizer_main->Add(m_line_top, 0, wxEXPAND, 0);
|
||||
|
||||
auto* m_message = new wxStaticText(this, wxID_ANY,
|
||||
_L("Cloud agent is not available. Please restart OrcaSlicer and try again."),
|
||||
wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_message->SetForegroundColour(*wxBLACK);
|
||||
m_message->Wrap(FromDIP(360));
|
||||
m_sizer_main->Add(m_message, 0, wxALIGN_CENTER | wxALL, FromDIP(15));
|
||||
|
||||
m_sizer_main->Add(0, 0, 1, wxBOTTOM, 10);
|
||||
SetSizer(m_sizer_main);
|
||||
m_sizer_main->SetSizeHints(this);
|
||||
Layout();
|
||||
Fit();
|
||||
CentreOnParent();
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto bblnetwork_enabled = wxGetApp().app_config->get_bool("installed_networking");
|
||||
if (m_cloud_agent->get_id() == BBL_CLOUD_PROVIDER && !bblnetwork_enabled) {
|
||||
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
@@ -74,19 +141,15 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
|
||||
Layout();
|
||||
Fit();
|
||||
CentreOnParent();
|
||||
}
|
||||
else {
|
||||
// Get the login URL from the cloud service agent
|
||||
} else {
|
||||
// Get the login URL from the injected cloud service agent
|
||||
wxString strlang = wxGetApp().current_language_code_safe();
|
||||
strlang.Replace("_", "-");
|
||||
TargetUrl = wxString::FromUTF8(agent->get_cloud_login_url(strlang.ToStdString()));
|
||||
m_networkOk = TargetUrl.StartsWith("file://");
|
||||
TargetUrl = wxString::FromUTF8(m_cloud_agent->get_cloud_login_url(strlang.ToStdString()));
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "login url = " << TargetUrl.ToStdString();
|
||||
|
||||
m_bbl_user_agent = wxString::Format("BBL-Slicer/v%s", SLIC3R_VERSION);
|
||||
|
||||
// set the frame icon
|
||||
m_bbl_user_agent = wxString::Format("BBL-Slicer/v%s", wxGetApp().get_bbl_client_version());
|
||||
|
||||
// Create the webview
|
||||
m_browser = WebView::CreateWebView(this, TargetUrl);
|
||||
@@ -97,12 +160,6 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
|
||||
m_browser->Hide();
|
||||
m_browser->SetSize(0, 0);
|
||||
|
||||
// Log backend information
|
||||
// wxLogMessage(wxWebView::GetBackendVersionInfo().ToString());
|
||||
// wxLogMessage("Backend: %s Version: %s",
|
||||
// m_browser->GetClassInfo()->GetClassName(),wxWebView::GetBackendVersionInfo().ToString());
|
||||
// wxLogMessage("User Agent: %s", m_browser->GetUserAgent());
|
||||
|
||||
// Connect the webview events
|
||||
Bind(wxEVT_WEBVIEW_NAVIGATING, &ZUserLogin::OnNavigationRequest, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_NAVIGATED, &ZUserLogin::OnNavigationComplete, this, m_browser->GetId());
|
||||
@@ -113,19 +170,15 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
|
||||
Bind(wxEVT_WEBVIEW_FULLSCREEN_CHANGED, &ZUserLogin::OnFullScreenChanged, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &ZUserLogin::OnScriptMessage, this, m_browser->GetId());
|
||||
|
||||
// Connect the idle events
|
||||
// Bind(wxEVT_IDLE, &ZUserLogin::OnIdle, this);
|
||||
// Bind(wxEVT_CLOSE_WINDOW, &ZUserLogin::OnClose, this);
|
||||
|
||||
// UI
|
||||
SetTitle(_L("Login"));
|
||||
// Set a more sensible size for web browsing
|
||||
wxSize pSize = FromDIP(wxSize(650, 840));
|
||||
SetSize(pSize);
|
||||
|
||||
int screenheight = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y, NULL);
|
||||
int screenwidth = wxSystemSettings::GetMetric(wxSYS_SCREEN_X, NULL);
|
||||
int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0;
|
||||
int screenheight = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y, NULL);
|
||||
int screenwidth = wxSystemSettings::GetMetric(wxSYS_SCREEN_X, NULL);
|
||||
int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0;
|
||||
wxPoint tmpPT((screenwidth - pSize.x) / 2, MaxY);
|
||||
Move(tmpPT);
|
||||
}
|
||||
@@ -221,8 +274,7 @@ void ZUserLogin::OnDocumentLoaded(wxWebViewEvent &evt)
|
||||
{
|
||||
// Only notify if the document is the main frame, not a subframe
|
||||
wxString tmpUrl = evt.GetURL();
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
std::string strHost = agent->get_cloud_service_host();
|
||||
std::string strHost = m_cloud_agent->get_cloud_service_host();
|
||||
|
||||
if (tmpUrl.StartsWith("file://") || tmpUrl.Contains(strHost)) {
|
||||
m_networkOk = true;
|
||||
@@ -270,11 +322,10 @@ void ZUserLogin::OnScriptMessage(wxWebViewEvent &evt)
|
||||
json j = json::parse(into_u8(str_input));
|
||||
wxString strCmd = j["command"];
|
||||
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (agent && strCmd == "get_login_cmd" && agent->get_cloud_agent()) {
|
||||
if (m_cloud_agent && strCmd == "get_login_cmd") {
|
||||
// Return login config (backend_url, apikey, pkce)
|
||||
// WebView handles provider selection internally
|
||||
std::string login_cmd = agent->build_login_cmd();
|
||||
std::string login_cmd = m_cloud_agent->build_login_cmd();
|
||||
m_loopback_port = 0;
|
||||
try {
|
||||
json cfg = json::parse(login_cmd);
|
||||
@@ -329,13 +380,11 @@ void ZUserLogin::OnScriptMessage(wxWebViewEvent &evt)
|
||||
|
||||
// Handle message after modal dialog ends to avoid deadlock
|
||||
// Use wxTheApp->CallAfter to ensure it runs after modal loop exits
|
||||
wxTheApp->CallAfter([message_json]() {
|
||||
wxGetApp().handle_script_message(message_json);
|
||||
});
|
||||
const auto provider = m_cloud_agent->get_id();
|
||||
wxTheApp->CallAfter([message_json, provider]() { wxGetApp().handle_script_message(message_json, provider); });
|
||||
}
|
||||
else if (strCmd == "get_localhost_url") {
|
||||
int loopback_port = m_loopback_port > 0 ? m_loopback_port : LOCALHOST_PORT;
|
||||
wxGetApp().start_http_server(loopback_port);
|
||||
int loopback_port = ensure_loopback_port();
|
||||
std::string sequence_id = j["sequence_id"].get<std::string>();
|
||||
CallAfter([this, sequence_id] {
|
||||
json ack_j;
|
||||
@@ -351,8 +400,8 @@ void ZUserLogin::OnScriptMessage(wxWebViewEvent &evt)
|
||||
else if (strCmd == "thirdparty_login") {
|
||||
if (j["data"].contains("url")) {
|
||||
std::string jump_url = j["data"]["url"].get<std::string>();
|
||||
int loopback_port = m_loopback_port > 0 ? m_loopback_port : LOCALHOST_PORT;
|
||||
wxGetApp().start_http_server(loopback_port);
|
||||
int loopback_port = ensure_loopback_port();
|
||||
jump_url = rewrite_loopback_url(jump_url, loopback_port);
|
||||
CallAfter([this, jump_url] {
|
||||
wxString url = wxString::FromUTF8(jump_url);
|
||||
wxLaunchDefaultBrowser(url);
|
||||
|
||||
@@ -28,12 +28,14 @@
|
||||
#include <wx/tbarbase.h>
|
||||
#include "wx/textctrl.h"
|
||||
|
||||
#include "slic3r/Utils/ICloudServiceAgent.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class ZUserLogin : public wxDialog
|
||||
{
|
||||
public:
|
||||
ZUserLogin();
|
||||
explicit ZUserLogin(std::shared_ptr<ICloudServiceAgent> cloud_agent);
|
||||
virtual ~ZUserLogin();
|
||||
|
||||
void load_url(wxString &url);
|
||||
@@ -84,6 +86,9 @@ private:
|
||||
|
||||
wxString m_bbl_user_agent;
|
||||
|
||||
std::shared_ptr<ICloudServiceAgent> m_cloud_agent;
|
||||
int ensure_loopback_port();
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
|
||||
@@ -417,8 +417,13 @@ void WebViewPanel::OnClose(wxCloseEvent& evt)
|
||||
void WebViewPanel::OnFreshLoginStatus(wxTimerEvent &event)
|
||||
{
|
||||
auto mainframe = Slic3r::GUI::wxGetApp().mainframe;
|
||||
if (mainframe && mainframe->m_webview == this)
|
||||
Slic3r::GUI::wxGetApp().get_login_info();
|
||||
if (mainframe && mainframe->m_webview == this) {
|
||||
Slic3r::GUI::wxGetApp().get_login_info(ORCA_CLOUD_PROVIDER);
|
||||
auto* app_config = Slic3r::GUI::wxGetApp().app_config;
|
||||
if (app_config && app_config->has_cloud_provider(BBL_CLOUD_PROVIDER)) {
|
||||
Slic3r::GUI::wxGetApp().get_login_info(BBL_CLOUD_PROVIDER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebViewPanel::SetLoginPanelVisibility(bool bshow)
|
||||
@@ -509,6 +514,27 @@ void WebViewPanel::ShowNetpluginTip()
|
||||
RunScript(strJS);
|
||||
}
|
||||
|
||||
void WebViewPanel::SendCloudProvidersInfo()
|
||||
{
|
||||
auto* app_config = wxGetApp().app_config;
|
||||
if (!app_config)
|
||||
return;
|
||||
|
||||
auto providers = app_config->get_cloud_providers();
|
||||
json j;
|
||||
j["command"] = "cloud_providers_info";
|
||||
json data;
|
||||
json provider_array = json::array();
|
||||
for (const auto& p : providers) {
|
||||
provider_array.push_back(p);
|
||||
}
|
||||
data["providers"] = provider_array;
|
||||
j["data"] = data;
|
||||
|
||||
wxString strJS = wxString::Format("window.postMessage(%s)", j.dump());
|
||||
RunScript(strJS);
|
||||
}
|
||||
|
||||
void WebViewPanel::get_design_staffpick(int offset, int limit, std::function<void(std::string)> callback)
|
||||
{
|
||||
// auto host = wxGetApp().get_http_url(wxGetApp().app_config->get_country_code(), "v1/design-service/design/staffpick");
|
||||
@@ -613,6 +639,7 @@ void WebViewPanel::OnDocumentLoaded(wxWebViewEvent& evt)
|
||||
wxLogMessage("%s", "Document loaded; url='" + evt.GetURL() + "'");
|
||||
}
|
||||
UpdateState();
|
||||
SendCloudProvidersInfo();
|
||||
}
|
||||
|
||||
void WebViewPanel::OnTitleChanged(wxWebViewEvent &evt)
|
||||
|
||||
@@ -99,6 +99,7 @@ public:
|
||||
void OpenModelDetail(std::string id, NetworkAgent *agent);
|
||||
void SendLoginInfo();
|
||||
void ShowNetpluginTip();
|
||||
void SendCloudProvidersInfo();
|
||||
|
||||
void get_design_staffpick(int offset, int limit, std::function<void(std::string)> callback);
|
||||
int get_model_mall_detail_url(std::string *url, std::string id);
|
||||
|
||||
@@ -210,9 +210,19 @@ int ComboBox::Append(const wxString &text,
|
||||
}
|
||||
|
||||
int ComboBox::Append(const wxString &text, const wxBitmap &bitmap, const wxString &group, void *clientData, int style)
|
||||
{
|
||||
return Append(text, bitmap, group, group, clientData, style);
|
||||
}
|
||||
|
||||
int ComboBox::Append(const wxString &text,
|
||||
const wxBitmap &bitmap,
|
||||
const wxString &group_key,
|
||||
const wxString &group_label,
|
||||
void *clientData,
|
||||
int style)
|
||||
{
|
||||
auto valid_bit_map = (&bitmap && bitmap.IsOk()) ? bitmap : wxNullBitmap;
|
||||
Item item{text, wxEmptyString, valid_bit_map, valid_bit_map, clientData, group};
|
||||
Item item{text, wxEmptyString, valid_bit_map, valid_bit_map, clientData, group_key, group_label};
|
||||
item.style = style;
|
||||
items.push_back(item);
|
||||
SetClientDataType(wxClientData_Void);
|
||||
@@ -290,6 +300,18 @@ void ComboBox::SetItemTooltip(unsigned int n, wxString const &value) {
|
||||
if (n == drop.GetSelection()) drop.SetToolTip(value);
|
||||
}
|
||||
|
||||
wxString ComboBox::GetItemAlias(unsigned int n) const
|
||||
{
|
||||
if (n >= items.size()) return wxString();
|
||||
return items[n].alias;
|
||||
}
|
||||
|
||||
void ComboBox::SetItemAlias(unsigned int n, wxString const &value)
|
||||
{
|
||||
if (n >= items.size()) return;
|
||||
items[n].alias = value;
|
||||
}
|
||||
|
||||
wxBitmap ComboBox::GetItemBitmap(unsigned int n) { return items[n].icon; }
|
||||
|
||||
void ComboBox::SetItemBitmap(unsigned int n, wxBitmap const &bitmap)
|
||||
|
||||
@@ -37,6 +37,12 @@ public:
|
||||
int Append(const wxString &item, const wxBitmap &bitmap = wxNullBitmap, int item_style = 0);
|
||||
int Append(const wxString &item, const wxBitmap &bitmap, void *clientData, int item_style = 0);
|
||||
int Append(const wxString &item, const wxBitmap &bitmap, const wxString &group, void *clientData = nullptr, int item_style = 0);
|
||||
int Append(const wxString& item,
|
||||
const wxBitmap& bitmap,
|
||||
const wxString& group_key,
|
||||
const wxString& group_label,
|
||||
void* clientData = nullptr,
|
||||
int item_style = 0);
|
||||
|
||||
int SetItems(const std::vector<DropDown::Item>& the_items);
|
||||
|
||||
|
||||
@@ -340,14 +340,14 @@ void DropDown::render(wxDC &dc)
|
||||
states2 &= ~StateColor::Enabled;
|
||||
// Skip by group
|
||||
if (group.IsEmpty()) {
|
||||
if (!item.group.IsEmpty()) {
|
||||
if (groups.find(item.group) != groups.end())
|
||||
if (!item.group_key.IsEmpty()) {
|
||||
if (groups.find(item.group_key) != groups.end())
|
||||
continue;
|
||||
groups.insert(item.group);
|
||||
if (!item.group.IsEmpty()) {
|
||||
groups.insert(item.group_key);
|
||||
if (!item.group_key.IsEmpty()) {
|
||||
bool disabled = true;
|
||||
for (int j = i + 1; j < items.size(); ++j) {
|
||||
if (items[i].group != item.group && (items[j].style & DD_ITEM_STYLE_DISABLED) == 0) {
|
||||
if (items[j].group_key == item.group_key && (items[j].style & DD_ITEM_STYLE_DISABLED) == 0) {
|
||||
disabled = false;
|
||||
break;
|
||||
}
|
||||
@@ -357,7 +357,7 @@ void DropDown::render(wxDC &dc)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (item.group != group)
|
||||
if (item.group_key != group)
|
||||
continue;
|
||||
}
|
||||
bool is_hover = index == hover_item;
|
||||
@@ -391,8 +391,8 @@ void DropDown::render(wxDC &dc)
|
||||
pt.y = rcContent.y;
|
||||
}
|
||||
auto text = group.IsEmpty()
|
||||
? (item.group.IsEmpty() ? item.text : item.group)
|
||||
: (item.text.StartsWith(group) && !group.EndsWith(' ') ? item.text.substr(group.size()).Trim(false) : item.text);
|
||||
? (item.group_key.IsEmpty() ? item.text : item.group_label)
|
||||
: item.text;
|
||||
if (!text_off && !text.IsEmpty()) {
|
||||
wxSize tSize = dc.GetMultiLineTextExtent(text);
|
||||
if (pt.x + tSize.x > rcContent.GetRight()) {
|
||||
@@ -405,7 +405,7 @@ void DropDown::render(wxDC &dc)
|
||||
dc.SetFont(GetFont());
|
||||
dc.SetTextForeground(text_color.colorForStates(states2));
|
||||
dc.DrawText(text, pt);
|
||||
if (group.IsEmpty() && !item.group.IsEmpty()) {
|
||||
if (group.IsEmpty() && !item.group_key.IsEmpty()) {
|
||||
auto szBmp = arrow_bitmap.GetBmpSize();
|
||||
pt.x = rcContent.GetRight() - szBmp.x - 5;
|
||||
pt.y = rcContent.y + (rcContent.height - szBmp.y) / 2;
|
||||
@@ -430,18 +430,18 @@ int DropDown::hoverIndex()
|
||||
auto &item = items[i];
|
||||
// Skip by group
|
||||
if (group.IsEmpty()) {
|
||||
if (!item.group.IsEmpty()) {
|
||||
if (groups.find(item.group) == groups.end())
|
||||
groups.insert(item.group);
|
||||
if (!item.group_key.IsEmpty()) {
|
||||
if (groups.find(item.group_key) == groups.end())
|
||||
groups.insert(item.group_key);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (item.group != group)
|
||||
if (item.group_key != group)
|
||||
continue;
|
||||
}
|
||||
if (++index == hover_item)
|
||||
return (item.group.IsEmpty() || !group.IsEmpty()) ? i : -i - 2;
|
||||
return (item.group_key.IsEmpty() || !group.IsEmpty()) ? i : -i - 2;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -455,7 +455,7 @@ int DropDown::selectedItem()
|
||||
if (count == items.size() && subDropDown == nullptr)
|
||||
return selection;
|
||||
auto & sel = items[selection];
|
||||
if (group.IsEmpty() ? !sel.group.IsEmpty() : sel.group != group)
|
||||
if (group.IsEmpty() ? !sel.group_key.IsEmpty() : sel.group_key != group)
|
||||
return -1;
|
||||
if (selection == 0)
|
||||
return 0;
|
||||
@@ -465,14 +465,14 @@ int DropDown::selectedItem()
|
||||
auto &item = items[i];
|
||||
// Skip by group
|
||||
if (group.IsEmpty()) {
|
||||
if (!item.group.IsEmpty()) {
|
||||
if (groups.find(item.group) == groups.end())
|
||||
groups.insert(item.group);
|
||||
if (!item.group_key.IsEmpty()) {
|
||||
if (groups.find(item.group_key) == groups.end())
|
||||
groups.insert(item.group_key);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (item.group != group)
|
||||
if (item.group_key != group)
|
||||
continue;
|
||||
}
|
||||
++index;
|
||||
@@ -493,24 +493,24 @@ void DropDown::messureSize()
|
||||
auto &item = items[i];
|
||||
// Skip by group
|
||||
if (group.IsEmpty()) {
|
||||
if (!item.group.IsEmpty()) {
|
||||
if (groups.find(item.group) == groups.end())
|
||||
groups.insert(item.group);
|
||||
if (!item.group_key.IsEmpty()) {
|
||||
if (groups.find(item.group_key) == groups.end())
|
||||
groups.insert(item.group_key);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (item.group != group)
|
||||
if (item.group_key != group)
|
||||
continue;
|
||||
}
|
||||
++count;
|
||||
wxSize size1;
|
||||
if (!text_off) {
|
||||
auto text = group.IsEmpty()
|
||||
? (item.group.IsEmpty() ? item.text : item.group)
|
||||
: (item.text.StartsWith(group) && !group.EndsWith(' ') ? item.text.substr(group.size()).Trim(false) : item.text);
|
||||
? (item.group_key.IsEmpty() ? item.text : item.group_label)
|
||||
: item.text;
|
||||
size1 = dc.GetMultiLineTextExtent(text);
|
||||
if (group.IsEmpty() && !item.group.IsEmpty())
|
||||
if (group.IsEmpty() && !item.group_key.IsEmpty())
|
||||
size1.x += 5 + arrow_bitmap.GetBmpWidth();
|
||||
}
|
||||
if (item.icon.IsOk()) {
|
||||
@@ -696,7 +696,7 @@ void DropDown::mouseMove(wxMouseEvent &event)
|
||||
int index = hoverIndex();
|
||||
if (index < -1) {
|
||||
auto & drop = *subDropDown;
|
||||
drop.group = items[-index - 2].group;
|
||||
drop.group = items[-index - 2].group_key;
|
||||
drop.need_sync = true;
|
||||
drop.messureSize();
|
||||
drop.autoPosition();
|
||||
|
||||
@@ -26,7 +26,8 @@ public:
|
||||
wxBitmap icon;
|
||||
wxBitmap icon_textctrl;// display icon for TextInput.eg.PrinterInfoBox
|
||||
void * data{nullptr};
|
||||
wxString group{};
|
||||
wxString group_key{};
|
||||
wxString group_label{};
|
||||
wxString alias{};
|
||||
wxString tip{};
|
||||
int flag{0};
|
||||
|
||||
@@ -276,7 +276,7 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url)
|
||||
#ifdef __WIN32__
|
||||
webView->SetUserAgent(wxString::Format("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.52 BBL-Slicer/v%s (%s) BBL-Language/%s",
|
||||
SLIC3R_VERSION, Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
|
||||
Slic3r::GUI::wxGetApp().get_bbl_client_version(), Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
|
||||
webView->Create(parent, wxID_ANY, url2, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||
// We register the wxfs:// protocol for testing purposes
|
||||
webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewArchiveHandler("bbl")));
|
||||
@@ -294,7 +294,7 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url)
|
||||
}
|
||||
webView->Create(parent, wxID_ANY, url2, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||
webView->SetUserAgent(wxString::Format("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) BBL-Slicer/v%s (%s) BBL-Language/%s",
|
||||
SLIC3R_VERSION, Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
|
||||
Slic3r::GUI::wxGetApp().get_bbl_client_version(), Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
|
||||
#endif
|
||||
#ifdef __WXMAC__
|
||||
WKWebView * wkWebView = (WKWebView *) webView->GetNativeBackend();
|
||||
@@ -399,7 +399,7 @@ void WebView::RecreateAll()
|
||||
language_code = language_code.ToStdString();
|
||||
for (auto webView : g_webviews) {
|
||||
webView->SetUserAgent(wxString::Format("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) BBL-Slicer/v%s (%s) BBL-Language/%s",
|
||||
SLIC3R_VERSION, dark ? "dark" : "light", language_code.mb_str()));
|
||||
Slic3r::GUI::wxGetApp().get_bbl_client_version(), dark ? "dark" : "light", language_code.mb_str()));
|
||||
webView->Reload();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user