OrcaSlicer/src/slic3r/GUI/MainFrame.cpp
Ocraftyone 25a055491e
Update wxWidgets to v3.2.1 (#2905)
* Upgrade wxWidgets to 3.2.1
Based on prusa3d/PrusaSlicer@9a7e024

Co-authored-by: tamasmeszaros <meszaros.q@gmail.com>

* Implement BitmapCache

* update wxExtensions while keeping legacy items

* update dc.DrawBitmap calls to use get_bitmap

* Fix GetSize/Width/Height calls

* update BitmapComboBox

* fix ifndef in wxExtensions.hpp

* update my todos to OcraftyoneTODO

* Get to a compilable state
Everything seems to be working (including the plater). I am not seeing any graphical issues

* fix extruder color icons

* fix crash on opening support tab

* remove GetBmpSize method from DropDown.cpp

* Update TextInput to use bitmap bundles

* update a TODO after testing

* fix the rendering of the icons on combobox

* fix a few todos

* fix WipeTowerDialog.cpp

* Overhaul WipeTowerDialog

Removed simple version of the dialog since BBS removed the functionality but left the code.
Center the table (only seen when the table is smaller than the minimum size of the dialog)
Fix issue where editing a value causes the m_min_flush_label to change colors slightly
Fix an issue where changing a value or running an auto calc changes the disabled value from "-" to "0"

* update a few todos

* Update some todos

* Show dropdown when editing is started

* Update NanoSVG.cmake

Update NanoSVG to work with PR #2780

* Dim the icon on ComboBox when disabled

* solve ObjectDataViewModel todos

leaving colPrint and colEditing cases alone as it does not seem to impact anything

* Update names in wxExtensions

-Rename msw_rescale to sys_color_changed
-Replace GetBmpSize, GetBmpWidth, GetBmpHeight with renamed version (same name without "Bmp")

Both of these changes were also made by PrusaSlicer.

Original Commit: Prusa3D/PrusaSlicer@066b567
Co-authored-by: YuSanka <yusanka@gmail.com>

* update BitmapCache::from_svg

disable finding bundle in the cache to match load_svg
update to match values used in load_svg

* Update ScalableButton

change the signature and remove functions/vars pertaining to a default bmp
fix TODOs in ScalableButton

Original Commit: Prusa3D/PrusaSlicer@066b567
Co-authored-by: YuSanka <yusanka@gmail.com>

* fix up some more todos in wxExtensions

* update ScalableBitmap to use bmp bundles

use wxBitmapBundle by default
add flag to use old scaled bitmap function (specifically to solve issue with advanced toggle)

* attempt to fix macos deps build

* fix ubuntu build

* Revert "attempt to fix macos deps build"

Mistakenly made change to wrong file

This reverts commit d9c20b5121.

* update wxWidgets patch

an attempt to fix macOS build

* Remove duplicate variable from OrcaSlicer.cpp

* Fix macOS build issue

* Fix blank DataViewItem being added to objects list

* Filament ComboBox editor updates

-Add show drop down feature to ObjectTable
-Call finish editing when ComboBox is closed in ObjectList

* remove Apple specific declarations missed during refactor

* delete old wxWidgets patch

* fix ubuntu seg fault

* include patch from #2926

* update patch to include wxWidgets/wxWidgets@991a74c

* fix deps not compiling on Windows

* update WipeTowerDialog

relocates the recalculate button back to its previous position
changes the wording of the tip message label
add spacing below the matrix

* finish patching wxWidgets

from prusa3d/PrusaSlicer@f8477d1 and prusa3d/PrusaSlicer@066b567

Co-authored-by: YuSanka <yusanka@gmail.com>

* fix combobox crash

* revert outside plate changes

---------

Co-authored-by: tamasmeszaros <meszaros.q@gmail.com>
Co-authored-by: YuSanka <yusanka@gmail.com>
2023-12-04 15:21:49 +00:00

3755 lines
147 KiB
C++

#include "MainFrame.hpp"
#include <wx/panel.h>
#include <wx/notebook.h>
#include <wx/listbook.h>
#include <wx/simplebook.h>
#include <wx/icon.h>
#include <wx/sizer.h>
#include <wx/menu.h>
#include <wx/progdlg.h>
#include <wx/tooltip.h>
//#include <wx/glcanvas.h>
#include <wx/filename.h>
#include <wx/debug.h>
#include <wx/utils.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/log/trivial.hpp>
#include "libslic3r/Print.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "Tab.hpp"
#include "ProgressStatusBar.hpp"
#include "3DScene.hpp"
#include "ParamsDialog.hpp"
#include "PrintHostDialogs.hpp"
#include "wxExtensions.hpp"
#include "GUI_ObjectList.hpp"
#include "Mouse3DController.hpp"
//#include "RemovableDriveManager.hpp"
#include "InstanceCheck.hpp"
#include "I18N.hpp"
#include "GLCanvas3D.hpp"
#include "Plater.hpp"
#include "WebViewDialog.hpp"
#include "../Utils/Process.hpp"
#include "format.hpp"
// BBS
#include "PartPlate.hpp"
#include "Preferences.hpp"
#include "Widgets/ProgressDialog.hpp"
#include "BindDialog.hpp"
#include "../Utils/MacDarkMode.hpp"
#include <fstream>
#include <string_view>
#include "GUI_App.hpp"
#include "UnsavedChangesDialog.hpp"
#include "MsgDialog.hpp"
#include "Notebook.hpp"
#include "GUI_Factories.hpp"
#include "GUI_ObjectList.hpp"
#include "NotificationManager.hpp"
#include "MarkdownTip.hpp"
#include "NetworkTestDialog.hpp"
#include "ConfigWizard.hpp"
#include "Widgets/WebView.hpp"
#include "DailyTips.hpp"
#ifdef _WIN32
#include <dbt.h>
#include <shlobj.h>
#endif // _WIN32
#include <slic3r/GUI/CreatePresetsDialog.hpp>
namespace Slic3r {
namespace GUI {
wxDEFINE_EVENT(EVT_SELECT_TAB, wxCommandEvent);
wxDEFINE_EVENT(EVT_HTTP_ERROR, wxCommandEvent);
wxDEFINE_EVENT(EVT_USER_LOGIN, wxCommandEvent);
wxDEFINE_EVENT(EVT_USER_LOGIN_HANDLE, wxCommandEvent);
wxDEFINE_EVENT(EVT_CHECK_PRIVACY_VER, wxCommandEvent);
wxDEFINE_EVENT(EVT_CHECK_PRIVACY_SHOW, wxCommandEvent);
wxDEFINE_EVENT(EVT_SHOW_IP_DIALOG, wxCommandEvent);
wxDEFINE_EVENT(EVT_SET_SELECTED_MACHINE, wxCommandEvent);
wxDEFINE_EVENT(EVT_UPDATE_PRESET_CB, SimpleEvent);
// BBS: backup
wxDEFINE_EVENT(EVT_BACKUP_POST, wxCommandEvent);
wxDEFINE_EVENT(EVT_LOAD_URL, wxCommandEvent);
wxDEFINE_EVENT(EVT_LOAD_PRINTER_URL, LoadPrinterViewEvent);
enum class ERescaleTarget
{
Mainframe,
SettingsDialog
};
#ifdef __APPLE__
class OrcaSlicerTaskBarIcon : public wxTaskBarIcon
{
public:
OrcaSlicerTaskBarIcon(wxTaskBarIconType iconType = wxTBI_DEFAULT_TYPE) : wxTaskBarIcon(iconType) {}
wxMenu *CreatePopupMenu() override {
wxMenu *menu = new wxMenu;
//if (wxGetApp().app_config->get("single_instance") == "false") {
// Only allow opening a new PrusaSlicer instance on OSX if "single_instance" is disabled,
// as starting new instances would interfere with the locking mechanism of "single_instance" support.
append_menu_item(menu, wxID_ANY, _L("New Window"), _L("Open a new window"),
[](wxCommandEvent&) { start_new_slicer(); }, "", nullptr);
//}
// append_menu_item(menu, wxID_ANY, _L("G-code Viewer") + dots, _L("Open G-code Viewer"),
// [](wxCommandEvent&) { start_new_gcodeviewer_open_file(); }, "", nullptr);
return menu;
}
};
/*class GCodeViewerTaskBarIcon : public wxTaskBarIcon
{
public:
GCodeViewerTaskBarIcon(wxTaskBarIconType iconType = wxTBI_DEFAULT_TYPE) : wxTaskBarIcon(iconType) {}
wxMenu *CreatePopupMenu() override {
wxMenu *menu = new wxMenu;
append_menu_item(menu, wxID_ANY, _L("Open PrusaSlicer"), _L("Open a new PrusaSlicer"),
[](wxCommandEvent&) { start_new_slicer(nullptr, true); }, "", nullptr);
//append_menu_item(menu, wxID_ANY, _L("G-code Viewer") + dots, _L("Open new G-code Viewer"),
// [](wxCommandEvent&) { start_new_gcodeviewer_open_file(); }, "", nullptr);
return menu;
}
};*/
#endif // __APPLE__
// Load the icon either from the exe, or from the ico file.
static wxIcon main_frame_icon(GUI_App::EAppMode app_mode)
{
#if _WIN32
std::wstring path(size_t(MAX_PATH), wchar_t(0));
int len = int(::GetModuleFileName(nullptr, path.data(), MAX_PATH));
if (len > 0 && len < MAX_PATH) {
path.erase(path.begin() + len, path.end());
//BBS: remove GCodeViewer as seperate APP logic
/*if (app_mode == GUI_App::EAppMode::GCodeViewer) {
// Only in case the slicer was started with --gcodeviewer parameter try to load the icon from prusa-gcodeviewer.exe
// Otherwise load it from the exe.
for (const std::wstring_view exe_name : { std::wstring_view(L"prusa-slicer.exe"), std::wstring_view(L"prusa-slicer-console.exe") })
if (boost::iends_with(path, exe_name)) {
path.erase(path.end() - exe_name.size(), path.end());
path += L"prusa-gcodeviewer.exe";
break;
}
}*/
}
return wxIcon(path, wxBITMAP_TYPE_ICO);
#else // _WIN32
return wxIcon(Slic3r::var("OrcaSlicer_128px.png"), wxBITMAP_TYPE_PNG);
#endif // _WIN32
}
// BBS
#ifndef __APPLE__
#define BORDERLESS_FRAME_STYLE (wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxCLOSE_BOX)
#else
#define BORDERLESS_FRAME_STYLE (wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxCLOSE_BOX)
#endif
wxDEFINE_EVENT(EVT_SYNC_CLOUD_PRESET, SimpleEvent);
#ifdef __APPLE__
static const wxString ctrl = ("Ctrl+");
#else
static const wxString ctrl = _L("Ctrl+");
#endif
MainFrame::MainFrame() :
DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_STYLE, "mainframe")
, m_printhost_queue_dlg(new PrintHostQueueDialog(this))
// BBS
, m_recent_projects(18)
, m_settings_dialog(this)
, diff_dialog(this)
{
#ifdef __WXOSX__
set_miniaturizable(GetHandle());
#endif
if (!wxGetApp().app_config->has("user_mode")) {
wxGetApp().app_config->set("user_mode", "simple");
wxGetApp().app_config->set_bool("developer_mode", false);
wxGetApp().app_config->save();
}
wxGetApp().app_config->set_bool("internal_developer_mode", false);
wxString max_recent_count_str = wxGetApp().app_config->get("max_recent_count");
long max_recent_count = 18;
if (max_recent_count_str.ToLong(&max_recent_count))
set_max_recent_count((int)max_recent_count);
//reset log level
auto loglevel = wxGetApp().app_config->get("severity_level");
Slic3r::set_logging_level(Slic3r::level_string_to_boost(loglevel));
// BBS
m_recent_projects.SetMenuPathStyle(wxFH_PATH_SHOW_ALWAYS);
MarkdownTip::Recreate(this);
// Fonts were created by the DPIFrame constructor for the monitor, on which the window opened.
wxGetApp().update_fonts(this);
#ifndef __APPLE__
m_topbar = new BBLTopbar(this);
#else
auto panel_topbar = new wxPanel(this, wxID_ANY);
panel_topbar->SetBackgroundColour(wxColour(38, 46, 48));
auto sizer_tobar = new wxBoxSizer(wxVERTICAL);
panel_topbar->SetSizer(sizer_tobar);
panel_topbar->Layout();
#endif
//wxAuiToolBar* toolbar = new wxAuiToolBar();
/*
#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
this->SetFont(this->normal_font());
#endif
// Font is already set in DPIFrame constructor
*/
#ifdef __APPLE__
m_reset_title_text_colour_timer = new wxTimer();
m_reset_title_text_colour_timer->SetOwner(this);
Bind(wxEVT_TIMER, [this](auto& e) {
set_title_colour_after_set_title(GetHandle());
m_reset_title_text_colour_timer->Stop();
});
this->Bind(wxEVT_FULLSCREEN, [this](wxFullScreenEvent& e) {
set_tag_when_enter_full_screen(e.IsFullScreen());
if (!e.IsFullScreen()) {
if (m_reset_title_text_colour_timer) {
m_reset_title_text_colour_timer->Stop();
m_reset_title_text_colour_timer->Start(500);
}
}
e.Skip();
});
#endif
#ifdef __APPLE__
// Initialize the docker task bar icon.
switch (wxGetApp().get_app_mode()) {
default:
case GUI_App::EAppMode::Editor:
m_taskbar_icon = std::make_unique<OrcaSlicerTaskBarIcon>(wxTBI_DOCK);
m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("OrcaSlicer-mac_256px.ico"), wxBITMAP_TYPE_ICO), "OrcaSlicer");
break;
case GUI_App::EAppMode::GCodeViewer:
break;
}
#endif // __APPLE__
// Load the icon either from the exe, or from the ico file.
SetIcon(main_frame_icon(wxGetApp().get_app_mode()));
// initialize tabpanel and menubar
init_tabpanel();
if (wxGetApp().is_gcode_viewer())
init_menubar_as_gcodeviewer();
else
init_menubar_as_editor();
// BBS
#if 0
// This is needed on Windows to fake the CTRL+# of the window menu when using the numpad
wxAcceleratorEntry entries[6];
entries[0].Set(wxACCEL_CTRL, WXK_NUMPAD1, wxID_HIGHEST + 1);
entries[1].Set(wxACCEL_CTRL, WXK_NUMPAD2, wxID_HIGHEST + 2);
entries[2].Set(wxACCEL_CTRL, WXK_NUMPAD3, wxID_HIGHEST + 3);
entries[3].Set(wxACCEL_CTRL, WXK_NUMPAD4, wxID_HIGHEST + 4);
entries[4].Set(wxACCEL_CTRL, WXK_NUMPAD5, wxID_HIGHEST + 5);
entries[5].Set(wxACCEL_CTRL, WXK_NUMPAD6, wxID_HIGHEST + 6);
wxAcceleratorTable accel(6, entries);
SetAcceleratorTable(accel);
#endif // _WIN32
// BBS
//wxAcceleratorEntry entries[13];
//int index = 0;
//entries[index++].Set(wxACCEL_CTRL, (int)'N', wxID_HIGHEST + wxID_NEW);
//entries[index++].Set(wxACCEL_CTRL, (int)'O', wxID_HIGHEST + wxID_OPEN);
//entries[index++].Set(wxACCEL_CTRL, (int)'S', wxID_HIGHEST + wxID_SAVE);
//entries[index++].Set(wxACCEL_CTRL | wxACCEL_SHIFT, (int)'S', wxID_HIGHEST + wxID_SAVEAS);
//entries[index++].Set(wxACCEL_CTRL, (int)'X', wxID_HIGHEST + wxID_CUT);
////entries[index++].Set(wxACCEL_CTRL, (int)'I', wxID_HIGHEST + wxID_ADD);
//entries[index++].Set(wxACCEL_CTRL, (int)'A', wxID_HIGHEST + wxID_SELECTALL);
//entries[index++].Set(wxACCEL_NORMAL, (int)27 /* escape */, wxID_HIGHEST + wxID_CANCEL);
//entries[index++].Set(wxACCEL_CTRL, (int)'Z', wxID_HIGHEST + wxID_UNDO);
//entries[index++].Set(wxACCEL_CTRL, (int)'Y', wxID_HIGHEST + wxID_REDO);
//entries[index++].Set(wxACCEL_CTRL, (int)'C', wxID_HIGHEST + wxID_COPY);
//entries[index++].Set(wxACCEL_CTRL, (int)'V', wxID_HIGHEST + wxID_PASTE);
//entries[index++].Set(wxACCEL_CTRL, (int)'P', wxID_HIGHEST + wxID_PREFERENCES);
//entries[index++].Set(wxACCEL_CTRL, (int)'I', wxID_HIGHEST + wxID_FILE6);
//wxAcceleratorTable accel(sizeof(entries) / sizeof(entries[0]), entries);
//SetAcceleratorTable(accel);
//Bind(wxEVT_MENU, [this](wxCommandEvent&) { m_plater->new_project(); }, wxID_HIGHEST + wxID_NEW);
//Bind(wxEVT_MENU, [this](wxCommandEvent&) { m_plater->load_project(); }, wxID_HIGHEST + wxID_OPEN);
//// BBS: close save project
//Bind(wxEVT_MENU, [this](wxCommandEvent&) { if (m_plater) m_plater->save_project(); }, wxID_HIGHEST + wxID_SAVE);
//Bind(wxEVT_MENU, [this](wxCommandEvent&) { if (m_plater) m_plater->save_project(true); }, wxID_HIGHEST + wxID_SAVEAS);
////Bind(wxEVT_MENU, [this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, wxID_HIGHEST + wxID_ADD);
////Bind(wxEVT_MENU, [this](wxCommandEvent&) { m_plater->remove_selected(); }, wxID_HIGHEST + wxID_DELETE);
//Bind(wxEVT_MENU, [this](wxCommandEvent&) {
// if (!can_add_models())
// return;
// if (m_plater) {
// m_plater->add_model();
// }
// }, wxID_HIGHEST + wxID_FILE6);
//Bind(wxEVT_MENU, [this](wxCommandEvent&) { m_plater->select_all(); }, wxID_HIGHEST + wxID_SELECTALL);
//Bind(wxEVT_MENU, [this](wxCommandEvent&) { m_plater->deselect_all(); }, wxID_HIGHEST + wxID_CANCEL);
//Bind(wxEVT_MENU, [this](wxCommandEvent&) {
// if (m_plater->is_view3D_shown())
// m_plater->undo();
// }, wxID_HIGHEST + wxID_UNDO);
//Bind(wxEVT_MENU, [this](wxCommandEvent&) {
// if (m_plater->is_view3D_shown())
// m_plater->redo();
// }, wxID_HIGHEST + wxID_REDO);
//Bind(wxEVT_MENU, [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); }, wxID_HIGHEST + wxID_COPY);
//Bind(wxEVT_MENU, [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); }, wxID_HIGHEST + wxID_PASTE);
//Bind(wxEVT_MENU, [this](wxCommandEvent&) { m_plater->cut_selection_to_clipboard(); }, wxID_HIGHEST + wxID_CUT);
Bind(wxEVT_SIZE, [this](wxSizeEvent&) {
BOOST_LOG_TRIVIAL(trace) << "mainframe: size changed, is maximized = " << this->IsMaximized();
#ifndef __APPLE__
if (this->IsMaximized()) {
m_topbar->SetWindowSize();
} else {
m_topbar->SetMaximizedSize();
}
#endif
Refresh();
Layout();
});
//BBS
Bind(EVT_SELECT_TAB, [this](wxCommandEvent&evt) {
TabPosition pos = (TabPosition)evt.GetInt();
m_tabpanel->SetSelection(pos);
});
Bind(EVT_SYNC_CLOUD_PRESET, &MainFrame::on_select_default_preset, this);
// Bind(wxEVT_MENU,
// [this](wxCommandEvent&)
// {
// PreferencesDialog dlg(this);
// dlg.ShowModal();
//#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
// if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
//#else
// if (dlg.seq_top_layer_only_changed())
//#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
// plater()->refresh_print();
// }, wxID_HIGHEST + wxID_PREFERENCES);
// set default tooltip timer in msec
// SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values
// (SetAutoPop is not available on GTK.)
wxToolTip::SetAutoPop(32767);
m_loaded = true;
// initialize layout
m_main_sizer = new wxBoxSizer(wxVERTICAL);
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
#ifndef __APPLE__
sizer->Add(m_topbar, 0, wxEXPAND);
#else
sizer->Add(panel_topbar, 0, wxEXPAND);
#endif // __WINDOWS__
sizer->Add(m_main_sizer, 1, wxEXPAND);
SetSizerAndFit(sizer);
// initialize layout from config
update_layout();
sizer->SetSizeHints(this);
#ifdef WIN32
auto setMaxSize = [this]() {
wxDisplay display(this);
auto size = display.GetClientArea().GetSize();
HWND hWnd = GetHandle();
RECT borderThickness;
SetRectEmpty(&borderThickness);
AdjustWindowRectEx(&borderThickness, GetWindowLongPtr(hWnd, GWL_STYLE), FALSE, 0);
SetMaxSize(size + wxSize{-borderThickness.left + borderThickness.right, -borderThickness.top + borderThickness.bottom});
};
this->Bind(wxEVT_DPI_CHANGED, [setMaxSize](auto & e) {
setMaxSize();
e.Skip();
});
setMaxSize();
// SetMaximize already position window at left/top corner, even if Windows Task Bar is at left side.
// Not known why, but fix it here
this->Bind(wxEVT_MAXIMIZE, [this](auto &e) {
wxDisplay display(this);
auto pos = display.GetClientArea().GetPosition();
HWND hWnd = GetHandle();
RECT borderThickness;
SetRectEmpty(&borderThickness);
AdjustWindowRectEx(&borderThickness, GetWindowLongPtr(hWnd, GWL_STYLE), FALSE, 0);
Move(pos + wxPoint{borderThickness.left, borderThickness.top});
e.Skip();
});
#endif // WIN32
// BBS
Fit();
const wxSize min_size = wxGetApp().get_min_size(); //wxSize(76*wxGetApp().em_unit(), 49*wxGetApp().em_unit());
SetMinSize(min_size/*wxSize(760, 490)*/);
SetSize(wxSize(FromDIP(1200), FromDIP(800)));
Layout();
update_title();
// declare events
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ": mainframe received close_widow event";
if (event.CanVeto() && m_plater->get_view3D_canvas3D()->get_gizmos_manager().is_in_editing_mode(true)) {
// prevents to open the save dirty project dialog
event.Veto();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< "cancelled by gizmo in editing";
return;
}
//BBS:
//if (event.CanVeto() && !wxGetApp().check_and_save_current_preset_changes(_L("Application is closing"), _L("Closing Application while some presets are modified."))) {
// event.Veto();
// return;
//}
auto check = [](bool yes_or_no) {
if (yes_or_no)
return true;
return wxGetApp().check_and_save_current_preset_changes(_L("Application is closing"), _L("Closing Application while some presets are modified."));
};
// BBS: close save project
int result;
if (event.CanVeto() && ((result = m_plater->close_with_confirm(check)) == wxID_CANCEL)) {
event.Veto();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< "cancelled by close_with_confirm selection";
return;
}
if (event.CanVeto() && !wxGetApp().check_print_host_queue()) {
event.Veto();
return;
}
#if 0 // BBS
//if (m_plater != nullptr) {
// int saved_project = m_plater->save_project_if_dirty(_L("Closing Application. Current project is modified."));
// if (saved_project == wxID_CANCEL) {
// event.Veto();
// return;
// }
// // check unsaved changes only if project wasn't saved
// else if (plater()->is_project_dirty() && saved_project == wxID_NO && event.CanVeto() &&
// (plater()->is_presets_dirty() && !wxGetApp().check_and_save_current_preset_changes(_L("Application is closing"), _L("Closing Application while some presets are modified.")))) {
// event.Veto();
// return;
// }
//}
#endif
MarkdownTip::ExitTip();
m_plater->reset();
this->shutdown();
// propagate event
wxGetApp().remove_mall_system_dialog();
event.Skip();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ": mainframe finished process close_widow event";
});
//FIXME it seems this method is not called on application start-up, at least not on Windows. Why?
// The same applies to wxEVT_CREATE, it is not being called on startup on Windows.
Bind(wxEVT_ACTIVATE, [this](wxActivateEvent& event) {
if (m_plater != nullptr && event.GetActive())
m_plater->on_activate();
event.Skip();
});
// OSX specific issue:
// When we move application between Retina and non-Retina displays, The legend on a canvas doesn't redraw
// So, redraw explicitly canvas, when application is moved
//FIXME maybe this is useful for __WXGTK3__ as well?
#if __APPLE__
Bind(wxEVT_MOVE, [](wxMoveEvent& event) {
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
event.Skip();
});
#endif
update_ui_from_settings(); // FIXME (?)
if (m_plater != nullptr) {
// BBS
update_slice_print_status(eEventSliceUpdate, true, true);
// BBS: backup project
if (wxGetApp().app_config->get("backup_switch") == "true") {
std::string backup_interval;
if (!wxGetApp().app_config->get("app", "backup_interval", backup_interval))
backup_interval = "10";
Slic3r::set_backup_interval(boost::lexical_cast<long>(backup_interval));
} else {
Slic3r::set_backup_interval(0);
}
Slic3r::set_backup_callback([this](int action) {
if (action == 0) {
wxPostEvent(this, wxCommandEvent(EVT_BACKUP_POST));
}
else if (action == 1) {
if (!m_plater->up_to_date(false, true)) {
m_plater->export_3mf(m_plater->model().get_backup_path() + "/.3mf", SaveStrategy::Backup);
m_plater->up_to_date(true, true);
}
}
});
Bind(EVT_BACKUP_POST, [](wxCommandEvent& e) {
Slic3r::run_backup_ui_tasks();
});
; }
this->Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent &evt) {
#ifdef __APPLE__
if (evt.CmdDown() && (evt.GetKeyCode() == 'H')) {
//call parent_menu hide behavior
return;}
if (evt.CmdDown() && (evt.GetKeyCode() == 'M')) {
this->Iconize();
return;
}
if (evt.CmdDown() && evt.GetKeyCode() == 'Q') { wxPostEvent(this, wxCloseEvent(wxEVT_CLOSE_WINDOW)); return;}
if (evt.CmdDown() && evt.RawControlDown() && evt.GetKeyCode() == 'F') {
EnableFullScreenView(true);
if (IsFullScreen()) {
ShowFullScreen(false);
} else {
ShowFullScreen(true);
}
return;}
#endif
if (evt.CmdDown() && evt.GetKeyCode() == 'R') { if (m_slice_enable) { wxGetApp().plater()->update(true, true); wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SLICE_PLATE)); this->m_tabpanel->SetSelection(tpPreview); } return; }
if (evt.CmdDown() && evt.ShiftDown() && evt.GetKeyCode() == 'G') {
m_plater->apply_background_progress();
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
if (m_print_enable) {
if (wxGetApp().preset_bundle->is_bbl_vendor())
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_PRINT_PLATE));
else
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SEND_GCODE));
}
evt.Skip();
return;
}
else if (evt.CmdDown() && evt.GetKeyCode() == 'G') { if (can_export_gcode()) { wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_SLICED_FILE)); } evt.Skip(); return; }
if (evt.CmdDown() && evt.GetKeyCode() == 'J') { m_printhost_queue_dlg->Show(); return; }
if (evt.CmdDown() && evt.GetKeyCode() == 'N') { m_plater->new_project(); return;}
if (evt.CmdDown() && evt.GetKeyCode() == 'O') { m_plater->load_project(); return;}
if (evt.CmdDown() && evt.ShiftDown() && evt.GetKeyCode() == 'S') { if (can_save_as()) m_plater->save_project(true); return;}
else if (evt.CmdDown() && evt.GetKeyCode() == 'S') { if (can_save()) m_plater->save_project(); return;}
if (evt.CmdDown() && evt.GetKeyCode() == 'F') {
if (m_plater && (m_tabpanel->GetSelection() == TabPosition::tp3DEditor || m_tabpanel->GetSelection() == TabPosition::tpPreview)) {
m_plater->sidebar().can_search();
}
}
#ifdef __APPLE__
if (evt.CmdDown() && evt.GetKeyCode() == ',')
#else
if (evt.CmdDown() && evt.GetKeyCode() == 'P')
#endif
{
PreferencesDialog dlg(this);
dlg.ShowModal();
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
#else
if (dlg.seq_top_layer_only_changed())
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
plater()->refresh_print();
return;
}
if (evt.CmdDown() && evt.GetKeyCode() == 'I') {
if (!can_add_models()) return;
if (m_plater) { m_plater->add_file(); }
return;
}
evt.Skip();
});
#ifdef _MSW_DARK_MODE
wxGetApp().UpdateDarkUIWin(this);
#endif // _MSW_DARK_MODE
wxGetApp().persist_window_geometry(this, true);
wxGetApp().persist_window_geometry(&m_settings_dialog, true);
}
#ifdef __WIN32__
WXLRESULT MainFrame::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
{
/* When we have a custom titlebar in the window, we don't need the non-client area of a normal window
* to be painted. In order to achieve this, we handle the "WM_NCCALCSIZE" which is responsible for the
* size of non-client area of a window and set the return value to 0. Also we have to tell the
* application to not paint this area on activate and deactivation events so we also handle
* "WM_NCACTIVATE" message. */
switch (nMsg) {
case WM_NCACTIVATE: {
/* Returning 0 from this message disable the window from receiving activate events which is not
desirable. However When a visual style is not active (?) for this window, "lParam" is a handle to an
optional update region for the nonclient area of the window. If this parameter is set to -1,
DefWindowProc does not repaint the nonclient area to reflect the state change. */
lParam = -1;
break;
}
/* To remove the standard window frame, you must handle the WM_NCCALCSIZE message, specifically when
its wParam value is TRUE and the return value is 0 */
case WM_NCCALCSIZE:
if (wParam) {
HWND hWnd = GetHandle();
/* Detect whether window is maximized or not. We don't need to change the resize border when win is
* maximized because all resize borders are gone automatically */
WINDOWPLACEMENT wPos;
// GetWindowPlacement fail if this member is not set correctly.
wPos.length = sizeof(wPos);
GetWindowPlacement(hWnd, &wPos);
if (wPos.showCmd != SW_SHOWMAXIMIZED) {
RECT borderThickness;
SetRectEmpty(&borderThickness);
AdjustWindowRectEx(&borderThickness, GetWindowLongPtr(hWnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
borderThickness.left *= -1;
borderThickness.top *= -1;
NCCALCSIZE_PARAMS *sz = reinterpret_cast<NCCALCSIZE_PARAMS *>(lParam);
// Add 1 pixel to the top border to make the window resizable from the top border
sz->rgrc[0].top += 1; // borderThickness.top;
sz->rgrc[0].left += borderThickness.left;
sz->rgrc[0].right -= borderThickness.right;
sz->rgrc[0].bottom -= borderThickness.bottom;
return 0;
}
}
break;
}
return wxFrame::MSWWindowProc(nMsg, wParam, lParam);
}
#endif
void MainFrame::show_log_window()
{
m_log_window = new wxLogWindow(this, _L("Logging"), true, false);
m_log_window->Show();
}
//BBS GUI refactor: remove unused layout new/dlg
void MainFrame::update_layout()
{
auto restore_to_creation = [this]() {
auto clean_sizer = [](wxSizer* sizer) {
while (!sizer->GetChildren().IsEmpty()) {
sizer->Detach(0);
}
};
// On Linux m_plater needs to be removed from m_tabpanel before to reparent it
int plater_page_id = m_tabpanel->FindPage(m_plater);
if (plater_page_id != wxNOT_FOUND)
m_tabpanel->RemovePage(plater_page_id);
if (m_plater->GetParent() != this)
m_plater->Reparent(this);
if (m_tabpanel->GetParent() != this)
m_tabpanel->Reparent(this);
plater_page_id = (m_plater_page != nullptr) ? m_tabpanel->FindPage(m_plater_page) : wxNOT_FOUND;
if (plater_page_id != wxNOT_FOUND) {
m_tabpanel->DeletePage(plater_page_id);
m_plater_page = nullptr;
}
clean_sizer(m_main_sizer);
clean_sizer(m_settings_dialog.GetSizer());
if (m_settings_dialog.IsShown())
m_settings_dialog.Close();
m_tabpanel->Hide();
m_plater->Hide();
Layout();
};
//BBS GUI refactor: remove unused layout new/dlg
//ESettingsLayout layout = wxGetApp().is_gcode_viewer() ? ESettingsLayout::GCodeViewer : ESettingsLayout::Old;
ESettingsLayout layout = ESettingsLayout::Old;
if (m_layout == layout)
return;
wxBusyCursor busy;
Freeze();
// Remove old settings
if (m_layout != ESettingsLayout::Unknown)
restore_to_creation();
ESettingsLayout old_layout = m_layout;
m_layout = layout;
// From the very beginning the Print settings should be selected
//m_last_selected_tab = m_layout == ESettingsLayout::Dlg ? 0 : 1;
m_last_selected_tab = 1;
// Set new settings
switch (m_layout)
{
case ESettingsLayout::Old:
{
m_plater->Reparent(m_tabpanel);
m_tabpanel->InsertPage(tp3DEditor, m_plater, _L("Prepare"), std::string("tab_3d_active"), std::string("tab_3d_active"), false);
m_tabpanel->InsertPage(tpPreview, m_plater, _L("Preview"), std::string("tab_preview_active"), std::string("tab_preview_active"), false);
m_main_sizer->Add(m_tabpanel, 1, wxEXPAND | wxTOP, 0);
m_tabpanel->Bind(wxCUSTOMEVT_NOTEBOOK_SEL_CHANGED, [this](wxCommandEvent& evt)
{
// jump to 3deditor under preview_only mode
if (evt.GetId() == tp3DEditor){
m_plater->update(true);
if (!preview_only_hint())
return;
}
evt.Skip();
});
m_plater->Show();
m_tabpanel->Show();
break;
}
case ESettingsLayout::GCodeViewer:
{
m_main_sizer->Add(m_plater, 1, wxEXPAND);
//BBS: add bed exclude area
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, {}, 0.0, {}, {}, true);
m_plater->get_collapse_toolbar().set_enabled(false);
m_plater->collapse_sidebar(true);
m_plater->Show();
break;
}
default:
break;
}
//BBS GUI refactor: remove unused layout new/dlg
//#ifdef __APPLE__
// // Using SetMinSize() on Mac messes up the window position in some cases
// // cf. https://groups.google.com/forum/#!topic/wx-users/yUKPBBfXWO0
// // So, if we haven't possibility to set MinSize() for the MainFrame,
// // set the MinSize() as a half of regular for the m_plater and m_tabpanel, when settings layout is in slNew mode
// // Otherwise, MainFrame will be maximized by height
// if (m_layout == ESettingsLayout::New) {
// wxSize size = wxGetApp().get_min_size();
// size.SetHeight(int(0.5 * size.GetHeight()));
// m_plater->SetMinSize(size);
// m_tabpanel->SetMinSize(size);
// }
//#endif
#ifdef __APPLE__
m_plater->sidebar().change_top_border_for_mode_sizer(m_layout != ESettingsLayout::Old);
#endif
Layout();
Thaw();
}
// Called when closing the application and when switching the application language.
void MainFrame::shutdown()
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "MainFrame::shutdown enter";
// BBS: backup
Slic3r::set_backup_callback(nullptr);
#ifdef _WIN32
if (m_hDeviceNotify) {
::UnregisterDeviceNotification(HDEVNOTIFY(m_hDeviceNotify));
m_hDeviceNotify = nullptr;
}
if (m_ulSHChangeNotifyRegister) {
SHChangeNotifyDeregister(m_ulSHChangeNotifyRegister);
m_ulSHChangeNotifyRegister = 0;
}
#endif // _WIN32
if (m_plater != nullptr) {
m_plater->stop_jobs();
// Unbinding of wxWidgets event handling in canvases needs to be done here because on MAC,
// when closing the application using Command+Q, a mouse event is triggered after this lambda is completed,
// causing a crash
m_plater->unbind_canvas_event_handlers();
// Cleanup of canvases' volumes needs to be done here or a crash may happen on some Linux Debian flavours
m_plater->reset_canvas_volumes();
}
// Weird things happen as the Paint messages are floating around the windows being destructed.
// Avoid the Paint messages by hiding the main window.
// Also the application closes much faster without these unnecessary screen refreshes.
// In addition, there were some crashes due to the Paint events sent to already destructed windows.
this->Show(false);
if (m_settings_dialog.IsShown())
// call Close() to trigger call to lambda defined into GUI_App::persist_window_geometry()
m_settings_dialog.Close();
if (m_plater != nullptr) {
// Stop the background thread (Windows and Linux).
// Disconnect from a 3DConnextion driver (OSX).
m_plater->get_mouse3d_controller().shutdown();
// Store the device parameter database back to appconfig.
m_plater->get_mouse3d_controller().save_config(*wxGetApp().app_config);
}
// stop agent
NetworkAgent* agent = wxGetApp().getAgent();
if (agent)
agent->track_enable(false);
// Stop the background thread of the removable drive manager, so that no new updates will be sent to the Plater.
//wxGetApp().removable_drive_manager()->shutdown();
//stop listening for messages from other instances
//wxGetApp().other_instance_message_handler()->shutdown(this);
// Save the slic3r.ini.Usually the ini file is saved from "on idle" callback,
// but in rare cases it may not have been called yet.
if(wxGetApp().app_config->dirty())
wxGetApp().app_config->save();
// if (m_plater)
// m_plater->print = undef;
// Slic3r::GUI::deregister_on_request_update_callback();
// set to null tabs and a plater
// to avoid any manipulations with them from App->wxEVT_IDLE after of the mainframe closing
wxGetApp().tabs_list.clear();
wxGetApp().model_tabs_list.clear();
wxGetApp().shutdown();
// BBS: why clear ?
//wxGetApp().plater_ = nullptr;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "MainFrame::shutdown exit";
}
void MainFrame::update_filament_tab_ui()
{
wxGetApp().get_tab(Preset::Type::TYPE_FILAMENT)->reload_config();
wxGetApp().get_tab(Preset::Type::TYPE_FILAMENT)->update_dirty();
wxGetApp().get_tab(Preset::Type::TYPE_FILAMENT)->update_tab_ui();
}
void MainFrame::update_title()
{
return;
}
void MainFrame::show_publish_button(bool show)
{
// m_publish_btn->Show(show);
// Layout();
}
void MainFrame::show_calibration_button(bool show)
{
// #ifdef __APPLE__
// bool shown = m_menubar->FindMenu(_L("Calibration")) != wxNOT_FOUND;
// if (shown == show)
// ;
// else if (show)
// m_menubar->Insert(3, m_calib_menu, wxString::Format("&%s", _L("Calibration")));
// else
// m_menubar->Remove(3);
// #else
// topbar()->ShowCalibrationButton(show);
// #endif
show = !show;
auto shown2 = m_tabpanel->FindPage(m_calibration) != wxNOT_FOUND;
if (shown2 == show)
;
else if (show)
m_tabpanel->InsertPage(tpCalibration, m_calibration, _L("Calibration"), std::string("tab_monitor_active"), std::string("tab_monitor_active"), false);
else
m_tabpanel->RemovePage(tpCalibration);
}
void MainFrame::update_title_colour_after_set_title()
{
#ifdef __APPLE__
set_title_colour_after_set_title(GetHandle());
#endif
}
void MainFrame::show_option(bool show)
{
if (!this) { return; }
if (!show) {
if (m_slice_btn->IsShown()) {
m_slice_btn->Hide();
m_print_btn->Hide();
m_slice_option_btn->Hide();
m_print_option_btn->Hide();
Layout();
}
} else {
if (!m_slice_btn->IsShown()) {
m_slice_btn->Show();
m_print_btn->Show();
m_slice_option_btn->Show();
m_print_option_btn->Show();
Layout();
}
}
}
void MainFrame::init_tabpanel() {
// wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on
// Windows 10 with multiple high resolution displays connected.
// BBS
wxBoxSizer *side_tools = create_side_tools();
m_tabpanel = new Notebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, side_tools,
wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME);
m_tabpanel->SetBackgroundColour(*wxWHITE);
#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font());
#endif
m_tabpanel->Hide();
m_settings_dialog.set_tabpanel(m_tabpanel);
m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGING, [this](wxBookCtrlEvent &e) {
int old_sel = e.GetOldSelection();
int new_sel = e.GetSelection();
if (wxGetApp().preset_bundle && wxGetApp().preset_bundle->is_bbl_vendor() && new_sel == tpMonitor) {
if (!wxGetApp().getAgent()) {
e.Veto();
BOOST_LOG_TRIVIAL(info) << boost::format("skipped tab switch from %1% to %2%, lack of network plugins") % old_sel % new_sel;
if (m_plater) {
wxCommandEvent *evt = new wxCommandEvent(EVT_INSTALL_PLUGIN_HINT);
wxQueueEvent(m_plater, evt);
}
}
} else {
if (new_sel == tpMonitor && wxGetApp().preset_bundle != nullptr) {
auto cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
wxString url = cfg.opt_string("print_host_webui").empty() ? cfg.opt_string("print_host") : cfg.opt_string("print_host_webui");
if (url.empty()) {
wxString url = wxString::Format("file://%s/web/orca/missing_connection.html", from_u8(resources_dir()));
m_printer_view->load_url(url);
}
}
}
});
#ifdef __WXMSW__
m_tabpanel->Bind(wxEVT_BOOKCTRL_PAGE_CHANGED, [this](wxBookCtrlEvent& e) {
#else
m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxBookCtrlEvent& e) {
#endif
//BBS
wxWindow* panel = m_tabpanel->GetCurrentPage();
int sel = m_tabpanel->GetSelection();
//wxString page_text = m_tabpanel->GetPageText(sel);
m_last_selected_tab = m_tabpanel->GetSelection();
if (panel == m_plater) {
if (sel == tp3DEditor) {
wxPostEvent(m_plater, SimpleEvent(EVT_GLVIEWTOOLBAR_3D));
m_param_panel->OnActivate();
}
else if (sel == tpPreview) {
wxPostEvent(m_plater, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW));
m_param_panel->OnActivate();
}
}
//else if (panel == m_param_panel)
// m_param_panel->OnActivate();
else if (panel == m_monitor) {
//monitor
}
#ifndef __APPLE__
if (sel == tp3DEditor) {
m_topbar->EnableUndoRedoItems();
}
else {
m_topbar->DisableUndoRedoItems();
}
#endif
if (panel)
panel->SetFocus();
/*switch (sel) {
case TabPosition::tpHome:
show_option(false);
break;
case TabPosition::tp3DEditor:
show_option(true);
break;
case TabPosition::tpPreview:
show_option(true);
break;
case TabPosition::tpMonitor:
show_option(false);
break;
default:
show_option(false);
break;
}*/
});
if (wxGetApp().is_editor()) {
m_webview = new WebViewPanel(m_tabpanel);
Bind(EVT_LOAD_URL, [this](wxCommandEvent &evt) {
wxString url = evt.GetString();
select_tab(MainFrame::tpHome);
m_webview->load_url(url);
});
m_tabpanel->AddPage(m_webview, "", "tab_home_active", "tab_home_active", false);
m_param_panel = new ParamsPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
}
m_plater = new Plater(this, this);
m_plater->SetBackgroundColour(*wxWHITE);
m_plater->Hide();
wxGetApp().plater_ = m_plater;
create_preset_tabs();
//BBS add pages
m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_monitor->SetBackgroundColour(*wxWHITE);
m_tabpanel->AddPage(m_monitor, _L("Device"), std::string("tab_monitor_active"), std::string("tab_monitor_active"), false);
m_printer_view = new PrinterWebView(m_tabpanel);
Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent &evt) {
wxString url = evt.GetString();
wxString key = evt.GetAPIkey();
//select_tab(MainFrame::tpMonitor);
m_printer_view->load_url(url, key);
});
m_printer_view->Hide();
m_project = new ProjectPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_project->SetBackgroundColour(*wxWHITE);
m_tabpanel->AddPage(m_project, _L("Project"), std::string("tab_auxiliary_avtice"), std::string("tab_auxiliary_avtice"), false);
m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_calibration->SetBackgroundColour(*wxWHITE);
m_tabpanel->AddPage(m_calibration, _L("Calibration"), std::string("tab_monitor_active"), std::string("tab_monitor_active"), false);
if (m_plater) {
// load initial config
auto full_config = wxGetApp().preset_bundle->full_config();
m_plater->on_config_change(full_config);
// Show a correct number of filament fields.
// nozzle_diameter is undefined when SLA printer is selected
// BBS
if (full_config.has("filament_colour")) {
m_plater->on_filaments_change(full_config.option<ConfigOptionStrings>("filament_colour")->values.size());
}
}
}
// SoftFever
void MainFrame::show_device(bool bBBLPrinter) {
if (m_tabpanel->GetPage(tpMonitor) != m_monitor &&
m_tabpanel->GetPage(tpMonitor) != m_printer_view) {
BOOST_LOG_TRIVIAL(error) << "Failed to find device tab";
return;
}
if (bBBLPrinter) {
if (m_tabpanel->GetPage(tpMonitor) != m_monitor) {
m_printer_view->Hide();
m_monitor->Show(true);
m_tabpanel->RemovePage(tpMonitor);
m_tabpanel->InsertPage(tpMonitor, m_monitor, _L("Device"),
std::string("tab_monitor_active"),
std::string("tab_monitor_active"));
//m_tabpanel->SetSelection(tp3DEditor);
}
} else {
if (m_tabpanel->GetPage(tpMonitor) != m_printer_view) {
m_printer_view->Show();
m_monitor->Show(false);
m_tabpanel->RemovePage(tpMonitor);
m_tabpanel->InsertPage(tpMonitor, m_printer_view, _L("Device"),
std::string("tab_monitor_active"),
std::string("tab_monitor_active"));
//m_tabpanel->SetSelection(tp3DEditor);
}
}
}
bool MainFrame::preview_only_hint()
{
if (m_plater && (m_plater->only_gcode_mode() || (m_plater->using_exported_file()))) {
BOOST_LOG_TRIVIAL(info) << boost::format("skipped tab switch from %1% to %2% in preview mode")%m_tabpanel->GetSelection() %tp3DEditor;
ConfirmBeforeSendDialog confirm_dlg(this, wxID_ANY, _L("Warning"));
confirm_dlg.Bind(EVT_SECONDARY_CHECK_CONFIRM, [this](wxCommandEvent& e) {
preview_only_to_editor = true;
});
confirm_dlg.update_btn_label(_L("Yes"), _L("No"));
auto filename = m_plater->get_preview_only_filename();
confirm_dlg.update_text(filename + " " + _L("will be closed before creating a new model. Do you want to continue?"));
confirm_dlg.on_show();
if (preview_only_to_editor) {
m_plater->new_project();
preview_only_to_editor = false;
}
return false;
}
return true;
}
#ifdef WIN32
void MainFrame::register_win32_callbacks()
{
//static GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED };
//static GUID GUID_DEVINTERFACE_DISK = { 0x53f56307, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b };
//static GUID GUID_DEVINTERFACE_VOLUME = { 0x71a27cdd, 0x812a, 0x11d0, 0xbe, 0xc7, 0x08, 0x00, 0x2b, 0xe2, 0x09, 0x2f };
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 };
// Register USB HID (Human Interface Devices) notifications to trigger the 3DConnexion enumeration.
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = { 0 };
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_HID;
m_hDeviceNotify = ::RegisterDeviceNotification(this->GetHWND(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
// or register for file handle change?
// DEV_BROADCAST_HANDLE NotificationFilter = { 0 };
// NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
// NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
// Using Win32 Shell API to register for media insert / removal events.
LPITEMIDLIST ppidl;
if (SHGetSpecialFolderLocation(this->GetHWND(), CSIDL_DESKTOP, &ppidl) == NOERROR) {
SHChangeNotifyEntry shCNE;
shCNE.pidl = ppidl;
shCNE.fRecursive = TRUE;
// Returns a positive integer registration identifier (ID).
// Returns zero if out of memory or in response to invalid parameters.
m_ulSHChangeNotifyRegister = SHChangeNotifyRegister(this->GetHWND(), // Hwnd to receive notification
SHCNE_DISKEVENTS, // Event types of interest (sources)
SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED,
//SHCNE_UPDATEITEM, // Events of interest - use SHCNE_ALLEVENTS for all events
WM_USER_MEDIACHANGED, // Notification message to be sent upon the event
1, // Number of entries in the pfsne array
&shCNE); // Array of SHChangeNotifyEntry structures that
// contain the notifications. This array should
// always be set to one when calling SHChnageNotifyRegister
// or SHChangeNotifyDeregister will not work properly.
assert(m_ulSHChangeNotifyRegister != 0); // Shell notification failed
} else {
// Failed to get desktop location
assert(false);
}
{
static constexpr int device_count = 1;
RAWINPUTDEVICE devices[device_count] = { 0 };
// multi-axis mouse (SpaceNavigator, etc.)
devices[0].usUsagePage = 0x01;
devices[0].usUsage = 0x08;
if (! RegisterRawInputDevices(devices, device_count, sizeof(RAWINPUTDEVICE)))
BOOST_LOG_TRIVIAL(error) << "RegisterRawInputDevices failed";
}
}
#endif // _WIN32
void MainFrame::create_preset_tabs()
{
wxGetApp().update_label_colours_from_appconfig();
//BBS: GUI refactor
//m_param_panel = new ParamsPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
m_param_dialog = new ParamsDialog(m_plater);
add_created_tab(new TabPrint(m_param_panel), "cog");
add_created_tab(new TabPrintPlate(m_param_panel), "cog");
add_created_tab(new TabPrintObject(m_param_panel), "cog");
add_created_tab(new TabPrintPart(m_param_panel), "cog");
add_created_tab(new TabPrintLayer(m_param_panel), "cog");
add_created_tab(new TabFilament(m_param_dialog->panel()), "spool");
/* BBS work around to avoid appearance bug */
//add_created_tab(new TabSLAPrint(m_param_panel));
//add_created_tab(new TabSLAMaterial(m_param_panel));
add_created_tab(new TabPrinter(m_param_dialog->panel()), "printer");
m_param_panel->rebuild_panels();
m_param_dialog->panel()->rebuild_panels();
//m_tabpanel->AddPage(m_param_panel, "Parameters", "notebook_presets_active");
//m_tabpanel->InsertPage(tpSettings, m_param_panel, _L("Parameters"), std::string("cog"));
}
void MainFrame::add_created_tab(Tab* panel, const std::string& bmp_name /*= ""*/)
{
panel->create_preset_tab();
if (panel->type() == Preset::TYPE_PLATE) {
wxGetApp().tabs_list.pop_back();
wxGetApp().plate_tab = panel;
}
// BBS: model config
if (panel->type() == Preset::TYPE_MODEL) {
wxGetApp().tabs_list.pop_back();
wxGetApp().model_tabs_list.push_back(panel);
}
}
bool MainFrame::is_active_and_shown_tab(wxPanel* panel)
{
if (panel == m_param_panel)
panel = m_plater;
else
return m_param_dialog->IsShown();
if (m_tabpanel->GetCurrentPage() != panel)
return false;
return true;
}
bool MainFrame::can_start_new_project() const
{
/*return m_plater && (!m_plater->get_project_filename(".3mf").IsEmpty() ||
GetTitle().StartsWith('*')||
wxGetApp().has_current_preset_changes() ||
!m_plater->model().objects.empty());*/
return (m_plater && !m_plater->is_background_process_slicing());
}
bool MainFrame::can_open_project() const
{
return (m_plater && !m_plater->is_background_process_slicing());
}
bool MainFrame::can_add_models() const
{
return (m_plater && !m_plater->is_background_process_slicing() && !m_plater->only_gcode_mode() && !m_plater->using_exported_file());
}
bool MainFrame::can_save() const
{
return (m_plater != nullptr) &&
!m_plater->get_view3D_canvas3D()->get_gizmos_manager().is_in_editing_mode(false) &&
m_plater->is_project_dirty() && !m_plater->using_exported_file() && !m_plater->only_gcode_mode();
}
bool MainFrame::can_save_as() const
{
return (m_plater != nullptr) &&
!m_plater->get_view3D_canvas3D()->get_gizmos_manager().is_in_editing_mode(false) && !m_plater->using_exported_file() && !m_plater->only_gcode_mode();
}
void MainFrame::save_project()
{
save_project_as(m_plater->get_project_filename(".3mf"));
}
bool MainFrame::save_project_as(const wxString& filename)
{
bool ret = (m_plater != nullptr) ? m_plater->export_3mf(into_path(filename)) : false;
if (ret) {
// wxGetApp().update_saved_preset_from_current_preset();
m_plater->reset_project_dirty_after_save();
}
return ret;
}
bool MainFrame::can_upload() const
{
return true;
}
bool MainFrame::can_export_model() const
{
return (m_plater != nullptr) && !m_plater->model().objects.empty();
}
bool MainFrame::can_export_toolpaths() const
{
return (m_plater != nullptr) && (m_plater->printer_technology() == ptFFF) && m_plater->is_preview_shown() && m_plater->is_preview_loaded() && m_plater->has_toolpaths_to_export();
}
bool MainFrame::can_export_supports() const
{
if ((m_plater == nullptr) || (m_plater->printer_technology() != ptSLA) || m_plater->model().objects.empty())
return false;
bool can_export = false;
const PrintObjects& objects = m_plater->sla_print().objects();
for (const SLAPrintObject* object : objects)
{
if (object->has_mesh(slaposPad) || object->has_mesh(slaposSupportTree))
{
can_export = true;
break;
}
}
return can_export;
}
bool MainFrame::can_export_gcode() const
{
if (m_plater == nullptr)
return false;
if (m_plater->model().objects.empty())
return false;
if (m_plater->is_export_gcode_scheduled())
return false;
// TODO:: add other filters
PartPlateList &part_plate_list = m_plater->get_partplate_list();
PartPlate *current_plate = part_plate_list.get_curr_plate();
if (!current_plate->is_slice_result_ready_for_print())
return false;
return true;
}
bool MainFrame::can_export_all_gcode() const
{
if (m_plater == nullptr)
return false;
if (m_plater->model().objects.empty())
return false;
if (m_plater->is_export_gcode_scheduled())
return false;
// TODO:: add other filters
PartPlateList& part_plate_list = m_plater->get_partplate_list();
return part_plate_list.is_all_slice_results_ready_for_print();
}
bool MainFrame::can_print_3mf() const
{
if (m_plater && !m_plater->model().objects.empty()) {
if (wxGetApp().preset_bundle->printers.get_edited_preset().is_custom_defined())
return false;
}
return true;
}
bool MainFrame::can_send_gcode() const
{
if (m_plater && !m_plater->model().objects.empty())
{
auto cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
if (const auto *print_host_opt = cfg.option<ConfigOptionString>("print_host"); print_host_opt)
return !print_host_opt->value.empty();
}
return true;
}
/*bool MainFrame::can_export_gcode_sd() const
{
if (m_plater == nullptr)
return false;
if (m_plater->model().objects.empty())
return false;
if (m_plater->is_export_gcode_scheduled())
return false;
// TODO:: add other filters
return wxGetApp().removable_drive_manager()->status().has_removable_drives;
}
bool MainFrame::can_eject() const
{
return wxGetApp().removable_drive_manager()->status().has_eject;
}*/
bool MainFrame::can_slice() const
{
#ifdef SUPPORT_BACKGROUND_PROCESSING
bool bg_proc = wxGetApp().app_config->get("background_processing") == "1";
return (m_plater != nullptr) ? !m_plater->model().objects.empty() && !bg_proc : false;
#else
return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false;
#endif
}
bool MainFrame::can_change_view() const
{
switch (m_layout)
{
default: { return false; }
//BBS GUI refactor: remove unused layout new/dlg
case ESettingsLayout::Old: {
int page_id = m_tabpanel->GetSelection();
return page_id != wxNOT_FOUND && dynamic_cast<const Slic3r::GUI::Plater*>(m_tabpanel->GetPage((size_t)page_id)) != nullptr;
}
case ESettingsLayout::GCodeViewer: { return true; }
}
}
bool MainFrame::can_clone() const {
return can_select() && !m_plater->is_selection_empty();
}
bool MainFrame::can_select() const
{
return (m_plater != nullptr) && (m_tabpanel->GetSelection() == TabPosition::tp3DEditor) && !m_plater->model().objects.empty();
}
bool MainFrame::can_deselect() const
{
return (m_plater != nullptr) && (m_tabpanel->GetSelection() == TabPosition::tp3DEditor) && !m_plater->is_selection_empty();
}
bool MainFrame::can_delete() const
{
return (m_plater != nullptr) && (m_tabpanel->GetSelection() == TabPosition::tp3DEditor) && !m_plater->is_selection_empty();
}
bool MainFrame::can_delete_all() const
{
return (m_plater != nullptr) && (m_tabpanel->GetSelection() == TabPosition::tp3DEditor) && !m_plater->model().objects.empty();
}
bool MainFrame::can_reslice() const
{
return (m_plater != nullptr) && !m_plater->model().objects.empty();
}
wxBoxSizer* MainFrame::create_side_tools()
{
int em = em_unit();
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
m_slice_select = eSlicePlate;
m_print_select = ePrintPlate;
// m_publish_btn = new Button(this, _L("Upload"), "bar_publish", 0, FromDIP(16));
m_slice_btn = new SideButton(this, _L("Slice plate"), "");
m_slice_option_btn = new SideButton(this, "", "sidebutton_dropdown", 0, FromDIP(14));
m_print_btn = new SideButton(this, _L("Print plate"), "");
m_print_option_btn = new SideButton(this, "", "sidebutton_dropdown", 0, FromDIP(14));
update_side_button_style();
// m_publish_btn->Hide();
m_slice_option_btn->Enable();
m_print_option_btn->Enable();
// sizer->Add(m_publish_btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(1));
// sizer->Add(FromDIP(15), 0, 0, 0, 0);
sizer->Add(m_slice_option_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, FromDIP(1));
sizer->Add(m_slice_btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(1));
sizer->Add(FromDIP(15), 0, 0, 0, 0);
sizer->Add(m_print_option_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, FromDIP(1));
sizer->Add(m_print_btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(1));
sizer->Add(FromDIP(19), 0, 0, 0, 0);
sizer->Layout();
// m_publish_btn->Bind(wxEVT_BUTTON, [this](auto& e) {
// CallAfter([this] {
// wxGetApp().open_publish_page_dialog();
// if (!wxGetApp().getAgent()) {
// BOOST_LOG_TRIVIAL(info) << "publish: no agent";
// return;
// }
// // record
// json j;
// NetworkAgent* agent = GUI::wxGetApp().getAgent();
// });
// });
m_slice_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event)
{
//this->m_plater->select_view_3D("Preview");
m_plater->update(false, true);
if (m_slice_select == eSliceAll)
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SLICE_ALL));
else
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SLICE_PLATE));
this->m_tabpanel->SetSelection(tpPreview);
});
m_print_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event)
{
//this->m_plater->select_view_3D("Preview");
if (m_print_select == ePrintAll || m_print_select == ePrintPlate)
{
m_plater->apply_background_progress();
// check valid of print
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
if (m_print_enable) {
if (m_print_select == ePrintAll)
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_PRINT_ALL));
if (m_print_select == ePrintPlate)
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_PRINT_PLATE));
}
}
else if (m_print_select == eExportGcode)
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_GCODE));
else if (m_print_select == eSendGcode)
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SEND_GCODE));
else if (m_print_select == eUploadGcode)
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_UPLOAD_GCODE));
else if (m_print_select == eExportSlicedFile)
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_SLICED_FILE));
else if (m_print_select == eExportAllSlicedFile)
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_ALL_SLICED_FILE));
else if (m_print_select == eSendToPrinter)
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SEND_TO_PRINTER));
else if (m_print_select == eSendToPrinterAll)
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SEND_TO_PRINTER_ALL));
});
m_slice_option_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event)
{
SidePopup* p = new SidePopup(this);
SideButton* slice_all_btn = new SideButton(p, _L("Slice all"), "");
slice_all_btn->SetCornerRadius(0);
SideButton* slice_plate_btn = new SideButton(p, _L("Slice plate"), "");
slice_plate_btn->SetCornerRadius(0);
slice_all_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
m_slice_btn->SetLabel(_L("Slice all"));
m_slice_select = eSliceAll;
m_slice_enable = get_enable_slice_status();
m_slice_btn->Enable(m_slice_enable);
this->Layout();
p->Dismiss();
});
slice_plate_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
m_slice_btn->SetLabel(_L("Slice plate"));
m_slice_select = eSlicePlate;
m_slice_enable = get_enable_slice_status();
m_slice_btn->Enable(m_slice_enable);
this->Layout();
p->Dismiss();
});
p->append_button(slice_all_btn);
p->append_button(slice_plate_btn);
p->Popup(m_slice_btn);
}
);
m_print_option_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event)
{
SidePopup* p = new SidePopup(this);
if (wxGetApp().preset_bundle
&& !wxGetApp().preset_bundle->is_bbl_vendor()) {
// ThirdParty Buttons
SideButton* export_gcode_btn = new SideButton(p, _L("Export G-code file"), "");
export_gcode_btn->SetCornerRadius(0);
export_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
m_print_btn->SetLabel(_L("Export G-code file"));
m_print_select = eExportGcode;
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
p->Dismiss();
});
// upload and print
SideButton* send_gcode_btn = new SideButton(p, _L("Print"), "");
send_gcode_btn->SetCornerRadius(0);
send_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
m_print_btn->SetLabel(_L("Print"));
m_print_select = eSendGcode;
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
p->Dismiss();
});
p->append_button(send_gcode_btn);
p->append_button(export_gcode_btn);
}
else {
//Orca Slicer Buttons
SideButton* print_plate_btn = new SideButton(p, _L("Print plate"), "");
print_plate_btn->SetCornerRadius(0);
SideButton* send_to_printer_btn = new SideButton(p, _L("Send"), "");
send_to_printer_btn->SetCornerRadius(0);
SideButton* export_sliced_file_btn = new SideButton(p, _L("Export plate sliced file"), "");
export_sliced_file_btn->SetCornerRadius(0);
SideButton* export_all_sliced_file_btn = new SideButton(p, _L("Export all sliced file"), "");
export_all_sliced_file_btn->SetCornerRadius(0);
print_plate_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
m_print_btn->SetLabel(_L("Print plate"));
m_print_select = ePrintPlate;
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
p->Dismiss();
});
SideButton* print_all_btn = new SideButton(p, _L("Print all"), "");
print_all_btn->SetCornerRadius(0);
print_all_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
m_print_btn->SetLabel(_L("Print all"));
m_print_select = ePrintAll;
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
p->Dismiss();
});
send_to_printer_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
m_print_btn->SetLabel(_L("Send"));
m_print_select = eSendToPrinter;
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
p->Dismiss();
});
SideButton* send_to_printer_all_btn = new SideButton(p, _L("Send all"), "");
send_to_printer_all_btn->SetCornerRadius(0);
send_to_printer_all_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
m_print_btn->SetLabel(_L("Send all"));
m_print_select = eSendToPrinterAll;
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
p->Dismiss();
});
export_sliced_file_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
m_print_btn->SetLabel(_L("Export plate sliced file"));
m_print_select = eExportSlicedFile;
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
p->Dismiss();
});
export_all_sliced_file_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
m_print_btn->SetLabel(_L("Export all sliced file"));
m_print_select = eExportAllSlicedFile;
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
p->Dismiss();
});
SideButton* export_gcode_btn = new SideButton(p, _L("Export G-code file"), "");
export_gcode_btn->SetCornerRadius(0);
export_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) {
m_print_btn->SetLabel(_L("Export G-code file"));
m_print_select = eExportGcode;
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
p->Dismiss();
});
p->append_button(print_plate_btn);
p->append_button(print_all_btn);
p->append_button(send_to_printer_btn);
p->append_button(send_to_printer_all_btn);
p->append_button(export_sliced_file_btn);
p->append_button(export_all_sliced_file_btn);
p->append_button(export_gcode_btn);
}
p->Popup(m_print_btn);
}
);
/*
Button * aux_btn = new Button(this, _L("Auxiliary"));
aux_btn->SetBackgroundColour(0x3B4446);
aux_btn->Bind(wxEVT_BUTTON, [](auto e) {
wxGetApp().sidebar().show_auxiliary_dialog();
});
sizer->Add(aux_btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1 * em / 10);
*/
sizer->Add(FromDIP(19), 0, 0, 0, 0);
return sizer;
}
bool MainFrame::get_enable_slice_status()
{
bool enable = true;
bool on_slicing = m_plater->is_background_process_slicing();
if (on_slicing) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": on slicing, return false directly!");
return false;
}
else if (m_plater->only_gcode_mode() || m_plater->using_exported_file()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": in gcode/exported 3mf mode, return false directly!");
return false;
}
PartPlateList &part_plate_list = m_plater->get_partplate_list();
PartPlate *current_plate = part_plate_list.get_curr_plate();
if (m_slice_select == eSliceAll)
{
/*if (part_plate_list.is_all_slice_results_valid())
{
enable = false;
}
else if (!part_plate_list.is_all_plates_ready_for_slice())
{
enable = false;
}*/
//always enable slice_all button
enable = true;
}
else if (m_slice_select == eSlicePlate)
{
if (current_plate->is_slice_result_valid())
{
enable = false;
}
else if (!current_plate->can_slice())
{
enable = false;
}
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": m_slice_select %1%, enable= %2% ")%m_slice_select %enable;
return enable;
}
bool MainFrame::get_enable_print_status()
{
bool enable = true;
PartPlateList &part_plate_list = m_plater->get_partplate_list();
PartPlate *current_plate = part_plate_list.get_curr_plate();
bool is_all_plates = wxGetApp().plater()->get_preview_canvas3D()->is_all_plates_selected();
if (m_print_select == ePrintAll)
{
if (!part_plate_list.is_all_slice_results_ready_for_print())
{
enable = false;
}
}
else if (m_print_select == ePrintPlate)
{
if (!current_plate->is_slice_result_ready_for_print())
{
enable = false;
}
enable = enable && !is_all_plates;
}
else if (m_print_select == eExportGcode)
{
if (!current_plate->is_slice_result_valid())
{
enable = false;
}
enable = enable && !is_all_plates;
}
else if (m_print_select == eSendGcode)
{
if (!current_plate->is_slice_result_valid())
enable = false;
if (!can_send_gcode())
enable = false;
enable = enable && !is_all_plates;
}
else if (m_print_select == eUploadGcode)
{
if (!current_plate->is_slice_result_valid())
enable = false;
if (!can_send_gcode())
enable = false;
enable = enable && !is_all_plates;
}
else if (m_print_select == eExportSlicedFile)
{
if (!current_plate->is_slice_result_ready_for_export())
{
enable = false;
}
enable = enable && !is_all_plates;
}
else if (m_print_select == eSendToPrinter)
{
if (!current_plate->is_slice_result_ready_for_print())
{
enable = false;
}
enable = enable && !is_all_plates;
}
else if (m_print_select == eSendToPrinterAll)
{
if (!part_plate_list.is_all_slice_results_ready_for_print())
{
enable = false;
}
}
else if (m_print_select == eExportAllSlicedFile)
{
if (!part_plate_list.is_all_slice_result_ready_for_export())
{
enable = false;
}
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": m_print_select %1%, enable= %2% ")%m_print_select %enable;
return enable;
}
void MainFrame::update_side_button_style()
{
// BBS
int em = em_unit();
/*m_slice_btn->SetLayoutStyle(1);
m_slice_btn->SetTextLayout(SideButton::EHorizontalOrientation::HO_Center, FromDIP(15));
m_slice_btn->SetMinSize(wxSize(-1, FromDIP(24)));
m_slice_btn->SetCornerRadius(FromDIP(12));
m_slice_btn->SetExtraSize(wxSize(FromDIP(38), FromDIP(10)));
m_slice_btn->SetBottomColour(wxColour(0x3B4446));*/
StateColor m_btn_bg_enable = StateColor(
std::pair<wxColour, int>(wxColour(0, 137, 123), StateColor::Pressed),
std::pair<wxColour, int>(wxColour(48, 221, 112), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal)
);
// m_publish_btn->SetMinSize(wxSize(FromDIP(125), FromDIP(24)));
// m_publish_btn->SetCornerRadius(FromDIP(12));
// m_publish_btn->SetBackgroundColor(m_btn_bg_enable);
// m_publish_btn->SetBorderColor(m_btn_bg_enable);
// m_publish_btn->SetBackgroundColour(wxColour(59,68,70));
// m_publish_btn->SetTextColor(StateColor::darkModeColorFor("#FFFFFE"));
m_slice_btn->SetTextLayout(SideButton::EHorizontalOrientation::HO_Left, FromDIP(15));
m_slice_btn->SetCornerRadius(FromDIP(12));
m_slice_btn->SetExtraSize(wxSize(FromDIP(38), FromDIP(10)));
m_slice_btn->SetMinSize(wxSize(-1, FromDIP(24)));
m_slice_option_btn->SetTextLayout(SideButton::EHorizontalOrientation::HO_Center);
m_slice_option_btn->SetCornerRadius(FromDIP(12));
m_slice_option_btn->SetExtraSize(wxSize(FromDIP(10), FromDIP(10)));
m_slice_option_btn->SetIconOffset(FromDIP(2));
m_slice_option_btn->SetMinSize(wxSize(FromDIP(24), FromDIP(24)));
m_print_btn->SetTextLayout(SideButton::EHorizontalOrientation::HO_Left, FromDIP(15));
m_print_btn->SetCornerRadius(FromDIP(12));
m_print_btn->SetExtraSize(wxSize(FromDIP(38), FromDIP(10)));
m_print_btn->SetMinSize(wxSize(-1, FromDIP(24)));
m_print_option_btn->SetTextLayout(SideButton::EHorizontalOrientation::HO_Center);
m_print_option_btn->SetCornerRadius(FromDIP(12));
m_print_option_btn->SetExtraSize(wxSize(FromDIP(10), FromDIP(10)));
m_print_option_btn->SetIconOffset(FromDIP(2));
m_print_option_btn->SetMinSize(wxSize(FromDIP(24), FromDIP(24)));
}
void MainFrame::update_slice_print_status(SlicePrintEventType event, bool can_slice, bool can_print)
{
bool enable_print = true, enable_slice = true;
if (!can_slice)
{
if (m_slice_select == eSlicePlate)
enable_slice = false;
}
if (!can_print)
enable_print = false;
//process print logic
if (enable_print)
{
enable_print = get_enable_print_status();
}
//process slice logic
if (enable_slice)
{
enable_slice = get_enable_slice_status();
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" m_slice_select %1%: can_slice= %2%, can_print %3%, enable_slice %4%, enable_print %5% ")%m_slice_select % can_slice %can_print %enable_slice %enable_print;
m_print_btn->Enable(enable_print);
m_slice_btn->Enable(enable_slice);
m_slice_enable = enable_slice;
m_print_enable = enable_print;
}
void MainFrame::on_dpi_changed(const wxRect& suggested_rect)
{
wxGetApp().update_fonts(this);
this->SetFont(this->normal_font());
#ifdef _MSW_DARK_MODE
// update common mode sizer
if (!wxGetApp().tabs_as_menu())
dynamic_cast<Notebook*>(m_tabpanel)->Rescale();
#endif
#ifndef __APPLE__
// BBS
m_topbar->Rescale();
#endif
m_tabpanel->Rescale();
update_side_button_style();
m_slice_btn->Rescale();
m_print_btn->Rescale();
m_slice_option_btn->Rescale();
m_print_option_btn->Rescale();
// update Plater
wxGetApp().plater()->msw_rescale();
// update Tabs
//BBS GUI refactor: remove unused layout new/dlg
//if (m_layout != ESettingsLayout::Dlg) // Do not update tabs if the Settings are in the separated dialog
m_param_panel->msw_rescale();
m_project->msw_rescale();
m_monitor->msw_rescale();
m_calibration->msw_rescale();
// Workarounds for correct Window rendering after rescale
/* Even if Window is maximized during moving,
* first of all we should imitate Window resizing. So:
* 1. cancel maximization, if it was set
* 2. imitate resizing
* 3. set maximization, if it was set
*/
const bool is_maximized = this->IsMaximized();
if (is_maximized)
this->Maximize(false);
/* To correct window rendering (especially redraw of a status bar)
* we should imitate window resizing.
*/
const wxSize& sz = this->GetSize();
this->SetSize(sz.x + 1, sz.y + 1);
this->SetSize(sz);
this->Maximize(is_maximized);
}
void MainFrame::on_sys_color_changed()
{
wxBusyCursor wait;
// update label colors in respect to the system mode
wxGetApp().init_label_colours();
#ifndef __WINDOWS__
wxGetApp().force_colors_update();
wxGetApp().update_ui_from_settings();
#endif //__APPLE__
#ifdef __WXMSW__
wxGetApp().UpdateDarkUI(m_tabpanel);
// m_statusbar->update_dark_ui();
#ifdef _MSW_DARK_MODE
// update common mode sizer
if (!wxGetApp().tabs_as_menu())
dynamic_cast<Notebook*>(m_tabpanel)->OnColorsChanged();
#endif
#endif
// BBS
m_tabpanel->Rescale();
m_param_panel->msw_rescale();
// update Plater
wxGetApp().plater()->sys_color_changed();
m_monitor->on_sys_color_changed();
m_calibration->on_sys_color_changed();
// update Tabs
for (auto tab : wxGetApp().tabs_list)
tab->sys_color_changed();
for (auto tab : wxGetApp().model_tabs_list)
tab->sys_color_changed();
wxGetApp().plate_tab->sys_color_changed();
MenuFactory::sys_color_changed(m_menubar);
WebView::RecreateAll();
this->Refresh();
}
#ifdef _MSC_VER
// \xA0 is a non-breaking space. It is entered here to spoil the automatic accelerators,
// as the simple numeric accelerators spoil all numeric data entry.
static const wxString sep = "\t\xA0";
static const wxString sep_space = "\xA0";
#else
static const wxString sep = " - ";
static const wxString sep_space = "";
#endif
static wxMenu* generate_help_menu()
{
wxMenu* helpMenu = new wxMenu();
// shortcut key
append_menu_item(helpMenu, wxID_ANY, _L("Keyboard Shortcuts") + sep + "&?", _L("Show the list of the keyboard shortcuts"),
[](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); });
// Show Beginner's Tutorial
append_menu_item(helpMenu, wxID_ANY, _L("Setup Wizard"), _L("Setup Wizard"), [](wxCommandEvent &) {wxGetApp().ShowUserGuide();});
helpMenu->AppendSeparator();
// Open Config Folder
append_menu_item(helpMenu, wxID_ANY, _L("Show Configuration Folder"), _L("Show Configuration Folder"),
[](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); });
append_menu_item(helpMenu, wxID_ANY, _L("Show Tip of the Day"), _L("Show Tip of the Day"), [](wxCommandEvent&) {
wxGetApp().plater()->get_dailytips()->open();
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
});
// Report a bug
//append_menu_item(helpMenu, wxID_ANY, _L("Report Bug(TODO)"), _L("Report a bug of OrcaSlicer"),
// [](wxCommandEvent&) {
// //TODO
// });
// Check New Version
append_menu_item(helpMenu, wxID_ANY, _L("Check for Update"), _L("Check for Update"),
[](wxCommandEvent&) {
wxGetApp().check_new_version_sf(true, 1);
}, "", nullptr, []() {
return true;
});
append_menu_item(helpMenu, wxID_ANY, _L("Open Network Test"), _L("Open Network Test"), [](wxCommandEvent&) {
NetworkTestDialog dlg(wxGetApp().mainframe);
dlg.ShowModal();
});
// About
#ifndef __APPLE__
wxString about_title = wxString::Format(_L("&About %s"), SLIC3R_APP_FULL_NAME);
append_menu_item(helpMenu, wxID_ANY, about_title, about_title,
[](wxCommandEvent&) { Slic3r::GUI::about(); });
#endif
return helpMenu;
}
static void add_common_publish_menu_items(wxMenu* publish_menu, MainFrame* mainFrame)
{
#ifndef __WINDOWS__
append_menu_item(publish_menu, wxID_ANY, _L("Upload Models"), _L("Upload Models"),
[](wxCommandEvent&) {
if (!wxGetApp().getAgent()) {
BOOST_LOG_TRIVIAL(info) << "publish: no agent";
return;
}
json j;
NetworkAgent* agent = GUI::wxGetApp().getAgent();
//if (GUI::wxGetApp().plater()->model().objects.empty()) return;
wxGetApp().open_publish_page_dialog();
});
append_menu_item(publish_menu, wxID_ANY, _L("Download Models"), _L("Download Models"),
[](wxCommandEvent&) {
if (!wxGetApp().getAgent()) {
BOOST_LOG_TRIVIAL(info) << "publish: no agent";
return;
}
//if (GUI::wxGetApp().plater()->model().objects.empty()) return;
wxGetApp().open_mall_page_dialog();
});
#endif
}
static void add_common_view_menu_items(wxMenu* view_menu, MainFrame* mainFrame, std::function<bool(void)> can_change_view)
{
// The camera control accelerators are captured by GLCanvas3D::on_char().
append_menu_item(view_menu, wxID_ANY, _L("Default View") + "\t" + ctrl + "0", _L("Default View"), [mainFrame](wxCommandEvent&) {
mainFrame->select_view("plate");
mainFrame->plater()->get_current_canvas3D()->zoom_to_bed();
},
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
//view_menu->AppendSeparator();
//TRN To be shown in the main menu View->Top
append_menu_item(view_menu, wxID_ANY, _L("Top") + "\t" + ctrl + "1", _L("Top View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("top"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
//TRN To be shown in the main menu View->Bottom
append_menu_item(view_menu, wxID_ANY, _L("Bottom") + "\t" + ctrl + "2", _L("Bottom View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("bottom"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
append_menu_item(view_menu, wxID_ANY, _L("Front") + "\t" + ctrl + "3", _L("Front View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("front"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
append_menu_item(view_menu, wxID_ANY, _L("Rear") + "\t" + ctrl + "4", _L("Rear View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("rear"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
append_menu_item(view_menu, wxID_ANY, _L("Left") + "\t" + ctrl + "5", _L("Left View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("left"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
append_menu_item(view_menu, wxID_ANY, _L("Right") + "\t" + ctrl + "6", _L("Right View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("right"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
}
void MainFrame::init_menubar_as_editor()
{
#ifdef __APPLE__
m_menubar = new wxMenuBar();
#endif
// File menu
wxMenu* fileMenu = new wxMenu;
{
#ifdef __APPLE__
// New Window
append_menu_item(fileMenu, wxID_ANY, _L("New Window"), _L("Start a new window"),
[](wxCommandEvent&) { start_new_slicer(); }, "", nullptr,
[]{ return true; }, this);
#endif
// New Project
append_menu_item(fileMenu, wxID_ANY, _L("New Project") + "\t" + ctrl + "N", _L("Start a new project"),
[this](wxCommandEvent&) { if (m_plater) m_plater->new_project(); }, "", nullptr,
[this](){return can_start_new_project(); }, this);
// Open Project
#ifndef __APPLE__
append_menu_item(fileMenu, wxID_ANY, _L("Open Project") + dots + "\t" + ctrl + "O", _L("Open a project file"),
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "menu_open", nullptr,
[this](){return can_open_project(); }, this);
#else
append_menu_item(fileMenu, wxID_ANY, _L("Open Project") + dots + "\t" + ctrl + "O", _L("Open a project file"),
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "", nullptr,
[this](){return can_open_project(); }, this);
#endif
// Recent Project
wxMenu* recent_projects_menu = new wxMenu();
wxMenuItem* recent_projects_submenu = append_submenu(fileMenu, recent_projects_menu, wxID_ANY, _L("Recent projects"), "");
m_recent_projects.UseMenu(recent_projects_menu);
Bind(wxEVT_MENU, [this](wxCommandEvent& evt) {
size_t file_id = evt.GetId() - wxID_FILE1;
wxString filename = m_recent_projects.GetHistoryFile(file_id);
open_recent_project(file_id, filename);
}, wxID_FILE1, wxID_FILE1 + 49); // [5050, 5100)
std::vector<std::string> recent_projects = wxGetApp().app_config->get_recent_projects();
std::reverse(recent_projects.begin(), recent_projects.end());
for (const std::string& project : recent_projects)
{
m_recent_projects.AddFileToHistory(from_u8(project));
}
m_recent_projects.LoadThumbnails();
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_open_project() && (m_recent_projects.GetCount() > 0)); }, recent_projects_submenu->GetId());
// BBS: close save project
#ifndef __APPLE__
append_menu_item(fileMenu, wxID_ANY, _L("Save Project") + "\t" + ctrl + "S", _L("Save current project to file"),
[this](wxCommandEvent&) { if (m_plater) m_plater->save_project(); }, "menu_save", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
#else
append_menu_item(fileMenu, wxID_ANY, _L("Save Project") + "\t" + ctrl + "S", _L("Save current project to file"),
[this](wxCommandEvent&) { if (m_plater) m_plater->save_project(); }, "", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
#endif
#ifndef __APPLE__
append_menu_item(fileMenu, wxID_ANY, _L("Save Project as") + dots + "\t" + ctrl + _L("Shift+") + "S", _L("Save current project as"),
[this](wxCommandEvent&) { if (m_plater) m_plater->save_project(true); }, "menu_save", nullptr,
[this](){return m_plater != nullptr && can_save_as(); }, this);
#else
append_menu_item(fileMenu, wxID_ANY, _L("Save Project as") + dots + "\t" + ctrl + _L("Shift+") + "S", _L("Save current project as"),
[this](wxCommandEvent&) { if (m_plater) m_plater->save_project(true); }, "", nullptr,
[this](){return m_plater != nullptr && can_save_as(); }, this);
#endif
fileMenu->AppendSeparator();
// BBS
wxMenu *import_menu = new wxMenu();
#ifndef __APPLE__
append_menu_item(import_menu, wxID_ANY, _L("Import 3MF/STL/STEP/SVG/OBJ/AMF") + dots + "\t" + ctrl + "I", _L("Load a model"),
[this](wxCommandEvent&) { if (m_plater) {
m_plater->add_file();
} }, "menu_import", nullptr,
[this](){return can_add_models(); }, this);
#else
append_menu_item(import_menu, wxID_ANY, _L("Import 3MF/STL/STEP/SVG/OBJ/AMF") + dots + "\t" + ctrl + "I", _L("Load a model"),
[this](wxCommandEvent&) { if (m_plater) { m_plater->add_model(); } }, "", nullptr,
[this](){return can_add_models(); }, this);
#endif
append_menu_item(import_menu, wxID_ANY, _L("Import Configs") + dots /*+ "\tCtrl+I"*/, _L("Load configs"),
[this](wxCommandEvent&) { load_config_file(); }, "menu_import", nullptr,
[this](){return true; }, this);
append_submenu(fileMenu, import_menu, wxID_ANY, _L("Import"), "");
wxMenu* export_menu = new wxMenu();
// BBS export as STL
append_menu_item(export_menu, wxID_ANY, _L("Export all objects as one STL") + dots, _L("Export all objects as one STL"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "menu_export_stl", nullptr,
[this](){return can_export_model(); }, this);
append_menu_item(export_menu, wxID_ANY, _L("Export all objects as STLs") + dots, _L("Export all objects as STLs"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(false, false, true); }, "menu_export_stl", nullptr,
[this](){return can_export_model(); }, this);
append_menu_item(export_menu, wxID_ANY, _L("Export Generic 3MF") + dots/* + "\tCtrl+G"*/, _L("Export 3mf file without using some 3mf-extensions"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_core_3mf(); }, "menu_export_sliced_file", nullptr,
[this](){return can_export_model(); }, this);
// BBS export .gcode.3mf
append_menu_item(export_menu, wxID_ANY, _L("Export plate sliced file") + dots + "\t" + ctrl + "G", _L("Export current sliced file"),
[this](wxCommandEvent&) { if (m_plater) wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_SLICED_FILE)); }, "menu_export_sliced_file", nullptr,
[this](){return can_export_gcode(); }, this);
append_menu_item(export_menu, wxID_ANY, _L("Export all plate sliced file") + dots/* + "\tCtrl+G"*/, _L("Export all plate sliced file"),
[this](wxCommandEvent&) { if (m_plater) wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_ALL_SLICED_FILE)); }, "menu_export_sliced_file", nullptr,
[this]() {return can_export_all_gcode(); }, this);
append_menu_item(export_menu, wxID_ANY, _L("Export G-code") + dots/* + "\tCtrl+G"*/, _L("Export current plate as G-code"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(false); }, "menu_export_gcode", nullptr,
[this]() {return can_export_gcode(); }, this);
append_menu_item(
export_menu, wxID_ANY, _L("Export &Configs") + dots /* + "\tCtrl+E"*/, _L("Export current configuration to files"),
[this](wxCommandEvent &) { export_config(); },
"menu_export_config", nullptr,
[]() { return true; }, this);
append_submenu(fileMenu, export_menu, wxID_ANY, _L("Export"), "");
fileMenu->AppendSeparator();
#ifndef __APPLE__
append_menu_item(fileMenu, wxID_EXIT, _L("Quit"), wxString::Format(_L("Quit")),
[this](wxCommandEvent&) { Close(false); }, "menu_exit", nullptr);
#else
append_menu_item(fileMenu, wxID_EXIT, _L("Quit"), wxString::Format(_L("Quit")),
[this](wxCommandEvent&) { Close(false); }, "", nullptr);
#endif
}
// Edit menu
wxMenu* editMenu = nullptr;
if (m_plater != nullptr)
{
editMenu = new wxMenu();
#ifdef __APPLE__
// Backspace sign
wxString hotkey_delete = "\u232b";
#else
wxString hotkey_delete = "Del";
#endif
auto handle_key_event = [](wxKeyEvent& evt) {
if (wxGetApp().imgui()->update_key_data(evt)) {
wxGetApp().plater()->get_current_canvas3D()->render();
return true;
}
return false;
};
#ifndef __APPLE__
// BBS undo
append_menu_item(editMenu, wxID_ANY, _L("Undo") + "\t" + ctrl + "Z",
_L("Undo"), [this](wxCommandEvent&) { m_plater->undo(); },
"menu_undo", nullptr, [this](){return m_plater->can_undo(); }, this);
// BBS redo
append_menu_item(editMenu, wxID_ANY, _L("Redo") + "\t" + ctrl + "Y",
_L("Redo"), [this](wxCommandEvent&) { m_plater->redo(); },
"menu_redo", nullptr, [this](){return m_plater->can_redo(); }, this);
editMenu->AppendSeparator();
// BBS Cut TODO
append_menu_item(editMenu, wxID_ANY, _L("Cut") + "\t" + ctrl + "X",
_L("Cut selection to clipboard"), [this](wxCommandEvent&) {m_plater->cut_selection_to_clipboard(); },
"menu_cut", nullptr, [this]() {return m_plater->can_copy_to_clipboard(); }, this);
// BBS Copy
append_menu_item(editMenu, wxID_ANY, _L("Copy") + "\t" + ctrl + "C",
_L("Copy selection to clipboard"), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); },
"menu_copy", nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this);
// BBS Paste
append_menu_item(editMenu, wxID_ANY, _L("Paste") + "\t" + ctrl + "V",
_L("Paste clipboard"), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); },
"menu_paste", nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this);
// BBS Delete selected
append_menu_item(editMenu, wxID_ANY, _L("Delete selected") + "\t" + _L("Del"),
_L("Deletes the current selection"),[this](wxCommandEvent&) { m_plater->remove_selected(); },
"menu_remove", nullptr, [this](){return can_delete(); }, this);
//BBS: delete all
append_menu_item(editMenu, wxID_ANY, _L("Delete all") + "\t" + ctrl + "D",
_L("Deletes all objects"),[this](wxCommandEvent&) { m_plater->delete_all_objects_from_model(); },
"menu_remove", nullptr, [this](){return can_delete_all(); }, this);
editMenu->AppendSeparator();
// BBS Clone Selected
append_menu_item(editMenu, wxID_ANY, _L("Clone selected") /*+ "\tCtrl+M"*/,
_L("Clone copies of selections"),[this](wxCommandEvent&) {
m_plater->clone_selection();
},
"menu_remove", nullptr, [this](){return can_clone(); }, this);
editMenu->AppendSeparator();
#else
// BBS undo
append_menu_item(editMenu, wxID_ANY, _L("Undo") + "\t" + ctrl + "Z",
_L("Undo"), [this, handle_key_event](wxCommandEvent&) {
wxKeyEvent e;
e.SetEventType(wxEVT_KEY_DOWN);
e.SetControlDown(true);
e.m_keyCode = 'Z';
if (handle_key_event(e)) {
return;
}
m_plater->undo(); },
"", nullptr, [this](){return m_plater->can_undo(); }, this);
// BBS redo
append_menu_item(editMenu, wxID_ANY, _L("Redo") + "\t" + ctrl + "Y",
_L("Redo"), [this, handle_key_event](wxCommandEvent&) {
wxKeyEvent e;
e.SetEventType(wxEVT_KEY_DOWN);
e.SetControlDown(true);
e.m_keyCode = 'Y';
if (handle_key_event(e)) {
return;
}
m_plater->redo(); },
"", nullptr, [this](){return m_plater->can_redo(); }, this);
editMenu->AppendSeparator();
// BBS Cut TODO
append_menu_item(editMenu, wxID_ANY, _L("Cut") + "\t" + ctrl + "X",
_L("Cut selection to clipboard"), [this, handle_key_event](wxCommandEvent&) {
wxKeyEvent e;
e.SetEventType(wxEVT_KEY_DOWN);
e.SetControlDown(true);
e.m_keyCode = 'X';
if (handle_key_event(e)) {
return;
}
m_plater->cut_selection_to_clipboard(); },
"", nullptr, [this]() {return m_plater->can_copy_to_clipboard(); }, this);
// BBS Copy
append_menu_item(editMenu, wxID_ANY, _L("Copy") + "\t" + ctrl + "C",
_L("Copy selection to clipboard"), [this, handle_key_event](wxCommandEvent&) {
wxKeyEvent e;
e.SetEventType(wxEVT_KEY_DOWN);
e.SetControlDown(true);
e.m_keyCode = 'C';
if (handle_key_event(e)) {
return;
}
m_plater->copy_selection_to_clipboard(); },
"", nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this);
// BBS Paste
append_menu_item(editMenu, wxID_ANY, _L("Paste") + "\t" + ctrl + "V",
_L("Paste clipboard"), [this, handle_key_event](wxCommandEvent&) {
wxKeyEvent e;
e.SetEventType(wxEVT_KEY_DOWN);
e.SetControlDown(true);
e.m_keyCode = 'V';
if (handle_key_event(e)) {
return;
}
m_plater->paste_from_clipboard(); },
"", nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this);
#if 0
// BBS Delete selected
append_menu_item(editMenu, wxID_ANY, _L("Delete selected") + "\tBackSpace",
_L("Deletes the current selection"),[this](wxCommandEvent&) {
m_plater->remove_selected();
},
"", nullptr, [this](){return can_delete(); }, this);
#endif
//BBS: delete all
append_menu_item(editMenu, wxID_ANY, _L("Delete all") + "\t" + ctrl + "D",
_L("Deletes all objects"),[this, handle_key_event](wxCommandEvent&) {
wxKeyEvent e;
e.SetEventType(wxEVT_KEY_DOWN);
e.SetControlDown(true);
e.m_keyCode = 'D';
if (handle_key_event(e)) {
return;
}
m_plater->delete_all_objects_from_model(); },
"", nullptr, [this](){return can_delete_all(); }, this);
editMenu->AppendSeparator();
// BBS Clone Selected
append_menu_item(editMenu, wxID_ANY, _L("Clone selected") + "\t" + ctrl + "K",
_L("Clone copies of selections"),[this, handle_key_event](wxCommandEvent&) {
wxKeyEvent e;
e.SetEventType(wxEVT_KEY_DOWN);
e.SetControlDown(true);
e.m_keyCode = 'M';
if (handle_key_event(e)) {
return;
}
m_plater->clone_selection();
},
"", nullptr, [this](){return can_clone(); }, this);
editMenu->AppendSeparator();
#endif
// BBS Select All
append_menu_item(editMenu, wxID_ANY, _L("Select all") + "\t" + ctrl + "A",
_L("Selects all objects"), [this, handle_key_event](wxCommandEvent&) {
wxKeyEvent e;
e.SetEventType(wxEVT_KEY_DOWN);
e.SetControlDown(true);
e.m_keyCode = 'A';
if (handle_key_event(e)) {
return;
}
m_plater->select_all(); },
"", nullptr, [this](){return can_select(); }, this);
// BBS Deslect All
append_menu_item(editMenu, wxID_ANY, _L("Deselect all") + "\tEsc",
_L("Deselects all objects"), [this, handle_key_event](wxCommandEvent&) {
wxKeyEvent e;
e.SetEventType(wxEVT_KEY_DOWN);
e.m_keyCode = WXK_ESCAPE;
if (handle_key_event(e)) {
return;
}
m_plater->deselect_all(); },
"", nullptr, [this](){return can_deselect(); }, this);
//editMenu->AppendSeparator();
//append_menu_check_item(editMenu, wxID_ANY, _L("Show Model Mesh(TODO)"),
// _L("Display triangles of models"), [this](wxCommandEvent& evt) {
// wxGetApp().app_config->set_bool("show_model_mesh", evt.GetInt() == 1);
// }, nullptr, [this]() {return can_select(); }, [this]() { return wxGetApp().app_config->get("show_model_mesh").compare("true") == 0; }, this);
//append_menu_check_item(editMenu, wxID_ANY, _L("Show Model Shadow(TODO)"), _L("Display shadow of objects"),
// [this](wxCommandEvent& evt) {
// wxGetApp().app_config->set_bool("show_model_shadow", evt.GetInt() == 1);
// }, nullptr, [this]() {return can_select(); }, [this]() { return wxGetApp().app_config->get("show_model_shadow").compare("true") == 0; }, this);
//editMenu->AppendSeparator();
//append_menu_check_item(editMenu, wxID_ANY, _L("Show Printable Box(TODO)"), _L("Display printable box"),
// [this](wxCommandEvent& evt) {
// wxGetApp().app_config->set_bool("show_printable_box", evt.GetInt() == 1);
// }, nullptr, [this]() {return can_select(); }, [this]() { return wxGetApp().app_config->get("show_printable_box").compare("true") == 0; }, this);
}
// BBS
//publish menu
/*if (m_plater) {
publishMenu = new wxMenu();
add_common_publish_menu_items(publishMenu, this);
publishMenu->AppendSeparator();
}*/
// View menu
wxMenu* viewMenu = nullptr;
if (m_plater) {
viewMenu = new wxMenu();
add_common_view_menu_items(viewMenu, this, std::bind(&MainFrame::can_change_view, this));
viewMenu->AppendSeparator();
//BBS perspective view
wxWindowID camera_id_base = wxWindow::NewControlId(int(wxID_CAMERA_COUNT));
auto perspective_item = append_menu_radio_item(viewMenu, wxID_CAMERA_PERSPECTIVE + camera_id_base, _L("Use Perspective View"), _L("Use Perspective View"),
[this](wxCommandEvent&) {
wxGetApp().app_config->set_bool("use_perspective_camera", true);
wxGetApp().update_ui_from_settings();
}, nullptr);
//BBS orthogonal view
auto orthogonal_item = append_menu_radio_item(viewMenu, wxID_CAMERA_ORTHOGONAL + camera_id_base, _L("Use Orthogonal View"), _L("Use Orthogonal View"),
[this](wxCommandEvent&) {
wxGetApp().app_config->set_bool("use_perspective_camera", false);
wxGetApp().update_ui_from_settings();
}, nullptr);
if (wxGetApp().app_config->get("use_perspective_camera").compare("true") == 0)
viewMenu->Check(wxID_CAMERA_PERSPECTIVE + camera_id_base, true);
else
viewMenu->Check(wxID_CAMERA_ORTHOGONAL + camera_id_base, true);
viewMenu->AppendSeparator();
append_menu_check_item(viewMenu, wxID_ANY, _L("Show &G-code Window") + "\tC", _L("Show g-code window in Previce scene"),
[this](wxCommandEvent &) {
wxGetApp().toggle_show_gcode_window();
m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT));
},
this, [this]() { return m_plater->is_preview_shown(); },
[this]() { return wxGetApp().show_gcode_window(); }, this);
viewMenu->AppendSeparator();
append_menu_check_item(viewMenu, wxID_ANY, _L("Show &Labels") + "\t" + ctrl + "E", _L("Show object labels in 3D scene"),
[this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT)); }, this,
[this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this);
append_menu_check_item(viewMenu, wxID_ANY, _L("Show &Overhang"), _L("Show object overhang highlight in 3D scene"),
[this](wxCommandEvent &) {
m_plater->show_view3D_overhang(!m_plater->is_view3D_overhang_shown());
m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT));
},
this, [this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->is_view3D_overhang_shown(); }, this);
/*viewMenu->AppendSeparator();
append_menu_check_item(viewMenu, wxID_ANY, _L("Show &Wireframe") + "\tCtrl+Shift+Enter", _L("Show wireframes in 3D scene"),
[this](wxCommandEvent&) { m_plater->toggle_show_wireframe(); m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT)); }, this,
[this]() { return m_plater->is_wireframe_enabled(); }, [this]() { return m_plater->is_show_wireframe(); }, this);*/
//viewMenu->AppendSeparator();
////BBS orthogonal view
//append_menu_check_item(viewMenu, wxID_ANY, _L("Show Edges(TODO)"), _L("Show Edges"),
// [this](wxCommandEvent& evt) {
// wxGetApp().app_config->set("show_build_edges", evt.GetInt() == 1 ? "true" : "false");
// }, nullptr, [this]() {return can_select(); }, [this]() {
// std::string show_build_edges = wxGetApp().app_config->get("show_build_edges");
// return show_build_edges.compare("true") == 0;
// }, this);
}
wxWindowID config_id_base = wxWindow::NewControlId(int(ConfigMenuCnt));
//TODO remove
//auto config_wizard_name = _(ConfigWizard::name(true) + "(Debug)");
//const auto config_wizard_tooltip = from_u8((boost::format(_utf8(L("Run %s"))) % config_wizard_name).str());
//auto config_item = new wxMenuItem(m_topbar->GetTopMenu(), ConfigMenuWizard + config_id_base, config_wizard_name, config_wizard_tooltip);
#ifdef __APPLE__
wxWindowID bambu_studio_id_base = wxWindow::NewControlId(int(2));
wxMenu* parent_menu = m_menubar->OSXGetAppleMenu();
//auto preference_item = new wxMenuItem(parent_menu, OrcaSlicerMenuPreferences + bambu_studio_id_base, _L("Preferences") + "\tCtrl+,", "");
#else
wxMenu* parent_menu = m_topbar->GetTopMenu();
auto preference_item = new wxMenuItem(parent_menu, ConfigMenuPreferences + config_id_base, _L("Preferences") + "\t" + ctrl + "P", "");
#endif
//auto printer_item = new wxMenuItem(parent_menu, ConfigMenuPrinter + config_id_base, _L("Printer"), "");
//auto language_item = new wxMenuItem(parent_menu, ConfigMenuLanguage + config_id_base, _L("Switch Language"), "");
// parent_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent& event) {
// switch (event.GetId() - config_id_base) {
// //case ConfigMenuLanguage:
// //{
// // /* Before change application language, let's check unsaved changes on 3D-Scene
// // * and draw user's attention to the application restarting after a language change
// // */
// // {
// // // the dialog needs to be destroyed before the call to switch_language()
// // // or sometimes the application crashes into wxDialogBase() destructor
// // // so we put it into an inner scope
// // wxString title = _L("Language selection");
// // wxMessageDialog dialog(nullptr,
// // _L("Switching the language requires application restart.\n") + "\n\n" +
// // _L("Do you want to continue?"),
// // title,
// // wxICON_QUESTION | wxOK | wxCANCEL);
// // if (dialog.ShowModal() == wxID_CANCEL)
// // return;
// // }
//
// // wxGetApp().switch_language();
// // break;
// //}
// //case ConfigMenuWizard:
// //{
// // wxGetApp().run_wizard(ConfigWizard::RR_USER);
// // break;
// //}
// case ConfigMenuPrinter:
// {
// wxGetApp().params_dialog()->Popup();
// wxGetApp().get_tab(Preset::TYPE_PRINTER)->restore_last_select_item();
// break;
// }
// case ConfigMenuPreferences:
// {
// CallAfter([this] {
// PreferencesDialog dlg(this);
// dlg.ShowModal();
//#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
// if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
//#else
// if (dlg.seq_top_layer_only_changed())
//#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
// plater()->refresh_print();
//#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
//#ifdef _WIN32
// /*
// if (wxGetApp().app_config()->get("associate_3mf") == "true")
// wxGetApp().associate_3mf_files();
// if (wxGetApp().app_config()->get("associate_stl") == "true")
// wxGetApp().associate_stl_files();
// /*if (wxGetApp().app_config()->get("associate_step") == "true")
// wxGetApp().associate_step_files();*/
//#endif // _WIN32
//#endif
// });
// break;
// }
// default:
// break;
// }
// });
#ifdef __APPLE__
wxString about_title = wxString::Format(_L("&About %s"), SLIC3R_APP_FULL_NAME);
//auto about_item = new wxMenuItem(parent_menu, OrcaSlicerMenuAbout + bambu_studio_id_base, about_title, "");
//parent_menu->Bind(wxEVT_MENU, [this, bambu_studio_id_base](wxEvent& event) {
// switch (event.GetId() - bambu_studio_id_base) {
// case OrcaSlicerMenuAbout:
// Slic3r::GUI::about();
// break;
// case OrcaSlicerMenuPreferences:
// CallAfter([this] {
// PreferencesDialog dlg(this);
// dlg.ShowModal();
//#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
// if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
//#else
// if (dlg.seq_top_layer_only_changed())
//#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
// plater()->refresh_print();
// });
// break;
// default:
// break;
// }
//});
//parent_menu->Insert(0, about_item);
append_menu_item(
parent_menu, wxID_ANY, _L(about_title), "",
[this](wxCommandEvent &) { Slic3r::GUI::about();},
"", nullptr, []() { return true; }, this, 0);
append_menu_item(
parent_menu, wxID_ANY, _L("Preferences") + "\t" + ctrl + ",", "",
[this](wxCommandEvent &) {
PreferencesDialog dlg(this);
dlg.ShowModal();
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
#else
if (dlg.seq_top_layer_only_changed())
#endif
plater()->refresh_print();
},
"", nullptr, []() { return true; }, this, 1);
//parent_menu->Insert(1, preference_item);
#endif
// Help menu
auto helpMenu = generate_help_menu();
#ifndef __APPLE__
m_topbar->SetFileMenu(fileMenu);
if (editMenu)
m_topbar->AddDropDownSubMenu(editMenu, _L("Edit"));
if (viewMenu)
m_topbar->AddDropDownSubMenu(viewMenu, _L("View"));
//BBS add Preference
append_menu_item(
m_topbar->GetTopMenu(), wxID_ANY, _L("Preferences") + "\t" + ctrl + "P", "",
[this](wxCommandEvent &) {
PreferencesDialog dlg(this);
dlg.ShowModal();
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
#else
if (dlg.seq_top_layer_only_changed())
#endif
plater()->refresh_print();
},
"", nullptr, []() { return true; }, this);
//m_topbar->AddDropDownMenuItem(preference_item);
//m_topbar->AddDropDownMenuItem(printer_item);
//m_topbar->AddDropDownMenuItem(language_item);
//m_topbar->AddDropDownMenuItem(config_item);
m_topbar->AddDropDownSubMenu(helpMenu, _L("Help"));
// SoftFever calibrations
// Flowrate
append_menu_item(m_topbar->GetCalibMenu(), wxID_ANY, _L("Temperature"), _L("Temperature Calibration"),
[this](wxCommandEvent&) {
if (!m_temp_calib_dlg)
m_temp_calib_dlg = new Temp_Calibration_Dlg((wxWindow*)this, wxID_ANY, m_plater);
m_temp_calib_dlg->ShowModal();
}, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
auto flowrate_menu = new wxMenu();
append_menu_item(
flowrate_menu, wxID_ANY, _L("Pass 1"), _L("Flow rate test - Pass 1"),
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(1); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 2"), _L("Flow rate test - Pass 2"),
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(2); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
m_topbar->GetCalibMenu()->AppendSubMenu(flowrate_menu, _L("Flow rate"));
append_menu_item(m_topbar->GetCalibMenu(), wxID_ANY, _L("Pressure advance"), _L("Pressure advance"),
[this](wxCommandEvent&) {
if (!m_pa_calib_dlg)
m_pa_calib_dlg = new PA_Calibration_Dlg((wxWindow*)this, wxID_ANY, m_plater);
m_pa_calib_dlg->ShowModal();
}, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
append_menu_item(m_topbar->GetCalibMenu(), wxID_ANY, _L("Retraction test"), _L("Retraction test"),
[this](wxCommandEvent&) {
if (!m_retraction_calib_dlg)
m_retraction_calib_dlg = new Retraction_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater);
m_retraction_calib_dlg->ShowModal();
}, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
append_menu_item(m_topbar->GetCalibMenu(), wxID_ANY, _L("Orca Tolerance Test"), _L("Orca Tolerance Test"),
[this](wxCommandEvent&) {
m_plater->new_project();
m_plater->add_model(false, Slic3r::resources_dir() + "/calib/tolerance_test/OrcaToleranceTest.stl");
}, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
// Advance calibrations
auto advance_menu = new wxMenu();
append_menu_item(
advance_menu, wxID_ANY, _L("Max flowrate"), _L("Max flowrate"),
[this](wxCommandEvent&) {
if (!m_vol_test_dlg)
m_vol_test_dlg = new MaxVolumetricSpeed_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater);
m_vol_test_dlg->ShowModal();
},
"", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
append_menu_item(
advance_menu, wxID_ANY, _L("VFA"), _L("VFA"),
[this](wxCommandEvent&) {
if (!m_vfa_test_dlg)
m_vfa_test_dlg = new VFA_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater);
m_vfa_test_dlg->ShowModal();
},
"", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
m_topbar->GetCalibMenu()->AppendSubMenu(advance_menu, _L("More..."));
// help
append_menu_item(m_topbar->GetCalibMenu(), wxID_ANY, _L("Tutorial"), _L("Calibration help"),
[this](wxCommandEvent&) {
std::string url = "https://github.com/SoftFever/OrcaSlicer/wiki/Calibration";
if (const std::string country_code = wxGetApp().app_config->get_country_code(); country_code == "CN") {
// Use gitee mirror for China users
url = "https://gitee.com/n0isyfox/orca-slicer-docs/wikis/%E6%A0%A1%E5%87%86/%E6%89%93%E5%8D%B0%E5%8F%82%E6%95%B0%E6%A0%A1%E5%87%86";
}
wxLaunchDefaultBrowser(url, wxBROWSER_NEW_WINDOW);
}, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
#else
m_menubar->Append(fileMenu, wxString::Format("&%s", _L("File")));
if (editMenu)
m_menubar->Append(editMenu, wxString::Format("&%s", _L("Edit")));
if (viewMenu)
m_menubar->Append(viewMenu, wxString::Format("&%s", _L("View")));
/*if (publishMenu)
m_menubar->Append(publishMenu, wxString::Format("&%s", _L("3D Models")));*/
// SoftFever calibrations
auto calib_menu = new wxMenu();
// PA
append_menu_item(calib_menu, wxID_ANY, _L("Temperature"), _L("Temperature"),
[this](wxCommandEvent&) {
if (!m_temp_calib_dlg)
m_temp_calib_dlg = new Temp_Calibration_Dlg((wxWindow*)this, wxID_ANY, m_plater);
m_temp_calib_dlg->ShowModal();
}, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
// Flowrate
auto flowrate_menu = new wxMenu();
append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 1"), _L("Flow rate test - Pass 1"),
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(1); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 2"), _L("Flow rate test - Pass 2"),
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(2); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
append_submenu(calib_menu,flowrate_menu,wxID_ANY,_L("Flow rate"),_L("Flow rate"),"",
[this]() {return m_plater->is_view3D_shown();; });
// PA
append_menu_item(calib_menu, wxID_ANY, _L("Pressure advance"), _L("Pressure advance"),
[this](wxCommandEvent&) {
if (!m_pa_calib_dlg)
m_pa_calib_dlg = new PA_Calibration_Dlg((wxWindow*)this, wxID_ANY, m_plater);
m_pa_calib_dlg->ShowModal();
}, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
// Retraction
append_menu_item(calib_menu, wxID_ANY, _L("Retraction test"), _L("Retraction test"),
[this](wxCommandEvent&) {
if (!m_retraction_calib_dlg)
m_retraction_calib_dlg = new Retraction_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater);
m_retraction_calib_dlg->ShowModal();
}, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
// Tolerance Test
append_menu_item(calib_menu, wxID_ANY, _L("Orca Tolerance Test"), _L("Orca Tolerance Test"),
[this](wxCommandEvent&) {
m_plater->new_project();
m_plater->add_model(false, Slic3r::resources_dir() + "/calib/tolerance_test/OrcaToleranceTest.stl");
}, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
// Advance calibrations
auto advance_menu = new wxMenu();
append_menu_item(
advance_menu, wxID_ANY, _L("Max flowrate"), _L("Max flowrate"),
[this](wxCommandEvent&) {
if (!m_vol_test_dlg)
m_vol_test_dlg = new MaxVolumetricSpeed_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater);
m_vol_test_dlg->ShowModal();
}, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
append_menu_item(
advance_menu, wxID_ANY, _L("VFA"), _L("VFA"),
[this](wxCommandEvent&) {
if (!m_vfa_test_dlg)
m_vfa_test_dlg = new VFA_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater);
m_vfa_test_dlg->ShowModal();
}, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
append_submenu(calib_menu, advance_menu, wxID_ANY, _L("More..."), _L("More calibrations"), "",
[this]() {return m_plater->is_view3D_shown();; });
// help
append_menu_item(calib_menu, wxID_ANY, _L("Tutorial"), _L("Calibration help"),
[this](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/SoftFever/OrcaSlicer/wiki/Calibration", wxBROWSER_NEW_WINDOW); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
m_menubar->Append(calib_menu,wxString::Format("&%s", _L("Calibration")));
if (helpMenu)
m_menubar->Append(helpMenu, wxString::Format("&%s", _L("Help")));
SetMenuBar(m_menubar);
#endif
#ifdef _MSW_DARK_MODE
if (wxGetApp().tabs_as_menu())
m_menubar->EnableTop(6, false);
#endif
#ifdef __APPLE__
// This fixes a bug on Mac OS where the quit command doesn't emit window close events
// wx bug: https://trac.wxwidgets.org/ticket/18328
wxMenu* apple_menu = m_menubar->OSXGetAppleMenu();
if (apple_menu != nullptr) {
apple_menu->Bind(wxEVT_MENU, [this](wxCommandEvent &) {
Close();
}, wxID_EXIT);
}
#endif // __APPLE__
}
void MainFrame::set_max_recent_count(int max)
{
max = max < 0 ? 0 : max > 10000 ? 10000 : max;
size_t count = m_recent_projects.GetCount();
m_recent_projects.SetMaxFiles(max);
if (count != m_recent_projects.GetCount()) {
count = m_recent_projects.GetCount();
std::vector<std::string> recent_projects;
for (size_t i = 0; i < count; ++i) {
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
}
wxGetApp().app_config->set_recent_projects(recent_projects);
wxGetApp().app_config->save();
m_webview->SendRecentList(-1);
}
}
void MainFrame::open_menubar_item(const wxString& menu_name,const wxString& item_name)
{
if (m_menubar == nullptr)
return;
// Get menu object from menubar
int menu_index = m_menubar->FindMenu(menu_name);
wxMenu* menu = m_menubar->GetMenu(menu_index);
if (menu == nullptr) {
BOOST_LOG_TRIVIAL(error) << "Mainframe open_menubar_item function couldn't find menu: " << menu_name;
return;
}
// Get item id from menu
int item_id = menu->FindItem(item_name);
if (item_id == wxNOT_FOUND)
{
// try adding three dots char
item_id = menu->FindItem(item_name + dots);
}
if (item_id == wxNOT_FOUND)
{
BOOST_LOG_TRIVIAL(error) << "Mainframe open_menubar_item function couldn't find item: " << item_name;
return;
}
// wxEVT_MENU will trigger item
wxPostEvent((wxEvtHandler*)menu, wxCommandEvent(wxEVT_MENU, item_id));
}
void MainFrame::init_menubar_as_gcodeviewer()
{
//BBS do not show gcode viewer mebu
#if 0
wxMenu* fileMenu = new wxMenu;
{
append_menu_item(fileMenu, wxID_ANY, _L("&Open G-code") + dots + "\tCtrl+O", _L("Open a G-code file"),
[this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->load_gcode(); }, "open", nullptr,
[this]() {return m_plater != nullptr; }, this);
#ifdef __APPLE__
append_menu_item(fileMenu, wxID_ANY, _L("Re&load from Disk") + dots + "\tCtrl+Shift+R",
_L("Reload the plater from disk"), [this](wxCommandEvent&) { m_plater->reload_gcode_from_disk(); },
"", nullptr, [this]() { return !m_plater->get_last_loaded_gcode().empty(); }, this);
#else
append_menu_item(fileMenu, wxID_ANY, _L("Re&load from Disk") + sep + "F5",
_L("Reload the plater from disk"), [this](wxCommandEvent&) { m_plater->reload_gcode_from_disk(); },
"", nullptr, [this]() { return !m_plater->get_last_loaded_gcode().empty(); }, this);
#endif // __APPLE__
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_ANY, _L("Export &Toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"),
[this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr,
[this]() {return can_export_toolpaths(); }, this);
append_menu_item(fileMenu, wxID_ANY, _L("Open &Studio") + dots, _L("Open Studio"),
[](wxCommandEvent&) { start_new_slicer(); }, "", nullptr,
[]() {return true; }, this);
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME),
[this](wxCommandEvent&) { Close(false); });
}
// View menu
wxMenu* viewMenu = nullptr;
if (m_plater != nullptr) {
viewMenu = new wxMenu();
add_common_view_menu_items(viewMenu, this, std::bind(&MainFrame::can_change_view, this));
}
// helpmenu
auto helpMenu = generate_help_menu();
m_menubar = new wxMenuBar();
m_menubar->Append(fileMenu, _L("&File"));
if (viewMenu != nullptr) m_menubar->Append(viewMenu, _L("&View"));
// Add additional menus from C++
wxGetApp().add_config_menu(m_menubar);
m_menubar->Append(helpMenu, _L("&Help"));
SetMenuBar(m_menubar);
#ifdef __APPLE__
// This fixes a bug on Mac OS where the quit command doesn't emit window close events
// wx bug: https://trac.wxwidgets.org/ticket/18328
wxMenu* apple_menu = m_menubar->OSXGetAppleMenu();
if (apple_menu != nullptr) {
apple_menu->Bind(wxEVT_MENU, [this](wxCommandEvent&) {
Close();
}, wxID_EXIT);
}
#endif // __APPLE__
#endif
}
void MainFrame::update_menubar()
{
if (wxGetApp().is_gcode_viewer())
return;
const bool is_fff = plater()->printer_technology() == ptFFF;
}
void MainFrame::reslice_now()
{
if (m_plater)
m_plater->reslice();
}
struct ConfigsOverwriteConfirmDialog : MessageDialog
{
ConfigsOverwriteConfirmDialog(wxWindow *parent, wxString name, bool exported)
: MessageDialog(parent,
wxString::Format(exported ? _L("A file exists with the same name: %s, do you want to override it.") :
_L("A config exists with the same name: %s, do you want to override it."),
name),
_L(exported ? "Overwrite file" : "Overwrite config"),
wxYES_NO | wxNO_DEFAULT)
{
add_button(wxID_YESTOALL, false, _L("Yes to All"));
add_button(wxID_NOTOALL, false, _L("No to All"));
}
};
void MainFrame::export_config()
{
ExportConfigsDialog export_configs_dlg(nullptr);
export_configs_dlg.ShowModal();
return;
// Generate a cummulative configuration for the selected print, filaments and printer.
wxDirDialog dlg(this, _L("Choose a directory"),
from_u8(!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir()), wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
wxString path;
if (dlg.ShowModal() == wxID_OK)
path = dlg.GetPath();
if (!path.IsEmpty()) {
// Export the config bundle.
wxGetApp().app_config->update_config_dir(into_u8(path));
try {
auto files = wxGetApp().preset_bundle->export_current_configs(into_u8(path), [this](std::string const & name) {
ConfigsOverwriteConfirmDialog dlg(this, from_u8(name), true);
int res = dlg.ShowModal();
int ids[]{wxID_NO, wxID_YES, wxID_NOTOALL, wxID_YESTOALL};
return std::find(ids, ids + 4, res) - ids;
}, false);
if (!files.empty())
m_last_config = from_u8(files.back());
MessageDialog dlg(this, wxString::Format(_L_PLURAL("There is %d config exported. (Only non-system configs)",
"There are %d configs exported. (Only non-system configs)", files.size()), files.size()),
_L("Export result"), wxOK);
dlg.ShowModal();
} catch (const std::exception &ex) {
show_error(this, ex.what());
}
}
}
// Load a config file containing a Print, Filament & Printer preset.
void MainFrame::load_config_file()
{
//BBS do not load config file
// if (!wxGetApp().check_and_save_current_preset_changes(_L("Loading profile file"), "", false))
// 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;*.bbscfg;*.bbsflmt)|*.json;*.zip;*.bbscfg;*.bbsflmt", wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
wxArrayString files;
if (dlg.ShowModal() != wxID_OK)
return;
dlg.GetPaths(files);
std::vector<std::string> cfiles;
for (auto file : files) {
cfiles.push_back(into_u8(file));
m_last_config = file;
}
bool update = false;
wxGetApp().preset_bundle->import_presets(cfiles, [this](std::string const & name) {
ConfigsOverwriteConfirmDialog dlg(this, from_u8(name), false);
int res = dlg.ShowModal();
int ids[]{wxID_NO, wxID_YES, wxID_NOTOALL, wxID_YESTOALL};
return std::find(ids, ids + 4, res) - ids;
},
ForwardCompatibilitySubstitutionRule::Enable);
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();
MessageDialog dlg2(this, wxString::Format(_L_PLURAL("There is %d config imported. (Only non-system and compatible configs)",
"There are %d configs imported. (Only non-system and compatible configs)", cfiles.size()), cfiles.size()),
_L("Import result"), wxOK);
dlg2.ShowModal();
}
// Load a config file containing a Print, Filament & Printer preset from command line.
bool MainFrame::load_config_file(const std::string &path)
{
try {
ConfigSubstitutions config_substitutions = wxGetApp().preset_bundle->load_config_file(path, ForwardCompatibilitySubstitutionRule::Enable);
if (!config_substitutions.empty())
show_substitutions_info(config_substitutions, path);
} catch (const std::exception &ex) {
show_error(this, ex.what());
return false;
}
wxGetApp().load_current_presets();
return true;
}
//BBS: export current config bundle as BBL default reference
//void MainFrame::export_current_configbundle()
//{
// BBS do not export profile
// if (!wxGetApp().check_and_save_current_preset_changes(_L("Exporting current profile bundle"),
// _L("Some presets are modified and the unsaved changes will not be exported into profile bundle."), false, true))
// return;
// // validate current configuration in case it's dirty
// auto err = wxGetApp().preset_bundle->full_config().validate();
// if (! err.empty()) {
// show_error(this, err);
// return;
// }
// // Ask user for a file name.
// wxFileDialog dlg(this, _L("Save BBL Default bundle as:"),
// !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
// "BBL_config_bundle.ini",
// file_wildcards(FT_INI), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
// wxString file;
// if (dlg.ShowModal() == wxID_OK)
// file = dlg.GetPath();
// if (!file.IsEmpty()) {
// // Export the config bundle.
// wxGetApp().app_config->update_config_dir(get_dir_name(file));
// try {
// wxGetApp().preset_bundle->export_current_configbundle(file.ToUTF8().data());
// } catch (const std::exception &ex) {
//show_error(this, ex.what());
// }
// }
//}
//BBS: export all the system preset configs to seperate files
/*void MainFrame::export_system_configs()
{
// Ask user for a file name.
wxDirDialog dlg(this, _L("choose a directory"),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
wxString path;
if (dlg.ShowModal() == wxID_OK)
path = dlg.GetPath();
if (!path.IsEmpty()) {
// Export the config bundle.
wxGetApp().app_config->update_config_dir(path.ToStdString());
try {
wxGetApp().preset_bundle->export_system_configs(path.ToUTF8().data());
} catch (const std::exception &ex) {
show_error(this, ex.what());
}
}
}*/
//void MainFrame::export_configbundle(bool export_physical_printers /*= false*/)
//{
//// ; //BBS do not export config bundle
//}
// Loading a config bundle with an external file name used to be used
// to auto - install a config bundle on a fresh user account,
// but that behavior was not documented and likely buggy.
//void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool reset_user_profile*/)
//{
// ; //BBS do not import config bundle
//}
// Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset.
// Also update the plater with the new presets.
void MainFrame::load_config(const DynamicPrintConfig& config)
{
PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
const auto *opt_printer_technology = config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
if (opt_printer_technology != nullptr && opt_printer_technology->value != printer_technology) {
printer_technology = opt_printer_technology->value;
this->plater()->set_printer_technology(printer_technology);
}
#if 0
for (auto tab : wxGetApp().tabs_list)
if (tab->supports_printer_technology(printer_technology)) {
if (tab->type() == Slic3r::Preset::TYPE_PRINTER)
static_cast<TabPrinter*>(tab)->update_pages();
tab->load_config(config);
}
if (m_plater)
m_plater->on_config_change(config);
#else
// Load the currently selected preset into the GUI, update the preset selection box.
//FIXME this is not quite safe for multi-extruder printers,
// as the number of extruders is not adjusted for the vector values.
// (see PresetBundle::update_multi_material_filament_presets())
// Better to call PresetBundle::load_config() instead?
for (auto tab : wxGetApp().tabs_list)
if (tab->supports_printer_technology(printer_technology)) {
// Only apply keys, which are present in the tab's config. Ignore the other keys.
for (const std::string &opt_key : tab->get_config()->diff(config))
// Ignore print_settings_id, printer_settings_id, filament_settings_id etc.
if (! boost::algorithm::ends_with(opt_key, "_settings_id"))
tab->get_config()->option(opt_key)->set(config.option(opt_key));
}
wxGetApp().load_current_presets();
#endif
}
//BBS: GUI refactor
void MainFrame::select_tab(wxPanel* panel)
{
if (!panel)
return;
if (panel == m_param_panel) {
panel = m_plater;
} else if (dynamic_cast<ParamsPanel*>(panel)) {
wxGetApp().params_dialog()->Popup();
return;
}
int page_idx = m_tabpanel->FindPage(panel);
if (page_idx == tp3DEditor && m_tabpanel->GetSelection() == tpPreview)
return;
//BBS GUI refactor: remove unused layout new/dlg
/*if (page_idx != wxNOT_FOUND && m_layout == ESettingsLayout::Dlg)
page_idx++;*/
select_tab(size_t(page_idx));
}
//BBS
void MainFrame::jump_to_monitor(std::string dev_id)
{
m_tabpanel->SetSelection(tpMonitor);
((MonitorPanel*)m_monitor)->select_machine(dev_id);
}
//BBS GUI refactor: remove unused layout new/dlg
void MainFrame::select_tab(size_t tab/* = size_t(-1)*/)
{
//bool tabpanel_was_hidden = false;
// Controls on page are created on active page of active tab now.
// We should select/activate tab before its showing to avoid an UI-flickering
auto select = [this, tab](bool was_hidden) {
// when tab == -1, it means we should show the last selected tab
//BBS GUI refactor: remove unused layout new/dlg
//size_t new_selection = tab == (size_t)(-1) ? m_last_selected_tab : (m_layout == ESettingsLayout::Dlg && tab != 0) ? tab - 1 : tab;
size_t new_selection = tab == (size_t)(-1) ? m_last_selected_tab : tab;
if (m_tabpanel->GetSelection() != (int)new_selection)
m_tabpanel->SetSelection(new_selection);
#ifdef _MSW_DARK_MODE
/*if (wxGetApp().tabs_as_menu()) {
if (Tab* cur_tab = dynamic_cast<Tab*>(m_tabpanel->GetPage(new_selection)))
update_marker_for_tabs_menu((m_layout == ESettingsLayout::Old ? m_menubar : m_settings_dialog.menubar()), cur_tab->title(), m_layout == ESettingsLayout::Old);
else if (tab == 0 && m_layout == ESettingsLayout::Old)
m_plater->get_current_canvas3D()->render();
}*/
#endif
if (tab == MainFrame::tp3DEditor && m_layout == ESettingsLayout::Old)
m_plater->canvas3D()->render();
else if (was_hidden) {
Tab* cur_tab = dynamic_cast<Tab*>(m_tabpanel->GetPage(new_selection));
if (cur_tab)
cur_tab->OnActivate();
}
};
select(false);
}
void MainFrame::request_select_tab(TabPosition pos)
{
wxCommandEvent* evt = new wxCommandEvent(EVT_SELECT_TAB);
evt->SetInt(pos);
wxQueueEvent(this, evt);
}
int MainFrame::get_calibration_curr_tab() {
if (m_calibration)
return m_calibration->get_tabpanel()->GetSelection();
return -1;
}
// Set a camera direction, zoom to all objects.
void MainFrame::select_view(const std::string& direction)
{
if (m_plater)
m_plater->select_view(direction);
}
// #ys_FIXME_to_delete
void MainFrame::on_presets_changed(SimpleEvent &event)
{
auto *tab = dynamic_cast<Tab*>(event.GetEventObject());
wxASSERT(tab != nullptr);
if (tab == nullptr) {
return;
}
// Update preset combo boxes(Print settings, Filament, Material, Printer) from their respective tabs.
auto presets = tab->get_presets();
if (m_plater != nullptr && presets != nullptr) {
// FIXME: The preset type really should be a property of Tab instead
Slic3r::Preset::Type preset_type = tab->type();
if (preset_type == Slic3r::Preset::TYPE_INVALID) {
wxASSERT(false);
return;
}
m_plater->on_config_change(*tab->get_config());
m_plater->sidebar().update_presets(preset_type);
}
}
// #ys_FIXME_to_delete
void MainFrame::on_value_changed(wxCommandEvent& event)
{
auto *tab = dynamic_cast<Tab*>(event.GetEventObject());
wxASSERT(tab != nullptr);
if (tab == nullptr)
return;
auto opt_key = event.GetString();
if (m_plater) {
m_plater->on_config_change(*tab->get_config()); // propagate config change events to the plater
if (opt_key == "extruders_count") {
auto value = event.GetInt();
m_plater->on_filaments_change(value);
}
}
}
void MainFrame::on_config_changed(DynamicPrintConfig* config) const
{
if (m_plater)
m_plater->on_config_change(*config); // propagate config change events to the plater
}
void MainFrame::set_print_button_to_default(PrintSelectType select_type)
{
if (select_type == PrintSelectType::ePrintPlate) {
m_print_btn->SetLabel(_L("Print plate"));
m_print_select = ePrintPlate;
if (m_print_enable)
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
} else if (select_type == PrintSelectType::eSendGcode) {
m_print_btn->SetLabel(_L("Print"));
m_print_select = eSendGcode;
if (m_print_enable)
m_print_enable = get_enable_print_status() && can_send_gcode();
m_print_btn->Enable(m_print_enable);
this->Layout();
} else if (select_type == PrintSelectType::eExportGcode) {
m_print_btn->SetLabel(_L("Export G-code file"));
m_print_select = eExportGcode;
if (m_print_enable)
m_print_enable = get_enable_print_status() && can_send_gcode();
m_print_btn->Enable(m_print_enable);
this->Layout();
} else {
// unsupport
return;
}
}
void MainFrame::add_to_recent_projects(const wxString& filename)
{
if (wxFileExists(filename))
{
m_recent_projects.AddFileToHistory(filename);
std::vector<std::string> recent_projects;
size_t count = m_recent_projects.GetCount();
for (size_t i = 0; i < count; ++i)
{
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
}
wxGetApp().app_config->set_recent_projects(recent_projects);
m_webview->SendRecentList(0);
}
}
std::wstring MainFrame::FileHistory::GetThumbnailUrl(int index) const
{
if (m_thumbnails[index].empty()) return L"";
std::wstringstream wss;
wss << L"data:image/png;base64,";
wss << wxBase64Encode(m_thumbnails[index].data(), m_thumbnails[index].size());
return wss.str();
}
void MainFrame::FileHistory::AddFileToHistory(const wxString &file)
{
if (this->m_fileMaxFiles == 0)
return;
wxFileHistory::AddFileToHistory(file);
if (m_load_called)
m_thumbnails.push_front(bbs_3mf_get_thumbnail(into_u8(file).c_str()));
else
m_thumbnails.push_front("");
}
void MainFrame::FileHistory::RemoveFileFromHistory(size_t i)
{
if (i >= m_thumbnails.size()) // FIX zero max
return;
wxFileHistory::RemoveFileFromHistory(i);
m_thumbnails.erase(m_thumbnails.begin() + i);
}
size_t MainFrame::FileHistory::FindFileInHistory(const wxString & file)
{
return m_fileHistory.Index(file);
}
void MainFrame::FileHistory::LoadThumbnails()
{
tbb::parallel_for(tbb::blocked_range<size_t>(0, GetCount()), [this](tbb::blocked_range<size_t> range) {
for (size_t i = range.begin(); i < range.end(); ++i) {
auto thumbnail = bbs_3mf_get_thumbnail(into_u8(GetHistoryFile(i)).c_str());
if (!thumbnail.empty()) {
m_thumbnails[i] = thumbnail;
}
}
});
m_load_called = true;
}
inline void MainFrame::FileHistory::SetMaxFiles(int max)
{
m_fileMaxFiles = max;
size_t numFiles = m_fileHistory.size();
while (numFiles > m_fileMaxFiles)
RemoveFileFromHistory(--numFiles);
}
void MainFrame::get_recent_projects(boost::property_tree::wptree &tree, int images)
{
for (size_t i = 0; i < m_recent_projects.GetCount(); ++i) {
boost::property_tree::wptree item;
std::wstring proj = m_recent_projects.GetHistoryFile(i).ToStdWstring();
item.put(L"project_name", proj.substr(proj.find_last_of(L"/\\") + 1));
item.put(L"path", proj);
boost::system::error_code ec;
std::time_t t = boost::filesystem::last_write_time(proj, ec);
if (!ec) {
std::wstring time = wxDateTime(t).FormatISOCombined(' ').ToStdWstring();
item.put(L"time", time);
if (i <= images) {
auto thumbnail = m_recent_projects.GetThumbnailUrl(i);
if (!thumbnail.empty()) item.put(L"image", thumbnail);
}
} else {
item.put(L"time", _L("File is missing"));
}
tree.push_back({L"", item});
}
}
void MainFrame::open_recent_project(size_t file_id, wxString const & filename)
{
if (file_id == size_t(-1)) {
file_id = m_recent_projects.FindFileInHistory(filename);
}
if (wxFileExists(filename)) {
CallAfter([this, filename] {
if (wxGetApp().can_load_project())
m_plater->load_project(filename);
});
}
else
{
MessageDialog msg(this, _L("The project is no longer available."), _L("Error"), wxOK | wxYES_DEFAULT);
if (msg.ShowModal() == wxID_YES)
{
m_recent_projects.RemoveFileFromHistory(file_id);
std::vector<std::string> recent_projects;
size_t count = m_recent_projects.GetCount();
for (size_t i = 0; i < count; ++i)
{
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
}
wxGetApp().app_config->set_recent_projects(recent_projects);
m_webview->SendRecentList(-1);
}
}
}
void MainFrame::remove_recent_project(size_t file_id, wxString const &filename)
{
if (file_id == size_t(-1)) {
if (filename.IsEmpty())
while (m_recent_projects.GetCount() > 0)
m_recent_projects.RemoveFileFromHistory(0);
else
file_id = m_recent_projects.FindFileInHistory(filename);
}
if (file_id != size_t(-1))
m_recent_projects.RemoveFileFromHistory(file_id);
std::vector<std::string> recent_projects;
size_t count = m_recent_projects.GetCount();
for (size_t i = 0; i < count; ++i)
{
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
}
wxGetApp().app_config->set_recent_projects(recent_projects);
m_webview->SendRecentList(-1);
}
void MainFrame::load_url(wxString url)
{
BOOST_LOG_TRIVIAL(trace) << "load_url:" << url;
auto evt = new wxCommandEvent(EVT_LOAD_URL, this->GetId());
evt->SetString(url);
wxQueueEvent(this, evt);
}
void MainFrame::load_printer_url(wxString url, wxString apikey)
{
BOOST_LOG_TRIVIAL(trace) << "load_printer_url:" << url;
auto evt = new LoadPrinterViewEvent(EVT_LOAD_PRINTER_URL, this->GetId());
evt->SetString(url);
evt->SetAPIkey(apikey);
wxQueueEvent(this, evt);
}
void MainFrame::load_printer_url()
{
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
if (preset_bundle.is_bbl_vendor())
return;
auto cfg = preset_bundle.printers.get_edited_preset().config;
wxString url = cfg.opt_string("print_host_webui").empty() ? cfg.opt_string("print_host") : cfg.opt_string("print_host_webui");
wxString apikey;
if (cfg.has("printhost_apikey") && (cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value == htPrusaLink ||
cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value == htPrusaConnect))
apikey = cfg.opt_string("printhost_apikey");
if (!url.empty()) {
if (!url.Lower().starts_with("http"))
url = wxString::Format("http://%s", url);
load_printer_url(url, apikey);
}
}
bool MainFrame::is_printer_view() const { return m_tabpanel->GetSelection() == TabPosition::tpMonitor; }
void MainFrame::refresh_plugin_tips()
{
if (m_webview != nullptr)
m_webview->ShowNetpluginTip();
}
void MainFrame::RunScript(wxString js)
{
if (m_webview != nullptr)
m_webview->RunScript(js);
}
void MainFrame::technology_changed()
{
// upadte DiffDlg
diff_dialog.update_presets();
// update menu titles
PrinterTechnology pt = plater()->printer_technology();
if (int id = m_menubar->FindMenu(pt == ptFFF ? _omitL("Material Settings") : _L("Filament Settings")); id != wxNOT_FOUND)
m_menubar->SetMenuLabel(id, pt == ptSLA ? _omitL("Material Settings") : _L("Filament Settings"));
}
//
// Called after the Preferences dialog is closed and the program settings are saved.
// Update the UI based on the current preferences.
void MainFrame::update_ui_from_settings()
{
if (m_plater)
m_plater->update_ui_from_settings();
for (auto tab: wxGetApp().tabs_list)
tab->update_ui_from_settings();
}
void MainFrame::show_sync_dialog()
{
SimpleEvent* evt = new SimpleEvent(EVT_SYNC_CLOUD_PRESET);
wxQueueEvent(this, evt);
}
void MainFrame::update_side_preset_ui()
{
// select last preset
for (auto tab : wxGetApp().tabs_list) {
tab->update_tab_ui();
}
//BBS: update the preset
m_plater->sidebar().update_presets(Preset::TYPE_PRINTER);
m_plater->sidebar().update_presets(Preset::TYPE_FILAMENT);
}
void MainFrame::on_select_default_preset(SimpleEvent& evt)
{
MessageDialog dialog(this,
_L("Do you want to synchronize your personal data from Bambu Cloud? \n"
"It contains the following information:\n"
"1. The Process presets\n"
"2. The Filament presets\n"
"3. The Printer presets"),
_L("Synchronization"),
wxCENTER |
wxYES_DEFAULT | wxYES_NO |
wxICON_INFORMATION);
/* get setting list */
NetworkAgent* agent = wxGetApp().getAgent();
switch ( dialog.ShowModal() )
{
case wxID_YES: {
wxGetApp().app_config->set_bool("sync_user_preset", true);
wxGetApp().start_sync_user_preset(true);
break;
}
case wxID_NO:
wxGetApp().app_config->set_bool("sync_user_preset", false);
wxGetApp().stop_sync_user_preset();
break;
default:
break;
}
update_side_preset_ui();
}
std::string MainFrame::get_base_name(const wxString &full_name, const char *extension) const
{
boost::filesystem::path filename = boost::filesystem::path(full_name.wx_str()).filename();
if (extension != nullptr)
filename = filename.replace_extension(extension);
return filename.string();
}
std::string MainFrame::get_dir_name(const wxString &full_name) const
{
return boost::filesystem::path(into_u8(full_name)).parent_path().string();
}
// ----------------------------------------------------------------------------
// SettingsDialog
// ----------------------------------------------------------------------------
SettingsDialog::SettingsDialog(MainFrame* mainframe)
:DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "settings_dialog"),
//: DPIDialog(mainframe, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings"), wxDefaultPosition, wxDefaultSize,
// wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX, "settings_dialog"),
m_main_frame(mainframe)
{
if (wxGetApp().is_gcode_viewer())
return;
#if defined(__WXMSW__)
// ys_FIXME! temporary workaround for correct font scaling
// Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts,
// From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT
this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
#else
this->SetFont(wxGetApp().normal_font());
this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#endif // __WXMSW__
// Load the icon either from the exe, or from the ico file.
#if _WIN32
{
TCHAR szExeFileName[MAX_PATH];
GetModuleFileName(nullptr, szExeFileName, MAX_PATH);
SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO));
}
#else
SetIcon(wxIcon(var("OrcaSlicer_128px.png"), wxBITMAP_TYPE_PNG));
#endif // _WIN32
//just hide the Frame on closing
this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& evt) { this->Hide(); });
#ifdef _MSW_DARK_MODE
if (wxGetApp().tabs_as_menu()) {
// menubar
//m_menubar = new wxMenuBar();
//add_tabs_as_menu(m_menubar, mainframe, this);
//this->SetMenuBar(m_menubar);
}
#endif
// initialize layout
auto sizer = new wxBoxSizer(wxVERTICAL);
sizer->SetSizeHints(this);
SetSizer(sizer);
Fit();
const wxSize min_size = wxSize(85 * em_unit(), 50 * em_unit());
#ifdef __APPLE__
// Using SetMinSize() on Mac messes up the window position in some cases
// cf. https://groups.google.com/forum/#!topic/wx-users/yUKPBBfXWO0
SetSize(min_size);
#else
SetMinSize(min_size);
SetSize(GetMinSize());
#endif
Layout();
}
void SettingsDialog::on_dpi_changed(const wxRect& suggested_rect)
{
if (wxGetApp().is_gcode_viewer())
return;
const int& em = em_unit();
const wxSize& size = wxSize(85 * em, 50 * em);
// BBS
m_tabpanel->Rescale();
// update Tabs
for (auto tab : wxGetApp().tabs_list)
tab->msw_rescale();
SetMinSize(size);
Fit();
Refresh();
}
} // GUI
} // Slic3r