~ubuntu-branches/ubuntu/utopic/slic3r/utopic

« back to all changes in this revision

Viewing changes to lib/Slic3r/GUI/SkeinPanel.pm

  • Committer: Package Import Robot
  • Author(s): Chow Loong Jin
  • Date: 2014-06-17 01:27:26 UTC
  • Revision ID: package-import@ubuntu.com-20140617012726-2wrs4zdo251nr4vg
Tags: upstream-1.1.4+dfsg
ImportĀ upstreamĀ versionĀ 1.1.4+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package Slic3r::GUI::SkeinPanel;
 
2
use strict;
 
3
use warnings;
 
4
use utf8;
 
5
 
 
6
use File::Basename qw(basename dirname);
 
7
use List::Util qw(min);
 
8
use Slic3r::Geometry qw(X Y);
 
9
use Wx qw(:dialog :filedialog :font :icon :id :misc :notebook :panel :sizer);
 
10
use Wx::Event qw(EVT_BUTTON);
 
11
use base 'Wx::Panel';
 
12
 
 
13
our $last_input_file;
 
14
our $last_output_file;
 
15
our $last_config;
 
16
 
 
17
use constant FILE_WILDCARDS => {
 
18
    known   => 'Known files (*.stl, *.obj, *.amf, *.xml)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML',
 
19
    stl     => 'STL files (*.stl)|*.stl;*.STL',
 
20
    obj     => 'OBJ files (*.obj)|*.obj;*.OBJ',
 
21
    amf     => 'AMF files (*.amf)|*.amf;*.AMF;*.xml;*.XML',
 
22
    ini     => 'INI files *.ini|*.ini;*.INI',
 
23
    gcode   => 'G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC',
 
24
    svg     => 'SVG files *.svg|*.svg;*.SVG',
 
25
};
 
26
use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf)};
 
27
 
 
28
sub new {
 
29
    my $class = shift;
 
30
    my ($parent, %params) = @_;
 
31
    my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
 
32
    $self->{mode} = $params{mode};
 
33
    $self->{mode} = 'expert' if $self->{mode} !~ /^(?:simple|expert)$/;
 
34
    
 
35
    $self->{tabpanel} = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
 
36
    $self->{tabpanel}->AddPage($self->{plater} = Slic3r::GUI::Plater->new($self->{tabpanel}), "Plater")
 
37
        unless $params{no_plater};
 
38
    $self->{options_tabs} = {};
 
39
    
 
40
    my $simple_config;
 
41
    if ($self->{mode} eq 'simple') {
 
42
        $simple_config = Slic3r::Config->load("$Slic3r::GUI::datadir/simple.ini")
 
43
            if -e "$Slic3r::GUI::datadir/simple.ini";
 
44
    }
 
45
    
 
46
    my $class_prefix = $self->{mode} eq 'simple' ? "Slic3r::GUI::SimpleTab::" : "Slic3r::GUI::Tab::";
 
47
    my $init = 0;
 
48
    for my $tab_name (qw(print filament printer)) {
 
49
        my $tab;
 
50
        $tab = $self->{options_tabs}{$tab_name} = ($class_prefix . ucfirst $tab_name)->new(
 
51
            $self->{tabpanel},
 
52
            on_value_change     => sub {
 
53
                $self->{plater}->on_config_change(@_) if $self->{plater}; # propagate config change events to the plater
 
54
                if ($init) {  # don't save while loading for the first time
 
55
                    if ($self->{mode} eq 'simple') {
 
56
                        # save config
 
57
                        $self->config->save("$Slic3r::GUI::datadir/simple.ini");
 
58
                        
 
59
                        # save a copy into each preset section
 
60
                        # so that user gets the config when switching to expert mode
 
61
                        $tab->config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $tab->name, 'Simple Mode');
 
62
                        $Slic3r::GUI::Settings->{presets}{$tab->name} = 'Simple Mode.ini';
 
63
                        Slic3r::GUI->save_settings;
 
64
                    }
 
65
                    $self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave;
 
66
                }
 
67
            },
 
68
            on_presets_changed  => sub {
 
69
                $self->{plater}->update_presets($tab_name, @_) if $self->{plater};
 
70
            },
 
71
        );
 
72
        $self->{tabpanel}->AddPage($tab, $tab->title);
 
73
        $tab->load_config($simple_config) if $simple_config;
 
74
    }
 
75
    $init = 1;
 
76
    
 
77
    my $sizer = Wx::BoxSizer->new(wxVERTICAL);
 
78
    $sizer->Add($self->{tabpanel}, 1, wxEXPAND);
 
79
    
 
80
    $sizer->SetSizeHints($self);
 
81
    $self->SetSizer($sizer);
 
82
    $self->Layout;
 
83
    
 
84
    return $self;
 
85
}
 
86
 
 
87
sub quick_slice {
 
88
    my $self = shift;
 
89
    my %params = @_;
 
90
    
 
91
    my $progress_dialog;
 
92
    eval {
 
93
        # validate configuration
 
94
        my $config = $self->config;
 
95
        $config->validate;
 
96
        
 
97
        # select input file
 
98
        my $input_file;
 
99
        my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
 
100
        if (!$params{reslice}) {
 
101
            my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF):', $dir, "", MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
 
102
            if ($dialog->ShowModal != wxID_OK) {
 
103
                $dialog->Destroy;
 
104
                return;
 
105
            }
 
106
            $input_file = $dialog->GetPaths;
 
107
            $dialog->Destroy;
 
108
            $last_input_file = $input_file unless $params{export_svg};
 
109
        } else {
 
110
            if (!defined $last_input_file) {
 
111
                Wx::MessageDialog->new($self, "No previously sliced file.",
 
112
                                       'Error', wxICON_ERROR | wxOK)->ShowModal();
 
113
                return;
 
114
            }
 
115
            if (! -e $last_input_file) {
 
116
                Wx::MessageDialog->new($self, "Previously sliced file ($last_input_file) not found.",
 
117
                                       'File Not Found', wxICON_ERROR | wxOK)->ShowModal();
 
118
                return;
 
119
            }
 
120
            $input_file = $last_input_file;
 
121
        }
 
122
        my $input_file_basename = basename($input_file);
 
123
        $Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_file);
 
124
        Slic3r::GUI->save_settings;
 
125
        
 
126
        my $sprint = Slic3r::Print::Simple->new(
 
127
            status_cb       => sub {
 
128
                my ($percent, $message) = @_;
 
129
                return if &Wx::wxVERSION_STRING !~ / 2\.(8\.|9\.[2-9])/;
 
130
                $progress_dialog->Update($percent, "$messageā€¦");
 
131
            },
 
132
        );
 
133
        
 
134
        $sprint->apply_config($config);
 
135
        $sprint->set_model(Slic3r::Model->read_from_file($input_file));
 
136
        
 
137
        {
 
138
            my $extra = $self->extra_variables;
 
139
            $sprint->placeholder_parser->set($_, $extra->{$_}) for keys %$extra;
 
140
        }
 
141
        
 
142
        # select output file
 
143
        my $output_file;
 
144
        if ($params{reslice}) {
 
145
            $output_file = $last_output_file if defined $last_output_file;
 
146
        } elsif ($params{save_as}) {
 
147
            $output_file = $sprint->expanded_output_filepath;
 
148
            $output_file =~ s/\.gcode$/.svg/i if $params{export_svg};
 
149
            my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:',
 
150
                Slic3r::GUI->output_path(dirname($output_file)),
 
151
                basename($output_file), $params{export_svg} ? FILE_WILDCARDS->{svg} : FILE_WILDCARDS->{gcode}, wxFD_SAVE);
 
152
            if ($dlg->ShowModal != wxID_OK) {
 
153
                $dlg->Destroy;
 
154
                return;
 
155
            }
 
156
            $output_file = $dlg->GetPath;
 
157
            $last_output_file = $output_file unless $params{export_svg};
 
158
            $Slic3r::GUI::Settings->{_}{last_output_path} = dirname($output_file);
 
159
            Slic3r::GUI->save_settings;
 
160
            $dlg->Destroy;
 
161
        }
 
162
        
 
163
        # show processbar dialog
 
164
        $progress_dialog = Wx::ProgressDialog->new('Slicingā€¦', "Processing $input_file_basenameā€¦", 
 
165
            100, $self, 0);
 
166
        $progress_dialog->Pulse;
 
167
        
 
168
        {
 
169
            my @warnings = ();
 
170
            local $SIG{__WARN__} = sub { push @warnings, $_[0] };
 
171
            
 
172
            $sprint->output_file($output_file);
 
173
            if ($params{export_svg}) {
 
174
                $sprint->export_svg;
 
175
            } else {
 
176
                $sprint->export_gcode;
 
177
            }
 
178
            $sprint->status_cb(undef);
 
179
            Slic3r::GUI::warning_catcher($self)->($_) for @warnings;
 
180
        }
 
181
        $progress_dialog->Destroy;
 
182
        undef $progress_dialog;
 
183
        
 
184
        my $message = "$input_file_basename was successfully sliced.";
 
185
        &Wx::wxTheApp->notify($message);
 
186
        Wx::MessageDialog->new($self, $message, 'Slicing Done!', 
 
187
            wxOK | wxICON_INFORMATION)->ShowModal;
 
188
    };
 
189
    Slic3r::GUI::catch_error($self, sub { $progress_dialog->Destroy if $progress_dialog });
 
190
}
 
191
 
 
192
sub repair_stl {
 
193
    my $self = shift;
 
194
    
 
195
    my $input_file;
 
196
    {
 
197
        my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
 
198
        my $dialog = Wx::FileDialog->new($self, 'Select the STL file to repair:', $dir, "", FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
 
199
        if ($dialog->ShowModal != wxID_OK) {
 
200
            $dialog->Destroy;
 
201
            return;
 
202
        }
 
203
        $input_file = $dialog->GetPaths;
 
204
        $dialog->Destroy;
 
205
    }
 
206
    
 
207
    my $output_file = $input_file;
 
208
    {
 
209
        $output_file =~ s/\.stl$/_fixed.obj/i;
 
210
        my $dlg = Wx::FileDialog->new($self, "Save OBJ file (less prone to coordinate errors than STL) as:", dirname($output_file),
 
211
            basename($output_file), &Slic3r::GUI::SkeinPanel::FILE_WILDCARDS->{obj}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
 
212
        if ($dlg->ShowModal != wxID_OK) {
 
213
            $dlg->Destroy;
 
214
            return undef;
 
215
        }
 
216
        $output_file = $dlg->GetPath;
 
217
        $dlg->Destroy;
 
218
    }
 
219
    
 
220
    my $tmesh = Slic3r::TriangleMesh->new;
 
221
    $tmesh->ReadSTLFile(Slic3r::encode_path($input_file));
 
222
    $tmesh->repair;
 
223
    $tmesh->WriteOBJFile(Slic3r::encode_path($output_file));
 
224
    Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair");
 
225
}
 
226
 
 
227
sub extra_variables {
 
228
    my $self = shift;
 
229
    
 
230
    my %extra_variables = ();
 
231
    if ($self->{mode} eq 'expert') {
 
232
        $extra_variables{"${_}_preset"} = $self->{options_tabs}{$_}->current_preset->{name}
 
233
            for qw(print filament printer);
 
234
    }
 
235
    return { %extra_variables };
 
236
}
 
237
 
 
238
sub export_config {
 
239
    my $self = shift;
 
240
    
 
241
    my $config = $self->config;
 
242
    eval {
 
243
        # validate configuration
 
244
        $config->validate;
 
245
    };
 
246
    Slic3r::GUI::catch_error($self) and return;
 
247
    
 
248
    my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
 
249
    my $filename = $last_config ? basename($last_config) : "config.ini";
 
250
    my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename, 
 
251
        FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
 
252
    if ($dlg->ShowModal == wxID_OK) {
 
253
        my $file = $dlg->GetPath;
 
254
        $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
 
255
        Slic3r::GUI->save_settings;
 
256
        $last_config = $file;
 
257
        $config->save($file);
 
258
    }
 
259
    $dlg->Destroy;
 
260
}
 
261
 
 
262
sub load_config_file {
 
263
    my $self = shift;
 
264
    my ($file) = @_;
 
265
    
 
266
    if (!$file) {
 
267
        return unless $self->check_unsaved_changes;
 
268
        my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
 
269
        my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini", 
 
270
                FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
 
271
        return unless $dlg->ShowModal == wxID_OK;
 
272
        ($file) = $dlg->GetPaths;
 
273
        $dlg->Destroy;
 
274
    }
 
275
    $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
 
276
    Slic3r::GUI->save_settings;
 
277
    $last_config = $file;
 
278
    for my $tab (values %{$self->{options_tabs}}) {
 
279
        $tab->load_config_file($file);
 
280
    }
 
281
}
 
282
 
 
283
sub export_configbundle {
 
284
    my $self = shift;
 
285
    
 
286
    eval {
 
287
        # validate current configuration in case it's dirty
 
288
        $self->config->validate;
 
289
    };
 
290
    Slic3r::GUI::catch_error($self) and return;
 
291
    
 
292
    my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
 
293
    my $filename = "Slic3r_config_bundle.ini";
 
294
    my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:', $dir, $filename, 
 
295
        FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
 
296
    if ($dlg->ShowModal == wxID_OK) {
 
297
        my $file = $dlg->GetPath;
 
298
        $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
 
299
        Slic3r::GUI->save_settings;
 
300
        
 
301
        # leave default category empty to prevent the bundle from being parsed as a normal config file
 
302
        my $ini = { _ => {} };
 
303
        $ini->{settings}{$_} = $Slic3r::GUI::Settings->{_}{$_} for qw(autocenter mode);
 
304
        $ini->{presets} = $Slic3r::GUI::Settings->{presets};
 
305
        if (-e "$Slic3r::GUI::datadir/simple.ini") {
 
306
            my $config = Slic3r::Config->load("$Slic3r::GUI::datadir/simple.ini");
 
307
            $ini->{simple} = $config->as_ini->{_};
 
308
        }
 
309
        
 
310
        foreach my $section (qw(print filament printer)) {
 
311
            my %presets = Slic3r::GUI->presets($section);
 
312
            foreach my $preset_name (keys %presets) {
 
313
                my $config = Slic3r::Config->load($presets{$preset_name});
 
314
                $ini->{"$section:$preset_name"} = $config->as_ini->{_};
 
315
            }
 
316
        }
 
317
        
 
318
        Slic3r::Config->write_ini($file, $ini);
 
319
    }
 
320
    $dlg->Destroy;
 
321
}
 
322
 
 
323
sub load_configbundle {
 
324
    my $self = shift;
 
325
    
 
326
    my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
 
327
    my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini", 
 
328
            FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
 
329
    return unless $dlg->ShowModal == wxID_OK;
 
330
    my ($file) = $dlg->GetPaths;
 
331
    $dlg->Destroy;
 
332
    
 
333
    $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
 
334
    Slic3r::GUI->save_settings;
 
335
    
 
336
    # load .ini file
 
337
    my $ini = Slic3r::Config->read_ini($file);
 
338
    
 
339
    if ($ini->{settings}) {
 
340
        $Slic3r::GUI::Settings->{_}{$_} = $ini->{settings}{$_} for keys %{$ini->{settings}};
 
341
        Slic3r::GUI->save_settings;
 
342
    }
 
343
    if ($ini->{presets}) {
 
344
        $Slic3r::GUI::Settings->{presets} = $ini->{presets};
 
345
        Slic3r::GUI->save_settings;
 
346
    }
 
347
    if ($ini->{simple}) {
 
348
        my $config = Slic3r::Config->load_ini_hash($ini->{simple});
 
349
        $config->save("$Slic3r::GUI::datadir/simple.ini");
 
350
        if ($self->{mode} eq 'simple') {
 
351
            foreach my $tab (values %{$self->{options_tabs}}) {
 
352
                $tab->load_config($config) for values %{$self->{options_tabs}};
 
353
            }
 
354
        }
 
355
    }
 
356
    my $imported = 0;
 
357
    foreach my $ini_category (sort keys %$ini) {
 
358
        next unless $ini_category =~ /^(print|filament|printer):(.+)$/;
 
359
        my ($section, $preset_name) = ($1, $2);
 
360
        my $config = Slic3r::Config->load_ini_hash($ini->{$ini_category});
 
361
        $config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $section, $preset_name);
 
362
        $imported++;
 
363
    }
 
364
    if ($self->{mode} eq 'expert') {
 
365
        foreach my $tab (values %{$self->{options_tabs}}) {
 
366
            $tab->load_presets;
 
367
        }
 
368
    }
 
369
    my $message = sprintf "%d presets successfully imported.", $imported;
 
370
    if ($self->{mode} eq 'simple' && $Slic3r::GUI::Settings->{_}{mode} eq 'expert') {
 
371
        Slic3r::GUI::show_info($self, "$message You need to restart Slic3r to make the changes effective.");
 
372
    } else {
 
373
        Slic3r::GUI::show_info($self, $message);
 
374
    }
 
375
}
 
376
 
 
377
sub load_config {
 
378
    my $self = shift;
 
379
    my ($config) = @_;
 
380
    
 
381
    foreach my $tab (values %{$self->{options_tabs}}) {
 
382
        $tab->set_value($_, $config->$_) for @{$config->get_keys};
 
383
    }
 
384
}
 
385
 
 
386
sub config_wizard {
 
387
    my $self = shift;
 
388
 
 
389
    return unless $self->check_unsaved_changes;
 
390
    if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) {
 
391
        if ($self->{mode} eq 'expert') {
 
392
            for my $tab (values %{$self->{options_tabs}}) {
 
393
                $tab->select_default_preset;
 
394
            }
 
395
        }
 
396
        $self->load_config($config);
 
397
        if ($self->{mode} eq 'expert') {
 
398
            for my $tab (values %{$self->{options_tabs}}) {
 
399
                $tab->save_preset('My Settings');
 
400
            }
 
401
        }
 
402
    }
 
403
}
 
404
 
 
405
sub combine_stls {
 
406
    my $self = shift;
 
407
    
 
408
    # get input files
 
409
    my @input_files = ();
 
410
    my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
 
411
    {
 
412
        my $dlg_message = 'Choose one or more files to combine (STL/OBJ)';
 
413
        while (1) {
 
414
            my $dialog = Wx::FileDialog->new($self, "$dlg_message:", $dir, "", MODEL_WILDCARD, 
 
415
                wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
 
416
            if ($dialog->ShowModal != wxID_OK) {
 
417
                $dialog->Destroy;
 
418
                last;
 
419
            }
 
420
            push @input_files, $dialog->GetPaths;
 
421
            $dialog->Destroy;
 
422
            $dlg_message .= " or hit Cancel if you have finished";
 
423
            $dir = dirname($input_files[0]);
 
424
        }
 
425
        return if !@input_files;
 
426
    }
 
427
    
 
428
    # get output file
 
429
    my $output_file = $input_files[0];
 
430
    {
 
431
        $output_file =~ s/\.(?:stl|obj)$/.amf.xml/i;
 
432
        my $dlg = Wx::FileDialog->new($self, 'Save multi-material AMF file as:', dirname($output_file),
 
433
            basename($output_file), FILE_WILDCARDS->{amf}, wxFD_SAVE);
 
434
        if ($dlg->ShowModal != wxID_OK) {
 
435
            $dlg->Destroy;
 
436
            return;
 
437
        }
 
438
        $output_file = $dlg->GetPath;
 
439
    }
 
440
    
 
441
    my @models = eval { map Slic3r::Model->read_from_file($_), @input_files };
 
442
    Slic3r::GUI::show_error($self, $@) if $@;
 
443
    
 
444
    my $new_model = Slic3r::Model->new;
 
445
    my $new_object = $new_model->add_object;
 
446
    for my $m (0 .. $#models) {
 
447
        my $model = $models[$m];
 
448
        
 
449
        my $material_name = basename($input_files[$m]);
 
450
        $material_name =~ s/\.(stl|obj)$//i;
 
451
        
 
452
        $new_model->set_material($m, { Name => $material_name });
 
453
        $new_object->add_volume(
 
454
            material_id => $m,
 
455
            mesh        => $model->objects->[0]->volumes->[0]->mesh,
 
456
        );
 
457
    }
 
458
    
 
459
    Slic3r::Format::AMF->write_file($output_file, $new_model);
 
460
}
 
461
 
 
462
=head2 config
 
463
 
 
464
This method collects all config values from the tabs and merges them into a single config object.
 
465
 
 
466
=cut
 
467
 
 
468
sub config {
 
469
    my $self = shift;
 
470
    
 
471
    # retrieve filament presets and build a single config object for them
 
472
    my $filament_config;
 
473
    if (!$self->{plater} || $self->{plater}->filament_presets == 1 || $self->{mode} eq 'simple') {
 
474
        $filament_config = $self->{options_tabs}{filament}->config;
 
475
    } else {
 
476
        # TODO: handle dirty presets.
 
477
        # perhaps plater shouldn't expose dirty presets at all in multi-extruder environments.
 
478
        my $i = -1;
 
479
        foreach my $preset_idx ($self->{plater}->filament_presets) {
 
480
            $i++;
 
481
            my $preset = $self->{options_tabs}{filament}->get_preset($preset_idx);
 
482
            my $config = $self->{options_tabs}{filament}->get_preset_config($preset);
 
483
            if (!$filament_config) {
 
484
                $filament_config = $config->clone;
 
485
                next;
 
486
            }
 
487
            foreach my $opt_key (@{$config->get_keys}) {
 
488
                my $value = $filament_config->get($opt_key);
 
489
                next unless ref $value eq 'ARRAY';
 
490
                $value->[$i] = $config->get($opt_key)->[0];
 
491
                $filament_config->set($opt_key, $value);
 
492
            }
 
493
        }
 
494
    }
 
495
    
 
496
    my $config = Slic3r::Config->merge(
 
497
        Slic3r::Config->new_from_defaults,
 
498
        $self->{options_tabs}{print}->config,
 
499
        $self->{options_tabs}{printer}->config,
 
500
        $filament_config,
 
501
    );
 
502
    
 
503
    if ($self->{mode} eq 'simple') {
 
504
        # set some sensible defaults
 
505
        $config->set('first_layer_height', $config->nozzle_diameter->[0]);
 
506
        $config->set('avoid_crossing_perimeters', 1);
 
507
        $config->set('infill_every_layers', 10);
 
508
    } else {
 
509
        my $extruders_count = $self->{options_tabs}{printer}{extruders_count};
 
510
        $config->set("${_}_extruder", min($config->get("${_}_extruder"), $extruders_count))
 
511
            for qw(perimeter infill support_material support_material_interface);
 
512
    }
 
513
    
 
514
    return $config;
 
515
}
 
516
 
 
517
sub set_value {
 
518
    my $self = shift;
 
519
    my ($opt_key, $value) = @_;
 
520
    
 
521
    my $changed = 0;
 
522
    foreach my $tab (values %{$self->{options_tabs}}) {
 
523
        $changed = 1 if $tab->set_value($opt_key, $value);
 
524
    }
 
525
    return $changed;
 
526
}
 
527
 
 
528
sub check_unsaved_changes {
 
529
    my $self = shift;
 
530
    
 
531
    my @dirty = map $_->title, grep $_->is_dirty, values %{$self->{options_tabs}};
 
532
    if (@dirty) {
 
533
        my $titles = join ', ', @dirty;
 
534
        my $confirm = Wx::MessageDialog->new($self, "You have unsaved changes ($titles). Discard changes and continue anyway?",
 
535
                                             'Unsaved Presets', wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
 
536
        return ($confirm->ShowModal == wxID_YES);
 
537
    }
 
538
    
 
539
    return 1;
 
540
}
 
541
 
 
542
sub select_tab {
 
543
    my ($self, $tab) = @_;
 
544
    $self->{tabpanel}->ChangeSelection($tab);
 
545
}
 
546
 
 
547
1;