diff options
Diffstat (limited to 'lib/Slic3r/GUI/MainFrame.pm')
-rw-r--r-- | lib/Slic3r/GUI/MainFrame.pm | 393 |
1 files changed, 159 insertions, 234 deletions
diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index ad2eca91d..a6baef8f9 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -6,6 +6,7 @@ use warnings; use utf8; use File::Basename qw(basename dirname); +use FindBin; use List::Util qw(min); use Slic3r::Geometry qw(X Y); use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog @@ -21,13 +22,14 @@ sub new { my ($class, %params) = @_; my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE); + Slic3r::GUI::set_main_frame($self); if ($^O eq 'MSWin32') { - # Load the icon either from the exe, or fron the ico file. - my $iconfile = $Slic3r::var->('..\slic3r.exe'); - $iconfile = $Slic3r::var->("Slic3r.ico") unless -f $iconfile; + # Load the icon either from the exe, or from the ico file. + my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe'; + $iconfile = Slic3r::var("Slic3r.ico") unless -f $iconfile; $self->SetIcon(Wx::Icon->new($iconfile, wxBITMAP_TYPE_ICO)); } else { - $self->SetIcon(Wx::Icon->new($Slic3r::var->("Slic3r_128px.png"), wxBITMAP_TYPE_PNG)); + $self->SetIcon(Wx::Icon->new(Slic3r::var("Slic3r_128px.png"), wxBITMAP_TYPE_PNG)); } # store input params @@ -69,15 +71,15 @@ sub new { # declare events EVT_CLOSE($self, sub { my (undef, $event) = @_; - if ($event->CanVeto && !$self->check_unsaved_changes) { $event->Veto; return; } - # save window size wxTheApp->save_window_pos($self, "main_frame"); - + # 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. + wxTheApp->{app_config}->save; # propagate event $event->Skip; }); @@ -91,6 +93,8 @@ sub _init_tabpanel { my ($self) = @_; $self->{tabpanel} = my $panel = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL); + Slic3r::GUI::set_tab_panel($panel); + EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{tabpanel}, sub { my $panel = $self->{tabpanel}->GetCurrentPage; $panel->OnActivate if $panel->can('OnActivate'); @@ -112,7 +116,7 @@ sub _init_tabpanel { # Callback to be executed after any of the configuration fields (Perl class Slic3r::GUI::OptionsGroup::Field) change their value. $tab->on_value_change(sub { my ($opt_key, $value) = @_; - my $config = $tab->config; + my $config = $tab->{presets}->get_current_preset->config; if ($self->{plater}) { $self->{plater}->on_config_change($config); # propagate config change events to the plater $self->{plater}->on_extruders_change($value) if $opt_key eq 'extruders_count'; @@ -126,24 +130,38 @@ sub _init_tabpanel { if ($self->{plater}) { # Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs. $self->{plater}->update_presets($tab_name, @_); - $self->{plater}->on_config_change($tab->config); - if ($self->{controller}) { - $self->{controller}->update_presets($tab_name, @_); + if ($tab_name eq 'printer') { + # Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. + wxTheApp->{preset_bundle}->print->update_tab_ui( + $self->{options_tabs}{'print'}->{presets_choice}, + $self->{options_tabs}{'print'}->{show_incompatible_presets}); + wxTheApp->{preset_bundle}->filament->update_tab_ui( + $self->{options_tabs}{'filament'}->{presets_choice}, + $self->{options_tabs}{'filament'}->{show_incompatible_presets}); + # Update the controller printers. + $self->{controller}->update_presets(@_) if $self->{controller}; } + $self->{plater}->on_config_change($tab->{presets}->get_current_preset->config); } }); - $tab->load_presets; + # Load the currently selected preset into the GUI, update the preset selection box. + $tab->load_current_preset; $panel->AddPage($tab, $tab->title); } + +#TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view. +# Slic3r::GUI::create_preset_tab("print"); if ($self->{plater}) { $self->{plater}->on_select_preset(sub { - my ($group, $i) = @_; - $self->{options_tabs}{$group}->select_preset($i); + my ($group, $name) = @_; + $self->{options_tabs}{$group}->select_preset($name); }); - # load initial config - $self->{plater}->on_config_change($self->config); + my $full_config = wxTheApp->{preset_bundle}->full_config; + $self->{plater}->on_config_change($full_config); + # Show a correct number of filament fields. + $self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter})); } } @@ -153,6 +171,9 @@ sub _init_menubar { # File menu my $fileMenu = Wx::Menu->new; { + wxTheApp->append_menu_item($fileMenu, "Open STL/OBJ/AMF…\tCtrl+O", 'Open a model', sub { + $self->{plater}->add if $self->{plater}; + }, undef, undef); #'brick_add.png'); $self->_append_menu_item($fileMenu, "&Load Config…\tCtrl+L", 'Load exported configuration file', sub { $self->load_config_file; }, undef, 'plugin_add.png'); @@ -262,13 +283,14 @@ sub _init_menubar { # \xA0 is a non-breaing space. It is entered here to spoil the automatic accelerators, # as the simple numeric accelerators spoil all numeric data entry. # The camera control accelerators are captured by 3DScene Perl module instead. - $self->_append_menu_item($self->{viewMenu}, "Iso\t\xA00" , 'Iso View' , sub { $self->select_view('iso' ); }); - $self->_append_menu_item($self->{viewMenu}, "Top\t\xA01" , 'Top View' , sub { $self->select_view('top' ); }); - $self->_append_menu_item($self->{viewMenu}, "Bottom\t\xA02" , 'Bottom View' , sub { $self->select_view('bottom' ); }); - $self->_append_menu_item($self->{viewMenu}, "Front\t\xA03" , 'Front View' , sub { $self->select_view('front' ); }); - $self->_append_menu_item($self->{viewMenu}, "Rear\t\xA04" , 'Rear View' , sub { $self->select_view('rear' ); }); - $self->_append_menu_item($self->{viewMenu}, "Left\t\xA05" , 'Left View' , sub { $self->select_view('left' ); }); - $self->_append_menu_item($self->{viewMenu}, "Right\t\xA06" , 'Right View' , sub { $self->select_view('right' ); }); + my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] }; + $self->_append_menu_item($self->{viewMenu}, $accel->('Iso', '0'), 'Iso View' , sub { $self->select_view('iso' ); }); + $self->_append_menu_item($self->{viewMenu}, $accel->('Top', '1'), 'Top View' , sub { $self->select_view('top' ); }); + $self->_append_menu_item($self->{viewMenu}, $accel->('Bottom', '2'), 'Bottom View' , sub { $self->select_view('bottom' ); }); + $self->_append_menu_item($self->{viewMenu}, $accel->('Front', '3'), 'Front View' , sub { $self->select_view('front' ); }); + $self->_append_menu_item($self->{viewMenu}, $accel->('Rear', '4'), 'Rear View' , sub { $self->select_view('rear' ); }); + $self->_append_menu_item($self->{viewMenu}, $accel->('Left', '5'), 'Left View' , sub { $self->select_view('left' ); }); + $self->_append_menu_item($self->{viewMenu}, $accel->('Right', '6'), 'Right View' , sub { $self->select_view('right' ); }); } # Help menu @@ -317,6 +339,8 @@ sub _init_menubar { $menubar->Append($windowMenu, "&Window"); $menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu}; $menubar->Append($helpMenu, "&Help"); + # Add an optional debug menu. In production code, the add_debug_menu() call should do nothing. + Slic3r::GUI::add_debug_menu($menubar); $self->SetMenuBar($menubar); } } @@ -329,7 +353,6 @@ sub is_loaded { # Selection of a 3D object changed on the platter. sub on_plater_selection_changed { my ($self, $have_selection) = @_; - return if !defined $self->{object_menu}; $self->{object_menu}->Enable($_->GetId, $have_selection) for $self->{object_menu}->GetMenuItems; @@ -337,20 +360,20 @@ sub on_plater_selection_changed { # To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG". sub quick_slice { - my $self = shift; - my %params = @_; + my ($self, %params) = @_; my $progress_dialog; eval { # validate configuration - my $config = $self->config; + my $config = wxTheApp->{preset_bundle}->full_config(); $config->validate; # select input file my $input_file; - my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || ''; if (!$params{reslice}) { - my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/PRUSA):', $dir, "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/PRUSA):', + wxTheApp->{app_config}->get_last_dir, "", + &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); if ($dialog->ShowModal != wxID_OK) { $dialog->Destroy; return; @@ -372,8 +395,7 @@ sub quick_slice { $input_file = $qs_last_input_file; } my $input_file_basename = basename($input_file); - $Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_file); - wxTheApp->save_settings; + wxTheApp->{app_config}->update_skein_dir(dirname($input_file)); my $print_center; { @@ -395,20 +417,19 @@ sub quick_slice { $sprint->apply_config($config); $sprint->set_model($model); - { - my $extra = $self->extra_variables; - $sprint->placeholder_parser->set($_, $extra->{$_}) for keys %$extra; - } - + # Copy the names of active presets into the placeholder parser. + wxTheApp->{preset_bundle}->export_selections_pp($sprint->placeholder_parser); + # select output file my $output_file; if ($params{reslice}) { $output_file = $qs_last_output_file if defined $qs_last_output_file; } elsif ($params{save_as}) { + # The following line may die if the output_filename_format template substitution fails. $output_file = $sprint->output_filepath; $output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ if $params{export_svg}; my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:', - wxTheApp->output_path(dirname($output_file)), + wxTheApp->{app_config}->get_last_output_dir(dirname($output_file)), basename($output_file), $params{export_svg} ? &Slic3r::GUI::FILE_WILDCARDS->{svg} : &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if ($dlg->ShowModal != wxID_OK) { $dlg->Destroy; @@ -416,8 +437,7 @@ sub quick_slice { } $output_file = $dlg->GetPath; $qs_last_output_file = $output_file unless $params{export_svg}; - $Slic3r::GUI::Settings->{_}{last_output_path} = dirname($output_file); - wxTheApp->save_settings; + wxTheApp->{app_config}->update_last_output_dir(dirname($output_file)); $dlg->Destroy; } @@ -452,9 +472,7 @@ sub quick_slice { sub reslice_now { my ($self) = @_; - if ($self->{plater}) { - $self->{plater}->reslice; - } + $self->{plater}->reslice if $self->{plater}; } sub repair_stl { @@ -462,8 +480,9 @@ sub repair_stl { my $input_file; { - my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || ''; - my $dialog = Wx::FileDialog->new($self, 'Select the STL file to repair:', $dir, "", &Slic3r::GUI::FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + my $dialog = Wx::FileDialog->new($self, 'Select the STL file to repair:', + wxTheApp->{app_config}->get_last_dir, "", + &Slic3r::GUI::FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST); if ($dialog->ShowModal != wxID_OK) { $dialog->Destroy; return; @@ -492,36 +511,25 @@ sub repair_stl { Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair"); } -sub extra_variables { - my $self = shift; - my %extra_variables = (); - $extra_variables{"${_}_preset"} = $self->{options_tabs}{$_}->get_current_preset->name - for qw(print filament printer); - return { %extra_variables }; -} - sub export_config { my $self = shift; - - my $config = $self->config; - eval { - # validate configuration - $config->validate; - }; + # Generate a cummulative configuration for the selected print, filaments and printer. + my $config = wxTheApp->{preset_bundle}->full_config(); + # Validate the cummulative configuration. + eval { $config->validate; }; Slic3r::GUI::catch_error($self) and return; - - my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || ''; - my $filename = $last_config ? basename($last_config) : "config.ini"; - my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename, + # Ask user for the file name for the config file. + my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', + $last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir, + $last_config ? basename($last_config) : "config.ini", &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if ($dlg->ShowModal == wxID_OK) { - my $file = $dlg->GetPath; - $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); - wxTheApp->save_settings; + my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef; + $dlg->Destroy; + if (defined $file) { + wxTheApp->{app_config}->update_config_dir(dirname($file)); $last_config = $file; $config->save($file); } - $dlg->Destroy; } # Load a config file containing a Print, Filament & Printer preset. @@ -529,222 +537,139 @@ sub load_config_file { my ($self, $file) = @_; if (!$file) { return unless $self->check_unsaved_changes; - my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || ''; - my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini", - 'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST); + my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', + $last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir, + "config.ini", + 'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST); return unless $dlg->ShowModal == wxID_OK; $file = $dlg->GetPaths; $dlg->Destroy; } - for my $tab (values %{$self->{options_tabs}}) { - # Dont proceed further if the config file cannot be loaded. - return undef if ! $tab->load_config_file($file); - } - $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); - wxTheApp->save_settings; + eval { wxTheApp->{preset_bundle}->load_config_file($file); }; + # Dont proceed further if the config file cannot be loaded. + return if Slic3r::GUI::catch_error($self); + $_->load_current_preset for (values %{$self->{options_tabs}}); + wxTheApp->{app_config}->update_config_dir(dirname($file)); $last_config = $file; } sub export_configbundle { - my $self = shift; - - eval { - # validate current configuration in case it's dirty - $self->config->validate; - }; + my ($self) = @_; + return unless $self->check_unsaved_changes; + # validate current configuration in case it's dirty + eval { wxTheApp->{preset_bundle}->full_config->validate; }; Slic3r::GUI::catch_error($self) and return; - - my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || ''; - my $filename = "Slic3r_config_bundle.ini"; - my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:', $dir, $filename, + # Ask user for a file name. + my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:', + $last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir, + "Slic3r_config_bundle.ini", &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if ($dlg->ShowModal == wxID_OK) { - my $file = $dlg->GetPath; - $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); - wxTheApp->save_settings; - - # leave default category empty to prevent the bundle from being parsed as a normal config file - my $ini = { _ => {} }; - $ini->{settings}{$_} = $Slic3r::GUI::Settings->{_}{$_} for qw(autocenter); - $ini->{presets} = $Slic3r::GUI::Settings->{presets}; - - foreach my $section (qw(print filament printer)) { - my %presets = wxTheApp->presets($section); - foreach my $preset_name (keys %presets) { - my $config = Slic3r::Config->load($presets{$preset_name}); - $ini->{"$section:$preset_name"} = $config->as_ini->{_}; - } - } - - Slic3r::Config->write_ini($file, $ini); - } + my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef; $dlg->Destroy; + if (defined $file) { + # Export the config bundle. + wxTheApp->{app_config}->update_config_dir(dirname($file)); + eval { wxTheApp->{preset_bundle}->export_configbundle($file); }; + Slic3r::GUI::catch_error($self) and return; + } } +# 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. sub load_configbundle { - my ($self, $file, $skip_no_id) = @_; - + my ($self, $file) = @_; + return unless $self->check_unsaved_changes; if (!$file) { - my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || ''; - my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini", - &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', + $last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir, + "config.ini", + &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST); return unless $dlg->ShowModal == wxID_OK; $file = $dlg->GetPaths; $dlg->Destroy; } - $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); - wxTheApp->save_settings; - - # load .ini file - my $ini = Slic3r::Config->read_ini($file); - - if ($ini->{settings}) { - $Slic3r::GUI::Settings->{_}{$_} = $ini->{settings}{$_} for keys %{$ini->{settings}}; - wxTheApp->save_settings; - } - if ($ini->{presets}) { - $Slic3r::GUI::Settings->{presets} = $ini->{presets}; - wxTheApp->save_settings; - } + wxTheApp->{app_config}->update_config_dir(dirname($file)); - my $imported = 0; - INI_BLOCK: foreach my $ini_category (sort keys %$ini) { - next unless $ini_category =~ /^(print|filament|printer):(.+)$/; - my ($section, $preset_name) = ($1, $2); - my $config = Slic3r::Config->load_ini_hash($ini->{$ini_category}); - next if $skip_no_id && !$config->get($section . "_settings_id"); - - { - my %current_presets = Slic3r::GUI->presets($section); - my %current_ids = map { $_ => 1 } - grep $_, - map Slic3r::Config->load($_)->get($section . "_settings_id"), - values %current_presets; - next INI_BLOCK if exists $current_ids{$config->get($section . "_settings_id")}; - } - - $config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $section, $preset_name); - Slic3r::debugf "Imported %s preset %s\n", $section, $preset_name; - $imported++; - } + my $presets_imported = 0; + eval { $presets_imported = wxTheApp->{preset_bundle}->load_configbundle($file); }; + Slic3r::GUI::catch_error($self) and return; + + # Load the currently selected preset into the GUI, update the preset selection box. foreach my $tab (values %{$self->{options_tabs}}) { - $tab->load_presets; + $tab->load_current_preset; } - return if !$imported; - - my $message = sprintf "%d presets successfully imported.", $imported; + my $message = sprintf "%d presets successfully imported.", $presets_imported; Slic3r::GUI::show_info($self, $message); } # Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset. # Also update the platter with the new presets. sub load_config { - my $self = shift; - my ($config) = @_; - - foreach my $tab (values %{$self->{options_tabs}}) { - $tab->load_config($config); - } - if ($self->{plater}) { - $self->{plater}->on_config_change($config); - } + my ($self, $config) = @_; + $_->load_config($config) foreach values %{$self->{options_tabs}}; + $self->{plater}->on_config_change($config) if $self->{plater}; } sub config_wizard { - my $self = shift; - + my ($self) = @_; + # Exit wizard if there are unsaved changes and the user cancels the action. return unless $self->check_unsaved_changes; - if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) { - for my $tab (values %{$self->{options_tabs}}) { - $tab->select_default_preset; - } - $self->load_config($config); - for my $tab (values %{$self->{options_tabs}}) { - $tab->save_preset('My Settings'); + # Enumerate the profiles bundled with the Slic3r installation under resources/profiles. + my $directory = Slic3r::resources_dir() . "/profiles"; + my @profiles = (); + if (opendir(DIR, Slic3r::encode_path($directory))) { + while (my $file = readdir(DIR)) { + if ($file =~ /\.ini$/) { + $file =~ s/\.ini$//; + push @profiles, Slic3r::decode_path($file); + } } + closedir(DIR); } -} - -=head2 config - -This method collects all config values from the tabs and merges them into a single config object. - -=cut - -sub config { - my $self = shift; - - return Slic3r::Config->new_from_defaults - if !exists $self->{options_tabs}{print} - || !exists $self->{options_tabs}{filament} - || !exists $self->{options_tabs}{printer}; - - # retrieve filament presets and build a single config object for them - my $filament_config; - if (!$self->{plater} || $self->{plater}->filament_presets == 1) { - $filament_config = $self->{options_tabs}{filament}->config; - } else { - my $i = -1; - foreach my $preset_idx ($self->{plater}->filament_presets) { - $i++; - my $config; - if ($preset_idx == $self->{options_tabs}{filament}->current_preset) { - # the selected preset for this extruder is the one in the tab - # use the tab's config instead of the preset in case it is dirty - # perhaps plater shouldn't expose dirty presets at all in multi-extruder environments. - $config = $self->{options_tabs}{filament}->config; - } else { - my $preset = $self->{options_tabs}{filament}->get_preset($preset_idx); - $config = $self->{options_tabs}{filament}->get_preset_config($preset); + # Open the wizard. + if (my $config = Slic3r::GUI::ConfigWizard->new($self, \@profiles)->run) { + if (ref($config)) { + # Wizard returned a config. Add the config to each of the preset types. + for my $tab (values %{$self->{options_tabs}}) { + # Select the first visible preset, force. + $tab->select_preset(undef, 1); } - if (!$filament_config) { - $filament_config = $config->clone; - next; + # Load the config over the previously selected defaults. + $self->load_config($config); + for my $tab (values %{$self->{options_tabs}}) { + # Save the settings under a new name, select the name. + $tab->save_preset('My Settings'); } - foreach my $opt_key (@{$config->get_keys}) { - my $value = $filament_config->get($opt_key); - next unless ref $value eq 'ARRAY'; - $value->[$i] = $config->get($opt_key)->[0]; - $filament_config->set($opt_key, $value); + } else { + # Wizard returned a name of a preset bundle bundled with the installation. Unpack it. + eval { wxTheApp->{preset_bundle}->load_configbundle($directory . '/' . $config . '.ini'); }; + Slic3r::GUI::catch_error($self) and return; + # Load the currently selected preset into the GUI, update the preset selection box. + foreach my $tab (values %{$self->{options_tabs}}) { + $tab->load_current_preset; } } } - - my $config = Slic3r::Config->merge( - Slic3r::Config->new_from_defaults, - $self->{options_tabs}{print}->config, - $self->{options_tabs}{printer}->config, - $filament_config, - ); - - my $extruders_count = $self->{options_tabs}{printer}{extruders_count}; - $config->set("${_}_extruder", min($config->get("${_}_extruder"), $extruders_count)) - for qw(perimeter infill solid_infill support_material support_material_interface); - - return $config; -} - -sub filament_preset_names { - my ($self) = @_; - return map $self->{options_tabs}{filament}->get_preset($_)->name, - $self->{plater}->filament_presets; } +# This is called when closing the application, when loading a config file or when starting the config wizard +# to notify the user whether he is aware that some preset changes will be lost. sub check_unsaved_changes { my $self = shift; my @dirty = (); foreach my $tab (values %{$self->{options_tabs}}) { - push @dirty, $tab->title if $tab->is_dirty; + push @dirty, $tab->title if $tab->{presets}->current_is_dirty; } if (@dirty) { my $titles = join ', ', @dirty; my $confirm = Wx::MessageDialog->new($self, "You have unsaved changes ($titles). Discard changes and continue anyway?", 'Unsaved Presets', wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT); - return ($confirm->ShowModal == wxID_YES); + return $confirm->ShowModal == wxID_YES; } return 1; @@ -765,21 +690,18 @@ sub select_view { sub _append_menu_item { my ($self, $menu, $string, $description, $cb, $id, $icon) = @_; - $id //= &Wx::NewId(); my $item = $menu->Append($id, $string, $description); $self->_set_menu_item_icon($item, $icon); - EVT_MENU($self, $id, $cb); return $item; } sub _set_menu_item_icon { my ($self, $menuItem, $icon) = @_; - # SetBitmap was not available on OS X before Wx 0.9927 if ($icon && $menuItem->can('SetBitmap')) { - $menuItem->SetBitmap(Wx::Bitmap->new($Slic3r::var->($icon), wxBITMAP_TYPE_PNG)); + $menuItem->SetBitmap(Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG)); } } @@ -787,8 +709,11 @@ sub _set_menu_item_icon { # Update the UI based on the current preferences. sub update_ui_from_settings { my ($self) = @_; - $self->{menu_item_reslice_now}->Enable(! $Slic3r::GUI::Settings->{_}{background_processing}); + $self->{menu_item_reslice_now}->Enable(! wxTheApp->{app_config}->get("background_processing")); $self->{plater}->update_ui_from_settings if ($self->{plater}); + for my $tab_name (qw(print filament printer)) { + $self->{options_tabs}{$tab_name}->update_ui_from_settings; + } } 1; |