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>
2018-10-18 15:13:38 +02:00
#include <wx/glcanvas.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"
2018-11-26 14:41:58 +01:00
2018-09-20 08:40:22 +02:00
#include "Tab.hpp"
#include "PresetBundle.hpp"
#include "ProgressStatusBar.hpp"
#include "3DScene.hpp"
#include "AppConfig.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"
2018-11-26 14:41:58 +01:00
#include "I18N.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
namespace Slic3r {
2018-09-21 01:33:41 +02:00
namespace GUI {
2018-12-19 17:38:41 +01:00
MainFrame :: MainFrame () :
2019-04-02 12:00:50 +02:00
DPIFrame ( NULL , wxID_ANY , SLIC3R_BUILD , wxDefaultPosition , wxDefaultSize , wxDEFAULT_FRAME_STYLE , "mainframe" ),
2018-12-14 15:27:34 +01:00
m_printhost_queue_dlg ( new PrintHostQueueDialog ( 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 );
this -> SetFont ( this -> normal_font ());
// initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
wxGetApp (). set_em_unit ( std :: max < size_t > ( 10 , GetTextExtent ( "m" ). x - 1 ));
2018-09-24 14:27:03 +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
2018-09-21 01:33:41 +02:00
SetIcon ( wxIcon ( Slic3r :: var ( "Slic3r_128px.png" ), wxBITMAP_TYPE_PNG ));
2018-09-24 14:27:03 +02:00
#endif // _WIN32
2018-09-20 08:40:22 +02:00
2018-12-04 11:14:39 +01:00
// initialize status bar
m_statusbar = new ProgressStatusBar ( this );
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 +
_ ( L ( " - Remember to check for updates at http://github.com/prusa3d/slic3r/releases" )));
2019-04-08 09:37:23 +02:00
/* Load default preset bitmaps before a tabpanel initialization,
* but after filling of an em_unit value
*/
2019-04-09 17:40:14 +02:00
wxGetApp (). preset_bundle -> load_default_preset_bitmaps ( this );
2019-04-08 09:37:23 +02:00
2018-09-20 08:40:22 +02:00
// initialize tabpanel and menubar
2018-09-21 01:33:41 +02:00
init_tabpanel ();
init_menubar ();
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
auto sizer = new wxBoxSizer ( wxVERTICAL );
if ( m_tabpanel )
sizer -> Add ( m_tabpanel , 1 , wxEXPAND );
sizer -> SetSizeHints ( this );
SetSizer ( sizer );
Fit ();
2019-03-18 10:18:24 +01:00
const wxSize 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
// declare events
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 ;
}
2019-01-11 18:09:21 +01:00
2019-03-18 23:22:38 +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 );
2018-09-21 01:33:41 +02: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 ();
2018-09-20 08:40:22 +02:00
// if (m_plater)
// m_plater->print = undef;
2018-09-21 01:33:41 +02:00
_3DScene :: remove_all_canvases ();
2018-09-20 08:40:22 +02:00
// Slic3r::GUI::deregister_on_request_update_callback();
2019-01-25 13:16:32 +01:00
2019-01-28 12:12:14 +01:00
// set to null tabs and a platter
2019-01-25 13:16:32 +01:00
// to avoid any manipulations with them from App->wxEVT_IDLE after of the mainframe closing
2019-01-28 12:12:14 +01:00
wxGetApp (). tabs_list . clear ();
wxGetApp (). plater_ = nullptr ;
2019-01-25 13:16:32 +01:00
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 );
2018-10-17 14:01:10 +02:00
2019-01-11 18:09:21 +01:00
update_ui_from_settings (); // FIXME (?)
2018-09-20 08:40:22 +02:00
}
2019-01-28 12:12:14 +01:00
2018-09-20 08:40:22 +02:00
void MainFrame :: init_tabpanel ()
{
2019-03-18 20:54:01 +01: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 );
2019-05-06 18:28:23 +02:00
m_tabpanel -> SetFont ( Slic3r :: GUI :: wxGetApp (). normal_font ());
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 & ) {
2018-09-21 01:33:41 +02:00
auto panel = m_tabpanel -> GetCurrentPage ();
2018-10-02 13:23:38 +02:00
if ( panel == nullptr )
2018-09-17 12:15:11 +02:00
return ;
2018-11-19 13:17:14 +01:00
auto & tabs_list = wxGetApp (). tabs_list ;
if ( find ( tabs_list . begin (), tabs_list . end (), panel ) != tabs_list . end ()) {
// On GTK, the wxEVT_NOTEBOOK_PAGE_CHANGED event is triggered
// before the MainFrame is fully set up.
static_cast < Tab *> ( panel ) -> OnActivate ();
2018-09-21 01:33:41 +02:00
}
});
2018-09-20 08:40:22 +02:00
2018-12-19 17:38:41 +01:00
m_plater = new Slic3r :: GUI :: Plater ( m_tabpanel , this );
wxGetApp (). plater_ = m_plater ;
m_tabpanel -> AddPage ( m_plater , _ ( L ( "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 ());
}
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 ();
}
bool MainFrame :: can_export_supports () const
{
if (( m_plater == nullptr ) || ( m_plater -> printer_technology () != ptSLA ) || m_plater -> model (). objects . empty ())
return false ;
bool can_export = false ;
const PrintObjects & objects = m_plater -> sla_print (). objects ();
for ( const SLAPrintObject * object : objects )
{
if ( object -> has_mesh ( slaposBasePool ) || object -> has_mesh ( slaposSupportTree ))
{
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-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
{
int page_id = m_tabpanel -> GetSelection ();
2019-01-10 14:45:09 +01:00
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
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-04-02 12:00:50 +02:00
void MainFrame :: on_dpi_changed ( const wxRect & suggested_rect )
{
2019-04-18 02:03:40 +02:00
wxGetApp (). update_fonts ();
2019-05-06 18:28:23 +02:00
this -> SetFont ( this -> normal_font ());
// initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
wxGetApp (). set_em_unit ( std :: max < size_t > ( 10 , GetTextExtent ( "m" ). x - 1 ));
2019-04-10 09:56:32 +02:00
2019-04-17 11:22:30 +02:00
/* Load default preset bitmaps before a tabpanel initialization,
* but after filling of an em_unit value
*/
wxGetApp (). preset_bundle -> load_default_preset_bitmaps ( this );
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
for ( auto tab : wxGetApp (). tabs_list )
2019-04-25 01:45:00 +02:00
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
/* Even if Window is maximized during moving,
* first of all we should imitate Window resizing. So:
* 1. cancel maximization, if it was set
* 2. imitate resizing
* 3. set maximization, if it was set
*/
const bool is_maximized = this -> IsMaximized ();
if ( is_maximized )
this -> Maximize ( false );
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 );
2019-04-02 12:00:50 +02:00
}
2019-04-30 15:13:38 +02:00
static std :: string menu_icon ( const std :: string & icon_name )
{
#ifdef __WXMSW__
const std :: string folder = "white \\ " ;
#else
const std :: string folder = "white/" ;
#endif
return wxGetApp (). dark_mode_menus () ? folder + icon_name : icon_name ;
}
2018-09-20 08:40:22 +02:00
void MainFrame :: init_menubar ()
{
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
{
2019-01-29 14:44:10 +01:00
wxMenuItem * item_open = append_menu_item ( fileMenu , wxID_ANY , _ ( L ( "&Open Project" )) + dots + " \t Ctrl+O" , _ ( L ( "Open a project file" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> load_project (); }, menu_icon ( "open" ));
2019-01-29 14:44:10 +01:00
wxMenuItem * item_save = append_menu_item ( fileMenu , wxID_ANY , _ ( L ( "&Save Project" )) + " \t Ctrl+S" , _ ( L ( "Save current project file" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_3mf ( into_path ( m_plater -> get_project_filename ())); }, menu_icon ( "save" ));
2019-01-29 14:44:10 +01:00
wxMenuItem * item_save_as = append_menu_item ( fileMenu , wxID_ANY , _ ( L ( "Save Project &as" )) + dots + " \t Ctrl+Alt+S" , _ ( L ( "Save current project file as" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_3mf (); }, menu_icon ( "save" ));
2018-11-15 15:27:39 +01:00
fileMenu -> AppendSeparator ();
wxMenu * import_menu = new wxMenu ();
2019-01-21 12:34:28 +01:00
wxMenuItem * item_import_model = append_menu_item ( import_menu , wxID_ANY , _ ( L ( "Import STL/OBJ/AM&F/3MF" )) + dots + " \t Ctrl+I" , _ ( L ( "Load a model" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> add_model (); }, menu_icon ( "import_plater" ));
2018-11-15 15:27:39 +01:00
import_menu -> AppendSeparator ();
2019-01-21 12:34:28 +01:00
append_menu_item ( import_menu , wxID_ANY , _ ( L ( "Import &Config" )) + dots + " \t Ctrl+L" , _ ( L ( "Load exported configuration file" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { load_config_file (); }, menu_icon ( "import_config" ));
2019-01-21 12:34:28 +01: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-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> extract_config_from_project (); }, menu_icon ( "import_config" ));
2018-11-16 09:26:41 +01:00
import_menu -> AppendSeparator ();
2019-01-21 12:34:28 +01:00
append_menu_item ( import_menu , wxID_ANY , _ ( L ( "Import Config &Bundle" )) + dots , _ ( L ( "Load presets from a bundle" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { load_configbundle (); }, menu_icon ( "import_config_bundle" ));
2019-01-21 12:34:28 +01:00
append_submenu ( fileMenu , import_menu , wxID_ANY , _ ( L ( "&Import" )), "" );
2018-11-15 15:27:39 +01:00
wxMenu * export_menu = new wxMenu ();
2019-01-21 12:34:28 +01: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" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_gcode (); }, menu_icon ( "export_gcode" ));
2019-04-30 13:51:36 +02:00
m_changeable_menu_items . push_back ( item_export_gcode );
2018-11-15 15:27:39 +01:00
export_menu -> AppendSeparator ();
2019-01-21 12:34:28 +01:00
wxMenuItem * item_export_stl = append_menu_item ( export_menu , wxID_ANY , _ ( L ( "Export plate as &STL" )) + dots , _ ( L ( "Export current plate as STL" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_stl (); }, menu_icon ( "export_plater" ));
2019-05-02 13:46:39 +02:00
wxMenuItem * item_export_stl_sla = append_menu_item ( export_menu , wxID_ANY , _ ( L ( "Export plate as STL including supports" )) + dots , _ ( L ( "Export current plate as STL including supports" )),
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_stl ( true ); }, menu_icon ( "export_plater" ));
2019-01-21 12:34:28 +01:00
wxMenuItem * item_export_amf = append_menu_item ( export_menu , wxID_ANY , _ ( L ( "Export plate as &AMF" )) + dots , _ ( L ( "Export current plate as AMF" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater ) m_plater -> export_amf (); }, menu_icon ( "export_plater" ));
2018-11-15 15:27:39 +01:00
export_menu -> AppendSeparator ();
2019-01-21 12:34:28 +01:00
append_menu_item ( export_menu , wxID_ANY , _ ( L ( "Export &Config" )) + dots + " \t Ctrl+E" , _ ( L ( "Export current configuration to file" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { export_config (); }, menu_icon ( "export_config" ));
2019-01-21 12:34:28 +01:00
append_menu_item ( export_menu , wxID_ANY , _ ( L ( "Export Config &Bundle" )) + dots , _ ( L ( "Export all presets to file" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { export_configbundle (); }, menu_icon ( "export_config_bundle" ));
2019-01-21 12:34:28 +01:00
append_submenu ( fileMenu , export_menu , wxID_ANY , _ ( L ( "&Export" )), "" );
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;
2019-01-21 12:34:28 +01: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");
2019-01-21 12:34:28 +01: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");
2019-01-21 12:34:28 +01: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
2019-01-21 12:34:28 +01:00
m_menu_item_reslice_now = append_menu_item ( fileMenu , wxID_ANY , _ ( L ( "(Re)Slice &Now" )) + " \t Ctrl+R" , _ ( L ( "Start new slicing process" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { reslice_now (); }, menu_icon ( "re_slice" ));
2018-09-20 08:40:22 +02:00
fileMenu -> AppendSeparator ();
2019-01-21 12:34:28 +01:00
append_menu_item ( fileMenu , wxID_ANY , _ ( L ( "&Repair STL file" )) + dots , _ ( L ( "Automatically repair an STL file" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { repair_stl (); }, menu_icon ( "wrench" ));
2018-09-20 08:40:22 +02:00
fileMenu -> AppendSeparator ();
2019-04-26 10:52:38 +02:00
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-11-15 15:27:39 +01:00
2018-11-16 09:26:41 +01:00
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( m_plater != nullptr ); }, item_open -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable (( m_plater != nullptr ) && can_save ()); }, item_save -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable (( m_plater != nullptr ) && can_save ()); }, item_save_as -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( m_plater != nullptr ); }, item_import_model -> GetId ());
2019-05-02 13:46:39 +02:00
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_export_gcode ()); }, item_export_gcode -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_export_model ()); }, item_export_stl -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_export_supports ()); }, item_export_stl_sla -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_export_model ()); }, item_export_amf -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_slice ()); }, m_menu_item_reslice_now -> GetId ());
2018-09-20 08:40:22 +02:00
}
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
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
wxMenuItem * item_select_all = append_menu_item ( editMenu , wxID_ANY , _ ( L ( "&Select all" )) + sep + GUI :: shortkey_ctrl_prefix () + sep_space + "A" , _ ( L ( "Selects all objects" )),
2019-05-02 13:52:13 +02:00
[ this ]( wxCommandEvent & ) { if ( m_plater != nullptr ) m_plater -> select_all (); }, "" );
2018-11-22 11:31:53 +01:00
editMenu -> AppendSeparator ();
2019-02-04 15:46:12 +01:00
wxMenuItem * item_delete_sel = append_menu_item ( editMenu , wxID_ANY , _ ( L ( "&Delete selected" )) + sep + hotkey_delete , _ ( L ( "Deletes the current selection" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { m_plater -> remove_selected (); }, menu_icon ( "remove_menu" ));
2019-02-04 15:46:12 +01:00
wxMenuItem * item_delete_all = append_menu_item ( editMenu , wxID_ANY , _ ( L ( "Delete &all" )) + sep + GUI :: shortkey_ctrl_prefix () + sep_space + hotkey_delete , _ ( L ( "Deletes all objects" )),
2019-05-02 13:52:13 +02:00
[ this ]( wxCommandEvent & ) { m_plater -> reset_with_confirm (); }, menu_icon ( "delete_all_menu" ));
2018-11-21 15:28:35 +01:00
2019-04-10 08:40:58 +02:00
editMenu -> AppendSeparator ();
2019-04-16 13:47:37 +02:00
wxMenuItem * item_copy = append_menu_item ( editMenu , wxID_ANY , _ ( L ( "&Copy" )) + sep + GUI :: shortkey_ctrl_prefix () + sep_space + "C" , _ ( L ( "Copy selection to clipboard" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { m_plater -> copy_selection_to_clipboard (); }, menu_icon ( "copy_menu" ));
2019-04-16 13:47:37 +02:00
wxMenuItem * item_paste = append_menu_item ( editMenu , wxID_ANY , _ ( L ( "&Paste" )) + sep + GUI :: shortkey_ctrl_prefix () + sep_space + "V" , _ ( L ( "Paste clipboard" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { m_plater -> paste_from_clipboard (); }, menu_icon ( "paste_menu" ));
2018-11-21 15:28:35 +01:00
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_select ()); }, item_select_all -> GetId ());
2018-11-21 15:47:41 +01:00
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_delete ()); }, item_delete_sel -> GetId ());
2018-11-22 11:31:53 +01:00
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_delete_all ()); }, item_delete_all -> GetId ());
2019-04-11 13:20:34 +02:00
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( m_plater -> can_copy ()); }, item_copy -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( m_plater -> can_paste ()); }, item_paste -> GetId ());
2018-11-21 15:28:35 +01:00
}
2018-09-20 08:40:22 +02:00
// Window menu
auto windowMenu = new wxMenu ();
{
size_t tab_offset = 0 ;
if ( m_plater ) {
2019-01-21 12:34:28 +01:00
append_menu_item ( windowMenu , wxID_HIGHEST + 1 , _ ( L ( "&Plater Tab" )) + " \t Ctrl+1" , _ ( L ( "Show the plater" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { select_tab ( 0 ); }, menu_icon ( "plater" ));
2018-09-20 08:40:22 +02:00
tab_offset += 1 ;
}
if ( tab_offset > 0 ) {
windowMenu -> AppendSeparator ();
}
2019-01-21 12:34:28 +01:00
append_menu_item ( windowMenu , wxID_HIGHEST + 2 , _ ( L ( "P&rint Settings Tab" )) + " \t Ctrl+2" , _ ( L ( "Show the print settings" )),
2019-04-30 15:13:38 +02:00
[ this , tab_offset ]( wxCommandEvent & ) { select_tab ( tab_offset + 0 ); }, menu_icon ( "cog" ));
2019-04-30 13:51:36 +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" )),
2019-04-30 15:13:38 +02:00
[ this , tab_offset ]( wxCommandEvent & ) { select_tab ( tab_offset + 1 ); }, menu_icon ( "spool" ));
2019-04-30 13:51:36 +02:00
m_changeable_menu_items . push_back ( item_material_tab );
2019-01-21 12:34:28 +01:00
append_menu_item ( windowMenu , wxID_HIGHEST + 4 , _ ( L ( "Print&er Settings Tab" )) + " \t Ctrl+4" , _ ( L ( "Show the printer settings" )),
2019-04-30 15:13:38 +02:00
[ this , tab_offset ]( wxCommandEvent & ) { select_tab ( tab_offset + 2 ); }, menu_icon ( "printer" ));
2018-12-04 13:55:25 +01:00
if ( m_plater ) {
windowMenu -> AppendSeparator ();
2019-01-21 12:34:28 +01:00
wxMenuItem * item_3d = append_menu_item ( windowMenu , wxID_HIGHEST + 5 , _ ( L ( "3&D" )) + " \t Ctrl+5" , _ ( L ( "Show the 3D editing view" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { m_plater -> select_view_3D ( "3D" ); }, menu_icon ( "editor_menu" ));
2019-01-21 12:34:28 +01:00
wxMenuItem * item_preview = append_menu_item ( windowMenu , wxID_HIGHEST + 6 , _ ( L ( "Pre&view" )) + " \t Ctrl+6" , _ ( L ( "Show the 3D slices preview" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { m_plater -> select_view_3D ( "Preview" ); }, menu_icon ( "preview_menu" ));
2018-12-04 13:55:25 +01:00
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_change_view ()); }, item_3d -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_change_view ()); }, item_preview -> GetId ());
}
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
2018-12-14 15:27:34 +01:00
windowMenu -> AppendSeparator ();
2019-01-21 12:34:28 +01:00
append_menu_item ( windowMenu , wxID_ANY , _ ( L ( "Print &Host Upload Queue" )) + " \t Ctrl+J" , _ ( L ( "Display the Print Host Upload Queue window" )),
2019-04-30 15:13:38 +02:00
[ this ]( wxCommandEvent & ) { m_printhost_queue_dlg -> Show (); }, menu_icon ( "upload_queue" ));
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 ();
// The camera control accelerators are captured by GLCanvas3D::on_char().
2019-02-03 19:10:20 +01:00
wxMenuItem * item_iso = append_menu_item ( viewMenu , wxID_ANY , _ ( L ( "Iso" )) + sep + "&0" , _ ( L ( "Iso View" )), [ this ]( wxCommandEvent & ) { select_view ( "iso" ); });
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
2019-02-03 19:10:20 +01:00
wxMenuItem * item_top = append_menu_item ( viewMenu , wxID_ANY , _ ( L ( "Top" )) + sep + "&1" , _ ( L ( "Top View" )), [ this ]( wxCommandEvent & ) { select_view ( "top" ); });
2019-05-04 02:07:07 +02:00
//TRN To be shown in the main menu View->Bottom
2019-02-03 19:10:20 +01:00
wxMenuItem * item_bottom = append_menu_item ( viewMenu , wxID_ANY , _ ( L ( "Bottom" )) + sep + "&2" , _ ( L ( "Bottom View" )), [ this ]( wxCommandEvent & ) { select_view ( "bottom" ); });
wxMenuItem * item_front = append_menu_item ( viewMenu , wxID_ANY , _ ( L ( "Front" )) + sep + "&3" , _ ( L ( "Front View" )), [ this ]( wxCommandEvent & ) { select_view ( "front" ); });
wxMenuItem * item_rear = append_menu_item ( viewMenu , wxID_ANY , _ ( L ( "Rear" )) + sep + "&4" , _ ( L ( "Rear View" )), [ this ]( wxCommandEvent & ) { select_view ( "rear" ); });
wxMenuItem * item_left = append_menu_item ( viewMenu , wxID_ANY , _ ( L ( "Left" )) + sep + "&5" , _ ( L ( "Left View" )), [ this ]( wxCommandEvent & ) { select_view ( "left" ); });
wxMenuItem * item_right = append_menu_item ( viewMenu , wxID_ANY , _ ( L ( "Right" )) + sep + "&6" , _ ( L ( "Right View" )), [ this ]( wxCommandEvent & ) { select_view ( "right" ); });
2018-11-16 12:31:06 +01:00
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_change_view ()); }, item_iso -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_change_view ()); }, item_top -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_change_view ()); }, item_bottom -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_change_view ()); }, item_front -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_change_view ()); }, item_rear -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_change_view ()); }, item_left -> GetId ());
Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_change_view ()); }, item_right -> GetId ());
2018-11-15 15:27:39 +01:00
}
2018-09-20 08:40:22 +02:00
// Help menu
auto helpMenu = new wxMenu ();
{
2019-01-21 12:34:28 +01:00
append_menu_item ( helpMenu , wxID_ANY , _ ( L ( "Prusa 3D &Drivers" )), _ ( L ( "Open the Prusa3D drivers download page in your browser" )),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { wxLaunchDefaultBrowser ( "http://www.prusa3d.com/drivers/" ); });
2019-01-21 12:34:28 +01:00
append_menu_item ( helpMenu , wxID_ANY , _ ( L ( "Prusa Edition &Releases" )), _ ( L ( "Open the Prusa Edition releases page in your browser" )),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { wxLaunchDefaultBrowser ( "http://github.com/prusa3d/slic3r/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);
2019-04-26 10:52:38 +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-10 14:43:35 +02:00
[ this ]( wxCommandEvent & ) { wxLaunchDefaultBrowser ( "https://www.prusa3d.com/slic3r-prusa-edition/" ); });
// 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 ();
2019-01-21 12:34:28 +01: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 (); });
2019-01-21 12:34:28 +01: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 (); });
2019-04-15 16:14:19 +02:00
append_menu_item ( helpMenu , wxID_ANY , _ ( L ( "Report an I&ssue" )), wxString :: Format ( _ ( L ( "Report an issue on %s" )), SLIC3R_APP_NAME ),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { wxLaunchDefaultBrowser ( "http://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 ();
2019-02-03 19:10:20 +01: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 (); });
2018-09-20 08:40:22 +02:00
}
// menubar
// assign menubar to frame after appending items, otherwise special items
// will not be handled correctly
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 );
#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
2019-01-14 16:48:23 +01:00
wxMenu * apple_menu = menubar -> OSXGetAppleMenu ();
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 )
update_menubar ();
}
void MainFrame :: update_menubar ()
{
const bool is_fff = plater () -> printer_technology () == ptFFF ;
m_changeable_menu_items [ miExport ] -> SetItemLabel (( is_fff ? _ ( L ( "Export &G-code" )) : _ ( L ( "Export" )) ) + dots + " \t Ctrl+G" );
m_changeable_menu_items [ miMaterialTab ] -> SetItemLabel (( is_fff ? _ ( L ( "&Filament Settings Tab" )) : _ ( L ( "Mate&rial Settings Tab" ))) + " \t Ctrl+3" );
2019-04-30 15:13:38 +02:00
m_changeable_menu_items [ miMaterialTab ] -> SetBitmap ( create_scaled_bitmap ( this , menu_icon ( is_fff ? "spool" : "resin" )));
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 )) {
2018-09-20 08:40:22 +02:00
auto dlg = new wxFileDialog ( 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 );
2018-09-20 08:40:22 +02:00
if ( dlg -> ShowModal () != wxID_OK ) {
dlg -> Destroy ();
return ;
}
input_file = dlg -> GetPath ();
dlg -> Destroy ();
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 ()) {
auto dlg = new wxMessageDialog ( this , _ ( L ( "No previously sliced file." )),
_ ( L ( "Error" )), wxICON_ERROR | wxOK );
dlg -> ShowModal ();
return ;
}
2019-04-13 14:45:35 +02:00
if ( std :: ifstream ( m_qs_last_input_file . ToUTF8 (). data ())) {
2018-09-20 08:40:22 +02:00
auto dlg = new wxMessageDialog ( this , _ ( L ( "Previously sliced file (" )) + m_qs_last_input_file + _ ( L ( ") not found." )),
_ ( L ( "File Not Found" )), wxICON_ERROR | wxOK );
dlg -> ShowModal ();
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.
2018-09-21 01:33:41 +02:00
auto dlg = new wxFileDialog ( this , _ ( L ( "Save " )) + ( qs & qsExportSVG ? _ ( L ( "SVG" )) : _ ( L ( "G-code" ))) + _ ( L ( " file as:" )),
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 );
if ( dlg -> ShowModal () != wxID_OK ) {
dlg -> Destroy ();
return ;
}
output_file = dlg -> GetPath ();
dlg -> Destroy ();
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 ) {
2018-09-20 08:40:22 +02:00
auto dlg = new wxFileDialog ( 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 );
2018-09-20 08:40:22 +02:00
if ( dlg -> ShowModal () != wxID_OK ) {
dlg -> Destroy ();
return ;
}
output_file = dlg -> GetPath ();
dlg -> Destroy ();
}
// show processbar dialog
2019-01-21 12:34:28 +01:00
m_progress_dialog = new wxProgressDialog ( _ ( L ( "Slicing" )) + dots , _ ( L ( "Processing " )) + input_file_basename + "…" ,
2018-09-20 08:40:22 +02:00
100 , this , 4 );
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 ;
{
auto dlg = new wxFileDialog ( 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 );
2018-09-20 08:40:22 +02:00
if ( dlg -> ShowModal () != wxID_OK ) {
dlg -> Destroy ();
return ;
}
input_file = dlg -> GetPath ();
dlg -> Destroy ();
}
2019-04-13 14:45:35 +02:00
wxString output_file = input_file ;
2018-09-20 08:40:22 +02:00
{
auto dlg = new wxFileDialog ( 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 );
2018-09-20 08:40:22 +02:00
if ( dlg -> ShowModal () != wxID_OK ) {
dlg -> Destroy ();
2019-04-13 14:45:35 +02:00
return ;
2018-09-20 08:40:22 +02:00
}
output_file = dlg -> GetPath ();
dlg -> Destroy ();
}
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.
auto dlg = new wxFileDialog ( 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 ;
if ( dlg -> ShowModal () == wxID_OK )
file = dlg -> GetPath ();
2018-09-20 08:40:22 +02:00
dlg -> Destroy ();
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 ;
auto dlg = new wxFileDialog ( this , _ ( L ( "Select configuration to load:" )),
! 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 ;
if ( dlg -> ShowModal () == wxID_OK )
file = dlg -> GetPath ();
dlg -> Destroy ();
if ( ! file . IsEmpty () && this -> load_config_file ( file . ToUTF8 (). data ())) {
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.
auto dlg = new wxFileDialog ( 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 ;
if ( dlg -> ShowModal () == wxID_OK )
file = dlg -> GetPath ();
2018-09-20 08:40:22 +02:00
dlg -> Destroy ();
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 ()) {
auto dlg = new wxFileDialog ( 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-01-09 10:43:17 +01:00
if ( dlg -> ShowModal () != wxID_OK ) {
dlg -> Destroy ();
return ;
}
2018-09-20 08:40:22 +02:00
file = dlg -> GetPath ();
2019-01-09 10:43:17 +01:00
dlg -> Destroy ();
}
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 );
Slic3r :: GUI :: show_info ( this , message , "Info" );
}
// Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset.
// Also update the platter 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 ));
}
wxGetApp (). load_current_presets ();
#endif
2018-09-20 08:40:22 +02:00
}
2018-10-17 11:12:38 +02:00
void MainFrame :: select_tab ( size_t tab ) const
{
2018-09-20 08:40:22 +02:00
m_tabpanel -> SetSelection ( tab );
}
// 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
}
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 ()
{
2019-03-01 11:00:34 +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);
2018-12-11 10:33:11 +01:00
m_plater -> sidebar (). show_reslice ( ! bp_on );
2019-03-01 11:00:34 +01:00
m_plater -> sidebar (). show_export ( bp_on );
2018-12-10 10:38:32 +01:00
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
}
} // GUI
2018-09-21 01:33:41 +02:00
} // Slic3r