diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index d04d470f6..c06f0ccdb 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -105,15 +105,6 @@ sub OnInit { $self->{preset_updater} = Slic3r::PresetUpdater->new($VERSION_ONLINE_EVENT); Slic3r::GUI::set_preset_updater($self->{preset_updater}); - eval { - if (! $self->{preset_updater}->config_update()) { - exit 0; - } - }; - if ($@) { - warn $@ . "\n"; - fatal_error(undef, $@); - } Slic3r::GUI::load_language(); @@ -137,6 +128,7 @@ sub OnInit { ); $self->SetTopWindow($frame); + # This makes CallAfter() work EVT_IDLE($self->{mainframe}, sub { while (my $cb = shift @cb) { $cb->(); @@ -144,8 +136,21 @@ sub OnInit { $self->{app_config}->save if $self->{app_config}->dirty; }); - # On OSX the UI was not initialized correctly if the wizard was called - # before the UI was up and running. + # On OS X the UI tends to freeze in weird ways if modal dialogs (config wizard, update notifications, ...) + # are shown before or in the same event callback with the main frame creation. + # Therefore we schedule them for later using CallAfter. + $self->CallAfter(sub { + eval { + if (! $self->{preset_updater}->config_update()) { + exit 0; + } + }; + if ($@) { + warn $@ . "\n"; + fatal_error(undef, $@); + } + }); + $self->CallAfter(sub { if (! Slic3r::GUI::config_wizard_startup($app_conf_exists)) { # Only notify if there was not wizard so as not to bother too much ... diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 8946b8765..d0d03dc6c 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -29,7 +29,7 @@ if(WIN32) # -D_ITERATOR_DEBUG_LEVEL) endif() -add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE) +add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE -DWXINTL_NO_GETTEXT_MACRO) add_library(libslic3r STATIC ${LIBDIR}/libslic3r/BoundingBox.cpp diff --git a/xs/src/libslic3r/GCodeSender.cpp b/xs/src/libslic3r/GCodeSender.cpp index bbeaf836d..c3530e00f 100644 --- a/xs/src/libslic3r/GCodeSender.cpp +++ b/xs/src/libslic3r/GCodeSender.cpp @@ -41,6 +41,7 @@ struct termios2 { //#define DEBUG_SERIAL #ifdef DEBUG_SERIAL +#include #include std::fstream fs; #endif @@ -52,7 +53,11 @@ namespace Slic3r { GCodeSender::GCodeSender() : io(), serial(io), can_send(false), sent(0), open(false), error(false), connected(false), queue_paused(false) -{} +{ +#ifdef DEBUG_SERIAL + std::srand(std::time(nullptr)); +#endif +} GCodeSender::~GCodeSender() { @@ -358,15 +363,23 @@ GCodeSender::on_read(const boost::system::error_code& error, // extract the first number from line boost::algorithm::trim_left_if(line, !boost::algorithm::is_digit()); size_t toresend = boost::lexical_cast(line.substr(0, line.find_first_not_of("0123456789"))); - ++ toresend; // N is 0-based - if (toresend >= this->sent - this->last_sent.size() && toresend < this->last_sent.size()) { + +#ifdef DEBUG_SERIAL + fs << "!! line num out of sync: toresend = " << toresend << ", sent = " << sent << ", last_sent.size = " << last_sent.size() << std::endl; +#endif + + if (toresend > this->sent - this->last_sent.size() && toresend <= this->sent) { { boost::lock_guard l(this->queue_mutex); + const auto lines_to_resend = this->sent - toresend + 1; +#ifdef DEBUG_SERIAL + fs << "!! resending " << lines_to_resend << " lines" << std::endl; +#endif // move the unsent lines to priqueue this->priqueue.insert( this->priqueue.begin(), // insert at the beginning - this->last_sent.begin() + toresend - (this->sent - this->last_sent.size()) - 1, + this->last_sent.begin() + this->last_sent.size() - lines_to_resend, this->last_sent.end() ); @@ -477,8 +490,14 @@ GCodeSender::do_send() if (line.empty()) return; // compute full line - std::string full_line = "N" + boost::lexical_cast(this->sent) + " " + line; ++ this->sent; +#ifndef DEBUG_SERIAL + const auto line_num = this->sent; +#else + // In DEBUG_SERIAL mode, test line re-synchronization by sending bad line number 1/4 of the time + const auto line_num = std::rand() < RAND_MAX/4 ? 0 : this->sent; +#endif + std::string full_line = "N" + boost::lexical_cast(line_num) + " " + line; // calculate checksum int cs = 0; @@ -497,8 +516,9 @@ GCodeSender::do_send() this->last_sent.push_back(line); this->can_send = false; - if (this->last_sent.size() > KEEP_SENT) - this->last_sent.erase(this->last_sent.begin(), this->last_sent.end() - KEEP_SENT); + while (this->last_sent.size() > KEEP_SENT) { + this->last_sent.pop_front(); + } // we can't supply boost::asio::buffer(full_line) to async_write() because full_line is on the // stack and the buffer would lose its underlying storage causing memory corruption diff --git a/xs/src/libslic3r/GCodeSender.hpp b/xs/src/libslic3r/GCodeSender.hpp index 3022993cb..d7663ca55 100644 --- a/xs/src/libslic3r/GCodeSender.hpp +++ b/xs/src/libslic3r/GCodeSender.hpp @@ -51,7 +51,7 @@ class GCodeSender : private boost::noncopyable { bool can_send; bool queue_paused; size_t sent; - std::vector last_sent; + std::deque last_sent; // this mutex guards log, T, B mutable boost::mutex log_mutex; diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index d42fd3538..3a8f2163f 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -308,6 +308,8 @@ enum ConfigMenuIDs { ConfigMenuCnt, }; +static wxString dots("…", wxConvUTF8); + void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change) { auto local_menu = new wxMenu(); @@ -315,12 +317,12 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), ConfigWizard::name()); // Cmd+, is standard on OS X - what about other operating systems? - local_menu->Append(config_id_base + ConfigMenuWizard, ConfigWizard::name() + "\u2026", config_wizard_tooltip); - local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("Configuration Snapshots"))+"\u2026", _(L("Inspect / activate configuration snapshots"))); + local_menu->Append(config_id_base + ConfigMenuWizard, ConfigWizard::name() + dots, config_wizard_tooltip); + local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("Configuration Snapshots"))+dots, _(L("Inspect / activate configuration snapshots"))); local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration Snapshot")), _(L("Capture a configuration snapshot"))); // local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates"))); local_menu->AppendSeparator(); - local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences"))+"\u2026\tCtrl+,", _(L("Application preferences"))); + local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences"))+dots+"\tCtrl+,", _(L("Application preferences"))); local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language"))); local_menu->AppendSeparator(); local_menu->Append(config_id_base + ConfigMenuFlashFirmware, _(L("Flash printer firmware")), _(L("Upload a firmware image into an Arduino based printer"))); @@ -356,8 +358,7 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *g_AppConfig).id); g_PresetBundle->load_presets(*g_AppConfig); // Load the currently selected preset into the GUI, update the preset selection box. - for (Tab *tab : g_tabs_list) - tab->load_current_preset(); + load_current_presets(); } } break; @@ -451,9 +452,8 @@ void config_wizard(int reason) show_error(nullptr, e.what()); } - // Load the currently selected preset into the GUI, update the preset selection box. - for (Tab *tab : g_tabs_list) - tab->load_current_preset(); + // Load the currently selected preset into the GUI, update the preset selection box. + load_current_presets(); } void open_preferences_dialog(int event_preferences) @@ -605,6 +605,13 @@ void add_created_tab(Tab* panel) g_wxTabPanel->AddPage(panel, panel->title()); } +void load_current_presets() +{ + for (Tab *tab : g_tabs_list) { + tab->load_current_preset(); + } +} + void show_error(wxWindow* parent, const wxString& message) { ErrorDialog msg(parent, message); msg.ShowModal(); @@ -841,7 +848,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl Line line = { "", "" }; line.widget = [config](wxWindow* parent){ - g_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + "\u2026", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + g_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(g_wiping_dialog_button); g_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e) diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 4deee1910..2dc18ae3b 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -5,6 +5,9 @@ #include #include "Config.hpp" +#include +#include + class wxApp; class wxWindow; class wxFrame; @@ -30,6 +33,12 @@ class PresetUpdater; class DynamicPrintConfig; class TabIface; +#define _(s) Slic3r::translate((s)) +inline wxString translate(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)); } +inline wxString translate(const wchar_t *s) { return wxGetTranslation(s); } +inline wxString translate(const std::string &s) { return wxGetTranslation(wxString(s.c_str(), wxConvUTF8)); } +inline wxString translate(const std::wstring &s) { return wxGetTranslation(s.c_str()); } + // !!! If you needed to translate some wxString, // !!! please use _(L(string)) // !!! _() - is a standard wxWidgets macro to translate @@ -116,6 +125,9 @@ void add_created_tab(Tab* panel); // Change option value in config void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0); +// Update UI / Tabs to reflect changes in the currently loaded presets +void load_current_presets(); + void show_error(wxWindow* parent, const wxString& message); void show_error_id(int id, const std::string& message); // For Perl void show_info(wxWindow* parent, const wxString& message, const wxString& title); diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 98d351069..d36ef7b6f 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -101,6 +101,7 @@ PresetBundle::~PresetBundle() void PresetBundle::reset(bool delete_files) { // Clear the existing presets, delete their respective files. + this->vendors.clear(); this->prints .reset(delete_files); this->filaments.reset(delete_files); this->printers .reset(delete_files); diff --git a/xs/src/slic3r/GUI/RammingChart.cpp b/xs/src/slic3r/GUI/RammingChart.cpp index 97a6b7712..2603a5eab 100644 --- a/xs/src/slic3r/GUI/RammingChart.cpp +++ b/xs/src/slic3r/GUI/RammingChart.cpp @@ -2,12 +2,7 @@ #include #include "RammingChart.hpp" - - -//! macro used to mark string used at localization, -//! return same string -#define L(s) s - +#include "GUI.hpp" wxDEFINE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent); @@ -83,7 +78,7 @@ void Chart::draw() { int text_height = 0; dc.GetTextExtent(label,&text_width,&text_height); dc.DrawText(label,wxPoint(0.5*(m_rect.GetRight()+m_rect.GetLeft())-text_width/2.f, m_rect.GetBottom()+25)); - label = _(L("Volumetric speed")) + " (" + _(L("mm")) + "\u00B3/" + _(L("s")) + ")"; + label = _(L("Volumetric speed")) + " (" + _(L("mm")) + wxString("³/", wxConvUTF8) + _(L("s")) + ")"; dc.GetTextExtent(label,&text_width,&text_height); dc.DrawRotatedText(label,wxPoint(0,0.5*(m_rect.GetBottom()+m_rect.GetTop())+text_width/2.f),90); } diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index ab5aa2bb0..f24b5ebc6 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -32,6 +32,8 @@ namespace Slic3r { namespace GUI { +static wxString dots("…", wxConvUTF8); + // sub new void Tab::create_preset_tab(PresetBundle *preset_bundle) { @@ -1261,7 +1263,7 @@ void TabFilament::build() optgroup->append_single_option_line("filament_density"); optgroup->append_single_option_line("filament_cost"); - optgroup = page->new_optgroup(_(L("Temperature ")) +" (\u00B0C)"); // degree sign + optgroup = page->new_optgroup(_(L("Temperature ")) + wxString("°C", wxConvUTF8)); Line line = { _(L("Extruder")), "" }; line.append_option(optgroup->get_option("first_layer_temperature")); line.append_option(optgroup->get_option("temperature")); @@ -1319,7 +1321,7 @@ void TabFilament::build() optgroup->append_single_option_line("filament_toolchange_delay"); line = { _(L("Ramming")), "" }; line.widget = [this](wxWindow* parent){ - auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(ramming_dialog_btn); @@ -1441,7 +1443,7 @@ void TabPrinter::build() Line line{ _(L("Bed shape")), "" }; line.widget = [this](wxWindow* parent){ - auto btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); + auto btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); // btn->SetFont(Slic3r::GUI::small_font); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG)); @@ -1540,7 +1542,7 @@ void TabPrinter::build() optgroup = page->new_optgroup(_(L("OctoPrint upload"))); auto octoprint_host_browse = [this, optgroup] (wxWindow* parent) { - auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); @@ -1589,7 +1591,7 @@ void TabPrinter::build() Line cafile_line = optgroup->create_single_option_line("octoprint_cafile"); auto octoprint_cafile_browse = [this, optgroup] (wxWindow* parent) { - auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); @@ -2211,7 +2213,7 @@ void Tab::update_ui_from_settings() wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn) { *checkbox = new wxCheckBox(parent, wxID_ANY, _(L("All"))); - *btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); + *btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); (*btn)->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG)); diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp index 4b25ccd3a..eef4017c1 100644 --- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp @@ -1,15 +1,10 @@ #include #include #include "WipeTowerDialog.hpp" +#include "GUI.hpp" #include -//! macro used to mark string used at localization, -//! return same string -#define L(s) s - - - RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters) : wxDialog(parent, wxID_ANY, _(L("Ramming customization")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/) { @@ -81,7 +76,7 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters) auto gsizer_param = new wxFlexGridSizer(2, 5, 15); gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total ramming time")) + " (" + _(L("s")) + "):")), 0, wxALIGN_CENTER_VERTICAL); gsizer_param->Add(m_widget_time); - gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total rammed volume")) + " (" + _(L("mm")) + "\u00B3):")), 0, wxALIGN_CENTER_VERTICAL); + gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total rammed volume")) + " (" + _(L("mm")) + wxString("³):", wxConvUTF8))), 0, wxALIGN_CENTER_VERTICAL); gsizer_param->Add(m_widget_volume); gsizer_param->AddSpacer(20); gsizer_param->AddSpacer(20); @@ -220,7 +215,7 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector& matrix, con // collect and format sizer format_sizer(m_sizer_advanced, m_page_advanced, m_gridsizer_advanced, - wxString::Format(_(L("Here you can adjust required purging volume (mm%s) for any given pair of tools.")), "\u00B3"), + _(L("Here you can adjust required purging volume (mm³) for any given pair of tools.")), _(L("Extruder changed to"))); // Hide preview page before new page creating @@ -243,7 +238,7 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector& matrix, con // collect and format sizer format_sizer(m_sizer_simple, m_page_simple, gridsizer_simple, _(L("Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded.")), - wxString::Format(_(L("Volume to purge (mm%s) when the filament is being")), "\u00B3"), 50); + _(L("Volume to purge (mm³) when the filament is being")), 50); m_sizer = new wxBoxSizer(wxVERTICAL); m_sizer->Add(m_page_simple, 0, wxEXPAND | wxALL, 25); diff --git a/xs/src/slic3r/Utils/PresetUpdater.cpp b/xs/src/slic3r/Utils/PresetUpdater.cpp index 9a4d1048f..dd46c43fe 100644 --- a/xs/src/slic3r/Utils/PresetUpdater.cpp +++ b/xs/src/slic3r/Utils/PresetUpdater.cpp @@ -162,7 +162,7 @@ bool PresetUpdater::priv::get_file(const std::string &url, const fs::path &targe % http_status % error; }) - .on_complete([&](std::string body, unsigned http_status) { + .on_complete([&](std::string body, unsigned /* http_status */) { fs::fstream file(tmp_path, std::ios::out | std::ios::binary | std::ios::trunc); file.write(body.c_str(), body.size()); file.close(); @@ -204,7 +204,7 @@ void PresetUpdater::priv::sync_version() const % http_status % error; }) - .on_complete([&](std::string body, unsigned http_status) { + .on_complete([&](std::string body, unsigned /* http_status */) { boost::trim(body); BOOST_LOG_TRIVIAL(info) << boost::format("Got Slic3rPE online version: `%1%`. Sending to GUI thread...") % body; wxCommandEvent* evt = new wxCommandEvent(version_online_event); @@ -553,6 +553,12 @@ bool PresetUpdater::config_update() const if (res == wxID_OK) { BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update"; p->perform_updates(std::move(updates)); + + // Reload global configuration + auto *app_config = GUI::get_app_config(); + app_config->reset_selections(); + GUI::get_preset_bundle()->load_presets(*app_config); + GUI::load_current_presets(); } else { BOOST_LOG_TRIVIAL(info) << "User refused the update"; }