mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-03 01:40:44 +08:00
Allow dirty presets, and many more improvements including remembering the preset dialog position and closing it with the Esc key
This commit is contained in:
parent
9a9597df83
commit
4ca5f9ee16
@ -5,7 +5,9 @@ use utf8;
|
||||
|
||||
use File::Basename qw(basename);
|
||||
use FindBin;
|
||||
use List::Util qw(first);
|
||||
use List::Util qw(first any);
|
||||
use Slic3r::Geometry qw(X Y);
|
||||
|
||||
use Slic3r::GUI::2DBed;
|
||||
use Slic3r::GUI::AboutDialog;
|
||||
use Slic3r::GUI::BedShapeDialog;
|
||||
@ -98,7 +100,7 @@ sub OnInit {
|
||||
Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION;
|
||||
|
||||
$self->{notifier} = Slic3r::GUI::Notifier->new;
|
||||
$self->{external_presets} = [];
|
||||
$self->{presets} = { print => [], filament => [], printer => [] };
|
||||
|
||||
# locate or create data directory
|
||||
# Unix: ~/.Slic3r
|
||||
@ -142,6 +144,8 @@ sub OnInit {
|
||||
unlink "$enc_datadir/simple.ini";
|
||||
}
|
||||
|
||||
$self->load_presets;
|
||||
|
||||
# application frame
|
||||
Wx::Image::AddHandler(Wx::PNGHandler->new);
|
||||
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
|
||||
@ -283,47 +287,64 @@ sub save_settings {
|
||||
Slic3r::Config->write_ini("$datadir/slic3r.ini", $Settings);
|
||||
}
|
||||
|
||||
sub presets {
|
||||
my ($self, $section, $force_default) = @_;
|
||||
sub presets { return $_[0]->{presets}; }
|
||||
|
||||
sub load_presets {
|
||||
my ($self) = @_;
|
||||
|
||||
my @presets = ();
|
||||
for my $group (qw(printer filament print)) {
|
||||
my $presets = $self->{presets}{$group};
|
||||
|
||||
# keep external or dirty presets
|
||||
@$presets = grep { ($_->external && $_->file_exists) || $_->dirty } @$presets;
|
||||
|
||||
my $dir = "$Slic3r::GUI::datadir/$group";
|
||||
opendir my $dh, Slic3r::encode_path($dir)
|
||||
or die "Failed to read directory $dir (errno: $!)\n";
|
||||
foreach my $file (grep /\.ini$/i, readdir $dh) {
|
||||
$file = Slic3r::decode_path($file);
|
||||
my $name = basename($file);
|
||||
$name =~ s/\.ini$//i;
|
||||
|
||||
# skip if we already have it
|
||||
next if any { $_->name eq $name } @$presets;
|
||||
|
||||
push @$presets, Slic3r::GUI::Preset->new(
|
||||
group => $group,
|
||||
name => $name,
|
||||
file => "$dir/$file",
|
||||
);
|
||||
}
|
||||
closedir $dh;
|
||||
|
||||
opendir my $dh, Slic3r::encode_path("$Slic3r::GUI::datadir/$section")
|
||||
or die "Failed to read directory $Slic3r::GUI::datadir/$section (errno: $!)\n";
|
||||
foreach my $file (grep /\.ini$/i, readdir $dh) {
|
||||
$file = Slic3r::decode_path($file);
|
||||
my $name = basename($file);
|
||||
$name =~ s/\.ini$//i;
|
||||
push @presets, Slic3r::GUI::Preset->new(
|
||||
name => $name,
|
||||
file => "$Slic3r::GUI::datadir/$section/$file",
|
||||
);
|
||||
}
|
||||
closedir $dh;
|
||||
@$presets = sort { $a->name cmp $b->name } @$presets;
|
||||
|
||||
@presets = sort { $a->name cmp $b->name }
|
||||
@presets,
|
||||
(grep -e $_->file, @{$self->{external_presets}});
|
||||
|
||||
if ($force_default || !@presets) {
|
||||
unshift @presets, Slic3r::GUI::Preset->new(
|
||||
unshift @$presets, Slic3r::GUI::Preset->new(
|
||||
group => $group,
|
||||
default => 1,
|
||||
name => '- default -',
|
||||
);
|
||||
}
|
||||
|
||||
return @presets;
|
||||
}
|
||||
|
||||
sub add_external_preset {
|
||||
my ($self, $file) = @_;
|
||||
|
||||
push @{$self->{external_presets}}, my $preset = Slic3r::GUI::Preset->new(
|
||||
name => basename($file), # keep .ini suffix
|
||||
file => $file,
|
||||
external => 1,
|
||||
);
|
||||
return $preset;
|
||||
my $name = basename($file); # keep .ini suffix
|
||||
for my $group (qw(printer filament print)) {
|
||||
my $presets = $self->{presets}{$group};
|
||||
|
||||
# remove any existing preset with the same name
|
||||
@$presets = grep { $_->name ne $name } @$presets;
|
||||
|
||||
push @$presets, Slic3r::GUI::Preset->new(
|
||||
group => $group,
|
||||
name => $name,
|
||||
file => $file,
|
||||
external => 1,
|
||||
);
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
sub have_version_check {
|
||||
@ -416,4 +437,29 @@ sub set_menu_item_icon {
|
||||
}
|
||||
}
|
||||
|
||||
sub save_window_pos {
|
||||
my ($self, $window, $name) = @_;
|
||||
|
||||
$Settings->{_}{"${name}_pos"} = join ',', $window->GetScreenPositionXY;
|
||||
$Settings->{_}{"${name}_size"} = join ',', $window->GetSizeWH;
|
||||
$Settings->{_}{"${name}_maximized"} = $window->IsMaximized;
|
||||
$self->save_settings;
|
||||
}
|
||||
|
||||
sub restore_window_pos {
|
||||
my ($self, $window, $name) = @_;
|
||||
|
||||
if (defined $Settings->{_}{"${name}_pos"}) {
|
||||
my $size = [ split ',', $Settings->{_}{"${name}_size"}, 2 ];
|
||||
$window->SetSize($size);
|
||||
|
||||
my $display = Wx::Display->new->GetClientArea();
|
||||
my $pos = [ split ',', $Settings->{_}{"${name}_pos"}, 2 ];
|
||||
if (($pos->[X] + $size->[X]/2) < $display->GetRight && ($pos->[Y] + $size->[Y]/2) < $display->GetBottom) {
|
||||
$window->Move($pos);
|
||||
}
|
||||
$window->Maximize(1) if $Settings->{_}{"${name}_maximized"};
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -47,7 +47,7 @@ sub new {
|
||||
delete $presets{$_} for map $_->printer_name, @panels;
|
||||
|
||||
foreach my $preset_name (sort keys %presets) {
|
||||
my $config = $presets{$preset_name}->load_config;
|
||||
my $config = $presets{$preset_name}->dirty_config;
|
||||
next if !$config->serial_port;
|
||||
|
||||
my $id = &Wx::NewId();
|
||||
@ -101,7 +101,7 @@ sub OnActivate {
|
||||
# get all available presets
|
||||
my %presets = ();
|
||||
{
|
||||
my %all = map { $_->name => $_ } wxTheApp->presets('printer');
|
||||
my %all = map { $_->name => $_ } @{wxTheApp->presets->{printer}};
|
||||
my %configs = map { my $name = $_; $name => $all{$name}->load_config } keys %all;
|
||||
%presets = map { $_ => $configs{$_} } grep $configs{$_}->serial_port, keys %all;
|
||||
}
|
||||
|
@ -56,19 +56,8 @@ sub new {
|
||||
$self->SetSizer($sizer);
|
||||
$self->Fit;
|
||||
$self->SetMinSize([760, 490]);
|
||||
if (defined $Slic3r::GUI::Settings->{_}{main_frame_size}) {
|
||||
my $size = [ split ',', $Slic3r::GUI::Settings->{_}{main_frame_size}, 2 ];
|
||||
$self->SetSize($size);
|
||||
|
||||
my $display = Wx::Display->new->GetClientArea();
|
||||
my $pos = [ split ',', $Slic3r::GUI::Settings->{_}{main_frame_pos}, 2 ];
|
||||
if (($pos->[X] + $size->[X]/2) < $display->GetRight && ($pos->[Y] + $size->[Y]/2) < $display->GetBottom) {
|
||||
$self->Move($pos);
|
||||
}
|
||||
$self->Maximize(1) if $Slic3r::GUI::Settings->{_}{main_frame_maximized};
|
||||
} else {
|
||||
$self->SetSize($self->GetMinSize);
|
||||
}
|
||||
$self->SetSize($self->GetMinSize);
|
||||
wxTheApp->restore_window_pos($self, "main_frame");
|
||||
$self->Show;
|
||||
$self->Layout;
|
||||
}
|
||||
@ -78,23 +67,23 @@ sub new {
|
||||
my (undef, $event) = @_;
|
||||
|
||||
if ($event->CanVeto) {
|
||||
my $veto = 0;
|
||||
if (!$self->{plater}->prompt_unsaved_changes) {
|
||||
$event->Veto;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($self->{controller} && $self->{controller}->printing) {
|
||||
my $confirm = Wx::MessageDialog->new($self, "You are currently printing. Do you want to stop printing and continue anyway?",
|
||||
'Unfinished Print', wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
|
||||
$veto = 1 if $confirm->ShowModal == wxID_YES;
|
||||
}
|
||||
if ($veto) {
|
||||
$event->Veto;
|
||||
return;
|
||||
if ($confirm->ShowModal == wxID_NO) {
|
||||
$event->Veto;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# save window size
|
||||
$Slic3r::GUI::Settings->{_}{main_frame_pos} = join ',', $self->GetScreenPositionXY;
|
||||
$Slic3r::GUI::Settings->{_}{main_frame_size} = join ',', $self->GetSizeWH;
|
||||
$Slic3r::GUI::Settings->{_}{main_frame_maximized} = $self->IsMaximized;
|
||||
wxTheApp->save_settings;
|
||||
wxTheApp->save_window_pos($self, "main_frame");
|
||||
|
||||
# propagate event
|
||||
$event->Skip;
|
||||
@ -507,9 +496,9 @@ sub load_config_file {
|
||||
wxTheApp->save_settings;
|
||||
$last_config = $file;
|
||||
|
||||
my $preset = wxTheApp->add_external_preset($file);
|
||||
my $name = wxTheApp->add_external_preset($file);
|
||||
$self->{plater}->load_presets;
|
||||
$self->{plater}->select_preset_by_name($preset->name, $_) for qw(print filament printer);
|
||||
$self->{plater}->select_preset_by_name($name, $_) for qw(print filament printer);
|
||||
}
|
||||
|
||||
sub export_configbundle {
|
||||
@ -536,8 +525,9 @@ sub export_configbundle {
|
||||
$ini->{presets} = $Slic3r::GUI::Settings->{presets};
|
||||
|
||||
foreach my $section (qw(print filament printer)) {
|
||||
my @presets = wxTheApp->presets($section);
|
||||
my @presets = @{wxTheApp->presets->{$section}};
|
||||
foreach my $preset (@presets) {
|
||||
next if $preset->default || $preset->external;
|
||||
$ini->{"$section:" . $preset->name} = $preset->load_config->as_ini->{_};
|
||||
}
|
||||
}
|
||||
@ -581,10 +571,10 @@ sub load_configbundle {
|
||||
next if $skip_no_id && !$config->get($section . "_settings_id");
|
||||
|
||||
{
|
||||
my @current_presets = Slic3r::GUI->presets($section);
|
||||
my @current_presets = @{wxTheApp->presets->{$section}};
|
||||
my %current_ids = map { $_ => 1 }
|
||||
grep $_,
|
||||
map $_->load_config->get($section . "_settings_id"),
|
||||
map $_->dirty_config->get($section . "_settings_id"),
|
||||
@current_presets;
|
||||
next INI_BLOCK if exists $current_ids{$config->get($section . "_settings_id")};
|
||||
}
|
||||
|
@ -368,6 +368,7 @@ sub new {
|
||||
printer => 'Printer',
|
||||
);
|
||||
$self->{preset_choosers} = {};
|
||||
$self->{preset_choosers_names} = {}; # wxChoice* => []
|
||||
for my $group (qw(print filament printer)) {
|
||||
# label
|
||||
my $text = Wx::StaticText->new($self, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
|
||||
@ -380,7 +381,7 @@ sub new {
|
||||
EVT_COMBOBOX($choice, $choice, sub {
|
||||
my ($choice) = @_;
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_on_select_preset($group);
|
||||
$self->_on_change_combobox($group, $choice);
|
||||
});
|
||||
});
|
||||
|
||||
@ -512,11 +513,52 @@ sub new {
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub prompt_unsaved_changes {
|
||||
my ($self) = @_;
|
||||
|
||||
foreach my $group (qw(printer filament print)) {
|
||||
foreach my $choice (@{$self->{preset_choosers}{$group}}) {
|
||||
my $pp = $self->{preset_choosers_names}{$choice};
|
||||
for my $i (0..$#$pp) {
|
||||
my $preset = first { $_->name eq $pp->[$i] } @{wxTheApp->presets->{$group}};
|
||||
if (!$preset->prompt_unsaved_changes($self)) {
|
||||
# Restore the previous one
|
||||
$choice->SetSelection($i);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub _on_change_combobox {
|
||||
my ($self, $group, $choice) = @_;
|
||||
|
||||
if (0) {
|
||||
# This code is disabled because wxPerl doesn't provide GetCurrentSelection
|
||||
my $current_name = $self->{preset_choosers_names}{$choice}[$choice->GetCurrentSelection];
|
||||
my $current = first { $_->name eq $current_name } @{wxTheApp->presets->{$group}};
|
||||
if (!$current->prompt_unsaved_changes($self)) {
|
||||
# Restore the previous one
|
||||
$choice->SetSelection($choice->GetCurrentSelection);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return 0 if !$self->prompt_unsaved_changes;
|
||||
}
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_on_select_preset($group);
|
||||
|
||||
# This will remove the "(modified)" mark from any dirty preset handled here.
|
||||
$self->load_presets;
|
||||
});
|
||||
}
|
||||
|
||||
sub _on_select_preset {
|
||||
my ($self, $group) = @_;
|
||||
|
||||
my @presets = map $self->presets->{$group}[scalar $_->GetSelection],
|
||||
@{$self->{preset_choosers}{$group}};
|
||||
my @presets = $self->selected_presets($group);
|
||||
|
||||
$Slic3r::GUI::Settings->{presets}{$group} = $presets[0]->name;
|
||||
$Slic3r::GUI::Settings->{presets}{"${group}_${_}"} = $presets[$_]->name
|
||||
@ -565,17 +607,16 @@ sub GetFrame {
|
||||
sub load_presets {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->presets({});
|
||||
|
||||
my $selected_printer_name;
|
||||
foreach my $group (qw(printer filament print)) {
|
||||
my @presets = wxTheApp->presets($group);
|
||||
my @presets = @{wxTheApp->presets->{$group}};
|
||||
|
||||
# Skip presets not compatible with the selected printer, if they
|
||||
# have other compatible printers configured (and at least one of them exists).
|
||||
if ($group eq 'filament' || $group eq 'print') {
|
||||
my %printer_names = map { $_->name => 1 } @{ $self->presets->{printer} };
|
||||
# Skip presets not compatible with the selected printer, if they
|
||||
# have other compatible printers configured (and at least one of them exists).
|
||||
my %printer_names = map { $_->name => 1 } @{ wxTheApp->presets->{printer} };
|
||||
for (my $i = 0; $i <= $#presets; ++$i) {
|
||||
my $config = $presets[$i]->config;
|
||||
my $config = $presets[$i]->dirty_config;
|
||||
next if !$config->has('compatible_printers');
|
||||
my @compat = @{$config->compatible_printers};
|
||||
if (@compat
|
||||
@ -586,14 +627,11 @@ sub load_presets {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!@presets) {
|
||||
unshift @presets, Slic3r::GUI::Preset->new(
|
||||
default => 1,
|
||||
name => '- default -',
|
||||
);
|
||||
}
|
||||
|
||||
$self->presets->{$group} = [@presets];
|
||||
# Only show the default presets if we have no other presets.
|
||||
if (@presets > 1) {
|
||||
@presets = grep { !$_->default } @presets;
|
||||
}
|
||||
|
||||
# get the wxChoice objects for this group
|
||||
my @choosers = @{ $self->{preset_choosers}{$group} };
|
||||
@ -613,11 +651,12 @@ sub load_presets {
|
||||
# populate the wxChoice objects
|
||||
foreach my $choice (@choosers) {
|
||||
$choice->Clear;
|
||||
$self->{preset_choosers_names}{$choice} = [];
|
||||
foreach my $preset (@presets) {
|
||||
# load/generate the proper icon
|
||||
my $bitmap;
|
||||
if ($group eq 'filament') {
|
||||
my $config = $preset->config;
|
||||
my $config = $preset->dirty_config;
|
||||
if ($preset->default || !$config->has('filament_colour')) {
|
||||
$bitmap = Wx::Bitmap->new($Slic3r::var->("spool.png"), wxBITMAP_TYPE_PNG);
|
||||
} else {
|
||||
@ -634,7 +673,8 @@ sub load_presets {
|
||||
} elsif ($group eq 'printer') {
|
||||
$bitmap = Wx::Bitmap->new($Slic3r::var->("printer_empty.png"), wxBITMAP_TYPE_PNG);
|
||||
}
|
||||
$choice->AppendString($preset->name, $bitmap);
|
||||
$choice->AppendString($preset->dropdown_name, $bitmap);
|
||||
push @{$self->{preset_choosers_names}{$choice}}, $preset->name;
|
||||
}
|
||||
|
||||
my $selected = shift @sel;
|
||||
@ -643,14 +683,13 @@ sub load_presets {
|
||||
# won't be picked up as the visible string
|
||||
$choice->SetSelection($selected);
|
||||
|
||||
my $preset_name = $choice->GetString($selected);
|
||||
my $preset_name = $self->{preset_choosers_names}{$choice}[$selected];
|
||||
$self->{print}->placeholder_parser->set("${group}_preset", $preset_name);
|
||||
# TODO: populate other filament preset placeholders
|
||||
$selected_printer_name = $preset_name if $group eq 'printer';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#$self->_on_select_preset($_) for qw(printer filament print);
|
||||
}
|
||||
|
||||
sub select_preset_by_name {
|
||||
@ -658,11 +697,12 @@ sub select_preset_by_name {
|
||||
|
||||
# $n is optional
|
||||
|
||||
my $presets = $self->presets->{$group};
|
||||
my $i = first { $presets->[$_]->name eq $name } 0..$#$presets;
|
||||
my $presets = wxTheApp->presets->{$group};
|
||||
my $choosers = $self->{preset_choosers}{$group};
|
||||
my $names = $self->{preset_choosers_names}{$choosers->[0]};
|
||||
my $i = first { $names->[$_] eq $name } 0..$#$names;
|
||||
return if !defined $i;
|
||||
|
||||
my $choosers = $self->{preset_choosers}{$group};
|
||||
if (defined $n && $n <= $#$choosers) {
|
||||
$choosers->[$n]->SetSelection($i);
|
||||
} else {
|
||||
@ -674,10 +714,16 @@ sub select_preset_by_name {
|
||||
sub selected_presets {
|
||||
my ($self, $group) = @_;
|
||||
|
||||
my %presets;
|
||||
my %presets = ();
|
||||
foreach my $group (qw(printer filament print)) {
|
||||
my @i = map scalar($_->GetSelection), @{ $self->{preset_choosers}{$group} };
|
||||
$presets{$group} = [ @{$self->presets->{$group}}[@i] ];
|
||||
$presets{$group} = [];
|
||||
foreach my $choice (@{$self->{preset_choosers}{$group}}) {
|
||||
my $sel = $choice->GetSelection;
|
||||
$sel = 0 if $sel == -1;
|
||||
push @{ $presets{$group} },
|
||||
grep { $_->name eq $self->{preset_choosers_names}{$choice}[$sel] }
|
||||
@{wxTheApp->presets->{$group}};
|
||||
}
|
||||
}
|
||||
return $group ? @{$presets{$group}} : %presets;
|
||||
}
|
||||
@ -697,7 +743,7 @@ sub show_preset_editor {
|
||||
|
||||
# Select the preset that was last selected in the editor.
|
||||
$self->select_preset_by_name
|
||||
($dlg->preset_editor->get_current_preset->name, $group, $i, 1);
|
||||
($dlg->preset_editor->current_preset->name, $group, $i, 1);
|
||||
}
|
||||
|
||||
# Returns the current config by merging the selected presets and the overrides.
|
||||
@ -715,16 +761,13 @@ sub config {
|
||||
qw(print filament printer);
|
||||
|
||||
my %presets = $self->selected_presets;
|
||||
$config->apply($_->config([ $classes{printer}->options ]))
|
||||
for @{ $presets{printer} };
|
||||
$config->apply($_->dirty_config) for @{ $presets{printer} };
|
||||
if (@{ $presets{filament} }) {
|
||||
my @opt_keys = ($classes{filament}->options, $classes{filament}->overriding_options);
|
||||
my $filament_config = shift(@{ $presets{filament} })
|
||||
->config(\@opt_keys);
|
||||
my $filament_config = $presets{filament}[0]->dirty_config;
|
||||
|
||||
my $i = 1;
|
||||
for my $preset (@{ $presets{filament} }) {
|
||||
my $config = $preset->config(\@opt_keys);
|
||||
for my $i (1..$#{ $presets{filament} }) {
|
||||
my $preset = $presets{filament}[$i];
|
||||
my $config = $preset->dirty_config;
|
||||
foreach my $opt_key (@{$config->get_keys}) {
|
||||
if ($filament_config->has($opt_key)) {
|
||||
my $value = $filament_config->get($opt_key);
|
||||
@ -733,14 +776,11 @@ sub config {
|
||||
$filament_config->set($opt_key, $value);
|
||||
}
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
|
||||
$config->apply($filament_config);
|
||||
}
|
||||
$config->apply($_->config([ $classes{print}->options ]))
|
||||
for @{ $presets{print} };
|
||||
|
||||
$config->apply($_->dirty_config) for @{ $presets{print} };
|
||||
$config->apply($self->{settings_override_config});
|
||||
|
||||
return $config;
|
||||
@ -1896,7 +1936,7 @@ sub on_extruders_change {
|
||||
EVT_COMBOBOX($choice, $choice, sub {
|
||||
my ($choice) = @_;
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_on_select_preset('filament');
|
||||
$self->_on_change_combobox('filament', $choice);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -71,7 +71,7 @@ sub new {
|
||||
EVT_MENU($menu, $id, sub {
|
||||
$self->{config}->set($opt_key, $self->{default_config}->get($opt_key));
|
||||
$self->update_optgroup;
|
||||
$self->{on_change}->() if $self->{on_change};
|
||||
$self->{on_change}->($opt_key) if $self->{on_change};
|
||||
});
|
||||
}
|
||||
$self->PopupMenu($menu, $btn->GetPosition);
|
||||
@ -155,7 +155,10 @@ sub update_optgroup {
|
||||
label_font => $Slic3r::GUI::small_font,
|
||||
sidetext_font => $Slic3r::GUI::small_font,
|
||||
label_width => 120,
|
||||
on_change => sub { $self->{on_change}->() if $self->{on_change} },
|
||||
on_change => sub {
|
||||
my ($opt_key) = @_;
|
||||
$self->{on_change}->($opt_key) if $self->{on_change};
|
||||
},
|
||||
extra_column => sub {
|
||||
my ($line) = @_;
|
||||
|
||||
@ -169,7 +172,7 @@ sub update_optgroup {
|
||||
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
|
||||
EVT_BUTTON($self, $btn, sub {
|
||||
$self->{config}->erase($opt_key);
|
||||
$self->{on_change}->() if $self->{on_change};
|
||||
$self->{on_change}->($opt_key) if $self->{on_change};
|
||||
wxTheApp->CallAfter(sub { $self->update_optgroup });
|
||||
});
|
||||
return $btn;
|
||||
|
@ -1,11 +1,16 @@
|
||||
package Slic3r::GUI::Preset;
|
||||
use Moo;
|
||||
use Unicode::Normalize;
|
||||
|
||||
use Unicode::Normalize;
|
||||
use Wx qw(:dialog :icon :id wxTheApp);
|
||||
|
||||
has 'group' => (is => 'ro', required => 1);
|
||||
has 'default' => (is => 'ro', default => sub { 0 });
|
||||
has 'external' => (is => 'ro', default => sub { 0 });
|
||||
has 'name' => (is => 'rw', required => 1);
|
||||
has 'file' => (is => 'rw');
|
||||
has '_config' => (is => 'rw', default => sub { Slic3r::Config->new });
|
||||
has '_dirty_config' => (is => 'ro', default => sub { Slic3r::Config->new });
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
@ -13,40 +18,193 @@ sub BUILD {
|
||||
$self->name(Unicode::Normalize::NFC($self->name));
|
||||
}
|
||||
|
||||
sub config {
|
||||
my ($self, $keys, $extra_keys) = @_;
|
||||
sub _loaded {
|
||||
my ($self) = @_;
|
||||
|
||||
if ($self->default) {
|
||||
return Slic3r::Config->new_from_defaults(@$keys);
|
||||
} else {
|
||||
if (!-e Slic3r::encode_path($self->file)) {
|
||||
Slic3r::GUI::show_error(undef, "The selected preset does not exist anymore (" . $self->file . ").");
|
||||
return undef;
|
||||
}
|
||||
my $external_config = $self->load_config;
|
||||
if (!$keys) {
|
||||
return $external_config;
|
||||
} else {
|
||||
# apply preset values on top of defaults
|
||||
my $config = Slic3r::Config->new_from_defaults(@$keys);
|
||||
$config->set($_, $external_config->get($_))
|
||||
for grep $external_config->has($_), @$keys;
|
||||
|
||||
# For extra_keys we don't populate defaults.
|
||||
if ($extra_keys && !$self->external) {
|
||||
$config->set($_, $external_config->get($_))
|
||||
for grep $external_config->has($_), @$extra_keys;
|
||||
}
|
||||
return !$self->_config->empty;
|
||||
}
|
||||
|
||||
sub dirty_options {
|
||||
my ($self) = @_;
|
||||
|
||||
my @dirty = ();
|
||||
|
||||
# Options present in both configs with different values:
|
||||
push @dirty, @{$self->_config->diff($self->_dirty_config)};
|
||||
|
||||
# Overrides added to the dirty config:
|
||||
my @extra = $self->_group_class->overriding_options;
|
||||
push @dirty, grep { !$self->_config->has($_) && $self->_dirty_config->has($_) } @extra;
|
||||
# Overrides removed from the dirty config:
|
||||
push @dirty, grep { $self->_config->has($_) && !$self->_dirty_config->has($_) } @extra;
|
||||
|
||||
return @dirty;
|
||||
}
|
||||
|
||||
sub dirty {
|
||||
my ($self) = @_;
|
||||
|
||||
return !!$self->dirty_options;
|
||||
}
|
||||
|
||||
sub dropdown_name {
|
||||
my ($self) = @_;
|
||||
|
||||
my $name = $self->name;
|
||||
$name .= " (modified)" if $self->dirty;
|
||||
return $name;
|
||||
}
|
||||
|
||||
sub file_exists {
|
||||
my ($self) = @_;
|
||||
|
||||
die "Can't call file_exists() on a non-file preset" if !$self->file;
|
||||
return -e Slic3r::encode_path($self->file);
|
||||
}
|
||||
|
||||
sub rename {
|
||||
my ($self, $name) = @_;
|
||||
|
||||
$self->name($name);
|
||||
$self->file(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $self->group, $name);
|
||||
}
|
||||
|
||||
sub prompt_unsaved_changes {
|
||||
my ($self, $parent) = @_;
|
||||
|
||||
if ($self->dirty) {
|
||||
my $name = $self->default ? 'Default preset' : "Preset \"" . $self->name . "\"";
|
||||
|
||||
return $config;
|
||||
my $opts = '';
|
||||
foreach my $opt_key ($self->dirty_options) {
|
||||
my $opt = $Slic3r::Config::Options->{$opt_key};
|
||||
my $name = $opt->{full_label} // $opt->{label};
|
||||
if ($opt->{category}) {
|
||||
$name = $opt->{category} . " > $name";
|
||||
}
|
||||
$opts .= "- $name\n";
|
||||
}
|
||||
|
||||
my $msg = sprintf "%s has unsaved changes:\n%s\nDo you want to save them?", $name, $opts;
|
||||
my $confirm = Wx::MessageDialog->new($parent, $msg,
|
||||
'Unsaved Changes', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION);
|
||||
$confirm->SetYesNoCancelLabels('Save', 'Discard', 'Cancel');
|
||||
my $res = $confirm->ShowModal;
|
||||
|
||||
if ($res == wxID_CANCEL) {
|
||||
return 0;
|
||||
} elsif ($res == wxID_YES) {
|
||||
return $self->save($self->default ? undef : $self->name);
|
||||
} elsif ($res == wxID_NO) {
|
||||
$self->dismiss_changes;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub save {
|
||||
my ($self, $name, $parent) = @_;
|
||||
|
||||
if (!$name) {
|
||||
my $default_name = $self->default ? 'Untitled' : $self->name;
|
||||
$default_name =~ s/\.ini$//i;
|
||||
|
||||
my $dlg = Slic3r::GUI::SavePresetWindow->new($parent,
|
||||
default => $default_name,
|
||||
values => [ map $_->name, grep !$_->default && !$_->external, @{wxTheApp->presets->{$self->name}} ],
|
||||
);
|
||||
return 0 unless $dlg->ShowModal == wxID_OK;
|
||||
$name = $dlg->get_name;
|
||||
}
|
||||
|
||||
$self->rename($name);
|
||||
|
||||
if (!$self->file) {
|
||||
die "Calling save() without setting filename";
|
||||
}
|
||||
|
||||
$self->_config->clear;
|
||||
$self->_config->apply($self->_dirty_config);
|
||||
$self->_config->save($self->file);
|
||||
wxTheApp->load_presets;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub dismiss_changes {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_dirty_config->clear;
|
||||
$self->_dirty_config->apply($self->_config);
|
||||
}
|
||||
|
||||
sub delete {
|
||||
my ($self) = @_;
|
||||
|
||||
die "Default config can't be deleted" if $self->default;
|
||||
die "External configs can't be deleted" if $self->external;
|
||||
|
||||
# Otherwise wxTheApp->load_presets() will keep it
|
||||
$self->dismiss_changes;
|
||||
|
||||
if ($self->file) {
|
||||
unlink Slic3r::encode_path($self->file) if $self->file_exists;
|
||||
$self->file(undef);
|
||||
}
|
||||
}
|
||||
|
||||
# This returns the loaded config with the dirty options applied.
|
||||
sub dirty_config {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->load_config if !$self->_loaded;
|
||||
|
||||
return $self->_dirty_config->clone;
|
||||
}
|
||||
|
||||
sub load_config {
|
||||
my ($self) = @_;
|
||||
|
||||
return Slic3r::Config->load($self->file);
|
||||
my @keys = $self->_group_class->options;
|
||||
my @extra_keys = $self->_group_class->overriding_options;
|
||||
|
||||
if ($self->default) {
|
||||
$self->_config(Slic3r::Config->new_from_defaults(@keys));
|
||||
} elsif ($self->file) {
|
||||
if (!$self->file_exists) {
|
||||
Slic3r::GUI::show_error(undef, "The selected preset does not exist anymore (" . $self->file . ").");
|
||||
return undef;
|
||||
}
|
||||
my $external_config = Slic3r::Config->load($self->file);
|
||||
if (!@keys) {
|
||||
$self->_config($external_config);
|
||||
} else {
|
||||
# apply preset values on top of defaults
|
||||
my $config = Slic3r::Config->new_from_defaults(@keys);
|
||||
$config->set($_, $external_config->get($_))
|
||||
for grep $external_config->has($_), @keys;
|
||||
|
||||
# For extra_keys we don't populate defaults.
|
||||
if (@extra_keys && !$self->external) {
|
||||
$config->set($_, $external_config->get($_))
|
||||
for grep $external_config->has($_), @extra_keys;
|
||||
}
|
||||
|
||||
$self->_config($config);
|
||||
}
|
||||
}
|
||||
|
||||
$self->_dirty_config->apply($self->_config);
|
||||
|
||||
return $self->_config;
|
||||
}
|
||||
|
||||
sub _group_class {
|
||||
my ($self) = @_;
|
||||
|
||||
return "Slic3r::GUI::PresetEditor::".ucfirst $self->group;
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -10,13 +10,15 @@ use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :treectrl :wi
|
||||
use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN EVT_TREE_SEL_CHANGED);
|
||||
use base qw(Wx::Panel Class::Accessor);
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(current_preset));
|
||||
__PACKAGE__->mk_accessors(qw(current_preset config));
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, %params) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
|
||||
|
||||
$self->{presets} = wxTheApp->presets->{$self->name};
|
||||
|
||||
# horizontal sizer
|
||||
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
#$self->{sizer}->SetSizeHints($self);
|
||||
@ -91,78 +93,39 @@ sub new {
|
||||
EVT_BUTTON($self, $self->{btn_save_preset}, sub { $self->save_preset });
|
||||
|
||||
EVT_BUTTON($self, $self->{btn_delete_preset}, sub {
|
||||
my $i = $self->current_preset;
|
||||
return if $i == 0; # this shouldn't happen but let's trap it anyway
|
||||
my $res = Wx::MessageDialog->new($self, "Are you sure you want to delete the selected preset?", 'Delete Preset', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal;
|
||||
return unless $res == wxID_YES;
|
||||
if (-e $self->{presets}[$i]->file) {
|
||||
unlink $self->{presets}[$i]->file;
|
||||
}
|
||||
splice @{$self->{presets}}, $i, 1;
|
||||
$self->{presets_choice}->Delete($i);
|
||||
|
||||
$self->current_preset->delete;
|
||||
$self->current_preset(undef);
|
||||
$self->select_preset(0);
|
||||
wxTheApp->load_presets;
|
||||
$self->load_presets;
|
||||
$self->select_preset(0, 1);
|
||||
});
|
||||
|
||||
# C++ instance DynamicPrintConfig
|
||||
$self->{config} = Slic3r::Config->new;
|
||||
# Initialize the DynamicPrintConfig by default keys/values.
|
||||
$self->{config}->apply(Slic3r::Config->new_from_defaults($self->options));
|
||||
$self->config(Slic3r::Config->new_from_defaults($self->options));
|
||||
|
||||
|
||||
|
||||
# Possible %params keys: no_controller
|
||||
$self->build(%params);
|
||||
$self->build;
|
||||
$self->update_tree;
|
||||
$self->load_presets;
|
||||
$self->_update;
|
||||
if ($self->hidden_options) {
|
||||
$self->{config}->apply(Slic3r::Config->new_from_defaults($self->hidden_options));
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_current_preset {
|
||||
my $self = shift;
|
||||
return $self->get_preset($self->current_preset);
|
||||
}
|
||||
|
||||
sub get_current_preset_config {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->get_preset_config($self->get_current_preset);
|
||||
}
|
||||
|
||||
sub get_preset {
|
||||
my ($self, $i) = @_;
|
||||
return $self->{presets}[$i];
|
||||
}
|
||||
|
||||
# This is called by the save button.
|
||||
sub save_preset {
|
||||
my ($self, $name) = @_;
|
||||
my ($self) = @_;
|
||||
|
||||
# since buttons (and choices too) don't get focus on Mac, we set focus manually
|
||||
# to the treectrl so that the EVT_* events are fired for the input field having
|
||||
# focus currently. is there anything better than this?
|
||||
$self->{treectrl}->SetFocus;
|
||||
|
||||
if (!defined $name) {
|
||||
my $preset = $self->get_current_preset;
|
||||
my $default_name = $preset->default ? 'Untitled' : $preset->name;
|
||||
$default_name =~ s/\.ini$//i;
|
||||
|
||||
my $dlg = Slic3r::GUI::SavePresetWindow->new($self,
|
||||
title => lc($self->title),
|
||||
default => $default_name,
|
||||
values => [ map $_->name, grep !$_->default && !$_->external, @{$self->{presets}} ],
|
||||
);
|
||||
return 0 unless $dlg->ShowModal == wxID_OK;
|
||||
$name = $dlg->get_name;
|
||||
}
|
||||
|
||||
$self->config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $self->name, $name);
|
||||
my $preset = $self->current_preset;
|
||||
$preset->save(undef, $self);
|
||||
$self->load_presets;
|
||||
$self->select_preset_by_name($name);
|
||||
$self->select_preset_by_name($preset->name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -176,17 +139,17 @@ sub on_value_change {
|
||||
# or changed by user (so also when a preset is loaded).
|
||||
# propagate event to the parent
|
||||
sub _on_value_change {
|
||||
my $self = shift;
|
||||
my ($self, $opt_key) = @_;
|
||||
|
||||
$self->{on_value_change}->(@_) if $self->{on_value_change};
|
||||
$self->current_preset->_dirty_config->apply($self->config);
|
||||
$self->{on_value_change}->($opt_key) if $self->{on_value_change};
|
||||
$self->load_presets;
|
||||
$self->_update;
|
||||
}
|
||||
|
||||
sub _update {}
|
||||
|
||||
sub on_preset_loaded {}
|
||||
sub hidden_options {}
|
||||
sub config { $_[0]->{config}->clone }
|
||||
|
||||
sub select_preset {
|
||||
my ($self, $i, $force) = @_;
|
||||
@ -198,62 +161,54 @@ sub select_preset {
|
||||
sub select_preset_by_name {
|
||||
my ($self, $name, $force) = @_;
|
||||
|
||||
my $i = first { $self->{presets}[$_]->name eq $name } 0 .. $#{$self->{presets}};
|
||||
return if !defined $i;
|
||||
$self->select_preset($i, $force);
|
||||
my $presets = wxTheApp->presets->{$self->name};
|
||||
my $i = first { $presets->[$_]->name eq $name } 0..$#$presets;
|
||||
$self->{presets_choice}->SetSelection($i);
|
||||
$self->on_select_preset($force);
|
||||
}
|
||||
|
||||
sub prompt_unsaved_changes {
|
||||
my ($self) = @_;
|
||||
|
||||
if ($self->is_dirty) {
|
||||
my $old_preset = $self->get_current_preset;
|
||||
my $name = $old_preset->default ? 'Default preset' : "Preset \"" . $old_preset->name . "\"";
|
||||
|
||||
my @option_names = ();
|
||||
foreach my $opt_key (@{$self->dirty_options}) {
|
||||
my $opt = $Slic3r::Config::Options->{$opt_key};
|
||||
my $name = $opt->{full_label} // $opt->{label};
|
||||
if ($opt->{category}) {
|
||||
$name = $opt->{category} . " > $name";
|
||||
}
|
||||
push @option_names, $name;
|
||||
}
|
||||
|
||||
my $changes = join "\n", map "- $_", @option_names;
|
||||
my $confirm = Wx::MessageDialog->new($self, "$name has unsaved changes:\n$changes\n\nDo you want to save them?",
|
||||
'Unsaved Changes', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION);
|
||||
$confirm->SetYesNoCancelLabels('Save', 'Discard', 'Cancel');
|
||||
my $res = $confirm->ShowModal;
|
||||
|
||||
if ($res == wxID_CANCEL) {
|
||||
return 0;
|
||||
} elsif ($res == wxID_YES) {
|
||||
return $self->save_preset($old_preset->default ? undef : $old_preset->name);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 1 if !$self->current_preset;
|
||||
return $self->current_preset->prompt_unsaved_changes($self);
|
||||
}
|
||||
|
||||
sub on_select_preset {
|
||||
my ($self, $force) = @_;
|
||||
|
||||
my $i = $self->{presets_choice}->GetSelection;
|
||||
return if defined $self->current_preset && $i == $self->current_preset;
|
||||
# This method is called:
|
||||
# - upon first initialization;
|
||||
# - whenever user selects a preset from the dropdown;
|
||||
# - whenever select_preset() or select_preset_by_name() are called.
|
||||
|
||||
if (!$force && defined $self->current_preset && !$self->prompt_unsaved_changes) {
|
||||
$self->{presets_choice}->SetSelection($self->current_preset);
|
||||
# Get the selected name.
|
||||
my $preset = wxTheApp->presets->{$self->name}->[$self->{presets_choice}->GetSelection];
|
||||
|
||||
# If selection didn't change, do nothing.
|
||||
return if defined $self->current_preset && $preset->name eq $self->current_preset->name;
|
||||
|
||||
# If we have unsaved changes, prompt user.
|
||||
if (!$force && !$self->prompt_unsaved_changes) {
|
||||
# User decided not to save the current changes, so we restore the previous selection.
|
||||
my $presets = wxTheApp->presets->{$self->name};
|
||||
my $i = first { $presets->[$_]->name eq $self->current_preset->name } 0..$#$presets;
|
||||
$self->{presets_choice}->SetSelection($i);
|
||||
return;
|
||||
}
|
||||
|
||||
$self->current_preset($i);
|
||||
my $preset = $self->get_current_preset;
|
||||
my $preset_config = $self->get_preset_config($preset);
|
||||
$self->current_preset($preset);
|
||||
|
||||
# We reload presets in order to remove the "(modified)" suffix in case user was
|
||||
# prompted and chose to discard changes.
|
||||
$self->load_presets;
|
||||
|
||||
$preset->load_config if !$preset->_loaded;
|
||||
$self->config->clear;
|
||||
$self->config->apply($preset->dirty_config);
|
||||
|
||||
eval {
|
||||
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
|
||||
$self->{config}->clear;
|
||||
$self->{config}->apply($preset_config);
|
||||
($preset->default || $preset->external)
|
||||
? $self->{btn_delete_preset}->Disable
|
||||
: $self->{btn_delete_preset}->Enable;
|
||||
@ -266,14 +221,6 @@ sub on_select_preset {
|
||||
$@ = "I was unable to load the selected config file: $@";
|
||||
Slic3r::GUI::catch_error($self);
|
||||
}
|
||||
|
||||
# use CallAfter because some field triggers schedule on_change calls using CallAfter,
|
||||
# and we don't want them to be called after this update_dirty() as they would mark the
|
||||
# preset dirty again
|
||||
# (not sure this is true anymore now that update_dirty is idempotent)
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->update_dirty;
|
||||
});
|
||||
}
|
||||
|
||||
sub add_options_page {
|
||||
@ -323,89 +270,35 @@ sub update_tree {
|
||||
}
|
||||
}
|
||||
|
||||
sub update_dirty {
|
||||
my $self = shift;
|
||||
|
||||
foreach my $i (0..$#{$self->{presets}}) {
|
||||
my $preset = $self->get_preset($i);
|
||||
if ($i == $self->current_preset && $self->is_dirty) {
|
||||
$self->{presets_choice}->SetString($i, $preset->name . " (modified)");
|
||||
} else {
|
||||
$self->{presets_choice}->SetString($i, $preset->name);
|
||||
}
|
||||
}
|
||||
$self->{presets_choice}->SetSelection($self->current_preset); # http://trac.wxwidgets.org/ticket/13769
|
||||
}
|
||||
|
||||
sub is_dirty {
|
||||
my $self = shift;
|
||||
return @{$self->dirty_options} > 0;
|
||||
}
|
||||
|
||||
sub dirty_options {
|
||||
my $self = shift;
|
||||
|
||||
return [] if !defined $self->current_preset; # happens during initialization
|
||||
|
||||
my $preset_config = $self->get_current_preset_config;
|
||||
return [
|
||||
(grep !$preset_config->has($_), @{$self->{config}->get_keys}),
|
||||
(grep !$self->{config}->has($_), @{$preset_config->get_keys}),
|
||||
@{ $self->{config}->diff($self->get_current_preset_config) },
|
||||
];
|
||||
}
|
||||
|
||||
sub load_presets {
|
||||
my $self = shift;
|
||||
|
||||
$self->{presets} = [ wxTheApp->presets($self->name, 1) ];
|
||||
$self->current_preset(undef);
|
||||
my $presets = wxTheApp->presets->{$self->name};
|
||||
$self->{presets_choice}->Clear;
|
||||
$self->{presets_choice}->Append($_->name) for @{$self->{presets}};
|
||||
$self->select_preset(0);
|
||||
}
|
||||
|
||||
sub load_config_file {
|
||||
my $self = shift;
|
||||
my ($file) = @_;
|
||||
|
||||
# look for the loaded config among the existing menu items
|
||||
my $i = first { $self->{presets}[$_]{file} eq $file && $self->{presets}[$_]{external} } 1..$#{$self->{presets}};
|
||||
if (!$i) {
|
||||
my $preset_name = basename($file); # keep the .ini suffix
|
||||
push @{$self->{presets}}, Slic3r::GUI::Preset->new(
|
||||
file => $file,
|
||||
name => $preset_name,
|
||||
external => 1,
|
||||
);
|
||||
$self->{presets_choice}->Append($preset_name);
|
||||
$i = $#{$self->{presets}};
|
||||
foreach my $preset (@$presets) {
|
||||
$self->{presets_choice}->Append($preset->dropdown_name);
|
||||
|
||||
# Preserve selection.
|
||||
if ($self->current_preset && $self->current_preset->name eq $preset->name) {
|
||||
$self->{presets_choice}->SetSelection($self->{presets_choice}->GetCount-1);
|
||||
}
|
||||
}
|
||||
$self->{presets_choice}->SetSelection($i);
|
||||
$self->on_select_preset;
|
||||
}
|
||||
|
||||
sub load_config {
|
||||
# This is called internally whenever we make automatic adjustments to configuration
|
||||
# based on user actions.
|
||||
sub _load_config {
|
||||
my $self = shift;
|
||||
my ($config) = @_;
|
||||
|
||||
foreach my $opt_key (@{$self->{config}->diff($config)}) {
|
||||
$self->{config}->set($opt_key, $config->get($opt_key));
|
||||
$self->update_dirty;
|
||||
}
|
||||
my $diff = $self->config->diff($config);
|
||||
$self->config->set($_, $config->get($_)) for @$diff;
|
||||
# First apply all changes, then call all the _on_value_change triggers.
|
||||
$self->_on_value_change($_) for @$diff;
|
||||
$self->reload_config;
|
||||
$self->_update;
|
||||
}
|
||||
|
||||
sub get_preset_config {
|
||||
my ($self, $preset) = @_;
|
||||
|
||||
return $preset->config(
|
||||
[ $self->options ],
|
||||
[ $self->overriding_options ],
|
||||
);
|
||||
}
|
||||
|
||||
sub get_field {
|
||||
my ($self, $opt_key, $opt_index) = @_;
|
||||
|
||||
@ -452,16 +345,15 @@ sub _compatible_printers_widget {
|
||||
"Compatible printers", \@presets);
|
||||
|
||||
my @selections = ();
|
||||
foreach my $preset_name (@{ $self->{config}->get('compatible_printers') }) {
|
||||
foreach my $preset_name (@{ $self->config->get('compatible_printers') }) {
|
||||
push @selections, first { $presets[$_] eq $preset_name } 0..$#presets;
|
||||
}
|
||||
$dlg->SetSelections(@selections);
|
||||
|
||||
if ($dlg->ShowModal == wxID_OK) {
|
||||
my $value = [ @presets[$dlg->GetSelections] ];
|
||||
$self->{config}->set('compatible_printers', $value);
|
||||
$self->update_dirty;
|
||||
$self->_on_value_change('compatible_printers', $value);
|
||||
$self->config->set('compatible_printers', $value);
|
||||
$self->_on_value_change('compatible_printers');
|
||||
}
|
||||
});
|
||||
|
||||
@ -554,16 +446,15 @@ sub build {
|
||||
[ map $options{$_}, @opt_keys ]);
|
||||
|
||||
my @selections = ();
|
||||
foreach my $opt_key (@{ $self->{config}->get('overridable') }) {
|
||||
foreach my $opt_key (@{ $self->config->get('overridable') }) {
|
||||
push @selections, first { $opt_keys[$_] eq $opt_key } 0..$#opt_keys;
|
||||
}
|
||||
$dlg->SetSelections(@selections);
|
||||
|
||||
if ($dlg->ShowModal == wxID_OK) {
|
||||
my $value = [ @opt_keys[$dlg->GetSelections] ];
|
||||
$self->{config}->set('overridable', $value);
|
||||
$self->update_dirty;
|
||||
$self->_on_value_change('overridable', $value);
|
||||
$self->config->set('overridable', $value);
|
||||
$self->_on_value_change('overridable');
|
||||
}
|
||||
});
|
||||
|
||||
@ -850,11 +741,11 @@ sub _update {
|
||||
$new_conf->set("top_solid_layers", 0);
|
||||
$new_conf->set("fill_density", 0);
|
||||
$new_conf->set("support_material", 0);
|
||||
$self->load_config($new_conf);
|
||||
$self->_load_config($new_conf);
|
||||
} else {
|
||||
my $new_conf = Slic3r::Config->new;
|
||||
$new_conf->set("spiral_vase", 0);
|
||||
$self->load_config($new_conf);
|
||||
$self->_load_config($new_conf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -880,7 +771,7 @@ sub _update {
|
||||
$new_conf->set("support_material", 0);
|
||||
$self->{support_material_overhangs_queried} = 0;
|
||||
}
|
||||
$self->load_config($new_conf);
|
||||
$self->_load_config($new_conf);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -900,7 +791,7 @@ sub _update {
|
||||
} else {
|
||||
$new_conf->set("fill_density", 40);
|
||||
}
|
||||
$self->load_config($new_conf);
|
||||
$self->_load_config($new_conf);
|
||||
}
|
||||
|
||||
my $have_perimeters = $config->perimeters > 0;
|
||||
@ -975,8 +866,6 @@ sub _update {
|
||||
for qw(standby_temperature_delta);
|
||||
}
|
||||
|
||||
sub hidden_options { !$Slic3r::have_threads ? qw(threads) : () }
|
||||
|
||||
package Slic3r::GUI::PresetEditor::Filament;
|
||||
use base 'Slic3r::GUI::PresetEditor';
|
||||
|
||||
@ -1150,9 +1039,11 @@ sub build {
|
||||
$self->{overrides_panel} = my $panel = Slic3r::GUI::Plater::OverrideSettingsPanel->new($parent,
|
||||
size => [-1, 300],
|
||||
on_change => sub {
|
||||
$self->{config}->erase($_) for @overridable;
|
||||
$self->{config}->apply($self->{overrides_config});
|
||||
$self->update_dirty;
|
||||
my ($opt_key) = @_;
|
||||
$self->config->erase($_) for @overridable;
|
||||
$self->current_preset->_dirty_config->erase($_) for @overridable;
|
||||
$self->config->apply($self->{overrides_config});
|
||||
$self->_on_value_change($opt_key);
|
||||
});
|
||||
$panel->set_editable(1);
|
||||
$panel->set_default_config($self->{overrides_default_config});
|
||||
@ -1172,8 +1063,8 @@ sub reload_config {
|
||||
|
||||
$self->{overrides_config}->clear;
|
||||
foreach my $opt_key (@{$self->{overrides_default_config}->get_keys}) {
|
||||
if ($self->{config}->has($opt_key)) {
|
||||
$self->{overrides_config}->set($opt_key, $self->{config}->get($opt_key));
|
||||
if ($self->config->has($opt_key)) {
|
||||
$self->{overrides_config}->set($opt_key, $self->config->get($opt_key));
|
||||
}
|
||||
}
|
||||
$self->{overrides_panel}->update_optgroup;
|
||||
@ -1186,10 +1077,10 @@ sub _update {
|
||||
|
||||
$self->_update_description;
|
||||
|
||||
my $cooling = $self->{config}->cooling;
|
||||
my $cooling = $self->config->cooling;
|
||||
$self->get_field($_)->toggle($cooling)
|
||||
for qw(max_fan_speed fan_below_layer_time slowdown_below_layer_time min_print_speed);
|
||||
$self->get_field($_)->toggle($cooling || $self->{config}->fan_always_on)
|
||||
$self->get_field($_)->toggle($cooling || $self->config->fan_always_on)
|
||||
for qw(min_fan_speed disable_fan_first_layers);
|
||||
}
|
||||
|
||||
@ -1269,12 +1160,11 @@ sub build {
|
||||
label => 'Bed shape',
|
||||
);
|
||||
$line->append_button("Set…", "cog.png", sub {
|
||||
my $dlg = Slic3r::GUI::BedShapeDialog->new($self, $self->{config}->bed_shape);
|
||||
my $dlg = Slic3r::GUI::BedShapeDialog->new($self, $self->config->bed_shape);
|
||||
if ($dlg->ShowModal == wxID_OK) {
|
||||
my $value = $dlg->GetValue;
|
||||
$self->{config}->set('bed_shape', $value);
|
||||
$self->update_dirty;
|
||||
$self->_on_value_change('bed_shape', $value);
|
||||
$self->config->set('bed_shape', $value);
|
||||
$self->_on_value_change('bed_shape');
|
||||
}
|
||||
});
|
||||
$optgroup->append_line($line);
|
||||
@ -1301,7 +1191,6 @@ sub build {
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_extruders_count_changed($optgroup->get_value('extruders_count'));
|
||||
});
|
||||
$self->update_dirty;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1328,8 +1217,8 @@ sub build {
|
||||
$line->append_button("Test", "wrench.png", sub {
|
||||
my $sender = Slic3r::GCode::Sender->new;
|
||||
my $res = $sender->connect(
|
||||
$self->{config}->serial_port,
|
||||
$self->{config}->serial_speed,
|
||||
$self->config->serial_port,
|
||||
$self->config->serial_speed,
|
||||
);
|
||||
if ($res && $sender->wait_connected) {
|
||||
Slic3r::GUI::show_info($self, "Connection to printer works correctly.", "Success!");
|
||||
@ -1355,10 +1244,8 @@ sub build {
|
||||
my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries);
|
||||
if ($dlg->ShowModal == wxID_OK) {
|
||||
my $value = $dlg->GetValue . ":" . $dlg->GetPort;
|
||||
$self->{config}->set('octoprint_host', $value);
|
||||
$self->update_dirty;
|
||||
$self->_on_value_change('octoprint_host', $value);
|
||||
$self->reload_config;
|
||||
$self->config->set('octoprint_host', $value);
|
||||
$self->_on_value_change('octoprint_host');
|
||||
}
|
||||
} else {
|
||||
Wx::MessageDialog->new($self, 'No Bonjour device found', 'Device Browser', wxOK | wxICON_INFORMATION)->ShowModal;
|
||||
@ -1369,8 +1256,8 @@ sub build {
|
||||
$ua->timeout(10);
|
||||
|
||||
my $res = $ua->get(
|
||||
"http://" . $self->{config}->octoprint_host . "/api/version",
|
||||
'X-Api-Key' => $self->{config}->octoprint_apikey,
|
||||
"http://" . $self->config->octoprint_host . "/api/version",
|
||||
'X-Api-Key' => $self->config->octoprint_apikey,
|
||||
);
|
||||
if ($res->is_success) {
|
||||
Slic3r::GUI::show_info($self, "Connection to OctoPrint works correctly.", "Success!");
|
||||
@ -1462,7 +1349,7 @@ sub _extruders_count_changed {
|
||||
|
||||
$self->{extruders_count} = $extruders_count;
|
||||
$self->_build_extruder_pages;
|
||||
$self->_on_value_change('extruders_count', $extruders_count);
|
||||
$self->_on_value_change('extruders_count');
|
||||
$self->_update;
|
||||
}
|
||||
|
||||
@ -1477,7 +1364,7 @@ sub _build_extruder_pages {
|
||||
foreach my $extruder_idx (@{$self->{extruder_pages}} .. $self->{extruders_count}-1) {
|
||||
# extend options
|
||||
foreach my $opt_key ($self->_extruder_options) {
|
||||
my $values = $self->{config}->get($opt_key);
|
||||
my $values = $self->config->get($opt_key);
|
||||
if (!defined $values) {
|
||||
$values = [ $default_config->get_at($opt_key, 0) ];
|
||||
} else {
|
||||
@ -1485,7 +1372,7 @@ sub _build_extruder_pages {
|
||||
my $last_value = $values->[-1];
|
||||
$values->[$extruder_idx] //= $last_value;
|
||||
}
|
||||
$self->{config}->set($opt_key, $values)
|
||||
$self->config->set($opt_key, $values)
|
||||
or die "Unable to extend $opt_key";
|
||||
}
|
||||
|
||||
@ -1531,9 +1418,9 @@ sub _build_extruder_pages {
|
||||
|
||||
# remove extra config values
|
||||
foreach my $opt_key ($self->_extruder_options) {
|
||||
my $values = $self->{config}->get($opt_key);
|
||||
my $values = $self->config->get($opt_key);
|
||||
splice @$values, $self->{extruders_count} if $self->{extruders_count} <= $#$values;
|
||||
$self->{config}->set($opt_key, $values)
|
||||
$self->config->set($opt_key, $values)
|
||||
or die "Unable to truncate $opt_key";
|
||||
}
|
||||
|
||||
@ -1610,7 +1497,7 @@ sub _update {
|
||||
} else {
|
||||
$new_conf->set("use_firmware_retraction", 0);
|
||||
}
|
||||
$self->load_config($new_conf);
|
||||
$self->_load_config($new_conf);
|
||||
}
|
||||
|
||||
$self->get_field('retract_length_toolchange', $i)->toggle($have_multiple_extruders);
|
||||
@ -1628,7 +1515,7 @@ sub on_preset_loaded {
|
||||
# update the extruders count field
|
||||
{
|
||||
# update the GUI field according to the number of nozzle diameters supplied
|
||||
my $extruders_count = scalar @{ $self->{config}->nozzle_diameter };
|
||||
my $extruders_count = scalar @{ $self->config->nozzle_diameter };
|
||||
$self->set_value('extruders_count', $extruders_count);
|
||||
$self->_extruders_count_changed($extruders_count);
|
||||
}
|
||||
@ -1642,7 +1529,7 @@ sub load_config_file {
|
||||
"Your configuration was imported. However, Slic3r is currently only able to import settings "
|
||||
. "for the first defined filament. We recommend you don't use exported configuration files "
|
||||
. "for multi-extruder setups and rely on the built-in preset management system instead.")
|
||||
if @{ $self->{config}->nozzle_diameter } > 1;
|
||||
if @{ $self->config->nozzle_diameter } > 1;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::PresetEditor::Page;
|
||||
@ -1676,8 +1563,7 @@ sub new_optgroup {
|
||||
on_change => sub {
|
||||
my ($opt_key, $value) = @_;
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->GetParent->update_dirty;
|
||||
$self->GetParent->_on_value_change($opt_key, $value);
|
||||
$self->GetParent->_on_value_change($opt_key);
|
||||
});
|
||||
},
|
||||
);
|
||||
@ -1726,7 +1612,7 @@ sub new {
|
||||
|
||||
my @values = @{$params{values}};
|
||||
|
||||
my $text = Wx::StaticText->new($self, -1, "Save " . lc($params{title}) . " as:", wxDefaultPosition, wxDefaultSize);
|
||||
my $text = Wx::StaticText->new($self, -1, "Save profile as:", wxDefaultPosition, wxDefaultSize);
|
||||
$self->{combo} = Wx::ComboBox->new($self, -1, $params{default}, wxDefaultPosition, wxDefaultSize, \@values,
|
||||
wxTE_PROCESS_ENTER);
|
||||
my $buttons = $self->CreateStdDialogButtonSizer(wxOK | wxCANCEL);
|
||||
|
@ -1,8 +1,8 @@
|
||||
package Slic3r::GUI::PresetEditorDialog;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Wx qw(:dialog :id :misc :sizer :button :icon wxTheApp);
|
||||
use Wx::Event qw(EVT_CLOSE);
|
||||
use Wx qw(:dialog :id :misc :sizer :button :icon wxTheApp WXK_ESCAPE);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_CHAR_HOOK);
|
||||
use base qw(Wx::Dialog Class::Accessor);
|
||||
use utf8;
|
||||
|
||||
@ -15,7 +15,6 @@ sub new {
|
||||
|
||||
$self->preset_editor($self->preset_editor_class->new($self));
|
||||
$self->SetTitle($self->preset_editor->title);
|
||||
$self->preset_editor->load_presets;
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$sizer->Add($self->preset_editor, 1, wxEXPAND);
|
||||
@ -24,23 +23,32 @@ sub new {
|
||||
#$sizer->SetSizeHints($self);
|
||||
|
||||
if (0) {
|
||||
# This does not call the EVT_CLOSE below
|
||||
my $buttons = $self->CreateStdDialogButtonSizer(wxCLOSE);
|
||||
$sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
}
|
||||
|
||||
wxTheApp->restore_window_pos($self, "preset_editor");
|
||||
|
||||
EVT_CLOSE($self, sub {
|
||||
my (undef, $event) = @_;
|
||||
|
||||
if ($event->CanVeto && !$self->preset_editor->prompt_unsaved_changes) {
|
||||
$event->Veto;
|
||||
return;
|
||||
}
|
||||
# save window size
|
||||
wxTheApp->save_window_pos($self, "preset_editor");
|
||||
|
||||
# propagate event
|
||||
$event->Skip;
|
||||
});
|
||||
|
||||
EVT_CHAR_HOOK($self, sub {
|
||||
my (undef, $event) = @_;
|
||||
|
||||
if ($event->GetKeyCode == WXK_ESCAPE) {
|
||||
$self->Close;
|
||||
} else {
|
||||
$event->Skip;
|
||||
}
|
||||
});
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
|
@ -532,6 +532,11 @@ DynamicConfig::clear() {
|
||||
this->options.clear();
|
||||
}
|
||||
|
||||
bool
|
||||
DynamicConfig::empty() const {
|
||||
return this->options.empty();
|
||||
}
|
||||
|
||||
void
|
||||
DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra)
|
||||
{
|
||||
|
@ -678,6 +678,7 @@ class DynamicConfig : public virtual ConfigBase
|
||||
t_config_option_keys keys() const;
|
||||
void erase(const t_config_option_key &opt_key);
|
||||
void clear();
|
||||
bool empty() const;
|
||||
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra);
|
||||
void read_cli(const int argc, const char **argv, t_config_option_keys* extra);
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
||||
%name{get_keys} std::vector<std::string> keys();
|
||||
void erase(t_config_option_key opt_key);
|
||||
void clear();
|
||||
bool empty();
|
||||
void normalize();
|
||||
%name{setenv} void setenv_();
|
||||
double min_object_distance();
|
||||
|
Loading…
x
Reference in New Issue
Block a user