2018-09-20 08:40:22 +02:00
#include "MainFrame.hpp"
#include <wx/panel.h>
#include <wx/notebook.h>
#include <wx/icon.h>
#include <wx/sizer.h>
#include <wx/menu.h>
#include <wx/progdlg.h>
2018-09-21 15:42:31 +02:00
#include <wx/tooltip.h>
2020-05-12 23:07:06 +02:00
//#include <wx/glcanvas.h>
2020-08-26 14:56:26 +02:00
#include <wx/filename.h>
#include <wx/stdpaths.h>
2018-10-03 16:27:02 +02:00
#include <wx/debug.h>
2018-09-20 08:40:22 +02:00
2019-01-09 10:43:17 +01:00
#include <boost/algorithm/string/predicate.hpp>
2018-11-26 14:41:58 +01:00
#include "libslic3r/Print.hpp"
#include "libslic3r/Polygon.hpp"
2019-05-02 13:46:39 +02:00
#include "libslic3r/SLAPrint.hpp"
2020-06-16 16:58:41 +02:00
#include "libslic3r/PresetBundle.hpp"
2018-11-26 14:41:58 +01:00
2018-09-20 08:40:22 +02:00
#include "Tab.hpp"
#include "ProgressStatusBar.hpp"
#include "3DScene.hpp"
2018-12-14 15:27:34 +01:00
#include "PrintHostDialogs.hpp"
2018-10-17 12:17:25 +02:00
#include "wxExtensions.hpp"
2019-01-30 10:05:54 +01:00
#include "GUI_ObjectList.hpp"
2019-09-27 14:52:19 +02:00
#include "Mouse3DController.hpp"
2020-03-06 15:10:58 +01:00
#include "RemovableDriveManager.hpp"
2020-04-29 10:50:28 +02:00
#include "InstanceCheck.hpp"
2018-11-26 14:41:58 +01:00
#include "I18N.hpp"
2020-05-11 22:00:26 +02:00
#include "GLCanvas3D.hpp"
2020-05-26 17:42:57 +02:00
#include "Plater.hpp"
2018-09-20 08:40:22 +02:00
#include <fstream>
2018-09-21 01:33:41 +02:00
#include "GUI_App.hpp"
2018-09-20 08:40:22 +02:00
2020-03-13 14:19:02 +01:00
#ifdef _WIN32
#include <dbt.h>
2020-03-27 08:10:00 +01:00
#include <shlobj.h>
2020-03-13 14:19:02 +01:00
#endif // _WIN32
2020-08-26 15:50:05 +02:00
// For starting another PrusaSlicer instance on OSX.
// Fails to compile on Windows on the build server.
#ifdef __APPLE__
#include <boost/process/spawn.hpp>
#endif
2018-09-20 08:40:22 +02:00
namespace Slic3r {
2018-09-21 01:33:41 +02:00
namespace GUI {
2020-06-19 11:04:08 +02:00
enum class ERescaleTarget
{
Mainframe ,
SettingsDialog
};
static void rescale_dialog_after_dpi_change ( MainFrame & mainframe , SettingsDialog & dialog , ERescaleTarget target )
{
int mainframe_dpi = get_dpi_for_window ( & mainframe );
int dialog_dpi = get_dpi_for_window ( & dialog );
if ( mainframe_dpi != dialog_dpi ) {
if ( target == ERescaleTarget :: SettingsDialog ) {
dialog . enable_force_rescale ();
#if wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3)
dialog . GetEventHandler () -> AddPendingEvent ( wxDPIChangedEvent ( wxSize ( mainframe_dpi , mainframe_dpi ), wxSize ( dialog_dpi , dialog_dpi )));
#else
dialog . GetEventHandler () -> AddPendingEvent ( DpiChangedEvent ( EVT_DPI_CHANGED_SLICER , dialog_dpi , dialog . GetRect ()));
#endif // wxVERSION_EQUAL_OR_GREATER_THAN
} else {
#if wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3)
mainframe . GetEventHandler () -> AddPendingEvent ( wxDPIChangedEvent ( wxSize ( dialog_dpi , dialog_dpi ), wxSize ( mainframe_dpi , mainframe_dpi )));
#else
mainframe . enable_force_rescale ();
mainframe . GetEventHandler () -> AddPendingEvent ( DpiChangedEvent ( EVT_DPI_CHANGED_SLICER , mainframe_dpi , mainframe . GetRect ()));
#endif // wxVERSION_EQUAL_OR_GREATER_THAN
}
}
}
2018-12-19 17:38:41 +01:00
MainFrame :: MainFrame () :
2019-05-14 15:00:24 +02:00
DPIFrame ( NULL , wxID_ANY , "" , wxDefaultPosition , wxDefaultSize , wxDEFAULT_FRAME_STYLE , "mainframe" ),
m_printhost_queue_dlg ( new PrintHostQueueDialog ( this ))
2019-07-12 15:36:01 +02:00
, m_recent_projects ( 9 )
2020-06-11 14:02:59 +02:00
, m_settings_dialog ( this )
2018-09-20 08:40:22 +02:00
{
2019-05-06 18:28:23 +02:00
// Fonts were created by the DPIFrame constructor for the monitor, on which the window opened.
wxGetApp (). update_fonts ( this );
2019-06-19 11:38:42 +02:00
/*
2019-05-14 15:40:30 +02:00
#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
2019-05-06 18:28:23 +02:00
this->SetFont(this->normal_font());
2019-05-14 15:40:30 +02:00
#endif
2019-06-19 11:38:42 +02:00
// Font is already set in DPIFrame constructor
*/
2020-08-26 13:01:54 +02:00
2019-05-13 14:11:21 +02:00
SetIcon ( wxIcon ( Slic3r :: var ( "PrusaSlicer_128px.png" ), wxBITMAP_TYPE_PNG ));
2020-08-26 13:01:54 +02:00
// // 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(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG));
//#endif // _WIN32
2018-09-20 08:40:22 +02:00
2018-12-04 11:14:39 +01:00
// initialize status bar
2019-12-16 09:47:31 +01:00
m_statusbar = std :: make_shared < ProgressStatusBar > ( this );
m_statusbar -> set_font ( GUI :: wxGetApp (). normal_font ());
2018-12-04 11:14:39 +01:00
m_statusbar -> embed ( this );
2019-01-21 12:34:28 +01:00
m_statusbar -> set_status_text ( _ ( L ( "Version" )) + " " +
2018-12-04 11:14:39 +01:00
SLIC3R_VERSION +
2020-06-14 23:53:17 +02:00
_ ( L ( " - Remember to check for updates at https://github.com/prusa3d/PrusaSlicer/releases" )));
2018-12-04 11:14:39 +01:00
2018-09-20 08:40:22 +02:00
// initialize tabpanel and menubar
2018-09-21 01:33:41 +02:00
init_tabpanel ();
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-05 16:50:17 +02:00
init_editor_menubar ();
init_gcodeviewer_menubar ();
#if _WIN32
// This is needed on Windows to fake the CTRL+# of the window menu when using the numpad
wxAcceleratorEntry entries [ 6 ];
entries [ 0 ]. Set ( wxACCEL_CTRL , WXK_NUMPAD1 , wxID_HIGHEST + 1 );
entries [ 1 ]. Set ( wxACCEL_CTRL , WXK_NUMPAD2 , wxID_HIGHEST + 2 );
entries [ 2 ]. Set ( wxACCEL_CTRL , WXK_NUMPAD3 , wxID_HIGHEST + 3 );
entries [ 3 ]. Set ( wxACCEL_CTRL , WXK_NUMPAD4 , wxID_HIGHEST + 4 );
entries [ 4 ]. Set ( wxACCEL_CTRL , WXK_NUMPAD5 , wxID_HIGHEST + 5 );
entries [ 5 ]. Set ( wxACCEL_CTRL , WXK_NUMPAD6 , wxID_HIGHEST + 6 );
wxAcceleratorTable accel ( 6 , entries );
SetAcceleratorTable ( accel );
#endif // _WIN32
#else
2018-09-21 01:33:41 +02:00
init_menubar ();
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2018-09-20 08:40:22 +02:00
// 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.)
2018-09-21 01:33:41 +02:00
wxToolTip :: SetAutoPop ( 32767 );
2018-09-20 08:40:22 +02:00
2018-09-21 01:33:41 +02:00
m_loaded = true ;
// initialize layout
2020-06-29 15:23:24 +02:00
m_main_sizer = new wxBoxSizer ( wxVERTICAL );
wxSizer * sizer = new wxBoxSizer ( wxVERTICAL );
sizer -> Add ( m_main_sizer , 1 , wxEXPAND );
2020-06-11 14:02:59 +02:00
SetSizer ( sizer );
// initialize layout from config
update_layout ();
sizer -> SetSizeHints ( this );
Fit ();
2019-03-18 10:18:24 +01:00
2020-05-22 15:23:05 +02:00
const wxSize min_size = wxGetApp (). get_min_size (); //wxSize(76*wxGetApp().em_unit(), 49*wxGetApp().em_unit());
2019-01-21 17:36:25 +01:00
#ifdef __APPLE__
// Using SetMinSize() on Mac messes up the window position in some cases
// cf. https://groups.google.com/forum/#!topic/wx-users/yUKPBBfXWO0
2019-03-18 10:18:24 +01:00
SetSize ( min_size /*wxSize(760, 490)*/ );
2019-01-21 17:36:25 +01:00
#else
2019-03-18 10:18:24 +01:00
SetMinSize ( min_size /*wxSize(760, 490)*/ );
2018-09-21 01:33:41 +02:00
SetSize ( GetMinSize ());
2019-01-21 17:36:25 +01:00
#endif
2018-09-21 01:33:41 +02:00
Layout ();
2018-09-20 08:40:22 +02:00
2019-05-14 15:00:24 +02:00
update_title ();
2018-09-20 08:40:22 +02:00
// declare events
2020-03-13 14:19:02 +01:00
Bind ( wxEVT_CREATE , [ this ]( wxWindowCreateEvent & event ) {
#ifdef _WIN32
//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;
2020-03-27 08:10:00 +01:00
// 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 );
}
2020-03-31 09:01:48 +02:00
{
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" ;
}
2020-03-13 14:19:02 +01:00
#endif // _WIN32
// propagate event
event . Skip ();
});
2018-10-31 12:56:08 +01:00
Bind ( wxEVT_CLOSE_WINDOW , [ this ]( wxCloseEvent & event ) {
2018-09-21 01:33:41 +02:00
if ( event . CanVeto () && ! wxGetApp (). check_unsaved_changes ()) {
event . Veto ();
return ;
}
2020-03-07 12:24:40 +01:00
this -> shutdown ();
2018-09-21 01:33:41 +02:00
// propagate event
event . Skip ();
});
2018-09-20 08:40:22 +02:00
2019-02-19 14:57:59 +01:00
Bind ( wxEVT_ACTIVATE , [ this ]( wxActivateEvent & event ) {
if ( m_plater != nullptr && event . GetActive ())
m_plater -> on_activate ();
2019-02-20 12:21:22 +01:00
event . Skip ();
2019-02-19 14:57:59 +01:00
});
2019-04-29 16:55:41 +02:00
wxGetApp (). persist_window_geometry ( this , true );
2020-06-11 14:02:59 +02:00
wxGetApp (). persist_window_geometry ( & m_settings_dialog , true );
2018-10-17 14:01:10 +02:00
2019-01-11 18:09:21 +01:00
update_ui_from_settings (); // FIXME (?)
2020-02-28 11:19:46 +01:00
2020-07-29 15:40:28 +02:00
if ( m_plater != nullptr ) {
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-07-29 15:40:28 +02:00
m_plater -> get_collapse_toolbar (). set_enabled ( wxGetApp (). app_config -> get ( "show_collapse_button" ) == "1" );
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2020-02-28 11:19:46 +01:00
m_plater -> show_action_buttons ( true );
2020-07-29 15:40:28 +02:00
}
2018-09-20 08:40:22 +02:00
}
2020-06-11 14:02:59 +02:00
void MainFrame :: update_layout ()
{
2020-06-16 15:16:28 +02:00
auto restore_to_creation = [ this ]() {
auto clean_sizer = []( wxSizer * sizer ) {
while ( ! sizer -> GetChildren (). IsEmpty ()) {
sizer -> Detach ( 0 );
}
};
2020-06-17 09:34:33 +02:00
// 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 );
2020-06-16 15:16:28 +02:00
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 ;
}
2020-06-19 11:04:08 +02:00
if ( m_layout == ESettingsLayout :: Dlg )
rescale_dialog_after_dpi_change ( * this , m_settings_dialog , ERescaleTarget :: Mainframe );
2020-06-29 15:23:24 +02:00
clean_sizer ( m_main_sizer );
2020-06-16 15:16:28 +02:00
clean_sizer ( m_settings_dialog . GetSizer ());
if ( m_settings_dialog . IsShown ())
m_settings_dialog . Close ();
m_tabpanel -> Hide ();
m_plater -> Hide ();
Layout ();
};
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-19 15:32:44 +02:00
ESettingsLayout layout = ( m_mode == EMode :: GCodeViewer ) ? ESettingsLayout :: GCodeViewer :
( wxGetApp (). app_config -> get ( "old_settings_layout_mode" ) == "1" ? ESettingsLayout :: Old :
wxGetApp (). app_config -> get ( "new_settings_layout_mode" ) == "1" ? ESettingsLayout :: New :
wxGetApp (). app_config -> get ( "dlg_settings_layout_mode" ) == "1" ? ESettingsLayout :: Dlg : ESettingsLayout :: Old );
#else
2020-06-11 14:02:59 +02:00
ESettingsLayout layout = wxGetApp (). app_config -> get ( "old_settings_layout_mode" ) == "1" ? ESettingsLayout :: Old :
wxGetApp (). app_config -> get ( "new_settings_layout_mode" ) == "1" ? ESettingsLayout :: New :
wxGetApp (). app_config -> get ( "dlg_settings_layout_mode" ) == "1" ? ESettingsLayout :: Dlg : ESettingsLayout :: Old ;
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2020-06-11 14:02:59 +02:00
if ( m_layout == layout )
return ;
wxBusyCursor busy ;
Freeze ();
2020-06-16 15:16:28 +02:00
// Remove old settings
if ( m_layout != ESettingsLayout :: Unknown )
restore_to_creation ();
2020-06-11 14:02:59 +02:00
m_layout = layout ;
// From the very beginning the Print settings should be selected
m_last_selected_tab = m_layout == ESettingsLayout :: Dlg ? 0 : 1 ;
// Set new settings
2020-06-16 15:16:28 +02:00
switch ( m_layout )
{
2020-07-08 13:33:50 +02:00
case ESettingsLayout :: Unknown :
{
break ;
}
2020-06-16 15:16:28 +02:00
case ESettingsLayout :: Old :
{
2020-06-11 14:02:59 +02:00
m_plater -> Reparent ( m_tabpanel );
m_tabpanel -> InsertPage ( 0 , m_plater , _L ( "Plater" ));
2020-06-29 15:23:24 +02:00
m_main_sizer -> Add ( m_tabpanel , 1 , wxEXPAND );
2020-06-16 15:16:28 +02:00
m_plater -> Show ();
m_tabpanel -> Show ();
break ;
}
case ESettingsLayout :: New :
{
2020-06-29 15:23:24 +02:00
m_main_sizer -> Add ( m_plater , 1 , wxEXPAND );
2020-06-16 15:16:28 +02:00
m_tabpanel -> Hide ();
2020-06-29 15:23:24 +02:00
m_main_sizer -> Add ( m_tabpanel , 1 , wxEXPAND );
2020-06-16 15:16:28 +02:00
m_plater_page = new wxPanel ( m_tabpanel );
m_tabpanel -> InsertPage ( 0 , m_plater_page , _L ( "Plater" )); // empty panel just for Plater tab */
m_plater -> Show ();
break ;
}
case ESettingsLayout :: Dlg :
{
2020-06-29 15:23:24 +02:00
m_main_sizer -> Add ( m_plater , 1 , wxEXPAND );
2020-06-16 15:16:28 +02:00
m_tabpanel -> Reparent ( & m_settings_dialog );
m_settings_dialog . GetSizer () -> Add ( m_tabpanel , 1 , wxEXPAND );
2020-06-19 11:04:08 +02:00
rescale_dialog_after_dpi_change ( * this , m_settings_dialog , ERescaleTarget :: SettingsDialog );
2020-06-16 15:16:28 +02:00
m_tabpanel -> Show ();
m_plater -> Show ();
break ;
}
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-19 15:32:44 +02:00
case ESettingsLayout :: GCodeViewer :
{
2020-07-03 13:04:52 +02:00
m_main_sizer -> Add ( m_plater , 1 , wxEXPAND );
2020-06-19 15:32:44 +02:00
m_plater -> Show ();
break ;
}
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2020-06-11 14:02:59 +02:00
}
2020-06-12 12:35:17 +02:00
//#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
2020-06-11 14:02:59 +02:00
Layout ();
Thaw ();
}
2020-03-07 12:24:40 +01:00
// Called when closing the application and when switching the application language.
void MainFrame :: shutdown ()
{
2020-03-13 14:19:02 +01:00
#ifdef _WIN32
2020-03-27 08:10:00 +01:00
if ( m_hDeviceNotify ) {
:: UnregisterDeviceNotification ( HDEVNOTIFY ( m_hDeviceNotify ));
m_hDeviceNotify = nullptr ;
}
if ( m_ulSHChangeNotifyRegister ) {
SHChangeNotifyDeregister ( m_ulSHChangeNotifyRegister );
m_ulSHChangeNotifyRegister = 0 ;
}
2020-03-13 14:19:02 +01:00
#endif // _WIN32
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-05 16:50:17 +02:00
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
// see: https://github.com/prusa3d/PrusaSlicer/issues/3964
m_plater -> reset_canvas_volumes ();
}
#else
2020-03-07 12:24:40 +01:00
if ( m_plater )
m_plater -> stop_jobs ();
2020-03-09 16:03:15 +01:00
// 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
if ( m_plater ) m_plater -> unbind_canvas_event_handlers ();
2020-04-01 09:58:31 +02:00
// Cleanup of canvases' volumes needs to be done here or a crash may happen on some Linux Debian flavours
// see: https://github.com/prusa3d/PrusaSlicer/issues/3964
if ( m_plater ) m_plater -> reset_canvas_volumes ();
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2020-03-09 16:03:15 +01:00
2020-03-07 12:24:40 +01:00
// 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 );
2020-06-11 14:02:59 +02:00
if ( m_settings_dialog . IsShown ())
// call Close() to trigger call to lambda defined into GUI_App::persist_window_geometry()
m_settings_dialog . Close ();
2020-05-05 09:35:28 +02:00
2020-06-05 16:50:17 +02:00
if ( m_plater != nullptr ) {
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-08 09:12:20 +02:00
// restore sidebar if it was hidden when switching to gcode viewer mode
2020-06-05 16:50:17 +02:00
if ( m_restore_from_gcode_viewer . collapsed_sidebar )
m_plater -> collapse_sidebar ( false );
2020-08-21 11:36:08 +02:00
// restore sla printer if it was deselected when switching to gcode viewer mode
if ( m_restore_from_gcode_viewer . sla_technology )
m_plater -> set_printer_technology ( ptSLA );
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2020-06-05 16:50:17 +02:00
// 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 );
}
2020-03-07 12:24:40 +01:00
// 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 ();
2020-04-29 10:50:28 +02:00
//stop listening for messages from other instances
2020-05-14 12:15:02 +02:00
wxGetApp (). other_instance_message_handler () -> shutdown ( this );
2020-03-07 12:24:40 +01:00
// 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.
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 (). plater_ = nullptr ;
}
2019-05-14 15:00:24 +02:00
void MainFrame :: update_title ()
{
wxString title = wxEmptyString ;
if ( m_plater != nullptr )
{
2019-05-20 14:30:55 +02:00
// m_plater->get_project_filename() produces file name including path, but excluding extension.
// Don't try to remove the extension, it would remove part of the file name after the last dot!
wxString project = from_path ( into_path ( m_plater -> get_project_filename ()). filename ());
2019-05-14 15:00:24 +02:00
if ( ! project . empty ())
title += ( project + " - " );
}
2020-02-06 10:17:02 +01:00
std :: string build_id = SLIC3R_BUILD_ID ;
size_t idx_plus = build_id . find ( '+' );
if ( idx_plus != build_id . npos ) {
// Parse what is behind the '+'. If there is a number, then it is a build number after the label, and full build ID is shown.
int commit_after_label ;
2020-03-25 14:35:29 +01:00
if ( ! boost :: starts_with ( build_id . data () + idx_plus + 1 , "UNKNOWN" ) &&
( build_id . at ( idx_plus + 1 ) == '-' || sscanf ( build_id . data () + idx_plus + 1 , "%d-" , & commit_after_label ) == 0 )) {
2020-02-06 10:17:02 +01:00
// It is a release build.
build_id . erase ( build_id . begin () + idx_plus , build_id . end ());
#if defined(_WIN32) && ! defined(_WIN64)
// People are using 32bit slicer on a 64bit machine by mistake. Make it explicit.
2020-02-07 11:15:01 +01:00
build_id += " 32 bit" ;
2020-02-06 10:17:02 +01:00
#endif
}
}
title += ( wxString ( build_id ) + " " + _ ( L ( "based on Slic3r" )));
2019-05-14 15:00:24 +02:00
SetTitle ( title );
}
2019-01-28 12:12:14 +01:00
2018-09-20 08:40:22 +02:00
void MainFrame :: init_tabpanel ()
{
2020-06-11 14:02:59 +02:00
// wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10
// with multiple high resolution displays connected.
m_tabpanel = new wxNotebook ( this , wxID_ANY , wxDefaultPosition , wxDefaultSize , wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME );
#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
m_tabpanel -> SetFont ( Slic3r :: GUI :: wxGetApp (). normal_font ());
#endif
2020-06-16 15:16:28 +02:00
m_tabpanel -> Hide ();
2020-06-11 14:02:59 +02:00
m_settings_dialog . set_tabpanel ( m_tabpanel );
2018-09-20 08:40:22 +02:00
2018-10-31 12:56:08 +01:00
m_tabpanel -> Bind ( wxEVT_NOTEBOOK_PAGE_CHANGED , [ this ]( wxEvent & ) {
2020-06-02 11:42:22 +02:00
wxWindow * panel = m_tabpanel -> GetCurrentPage ();
Tab * tab = dynamic_cast < Tab *> ( panel );
2018-09-21 01:33:41 +02:00
2020-05-28 14:04:15 +02:00
// There shouldn't be a case, when we try to select a tab, which doesn't support a printer technology
2020-06-12 12:35:17 +02:00
if ( panel == nullptr || ( tab != nullptr && ! tab -> supports_printer_technology ( m_plater -> printer_technology ())))
2018-09-17 12:15:11 +02:00
return ;
2018-11-19 13:17:14 +01:00
auto & tabs_list = wxGetApp (). tabs_list ;
2020-06-02 11:42:22 +02:00
if ( tab && std :: find ( tabs_list . begin (), tabs_list . end (), tab ) != tabs_list . end ()) {
2018-11-19 13:17:14 +01:00
// On GTK, the wxEVT_NOTEBOOK_PAGE_CHANGED event is triggered
// before the MainFrame is fully set up.
2020-06-02 11:42:22 +02:00
tab -> OnActivate ();
2020-05-04 22:30:38 +02:00
m_last_selected_tab = m_tabpanel -> GetSelection ();
2018-09-21 01:33:41 +02:00
}
2020-04-24 11:01:14 +02:00
else
2020-06-02 11:42:22 +02:00
select_tab ( 0 ); // select Plater
2018-09-21 01:33:41 +02:00
});
2018-09-20 08:40:22 +02:00
2020-06-11 14:02:59 +02:00
m_plater = new Plater ( this , this );
2020-06-16 15:16:28 +02:00
m_plater -> Hide ();
2020-08-03 11:08:17 +02:00
2018-12-19 17:38:41 +01:00
wxGetApp (). plater_ = m_plater ;
2018-09-20 08:40:22 +02:00
2019-01-30 10:05:54 +01:00
wxGetApp (). obj_list () -> create_popup_menus ();
2018-10-09 12:41:05 +02:00
// The following event is emited by Tab implementation on config value change.
2019-02-22 09:38:56 +01:00
Bind ( EVT_TAB_VALUE_CHANGED , & MainFrame :: on_value_changed , this ); // #ys_FIXME_to_delete
2018-09-20 08:40:22 +02:00
2018-10-03 16:27:02 +02:00
// The following event is emited by Tab on preset selection,
2018-09-20 08:40:22 +02:00
// or when the preset's "modified" status changes.
2019-02-22 09:38:56 +01:00
Bind ( EVT_TAB_PRESETS_CHANGED , & MainFrame :: on_presets_changed , this ); // #ys_FIXME_to_delete
2018-10-03 16:27:02 +02:00
2018-09-21 01:33:41 +02:00
create_preset_tabs ();
2018-09-20 08:40:22 +02:00
if ( m_plater ) {
// load initial config
2018-09-21 01:33:41 +02:00
auto full_config = wxGetApp (). preset_bundle -> full_config ();
2018-10-17 11:12:38 +02:00
m_plater -> on_config_change ( full_config );
2018-09-20 08:40:22 +02:00
// Show a correct number of filament fields.
2018-10-10 13:53:45 +02:00
// nozzle_diameter is undefined when SLA printer is selected
2018-10-31 12:56:08 +01:00
if ( full_config . has ( "nozzle_diameter" )) {
2018-10-10 13:53:45 +02:00
m_plater -> on_extruders_change ( full_config . option < ConfigOptionFloats > ( "nozzle_diameter" ) -> values . size ());
}
2018-09-20 08:40:22 +02:00
}
}
2018-09-21 01:33:41 +02:00
void MainFrame :: create_preset_tabs ()
{
2018-10-01 15:09:31 +02:00
wxGetApp (). update_label_colours_from_appconfig ();
2018-09-21 01:33:41 +02:00
add_created_tab ( new TabPrint ( m_tabpanel ));
add_created_tab ( new TabFilament ( m_tabpanel ));
2018-11-19 13:17:14 +01:00
add_created_tab ( new TabSLAPrint ( m_tabpanel ));
2018-09-21 01:33:41 +02:00
add_created_tab ( new TabSLAMaterial ( m_tabpanel ));
add_created_tab ( new TabPrinter ( m_tabpanel ));
}
void MainFrame :: add_created_tab ( Tab * panel )
{
2018-10-04 16:43:10 +02:00
panel -> create_preset_tab ();
2018-11-19 09:15:53 +01:00
2018-11-19 13:17:14 +01:00
const auto printer_tech = wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology ();
2018-09-21 01:33:41 +02:00
2018-11-19 13:17:14 +01:00
if ( panel -> supports_printer_technology ( printer_tech ))
2018-09-21 01:33:41 +02:00
m_tabpanel -> AddPage ( panel , panel -> title ());
}
2019-05-14 15:00:24 +02:00
bool MainFrame :: can_start_new_project () const
{
return ( m_plater != nullptr ) && ! m_plater -> model (). objects . empty ();
}
2018-11-15 15:27:39 +01:00
bool MainFrame :: can_save () const
{
2019-05-02 13:46:39 +02:00
return ( m_plater != nullptr ) && ! m_plater -> model (). objects . empty ();
2018-11-15 15:27:39 +01:00
}
bool MainFrame :: can_export_model () const
{
2019-05-02 13:46:39 +02:00
return ( m_plater != nullptr ) && ! m_plater -> model (). objects . empty ();
}
2019-08-20 09:01:09 +02:00
bool MainFrame :: can_export_toolpaths () const
{
2019-08-20 11:33:58 +02:00
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 ();
2019-08-20 09:01:09 +02:00
}
2019-05-02 13:46:39 +02:00
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 )
{
2019-09-24 15:15:49 +02:00
if ( object -> has_mesh ( slaposPad ) || object -> has_mesh ( slaposSupportTree ))
2019-05-02 13:46:39 +02:00
{
can_export = true ;
break ;
}
}
return can_export ;
2018-11-15 15:27:39 +01:00
}
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
return true ;
}
2018-11-16 12:31:06 +01:00
2019-08-01 16:20:46 +02:00
bool MainFrame :: can_send_gcode () const
{
if ( m_plater == nullptr )
return false ;
if ( m_plater -> model (). objects . empty ())
return false ;
2019-08-05 14:36:50 +02:00
const auto print_host_opt = wxGetApp (). preset_bundle -> printers . get_edited_preset (). config . option < ConfigOptionString > ( "print_host" );
return print_host_opt != nullptr && ! print_host_opt -> value . empty ();
2019-08-01 16:20:46 +02:00
}
2020-03-06 15:33:30 +01:00
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 ;
}
2019-01-21 12:34:28 +01:00
bool MainFrame :: can_slice () const
{
bool bg_proc = wxGetApp (). app_config -> get ( "background_processing" ) == "1" ;
return ( m_plater != nullptr ) ? ! m_plater -> model (). objects . empty () && ! bg_proc : false ;
}
2018-11-16 12:31:06 +01:00
bool MainFrame :: can_change_view () const
{
2020-06-11 14:02:59 +02:00
switch ( m_layout )
{
default : { return false ; }
case ESettingsLayout :: New : { return m_plater -> IsShown (); }
case ESettingsLayout :: Dlg : { return true ; }
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 ;
}
}
2018-11-16 12:31:06 +01:00
}
2018-11-21 15:28:35 +01:00
bool MainFrame :: can_select () const
{
2019-05-02 13:46:39 +02:00
return ( m_plater != nullptr ) && ! m_plater -> model (). objects . empty ();
2018-11-21 15:28:35 +01:00
}
2018-11-21 15:47:41 +01:00
2019-05-14 11:57:39 +02:00
bool MainFrame :: can_deselect () const
{
return ( m_plater != nullptr ) && ! m_plater -> is_selection_empty ();
}
2018-11-21 15:47:41 +01:00
bool MainFrame :: can_delete () const
{
2019-05-02 13:46:39 +02:00
return ( m_plater != nullptr ) && ! m_plater -> is_selection_empty ();
2018-11-21 15:47:41 +01:00
}
2018-11-22 11:31:53 +01:00
bool MainFrame :: can_delete_all () const
{
2019-05-02 13:46:39 +02:00
return ( m_plater != nullptr ) && ! m_plater -> model (). objects . empty ();
2018-11-22 11:31:53 +01:00
}
2018-11-15 15:27:39 +01:00
2019-05-14 15:00:24 +02:00
bool MainFrame :: can_reslice () const
{
return ( m_plater != nullptr ) && ! m_plater -> model (). objects . empty ();
}
2020-06-19 11:04:08 +02:00
void MainFrame :: on_dpi_changed ( const wxRect & suggested_rect )
2019-04-02 12:00:50 +02:00
{
2020-06-15 16:20:34 +02:00
#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
wxGetApp (). update_fonts ( this );
#else
2019-04-18 02:03:40 +02:00
wxGetApp (). update_fonts ();
2020-06-15 16:20:34 +02:00
#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
2019-05-06 18:28:23 +02:00
this -> SetFont ( this -> normal_font ());
2019-04-10 09:56:32 +02:00
2019-04-17 11:22:30 +02:00
// update Plater
2019-04-25 01:45:00 +02:00
wxGetApp (). plater () -> msw_rescale ();
2019-04-10 09:56:32 +02:00
2019-04-17 11:22:30 +02:00
// update Tabs
2020-06-19 11:04:08 +02:00
if ( m_layout != ESettingsLayout :: Dlg ) // Do not update tabs if the Settings are in the separated dialog
2020-05-25 10:21:30 +02:00
for ( auto tab : wxGetApp (). tabs_list )
tab -> msw_rescale ();
2019-04-23 16:33:06 +02:00
2019-05-03 09:44:19 +02:00
wxMenuBar * menu_bar = this -> GetMenuBar ();
for ( size_t id = 0 ; id < menu_bar -> GetMenuCount (); id ++ )
msw_rescale_menu ( menu_bar -> GetMenu ( id ));
2019-04-26 12:29:34 +02:00
// Workarounds for correct Window rendering after rescale
2020-06-19 11:04:08 +02:00
/* Even if Window is maximized during moving,
2019-04-26 12:29:34 +02:00
* 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 );
2019-04-23 16:33:06 +02:00
/* 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 );
2019-04-26 12:29:34 +02:00
this -> Maximize ( is_maximized );
2020-06-19 11:04:08 +02:00
if ( m_layout == ESettingsLayout :: Dlg )
rescale_dialog_after_dpi_change ( * this , m_settings_dialog , ERescaleTarget :: SettingsDialog );
2019-04-02 12:00:50 +02:00
}
2020-05-21 17:29:00 +02:00
void MainFrame :: on_sys_color_changed ()
{
wxBusyCursor wait ;
// update label colors in respect to the system mode
wxGetApp (). init_label_colours ();
// update Plater
wxGetApp (). plater () -> sys_color_changed ();
// update Tabs
for ( auto tab : wxGetApp (). tabs_list )
tab -> sys_color_changed ();
// msw_rescale_menu updates just icons, so use it
wxMenuBar * menu_bar = this -> GetMenuBar ();
for ( size_t id = 0 ; id < menu_bar -> GetMenuCount (); id ++ )
msw_rescale_menu ( menu_bar -> GetMenu ( id ));
}
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-05 16:50:17 +02:00
#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 ();
append_menu_item ( helpMenu , wxID_ANY , _L ( "Prusa 3D &Drivers" ), _L ( "Open the Prusa3D drivers download page in your browser" ),
[]( wxCommandEvent & ) { wxGetApp (). open_web_page_localized ( "https://www.prusa3d.com/downloads" ); });
2020-07-27 14:53:17 +02:00
append_menu_item ( helpMenu , wxID_ANY , _ ( L ( "Software &Releases" )), _ ( L ( "Open the software releases page in your browser" )),
[]( wxCommandEvent & ) { wxLaunchDefaultBrowser ( "https://github.com/prusa3d/PrusaSlicer/releases" ); });
2020-06-05 16:50:17 +02:00
//# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", "Check for new Slic3r versions", sub{
//# wxTheApp->check_version(1);
//# });
//# $versioncheck->Enable(wxTheApp->have_version_check);
append_menu_item ( helpMenu , wxID_ANY , wxString :: Format ( _L ( "%s &Website" ), SLIC3R_APP_NAME ),
wxString :: Format ( _L ( "Open the %s website in your browser" ), SLIC3R_APP_NAME ),
[]( wxCommandEvent & ) { wxGetApp (). open_web_page_localized ( "https://www.prusa3d.com/slicerweb" ); });
// append_menu_item(helpMenu, wxID_ANY, wxString::Format(_(L("%s &Manual")), SLIC3R_APP_NAME),
// wxString::Format(_(L("Open the %s manual in your browser")), SLIC3R_APP_NAME),
// [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://manual.slic3r.org/"); });
helpMenu -> AppendSeparator ();
append_menu_item ( helpMenu , wxID_ANY , _L ( "System &Info" ), _L ( "Show system information" ),
[]( wxCommandEvent & ) { wxGetApp (). system_info (); });
append_menu_item ( helpMenu , wxID_ANY , _L ( "Show &Configuration Folder" ), _L ( "Show user configuration folder (datadir)" ),
[]( wxCommandEvent & ) { Slic3r :: GUI :: desktop_open_datadir_folder (); });
2020-07-27 14:53:17 +02:00
append_menu_item ( helpMenu , wxID_ANY , _ ( L "Report an I&ssue" ), wxString :: Format ( _L ( "Report an issue on %s" ), SLIC3R_APP_NAME ),
[]( wxCommandEvent & ) { wxLaunchDefaultBrowser ( "https://github.com/prusa3d/slic3r/issues/new" ); });
2020-06-05 16:50:17 +02:00
append_menu_item ( helpMenu , wxID_ANY , wxString :: Format ( _L ( "&About %s" ), SLIC3R_APP_NAME ), _L ( "Show about dialog" ),
[]( wxCommandEvent & ) { Slic3r :: GUI :: about (); });
helpMenu -> AppendSeparator ();
append_menu_item ( helpMenu , wxID_ANY , _L ( "Keyboard Shortcuts" ) + sep + "&?" , _L ( "Show the list of the keyboard shortcuts" ),
[]( wxCommandEvent & ) { wxGetApp (). keyboard_shortcuts (); });
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
helpMenu -> AppendSeparator ();
append_menu_item ( helpMenu , wxID_ANY , "DEBUG gcode thumbnails" , "DEBUG ONLY - read the selected gcode file and generates png for the contained thumbnails" ,
[]( wxCommandEvent & ) { wxGetApp (). gcode_thumbnails_debug (); });
#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG
return helpMenu ;
}
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 ( "Iso" ) + sep + "&0" , _L ( "Iso View" ), [ mainFrame ]( wxCommandEvent & ) { mainFrame -> select_view ( "iso" ); },
"" , 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" ) + sep + "&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" ) + sep + "&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" ) + sep + "&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" ) + sep + "&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" ) + sep + "&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" ) + sep + "&6" , _L ( "Right View" ), [ mainFrame ]( wxCommandEvent & ) { mainFrame -> select_view ( "right" ); },
"" , nullptr , [ can_change_view ]() { return can_change_view (); }, mainFrame );
}
void MainFrame :: init_editor_menubar ()
#else
2018-09-20 08:40:22 +02:00
void MainFrame :: init_menubar ()
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2018-09-20 08:40:22 +02:00
{
2019-01-11 17:06:46 +01:00
#ifdef __APPLE__
wxMenuBar :: SetAutoWindowMenu ( false );
#endif
2018-09-20 08:40:22 +02:00
// File menu
2018-09-21 01:33:41 +02:00
wxMenu * fileMenu = new wxMenu ;
2018-09-20 08:40:22 +02:00
{
2020-06-05 16:50:17 +02:00
append_menu_item ( fileMenu , wxID_ANY , _L ( "&New Project" ) + " \t Ctrl+N" , _L ( "Start a new project" ),
2019-05-14 15:00:24 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> new_project (); }, "" , nullptr ,
[ this ](){ return m_plater != nullptr && can_start_new_project (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( fileMenu , wxID_ANY , _L ( "&Open Project" ) + dots + " \t Ctrl+O" , _L ( "Open a project file" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> load_project (); }, "open" , nullptr ,
2019-05-13 14:27:51 +02:00
[ this ](){ return m_plater != nullptr ; }, this );
2019-07-12 15:36:01 +02:00
wxMenu * recent_projects_menu = new wxMenu ();
2020-06-05 16:50:17 +02:00
wxMenuItem * recent_projects_submenu = append_submenu ( fileMenu , recent_projects_menu , wxID_ANY , _L ( "Recent projects" ), "" );
2019-07-12 15:36:01 +02:00
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 );
if ( wxFileExists ( filename ))
m_plater -> load_project ( filename );
else
{
2020-06-05 16:50:17 +02:00
wxMessageDialog msg ( this , _L ( "The selected project is no longer available. \n Do you want to remove it from the recent projects list?" ), _L ( "Error" ), wxYES_NO | wxYES_DEFAULT );
2020-03-02 12:33:17 +01:00
if ( msg . ShowModal () == wxID_YES )
2019-07-12 15:36:01 +02:00
{
2020-03-02 12:33:17 +01:00
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 );
wxGetApp (). app_config -> save ();
2019-07-12 15:36:01 +02:00
}
}
}, wxID_FILE1 , wxID_FILE9 );
std :: vector < std :: string > recent_projects = wxGetApp (). app_config -> get_recent_projects ();
2019-08-01 09:03:06 +02:00
std :: reverse ( recent_projects . begin (), recent_projects . end ());
2019-07-12 15:36:01 +02:00
for ( const std :: string & project : recent_projects )
{
m_recent_projects . AddFileToHistory ( from_u8 ( project ));
}
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( m_recent_projects . GetCount () > 0 ); }, recent_projects_submenu -> GetId ());
2020-06-05 16:50:17 +02:00
append_menu_item ( fileMenu , wxID_ANY , _L ( "&Save Project" ) + " \t Ctrl+S" , _L ( "Save current project file" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_3mf ( into_path ( m_plater -> get_project_filename ( ".3mf" ))); }, "save" , nullptr ,
2019-05-13 14:27:51 +02:00
[ this ](){ return m_plater != nullptr && can_save (); }, this );
2019-05-20 10:02:51 +02:00
#ifdef __APPLE__
2020-06-05 16:50:17 +02:00
append_menu_item ( fileMenu , wxID_ANY , _L ( "Save Project &as" ) + dots + " \t Ctrl+Shift+S" , _L ( "Save current project file as" ),
2019-05-20 10:02:51 +02:00
#else
2020-06-05 16:50:17 +02:00
append_menu_item ( fileMenu , wxID_ANY , _L ( "Save Project &as" ) + dots + " \t Ctrl+Alt+S" , _L ( "Save current project file as" ),
2019-05-20 10:02:51 +02:00
#endif // __APPLE__
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_3mf (); }, "save" , nullptr ,
2019-05-13 14:27:51 +02:00
[ this ](){ return m_plater != nullptr && can_save (); }, this );
2018-11-15 15:27:39 +01:00
fileMenu -> AppendSeparator ();
wxMenu * import_menu = new wxMenu ();
2020-06-05 16:50:17 +02:00
append_menu_item ( import_menu , wxID_ANY , _L ( "Import STL/OBJ/AM&F/3MF" ) + dots + " \t Ctrl+I" , _L ( "Load a model" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> add_model (); }, "import_plater" , nullptr ,
2019-05-13 14:27:51 +02:00
[ this ](){ return m_plater != nullptr ; }, this );
2020-04-13 12:31:37 +02:00
2020-04-29 19:10:13 +02:00
append_menu_item ( import_menu , wxID_ANY , _L ( "Import STL (imperial units)" ), _L ( "Load an model saved with imperial units" ),
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> add_model ( true ); }, "import_plater" , nullptr ,
[ this ](){ return m_plater != nullptr ; }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( import_menu , wxID_ANY , _L ( "Import SL1 archive" ) + dots , _L ( "Load an SL1 output archive" ),
2020-04-13 12:31:37 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> import_sl1_archive (); }, "import_plater" , nullptr ,
[ this ](){ return m_plater != nullptr ; }, this );
2018-11-15 15:27:39 +01:00
import_menu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( import_menu , wxID_ANY , _L ( "Import &Config" ) + dots + " \t Ctrl+L" , _L ( "Load exported configuration file" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { load_config_file (); }, "import_config" , nullptr ,
[ this ]() { return true ; }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( import_menu , wxID_ANY , _L ( "Import Config from &project" ) + dots + " \t Ctrl+Alt+L" , _L ( "Load configuration from project file" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> extract_config_from_project (); }, "import_config" , nullptr ,
[ this ]() { return true ; }, this );
2018-11-16 09:26:41 +01:00
import_menu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( import_menu , wxID_ANY , _L ( "Import Config &Bundle" ) + dots , _L ( "Load presets from a bundle" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { load_configbundle (); }, "import_config_bundle" , nullptr ,
[ this ]() { return true ; }, this );
2020-06-05 16:50:17 +02:00
append_submenu ( fileMenu , import_menu , wxID_ANY , _L ( "&Import" ), "" );
2018-11-15 15:27:39 +01:00
wxMenu * export_menu = new wxMenu ();
2020-06-05 16:50:17 +02:00
wxMenuItem * item_export_gcode = append_menu_item ( export_menu , wxID_ANY , _L ( "Export &G-code" ) + dots + " \t Ctrl+G" , _L ( "Export current plate as G-code" ),
2020-06-15 00:34:12 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_gcode ( false ); }, "export_gcode" , nullptr ,
2019-05-13 14:27:51 +02:00
[ this ](){ return can_export_gcode (); }, this );
2019-04-30 13:51:36 +02:00
m_changeable_menu_items . push_back ( item_export_gcode );
2020-06-05 16:50:17 +02:00
wxMenuItem * item_send_gcode = append_menu_item ( export_menu , wxID_ANY , _L ( "S&end G-code" ) + dots + " \t Ctrl+Shift+G" , _L ( "Send to print current plate as G-code" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> send_gcode (); }, "export_gcode" , nullptr ,
2019-08-01 16:20:46 +02:00
[ this ](){ return can_send_gcode (); }, this );
m_changeable_menu_items . push_back ( item_send_gcode );
2020-06-05 16:50:17 +02:00
append_menu_item ( export_menu , wxID_ANY , _L ( "Export G-code to SD card / Flash drive" ) + dots + " \t Ctrl+U" , _L ( "Export current plate as G-code to SD card / Flash drive" ),
2020-03-06 15:33:30 +01:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_gcode ( true ); }, "export_to_sd" , nullptr ,
[ this ]() { return can_export_gcode_sd (); }, this );
2018-11-15 15:27:39 +01:00
export_menu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( export_menu , wxID_ANY , _L ( "Export plate as &STL" ) + dots , _L ( "Export current plate as STL" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_stl (); }, "export_plater" , nullptr ,
2019-05-13 14:27:51 +02:00
[ this ](){ return can_export_model (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( export_menu , wxID_ANY , _L ( "Export plate as STL &including supports" ) + dots , _L ( "Export current plate as STL including supports" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_stl ( true ); }, "export_plater" , nullptr ,
2019-05-13 14:27:51 +02:00
[ this ](){ return can_export_supports (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( export_menu , wxID_ANY , _L ( "Export plate as &AMF" ) + dots , _L ( "Export current plate as AMF" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_amf (); }, "export_plater" , nullptr ,
2019-05-13 14:27:51 +02:00
[ this ](){ return can_export_model (); }, this );
2018-11-15 15:27:39 +01:00
export_menu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( export_menu , wxID_ANY , _L ( "Export &toolpaths as OBJ" ) + dots , _L ( "Export toolpaths as OBJ" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_toolpaths_to_obj (); }, "export_plater" , nullptr ,
2019-08-20 09:01:09 +02:00
[ this ]() { return can_export_toolpaths (); }, this );
export_menu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( export_menu , wxID_ANY , _L ( "Export &Config" ) + dots + " \t Ctrl+E" , _L ( "Export current configuration to file" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { export_config (); }, "export_config" , nullptr ,
[ this ]() { return true ; }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( export_menu , wxID_ANY , _L ( "Export Config &Bundle" ) + dots , _L ( "Export all presets to file" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { export_configbundle (); }, "export_config_bundle" , nullptr ,
[ this ]() { return true ; }, this );
2020-06-05 16:50:17 +02:00
append_submenu ( fileMenu , export_menu , wxID_ANY , _L ( "&Export" ), "" );
2018-11-15 15:27:39 +01:00
2020-06-05 16:50:17 +02:00
append_menu_item ( fileMenu , wxID_ANY , _L ( "Ejec&t SD card / Flash drive" ) + dots + " \t Ctrl+T" , _L ( "Eject SD card / Flash drive after the G-code was exported to it." ),
2020-03-06 15:33:30 +01:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> eject_drive (); }, "eject_sd" , nullptr ,
[ this ]() { return can_eject (); }, this );
2018-11-15 15:27:39 +01:00
fileMenu -> AppendSeparator ();
2018-12-06 14:47:53 +01:00
#if 0
2018-11-15 12:21:09 +01:00
m_menu_item_repeat = nullptr;
2020-06-05 16:50:17 +02:00
append_menu_item(fileMenu, wxID_ANY, _L("Quick Slice") +dots+ "\tCtrl+U", _L("Slice a file into a G-code"),
2018-11-15 12:21:09 +01:00
[this](wxCommandEvent&) {
wxTheApp->CallAfter([this]() {
2018-09-20 08:40:22 +02:00
quick_slice();
2018-11-15 12:21:09 +01:00
m_menu_item_repeat->Enable(is_last_input_file());
}); }, "cog_go.png");
2020-06-05 16:50:17 +02:00
append_menu_item(fileMenu, wxID_ANY, _L("Quick Slice and Save As") +dots +"\tCtrl+Alt+U", _L("Slice a file into a G-code, save as"),
2018-11-15 12:21:09 +01:00
[this](wxCommandEvent&) {
wxTheApp->CallAfter([this]() {
2018-09-21 01:33:41 +02:00
quick_slice(qsSaveAs);
2018-11-15 12:21:09 +01:00
m_menu_item_repeat->Enable(is_last_input_file());
}); }, "cog_go.png");
2020-06-05 16:50:17 +02:00
m_menu_item_repeat = append_menu_item(fileMenu, wxID_ANY, _L("Repeat Last Quick Slice") +"\tCtrl+Shift+U", _L("Repeat last quick slice"),
2018-10-31 12:56:08 +01:00
[this](wxCommandEvent&) {
wxTheApp->CallAfter([this]() {
2018-09-21 01:33:41 +02:00
quick_slice(qsReslice);
2018-09-20 08:40:22 +02:00
}); }, "cog_go.png");
2018-11-15 12:21:09 +01:00
m_menu_item_repeat->Enable(false);
2018-09-20 08:40:22 +02:00
fileMenu->AppendSeparator();
2018-12-06 14:47:53 +01:00
#endif
2020-06-05 16:50:17 +02:00
m_menu_item_reslice_now = append_menu_item ( fileMenu , wxID_ANY , _L ( "(Re)Slice No&w" ) + " \t Ctrl+R" , _L ( "Start new slicing process" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { reslice_now (); }, "re_slice" , nullptr ,
2020-06-19 15:32:44 +02:00
[ this ]() { return m_plater != nullptr && can_reslice (); }, this );
2018-09-20 08:40:22 +02:00
fileMenu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( fileMenu , wxID_ANY , _L ( "&Repair STL file" ) + dots , _L ( "Automatically repair an STL file" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { repair_stl (); }, "wrench" , nullptr ,
2020-06-19 15:32:44 +02:00
[ this ]() { return true ; }, this );
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2018-09-20 08:40:22 +02:00
fileMenu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( fileMenu , wxID_ANY , _L ( "&G-code preview" ), _L ( "Switch to G-code preview mode" ),
2020-06-22 12:10:18 +02:00
[ this ]( wxCommandEvent & ) {
2020-08-11 15:44:32 +02:00
if ( m_plater -> model (). objects . empty () ||
wxMessageDialog (( wxWindow * ) this , _L ( "Switching to G-code preview mode will remove all objects, continue?" ),
2020-08-12 15:07:02 +02:00
wxString ( SLIC3R_APP_NAME ) + " - " + _L ( "Switch to G-code preview mode" ), wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION | wxCENTRE ). ShowModal () == wxID_YES )
2020-06-22 12:10:18 +02:00
set_mode ( EMode :: GCodeViewer );
2020-08-21 11:36:08 +02:00
}, "" , nullptr );
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2020-06-05 16:50:17 +02:00
fileMenu -> AppendSeparator ();
append_menu_item ( fileMenu , wxID_EXIT , _L ( "&Quit" ), wxString :: Format ( _L ( "Quit %s" ), SLIC3R_APP_NAME ),
2018-11-21 10:21:12 +01:00
[ this ]( wxCommandEvent & ) { Close ( false ); });
2018-09-20 08:40:22 +02:00
}
2020-08-03 15:00:19 +02:00
#if !ENABLE_GCODE_VIEWER
2019-02-04 12:04:42 +01:00
#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.
wxString sep = " \t\xA0 " ;
wxString sep_space = " \xA0 " ;
#else
wxString sep = " - " ;
2019-02-04 12:08:48 +01:00
wxString sep_space = "" ;
2019-02-04 12:04:42 +01:00
#endif
2020-08-03 15:00:19 +02:00
#endif // !ENABLE_GCODE_VIEWER
2019-02-04 12:04:42 +01:00
2018-11-21 15:28:35 +01:00
// Edit menu
wxMenu * editMenu = nullptr ;
if ( m_plater != nullptr )
{
editMenu = new wxMenu ();
2019-02-04 15:46:12 +01:00
#ifdef __APPLE__
// Backspace sign
wxString hotkey_delete = " \u232b " ;
#else
wxString hotkey_delete = "Del" ;
#endif
2020-06-05 16:50:17 +02:00
append_menu_item ( editMenu , wxID_ANY , _L ( "&Select all" ) + sep + GUI :: shortkey_ctrl_prefix () + sep_space + "A" ,
_L ( "Selects all objects" ), [ this ]( wxCommandEvent & ) { m_plater -> select_all (); },
2019-05-13 14:27:51 +02:00
"" , nullptr , [ this ](){ return can_select (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( editMenu , wxID_ANY , _L ( "D&eselect all" ) + sep + "Esc" ,
_L ( "Deselects all objects" ), [ this ]( wxCommandEvent & ) { m_plater -> deselect_all (); },
2019-05-14 11:57:39 +02:00
"" , nullptr , [ this ](){ return can_deselect (); }, this );
2018-11-22 11:31:53 +01:00
editMenu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( editMenu , wxID_ANY , _L ( "&Delete selected" ) + sep + hotkey_delete ,
_L ( "Deletes the current selection" ),[ this ]( wxCommandEvent & ) { m_plater -> remove_selected (); },
2019-12-18 15:54:01 +01:00
"remove_menu" , nullptr , [ this ](){ return can_delete (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( editMenu , wxID_ANY , _L ( "Delete &all" ) + sep + GUI :: shortkey_ctrl_prefix () + sep_space + hotkey_delete ,
_L ( "Deletes all objects" ), [ this ]( wxCommandEvent & ) { m_plater -> reset_with_confirm (); },
2019-12-18 15:54:01 +01:00
"delete_all_menu" , nullptr , [ this ](){ return can_delete_all (); }, this );
2018-11-21 15:28:35 +01:00
2019-07-08 10:57:35 +02:00
editMenu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( editMenu , wxID_ANY , _L ( "&Undo" ) + sep + GUI :: shortkey_ctrl_prefix () + sep_space + "Z" ,
_L ( "Undo" ), [ this ]( wxCommandEvent & ) { m_plater -> undo (); },
2019-08-30 16:05:12 +02:00
"undo_menu" , nullptr , [ this ](){ return m_plater -> can_undo (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( editMenu , wxID_ANY , _L ( "&Redo" ) + sep + GUI :: shortkey_ctrl_prefix () + sep_space + "Y" ,
_L ( "Redo" ), [ this ]( wxCommandEvent & ) { m_plater -> redo (); },
2019-08-30 16:05:12 +02:00
"redo_menu" , nullptr , [ this ](){ return m_plater -> can_redo (); }, this );
2019-07-08 10:57:35 +02:00
2019-04-10 08:40:58 +02:00
editMenu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( editMenu , wxID_ANY , _L ( "&Copy" ) + sep + GUI :: shortkey_ctrl_prefix () + sep_space + "C" ,
_L ( "Copy selection to clipboard" ), [ this ]( wxCommandEvent & ) { m_plater -> copy_selection_to_clipboard (); },
2019-12-18 15:54:01 +01:00
"copy_menu" , nullptr , [ this ](){ return m_plater -> can_copy_to_clipboard (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( editMenu , wxID_ANY , _L ( "&Paste" ) + sep + GUI :: shortkey_ctrl_prefix () + sep_space + "V" ,
_L ( "Paste clipboard" ), [ this ]( wxCommandEvent & ) { m_plater -> paste_from_clipboard (); },
2019-12-18 15:54:01 +01:00
"paste_menu" , nullptr , [ this ](){ return m_plater -> can_paste_from_clipboard (); }, this );
2020-01-31 12:15:04 +01:00
editMenu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( editMenu , wxID_ANY , _L ( "Re&load from disk" ) + sep + "F5" ,
_L ( "Reload the plater from disk" ), [ this ]( wxCommandEvent & ) { m_plater -> reload_all_from_disk (); },
2020-01-31 12:15:04 +01:00
"" , nullptr , [ this ]() { return ! m_plater -> model (). objects . empty (); }, this );
2020-04-22 15:46:23 +02:00
editMenu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( editMenu , wxID_ANY , _L ( "Searc&h" ) + " \t Ctrl+F" ,
_L ( "Find option" ), [ this ]( wxCommandEvent & ) { m_plater -> search ( /*m_tabpanel->GetCurrentPage() == */ m_plater -> IsShown ()); },
2020-04-22 15:46:23 +02:00
"search" , nullptr , [ this ]() { return true ; }, this );
2018-11-21 15:28:35 +01:00
}
2018-09-20 08:40:22 +02:00
// Window menu
auto windowMenu = new wxMenu ();
{
if ( m_plater ) {
2020-06-05 16:50:17 +02:00
append_menu_item ( windowMenu , wxID_HIGHEST + 1 , _L ( "&Plater Tab" ) + " \t Ctrl+1" , _L ( "Show the plater" ),
2020-05-04 22:30:38 +02:00
[ this ]( wxCommandEvent & ) { select_tab ( 0 ); }, "plater" , nullptr ,
2019-12-18 15:54:01 +01:00
[ this ]() { return true ; }, this );
2018-09-20 08:40:22 +02:00
windowMenu -> AppendSeparator ();
}
2020-06-05 16:50:17 +02:00
append_menu_item ( windowMenu , wxID_HIGHEST + 2 , _L ( "P&rint Settings Tab" ) + " \t Ctrl+2" , _L ( "Show the print settings" ),
2020-05-04 22:30:38 +02:00
[ this /*, tab_offset*/ ]( wxCommandEvent & ) { select_tab ( 1 ); }, "cog" , nullptr ,
2019-12-18 15:54:01 +01:00
[ this ]() { return true ; }, this );
2020-06-05 16:50:17 +02:00
wxMenuItem * item_material_tab = append_menu_item ( windowMenu , wxID_HIGHEST + 3 , _L ( "&Filament Settings Tab" ) + " \t Ctrl+3" , _L ( "Show the filament settings" ),
2020-05-04 22:30:38 +02:00
[ this /*, tab_offset*/ ]( wxCommandEvent & ) { select_tab ( 2 ); }, "spool" , nullptr ,
2019-12-18 15:54:01 +01:00
[ this ]() { return true ; }, this );
2019-04-30 13:51:36 +02:00
m_changeable_menu_items . push_back ( item_material_tab );
2020-06-05 16:50:17 +02:00
wxMenuItem * item_printer_tab = append_menu_item ( windowMenu , wxID_HIGHEST + 4 , _L ( "Print&er Settings Tab" ) + " \t Ctrl+4" , _L ( "Show the printer settings" ),
2020-05-04 22:30:38 +02:00
[ this /*, tab_offset*/ ]( wxCommandEvent & ) { select_tab ( 3 ); }, "printer" , nullptr ,
2019-12-18 15:54:01 +01:00
[ this ]() { return true ; }, this );
2020-04-23 12:41:38 +02:00
m_changeable_menu_items . push_back ( item_printer_tab );
2018-12-04 13:55:25 +01:00
if ( m_plater ) {
windowMenu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( windowMenu , wxID_HIGHEST + 5 , _L ( "3&D" ) + " \t Ctrl+5" , _L ( "Show the 3D editing view" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { m_plater -> select_view_3D ( "3D" ); }, "editor_menu" , nullptr ,
2019-05-13 14:27:51 +02:00
[ this ](){ return can_change_view (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( windowMenu , wxID_HIGHEST + 6 , _L ( "Pre&view" ) + " \t Ctrl+6" , _L ( "Show the 3D slices preview" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { m_plater -> select_view_3D ( "Preview" ); }, "preview_menu" , nullptr ,
2019-05-13 14:27:51 +02:00
[ this ](){ return can_change_view (); }, this );
2018-12-04 13:55:25 +01:00
}
2018-12-17 14:40:54 +01:00
2020-08-03 15:00:19 +02:00
#if !ENABLE_GCODE_VIEWER
2018-12-17 14:40:54 +01:00
#if _WIN32
// This is needed on Windows to fake the CTRL+# of the window menu when using the numpad
wxAcceleratorEntry entries [ 6 ];
entries [ 0 ]. Set ( wxACCEL_CTRL , WXK_NUMPAD1 , wxID_HIGHEST + 1 );
entries [ 1 ]. Set ( wxACCEL_CTRL , WXK_NUMPAD2 , wxID_HIGHEST + 2 );
entries [ 2 ]. Set ( wxACCEL_CTRL , WXK_NUMPAD3 , wxID_HIGHEST + 3 );
entries [ 3 ]. Set ( wxACCEL_CTRL , WXK_NUMPAD4 , wxID_HIGHEST + 4 );
entries [ 4 ]. Set ( wxACCEL_CTRL , WXK_NUMPAD5 , wxID_HIGHEST + 5 );
entries [ 5 ]. Set ( wxACCEL_CTRL , WXK_NUMPAD6 , wxID_HIGHEST + 6 );
wxAcceleratorTable accel ( 6 , entries );
SetAcceleratorTable ( accel );
#endif // _WIN32
2020-08-03 15:00:19 +02:00
#endif // !ENABLE_GCODE_VIEWER
2018-12-14 15:27:34 +01:00
windowMenu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( windowMenu , wxID_ANY , _L ( "Print &Host Upload Queue" ) + " \t Ctrl+J" , _L ( "Display the Print Host Upload Queue window" ),
2019-12-18 15:54:01 +01:00
[ this ]( wxCommandEvent & ) { m_printhost_queue_dlg -> Show (); }, "upload_queue" , nullptr ,
[ this ]() { return true ; }, this );
2020-08-26 14:56:26 +02:00
windowMenu -> AppendSeparator ();
append_menu_item ( windowMenu , wxID_ANY , _ ( L ( "Open new instance" )) + " \t Ctrl+I" , _ ( L ( "Open a new PrusaSlicer instance" )),
[ this ]( wxCommandEvent & ) {
wxString path = wxStandardPaths :: Get (). GetExecutablePath ();
#ifdef __APPLE__
boost :: process :: spawn (( const char * ) path . c_str ());
#else
wxExecute ( wxStandardPaths :: Get (). GetExecutablePath (), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER );
#endif
}, "upload_queue" , nullptr ,
[ this ]() { return true ; }, this );
2018-09-20 08:40:22 +02:00
}
// View menu
2018-11-15 15:27:39 +01:00
wxMenu * viewMenu = nullptr ;
if ( m_plater ) {
viewMenu = new wxMenu ();
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-05 16:50:17 +02:00
add_common_view_menu_items ( viewMenu , this , std :: bind ( & MainFrame :: can_change_view , this ));
#else
2018-11-15 15:27:39 +01:00
// The camera control accelerators are captured by GLCanvas3D::on_char().
2020-06-05 16:50:17 +02:00
append_menu_item ( viewMenu , wxID_ANY , _L ( "Iso" ) + sep + "&0" , _L ( "Iso View" ), [ this ]( wxCommandEvent & ) { select_view ( "iso" ); },
2019-05-13 14:27:51 +02:00
"" , nullptr , [ this ](){ return can_change_view (); }, this );
2018-11-15 15:27:39 +01:00
viewMenu -> AppendSeparator ();
2019-05-04 02:07:07 +02:00
//TRN To be shown in the main menu View->Top
2020-06-05 16:50:17 +02:00
append_menu_item ( viewMenu , wxID_ANY , _L ( "Top" ) + sep + "&1" , _L ( "Top View" ), [ this ]( wxCommandEvent & ) { select_view ( "top" ); },
2019-05-13 14:27:51 +02:00
"" , nullptr , [ this ](){ return can_change_view (); }, this );
2019-05-04 02:07:07 +02:00
//TRN To be shown in the main menu View->Bottom
2020-06-05 16:50:17 +02:00
append_menu_item ( viewMenu , wxID_ANY , _L ( "Bottom" ) + sep + "&2" , _L ( "Bottom View" ), [ this ]( wxCommandEvent & ) { select_view ( "bottom" ); },
2019-05-13 14:27:51 +02:00
"" , nullptr , [ this ](){ return can_change_view (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( viewMenu , wxID_ANY , _L ( "Front" ) + sep + "&3" , _L ( "Front View" ), [ this ]( wxCommandEvent & ) { select_view ( "front" ); },
2019-05-13 14:27:51 +02:00
"" , nullptr , [ this ](){ return can_change_view (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( viewMenu , wxID_ANY , _L ( "Rear" ) + sep + "&4" , _L ( "Rear View" ), [ this ]( wxCommandEvent & ) { select_view ( "rear" ); },
2019-05-13 14:27:51 +02:00
"" , nullptr , [ this ](){ return can_change_view (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( viewMenu , wxID_ANY , _L ( "Left" ) + sep + "&5" , _L ( "Left View" ), [ this ]( wxCommandEvent & ) { select_view ( "left" ); },
2019-05-13 14:27:51 +02:00
"" , nullptr , [ this ](){ return can_change_view (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_item ( viewMenu , wxID_ANY , _L ( "Right" ) + sep + "&6" , _L ( "Right View" ), [ this ]( wxCommandEvent & ) { select_view ( "right" ); },
2019-05-13 14:27:51 +02:00
"" , nullptr , [ this ](){ return can_change_view (); }, this );
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2020-02-04 14:42:26 +01:00
viewMenu -> AppendSeparator ();
2020-03-13 15:09:07 +01:00
#if ENABLE_SLOPE_RENDERING
wxMenu * options_menu = new wxMenu ();
2020-06-05 16:50:17 +02:00
append_menu_check_item ( options_menu , wxID_ANY , _L ( "Show &labels" ) + sep + "E" , _L ( "Show object/instance labels in 3D scene" ),
2020-03-13 15:09:07 +01:00
[ this ]( wxCommandEvent & ) { m_plater -> show_view3D_labels ( ! m_plater -> are_view3D_labels_shown ()); }, this ,
[ this ]() { return m_plater -> is_view3D_shown (); }, [ this ]() { return m_plater -> are_view3D_labels_shown (); }, this );
2020-06-05 16:50:17 +02:00
append_menu_check_item ( options_menu , wxID_ANY , _L ( "Show &slope" ) + sep + "D" , _L ( "Objects coloring using faces' slope" ),
2020-03-13 15:09:07 +01:00
[ this ]( wxCommandEvent & ) { m_plater -> show_view3D_slope ( ! m_plater -> is_view3D_slope_shown ()); }, this ,
[ this ]() { return m_plater -> is_view3D_shown () && ! m_plater -> is_view3D_layers_editing_enabled (); }, [ this ]() { return m_plater -> is_view3D_slope_shown (); }, this );
2020-06-05 16:50:17 +02:00
append_submenu ( viewMenu , options_menu , wxID_ANY , _L ( "&Options" ), "" );
2020-03-13 15:09:07 +01:00
#else
2020-06-05 16:50:17 +02:00
append_menu_check_item ( viewMenu , wxID_ANY , _L ( "Show &labels" ) + sep + "E" , _L ( "Show object/instance labels in 3D scene" ),
2020-02-04 14:42:26 +01:00
[ this ]( wxCommandEvent & ) { m_plater -> show_view3D_labels ( ! m_plater -> are_view3D_labels_shown ()); }, this ,
[ this ]() { return m_plater -> is_view3D_shown (); }, [ this ]() { return m_plater -> are_view3D_labels_shown (); }, this );
2020-03-13 15:09:07 +01:00
#endif // ENABLE_SLOPE_RENDERING
2020-06-05 16:50:17 +02:00
append_menu_check_item ( viewMenu , wxID_ANY , _L ( "&Collapse sidebar" ), _L ( "Collapse sidebar" ),
2020-04-29 15:58:57 +02:00
[ this ]( wxCommandEvent & ) { m_plater -> collapse_sidebar ( ! m_plater -> is_sidebar_collapsed ()); }, this ,
2020-03-16 12:49:39 +01:00
[ this ]() { return true ; }, [ this ]() { return m_plater -> is_sidebar_collapsed (); }, this );
2018-11-15 15:27:39 +01:00
}
2018-09-20 08:40:22 +02:00
// Help menu
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-05 16:50:17 +02:00
auto helpMenu = generate_help_menu ();
#else
2018-09-20 08:40:22 +02:00
auto helpMenu = new wxMenu ();
{
2020-06-05 16:50:17 +02:00
append_menu_item ( helpMenu , wxID_ANY , _L ( "Prusa 3D &Drivers" ), _L ( "Open the Prusa3D drivers download page in your browser" ),
2019-05-13 16:34:41 +02:00
[ this ]( wxCommandEvent & ) { wxGetApp (). open_web_page_localized ( "https://www.prusa3d.com/downloads" ); });
2020-06-05 16:50:17 +02:00
append_menu_item ( helpMenu , wxID_ANY , _L ( "Software &Releases" ), _L ( "Open the software releases page in your browser" ),
2020-06-14 23:53:17 +02:00
[ this ]( wxCommandEvent & ) { wxLaunchDefaultBrowser ( "https://github.com/prusa3d/PrusaSlicer/releases" ); });
2018-09-20 08:40:22 +02:00
//# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", "Check for new Slic3r versions", sub{
//# wxTheApp->check_version(1);
//# });
//# $versioncheck->Enable(wxTheApp->have_version_check);
2020-06-05 16:50:17 +02:00
append_menu_item ( helpMenu , wxID_ANY , wxString :: Format ( _L ( "%s &Website" ), SLIC3R_APP_NAME ),
wxString :: Format ( _L ( "Open the %s website in your browser" ), SLIC3R_APP_NAME ),
2019-05-13 16:34:41 +02:00
[ this ]( wxCommandEvent & ) { wxGetApp (). open_web_page_localized ( "https://www.prusa3d.com/slicerweb" ); });
2019-05-10 14:43:35 +02:00
// append_menu_item(helpMenu, wxID_ANY, wxString::Format(_(L("%s &Manual")), SLIC3R_APP_NAME),
// wxString::Format(_(L("Open the %s manual in your browser")), SLIC3R_APP_NAME),
// [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://manual.slic3r.org/"); });
2018-09-20 08:40:22 +02:00
helpMenu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( helpMenu , wxID_ANY , _L ( "System &Info" ), _L ( "Show system information" ),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { wxGetApp (). system_info (); });
2020-06-05 16:50:17 +02:00
append_menu_item ( helpMenu , wxID_ANY , _L ( "Show &Configuration Folder" ), _L ( "Show user configuration folder (datadir)" ),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { Slic3r :: GUI :: desktop_open_datadir_folder (); });
2020-06-05 16:50:17 +02:00
append_menu_item ( helpMenu , wxID_ANY , _L ( "Report an I&ssue" ), wxString :: Format ( _L ( "Report an issue on %s" ), SLIC3R_APP_NAME ),
2020-06-14 23:53:17 +02:00
[ this ]( wxCommandEvent & ) { wxLaunchDefaultBrowser ( "https://github.com/prusa3d/slic3r/issues/new" ); });
2019-04-15 19:49:37 +02:00
append_menu_item ( helpMenu , wxID_ANY , wxString :: Format ( _ ( L ( "&About %s" )), SLIC3R_APP_NAME ), _ ( L ( "Show about dialog" )),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { Slic3r :: GUI :: about (); });
2018-12-19 13:06:24 +01:00
helpMenu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( helpMenu , wxID_ANY , _L ( "Keyboard Shortcuts" ) + sep + "&?" , _L ( "Show the list of the keyboard shortcuts" ),
2018-12-19 13:06:24 +01:00
[ this ]( wxCommandEvent & ) { wxGetApp (). keyboard_shortcuts (); });
2019-11-07 09:01:28 +01:00
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
2019-10-25 12:18:10 +02:00
helpMenu -> AppendSeparator ();
2019-12-06 08:59:25 +01:00
append_menu_item ( helpMenu , wxID_ANY , "DEBUG gcode thumbnails" , "DEBUG ONLY - read the selected gcode file and generates png for the contained thumbnails" ,
2019-10-25 12:18:10 +02:00
[ this ]( wxCommandEvent & ) { wxGetApp (). gcode_thumbnails_debug (); });
2019-11-07 09:01:28 +01:00
#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG
2018-09-20 08:40:22 +02:00
}
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2018-09-20 08:40:22 +02:00
// menubar
// assign menubar to frame after appending items, otherwise special items
// will not be handled correctly
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-05 16:50:17 +02:00
m_editor_menubar = new wxMenuBar ();
m_editor_menubar -> Append ( fileMenu , _L ( "&File" ));
if ( editMenu ) m_editor_menubar -> Append ( editMenu , _L ( "&Edit" ));
m_editor_menubar -> Append ( windowMenu , _L ( "&Window" ));
if ( viewMenu ) m_editor_menubar -> Append ( viewMenu , _L ( "&View" ));
// Add additional menus from C++
wxGetApp (). add_config_menu ( m_editor_menubar );
m_editor_menubar -> Append ( helpMenu , _L ( "&Help" ));
SetMenuBar ( m_editor_menubar );
#else
2019-01-14 16:48:23 +01:00
auto menubar = new wxMenuBar ();
2019-01-21 12:34:28 +01:00
menubar -> Append ( fileMenu , _ ( L ( "&File" )));
if ( editMenu ) menubar -> Append ( editMenu , _ ( L ( "&Edit" )));
menubar -> Append ( windowMenu , _ ( L ( "&Window" )));
if ( viewMenu ) menubar -> Append ( viewMenu , _ ( L ( "&View" )));
2019-01-14 16:48:23 +01:00
// Add additional menus from C++
wxGetApp (). add_config_menu ( menubar );
2019-01-21 12:34:28 +01:00
menubar -> Append ( helpMenu , _ ( L ( "&Help" )));
2019-01-14 16:48:23 +01:00
SetMenuBar ( menubar );
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2019-01-14 16:48:23 +01:00
#ifdef __APPLE__
2019-01-11 18:09:21 +01:00
// 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
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-09 11:44:25 +02:00
wxMenu * apple_menu = m_editor_menubar -> OSXGetAppleMenu ();
#else
2019-01-14 16:48:23 +01:00
wxMenu * apple_menu = menubar -> OSXGetAppleMenu ();
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2019-01-14 16:48:23 +01:00
if ( apple_menu != nullptr ) {
apple_menu -> Bind ( wxEVT_MENU , [ this ]( wxCommandEvent & ) {
Close ();
}, wxID_EXIT );
2018-09-20 08:40:22 +02:00
}
2019-01-14 16:48:23 +01:00
#endif
2019-04-30 13:51:36 +02:00
if ( plater () -> printer_technology () == ptSLA )
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-05 16:50:17 +02:00
update_editor_menubar ();
#else
2019-04-30 13:51:36 +02:00
update_menubar ();
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2019-04-30 13:51:36 +02:00
}
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-05 16:50:17 +02:00
void MainFrame :: init_gcodeviewer_menubar ()
{
wxMenu * fileMenu = new wxMenu ;
{
append_menu_item ( fileMenu , wxID_ANY , _L ( "&Open G-code" ) + dots + " \t Ctrl+O" , _L ( "Open a G-code file" ),
2020-07-03 12:17:12 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater != nullptr ) m_plater -> load_gcode (); }, "open" , nullptr ,
2020-06-05 16:50:17 +02:00
[ this ]() { return m_plater != nullptr ; }, this );
fileMenu -> AppendSeparator ();
2020-07-03 12:17:12 +02:00
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 );
fileMenu -> AppendSeparator ();
2020-06-05 16:50:17 +02:00
append_menu_item ( fileMenu , wxID_ANY , _L ( "Exit &G-code preview" ), _L ( "Switch to editor mode" ),
[ this ]( wxCommandEvent & ) { set_mode ( EMode :: Editor ); });
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_gcodeviewer_menubar = new wxMenuBar ();
m_gcodeviewer_menubar -> Append ( fileMenu , _L ( "&File" ));
if (( viewMenu != nullptr ))
m_gcodeviewer_menubar -> Append ( viewMenu , _L ( "&View" ));
m_gcodeviewer_menubar -> Append ( helpMenu , _L ( "&Help" ));
}
void MainFrame :: set_mode ( EMode mode )
{
2020-06-19 15:32:44 +02:00
if ( m_mode == mode )
return ;
wxBusyCursor busy ;
2020-06-05 16:50:17 +02:00
m_mode = mode ;
switch ( m_mode )
{
default :
case EMode :: Editor :
{
2020-06-19 15:32:44 +02:00
update_layout ();
select_tab ( 0 );
2020-06-05 16:50:17 +02:00
m_plater -> reset ();
2020-06-22 11:49:58 +02:00
m_plater -> reset_gcode_toolpaths ();
2020-06-05 16:50:17 +02:00
2020-06-08 14:37:40 +02:00
m_plater -> Freeze ();
2020-06-19 15:32:44 +02:00
// reinitialize undo/redo stack
m_plater -> clear_undo_redo_stack_main ();
m_plater -> take_snapshot ( _L ( "New Project" ));
2020-08-21 11:36:08 +02:00
// restore sla printer if it was deselected when switching to gcode viewer mode
if ( m_restore_from_gcode_viewer . sla_technology ) {
m_plater -> set_printer_technology ( ptSLA );
m_restore_from_gcode_viewer . sla_technology = false ;
}
2020-06-05 16:50:17 +02:00
// switch view
m_plater -> select_view_3D ( "3D" );
2020-06-08 12:27:32 +02:00
m_plater -> select_view ( "iso" );
// switch printbed
m_plater -> set_bed_shape ();
2020-06-05 16:50:17 +02:00
// switch menubar
SetMenuBar ( m_editor_menubar );
// show toolbars
m_plater -> enable_view_toolbar ( true );
if ( m_restore_from_gcode_viewer . collapse_toolbar_enabled ) {
m_plater -> get_collapse_toolbar (). set_enabled ( true );
m_restore_from_gcode_viewer . collapse_toolbar_enabled = false ;
}
// show sidebar
if ( m_restore_from_gcode_viewer . collapsed_sidebar ) {
m_plater -> collapse_sidebar ( false );
m_restore_from_gcode_viewer . collapsed_sidebar = false ;
}
2020-06-08 14:37:40 +02:00
m_plater -> Thaw ();
2020-08-26 13:01:54 +02:00
SetIcon ( wxIcon ( Slic3r :: var ( "PrusaSlicer_128px.png" ), wxBITMAP_TYPE_PNG ));
2020-06-05 16:50:17 +02:00
break ;
}
case EMode :: GCodeViewer :
{
2020-06-19 15:32:44 +02:00
update_layout ();
2020-06-05 16:50:17 +02:00
m_plater -> reset ();
2020-06-08 09:12:20 +02:00
m_plater -> reset_last_loaded_gcode ();
2020-06-22 11:49:58 +02:00
m_plater -> reset_gcode_toolpaths ();
2020-06-05 16:50:17 +02:00
2020-06-08 14:37:40 +02:00
m_plater -> Freeze ();
2020-06-05 16:50:17 +02:00
// reinitialize undo/redo stack
m_plater -> clear_undo_redo_stack_main ();
m_plater -> take_snapshot ( _L ( "New Project" ));
2020-08-21 11:36:08 +02:00
// switch to FFF printer mode
m_restore_from_gcode_viewer . sla_technology = m_plater -> set_printer_technology ( ptFFF );
2020-06-05 16:50:17 +02:00
// switch view
m_plater -> select_view_3D ( "Preview" );
2020-06-08 12:27:32 +02:00
m_plater -> select_view ( "iso" );
// switch printbed
2020-07-29 14:20:01 +02:00
m_plater -> set_bed_shape ({ { 0.0 , 0.0 }, { 200.0 , 0.0 }, { 200.0 , 200.0 }, { 0.0 , 200.0 } }, "" , "" , true );
2020-06-08 12:27:32 +02:00
2020-06-05 16:50:17 +02:00
// switch menubar
SetMenuBar ( m_gcodeviewer_menubar );
// hide toolbars
m_plater -> enable_view_toolbar ( false );
if ( wxGetApp (). app_config -> get ( "show_collapse_button" ) == "1" ) {
m_plater -> get_collapse_toolbar (). set_enabled ( false );
m_restore_from_gcode_viewer . collapse_toolbar_enabled = true ;
}
// hide sidebar
if ( wxGetApp (). app_config -> get ( "collapsed_sidebar" ) != "1" ) {
m_plater -> collapse_sidebar ( true );
m_restore_from_gcode_viewer . collapsed_sidebar = true ;
}
2020-06-08 14:37:40 +02:00
m_plater -> Thaw ();
2020-08-26 13:01:54 +02:00
SetIcon ( wxIcon ( Slic3r :: var ( "PrusaSlicerGCodeViewer_128px.png" ), wxBITMAP_TYPE_PNG ));
2020-06-05 16:50:17 +02:00
break ;
}
}
}
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2020-06-05 16:50:17 +02:00
2020-08-03 15:00:19 +02:00
#if ENABLE_GCODE_VIEWER
2020-06-05 16:50:17 +02:00
void MainFrame :: update_editor_menubar ()
#else
2019-04-30 13:51:36 +02:00
void MainFrame :: update_menubar ()
2020-08-03 15:00:19 +02:00
#endif // ENABLE_GCODE_VIEWER
2019-04-30 13:51:36 +02:00
{
const bool is_fff = plater () -> printer_technology () == ptFFF ;
2019-08-01 16:20:46 +02:00
m_changeable_menu_items [ miExport ] -> SetItemLabel (( is_fff ? _ ( L ( "Export &G-code" )) : _ ( L ( "E&xport" )) ) + dots + " \t Ctrl+G" );
m_changeable_menu_items [ miSend ] -> SetItemLabel (( is_fff ? _ ( L ( "S&end G-code" )) : _ ( L ( "S&end to print" ))) + dots + " \t Ctrl+Shift+G" );
2019-04-30 13:51:36 +02:00
m_changeable_menu_items [ miMaterialTab ] -> SetItemLabel (( is_fff ? _ ( L ( "&Filament Settings Tab" )) : _ ( L ( "Mate&rial Settings Tab" ))) + " \t Ctrl+3" );
2020-04-23 12:41:38 +02:00
m_changeable_menu_items [ miMaterialTab ] -> SetBitmap ( create_scaled_bitmap ( is_fff ? "spool" : "resin" ));
m_changeable_menu_items [ miPrinterTab ] -> SetBitmap ( create_scaled_bitmap ( is_fff ? "printer" : "sla_printer" ));
2018-09-20 08:40:22 +02:00
}
// To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
2018-10-31 12:56:08 +01:00
void MainFrame :: quick_slice ( const int qs )
{
2018-09-20 08:40:22 +02:00
// my $progress_dialog;
wxString input_file ;
// eval
// {
// validate configuration
2018-09-21 01:33:41 +02:00
auto config = wxGetApp (). preset_bundle -> full_config ();
2019-05-09 14:15:56 +02:00
auto valid = config . validate ();
if ( ! valid . empty ()) {
show_error ( this , valid );
return ;
}
2018-09-20 08:40:22 +02:00
// select input file
2018-09-21 01:33:41 +02:00
if ( ! ( qs & qsReslice )) {
2019-08-15 12:52:56 +02:00
wxFileDialog dlg ( this , _ ( L ( "Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA) : ")),
2018-09-21 01:33:41 +02:00
wxGetApp (). app_config -> get_last_dir (), "" ,
2018-12-06 17:32:49 +01:00
file_wildcards ( FT_MODEL ), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
2019-08-15 12:52:56 +02:00
if ( dlg . ShowModal () != wxID_OK )
2018-09-20 08:40:22 +02:00
return ;
2019-08-15 12:52:56 +02:00
input_file = dlg . GetPath ();
2018-09-21 01:33:41 +02:00
if ( ! ( qs & qsExportSVG ))
2018-09-20 08:40:22 +02:00
m_qs_last_input_file = input_file ;
}
else {
if ( m_qs_last_input_file . IsEmpty ()) {
2019-08-15 12:52:56 +02:00
wxMessageDialog dlg ( this , _ ( L ( "No previously sliced file." )),
2018-09-20 08:40:22 +02:00
_ ( L ( "Error" )), wxICON_ERROR | wxOK );
2019-08-15 12:52:56 +02:00
dlg . ShowModal ();
2018-09-20 08:40:22 +02:00
return ;
}
2019-04-13 14:45:35 +02:00
if ( std :: ifstream ( m_qs_last_input_file . ToUTF8 (). data ())) {
2019-08-15 12:52:56 +02:00
wxMessageDialog dlg ( this , _ ( L ( "Previously sliced file (" )) + m_qs_last_input_file + _ ( L ( ") not found." )),
2018-09-20 08:40:22 +02:00
_ ( L ( "File Not Found" )), wxICON_ERROR | wxOK );
2019-08-15 12:52:56 +02:00
dlg . ShowModal ();
2018-09-20 08:40:22 +02:00
return ;
}
input_file = m_qs_last_input_file ;
}
auto input_file_basename = get_base_name ( input_file );
2018-09-21 01:33:41 +02:00
wxGetApp (). app_config -> update_skein_dir ( get_dir_name ( input_file ));
2018-09-20 08:40:22 +02:00
auto bed_shape = Slic3r :: Polygon :: new_scale ( config . option < ConfigOptionPoints > ( "bed_shape" ) -> values );
// auto print_center = Slic3r::Pointf->new_unscale(bed_shape.bounding_box().center());
//
// auto sprint = new Slic3r::Print::Simple(
// print_center = > print_center,
2018-10-31 12:56:08 +01:00
// status_cb = > [](int percent, const wxString& msg) {
2018-09-21 01:33:41 +02:00
// m_progress_dialog->Update(percent, msg+"…");
2018-09-20 08:40:22 +02:00
// });
// keep model around
2019-01-03 14:34:53 +01:00
auto model = Slic3r :: Model :: read_from_file ( input_file . ToUTF8 (). data ());
2018-09-20 08:40:22 +02:00
// sprint->apply_config(config);
// sprint->set_model(model);
// Copy the names of active presets into the placeholder parser.
2018-09-21 01:33:41 +02:00
// wxGetApp().preset_bundle->export_selections(sprint->placeholder_parser);
2018-09-20 08:40:22 +02:00
// select output file
wxString output_file ;
2018-09-21 01:33:41 +02:00
if ( qs & qsReslice ) {
2018-09-20 08:40:22 +02:00
if ( ! m_qs_last_output_file . IsEmpty ())
output_file = m_qs_last_output_file ;
}
2018-09-21 01:33:41 +02:00
else if ( qs & qsSaveAs ) {
2018-09-20 08:40:22 +02:00
// The following line may die if the output_filename_format template substitution fails.
2020-03-02 11:48:31 +01:00
wxFileDialog dlg ( this , from_u8 (( boost :: format ( _utf8 ( L ( "Save %s file as:" ))) % (( qs & qsExportSVG ) ? _ ( L ( "SVG" )) : _ ( L ( "G-code" )))). str ()),
2018-09-21 01:33:41 +02:00
wxGetApp (). app_config -> get_last_output_dir ( get_dir_name ( output_file )), get_base_name ( input_file ),
2018-12-06 17:32:49 +01:00
qs & qsExportSVG ? file_wildcards ( FT_SVG ) : file_wildcards ( FT_GCODE ),
2018-09-20 08:40:22 +02:00
wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
2019-08-15 12:52:56 +02:00
if ( dlg . ShowModal () != wxID_OK )
2018-09-20 08:40:22 +02:00
return ;
2019-08-15 12:52:56 +02:00
output_file = dlg . GetPath ();
2018-09-21 01:33:41 +02:00
if ( ! ( qs & qsExportSVG ))
2018-09-20 08:40:22 +02:00
m_qs_last_output_file = output_file ;
2018-09-21 01:33:41 +02:00
wxGetApp (). app_config -> update_last_output_dir ( get_dir_name ( output_file ));
2018-09-20 08:40:22 +02:00
}
2018-09-21 01:33:41 +02:00
else if ( qs & qsExportPNG ) {
2019-08-15 12:52:56 +02:00
wxFileDialog dlg ( this , _ ( L ( "Save zip file as:" )),
2018-09-21 01:33:41 +02:00
wxGetApp (). app_config -> get_last_output_dir ( get_dir_name ( output_file )),
2019-02-25 15:01:39 +01:00
get_base_name ( output_file ), "*.sl1" , wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
2019-08-15 12:52:56 +02:00
if ( dlg . ShowModal () != wxID_OK )
2018-09-20 08:40:22 +02:00
return ;
2019-08-15 12:52:56 +02:00
output_file = dlg . GetPath ();
2018-09-20 08:40:22 +02:00
}
// show processbar dialog
2020-07-10 10:20:57 +02:00
m_progress_dialog = new wxProgressDialog ( _L ( "Slicing" ) + dots ,
// TRN "Processing input_file_basename"
from_u8 (( boost :: format ( _utf8 ( L ( "Processing %s" ))) % ( input_file_basename + dots )). str ()),
100 , nullptr , wxPD_AUTO_HIDE );
2018-09-20 08:40:22 +02:00
m_progress_dialog -> Pulse ();
{
// my @warnings = ();
// local $SIG{ __WARN__ } = sub{ push @warnings, $_[0] };
// sprint->output_file(output_file);
// if (export_svg) {
// sprint->export_svg();
// }
// else if(export_png) {
// sprint->export_png();
// }
// else {
// sprint->export_gcode();
// }
// sprint->status_cb(undef);
// Slic3r::GUI::warning_catcher($self)->($_) for @warnings;
}
m_progress_dialog -> Destroy ();
m_progress_dialog = nullptr ;
auto message = input_file_basename + _ ( L ( " was successfully sliced." ));
// wxTheApp->notify(message);
wxMessageDialog ( this , message , _ ( L ( "Slicing Done!" )), wxOK | wxICON_INFORMATION ). ShowModal ();
// };
2018-10-31 12:56:08 +01:00
// Slic3r::GUI::catch_error(this, []() { if (m_progress_dialog) m_progress_dialog->Destroy(); });
2018-09-20 08:40:22 +02:00
}
2018-10-17 11:12:38 +02:00
void MainFrame :: reslice_now ()
{
if ( m_plater )
m_plater -> reslice ();
2018-09-20 08:40:22 +02:00
}
void MainFrame :: repair_stl ()
{
wxString input_file ;
{
2019-08-15 12:52:56 +02:00
wxFileDialog dlg ( this , _ ( L ( "Select the STL file to repair:" )),
2018-09-21 01:33:41 +02:00
wxGetApp (). app_config -> get_last_dir (), "" ,
2018-12-06 17:32:49 +01:00
file_wildcards ( FT_STL ), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
2019-08-15 12:52:56 +02:00
if ( dlg . ShowModal () != wxID_OK )
2018-09-20 08:40:22 +02:00
return ;
2019-08-15 12:52:56 +02:00
input_file = dlg . GetPath ();
2018-09-20 08:40:22 +02:00
}
2019-04-13 14:45:35 +02:00
wxString output_file = input_file ;
2018-09-20 08:40:22 +02:00
{
2019-08-15 12:52:56 +02:00
wxFileDialog dlg ( this , L ( "Save OBJ file (less prone to coordinate errors than STL) as : "),
2019-04-13 14:45:35 +02:00
get_dir_name ( output_file ), get_base_name ( output_file , ".obj" ),
2018-12-06 17:32:49 +01:00
file_wildcards ( FT_OBJ ), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
2019-08-15 12:52:56 +02:00
if ( dlg . ShowModal () != wxID_OK )
2019-04-13 14:45:35 +02:00
return ;
2019-08-15 12:52:56 +02:00
output_file = dlg . GetPath ();
2018-09-20 08:40:22 +02:00
}
auto tmesh = new Slic3r :: TriangleMesh ();
2019-04-13 14:45:35 +02:00
tmesh -> ReadSTLFile ( input_file . ToUTF8 (). data ());
2018-09-20 08:40:22 +02:00
tmesh -> repair ();
2019-04-13 14:45:35 +02:00
tmesh -> WriteOBJFile ( output_file . ToUTF8 (). data ());
2018-09-20 08:40:22 +02:00
Slic3r :: GUI :: show_info ( this , L ( "Your file was repaired." ), L ( "Repair" ));
}
void MainFrame :: export_config ()
{
// Generate a cummulative configuration for the selected print, filaments and printer.
2018-09-21 01:33:41 +02:00
auto config = wxGetApp (). preset_bundle -> full_config ();
2018-09-20 08:40:22 +02:00
// Validate the cummulative configuration.
auto valid = config . validate ();
2019-05-09 14:15:56 +02:00
if ( ! valid . empty ()) {
show_error ( this , valid );
2018-09-20 08:40:22 +02:00
return ;
}
// Ask user for the file name for the config file.
2019-08-15 12:52:56 +02:00
wxFileDialog dlg ( this , _ ( L ( "Save configuration as:" )),
2018-09-21 01:33:41 +02:00
! m_last_config . IsEmpty () ? get_dir_name ( m_last_config ) : wxGetApp (). app_config -> get_last_dir (),
2018-09-20 08:40:22 +02:00
! m_last_config . IsEmpty () ? get_base_name ( m_last_config ) : "config.ini" ,
2018-12-06 17:32:49 +01:00
file_wildcards ( FT_INI ), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
2018-09-21 15:42:31 +02:00
wxString file ;
2019-08-15 12:52:56 +02:00
if ( dlg . ShowModal () == wxID_OK )
file = dlg . GetPath ();
2018-09-20 08:40:22 +02:00
if ( ! file . IsEmpty ()) {
2018-09-21 01:33:41 +02:00
wxGetApp (). app_config -> update_config_dir ( get_dir_name ( file ));
2018-09-20 08:40:22 +02:00
m_last_config = file ;
2019-01-03 14:34:53 +01:00
config . save ( file . ToUTF8 (). data ());
2018-09-20 08:40:22 +02:00
}
}
// Load a config file containing a Print, Filament & Printer preset.
2019-01-09 10:43:17 +01:00
void MainFrame :: load_config_file ()
2018-09-20 08:40:22 +02:00
{
2019-01-09 10:43:17 +01:00
if ( ! wxGetApp (). check_unsaved_changes ())
return ;
2019-08-15 12:52:56 +02:00
wxFileDialog dlg ( this , _ ( L ( "Select configuration to load:" )),
2019-01-09 10:43:17 +01:00
! m_last_config . IsEmpty () ? get_dir_name ( m_last_config ) : wxGetApp (). app_config -> get_last_dir (),
"config.ini" , "INI files (*.ini, *.gcode)|*.ini ; * . INI ; * . gcode ; * . g ", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
wxString file ;
2019-08-15 12:52:56 +02:00
if ( dlg . ShowModal () == wxID_OK )
file = dlg . GetPath ();
2019-09-24 16:01:01 +02:00
if ( ! file . IsEmpty () && this -> load_config_file ( file . ToUTF8 (). data ())) {
2019-01-09 10:43:17 +01:00
wxGetApp (). app_config -> update_config_dir ( get_dir_name ( file ));
m_last_config = file ;
2018-09-20 08:40:22 +02:00
}
2019-01-09 10:43:17 +01:00
}
// Load a config file containing a Print, Filament & Printer preset from command line.
bool MainFrame :: load_config_file ( const std :: string & path )
{
2018-10-17 11:12:38 +02:00
try {
2019-01-09 10:43:17 +01:00
wxGetApp (). preset_bundle -> load_config_file ( path );
2018-12-03 13:14:28 +01:00
} catch ( const std :: exception & ex ) {
show_error ( this , ex . what ());
2019-01-09 10:43:17 +01:00
return false ;
2018-10-17 11:12:38 +02:00
}
2019-01-09 10:43:17 +01:00
wxGetApp (). load_current_presets ();
return true ;
2018-09-20 08:40:22 +02:00
}
void MainFrame :: export_configbundle ()
{
2018-09-21 01:33:41 +02:00
if ( ! wxGetApp (). check_unsaved_changes ())
2018-09-20 08:40:22 +02:00
return ;
// validate current configuration in case it's dirty
2018-12-03 13:14:28 +01:00
auto err = wxGetApp (). preset_bundle -> full_config (). validate ();
if ( ! err . empty ()) {
show_error ( this , err );
2018-09-20 08:40:22 +02:00
return ;
}
// Ask user for a file name.
2019-08-15 12:52:56 +02:00
wxFileDialog dlg ( this , _ ( L ( "Save presets bundle as:" )),
2018-09-21 01:33:41 +02:00
! m_last_config . IsEmpty () ? get_dir_name ( m_last_config ) : wxGetApp (). app_config -> get_last_dir (),
2019-04-15 19:49:37 +02:00
SLIC3R_APP_KEY "_config_bundle.ini" ,
2018-12-06 17:32:49 +01:00
file_wildcards ( FT_INI ), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
2018-09-21 15:42:31 +02:00
wxString file ;
2019-08-15 12:52:56 +02:00
if ( dlg . ShowModal () == wxID_OK )
file = dlg . GetPath ();
2018-09-20 08:40:22 +02:00
if ( ! file . IsEmpty ()) {
// Export the config bundle.
2018-10-17 11:12:38 +02:00
wxGetApp (). app_config -> update_config_dir ( get_dir_name ( file ));
try {
2019-01-03 14:34:53 +01:00
wxGetApp (). preset_bundle -> export_configbundle ( file . ToUTF8 (). data ());
2018-12-03 13:14:28 +01:00
} catch ( const std :: exception & ex ) {
show_error ( this , ex . what ());
2018-10-17 11:12:38 +02:00
}
2018-09-20 08:40:22 +02:00
}
}
// 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.
2018-10-17 11:12:38 +02:00
void MainFrame :: load_configbundle ( wxString file /* = wxEmptyString, const bool reset_user_profile*/ )
{
2018-09-21 01:33:41 +02:00
if ( ! wxGetApp (). check_unsaved_changes ())
2018-09-20 08:40:22 +02:00
return ;
if ( file . IsEmpty ()) {
2019-08-15 12:52:56 +02:00
wxFileDialog dlg ( this , _ ( L ( "Select configuration to load:" )),
2018-09-21 01:33:41 +02:00
! m_last_config . IsEmpty () ? get_dir_name ( m_last_config ) : wxGetApp (). app_config -> get_last_dir (),
2018-12-06 17:32:49 +01:00
"config.ini" , file_wildcards ( FT_INI ), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
2019-08-15 12:52:56 +02:00
if ( dlg . ShowModal () != wxID_OK )
return ;
file = dlg . GetPath ();
2019-01-09 10:43:17 +01:00
}
2018-09-20 08:40:22 +02:00
2018-09-21 01:33:41 +02:00
wxGetApp (). app_config -> update_config_dir ( get_dir_name ( file ));
2018-09-20 08:40:22 +02:00
auto presets_imported = 0 ;
2018-10-17 11:12:38 +02:00
try {
2019-01-03 14:34:53 +01:00
presets_imported = wxGetApp (). preset_bundle -> load_configbundle ( file . ToUTF8 (). data ());
2018-12-03 13:14:28 +01:00
} catch ( const std :: exception & ex ) {
show_error ( this , ex . what ());
return ;
2018-10-17 11:12:38 +02:00
}
2018-09-20 08:40:22 +02:00
// Load the currently selected preset into the GUI, update the preset selection box.
2018-11-07 14:57:50 +01:00
wxGetApp (). load_current_presets ();
2018-09-20 08:40:22 +02:00
const auto message = wxString :: Format ( _ ( L ( "%d presets successfully imported." )), presets_imported );
2020-03-04 09:24:13 +01:00
Slic3r :: GUI :: show_info ( this , message , wxString ( "Info" ));
2018-09-20 08:40:22 +02:00
}
// Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset.
2019-12-02 22:48:01 -05:00
// Also update the plater with the new presets.
2018-10-17 11:12:38 +02:00
void MainFrame :: load_config ( const DynamicPrintConfig & config )
{
2019-01-09 10:43:17 +01:00
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)) {
2019-05-10 15:10:17 +02:00
if (tab->type() == Slic3r::Preset::TYPE_PRINTER)
2019-01-09 10:43:17 +01:00
static_cast<TabPrinter*>(tab)->update_pages();
tab->load_config(config);
}
if (m_plater)
2018-10-17 11:12:38 +02:00
m_plater->on_config_change(config);
2019-01-09 10:43:17 +01:00
#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 ));
}
2019-06-17 10:05:46 +02:00
wxGetApp (). load_current_presets ();
2019-01-09 10:43:17 +01:00
#endif
2018-09-20 08:40:22 +02:00
}
2020-05-04 22:30:38 +02:00
void MainFrame :: select_tab ( size_t tab /* = size_t(-1)*/ )
2018-10-17 11:12:38 +02:00
{
2020-07-22 15:52:01 +02:00
bool tabpanel_was_hidden = false ;
2020-06-11 14:02:59 +02:00
if ( m_layout == ESettingsLayout :: Dlg ) {
2020-05-05 17:23:58 +02:00
if ( tab == 0 ) {
2020-06-11 14:02:59 +02:00
if ( m_settings_dialog . IsShown ())
this -> SetFocus ();
2020-05-11 22:00:26 +02:00
// plater should be focused for correct navigation inside search window
if ( m_plater -> canvas3D () -> is_search_pressed ())
m_plater -> SetFocus ();
2020-05-04 22:30:38 +02:00
return ;
2020-05-05 17:23:58 +02:00
}
2020-06-11 14:02:59 +02:00
// Show/Activate Settings Dialog
#ifdef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
if ( m_settings_dialog . IsShown ())
m_settings_dialog . Hide ();
m_tabpanel -> Show ();
m_settings_dialog . Show ();
#else
if ( m_settings_dialog . IsShown ())
m_settings_dialog . SetFocus ();
else {
2020-07-22 15:52:01 +02:00
tabpanel_was_hidden = true ;
2020-06-11 14:02:59 +02:00
m_tabpanel -> Show ();
m_settings_dialog . Show ();
}
#endif
2020-04-23 12:41:38 +02:00
}
2020-06-11 14:02:59 +02:00
else if ( m_layout == ESettingsLayout :: New ) {
2020-06-29 15:23:24 +02:00
m_main_sizer -> Show ( m_plater , tab == 0 );
2020-07-22 15:52:01 +02:00
tabpanel_was_hidden = ! m_main_sizer -> IsShown ( m_tabpanel );
2020-06-29 15:23:24 +02:00
m_main_sizer -> Show ( m_tabpanel , tab != 0 );
2020-05-11 22:00:26 +02:00
// plater should be focused for correct navigation inside search window
if ( tab == 0 && m_plater -> canvas3D () -> is_search_pressed ())
m_plater -> SetFocus ();
2020-05-04 22:30:38 +02:00
Layout ();
2020-04-23 12:41:38 +02:00
}
2020-05-04 22:30:38 +02:00
2020-07-22 15:52:01 +02:00
// When we run application in ESettingsLayout::New or ESettingsLayout::Dlg mode, tabpanel is hidden from the very beginning
// and as a result Tab::update_changed_tree_ui() function couldn't update m_is_nonsys_values values,
// which are used for update TreeCtrl and "revert_buttons".
// So, force the call of this function for Tabs, if tab panel was hidden
if ( tabpanel_was_hidden )
for ( auto tab : wxGetApp (). tabs_list )
tab -> update_changed_tree_ui ();
2020-06-12 12:35:17 +02:00
// when tab == -1, it means we should show the last selected tab
2020-06-11 14:02:59 +02:00
m_tabpanel -> SetSelection ( tab == ( size_t )( - 1 ) ? m_last_selected_tab : ( m_layout == ESettingsLayout :: Dlg && tab != 0 ) ? tab - 1 : tab );
2018-09-20 08:40:22 +02:00
}
// Set a camera direction, zoom to all objects.
2018-10-17 11:12:38 +02:00
void MainFrame :: select_view ( const std :: string & direction )
{
2018-10-17 12:59:58 +02:00
if ( m_plater )
m_plater -> select_view ( direction );
2018-09-20 08:40:22 +02:00
}
2019-02-22 09:38:56 +01:00
// #ys_FIXME_to_delete
2018-10-03 16:27:02 +02:00
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 ();
2018-10-04 11:12:55 +02:00
if ( m_plater != nullptr && presets != nullptr ) {
2018-10-03 16:27:02 +02:00
// FIXME: The preset type really should be a property of Tab instead
2018-10-09 12:41:05 +02:00
Slic3r :: Preset :: Type preset_type = tab -> type ();
2018-10-31 12:56:08 +01:00
if ( preset_type == Slic3r :: Preset :: TYPE_INVALID ) {
2018-10-03 16:27:02 +02:00
wxASSERT ( false );
return ;
}
2018-10-17 11:12:38 +02:00
m_plater -> on_config_change ( * tab -> get_config ());
2018-11-16 11:14:56 +01:00
m_plater -> sidebar (). update_presets ( preset_type );
2018-10-03 16:27:02 +02:00
}
}
2019-02-22 09:38:56 +01:00
// #ys_FIXME_to_delete
2018-10-10 13:53:45 +02:00
void MainFrame :: on_value_changed ( wxCommandEvent & event )
2018-10-09 12:41:05 +02:00
{
2018-10-10 13:53:45 +02:00
auto * tab = dynamic_cast < Tab *> ( event . GetEventObject ());
wxASSERT ( tab != nullptr );
if ( tab == nullptr )
return ;
auto opt_key = event . GetString ();
if ( m_plater ) {
2018-10-17 11:12:38 +02:00
m_plater -> on_config_change ( * tab -> get_config ()); // propagate config change events to the plater
2018-10-31 12:56:08 +01:00
if ( opt_key == "extruders_count" ) {
2018-10-17 11:12:38 +02:00
auto value = event . GetInt ();
m_plater -> on_extruders_change ( value );
}
}
2019-02-22 09:38:56 +01:00
}
void MainFrame :: on_config_changed ( DynamicPrintConfig * config ) const
{
2019-02-22 17:18:58 +01:00
if ( m_plater )
2019-02-22 09:38:56 +01:00
m_plater -> on_config_change ( * config ); // propagate config change events to the plater
2018-10-09 12:41:05 +02:00
}
2019-07-12 15:36:01 +02:00
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 );
wxGetApp (). app_config -> save ();
}
}
//
2018-09-20 08:40:22 +02:00
// 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 ()
{
2020-02-26 10:18:04 +01:00
// const bool bp_on = wxGetApp().app_config->get("background_processing") == "1";
2019-01-21 12:34:28 +01:00
// m_menu_item_reslice_now->Enable(!bp_on);
2020-02-26 10:18:04 +01:00
// m_plater->sidebar().show_reslice(!bp_on);
// m_plater->sidebar().show_export(bp_on);
// m_plater->sidebar().Layout();
2018-12-04 11:14:39 +01:00
if ( m_plater )
m_plater -> update_ui_from_settings ();
2018-11-16 11:14:56 +01:00
for ( auto tab : wxGetApp (). tabs_list )
tab -> update_ui_from_settings ();
2018-09-20 08:40:22 +02:00
}
2019-04-13 14:45:35 +02:00
std :: string MainFrame :: get_base_name ( const wxString & full_name , const char * extension ) const
2018-09-20 08:40:22 +02:00
{
2019-04-13 14:45:35 +02:00
boost :: filesystem :: path filename = boost :: filesystem :: path ( full_name . wx_str ()). filename ();
if ( extension != nullptr )
filename = filename . replace_extension ( extension );
return filename . string ();
2018-09-20 08:40:22 +02:00
}
2019-01-09 10:43:17 +01:00
std :: string MainFrame :: get_dir_name ( const wxString & full_name ) const
2018-09-20 08:40:22 +02:00
{
2019-01-09 10:43:17 +01:00
return boost :: filesystem :: path ( full_name . wx_str ()). parent_path (). string ();
2018-09-20 08:40:22 +02:00
}
2020-05-04 22:30:38 +02:00
// ----------------------------------------------------------------------------
// SettingsDialog
// ----------------------------------------------------------------------------
2020-05-05 17:23:58 +02:00
SettingsDialog :: SettingsDialog ( MainFrame * mainframe )
2020-06-11 14:02:59 +02:00
: DPIDialog ( mainframe , wxID_ANY , wxString ( SLIC3R_APP_NAME ) + " - " + _L ( "Settings" ), wxDefaultPosition , wxDefaultSize ,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX , "settings_dialog" ),
2020-05-05 17:23:58 +02:00
m_main_frame ( mainframe )
2020-05-04 22:30:38 +02:00
{
this -> SetFont ( wxGetApp (). normal_font ());
2020-06-11 14:02:59 +02:00
this -> SetBackgroundColour ( wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOW ));
2020-05-04 22:30:38 +02:00
2020-08-26 13:01:54 +02:00
SetIcon ( wxIcon ( Slic3r :: var ( "PrusaSlicer_128px.png" ), wxBITMAP_TYPE_PNG ));
// // 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("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG));
//#endif // _WIN32
2020-05-04 22:30:38 +02:00
2020-06-11 14:02:59 +02:00
this -> Bind ( wxEVT_SHOW , [ this ]( wxShowEvent & evt ) {
auto key_up_handker = [ this ]( wxKeyEvent & evt ) {
if (( evt . GetModifiers () & wxMOD_CONTROL ) != 0 ) {
switch ( evt . GetKeyCode ()) {
case '1' : { m_main_frame -> select_tab ( 0 ); break ; }
case '2' : { m_main_frame -> select_tab ( 1 ); break ; }
case '3' : { m_main_frame -> select_tab ( 2 ); break ; }
case '4' : { m_main_frame -> select_tab ( 3 ); break ; }
#ifdef __APPLE__
case 'f' :
#else /* __APPLE__ */
case WXK_CONTROL_F :
#endif /* __APPLE__ */
case 'F' : { m_main_frame -> plater () -> search ( false ); break ; }
default : break ;
}
}
};
if ( evt . IsShown ()) {
if ( m_tabpanel != nullptr )
m_tabpanel -> Bind ( wxEVT_KEY_UP , key_up_handker );
}
else {
if ( m_tabpanel != nullptr )
m_tabpanel -> Unbind ( wxEVT_KEY_UP , key_up_handker );
}
});
2020-05-05 17:23:58 +02:00
2020-05-04 22:30:38 +02:00
// 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 )
{
const int & em = em_unit ();
const wxSize & size = wxSize ( 85 * em , 50 * em );
2020-05-25 10:21:30 +02:00
// update Tabs
for ( auto tab : wxGetApp (). tabs_list )
tab -> msw_rescale ();
2020-05-04 22:30:38 +02:00
SetMinSize ( size );
Fit ();
Refresh ();
}
2018-09-20 08:40:22 +02:00
} // GUI
2018-09-21 01:33:41 +02:00
} // Slic3r