This commit is contained in:
bubnikv 2018-05-03 21:35:10 +02:00
commit 81bfd8ce7e
82 changed files with 4313 additions and 1843 deletions

View File

@ -1,18 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--
This file is autogenerated by CMake
Note: In the .in template file, the $ {}-style variables are interpreted by CMake while the $()-style variables belong to MSVC
-->
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets"> <ImportGroup Label="PropertySheets">
</ImportGroup> </ImportGroup>
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup> <PropertyGroup>
<ExecutablePath>$(VC_ExecutablePath_x64);$(WindowsSDK_ExecutablePath);$(VS_ExecutablePath);$(MSBuild_ExecutablePath);$(FxCopDir);$(PATH);c:\wperl64d\bin\;</ExecutablePath> <ExecutablePath>$(VC_ExecutablePath_x64);$(WindowsSDK_ExecutablePath);$(VS_ExecutablePath);$(MSBuild_ExecutablePath);$(FxCopDir);$(PATH);${PROPS_PERL_BIN_PATH}\;</ExecutablePath>
</PropertyGroup> </PropertyGroup>
<ItemDefinitionGroup /> <ItemDefinitionGroup />
<ItemGroup /> <ItemGroup />
<PropertyGroup> <PropertyGroup>
<LocalDebuggerCommand>C:\wperl64d\bin\perl.exe</LocalDebuggerCommand> <LocalDebuggerCommand>${PROPS_PERL_EXECUTABLE}</LocalDebuggerCommand>
<LocalDebuggerCommandArguments>slic3r.pl</LocalDebuggerCommandArguments> <LocalDebuggerCommandArguments>slic3r.pl</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerWorkingDirectory>..\..</LocalDebuggerWorkingDirectory> <LocalDebuggerWorkingDirectory>${PROPS_CMAKE_SOURCE_DIR}</LocalDebuggerWorkingDirectory>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

52
doc/updating/Updatig.md Normal file
View File

@ -0,0 +1,52 @@
# Slic3r PE 1.40 configuration update
Slic3r PE 1.40.0 comes with a major re-work of the way configuration presets work.
There are three new features:
+ A two-tier system of presets being divided into _System_ and _User_ groups
+ Configuration snapshots
+ Configuration updating from the internet
## System and User presets
- _System preset_: These are the presets that come with Slic3r PE installation. They come from a vendor configuration bundle (not individual files like before). They are **read-only** a user cannot modify them, but may instead create a derived User preset based on a System preset
- _User preset_: These are regular presets stored in files just like before. Additionally, they may be derived (inherited) from one of the System presets
A derived User preset keeps track of wich settings are inherited from the parent System preset and which are modified by the user. When a system preset is updated (either via installation of a new Slic3r or automatically from the internet), in a User preset the settings that are modified by the user will stay that way, while the ones that are inherited reflect the updated System preset.
This system ensures that we don't overwrite user's settings when there is an update to the built in presets.
Slic3r GUI now displays accurately which settings are inherited and which are modified.
A setting derived from a System preset is represeted by green label and a locked lock icon:
![a system setting](setting_sys.png)
A settings modified in a User preset has an open lock icon:
![a user setting](setting_user.png)
Clickign the open lock icon restored the system setting.
Additionaly, any setting that is modified but not yet saved onto disk is represented by orange label and a back-arrow:
![a modified setting](setting_mod.png)
Clicking the back-arrow restores the value that was previously saved in this Preset.
## Configuration snapshots
Configuration snapshots can now be taken via the _Configuration_ menu.
A snapshot contains complete configuration from the point when the snapshot was taken.
Users may move back and forth between snapshots at will using a dialog:
![snapshots dialog](snapshots_dialog.png)
# Updating from the internet
Slic3r PE 1.40.0 checks for updates of the built-in System presets and downloads them.
The first-time configuration assistant will ask you if you want to enable this feature - it is **not** mandatory.
Updates are checked for and downloaded in the background. If there's is an update, Slic3r will prompt about it
next time it is launched, never during normal program operation. An update may be either accepted or refused.
Before any update is applied a configuration snapshot (as described above) is taken.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@ -161,8 +161,13 @@ sub thread_cleanup {
*Slic3r::Print::SupportMaterial2::DESTROY = sub {}; *Slic3r::Print::SupportMaterial2::DESTROY = sub {};
*Slic3r::TriangleMesh::DESTROY = sub {}; *Slic3r::TriangleMesh::DESTROY = sub {};
*Slic3r::GUI::AppConfig::DESTROY = sub {}; *Slic3r::GUI::AppConfig::DESTROY = sub {};
*Slic3r::GUI::GCodePreviewData::DESTROY = sub {};
*Slic3r::GUI::PresetBundle::DESTROY = sub {}; *Slic3r::GUI::PresetBundle::DESTROY = sub {};
*Slic3r::GUI::Tab::DESTROY = sub {}; *Slic3r::GUI::Tab::DESTROY = sub {};
*Slic3r::GUI::PresetHints::DESTROY = sub {};
*Slic3r::GUI::TabIface::DESTROY = sub {};
*Slic3r::OctoPrint::DESTROY = sub {};
*Slic3r::PresetUpdater::DESTROY = sub {};
return undef; # this prevents a "Scalars leaked" warning return undef; # this prevents a "Scalars leaked" warning
} }

View File

@ -86,8 +86,9 @@ sub OnInit {
Slic3r::GUI::set_wxapp($self); Slic3r::GUI::set_wxapp($self);
$self->{app_config} = Slic3r::GUI::AppConfig->new; $self->{app_config} = Slic3r::GUI::AppConfig->new;
$self->{preset_bundle} = Slic3r::GUI::PresetBundle->new;
Slic3r::GUI::set_app_config($self->{app_config}); Slic3r::GUI::set_app_config($self->{app_config});
$self->{preset_bundle} = Slic3r::GUI::PresetBundle->new;
Slic3r::GUI::set_preset_bundle($self->{preset_bundle});
# just checking for existence of Slic3r::data_dir is not enough: it may be an empty directory # just checking for existence of Slic3r::data_dir is not enough: it may be an empty directory
# supplied as argument to --datadir; in that case we should still run the wizard # supplied as argument to --datadir; in that case we should still run the wizard
@ -104,7 +105,11 @@ sub OnInit {
$self->{preset_updater} = Slic3r::PresetUpdater->new($VERSION_ONLINE_EVENT); $self->{preset_updater} = Slic3r::PresetUpdater->new($VERSION_ONLINE_EVENT);
Slic3r::GUI::set_preset_updater($self->{preset_updater}); Slic3r::GUI::set_preset_updater($self->{preset_updater});
eval { $self->{preset_updater}->config_update(); }; eval {
if (! $self->{preset_updater}->config_update()) {
exit 0;
}
};
if ($@) { if ($@) {
warn $@ . "\n"; warn $@ . "\n";
fatal_error(undef, $@); fatal_error(undef, $@);
@ -120,8 +125,6 @@ sub OnInit {
show_error(undef, $@); show_error(undef, $@);
} }
Slic3r::GUI::set_preset_bundle($self->{preset_bundle});
# application frame # application frame
print STDERR "Creating main frame...\n"; print STDERR "Creating main frame...\n";
Wx::Image::FindHandlerType(wxBITMAP_TYPE_PNG) || Wx::Image::AddHandler(Wx::PNGHandler->new); Wx::Image::FindHandlerType(wxBITMAP_TYPE_PNG) || Wx::Image::AddHandler(Wx::PNGHandler->new);
@ -144,8 +147,10 @@ sub OnInit {
# On OSX the UI was not initialized correctly if the wizard was called # On OSX the UI was not initialized correctly if the wizard was called
# before the UI was up and running. # before the UI was up and running.
$self->CallAfter(sub { $self->CallAfter(sub {
Slic3r::GUI::config_wizard_startup($app_conf_exists); if (! Slic3r::GUI::config_wizard_startup($app_conf_exists)) {
$self->{preset_updater}->slic3r_update_notify(); # Only notify if there was not wizard so as not to bother too much ...
$self->{preset_updater}->slic3r_update_notify();
}
$self->{preset_updater}->sync($self->{preset_bundle}); $self->{preset_updater}->sync($self->{preset_bundle});
}); });
@ -244,7 +249,7 @@ sub catch_error {
# static method accepting a wxWindow object as first parameter # static method accepting a wxWindow object as first parameter
sub show_error { sub show_error {
my ($parent, $message) = @_; my ($parent, $message) = @_;
Wx::MessageDialog->new($parent, $message, 'Error', wxOK | wxICON_ERROR)->ShowModal; Slic3r::GUI::show_error_id($parent ? $parent->GetId() : 0, $message);
} }
# static method accepting a wxWindow object as first parameter # static method accepting a wxWindow object as first parameter

View File

@ -389,7 +389,7 @@ sub mouse_event {
$self->_mouse_dragging($e->Dragging); $self->_mouse_dragging($e->Dragging);
if ($e->Entering && &Wx::wxMSW) { if ($e->Entering && (&Wx::wxMSW || $^O eq 'linux')) {
# wxMSW needs focus in order to catch mouse wheel events # wxMSW needs focus in order to catch mouse wheel events
$self->SetFocus; $self->SetFocus;
$self->_drag_start_xy(undef); $self->_drag_start_xy(undef);
@ -687,7 +687,7 @@ sub select_view {
} }
sub get_zoom_to_bounding_box_factor { sub get_zoom_to_bounding_box_factor {
my ($self, $bb) = @_; my ($self, $bb) = @_;
my $max_bb_size = max(@{ $bb->size }); my $max_bb_size = max(@{ $bb->size });
return undef if ($max_bb_size == 0); return undef if ($max_bb_size == 0);
@ -760,6 +760,8 @@ sub get_zoom_to_bounding_box_factor {
$max_y = max($max_y, $margin_factor * 2 * abs($y_on_plane)); $max_y = max($max_y, $margin_factor * 2 * abs($y_on_plane));
} }
return undef if (($max_x == 0) || ($max_y == 0));
my ($cw, $ch) = $self->GetSizeWH; my ($cw, $ch) = $self->GetSizeWH;
my $min_ratio = min($cw / $max_x, $ch / $max_y); my $min_ratio = min($cw / $max_x, $ch / $max_y);
@ -906,6 +908,9 @@ sub deselect_volumes {
sub select_volume { sub select_volume {
my ($self, $volume_idx) = @_; my ($self, $volume_idx) = @_;
return if ($volume_idx >= scalar(@{$self->volumes}));
$self->volumes->[$volume_idx]->set_selected(1) $self->volumes->[$volume_idx]->set_selected(1)
if $volume_idx != -1; if $volume_idx != -1;
} }
@ -1112,7 +1117,7 @@ sub Resize {
# is only a workaround for an incorrectly set camera. # is only a workaround for an incorrectly set camera.
# This workaround harms Z-buffer accuracy! # This workaround harms Z-buffer accuracy!
# my $depth = 1.05 * $self->max_bounding_box->radius(); # my $depth = 1.05 * $self->max_bounding_box->radius();
my $depth = max(@{ $self->max_bounding_box->size }); my $depth = 5.0 * max(@{ $self->max_bounding_box->size });
glOrtho( glOrtho(
-$x/2, $x/2, -$y/2, $y/2, -$x/2, $x/2, -$y/2, $y/2,
-$depth, $depth, -$depth, $depth,
@ -1150,6 +1155,8 @@ sub InitGL {
$self->volumes->finalize_geometry(1) $self->volumes->finalize_geometry(1)
if ($^O eq 'linux' && $self->UseVBOs); if ($^O eq 'linux' && $self->UseVBOs);
$self->zoom_to_bed;
glClearColor(0, 0, 0, 1); glClearColor(0, 0, 0, 1);
glColor3f(1, 0, 0); glColor3f(1, 0, 0);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);

View File

@ -149,6 +149,7 @@ sub _init_tabpanel {
if (defined $presets){ if (defined $presets){
my $reload_dependent_tabs = $tab->get_dependent_tabs; my $reload_dependent_tabs = $tab->get_dependent_tabs;
$self->{plater}->update_presets($tab_name, $reload_dependent_tabs, $presets); $self->{plater}->update_presets($tab_name, $reload_dependent_tabs, $presets);
$self->{plater}->{"selected_item_$tab_name"} = $tab->get_selected_preset_item;
if ($tab_name eq 'printer') { if ($tab_name eq 'printer') {
# Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. # Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
for my $tab_name_other (qw(print filament)) { for my $tab_name_other (qw(print filament)) {

View File

@ -486,7 +486,7 @@ sub new {
my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); my $right_sizer = Wx::BoxSizer->new(wxVERTICAL);
$right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets; $right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets;
$right_sizer->Add($frequently_changed_parameters_sizer, 0, wxEXPAND | wxTOP, 10) if defined $frequently_changed_parameters_sizer; $right_sizer->Add($frequently_changed_parameters_sizer, 0, wxEXPAND | wxTOP, 0) if defined $frequently_changed_parameters_sizer;
$right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM, 5); $right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM, 5);
$right_sizer->Add($self->{list}, 1, wxEXPAND, 5); $right_sizer->Add($self->{list}, 1, wxEXPAND, 5);
$right_sizer->Add($object_info_sizer, 0, wxEXPAND, 0); $right_sizer->Add($object_info_sizer, 0, wxEXPAND, 0);
@ -514,6 +514,13 @@ sub new {
$self->SetSizer($sizer); $self->SetSizer($sizer);
} }
# Last correct selected item for each preset
{
$self->{selected_item_print} = 0;
$self->{selected_item_filament} = 0;
$self->{selected_item_printer} = 0;
}
$self->update_ui_from_settings(); $self->update_ui_from_settings();
return $self; return $self;
@ -538,9 +545,21 @@ sub _on_select_preset {
# Only update the platter UI for the 2nd and other filaments. # Only update the platter UI for the 2nd and other filaments.
wxTheApp->{preset_bundle}->update_platter_filament_ui($idx, $choice); wxTheApp->{preset_bundle}->update_platter_filament_ui($idx, $choice);
} else { } else {
my $selected_item = $choice->GetSelection();
return if ($selected_item == $self->{"selected_item_$group"});
my $selected_string = $choice->GetString($selected_item);
if ($selected_string eq ("------- ".L("System presets")." -------") ||
$selected_string eq ("------- ".L("User presets")." -------") ){
$choice->SetSelection($self->{"selected_item_$group"});
return;
}
# call GetSelection() in scalar context as it's context-aware # call GetSelection() in scalar context as it's context-aware
$self->{on_select_preset}->($group, $choice->GetStringSelection) # $self->{on_select_preset}->($group, $choice->GetStringSelection)
if $self->{on_select_preset}; $self->{on_select_preset}->($group, $selected_string)
if $self->{on_select_preset};
$self->{"selected_item_$group"} = $selected_item;
} }
# Synchronize config.ini with the current selections. # Synchronize config.ini with the current selections.
wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config}); wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config});
@ -1786,22 +1805,24 @@ sub on_config_change {
sub list_item_deselected { sub list_item_deselected {
my ($self, $event) = @_; my ($self, $event) = @_;
return if $PreventListEvents; return if $PreventListEvents;
$self->{_lecursor} = Wx::BusyCursor->new();
if ($self->{list}->GetFirstSelected == -1) { if ($self->{list}->GetFirstSelected == -1) {
$self->select_object(undef); $self->select_object(undef);
$self->{canvas}->Refresh; $self->{canvas}->Refresh;
#FIXME VBOs are being refreshed just to change a selection color? $self->{canvas3D}->deselect_volumes if $self->{canvas3D};
$self->{canvas3D}->reload_scene if $self->{canvas3D};
} }
undef $self->{_lecursor};
} }
sub list_item_selected { sub list_item_selected {
my ($self, $event) = @_; my ($self, $event) = @_;
return if $PreventListEvents; return if $PreventListEvents;
$self->{_lecursor} = Wx::BusyCursor->new();
my $obj_idx = $event->GetIndex; my $obj_idx = $event->GetIndex;
$self->select_object($obj_idx); $self->select_object($obj_idx);
$self->{canvas}->Refresh; $self->{canvas}->Refresh;
#FIXME VBOs are being refreshed just to change a selection color? $self->{canvas3D}->update_volumes_selection if $self->{canvas3D};
$self->{canvas3D}->reload_scene if $self->{canvas3D}; undef $self->{_lecursor};
} }
sub list_item_activated { sub list_item_activated {
@ -1935,7 +1956,8 @@ sub selection_changed {
my ($self) = @_; my ($self) = @_;
my ($obj_idx, $object) = $self->selected_object; my ($obj_idx, $object) = $self->selected_object;
my $have_sel = defined $obj_idx; my $have_sel = defined $obj_idx;
$self->Freeze;
if ($self->{htoolbar}) { if ($self->{htoolbar}) {
# On OSX or Linux # On OSX or Linux
$self->{htoolbar}->EnableTool($_, $have_sel) $self->{htoolbar}->EnableTool($_, $have_sel)
@ -1986,12 +2008,20 @@ sub selection_changed {
# prepagate the event to the frame (a custom Wx event would be cleaner) # prepagate the event to the frame (a custom Wx event would be cleaner)
$self->GetFrame->on_plater_selection_changed($have_sel); $self->GetFrame->on_plater_selection_changed($have_sel);
$self->Thaw;
} }
sub select_object { sub select_object {
my ($self, $obj_idx) = @_; my ($self, $obj_idx) = @_;
# remove current selection
foreach my $o (0..$#{$self->{objects}}) {
$PreventListEvents = 1;
$self->{objects}->[$o]->selected(0);
$self->{list}->Select($o, 0);
$PreventListEvents = 0;
}
$_->selected(0) for @{ $self->{objects} };
if (defined $obj_idx) { if (defined $obj_idx) {
$self->{objects}->[$obj_idx]->selected(1); $self->{objects}->[$obj_idx]->selected(1);
# We use this flag to avoid circular event handling # We use this flag to avoid circular event handling

View File

@ -31,7 +31,9 @@ sub new {
$self->{on_select_object} = sub {}; $self->{on_select_object} = sub {};
$self->{on_instances_moved} = sub {}; $self->{on_instances_moved} = sub {};
$self->{on_wipe_tower_moved} = sub {}; $self->{on_wipe_tower_moved} = sub {};
$self->{objects_volumes_idxs} = ();
$self->on_select(sub { $self->on_select(sub {
my ($volume_idx) = @_; my ($volume_idx) = @_;
$self->{on_select_object}->(($volume_idx == -1) ? undef : $self->volumes->[$volume_idx]->object_idx) $self->{on_select_object}->(($volume_idx == -1) ? undef : $self->volumes->[$volume_idx]->object_idx)
@ -181,6 +183,17 @@ sub set_on_enable_action_buttons {
$self->on_enable_action_buttons($cb); $self->on_enable_action_buttons($cb);
} }
sub update_volumes_selection {
my ($self) = @_;
foreach my $obj_idx (0..$#{$self->{model}->objects}) {
if ($self->{objects}[$obj_idx]->selected) {
my @volume_idxs = @{$self->{objects_volumes_idxs}[$obj_idx]};
$self->select_volume($_) for @volume_idxs;
}
}
}
sub reload_scene { sub reload_scene {
my ($self, $force) = @_; my ($self, $force) = @_;
@ -194,12 +207,14 @@ sub reload_scene {
$self->{reload_delayed} = 0; $self->{reload_delayed} = 0;
$self->{objects_volumes_idxs} = ();
foreach my $obj_idx (0..$#{$self->{model}->objects}) { foreach my $obj_idx (0..$#{$self->{model}->objects}) {
my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx); my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx);
if ($self->{objects}[$obj_idx]->selected) { push(@{$self->{objects_volumes_idxs}}, \@volume_idxs);
$self->select_volume($_) for @volume_idxs;
}
} }
$self->update_volumes_selection;
if (defined $self->{config}->nozzle_diameter) { if (defined $self->{config}->nozzle_diameter) {
# Should the wipe tower be visualized? # Should the wipe tower be visualized?
my $extruders_count = scalar @{ $self->{config}->nozzle_diameter }; my $extruders_count = scalar @{ $self->{config}->nozzle_diameter };

View File

@ -116,7 +116,6 @@ sub new {
$canvas->set_auto_bed_shape; $canvas->set_auto_bed_shape;
$canvas->SetSize([500,500]); $canvas->SetSize([500,500]);
$canvas->SetMinSize($canvas->GetSize); $canvas->SetMinSize($canvas->GetSize);
$canvas->zoom_to_volumes;
} }
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
@ -227,12 +226,14 @@ sub _update {
push @objects, $self->{model_object}; push @objects, $self->{model_object};
} }
my $z_cut = $z + $self->{model_object}->bounding_box->z_min;
# get section contour # get section contour
my @expolygons = (); my @expolygons = ();
foreach my $volume (@{$self->{model_object}->volumes}) { foreach my $volume (@{$self->{model_object}->volumes}) {
next if !$volume->mesh; next if !$volume->mesh;
next if $volume->modifier; next if $volume->modifier;
my $expp = $volume->mesh->slice([ $z + $volume->mesh->bounding_box->z_min ])->[0]; my $expp = $volume->mesh->slice([ $z_cut ])->[0];
push @expolygons, @$expp; push @expolygons, @$expp;
} }
foreach my $expolygon (@expolygons) { foreach my $expolygon (@expolygons) {

View File

@ -163,7 +163,6 @@ sub new {
$canvas->set_auto_bed_shape; $canvas->set_auto_bed_shape;
$canvas->SetSize([500,700]); $canvas->SetSize([500,700]);
$canvas->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); $canvas->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config});
$canvas->zoom_to_volumes;
} }
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,23 @@
xs/src/slic3r/GUI/AboutDialog.cpp
xs/src/slic3r/GUI/BedShapeDialog.cpp xs/src/slic3r/GUI/BedShapeDialog.cpp
xs/src/slic3r/GUI/BedShapeDialog.hpp xs/src/slic3r/GUI/BedShapeDialog.hpp
xs/src/slic3r/GUI/BonjourDialog.cpp
xs/src/slic3r/GUI/ButtonsDescription.cpp
xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp
xs/src/slic3r/GUI/ConfigWizard.cpp
xs/src/slic3r/GUI/GUI.cpp xs/src/slic3r/GUI/GUI.cpp
xs/src/slic3r/GUI/MsgDialog.cpp
xs/src/slic3r/GUI/Tab.cpp xs/src/slic3r/GUI/Tab.cpp
xs/src/slic3r/GUI/Tab.hpp xs/src/slic3r/GUI/Tab.hpp
xs/src/slic3r/GUI/Field.cpp xs/src/slic3r/GUI/Field.cpp
xs/src/slic3r/GUI/OptionsGroup.cpp xs/src/slic3r/GUI/OptionsGroup.cpp
xs/src/slic3r/GUI/2DBed.cpp xs/src/slic3r/GUI/Preset.cpp
xs/src/slic3r/GUI/PresetBundle.cpp
xs/src/slic3r/GUI/PresetHints.cpp xs/src/slic3r/GUI/PresetHints.cpp
xs/src/slic3r/GUI/Preferences.hpp
xs/src/slic3r/GUI/Preferences.cpp xs/src/slic3r/GUI/Preferences.cpp
xs/src/slic3r/GUI/BonjourDialog.cpp xs/src/slic3r/GUI/RammingChart.cpp
xs/src/slic3r/GUI/UpdateDialogs.cpp
xs/src/slic3r/GUI/WipeTowerDialog.cpp
xs/src/slic3r/Utils/OctoPrint.cpp xs/src/slic3r/Utils/OctoPrint.cpp
xs/src/libslic3r/PrintConfig.cpp xs/src/libslic3r/PrintConfig.cpp
xs/src/libslic3r/GCode/PreviewData.cpp xs/src/libslic3r/GCode/PreviewData.cpp

View File

@ -1,15 +1 @@
# This is an example configuration version index. 0.1.0 Initial
# The index contains version numbers
min_slic3r_version =1.39.0
1.1.1
1.1.0
0.2.0-alpha "some test comment"
max_slic3r_version= 1.39.4
0.1.0 another test comment
# some empty lines
# version without a comment
min_slic3r_version = 1.0.0
max_slic3r_version = 1.1.0
0.0.1

View File

@ -7,13 +7,13 @@ name = Prusa Research
# This means, the server may force the Slic3r configuration to be downgraded. # This means, the server may force the Slic3r configuration to be downgraded.
config_version = 0.1.0 config_version = 0.1.0
# Where to get the updates from? # Where to get the updates from?
# TODO: proper URL config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/
config_update_url = https://raw.githubusercontent.com/vojtechkral/slic3r-settings-tmp/master/PrusaResearch
# The printer models will be shown by the Configuration Wizard in this order, # The printer models will be shown by the Configuration Wizard in this order,
# also the first model installed & the first nozzle installed will be activated after install. # also the first model installed & the first nozzle installed will be activated after install.
#TODO: One day we may differentiate variants of the nozzles / hot ends, #TODO: One day we may differentiate variants of the nozzles / hot ends,
#for example by the melt zone size, or whether the nozzle is hardened. #for example by the melt zone size, or whether the nozzle is hardened.
# Printer model name will be shown by the installation wizard.
[printer_model:MK3] [printer_model:MK3]
name = Original Prusa i3 MK3 name = Original Prusa i3 MK3
variants = 0.4; 0.25; 0.6 variants = 0.4; 0.25; 0.6
@ -23,7 +23,6 @@ name = Original Prusa i3 MK2S
variants = 0.4; 0.25; 0.6 variants = 0.4; 0.25; 0.6
[printer_model:MK2SMM] [printer_model:MK2SMM]
# Printer model name will be shown by the installation wizard.
name = Original Prusa i3 MK2SMM name = Original Prusa i3 MK2SMM
variants = 0.4; 0.6 variants = 0.4; 0.6
@ -69,6 +68,7 @@ infill_first = 0
infill_only_where_needed = 0 infill_only_where_needed = 0
infill_overlap = 25% infill_overlap = 25%
interface_shells = 0 interface_shells = 0
max_print_height = 200
max_print_speed = 100 max_print_speed = 100
max_volumetric_extrusion_rate_slope_negative = 0 max_volumetric_extrusion_rate_slope_negative = 0
max_volumetric_extrusion_rate_slope_positive = 0 max_volumetric_extrusion_rate_slope_positive = 0
@ -120,7 +120,7 @@ thin_walls = 0
top_infill_extrusion_width = 0.45 top_infill_extrusion_width = 0.45
top_solid_infill_speed = 40 top_solid_infill_speed = 40
travel_speed = 180 travel_speed = 180
wipe_tower = 0 wipe_tower = 1
wipe_tower_per_color_wipe = 20 wipe_tower_per_color_wipe = 20
wipe_tower_width = 60 wipe_tower_width = 60
wipe_tower_x = 180 wipe_tower_x = 180
@ -939,6 +939,8 @@ inherits = *multimaterial*
end_gcode = G1 E-4 F2100.00000\nG91\nG1 Z1 F7200.000\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7 \nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3 \nG1 E-15.0000 F5000\nG1 E-50.0000 F5400\nG1 E-15.0000 F3000\nG1 E-12.0000 F2000\nG1 F1600\nG1 X0 Y1 E3.0000\nG1 X50 Y1 E-5.0000\nG1 F2000\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-3.0000\nG4 S0\nM107 ; fan off\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM84 ; disable motors\n\n end_gcode = G1 E-4 F2100.00000\nG91\nG1 Z1 F7200.000\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7 \nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3 \nG1 E-15.0000 F5000\nG1 E-50.0000 F5400\nG1 E-15.0000 F3000\nG1 E-12.0000 F2000\nG1 F1600\nG1 X0 Y1 E3.0000\nG1 X50 Y1 E-5.0000\nG1 F2000\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-3.0000\nG4 S0\nM107 ; fan off\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM84 ; disable motors\n\n
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\nPRINTER_HAS_BOWDEN printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\nPRINTER_HAS_BOWDEN
start_gcode = M115 U3.1.0 ; tell printer latest fw version\nM201 X9000 Y9000 Z500 E10000 ; sets maximum accelerations, mm/sec^2\nM203 X500 Y500 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1500 T1500 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.2 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\n; Start G-Code sequence START\nT?\nM104 S[first_layer_temperature]\nM140 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nM190 S[first_layer_bed_temperature]\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100\nM92 E140\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG1 E-4 F1000.0\nG92 E0.0 start_gcode = M115 U3.1.0 ; tell printer latest fw version\nM201 X9000 Y9000 Z500 E10000 ; sets maximum accelerations, mm/sec^2\nM203 X500 Y500 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1500 T1500 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.2 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\n; Start G-Code sequence START\nT?\nM104 S[first_layer_temperature]\nM140 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nM190 S[first_layer_bed_temperature]\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100\nM92 E140\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG1 E-4 F1000.0\nG92 E0.0
default_print_profile = 0.15mm OPTIMAL
default_filament_profile = Prusa PLA
[printer:*mm-multi*] [printer:*mm-multi*]
inherits = *multimaterial* inherits = *multimaterial*
@ -948,10 +950,11 @@ nozzle_diameter = 0.4,0.4,0.4,0.4
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\nPRINTER_HAS_BOWDEN printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\nPRINTER_HAS_BOWDEN
start_gcode = M115 U3.1.0 ; tell printer latest fw version\nM201 X9000 Y9000 Z500 E10000 ; sets maximum accelerations, mm/sec^2\nM203 X500 Y500 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1500 T1500 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.2 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\n; Start G-Code sequence START\nT[initial_tool]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100 ; set max feedrate\nM92 E140 ; E-steps per filament milimeter\n{if not has_wipe_tower}\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG1 E-4 F1000.0\n{endif}\nG92 E0.0 start_gcode = M115 U3.1.0 ; tell printer latest fw version\nM201 X9000 Y9000 Z500 E10000 ; sets maximum accelerations, mm/sec^2\nM203 X500 Y500 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1500 T1500 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.2 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\n; Start G-Code sequence START\nT[initial_tool]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100 ; set max feedrate\nM92 E140 ; E-steps per filament milimeter\n{if not has_wipe_tower}\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG1 E-4 F1000.0\n{endif}\nG92 E0.0
variable_layer_height = 0 variable_layer_height = 0
default_print_profile = 0.15mm OPTIMAL
default_filament_profile = Prusa PLA
[printer:Original Prusa i3 MK2] [printer:Original Prusa i3 MK2]
inherits = *common* inherits = *common*
default_print_profile = 0.15mm OPTIMAL
[printer:Original Prusa i3 MK2 0.25 nozzle] [printer:Original Prusa i3 MK2 0.25 nozzle]
inherits = *common* inherits = *common*
@ -989,12 +992,14 @@ nozzle_diameter = 0.4,0.4,0.4,0.4
inherits = *mm-multi* inherits = *mm-multi*
nozzle_diameter = 0.6,0.6,0.6,0.6 nozzle_diameter = 0.6,0.6,0.6,0.6
printer_variant = 0.6 printer_variant = 0.6
default_print_profile = 0.20mm NORMAL 0.6 nozzle
[printer:Original Prusa i3 MK3] [printer:Original Prusa i3 MK3]
inherits = *common* inherits = *common*
end_gcode = G4 ; wait\nM221 S100\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors end_gcode = G4 ; wait\nM221 S100\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n
retract_lift_below = 209 retract_lift_below = 209
max_print_height = 210
start_gcode = M115 U3.1.1-RC5 ; tell printer latest fw version\nM201 X1000 Y1000 Z200 E5000 ; sets maximum accelerations, mm/sec^2\nM203 X200 Y200 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1250 T1250 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.4 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif} start_gcode = M115 U3.1.1-RC5 ; tell printer latest fw version\nM201 X1000 Y1000 Z200 E5000 ; sets maximum accelerations, mm/sec^2\nM203 X200 Y200 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1250 T1250 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.4 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif}
printer_model = MK3 printer_model = MK3
default_print_profile = 0.15mm OPTIMAL MK3 default_print_profile = 0.15mm OPTIMAL MK3
@ -1002,21 +1007,21 @@ default_print_profile = 0.15mm OPTIMAL MK3
[printer:Original Prusa i3 MK3 0.25 nozzle] [printer:Original Prusa i3 MK3 0.25 nozzle]
inherits = *common* inherits = *common*
nozzle_diameter = 0.25 nozzle_diameter = 0.25
printer_variant = 0.25
end_gcode = G4 ; wait\nM221 S100\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors end_gcode = G4 ; wait\nM221 S100\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n
retract_lift_below = 209 retract_lift_below = 209
max_print_height = 210
start_gcode = M115 U3.1.1-RC5 ; tell printer latest fw version\nM201 X1000 Y1000 Z200 E5000 ; sets maximum accelerations, mm/sec^2\nM203 X200 Y200 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1250 T1250 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.4 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif} start_gcode = M115 U3.1.1-RC5 ; tell printer latest fw version\nM201 X1000 Y1000 Z200 E5000 ; sets maximum accelerations, mm/sec^2\nM203 X200 Y200 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1250 T1250 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.4 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif}
printer_model = MK3 printer_model = MK3
default_print_profile = 0.10mm DETAIL MK3 default_print_profile = 0.10mm DETAIL 0.25 nozzle MK3
[printer:Original Prusa i3 MK3 0.6 nozzle] [printer:Original Prusa i3 MK3 0.6 nozzle]
inherits = *common* inherits = *common*
nozzle_diameter = 0.6 nozzle_diameter = 0.6
printer_variant = 0.6
end_gcode = G4 ; wait\nM221 S100\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors end_gcode = G4 ; wait\nM221 S100\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n
retract_lift_below = 209 retract_lift_below = 209
max_print_height = 210
start_gcode = M115 U3.1.1-RC5 ; tell printer latest fw version\nM201 X1000 Y1000 Z200 E5000 ; sets maximum accelerations, mm/sec^2\nM203 X200 Y200 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1250 T1250 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.4 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif} start_gcode = M115 U3.1.1-RC5 ; tell printer latest fw version\nM201 X1000 Y1000 Z200 E5000 ; sets maximum accelerations, mm/sec^2\nM203 X200 Y200 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1250 T1250 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.4 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif}
printer_model = MK3 printer_model = MK3
default_print_profile = 0.15mm OPTIMAL MK3 default_print_profile = 0.15mm OPTIMAL 0.6 nozzle MK3

View File

@ -212,6 +212,8 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/GUI/RammingChart.hpp ${LIBDIR}/slic3r/GUI/RammingChart.hpp
${LIBDIR}/slic3r/GUI/BonjourDialog.cpp ${LIBDIR}/slic3r/GUI/BonjourDialog.cpp
${LIBDIR}/slic3r/GUI/BonjourDialog.hpp ${LIBDIR}/slic3r/GUI/BonjourDialog.hpp
${LIBDIR}/slic3r/GUI/ButtonsDescription.cpp
${LIBDIR}/slic3r/GUI/ButtonsDescription.hpp
${LIBDIR}/slic3r/Config/Snapshot.cpp ${LIBDIR}/slic3r/Config/Snapshot.cpp
${LIBDIR}/slic3r/Config/Snapshot.hpp ${LIBDIR}/slic3r/Config/Snapshot.hpp
${LIBDIR}/slic3r/Config/Version.cpp ${LIBDIR}/slic3r/Config/Version.cpp
@ -220,6 +222,10 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/Utils/ASCIIFolding.hpp ${LIBDIR}/slic3r/Utils/ASCIIFolding.hpp
${LIBDIR}/slic3r/GUI/ConfigWizard.cpp ${LIBDIR}/slic3r/GUI/ConfigWizard.cpp
${LIBDIR}/slic3r/GUI/ConfigWizard.hpp ${LIBDIR}/slic3r/GUI/ConfigWizard.hpp
${LIBDIR}/slic3r/GUI/MsgDialog.cpp
${LIBDIR}/slic3r/GUI/MsgDialog.hpp
${LIBDIR}/slic3r/GUI/UpdateDialogs.cpp
${LIBDIR}/slic3r/GUI/UpdateDialogs.hpp
${LIBDIR}/slic3r/Utils/Http.cpp ${LIBDIR}/slic3r/Utils/Http.cpp
${LIBDIR}/slic3r/Utils/Http.hpp ${LIBDIR}/slic3r/Utils/Http.hpp
${LIBDIR}/slic3r/Utils/OctoPrint.cpp ${LIBDIR}/slic3r/Utils/OctoPrint.cpp
@ -634,6 +640,7 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:XS>" "${PERL_LOCAL_LIB_DIR}/auto/Slic3r/XS/" COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:XS>" "${PERL_LOCAL_LIB_DIR}/auto/Slic3r/XS/"
COMMAND ${CMAKE_COMMAND} -E make_directory "${PERL_LOCAL_LIB_DIR}/Slic3r/" COMMAND ${CMAKE_COMMAND} -E make_directory "${PERL_LOCAL_LIB_DIR}/Slic3r/"
COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/xs/lib/Slic3r/XS.pm" "${PERL_LOCAL_LIB_DIR}/Slic3r/" COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/xs/lib/Slic3r/XS.pm" "${PERL_LOCAL_LIB_DIR}/Slic3r/"
COMMENT "Installing XS.pm and XS.{so,dll,bundle} into the local-lib directory ..."
) )
if(APPLE) if(APPLE)
add_custom_command( add_custom_command(
@ -658,7 +665,12 @@ endif ()
if (MSVC) if (MSVC)
# Here we associate some additional properties with the MSVC project to enable compilation and debugging out of the box. # Here we associate some additional properties with the MSVC project to enable compilation and debugging out of the box.
set_target_properties(XS PROPERTIES VS_USER_PROPS "${PROJECT_SOURCE_DIR}/cmake/msvc/xs.wperl64d.props") get_filename_component(PROPS_PERL_BIN_PATH "${PERL_EXECUTABLE}" DIRECTORY)
string(REPLACE "/" "\\" PROPS_PERL_BIN_PATH "${PROPS_PERL_BIN_PATH}")
string(REPLACE "/" "\\" PROPS_PERL_EXECUTABLE "${PERL_EXECUTABLE}")
string(REPLACE "/" "\\" PROPS_CMAKE_SOURCE_DIR "${CMAKE_SOURCE_DIR}")
configure_file("${PROJECT_SOURCE_DIR}/cmake/msvc/xs.wperl.props.in" "${CMAKE_BINARY_DIR}/xs.wperl.props" NEWLINE_STYLE CRLF)
set_target_properties(XS PROPERTIES VS_USER_PROPS "${CMAKE_BINARY_DIR}/xs.wperl.props")
endif() endif()
# l10n # l10n

View File

@ -206,6 +206,44 @@ t_config_option_keys ConfigBase::diff(const ConfigBase &other) const
return diff; return diff;
} }
template<class T>
void add_correct_opts_to_diff(const std::string &opt_key, t_config_option_keys& vec, const ConfigBase &other, const ConfigBase *this_c)
{
const T* opt_init = static_cast<const T*>(other.option(opt_key));
const T* opt_cur = static_cast<const T*>(this_c->option(opt_key));
int opt_init_max_id = opt_init->values.size() - 1;
for (int i = 0; i < opt_cur->values.size(); i++)
{
int init_id = i <= opt_init_max_id ? i : 0;
if (opt_cur->values[i] != opt_init->values[init_id])
vec.emplace_back(opt_key + "#" + std::to_string(i));
}
}
t_config_option_keys ConfigBase::deep_diff(const ConfigBase &other) const
{
t_config_option_keys diff;
for (const t_config_option_key &opt_key : this->keys()) {
const ConfigOption *this_opt = this->option(opt_key);
const ConfigOption *other_opt = other.option(opt_key);
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
{
if (opt_key == "bed_shape"){ diff.emplace_back(opt_key); continue; }
switch (other_opt->type())
{
case coInts: add_correct_opts_to_diff<ConfigOptionInts >(opt_key, diff, other, this); break;
case coBools: add_correct_opts_to_diff<ConfigOptionBools >(opt_key, diff, other, this); break;
case coFloats: add_correct_opts_to_diff<ConfigOptionFloats >(opt_key, diff, other, this); break;
case coStrings: add_correct_opts_to_diff<ConfigOptionStrings >(opt_key, diff, other, this); break;
case coPercents:add_correct_opts_to_diff<ConfigOptionPercents >(opt_key, diff, other, this); break;
case coPoints: add_correct_opts_to_diff<ConfigOptionPoints >(opt_key, diff, other, this); break;
default: diff.emplace_back(opt_key); break;
}
}
}
return diff;
}
t_config_option_keys ConfigBase::equal(const ConfigBase &other) const t_config_option_keys ConfigBase::equal(const ConfigBase &other) const
{ {
t_config_option_keys equal; t_config_option_keys equal;

View File

@ -1047,6 +1047,9 @@ public:
void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false); void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false);
bool equals(const ConfigBase &other) const { return this->diff(other).empty(); } bool equals(const ConfigBase &other) const { return this->diff(other).empty(); }
t_config_option_keys diff(const ConfigBase &other) const; t_config_option_keys diff(const ConfigBase &other) const;
// Use deep_diff to correct return of changed options,
// considering individual options for each extruder
t_config_option_keys deep_diff(const ConfigBase &other) const;
t_config_option_keys equal(const ConfigBase &other) const; t_config_option_keys equal(const ConfigBase &other) const;
std::string serialize(const t_config_option_key &opt_key) const; std::string serialize(const t_config_option_key &opt_key) const;
// Set a configuration value from a string, it will call an overridable handle_legacy() // Set a configuration value from a string, it will call an overridable handle_legacy()

View File

@ -16,6 +16,12 @@
#include <Eigen/Dense> #include <Eigen/Dense>
#include <miniz/miniz_zip.h> #include <miniz/miniz_zip.h>
// VERSION NUMBERS
// 0 : .3mf, files saved by older slic3r or other applications. No version definition in them.
// 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files.
const unsigned int VERSION_3MF = 1;
const char* SLIC3RPE_3MF_VERSION = "slic3rpe:Version3mf"; // definition of the metadata name saved into .model file
const std::string MODEL_FOLDER = "3D/"; const std::string MODEL_FOLDER = "3D/";
const std::string MODEL_EXTENSION = ".model"; const std::string MODEL_EXTENSION = ".model";
const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format of the string which works with CURA const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format of the string which works with CURA
@ -23,6 +29,7 @@ const std::string CONTENT_TYPES_FILE = "[Content_Types].xml";
const std::string RELATIONSHIPS_FILE = "_rels/.rels"; const std::string RELATIONSHIPS_FILE = "_rels/.rels";
const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config"; const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config";
const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config"; const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config";
const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt";
const char* MODEL_TAG = "model"; const char* MODEL_TAG = "model";
const char* RESOURCES_TAG = "resources"; const char* RESOURCES_TAG = "resources";
@ -36,9 +43,9 @@ const char* COMPONENTS_TAG = "components";
const char* COMPONENT_TAG = "component"; const char* COMPONENT_TAG = "component";
const char* BUILD_TAG = "build"; const char* BUILD_TAG = "build";
const char* ITEM_TAG = "item"; const char* ITEM_TAG = "item";
const char* METADATA_TAG = "metadata";
const char* CONFIG_TAG = "config"; const char* CONFIG_TAG = "config";
const char* METADATA_TAG = "metadata";
const char* VOLUME_TAG = "volume"; const char* VOLUME_TAG = "volume";
const char* UNIT_ATTR = "unit"; const char* UNIT_ATTR = "unit";
@ -315,6 +322,10 @@ namespace Slic3r {
typedef std::vector<Instance> InstancesList; typedef std::vector<Instance> InstancesList;
typedef std::map<int, ObjectMetadata> IdToMetadataMap; typedef std::map<int, ObjectMetadata> IdToMetadataMap;
typedef std::map<int, Geometry> IdToGeometryMap; typedef std::map<int, Geometry> IdToGeometryMap;
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
// Version of the 3mf file
unsigned int m_version;
XML_Parser m_xml_parser; XML_Parser m_xml_parser;
Model* m_model; Model* m_model;
@ -326,6 +337,9 @@ namespace Slic3r {
IdToGeometryMap m_geometries; IdToGeometryMap m_geometries;
CurrentConfig m_curr_config; CurrentConfig m_curr_config;
IdToMetadataMap m_objects_metadata; IdToMetadataMap m_objects_metadata;
IdToLayerHeightsProfileMap m_layer_heights_profiles;
std::string m_curr_metadata_name;
std::string m_curr_characters;
public: public:
_3MF_Importer(); _3MF_Importer();
@ -339,12 +353,14 @@ namespace Slic3r {
bool _load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle); bool _load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle);
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
bool _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename); void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename);
bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model); bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
// handlers to parse the .model file // handlers to parse the .model file
void _handle_start_model_xml_element(const char* name, const char** attributes); void _handle_start_model_xml_element(const char* name, const char** attributes);
void _handle_end_model_xml_element(const char* name); void _handle_end_model_xml_element(const char* name);
void _handle_model_xml_characters(const XML_Char* s, int len);
// handlers to parse the MODEL_CONFIG_FILE file // handlers to parse the MODEL_CONFIG_FILE file
void _handle_start_config_xml_element(const char* name, const char** attributes); void _handle_start_config_xml_element(const char* name, const char** attributes);
@ -386,6 +402,9 @@ namespace Slic3r {
bool _handle_start_item(const char** attributes, unsigned int num_attributes); bool _handle_start_item(const char** attributes, unsigned int num_attributes);
bool _handle_end_item(); bool _handle_end_item();
bool _handle_start_metadata(const char** attributes, unsigned int num_attributes);
bool _handle_end_metadata();
bool _create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter); bool _create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter);
void _apply_transform(ModelInstance& instance, const Matrix4x4& matrix); void _apply_transform(ModelInstance& instance, const Matrix4x4& matrix);
@ -407,6 +426,7 @@ namespace Slic3r {
// callbacks to parse the .model file // callbacks to parse the .model file
static void XMLCALL _handle_start_model_xml_element(void* userData, const char* name, const char** attributes); static void XMLCALL _handle_start_model_xml_element(void* userData, const char* name, const char** attributes);
static void XMLCALL _handle_end_model_xml_element(void* userData, const char* name); static void XMLCALL _handle_end_model_xml_element(void* userData, const char* name);
static void XMLCALL _handle_model_xml_characters(void* userData, const XML_Char* s, int len);
// callbacks to parse the MODEL_CONFIG_FILE file // callbacks to parse the MODEL_CONFIG_FILE file
static void XMLCALL _handle_start_config_xml_element(void* userData, const char* name, const char** attributes); static void XMLCALL _handle_start_config_xml_element(void* userData, const char* name, const char** attributes);
@ -414,9 +434,12 @@ namespace Slic3r {
}; };
_3MF_Importer::_3MF_Importer() _3MF_Importer::_3MF_Importer()
: m_xml_parser(nullptr) : m_version(0)
, m_xml_parser(nullptr)
, m_model(nullptr) , m_model(nullptr)
, m_unit_factor(1.0f) , m_unit_factor(1.0f)
, m_curr_metadata_name("")
, m_curr_characters("")
{ {
} }
@ -427,6 +450,7 @@ namespace Slic3r {
bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle) bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle)
{ {
m_version = 0;
m_model = &model; m_model = &model;
m_unit_factor = 1.0f; m_unit_factor = 1.0f;
m_curr_object.reset(); m_curr_object.reset();
@ -437,6 +461,9 @@ namespace Slic3r {
m_curr_config.object_id = -1; m_curr_config.object_id = -1;
m_curr_config.volume_id = -1; m_curr_config.volume_id = -1;
m_objects_metadata.clear(); m_objects_metadata.clear();
m_layer_heights_profiles.clear();
m_curr_metadata_name.clear();
m_curr_characters.clear();
clear_errors(); clear_errors();
return _load_model_from_file(filename, model, bundle); return _load_model_from_file(filename, model, bundle);
@ -472,6 +499,8 @@ namespace Slic3r {
mz_uint num_entries = mz_zip_reader_get_num_files(&archive); mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
mz_zip_archive_file_stat stat; mz_zip_archive_file_stat stat;
// we first loop the entries to read from the archive the .model file only, in order to extract the version from it
for (mz_uint i = 0; i < num_entries; ++i) for (mz_uint i = 0; i < num_entries; ++i)
{ {
if (mz_zip_reader_file_stat(&archive, i, &stat)) if (mz_zip_reader_file_stat(&archive, i, &stat))
@ -489,15 +518,26 @@ namespace Slic3r {
return false; return false;
} }
} }
}
}
// we then loop again the entries to read other files stored in the archive
for (mz_uint i = 0; i < num_entries; ++i)
{
if (mz_zip_reader_file_stat(&archive, i, &stat))
{
std::string name(stat.m_filename);
std::replace(name.begin(), name.end(), '\\', '/');
if (boost::algorithm::iequals(name, LAYER_HEIGHTS_PROFILE_FILE))
{
// extract slic3r lazer heights profile file
_extract_layer_heights_profile_config_from_archive(archive, stat);
}
else if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE)) else if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE))
{ {
// extract slic3r print config file // extract slic3r print config file
if (!_extract_print_config_from_archive(archive, stat, bundle, filename)) _extract_print_config_from_archive(archive, stat, bundle, filename);
{
mz_zip_reader_end(&archive);
add_error("Archive does not contain a valid print config");
return false;
}
} }
else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE)) else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE))
{ {
@ -526,6 +566,13 @@ namespace Slic3r {
return false; return false;
} }
IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.first);
if (obj_layer_heights_profile != m_layer_heights_profiles.end())
{
object.second->layer_height_profile = obj_layer_heights_profile->second;
object.second->layer_height_profile_valid = true;
}
IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first); IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first);
if (obj_metadata != m_objects_metadata.end()) if (obj_metadata != m_objects_metadata.end())
{ {
@ -583,6 +630,7 @@ namespace Slic3r {
XML_SetUserData(m_xml_parser, (void*)this); XML_SetUserData(m_xml_parser, (void*)this);
XML_SetElementHandler(m_xml_parser, _3MF_Importer::_handle_start_model_xml_element, _3MF_Importer::_handle_end_model_xml_element); XML_SetElementHandler(m_xml_parser, _3MF_Importer::_handle_start_model_xml_element, _3MF_Importer::_handle_end_model_xml_element);
XML_SetCharacterDataHandler(m_xml_parser, _3MF_Importer::_handle_model_xml_characters);
void* parser_buffer = XML_GetBuffer(m_xml_parser, (int)stat.m_uncomp_size); void* parser_buffer = XML_GetBuffer(m_xml_parser, (int)stat.m_uncomp_size);
if (parser_buffer == nullptr) if (parser_buffer == nullptr)
@ -609,23 +657,90 @@ namespace Slic3r {
return true; return true;
} }
bool _3MF_Importer::_extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename) void _3MF_Importer::_extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename)
{ {
if (stat.m_uncomp_size > 0) if (stat.m_uncomp_size > 0)
{ {
std::vector<char> buffer((size_t)stat.m_uncomp_size + 1, 0); std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) if (res == 0)
{ {
add_error("Error while reading config data to buffer"); add_error("Error while reading config data to buffer");
return false; return;
} }
buffer.back() = '\0';
bundle.load_config_string(buffer.data(), archive_filename.c_str()); bundle.load_config_string(buffer.data(), archive_filename.c_str());
} }
}
return true; void _3MF_Importer::_extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
{
if (stat.m_uncomp_size > 0)
{
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0)
{
add_error("Error while reading layer heights profile data to buffer");
return;
}
if (buffer.back() == '\n')
buffer.pop_back();
std::vector<std::string> objects;
boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
for (const std::string& object : objects)
{
std::vector<std::string> object_data;
boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
if (object_data.size() != 2)
{
add_error("Error while reading object data");
continue;
}
std::vector<std::string> object_data_id;
boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off);
if (object_data_id.size() != 2)
{
add_error("Error while reading object id");
continue;
}
int object_id = std::atoi(object_data_id[1].c_str());
if (object_id == 0)
{
add_error("Found invalid object id");
continue;
}
IdToLayerHeightsProfileMap::iterator object_item = m_layer_heights_profiles.find(object_id);
if (object_item != m_layer_heights_profiles.end())
{
add_error("Found duplicated layer heights profile");
continue;
}
std::vector<std::string> object_data_profile;
boost::split(object_data_profile, object_data[1], boost::is_any_of(";"), boost::token_compress_off);
if ((object_data_profile.size() <= 4) || (object_data_profile.size() % 2 != 0))
{
add_error("Found invalid layer heights profile");
continue;
}
std::vector<coordf_t> profile;
profile.reserve(object_data_profile.size());
for (const std::string& value : object_data_profile)
{
profile.push_back((coordf_t)std::atof(value.c_str()));
}
m_layer_heights_profiles.insert(IdToLayerHeightsProfileMap::value_type(object_id, profile));
}
}
} }
bool _3MF_Importer::_extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model) bool _3MF_Importer::_extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model)
@ -705,6 +820,8 @@ namespace Slic3r {
res = _handle_start_build(attributes, num_attributes); res = _handle_start_build(attributes, num_attributes);
else if (::strcmp(ITEM_TAG, name) == 0) else if (::strcmp(ITEM_TAG, name) == 0)
res = _handle_start_item(attributes, num_attributes); res = _handle_start_item(attributes, num_attributes);
else if (::strcmp(METADATA_TAG, name) == 0)
res = _handle_start_metadata(attributes, num_attributes);
if (!res) if (!res)
_stop_xml_parser(); _stop_xml_parser();
@ -741,11 +858,18 @@ namespace Slic3r {
res = _handle_end_build(); res = _handle_end_build();
else if (::strcmp(ITEM_TAG, name) == 0) else if (::strcmp(ITEM_TAG, name) == 0)
res = _handle_end_item(); res = _handle_end_item();
else if (::strcmp(METADATA_TAG, name) == 0)
res = _handle_end_metadata();
if (!res) if (!res)
_stop_xml_parser(); _stop_xml_parser();
} }
void _3MF_Importer::_handle_model_xml_characters(const XML_Char* s, int len)
{
m_curr_characters.append(s, len);
}
void _3MF_Importer::_handle_start_config_xml_element(const char* name, const char** attributes) void _3MF_Importer::_handle_start_config_xml_element(const char* name, const char** attributes)
{ {
if (m_xml_parser == nullptr) if (m_xml_parser == nullptr)
@ -1052,6 +1176,25 @@ namespace Slic3r {
return true; return true;
} }
bool _3MF_Importer::_handle_start_metadata(const char** attributes, unsigned int num_attributes)
{
m_curr_characters.clear();
std::string name = get_attribute_value_string(attributes, num_attributes, NAME_ATTR);
if (!name.empty())
m_curr_metadata_name = name;
return true;
}
bool _3MF_Importer::_handle_end_metadata()
{
if (m_curr_metadata_name == SLIC3RPE_3MF_VERSION)
m_version = (unsigned int)atoi(m_curr_characters.c_str());
return true;
}
bool _3MF_Importer::_create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter) bool _3MF_Importer::_create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter)
{ {
static const unsigned int MAX_RECURSIONS = 10; static const unsigned int MAX_RECURSIONS = 10;
@ -1358,6 +1501,13 @@ namespace Slic3r {
importer->_handle_end_model_xml_element(name); importer->_handle_end_model_xml_element(name);
} }
void XMLCALL _3MF_Importer::_handle_model_xml_characters(void* userData, const XML_Char* s, int len)
{
_3MF_Importer* importer = (_3MF_Importer*)userData;
if (importer != nullptr)
importer->_handle_model_xml_characters(s, len);
}
void XMLCALL _3MF_Importer::_handle_start_config_xml_element(void* userData, const char* name, const char** attributes) void XMLCALL _3MF_Importer::_handle_start_config_xml_element(void* userData, const char* name, const char** attributes)
{ {
_3MF_Importer* importer = (_3MF_Importer*)userData; _3MF_Importer* importer = (_3MF_Importer*)userData;
@ -1429,6 +1579,7 @@ namespace Slic3r {
bool _add_object_to_model_stream(std::stringstream& stream, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets); bool _add_object_to_model_stream(std::stringstream& stream, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets);
bool _add_mesh_to_object_stream(std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets); bool _add_mesh_to_object_stream(std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets);
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items); bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items);
bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print); bool _add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print);
bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model); bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model);
}; };
@ -1477,6 +1628,14 @@ namespace Slic3r {
return false; return false;
} }
// adds layer height profile file
if (!_add_layer_height_profile_file_to_archive(archive, model))
{
mz_zip_writer_end(&archive);
boost::filesystem::remove(filename);
return false;
}
// adds slic3r print config file // adds slic3r print config file
if (export_print_config) if (export_print_config)
{ {
@ -1552,7 +1711,8 @@ namespace Slic3r {
{ {
std::stringstream stream; std::stringstream stream;
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">\n"; stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
stream << " <" << RESOURCES_TAG << ">\n"; stream << " <" << RESOURCES_TAG << ">\n";
BuildItemsList build_items; BuildItemsList build_items;
@ -1736,6 +1896,44 @@ namespace Slic3r {
return true; return true;
} }
bool _3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model)
{
std::string out = "";
char buffer[1024];
unsigned int count = 0;
for (const ModelObject* object : model.objects)
{
++count;
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
if ((layer_height_profile.size() >= 4) && ((layer_height_profile.size() % 2) == 0))
{
sprintf(buffer, "object_id=%d|", count);
out += buffer;
// Store the layer height profile as a single semicolon separated list.
for (size_t i = 0; i < layer_height_profile.size(); ++i)
{
sprintf(buffer, (i == 0) ? "%f" : ";%f", layer_height_profile[i]);
out += buffer;
}
out += "\n";
}
}
if (!out.empty())
{
if (!mz_zip_writer_add_mem(&archive, LAYER_HEIGHTS_PROFILE_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
{
add_error("Unable to add layer heights profile file to archive");
return false;
}
}
return true;
}
bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print) bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print)
{ {
char buffer[1024]; char buffer[1024];
@ -1744,10 +1942,13 @@ namespace Slic3r {
GCode::append_full_config(print, out); GCode::append_full_config(print, out);
if (!mz_zip_writer_add_mem(&archive, PRINT_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) if (!out.empty())
{ {
add_error("Unable to add print config file to archive"); if (!mz_zip_writer_add_mem(&archive, PRINT_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
return false; {
add_error("Unable to add print config file to archive");
return false;
}
} }
return true; return true;
@ -1832,10 +2033,7 @@ namespace Slic3r {
_3MF_Importer importer; _3MF_Importer importer;
bool res = importer.load_model_from_file(path, *model, *bundle); bool res = importer.load_model_from_file(path, *model, *bundle);
importer.log_errors();
if (!res)
importer.log_errors();
return res; return res;
} }

View File

@ -24,6 +24,12 @@
#include <assert.h> #include <assert.h>
// VERSION NUMBERS
// 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them.
// 1 : Introduction of amf versioning. No other change in data saved into amf files.
const unsigned int VERSION_AMF = 1;
const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version";
const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config"; const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config";
namespace Slic3r namespace Slic3r
@ -32,6 +38,7 @@ namespace Slic3r
struct AMFParserContext struct AMFParserContext
{ {
AMFParserContext(XML_Parser parser, const std::string& archive_filename, PresetBundle* preset_bundle, Model *model) : AMFParserContext(XML_Parser parser, const std::string& archive_filename, PresetBundle* preset_bundle, Model *model) :
m_version(0),
m_parser(parser), m_parser(parser),
m_model(*model), m_model(*model),
m_object(nullptr), m_object(nullptr),
@ -137,6 +144,8 @@ struct AMFParserContext
std::vector<Instance> instances; std::vector<Instance> instances;
}; };
// Version of the amf file
unsigned int m_version;
// Current Expat XML parser instance. // Current Expat XML parser instance.
XML_Parser m_parser; XML_Parser m_parser;
// Model to receive objects extracted from an AMF file. // Model to receive objects extracted from an AMF file.
@ -360,9 +369,9 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VERTEX: case NODE_TYPE_VERTEX:
assert(m_object); assert(m_object);
// Parse the vertex data // Parse the vertex data
m_object_vertices.emplace_back(atof(m_value[0].c_str())); m_object_vertices.emplace_back((float)atof(m_value[0].c_str()));
m_object_vertices.emplace_back(atof(m_value[1].c_str())); m_object_vertices.emplace_back((float)atof(m_value[1].c_str()));
m_object_vertices.emplace_back(atof(m_value[2].c_str())); m_object_vertices.emplace_back((float)atof(m_value[2].c_str()));
m_value[0].clear(); m_value[0].clear();
m_value[1].clear(); m_value[1].clear();
m_value[2].clear(); m_value[2].clear();
@ -462,6 +471,10 @@ void AMFParserContext::endElement(const char * /* name */)
if (m_volume && m_value[0] == "name") if (m_volume && m_value[0] == "name")
m_volume->name = std::move(m_value[1]); m_volume->name = std::move(m_value[1]);
} }
else if (strncmp(m_value[0].c_str(), SLIC3RPE_AMF_VERSION, strlen(SLIC3RPE_AMF_VERSION)) == 0) {
m_version = (unsigned int)atoi(m_value[1].c_str());
}
m_value[0].clear(); m_value[0].clear();
m_value[1].clear(); m_value[1].clear();
break; break;
@ -543,46 +556,8 @@ bool load_amf_file(const char *path, PresetBundle* bundle, Model *model)
return result; return result;
} }
// Load an AMF archive into a provided model. bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, const char* path, PresetBundle* bundle, Model* model, unsigned int& version)
bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
{ {
if ((path == nullptr) || (model == nullptr))
return false;
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
mz_bool res = mz_zip_reader_init_file(&archive, path, 0);
if (res == 0)
{
printf("Unable to init zip reader\n");
return false;
}
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
if (num_entries != 1)
{
printf("Found invalid number of entries\n");
mz_zip_reader_end(&archive);
return false;
}
mz_zip_archive_file_stat stat;
res = mz_zip_reader_file_stat(&archive, 0, &stat);
if (res == 0)
{
printf("Unable to extract entry statistics\n");
mz_zip_reader_end(&archive);
return false;
}
if (!boost::iends_with(stat.m_filename, ".amf"))
{
printf("Found invalid internal filename\n");
mz_zip_reader_end(&archive);
return false;
}
if (stat.m_uncomp_size == 0) if (stat.m_uncomp_size == 0)
{ {
printf("Found invalid size\n"); printf("Found invalid size\n");
@ -610,7 +585,7 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
return false; return false;
} }
res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0); mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0);
if (res == 0) if (res == 0)
{ {
printf("Error while reading model data to buffer\n"); printf("Error while reading model data to buffer\n");
@ -627,6 +602,62 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
ctx.endDocument(); ctx.endDocument();
version = ctx.m_version;
return true;
}
// Load an AMF archive into a provided model.
bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
{
if ((path == nullptr) || (model == nullptr))
return false;
unsigned int version = 0;
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
mz_bool res = mz_zip_reader_init_file(&archive, path, 0);
if (res == 0)
{
printf("Unable to init zip reader\n");
return false;
}
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
mz_zip_archive_file_stat stat;
// we first loop the entries to read from the archive the .amf file only, in order to extract the version from it
for (mz_uint i = 0; i < num_entries; ++i)
{
if (mz_zip_reader_file_stat(&archive, i, &stat))
{
if (boost::iends_with(stat.m_filename, ".amf"))
{
if (!extract_model_from_archive(archive, stat, path, bundle, model, version))
{
mz_zip_reader_end(&archive);
printf("Archive does not contain a valid model");
return false;
}
break;
}
}
}
#if 0 // forward compatibility
// we then loop again the entries to read other files stored in the archive
for (mz_uint i = 0; i < num_entries; ++i)
{
if (mz_zip_reader_file_stat(&archive, i, &stat))
{
// add code to extract the file
}
}
#endif // forward compatibility
mz_zip_reader_end(&archive); mz_zip_reader_end(&archive);
return true; return true;
} }
@ -664,6 +695,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
stream << "<amf unit=\"millimeter\">\n"; stream << "<amf unit=\"millimeter\">\n";
stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n"; stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n";
stream << "<metadata type=\"" << SLIC3RPE_AMF_VERSION << "\">" << VERSION_AMF << "</metadata>\n";
if (export_print_config) if (export_print_config)
{ {

View File

@ -718,10 +718,10 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
Helper::store_polyline(polyline, data, z, preview_data); Helper::store_polyline(polyline, data, z, preview_data);
// updates preview ranges data // updates preview ranges data
preview_data.ranges.height.set_from(height_range); preview_data.ranges.height.update_from(height_range);
preview_data.ranges.width.set_from(width_range); preview_data.ranges.width.update_from(width_range);
preview_data.ranges.feedrate.set_from(feedrate_range); preview_data.ranges.feedrate.update_from(feedrate_range);
preview_data.ranges.volumetric_rate.set_from(volumetric_rate_range); preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range);
} }
void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data) void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
@ -790,9 +790,9 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
Helper::store_polyline(polyline, type, direction, feedrate, extruder_id, preview_data); Helper::store_polyline(polyline, type, direction, feedrate, extruder_id, preview_data);
// updates preview ranges data // updates preview ranges data
preview_data.ranges.height.set_from(height_range); preview_data.ranges.height.update_from(height_range);
preview_data.ranges.width.set_from(width_range); preview_data.ranges.width.update_from(width_range);
preview_data.ranges.feedrate.set_from(feedrate_range); preview_data.ranges.feedrate.update_from(feedrate_range);
} }
void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data) void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data)

View File

@ -99,17 +99,31 @@ void GCodePreviewData::Range::set_from(const Range& other)
float GCodePreviewData::Range::step_size() const float GCodePreviewData::Range::step_size() const
{ {
return (max - min) / (float)Colors_Count; return (max - min) / (float)(Colors_Count - 1);
} }
const GCodePreviewData::Color& GCodePreviewData::Range::get_color_at_max() const GCodePreviewData::Color GCodePreviewData::Range::get_color_at(float value) const
{ {
return colors[Colors_Count - 1]; if (empty())
} return Color::Dummy;
const GCodePreviewData::Color& GCodePreviewData::Range::get_color_at(float value) const float global_t = (value - min) / step_size();
{
return empty() ? get_color_at_max() : colors[clamp((unsigned int)0, Colors_Count - 1, (unsigned int)((value - min) / step_size()))]; unsigned int low = (unsigned int)global_t;
unsigned int high = clamp((unsigned int)0, Colors_Count - 1, low + 1);
Color color_low = colors[low];
Color color_high = colors[high];
float local_t = global_t - (float)low;
// interpolate in RGB space
Color ret;
for (unsigned int i = 0; i < 4; ++i)
{
ret.rgba[i] = lerp(color_low.rgba[i], color_high.rgba[i], local_t);
}
return ret;
} }
GCodePreviewData::LegendItem::LegendItem(const std::string& text, const GCodePreviewData::Color& color) GCodePreviewData::LegendItem::LegendItem(const std::string& text, const GCodePreviewData::Color& color)
@ -266,22 +280,22 @@ const GCodePreviewData::Color& GCodePreviewData::get_extrusion_role_color(Extrus
return extrusion.role_colors[role]; return extrusion.role_colors[role];
} }
const GCodePreviewData::Color& GCodePreviewData::get_height_color(float height) const GCodePreviewData::Color GCodePreviewData::get_height_color(float height) const
{ {
return ranges.height.get_color_at(height); return ranges.height.get_color_at(height);
} }
const GCodePreviewData::Color& GCodePreviewData::get_width_color(float width) const GCodePreviewData::Color GCodePreviewData::get_width_color(float width) const
{ {
return ranges.width.get_color_at(width); return ranges.width.get_color_at(width);
} }
const GCodePreviewData::Color& GCodePreviewData::get_feedrate_color(float feedrate) const GCodePreviewData::Color GCodePreviewData::get_feedrate_color(float feedrate) const
{ {
return ranges.feedrate.get_color_at(feedrate); return ranges.feedrate.get_color_at(feedrate);
} }
const GCodePreviewData::Color& GCodePreviewData::get_volumetric_rate_color(float rate) const GCodePreviewData::Color GCodePreviewData::get_volumetric_rate_color(float rate) const
{ {
return ranges.volumetric_rate.get_color_at(rate); return ranges.volumetric_rate.get_color_at(rate);
} }
@ -370,10 +384,10 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
list.reserve(Range::Colors_Count); list.reserve(Range::Colors_Count);
float step = range.step_size(); float step = range.step_size();
for (unsigned int i = 0; i < Range::Colors_Count; ++i) for (int i = Range::Colors_Count - 1; i >= 0; --i)
{ {
char buf[1024]; char buf[1024];
sprintf(buf, "%.*f/%.*f", decimals, scale_factor * (range.min + (float)i * step), decimals, scale_factor * (range.min + (float)(i + 1) * step)); sprintf(buf, "%.*f", decimals, scale_factor * (range.min + (float)i * step));
list.emplace_back(buf, range.colors[i]); list.emplace_back(buf, range.colors[i]);
} }
} }
@ -408,7 +422,7 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
} }
case Extrusion::Feedrate: case Extrusion::Feedrate:
{ {
Helper::FillListFromRange(items, ranges.feedrate, 0, 1.0f); Helper::FillListFromRange(items, ranges.feedrate, 1, 1.0f);
break; break;
} }
case Extrusion::VolumetricRate: case Extrusion::VolumetricRate:

View File

@ -41,8 +41,7 @@ public:
void set_from(const Range& other); void set_from(const Range& other);
float step_size() const; float step_size() const;
const Color& get_color_at(float value) const; Color get_color_at(float value) const;
const Color& get_color_at_max() const;
}; };
struct Ranges struct Ranges
@ -189,10 +188,10 @@ public:
bool empty() const; bool empty() const;
const Color& get_extrusion_role_color(ExtrusionRole role) const; const Color& get_extrusion_role_color(ExtrusionRole role) const;
const Color& get_height_color(float height) const; Color get_height_color(float height) const;
const Color& get_width_color(float width) const; Color get_width_color(float width) const;
const Color& get_feedrate_color(float feedrate) const; Color get_feedrate_color(float feedrate) const;
const Color& get_volumetric_rate_color(float rate) const; Color get_volumetric_rate_color(float rate) const;
void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha); void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha);
void set_extrusion_paths_colors(const std::vector<std::string>& colors); void set_extrusion_paths_colors(const std::vector<std::string>& colors);

View File

@ -269,6 +269,10 @@ TriangleMesh Model::mesh() const
static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out) static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out)
{ {
if (sizes.empty())
// return if the list is empty or the following call to BoundingBoxf constructor will lead to a crash
return true;
// we supply unscaled data to arrange() // we supply unscaled data to arrange()
bool result = Slic3r::Geometry::arrange( bool result = Slic3r::Geometry::arrange(
sizes.size(), // number of parts sizes.size(), // number of parts

View File

@ -598,10 +598,10 @@ std::string Print::validate() const
return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1)."; return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).";
SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters(); SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters();
const PrintObject* most_layered_object = this->objects.front(); // object with highest layer_height_profile.size() encountered so far const PrintObject* tallest_object = this->objects.front(); // let's find the tallest object
for (const auto* object : objects) for (const auto* object : objects)
if (object->layer_height_profile.size() > most_layered_object->layer_height_profile.size()) if (*(object->layer_height_profile.end()-2) > *(tallest_object->layer_height_profile.end()-2) )
most_layered_object = object; tallest_object = object;
for (PrintObject *object : this->objects) { for (PrintObject *object : this->objects) {
SlicingParameters slicing_params = object->slicing_parameters(); SlicingParameters slicing_params = object->slicing_parameters();
@ -618,17 +618,26 @@ std::string Print::validate() const
object->update_layer_height_profile(); object->update_layer_height_profile();
object->layer_height_profile_valid = was_layer_height_profile_valid; object->layer_height_profile_valid = was_layer_height_profile_valid;
if ( this->config.variable_layer_height ) { if ( this->config.variable_layer_height ) { // comparing layer height profiles
int i = 0; bool failed = false;
while ( i < object->layer_height_profile.size() ) { if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size() ) {
if (std::abs(most_layered_object->layer_height_profile[i] - object->layer_height_profile[i]) > EPSILON) int i = 0;
return "The Wipe tower is only supported if all objects have the same layer height profile"; while ( i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) {
++i; if (std::abs(tallest_object->layer_height_profile[i] - object->layer_height_profile[i])) {
if (i == object->layer_height_profile.size()-2) // this element contains the objects max z, if the other object is taller, failed = true;
// it does not have to match - we will step over it break;
if (most_layered_object->layer_height_profile[i] > object->layer_height_profile[i]) }
++i; ++i;
if (i == object->layer_height_profile.size()-2) // this element contains this objects max z
if (tallest_object->layer_height_profile[i] > object->layer_height_profile[i]) // the difference does not matter in this case
++i;
}
} }
else
failed = true;
if (failed)
return "The Wipe tower is only supported if all objects have the same layer height profile";
} }
/*for (size_t i = 5; i < object->layer_height_profile.size(); i += 2) /*for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)

View File

@ -51,9 +51,6 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Point3>& fa
for (int i = 0; i < stl.stats.number_of_facets; i++) { for (int i = 0; i < stl.stats.number_of_facets; i++) {
stl_facet facet; stl_facet facet;
facet.normal.x = 0;
facet.normal.y = 0;
facet.normal.z = 0;
const Pointf3& ref_f1 = points[facets[i].x]; const Pointf3& ref_f1 = points[facets[i].x];
facet.vertex[0].x = ref_f1.x; facet.vertex[0].x = ref_f1.x;
@ -73,6 +70,13 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Point3>& fa
facet.extra[0] = 0; facet.extra[0] = 0;
facet.extra[1] = 0; facet.extra[1] = 0;
float normal[3];
stl_calculate_normal(normal, &facet);
stl_normalize_vector(normal);
facet.normal.x = normal[0];
facet.normal.y = normal[1];
facet.normal.z = normal[2];
stl.facet_start[i] = facet; stl.facet_start[i] = facet;
} }
stl_get_size(&stl); stl_get_size(&stl);

View File

@ -62,6 +62,9 @@ extern std::string timestamp_str();
// to be placed at the top of Slic3r generated files. // to be placed at the top of Slic3r generated files.
inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_FORK_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); } inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_FORK_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); }
// getpid platform wrapper
extern unsigned get_current_pid();
// Compute the next highest power of 2 of 32-bit v // Compute the next highest power of 2 of 32-bit v
// http://graphics.stanford.edu/~seander/bithacks.html // http://graphics.stanford.edu/~seander/bithacks.html
template<typename T> template<typename T>

View File

@ -1,6 +1,12 @@
#include <locale> #include <locale>
#include <ctime> #include <ctime>
#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <boost/log/core.hpp> #include <boost/log/core.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp> #include <boost/log/expressions.hpp>
@ -271,4 +277,13 @@ std::string timestamp_str()
return buf; return buf;
} }
unsigned get_current_pid()
{
#ifdef WIN32
return GetCurrentProcessId();
#else
return ::getpid();
#endif
}
}; // namespace Slic3r }; // namespace Slic3r

View File

@ -620,7 +620,7 @@ semver_numeric (semver_t *x) {
return num; return num;
} }
static char *semver_strdup(const char *src) { char *semver_strdup(const char *src) {
if (src == NULL) return NULL; if (src == NULL) return NULL;
size_t len = strlen(src) + 1; size_t len = strlen(src) + 1;
char *res = malloc(len); char *res = malloc(len);

View File

@ -98,6 +98,9 @@ semver_is_valid (const char *s);
int int
semver_clean (char *s); semver_clean (char *s);
char *
semver_strdup(const char *src);
semver_t semver_t
semver_copy(const semver_t *ver); semver_copy(const semver_t *ver);

View File

@ -117,11 +117,11 @@ void Snapshot::load_ini(const std::string &path)
if (! semver) if (! semver)
throw_on_parse_error("invalid " + kvp.first + " format for " + section.first); throw_on_parse_error("invalid " + kvp.first + " format for " + section.first);
if (kvp.first == "version") if (kvp.first == "version")
vc.version = *semver; vc.version.config_version = *semver;
else if (kvp.first == "min_slic3r_version") else if (kvp.first == "min_slic3r_version")
vc.min_slic3r_version = *semver; vc.version.min_slic3r_version = *semver;
else else
vc.max_slic3r_version = *semver; vc.version.max_slic3r_version = *semver;
} else if (boost::starts_with(kvp.first, key_prefix_model) && kvp.first.size() > key_prefix_model.size()) { } else if (boost::starts_with(kvp.first, key_prefix_model) && kvp.first.size() > key_prefix_model.size()) {
// Parse the printer variants installed for the current model. // Parse the printer variants installed for the current model.
auto &set_variants = vc.models_variants_installed[kvp.first.substr(key_prefix_model.size())]; auto &set_variants = vc.models_variants_installed[kvp.first.substr(key_prefix_model.size())];
@ -181,9 +181,9 @@ void Snapshot::save_ini(const std::string &path)
// Export the vendor configs. // Export the vendor configs.
for (const VendorConfig &vc : this->vendor_configs) { for (const VendorConfig &vc : this->vendor_configs) {
c << std::endl << "[Vendor:" << vc.name << "]" << std::endl; c << std::endl << "[Vendor:" << vc.name << "]" << std::endl;
c << "version = " << vc.version.to_string() << std::endl; c << "version = " << vc.version.config_version.to_string() << std::endl;
c << "min_slic3r_version = " << vc.min_slic3r_version.to_string() << std::endl; c << "min_slic3r_version = " << vc.version.min_slic3r_version.to_string() << std::endl;
c << "max_slic3r_version = " << vc.max_slic3r_version.to_string() << std::endl; c << "max_slic3r_version = " << vc.version.max_slic3r_version.to_string() << std::endl;
// Export installed printer models and their variants. // Export installed printer models and their variants.
for (const auto &model : vc.models_variants_installed) { for (const auto &model : vc.models_variants_installed) {
if (model.second.size() == 0) if (model.second.size() == 0)
@ -326,10 +326,10 @@ void SnapshotDB::update_slic3r_versions(std::vector<Index> &index_db)
for (Snapshot::VendorConfig &vendor_config : snapshot.vendor_configs) { for (Snapshot::VendorConfig &vendor_config : snapshot.vendor_configs) {
auto it = std::find_if(index_db.begin(), index_db.end(), [&vendor_config](const Index &idx) { return idx.vendor() == vendor_config.name; }); auto it = std::find_if(index_db.begin(), index_db.end(), [&vendor_config](const Index &idx) { return idx.vendor() == vendor_config.name; });
if (it != index_db.end()) { if (it != index_db.end()) {
Index::const_iterator it_version = it->find(vendor_config.version); Index::const_iterator it_version = it->find(vendor_config.version.config_version);
if (it_version != it->end()) { if (it_version != it->end()) {
vendor_config.min_slic3r_version = it_version->min_slic3r_version; vendor_config.version.min_slic3r_version = it_version->min_slic3r_version;
vendor_config.max_slic3r_version = it_version->max_slic3r_version; vendor_config.version.max_slic3r_version = it_version->max_slic3r_version;
} }
} }
} }
@ -395,16 +395,16 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LOAD_CFGBUNDLE_VENDOR_ONLY); bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LOAD_CFGBUNDLE_VENDOR_ONLY);
for (const VendorProfile &vp : bundle.vendors) for (const VendorProfile &vp : bundle.vendors)
if (vp.id == cfg.name) if (vp.id == cfg.name)
cfg.version = vp.config_version; cfg.version.config_version = vp.config_version;
// Fill-in the min/max slic3r version from the config index, if possible. // Fill-in the min/max slic3r version from the config index, if possible.
try { try {
// Load the config index for the vendor. // Load the config index for the vendor.
Index index; Index index;
index.load(data_dir / "vendor" / (cfg.name + ".idx")); index.load(data_dir / "vendor" / (cfg.name + ".idx"));
auto it = index.find(cfg.version); auto it = index.find(cfg.version.config_version);
if (it != index.end()) { if (it != index.end()) {
cfg.min_slic3r_version = it->min_slic3r_version; cfg.version.min_slic3r_version = it->min_slic3r_version;
cfg.max_slic3r_version = it->max_slic3r_version; cfg.version.max_slic3r_version = it->max_slic3r_version;
} }
} catch (const std::runtime_error &err) { } catch (const std::runtime_error &err) {
} }
@ -476,7 +476,7 @@ SnapshotDB::const_iterator SnapshotDB::snapshot_with_vendor_preset(const std::st
auto it_vendor_config = std::lower_bound(snapshot.vendor_configs.begin(), snapshot.vendor_configs.end(), auto it_vendor_config = std::lower_bound(snapshot.vendor_configs.begin(), snapshot.vendor_configs.end(),
key, [](const Snapshot::VendorConfig &cfg1, const Snapshot::VendorConfig &cfg2) { return cfg1.name < cfg2.name; }); key, [](const Snapshot::VendorConfig &cfg1, const Snapshot::VendorConfig &cfg2) { return cfg1.name < cfg2.name; });
if (it_vendor_config != snapshot.vendor_configs.end() && it_vendor_config->name == vendor_name && if (it_vendor_config != snapshot.vendor_configs.end() && it_vendor_config->name == vendor_name &&
config_version == it_vendor_config->version) { config_version == it_vendor_config->version.config_version) {
// Vendor config found with the correct version. // Vendor config found with the correct version.
// Save it, but continue searching, as we want the newest snapshot. // Save it, but continue searching, as we want the newest snapshot.
it_found = it; it_found = it;

View File

@ -18,7 +18,6 @@ class AppConfig;
namespace GUI { namespace GUI {
namespace Config { namespace Config {
class Version;
class Index; class Index;
// A snapshot contains: // A snapshot contains:
@ -76,12 +75,8 @@ public:
struct VendorConfig { struct VendorConfig {
// Name of the vendor contained in this snapshot. // Name of the vendor contained in this snapshot.
std::string name; std::string name;
// Version of the vendor config contained in this snapshot. // Version of the vendor config contained in this snapshot, along with compatibility data.
Semver version = Semver::invalid(); Version version;
// Minimum Slic3r version compatible with this vendor configuration.
Semver min_slic3r_version = Semver::zero();
// Maximum Slic3r version compatible with this vendor configuration, or empty.
Semver max_slic3r_version = Semver::inf();
// Which printer models of this vendor were installed, and which variants of the models? // Which printer models of this vendor were installed, and which variants of the models?
std::map<std::string, std::set<std::string>> models_variants_installed; std::map<std::string, std::set<std::string>> models_variants_installed;
}; };

View File

@ -13,7 +13,7 @@ namespace Slic3r {
namespace GUI { namespace GUI {
namespace Config { namespace Config {
static boost::optional<Semver> s_current_slic3r_semver = Semver::parse(SLIC3R_VERSION); static const Semver s_current_slic3r_semver(SLIC3R_VERSION);
// Optimized lexicographic compare of two pre-release versions, ignoring the numeric suffix. // Optimized lexicographic compare of two pre-release versions, ignoring the numeric suffix.
static int compare_prerelease(const char *p1, const char *p2) static int compare_prerelease(const char *p1, const char *p2)
@ -62,7 +62,7 @@ bool Version::is_slic3r_supported(const Semver &slic3r_version) const
bool Version::is_current_slic3r_supported() const bool Version::is_current_slic3r_supported() const
{ {
return this->is_slic3r_supported(*s_current_slic3r_semver); return this->is_slic3r_supported(s_current_slic3r_semver);
} }
#if 0 #if 0

View File

@ -1,3 +1,6 @@
#ifndef slic3r_2DBed_hpp_
#define slic3r_2DBed_hpp_
#include <wx/wx.h> #include <wx/wx.h>
#include "Config.hpp" #include "Config.hpp"
@ -45,3 +48,5 @@ public:
} // GUI } // GUI
} // Slic3r } // Slic3r
#endif /* slic3r_2DBed_hpp_ */

View File

@ -443,13 +443,16 @@ std::vector<int> GLVolumeCollection::load_object(
int GLVolumeCollection::load_wipe_tower_preview( int GLVolumeCollection::load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs) int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs)
{ {
float color[4] = { 1.0f, 1.0f, 0.0f, 0.5f }; float color[4] = { 0.5f, 0.5f, 0.0f, 0.5f };
this->volumes.emplace_back(new GLVolume(color)); this->volumes.emplace_back(new GLVolume(color));
GLVolume &v = *this->volumes.back(); GLVolume &v = *this->volumes.back();
auto mesh = make_cube(width, depth, height); if (height == 0.0f)
mesh.translate(-width/2.f,-depth/2.f,0.f); height = 0.1f;
Point origin_of_rotation(0.f,0.f);
auto mesh = make_cube(width, depth, height);
mesh.translate(-width / 2.f, -depth / 2.f, 0.f);
Point origin_of_rotation(0.f, 0.f);
mesh.rotate(rotation_angle,&origin_of_rotation); mesh.rotate(rotation_angle,&origin_of_rotation);
if (use_VBOs) if (use_VBOs)
@ -751,7 +754,10 @@ std::vector<double> GLVolumeCollection::get_current_print_zs() const
// Collect layer top positions of all volumes. // Collect layer top positions of all volumes.
std::vector<double> print_zs; std::vector<double> print_zs;
for (GLVolume *vol : this->volumes) for (GLVolume *vol : this->volumes)
append(print_zs, vol->print_zs); {
if (vol->is_active)
append(print_zs, vol->print_zs);
}
std::sort(print_zs.begin(), print_zs.end()); std::sort(print_zs.begin(), print_zs.end());
// Replace intervals of layers with similar top positions with their average value. // Replace intervals of layers with similar top positions with their average value.
@ -1757,6 +1763,11 @@ void _3DScene::load_gcode_preview(const Print* print, const GCodePreviewData* pr
{ {
_generate_legend_texture(*preview_data, tool_colors); _generate_legend_texture(*preview_data, tool_colors);
_load_shells(*print, *volumes, use_VBOs); _load_shells(*print, *volumes, use_VBOs);
// removes empty volumes
volumes->volumes.erase(std::remove_if(volumes->volumes.begin(), volumes->volumes.end(),
[](const GLVolume *volume) { return volume->print_zs.empty(); }),
volumes->volumes.end());
} }
} }

View File

@ -31,13 +31,14 @@ void AboutDialogLogo::onRepaint(wxEvent &event)
AboutDialog::AboutDialog() AboutDialog::AboutDialog()
: wxDialog(NULL, wxID_ANY, _(L("About Slic3r")), wxDefaultPosition, wxSize(600, 340), wxCAPTION) : wxDialog(NULL, wxID_ANY, _(L("About Slic3r")), wxDefaultPosition, wxSize(600, 340), wxCAPTION)
{ {
this->SetBackgroundColour(*wxWHITE); SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)/**wxWHITE*/);
wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL);
this->SetSizer(hsizer); this->SetSizer(hsizer);
// logo // logo
AboutDialogLogo* logo = new AboutDialogLogo(this); // AboutDialogLogo* logo = new AboutDialogLogo(this);
wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG);
auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(logo_bmp));
hsizer->Add(logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 30); hsizer->Add(logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 30);
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
@ -56,7 +57,7 @@ AboutDialog::AboutDialog()
// version // version
{ {
auto version_string = _(L("Version ")) + std::string(SLIC3R_VERSION); auto version_string = _(L("Version"))+ " " + std::string(SLIC3R_VERSION);
wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize); wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize);
wxFont version_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); wxFont version_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
#ifdef __WXMSW__ #ifdef __WXMSW__
@ -78,6 +79,7 @@ AboutDialog::AboutDialog()
int size[] = {11,11,11,11,11,11,11}; int size[] = {11,11,11,11,11,11,11};
#endif #endif
html->SetFonts(font.GetFaceName(), font.GetFaceName(), size); html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
html->SetHTMLBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
html->SetBorders(2); html->SetBorders(2);
const char* text = const char* text =
"<html>" "<html>"
@ -106,7 +108,6 @@ AboutDialog::AboutDialog()
this->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this); this->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
logo->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this); logo->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
html->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
} }
void AboutDialog::onLinkClicked(wxHtmlLinkEvent &event) void AboutDialog::onLinkClicked(wxHtmlLinkEvent &event)

View File

@ -21,6 +21,7 @@ namespace Slic3r {
static const std::string VENDOR_PREFIX = "vendor:"; static const std::string VENDOR_PREFIX = "vendor:";
static const std::string MODEL_PREFIX = "model:"; static const std::string MODEL_PREFIX = "model:";
static const std::string VERSION_CHECK_URL = "https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/Slic3rPE.version";
void AppConfig::reset() void AppConfig::reset()
{ {
@ -49,9 +50,6 @@ void AppConfig::set_defaults()
if (get("version_check").empty()) if (get("version_check").empty())
set("version_check", "1"); set("version_check", "1");
// TODO: proper URL
if (get("version_check_url").empty())
set("version_check_url", "https://gist.githubusercontent.com/vojtechkral/4d8fd4a3b8699a01ec892c264178461c/raw/2f05a64db19e45a7f8fe2cedeff555d544af679b/slic3rPE.version");
if (get("preset_update").empty()) if (get("preset_update").empty())
set("preset_update", "1"); set("preset_update", "1");
@ -100,7 +98,13 @@ void AppConfig::load()
// Figure out if datadir has legacy presets // Figure out if datadir has legacy presets
auto ini_ver = Semver::parse(get("version")); auto ini_ver = Semver::parse(get("version"));
m_legacy_datadir = ini_ver ? *ini_ver < Semver(1, 40, 0) : true; m_legacy_datadir = false;
if (ini_ver) {
// Make 1.40.0 alphas compare well
ini_ver->set_metadata(boost::none);
ini_ver->set_prerelease(boost::none);
m_legacy_datadir = ini_ver < Semver(1, 40, 0);
}
// Override missing or keys with their defaults. // Override missing or keys with their defaults.
this->set_defaults(); this->set_defaults();
@ -235,6 +239,12 @@ std::string AppConfig::config_path()
return (boost::filesystem::path(Slic3r::data_dir()) / "slic3r.ini").make_preferred().string(); return (boost::filesystem::path(Slic3r::data_dir()) / "slic3r.ini").make_preferred().string();
} }
std::string AppConfig::version_check_url() const
{
auto from_settings = get("version_check_url");
return from_settings.empty() ? VERSION_CHECK_URL : from_settings;
}
bool AppConfig::exists() bool AppConfig::exists()
{ {
return boost::filesystem::exists(AppConfig::config_path()); return boost::filesystem::exists(AppConfig::config_path());

View File

@ -92,9 +92,14 @@ public:
// Get the default config path from Slic3r::data_dir(). // Get the default config path from Slic3r::data_dir().
static std::string config_path(); static std::string config_path();
// Returns true if the user's data directory comes from before Slic3r 1.40.0 (no updating)
bool legacy_datadir() const { return m_legacy_datadir; } bool legacy_datadir() const { return m_legacy_datadir; }
// Get the Slic3r version check url.
// This returns a hardcoded string unless it is overriden by "version_check_url" in the ini file.
std::string version_check_url() const;
// Does the config file exist? // Does the config file exist?
static bool exists(); static bool exists();

View File

@ -1,3 +1,5 @@
#ifndef slic3r_BedShapeDialog_hpp_
#define slic3r_BedShapeDialog_hpp_
// The bed shape dialog. // The bed shape dialog.
// The dialog opens from Print Settins tab->Bed Shape : Set... // The dialog opens from Print Settins tab->Bed Shape : Set...
@ -49,3 +51,6 @@ public:
} // GUI } // GUI
} // Slic3r } // Slic3r
#endif /* slic3r_BedShapeDialog_hpp_ */

View File

@ -191,7 +191,7 @@ void BonjourDialog::on_timer(wxTimerEvent &)
label->SetLabel(wxString::Format("%s %s", search_str, dots)); label->SetLabel(wxString::Format("%s %s", search_str, dots));
timer_state = (timer_state) % 3 + 1; timer_state = (timer_state) % 3 + 1;
} else { } else {
label->SetLabel(wxString::Format("%s: %s", search_str, _(L("Finished.")))); label->SetLabel(wxString::Format("%s: %s", search_str, _(L("Finished"))+"."));
timer->Stop(); timer->Stop();
} }
} }

View File

@ -0,0 +1,84 @@
#include "ButtonsDescription.hpp"
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/statbmp.h>
#include <wx/clrpicker.h>
#include "GUI.hpp"
namespace Slic3r {
namespace GUI {
ButtonsDescription::ButtonsDescription(wxWindow* parent, t_icon_descriptions* icon_descriptions) :
wxDialog(parent, wxID_ANY, _(L("Buttons And Text Colors Description")), wxDefaultPosition, wxDefaultSize),
m_icon_descriptions(icon_descriptions)
{
auto grid_sizer = new wxFlexGridSizer(3, 20, 20);
auto main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(grid_sizer, 0, wxEXPAND | wxALL, 20);
// Icon description
for (auto pair : *m_icon_descriptions)
{
auto icon = new wxStaticBitmap(this, wxID_ANY, *pair.first);
grid_sizer->Add(icon, -1, wxALIGN_CENTRE_VERTICAL);
std::istringstream f(pair.second);
std::string s;
getline(f, s, ';');
auto description = new wxStaticText(this, wxID_ANY, _(s));
grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL);
getline(f, s, ';');
description = new wxStaticText(this, wxID_ANY, _(s));
grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
}
// Text color description
auto sys_label = new wxStaticText(this, wxID_ANY, _(L("Value is the same as the system value")));
sys_label->SetForegroundColour(get_label_clr_sys());
auto sys_colour = new wxColourPickerCtrl(this, wxID_ANY, get_label_clr_sys());
sys_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([sys_colour, sys_label](wxCommandEvent e)
{
sys_label->SetForegroundColour(sys_colour->GetColour());
sys_label->Refresh();
}));
size_t t= 0;
while (t < 3){
grid_sizer->Add(new wxStaticText(this, wxID_ANY, ""), -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
++t;
}
grid_sizer->Add(0, -1, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(sys_colour, -1, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(sys_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
auto mod_label = new wxStaticText(this, wxID_ANY, _(L("Value was changed and is not equal to the system value or the last saved preset")));
mod_label->SetForegroundColour(get_label_clr_modified());
auto mod_colour = new wxColourPickerCtrl(this, wxID_ANY, get_label_clr_modified());
mod_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([mod_colour, mod_label](wxCommandEvent e)
{
mod_label->SetForegroundColour(mod_colour->GetColour());
mod_label->Refresh();
}));
grid_sizer->Add(0, -1, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(mod_colour, -1, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(mod_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
auto buttons = CreateStdDialogButtonSizer(wxOK|wxCANCEL);
main_sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
wxButton* btn = static_cast<wxButton*>(FindWindowById(wxID_OK, this));
btn->Bind(wxEVT_BUTTON, [sys_colour, mod_colour, this](wxCommandEvent&) {
set_label_clr_sys(sys_colour->GetColour());
set_label_clr_modified(mod_colour->GetColour());
EndModal(wxID_OK);
});
SetSizer(main_sizer);
main_sizer->SetSizeHints(this);
}
} // GUI
} // Slic3r

View File

@ -0,0 +1,27 @@
#ifndef slic3r_ButtonsDescription_hpp
#define slic3r_ButtonsDescription_hpp
#include <wx/dialog.h>
#include <vector>
namespace Slic3r {
namespace GUI {
using t_icon_descriptions = std::vector<std::pair<wxBitmap*, std::string>>;
class ButtonsDescription : public wxDialog
{
t_icon_descriptions* m_icon_descriptions;
public:
ButtonsDescription(wxWindow* parent, t_icon_descriptions* icon_descriptions);
~ButtonsDescription(){}
};
} // GUI
} // Slic3r
#endif

View File

@ -8,49 +8,48 @@
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
static std::string format_reason(const Config::Snapshot::Reason reason) static wxString format_reason(const Config::Snapshot::Reason reason)
{ {
switch (reason) { switch (reason) {
case Config::Snapshot::SNAPSHOT_UPGRADE: case Config::Snapshot::SNAPSHOT_UPGRADE:
return std::string(_(L("Upgrade"))); return wxString(_(L("Upgrade")));
case Config::Snapshot::SNAPSHOT_DOWNGRADE: case Config::Snapshot::SNAPSHOT_DOWNGRADE:
return std::string(_(L("Downgrade"))); return wxString(_(L("Downgrade")));
case Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK: case Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK:
return std::string(_(L("Before roll back"))); return wxString(_(L("Before roll back")));
case Config::Snapshot::SNAPSHOT_USER: case Config::Snapshot::SNAPSHOT_USER:
return std::string(_(L("User"))); return wxString(_(L("User")));
case Config::Snapshot::SNAPSHOT_UNKNOWN: case Config::Snapshot::SNAPSHOT_UNKNOWN:
default: default:
return std::string(_(L("Unknown"))); return wxString(_(L("Unknown")));
} }
} }
static std::string generate_html_row(const Config::Snapshot &snapshot, bool row_even, bool snapshot_active) static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_even, bool snapshot_active)
{ {
// Start by declaring a row with an alternating background color. // Start by declaring a row with an alternating background color.
std::string text = "<tr bgcolor=\""; wxString text = "<tr bgcolor=\"";
text += snapshot_active ? "#B3FFCB" : (row_even ? "#FFFFFF" : "#D5D5D5"); text += snapshot_active ? "#B3FFCB" : (row_even ? "#FFFFFF" : "#D5D5D5");
text += "\">"; text += "\">";
text += "<td>"; text += "<td>";
// Format the row header. // Format the row header.
text += std::string("<font size=\"5\"><b>") + (snapshot_active ? _(L("Active: ")) : "") + text += wxString("<font size=\"5\"><b>") + (snapshot_active ? _(L("Active: ")) : "") +
Utils::format_local_date_time(snapshot.time_captured) + ": " + format_reason(snapshot.reason); Utils::format_local_date_time(snapshot.time_captured) + ": " + format_reason(snapshot.reason);
if (! snapshot.comment.empty()) if (! snapshot.comment.empty())
text += " (" + snapshot.comment + ")"; text += " (" + snapshot.comment + ")";
text += "</b></font><br>"; text += "</b></font><br>";
// End of row header. // End of row header.
// text += _(L("ID:")) + " " + snapshot.id + "<br>";
// text += _(L("time captured:")) + " " + Utils::format_local_date_time(snapshot.time_captured) + "<br>";
text += _(L("slic3r version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>"; text += _(L("slic3r version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>";
// text += "reason: " + snapshot.reason + "<br>";
text += _(L("print")) + ": " + snapshot.print + "<br>"; text += _(L("print")) + ": " + snapshot.print + "<br>";
text += _(L("filaments")) + ": " + snapshot.filaments.front() + "<br>"; text += _(L("filaments")) + ": " + snapshot.filaments.front() + "<br>";
text += _(L("printer")) + ": " + snapshot.printer + "<br>"; text += _(L("printer")) + ": " + snapshot.printer + "<br>";
bool compatible = true;
for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) { for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) {
text += _(L("vendor")) + ": " + vc.name + ", ver: " + vc.version.to_string() + ", min slic3r ver: " + vc.min_slic3r_version.to_string(); text += _(L("vendor")) + ": " + vc.name +", " + _(L("version")) + ": " + vc.version.config_version.to_string() +
if (vc.max_slic3r_version != Semver::inf()) ", " + _(L("min slic3r version")) + ": " + vc.version.min_slic3r_version.to_string();
text += ", max slic3r ver: " + vc.max_slic3r_version.to_string(); if (vc.version.max_slic3r_version != Semver::inf())
text += ", " + _(L("max slic3r version")) + ": " + vc.version.max_slic3r_version.to_string();
text += "<br>"; text += "<br>";
for (const std::pair<std::string, std::set<std::string>> &model : vc.models_variants_installed) { for (const std::pair<std::string, std::set<std::string>> &model : vc.models_variants_installed) {
text += _(L("model")) + ": " + model.first + ", " + _(L("variants")) + ": "; text += _(L("model")) + ": " + model.first + ", " + _(L("variants")) + ": ";
@ -61,18 +60,22 @@ static std::string generate_html_row(const Config::Snapshot &snapshot, bool row_
} }
text += "<br>"; text += "<br>";
} }
if (! vc.version.is_current_slic3r_supported()) { compatible = false; }
} }
if (! snapshot_active) if (! compatible) {
text += "<p align=\"right\">" + _(L("Incompatible with this Slic3r")) + "</p>";
}
else if (! snapshot_active)
text += "<p align=\"right\"><a href=\"" + snapshot.id + "\">" + _(L("Activate")) + "</a></p>"; text += "<p align=\"right\"><a href=\"" + snapshot.id + "\">" + _(L("Activate")) + "</a></p>";
text += "</td>"; text += "</td>";
text += "</tr>"; text += "</tr>";
return text; return text;
} }
static std::string generate_html_page(const Config::SnapshotDB &snapshot_db, const std::string &on_snapshot) static wxString generate_html_page(const Config::SnapshotDB &snapshot_db, const wxString &on_snapshot)
{ {
std::string text = wxString text =
"<html>" "<html>"
"<body bgcolor=\"#ffffff\" cellspacing=\"2\" cellpadding=\"0\" border=\"0\" link=\"#800000\">" "<body bgcolor=\"#ffffff\" cellspacing=\"2\" cellpadding=\"0\" border=\"0\" link=\"#800000\">"
"<font color=\"#000000\">"; "<font color=\"#000000\">";
@ -89,7 +92,7 @@ static std::string generate_html_page(const Config::SnapshotDB &snapshot_db, con
return text; return text;
} }
ConfigSnapshotDialog::ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db, const std::string &on_snapshot) ConfigSnapshotDialog::ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db, const wxString &on_snapshot)
: wxDialog(NULL, wxID_ANY, _(L("Configuration Snapshots")), wxDefaultPosition, wxSize(600, 500), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX) : wxDialog(NULL, wxID_ANY, _(L("Configuration Snapshots")), wxDefaultPosition, wxSize(600, 500), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX)
{ {
this->SetBackgroundColour(*wxWHITE); this->SetBackgroundColour(*wxWHITE);
@ -108,8 +111,8 @@ ConfigSnapshotDialog::ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db
#endif #endif
html->SetFonts(font.GetFaceName(), font.GetFaceName(), size); html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
html->SetBorders(2); html->SetBorders(2);
std::string text = generate_html_page(snapshot_db, on_snapshot); wxString text = generate_html_page(snapshot_db, on_snapshot);
html->SetPage(text.c_str()); html->SetPage(text);
vsizer->Add(html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 0); vsizer->Add(html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 0);
html->Bind(wxEVT_HTML_LINK_CLICKED, &ConfigSnapshotDialog::onLinkClicked, this); html->Bind(wxEVT_HTML_LINK_CLICKED, &ConfigSnapshotDialog::onLinkClicked, this);
} }

View File

@ -17,7 +17,7 @@ namespace Config {
class ConfigSnapshotDialog : public wxDialog class ConfigSnapshotDialog : public wxDialog
{ {
public: public:
ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db, const std::string &id); ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db, const wxString &id);
const std::string& snapshot_to_activate() const { return m_snapshot_to_activate; } const std::string& snapshot_to_activate() const { return m_snapshot_to_activate; }
private: private:

View File

@ -4,6 +4,8 @@
#include <utility> #include <utility>
#include <unordered_map> #include <unordered_map>
#include <boost/log/trivial.hpp>
#include <wx/settings.h> #include <wx/settings.h>
#include <wx/stattext.h> #include <wx/stattext.h>
#include <wx/textctrl.h> #include <wx/textctrl.h>
@ -205,15 +207,16 @@ PageWelcome::PageWelcome(ConfigWizard *parent) :
ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))), ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))),
printer_picker(nullptr), printer_picker(nullptr),
others_buttons(new wxPanel(parent)), others_buttons(new wxPanel(parent)),
cbox_reset(new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)")))) cbox_reset(nullptr)
{ {
if (wizard_p()->flag_startup && wizard_p()->flag_empty_datadir) { if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) {
wxString::Format(_(L("Run %s")), ConfigWizard::name()); wxString::Format(_(L("Run %s")), ConfigWizard::name());
append_text(wxString::Format( append_text(wxString::Format(
_(L("Hello, welcome to Slic3r Prusa Edition! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")), _(L("Hello, welcome to Slic3r Prusa Edition! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")),
ConfigWizard::name()) ConfigWizard::name())
); );
} else { } else {
cbox_reset = new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)")));
append(cbox_reset); append(cbox_reset);
} }
@ -708,7 +711,7 @@ void ConfigWizard::priv::on_custom_setup()
set_page(page_firmware); set_page(page_firmware);
} }
void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, PresetUpdater *updater) void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater)
{ {
const bool is_custom_setup = page_welcome->page_next() == page_firmware; const bool is_custom_setup = page_welcome->page_next() == page_firmware;
@ -729,22 +732,30 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
install_bundles.emplace_back(vendor_rsrc.second); install_bundles.emplace_back(vendor_rsrc.second);
} }
// If the datadir was empty don't take a snapshot (it would just be an empty snapshot) // Decide whether to create snapshot based on run_reason and the reset profile checkbox
const bool snapshot = !flag_empty_datadir || page_welcome->reset_user_profile(); 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) { if (install_bundles.size() > 0) {
// Install bundles from resources. // Install bundles from resources.
updater->install_bundles_rsrc(std::move(install_bundles), snapshot); updater->install_bundles_rsrc(std::move(install_bundles), snapshot);
} else {
BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources";
} }
if (page_welcome->reset_user_profile()) { if (page_welcome->reset_user_profile()) {
BOOST_LOG_TRIVIAL(info) << "Resetting user profiles...";
preset_bundle->reset(true); preset_bundle->reset(true);
} }
app_config->set_vendors(appconfig_vendors); app_config->set_vendors(appconfig_vendors);
app_config->set("version_check", page_update->version_check ? "1" : "0"); app_config->set("version_check", page_update->version_check ? "1" : "0");
app_config->set("preset_update", page_update->preset_update ? "1" : "0"); app_config->set("preset_update", page_update->preset_update ? "1" : "0");
if (flag_startup) app_config->reset_selections();
app_config->reset_selections();
// ^ TODO: replace with appropriate printer selection // ^ TODO: replace with appropriate printer selection
preset_bundle->load_presets(*app_config); preset_bundle->load_presets(*app_config);
} else { } else {
@ -759,12 +770,11 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
// Public // Public
ConfigWizard::ConfigWizard(wxWindow *parent, bool startup, bool empty_datadir) : ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
wxDialog(parent, wxID_ANY, name(), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), wxDialog(parent, wxID_ANY, name(), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
p(new priv(this)) p(new priv(this))
{ {
p->flag_startup = startup; p->run_reason = reason;
p->flag_empty_datadir = empty_datadir;
p->load_vendors(); p->load_vendors();
p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({ p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({
@ -821,10 +831,16 @@ ConfigWizard::ConfigWizard(wxWindow *parent, bool startup, bool empty_datadir) :
ConfigWizard::~ConfigWizard() {} ConfigWizard::~ConfigWizard() {}
void ConfigWizard::run(PresetBundle *preset_bundle, PresetUpdater *updater) bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater)
{ {
BOOST_LOG_TRIVIAL(info) << "Running ConfigWizard, reason: " << p->run_reason;
if (ShowModal() == wxID_OK) { if (ShowModal() == wxID_OK) {
p->apply_config(GUI::get_app_config(), preset_bundle, updater); p->apply_config(GUI::get_app_config(), preset_bundle, updater);
BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied";
return true;
} else {
BOOST_LOG_TRIVIAL(info) << "ConfigWizard cancelled";
return false;
} }
} }

View File

@ -16,14 +16,23 @@ namespace GUI {
class ConfigWizard: public wxDialog class ConfigWizard: public wxDialog
{ {
public: public:
ConfigWizard(wxWindow *parent, bool startup, bool empty_datadir); // Why is the Wizard run
enum RunReason {
RR_DATA_EMPTY, // No or empty datadir
RR_DATA_LEGACY, // Pre-updating datadir
RR_DATA_INCOMPAT, // Incompatible datadir - Slic3r downgrade situation
RR_USER, // User requested the Wizard from the menus
};
ConfigWizard(wxWindow *parent, RunReason run_reason);
ConfigWizard(ConfigWizard &&) = delete; ConfigWizard(ConfigWizard &&) = delete;
ConfigWizard(const ConfigWizard &) = delete; ConfigWizard(const ConfigWizard &) = delete;
ConfigWizard &operator=(ConfigWizard &&) = delete; ConfigWizard &operator=(ConfigWizard &&) = delete;
ConfigWizard &operator=(const ConfigWizard &) = delete; ConfigWizard &operator=(const ConfigWizard &) = delete;
~ConfigWizard(); ~ConfigWizard();
void run(PresetBundle *preset_bundle, PresetUpdater *updater); // Run the Wizard. Return whether it was completed.
bool run(PresetBundle *preset_bundle, const PresetUpdater *updater);
static const wxString& name(); static const wxString& name();
private: private:

View File

@ -107,7 +107,7 @@ struct PageWelcome: ConfigWizardPage
virtual wxPanel* extra_buttons() { return others_buttons; } virtual wxPanel* extra_buttons() { return others_buttons; }
virtual void on_page_set(); virtual void on_page_set();
bool reset_user_profile() const { return cbox_reset->GetValue(); } bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; }
void on_variant_checked(); void on_variant_checked();
}; };
@ -190,8 +190,7 @@ private:
struct ConfigWizard::priv struct ConfigWizard::priv
{ {
ConfigWizard *q; ConfigWizard *q;
bool flag_startup; ConfigWizard::RunReason run_reason;
bool flag_empty_datadir;
AppConfig appconfig_vendors; AppConfig appconfig_vendors;
std::unordered_map<std::string, VendorProfile> vendors; std::unordered_map<std::string, VendorProfile> vendors;
std::unordered_map<std::string, std::string> vendors_rsrc; std::unordered_map<std::string, std::string> vendors_rsrc;
@ -228,7 +227,7 @@ struct ConfigWizard::priv
void on_other_vendors(); void on_other_vendors();
void on_custom_setup(); void on_custom_setup();
void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, PresetUpdater *updater); void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
}; };

View File

@ -20,21 +20,22 @@ namespace Slic3r { namespace GUI {
void Field::PostInitialize(){ void Field::PostInitialize(){
auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
m_Undo_btn = new wxButton(m_parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); m_Undo_btn = new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
m_Undo_to_sys_btn = new wxButton(m_parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); m_Undo_to_sys_btn = new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
if (wxMSW) { if (wxMSW) {
m_Undo_btn->SetBackgroundColour(color); m_Undo_btn->SetBackgroundColour(color);
m_Undo_to_sys_btn->SetBackgroundColour(color); m_Undo_to_sys_btn->SetBackgroundColour(color);
} }
m_Undo_btn->SetBitmap(wxBitmap(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG));
m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_initial_value(); })); m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_initial_value(); }));
m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_sys_value(); })); m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_sys_value(); }));
BUILD(); //set default bitmap
} wxBitmap bmp;
bmp.LoadFile(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG);
set_undo_bitmap(&bmp);
set_undo_to_sys_bitmap(&bmp);
void Field::set_nonsys_btn_icon(const std::string& icon){ BUILD();
m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(icon)), wxBITMAP_TYPE_PNG));
} }
void Field::on_kill_focus(wxEvent& event) { void Field::on_kill_focus(wxEvent& event) {
@ -81,12 +82,13 @@ namespace Slic3r { namespace GUI {
return std::regex_match(string, regex_pattern); return std::regex_match(string, regex_pattern);
} }
boost::any Field::get_value_by_opt_type(wxString& str) // boost::any Field::get_value_by_opt_type(wxString& str)
void Field::get_value_by_opt_type(wxString& str)
{ {
boost::any ret_val; // boost::any m_value;
switch (m_opt.type){ switch (m_opt.type){
case coInt: case coInt:
ret_val = wxAtoi(str); m_value = wxAtoi(str);
break; break;
case coPercent: case coPercent:
case coPercents: case coPercents:
@ -96,18 +98,18 @@ namespace Slic3r { namespace GUI {
str.RemoveLast(); str.RemoveLast();
double val; double val;
str.ToCDouble(&val); str.ToCDouble(&val);
ret_val = val; m_value = val;
break; } break; }
case coString: case coString:
case coStrings: case coStrings:
case coFloatOrPercent: case coFloatOrPercent:
ret_val = str.ToStdString(); m_value = str.ToStdString();
break; break;
default: default:
break; break;
} }
return ret_val; // return m_value;
} }
void TextCtrl::BUILD() { void TextCtrl::BUILD() {
@ -183,12 +185,12 @@ namespace Slic3r { namespace GUI {
window = dynamic_cast<wxWindow*>(temp); window = dynamic_cast<wxWindow*>(temp);
} }
boost::any TextCtrl::get_value() boost::any& TextCtrl::get_value()
{ {
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue(); wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
boost::any ret_val = get_value_by_opt_type(ret_str); /*boost::any ret_val*/get_value_by_opt_type(ret_str);
return ret_val; return m_value;//ret_val;
} }
void TextCtrl::enable() { dynamic_cast<wxTextCtrl*>(window)->Enable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(true); } void TextCtrl::enable() { dynamic_cast<wxTextCtrl*>(window)->Enable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(true); }
@ -216,15 +218,15 @@ void CheckBox::BUILD() {
window = dynamic_cast<wxWindow*>(temp); window = dynamic_cast<wxWindow*>(temp);
} }
boost::any CheckBox::get_value() boost::any& CheckBox::get_value()
{ {
boost::any ret_val; // boost::any m_value;
bool value = dynamic_cast<wxCheckBox*>(window)->GetValue(); bool value = dynamic_cast<wxCheckBox*>(window)->GetValue();
if (m_opt.type == coBool) if (m_opt.type == coBool)
ret_val = static_cast<bool>(value); m_value = static_cast<bool>(value);
else else
ret_val = static_cast<unsigned char>(value); m_value = static_cast<unsigned char>(value);
return ret_val; return m_value;
} }
int undef_spin_val = -9999; //! Probably, It's not necessary int undef_spin_val = -9999; //! Probably, It's not necessary
@ -424,7 +426,33 @@ void Choice::set_value(const boost::any& value, bool change_event)
break; break;
} }
case coEnum:{ case coEnum:{
dynamic_cast<wxComboBox*>(window)->SetSelection(boost::any_cast<int>(value)); int val = boost::any_cast<int>(value);
if (m_opt_id.compare("external_fill_pattern") == 0)
{
if (!m_opt.enum_values.empty()){
std::string key;
t_config_enum_values map_names = ConfigOptionEnum<InfillPattern>::get_enum_values();
for (auto it : map_names) {
if (val == it.second) {
key = it.first;
break;
}
}
size_t idx = 0;
for (auto el : m_opt.enum_values)
{
if (el.compare(key) == 0)
break;
++idx;
}
val = idx == m_opt.enum_values.size() ? 0 : idx;
}
else
val = 0;
}
dynamic_cast<wxComboBox*>(window)->SetSelection(val);
break; break;
} }
default: default:
@ -454,16 +482,16 @@ void Choice::set_values(const std::vector<std::string>& values)
m_disable_change_event = false; m_disable_change_event = false;
} }
boost::any Choice::get_value() boost::any& Choice::get_value()
{ {
boost::any ret_val; // boost::any m_value;
wxString ret_str = static_cast<wxComboBox*>(window)->GetValue(); wxString ret_str = static_cast<wxComboBox*>(window)->GetValue();
if (m_opt_id == "support") if (m_opt_id == "support")
return ret_str; return m_value = boost::any(ret_str);//ret_str;
if (m_opt.type != coEnum) if (m_opt.type != coEnum)
ret_val = get_value_by_opt_type(ret_str); /*m_value = */get_value_by_opt_type(ret_str);
else else
{ {
int ret_enum = static_cast<wxComboBox*>(window)->GetSelection(); int ret_enum = static_cast<wxComboBox*>(window)->GetSelection();
@ -474,22 +502,22 @@ boost::any Choice::get_value()
t_config_enum_values map_names = ConfigOptionEnum<InfillPattern>::get_enum_values(); t_config_enum_values map_names = ConfigOptionEnum<InfillPattern>::get_enum_values();
int value = map_names.at(key); int value = map_names.at(key);
ret_val = static_cast<InfillPattern>(value); m_value = static_cast<InfillPattern>(value);
} }
else else
ret_val = static_cast<InfillPattern>(0); m_value = static_cast<InfillPattern>(0);
} }
if (m_opt_id.compare("fill_pattern") == 0) if (m_opt_id.compare("fill_pattern") == 0)
ret_val = static_cast<InfillPattern>(ret_enum); m_value = static_cast<InfillPattern>(ret_enum);
else if (m_opt_id.compare("gcode_flavor") == 0) else if (m_opt_id.compare("gcode_flavor") == 0)
ret_val = static_cast<GCodeFlavor>(ret_enum); m_value = static_cast<GCodeFlavor>(ret_enum);
else if (m_opt_id.compare("support_material_pattern") == 0) else if (m_opt_id.compare("support_material_pattern") == 0)
ret_val = static_cast<SupportMaterialPattern>(ret_enum); m_value = static_cast<SupportMaterialPattern>(ret_enum);
else if (m_opt_id.compare("seam_position") == 0) else if (m_opt_id.compare("seam_position") == 0)
ret_val = static_cast<SeamPosition>(ret_enum); m_value = static_cast<SeamPosition>(ret_enum);
} }
return ret_val; return m_value;
} }
void ColourPicker::BUILD() void ColourPicker::BUILD()
@ -509,14 +537,14 @@ void ColourPicker::BUILD()
temp->SetToolTip(get_tooltip_text(clr)); temp->SetToolTip(get_tooltip_text(clr));
} }
boost::any ColourPicker::get_value(){ boost::any& ColourPicker::get_value(){
boost::any ret_val; // boost::any m_value;
auto colour = static_cast<wxColourPickerCtrl*>(window)->GetColour(); auto colour = static_cast<wxColourPickerCtrl*>(window)->GetColour();
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue()); auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue());
ret_val = clr_str.ToStdString(); m_value = clr_str.ToStdString();
return ret_val; return m_value;
} }
void PointCtrl::BUILD() void PointCtrl::BUILD()
@ -580,7 +608,7 @@ void PointCtrl::set_value(const boost::any& value, bool change_event)
set_value(pt, change_event); set_value(pt, change_event);
} }
boost::any PointCtrl::get_value() boost::any& PointCtrl::get_value()
{ {
Pointf ret_point; Pointf ret_point;
double val; double val;
@ -588,7 +616,7 @@ boost::any PointCtrl::get_value()
ret_point.x = val; ret_point.x = val;
y_textctrl->GetValue().ToDouble(&val); y_textctrl->GetValue().ToDouble(&val);
ret_point.y = val; ret_point.y = val;
return ret_point; return m_value = ret_point;
} }
} // GUI } // GUI

View File

@ -36,6 +36,24 @@ using t_back_to_init = std::function<void(const std::string&)>;
wxString double_to_string(double const value); wxString double_to_string(double const value);
class MyButton : public wxButton
{
public:
MyButton() {}
MyButton(wxWindow* parent, wxWindowID id, const wxString& label = wxEmptyString,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = 0,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxTextCtrlNameStr)
{
this->Create(parent, id, label, pos, size, style, validator, name);
}
// overridden from wxWindow base class
virtual bool
AcceptsFocusFromKeyboard() const { return false; }
};
class Field { class Field {
protected: protected:
// factory function to defer and enforce creation of derived type. // factory function to defer and enforce creation of derived type.
@ -85,22 +103,18 @@ public:
/// Gets a boost::any representing this control. /// Gets a boost::any representing this control.
/// subclasses should overload with a specific version /// subclasses should overload with a specific version
virtual boost::any get_value() = 0; virtual boost::any& get_value() = 0;
virtual void enable() = 0; virtual void enable() = 0;
virtual void disable() = 0; virtual void disable() = 0;
wxStaticText* m_Label = nullptr; /// Fires the enable or disable function, based on the input.
wxButton* m_Undo_btn = nullptr;
wxButton* m_Undo_to_sys_btn = nullptr;
/// Fires the enable or disable function, based on the input.
inline void toggle(bool en) { en ? enable() : disable(); } inline void toggle(bool en) { en ? enable() : disable(); }
virtual wxString get_tooltip_text(const wxString& default_string); virtual wxString get_tooltip_text(const wxString& default_string);
// set icon to "UndoToSystemValue" button according to an inheritance of preset // set icon to "UndoToSystemValue" button according to an inheritance of preset
void set_nonsys_btn_icon(const std::string& icon); // void set_nonsys_btn_icon(const wxBitmap& icon);
Field(const ConfigOptionDef& opt, const t_config_option_key& id) : m_opt(opt), m_opt_id(id) {}; Field(const ConfigOptionDef& opt, const t_config_option_key& id) : m_opt(opt), m_opt_id(id) {};
Field(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : m_parent(parent), m_opt(opt), m_opt_id(id) {}; Field(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : m_parent(parent), m_opt(opt), m_opt_id(id) {};
@ -110,7 +124,8 @@ public:
virtual wxWindow* getWindow() { return nullptr; } virtual wxWindow* getWindow() { return nullptr; }
bool is_matched(const std::string& string, const std::string& pattern); bool is_matched(const std::string& string, const std::string& pattern);
boost::any get_value_by_opt_type(wxString& str); // boost::any get_value_by_opt_type(wxString& str);
void get_value_by_opt_type(wxString& str);
/// Factory method for generating new derived classes. /// Factory method for generating new derived classes.
template<class T> template<class T>
@ -120,6 +135,78 @@ public:
p->PostInitialize(); p->PostInitialize();
return std::move(p); //!p; return std::move(p); //!p;
} }
bool set_undo_bitmap(const wxBitmap *bmp) {
if (m_undo_bitmap != bmp) {
m_undo_bitmap = bmp;
m_Undo_btn->SetBitmap(*bmp);
return true;
}
return false;
}
bool set_undo_to_sys_bitmap(const wxBitmap *bmp) {
if (m_undo_to_sys_bitmap != bmp) {
m_undo_to_sys_bitmap = bmp;
m_Undo_to_sys_btn->SetBitmap(*bmp);
return true;
}
return false;
}
bool set_label_colour(const wxColour *clr) {
if (m_Label == nullptr) return false;
if (m_label_color != clr) {
m_label_color = clr;
m_Label->SetForegroundColour(*clr);
m_Label->Refresh(true);
}
return false;
}
bool set_label_colour_force(const wxColour *clr) {
if (m_Label == nullptr) return false;
m_Label->SetForegroundColour(*clr);
m_Label->Refresh(true);
return false;
}
bool set_undo_tooltip(const wxString *tip) {
if (m_undo_tooltip != tip) {
m_undo_tooltip = tip;
m_Undo_btn->SetToolTip(*tip);
return true;
}
return false;
}
bool set_undo_to_sys_tooltip(const wxString *tip) {
if (m_undo_to_sys_tooltip != tip) {
m_undo_to_sys_tooltip = tip;
m_Undo_to_sys_btn->SetToolTip(*tip);
return true;
}
return false;
}
protected:
MyButton* m_Undo_btn = nullptr;
// Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
const wxBitmap* m_undo_bitmap = nullptr;
const wxString* m_undo_tooltip = nullptr;
MyButton* m_Undo_to_sys_btn = nullptr;
// Bitmap and Tooltip text for m_Undo_to_sys_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
const wxBitmap* m_undo_to_sys_bitmap = nullptr;
const wxString* m_undo_to_sys_tooltip = nullptr;
wxStaticText* m_Label = nullptr;
// Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
const wxColour* m_label_color = nullptr;
// current value
boost::any m_value;
friend class OptionsGroup;
}; };
/// Convenience function, accepts a const reference to t_field and checks to see whether /// Convenience function, accepts a const reference to t_field and checks to see whether
@ -153,7 +240,7 @@ public:
m_disable_change_event = false; m_disable_change_event = false;
} }
boost::any get_value() override; boost::any& get_value() override;
virtual void enable(); virtual void enable();
virtual void disable(); virtual void disable();
@ -180,7 +267,7 @@ public:
dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value)); dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
m_disable_change_event = false; m_disable_change_event = false;
} }
boost::any get_value() override; boost::any& get_value() override;
void enable() override { dynamic_cast<wxCheckBox*>(window)->Enable(); } void enable() override { dynamic_cast<wxCheckBox*>(window)->Enable(); }
void disable() override { dynamic_cast<wxCheckBox*>(window)->Disable(); } void disable() override { dynamic_cast<wxCheckBox*>(window)->Disable(); }
@ -210,8 +297,9 @@ public:
dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value); dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value);
m_disable_change_event = false; m_disable_change_event = false;
} }
boost::any get_value() override { boost::any& get_value() override {
return boost::any(tmp_value); // return boost::any(tmp_value);
return m_value = tmp_value;
} }
void enable() override { dynamic_cast<wxSpinCtrl*>(window)->Enable(); } void enable() override { dynamic_cast<wxSpinCtrl*>(window)->Enable(); }
@ -233,7 +321,7 @@ public:
void set_value(const std::string& value, bool change_event = false); void set_value(const std::string& value, bool change_event = false);
void set_value(const boost::any& value, bool change_event = false); void set_value(const boost::any& value, bool change_event = false);
void set_values(const std::vector<std::string> &values); void set_values(const std::vector<std::string> &values);
boost::any get_value() override; boost::any& get_value() override;
void enable() override { dynamic_cast<wxComboBox*>(window)->Enable(); }; void enable() override { dynamic_cast<wxComboBox*>(window)->Enable(); };
void disable() override{ dynamic_cast<wxComboBox*>(window)->Disable(); }; void disable() override{ dynamic_cast<wxComboBox*>(window)->Disable(); };
@ -261,7 +349,7 @@ public:
m_disable_change_event = false; m_disable_change_event = false;
} }
boost::any get_value() override; boost::any& get_value() override;
void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); }; void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); };
void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); }; void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); };
@ -283,7 +371,7 @@ public:
void set_value(const Pointf& value, bool change_event = false); void set_value(const Pointf& value, bool change_event = false);
void set_value(const boost::any& value, bool change_event = false); void set_value(const boost::any& value, bool change_event = false);
boost::any get_value() override; boost::any& get_value() override;
void enable() override { void enable() override {
x_textctrl->Enable(); x_textctrl->Enable();

View File

@ -50,9 +50,11 @@
#include "AppConfig.hpp" #include "AppConfig.hpp"
#include "ConfigSnapshotDialog.hpp" #include "ConfigSnapshotDialog.hpp"
#include "Utils.hpp" #include "Utils.hpp"
#include "MsgDialog.hpp"
#include "ConfigWizard.hpp" #include "ConfigWizard.hpp"
#include "Preferences.hpp" #include "Preferences.hpp"
#include "PresetBundle.hpp" #include "PresetBundle.hpp"
#include "UpdateDialogs.hpp"
#include "../Utils/PresetUpdater.hpp" #include "../Utils/PresetUpdater.hpp"
#include "../Config/Snapshot.hpp" #include "../Config/Snapshot.hpp"
@ -187,6 +189,7 @@ PresetBundle *g_PresetBundle= nullptr;
PresetUpdater *g_PresetUpdater = nullptr; PresetUpdater *g_PresetUpdater = nullptr;
wxColour g_color_label_modified; wxColour g_color_label_modified;
wxColour g_color_label_sys; wxColour g_color_label_sys;
wxColour g_color_label_default;
std::vector<Tab *> g_tabs_list; std::vector<Tab *> g_tabs_list;
@ -200,12 +203,28 @@ static void init_label_colours()
{ {
auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
if (luma >= 128) { if (luma >= 128) {
g_color_label_modified = wxColour(253, 88, 0); g_color_label_modified = wxColour(252, 77, 1);
g_color_label_sys = wxColour(26, 132, 57); g_color_label_sys = wxColour(26, 132, 57);
} else { } else {
g_color_label_modified = wxColour(253, 111, 40); g_color_label_modified = wxColour(253, 111, 40);
g_color_label_sys = wxColour(115, 220, 103); g_color_label_sys = wxColour(115, 220, 103);
} }
g_color_label_default = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
}
void update_label_colours_from_appconfig()
{
if (g_AppConfig->has("label_clr_sys")){
auto str = g_AppConfig->get("label_clr_sys");
if (str != "")
g_color_label_sys = wxColour(str);
}
if (g_AppConfig->has("label_clr_modified")){
auto str = g_AppConfig->get("label_clr_modified");
if (str != "")
g_color_label_modified = wxColour(str);
}
} }
void set_wxapp(wxApp *app) void set_wxapp(wxApp *app)
@ -290,22 +309,18 @@ bool select_language(wxArrayString & names,
bool load_language() bool load_language()
{ {
long language; wxString language = wxEmptyString;
if (!g_AppConfig->has("translation_language")) if (g_AppConfig->has("translation_language"))
language = wxLANGUAGE_UNKNOWN; language = g_AppConfig->get("translation_language");
else {
auto str_language = g_AppConfig->get("translation_language");
language = str_language != "" ? stol(str_language) : wxLANGUAGE_UNKNOWN;
}
if (language == wxLANGUAGE_UNKNOWN) if (language.IsEmpty())
return false; return false;
wxArrayString names; wxArrayString names;
wxArrayLong identifiers; wxArrayLong identifiers;
get_installed_languages(names, identifiers); get_installed_languages(names, identifiers);
for (size_t i = 0; i < identifiers.Count(); i++) for (size_t i = 0; i < identifiers.Count(); i++)
{ {
if (identifiers[i] == language) if (wxLocale::GetLanguageCanonicalName(identifiers[i]) == language)
{ {
g_wxLocale = new wxLocale; g_wxLocale = new wxLocale;
g_wxLocale->Init(identifiers[i]); g_wxLocale->Init(identifiers[i]);
@ -320,12 +335,11 @@ bool load_language()
void save_language() void save_language()
{ {
long language = wxLANGUAGE_UNKNOWN; wxString language = wxEmptyString;
if (g_wxLocale) { if (g_wxLocale)
language = g_wxLocale->GetLanguage(); language = g_wxLocale->GetCanonicalName();
}
std::string str_language = std::to_string(language); g_AppConfig->set("translation_language", language.ToStdString());
g_AppConfig->set("translation_language", str_language);
g_AppConfig->save(); g_AppConfig->save();
} }
@ -379,18 +393,18 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), ConfigWizard::name()); const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), ConfigWizard::name());
// Cmd+, is standard on OS X - what about other operating systems? // Cmd+, is standard on OS X - what about other operating systems?
local_menu->Append(config_id_base + ConfigMenuWizard, ConfigWizard::name() + "\u2026", config_wizard_tooltip); local_menu->Append(config_id_base + ConfigMenuWizard, ConfigWizard::name() + "\u2026", config_wizard_tooltip);
local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("Configuration Snapshots\u2026")), _(L("Inspect / activate configuration snapshots"))); local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("Configuration Snapshots"))+"\u2026", _(L("Inspect / activate configuration snapshots")));
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration Snapshot")), _(L("Capture a configuration snapshot"))); local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration Snapshot")), _(L("Capture a configuration snapshot")));
local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates"))); local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates")));
local_menu->AppendSeparator(); local_menu->AppendSeparator();
local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences\u2026\tCtrl+,")), _(L("Application preferences"))); local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences"))+"\u2026\tCtrl+,", _(L("Application preferences")));
local_menu->AppendSeparator(); local_menu->AppendSeparator();
local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language"))); local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language")));
local_menu->Bind(wxEVT_MENU, [config_id_base, event_language_change, event_preferences_changed](wxEvent &event){ local_menu->Bind(wxEVT_MENU, [config_id_base, event_language_change, event_preferences_changed](wxEvent &event){
switch (event.GetId() - config_id_base) { switch (event.GetId() - config_id_base) {
case ConfigMenuWizard: case ConfigMenuWizard:
config_wizard(false, false); config_wizard(ConfigWizard::RR_USER);
break; break;
case ConfigMenuTakeSnapshot: case ConfigMenuTakeSnapshot:
// Take a configuration snapshot. // Take a configuration snapshot.
@ -469,47 +483,31 @@ bool check_unsaved_changes()
return dialog->ShowModal() == wxID_YES; return dialog->ShowModal() == wxID_YES;
} }
void config_wizard_startup(bool app_config_exists) bool config_wizard_startup(bool app_config_exists)
{ {
if (! app_config_exists || g_PresetBundle->has_defauls_only()) { if (! app_config_exists || g_PresetBundle->has_defauls_only()) {
config_wizard(true, true); config_wizard(ConfigWizard::RR_DATA_EMPTY);
return true;
} else if (g_AppConfig->legacy_datadir()) { } else if (g_AppConfig->legacy_datadir()) {
// Looks like user has legacy pre-vendorbundle data directory, // Looks like user has legacy pre-vendorbundle data directory,
// explain what this is and run the wizard // explain what this is and run the wizard
const auto msg = _(L("Configuration update")); MsgDataLegacy dlg;
const auto ext_msg = wxString::Format( dlg.ShowModal();
_(L(
"Slic3r PE now uses an updated configuration structure.\n\n"
"So called 'System presets' have been introduced, which hold the built-in default settings for various " config_wizard(ConfigWizard::RR_DATA_LEGACY);
"printers. These System presets cannot be modified, instead, users now may create their" return true;
"own presets inheriting settings from one of the System presets.\n"
"An inheriting preset may either inherit a particular value from its parent or override it with a customized value.\n\n"
"Please proceed with the %s that follows to set up the new presets "
"and to choose whether to enable automatic preset updates."
)),
ConfigWizard::name()
);
wxMessageDialog dlg(NULL, msg, _(L("Configuration update")), wxOK|wxCENTRE);
dlg.SetExtendedMessage(ext_msg);
const auto res = dlg.ShowModal();
config_wizard(true, false);
} }
return false;
} }
void config_wizard(bool startup, bool empty_datadir) void config_wizard(int reason)
{ {
if (g_wxMainFrame == nullptr)
throw std::runtime_error("Main frame not set");
// Exit wizard if there are unsaved changes and the user cancels the action. // Exit wizard if there are unsaved changes and the user cancels the action.
if (! check_unsaved_changes()) if (! check_unsaved_changes())
return; return;
ConfigWizard wizard(g_wxMainFrame, startup, empty_datadir); ConfigWizard wizard(nullptr, static_cast<ConfigWizard::RunReason>(reason));
wizard.run(g_PresetBundle, g_PresetUpdater); wizard.run(g_PresetBundle, g_PresetUpdater);
// Load the currently selected preset into the GUI, update the preset selection box. // Load the currently selected preset into the GUI, update the preset selection box.
@ -525,6 +523,7 @@ void open_preferences_dialog(int event_preferences)
void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed) void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed)
{ {
update_label_colours_from_appconfig();
add_created_tab(new TabPrint (g_wxTabPanel, no_controller)); add_created_tab(new TabPrint (g_wxTabPanel, no_controller));
add_created_tab(new TabFilament (g_wxTabPanel, no_controller)); add_created_tab(new TabFilament (g_wxTabPanel, no_controller));
add_created_tab(new TabPrinter (g_wxTabPanel, no_controller)); add_created_tab(new TabPrinter (g_wxTabPanel, no_controller));
@ -665,35 +664,65 @@ void add_created_tab(Tab* panel)
g_wxTabPanel->AddPage(panel, panel->title()); g_wxTabPanel->AddPage(panel, panel->title());
} }
void show_error(wxWindow* parent, const wxString& message){ void show_error(wxWindow* parent, const wxString& message) {
auto msg_wingow = new wxMessageDialog(parent, message, _(L("Error")), wxOK | wxICON_ERROR); ErrorDialog msg(parent, message);
msg_wingow->ShowModal(); msg.ShowModal();
}
void show_error_id(int id, const std::string& message) {
auto *parent = id != 0 ? wxWindow::FindWindowById(id) : nullptr;
show_error(parent, message);
} }
void show_info(wxWindow* parent, const wxString& message, const wxString& title){ void show_info(wxWindow* parent, const wxString& message, const wxString& title){
auto msg_wingow = new wxMessageDialog(parent, message, title.empty() ? _(L("Notice")) : title, wxOK | wxICON_INFORMATION); wxMessageDialog msg_wingow(parent, message, title.empty() ? _(L("Notice")) : title, wxOK | wxICON_INFORMATION);
msg_wingow->ShowModal(); msg_wingow.ShowModal();
} }
void warning_catcher(wxWindow* parent, const wxString& message){ void warning_catcher(wxWindow* parent, const wxString& message){
if (message == _(L("GLUquadricObjPtr | Attempt to free unreferenced scalar")) ) if (message == "GLUquadricObjPtr | " + _(L("Attempt to free unreferenced scalar")) )
return; return;
auto msg = new wxMessageDialog(parent, message, _(L("Warning")), wxOK | wxICON_WARNING); wxMessageDialog msg(parent, message, _(L("Warning")), wxOK | wxICON_WARNING);
msg->ShowModal(); msg.ShowModal();
} }
wxApp* get_app(){ wxApp* get_app(){
return g_wxApp; return g_wxApp;
} }
const wxColour& get_modified_label_clr() { PresetBundle* get_preset_bundle()
{
return g_PresetBundle;
}
const wxColour& get_label_clr_modified() {
return g_color_label_modified; return g_color_label_modified;
} }
const wxColour& get_sys_label_clr() { const wxColour& get_label_clr_sys() {
return g_color_label_sys; return g_color_label_sys;
} }
void set_label_clr_modified(const wxColour& clr) {
g_color_label_modified = clr;
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue());
std::string str = clr_str.ToStdString();
g_AppConfig->set("label_clr_modified", str);
g_AppConfig->save();
}
void set_label_clr_sys(const wxColour& clr) {
g_color_label_sys = clr;
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue());
std::string str = clr_str.ToStdString();
g_AppConfig->set("label_clr_sys", str);
g_AppConfig->save();
}
const wxColour& get_label_clr_default() {
return g_color_label_default;
}
unsigned get_colour_approx_luma(const wxColour &colour) unsigned get_colour_approx_luma(const wxColour &colour)
{ {
double r = colour.Red(); double r = colour.Red();
@ -780,8 +809,8 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
{ {
DynamicPrintConfig* config = &g_PresetBundle->prints.get_edited_preset().config; DynamicPrintConfig* config = &g_PresetBundle->prints.get_edited_preset().config;
m_optgroup = std::make_shared<ConfigOptionsGroup>(parent, "", config); m_optgroup = std::make_shared<ConfigOptionsGroup>(parent, "", config);
const wxArrayInt& ar = preset_sizer->GetColWidths(); // const wxArrayInt& ar = preset_sizer->GetColWidths();
m_optgroup->label_width = ar.IsEmpty() ? 100 : ar.front(); // m_optgroup->label_width = ar.IsEmpty() ? 100 : ar.front(); // doesn't work
m_optgroup->m_on_change = [config](t_config_option_key opt_key, boost::any value){ m_optgroup->m_on_change = [config](t_config_option_key opt_key, boost::any value){
TabPrint* tab_print = nullptr; TabPrint* tab_print = nullptr;
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) { for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) {
@ -835,10 +864,9 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
tab_print->update_dirty(); tab_print->update_dirty();
}; };
const int width = 250;
Option option = m_optgroup->get_option("fill_density"); Option option = m_optgroup->get_option("fill_density");
option.opt.sidetext = ""; option.opt.sidetext = "";
option.opt.width = width; option.opt.full_width = true;
m_optgroup->append_single_option_line(option); m_optgroup->append_single_option_line(option);
ConfigOptionDef def; ConfigOptionDef def;
@ -857,7 +885,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
"Everywhere"; "Everywhere";
def.default_value = new ConfigOptionStrings { selection }; def.default_value = new ConfigOptionStrings { selection };
option = Option(def, "support"); option = Option(def, "support");
option.opt.width = width; option.opt.full_width = true;
m_optgroup->append_single_option_line(option); m_optgroup->append_single_option_line(option);
m_brim_width = config->opt_float("brim_width"); m_brim_width = config->opt_float("brim_width");
@ -870,7 +898,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
m_optgroup->append_single_option_line(option); m_optgroup->append_single_option_line(option);
Line line = { _(L("")), "" }; Line line = { "", "" };
line.widget = [config](wxWindow* parent){ line.widget = [config](wxWindow* parent){
g_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + "\u2026", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); g_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + "\u2026", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
auto sizer = new wxBoxSizer(wxHORIZONTAL); auto sizer = new wxBoxSizer(wxHORIZONTAL);
@ -896,7 +924,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
sizer->Add(m_optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxBottom, 1); sizer->Add(m_optgroup->sizer, 1, wxEXPAND | wxBOTTOM, 2);
} }
ConfigOptionsGroup* get_optgroup() ConfigOptionsGroup* get_optgroup()
@ -915,6 +943,7 @@ wxWindow* export_option_creator(wxWindow* parent)
wxPanel* panel = new wxPanel(parent, -1); wxPanel* panel = new wxPanel(parent, -1);
wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
wxCheckBox* cbox = new wxCheckBox(panel, wxID_HIGHEST + 1, L("Export print config")); wxCheckBox* cbox = new wxCheckBox(panel, wxID_HIGHEST + 1, L("Export print config"));
cbox->SetValue(true);
sizer->AddSpacer(5); sizer->AddSpacer(5);
sizer->Add(cbox, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); sizer->Add(cbox, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
panel->SetSizer(sizer); panel->SetSizer(sizer);

View File

@ -82,10 +82,14 @@ void set_preset_updater(PresetUpdater *updater);
AppConfig* get_app_config(); AppConfig* get_app_config();
wxApp* get_app(); wxApp* get_app();
PresetBundle* get_preset_bundle();
const wxColour& get_modified_label_clr(); const wxColour& get_label_clr_modified();
const wxColour& get_sys_label_clr(); const wxColour& get_label_clr_sys();
const wxColour& get_label_clr_default();
unsigned get_colour_approx_luma(const wxColour &colour); unsigned get_colour_approx_luma(const wxColour &colour);
void set_label_clr_modified(const wxColour& clr);
void set_label_clr_sys(const wxColour& clr);
extern void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change); extern void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change);
@ -93,11 +97,13 @@ extern void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int
// to notify the user whether he is aware that some preset changes will be lost. // to notify the user whether he is aware that some preset changes will be lost.
extern bool check_unsaved_changes(); extern bool check_unsaved_changes();
// Checks if configuration wizard needs to run, calls config_wizard if so // Checks if configuration wizard needs to run, calls config_wizard if so.
extern void config_wizard_startup(bool app_config_exists); // Returns whether the Wizard ran.
extern bool config_wizard_startup(bool app_config_exists);
// Opens the configuration wizard, returns true if wizard is finished & accepted. // Opens the configuration wizard, returns true if wizard is finished & accepted.
extern void config_wizard(bool startup, bool empty_datadir); // The run_reason argument is actually ConfigWizard::RunReason, but int is used here because of Perl.
extern void config_wizard(int run_reason);
// Create "Preferences" dialog after selecting menu "Preferences" in Perl part // Create "Preferences" dialog after selecting menu "Preferences" in Perl part
extern void open_preferences_dialog(int event_preferences); extern void open_preferences_dialog(int event_preferences);
@ -112,6 +118,7 @@ void add_created_tab(Tab* panel);
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0); void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0);
void show_error(wxWindow* parent, const wxString& message); void show_error(wxWindow* parent, const wxString& message);
void show_error_id(int id, const std::string& message); // For Perl
void show_info(wxWindow* parent, const wxString& message, const wxString& title); void show_info(wxWindow* parent, const wxString& message, const wxString& title);
void warning_catcher(wxWindow* parent, const wxString& message); void warning_catcher(wxWindow* parent, const wxString& message);

View File

@ -0,0 +1,87 @@
#include "MsgDialog.hpp"
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/button.h>
#include <wx/statbmp.h>
#include <wx/scrolwin.h>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Utils.hpp"
#include "GUI.hpp"
#include "ConfigWizard.hpp"
namespace Slic3r {
namespace GUI {
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id) :
MsgDialog(parent, title, headline, wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG), button_id)
{}
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id) :
wxDialog(parent, wxID_ANY, title),
boldfont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)),
content_sizer(new wxBoxSizer(wxVERTICAL)),
btn_sizer(new wxBoxSizer(wxHORIZONTAL))
{
boldfont.SetWeight(wxFONTWEIGHT_BOLD);
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
auto *rightsizer = new wxBoxSizer(wxVERTICAL);
auto *headtext = new wxStaticText(this, wxID_ANY, headline);
headtext->SetFont(boldfont);
headtext->Wrap(CONTENT_WIDTH);
rightsizer->Add(headtext);
rightsizer->AddSpacer(VERT_SPACING);
rightsizer->Add(content_sizer, 1, wxEXPAND);
if (button_id != wxID_NONE) {
auto *button = new wxButton(this, button_id);
button->SetFocus();
btn_sizer->Add(button);
}
rightsizer->Add(btn_sizer, 0, wxALIGN_CENTRE_HORIZONTAL);
auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(bitmap));
topsizer->Add(logo, 0, wxALL, BORDER);
topsizer->Add(rightsizer, 1, wxALL | wxEXPAND, BORDER);
SetSizerAndFit(topsizer);
}
MsgDialog::~MsgDialog() {}
// ErrorDialog
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) :
MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")), wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG))
{
auto *panel = new wxScrolledWindow(this);
auto *p_sizer = new wxBoxSizer(wxVERTICAL);
panel->SetSizer(p_sizer);
auto *text = new wxStaticText(panel, wxID_ANY, msg);
text->Wrap(CONTENT_WIDTH);
p_sizer->Add(text, 1, wxEXPAND);
panel->SetMinSize(wxSize(CONTENT_WIDTH, CONTENT_HEIGHT));
panel->SetScrollRate(0, 5);
content_sizer->Add(panel, 1, wxEXPAND);
Fit();
}
ErrorDialog::~ErrorDialog() {}
}
}

View File

@ -0,0 +1,65 @@
#ifndef slic3r_MsgDialog_hpp_
#define slic3r_MsgDialog_hpp_
#include <string>
#include <unordered_map>
#include <wx/dialog.h>
#include <wx/font.h>
#include <wx/bitmap.h>
#include "slic3r/Utils/Semver.hpp"
class wxBoxSizer;
class wxCheckBox;
namespace Slic3r {
namespace GUI {
// A message / query dialog with a bitmap on the left and any content on the right
// with buttons underneath.
struct MsgDialog : wxDialog
{
MsgDialog(MsgDialog &&) = delete;
MsgDialog(const MsgDialog &) = delete;
MsgDialog &operator=(MsgDialog &&) = delete;
MsgDialog &operator=(const MsgDialog &) = delete;
virtual ~MsgDialog();
protected:
enum {
CONTENT_WIDTH = 500,
CONTENT_HEIGHT = 300,
BORDER = 30,
VERT_SPACING = 15,
HORIZ_SPACING = 5,
};
// button_id is an id of a button that can be added by default, use wxID_NONE to disable
MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id = wxID_OK);
MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id = wxID_OK);
wxFont boldfont;
wxBoxSizer *content_sizer;
wxBoxSizer *btn_sizer;
};
// Generic error dialog, used for displaying exceptions
struct ErrorDialog : MsgDialog
{
ErrorDialog(wxWindow *parent, const wxString &msg);
ErrorDialog(ErrorDialog &&) = delete;
ErrorDialog(const ErrorDialog &) = delete;
ErrorDialog &operator=(ErrorDialog &&) = delete;
ErrorDialog &operator=(const ErrorDialog &) = delete;
virtual ~ErrorDialog();
};
}
}
#endif

View File

@ -22,13 +22,13 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
// is the normal type. // is the normal type.
if (opt.gui_type.compare("select") == 0) { if (opt.gui_type.compare("select") == 0) {
} else if (opt.gui_type.compare("select_open") == 0) { } else if (opt.gui_type.compare("select_open") == 0) {
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(m_parent, opt, id))); m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
} else if (opt.gui_type.compare("color") == 0) { } else if (opt.gui_type.compare("color") == 0) {
m_fields.emplace(id, STDMOVE(ColourPicker::Create<ColourPicker>(m_parent, opt, id))); m_fields.emplace(id, STDMOVE(ColourPicker::Create<ColourPicker>(parent(), opt, id)));
} else if (opt.gui_type.compare("f_enum_open") == 0 || } else if (opt.gui_type.compare("f_enum_open") == 0 ||
opt.gui_type.compare("i_enum_open") == 0 || opt.gui_type.compare("i_enum_open") == 0 ||
opt.gui_type.compare("i_enum_closed") == 0) { opt.gui_type.compare("i_enum_closed") == 0) {
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(m_parent, opt, id))); m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
} else if (opt.gui_type.compare("slider") == 0) { } else if (opt.gui_type.compare("slider") == 0) {
} else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl } else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl
} else { } else {
@ -40,21 +40,21 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
case coPercents: case coPercents:
case coString: case coString:
case coStrings: case coStrings:
m_fields.emplace(id, STDMOVE(TextCtrl::Create<TextCtrl>(m_parent, opt, id))); m_fields.emplace(id, STDMOVE(TextCtrl::Create<TextCtrl>(parent(), opt, id)));
break; break;
case coBool: case coBool:
case coBools: case coBools:
m_fields.emplace(id, STDMOVE(CheckBox::Create<CheckBox>(m_parent, opt, id))); m_fields.emplace(id, STDMOVE(CheckBox::Create<CheckBox>(parent(), opt, id)));
break; break;
case coInt: case coInt:
case coInts: case coInts:
m_fields.emplace(id, STDMOVE(SpinCtrl::Create<SpinCtrl>(m_parent, opt, id))); m_fields.emplace(id, STDMOVE(SpinCtrl::Create<SpinCtrl>(parent(), opt, id)));
break; break;
case coEnum: case coEnum:
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(m_parent, opt, id))); m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
break; break;
case coPoints: case coPoints:
m_fields.emplace(id, STDMOVE(PointCtrl::Create<PointCtrl>(m_parent, opt, id))); m_fields.emplace(id, STDMOVE(PointCtrl::Create<PointCtrl>(parent(), opt, id)));
break; break;
case coNone: break; case coNone: break;
default: default:
@ -90,8 +90,8 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
field->m_Undo_btn->Hide(); field->m_Undo_btn->Hide();
field->m_Undo_to_sys_btn->Hide(); field->m_Undo_to_sys_btn->Hide();
} }
if (nonsys_btn_icon != nullptr) // if (nonsys_btn_icon != nullptr)
field->set_nonsys_btn_icon(nonsys_btn_icon()); // field->set_nonsys_btn_icon(*nonsys_btn_icon);
// assign function objects for callbacks, etc. // assign function objects for callbacks, etc.
return field; return field;
@ -118,30 +118,43 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width && if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width &&
option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr &&
line.get_extra_widgets().size() == 0) { line.get_extra_widgets().size() == 0) {
wxSizer* tmp_sizer;
#ifdef __WXGTK__
tmp_sizer = new wxBoxSizer(wxVERTICAL);
m_panel->SetSizer(tmp_sizer);
m_panel->Layout();
#else
tmp_sizer = sizer;
#endif /* __WXGTK__ */
const auto& option = option_set.front(); const auto& option = option_set.front();
const auto& field = build_field(option); const auto& field = build_field(option);
auto btn_sizer = new wxBoxSizer(wxHORIZONTAL); auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
btn_sizer->Add(field->m_Undo_to_sys_btn); btn_sizer->Add(field->m_Undo_to_sys_btn);
btn_sizer->Add(field->m_Undo_btn); btn_sizer->Add(field->m_Undo_btn);
sizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 0); tmp_sizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 0);
if (is_window_field(field)) if (is_window_field(field))
sizer->Add(field->getWindow(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5); tmp_sizer->Add(field->getWindow(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5);
if (is_sizer_field(field)) if (is_sizer_field(field))
sizer->Add(field->getSizer(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5); tmp_sizer->Add(field->getSizer(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5);
return; return;
} }
auto grid_sizer = m_grid_sizer; auto grid_sizer = m_grid_sizer;
#ifdef __WXGTK__
m_panel->SetSizer(m_grid_sizer);
m_panel->Layout();
#endif /* __WXGTK__ */
// Build a label if we have it // Build a label if we have it
wxStaticText* label=nullptr; wxStaticText* label=nullptr;
if (label_width != 0) { if (label_width != 0) {
label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ":"), label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ": "),
wxDefaultPosition, wxSize(label_width, -1)); wxDefaultPosition, staticbox ? wxSize(label_width, -1) : wxDefaultSize);
label->SetFont(label_font); label->SetFont(label_font);
label->Wrap(label_width); // avoid a Linux/GTK bug label->Wrap(label_width); // avoid a Linux/GTK bug
grid_sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL,0); grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT) | wxALIGN_CENTER_VERTICAL, 0);
if (line.label_tooltip.compare("") != 0) if (line.label_tooltip.compare("") != 0)
label->SetToolTip(line.label_tooltip); label->SetToolTip(line.label_tooltip);
} }
@ -149,7 +162,8 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
// If there's a widget, build it and add the result to the sizer. // If there's a widget, build it and add the result to the sizer.
if (line.widget != nullptr) { if (line.widget != nullptr) {
auto wgt = line.widget(parent()); auto wgt = line.widget(parent());
grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, wxOSX ? 0 : 5); // If widget doesn't have label, don't use border
grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, (wxOSX || line.label.IsEmpty()) ? 0 : 5);
if (colored_Label != nullptr) *colored_Label = label; if (colored_Label != nullptr) *colored_Label = label;
return; return;
} }
@ -165,7 +179,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL); sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL);
if (is_window_field(field)) if (is_window_field(field))
sizer->Add(field->getWindow(), 0, (option.opt.full_width ? wxEXPAND : 0) | sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, (option.opt.full_width ? wxEXPAND : 0) |
wxBOTTOM | wxTOP | wxALIGN_CENTER_VERTICAL, wxOSX ? 0 : 2); wxBOTTOM | wxTOP | wxALIGN_CENTER_VERTICAL, wxOSX ? 0 : 2);
if (is_sizer_field(field)) if (is_sizer_field(field))
sizer->Add(field->getSizer(), 0, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0); sizer->Add(field->getSizer(), 0, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0);
@ -467,10 +481,10 @@ Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int op
return opt_id.empty() ? nullptr : get_field(opt_id); return opt_id.empty() ? nullptr : get_field(opt_id);
} }
void ogStaticText::SetText(const wxString& value) void ogStaticText::SetText(const wxString& value, bool wrap/* = true*/)
{ {
SetLabel(value); SetLabel(value);
Wrap(400); if (wrap) Wrap(400);
GetParent()->Layout(); GetParent()->Layout();
} }

View File

@ -1,3 +1,6 @@
#ifndef slic3r_OptionsGroup_hpp_
#define slic3r_OptionsGroup_hpp_
#include <wx/wx.h> #include <wx/wx.h>
#include <wx/stattext.h> #include <wx/stattext.h>
#include <wx/settings.h> #include <wx/settings.h>
@ -86,12 +89,18 @@ public:
wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
std::function<std::string()> nonsys_btn_icon{ nullptr }; // std::function<const wxBitmap&()> nonsys_btn_icon{ nullptr };
/// Returns a copy of the pointer of the parent wxWindow. /// Returns a copy of the pointer of the parent wxWindow.
/// Accessor function is because users are not allowed to change the parent /// Accessor function is because users are not allowed to change the parent
/// but defining it as const means a lot of const_casts to deal with wx functions. /// but defining it as const means a lot of const_casts to deal with wx functions.
inline wxWindow* parent() const { return m_parent; } inline wxWindow* parent() const {
#ifdef __WXGTK__
return m_panel;
#else
return m_parent;
#endif /* __WXGTK__ */
}
void append_line(const Line& line, wxStaticText** colored_Label = nullptr); void append_line(const Line& line, wxStaticText** colored_Label = nullptr);
Line create_single_option_line(const Option& option) const; Line create_single_option_line(const Option& option) const;
@ -127,8 +136,13 @@ public:
m_grid_sizer = new wxFlexGridSizer(0, num_columns, 0,0); m_grid_sizer = new wxFlexGridSizer(0, num_columns, 0,0);
static_cast<wxFlexGridSizer*>(m_grid_sizer)->SetFlexibleDirection(wxHORIZONTAL); static_cast<wxFlexGridSizer*>(m_grid_sizer)->SetFlexibleDirection(wxHORIZONTAL);
static_cast<wxFlexGridSizer*>(m_grid_sizer)->AddGrowableCol(label_width != 0); static_cast<wxFlexGridSizer*>(m_grid_sizer)->AddGrowableCol(label_width != 0);
#ifdef __WXGTK__
sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX ? 0: 5); m_panel = new wxPanel( _parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
sizer->Fit(m_panel);
sizer->Add(m_panel, 0, wxEXPAND | wxALL, wxOSX||!staticbox ? 0: 5);
#else
sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX||!staticbox ? 0: 5);
#endif /* __WXGTK__ */
} }
protected: protected:
@ -144,6 +158,13 @@ protected:
// "true" if option is created in preset tabs // "true" if option is created in preset tabs
bool m_is_tab_opt{ false }; bool m_is_tab_opt{ false };
// This panel is needed for correct showing of the ToolTips for Button, StaticText and CheckBox
// Tooltips on GTK doesn't work inside wxStaticBoxSizer unless you insert a panel
// inside it before you insert the other controls.
#ifdef __WXGTK__
wxPanel* m_panel {nullptr};
#endif /* __WXGTK__ */
/// Generate a wxSizer or wxWindow from a configuration option /// Generate a wxSizer or wxWindow from a configuration option
/// Precondition: opt resolves to a known ConfigOption /// Precondition: opt resolves to a known ConfigOption
/// Postcondition: fields contains a wx gui object. /// Postcondition: fields contains a wx gui object.
@ -200,7 +221,9 @@ public:
ogStaticText(wxWindow* parent, const char *text) : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize){} ogStaticText(wxWindow* parent, const char *text) : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize){}
~ogStaticText(){} ~ogStaticText(){}
void SetText(const wxString& value); void SetText(const wxString& value, bool wrap = true);
}; };
}} }}
#endif /* slic3r_OptionsGroup_hpp_ */

View File

@ -5,6 +5,12 @@
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
PreferencesDialog::PreferencesDialog(wxWindow* parent, int event_preferences) :
wxDialog(parent, wxID_ANY, _(L("Preferences")), wxDefaultPosition, wxDefaultSize),
m_event_preferences(event_preferences) {
build();
}
void PreferencesDialog::build() void PreferencesDialog::build()
{ {
auto app_config = get_app_config(); auto app_config = get_app_config();

View File

@ -1,3 +1,6 @@
#ifndef slic3r_Preferences_hpp_
#define slic3r_Preferences_hpp_
#include "GUI.hpp" #include "GUI.hpp"
#include <wx/dialog.h> #include <wx/dialog.h>
@ -14,8 +17,7 @@ class PreferencesDialog : public wxDialog
std::shared_ptr<ConfigOptionsGroup> m_optgroup; std::shared_ptr<ConfigOptionsGroup> m_optgroup;
int m_event_preferences; int m_event_preferences;
public: public:
PreferencesDialog(wxWindow* parent, int event_preferences) : wxDialog(parent, wxID_ANY, _(L("Preferences")), PreferencesDialog(wxWindow* parent, int event_preferences);
wxDefaultPosition, wxDefaultSize), m_event_preferences(event_preferences) { build(); }
~PreferencesDialog(){ } ~PreferencesDialog(){ }
void build(); void build();
@ -25,3 +27,5 @@ public:
} // GUI } // GUI
} // Slic3r } // Slic3r
#endif /* slic3r_Preferences_hpp_ */

View File

@ -3,6 +3,7 @@
#include "Preset.hpp" #include "Preset.hpp"
#include "AppConfig.hpp" #include "AppConfig.hpp"
#include "BitmapCache.hpp"
#include <fstream> #include <fstream>
#include <stdexcept> #include <stdexcept>
@ -17,6 +18,7 @@
#include <boost/property_tree/ini_parser.hpp> #include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include <boost/locale.hpp> #include <boost/locale.hpp>
#include <boost/log/trivial.hpp>
#include <wx/image.h> #include <wx/image.h>
#include <wx/choice.h> #include <wx/choice.h>
@ -119,14 +121,15 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
model.id = section.first.substr(printer_model_key.size()); model.id = section.first.substr(printer_model_key.size());
model.name = section.second.get<std::string>("name", model.id); model.name = section.second.get<std::string>("name", model.id);
section.second.get<std::string>("variants", ""); section.second.get<std::string>("variants", "");
const auto variants_field = section.second.get<std::string>("variants", "");
std::vector<std::string> variants; std::vector<std::string> variants;
if (Slic3r::unescape_strings_cstyle(section.second.get<std::string>("variants", ""), variants)) { if (Slic3r::unescape_strings_cstyle(variants_field, variants)) {
for (const std::string &variant_name : variants) { for (const std::string &variant_name : variants) {
if (model.variant(variant_name) == nullptr) if (model.variant(variant_name) == nullptr)
model.variants.emplace_back(VendorProfile::PrinterVariant(variant_name)); model.variants.emplace_back(VendorProfile::PrinterVariant(variant_name));
} }
} else { } else {
// Log error? // XXX BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed variants field: `%2%`") % id % variants_field;
} }
if (! model.id.empty() && ! model.variants.empty()) if (! model.id.empty() && ! model.variants.empty())
res.models.push_back(std::move(model)); res.models.push_back(std::move(model));
@ -340,7 +343,8 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::str
m_type(type), m_type(type),
m_edited_preset(type, "", false), m_edited_preset(type, "", false),
m_idx_selected(0), m_idx_selected(0),
m_bitmap_main_frame(new wxBitmap) m_bitmap_main_frame(new wxBitmap),
m_bitmap_cache(new GUI::BitmapCache)
{ {
// Insert just the default preset. // Insert just the default preset.
m_presets.emplace_back(Preset(type, "- default -", true)); m_presets.emplace_back(Preset(type, "- default -", true));
@ -352,6 +356,8 @@ PresetCollection::~PresetCollection()
{ {
delete m_bitmap_main_frame; delete m_bitmap_main_frame;
m_bitmap_main_frame = nullptr; m_bitmap_main_frame = nullptr;
delete m_bitmap_cache;
m_bitmap_cache = nullptr;
} }
void PresetCollection::reset(bool delete_files) void PresetCollection::reset(bool delete_files)
@ -383,7 +389,9 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
// Remove the .ini suffix. // Remove the .ini suffix.
name.erase(name.size() - 4); name.erase(name.size() - 4);
if (this->find_preset(name, false)) { if (this->find_preset(name, false)) {
errors_cummulative += "The user preset \"" + name + "\" cannot be loaded. A system preset of the same name has already been loaded."; // This happens when there's is a preset (most likely legacy one) with the same name as a system preset
// that's already been loaded from a bundle.
BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name;
continue; continue;
} }
try { try {
@ -585,17 +593,41 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
// Otherwise fill in the list from scratch. // Otherwise fill in the list from scratch.
ui->Freeze(); ui->Freeze();
ui->Clear(); ui->Clear();
std::map<wxString, bool> nonsys_presets;
const Preset &selected_preset = this->get_selected_preset();
// Show wide icons if the currently selected preset is not compatible with the current printer,
// and draw a red flag in front of the selected preset.
bool wide_icons = !selected_preset.is_compatible && m_bitmap_incompatible != nullptr;
std::map<wxString, wxBitmap*> nonsys_presets;
wxString selected = ""; wxString selected = "";
for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { if (!this->m_presets.front().is_visible)
ui->Append("------- " +_(L("System presets")) + " -------", wxNullBitmap);
for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++i) {
const Preset &preset = this->m_presets[i]; const Preset &preset = this->m_presets[i];
if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected)) if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected))
continue; continue;
const wxBitmap *bmp = (i == 0 || preset.is_compatible) ? m_bitmap_main_frame : m_bitmap_incompatible; std::string bitmap_key = "";
// ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left
// (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); // to the filament color image.
// if (i == m_idx_selected) if (wide_icons)
// ui->SetSelection(ui->GetCount() - 1); bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt";
bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst";
wxBitmap *bmp = m_bitmap_cache->find(bitmap_key);
if (bmp == nullptr) {
// Create the bitmap with color bars.
std::vector<wxBitmap> bmps;
if (wide_icons)
// Paint a red flag for incompatible presets.
bmps.emplace_back(preset.is_compatible ? m_bitmap_cache->mkclear(16, 16) : *m_bitmap_incompatible);
// Paint the color bars.
bmps.emplace_back(m_bitmap_cache->mkclear(4, 16));
bmps.emplace_back(*m_bitmap_main_frame);
// Paint a lock at the system presets.
bmps.emplace_back(m_bitmap_cache->mkclear(6, 16));
bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(16, 16));
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
}
if (preset.is_default || preset.is_system){ if (preset.is_default || preset.is_system){
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
@ -605,20 +637,18 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
} }
else else
{ {
nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), preset.is_compatible); nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/);
if (i == m_idx_selected) if (i == m_idx_selected)
selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
} }
if (preset.is_default) if (preset.is_default)
ui->Append("------------------------------------", wxNullBitmap); ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
} }
if (!nonsys_presets.empty()) if (!nonsys_presets.empty())
{ {
ui->Append("------------------------------------", wxNullBitmap); ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap);
for (std::map<wxString, bool>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
const wxBitmap *bmp = it->second ? m_bitmap_compatible : m_bitmap_incompatible; ui->Append(it->first, *it->second);
ui->Append(it->first,
(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
if (it->first == selected) if (it->first == selected)
ui->SetSelection(ui->GetCount() - 1); ui->SetSelection(ui->GetCount() - 1);
} }
@ -626,51 +656,63 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
ui->Thaw(); ui->Thaw();
} }
void PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible) size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible)
{ {
if (ui == nullptr) if (ui == nullptr)
return; return 0;
ui->Freeze(); ui->Freeze();
ui->Clear(); ui->Clear();
std::map<wxString, bool> nonsys_presets; size_t selected_preset_item = 0;
std::map<wxString, wxBitmap*> nonsys_presets;
wxString selected = ""; wxString selected = "";
for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { if (!this->m_presets.front().is_visible)
ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++i) {
const Preset &preset = this->m_presets[i]; const Preset &preset = this->m_presets[i];
if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected)) if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected))
continue; continue;
const wxBitmap *bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible; std::string bitmap_key = "tab";
// ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt";
// (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst";
// if (i == m_idx_selected) wxBitmap *bmp = m_bitmap_cache->find(bitmap_key);
// ui->SetSelection(ui->GetCount() - 1); if (bmp == nullptr) {
// Create the bitmap with color bars.
std::vector<wxBitmap> bmps;
const wxBitmap* tmp_bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible;
bmps.emplace_back((tmp_bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *tmp_bmp);
// Paint a lock at the system presets.
bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(16, 16));
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
}
if (preset.is_default || preset.is_system){ if (preset.is_default || preset.is_system){
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
if (i == m_idx_selected) if (i == m_idx_selected)
ui->SetSelection(ui->GetCount() - 1); selected_preset_item = ui->GetCount() - 1;
} }
else else
{ {
nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), preset.is_compatible); nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/);
if (i == m_idx_selected) if (i == m_idx_selected)
selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
} }
if (preset.is_default) if (preset.is_default)
ui->Append("------------------------------------", wxNullBitmap); ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
} }
if (!nonsys_presets.empty()) if (!nonsys_presets.empty())
{ {
ui->Append("------------------------------------", wxNullBitmap); ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap);
for (std::map<wxString, bool>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
const wxBitmap *bmp = it->second ? m_bitmap_compatible : m_bitmap_incompatible; ui->Append(it->first, *it->second);
ui->Append(it->first,
(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
if (it->first == selected) if (it->first == selected)
ui->SetSelection(ui->GetCount() - 1); selected_preset_item = ui->GetCount() - 1;
} }
} }
ui->SetSelection(selected_preset_item);
ui->Thaw(); ui->Thaw();
return selected_preset_item;
} }
// Update a dirty floag of the current preset, update the labels of the UI component accordingly. // Update a dirty floag of the current preset, update the labels of the UI component accordingly.
@ -703,11 +745,13 @@ bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui)
return was_dirty != is_dirty; return was_dirty != is_dirty;
} }
std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, const Preset *reference) std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type /*= false*/)
{ {
std::vector<std::string> changed; std::vector<std::string> changed;
if (edited != nullptr && reference != nullptr) { if (edited != nullptr && reference != nullptr) {
changed = reference->config.diff(edited->config); changed = is_printer_type ?
reference->config.deep_diff(edited->config) :
reference->config.diff(edited->config);
// The "compatible_printers" option key is handled differently from the others: // The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer. // It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer. // If the key exists and it is empty, it means it is compatible with no printer.

View File

@ -20,6 +20,10 @@ namespace Slic3r {
class AppConfig; class AppConfig;
class PresetBundle; class PresetBundle;
namespace GUI {
class BitmapCache;
}
enum ConfigFileType enum ConfigFileType
{ {
CONFIG_FILE_TYPE_UNKNOWN, CONFIG_FILE_TYPE_UNKNOWN,
@ -302,18 +306,18 @@ public:
// Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ. // Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ.
bool current_is_dirty() const { return ! this->current_dirty_options().empty(); } bool current_is_dirty() const { return ! this->current_dirty_options().empty(); }
// Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ. // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
std::vector<std::string> current_dirty_options() const std::vector<std::string> current_dirty_options(const bool is_printer_type = false) const
{ return dirty_options(&this->get_edited_preset(), &this->get_selected_preset()); } { return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), is_printer_type); }
// Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ. // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
std::vector<std::string> current_different_from_parent_options() const std::vector<std::string> current_different_from_parent_options(const bool is_printer_type = false) const
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent()); } { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), is_printer_type); }
// Compare the content of get_selected_preset() with get_selected_preset_parent() configs, return the list of keys where they equal. // Compare the content of get_selected_preset() with get_selected_preset_parent() configs, return the list of keys where they equal.
std::vector<std::string> system_equal_options() const; std::vector<std::string> system_equal_options() const;
// Update the choice UI from the list of presets. // Update the choice UI from the list of presets.
// If show_incompatible, all presets are shown, otherwise only the compatible presets are shown. // If show_incompatible, all presets are shown, otherwise only the compatible presets are shown.
// If an incompatible preset is selected, it is shown as well. // If an incompatible preset is selected, it is shown as well.
void update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible); size_t update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible);
// Update the choice UI from the list of presets. // Update the choice UI from the list of presets.
// Only the compatible presets are shown. // Only the compatible presets are shown.
// If an incompatible preset is selected, it is shown as well. // If an incompatible preset is selected, it is shown as well.
@ -358,7 +362,7 @@ private:
size_t update_compatible_with_printer_internal(const Preset &active_printer, bool unselect_if_incompatible); size_t update_compatible_with_printer_internal(const Preset &active_printer, bool unselect_if_incompatible);
static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference); static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type = false);
// Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER. // Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER.
Preset::Type m_type; Preset::Type m_type;
@ -384,6 +388,9 @@ private:
// Path to the directory to store the config files into. // Path to the directory to store the config files into.
std::string m_dir_path; std::string m_dir_path;
// Caching color bitmaps for the filament combo box.
GUI::BitmapCache *m_bitmap_cache = nullptr;
// to access select_preset_by_name_strict() // to access select_preset_by_name_strict()
friend class PresetBundle; friend class PresetBundle;
}; };

View File

@ -1011,6 +1011,9 @@ void PresetBundle::export_configbundle(const std::string &path) //, const Dynami
// an optional "(modified)" suffix will be removed from the filament name. // an optional "(modified)" suffix will be removed from the filament name.
void PresetBundle::set_filament_preset(size_t idx, const std::string &name) void PresetBundle::set_filament_preset(size_t idx, const std::string &name)
{ {
if (name.find_first_of("-------") == 0)
return;
if (idx >= filament_presets.size()) if (idx >= filament_presets.size())
filament_presets.resize(idx + 1, filaments.default_preset().name); filament_presets.resize(idx + 1, filaments.default_preset().name);
filament_presets[idx] = Preset::remove_suffix_modified(name); filament_presets[idx] = Preset::remove_suffix_modified(name);
@ -1059,9 +1062,11 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
// and draw a red flag in front of the selected preset. // and draw a red flag in front of the selected preset.
bool wide_icons = selected_preset != nullptr && ! selected_preset->is_compatible && m_bitmapIncompatible != nullptr; bool wide_icons = selected_preset != nullptr && ! selected_preset->is_compatible && m_bitmapIncompatible != nullptr;
assert(selected_preset != nullptr); assert(selected_preset != nullptr);
std::map<wxString, wxBitmap> nonsys_presets; std::map<wxString, wxBitmap*> nonsys_presets;
wxString selected_str = ""; wxString selected_str = "";
for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++ i) { if (!this->filaments().front().is_visible)
ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++i) {
const Preset &preset = this->filaments.preset(i); const Preset &preset = this->filaments.preset(i);
bool selected = this->filament_presets[idx_extruder] == preset.name; bool selected = this->filament_presets[idx_extruder] == preset.name;
if (! preset.is_visible || (! preset.is_compatible && ! selected)) if (! preset.is_visible || (! preset.is_compatible && ! selected))
@ -1093,14 +1098,11 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
bmps.emplace_back(m_bitmapCache->mksolid(8, 16, rgb)); bmps.emplace_back(m_bitmapCache->mksolid(8, 16, rgb));
} }
// Paint a lock at the system presets. // Paint a lock at the system presets.
bmps.emplace_back(m_bitmapCache->mkclear(4, 16)); bmps.emplace_back(m_bitmapCache->mkclear(2, 16));
bmps.emplace_back((preset.is_system || preset.is_default) ? bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmapLock : m_bitmapCache->mkclear(16, 16));
(preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16)); // (preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16));
bitmap = m_bitmapCache->insert(bitmap_key, bmps); bitmap = m_bitmapCache->insert(bitmap_key, bmps);
} }
// ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap);
// if (selected)
// ui->SetSelection(ui->GetCount() - 1);
if (preset.is_default || preset.is_system){ if (preset.is_default || preset.is_system){
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()),
@ -1111,19 +1113,19 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
else else
{ {
nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()),
(bitmap == 0) ? wxNullBitmap : *bitmap); (bitmap == 0) ? &wxNullBitmap : bitmap);
if (selected) if (selected)
selected_str = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); selected_str = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str());
} }
if (preset.is_default) if (preset.is_default)
ui->Append("------------------------------------", wxNullBitmap); ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
} }
if (!nonsys_presets.empty()) if (!nonsys_presets.empty())
{ {
ui->Append("------------------------------------", wxNullBitmap); ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap);
for (std::map<wxString, wxBitmap>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
ui->Append(it->first, it->second); ui->Append(it->first, *it->second);
if (it->first == selected_str) if (it->first == selected_str)
ui->SetSelection(ui->GetCount() - 1); ui->SetSelection(ui->GetCount() - 1);
} }

View File

@ -25,7 +25,7 @@ void Chart::draw() {
dc.DrawRectangle(m_rect); dc.DrawRectangle(m_rect);
if (visible_area.m_width < 0.499) { if (visible_area.m_width < 0.499) {
dc.DrawText("NO RAMMING AT ALL",wxPoint(m_rect.GetLeft()+m_rect.GetWidth()/2-50,m_rect.GetBottom()-m_rect.GetHeight()/2)); dc.DrawText(_(L("NO RAMMING AT ALL")),wxPoint(m_rect.GetLeft()+m_rect.GetWidth()/2-50,m_rect.GetBottom()-m_rect.GetHeight()/2));
return; return;
} }
@ -78,12 +78,12 @@ void Chart::draw() {
} }
// axis labels: // axis labels:
wxString label = L("Time (s)"); wxString label = _(L("Time")) + " ("+_(L("s"))+")";
int text_width = 0; int text_width = 0;
int text_height = 0; int text_height = 0;
dc.GetTextExtent(label,&text_width,&text_height); dc.GetTextExtent(label,&text_width,&text_height);
dc.DrawText(label,wxPoint(0.5*(m_rect.GetRight()+m_rect.GetLeft())-text_width/2.f, m_rect.GetBottom()+25)); dc.DrawText(label,wxPoint(0.5*(m_rect.GetRight()+m_rect.GetLeft())-text_width/2.f, m_rect.GetBottom()+25));
label = L("Volumetric speed (mm\u00B3/s)"); label = _(L("Volumetric speed")) + " (" + _(L("mm")) + "\u00B3/" + _(L("s")) + ")";
dc.GetTextExtent(label,&text_width,&text_height); dc.GetTextExtent(label,&text_width,&text_height);
dc.DrawRotatedText(label,wxPoint(0,0.5*(m_rect.GetBottom()+m_rect.GetTop())+text_width/2.f),90); dc.DrawRotatedText(label,wxPoint(0,0.5*(m_rect.GetBottom()+m_rect.GetTop())+text_width/2.f),90);
} }

View File

@ -8,6 +8,7 @@
#include "slic3r/Utils/OctoPrint.hpp" #include "slic3r/Utils/OctoPrint.hpp"
#include "BonjourDialog.hpp" #include "BonjourDialog.hpp"
#include "WipeTowerDialog.hpp" #include "WipeTowerDialog.hpp"
#include "ButtonsDescription.hpp"
#include <wx/app.h> #include <wx/app.h>
#include <wx/button.h> #include <wx/button.h>
@ -23,6 +24,9 @@
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include "wxExtensions.hpp" #include "wxExtensions.hpp"
#include <wx/wupdlock.h>
#include <chrono>
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
@ -88,9 +92,9 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
if (wxMSW) m_btn_delete_preset->SetBackgroundColour(color); if (wxMSW) m_btn_delete_preset->SetBackgroundColour(color);
m_show_incompatible_presets = false; m_show_incompatible_presets = false;
m_bmp_show_incompatible_presets = new wxBitmap(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG); m_bmp_show_incompatible_presets.LoadFile(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG);
m_bmp_hide_incompatible_presets = new wxBitmap(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG); m_bmp_hide_incompatible_presets.LoadFile(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG);
m_btn_hide_incompatible_presets = new wxBitmapButton(panel, wxID_ANY, *m_bmp_hide_incompatible_presets, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); m_btn_hide_incompatible_presets = new wxBitmapButton(panel, wxID_ANY, m_bmp_hide_incompatible_presets, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
if (wxMSW) m_btn_hide_incompatible_presets->SetBackgroundColour(color); if (wxMSW) m_btn_hide_incompatible_presets->SetBackgroundColour(color);
m_btn_save_preset->SetToolTip(_(L("Save current ")) + m_title); m_btn_save_preset->SetToolTip(_(L("Save current ")) + m_title);
@ -99,14 +103,52 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
m_undo_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); m_undo_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
m_undo_to_sys_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); m_undo_to_sys_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
m_question_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
if (wxMSW) { if (wxMSW) {
m_undo_btn->SetBackgroundColour(color); m_undo_btn->SetBackgroundColour(color);
m_undo_to_sys_btn->SetBackgroundColour(color); m_undo_to_sys_btn->SetBackgroundColour(color);
m_question_btn->SetBackgroundColour(color);
} }
m_undo_btn->SetBitmap(wxBitmap(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG));
m_undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_initial_value(); })); m_question_btn->SetToolTip(_(L("Hover the cursor over buttons to find more information \n"
m_undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG)); "or click this button.")));
m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_sys_value(); }));
// Determine the theme color of OS (dark or light)
auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
// Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
m_bmp_value_lock .LoadFile(from_u8(var("sys_lock.png")), wxBITMAP_TYPE_PNG);
m_bmp_value_unlock .LoadFile(from_u8(var(luma >= 128 ? "sys_unlock.png" : "sys_unlock_grey.png")), wxBITMAP_TYPE_PNG);
m_bmp_non_system = &m_bmp_white_bullet;
// Bitmaps to be shown on the "Undo user changes" button next to each input field.
m_bmp_value_revert .LoadFile(from_u8(var(luma >= 128 ? "action_undo.png" : "action_undo_grey.png")), wxBITMAP_TYPE_PNG);
m_bmp_white_bullet .LoadFile(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG);
m_bmp_question .LoadFile(from_u8(var("question_mark_01.png")), wxBITMAP_TYPE_PNG);
fill_icon_descriptions();
set_tooltips_text();
m_undo_btn->SetBitmap(m_bmp_white_bullet);
m_undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_roll_back_value(); }));
m_undo_to_sys_btn->SetBitmap(m_bmp_white_bullet);
m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_roll_back_value(true); }));
m_question_btn->SetBitmap(m_bmp_question);
m_question_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent)
{
auto dlg = new ButtonsDescription(this, &m_icon_descriptions);
if (dlg->ShowModal() == wxID_OK){
// Colors for ui "decoration"
for (Tab *tab : get_tabs_list()){
tab->m_sys_label_clr = get_label_clr_sys();
tab->m_modified_label_clr = get_label_clr_modified();
tab->update_labels_colour();
}
}
}));
// Colors for ui "decoration"
m_sys_label_clr = get_label_clr_sys();
m_modified_label_clr = get_label_clr_modified();
m_default_text_clr = get_label_clr_default();
m_hsizer = new wxBoxSizer(wxHORIZONTAL); m_hsizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_hsizer, 0, wxBOTTOM, 3); sizer->Add(m_hsizer, 0, wxBOTTOM, 3);
@ -120,7 +162,8 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
m_hsizer->AddSpacer(64); m_hsizer->AddSpacer(64);
m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL); m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
// m_hsizer->AddSpacer(64); m_hsizer->AddSpacer(32);
m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL);
// m_hsizer->Add(m_cc_presets_choice, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); // m_hsizer->Add(m_cc_presets_choice, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3);
//Horizontal sizer to hold the tree and the selected page. //Horizontal sizer to hold the tree and the selected page.
@ -183,8 +226,17 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
//! select_preset(m_presets_choice->GetStringSelection().ToStdString()); //! select_preset(m_presets_choice->GetStringSelection().ToStdString());
//! we doing next: //! we doing next:
int selected_item = m_presets_choice->GetSelection(); int selected_item = m_presets_choice->GetSelection();
if (m_selected_preset_item == selected_item && !m_presets->current_is_dirty())
return;
if (selected_item >= 0){ if (selected_item >= 0){
std::string selected_string = m_presets_choice->GetString(selected_item).ToUTF8().data(); std::string selected_string = m_presets_choice->GetString(selected_item).ToUTF8().data();
if (selected_string.find_first_of("-------") == 0
/*selected_string == "------- System presets -------" ||
selected_string == "------- User presets -------"*/){
m_presets_choice->SetSelection(m_selected_preset_item);
return;
}
m_selected_preset_item = selected_item;
select_preset(selected_string); select_preset(selected_string);
} }
})); }));
@ -204,8 +256,9 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
void Tab::load_initial_data() void Tab::load_initial_data()
{ {
m_config = &m_presets->get_edited_preset().config; m_config = &m_presets->get_edited_preset().config;
m_nonsys_btn_icon = m_presets->get_selected_preset_parent() == nullptr ? m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet;
"bullet_white.png" : "sys_unlock.png"; m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
} }
PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages/* = false*/) PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages/* = false*/)
@ -234,140 +287,128 @@ PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bo
return page; return page;
} }
template<class T> void Tab::update_labels_colour()
void add_correct_opts_to_dirty_options(const std::string &opt_key, std::vector<std::string> *vec, TabPrinter *tab)
{ {
auto opt_init = static_cast<T*>(tab->m_presets->get_selected_preset().config.option(opt_key)); Freeze();
auto opt_cur = static_cast<T*>(tab->m_config->option(opt_key)); //update options "decoration"
int opt_init_max_id = opt_init->values.size()-1; for (const auto opt : m_options_list)
for (int i = 0; i < opt_cur->values.size(); i++)
{ {
int init_id = i <= opt_init_max_id ? i : 0; const wxColour *color = &m_sys_label_clr;
if (opt_cur->values[i] != opt_init->values[init_id])
vec->emplace_back(opt_key + "#" + std::to_string(i));
}
}
template<class T> // value isn't equal to system value
void add_correct_opts_to_sys_options(const std::string &opt_key, std::vector<std::string> *vec, TabPrinter *tab) if ((opt.second & osSystemValue) == 0){
{ // value is equal to last saved
const Preset* sys_preset = tab->m_presets->get_selected_preset_parent(); if ((opt.second & osInitValue) != 0)
if (sys_preset == nullptr) color = &m_default_text_clr;
return; // value is modified
T *opt_cur = static_cast<T*>(tab->m_config->option(opt_key)); else
const T *opt_sys = static_cast<const T*>(sys_preset->config.option(opt_key)); color = &m_modified_label_clr;
int opt_max_id = opt_sys->values.size()-1; }
for (int i = 0; i < opt_cur->values.size(); i++) if (opt.first == "bed_shape" || opt.first == "compatible_printers") {
{ if (m_colored_Label != nullptr) {
int init_id = i <= opt_max_id ? i : 0; m_colored_Label->SetForegroundColour(*color);
if (opt_cur->values[i] == opt_sys->values[init_id]) m_colored_Label->Refresh(true);
vec->emplace_back(opt_key + "#" + std::to_string(i)); }
continue;
}
Field* field = get_field(opt.first);
if (field == nullptr) continue;
field->set_label_colour_force(color);
}
Thaw();
auto cur_item = m_treectrl->GetFirstVisibleItem();
while (cur_item){
auto title = m_treectrl->GetItemText(cur_item);
for (auto page : m_pages)
{
if (page->title() != title)
continue;
const wxColor *clr = !page->m_is_nonsys_values ? &m_sys_label_clr :
page->m_is_modified_values ? &m_modified_label_clr :
&m_default_text_clr;
m_treectrl->SetItemTextColour(cur_item, *clr);
break;
}
cur_item = m_treectrl->GetNextVisible(cur_item);
} }
} }
// Update UI according to changes // Update UI according to changes
void Tab::update_changed_ui() void Tab::update_changed_ui()
{ {
auto dirty_options = m_presets->current_dirty_options(); if (m_postpone_update_ui)
return;
if (name() == "printer"){ const bool is_printer_type = (name() == "printer");
// Update dirty_options in case changes of Extruder's options auto dirty_options = m_presets->current_dirty_options(is_printer_type);
auto nonsys_options = m_presets->current_different_from_parent_options(is_printer_type);
if (is_printer_type){
TabPrinter* tab = static_cast<TabPrinter*>(this); TabPrinter* tab = static_cast<TabPrinter*>(this);
m_dirty_options.resize(0);
for (auto opt_key : dirty_options)
{
if (opt_key == "bed_shape"){ m_dirty_options.emplace_back(opt_key); continue; }
switch (m_config->option(opt_key)->type())
{
case coInts: add_correct_opts_to_dirty_options<ConfigOptionInts >(opt_key, &m_dirty_options, tab); break;
case coBools: add_correct_opts_to_dirty_options<ConfigOptionBools >(opt_key, &m_dirty_options, tab); break;
case coFloats: add_correct_opts_to_dirty_options<ConfigOptionFloats >(opt_key, &m_dirty_options, tab); break;
case coStrings: add_correct_opts_to_dirty_options<ConfigOptionStrings >(opt_key, &m_dirty_options, tab); break;
case coPercents:add_correct_opts_to_dirty_options<ConfigOptionPercents >(opt_key, &m_dirty_options, tab); break;
case coPoints: add_correct_opts_to_dirty_options<ConfigOptionPoints >(opt_key, &m_dirty_options, tab); break;
default: m_dirty_options.emplace_back(opt_key); break;
}
}
if (tab->m_initial_extruders_count != tab->m_extruders_count) if (tab->m_initial_extruders_count != tab->m_extruders_count)
m_dirty_options.emplace_back("extruders_count"); dirty_options.emplace_back("extruders_count");
if (tab->m_sys_extruders_count != tab->m_extruders_count)
m_sys_options.resize(0); nonsys_options.emplace_back("extruders_count");
const auto sys_preset = m_presets->get_selected_preset_parent();
if (sys_preset){
for (auto opt_key : m_config->keys())
{
if (opt_key == "bed_shape"){
if (*tab->m_config->option(opt_key) == *sys_preset->config.option(opt_key))
m_sys_options.emplace_back(opt_key);
continue;
}
switch (m_config->option(opt_key)->type())
{
case coInts: add_correct_opts_to_sys_options<ConfigOptionInts >(opt_key, &m_sys_options, tab); break;
case coBools: add_correct_opts_to_sys_options<ConfigOptionBools >(opt_key, &m_sys_options, tab); break;
case coFloats: add_correct_opts_to_sys_options<ConfigOptionFloats >(opt_key, &m_sys_options, tab); break;
case coStrings: add_correct_opts_to_sys_options<ConfigOptionStrings >(opt_key, &m_sys_options, tab); break;
case coPercents:add_correct_opts_to_sys_options<ConfigOptionPercents>(opt_key, &m_sys_options, tab); break;
case coPoints: add_correct_opts_to_sys_options<ConfigOptionPoints >(opt_key, &m_sys_options, tab); break;
default:{
const ConfigOption *opt_cur = tab->m_config->option(opt_key);
const ConfigOption *opt_sys = sys_preset->config.option(opt_key);
if (opt_cur != nullptr && opt_sys != nullptr && *opt_cur == *opt_sys)
m_sys_options.emplace_back(opt_key);
break;
}
}
}
if (tab->m_sys_extruders_count == tab->m_extruders_count)
m_sys_options.emplace_back("extruders_count");
}
}
else{
m_sys_options = m_presets->system_equal_options();
m_dirty_options = dirty_options;
} }
for (auto& it : m_options_list)
it.second = m_opt_status_value;
for (auto opt_key : dirty_options) m_options_list[opt_key] &= ~osInitValue;
for (auto opt_key : nonsys_options) m_options_list[opt_key] &= ~osSystemValue;
Freeze(); Freeze();
//update options "decoration" //update options "decoration"
for (const auto opt_key : m_full_options_list) for (const auto opt : m_options_list)
{ {
bool is_nonsys_value = false; bool is_nonsys_value = false;
bool is_modified_value = true; bool is_modified_value = true;
std::string sys_icon = "sys_lock.png"; const wxBitmap *sys_icon = &m_bmp_value_lock;
std::string icon = "action_undo.png"; const wxBitmap *icon = &m_bmp_value_revert;
wxColour color = get_sys_label_clr();
if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end()) { const wxColour *color = &m_sys_label_clr;
const wxString *sys_tt = &m_tt_value_lock;
const wxString *tt = &m_tt_value_revert;
// value isn't equal to system value
if ((opt.second & osSystemValue) == 0){
is_nonsys_value = true; is_nonsys_value = true;
sys_icon = m_nonsys_btn_icon; sys_icon = m_bmp_non_system;
if(find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end()) sys_tt = m_tt_non_system;
color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); // value is equal to last saved
if ((opt.second & osInitValue) != 0)
color = &m_default_text_clr;
// value is modified
else else
color = get_modified_label_clr(); color = &m_modified_label_clr;
} }
if (find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end()) if ((opt.second & osInitValue) != 0)
{ {
is_modified_value = false; is_modified_value = false;
icon = "bullet_white.png"; icon = &m_bmp_white_bullet;
tt = &m_tt_white_bullet;
} }
if (opt_key == "bed_shape" || opt_key == "compatible_printers") { if (opt.first == "bed_shape" || opt.first == "compatible_printers") {
if (m_colored_Label != nullptr) { if (m_colored_Label != nullptr) {
m_colored_Label->SetForegroundColour(color); m_colored_Label->SetForegroundColour(*color);
m_colored_Label->Refresh(true); m_colored_Label->Refresh(true);
} }
continue; continue;
} }
Field* field = get_field(opt_key); Field* field = get_field(opt.first);
if (field == nullptr) continue; if (field == nullptr) continue;
field->m_is_nonsys_value = is_nonsys_value; field->m_is_nonsys_value = is_nonsys_value;
field->m_is_modified_value = is_modified_value; field->m_is_modified_value = is_modified_value;
field->m_Undo_btn->SetBitmap(wxBitmap(from_u8(var(icon)), wxBITMAP_TYPE_PNG)); field->set_undo_bitmap(icon);
field->m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(sys_icon)), wxBITMAP_TYPE_PNG)); field->set_undo_to_sys_bitmap(sys_icon);
if (field->m_Label != nullptr){ field->set_undo_tooltip(tt);
field->m_Label->SetForegroundColour(color); field->set_undo_to_sys_tooltip(sys_tt);
field->m_Label->Refresh(true); field->set_label_colour(color);
}
} }
Thaw(); Thaw();
@ -376,67 +417,53 @@ void Tab::update_changed_ui()
}); });
} }
void Tab::init_options_list()
{
if (!m_options_list.empty())
m_options_list.clear();
for (const auto opt_key : m_config->keys())
m_options_list.emplace(opt_key, m_opt_status_value);
}
template<class T> template<class T>
void add_correct_opts_to_full_options_list(const std::string &opt_key, std::vector<std::string> *vec, TabPrinter *tab) void add_correct_opts_to_options_list(const std::string &opt_key, std::map<std::string, int>& map, TabPrinter *tab, const int& value)
{ {
T *opt_cur = static_cast<T*>(tab->m_config->option(opt_key)); T *opt_cur = static_cast<T*>(tab->m_config->option(opt_key));
for (int i = 0; i < opt_cur->values.size(); i++) for (int i = 0; i < opt_cur->values.size(); i++)
vec->emplace_back(opt_key + "#" + std::to_string(i)); map.emplace(opt_key + "#" + std::to_string(i), value);
} }
void Tab::update_full_options_list() void TabPrinter::init_options_list()
{ {
if (!m_full_options_list.empty()) if (!m_options_list.empty())
m_full_options_list.resize(0); m_options_list.clear();
if (m_name != "printer"){
m_full_options_list = m_config->keys();
return;
}
TabPrinter* tab = static_cast<TabPrinter*>(this);
for (const auto opt_key : m_config->keys()) for (const auto opt_key : m_config->keys())
{ {
if (opt_key == "bed_shape"){ if (opt_key == "bed_shape"){
m_full_options_list.emplace_back(opt_key); m_options_list.emplace(opt_key, m_opt_status_value);
continue; continue;
} }
switch (m_config->option(opt_key)->type()) switch (m_config->option(opt_key)->type())
{ {
case coInts: add_correct_opts_to_full_options_list<ConfigOptionInts >(opt_key, &m_full_options_list, tab); break; case coInts: add_correct_opts_to_options_list<ConfigOptionInts >(opt_key, m_options_list, this, m_opt_status_value); break;
case coBools: add_correct_opts_to_full_options_list<ConfigOptionBools >(opt_key, &m_full_options_list, tab); break; case coBools: add_correct_opts_to_options_list<ConfigOptionBools >(opt_key, m_options_list, this, m_opt_status_value); break;
case coFloats: add_correct_opts_to_full_options_list<ConfigOptionFloats >(opt_key, &m_full_options_list, tab); break; case coFloats: add_correct_opts_to_options_list<ConfigOptionFloats >(opt_key, m_options_list, this, m_opt_status_value); break;
case coStrings: add_correct_opts_to_full_options_list<ConfigOptionStrings >(opt_key, &m_full_options_list, tab); break; case coStrings: add_correct_opts_to_options_list<ConfigOptionStrings >(opt_key, m_options_list, this, m_opt_status_value); break;
case coPercents:add_correct_opts_to_full_options_list<ConfigOptionPercents >(opt_key, &m_full_options_list, tab); break; case coPercents:add_correct_opts_to_options_list<ConfigOptionPercents >(opt_key, m_options_list, this, m_opt_status_value); break;
case coPoints: add_correct_opts_to_full_options_list<ConfigOptionPoints >(opt_key, &m_full_options_list, tab); break; case coPoints: add_correct_opts_to_options_list<ConfigOptionPoints >(opt_key, m_options_list, this, m_opt_status_value); break;
default: m_full_options_list.emplace_back(opt_key); break; default: m_options_list.emplace(opt_key, m_opt_status_value); break;
} }
} }
m_full_options_list.emplace_back("extruders_count"); m_options_list.emplace("extruders_count", m_opt_status_value);
}
void Tab::update_sys_ui_after_sel_preset()
{
for (const auto opt_key : m_full_options_list){
Field* field = get_field(opt_key);
if (field != nullptr){
field->m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(m_nonsys_btn_icon)), wxBITMAP_TYPE_PNG));
field->m_is_nonsys_value = true;
if (field->m_Label != nullptr){
field->m_Label->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
field->m_Label->Refresh(true);
}
}
}
m_sys_options.resize(0);
} }
void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page) void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page)
{ {
if (sys_page && find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end()) auto opt = m_options_list.find(opt_key);
sys_page = false; if (sys_page) sys_page = (opt->second & osSystemValue) != 0;
if (!modified_page && find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end()) if (!modified_page) modified_page = (opt->second & osInitValue) == 0;
modified_page = true;
} }
void Tab::update_changed_tree_ui() void Tab::update_changed_tree_ui()
@ -457,7 +484,7 @@ void Tab::update_changed_tree_ui()
get_sys_and_mod_flags(opt_key, sys_page, modified_page); get_sys_and_mod_flags(opt_key, sys_page, modified_page);
} }
} }
if (title == _("Dependencies")){ if (title == _("Dependencies") && name() != "printer"){
get_sys_and_mod_flags("compatible_printers", sys_page, modified_page); get_sys_and_mod_flags("compatible_printers", sys_page, modified_page);
} }
for (auto group : page->m_optgroups) for (auto group : page->m_optgroups)
@ -469,12 +496,13 @@ void Tab::update_changed_tree_ui()
get_sys_and_mod_flags(opt_key, sys_page, modified_page); get_sys_and_mod_flags(opt_key, sys_page, modified_page);
} }
} }
if (sys_page)
m_treectrl->SetItemTextColour(cur_item, get_sys_label_clr()); const wxColor *clr = sys_page ? &m_sys_label_clr :
else if (modified_page) modified_page ? &m_modified_label_clr :
m_treectrl->SetItemTextColour(cur_item, get_modified_label_clr()); &m_default_text_clr;
else
m_treectrl->SetItemTextColour(cur_item, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); if (page->set_item_colour(clr))
m_treectrl->SetItemTextColour(cur_item, *clr);
page->m_is_nonsys_values = !sys_page; page->m_is_nonsys_values = !sys_page;
page->m_is_modified_values = modified_page; page->m_is_modified_values = modified_page;
@ -493,80 +521,62 @@ void Tab::update_changed_tree_ui()
void Tab::update_undo_buttons() void Tab::update_undo_buttons()
{ {
const std::string& undo_icon = !m_is_modified_values ? "bullet_white.png" : "action_undo.png"; m_undo_btn->SetBitmap(m_is_modified_values ? m_bmp_value_revert : m_bmp_white_bullet);
const std::string& undo_to_sys_icon = m_is_nonsys_values ? m_nonsys_btn_icon : "sys_lock.png"; m_undo_to_sys_btn->SetBitmap(m_is_nonsys_values ? *m_bmp_non_system : m_bmp_value_lock);
m_undo_btn->SetBitmap(wxBitmap(from_u8(var(undo_icon)), wxBITMAP_TYPE_PNG)); m_undo_btn->SetToolTip(m_is_modified_values ? m_ttg_value_revert : m_ttg_white_bullet);
m_undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(undo_to_sys_icon)), wxBITMAP_TYPE_PNG)); m_undo_to_sys_btn->SetToolTip(m_is_nonsys_values ? *m_ttg_non_system : m_ttg_value_lock);
} }
void Tab::on_back_to_initial_value() void Tab::on_roll_back_value(const bool to_sys /*= true*/)
{ {
if (!m_is_modified_values) return; int os;
if (to_sys) {
if (!m_is_nonsys_values) return;
os = osSystemValue;
}
else {
if (!m_is_modified_values) return;
os = osInitValue;
}
m_postpone_update_ui = true;
auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection());
for (auto page : m_pages) for (auto page : m_pages)
if (page->title() == selection) { if (page->title() == selection) {
for (auto group : page->m_optgroups){ for (auto group : page->m_optgroups){
if (group->title == _("Capabilities")){ if (group->title == _("Capabilities")){
if (find(m_dirty_options.begin(), m_dirty_options.end(), "extruders_count") != m_dirty_options.end()) if ((m_options_list["extruders_count"] & os) == 0)
group->back_to_initial_value("extruders_count"); to_sys ? group->back_to_sys_value("extruders_count") : group->back_to_initial_value("extruders_count");
} }
if (group->title == _("Size and coordinates")){ if (group->title == _("Size and coordinates")){
if (find(m_dirty_options.begin(), m_dirty_options.end(), "bed_shape") != m_dirty_options.end()) if ((m_options_list["bed_shape"] & os) == 0){
group->back_to_initial_value("bed_shape"); to_sys ? group->back_to_sys_value("bed_shape") : group->back_to_initial_value("bed_shape");
} load_key_value("bed_shape", true/*some value*/, true);
if (group->title == _("Profile dependencies")){ }
if (find(m_dirty_options.begin(), m_dirty_options.end(), "compatible_printers") != m_dirty_options.end())
group->back_to_initial_value("compatible_printers");
bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty(); }
m_compatible_printers_checkbox->SetValue(is_empty); if (group->title == _("Profile dependencies") && name() != "printer"){
is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable(); if ((m_options_list["compatible_printers"] & os) == 0){
to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers");
load_key_value("compatible_printers", true/*some value*/, true);
bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty();
m_compatible_printers_checkbox->SetValue(is_empty);
is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable();
}
} }
for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) { for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
const std::string& opt_key = it->first; const std::string& opt_key = it->first;
if (find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end()) if ((m_options_list[opt_key] & os) == 0)
group->back_to_initial_value(opt_key); to_sys ? group->back_to_sys_value(opt_key) : group->back_to_initial_value(opt_key);
} }
} }
break; break;
} }
update_changed_ui();
}
void Tab::on_back_to_sys_value() m_postpone_update_ui = false;
{
if (!m_is_nonsys_values) return;
auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection());
for (auto page : m_pages)
if (page->title() == selection) {
for (auto group : page->m_optgroups) {
if (group->title == _("Capabilities")){
if (find(m_sys_options.begin(), m_sys_options.end(), "extruders_count") == m_sys_options.end())
group->back_to_sys_value("extruders_count");
}
if (group->title == _("Size and coordinates")){
if (find(m_sys_options.begin(), m_sys_options.end(), "bed_shape") == m_sys_options.end())
group->back_to_sys_value("bed_shape");
}
if (group->title == _("Profile dependencies")){
if (find(m_sys_options.begin(), m_sys_options.end(), "compatible_printers") == m_sys_options.end())
group->back_to_sys_value("compatible_printers");
bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty();
m_compatible_printers_checkbox->SetValue(is_empty);
is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable();
}
for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
const std::string& opt_key = it->first;
if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end())
group->back_to_sys_value(opt_key);
}
}
break;
}
update_changed_ui(); update_changed_ui();
} }
@ -581,7 +591,7 @@ void Tab::update_dirty(){
void Tab::update_tab_ui() void Tab::update_tab_ui()
{ {
m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets); m_selected_preset_item = m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets);
// update_tab_presets(m_cc_presets_choice, m_show_incompatible_presets); // update_tab_presets(m_cc_presets_choice, m_show_incompatible_presets);
// update_presetsctrl(m_presetctrl, m_show_incompatible_presets); // update_presetsctrl(m_presetctrl, m_show_incompatible_presets);
} }
@ -636,9 +646,11 @@ bool Tab::set_value(const t_config_option_key& opt_key, const boost::any& value)
// To be called by custom widgets, load a value into a config, // To be called by custom widgets, load a value into a config,
// update the preset selection boxes (the dirty flags) // update the preset selection boxes (the dirty flags)
void Tab::load_key_value(const std::string& opt_key, const boost::any& value) // If value is saved before calling this function, put saved_value = true,
// and value can be some random value because in this case it will not been used
void Tab::load_key_value(const std::string& opt_key, const boost::any& value, bool saved_value /*= false*/)
{ {
change_opt_value(*m_config, opt_key, value); if (!saved_value) change_opt_value(*m_config, opt_key, value);
// Mark the print & filament enabled if they are compatible with the currently selected preset. // Mark the print & filament enabled if they are compatible with the currently selected preset.
if (opt_key.compare("compatible_printers") == 0) { if (opt_key.compare("compatible_printers") == 0) {
// Don't select another profile if this profile happens to become incompatible. // Don't select another profile if this profile happens to become incompatible.
@ -717,13 +729,52 @@ void Tab::on_presets_changed()
event.SetString(m_name); event.SetString(m_name);
g_wxMainFrame->ProcessWindowEvent(event); g_wxMainFrame->ProcessWindowEvent(event);
} }
update_preset_description_line();
}
void Tab::update_preset_description_line()
{
const Preset* parent = m_presets->get_selected_preset_parent(); const Preset* parent = m_presets->get_selected_preset_parent();
const wxString description_line = parent == nullptr ? const Preset& preset = m_presets->get_edited_preset();
_(L("It's default preset")) : parent == &m_presets->get_selected_preset() ?
_(L("It's system preset")) : wxString description_line = preset.is_default ?
_(L("Current preset is inherited from")) + ":\n" + parent->name; _(L("It's a default preset.")) : preset.is_system ?
m_parent_preset_description_line->SetText(description_line); _(L("It's a system preset.")) :
_(L("Current preset is inherited from ")) + (parent == nullptr ?
"default preset." :
":\n\t" + parent->name);
if (preset.is_default || preset.is_system)
description_line += "\n\t" + _(L("It can't be deleted or modified. ")) +
"\n\t" + _(L("Any modifications should be saved as a new preset inherited from this one. ")) +
"\n\t" + _(L("To do that please specify a new name for the preset."));
if (parent && parent->vendor)
{
description_line += "\n\n" + _(L("Additional information:")) + "\n";
description_line += "\t" + _(L("vendor")) + ": " + (name()=="printer" ? "\n\t\t" : "") + parent->vendor->name +
", ver: " + parent->vendor->config_version.to_string();
if (name() == "printer"){
const std::string &printer_model = preset.config.opt_string("printer_model");
const std::string &default_print_profile = preset.config.opt_string("default_print_profile");
const std::vector<std::string> &default_filament_profiles = preset.config.option<ConfigOptionStrings>("default_filament_profile")->values;
if (!printer_model.empty())
description_line += "\n\n\t" + _(L("printer model")) + ": \n\t\t" + printer_model;
if (!default_print_profile.empty())
description_line += "\n\n\t" + _(L("default print profile")) + ": \n\t\t" + default_print_profile;
if (!default_filament_profiles.empty())
{
description_line += "\n\n\t" + _(L("default filament profile")) + ": \n\t\t";
for (auto& profile : default_filament_profiles){
if (&profile != &*default_filament_profiles.begin())
description_line += ", ";
description_line += profile;
}
}
}
}
m_parent_preset_description_line->SetText(description_line, false);
} }
void Tab::update_frequently_changed_parameters() void Tab::update_frequently_changed_parameters()
@ -1012,29 +1063,6 @@ void TabPrint::update()
on_value_change("fill_density", fill_density); on_value_change("fill_density", fill_density);
} }
auto first_layer_height = m_config->option<ConfigOptionFloatOrPercent>("first_layer_height")->value;
auto layer_height = m_config->opt_float("layer_height");
if (m_config->opt_bool("wipe_tower") &&
(first_layer_height != 0.2 || layer_height < 0.15 || layer_height > 0.35)) {
wxString msg_text = _(L("The Wipe Tower currently supports only:\n"
"- first layer height 0.2mm\n"
"- layer height from 0.15mm to 0.35mm\n"
"\nShall I adjust those settings in order to enable the Wipe Tower?"));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Wipe Tower")), wxICON_WARNING | wxYES | wxNO);
DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_YES) {
const auto &val = *m_config->option<ConfigOptionFloatOrPercent>("first_layer_height");
auto percent = val.percent;
new_conf.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.2, percent));
if (m_config->opt_float("layer_height") < 0.15) new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.15));
if (m_config->opt_float("layer_height") > 0.35) new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.35));
}
else
new_conf.set_key_value("wipe_tower", new ConfigOptionBool(false));
load_config(new_conf);
}
if (m_config->opt_bool("wipe_tower") && m_config->opt_bool("support_material") && if (m_config->opt_bool("wipe_tower") && m_config->opt_bool("support_material") &&
m_config->opt_float("support_material_contact_distance") > 0. && m_config->opt_float("support_material_contact_distance") > 0. &&
(m_config->opt_int("support_material_extruder") != 0 || m_config->opt_int("support_material_interface_extruder") != 0)) { (m_config->opt_int("support_material_extruder") != 0 || m_config->opt_int("support_material_interface_extruder") != 0)) {
@ -1284,7 +1312,7 @@ void TabFilament::build()
}; };
optgroup->append_line(line); optgroup->append_line(line);
optgroup = page->new_optgroup(_(L("Toolchange behaviour"))); optgroup = page->new_optgroup(_(L("Toolchange parameters with single extruder MM printers")));
optgroup->append_single_option_line("filament_loading_speed"); optgroup->append_single_option_line("filament_loading_speed");
optgroup->append_single_option_line("filament_unloading_speed"); optgroup->append_single_option_line("filament_unloading_speed");
optgroup->append_single_option_line("filament_toolchange_delay"); optgroup->append_single_option_line("filament_toolchange_delay");
@ -1354,6 +1382,7 @@ void TabFilament::reload_config(){
void TabFilament::update() void TabFilament::update()
{ {
Freeze();
wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset())); wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()));
m_cooling_description_line->SetText(text); m_cooling_description_line->SetText(text);
text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle)); text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle));
@ -1367,6 +1396,7 @@ void TabFilament::update()
for (auto el : { "min_fan_speed", "disable_fan_first_layers" }) for (auto el : { "min_fan_speed", "disable_fan_first_layers" })
get_field(el)->toggle(fan_always_on); get_field(el)->toggle(fan_always_on);
Thaw();
} }
void TabFilament::OnActivate() void TabFilament::OnActivate()
@ -1382,7 +1412,7 @@ wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticTex
(*StaticText)->SetFont(font); (*StaticText)->SetFont(font);
auto sizer = new wxBoxSizer(wxHORIZONTAL); auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(*StaticText); sizer->Add(*StaticText, 1, wxEXPAND|wxALL, 0);
return sizer; return sizer;
} }
@ -1396,6 +1426,9 @@ void TabPrinter::build()
m_presets = &m_preset_bundle->printers; m_presets = &m_preset_bundle->printers;
load_initial_data(); load_initial_data();
// to avoid redundant memory allocation / deallocation during extruders count changing
m_pages.reserve(30);
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter")); auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"));
m_initial_extruders_count = m_extruders_count = nozzle_diameter->values.size(); m_initial_extruders_count = m_extruders_count = nozzle_diameter->values.size();
const Preset* parent_preset = m_presets->get_selected_preset_parent(); const Preset* parent_preset = m_presets->get_selected_preset_parent();
@ -1673,12 +1706,34 @@ void TabPrinter::extruders_count_changed(size_t extruders_count){
} }
void TabPrinter::build_extruder_pages(){ void TabPrinter::build_extruder_pages(){
for (auto extruder_idx = m_extruder_pages.size(); extruder_idx < m_extruders_count; ++extruder_idx){ size_t n_before_extruders = 2; // Count of pages before Extruder pages
size_t n_after_single_extruder_MM = 2; // Count of pages after single_extruder_multi_material page
if (m_extruders_count_old == m_extruders_count || m_extruders_count <= 2)
{
// if we have a single extruder MM setup, add a page with configuration options:
for (int i = 0; i < m_pages.size(); ++i) // first make sure it's not there already
if (m_pages[i]->title().find(_(L("Single extruder MM setup"))) != std::string::npos) {
m_pages.erase(m_pages.begin() + i);
break;
}
if (m_extruders_count > 1 && m_config->opt_bool("single_extruder_multi_material")) {
// create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves
auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png", true);
auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters")));
optgroup->append_single_option_line("cooling_tube_retraction");
optgroup->append_single_option_line("cooling_tube_length");
optgroup->append_single_option_line("parking_pos_retraction");
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
}
}
for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx){
//# build page //# build page
char buf[MIN_BUF_LENGTH_FOR_L]; char buf[MIN_BUF_LENGTH_FOR_L];
sprintf(buf, _CHB(L("Extruder %d")), extruder_idx + 1); sprintf(buf, _CHB(L("Extruder %d")), extruder_idx + 1);
auto page = add_options_page(from_u8(buf), "funnel.png", true); auto page = add_options_page(from_u8(buf), "funnel.png", true);
m_extruder_pages.push_back(page); m_pages.insert(m_pages.begin() + n_before_extruders + extruder_idx, page);
auto optgroup = page->new_optgroup(_(L("Size"))); auto optgroup = page->new_optgroup(_(L("Size")));
optgroup->append_single_option_line("nozzle_diameter", extruder_idx); optgroup->append_single_option_line("nozzle_diameter", extruder_idx);
@ -1716,36 +1771,11 @@ void TabPrinter::build_extruder_pages(){
} }
// # remove extra pages // # remove extra pages
if (m_extruders_count <= m_extruder_pages.size()) { if (m_extruders_count < m_extruders_count_old)
m_extruder_pages.resize(m_extruders_count); m_pages.erase( m_pages.begin() + n_before_extruders + m_extruders_count,
} m_pages.begin() + n_before_extruders + m_extruders_count_old);
// # rebuild page list m_extruders_count_old = m_extruders_count;
PageShp page_note = m_pages.back();
m_pages.pop_back();
while (m_pages.back()->title().find(_(L("Extruder"))) != std::string::npos)
m_pages.pop_back();
for (auto page_extruder : m_extruder_pages)
m_pages.push_back(page_extruder);
m_pages.push_back(page_note);
{
// if we have a single extruder MM setup, add a page with configuration options:
for (int i=0;i<m_pages.size();++i) // first make sure it's not there already
if (m_pages[i]->title().find(_(L("Single extruder MM setup"))) != std::string::npos) {
m_pages.erase(m_pages.begin()+i);
break;
}
if ( m_extruder_pages.size()>1 && m_config->opt_bool("single_extruder_multi_material")) {
// create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves
auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png",true);
auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters")));
optgroup->append_single_option_line("cooling_tube_retraction");
optgroup->append_single_option_line("cooling_tube_length");
optgroup->append_single_option_line("parking_pos_retraction");
m_pages.insert(m_pages.begin()+1,page);
}
}
rebuild_page_tree(); rebuild_page_tree();
} }
@ -1847,14 +1877,15 @@ void Tab::load_current_preset()
{ {
auto preset = m_presets->get_edited_preset(); auto preset = m_presets->get_edited_preset();
preset.is_default ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true); (preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true);
update(); update();
// For the printer profile, generate the extruder pages. // For the printer profile, generate the extruder pages.
on_preset_loaded(); on_preset_loaded();
// Reload preset pages with the new configuration values. // Reload preset pages with the new configuration values.
reload_config(); reload_config();
const Preset* parent = m_presets->get_selected_preset_parent(); m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet;
m_nonsys_btn_icon = parent == nullptr ? "bullet_white.png" : "sys_unlock.png"; m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
// use CallAfter because some field triggers schedule on_change calls using CallAfter, // 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 // and we don't want them to be called after this update_dirty() as they would mark the
@ -1875,8 +1906,8 @@ void Tab::load_current_preset()
static_cast<TabPrinter*>(this)->m_sys_extruders_count = parent_preset == nullptr ? 0 : static_cast<TabPrinter*>(this)->m_sys_extruders_count = parent_preset == nullptr ? 0 :
static_cast<const ConfigOptionFloats*>(parent_preset->config.option("nozzle_diameter"))->values.size(); static_cast<const ConfigOptionFloats*>(parent_preset->config.option("nozzle_diameter"))->values.size();
} }
update_sys_ui_after_sel_preset(); m_opt_status_value = (m_presets->get_selected_preset_parent() ? osSystemValue : 0) | osInitValue;
update_full_options_list(); init_options_list();
update_changed_ui(); update_changed_ui();
}); });
} }
@ -1888,11 +1919,13 @@ void Tab::rebuild_page_tree()
// get label of the currently selected item // get label of the currently selected item
auto selected = m_treectrl->GetItemText(m_treectrl->GetSelection()); auto selected = m_treectrl->GetItemText(m_treectrl->GetSelection());
auto rootItem = m_treectrl->GetRootItem(); auto rootItem = m_treectrl->GetRootItem();
m_treectrl->DeleteChildren(rootItem);
auto have_selection = 0; auto have_selection = 0;
m_treectrl->DeleteChildren(rootItem);
for (auto p : m_pages) for (auto p : m_pages)
{ {
auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID()); auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID());
m_treectrl->SetItemTextColour(itemId, p->get_item_colour());
if (p->title() == selected) { if (p->title() == selected) {
m_disable_tree_sel_changed_event = 1; m_disable_tree_sel_changed_event = 1;
m_treectrl->SelectItem(itemId); m_treectrl->SelectItem(itemId);
@ -1900,7 +1933,7 @@ void Tab::rebuild_page_tree()
have_selection = 1; have_selection = 1;
} }
} }
if (!have_selection) { if (!have_selection) {
// this is triggered on first load, so we don't disable the sel change event // this is triggered on first load, so we don't disable the sel change event
m_treectrl->SelectItem(m_treectrl->GetFirstVisibleItem());//! (treectrl->GetFirstChild(rootItem)); m_treectrl->SelectItem(m_treectrl->GetFirstVisibleItem());//! (treectrl->GetFirstChild(rootItem));
@ -2016,6 +2049,8 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr
void Tab::OnTreeSelChange(wxTreeEvent& event) void Tab::OnTreeSelChange(wxTreeEvent& event)
{ {
if (m_disable_tree_sel_changed_event) return; if (m_disable_tree_sel_changed_event) return;
wxWindowUpdateLocker noUpdates(this);
Page* page = nullptr; Page* page = nullptr;
auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection());
for (auto p : m_pages) for (auto p : m_pages)
@ -2142,7 +2177,7 @@ void Tab::toggle_show_hide_incompatible()
void Tab::update_show_hide_incompatible_button() void Tab::update_show_hide_incompatible_button()
{ {
m_btn_hide_incompatible_presets->SetBitmap(m_show_incompatible_presets ? m_btn_hide_incompatible_presets->SetBitmap(m_show_incompatible_presets ?
*m_bmp_show_incompatible_presets : *m_bmp_hide_incompatible_presets); m_bmp_show_incompatible_presets : m_bmp_hide_incompatible_presets);
m_btn_hide_incompatible_presets->SetToolTip(m_show_incompatible_presets ? m_btn_hide_incompatible_presets->SetToolTip(m_show_incompatible_presets ?
"Both compatible an incompatible presets are shown. Click to hide presets not compatible with the current printer." : "Both compatible an incompatible presets are shown. Click to hide presets not compatible with the current printer." :
"Only compatible presets are shown. Click to show both the presets compatible and not compatible with the current printer."); "Only compatible presets are shown. Click to show both the presets compatible and not compatible with the current printer.");
@ -2402,6 +2437,75 @@ void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
ui->Thaw(); ui->Thaw();
} }
void Tab::fill_icon_descriptions()
{
m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_lock, L("LOCKED LOCK;"
"indicates that the settings are the same as the system values for the current option group")));
m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_unlock, L("UNLOCKED LOCK;"
"indicates that some settings were changed and are not equal to the system values for "
"the current option group.\n"
"Click the UNLOCKED LOCK icon to reset all settings for current option group to "
"the system values.")));
m_icon_descriptions.push_back(t_icon_description(&m_bmp_white_bullet, L("WHITE BULLET;"
"for the left button: \tindicates a non-system preset,\n"
"for the right button: \tindicates that the settings hasn't been modified.")));
m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_revert, L("BACK ARROW;"
"indicates that the settings were changed and are not equal to the last saved preset for "
"the current option group.\n"
"Click the BACK ARROW icon to reset all settings for the current option group to "
"the last saved preset.")));
}
void Tab::set_tooltips_text()
{
// m_undo_to_sys_btn->SetToolTip(_(L( "LOCKED LOCK icon indicates that the settings are the same as the system values "
// "for the current option group.\n"
// "UNLOCKED LOCK icon indicates that some settings were changed and are not equal "
// "to the system values for the current option group.\n"
// "WHITE BULLET icon indicates a non system preset.\n\n"
// "Click the UNLOCKED LOCK icon to reset all settings for current option group to "
// "the system values.")));
//
// m_undo_btn->SetToolTip(_(L( "WHITE BULLET icon indicates that the settings are the same as in the last saved"
// "preset for the current option group.\n"
// "BACK ARROW icon indicates that the settings were changed and are not equal to "
// "the last saved preset for the current option group.\n\n"
// "Click the BACK ARROW icon to reset all settings for the current option group to "
// "the last saved preset.")));
// --- Tooltip text for reset buttons (for whole options group)
// Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
m_ttg_value_lock = _(L("LOCKED LOCK icon indicates that the settings are the same as the system values "
"for the current option group"));
m_ttg_value_unlock = _(L("UNLOCKED LOCK icon indicates that some settings were changed and are not equal "
"to the system values for the current option group.\n"
"Click to reset all settings for current option group to the system values."));
m_ttg_white_bullet_ns = _(L("WHITE BULLET icon indicates a non system preset."));
m_ttg_non_system = &m_ttg_white_bullet_ns;
// Text to be shown on the "Undo user changes" button next to each input field.
m_ttg_white_bullet = _(L("WHITE BULLET icon indicates that the settings are the same as in the last saved "
"preset for the current option group."));
m_ttg_value_revert = _(L("BACK ARROW icon indicates that the settings were changed and are not equal to "
"the last saved preset for the current option group.\n"
"Click to reset all settings for the current option group to the last saved preset."));
// --- Tooltip text for reset buttons (for each option in group)
// Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
m_tt_value_lock = _(L("LOCKED LOCK icon indicates that the value is the same as the system value."));
m_tt_value_unlock = _(L("UNLOCKED LOCK icon indicates that the value was changed and is not equal "
"to the system value.\n"
"Click to reset current value to the system value."));
// m_tt_white_bullet_ns= _(L("WHITE BULLET icon indicates a non system preset."));
m_tt_non_system = &m_ttg_white_bullet_ns;
// Text to be shown on the "Undo user changes" button next to each input field.
m_tt_white_bullet = _(L("WHITE BULLET icon indicates that the value is the same as in the last saved preset."));
m_tt_value_revert = _(L("BACK ARROW icon indicates that the value was changed and is not equal to the last saved preset.\n"
"Click to reset current value to the last saved preset."));
}
void Page::reload_config() void Page::reload_config()
{ {
for (auto group : m_optgroups) for (auto group : m_optgroups)
@ -2460,10 +2564,6 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la
return static_cast<Tab*>(GetParent())->m_presets->get_selected_preset_parent() != nullptr; return static_cast<Tab*>(GetParent())->m_presets->get_selected_preset_parent() != nullptr;
}; };
optgroup->nonsys_btn_icon = [this](){
return static_cast<Tab*>(GetParent())->m_nonsys_btn_icon;
};
vsizer()->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 10); vsizer()->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 10);
m_optgroups.push_back(optgroup); m_optgroups.push_back(optgroup);

View File

@ -1,3 +1,6 @@
#ifndef slic3r_Tab_hpp_
#define slic3r_Tab_hpp_
// The "Expert" tab at the right of the main tabbed window. // The "Expert" tab at the right of the main tabbed window.
// //
// This file implements following packages: // This file implements following packages:
@ -34,6 +37,9 @@
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
typedef std::pair<wxBitmap*, std::string> t_icon_description;
typedef std::vector<std::pair<wxBitmap*, std::string>> t_icon_descriptions;
// Single Tab page containing a{ vsizer } of{ optgroups } // Single Tab page containing a{ vsizer } of{ optgroups }
// package Slic3r::GUI::Tab::Page; // package Slic3r::GUI::Tab::Page;
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>; using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
@ -51,6 +57,7 @@ public:
{ {
Create(m_parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); Create(m_parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
m_vsizer = new wxBoxSizer(wxVERTICAL); m_vsizer = new wxBoxSizer(wxVERTICAL);
m_item_color = &get_label_clr_default();
SetSizer(m_vsizer); SetSizer(m_vsizer);
} }
~Page(){} ~Page(){}
@ -71,6 +78,22 @@ public:
Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const; Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const;
bool set_value(const t_config_option_key& opt_key, const boost::any& value); bool set_value(const t_config_option_key& opt_key, const boost::any& value);
ConfigOptionsGroupShp new_optgroup(const wxString& title, int noncommon_label_width = -1); ConfigOptionsGroupShp new_optgroup(const wxString& title, int noncommon_label_width = -1);
bool set_item_colour(const wxColour *clr) {
if (m_item_color != clr) {
m_item_color = clr;
return true;
}
return false;
}
const wxColour get_item_colour() {
return *m_item_color;
}
protected:
// Color of TreeCtrlItem. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
const wxColour* m_item_color;
}; };
// Slic3r::GUI::Tab; // Slic3r::GUI::Tab;
@ -85,8 +108,6 @@ protected:
wxBitmapComboBox* m_presets_choice; wxBitmapComboBox* m_presets_choice;
wxBitmapButton* m_btn_save_preset; wxBitmapButton* m_btn_save_preset;
wxBitmapButton* m_btn_delete_preset; wxBitmapButton* m_btn_delete_preset;
wxBitmap* m_bmp_show_incompatible_presets;
wxBitmap* m_bmp_hide_incompatible_presets;
wxBitmapButton* m_btn_hide_incompatible_presets; wxBitmapButton* m_btn_hide_incompatible_presets;
wxBoxSizer* m_hsizer; wxBoxSizer* m_hsizer;
wxBoxSizer* m_left_sizer; wxBoxSizer* m_left_sizer;
@ -96,10 +117,51 @@ protected:
wxButton* m_compatible_printers_btn; wxButton* m_compatible_printers_btn;
wxButton* m_undo_btn; wxButton* m_undo_btn;
wxButton* m_undo_to_sys_btn; wxButton* m_undo_to_sys_btn;
wxButton* m_question_btn;
wxComboCtrl* m_cc_presets_choice; wxComboCtrl* m_cc_presets_choice;
wxDataViewTreeCtrl* m_presetctrl; wxDataViewTreeCtrl* m_presetctrl;
wxImageList* m_preset_icons; wxImageList* m_preset_icons;
// Cached bitmaps.
// A "flag" icon to be displayned next to the preset name in the Tab's combo box.
wxBitmap m_bmp_show_incompatible_presets;
wxBitmap m_bmp_hide_incompatible_presets;
// Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
wxBitmap m_bmp_value_lock;
wxBitmap m_bmp_value_unlock;
wxBitmap m_bmp_white_bullet;
// The following bitmap points to either m_bmp_value_unlock or m_bmp_white_bullet, depending on whether the current preset has a parent preset.
wxBitmap *m_bmp_non_system;
// Bitmaps to be shown on the "Undo user changes" button next to each input field.
wxBitmap m_bmp_value_revert;
// wxBitmap m_bmp_value_unmodified;
wxBitmap m_bmp_question;
// Colors for ui "decoration"
wxColour m_sys_label_clr;
wxColour m_modified_label_clr;
wxColour m_default_text_clr;
// Tooltip text for reset buttons (for whole options group)
wxString m_ttg_value_lock;
wxString m_ttg_value_unlock;
wxString m_ttg_white_bullet_ns;
// The following text points to either m_ttg_value_unlock or m_ttg_white_bullet_ns, depending on whether the current preset has a parent preset.
wxString *m_ttg_non_system;
// Tooltip text to be shown on the "Undo user changes" button next to each input field.
wxString m_ttg_white_bullet;
wxString m_ttg_value_revert;
// Tooltip text for reset buttons (for each option in group)
wxString m_tt_value_lock;
wxString m_tt_value_unlock;
// The following text points to either m_tt_value_unlock or m_ttg_white_bullet_ns, depending on whether the current preset has a parent preset.
wxString *m_tt_non_system;
// Tooltip text to be shown on the "Undo user changes" button next to each input field.
wxString m_tt_white_bullet;
wxString m_tt_value_revert;
int m_icon_count; int m_icon_count;
std::map<std::string, size_t> m_icon_index; // Map from an icon file name to its index std::map<std::string, size_t> m_icon_index; // Map from an icon file name to its index
std::vector<PageShp> m_pages; std::vector<PageShp> m_pages;
@ -108,9 +170,11 @@ protected:
bool m_no_controller; bool m_no_controller;
std::vector<std::string> m_reload_dependent_tabs = {}; std::vector<std::string> m_reload_dependent_tabs = {};
std::vector<std::string> m_dirty_options = {}; enum OptStatus { osSystemValue = 1, osInitValue = 2 };
std::vector<std::string> m_sys_options = {}; std::map<std::string, int> m_options_list;
std::vector<std::string> m_full_options_list = {}; int m_opt_status_value;
t_icon_descriptions m_icon_descriptions = {};
// The two following two event IDs are generated at Plater.pm by calling Wx::NewEventType. // The two following two event IDs are generated at Plater.pm by calling Wx::NewEventType.
wxEventType m_event_value_change = 0; wxEventType m_event_value_change = 0;
@ -118,13 +182,15 @@ protected:
bool m_is_modified_values{ false }; bool m_is_modified_values{ false };
bool m_is_nonsys_values{ true }; bool m_is_nonsys_values{ true };
bool m_postpone_update_ui {false};
size_t m_selected_preset_item{ 0 };
public: public:
PresetBundle* m_preset_bundle; PresetBundle* m_preset_bundle;
bool m_show_btn_incompatible_presets = false; bool m_show_btn_incompatible_presets = false;
PresetCollection* m_presets; PresetCollection* m_presets;
DynamicPrintConfig* m_config; DynamicPrintConfig* m_config;
std::string m_nonsys_btn_icon;
ogStaticText* m_parent_preset_description_line; ogStaticText* m_parent_preset_description_line;
wxStaticText* m_colored_Label = nullptr; wxStaticText* m_colored_Label = nullptr;
@ -135,7 +201,9 @@ public:
Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL); Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
get_tabs_list().push_back(this); get_tabs_list().push_back(this);
} }
~Tab() { delete_tab_from_list(this); } ~Tab(){
delete_tab_from_list(this);
}
wxWindow* parent() const { return m_parent; } wxWindow* parent() const { return m_parent; }
wxString title() const { return m_title; } wxString title() const { return m_title; }
@ -153,7 +221,7 @@ public:
wxSizer* compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn); wxSizer* compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn);
void update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible); void update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible);
void load_key_value(const std::string& opt_key, const boost::any& value); void load_key_value(const std::string& opt_key, const boost::any& value, bool saved_value = false);
void reload_compatible_printers_widget(); void reload_compatible_printers_widget();
void OnTreeSelChange(wxTreeEvent& event); void OnTreeSelChange(wxTreeEvent& event);
@ -164,15 +232,13 @@ public:
void toggle_show_hide_incompatible(); void toggle_show_hide_incompatible();
void update_show_hide_incompatible_button(); void update_show_hide_incompatible_button();
void update_ui_from_settings(); void update_ui_from_settings();
void update_labels_colour();
void update_changed_ui(); void update_changed_ui();
void update_full_options_list();
void update_sys_ui_after_sel_preset();
void get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page); void get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page);
void update_changed_tree_ui(); void update_changed_tree_ui();
void update_undo_buttons(); void update_undo_buttons();
void on_back_to_initial_value(); void on_roll_back_value(const bool to_sys = false);
void on_back_to_sys_value();
PageShp add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages = false); PageShp add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages = false);
@ -180,6 +246,7 @@ public:
virtual void on_preset_loaded(){} virtual void on_preset_loaded(){}
virtual void build() = 0; virtual void build() = 0;
virtual void update() = 0; virtual void update() = 0;
virtual void init_options_list();
void load_initial_data(); void load_initial_data();
void update_dirty(); void update_dirty();
void update_tab_ui(); void update_tab_ui();
@ -189,20 +256,22 @@ public:
bool set_value(const t_config_option_key& opt_key, const boost::any& value); bool set_value(const t_config_option_key& opt_key, const boost::any& value);
wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText); wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText);
bool current_preset_is_dirty(); bool current_preset_is_dirty();
DynamicPrintConfig* get_config() { return m_config; } DynamicPrintConfig* get_config() { return m_config; }
PresetCollection* get_presets() PresetCollection* get_presets() { return m_presets; }
{
return m_presets;
}
std::vector<std::string> get_dependent_tabs() { return m_reload_dependent_tabs; } std::vector<std::string> get_dependent_tabs() { return m_reload_dependent_tabs; }
size_t get_selected_preset_item() { return m_selected_preset_item; }
void on_value_change(const std::string& opt_key, const boost::any& value); void on_value_change(const std::string& opt_key, const boost::any& value);
protected: protected:
void on_presets_changed(); void on_presets_changed();
void update_preset_description_line();
void update_frequently_changed_parameters(); void update_frequently_changed_parameters();
void update_wiping_button_visibility(); void update_wiping_button_visibility();
void update_tab_presets(wxComboCtrl* ui, bool show_incompatible); void update_tab_presets(wxComboCtrl* ui, bool show_incompatible);
void fill_icon_descriptions();
void set_tooltips_text();
}; };
//Slic3r::GUI::Tab::Print; //Slic3r::GUI::Tab::Print;
@ -248,9 +317,9 @@ public:
wxButton* m_octoprint_host_test_btn; wxButton* m_octoprint_host_test_btn;
size_t m_extruders_count; size_t m_extruders_count;
size_t m_extruders_count_old = 0;
size_t m_initial_extruders_count; size_t m_initial_extruders_count;
size_t m_sys_extruders_count; size_t m_sys_extruders_count;
std::vector<PageShp> m_extruder_pages;
TabPrinter() {} TabPrinter() {}
TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {} TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {}
@ -262,6 +331,7 @@ public:
void extruders_count_changed(size_t extruders_count); void extruders_count_changed(size_t extruders_count);
void build_extruder_pages(); void build_extruder_pages();
void on_preset_loaded() override; void on_preset_loaded() override;
void init_options_list() override;
}; };
class SavePresetWindow :public wxDialog class SavePresetWindow :public wxDialog
@ -280,3 +350,5 @@ public:
} // GUI } // GUI
} // Slic3r } // Slic3r
#endif /* slic3r_Tab_hpp_ */

View File

@ -11,6 +11,7 @@ void TabIface::load_config(DynamicPrintConfig* config) { m_tab->load_config(*con
void TabIface::load_key_value(char* opt_key, char* value){ m_tab->load_key_value(opt_key, static_cast<std::string>(value)); } void TabIface::load_key_value(char* opt_key, char* value){ m_tab->load_key_value(opt_key, static_cast<std::string>(value)); }
bool TabIface::current_preset_is_dirty() { return m_tab->current_preset_is_dirty();} bool TabIface::current_preset_is_dirty() { return m_tab->current_preset_is_dirty();}
void TabIface::OnActivate() { return m_tab->OnActivate();} void TabIface::OnActivate() { return m_tab->OnActivate();}
size_t TabIface::get_selected_preset_item() { return m_tab->get_selected_preset_item(); }
std::string TabIface::title() { return m_tab->title().ToUTF8().data(); } std::string TabIface::title() { return m_tab->title().ToUTF8().data(); }
DynamicPrintConfig* TabIface::get_config() { return m_tab->get_config(); } DynamicPrintConfig* TabIface::get_config() { return m_tab->get_config(); }
PresetCollection* TabIface::get_presets() { return m_tab!=nullptr ? m_tab->get_presets() : nullptr; } PresetCollection* TabIface::get_presets() { return m_tab!=nullptr ? m_tab->get_presets() : nullptr; }

View File

@ -1,3 +1,6 @@
#ifndef slic3r_TabIface_hpp_
#define slic3r_TabIface_hpp_
#include <vector> #include <vector>
#include <string> #include <string>
@ -27,9 +30,12 @@ public:
DynamicPrintConfig* get_config(); DynamicPrintConfig* get_config();
PresetCollection* get_presets(); PresetCollection* get_presets();
std::vector<std::string> get_dependent_tabs(); std::vector<std::string> get_dependent_tabs();
size_t get_selected_preset_item();
protected: protected:
GUI::Tab *m_tab; GUI::Tab *m_tab;
}; }; // namespace GUI
}; // namespace Slic3r }; // namespace Slic3r
#endif /* slic3r_TabIface_hpp_ */

View File

@ -0,0 +1,196 @@
#include "UpdateDialogs.hpp"
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/event.h>
#include <wx/stattext.h>
#include <wx/button.h>
#include <wx/hyperlink.h>
#include <wx/statbmp.h>
#include <wx/checkbox.h>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Utils.hpp"
#include "GUI.hpp"
#include "ConfigWizard.hpp"
namespace Slic3r {
namespace GUI {
static const std::string CONFIG_UPDATE_WIKI_URL("https://github.com/prusa3d/Slic3r/wiki/Slic3r-PE-1.40-configuration-update");
// MsgUpdateSlic3r
MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_online) :
MsgDialog(nullptr, _(L("Update available")), _(L("New version of Slic3r PE is available"))),
ver_current(ver_current),
ver_online(ver_online)
{
const auto url = wxString::Format("https://github.com/prusa3d/Slic3r/releases/tag/version_%s", ver_online.to_string());
auto *link = new wxHyperlinkCtrl(this, wxID_ANY, url, url);
auto *text = new wxStaticText(this, wxID_ANY, _(L("To download, follow the link below.")));
const auto link_width = link->GetSize().GetWidth();
text->Wrap(CONTENT_WIDTH > link_width ? CONTENT_WIDTH : link_width);
content_sizer->Add(text);
content_sizer->AddSpacer(VERT_SPACING);
auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING);
versions->Add(new wxStaticText(this, wxID_ANY, _(L("Current version:"))));
versions->Add(new wxStaticText(this, wxID_ANY, ver_current.to_string()));
versions->Add(new wxStaticText(this, wxID_ANY, _(L("New version:"))));
versions->Add(new wxStaticText(this, wxID_ANY, ver_online.to_string()));
content_sizer->Add(versions);
content_sizer->AddSpacer(VERT_SPACING);
content_sizer->Add(link);
content_sizer->AddSpacer(2*VERT_SPACING);
cbox = new wxCheckBox(this, wxID_ANY, _(L("Don't notify about new releases any more")));
content_sizer->Add(cbox);
content_sizer->AddSpacer(VERT_SPACING);
Fit();
}
MsgUpdateSlic3r::~MsgUpdateSlic3r() {}
bool MsgUpdateSlic3r::disable_version_check() const
{
return cbox->GetValue();
}
// MsgUpdateConfig
MsgUpdateConfig::MsgUpdateConfig(const std::unordered_map<std::string, std::string> &updates) :
MsgDialog(nullptr, _(L("Configuration update")), _(L("Configuration update is available")), wxID_NONE)
{
auto *text = new wxStaticText(this, wxID_ANY, _(L(
"Would you like to install it?\n\n"
"Note that a full configuration snapshot will be created first. It can then be restored at any time "
"should there be a problem with the new version.\n\n"
"Updated configuration bundles:"
)));
text->Wrap(CONTENT_WIDTH);
content_sizer->Add(text);
content_sizer->AddSpacer(VERT_SPACING);
auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING);
for (const auto &update : updates) {
auto *text_vendor = new wxStaticText(this, wxID_ANY, update.first);
text_vendor->SetFont(boldfont);
versions->Add(text_vendor);
versions->Add(new wxStaticText(this, wxID_ANY, update.second));
}
content_sizer->Add(versions);
content_sizer->AddSpacer(2*VERT_SPACING);
auto *btn_cancel = new wxButton(this, wxID_CANCEL);
btn_sizer->Add(btn_cancel);
btn_sizer->AddSpacer(HORIZ_SPACING);
auto *btn_ok = new wxButton(this, wxID_OK);
btn_sizer->Add(btn_ok);
btn_ok->SetFocus();
Fit();
}
MsgUpdateConfig::~MsgUpdateConfig() {}
// MsgDataIncompatible
MsgDataIncompatible::MsgDataIncompatible(const std::unordered_map<std::string, wxString> &incompats) :
MsgDialog(nullptr, _(L("Slic3r incompatibility")), _(L("Slic3r configuration is incompatible")), wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG), wxID_NONE)
{
auto *text = new wxStaticText(this, wxID_ANY, _(L(
"This version of Slic3r PE is not compatible with currently installed configuration bundles.\n"
"This probably happened as a result of running an older Slic3r PE after using a newer one.\n\n"
"You may either exit Slic3r and try again with a newer version, or you may re-run the initial configuration. "
"Doing so will create a backup snapshot of the existing configuration before installing files compatible with this Slic3r.\n"
)));
text->Wrap(CONTENT_WIDTH);
content_sizer->Add(text);
auto *text2 = new wxStaticText(this, wxID_ANY, wxString::Format(_(L("This Slic3r PE version: %s")), SLIC3R_VERSION));
text2->Wrap(CONTENT_WIDTH);
content_sizer->Add(text2);
content_sizer->AddSpacer(VERT_SPACING);
auto *text3 = new wxStaticText(this, wxID_ANY, _(L("Incompatible bundles:")));
text3->Wrap(CONTENT_WIDTH);
content_sizer->Add(text3);
content_sizer->AddSpacer(VERT_SPACING);
auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING);
for (const auto &incompat : incompats) {
auto *text_vendor = new wxStaticText(this, wxID_ANY, incompat.first);
text_vendor->SetFont(boldfont);
versions->Add(text_vendor);
versions->Add(new wxStaticText(this, wxID_ANY, incompat.second));
}
content_sizer->Add(versions);
content_sizer->AddSpacer(2*VERT_SPACING);
auto *btn_exit = new wxButton(this, wxID_EXIT, _(L("Exit Slic3r")));
btn_sizer->Add(btn_exit);
btn_sizer->AddSpacer(HORIZ_SPACING);
auto *btn_reconf = new wxButton(this, wxID_REPLACE, _(L("Re-configure")));
btn_sizer->Add(btn_reconf);
btn_exit->SetFocus();
auto exiter = [this](const wxCommandEvent& evt) { this->EndModal(evt.GetId()); };
btn_exit->Bind(wxEVT_BUTTON, exiter);
btn_reconf->Bind(wxEVT_BUTTON, exiter);
Fit();
}
MsgDataIncompatible::~MsgDataIncompatible() {}
// MsgDataLegacy
MsgDataLegacy::MsgDataLegacy() :
MsgDialog(nullptr, _(L("Configuration update")), _(L("Configuration update")))
{
auto *text = new wxStaticText(this, wxID_ANY, wxString::Format(
_(L(
"Slic3r PE now uses an updated configuration structure.\n\n"
"So called 'System presets' have been introduced, which hold the built-in default settings for various "
"printers. These System presets cannot be modified, instead, users now may create their "
"own presets inheriting settings from one of the System presets.\n"
"An inheriting preset may either inherit a particular value from its parent or override it with a customized value.\n\n"
"Please proceed with the %s that follows to set up the new presets "
"and to choose whether to enable automatic preset updates."
)),
ConfigWizard::name()
));
text->Wrap(CONTENT_WIDTH);
content_sizer->Add(text);
content_sizer->AddSpacer(VERT_SPACING);
auto *text2 = new wxStaticText(this, wxID_ANY, _(L("For more information please visit our wiki page:")));
static const wxString url("https://github.com/prusa3d/Slic3r/wiki/Slic3r-PE-1.40-configuration-update");
// The wiki page name is intentionally not localized:
auto *link = new wxHyperlinkCtrl(this, wxID_ANY, "Slic3r PE 1.40 configuration update", CONFIG_UPDATE_WIKI_URL);
content_sizer->Add(text2);
content_sizer->Add(link);
content_sizer->AddSpacer(VERT_SPACING);
Fit();
}
MsgDataLegacy::~MsgDataLegacy() {}
}
}

View File

@ -0,0 +1,81 @@
#ifndef slic3r_UpdateDialogs_hpp_
#define slic3r_UpdateDialogs_hpp_
#include <string>
#include <unordered_map>
#include "slic3r/Utils/Semver.hpp"
#include "MsgDialog.hpp"
class wxBoxSizer;
class wxCheckBox;
namespace Slic3r {
namespace GUI {
// A confirmation dialog listing configuration updates
class MsgUpdateSlic3r : public MsgDialog
{
public:
MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_online);
MsgUpdateSlic3r(MsgUpdateSlic3r &&) = delete;
MsgUpdateSlic3r(const MsgUpdateSlic3r &) = delete;
MsgUpdateSlic3r &operator=(MsgUpdateSlic3r &&) = delete;
MsgUpdateSlic3r &operator=(const MsgUpdateSlic3r &) = delete;
virtual ~MsgUpdateSlic3r();
// Tells whether the user checked the "don't bother me again" checkbox
bool disable_version_check() const;
private:
const Semver &ver_current;
const Semver &ver_online;
wxCheckBox *cbox;
};
// Confirmation dialog informing about configuration update. Lists updated bundles & their versions.
class MsgUpdateConfig : public MsgDialog
{
public:
// updates is a map of "vendor name" -> "version (comment)"
MsgUpdateConfig(const std::unordered_map<std::string, std::string> &updates);
MsgUpdateConfig(MsgUpdateConfig &&) = delete;
MsgUpdateConfig(const MsgUpdateConfig &) = delete;
MsgUpdateConfig &operator=(MsgUpdateConfig &&) = delete;
MsgUpdateConfig &operator=(const MsgUpdateConfig &) = delete;
~MsgUpdateConfig();
};
// Informs about currently installed bundles not being compatible with the running Slic3r. Asks about action.
class MsgDataIncompatible : public MsgDialog
{
public:
// incompats is a map of "vendor name" -> "version restrictions"
MsgDataIncompatible(const std::unordered_map<std::string, wxString> &incompats);
MsgDataIncompatible(MsgDataIncompatible &&) = delete;
MsgDataIncompatible(const MsgDataIncompatible &) = delete;
MsgDataIncompatible &operator=(MsgDataIncompatible &&) = delete;
MsgDataIncompatible &operator=(const MsgDataIncompatible &) = delete;
~MsgDataIncompatible();
};
// Informs about a legacy data directory - an update from Slic3r PE < 1.40
class MsgDataLegacy : public MsgDialog
{
public:
MsgDataLegacy();
MsgDataLegacy(MsgDataLegacy &&) = delete;
MsgDataLegacy(const MsgDataLegacy &) = delete;
MsgDataLegacy &operator=(MsgDataLegacy &&) = delete;
MsgDataLegacy &operator=(const MsgDataLegacy &) = delete;
~MsgDataLegacy();
};
}
}
#endif

View File

@ -79,15 +79,15 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters)
m_widget_ramming_step_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,10,200,100); m_widget_ramming_step_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,10,200,100);
auto gsizer_param = new wxFlexGridSizer(2, 5, 15); auto gsizer_param = new wxFlexGridSizer(2, 5, 15);
gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total ramming time (s):")))), 0, wxALIGN_CENTER_VERTICAL); gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total ramming time")) + " (" + _(L("s")) + "):")), 0, wxALIGN_CENTER_VERTICAL);
gsizer_param->Add(m_widget_time); gsizer_param->Add(m_widget_time);
gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total rammed volume (mm"))+"\u00B3):")), 0, wxALIGN_CENTER_VERTICAL); gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total rammed volume")) + " (" + _(L("mm")) + "\u00B3):")), 0, wxALIGN_CENTER_VERTICAL);
gsizer_param->Add(m_widget_volume); gsizer_param->Add(m_widget_volume);
gsizer_param->AddSpacer(20); gsizer_param->AddSpacer(20);
gsizer_param->AddSpacer(20); gsizer_param->AddSpacer(20);
gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Ramming line width (%):")))), 0, wxALIGN_CENTER_VERTICAL); gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Ramming line width")) + " (%):")), 0, wxALIGN_CENTER_VERTICAL);
gsizer_param->Add(m_widget_ramming_line_width_multiplicator); gsizer_param->Add(m_widget_ramming_line_width_multiplicator);
gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Ramming line spacing (%):")))), 0, wxALIGN_CENTER_VERTICAL); gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Ramming line spacing")) + " (%):")), 0, wxALIGN_CENTER_VERTICAL);
gsizer_param->Add(m_widget_ramming_step_multiplicator); gsizer_param->Add(m_widget_ramming_step_multiplicator);
sizer_param->Add(gsizer_param, 0, wxTOP, 100); sizer_param->Add(gsizer_param, 0, wxTOP, 100);
@ -220,7 +220,7 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con
// collect and format sizer // collect and format sizer
format_sizer(m_sizer_advanced, m_page_advanced, m_gridsizer_advanced, format_sizer(m_sizer_advanced, m_page_advanced, m_gridsizer_advanced,
_(L("Here you can adjust required purging volume (mm\u00B3) for any given pair of tools.")), wxString::Format(_(L("Here you can adjust required purging volume (mm%s) for any given pair of tools.")), "\u00B3"),
_(L("Extruder changed to"))); _(L("Extruder changed to")));
// Hide preview page before new page creating // Hide preview page before new page creating
@ -243,7 +243,7 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con
// collect and format sizer // collect and format sizer
format_sizer(m_sizer_simple, m_page_simple, gridsizer_simple, format_sizer(m_sizer_simple, m_page_simple, gridsizer_simple,
_(L("Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded.")), _(L("Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded.")),
_(L("Volume to purge (mm\u00B3) when the filament is being")), 50); wxString::Format(_(L("Volume to purge (mm%s) when the filament is being")), "\u00B3"), 50);
m_sizer = new wxBoxSizer(wxVERTICAL); m_sizer = new wxBoxSizer(wxVERTICAL);
m_sizer->Add(m_page_simple, 0, wxEXPAND | wxALL, 25); m_sizer->Add(m_page_simple, 0, wxEXPAND | wxALL, 25);

View File

@ -1,9 +1,9 @@
#include "PresetUpdater.hpp" #include "PresetUpdater.hpp"
#include <iostream> // XXX
#include <algorithm> #include <algorithm>
#include <thread> #include <thread>
#include <stack> #include <unordered_map>
#include <ostream>
#include <stdexcept> #include <stdexcept>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
@ -14,18 +14,13 @@
#include <wx/app.h> #include <wx/app.h>
#include <wx/event.h> #include <wx/event.h>
#include <wx/msgdlg.h> #include <wx/msgdlg.h>
#include <wx/dialog.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/button.h>
#include <wx/hyperlink.h>
#include <wx/statbmp.h>
#include <wx/checkbox.h>
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/PresetBundle.hpp"
#include "slic3r/GUI/UpdateDialogs.hpp"
#include "slic3r/GUI/ConfigWizard.hpp"
#include "slic3r/Utils/Http.hpp" #include "slic3r/Utils/Http.hpp"
#include "slic3r/Config/Version.hpp" #include "slic3r/Config/Version.hpp"
#include "slic3r/Config/Snapshot.hpp" #include "slic3r/Config/Snapshot.hpp"
@ -48,84 +43,49 @@ static const char *INDEX_FILENAME = "index.idx";
static const char *TMP_EXTENSION = ".download"; static const char *TMP_EXTENSION = ".download";
struct UpdateNotification : wxDialog
{
// If this dialog gets any more complex, it should probably be factored out...
enum {
CONTENT_WIDTH = 400,
BORDER = 30,
SPACING = 15,
};
wxCheckBox *cbox;
UpdateNotification(const Semver &ver_current, const Semver &ver_online) : wxDialog(nullptr, wxID_ANY, _(L("Update available")))
{
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
auto *sizer = new wxBoxSizer(wxVERTICAL);
const auto url = wxString::Format("https://github.com/prusa3d/Slic3r/releases/tag/version_%s", ver_online.to_string());
auto *link = new wxHyperlinkCtrl(this, wxID_ANY, url, url);
auto *text = new wxStaticText(this, wxID_ANY,
_(L("New version of Slic3r PE is available. To download, follow the link below.")));
const auto link_width = link->GetSize().GetWidth();
text->Wrap(CONTENT_WIDTH > link_width ? CONTENT_WIDTH : link_width);
sizer->Add(text);
sizer->AddSpacer(SPACING);
auto *versions = new wxFlexGridSizer(2, 0, SPACING);
versions->Add(new wxStaticText(this, wxID_ANY, _(L("Current version:"))));
versions->Add(new wxStaticText(this, wxID_ANY, ver_current.to_string()));
versions->Add(new wxStaticText(this, wxID_ANY, _(L("New version:"))));
versions->Add(new wxStaticText(this, wxID_ANY, ver_online.to_string()));
sizer->Add(versions);
sizer->AddSpacer(SPACING);
sizer->Add(link);
sizer->AddSpacer(2*SPACING);
cbox = new wxCheckBox(this, wxID_ANY, _(L("Don't notify about new releases any more")));
sizer->Add(cbox);
sizer->AddSpacer(SPACING);
auto *ok = new wxButton(this, wxID_OK);
ok->SetFocus();
sizer->Add(ok, 0, wxALIGN_CENTRE_HORIZONTAL);
auto *logo = new wxStaticBitmap(this, wxID_ANY, wxBitmap(GUI::from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG));
topsizer->Add(logo, 0, wxALL, BORDER);
topsizer->Add(sizer, 0, wxALL, BORDER);
SetSizerAndFit(topsizer);
}
bool disable_version_check() const { return cbox->GetValue(); }
};
struct Update struct Update
{ {
fs::path source; fs::path source;
fs::path target; fs::path target;
Version version; Version version;
Update(fs::path &&source, const fs::path &target, const Version &version) : Update(fs::path &&source, fs::path &&target, const Version &version) :
source(source), source(std::move(source)),
target(std::move(target)), target(std::move(target)),
version(version) version(version)
{} {}
Update(fs::path &&source, fs::path &&target) : std::string name() const { return source.stem().string(); }
source(source),
target(std::move(target))
{}
std::string name() { return source.stem().string(); } friend std::ostream& operator<<(std::ostream& os , const Update &self) {
os << "Update(" << self.source.string() << " -> " << self.target.string() << ')';
return os;
}
}; };
typedef std::vector<Update> Updates; struct Incompat
{
fs::path bundle;
Version version;
Incompat(fs::path &&bundle, const Version &version) :
bundle(std::move(bundle)),
version(version)
{}
std::string name() const { return bundle.stem().string(); }
friend std::ostream& operator<<(std::ostream& os , const Incompat &self) {
os << "Incompat(" << self.bundle.string() << ')';
return os;
}
};
struct Updates
{
std::vector<Incompat> incompats;
std::vector<Update> updates;
};
struct PresetUpdater::priv struct PresetUpdater::priv
@ -154,7 +114,7 @@ struct PresetUpdater::priv
void sync_config(const std::set<VendorProfile> vendors) const; void sync_config(const std::set<VendorProfile> vendors) const;
void check_install_indices() const; void check_install_indices() const;
Updates config_update() const; Updates get_config_updates() const;
void perform_updates(Updates &&updates, bool snapshot = true) const; void perform_updates(Updates &&updates, bool snapshot = true) const;
}; };
@ -171,25 +131,37 @@ PresetUpdater::priv::priv(int version_online_event) :
index_db = std::move(Index::load_db()); index_db = std::move(Index::load_db());
} }
// Pull relevant preferences from AppConfig
void PresetUpdater::priv::set_download_prefs(AppConfig *app_config) void PresetUpdater::priv::set_download_prefs(AppConfig *app_config)
{ {
enabled_version_check = app_config->get("version_check") == "1"; enabled_version_check = app_config->get("version_check") == "1";
version_check_url = app_config->get("version_check_url"); version_check_url = app_config->version_check_url();
enabled_config_update = app_config->get("preset_update") == "1"; enabled_config_update = app_config->get("preset_update") == "1";
} }
// Downloads a file (http get operation). Cancels if the Updater is being destroyed.
bool PresetUpdater::priv::get_file(const std::string &url, const fs::path &target_path) const bool PresetUpdater::priv::get_file(const std::string &url, const fs::path &target_path) const
{ {
std::cerr << "get_file(): " << url << " -> " << target_path << std::endl;
bool res = false; bool res = false;
fs::path tmp_path = target_path; fs::path tmp_path = target_path;
tmp_path += TMP_EXTENSION; tmp_path += (boost::format(".%1%%2%") % get_current_pid() % TMP_EXTENSION).str();
BOOST_LOG_TRIVIAL(info) << boost::format("Get: `%1%`\n\t-> `%2%`\n\tvia tmp path `%3%`")
% url
% target_path.string()
% tmp_path.string();
Http::get(url) Http::get(url)
.on_progress([this](Http::Progress, bool &cancel) { .on_progress([this](Http::Progress, bool &cancel) {
if (cancel) { cancel = true; } if (cancel) { cancel = true; }
}) })
.on_error([&](std::string body, std::string error, unsigned http_status) {
(void)body;
BOOST_LOG_TRIVIAL(error) << boost::format("Error getting: `%1%`: HTTP %2%, %3%")
% url
% http_status
% error;
})
.on_complete([&](std::string body, unsigned http_status) { .on_complete([&](std::string body, unsigned http_status) {
fs::fstream file(tmp_path, std::ios::out | std::ios::binary | std::ios::trunc); fs::fstream file(tmp_path, std::ios::out | std::ios::binary | std::ios::trunc);
file.write(body.c_str(), body.size()); file.write(body.c_str(), body.size());
@ -202,26 +174,39 @@ bool PresetUpdater::priv::get_file(const std::string &url, const fs::path &targe
return res; return res;
} }
// Remove leftover paritally downloaded files, if any.
void PresetUpdater::priv::prune_tmps() const void PresetUpdater::priv::prune_tmps() const
{ {
for (fs::directory_iterator it(cache_path); it != fs::directory_iterator(); ++it) { for (fs::directory_iterator it(cache_path); it != fs::directory_iterator(); ++it) {
if (it->path().extension() == TMP_EXTENSION) { if (it->path().extension() == TMP_EXTENSION) {
BOOST_LOG_TRIVIAL(debug) << "Cache prune: " << it->path().string();
fs::remove(it->path()); fs::remove(it->path());
} }
} }
} }
// Get Slic3rPE version available online, save in AppConfig.
void PresetUpdater::priv::sync_version() const void PresetUpdater::priv::sync_version() const
{ {
if (! enabled_version_check) { return; } if (! enabled_version_check) { return; }
BOOST_LOG_TRIVIAL(info) << boost::format("Downloading Slic3rPE online version from: `%1%`") % version_check_url;
Http::get(version_check_url) Http::get(version_check_url)
.size_limit(SLIC3R_VERSION_BODY_MAX) .size_limit(SLIC3R_VERSION_BODY_MAX)
.on_progress([this](Http::Progress, bool &cancel) { .on_progress([this](Http::Progress, bool &cancel) {
cancel = this->cancel; cancel = this->cancel;
}) })
.on_error([&](std::string body, std::string error, unsigned http_status) {
(void)body;
BOOST_LOG_TRIVIAL(error) << boost::format("Error getting: `%1%`: HTTP %2%, %3%")
% version_check_url
% http_status
% error;
})
.on_complete([&](std::string body, unsigned http_status) { .on_complete([&](std::string body, unsigned http_status) {
boost::trim(body); boost::trim(body);
BOOST_LOG_TRIVIAL(info) << boost::format("Got Slic3rPE online version: `%1%`. Sending to GUI thread...") % body;
wxCommandEvent* evt = new wxCommandEvent(version_online_event); wxCommandEvent* evt = new wxCommandEvent(version_online_event);
evt->SetString(body); evt->SetString(body);
GUI::get_app()->QueueEvent(evt); GUI::get_app()->QueueEvent(evt);
@ -229,9 +214,11 @@ void PresetUpdater::priv::sync_version() const
.perform_sync(); .perform_sync();
} }
// Download vendor indices. Also download new bundles if an index indicates there's a new one available.
// Both are saved in cache.
void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors) const void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors) const
{ {
std::cerr << "sync_config()" << std::endl; BOOST_LOG_TRIVIAL(info) << "Syncing configuration cache";
if (!enabled_config_update) { return; } if (!enabled_config_update) { return; }
@ -239,54 +226,65 @@ void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors) con
for (const auto &index : index_db) { for (const auto &index : index_db) {
if (cancel) { return; } if (cancel) { return; }
std::cerr << "Index: " << index.vendor() << std::endl;
const auto vendor_it = vendors.find(VendorProfile(index.vendor())); const auto vendor_it = vendors.find(VendorProfile(index.vendor()));
if (vendor_it == vendors.end()) { continue; } if (vendor_it == vendors.end()) {
BOOST_LOG_TRIVIAL(warning) << "No such vendor: " << index.vendor();
continue;
}
const VendorProfile &vendor = *vendor_it; const VendorProfile &vendor = *vendor_it;
if (vendor.config_update_url.empty()) { continue; } if (vendor.config_update_url.empty()) {
BOOST_LOG_TRIVIAL(info) << "Vendor has no config_update_url: " << vendor.name;
continue;
}
// Download a fresh index // Download a fresh index
BOOST_LOG_TRIVIAL(info) << "Downloading index for vendor: " << vendor.name;
const auto idx_url = vendor.config_update_url + "/" + INDEX_FILENAME; const auto idx_url = vendor.config_update_url + "/" + INDEX_FILENAME;
const auto idx_path = cache_path / (vendor.id + ".idx"); const auto idx_path = cache_path / (vendor.id + ".idx");
if (! get_file(idx_url, idx_path)) { continue; } if (! get_file(idx_url, idx_path)) { continue; }
if (cancel) { return; } if (cancel) { return; }
std::cerr << "Got a new index: " << idx_path << std::endl;
// Load the fresh index up // Load the fresh index up
Index new_index; Index new_index;
new_index.load(idx_path); new_index.load(idx_path);
// See if a there's a new version to download // See if a there's a new version to download
const auto recommended_it = new_index.recommended(); const auto recommended_it = new_index.recommended();
if (recommended_it == new_index.end()) { continue; } if (recommended_it == new_index.end()) {
BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % vendor.name;
continue;
}
const auto recommended = recommended_it->config_version; const auto recommended = recommended_it->config_version;
std::cerr << "Current vendor version: " << vendor.config_version.to_string() << std::endl; BOOST_LOG_TRIVIAL(debug) << boost::format("New index for vendor: %1%: current version: %2%, recommended version: %3%")
std::cerr << "Recommended version:\t" << recommended.to_string() << std::endl; % vendor.name
% vendor.config_version.to_string()
% recommended.to_string();
if (vendor.config_version >= recommended) { continue; } if (vendor.config_version >= recommended) { continue; }
// Download a fresh bundle // Download a fresh bundle
BOOST_LOG_TRIVIAL(info) << "Downloading new bundle for vendor: " << vendor.name;
const auto bundle_url = (boost::format("%1%/%2%.ini") % vendor.config_update_url % recommended.to_string()).str(); const auto bundle_url = (boost::format("%1%/%2%.ini") % vendor.config_update_url % recommended.to_string()).str();
const auto bundle_path = cache_path / (vendor.id + ".ini"); const auto bundle_path = cache_path / (vendor.id + ".ini");
if (! get_file(bundle_url, bundle_path)) { continue; } if (! get_file(bundle_url, bundle_path)) { continue; }
if (cancel) { return; } if (cancel) { return; }
std::cerr << "Got a new bundle: " << bundle_path << std::endl;
} }
} }
// Install indicies from resources. Only installs those that are either missing or older than in resources.
void PresetUpdater::priv::check_install_indices() const void PresetUpdater::priv::check_install_indices() const
{ {
BOOST_LOG_TRIVIAL(info) << "Checking if indices need to be installed from resources...";
for (fs::directory_iterator it(rsrc_path); it != fs::directory_iterator(); ++it) { for (fs::directory_iterator it(rsrc_path); it != fs::directory_iterator(); ++it) {
const auto &path = it->path(); const auto &path = it->path();
if (path.extension() == ".idx") { if (path.extension() == ".idx") {
const auto path_in_cache = cache_path / path.filename(); const auto path_in_cache = cache_path / path.filename();
if (! fs::exists(path_in_cache)) { if (! fs::exists(path_in_cache)) {
BOOST_LOG_TRIVIAL(info) << "Install index from resources: " << path.filename();
fs::copy_file(path, path_in_cache, fs::copy_option::overwrite_if_exists); fs::copy_file(path, path_in_cache, fs::copy_option::overwrite_if_exists);
} else { } else {
Index idx_rsrc, idx_cache; Index idx_rsrc, idx_cache;
@ -294,6 +292,7 @@ void PresetUpdater::priv::check_install_indices() const
idx_cache.load(path_in_cache); idx_cache.load(path_in_cache);
if (idx_cache.version() < idx_rsrc.version()) { if (idx_cache.version() < idx_rsrc.version()) {
BOOST_LOG_TRIVIAL(info) << "Update index from resources: " << path.filename();
fs::copy_file(path, path_in_cache, fs::copy_option::overwrite_if_exists); fs::copy_file(path, path_in_cache, fs::copy_option::overwrite_if_exists);
} }
} }
@ -301,14 +300,18 @@ void PresetUpdater::priv::check_install_indices() const
} }
} }
Updates PresetUpdater::priv::config_update() const // Generates a list of bundle updates that are to be performed
Updates PresetUpdater::priv::get_config_updates() const
{ {
Updates updates; Updates updates;
BOOST_LOG_TRIVIAL(info) << "Checking for cached configuration updates...";
for (const auto idx : index_db) { for (const auto idx : index_db) {
const auto bundle_path = vendor_path / (idx.vendor() + ".ini"); auto bundle_path = vendor_path / (idx.vendor() + ".ini");
if (! fs::exists(bundle_path)) { if (! fs::exists(bundle_path)) {
BOOST_LOG_TRIVIAL(info) << "Bundle not present for index, skipping: " << idx.vendor();
continue; continue;
} }
@ -317,30 +320,50 @@ Updates PresetUpdater::priv::config_update() const
const auto ver_current = idx.find(vp.config_version); const auto ver_current = idx.find(vp.config_version);
if (ver_current == idx.end()) { if (ver_current == idx.end()) {
BOOST_LOG_TRIVIAL(warning) << boost::format("Preset bundle (`%1%`) version not found in index: %2%") % idx.vendor() % vp.config_version.to_string(); BOOST_LOG_TRIVIAL(error) << boost::format("Preset bundle (`%1%`) version not found in index: %2%") % idx.vendor() % vp.config_version.to_string();
continue; continue;
} }
const auto recommended = idx.recommended(); const auto recommended = idx.recommended();
if (recommended == idx.end()) { if (recommended == idx.end()) {
throw std::runtime_error((boost::format("Invalid index: `%1%`") % idx.vendor()).str()); BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % idx.vendor();
} }
BOOST_LOG_TRIVIAL(debug) << boost::format("Vendor: %1%, version installed: %2%, version cached: %3%")
% vp.name
% ver_current->config_version.to_string()
% recommended->config_version.to_string();
if (! ver_current->is_current_slic3r_supported()) { if (! ver_current->is_current_slic3r_supported()) {
BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string();
// TODO: Downgrade situation updates.incompats.emplace_back(std::move(bundle_path), *ver_current);
} else if (recommended->config_version > ver_current->config_version) { } else if (recommended->config_version > ver_current->config_version) {
// Config bundle update situation // Config bundle update situation
// Check if the update is already present in a snapshot
const auto recommended_snap = SnapshotDB::singleton().snapshot_with_vendor_preset(vp.name, recommended->config_version);
if (recommended_snap != SnapshotDB::singleton().end()) {
BOOST_LOG_TRIVIAL(info) << boost::format("Bundle update %1% %2% already found in snapshot %3%, skipping...")
% vp.name
% recommended->config_version.to_string()
% recommended_snap->id;
continue;
}
auto path_in_cache = cache_path / (idx.vendor() + ".ini"); auto path_in_cache = cache_path / (idx.vendor() + ".ini");
if (! fs::exists(path_in_cache)) { if (! fs::exists(path_in_cache)) {
BOOST_LOG_TRIVIAL(warning) << "Index indicates update, but new bundle not found in cache: " << path_in_cache.string();
continue; continue;
} }
const auto cached_vp = VendorProfile::from_ini(path_in_cache, false); const auto cached_vp = VendorProfile::from_ini(path_in_cache, false);
if (cached_vp.config_version == recommended->config_version) { if (cached_vp.config_version == recommended->config_version) {
updates.emplace_back(std::move(path_in_cache), bundle_path, *recommended); updates.updates.emplace_back(std::move(path_in_cache), std::move(bundle_path), *recommended);
} else {
BOOST_LOG_TRIVIAL(warning) << boost::format("Vendor: %1%: Index indicates update (%2%) but cached bundle has a different version: %3%")
% idx.vendor()
% recommended->config_version.to_string()
% cached_vp.config_version.to_string();
} }
} }
} }
@ -350,23 +373,43 @@ Updates PresetUpdater::priv::config_update() const
void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) const void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) const
{ {
if (snapshot) { if (updates.incompats.size() > 0) {
SnapshotDB::singleton().take_snapshot(*GUI::get_app_config(), Snapshot::SNAPSHOT_UPGRADE); if (snapshot) {
BOOST_LOG_TRIVIAL(info) << "Taking a snapshot...";
SnapshotDB::singleton().take_snapshot(*GUI::get_app_config(), Snapshot::SNAPSHOT_DOWNGRADE);
}
BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% incompatible bundles") % updates.incompats.size();
for (const auto &incompat : updates.incompats) {
BOOST_LOG_TRIVIAL(info) << '\t' << incompat;
fs::remove(incompat.bundle);
}
} }
else if (updates.updates.size() > 0) {
if (snapshot) {
BOOST_LOG_TRIVIAL(info) << "Taking a snapshot...";
SnapshotDB::singleton().take_snapshot(*GUI::get_app_config(), Snapshot::SNAPSHOT_UPGRADE);
}
for (const auto &update : updates) { BOOST_LOG_TRIVIAL(info) << boost::format("Performing %1% updates") % updates.updates.size();
fs::copy_file(update.source, update.target, fs::copy_option::overwrite_if_exists);
PresetBundle bundle; for (const auto &update : updates.updates) {
bundle.load_configbundle(update.target.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM); BOOST_LOG_TRIVIAL(info) << '\t' << update;
auto preset_remover = [](const Preset &preset) { fs::copy_file(update.source, update.target, fs::copy_option::overwrite_if_exists);
fs::remove(preset.file);
};
for (const auto &preset : bundle.prints) { preset_remover(preset); } PresetBundle bundle;
for (const auto &preset : bundle.filaments) { preset_remover(preset); } bundle.load_configbundle(update.target.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM);
for (const auto &preset : bundle.printers) { preset_remover(preset); }
auto preset_remover = [](const Preset &preset) {
fs::remove(preset.file);
};
for (const auto &preset : bundle.prints) { preset_remover(preset); }
for (const auto &preset : bundle.filaments) { preset_remover(preset); }
for (const auto &preset : bundle.printers) { preset_remover(preset); }
}
} }
} }
@ -381,6 +424,8 @@ PresetUpdater::PresetUpdater(int version_online_event) :
PresetUpdater::~PresetUpdater() PresetUpdater::~PresetUpdater()
{ {
if (p && p->thread.joinable()) { if (p && p->thread.joinable()) {
// This will stop transfers being done by the thread, if any.
// Cancelling takes some time, but should complete soon enough.
p->cancel = true; p->cancel = true;
p->thread.join(); p->thread.join();
} }
@ -405,69 +450,111 @@ void PresetUpdater::sync(PresetBundle *preset_bundle)
void PresetUpdater::slic3r_update_notify() void PresetUpdater::slic3r_update_notify()
{ {
if (! p->enabled_version_check || p->had_config_update) { return; } if (! p->enabled_version_check) { return; }
// ^ We don't want to bother the user with updates multiple times, put off till next time.
if (p->had_config_update) {
BOOST_LOG_TRIVIAL(info) << "New Slic3r version available, but there was a configuration update, notification won't be displayed";
return;
}
auto* app_config = GUI::get_app_config(); auto* app_config = GUI::get_app_config();
const auto ver_slic3r = Semver::parse(SLIC3R_VERSION); const auto ver_slic3r = Semver::parse(SLIC3R_VERSION);
const auto ver_online = Semver::parse(app_config->get("version_online")); const auto ver_online_str = app_config->get("version_online");
const auto ver_online = Semver::parse(ver_online_str);
const auto ver_online_seen = Semver::parse(app_config->get("version_online_seen"));
if (! ver_slic3r) { if (! ver_slic3r) {
throw std::runtime_error("Could not parse Slic3r version string: " SLIC3R_VERSION); throw std::runtime_error("Could not parse Slic3r version string: " SLIC3R_VERSION);
} }
if (ver_online && *ver_online > *ver_slic3r) { if (ver_online) {
UpdateNotification notification(*ver_slic3r, *ver_online); // Only display the notification if the version available online is newer AND if we haven't seen it before
notification.ShowModal(); if (*ver_online > *ver_slic3r && (! ver_online_seen || *ver_online_seen < *ver_online)) {
if (notification.disable_version_check()) { GUI::MsgUpdateSlic3r notification(*ver_slic3r, *ver_online);
app_config->set("version_check", "0"); notification.ShowModal();
p->enabled_version_check = false; if (notification.disable_version_check()) {
app_config->set("version_check", "0");
p->enabled_version_check = false;
}
} }
app_config->set("version_online_seen", ver_online_str);
} }
} }
void PresetUpdater::config_update() const bool PresetUpdater::config_update() const
{ {
if (! p->enabled_config_update) { return; } if (! p->enabled_config_update) { return true; }
auto updates = p->config_update(); auto updates = p->get_config_updates();
if (updates.size() > 0) { if (updates.incompats.size() > 0) {
const auto msg = _(L("Configuration update is available. Would you like to install it?")); BOOST_LOG_TRIVIAL(info) << boost::format("%1% bundles incompatible. Asking for action...") % updates.incompats.size();
auto ext_msg = _(L( std::unordered_map<std::string, wxString> incompats_map;
"Note that a full configuration snapshot will be created first. It can then be restored at any time " for (const auto &incompat : updates.incompats) {
"should there be a problem with the new version.\n\n" auto vendor = incompat.name();
"Updated configuration bundles:\n" auto restrictions = wxString::Format(_(L("requires min. %s and max. %s")),
)); incompat.version.min_slic3r_version.to_string(),
incompat.version.max_slic3r_version.to_string()
for (const auto &update : updates) { );
ext_msg += update.target.stem().string() + " " + update.version.config_version.to_string(); incompats_map.emplace(std::make_pair(std::move(vendor), std::move(restrictions)));
if (! update.version.comment.empty()) {
ext_msg += std::string(" (") + update.version.comment + ")";
}
ext_msg += "\n";
} }
wxMessageDialog dlg(NULL, msg, _(L("Configuration update")), wxYES_NO|wxCENTRE); GUI::MsgDataIncompatible dlg(std::move(incompats_map));
dlg.SetExtendedMessage(ext_msg);
const auto res = dlg.ShowModal(); const auto res = dlg.ShowModal();
std::cerr << "After modal" << std::endl; if (res == wxID_REPLACE) {
if (res == wxID_YES) { BOOST_LOG_TRIVIAL(info) << "User wants to re-configure...";
// User gave clearance, updates are go
p->perform_updates(std::move(updates)); p->perform_updates(std::move(updates));
GUI::ConfigWizard wizard(nullptr, GUI::ConfigWizard::RR_DATA_INCOMPAT);
if (wizard.run(GUI::get_preset_bundle(), this)) {
p->had_config_update = true;
} else {
return false;
}
} else {
BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye...";
return false;
}
}
else if (updates.updates.size() > 0) {
BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. Asking for confirmation ...") % updates.updates.size();
std::unordered_map<std::string, std::string> updates_map;
for (const auto &update : updates.updates) {
auto vendor = update.name();
auto ver_str = update.version.config_version.to_string();
if (! update.version.comment.empty()) {
ver_str += std::string(" (") + update.version.comment + ")";
}
updates_map.emplace(std::make_pair(std::move(vendor), std::move(ver_str)));
}
GUI::MsgUpdateConfig dlg(std::move(updates_map));
const auto res = dlg.ShowModal();
if (res == wxID_OK) {
BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
p->perform_updates(std::move(updates));
} else {
BOOST_LOG_TRIVIAL(info) << "User refused the update";
} }
p->had_config_update = true; p->had_config_update = true;
} else {
BOOST_LOG_TRIVIAL(info) << "No configuration updates available.";
} }
return true;
} }
void PresetUpdater::install_bundles_rsrc(std::vector<std::string> &&bundles, bool snapshot) void PresetUpdater::install_bundles_rsrc(std::vector<std::string> bundles, bool snapshot) const
{ {
Updates updates; Updates updates;
BOOST_LOG_TRIVIAL(info) << boost::format("Installing %1% bundles from resources ...") % bundles.size();
for (const auto &bundle : bundles) { for (const auto &bundle : bundles) {
auto path_in_rsrc = p->rsrc_path / bundle; auto path_in_rsrc = p->rsrc_path / bundle;
auto path_in_vendors = p->vendor_path / bundle; auto path_in_vendors = p->vendor_path / bundle;
updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors)); updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version());
} }
p->perform_updates(std::move(updates), snapshot); p->perform_updates(std::move(updates), snapshot);

View File

@ -20,10 +20,18 @@ public:
PresetUpdater &operator=(const PresetUpdater &) = delete; PresetUpdater &operator=(const PresetUpdater &) = delete;
~PresetUpdater(); ~PresetUpdater();
// If either version check or config updating is enabled, get the appropriate data in the background and cache it.
void sync(PresetBundle *preset_bundle); void sync(PresetBundle *preset_bundle);
// If version check is enabled, check if chaced online slic3r version is newer, notify if so.
void slic3r_update_notify(); void slic3r_update_notify();
void config_update() const;
void install_bundles_rsrc(std::vector<std::string> &&bundles, bool snapshot = true); // If updating is enabled, check if updates are available in cache, if so, ask about installation.
// A false return value implies Slic3r should exit due to incompatibility of configuration.
bool config_update() const;
// "Update" a list of bundles from resources (behaves like an online update).
void install_bundles_rsrc(std::vector<std::string> bundles, bool snapshot = true) const;
private: private:
struct priv; struct priv;
std::unique_ptr<priv> p; std::unique_ptr<priv> p;

View File

@ -4,6 +4,7 @@
#include <string> #include <string>
#include <cstring> #include <cstring>
#include <ostream> #include <ostream>
#include <stdexcept>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
@ -22,14 +23,25 @@ public:
Semver() : ver(semver_zero()) {} Semver() : ver(semver_zero()) {}
Semver(int major, int minor, int patch, Semver(int major, int minor, int patch,
boost::optional<std::string> metadata = boost::none, boost::optional<const std::string&> metadata = boost::none,
boost::optional<std::string> prerelease = boost::none) boost::optional<const std::string&> prerelease = boost::none)
: ver(semver_zero())
{ {
ver.major = major; ver.major = major;
ver.minor = minor; ver.minor = minor;
ver.patch = patch; ver.patch = patch;
ver.metadata = metadata ? std::strcpy(ver.metadata, metadata->c_str()) : nullptr; set_metadata(metadata);
ver.prerelease = prerelease ? std::strcpy(ver.prerelease, prerelease->c_str()) : nullptr; set_prerelease(prerelease);
}
Semver(const std::string &str) : ver(semver_zero())
{
auto parsed = parse(str);
if (! parsed) {
throw std::runtime_error(std::string("Could not parse version string: ") + str);
}
ver = parsed->ver;
parsed->ver = semver_zero();
} }
static boost::optional<Semver> parse(const std::string &str) static boost::optional<Semver> parse(const std::string &str)
@ -82,6 +94,13 @@ public:
int patch() const { return ver.patch; } int patch() const { return ver.patch; }
const char* prerelease() const { return ver.prerelease; } const char* prerelease() const { return ver.prerelease; }
const char* metadata() const { return ver.metadata; } const char* metadata() const { return ver.metadata; }
// Setters
void set_maj(int maj) { ver.major = maj; }
void set_min(int min) { ver.minor = min; }
void set_patch(int patch) { ver.patch = patch; }
void set_metadata(boost::optional<const std::string&> meta) { ver.metadata = meta ? strdup(*meta) : nullptr; }
void set_prerelease(boost::optional<const std::string&> pre) { ver.prerelease = pre ? strdup(*pre) : nullptr; }
// Comparison // Comparison
bool operator<(const Semver &b) const { return ::semver_compare(ver, b.ver) == -1; } bool operator<(const Semver &b) const { return ::semver_compare(ver, b.ver) == -1; }
@ -124,6 +143,7 @@ private:
Semver(semver_t ver) : ver(ver) {} Semver(semver_t ver) : ver(ver) {}
static semver_t semver_zero() { return { 0, 0, 0, nullptr, nullptr }; } static semver_t semver_zero() { return { 0, 0, 0, nullptr, nullptr }; }
static char * strdup(const std::string &str) { return ::semver_strdup(const_cast<char*>(str.c_str())); }
}; };

View File

@ -42,6 +42,9 @@ void add_config_menu(SV *ui, int event_preferences_changed, int event_language_c
void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed) void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed)
%code%{ Slic3r::GUI::create_preset_tabs(no_controller, event_value_change, event_presets_changed); %}; %code%{ Slic3r::GUI::create_preset_tabs(no_controller, event_value_change, event_presets_changed); %};
void show_error_id(int id, std::string msg)
%code%{ Slic3r::GUI::show_error_id(id, msg); %};
TabIface* get_preset_tab(char *name) TabIface* get_preset_tab(char *name)
%code%{ RETVAL=Slic3r::GUI::get_preset_tab_iface(name); %}; %code%{ RETVAL=Slic3r::GUI::get_preset_tab_iface(name); %};
@ -60,10 +63,10 @@ void set_app_config(AppConfig *app_config)
bool check_unsaved_changes() bool check_unsaved_changes()
%code%{ RETVAL=Slic3r::GUI::check_unsaved_changes(); %}; %code%{ RETVAL=Slic3r::GUI::check_unsaved_changes(); %};
void config_wizard_startup(int app_config_exists) bool config_wizard_startup(int app_config_exists)
%code%{ %code%{
try { try {
Slic3r::GUI::config_wizard_startup(app_config_exists != 0); RETVAL=Slic3r::GUI::config_wizard_startup(app_config_exists != 0);
} catch (std::exception& e) { } catch (std::exception& e) {
croak("%s\n", e.what()); croak("%s\n", e.what());
} }

View File

@ -16,6 +16,7 @@
bool current_preset_is_dirty(); bool current_preset_is_dirty();
void load_key_value(char* opt_key, char* value); void load_key_value(char* opt_key, char* value);
void OnActivate(); void OnActivate();
size_t get_selected_preset_item();
std::string title(); std::string title();
Ref<DynamicPrintConfig> get_config(); Ref<DynamicPrintConfig> get_config();
Ref<PresetCollection> get_presets(); Ref<PresetCollection> get_presets();

View File

@ -9,5 +9,5 @@
PresetUpdater(int version_online_event); PresetUpdater(int version_online_event);
void sync(PresetBundle* preset_bundle); void sync(PresetBundle* preset_bundle);
void slic3r_update_notify(); void slic3r_update_notify();
void config_update(); bool config_update();
}; };