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

« back to all changes in this revision

Viewing changes to lib/Slic3r/GUI/OptionsGroup.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::OptionsGroup;
 
2
use Moo;
 
3
 
 
4
use List::Util qw(first);
 
5
use Wx qw(:combobox :font :misc :sizer :systemsettings :textctrl);
 
6
use Wx::Event qw(EVT_CHECKBOX EVT_COMBOBOX EVT_SPINCTRL EVT_TEXT EVT_KILL_FOCUS EVT_SLIDER);
 
7
 
 
8
=head1 NAME
 
9
 
 
10
Slic3r::GUI::OptionsGroup - pre-filled Wx::StaticBoxSizer wrapper containing one or more options
 
11
 
 
12
=head1 SYNOPSIS
 
13
 
 
14
    my $optgroup = Slic3r::GUI::OptionsGroup->new(
 
15
        parent  => $self->parent,
 
16
        title   => 'Layers',
 
17
        options => [
 
18
            {
 
19
                opt_key     => 'layer_height',  # mandatory
 
20
                type        => 'f',             # mandatory
 
21
                label       => 'Layer height',
 
22
                tooltip     => 'This setting controls the height (and thus the total number) of the slices/layers.',
 
23
                sidetext    => 'mm',
 
24
                width       => 200,
 
25
                full_width  => 0,
 
26
                height      => 50,
 
27
                min         => 0,
 
28
                max         => 100,
 
29
                labels      => [],
 
30
                values      => [],
 
31
                default     => 0.4,             # mandatory
 
32
                readonly    => 0,
 
33
                on_change   => sub { print "new value is $_[0]\n" },
 
34
            },
 
35
        ],
 
36
        on_change   => sub { print "new value for $_[0] is $_[1]\n" },
 
37
        no_labels   => 0,
 
38
        label_width => 180,
 
39
        extra_column => sub { ... },
 
40
    );
 
41
    $sizer->Add($optgroup->sizer);
 
42
 
 
43
=cut
 
44
 
 
45
has 'parent'        => (is => 'ro', required => 1);
 
46
has 'title'         => (is => 'ro', required => 1);
 
47
has 'options'       => (is => 'ro', required => 1, trigger => 1);
 
48
has 'lines'         => (is => 'lazy');
 
49
has 'on_change'     => (is => 'ro', default => sub { sub {} });
 
50
has 'no_labels'     => (is => 'ro', default => sub { 0 });
 
51
has 'label_width'   => (is => 'ro', default => sub { 180 });
 
52
has 'extra_column'  => (is => 'ro');
 
53
has 'label_font'    => (is => 'ro');
 
54
has 'sidetext_font' => (is => 'ro', default => sub { Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) });
 
55
has 'ignore_on_change_return' => (is => 'ro', default => sub { 1 });
 
56
 
 
57
has 'sizer'         => (is => 'rw');
 
58
has '_triggers'     => (is => 'ro', default => sub { {} });
 
59
has '_setters'      => (is => 'ro', default => sub { {} });
 
60
 
 
61
sub _trigger_options {}
 
62
 
 
63
sub BUILD {
 
64
    my $self = shift;
 
65
    
 
66
    {
 
67
        my $box = Wx::StaticBox->new($self->parent, -1, $self->title);
 
68
        $self->sizer(Wx::StaticBoxSizer->new($box, wxVERTICAL));
 
69
    }
 
70
    
 
71
    my $num_columns = $self->extra_column ? 3 : 2;
 
72
    my $grid_sizer = Wx::FlexGridSizer->new(scalar(@{$self->options}), $num_columns, 0, 0);
 
73
    $grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
 
74
    $grid_sizer->AddGrowableCol($self->no_labels ? 0 : 1);
 
75
    
 
76
    # TODO: border size may be related to wxWidgets 2.8.x vs. 2.9.x instead of wxMAC specific
 
77
    $self->sizer->Add($grid_sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 5);
 
78
    
 
79
    foreach my $line (@{$self->lines}) {
 
80
        if ($line->{sizer}) {
 
81
            $self->sizer->Add($line->{sizer}, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15);
 
82
        } elsif ($line->{widget}) {
 
83
            my $window = $line->{widget}->GetWindow($self->parent);
 
84
            $self->sizer->Add($window, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15);
 
85
        } else {
 
86
            $self->_build_line($line, $grid_sizer);
 
87
        }
 
88
    }
 
89
}
 
90
 
 
91
# default behavior: one option per line
 
92
sub _build_lines {
 
93
    my $self = shift;
 
94
    
 
95
    my $lines = [];
 
96
    foreach my $opt (@{$self->options}) {
 
97
        push @$lines, {
 
98
            label       => $opt->{label},
 
99
            sidetext    => $opt->{sidetext},
 
100
            full_width  => $opt->{full_width},
 
101
            options     => [$opt->{opt_key}],
 
102
        };
 
103
    }
 
104
    return $lines;
 
105
}
 
106
 
 
107
sub single_option_line {
 
108
    my $class = shift;
 
109
    my ($opt_key) = @_;
 
110
    
 
111
    return {
 
112
        label       => $Slic3r::Config::Options->{$opt_key}{label},
 
113
        sidetext    => $Slic3r::Config::Options->{$opt_key}{sidetext},
 
114
        options     => [$opt_key],
 
115
    };
 
116
}
 
117
 
 
118
sub _build_line {
 
119
    my $self = shift;
 
120
    my ($line, $grid_sizer) = @_;
 
121
    
 
122
    if ($self->extra_column) {
 
123
        if (defined (my $item = $self->extra_column->($line))) {
 
124
            $grid_sizer->Add($item, 0, wxALIGN_CENTER_VERTICAL, 0);
 
125
        } else {
 
126
            $grid_sizer->AddSpacer(1);
 
127
        }
 
128
    }
 
129
    
 
130
    my $label;
 
131
    if (!$self->no_labels) {
 
132
        $label = Wx::StaticText->new($self->parent, -1, $line->{label} ? "$line->{label}:" : "", wxDefaultPosition, [$self->label_width, -1]);
 
133
        $label->SetFont($self->label_font) if $self->label_font;
 
134
        $label->Wrap($self->label_width) ;  # needed to avoid Linux/GTK bug
 
135
        $grid_sizer->Add($label, 0, wxALIGN_CENTER_VERTICAL, 0);
 
136
        $label->SetToolTipString($line->{tooltip}) if $line->{tooltip};
 
137
    }
 
138
    
 
139
    my @fields = ();
 
140
    my @field_labels = ();
 
141
    foreach my $opt_key (@{$line->{options}}) {
 
142
        my $opt = first { $_->{opt_key} eq $opt_key } @{$self->options};
 
143
        push @fields, $self->_build_field($opt);
 
144
        push @field_labels, $opt->{label};
 
145
    }
 
146
    if (@fields > 1 || $line->{sidetext}) {
 
147
        my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
 
148
        for my $i (0 .. $#fields) {
 
149
            if (@fields > 1 && $field_labels[$i]) {
 
150
                my $field_label = Wx::StaticText->new($self->parent, -1, "$field_labels[$i]:", wxDefaultPosition, wxDefaultSize);
 
151
                $field_label->SetFont($self->sidetext_font);
 
152
                $sizer->Add($field_label, 0, wxALIGN_CENTER_VERTICAL, 0);
 
153
            }
 
154
            $sizer->Add($fields[$i], 0, wxALIGN_CENTER_VERTICAL, 0);
 
155
        }
 
156
        if ($line->{sidetext}) {
 
157
            my $sidetext = Wx::StaticText->new($self->parent, -1, $line->{sidetext}, wxDefaultPosition, wxDefaultSize);
 
158
            $sidetext->SetFont($self->sidetext_font);
 
159
            $sizer->Add($sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL , 4);
 
160
        }
 
161
        $grid_sizer->Add($sizer);
 
162
    } else {
 
163
        $grid_sizer->Add($fields[0], 0, ($line->{full_width} ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0);
 
164
    }
 
165
}
 
166
 
 
167
sub _build_field {
 
168
    my $self = shift;
 
169
    my ($opt) = @_;
 
170
    
 
171
    my $opt_key = $opt->{opt_key};
 
172
    $self->_triggers->{$opt_key} = $opt->{on_change} || sub { return 1 };
 
173
    
 
174
    my $on_kill_focus = sub {
 
175
        my ($s, $event) = @_;
 
176
        
 
177
        # Without this, there will be nasty focus bugs on Windows.
 
178
        # Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all 
 
179
        # non-command events to allow the default handling to take place."
 
180
        $event->Skip(1);
 
181
        
 
182
        $self->on_kill_focus($opt_key);
 
183
    };
 
184
    
 
185
    my $field;
 
186
    my $tooltip = $opt->{tooltip};
 
187
    if ($opt->{type} =~ /^(i|f|s|s@|percent|slider)$/) {
 
188
        my $style = 0;
 
189
        $style = wxTE_MULTILINE if $opt->{multiline};
 
190
        # default width on Windows is too large
 
191
        my $size = Wx::Size->new($opt->{width} || 60, $opt->{height} || -1);
 
192
        
 
193
        my $on_change = sub {
 
194
            my $value = $field->GetValue;
 
195
            $value ||= 0 if $opt->{type} =~ /^(i|f|percent)$/; # prevent crash trying to pass empty strings to Config
 
196
            $self->_on_change($opt_key, $value);
 
197
        };
 
198
        if ($opt->{type} eq 'i') {
 
199
            $field = Wx::SpinCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style, $opt->{min} || 0, $opt->{max} || 2147483647, $opt->{default});
 
200
            $self->_setters->{$opt_key} = sub { $field->SetValue($_[0]) };
 
201
            EVT_SPINCTRL ($self->parent, $field, $on_change);
 
202
            EVT_TEXT ($self->parent, $field, $on_change);
 
203
            EVT_KILL_FOCUS($field, $on_kill_focus);
 
204
        } elsif ($opt->{values}) {
 
205
            $field = Wx::ComboBox->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $opt->{labels} || $opt->{values});
 
206
            $self->_setters->{$opt_key} = sub {
 
207
                $field->SetValue($_[0]);
 
208
            };
 
209
            EVT_COMBOBOX($self->parent, $field, sub {
 
210
                # Without CallAfter, the field text is not populated on Windows.
 
211
                Slic3r::GUI->CallAfter(sub {
 
212
                    $field->SetValue($opt->{values}[ $field->GetSelection ]);  # set the text field to the selected value
 
213
                    $self->_on_change($opt_key, $on_change);
 
214
                });
 
215
            });
 
216
            EVT_TEXT($self->parent, $field, $on_change);
 
217
            EVT_KILL_FOCUS($field, $on_kill_focus);
 
218
        } elsif ($opt->{type} eq 'slider') {
 
219
            my $scale = 10;
 
220
            $field = Wx::BoxSizer->new(wxHORIZONTAL);
 
221
            my $slider = Wx::Slider->new($self->parent, -1, ($opt->{default} // $opt->{min})*$scale, ($opt->{min} // 0)*$scale, ($opt->{max} // 100)*$scale, wxDefaultPosition, $size);
 
222
            my $statictext = Wx::StaticText->new($self->parent, -1, $slider->GetValue/$scale);
 
223
            $field->Add($_, 0, wxALIGN_CENTER_VERTICAL, 0) for $slider, $statictext;
 
224
            $self->_setters->{$opt_key} = sub {
 
225
                $field->SetValue($_[0]*$scale);
 
226
            };
 
227
            EVT_SLIDER($self->parent, $slider, sub {
 
228
                my $value = $slider->GetValue/$scale;
 
229
                $statictext->SetLabel($value);
 
230
                $self->_on_change($opt_key, $value);
 
231
            });
 
232
        } else {
 
233
            $field = Wx::TextCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style);
 
234
            # value supplied to the setter callback might be undef in case user loads a config
 
235
            # that has empty string for multi-value options like 'wipe'
 
236
            $self->_setters->{$opt_key} = sub { $field->ChangeValue($_[0]) if defined $_[0] };
 
237
            EVT_TEXT($self->parent, $field, $on_change);
 
238
            EVT_KILL_FOCUS($field, $on_kill_focus);
 
239
        }
 
240
        $field->Disable if $opt->{readonly};
 
241
        $tooltip .= " (default: " . $opt->{default} .  ")" if ($opt->{default});
 
242
    } elsif ($opt->{type} eq 'bool') {
 
243
        $field = Wx::CheckBox->new($self->parent, -1, "");
 
244
        $field->SetValue($opt->{default});
 
245
        $field->Disable if $opt->{readonly};
 
246
        EVT_CHECKBOX($self->parent, $field, sub { $self->_on_change($opt_key, $field->GetValue); });
 
247
        $self->_setters->{$opt_key} = sub { $field->SetValue($_[0]) };
 
248
        $tooltip .= " (default: " . ($opt->{default} ? 'yes' : 'no') .  ")" if defined($opt->{default});
 
249
    } elsif ($opt->{type} eq 'point') {
 
250
        $field = Wx::BoxSizer->new(wxHORIZONTAL);
 
251
        my $field_size = Wx::Size->new(40, -1);
 
252
        my @items = (
 
253
            Wx::StaticText->new($self->parent, -1, "x:"),
 
254
                my $x_field = Wx::TextCtrl->new($self->parent, -1, $opt->{default}->[0], wxDefaultPosition, $field_size),
 
255
            Wx::StaticText->new($self->parent, -1, "  y:"),
 
256
                my $y_field = Wx::TextCtrl->new($self->parent, -1, $opt->{default}->[1], wxDefaultPosition, $field_size),
 
257
        );
 
258
        $field->Add($_, 0, wxALIGN_CENTER_VERTICAL, 0) for @items;
 
259
        if ($tooltip) {
 
260
            $_->SetToolTipString(
 
261
                $tooltip . " (default: " .  join(",", @{$opt->{default}}) .  ")"
 
262
            ) for @items;
 
263
        }
 
264
        foreach my $field ($x_field, $y_field) {
 
265
            EVT_TEXT($self->parent, $field, sub { $self->_on_change($opt_key, [ $x_field->GetValue, $y_field->GetValue ]) });
 
266
            EVT_KILL_FOCUS($field, $on_kill_focus);
 
267
        }
 
268
        $self->_setters->{$opt_key} = sub {
 
269
            $x_field->SetValue($_[0][0]);
 
270
            $y_field->SetValue($_[0][1]);
 
271
        };
 
272
    } elsif ($opt->{type} eq 'select') {
 
273
        $field = Wx::ComboBox->new($self->parent, -1, "", wxDefaultPosition, wxDefaultSize, $opt->{labels} || $opt->{values}, wxCB_READONLY);
 
274
        EVT_COMBOBOX($self->parent, $field, sub {
 
275
            $self->_on_change($opt_key, $opt->{values}[$field->GetSelection]);
 
276
        });
 
277
        $self->_setters->{$opt_key} = sub {
 
278
            $field->SetSelection(grep $opt->{values}[$_] eq $_[0], 0..$#{$opt->{values}});
 
279
        };
 
280
        $self->_setters->{$opt_key}->($opt->{default});
 
281
 
 
282
        $tooltip .= " (default: " 
 
283
                 . $opt->{labels}[ first { $opt->{values}[$_] eq $opt->{default} } 0..$#{$opt->{values}} ] 
 
284
                 . ")" if ($opt->{default});
 
285
    } else {
 
286
        die "Unsupported option type: " . $opt->{type};
 
287
    }
 
288
    if ($tooltip && $field->can('SetToolTipString')) {
 
289
        $field->SetToolTipString($tooltip);
 
290
    }
 
291
    return $field;
 
292
}
 
293
 
 
294
sub _option {
 
295
    my $self = shift;
 
296
    my ($opt_key) = @_;
 
297
    
 
298
    return first { $_->{opt_key} eq $opt_key } @{$self->options};
 
299
}
 
300
 
 
301
sub _on_change {
 
302
    my $self = shift;
 
303
    my ($opt_key, $value) = @_;
 
304
    
 
305
    return if $self->sizer->GetStaticBox->GetParent->{disabled};
 
306
    $self->_triggers->{$opt_key}->($value) or $self->ignore_on_change_return or return;
 
307
    $self->on_change->($opt_key, $value);
 
308
}
 
309
 
 
310
=head2 set_value
 
311
 
 
312
This method accepts an option key and a value. If this option group contains the supplied
 
313
option key, its field will be updated with the new value and the method will return a true
 
314
value, otherwise it will return false.
 
315
 
 
316
=cut
 
317
 
 
318
sub set_value {
 
319
    my $self = shift;
 
320
    my ($opt_key, $value) = @_;
 
321
    
 
322
    if ($self->_setters->{$opt_key}) {
 
323
        $self->_setters->{$opt_key}->($value);
 
324
        $self->_on_change($opt_key, $value);
 
325
        return 1;
 
326
    }
 
327
    
 
328
    return 0;
 
329
}
 
330
 
 
331
sub on_kill_focus {}
 
332
 
 
333
package Slic3r::GUI::ConfigOptionsGroup;
 
334
use Moo;
 
335
 
 
336
extends 'Slic3r::GUI::OptionsGroup';
 
337
 
 
338
=head1 NAME
 
339
 
 
340
Slic3r::GUI::ConfigOptionsGroup - pre-filled Wx::StaticBoxSizer wrapper containing one or more config options
 
341
 
 
342
=head1 SYNOPSIS
 
343
 
 
344
    my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
 
345
        parent      => $self->parent,
 
346
        title       => 'Layers',
 
347
        config      => $config,
 
348
        options     => ['layer_height'],
 
349
        on_change   => sub { print "new value for $_[0] is $_[1]\n" },
 
350
        no_labels   => 0,
 
351
        label_width => 180,
 
352
    );
 
353
    $sizer->Add($optgroup->sizer);
 
354
 
 
355
=cut
 
356
 
 
357
use List::Util qw(first);
 
358
 
 
359
has 'config' => (is => 'ro', required => 1);
 
360
has 'full_labels' => (is => 'ro', default => sub {0});
 
361
has '+ignore_on_change_return' => (is => 'ro', default => sub { 0 });
 
362
 
 
363
sub _trigger_options {
 
364
    my $self = shift;
 
365
    
 
366
    @{$self->options} = map {
 
367
        my $opt = $_;
 
368
        if (ref $opt ne 'HASH') {
 
369
            my $full_key = $opt;
 
370
            my ($opt_key, $index) = $self->_split_key($full_key);
 
371
            my $config_opt = $Slic3r::Config::Options->{$opt_key};
 
372
            
 
373
            $opt = {
 
374
                opt_key     => $full_key,
 
375
                config      => 1,
 
376
                label       => ($self->full_labels && defined $config_opt->{full_label}) ? $config_opt->{full_label} : $config_opt->{label},
 
377
                (map { $_   => $config_opt->{$_} } qw(type tooltip sidetext width height full_width min max labels values multiline readonly)),
 
378
                default     => $self->_get_config($opt_key, $index),
 
379
                on_change   => sub { return $self->_set_config($opt_key, $index, $_[0]) },
 
380
            };
 
381
        }
 
382
        $opt;
 
383
    } @{$self->options};
 
384
}
 
385
 
 
386
sub _option {
 
387
    my $self = shift;
 
388
    my ($opt_key) = @_;
 
389
    
 
390
    return first { $_->{opt_key} =~ /^\Q$opt_key\E(#.+)?$/ } @{$self->options};
 
391
}
 
392
 
 
393
sub set_value {
 
394
    my $self = shift;
 
395
    my ($opt_key, $value) = @_;
 
396
    
 
397
    my $opt = $self->_option($opt_key) or return 0; 
 
398
    
 
399
    # if user is setting a non-config option, forward the call to the parent
 
400
    if (!$opt->{config}) {
 
401
        return $self->SUPER::set_value($opt_key, $value);
 
402
    }
 
403
    
 
404
    my $changed = 0;
 
405
    foreach my $full_key (keys %{$self->_setters}) {
 
406
        my ($key, $index) = $self->_split_key($full_key);
 
407
        
 
408
        if ($key eq $opt_key) {
 
409
            $self->config->set($key, $value);
 
410
            $self->SUPER::set_value($full_key, $self->_get_config($key, $index));
 
411
            $changed = 1;
 
412
        }
 
413
    }
 
414
    return $changed;
 
415
}
 
416
 
 
417
sub on_kill_focus {
 
418
    my ($self, $full_key) = @_;
 
419
    
 
420
    # when a field loses focus, reapply the config value to it
 
421
    # (thus discarding any invalid input and reverting to the last
 
422
    # accepted value)
 
423
    my ($key, $index) = $self->_split_key($full_key);
 
424
    $self->SUPER::set_value($full_key, $self->_get_config($key, $index));
 
425
}
 
426
 
 
427
sub _split_key {
 
428
    my $self = shift;
 
429
    my ($opt_key) = @_;
 
430
    
 
431
    my $index;
 
432
    $opt_key =~ s/#(\d+)$// and $index = $1;
 
433
    return ($opt_key, $index);
 
434
}
 
435
 
 
436
sub _get_config {
 
437
    my $self = shift;
 
438
    my ($opt_key, $index, $config) = @_;
 
439
    
 
440
    my ($get_m, $serialized) = $self->_config_methods($opt_key, $index);
 
441
    my $value = ($config // $self->config)->$get_m($opt_key);
 
442
    if (defined $index) {
 
443
        $value->[$index] //= $value->[0]; #/
 
444
        $value = $value->[$index];
 
445
    }
 
446
    return $value;
 
447
}
 
448
 
 
449
sub _set_config {
 
450
    my $self = shift;
 
451
    my ($opt_key, $index, $value) = @_;
 
452
    
 
453
    my ($get_m, $serialized) = $self->_config_methods($opt_key, $index);
 
454
    if (defined $index) {
 
455
        my $values = $self->config->$get_m($opt_key);
 
456
        $values->[$index] = $value;
 
457
        
 
458
        # ignore set() return value
 
459
        $self->config->set($opt_key, $values);
 
460
    } else {
 
461
        if ($serialized) {        
 
462
            # ignore set_deserialize() return value
 
463
            return $self->config->set_deserialize($opt_key, $value);
 
464
        } else {        
 
465
            # ignore set() return value
 
466
            return $self->config->set($opt_key, $value);
 
467
        }
 
468
    }
 
469
}
 
470
 
 
471
sub _config_methods {
 
472
    my $self = shift;
 
473
    my ($opt_key, $index) = @_;
 
474
    
 
475
    # if it's an array type but no index was specified, use the serialized version
 
476
    return ($Slic3r::Config::Options->{$opt_key}{type} =~ /\@$/ && !defined $index)
 
477
        ? qw(serialize 1)
 
478
        : qw(get 0);
 
479
}
 
480
 
 
481
package Slic3r::GUI::OptionsGroup::StaticTextLine;
 
482
use Moo;
 
483
use Wx qw(:misc :systemsettings);
 
484
 
 
485
sub GetWindow {
 
486
    my $self = shift;
 
487
    my ($parent) = @_;
 
488
    
 
489
    $self->{statictext} = Wx::StaticText->new($parent, -1, "foo", wxDefaultPosition, wxDefaultSize);
 
490
    my $font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
 
491
    $self->{statictext}->SetFont($font);
 
492
    return $self->{statictext};
 
493
}
 
494
 
 
495
sub SetText {
 
496
    my $self = shift;
 
497
    my ($value) = @_;
 
498
    
 
499
    $self->{statictext}->SetLabel($value);
 
500
    $self->{statictext}->Wrap(400);
 
501
    $self->{statictext}->GetParent->Layout;
 
502
}
 
503
 
 
504
1;