diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index fd2c4f8651..bf5ecc7f5d 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -122,6 +122,9 @@ void AppConfig::set_defaults() if (get("auto_toolbar_size").empty()) set("auto_toolbar_size", "100"); + + if (get("notify_testing_release").empty()) + set("notify_testing_release", "1"); #if ENABLE_ENVIRONMENT_MAP if (get("use_environment_map").empty()) set("use_environment_map", "0"); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 9c90535c4e..c5471911a2 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -2671,12 +2671,12 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]); const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id]; const std::optional first_vertex = m_seams_detector.get_first_vertex(); - // the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later - if ((new_pos - *first_vertex).squaredNorm() < 0.0625f) { +// // the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later +// if ((new_pos - *first_vertex).squaredNorm() < 0.0625f) { set_end_position(0.5f * (new_pos + *first_vertex)); store_move_vertex(EMoveType::Seam); set_end_position(curr_pos); - } +// } m_seams_detector.activate(false); } diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index e60918fab6..ab99ea5f68 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -47,7 +47,7 @@ private: public: // Forwarded constructor template - inline CachedObject(Setter fn, Args &&... args) + inline CachedObject(Setter &&fn, Args &&... args) : m_obj(std::forward(args)...), m_valid(false), m_setter(fn) {} @@ -55,7 +55,7 @@ public: // the next retrieval (Setter will be called). The data that is used in // the setter function should be guarded as well during modification so // the modification has to take place in fn. - inline void invalidate(std::function fn) + template void invalidate(Fn &&fn) { std::lock_guard lck(m_lck); fn(); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 004f7d5550..1bc5489145 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -138,14 +138,14 @@ Transform3d SLAPrint::sla_trafo(const ModelObject &model_object) const offset(1) = 0.; rotation(2) = 0.; - offset(Z) *= corr(Z); + offset.z() *= corr.z(); auto trafo = Transform3d::Identity(); trafo.translate(offset); trafo.scale(corr); - trafo.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ())); - trafo.rotate(Eigen::AngleAxisd(rotation(1), Vec3d::UnitY())); - trafo.rotate(Eigen::AngleAxisd(rotation(0), Vec3d::UnitX())); + trafo.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ())); + trafo.rotate(Eigen::AngleAxisd(rotation.y(), Vec3d::UnitY())); + trafo.rotate(Eigen::AngleAxisd(rotation.x(), Vec3d::UnitX())); trafo.scale(model_instance.get_scaling_factor()); trafo.scale(model_instance.get_mirror()); diff --git a/src/miniz/CMakeLists.txt b/src/miniz/CMakeLists.txt index a664f74608..04d562b764 100644 --- a/src/miniz/CMakeLists.txt +++ b/src/miniz/CMakeLists.txt @@ -3,29 +3,15 @@ project(miniz) add_library(miniz INTERFACE) -if(NOT SLIC3R_STATIC OR CMAKE_SYSTEM_NAME STREQUAL "Linux") - find_package(miniz 2.1 QUIET) -endif() - -if(miniz_FOUND) - - message(STATUS "Using system miniz...") - target_link_libraries(miniz INTERFACE miniz::miniz) - -else() - - add_library(miniz_static STATIC - miniz.c - miniz.h - ) - - if(${CMAKE_C_COMPILER_ID} STREQUAL "GNU") - target_compile_definitions(miniz_static PRIVATE _GNU_SOURCE) - endif() - - target_link_libraries(miniz INTERFACE miniz_static) - target_include_directories(miniz INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) - - message(STATUS "Miniz NOT found in system, using bundled version...") +add_library(miniz_static STATIC + miniz.c + miniz.h +) +if(${CMAKE_C_COMPILER_ID} STREQUAL "GNU") + target_compile_definitions(miniz_static PRIVATE _GNU_SOURCE) endif() + +target_link_libraries(miniz INTERFACE miniz_static) +target_include_directories(miniz INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 77679a1ad9..d52d8be1c5 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -38,6 +38,25 @@ using GUI::format_wxstr; namespace DoubleSlider { +bool possible_threshold(const double& bottom_area, const double& top_area) +{ + // Check percent of the area decrease. + // This value have to be more than 25 mm2 + return (bottom_area - top_area > min_delta_area()) && + // and more 10% + (top_area / bottom_area < 0.9); +} + +bool equivalent_areas(const double& bottom_area, const double& top_area) +{ + return fabs(bottom_area - top_area <= min_threshold()); +} + +bool overhang(const double& bottom_area, const double& top_area) +{ + return top_area > bottom_area && !equivalent_areas(bottom_area, top_area); +} + wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); static std::string gcode(Type type) @@ -2049,8 +2068,6 @@ void Control::auto_color_change() int extruder = 2; const Print& print = GUI::wxGetApp().plater()->fff_print(); - double delta_area = scale_(scale_(25)); // equal to 25 mm2 - for (auto object : print.objects()) { if (object->layer_count() == 0) continue; @@ -2060,14 +2077,10 @@ void Control::auto_color_change() Layer* layer = object->get_layer(i); double cur_area = area(layer->lslices); - if (cur_area > prev_area && prev_area - cur_area > scale_(scale_(1))) + if (overhang(prev_area, cur_area)) break; - if (prev_area - cur_area > delta_area) { - // Check percent of the area decrease. - // Ignore it, if this value is less than 10% - if (cur_area / prev_area > 0.9) - continue; + if (possible_threshold(prev_area, cur_area)) { int tick = get_tick_from_value(layer->print_z); if (tick >= 0 && !m_ticks.has_tick(tick)) { if (m_mode == SingleExtruder) { diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 0f663f6630..13b7f483d0 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -25,6 +25,13 @@ namespace DoubleSlider { */ constexpr double epsilon() { return 0.0011; } +constexpr double min_delta_area() { return scale_(scale_(25)); } // equal to 25 mm2 +constexpr double min_threshold() { return scale_(scale_(1)); } // equal to 1 mm2 + +bool possible_threshold(const double& bottom_area, const double& top_area); +bool equivalent_areas(const double& bottom_area, const double& top_area); +bool overhang(const double& bottom_area, const double& top_area); + // custom message the slider sends to its parent to notify a tick-change: wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index e2a9df25d5..a210542019 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -865,8 +865,11 @@ bool GUI_App::on_init_inner() wxInitAllImageHandlers(); #ifdef _MSW_DARK_MODE - if (app_config->get("dark_color_mode") == "1") + if (bool dark_mode = app_config->get("dark_color_mode") == "1") { NppDarkMode::InitDarkMode(); + if (dark_mode != NppDarkMode::IsDarkMode()) + NppDarkMode::SetDarkMode(dark_mode); + } #endif SplashScreen* scrn = nullptr; if (app_config->get("show_splash_screen") == "1") { @@ -915,6 +918,25 @@ bool GUI_App::on_init_inner() } } }); + Bind(EVT_SLIC3R_ALPHA_VERSION_ONLINE, [this](const wxCommandEvent& evt) { + //app_config->set("version_alpha_online", into_u8(evt.GetString())); + app_config->save(); + if (this->plater_ != nullptr && app_config->get("notify_testing_release") == "1") { + if (*Semver::parse(SLIC3R_VERSION) < *Semver::parse(into_u8(evt.GetString()))) { + this->plater_->get_notification_manager()->push_notification(NotificationType::NewAlphaAvailable); + } + } + }); + Bind(EVT_SLIC3R_BETA_VERSION_ONLINE, [this](const wxCommandEvent& evt) { + //app_config->set("version_beta_online", into_u8(evt.GetString())); + app_config->save(); + if (this->plater_ != nullptr && app_config->get("notify_testing_release") == "1") { + if (*Semver::parse(SLIC3R_VERSION) < *Semver::parse(into_u8(evt.GetString()))) { + this->plater_->get_notification_manager()->close_notification_of_type(NotificationType::NewAlphaAvailable); + this->plater_->get_notification_manager()->push_notification(NotificationType::NewBetaAvailable); + } + } + }); } else { #ifdef __WXMSW__ diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 57840dda28..5584350199 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -687,7 +687,6 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee if (m_layers_slider->IsNewPrint()) { const Print& print = wxGetApp().plater()->fff_print(); - double delta_area = scale_(scale_(25)); // equal to 25 mm2 //bool is_possible_auto_color_change = false; for (auto object : print.objects()) { @@ -708,7 +707,7 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee int i, min_solid_height = int(0.25 * num_layers); for (i = 1; i <= min_solid_height; ++ i) { double cur_area = area(object->get_layer(i)->lslices); - if (cur_area != bottom_area && fabs(cur_area - bottom_area) > scale_(scale_(1))) { + if (!DoubleSlider::equivalent_areas(bottom_area, cur_area)) { // but due to the elephant foot compensation, the first layer may be slightly smaller than the others if (i == 1 && fabs(cur_area - bottom_area) / bottom_area < 0.1) { // So, let process this case and use second layer as a bottom @@ -725,7 +724,7 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee double prev_area = area(object->get_layer(i)->lslices); for ( i++; i < num_layers; i++) { double cur_area = area(object->get_layer(i)->lslices); - if (cur_area > prev_area && prev_area - cur_area > scale_(scale_(1))) + if (DoubleSlider::overhang(prev_area, cur_area)) break; prev_area = cur_area; } @@ -733,7 +732,7 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee continue; double top_area = area(object->get_layer(int(object->layers().size()) - 1)->lslices); - if( bottom_area - top_area > delta_area) { + if (DoubleSlider::possible_threshold(bottom_area, top_area)) { NotificationManager *notif_mngr = wxGetApp().plater()->get_notification_manager(); notif_mngr->push_notification( NotificationType::SignDetected, NotificationManager::NotificationLevel::PrintInfoNotificationLevel, diff --git a/src/slic3r/GUI/GalleryDialog.cpp b/src/slic3r/GUI/GalleryDialog.cpp index 6230f1c02a..fef131b887 100644 --- a/src/slic3r/GUI/GalleryDialog.cpp +++ b/src/slic3r/GUI/GalleryDialog.cpp @@ -66,7 +66,7 @@ bool GalleryDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& f GalleryDialog::GalleryDialog(wxWindow* parent, bool modify_gallery/* = false*/) : - DPIDialog(parent, wxID_ANY, _L("Shape Gallery"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + DPIDialog(parent, wxID_ANY, _L("Shape Gallery"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { #ifndef _WIN32 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); @@ -75,7 +75,7 @@ GalleryDialog::GalleryDialog(wxWindow* parent, bool modify_gallery/* = false*/) wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Select shape from the gallery") + ":"); - m_list_ctrl = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(55 * wxGetApp().em_unit(), 35 * wxGetApp().em_unit()), + m_list_ctrl = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(50 * wxGetApp().em_unit(), 35 * wxGetApp().em_unit()), wxLC_ICON | wxSIMPLE_BORDER); m_list_ctrl->Bind(wxEVT_LIST_ITEM_SELECTED, &GalleryDialog::select, this); m_list_ctrl->Bind(wxEVT_LIST_ITEM_DESELECTED, &GalleryDialog::deselect, this); @@ -152,7 +152,7 @@ void GalleryDialog::on_dpi_changed(const wxRect& suggested_rect) msw_buttons_rescale(this, em, { ID_BTN_ADD_CUSTOM_SHAPE, ID_BTN_DEL_CUSTOM_SHAPE, ID_BTN_REPLACE_CUSTOM_PNG, wxID_OK, wxID_CLOSE }); - wxSize size = wxSize(55 * em, 35 * em); + wxSize size = wxSize(50 * em, 35 * em); m_list_ctrl->SetMinSize(size); m_list_ctrl->SetSize(size); @@ -461,8 +461,11 @@ void GalleryDialog::replace_custom_png(wxEvent& event) } try { + fs::path png_path = fs::path(get_dir(false) / m_selected_items[0].name); + png_path.replace_extension("png"); + fs::path current = fs::path(into_u8(input_files.Item(0))); - fs::copy_file(current, get_dir(false) / (m_selected_items[0].name + ".png"), fs::copy_option::overwrite_if_exists); + fs::copy_file(current, png_path, fs::copy_option::overwrite_if_exists); } catch (fs::filesystem_error const& e) { std::cerr << e.what() << '\n'; @@ -535,12 +538,12 @@ bool GalleryDialog::load_files(const wxArrayString& input_files) if (!fs::exists(dest_dir / current.filename())) fs::copy_file(current, dest_dir / current.filename()); else { - std::string filename = current.filename().string(); + std::string filename = current.stem().string(); int file_idx = 0; for (auto& dir_entry : fs::directory_iterator(dest_dir)) if (is_gallery_file(dir_entry, ".stl") || is_gallery_file(dir_entry, ".obj")) { - std::string name = dir_entry.path().filename().string(); + std::string name = dir_entry.path().stem().string(); if (filename == name) { if (file_idx == 0) file_idx++; diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index ed6f11ddb9..b12256cf06 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -43,6 +43,10 @@ const NotificationManager::NotificationData NotificationManager::basic_notificat }, {NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) { wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }}, + {NotificationType::NewAlphaAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New alpha release is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) { + wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }}, + {NotificationType::NewBetaAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New beta release is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) { + wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }}, {NotificationType::EmptyColorChangeCode, NotificationLevel::PrintInfoNotificationLevel, 10, _u8L("You have just added a G-code for color change, but its value is empty.\n" "To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") }, diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index ad2e315b72..00065f795e 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -52,6 +52,9 @@ enum class NotificationType // Notification on the start of PrusaSlicer, when a new PrusaSlicer version is published. // Contains a hyperlink to open a web browser pointing to the PrusaSlicer download location. NewAppAvailable, + // Like NewAppAvailable but with text and link for alpha / bet release + NewAlphaAvailable, + NewBetaAvailable, // Notification on the start of PrusaSlicer, when updates of system profiles are detected. // Contains a hyperlink to execute installation of the new system profiles. PresetUpdateAvailable, diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 9d12369227..e7ecb0547f 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -375,6 +375,13 @@ void PreferencesDialog::build(size_t selected_tab) def.set_default_value(new ConfigOptionBool{ app_config->get("use_custom_toolbar_size") == "1" }); option = Option(def, "use_custom_toolbar_size"); m_optgroup_gui->append_single_option_line(option); + + def.label = L("Notify about testing releases"); + def.type = coBool; + def.tooltip = L("If enabled, you will be notified about alpha / beta releases available for download."); + def.set_default_value(new ConfigOptionBool{ app_config->get("notify_testing_release") == "1" }); + option = Option(def, "notify_testing_release"); + m_optgroup_gui->append_single_option_line(option); } activate_options_tab(m_optgroup_gui); diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 97c6cb2a50..f20ddb147d 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -141,7 +141,8 @@ struct Updates wxDEFINE_EVENT(EVT_SLIC3R_VERSION_ONLINE, wxCommandEvent); - +wxDEFINE_EVENT(EVT_SLIC3R_ALPHA_VERSION_ONLINE, wxCommandEvent); +wxDEFINE_EVENT(EVT_SLIC3R_BETA_VERSION_ONLINE, wxCommandEvent); struct PresetUpdater::priv { @@ -262,21 +263,58 @@ void PresetUpdater::priv::sync_version() const }) .on_complete([&](std::string body, unsigned /* http_status */) { boost::trim(body); - const auto nl_pos = body.find_first_of("\n\r"); - if (nl_pos != std::string::npos) { - body.resize(nl_pos); - } - - if (! Semver::parse(body)) { - BOOST_LOG_TRIVIAL(warning) << format("Received invalid contents from `%1%`: Not a correct semver: `%2%`", SLIC3R_APP_NAME, body); + // release version + std::string version; + const auto first_nl_pos = body.find_first_of("\n\r"); + if (first_nl_pos != std::string::npos) + version = body.substr(0,first_nl_pos); + else + version = body; + if (! Semver::parse(version)) { + BOOST_LOG_TRIVIAL(warning) << format("Received invalid contents from `%1%`: Not a correct semver: `%2%`", SLIC3R_APP_NAME, version); return; } - - BOOST_LOG_TRIVIAL(info) << format("Got %1% online version: `%2%`. Sending to GUI thread...", SLIC3R_APP_NAME, body); - + BOOST_LOG_TRIVIAL(info) << format("Got %1% online version: `%2%`. Sending to GUI thread...", SLIC3R_APP_NAME, version); wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_VERSION_ONLINE); - evt->SetString(GUI::from_u8(body)); + evt->SetString(GUI::from_u8(version)); GUI::wxGetApp().QueueEvent(evt); + + // alpha / beta version + size_t nexn_nl_pos = first_nl_pos; + while (nexn_nl_pos != std::string::npos && body.size() > nexn_nl_pos + 1) { + const auto last_nl_pos = nexn_nl_pos; + nexn_nl_pos = body.find_first_of("\n\r", last_nl_pos + 1); + std::string line; + if (nexn_nl_pos == std::string::npos) + line = body.substr(last_nl_pos + 1); + else + line = body.substr(last_nl_pos + 1, nexn_nl_pos - last_nl_pos - 1); + + // alpha + if (line.substr(0, 6) == "alpha=") { + version = line.substr(6); + if (!Semver::parse(version)) { + BOOST_LOG_TRIVIAL(warning) << format("Received invalid contents for alpha release from `%1%`: Not a correct semver: `%2%`", SLIC3R_APP_NAME, version); + return; + } + BOOST_LOG_TRIVIAL(info) << format("Got %1% online version of alpha release: `%2%`. Sending to GUI thread...", SLIC3R_APP_NAME, version); + wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_ALPHA_VERSION_ONLINE); + evt->SetString(GUI::from_u8(version)); + GUI::wxGetApp().QueueEvent(evt); + + // beta + } else if (line.substr(0, 5) == "beta=") { + version = line.substr(5); + if (!Semver::parse(version)) { + BOOST_LOG_TRIVIAL(warning) << format("Received invalid contents for beta release from `%1%`: Not a correct semver: `%2%`", SLIC3R_APP_NAME, version); + return; + } + BOOST_LOG_TRIVIAL(info) << format("Got %1% online version of beta release: `%2%`. Sending to GUI thread...", SLIC3R_APP_NAME, version); + wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_BETA_VERSION_ONLINE); + evt->SetString(GUI::from_u8(version)); + GUI::wxGetApp().QueueEvent(evt); + } + } }) .perform_sync(); } diff --git a/src/slic3r/Utils/PresetUpdater.hpp b/src/slic3r/Utils/PresetUpdater.hpp index d7eeb5604b..b7937c5748 100644 --- a/src/slic3r/Utils/PresetUpdater.hpp +++ b/src/slic3r/Utils/PresetUpdater.hpp @@ -61,7 +61,7 @@ private: }; wxDECLARE_EVENT(EVT_SLIC3R_VERSION_ONLINE, wxCommandEvent); - - +wxDECLARE_EVENT(EVT_SLIC3R_ALPHA_VERSION_ONLINE, wxCommandEvent); +wxDECLARE_EVENT(EVT_SLIC3R_BETA_VERSION_ONLINE, wxCommandEvent); } #endif