2018-03-13 12:39:57 +01:00
#include "ConfigWizard_private.hpp"
#include <algorithm>
2019-02-12 10:10:46 +01:00
#include <numeric>
2018-03-13 12:39:57 +01:00
#include <utility>
2018-04-16 16:52:11 +02:00
#include <unordered_map>
2018-06-04 09:07:29 +02:00
#include <boost/format.hpp>
2018-05-02 12:44:53 +02:00
#include <boost/log/trivial.hpp>
2019-01-30 15:22:17 +01:00
#include <boost/algorithm/string/predicate.hpp>
2018-05-02 12:44:53 +02:00
2018-03-13 12:39:57 +01:00
#include <wx/settings.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/dcclient.h>
#include <wx/statbmp.h>
#include <wx/checkbox.h>
#include <wx/statline.h>
2019-01-30 15:22:17 +01:00
#include <wx/dataview.h>
#include <wx/notebook.h>
#include <wx/display.h>
#include <wx/debug.h>
2018-03-13 12:39:57 +01:00
#include "libslic3r/Utils.hpp"
#include "PresetBundle.hpp"
#include "GUI.hpp"
2019-02-07 15:55:47 +01:00
#include "GUI_Utils.hpp"
2018-04-11 13:12:08 +02:00
#include "slic3r/Utils/PresetUpdater.hpp"
2018-03-13 12:39:57 +01:00
namespace Slic3r {
namespace GUI {
2018-04-05 18:30:03 +02:00
// Printer model picker GUI control
struct PrinterPickerEvent : public wxEvent
{
2019-01-30 15:22:17 +01:00
std :: string vendor_id ;
std :: string model_id ;
std :: string variant_name ;
bool enable ;
2018-04-05 18:30:03 +02:00
2019-01-30 15:22:17 +01:00
PrinterPickerEvent ( wxEventType eventType , int winid , std :: string vendor_id , std :: string model_id , std :: string variant_name , bool enable )
: wxEvent ( winid , eventType )
, vendor_id ( std :: move ( vendor_id ))
, model_id ( std :: move ( model_id ))
, variant_name ( std :: move ( variant_name ))
, enable ( enable )
{}
2018-04-05 18:30:03 +02:00
2019-01-30 15:22:17 +01:00
virtual wxEvent * Clone () const
{
return new PrinterPickerEvent ( * this );
}
2018-04-05 18:30:03 +02:00
};
wxDEFINE_EVENT ( EVT_PRINTER_PICK , PrinterPickerEvent );
2019-01-30 15:22:17 +01:00
PrinterPicker :: PrinterPicker ( wxWindow * parent , const VendorProfile & vendor , wxString title , size_t max_cols , const AppConfig & appconfig_vendors , const ModelFilter & filter )
: wxPanel ( parent )
, vendor_id ( vendor . id )
2019-02-12 10:10:46 +01:00
, width ( 0 )
2018-04-05 18:30:03 +02:00
{
2019-01-30 15:22:17 +01:00
const auto & models = vendor . models ;
2018-04-05 18:30:03 +02:00
2019-01-30 15:22:17 +01:00
auto * sizer = new wxBoxSizer ( wxVERTICAL );
2018-04-16 16:52:11 +02:00
2019-02-19 14:57:59 +01:00
const auto font_title = GetFont (). MakeBold (). Scaled ( 1.3f );
2019-01-30 15:22:17 +01:00
const auto font_name = GetFont (). MakeBold ();
2019-02-19 14:57:59 +01:00
const auto font_alt_nozzle = GetFont (). Scaled ( 0.9f );
2018-04-05 18:30:03 +02:00
2019-01-30 15:22:17 +01:00
// wxGrid appends widgets by rows, but we need to construct them in columns.
// These vectors are used to hold the elements so that they can be appended in the right order.
std :: vector < wxStaticText *> titles ;
std :: vector < wxStaticBitmap *> bitmaps ;
std :: vector < wxPanel *> variants_panels ;
2018-04-05 18:30:03 +02:00
2019-02-12 10:10:46 +01:00
int max_row_width = 0 ;
int current_row_width = 0 ;
2019-01-30 15:22:17 +01:00
for ( const auto & model : models ) {
if ( ! filter ( model )) { continue ; }
2018-07-23 11:43:06 +02:00
2019-01-30 15:22:17 +01:00
wxBitmap bitmap ( GUI :: from_u8 ( Slic3r :: var (( boost :: format ( "printers/%1%_%2%.png" ) % vendor . id % model . id ). str ())), wxBITMAP_TYPE_PNG );
2018-04-05 18:30:03 +02:00
2019-01-30 15:22:17 +01:00
auto * title = new wxStaticText ( this , wxID_ANY , model . name , wxDefaultPosition , wxDefaultSize , wxALIGN_LEFT );
title -> SetFont ( font_name );
2019-02-12 10:10:46 +01:00
const int wrap_width = std :: max (( int ) MODEL_MIN_WRAP , bitmap . GetWidth ());
title -> Wrap ( wrap_width );
current_row_width += wrap_width ;
if ( titles . size () % max_cols == max_cols - 1 ) {
max_row_width = std :: max ( max_row_width , current_row_width );
current_row_width = 0 ;
}
2019-01-30 15:22:17 +01:00
titles . push_back ( title );
2018-04-05 18:30:03 +02:00
2019-01-30 15:22:17 +01:00
auto * bitmap_widget = new wxStaticBitmap ( this , wxID_ANY , bitmap );
bitmaps . push_back ( bitmap_widget );
2018-07-23 11:43:06 +02:00
2019-01-30 15:22:17 +01:00
auto * variants_panel = new wxPanel ( this );
auto * variants_sizer = new wxBoxSizer ( wxVERTICAL );
variants_panel -> SetSizer ( variants_sizer );
const auto model_id = model . id ;
2018-04-05 18:30:03 +02:00
2019-01-30 15:22:17 +01:00
for ( size_t i = 0 ; i < model . variants . size (); i ++ ) {
const auto & variant = model . variants [ i ];
2018-04-05 18:30:03 +02:00
2019-01-30 15:22:17 +01:00
const auto label = model . technology == ptFFF
? wxString :: Format ( "%s %s %s" , variant . name , _ ( L ( "mm" )), _ ( L ( "nozzle" )))
: from_u8 ( model . name );
2018-04-05 18:30:03 +02:00
2019-01-30 15:22:17 +01:00
if ( i == 1 ) {
auto * alt_label = new wxStaticText ( variants_panel , wxID_ANY , _ ( L ( "Alternate nozzles:" )));
alt_label -> SetFont ( font_alt_nozzle );
variants_sizer -> Add ( alt_label , 0 , wxBOTTOM , 3 );
}
2018-07-23 11:43:06 +02:00
2019-01-30 15:22:17 +01:00
auto * cbox = new Checkbox ( variants_panel , label , model_id , variant . name );
i == 0 ? cboxes . push_back ( cbox ) : cboxes_alt . push_back ( cbox );
2018-04-16 16:52:11 +02:00
2019-01-30 15:22:17 +01:00
bool enabled = appconfig_vendors . get_variant ( "PrusaResearch" , model_id , variant . name );
cbox -> SetValue ( enabled );
variants_sizer -> Add ( cbox , 0 , wxBOTTOM , 3 );
cbox -> Bind ( wxEVT_CHECKBOX , [ this , cbox ]( wxCommandEvent & event ) {
on_checkbox ( cbox , event . IsChecked ());
});
}
variants_panels . push_back ( variants_panel );
}
2019-02-12 10:10:46 +01:00
width = std :: max ( max_row_width , current_row_width );
2019-01-30 15:22:17 +01:00
const size_t cols = std :: min ( max_cols , titles . size ());
auto * printer_grid = new wxFlexGridSizer ( cols , 0 , 20 );
printer_grid -> SetFlexibleDirection ( wxVERTICAL | wxHORIZONTAL );
if ( titles . size () > 0 ) {
const size_t odd_items = titles . size () % cols ;
for ( size_t i = 0 ; i < titles . size () - odd_items ; i += cols ) {
for ( size_t j = i ; j < i + cols ; j ++ ) { printer_grid -> Add ( titles [ j ], 0 , wxBOTTOM , 3 ); }
for ( size_t j = i ; j < i + cols ; j ++ ) { printer_grid -> Add ( bitmaps [ j ], 0 , wxBOTTOM , 20 ); }
for ( size_t j = i ; j < i + cols ; j ++ ) { printer_grid -> Add ( variants_panels [ j ]); }
// Add separator space
if ( i > 0 ) {
for ( size_t j = i ; j < i + cols ; j ++ ) { printer_grid -> Add ( 1 , 100 ); }
}
}
if ( odd_items > 0 ) {
for ( size_t i = 0 ; i < cols ; i ++ ) { printer_grid -> Add ( 1 , 100 ); }
const size_t rem = titles . size () - odd_items ;
for ( size_t i = rem ; i < titles . size (); i ++ ) { printer_grid -> Add ( titles [ i ], 0 , wxBOTTOM , 3 ); }
for ( size_t i = 0 ; i < cols - odd_items ; i ++ ) { printer_grid -> AddSpacer ( 1 ); }
for ( size_t i = rem ; i < titles . size (); i ++ ) { printer_grid -> Add ( bitmaps [ i ], 0 , wxBOTTOM , 20 ); }
for ( size_t i = 0 ; i < cols - odd_items ; i ++ ) { printer_grid -> AddSpacer ( 1 ); }
for ( size_t i = rem ; i < titles . size (); i ++ ) { printer_grid -> Add ( variants_panels [ i ]); }
}
}
auto * title_sizer = new wxBoxSizer ( wxHORIZONTAL );
2019-02-07 15:55:47 +01:00
if ( ! title . IsEmpty ()) {
auto * title_widget = new wxStaticText ( this , wxID_ANY , title );
title_widget -> SetFont ( font_title );
title_sizer -> Add ( title_widget );
}
2019-01-30 15:22:17 +01:00
title_sizer -> AddStretchSpacer ();
if ( titles . size () > 1 ) {
// It only makes sense to add the All / None buttons if there's multiple printers
2019-02-12 11:01:15 +01:00
auto * sel_all_std = new wxButton ( this , wxID_ANY , _ ( L ( "All standard" )));
2019-01-30 15:22:17 +01:00
auto * sel_all = new wxButton ( this , wxID_ANY , _ ( L ( "All" )));
auto * sel_none = new wxButton ( this , wxID_ANY , _ ( L ( "None" )));
2019-02-12 11:01:15 +01:00
sel_all_std -> Bind ( wxEVT_BUTTON , [ this ]( const wxCommandEvent & event ) { this -> select_all ( true , false ); });
sel_all -> Bind ( wxEVT_BUTTON , [ this ]( const wxCommandEvent & event ) { this -> select_all ( true , true ); });
2019-01-30 15:22:17 +01:00
sel_none -> Bind ( wxEVT_BUTTON , [ this ]( const wxCommandEvent & event ) { this -> select_all ( false ); });
2019-02-12 11:01:15 +01:00
title_sizer -> Add ( sel_all_std , 0 , wxRIGHT , BTN_SPACING );
2019-01-30 15:22:17 +01:00
title_sizer -> Add ( sel_all , 0 , wxRIGHT , BTN_SPACING );
title_sizer -> Add ( sel_none );
}
sizer -> Add ( title_sizer , 0 , wxEXPAND | wxBOTTOM , BTN_SPACING );
sizer -> Add ( printer_grid );
SetSizer ( sizer );
2018-04-16 16:52:11 +02:00
}
2019-01-30 15:22:17 +01:00
PrinterPicker :: PrinterPicker ( wxWindow * parent , const VendorProfile & vendor , wxString title , size_t max_cols , const AppConfig & appconfig_vendors )
: PrinterPicker ( parent , vendor , std :: move ( title ), max_cols , appconfig_vendors , []( const VendorProfile :: PrinterModel & ) { return true ; })
{}
2019-02-12 11:01:15 +01:00
void PrinterPicker :: select_all ( bool select , bool alternates )
2018-04-16 16:52:11 +02:00
{
2019-01-30 15:22:17 +01:00
for ( const auto & cb : cboxes ) {
if ( cb -> GetValue () != select ) {
cb -> SetValue ( select );
on_checkbox ( cb , select );
}
}
2019-02-12 11:01:15 +01:00
if ( ! select ) { alternates = false ; }
for ( const auto & cb : cboxes_alt ) {
if ( cb -> GetValue () != alternates ) {
cb -> SetValue ( alternates );
on_checkbox ( cb , alternates );
2019-01-30 15:22:17 +01:00
}
}
2018-04-16 16:52:11 +02:00
}
2018-06-22 15:01:33 +02:00
void PrinterPicker :: select_one ( size_t i , bool select )
{
2019-01-30 15:22:17 +01:00
if ( i < cboxes . size () && cboxes [ i ] -> GetValue () != select ) {
cboxes [ i ] -> SetValue ( select );
on_checkbox ( cboxes [ i ], select );
}
2018-06-22 15:01:33 +02:00
}
2018-04-16 16:52:11 +02:00
void PrinterPicker :: on_checkbox ( const Checkbox * cbox , bool checked )
{
2019-01-30 15:22:17 +01:00
PrinterPickerEvent evt ( EVT_PRINTER_PICK , GetId (), vendor_id , cbox -> model , cbox -> variant , checked );
AddPendingEvent ( evt );
2018-04-05 18:30:03 +02:00
}
2018-03-13 12:39:57 +01:00
// Wizard page base
2019-01-30 15:22:17 +01:00
ConfigWizardPage :: ConfigWizardPage ( ConfigWizard * parent , wxString title , wxString shortname , unsigned indent )
: wxPanel ( parent -> p -> hscroll )
, parent ( parent )
, shortname ( std :: move ( shortname ))
, indent ( indent )
2018-03-13 12:39:57 +01:00
{
2019-01-30 15:22:17 +01:00
auto * sizer = new wxBoxSizer ( wxVERTICAL );
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
auto * text = new wxStaticText ( this , wxID_ANY , std :: move ( title ), wxDefaultPosition , wxDefaultSize , wxALIGN_LEFT );
const auto font = GetFont (). MakeBold (). Scaled ( 1.5 );
text -> SetFont ( font );
sizer -> Add ( text , 0 , wxALIGN_LEFT , 0 );
sizer -> AddSpacer ( 10 );
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
content = new wxBoxSizer ( wxVERTICAL );
sizer -> Add ( content , 1 );
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
SetSizer ( sizer );
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
this -> Hide ();
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
Bind ( wxEVT_SIZE , [ this ]( wxSizeEvent & event ) {
this -> Layout ();
event . Skip ();
});
2018-03-13 12:39:57 +01:00
}
ConfigWizardPage ::~ ConfigWizardPage () {}
void ConfigWizardPage :: append_text ( wxString text )
{
2019-01-30 15:22:17 +01:00
auto * widget = new wxStaticText ( this , wxID_ANY , text , wxDefaultPosition , wxDefaultSize , wxALIGN_LEFT );
widget -> Wrap ( WRAP_WIDTH );
widget -> SetMinSize ( wxSize ( WRAP_WIDTH , - 1 ));
append ( widget );
2018-03-13 12:39:57 +01:00
}
void ConfigWizardPage :: append_spacer ( int space )
{
2019-01-30 15:22:17 +01:00
content -> AddSpacer ( space );
2018-03-13 12:39:57 +01:00
}
// Wizard pages
2019-01-30 15:22:17 +01:00
PageWelcome :: PageWelcome ( ConfigWizard * parent )
2019-04-15 16:14:19 +02:00
: ConfigWizardPage ( parent , wxString :: Format ( _ ( L ( "Welcome to the %s %s" )), SLIC3R_APP_NAME , ConfigWizard :: name ()), _ ( L ( "Welcome" )))
2019-01-30 15:22:17 +01:00
, cbox_reset ( nullptr )
2018-03-13 12:39:57 +01:00
{
2019-01-30 15:22:17 +01:00
if ( wizard_p () -> run_reason == ConfigWizard :: RR_DATA_EMPTY ) {
wxString :: Format ( _ ( L ( "Run %s" )), ConfigWizard :: name ());
append_text ( wxString :: Format (
2019-04-15 16:14:19 +02:00
_ ( L ( "Hello, welcome to %s! This %s helps you with the initial configuration; just a few settings and you will be ready to print." )),
SLIC3R_APP_NAME ,
2019-01-30 15:22:17 +01:00
ConfigWizard :: name ())
);
} else {
cbox_reset = new wxCheckBox ( this , wxID_ANY , _ ( L ( "Remove user profiles - install from scratch (a snapshot will be taken beforehand)" )));
append ( cbox_reset );
}
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
Show ();
2018-03-13 12:39:57 +01:00
}
2019-01-30 15:22:17 +01:00
PagePrinters :: PagePrinters ( ConfigWizard * parent , wxString title , wxString shortname , const VendorProfile & vendor , unsigned indent , Technology technology )
: ConfigWizardPage ( parent , std :: move ( title ), std :: move ( shortname ), indent )
2018-03-13 12:39:57 +01:00
{
2019-01-30 15:22:17 +01:00
enum {
COL_SIZE = 200 ,
};
bool check_first_variant = wizard_p () -> check_first_variant ();
AppConfig & appconfig_vendors = this -> wizard_p () -> appconfig_vendors ;
const auto families = vendor . families ();
for ( const auto & family : families ) {
const auto filter = [ & ]( const VendorProfile :: PrinterModel & model ) {
return ( model . technology == ptFFF && technology & T_FFF
|| model . technology == ptSLA && technology & T_SLA )
&& model . family == family ;
};
if ( std :: find_if ( vendor . models . begin (), vendor . models . end (), filter ) == vendor . models . end ()) {
continue ;
}
const auto picker_title = family . empty () ? wxString () : wxString :: Format ( _ ( L ( "%s Family" )), family );
auto * picker = new PrinterPicker ( this , vendor , picker_title , MAX_COLS , appconfig_vendors , filter );
if ( check_first_variant ) {
// Select the default (first) model/variant on the Prusa vendor
picker -> select_one ( 0 , true );
check_first_variant = false ;
}
picker -> Bind ( EVT_PRINTER_PICK , [ this , & appconfig_vendors ]( const PrinterPickerEvent & evt ) {
appconfig_vendors . set_variant ( evt . vendor_id , evt . model_id , evt . variant_name , evt . enable );
});
append ( new wxStaticLine ( this ));
append ( picker );
printer_pickers . push_back ( picker );
}
2018-03-13 12:39:57 +01:00
}
2019-02-12 11:01:15 +01:00
void PagePrinters :: select_all ( bool select , bool alternates )
2018-03-13 12:39:57 +01:00
{
2019-01-30 15:22:17 +01:00
for ( auto picker : printer_pickers ) {
2019-02-12 11:01:15 +01:00
picker -> select_all ( select , alternates );
2019-01-30 15:22:17 +01:00
}
2018-03-13 12:39:57 +01:00
}
2019-02-12 10:10:46 +01:00
int PagePrinters :: get_width () const
{
return std :: accumulate ( printer_pickers . begin (), printer_pickers . end (), 0 ,
[]( int acc , const PrinterPicker * picker ) { return std :: max ( acc , picker -> get_width ()); });
}
2019-01-30 15:22:17 +01:00
2019-02-07 15:55:47 +01:00
const char * PageCustom :: default_profile_name = "My Settings" ;
2019-01-30 15:22:17 +01:00
PageCustom :: PageCustom ( ConfigWizard * parent )
: ConfigWizardPage ( parent , _ ( L ( "Custom Printer Setup" )), _ ( L ( "Custom Printer" )))
2018-03-13 12:39:57 +01:00
{
2019-01-30 15:22:17 +01:00
cb_custom = new wxCheckBox ( this , wxID_ANY , _ ( L ( "Define a custom printer profile" )));
2019-02-07 15:55:47 +01:00
tc_profile_name = new wxTextCtrl ( this , wxID_ANY , default_profile_name );
2019-01-30 15:22:17 +01:00
auto * label = new wxStaticText ( this , wxID_ANY , _ ( L ( "Custom profile name:" )));
2018-04-05 16:10:44 +02:00
2019-02-12 11:01:15 +01:00
tc_profile_name -> Enable ( false );
2019-02-07 15:55:47 +01:00
tc_profile_name -> Bind ( wxEVT_KILL_FOCUS , [ this ]( wxFocusEvent & evt ) {
if ( tc_profile_name -> GetValue (). IsEmpty ()) {
if ( profile_name_prev . IsEmpty ()) { tc_profile_name -> SetValue ( default_profile_name ); }
else { tc_profile_name -> SetValue ( profile_name_prev ); }
} else {
profile_name_prev = tc_profile_name -> GetValue ();
}
evt . Skip ();
});
2019-01-30 15:22:17 +01:00
cb_custom -> Bind ( wxEVT_CHECKBOX , [ this ]( wxCommandEvent & event ) {
2019-02-12 11:01:15 +01:00
tc_profile_name -> Enable ( custom_wanted ());
2019-01-30 15:22:17 +01:00
wizard_p () -> on_custom_setup ( custom_wanted ());
});
2018-04-17 11:54:59 +02:00
2019-01-30 15:22:17 +01:00
append ( cb_custom );
append ( label );
append ( tc_profile_name );
2018-03-13 12:39:57 +01:00
}
2019-01-30 15:22:17 +01:00
PageUpdate :: PageUpdate ( ConfigWizard * parent )
: ConfigWizardPage ( parent , _ ( L ( "Automatic updates" )), _ ( L ( "Updates" )))
, version_check ( true )
, preset_update ( true )
2018-03-29 17:54:43 +02:00
{
2019-01-30 15:22:17 +01:00
const AppConfig * app_config = GUI :: get_app_config ();
auto boldfont = wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT );
boldfont . SetWeight ( wxFONTWEIGHT_BOLD );
2018-04-05 18:30:03 +02:00
2019-01-30 15:22:17 +01:00
auto * box_slic3r = new wxCheckBox ( this , wxID_ANY , _ ( L ( "Check for application updates" )));
box_slic3r -> SetValue ( app_config -> get ( "version_check" ) == "1" );
append ( box_slic3r );
2019-04-15 16:14:19 +02:00
append_text ( wxString :: Format ( _ ( L ( "If enabled, Slic3r checks for new versions of %s online. When a new version becomes available, "
"a notification is displayed at the next application startup (never during program usage). "
"This is only a notification mechanisms, no automatic installation is done." )), SLIC3R_APP_NAME ));
2018-03-29 17:54:43 +02:00
2019-01-30 15:22:17 +01:00
append_spacer ( VERTICAL_SPACING );
2018-04-05 18:30:03 +02:00
2019-01-30 15:22:17 +01:00
auto * box_presets = new wxCheckBox ( this , wxID_ANY , _ ( L ( "Update built-in Presets automatically" )));
box_presets -> SetValue ( app_config -> get ( "preset_update" ) == "1" );
append ( box_presets );
append_text ( _ ( L ( "If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup." )));
const auto text_bold = _ ( L ( "Updates are never applied without user's consent and never overwrite user's customized settings." ));
auto * label_bold = new wxStaticText ( this , wxID_ANY , text_bold );
label_bold -> SetFont ( boldfont );
label_bold -> Wrap ( WRAP_WIDTH );
append ( label_bold );
append_text ( _ ( L ( "Additionally a backup snapshot of the whole configuration is created before an update is applied." )));
2018-03-29 17:54:43 +02:00
2019-01-30 15:22:17 +01:00
box_slic3r -> Bind ( wxEVT_CHECKBOX , [ this ]( wxCommandEvent & event ) { this -> version_check = event . IsChecked (); });
box_presets -> Bind ( wxEVT_CHECKBOX , [ this ]( wxCommandEvent & event ) { this -> preset_update = event . IsChecked (); });
2018-04-05 18:30:03 +02:00
}
2019-01-30 15:22:17 +01:00
PageVendors :: PageVendors ( ConfigWizard * parent )
: ConfigWizardPage ( parent , _ ( L ( "Other Vendors" )), _ ( L ( "Other Vendors" )))
2018-04-05 18:30:03 +02:00
{
2019-04-15 16:14:19 +02:00
append_text ( wxString :: Format ( _ ( L ( "Pick another vendor supported by %s:" )), SLIC3R_APP_NAME ));
2019-01-30 15:22:17 +01:00
auto boldfont = wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT );
boldfont . SetWeight ( wxFONTWEIGHT_BOLD );
AppConfig & appconfig_vendors = this -> wizard_p () -> appconfig_vendors ;
wxArrayString choices_vendors ;
for ( const auto vendor_pair : wizard_p () -> vendors ) {
const auto & vendor = vendor_pair . second ;
if ( vendor . id == "PrusaResearch" ) { continue ; }
auto * picker = new PrinterPicker ( this , vendor , "" , MAX_COLS , appconfig_vendors );
picker -> Hide ();
pickers . push_back ( picker );
choices_vendors . Add ( vendor . name );
picker -> Bind ( EVT_PRINTER_PICK , [ this , & appconfig_vendors ]( const PrinterPickerEvent & evt ) {
appconfig_vendors . set_variant ( evt . vendor_id , evt . model_id , evt . variant_name , evt . enable );
});
}
auto * vendor_picker = new wxChoice ( this , wxID_ANY , wxDefaultPosition , wxDefaultSize , choices_vendors );
if ( choices_vendors . GetCount () > 0 ) {
vendor_picker -> SetSelection ( 0 );
on_vendor_pick ( 0 );
}
vendor_picker -> Bind ( wxEVT_CHOICE , [ this ]( wxCommandEvent & evt ) {
this -> on_vendor_pick ( evt . GetInt ());
});
append ( vendor_picker );
for ( PrinterPicker * picker : pickers ) { this -> append ( picker ); }
2018-04-05 18:30:03 +02:00
}
void PageVendors :: on_vendor_pick ( size_t i )
{
2019-01-30 15:22:17 +01:00
for ( PrinterPicker * picker : pickers ) { picker -> Hide (); }
if ( i < pickers . size ()) {
pickers [ i ] -> Show ();
parent -> Layout ();
}
2018-03-29 17:54:43 +02:00
}
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
PageFirmware :: PageFirmware ( ConfigWizard * parent )
: ConfigWizardPage ( parent , _ ( L ( "Firmware Type" )), _ ( L ( "Firmware" )), 1 )
, gcode_opt ( * print_config_def . get ( "gcode_flavor" ))
, gcode_picker ( nullptr )
2018-04-05 18:30:03 +02:00
{
2019-01-30 15:22:17 +01:00
append_text ( _ ( L ( "Choose the type of firmware used by your printer." )));
append_text ( gcode_opt . tooltip );
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
wxArrayString choices ;
choices . Alloc ( gcode_opt . enum_labels . size ());
for ( const auto & label : gcode_opt . enum_labels ) {
choices . Add ( label );
}
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
gcode_picker = new wxChoice ( this , wxID_ANY , wxDefaultPosition , wxDefaultSize , choices );
const auto & enum_values = gcode_opt . enum_values ;
auto needle = enum_values . cend ();
if ( gcode_opt . default_value != nullptr ) {
needle = std :: find ( enum_values . cbegin (), enum_values . cend (), gcode_opt . default_value -> serialize ());
}
if ( needle != enum_values . cend ()) {
gcode_picker -> SetSelection ( needle - enum_values . cbegin ());
} else {
gcode_picker -> SetSelection ( 0 );
}
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
append ( gcode_picker );
2018-04-05 16:10:44 +02:00
}
void PageFirmware :: apply_custom_config ( DynamicPrintConfig & config )
{
2019-01-30 15:22:17 +01:00
auto sel = gcode_picker -> GetSelection ();
if ( sel >= 0 && ( size_t ) sel < gcode_opt . enum_labels . size ()) {
auto * opt = new ConfigOptionEnum < GCodeFlavor > ( static_cast < GCodeFlavor > ( sel ));
config . set_key_value ( "gcode_flavor" , opt );
}
2018-04-05 16:10:44 +02:00
}
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
PageBedShape :: PageBedShape ( ConfigWizard * parent )
: ConfigWizardPage ( parent , _ ( L ( "Bed Shape and Size" )), _ ( L ( "Bed Shape" )), 1 )
, shape_panel ( new BedShapePanel ( this ))
2018-04-05 16:10:44 +02:00
{
2019-01-30 15:22:17 +01:00
append_text ( _ ( L ( "Set the shape of your printer's bed." )));
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
shape_panel -> build_panel ( wizard_p () -> custom_config -> option < ConfigOptionPoints > ( "bed_shape" ));
append ( shape_panel );
2018-04-05 16:10:44 +02:00
}
void PageBedShape :: apply_custom_config ( DynamicPrintConfig & config )
{
2019-01-30 15:22:17 +01:00
const auto points ( shape_panel -> GetValue ());
auto * opt = new ConfigOptionPoints ( points );
config . set_key_value ( "bed_shape" , opt );
2018-04-05 16:10:44 +02:00
}
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
PageDiameters :: PageDiameters ( ConfigWizard * parent )
: ConfigWizardPage ( parent , _ ( L ( "Filament and Nozzle Diameters" )), _ ( L ( "Print Diameters" )), 1 )
, spin_nozzle ( new wxSpinCtrlDouble ( this , wxID_ANY ))
, spin_filam ( new wxSpinCtrlDouble ( this , wxID_ANY ))
2018-04-05 16:10:44 +02:00
{
2019-01-30 15:22:17 +01:00
spin_nozzle -> SetDigits ( 2 );
spin_nozzle -> SetIncrement ( 0.1 );
const auto & def_nozzle = * print_config_def . get ( "nozzle_diameter" );
auto * default_nozzle = dynamic_cast < const ConfigOptionFloats *> ( def_nozzle . default_value );
spin_nozzle -> SetValue ( default_nozzle != nullptr && default_nozzle -> size () > 0 ? default_nozzle -> get_at ( 0 ) : 0.5 );
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
spin_filam -> SetDigits ( 2 );
spin_filam -> SetIncrement ( 0.25 );
const auto & def_filam = * print_config_def . get ( "filament_diameter" );
auto * default_filam = dynamic_cast < const ConfigOptionFloats *> ( def_filam . default_value );
spin_filam -> SetValue ( default_filam != nullptr && default_filam -> size () > 0 ? default_filam -> get_at ( 0 ) : 3.0 );
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
append_text ( _ ( L ( "Enter the diameter of your printer's hot end nozzle." )));
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
auto * sizer_nozzle = new wxFlexGridSizer ( 3 , 5 , 5 );
auto * text_nozzle = new wxStaticText ( this , wxID_ANY , _ ( L ( "Nozzle Diameter:" )));
auto * unit_nozzle = new wxStaticText ( this , wxID_ANY , _ ( L ( "mm" )));
sizer_nozzle -> AddGrowableCol ( 0 , 1 );
sizer_nozzle -> Add ( text_nozzle , 0 , wxALIGN_CENTRE_VERTICAL );
sizer_nozzle -> Add ( spin_nozzle );
sizer_nozzle -> Add ( unit_nozzle , 0 , wxALIGN_CENTRE_VERTICAL );
append ( sizer_nozzle );
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
append_spacer ( VERTICAL_SPACING );
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
append_text ( _ ( L ( "Enter the diameter of your filament." )));
append_text ( _ ( L ( "Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average." )));
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
auto * sizer_filam = new wxFlexGridSizer ( 3 , 5 , 5 );
auto * text_filam = new wxStaticText ( this , wxID_ANY , _ ( L ( "Filament Diameter:" )));
auto * unit_filam = new wxStaticText ( this , wxID_ANY , _ ( L ( "mm" )));
sizer_filam -> AddGrowableCol ( 0 , 1 );
sizer_filam -> Add ( text_filam , 0 , wxALIGN_CENTRE_VERTICAL );
sizer_filam -> Add ( spin_filam );
sizer_filam -> Add ( unit_filam , 0 , wxALIGN_CENTRE_VERTICAL );
append ( sizer_filam );
2018-04-05 16:10:44 +02:00
}
void PageDiameters :: apply_custom_config ( DynamicPrintConfig & config )
{
2019-01-30 15:22:17 +01:00
auto * opt_nozzle = new ConfigOptionFloats ( 1 , spin_nozzle -> GetValue ());
config . set_key_value ( "nozzle_diameter" , opt_nozzle );
auto * opt_filam = new ConfigOptionFloats ( 1 , spin_filam -> GetValue ());
config . set_key_value ( "filament_diameter" , opt_filam );
2018-04-05 16:10:44 +02:00
}
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
PageTemperatures :: PageTemperatures ( ConfigWizard * parent )
: ConfigWizardPage ( parent , _ ( L ( "Extruder and Bed Temperatures" )), _ ( L ( "Temperatures" )), 1 )
, spin_extr ( new wxSpinCtrlDouble ( this , wxID_ANY ))
, spin_bed ( new wxSpinCtrlDouble ( this , wxID_ANY ))
2018-04-05 16:10:44 +02:00
{
2019-01-30 15:22:17 +01:00
spin_extr -> SetIncrement ( 5.0 );
const auto & def_extr = * print_config_def . get ( "temperature" );
spin_extr -> SetRange ( def_extr . min , def_extr . max );
auto * default_extr = dynamic_cast < const ConfigOptionInts *> ( def_extr . default_value );
spin_extr -> SetValue ( default_extr != nullptr && default_extr -> size () > 0 ? default_extr -> get_at ( 0 ) : 200 );
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
spin_bed -> SetIncrement ( 5.0 );
const auto & def_bed = * print_config_def . get ( "bed_temperature" );
spin_bed -> SetRange ( def_bed . min , def_bed . max );
auto * default_bed = dynamic_cast < const ConfigOptionInts *> ( def_bed . default_value );
spin_bed -> SetValue ( default_bed != nullptr && default_bed -> size () > 0 ? default_bed -> get_at ( 0 ) : 0 );
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
append_text ( _ ( L ( "Enter the temperature needed for extruding your filament." )));
append_text ( _ ( L ( "A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS." )));
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
auto * sizer_extr = new wxFlexGridSizer ( 3 , 5 , 5 );
auto * text_extr = new wxStaticText ( this , wxID_ANY , _ ( L ( "Extrusion Temperature:" )));
auto * unit_extr = new wxStaticText ( this , wxID_ANY , _ ( L ( "°C" )));
sizer_extr -> AddGrowableCol ( 0 , 1 );
sizer_extr -> Add ( text_extr , 0 , wxALIGN_CENTRE_VERTICAL );
sizer_extr -> Add ( spin_extr );
sizer_extr -> Add ( unit_extr , 0 , wxALIGN_CENTRE_VERTICAL );
append ( sizer_extr );
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
append_spacer ( VERTICAL_SPACING );
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
append_text ( _ ( L ( "Enter the bed temperature needed for getting your filament to stick to your heated bed." )));
append_text ( _ ( L ( "A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed." )));
2018-04-05 16:10:44 +02:00
2019-01-30 15:22:17 +01:00
auto * sizer_bed = new wxFlexGridSizer ( 3 , 5 , 5 );
auto * text_bed = new wxStaticText ( this , wxID_ANY , _ ( L ( "Bed Temperature:" )));
auto * unit_bed = new wxStaticText ( this , wxID_ANY , _ ( L ( "°C" )));
sizer_bed -> AddGrowableCol ( 0 , 1 );
sizer_bed -> Add ( text_bed , 0 , wxALIGN_CENTRE_VERTICAL );
sizer_bed -> Add ( spin_bed );
sizer_bed -> Add ( unit_bed , 0 , wxALIGN_CENTRE_VERTICAL );
append ( sizer_bed );
2018-04-05 16:10:44 +02:00
}
void PageTemperatures :: apply_custom_config ( DynamicPrintConfig & config )
{
2019-01-30 15:22:17 +01:00
auto * opt_extr = new ConfigOptionInts ( 1 , spin_extr -> GetValue ());
config . set_key_value ( "temperature" , opt_extr );
auto * opt_extr1st = new ConfigOptionInts ( 1 , spin_extr -> GetValue ());
config . set_key_value ( "first_layer_temperature" , opt_extr1st );
auto * opt_bed = new ConfigOptionInts ( 1 , spin_bed -> GetValue ());
config . set_key_value ( "bed_temperature" , opt_bed );
auto * opt_bed1st = new ConfigOptionInts ( 1 , spin_bed -> GetValue ());
config . set_key_value ( "first_layer_bed_temperature" , opt_bed1st );
2018-04-05 16:10:44 +02:00
}
2018-03-13 12:39:57 +01:00
// Index
2019-01-30 15:22:17 +01:00
ConfigWizardIndex :: ConfigWizardIndex ( wxWindow * parent )
: wxPanel ( parent )
2019-04-17 16:47:15 +02:00
// XXX: use create_scaled_bitmap:
2019-01-30 15:22:17 +01:00
, bg ( GUI :: from_u8 ( Slic3r :: var ( "Slic3r_192px_transparent.png" )), wxBITMAP_TYPE_PNG )
, bullet_black ( GUI :: from_u8 ( Slic3r :: var ( "bullet_black.png" )), wxBITMAP_TYPE_PNG )
, bullet_blue ( GUI :: from_u8 ( Slic3r :: var ( "bullet_blue.png" )), wxBITMAP_TYPE_PNG )
, bullet_white ( GUI :: from_u8 ( Slic3r :: var ( "bullet_white.png" )), wxBITMAP_TYPE_PNG )
, item_active ( 0 )
, item_hover ( - 1 )
, last_page (( size_t ) - 1 )
2018-03-13 12:39:57 +01:00
{
2019-01-30 15:22:17 +01:00
SetMinSize ( bg . GetSize ());
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
const wxSize size = GetTextExtent ( "m" );
2019-04-18 17:21:46 +02:00
em_w = size . x ;
2019-01-30 15:22:17 +01:00
em_h = size . y ;
2018-04-09 10:41:34 +02:00
2019-01-30 15:22:17 +01:00
// Add logo bitmap.
// This could be done in on_paint() along with the index labels, but I've found it tricky
// to get the bitmap rendered well on all platforms with transparent background.
// In some cases it didn't work at all. And so wxStaticBitmap is used here instead,
// because it has all the platform quirks figured out.
auto * sizer = new wxBoxSizer ( wxVERTICAL );
auto * logo = new wxStaticBitmap ( this , wxID_ANY , bg );
sizer -> AddStretchSpacer ();
sizer -> Add ( logo );
SetSizer ( sizer );
2018-04-09 10:41:34 +02:00
2019-01-30 15:22:17 +01:00
Bind ( wxEVT_PAINT , & ConfigWizardIndex :: on_paint , this );
Bind ( wxEVT_MOTION , & ConfigWizardIndex :: on_mouse_move , this );
Bind ( wxEVT_LEAVE_WINDOW , [ this ]( wxMouseEvent & evt ) {
if ( item_hover != - 1 ) {
item_hover = - 1 ;
Refresh ();
}
evt . Skip ();
});
Bind ( wxEVT_LEFT_UP , [ this ]( wxMouseEvent & evt ) {
if ( item_hover >= 0 ) { go_to ( item_hover ); }
});
2018-03-13 12:39:57 +01:00
}
2019-01-30 15:22:17 +01:00
wxDECLARE_EVENT ( EVT_INDEX_PAGE , wxCommandEvent );
void ConfigWizardIndex :: add_page ( ConfigWizardPage * page )
2018-03-13 12:39:57 +01:00
{
2019-01-30 15:22:17 +01:00
last_page = items . size ();
items . emplace_back ( Item { page -> shortname , page -> indent , page });
Refresh ();
2018-03-13 12:39:57 +01:00
}
2019-01-30 15:22:17 +01:00
void ConfigWizardIndex :: add_label ( wxString label , unsigned indent )
2018-03-13 12:39:57 +01:00
{
2019-01-30 15:22:17 +01:00
items . emplace_back ( Item { std :: move ( label ), indent , nullptr });
Refresh ();
}
ConfigWizardPage * ConfigWizardIndex :: active_page () const
{
if ( item_active >= items . size ()) { return nullptr ; }
return items [ item_active ]. page ;
}
void ConfigWizardIndex :: go_prev ()
{
// Search for a preceiding item that is a page (not a label, ie. page != nullptr)
for ( size_t i = item_active ; i > 0 ; i -- ) {
if ( items [ i - 1 ]. page != nullptr ) {
go_to ( i - 1 );
return ;
}
}
}
void ConfigWizardIndex :: go_next ()
{
// Search for a next item that is a page (not a label, ie. page != nullptr)
for ( size_t i = item_active + 1 ; i < items . size (); i ++ ) {
if ( items [ i ]. page != nullptr ) {
go_to ( i );
return ;
}
}
}
void ConfigWizardIndex :: go_to ( size_t i )
{
if ( i < items . size () && items [ i ]. page != nullptr ) {
auto * former_active = active_page ();
if ( former_active != nullptr ) { former_active -> Hide (); }
item_active = i ;
items [ i ]. page -> Show ();
wxCommandEvent evt ( EVT_INDEX_PAGE , GetId ());
AddPendingEvent ( evt );
Refresh ();
}
}
void ConfigWizardIndex :: go_to ( ConfigWizardPage * page )
{
if ( page == nullptr ) { return ; }
for ( size_t i = 0 ; i < items . size (); i ++ ) {
if ( items [ i ]. page == page ) { go_to ( i ); }
}
}
void ConfigWizardIndex :: clear ()
{
auto * former_active = active_page ();
if ( former_active != nullptr ) { former_active -> Hide (); }
items . clear ();
item_active = 0 ;
2018-03-13 12:39:57 +01:00
}
void ConfigWizardIndex :: on_paint ( wxPaintEvent & evt )
{
2019-01-30 15:22:17 +01:00
const auto size = GetClientSize ();
if ( size . GetHeight () == 0 || size . GetWidth () == 0 ) { return ; }
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
wxPaintDC dc ( this );
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
const auto bullet_w = bullet_black . GetSize (). GetWidth ();
const auto bullet_h = bullet_black . GetSize (). GetHeight ();
const int yoff_icon = bullet_h < em_h ? ( em_h - bullet_h ) / 2 : 0 ;
const int yoff_text = bullet_h > em_h ? ( bullet_h - em_h ) / 2 : 0 ;
const int yinc = item_height ();
2018-03-13 12:39:57 +01:00
2019-04-17 16:47:15 +02:00
int index_width = 0 ;
2019-01-30 15:22:17 +01:00
unsigned y = 0 ;
for ( size_t i = 0 ; i < items . size (); i ++ ) {
const Item & item = items [ i ];
2019-04-18 17:21:46 +02:00
unsigned x = em_w / 2 + item . indent * em_w ;
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
if ( i == item_active || item_hover >= 0 && i == ( size_t ) item_hover ) {
dc . DrawBitmap ( bullet_blue , x , y + yoff_icon , false );
}
else if ( i < item_active ) { dc . DrawBitmap ( bullet_black , x , y + yoff_icon , false ); }
else if ( i > item_active ) { dc . DrawBitmap ( bullet_white , x , y + yoff_icon , false ); }
2019-04-18 17:21:46 +02:00
x += + bullet_w + em_w / 2 ;
2019-04-17 16:47:15 +02:00
const auto text_size = dc . GetTextExtent ( item . label );
dc . DrawText ( item . label , x , y + yoff_text );
2019-01-30 15:22:17 +01:00
y += yinc ;
2019-04-17 16:47:15 +02:00
index_width = std :: max ( index_width , ( int ) x + text_size . x );
}
if ( GetMinSize (). x < index_width ) {
CallAfter ([ this , index_width ]() {
SetMinSize ( wxSize ( index_width , GetMinSize (). y ));
2019-04-18 17:21:46 +02:00
Refresh ();
2019-04-17 16:47:15 +02:00
});
2019-01-30 15:22:17 +01:00
}
2018-03-13 12:39:57 +01:00
}
2019-01-30 15:22:17 +01:00
void ConfigWizardIndex :: on_mouse_move ( wxMouseEvent & evt )
{
const wxClientDC dc ( this );
const wxPoint pos = evt . GetLogicalPosition ( dc );
const ssize_t item_hover_new = pos . y / item_height ();
2019-02-19 14:57:59 +01:00
if ( item_hover_new < ssize_t ( items . size ()) && item_hover_new != item_hover ) {
2019-01-30 15:22:17 +01:00
item_hover = item_hover_new ;
Refresh ();
}
evt . Skip ();
}
2018-03-13 12:39:57 +01:00
// priv
2018-04-16 16:52:11 +02:00
static const std :: unordered_map < std :: string , std :: pair < std :: string , std :: string >> legacy_preset_map {{
2019-01-30 15:22:17 +01:00
{ "Original Prusa i3 MK2.ini" , std :: make_pair ( "MK2S" , "0.4" ) },
{ "Original Prusa i3 MK2 MM Single Mode.ini" , std :: make_pair ( "MK2SMM" , "0.4" ) },
{ "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini" , std :: make_pair ( "MK2SMM" , "0.6" ) },
{ "Original Prusa i3 MK2 MultiMaterial.ini" , std :: make_pair ( "MK2SMM" , "0.4" ) },
{ "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini" , std :: make_pair ( "MK2SMM" , "0.6" ) },
{ "Original Prusa i3 MK2 0.25 nozzle.ini" , std :: make_pair ( "MK2S" , "0.25" ) },
{ "Original Prusa i3 MK2 0.6 nozzle.ini" , std :: make_pair ( "MK2S" , "0.6" ) },
{ "Original Prusa i3 MK3.ini" , std :: make_pair ( "MK3" , "0.4" ) },
2018-04-16 16:52:11 +02:00
}};
2019-01-30 15:22:17 +01:00
void ConfigWizard :: priv :: load_pages ( bool custom_setup )
{
const auto former_active = index -> active_item ();
index -> clear ();
index -> add_page ( page_welcome );
index -> add_page ( page_fff );
index -> add_page ( page_msla );
index -> add_page ( page_custom );
if ( custom_setup ) {
index -> add_page ( page_firmware );
index -> add_page ( page_bed );
index -> add_page ( page_diams );
index -> add_page ( page_temps );
}
index -> add_page ( page_update );
index -> go_to ( former_active ); // Will restore the active item/page if possible
q -> Layout ();
}
bool ConfigWizard :: priv :: check_first_variant () const
{
return run_reason == RR_DATA_EMPTY || run_reason == RR_DATA_LEGACY ;
}
2018-03-29 17:54:43 +02:00
void ConfigWizard :: priv :: load_vendors ()
{
2019-01-30 15:22:17 +01:00
const auto vendor_dir = fs :: path ( Slic3r :: data_dir ()) / "vendor" ;
const auto rsrc_vendor_dir = fs :: path ( resources_dir ()) / "profiles" ;
2018-04-12 20:04:48 +02:00
2019-01-30 15:22:17 +01:00
// Load vendors from the "vendors" directory in datadir
2019-02-03 15:30:37 +01:00
for ( auto & dir_entry : boost :: filesystem :: directory_iterator ( vendor_dir ))
2019-01-30 15:22:17 +01:00
if ( Slic3r :: is_ini_file ( dir_entry )) {
try {
auto vp = VendorProfile :: from_ini ( dir_entry . path ());
vendors [ vp . id ] = std :: move ( vp );
}
catch ( const std :: exception & e ) {
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Error loading vendor bundle %1%: %2%" ) % dir_entry . path () % e . what ();
}
}
2018-04-12 20:04:48 +02:00
2019-01-30 15:22:17 +01:00
// Additionally load up vendors from the application resources directory, but only those not seen in the datadir
2019-02-03 15:30:37 +01:00
for ( auto & dir_entry : boost :: filesystem :: directory_iterator ( rsrc_vendor_dir ))
2019-01-30 15:22:17 +01:00
if ( Slic3r :: is_ini_file ( dir_entry )) {
const auto id = dir_entry . path (). stem (). string ();
if ( vendors . find ( id ) == vendors . end ()) {
try {
auto vp = VendorProfile :: from_ini ( dir_entry . path ());
vendors_rsrc [ vp . id ] = dir_entry . path (). filename (). string ();
vendors [ vp . id ] = std :: move ( vp );
}
catch ( const std :: exception & e ) {
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Error loading vendor bundle %1%: %2%" ) % dir_entry . path () % e . what ();
}
}
}
2018-03-29 17:54:43 +02:00
2019-01-30 15:22:17 +01:00
// Load up the set of vendors / models / variants the user has had enabled up till now
const AppConfig * app_config = GUI :: get_app_config ();
if ( ! app_config -> legacy_datadir ()) {
appconfig_vendors . set_vendors ( * app_config );
} else {
// In case of legacy datadir, try to guess the preference based on the printer preset files that are present
const auto printer_dir = fs :: path ( Slic3r :: data_dir ()) / "printer" ;
for ( auto & dir_entry : boost :: filesystem :: directory_iterator ( printer_dir ))
if ( Slic3r :: is_ini_file ( dir_entry )) {
auto needle = legacy_preset_map . find ( dir_entry . path (). filename (). string ());
if ( needle == legacy_preset_map . end ()) { continue ; }
2018-04-16 16:52:11 +02:00
2019-01-30 15:22:17 +01:00
const auto & model = needle -> second . first ;
const auto & variant = needle -> second . second ;
appconfig_vendors . set_variant ( "PrusaResearch" , model , variant , true );
}
}
2018-03-13 12:39:57 +01:00
}
void ConfigWizard :: priv :: add_page ( ConfigWizardPage * page )
{
2019-01-30 15:22:17 +01:00
hscroll_sizer -> Add ( page , 0 , wxEXPAND );
2018-03-13 12:39:57 +01:00
}
void ConfigWizard :: priv :: enable_next ( bool enable )
{
2019-01-30 15:22:17 +01:00
btn_next -> Enable ( enable );
btn_finish -> Enable ( enable );
2018-03-13 12:39:57 +01:00
}
2019-01-30 15:22:17 +01:00
void ConfigWizard :: priv :: on_custom_setup ( bool custom_wanted )
2018-03-13 12:39:57 +01:00
{
2019-01-30 15:22:17 +01:00
load_pages ( custom_wanted );
2018-03-13 12:39:57 +01:00
}
2018-04-24 18:06:42 +02:00
void ConfigWizard :: priv :: apply_config ( AppConfig * app_config , PresetBundle * preset_bundle , const PresetUpdater * updater )
2018-03-29 17:54:43 +02:00
{
2019-01-30 15:22:17 +01:00
const auto enabled_vendors = appconfig_vendors . vendors ();
2018-03-29 17:54:43 +02:00
2019-01-30 15:22:17 +01:00
// Install bundles from resources if needed:
std :: vector < std :: string > install_bundles ;
for ( const auto & vendor_rsrc : vendors_rsrc ) {
const auto vendor = enabled_vendors . find ( vendor_rsrc . first );
if ( vendor == enabled_vendors . end ()) { continue ; }
2018-04-16 16:52:11 +02:00
2019-01-30 15:22:17 +01:00
size_t size_sum = 0 ;
for ( const auto & model : vendor -> second ) { size_sum += model . second . size (); }
if ( size_sum == 0 ) { continue ; }
2018-04-16 16:52:11 +02:00
2019-01-30 15:22:17 +01:00
// This vendor needs to be installed
install_bundles . emplace_back ( vendor_rsrc . second );
}
2018-04-12 20:04:48 +02:00
2019-01-30 15:22:17 +01:00
// Decide whether to create snapshot based on run_reason and the reset profile checkbox
bool snapshot = true ;
switch ( run_reason ) {
case ConfigWizard :: RR_DATA_EMPTY : snapshot = false ; break ;
case ConfigWizard :: RR_DATA_LEGACY : snapshot = true ; break ;
case ConfigWizard :: RR_DATA_INCOMPAT : snapshot = false ; break ; // In this case snapshot is done by PresetUpdater with the appropriate reason
case ConfigWizard :: RR_USER : snapshot = page_welcome -> reset_user_profile (); break ;
}
if ( install_bundles . size () > 0 ) {
// Install bundles from resources.
updater -> install_bundles_rsrc ( std :: move ( install_bundles ), snapshot );
} else {
BOOST_LOG_TRIVIAL ( info ) << "No bundles need to be installed from resources" ;
}
2018-04-18 11:40:43 +02:00
2019-01-30 15:22:17 +01:00
if ( page_welcome -> reset_user_profile ()) {
BOOST_LOG_TRIVIAL ( info ) << "Resetting user profiles..." ;
preset_bundle -> reset ( true );
}
2018-04-18 11:40:43 +02:00
2019-01-30 15:22:17 +01:00
app_config -> set_vendors ( appconfig_vendors );
app_config -> set ( "version_check" , page_update -> version_check ? "1" : "0" );
app_config -> set ( "preset_update" , page_update -> preset_update ? "1" : "0" );
app_config -> reset_selections ();
preset_bundle -> load_presets ( * app_config );
2018-04-12 20:04:48 +02:00
2019-01-30 15:22:17 +01:00
if ( page_custom -> custom_wanted ()) {
page_firmware -> apply_custom_config ( * custom_config );
page_bed -> apply_custom_config ( * custom_config );
page_diams -> apply_custom_config ( * custom_config );
page_temps -> apply_custom_config ( * custom_config );
const std :: string profile_name = page_custom -> profile_name ();
preset_bundle -> load_config ( profile_name , * custom_config );
}
// Update the selections from the compatibilty.
preset_bundle -> export_selections ( * app_config );
2018-03-29 17:54:43 +02:00
}
2018-03-13 12:39:57 +01:00
// Public
2019-01-30 15:22:17 +01:00
ConfigWizard :: ConfigWizard ( wxWindow * parent , RunReason reason )
: wxDialog ( parent , wxID_ANY , _ ( name (). ToStdString ()), wxDefaultPosition , wxDefaultSize , wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER )
, p ( new priv ( this ))
2018-03-13 12:39:57 +01:00
{
2019-01-30 15:22:17 +01:00
p -> run_reason = reason ;
2018-04-18 11:40:43 +02:00
2019-01-30 15:22:17 +01:00
p -> load_vendors ();
p -> custom_config . reset ( DynamicPrintConfig :: new_from_defaults_keys ({
"gcode_flavor" , "bed_shape" , "nozzle_diameter" , "filament_diameter" , "temperature" , "bed_temperature" ,
}));
2018-03-29 17:54:43 +02:00
2019-01-30 15:22:17 +01:00
p -> index = new ConfigWizardIndex ( this );
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
auto * vsizer = new wxBoxSizer ( wxVERTICAL );
auto * topsizer = new wxBoxSizer ( wxHORIZONTAL );
auto * hline = new wxStaticLine ( this );
p -> btnsizer = new wxBoxSizer ( wxHORIZONTAL );
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
// Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling.
// Later, we compare that to the size of the current screen and set minimum width based on that (see below).
p -> hscroll = new wxScrolledWindow ( this );
p -> hscroll_sizer = new wxBoxSizer ( wxHORIZONTAL );
p -> hscroll -> SetSizer ( p -> hscroll_sizer );
2018-06-11 18:24:13 +02:00
2019-01-30 15:22:17 +01:00
topsizer -> Add ( p -> index , 0 , wxEXPAND );
topsizer -> AddSpacer ( INDEX_MARGIN );
topsizer -> Add ( p -> hscroll , 1 , wxEXPAND );
2018-03-13 12:39:57 +01:00
2019-02-12 11:01:15 +01:00
auto * btn_sel_all = new wxButton ( this , wxID_ANY , _ ( L ( "Select all standard printers" )));
2019-01-30 15:22:17 +01:00
p -> btnsizer -> Add ( btn_sel_all );
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
p -> btn_prev = new wxButton ( this , wxID_ANY , _ ( L ( "< &Back" )));
p -> btn_next = new wxButton ( this , wxID_ANY , _ ( L ( "&Next >" )));
p -> btn_finish = new wxButton ( this , wxID_APPLY , _ ( L ( "&Finish" )));
2019-02-28 13:04:37 +01:00
p -> btn_cancel = new wxButton ( this , wxID_CANCEL , _ ( L ( "Cancel" ))); // Note: The label needs to be present, otherwise we get accelerator bugs on Mac
2019-01-30 15:22:17 +01:00
p -> btnsizer -> AddStretchSpacer ();
p -> btnsizer -> Add ( p -> btn_prev , 0 , wxLEFT , BTN_SPACING );
p -> btnsizer -> Add ( p -> btn_next , 0 , wxLEFT , BTN_SPACING );
p -> btnsizer -> Add ( p -> btn_finish , 0 , wxLEFT , BTN_SPACING );
p -> btnsizer -> Add ( p -> btn_cancel , 0 , wxLEFT , BTN_SPACING );
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
const auto & vendors = p -> vendors ;
const auto vendor_prusa_it = vendors . find ( "PrusaResearch" );
wxCHECK_RET ( vendor_prusa_it != vendors . cend (), "Vendor PrusaResearch not found" );
const VendorProfile & vendor_prusa = vendor_prusa_it -> second ;
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
p -> add_page ( p -> page_welcome = new PageWelcome ( this ));
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
p -> page_fff = new PagePrinters ( this , _ ( L ( "Prusa FFF Technology Printers" )), "Prusa FFF" , vendor_prusa , 0 , PagePrinters :: T_FFF );
p -> add_page ( p -> page_fff );
2018-06-11 18:24:13 +02:00
2019-01-30 15:22:17 +01:00
p -> page_msla = new PagePrinters ( this , _ ( L ( "Prusa MSLA Technology Printers" )), "Prusa MSLA" , vendor_prusa , 0 , PagePrinters :: T_SLA );
p -> add_page ( p -> page_msla );
2018-03-13 12:39:57 +01:00
2019-01-30 15:22:17 +01:00
p -> add_page ( p -> page_custom = new PageCustom ( this ));
p -> add_page ( p -> page_update = new PageUpdate ( this ));
p -> add_page ( p -> page_vendors = new PageVendors ( this ));
p -> add_page ( p -> page_firmware = new PageFirmware ( this ));
p -> add_page ( p -> page_bed = new PageBedShape ( this ));
p -> add_page ( p -> page_diams = new PageDiameters ( this ));
p -> add_page ( p -> page_temps = new PageTemperatures ( this ));
p -> load_pages ( false );
vsizer -> Add ( topsizer , 1 , wxEXPAND | wxALL , DIALOG_MARGIN );
vsizer -> Add ( hline , 0 , wxEXPAND );
vsizer -> Add ( p -> btnsizer , 0 , wxEXPAND | wxALL , DIALOG_MARGIN );
SetSizer ( vsizer );
SetSizerAndFit ( vsizer );
// We can now enable scrolling on hscroll
p -> hscroll -> SetScrollRate ( 30 , 30 );
2019-02-07 15:55:47 +01:00
on_window_geometry ( this , [ this ]() {
// Clamp the Wizard size based on screen dimensions
const auto idx = wxDisplay :: GetFromWindow ( this );
wxDisplay display ( idx != wxNOT_FOUND ? idx : 0u );
const auto disp_rect = display . GetClientArea ();
wxRect window_rect (
disp_rect . x + disp_rect . width / 20 ,
disp_rect . y + disp_rect . height / 20 ,
9 * disp_rect . width / 10 ,
9 * disp_rect . height / 10 );
2019-04-18 17:21:46 +02:00
const int width_hint = p -> index -> GetSize (). GetWidth () + p -> page_fff -> get_width () + 30 * p -> em (); // XXX: magic constant, I found no better solution
2019-02-12 10:10:46 +01:00
if ( width_hint < window_rect . width ) {
window_rect . x += ( window_rect . width - width_hint ) / 2 ;
window_rect . width = width_hint ;
}
2019-02-07 15:55:47 +01:00
SetSize ( window_rect );
2019-01-30 15:22:17 +01:00
});
p -> btn_prev -> Bind ( wxEVT_BUTTON , [ this ]( const wxCommandEvent & ) { this -> p -> index -> go_prev (); });
p -> btn_next -> Bind ( wxEVT_BUTTON , [ this ]( const wxCommandEvent & ) { this -> p -> index -> go_next (); });
p -> btn_finish -> Bind ( wxEVT_BUTTON , [ this ]( const wxCommandEvent & ) { this -> EndModal ( wxID_OK ); });
p -> btn_finish -> Hide ();
btn_sel_all -> Bind ( wxEVT_BUTTON , [ this ]( const wxCommandEvent & ) {
2019-02-12 11:01:15 +01:00
p -> page_fff -> select_all ( true , false );
p -> page_msla -> select_all ( true , false );
2019-01-30 15:22:17 +01:00
p -> index -> go_to ( p -> page_update );
});
p -> index -> Bind ( EVT_INDEX_PAGE , [ this ]( const wxCommandEvent & ) {
const bool is_last = p -> index -> active_is_last ();
p -> btn_next -> Show ( ! is_last );
p -> btn_finish -> Show ( is_last );
Layout ();
});
2018-03-13 12:39:57 +01:00
}
ConfigWizard ::~ ConfigWizard () {}
2018-04-24 18:06:42 +02:00
bool ConfigWizard :: run ( PresetBundle * preset_bundle , const PresetUpdater * updater )
2018-03-13 12:39:57 +01:00
{
2019-01-30 15:22:17 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Running ConfigWizard, reason: " << p -> run_reason ;
if ( ShowModal () == wxID_OK ) {
auto * app_config = GUI :: get_app_config ();
p -> apply_config ( app_config , preset_bundle , updater );
app_config -> set_legacy_datadir ( false );
BOOST_LOG_TRIVIAL ( info ) << "ConfigWizard applied" ;
return true ;
} else {
BOOST_LOG_TRIVIAL ( info ) << "ConfigWizard cancelled" ;
return false ;
}
2018-03-13 12:39:57 +01:00
}
2019-01-21 12:34:28 +01:00
const wxString & ConfigWizard :: name ( const bool from_menu /* = false*/ )
2018-04-17 11:54:59 +02:00
{
2019-01-30 15:22:17 +01:00
// A different naming convention is used for the Wizard on Windows vs. OSX & GTK.
2018-04-17 11:54:59 +02:00
#if WIN32
2019-01-21 12:34:28 +01:00
static const wxString config_wizard_name = L ( "Configuration Wizard" );
static const wxString config_wizard_name_menu = L ( "Configuration &Wizard" );
2018-04-17 11:54:59 +02:00
#else
2019-01-30 15:22:17 +01:00
static const wxString config_wizard_name = L ( "Configuration Assistant" );
2019-01-21 12:34:28 +01:00
static const wxString config_wizard_name_menu = L ( "Configuration &Assistant" );
2018-04-17 11:54:59 +02:00
#endif
2019-01-30 15:22:17 +01:00
return from_menu ? config_wizard_name_menu : config_wizard_name ;
2018-04-17 11:54:59 +02:00
}
2018-03-13 12:39:57 +01:00
}
}