Preset.pm 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. package Slic3r::GUI::Preset;
  2. use Moo;
  3. use Unicode::Normalize;
  4. use Wx qw(:dialog :icon :id wxTheApp);
  5. has 'group' => (is => 'ro', required => 1);
  6. has 'default' => (is => 'ro', default => sub { 0 });
  7. has 'external' => (is => 'ro', default => sub { 0 });
  8. has 'name' => (is => 'rw', required => 1);
  9. has 'file' => (is => 'rw');
  10. has '_config' => (is => 'rw', default => sub { Slic3r::Config->new });
  11. has '_dirty_config' => (is => 'ro', default => sub { Slic3r::Config->new });
  12. sub BUILD {
  13. my ($self) = @_;
  14. $self->name(Unicode::Normalize::NFC($self->name));
  15. }
  16. sub _loaded {
  17. my ($self) = @_;
  18. return !$self->_config->empty;
  19. }
  20. sub dirty_options {
  21. my ($self) = @_;
  22. my @dirty = ();
  23. # Options present in both configs with different values:
  24. push @dirty, @{$self->_config->diff($self->_dirty_config)};
  25. # Overrides added to the dirty config:
  26. my @extra = $self->_group_class->overriding_options;
  27. push @dirty, grep { !$self->_config->has($_) && $self->_dirty_config->has($_) } @extra;
  28. # Overrides removed from the dirty config:
  29. push @dirty, grep { $self->_config->has($_) && !$self->_dirty_config->has($_) } @extra;
  30. return @dirty;
  31. }
  32. sub dirty {
  33. my ($self) = @_;
  34. return !!$self->dirty_options;
  35. }
  36. sub dropdown_name {
  37. my ($self) = @_;
  38. my $name = $self->name;
  39. $name .= " (modified)" if $self->dirty;
  40. return $name;
  41. }
  42. sub file_exists {
  43. my ($self) = @_;
  44. die "Can't call file_exists() on a non-file preset" if !$self->file;
  45. return -e Slic3r::encode_path($self->file);
  46. }
  47. sub rename {
  48. my ($self, $name) = @_;
  49. $self->name($name);
  50. $self->file(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $self->group, $name);
  51. }
  52. sub prompt_unsaved_changes {
  53. my ($self, $parent) = @_;
  54. if ($self->dirty) {
  55. my $name = $self->default ? 'Default preset' : "Preset \"" . $self->name . "\"";
  56. my $opts = '';
  57. foreach my $opt_key ($self->dirty_options) {
  58. my $opt = $Slic3r::Config::Options->{$opt_key};
  59. my $name = $opt->{full_label} // $opt->{label};
  60. if ($opt->{category}) {
  61. $name = $opt->{category} . " > $name";
  62. }
  63. $opts .= "- $name\n";
  64. }
  65. my $msg = sprintf "%s has unsaved changes:\n%s\nDo you want to save them?", $name, $opts;
  66. my $confirm = Wx::MessageDialog->new($parent, $msg,
  67. 'Unsaved Changes', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION);
  68. $confirm->SetYesNoCancelLabels('Save', 'Discard', 'Cancel');
  69. my $res = $confirm->ShowModal;
  70. if ($res == wxID_CANCEL) {
  71. return 0;
  72. } elsif ($res == wxID_YES) {
  73. return $self->default ? $self->save_prompt($parent) : $self->save;
  74. } elsif ($res == wxID_NO) {
  75. $self->dismiss_changes;
  76. return 1;
  77. }
  78. }
  79. return 1;
  80. }
  81. sub save_prompt {
  82. my ($self, $parent) = @_;
  83. my $default_name = $self->default ? 'Untitled' : $self->name;
  84. $default_name =~ s/\.ini$//i;
  85. my $dlg = Slic3r::GUI::SavePresetWindow->new($parent,
  86. default => $default_name,
  87. values => [ map $_->name, grep !$_->default && !$_->external, @{wxTheApp->presets->{$self->name}} ],
  88. );
  89. return 0 unless $dlg->ShowModal == wxID_OK;
  90. $self->save_as($dlg->get_name);
  91. }
  92. sub save {
  93. my ($self, $opt_keys) = @_;
  94. return $self->save_as($self->name, $opt_keys);
  95. }
  96. sub save_as {
  97. my ($self, $name, $opt_keys) = @_;
  98. $self->rename($name);
  99. if (!$self->file) {
  100. die "Calling save() without setting filename";
  101. }
  102. if ($opt_keys) {
  103. $self->_config->apply_only($self->_dirty_config, $opt_keys);
  104. } else {
  105. $self->_config->clear;
  106. $self->_config->apply($self->_dirty_config);
  107. }
  108. # unlink the file first to avoid problems on case-insensitive file systems
  109. unlink Slic3r::encode_path($self->file);
  110. $self->_config->save($self->file);
  111. wxTheApp->load_presets;
  112. return 1;
  113. }
  114. sub dismiss_changes {
  115. my ($self) = @_;
  116. $self->_dirty_config->clear;
  117. $self->_dirty_config->apply($self->_config);
  118. }
  119. sub delete {
  120. my ($self) = @_;
  121. die "Default config can't be deleted" if $self->default;
  122. die "External configs can't be deleted" if $self->external;
  123. # Otherwise wxTheApp->load_presets() will keep it
  124. $self->dismiss_changes;
  125. if ($self->file) {
  126. unlink Slic3r::encode_path($self->file) if $self->file_exists;
  127. $self->file(undef);
  128. }
  129. }
  130. # This returns the loaded config with the dirty options applied.
  131. sub dirty_config {
  132. my ($self) = @_;
  133. $self->load_config if !$self->_loaded;
  134. return $self->_dirty_config->clone;
  135. }
  136. sub load_config {
  137. my ($self) = @_;
  138. return $self->_config if $self->_loaded;
  139. my @keys = $self->_group_class->options;
  140. my @extra_keys = $self->_group_class->overriding_options;
  141. if ($self->default) {
  142. $self->_config(Slic3r::Config->new_from_defaults(@keys));
  143. } elsif ($self->file) {
  144. if (!$self->file_exists) {
  145. Slic3r::GUI::show_error(undef, "The selected preset does not exist anymore (" . $self->file . ").");
  146. return undef;
  147. }
  148. my $external_config = Slic3r::Config->load($self->file);
  149. if (!@keys) {
  150. $self->_config($external_config);
  151. } else {
  152. # apply preset values on top of defaults
  153. my $config = Slic3r::Config->new_from_defaults(@keys);
  154. $config->set($_, $external_config->get($_))
  155. for grep $external_config->has($_), @keys;
  156. # For extra_keys we don't populate defaults.
  157. if (@extra_keys && !$self->external) {
  158. $config->set($_, $external_config->get($_))
  159. for grep $external_config->has($_), @extra_keys;
  160. }
  161. $self->_config($config);
  162. }
  163. }
  164. $self->_dirty_config->apply($self->_config);
  165. return $self->_config;
  166. }
  167. sub _group_class {
  168. my ($self) = @_;
  169. return "Slic3r::GUI::PresetEditor::".ucfirst $self->group;
  170. }
  171. 1;