From 21d913fb4519352c577e3f1bc60222272c4e62a4 Mon Sep 17 00:00:00 2001 From: Oekn5w <38046255+Oekn5w@users.noreply.github.com> Date: Wed, 11 Apr 2018 20:05:30 +0200 Subject: [PATCH 01/10] C++ backend work to support reloading modifier files --- xs/src/libslic3r/Model.cpp | 4 ++++ xs/src/libslic3r/Model.hpp | 9 +++++++-- xs/xsp/Model.xsp | 15 ++++++++++++++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index aa29858afe..d724098ab4 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -959,6 +959,10 @@ ModelVolume::swap(ModelVolume &other) std::swap(this->mesh, other.mesh); std::swap(this->config, other.config); std::swap(this->modifier, other.modifier); + + std::swap(this->input_file, other.input_file); + std::swap(this->obj_idx, other.obj_idx); + std::swap(this->vol_idx, other.vol_idx); } t_model_material_id diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index db6299ee46..45498f60ff 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -457,9 +457,14 @@ class ModelVolume DynamicPrintConfig config; ///< Configuration parameters specific to an object model geometry or a modifier volume, ///< overriding the global Slic3r settings and the ModelObject settings. - + + /// Input file path needed for reloading the volume from disk + std::string input_file; ///< Input file path + int obj_idx; ///< Input file object index + int vol_idx; ///< Input file volume index + bool modifier; ///< Is it an object to be printed, or a modifier volume? - + /// Get the parent object owning this modifier volume. /// \return ModelObject* pointer to the owner ModelObject ModelObject* get_object() const { return this->object; }; diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 6772aee075..2ef532618c 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -258,6 +258,20 @@ ModelMaterial::attributes() %code%{ RETVAL = THIS->name; %}; void set_name(std::string value) %code%{ THIS->name = value; %}; + + std::string input_file() + %code%{ RETVAL = THIS->input_file; %}; + void set_input_file(std::string value) + %code%{ THIS->input_file = value; %}; + int obj_idx() + %code%{ RETVAL = THIS->obj_idx; %}; + void set_obj_idx(int obj_idx) + %code%{ THIS->obj_idx = obj_idx; %}; + int vol_idx() + %code%{ RETVAL = THIS->vol_idx; %}; + void set_vol_idx(int vol_idx) + %code%{ THIS->vol_idx = vol_idx; %}; + t_model_material_id material_id(); void set_material_id(t_model_material_id material_id) %code%{ THIS->material_id(material_id); %}; @@ -294,7 +308,6 @@ ModelMaterial::attributes() %}; }; - %name{Slic3r::Model::Instance} class ModelInstance { Ref object() %code%{ RETVAL = THIS->get_object(); %}; From 27e31b8909d86bb826887868a5f4b9ba0e8e2240 Mon Sep 17 00:00:00 2001 From: Oekn5w <38046255+Oekn5w@users.noreply.github.com> Date: Wed, 11 Apr 2018 21:14:21 +0200 Subject: [PATCH 02/10] UI update preserving configs and volumes of modifiers (those are not reloaded) --- lib/Slic3r/GUI/Plater.pm | 11 ++++++++++- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index ef364b08c1..1640e3e34f 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -2319,10 +2319,19 @@ sub reload_from_disk { $o->clear_instances; $o->add_instance($_) for @{$model_object->instances}; - if ($o->volumes_count == $model_object->volumes_count) { + if (($o->volumes_count) <= ($model_object->volumes_count)) { for my $i (0..($o->volumes_count-1)) { $o->get_volume($i)->config->apply($model_object->get_volume($i)->config); } + if ($o->volumes_count != $model_object->volumes_count) { + for my $i ($o->volumes_count..($model_object->volumes_count-1)) { + # add_volume merges material and config attributes + my $new_volume = $o->add_volume($model_object->get_volume($i)); + $new_volume->set_name($model_object->get_volume($i)->name); + $new_volume->set_input_file($model_object->get_volume($i)->input_file); + $new_volume->set_modifier($model_object->get_volume($i)->modifier); + } + } } } diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index bfd858f5c5..0402bbe662 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -358,7 +358,8 @@ sub on_btn_load { my $new_volume = $self->{model_object}->add_volume($volume); $new_volume->set_modifier($is_modifier); $new_volume->set_name(basename($input_file)); - + # input_file needed to reload / update modifiers' volumes + $new_volume->set_input_file($input_file); # apply the same translation we applied to the object $new_volume->mesh->translate(@{$self->{model_object}->origin_translation}); From e964e54f27bfd1334df50524ddbc8dc3de0eda62 Mon Sep 17 00:00:00 2001 From: Oekn5w <38046255+Oekn5w@users.noreply.github.com> Date: Wed, 11 Apr 2018 22:27:40 +0200 Subject: [PATCH 03/10] clarifying variable names --- xs/src/libslic3r/Model.cpp | 6 +++--- xs/src/libslic3r/Model.hpp | 4 ++-- xs/xsp/Model.xsp | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index d724098ab4..40e3d69b14 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -960,9 +960,9 @@ ModelVolume::swap(ModelVolume &other) std::swap(this->config, other.config); std::swap(this->modifier, other.modifier); - std::swap(this->input_file, other.input_file); - std::swap(this->obj_idx, other.obj_idx); - std::swap(this->vol_idx, other.vol_idx); + std::swap(this->input_file, other.input_file); + std::swap(this->input_file_obj_idx, other.input_file_obj_idx); + std::swap(this->input_file_vol_idx, other.input_file_vol_idx); } t_model_material_id diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 45498f60ff..e0b2a5000a 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -460,8 +460,8 @@ class ModelVolume /// Input file path needed for reloading the volume from disk std::string input_file; ///< Input file path - int obj_idx; ///< Input file object index - int vol_idx; ///< Input file volume index + int input_file_obj_idx; ///< Input file object index + int input_file_vol_idx; ///< Input file volume index bool modifier; ///< Is it an object to be printed, or a modifier volume? diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 2ef532618c..8bf2b05111 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -263,14 +263,14 @@ ModelMaterial::attributes() %code%{ RETVAL = THIS->input_file; %}; void set_input_file(std::string value) %code%{ THIS->input_file = value; %}; - int obj_idx() - %code%{ RETVAL = THIS->obj_idx; %}; - void set_obj_idx(int obj_idx) - %code%{ THIS->obj_idx = obj_idx; %}; - int vol_idx() - %code%{ RETVAL = THIS->vol_idx; %}; - void set_vol_idx(int vol_idx) - %code%{ THIS->vol_idx = vol_idx; %}; + int input_file_obj_idx() + %code%{ RETVAL = THIS->input_file_obj_idx; %}; + void set_input_file_obj_idx(int obj_idx) + %code%{ THIS->input_file_obj_idx = obj_idx; %}; + int input_file_vol_idx() + %code%{ RETVAL = THIS->input_file_vol_idx; %}; + void set_input_file_vol_idx(int vol_idx) + %code%{ THIS->input_file_vol_idx = vol_idx; %}; t_model_material_id material_id(); void set_material_id(t_model_material_id material_id) From 994ee6920da4e4844c0af0eb0989dd52b070a8bd Mon Sep 17 00:00:00 2001 From: Oekn5w <38046255+Oekn5w@users.noreply.github.com> Date: Sat, 14 Apr 2018 03:51:27 +0200 Subject: [PATCH 04/10] Setting up variables in the GUI enviroment --- lib/Slic3r/GUI/Plater.pm | 10 +++++++++- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 11 ++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 1640e3e34f..90a8246149 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1213,7 +1213,15 @@ sub load_file { my $i = 0; foreach my $obj_idx (@obj_idx) { $self->{objects}[$obj_idx]->input_file($input_file); - $self->{objects}[$obj_idx]->input_file_obj_idx($i++); + $self->{objects}[$obj_idx]->input_file_obj_idx($i); + + # additional information for reloading + for my $vol_idx (0..($self->{objects}[$obj_idx]->volumes_count-1)) { + $self->{objects}[$obj_idx]->get_volume($vol_idx)->set_input_file($input_file); + $self->{objects}[$obj_idx]->get_volume($vol_idx)->set_obj_idx($i); + $self->{objects}[$obj_idx]->get_volume($vol_idx)->set_vol_idx($vol_idx); + } + $i++; } $self->statusbar->SetStatusText("Loaded " . basename($input_file)); diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 0402bbe662..35c1af28be 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -353,13 +353,18 @@ sub on_btn_load { next; } - foreach my $object (@{$model->objects}) { - foreach my $volume (@{$object->volumes}) { - my $new_volume = $self->{model_object}->add_volume($volume); + for my $obj_idx (0..($model->objects_count-1)) { + my $object = $model->objects->[$obj_idx]; + for my $vol_idx (0..($object->volumes_count-1)) { + my $new_volume = $self->{model_object}->add_volume($object->get_volume($vol_idx)); $new_volume->set_modifier($is_modifier); $new_volume->set_name(basename($input_file)); + # input_file needed to reload / update modifiers' volumes $new_volume->set_input_file($input_file); + $new_volume->set_input_file_obj_idx($obj_idx); + $new_volume->set_input_file_vol_idx($vol_idx); + # apply the same translation we applied to the object $new_volume->mesh->translate(@{$self->{model_object}->origin_translation}); From 0d257a5cd453c8674f4d5020fecc6b27cbc75f76 Mon Sep 17 00:00:00 2001 From: Oekn5w <38046255+Oekn5w@users.noreply.github.com> Date: Sun, 15 Apr 2018 19:08:49 +0200 Subject: [PATCH 05/10] Implementation of added variables in (new ModelVolume(*)) funcion --- xs/src/libslic3r/Model.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 40e3d69b14..2d809db654 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -936,12 +936,18 @@ ModelObject::print_info() const ModelVolume::ModelVolume(ModelObject* object, const TriangleMesh &mesh) -: mesh(mesh), modifier(false), object(object) +: mesh(mesh), modifier(false), input_file(""), object(object) {} ModelVolume::ModelVolume(ModelObject* object, const ModelVolume &other) -: name(other.name), mesh(other.mesh), config(other.config), - modifier(other.modifier), object(object) +: name(other.name), + mesh(other.mesh), + config(other.config), + modifier(other.modifier), + input_file(other.input_file), + input_file_obj_idx(other.input_file_obj_idx), + input_file_vol_idx(other.input_file_vol_idx), + object(object) { this->material_id(other.material_id()); } From 8a6d5c99f3af070eca9be6690591e46736ad65b4 Mon Sep 17 00:00:00 2001 From: Oekn5w <38046255+Oekn5w@users.noreply.github.com> Date: Mon, 16 Apr 2018 17:07:53 +0200 Subject: [PATCH 06/10] Implementation of new reload function --- lib/Slic3r/GUI/Plater.pm | 98 +++++++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 26 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 90a8246149..ff951669c4 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1177,7 +1177,7 @@ sub add_tin { sub load_file { my $self = shift; - my ($input_file, $obj_idx) = @_; + my ($input_file, $obj_idx_to_load) = @_; $Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_file); wxTheApp->save_settings; @@ -1203,24 +1203,31 @@ sub load_file { } } - if (defined $obj_idx) { - return () if $obj_idx >= $model->objects_count; - @obj_idx = $self->load_model_objects($model->get_object($obj_idx)); + for my $obj_idx (0..($model->objects_count-1)) { + my $object = $model->objects->[$obj_idx]; + $object->set_input_file($input_file); + for my $vol_idx (0..($object->volumes_count-1)) { + my $volume = $object->get_volume($vol_idx); + $volume->set_input_file($input_file); + $volume->set_input_file_obj_idx($obj_idx); + $volume->set_input_file_obj_idx($vol_idx); + } + } + + my $i = 0; + + if (defined $obj_idx_to_load) { + return () if $obj_idx_to_load >= $model->objects_count; + @obj_idx = $self->load_model_objects($model->get_object($obj_idx_to_load)); + $i = $obj_idx_to_load; } else { @obj_idx = $self->load_model_objects(@{$model->objects}); } - my $i = 0; foreach my $obj_idx (@obj_idx) { + my $object = $self->{objects}[$obj_idx]; $self->{objects}[$obj_idx]->input_file($input_file); $self->{objects}[$obj_idx]->input_file_obj_idx($i); - - # additional information for reloading - for my $vol_idx (0..($self->{objects}[$obj_idx]->volumes_count-1)) { - $self->{objects}[$obj_idx]->get_volume($vol_idx)->set_input_file($input_file); - $self->{objects}[$obj_idx]->get_volume($vol_idx)->set_obj_idx($i); - $self->{objects}[$obj_idx]->get_volume($vol_idx)->set_vol_idx($vol_idx); - } $i++; } @@ -2321,25 +2328,59 @@ sub reload_from_disk { my @new_obj_idx = $self->load_file($object->input_file, $object->input_file_obj_idx); return if !@new_obj_idx; - my $model_object = $self->{model}->objects->[$obj_idx]; + my $original_object = $self->{model}->objects->[$obj_idx]; foreach my $new_obj_idx (@new_obj_idx) { my $o = $self->{model}->objects->[$new_obj_idx]; $o->clear_instances; - $o->add_instance($_) for @{$model_object->instances}; + $o->add_instance($_) for @{$original_object->instances}; + + my $vol_idx = 0; + my $new_obj_vol_count=$o->volumes_count; - if (($o->volumes_count) <= ($model_object->volumes_count)) { - for my $i (0..($o->volumes_count-1)) { - $o->get_volume($i)->config->apply($model_object->get_volume($i)->config); + for my $i (0..($original_object->volumes_count-1)) { + if ($vol_idx <= $new_obj_vol_count-1) { + # apply config from the originally read volumes + if($original_object->get_volume($i)->input_file eq $object->input_file) { + $o->get_volume($vol_idx)->config->apply($original_object->get_volume($i)->config); + }else{ + while ($vol_idx <= $new_obj_vol_count-1) { + $o->get_volume($vol_idx++)->config->apply($original_object->get_volume(0)->config); + } + next; #skip increment of $vol_idx, it already points the the next volume + } + }else{ + # reload volumes of additional parts and modifiers + if (length ($original_object->get_volume($i)->input_file) > 0) { + my $model = eval { Slic3r::Model->read_from_file($original_object->get_volume($i)->input_file) }; + + if ($@) { + $original_object->get_volume($i)->set_input_file(""); + }elsif ($original_object->get_volume($i)->input_file_obj_idx > ($model->objects_count-1)) { + # Object Index for that part / modifier not found in current version of the file + $original_object->get_volume($i)->set_input_file(""); + }else { + my $model_object = $model->objects->[$original_object->get_volume($i)->input_file_obj_idx]; + if ($original_object->get_volume($i)->input_file_vol_idx > ($model_object->volumes_count-1)) { + # Volume Index for that part / modifier not found in current version of the file + $original_object->get_volume($i)->set_input_file(""); + }else{ + my $new_volume = $o->add_volume($model_object->get_volume($original_object->get_volume($i)->input_file_vol_idx)); + $new_volume->set_name($original_object->get_volume($i)->name); + $new_volume->set_input_file($original_object->get_volume($i)->input_file); + $new_volume->set_input_file_obj_idx($original_object->get_volume($i)->input_file_obj_idx); + $new_volume->set_input_file_vol_idx($original_object->get_volume($i)->input_file_vol_idx); + $new_volume->config->apply($original_object->get_volume($i)->config); + $new_volume->set_modifier($original_object->get_volume($i)->modifier); + $new_volume->mesh->translate(@{$original_object->origin_translation}); + } + } + } + if (length ($original_object->get_volume($i)->input_file) == 0) { + my $new_volume = $o->add_volume($original_object->get_volume($i)); # error -> copy old volume + $self->statusbar->SetStatusText('Not all additional parts and modifiers could be found in the current files. Old meshes were kept for the affected parts.'); + } } - if ($o->volumes_count != $model_object->volumes_count) { - for my $i ($o->volumes_count..($model_object->volumes_count-1)) { - # add_volume merges material and config attributes - my $new_volume = $o->add_volume($model_object->get_volume($i)); - $new_volume->set_name($model_object->get_volume($i)->name); - $new_volume->set_input_file($model_object->get_volume($i)->input_file); - $new_volume->set_modifier($model_object->get_volume($i)->modifier); - } - } + $vol_idx++; } } @@ -2353,6 +2394,11 @@ sub reload_from_disk { # When porting to C++ we'll probably have cleaner ways to do this. $self->make_thumbnail($_-1) for @new_obj_idx; + # update print + $self->stop_background_process; + $self->{print}->reload_object($_-1) for @new_obj_idx; + $self->on_model_change; + # Empty the redo stack $self->{redo_stack} = []; } From 9878cb68eec0897949db78e9f0c91772d7754264 Mon Sep 17 00:00:00 2001 From: Oekn5w <38046255+Oekn5w@users.noreply.github.com> Date: Thu, 19 Apr 2018 21:50:11 +0200 Subject: [PATCH 07/10] Overhaul of the reload function, also renaming of some variables --- lib/Slic3r/GUI/Plater.pm | 120 ++++++++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 39 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index ff951669c4..a6755fd613 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1225,10 +1225,8 @@ sub load_file { } foreach my $obj_idx (@obj_idx) { - my $object = $self->{objects}[$obj_idx]; $self->{objects}[$obj_idx]->input_file($input_file); - $self->{objects}[$obj_idx]->input_file_obj_idx($i); - $i++; + $self->{objects}[$obj_idx]->input_file_obj_idx($i++); } $self->statusbar->SetStatusText("Loaded " . basename($input_file)); @@ -2321,69 +2319,109 @@ sub reload_from_disk { my ($obj_idx, $object) = $self->selected_object; return if !defined $obj_idx; - return if !$object->input_file - || !-e $object->input_file; + if (!$object->input_file) { + Slic3r::GUI::warning_catcher($self)->("The selected object couldn't be reloaded because it isn't referenced to its input file any more. This is the case after performing operations like cut or split."); + return; + } + if (!-e $object->input_file) { + Slic3r::GUI::warning_catcher($self)->("The selected object couldn't be reloaded because the file doesn't exist anymore on the disk."); + return; + } # Only reload the selected object and not all objects from the input file. my @new_obj_idx = $self->load_file($object->input_file, $object->input_file_obj_idx); - return if !@new_obj_idx; - - my $original_object = $self->{model}->objects->[$obj_idx]; + if (!@new_obj_idx) { + Slic3r::GUI::warning_catcher($self)->("The selected object couldn't be reloaded because the new file doesn't contain the object."); + return; + } + + my $org_obj = $self->{model}->objects->[$obj_idx]; + + # check if the object is dependant of more than one file + my $org_obj_has_modifiers=0; + for my $i (0..($org_obj->volumes_count-1)) { + if ($org_obj->input_file ne $org_obj->get_volume($i)->input_file) { + $org_obj_has_modifiers=1; + last; + } + } + + # ask the user how to proceed + my $res = wxID_YES; + if ($org_obj_has_modifiers) { + $res = Wx::MessageDialog->new($self, "Additional parts and modifiers are loaded in the current model. \n\nDo you want to also reload these volumes?\nSelecting no will discard the extra volumes and their configuration.", 'Reload additional parts and modifiers', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION)->ShowModal; + if ($res==wxID_CANCEL) { + $self->remove($_) for @new_obj_idx; + return; + } + } + + my $volume_unmatched=0; + foreach my $new_obj_idx (@new_obj_idx) { - my $o = $self->{model}->objects->[$new_obj_idx]; - $o->clear_instances; - $o->add_instance($_) for @{$original_object->instances}; + my $new_obj = $self->{model}->objects->[$new_obj_idx]; + $new_obj->clear_instances; + $new_obj->add_instance($_) for @{$org_obj->instances}; + $new_obj->config->apply($org_obj->config); my $vol_idx = 0; - my $new_obj_vol_count=$o->volumes_count; + my $new_obj_vol_count=$new_obj->volumes_count; - for my $i (0..($original_object->volumes_count-1)) { + for my $i (0..($org_obj->volumes_count-1)) { if ($vol_idx <= $new_obj_vol_count-1) { # apply config from the originally read volumes - if($original_object->get_volume($i)->input_file eq $object->input_file) { - $o->get_volume($vol_idx)->config->apply($original_object->get_volume($i)->config); - }else{ + if($org_obj->get_volume($i)->input_file eq $new_obj->input_file) { + $new_obj->get_volume($vol_idx++)->config->apply($org_obj->get_volume($i)->config); + } + if (($org_obj->get_volume($i)->input_file ne $new_obj->input_file) || ($i==$org_obj->volumes_count-1)) { + # basically else, also accounting for original having less volumes than reloaded (i.e. loop not parsed another time) while ($vol_idx <= $new_obj_vol_count-1) { - $o->get_volume($vol_idx++)->config->apply($original_object->get_volume(0)->config); + $new_obj->get_volume($vol_idx++)->config->apply($org_obj->get_volume(0)->config); + $volume_unmatched=1; } next; #skip increment of $vol_idx, it already points the the next volume } }else{ + last if $res==wxID_NO; # discard all other configurations according to the MessageDialog + if ($org_obj->input_file eq $org_obj->get_volume($i)->input_file) { + # original has more volumes than reloaded + $volume_unmatched=1; + next; + } # reload volumes of additional parts and modifiers - if (length ($original_object->get_volume($i)->input_file) > 0) { - my $model = eval { Slic3r::Model->read_from_file($original_object->get_volume($i)->input_file) }; - + if ($org_obj->get_volume($i)->input_file) { + my $model = eval { Slic3r::Model->read_from_file($org_obj->get_volume($i)->input_file) }; if ($@) { - $original_object->get_volume($i)->set_input_file(""); - }elsif ($original_object->get_volume($i)->input_file_obj_idx > ($model->objects_count-1)) { + $org_obj->get_volume($i)->set_input_file(""); + }elsif ($org_obj->get_volume($i)->input_file_obj_idx > ($model->objects_count-1)) { # Object Index for that part / modifier not found in current version of the file - $original_object->get_volume($i)->set_input_file(""); + $org_obj->get_volume($i)->set_input_file(""); }else { - my $model_object = $model->objects->[$original_object->get_volume($i)->input_file_obj_idx]; - if ($original_object->get_volume($i)->input_file_vol_idx > ($model_object->volumes_count-1)) { + my $prt_mod_obj = $model->objects->[$org_obj->get_volume($i)->input_file_obj_idx]; + if ($org_obj->get_volume($i)->input_file_vol_idx > ($prt_mod_obj->volumes_count-1)) { # Volume Index for that part / modifier not found in current version of the file - $original_object->get_volume($i)->set_input_file(""); + $org_obj->get_volume($i)->set_input_file(""); }else{ - my $new_volume = $o->add_volume($model_object->get_volume($original_object->get_volume($i)->input_file_vol_idx)); - $new_volume->set_name($original_object->get_volume($i)->name); - $new_volume->set_input_file($original_object->get_volume($i)->input_file); - $new_volume->set_input_file_obj_idx($original_object->get_volume($i)->input_file_obj_idx); - $new_volume->set_input_file_vol_idx($original_object->get_volume($i)->input_file_vol_idx); - $new_volume->config->apply($original_object->get_volume($i)->config); - $new_volume->set_modifier($original_object->get_volume($i)->modifier); - $new_volume->mesh->translate(@{$original_object->origin_translation}); + my $new_volume = $new_obj->add_volume($prt_mod_obj->get_volume($org_obj->get_volume($i)->input_file_vol_idx)); + $new_volume->set_name($org_obj->get_volume($i)->name); + $new_volume->set_input_file($org_obj->get_volume($i)->input_file); + $new_volume->set_input_file_obj_idx($org_obj->get_volume($i)->input_file_obj_idx); + $new_volume->set_input_file_vol_idx($org_obj->get_volume($i)->input_file_vol_idx); + $new_volume->config->apply($org_obj->get_volume($i)->config); + $new_volume->set_modifier($org_obj->get_volume($i)->modifier); + $new_volume->mesh->translate(@{$org_obj->origin_translation}); } } } - if (length ($original_object->get_volume($i)->input_file) == 0) { - my $new_volume = $o->add_volume($original_object->get_volume($i)); # error -> copy old volume - $self->statusbar->SetStatusText('Not all additional parts and modifiers could be found in the current files. Old meshes were kept for the affected parts.'); + if (!$org_obj->get_volume($i)->input_file) { + my $new_volume = $new_obj->add_volume($org_obj->get_volume($i)); # error -> copy old volume + $new_volume->set_name($new_volume->name . "-copied"); + $volume_unmatched=1; } } - $vol_idx++; } } - + $self->remove($obj_idx); # TODO: refresh object list which contains wrong count and scale @@ -2401,6 +2439,10 @@ sub reload_from_disk { # Empty the redo stack $self->{redo_stack} = []; + + if ($volume_unmatched) { + Slic3r::GUI::warning_catcher($self)->("At least 1 volume couldn't be matched between the original object and the reloaded one."); + } } sub export_object_stl { From 27f277fbedfcc417c201704505bd33d0361f0da9 Mon Sep 17 00:00:00 2001 From: Oekn5w <38046255+Oekn5w@users.noreply.github.com> Date: Fri, 20 Apr 2018 00:18:02 +0200 Subject: [PATCH 08/10] Rewriting the main loop of the reload function, explicitly differentiating between the original file and later added parts and modifiers pointing to other files --- lib/Slic3r/GUI/Plater.pm | 101 +++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 52 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index a6755fd613..33bcfdcfb1 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -2364,64 +2364,61 @@ sub reload_from_disk { $new_obj->add_instance($_) for @{$org_obj->instances}; $new_obj->config->apply($org_obj->config); - my $vol_idx = 0; - my $new_obj_vol_count=$new_obj->volumes_count; + my $new_vol_idx = 0; + my $org_vol_idx = 0; + my $new_vol_count=$new_obj->volumes_count; + my $org_vol_count=$org_obj->volumes_count; - for my $i (0..($org_obj->volumes_count-1)) { - if ($vol_idx <= $new_obj_vol_count-1) { - # apply config from the originally read volumes - if($org_obj->get_volume($i)->input_file eq $new_obj->input_file) { - $new_obj->get_volume($vol_idx++)->config->apply($org_obj->get_volume($i)->config); - } - if (($org_obj->get_volume($i)->input_file ne $new_obj->input_file) || ($i==$org_obj->volumes_count-1)) { - # basically else, also accounting for original having less volumes than reloaded (i.e. loop not parsed another time) - while ($vol_idx <= $new_obj_vol_count-1) { - $new_obj->get_volume($vol_idx++)->config->apply($org_obj->get_volume(0)->config); - $volume_unmatched=1; - } - next; #skip increment of $vol_idx, it already points the the next volume - } - }else{ - last if $res==wxID_NO; # discard all other configurations according to the MessageDialog - if ($org_obj->input_file eq $org_obj->get_volume($i)->input_file) { - # original has more volumes than reloaded - $volume_unmatched=1; - next; - } - # reload volumes of additional parts and modifiers - if ($org_obj->get_volume($i)->input_file) { - my $model = eval { Slic3r::Model->read_from_file($org_obj->get_volume($i)->input_file) }; - if ($@) { - $org_obj->get_volume($i)->set_input_file(""); - }elsif ($org_obj->get_volume($i)->input_file_obj_idx > ($model->objects_count-1)) { - # Object Index for that part / modifier not found in current version of the file - $org_obj->get_volume($i)->set_input_file(""); - }else { - my $prt_mod_obj = $model->objects->[$org_obj->get_volume($i)->input_file_obj_idx]; - if ($org_obj->get_volume($i)->input_file_vol_idx > ($prt_mod_obj->volumes_count-1)) { - # Volume Index for that part / modifier not found in current version of the file - $org_obj->get_volume($i)->set_input_file(""); - }else{ - my $new_volume = $new_obj->add_volume($prt_mod_obj->get_volume($org_obj->get_volume($i)->input_file_vol_idx)); - $new_volume->set_name($org_obj->get_volume($i)->name); - $new_volume->set_input_file($org_obj->get_volume($i)->input_file); - $new_volume->set_input_file_obj_idx($org_obj->get_volume($i)->input_file_obj_idx); - $new_volume->set_input_file_vol_idx($org_obj->get_volume($i)->input_file_vol_idx); - $new_volume->config->apply($org_obj->get_volume($i)->config); - $new_volume->set_modifier($org_obj->get_volume($i)->modifier); - $new_volume->mesh->translate(@{$org_obj->origin_translation}); - } + while ($new_vol_idx<=$new_vol_count-1) { + if (($org_vol_idx<=$org_vol_count-1) && ($org_obj->get_volume($org_vol_idx)->input_file eq $new_obj->input_file)) { + # apply config from the matching volumes + $new_obj->get_volume($new_vol_idx++)->config->apply($org_obj->get_volume($org_vol_idx++)->config); + } else { + # reload has more volumes than original (first file), apply config from the first volume + $new_obj->get_volume($new_vol_idx++)->config->apply($org_obj->get_volume(0)->config); + $volume_unmatched=1; + } + } + $org_vol_idx=$org_vol_count if $res==wxID_NO; # discard all other configurations according to the MessageDialog + while (($org_vol_idx<=$org_vol_count-1) && ($org_obj->get_volume($org_vol_idx)->input_file eq $new_obj->input_file)) { + # original has more volumes (first file), skip those + $org_vol_idx++; + $volume_unmatched=1; + } + while ($org_vol_idx<=$org_vol_count-1) { + if ($org_obj->get_volume($org_vol_idx)->input_file) { + my $model = eval { Slic3r::Model->read_from_file($org_obj->get_volume($org_vol_idx)->input_file) }; + if ($@) { + $org_obj->get_volume($org_vol_idx)->set_input_file(""); + }elsif ($org_obj->get_volume($org_vol_idx)->input_file_obj_idx > ($model->objects_count-1)) { + # Object Index for that part / modifier not found in current version of the file + $org_obj->get_volume($org_vol_idx)->set_input_file(""); + }else { + my $prt_mod_obj = $model->objects->[$org_obj->get_volume($org_vol_idx)->input_file_obj_idx]; + if ($org_obj->get_volume($org_vol_idx)->input_file_vol_idx > ($prt_mod_obj->volumes_count-1)) { + # Volume Index for that part / modifier not found in current version of the file + $org_obj->get_volume($org_vol_idx)->set_input_file(""); + }else{ + # all checks passed, load new mesh and copy metadata + my $new_volume = $new_obj->add_volume($prt_mod_obj->get_volume($org_obj->get_volume($org_vol_idx)->input_file_vol_idx)); + $new_volume->set_name($org_obj->get_volume($org_vol_idx)->name); + $new_volume->set_input_file($org_obj->get_volume($org_vol_idx)->input_file); + $new_volume->set_input_file_obj_idx($org_obj->get_volume($org_vol_idx)->input_file_obj_idx); + $new_volume->set_input_file_vol_idx($org_obj->get_volume($org_vol_idx)->input_file_vol_idx); + $new_volume->config->apply($org_obj->get_volume($org_vol_idx)->config); + $new_volume->set_modifier($org_obj->get_volume($org_vol_idx)->modifier); + $new_volume->mesh->translate(@{$new_obj->origin_translation}); } } - if (!$org_obj->get_volume($i)->input_file) { - my $new_volume = $new_obj->add_volume($org_obj->get_volume($i)); # error -> copy old volume - $new_volume->set_name($new_volume->name . "-copied"); - $volume_unmatched=1; - } } + if (!$org_obj->get_volume($org_vol_idx)->input_file) { + my $new_volume = $new_obj->add_volume($org_obj->get_volume($org_vol_idx)); # error -> copy old mesh + $new_volume->set_name($new_volume->name . " - no link to path") if !($new_volume->name =~ m/link to path\z/); + $volume_unmatched=1; + } + $org_vol_idx++; } } - $self->remove($obj_idx); # TODO: refresh object list which contains wrong count and scale From 7e6ae04718ba2eb343812df28cf680e4b3d0098d Mon Sep 17 00:00:00 2001 From: Oekn5w <38046255+Oekn5w@users.noreply.github.com> Date: Fri, 20 Apr 2018 00:28:59 +0200 Subject: [PATCH 09/10] Whitespace cleanup --- xs/src/libslic3r/Model.hpp | 2 +- xs/xsp/Model.xsp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index e0b2a5000a..30cbf5e0d7 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -464,7 +464,7 @@ class ModelVolume int input_file_vol_idx; ///< Input file volume index bool modifier; ///< Is it an object to be printed, or a modifier volume? - + /// Get the parent object owning this modifier volume. /// \return ModelObject* pointer to the owner ModelObject ModelObject* get_object() const { return this->object; }; diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 8bf2b05111..67bca3fd3b 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -308,6 +308,7 @@ ModelMaterial::attributes() %}; }; + %name{Slic3r::Model::Instance} class ModelInstance { Ref object() %code%{ RETVAL = THIS->get_object(); %}; From f808359fb8eaf0a63047df91b48f1a810accabc9 Mon Sep 17 00:00:00 2001 From: Oekn5w <38046255+Oekn5w@users.noreply.github.com> Date: Sat, 21 Apr 2018 00:49:53 +0200 Subject: [PATCH 10/10] Added dialog to choose from different reload behaviors, added hide and default option in preferences, copied volumes are matched the new object's origin translation --- lib/Slic3r/GUI.pm | 5 +- lib/Slic3r/GUI/Plater.pm | 95 +++++++++++++++++++++++----------- lib/Slic3r/GUI/Preferences.pm | 19 ++++++- lib/Slic3r/GUI/ReloadDialog.pm | 60 +++++++++++++++++++++ 4 files changed, 147 insertions(+), 32 deletions(-) create mode 100644 lib/Slic3r/GUI/ReloadDialog.pm diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index c8cd20c5cf..6d2e5e3a84 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -52,6 +52,7 @@ use Slic3r::GUI::Preset; use Slic3r::GUI::PresetEditor; use Slic3r::GUI::PresetEditorDialog; use Slic3r::GUI::SLAPrintOptions; +use Slic3r::GUI::ReloadDialog; our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1"; our $have_LWP = eval "use LWP::UserAgent; 1"; @@ -91,7 +92,9 @@ our $Settings = { color_toolpaths_by => 'role', tabbed_preset_editors => 1, show_host => 0, - nudge_val => 1 + nudge_val => 1, + reload_hide_dialog => 0, + reload_behavior => 0 }, }; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 33bcfdcfb1..8b2c26d0d1 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -2346,14 +2346,29 @@ sub reload_from_disk { } } - # ask the user how to proceed - my $res = wxID_YES; - if ($org_obj_has_modifiers) { - $res = Wx::MessageDialog->new($self, "Additional parts and modifiers are loaded in the current model. \n\nDo you want to also reload these volumes?\nSelecting no will discard the extra volumes and their configuration.", 'Reload additional parts and modifiers', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION)->ShowModal; + my $reload_behavior = $Slic3r::GUI::Settings->{_}{reload_behavior}; + + # ask the user how to proceed, if option is selected in preferences + if ($org_obj_has_modifiers && !$Slic3r::GUI::Settings->{_}{reload_hide_dialog}) { + my $dlg = Slic3r::GUI::ReloadDialog->new(undef,$reload_behavior); + my $res = $dlg->ShowModal; if ($res==wxID_CANCEL) { $self->remove($_) for @new_obj_idx; + $dlg->Destroy; return; } + $reload_behavior = $dlg->GetSelection; + my $save = 0; + if ($reload_behavior != $Slic3r::GUI::Settings->{_}{reload_behavior}) { + $Slic3r::GUI::Settings->{_}{reload_behavior} = $reload_behavior; + $save = 1; + } + if ($dlg->GetHideOnNext) { + $Slic3r::GUI::Settings->{_}{reload_hide_dialog} = 1; + $save = 1; + } + Slic3r::GUI->save_settings if $save; + $dlg->Destroy; } my $volume_unmatched=0; @@ -2379,42 +2394,62 @@ sub reload_from_disk { $volume_unmatched=1; } } - $org_vol_idx=$org_vol_count if $res==wxID_NO; # discard all other configurations according to the MessageDialog + $org_vol_idx=$org_vol_count if $reload_behavior==2; # Reload behavior: discard while (($org_vol_idx<=$org_vol_count-1) && ($org_obj->get_volume($org_vol_idx)->input_file eq $new_obj->input_file)) { # original has more volumes (first file), skip those $org_vol_idx++; $volume_unmatched=1; } while ($org_vol_idx<=$org_vol_count-1) { - if ($org_obj->get_volume($org_vol_idx)->input_file) { - my $model = eval { Slic3r::Model->read_from_file($org_obj->get_volume($org_vol_idx)->input_file) }; - if ($@) { - $org_obj->get_volume($org_vol_idx)->set_input_file(""); - }elsif ($org_obj->get_volume($org_vol_idx)->input_file_obj_idx > ($model->objects_count-1)) { - # Object Index for that part / modifier not found in current version of the file - $org_obj->get_volume($org_vol_idx)->set_input_file(""); - }else { - my $prt_mod_obj = $model->objects->[$org_obj->get_volume($org_vol_idx)->input_file_obj_idx]; - if ($org_obj->get_volume($org_vol_idx)->input_file_vol_idx > ($prt_mod_obj->volumes_count-1)) { - # Volume Index for that part / modifier not found in current version of the file + if ($reload_behavior==1) { # Reload behavior: copy + my $new_volume = $new_obj->add_volume($org_obj->get_volume($org_vol_idx)); + $new_volume->mesh->translate(@{$org_obj->origin_translation->negative}); + $new_volume->mesh->translate(@{$new_obj->origin_translation}); + if ($new_volume->name =~ m/link to path\z/) { + my $new_name = $new_volume->name; + $new_name =~ s/ - no link to path$/ - copied/; + $new_volume->set_name($new_name); + }elsif(!($new_volume->name =~ m/copied\z/)) { + $new_volume->set_name($new_volume->name . " - copied"); + } + }else{ # Reload behavior: Reload all, also fallback solution if ini was manually edited to a wrong value + if ($org_obj->get_volume($org_vol_idx)->input_file) { + my $model = eval { Slic3r::Model->read_from_file($org_obj->get_volume($org_vol_idx)->input_file) }; + if ($@) { + $org_obj->get_volume($org_vol_idx)->set_input_file(""); + }elsif ($org_obj->get_volume($org_vol_idx)->input_file_obj_idx > ($model->objects_count-1)) { + # Object Index for that part / modifier not found in current version of the file $org_obj->get_volume($org_vol_idx)->set_input_file(""); }else{ - # all checks passed, load new mesh and copy metadata - my $new_volume = $new_obj->add_volume($prt_mod_obj->get_volume($org_obj->get_volume($org_vol_idx)->input_file_vol_idx)); - $new_volume->set_name($org_obj->get_volume($org_vol_idx)->name); - $new_volume->set_input_file($org_obj->get_volume($org_vol_idx)->input_file); - $new_volume->set_input_file_obj_idx($org_obj->get_volume($org_vol_idx)->input_file_obj_idx); - $new_volume->set_input_file_vol_idx($org_obj->get_volume($org_vol_idx)->input_file_vol_idx); - $new_volume->config->apply($org_obj->get_volume($org_vol_idx)->config); - $new_volume->set_modifier($org_obj->get_volume($org_vol_idx)->modifier); - $new_volume->mesh->translate(@{$new_obj->origin_translation}); + my $prt_mod_obj = $model->objects->[$org_obj->get_volume($org_vol_idx)->input_file_obj_idx]; + if ($org_obj->get_volume($org_vol_idx)->input_file_vol_idx > ($prt_mod_obj->volumes_count-1)) { + # Volume Index for that part / modifier not found in current version of the file + $org_obj->get_volume($org_vol_idx)->set_input_file(""); + }else{ + # all checks passed, load new mesh and copy metadata + my $new_volume = $new_obj->add_volume($prt_mod_obj->get_volume($org_obj->get_volume($org_vol_idx)->input_file_vol_idx)); + $new_volume->set_input_file($org_obj->get_volume($org_vol_idx)->input_file); + $new_volume->set_input_file_obj_idx($org_obj->get_volume($org_vol_idx)->input_file_obj_idx); + $new_volume->set_input_file_vol_idx($org_obj->get_volume($org_vol_idx)->input_file_vol_idx); + $new_volume->config->apply($org_obj->get_volume($org_vol_idx)->config); + $new_volume->set_modifier($org_obj->get_volume($org_vol_idx)->modifier); + $new_volume->mesh->translate(@{$new_obj->origin_translation}); + } } } - } - if (!$org_obj->get_volume($org_vol_idx)->input_file) { - my $new_volume = $new_obj->add_volume($org_obj->get_volume($org_vol_idx)); # error -> copy old mesh - $new_volume->set_name($new_volume->name . " - no link to path") if !($new_volume->name =~ m/link to path\z/); - $volume_unmatched=1; + if (!$org_obj->get_volume($org_vol_idx)->input_file) { + my $new_volume = $new_obj->add_volume($org_obj->get_volume($org_vol_idx)); # error -> copy old mesh + $new_volume->mesh->translate(@{$org_obj->origin_translation->negative}); + $new_volume->mesh->translate(@{$new_obj->origin_translation}); + if ($new_volume->name =~ m/copied\z/) { + my $new_name = $new_volume->name; + $new_name =~ s/ - copied$/ - no link to path/; + $new_volume->set_name($new_name); + }elsif(!($new_volume->name =~ m/link to path\z/)) { + $new_volume->set_name($new_volume->name . " - no link to path"); + } + $volume_unmatched=1; + } } $org_vol_idx++; } diff --git a/lib/Slic3r/GUI/Preferences.pm b/lib/Slic3r/GUI/Preferences.pm index 23da73d8d2..e63ca8cbc8 100644 --- a/lib/Slic3r/GUI/Preferences.pm +++ b/lib/Slic3r/GUI/Preferences.pm @@ -92,6 +92,23 @@ sub new { tooltip => 'In 2D plater, Move objects using keyboard by nudge value of', default => $Slic3r::GUI::Settings->{_}{nudge_val}, )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( # reload hide dialog + opt_id => 'reload_hide_dialog', + type => 'bool', + label => 'Hide Dialog on Reload', + tooltip => 'When checked, the dialog on reloading files with added parts & modifiers is suppressed. The reload is performed according to the option given in \'Default Reload Behavior\'', + default => $Slic3r::GUI::Settings->{_}{reload_hide_dialog}, + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( # default reload behavior + opt_id => 'reload_behavior', + type => 'select', + label => 'Default Reload Behavior', + tooltip => 'Choose the default behavior of the \'Reload from disk\' function regarding additional parts and modifiers.', + labels => ['Reload all','Reload main, copy added','Reload main, discard added'], + values => [0, 1, 2], + default => $Slic3r::GUI::Settings->{_}{reload_behavior}, + width => 180, + )); $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( # colorscheme opt_id => 'colorscheme', type => 'select', @@ -100,7 +117,7 @@ sub new { labels => ['Default','Solarized'], # add more schemes, if you want in ColorScheme.pm. values => ['getDefault','getSolarized'], # add more schemes, if you want - those are the names of the corresponding function in ColorScheme.pm. default => $Slic3r::GUI::Settings->{_}{colorscheme} // 'getDefault', - width => 130, + width => 180, )); my $sizer = Wx::BoxSizer->new(wxVERTICAL); diff --git a/lib/Slic3r/GUI/ReloadDialog.pm b/lib/Slic3r/GUI/ReloadDialog.pm new file mode 100644 index 0000000000..bb11bf0898 --- /dev/null +++ b/lib/Slic3r/GUI/ReloadDialog.pm @@ -0,0 +1,60 @@ +# A tiny dialog to select how to reload an object that has additional parts or modifiers. + +package Slic3r::GUI::ReloadDialog; +use strict; +use warnings; +use utf8; + +use Wx qw(:button :dialog :id :misc :sizer :choicebook wxTAB_TRAVERSAL); +use Wx::Event qw(EVT_CLOSE); +use base 'Wx::Dialog'; + +sub new { + my $class = shift; + my ($parent,$default_selection) = @_; + my $self = $class->SUPER::new($parent, -1, "Additional parts and modifiers detected", wxDefaultPosition, [350,100], wxDEFAULT_DIALOG_STYLE); + + # label + my $text = Wx::StaticText->new($self, -1, "Additional parts and modifiers are loaded in the current model. \n\nHow do you want to proceed?", wxDefaultPosition, wxDefaultSize); + + # selector + $self->{choice} = my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, wxDefaultSize, []); + $choice->Append("Reload all linked files"); + $choice->Append("Reload main file, copy added parts & modifiers"); + $choice->Append("Reload main file, discard added parts & modifiers"); + $choice->SetSelection($default_selection); + + # checkbox + $self->{checkbox} = my $checkbox = Wx::CheckBox->new($self, -1, "Don't ask again"); + + my $vsizer = Wx::BoxSizer->new(wxVERTICAL); + my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); + $vsizer->Add($text, 0, wxEXPAND | wxALL, 10); + $vsizer->Add($choice, 0, wxEXPAND | wxALL, 10); + $hsizer->Add($checkbox, 1, wxEXPAND | wxALL, 10); + $hsizer->Add($self->CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxALL, 10); + $vsizer->Add($hsizer, 0, wxEXPAND | wxALL, 0); + + $self->SetSizer($vsizer); + $self->SetMinSize($self->GetSize); + $vsizer->SetSizeHints($self); + + # needed to actually free memory + EVT_CLOSE($self, sub { + $self->EndModal(wxID_CANCEL); + $self->Destroy; + }); + + return $self; +} + +sub GetSelection { + my ($self) = @_; + return $self->{choice}->GetSelection; +} +sub GetHideOnNext { + my ($self) = @_; + return $self->{checkbox}->GetValue; +} + +1;