File Coverage

blib/lib/Tk/Getopt.pm
Criterion Covered Total %
statement 165 801 20.6
branch 71 436 16.2
condition 18 154 11.6
subroutine 16 73 21.9
pod 11 13 84.6
total 281 1477 19.0


line stmt bran cond sub pod time code
1             # -*- perl -*-
2              
3             #
4             # Author: Slaven Rezic
5             #
6             # Copyright (C) 1997,1998,1999,2000,2003,2007,2008,2012,2014,2017 Slaven Rezic. All rights reserved.
7             # This package is free software; you can redistribute it and/or
8             # modify it under the same terms as Perl itself.
9             #
10             # Mail: srezic@cpan.org
11             # WWW: http://www.rezic.de/eserte/
12             #
13              
14             package Tk::Getopt;
15             require 5.005; # calling CODE refs
16 5     5   3387 use strict;
  5         10  
  5         153  
17 5         496 use vars qw($loadoptions $VERSION $x11_pass_through
18             $CHECKMARK_OFF $CHECKMARK_ON
19             $FILE_IMAGE $CURR_GEOMETRY_IMAGE $DEBUG
20 5     5   35 );
  5         11  
21 5     5   27 use constant OPTNAME => 0;
  5         10  
  5         399  
22 5     5   24 use constant OPTTYPE => 1;
  5         9  
  5         176  
23 5     5   22 use constant DEFVAL => 2;
  5         9  
  5         181  
24 5     5   25 use constant OPTEXTRA => 3;
  5         13  
  5         187  
25              
26 5     5   26 use Carp qw();
  5         11  
  5         40571  
27              
28             $VERSION = '0.51';
29             $VERSION =~ s{_}{};
30              
31             $DEBUG = 0;
32             $x11_pass_through = 0;
33              
34             sub new {
35 10     10 1 393 my($pkg, %a) = @_;
36 10         28 my $self = {};
37              
38 10 50       41 $self->{'options'} = delete $a{'-options'} if exists $a{'-options'};
39              
40 10 100       44 if (exists $a{'-opttable'}) {
    50          
41 3         10 $self->{'opttable'} = delete $a{'-opttable'};
42 3         8 foreach (@{$self->{'opttable'}}) {
  3         12  
43             # Convert from new style without hash for extra options for
44             # internal operation.
45             # ['opt', '=s', 'defval', 'x' => 'y', 'z' => 'a', ...] into
46             # ['opt', '=s', 'defval', {'x' => 'y', 'z' => 'a', ...}]
47 7 100 66     61 if (ref $_ eq 'ARRAY' and
      100        
48             defined $_->[OPTEXTRA] and
49             ref $_->[OPTEXTRA] ne 'HASH') {
50 1 50       5 if ((@$_ - OPTEXTRA) % 2 != 0) {
51 0         0 warn "Odd number of elements in definition for " . $_->[OPTNAME];
52             }
53 1         7 my %h = splice @$_, OPTEXTRA;
54 1         3 $_->[OPTEXTRA] = \%h;
55             }
56             # Handle aliases
57 7 50 33     43 if (ref $_ eq 'ARRAY' && $_->[OPTNAME] =~ /\|/) {
58 0         0 my($opt, @aliases) = split(/\|/, $_->[OPTNAME]);
59 0         0 $_->[OPTNAME] = $opt;
60 0         0 push(@{$_->[OPTEXTRA]{'aliases'}}, @aliases);
  0         0  
61             }
62             }
63             } elsif (exists $a{'-getopt'}) {
64             # build opttable from -getopt argument
65 7         18 my @optionlist;
66 7         13 my $genprefix = "(--|-|\\+)";
67 7 100       29 if (ref $a{'-getopt'} eq 'HASH') {
68             # convert hash to array
69             @optionlist
70 4         10 = map { ($_, $a{'-getopt'}->{$_}) } keys %{$a{'-getopt'}};
  37         88  
  4         26  
71             } else {
72 3         7 @optionlist = @{$a{'-getopt'}};
  3         9  
73             }
74 7         21 delete $a{'-getopt'};
75             # check if first argument is hash reference
76 7 100       26 if (ref $optionlist[0] eq 'HASH') {
77 2         6 $self->{'options'} = shift @optionlist;
78             }
79 7         29 while (@optionlist > 0) {
80 45         89 my $opt = shift @optionlist;
81             # Strip leading prefix so people can specify "--foo=i"
82             # if they like.
83 45 50       303 $opt = $2 if $opt =~ /^($genprefix)+(.*)$/;
84              
85 45 50       238 if ($opt !~ /^(\w+[-\w|]*)?(!|[=:][infse][@%]?)?$/) {
86 0         0 warn "Error in option spec: \"", $opt, "\"\n";
87 0         0 next;
88             }
89 45         142 my($o, $c) = ($1, $2);
90 45 100       102 $c = '' unless defined $c;
91 45         66 my @aliases;
92 45 100       109 if ($o =~ /\|/) {
93             # Handle alias names
94 1         4 @aliases = split(/\|/, $o);
95 1         3 $o = shift @aliases;
96             }
97 45         69 my $varref;
98             # If no linkage is supplied in the @optionlist, copy it from
99             # the userlinkage ($self->{'options'}) if available.
100 45 100 100     210 if (defined $self->{'options'} && !ref $optionlist[0]) {
    100          
101             $varref = (exists $self->{'options'}{$o} ?
102             $self->{'options'}{$o} :
103 5 100       19 \$self->{'options'}{$o});
104             } elsif (ref $optionlist[0]) {
105             # link to global variable
106 39         70 $varref = shift @optionlist;
107             }
108 45         71 my %a;
109 45 100       105 if (defined $varref) {
110 44 100       130 if (ref $varref eq 'CODE') {
111 2         2 my $code = $varref;
112             $a{'callback'} = sub {
113 2 100   2   9 if ($self->{'options'}{$o}) {
114 1         4 &$code;
115             }
116 2         9 };
117 2         6 $varref = \$self->{'options'}{$o};
118             }
119 44 50       182 if (ref($varref) =~ /^(SCALAR|HASH|ARRAY)$/) {
120 44         100 $a{'var'} = $varref;
121             } else {
122 0         0 die "Can't handle variable reference of type "
123             . ref $varref;
124             }
125             }
126 45 100       109 if (@aliases) {
127 1         2 $a{'alias'} = \@aliases;
128             }
129 45         67 push(@{$self->{'opttable'}}, [$o, $c, undef, \%a]);
  45         251  
130             }
131             } else {
132 0         0 die "No opttable array ref or getopt hash ref";
133             }
134              
135 10         60 $self->{'caller'} = (caller)[0];
136 10         33 $self->{'filename'} = delete $a{'-filename'};
137 10         27 $self->{'nosafe'} = delete $a{'-nosafe'};
138 10         22 $self->{'useerrordialog'} = delete $a{'-useerrordialog'};
139              
140 10 50       31 die "Unrecognized arguments: " . join(" ", %a) if %a;
141              
142 10         39 bless $self, $pkg;
143             }
144              
145             # Return a list with all option names, that is, section labels and
146             # descriptions are ignored.
147             sub _opt_array {
148 12     12   24 my $self = shift;
149 12         44 my @res;
150 12         31 foreach (@{$self->{'opttable'}}) {
  12         55  
151 80 50 33     392 push @res, $_
152             if ref $_ eq 'ARRAY' and
153             $_->[OPTNAME] ne '';
154             }
155 12         43 @res;
156             }
157              
158             # Return a reference to the option variable given by $opt
159             sub varref {
160 86     86 1 180 my($self, $opt) = @_;
161 86 100       212 if($opt->[OPTEXTRA]{'var'}) {
    50          
162 77         245 $opt->[OPTEXTRA]{'var'};
163             } elsif ($self->{'options'}) {
164 0         0 \$self->{'options'}{$opt->[OPTNAME]};
165             } else {
166             # Link to global $opt_XXX variable.
167             # Make sure a valid perl identifier results.
168 9         25 my $v;
169 9         39 ($v = $opt->[OPTNAME]) =~ s/\W/_/g;
170 9         440 eval q{\$} . $self->{'caller'} . q{::opt_} . $v; # XXX @, %
171             }
172             }
173             # Formerly the varref method was private:
174 0     0   0 sub _varref { shift->varref(@_) }
175              
176             sub optextra {
177 0     0 1 0 my($self, $opt, $arg) = @_;
178 0         0 $opt->[OPTEXTRA]{$arg};
179             }
180              
181             sub _is_separator {
182 0     0   0 my $opt = shift;
183 0 0 0     0 defined $opt->[OPTNAME] && $opt->[OPTNAME] eq '' &&
      0        
184             defined $opt->[DEFVAL] && $opt->[DEFVAL] eq '-';
185             }
186              
187             sub set_defaults {
188 0     0 1 0 my $self = shift;
189 0         0 my $opt;
190 0         0 foreach $opt ($self->_opt_array) {
191 0 0       0 if (defined $opt->[DEFVAL]) {
192 0         0 my $ref = ref $self->varref($opt);
193 0 0       0 if ($ref eq 'ARRAY') {
    0          
    0          
194 0         0 @ {$self->varref($opt)} = @{ $opt->[DEFVAL] };
  0         0  
  0         0  
195             } elsif ($ref eq 'HASH') {
196 0         0 % {$self->varref($opt)} = %{ $opt->[DEFVAL] };
  0         0  
  0         0  
197             } elsif ($ref eq 'SCALAR') {
198 0         0 $ {$self->varref($opt)} = $opt->[DEFVAL];
  0         0  
199             } else {
200 0         0 die "Invalid reference type for option $opt->[OPTNAME] while setting the default value (maybe you should specify as the default value)";
201             }
202             }
203             }
204             }
205              
206             sub load_options {
207 1     1 1 9 my($self, $filename) = @_;
208 1 50       6 $filename = $self->{'filename'} if !$filename;
209 1 50       5 return if !$filename;
210 1         8 require File::Spec;
211 1 50       15 if (!-e $filename) {
212 0         0 warn "File $filename does not exist";
213 0         0 return undef;
214             }
215 1 50       18 if (!File::Spec->file_name_is_absolute($filename)) {
216             # required for do operation with PERL_USE_UNSAFE_INC=0
217 1         44 $filename = File::Spec->rel2abs($filename);
218             }
219 1 50       7 if ($self->{'nosafe'}) {
220 0         0 require Safe;
221 0         0 my $c = new Safe;
222 0         0 $c->share('$loadoptions');
223 0 0       0 if (!$c->rdo($filename)) {
224 0         0 warn "Can't load $filename";
225 0         0 return undef;
226             }
227             } else {
228 1         3 eval {do $filename};
  1         463  
229 1 50       8 if ($@) {
230 0         0 warn $@;
231 0         0 return undef;
232             }
233             }
234              
235 1         3 my $opt;
236 1         6 foreach $opt ($self->_opt_array) {
237 7 50       28 if (exists $loadoptions->{$opt->[OPTNAME]}) {
238 7 50 66     18 if (ref $self->varref($opt) eq 'CODE') {
    100 66        
    100          
239 0 0       0 $self->varref($opt)->($opt, $loadoptions->{$opt->[OPTNAME]}) if $loadoptions->{$opt->[OPTNAME]};
240             } elsif (ref $self->varref($opt) eq 'ARRAY' &&
241             ref $loadoptions->{$opt->[OPTNAME]} eq 'ARRAY') {
242 1         4 @{ $self->varref($opt) } = @{ $loadoptions->{$opt->[OPTNAME]} };
  1         4  
  1         5  
243             } elsif (ref $self->varref($opt) eq 'HASH' &&
244             ref $loadoptions->{$opt->[OPTNAME]} eq 'HASH') {
245 1         4 %{ $self->varref($opt) } = %{ $loadoptions->{$opt->[OPTNAME]} };
  1         4  
  1         7  
246             } else {
247 5         15 $ {$self->varref($opt)} = $loadoptions->{$opt->[OPTNAME]};
  5         15  
248             }
249             }
250             }
251 1         4 1;
252             }
253              
254             sub save_options {
255 1     1 1 64 my($self, $filename) = @_;
256 1 50       9 $filename = $self->{'filename'} if !$filename;
257 1 50       3 die "Saving disabled" if !$filename;
258 1         65 eval "require Data::Dumper";
259 1 50       5259 if ($@) {
260 0         0 warn $@;
261 0         0 $self->my_die("No Data::Dumper available, cannot save options.\n");
262             } else {
263 1 50       77 if (open(OPT, ">$filename")) {
264 1         7 my %saveoptions;
265             my $opt;
266 1         7 foreach $opt ($self->_opt_array) {
267 11 50       40 if (!$opt->[OPTEXTRA]{'nosave'}) {
268 11         23 my $ref;
269 11 50       32 if ($opt->[OPTEXTRA]{'savevar'}) {
270 0         0 $ref = $opt->[OPTEXTRA]{'savevar'};
271             } else {
272 11         31 $ref = $self->varref($opt);
273             }
274 11 100       48 if (ref($ref) eq 'SCALAR') {
    50          
275 9         32 $saveoptions{$opt->[OPTNAME]} = $$ref;
276             } elsif (ref($ref) =~ /^(HASH|ARRAY)$/) {
277 2         17 $saveoptions{$opt->[OPTNAME]} = $ref;
278             }
279             }
280             }
281 1         5 local $Data::Dumper::Sortkeys = $Data::Dumper::Sortkeys = 1;
282 1         3 local $Data::Dumper::Indent = $Data::Dumper::Indent = 1;
283 1 50       14 if (Data::Dumper->can('Dumpxs')) {
284             # use faster version of Dump
285 1         17 print OPT
286             Data::Dumper->Dumpxs([\%saveoptions], ['loadoptions']);
287             } else {
288 0         0 print OPT
289             Data::Dumper->Dump([\%saveoptions], ['loadoptions']);
290             }
291 1         192 close OPT;
292 1 50       8 warn "Options written to $filename" if $DEBUG;
293 1         10 1;
294             } else {
295 0         0 $self->my_die("Writing to config file <$filename> failed: $!\n");
296 0         0 undef;
297             }
298             }
299             }
300              
301             sub get_options {
302 9     9 1 52 my $self = shift;
303 9         20 my %getopt;
304             my $opt;
305 9         32 foreach $opt ($self->_opt_array) {
306 45         100 $getopt{_getopt_long_string($opt->[OPTNAME], $opt->[OPTTYPE])} =
307             $self->varref($opt);
308             # process aliases
309 45         68 foreach (@{$opt->[OPTEXTRA]{'alias'}}) {
  45         119  
310 3         8 $getopt{_getopt_long_string($_, $opt->[OPTTYPE])} =
311             $self->varref($opt);
312             }
313             }
314 9         3428 require Getopt::Long;
315             # XXX anders implementieren ... vielleicht die X11-Optionen zusätzlich
316             # in die %getopt-Liste reinschreiben?
317 9 50       39639 if ($x11_pass_through) {
318 0         0 Getopt::Long::config('pass_through');
319             }
320 9         54 my $res = Getopt::Long::GetOptions(%getopt);
321             # Hack to pass standard X11 options (as defined in Tk::CmdLine)
322 9 50       7376 if ($x11_pass_through) {
323 0         0 eval {
324 0         0 require Tk::CmdLine;
325 0 0       0 if ($Tk::CmdLine::VERSION >= 3.012) {
326             # XXX nicht ausgetestet
327 0         0 my @args = @ARGV;
328 0   0     0 while (@args && $args[0] =~ /^-(\w+)$/) {
329 0         0 my $sw = $1;
330 0 0       0 return 0 if !$Tk::CmdLine::Method{$sw};
331 0 0       0 if ($Tk::CmdLine::Method{$sw} ne 'Flag_') {
332 0         0 shift @args;
333             }
334 0         0 shift @args;
335             }
336             } else {
337 0         0 my $flag_ref = \&Tk::CmdLine::flag;
338 0         0 my @args = @ARGV;
339 0   0     0 while (@args && $args[0] =~ /^-(\w+)$/) {
340 0         0 my $sw = $1;
341 0 0       0 return 0 if !$Tk::CmdLine::switch{$sw};
342 0 0       0 if ($Tk::CmdLine::switch{$sw} ne $flag_ref) {
343 0         0 shift @args;
344             }
345 0         0 shift @args;
346             }
347             }
348 0         0 $res = 1;
349             };
350 0 0       0 warn $@ if $@;
351             }
352 9         37 $res;
353             }
354              
355             # Builds a string for Getopt::Long. Arguments are option name and option
356             # type (e.g. '!' or '=s').
357             sub _getopt_long_string {
358 48     48   101 my($option, $type) = @_;
359 48 100 66     201 $option . (length($option) == 1 &&
360             (!defined $type || $type eq '' || $type eq '!')
361             ? '' : $type);
362             }
363              
364             # Prints option name with one or two dashes
365             sub _getopt_long_dash {
366 0     0   0 my $option = shift;
367 0 0       0 (length($option) == 1 ? '' : '-') . "-$option";
368             }
369              
370             sub usage {
371 0     0 1 0 my $self = shift;
372 0         0 my $usage = "Usage: $0 [options]\n";
373 0         0 my $opt;
374 0         0 foreach $opt ($self->_opt_array) {
375             # The following prints all options as a comma-seperated list
376             # with one or two dashes, depending on the length of the option.
377             # Options are sorted by length.
378             $usage .= join(', ',
379 0         0 sort { length $a <=> length $b }
380 0         0 map { _getopt_long_dash($_) }
381 0 0       0 map { ($opt->[OPTTYPE] eq '!' ? "[no]" : "") . $_ }
382 0         0 ($opt->[OPTNAME], @{$opt->[OPTEXTRA]{'alias'}}));
  0         0  
383 0         0 $usage .= "\t";
384 0 0       0 $usage .= $opt->[OPTEXTRA]{'help'} if $opt->[OPTEXTRA]{'help'};
385 0 0       0 $usage .= " (default: " . $opt->[DEFVAL] . ") " if $opt->[DEFVAL];
386 0         0 $usage .= "\n";
387             }
388 0         0 $usage;
389             }
390              
391             sub process_options {
392 1     1 1 10 my($self, $former, $fromgui) = @_;
393 1         3 my $bag = {};
394 1         6 foreach my $optdef ($self->_opt_array) {
395 17         24 my $opt = $optdef->[OPTNAME];
396              
397 17         22 my $callback;
398 17 50       35 if ($fromgui) {
399 0         0 $callback = $optdef->[OPTEXTRA]{'callback-interactive'};
400             }
401 17 50       34 if (!$callback) {
402 17         24 $callback = $optdef->[OPTEXTRA]{'callback'};
403             }
404 17 100       35 if ($callback) {
405             # no warnings here ... it would be too complicated to catch
406             # all undefined values
407 2         5 my $old_w = $^W;
408 2         6 local($^W) = 0;
409             # execute callback if value has changed
410 2 50 0     8 if (!(defined $former
      33        
411             && (!exists $former->{$opt}
412             || $ {$self->varref($optdef)} eq $former->{$opt}))) {
413 2         5 local($^W) = $old_w; # fall back to original value
414 2         6 &$callback(optdef => $optdef, bag => $bag);
415             }
416             }
417 17 50 33     379 if ($optdef->[OPTEXTRA]{'strict'} &&
418             UNIVERSAL::isa($optdef->[OPTEXTRA]{'choices'},'ARRAY')) {
419             # check for valid values (valid are: choices and default value)
420 0           my $v = $ {$self->varref($optdef)};
  0            
421 0           my @choices = @{$optdef->[OPTEXTRA]{'choices'}};
  0            
422 0 0         push(@choices, $optdef->[DEFVAL]) if defined $optdef->[DEFVAL];
423 0           my $seen;
424 0           for my $choice (@choices) {
425 0 0         my $value = (ref $choice eq 'ARRAY' ? $choice->[1] : $choice);
426 0 0         if ($value eq $v) {
427 0           $seen = 1;
428 0           last;
429             }
430             }
431 0 0         if (!$seen) {
432 0 0         if (defined $former) {
433 0           warn "Not allowed: " . $ {$self->varref($optdef)}
  0            
434             . " for -$opt. Using old value $former->{$opt}";
435 0           $ {$self->varref($optdef)} = $former->{$opt};
  0            
436             } else {
437             die "Not allowed: "
438 0           . $ {$self->varref($optdef)} . " for -$opt\n"
  0            
439             . "Allowed is only: " . join(", ", @choices);
440             }
441             }
442             }
443             }
444             }
445              
446             sub my_die {
447 0     0 0   my($self, $msg, $is_safe) = @_;
448 0           my $use_tk;
449 0 0 0       if ($self->{'useerrordialog'} && defined &Tk::MainWindow::Existing) {
450 0           for my $mw (Tk::MainWindow::Existing()) {
451 0 0         if (Tk::Exists($mw)) {
452 0           $use_tk = $mw;
453 0           last;
454             }
455             }
456 0 0 0       if ($use_tk && !defined $is_safe) {
457 0           for(my $i=0; $i<100; $i++) {
458 0           my(undef,undef,undef,$subroutine) = caller($i);
459 0 0         last if !defined $subroutine;
460 0 0         if ($subroutine eq '(eval)') {
461 0           $use_tk = 0;
462 0           last;
463             }
464             }
465             }
466             }
467 0 0         if ($use_tk) {
468 0           eval {
469 0           $use_tk->messageBox(-icon => "error",
470             -message => $msg,
471             -title => "Error",
472             );
473             };
474 0 0         if ($@) {
475 0           Carp::croak($msg);
476             }
477             } else {
478 0           Carp::croak($msg);
479             }
480             }
481              
482             # try to work around weird browse entry
483             sub _fix_layout {
484 0     0     my($self, $frame, $widget, %args) = @_;
485 0           my($w, $real_w);
486 0 0         if ($Tk::VERSION < 804) {
487 0           my $f = $frame->Frame;
488 0           $f->Label->pack(-side => "left"); # dummy
489 0           $real_w = $f->$widget(%args)->pack(-side => "left", -padx => 1);
490 0           $w = $f;
491             } else {
492 0           $w = $real_w = $frame->$widget(%args);
493             }
494 0           ($w, $real_w);
495             }
496              
497             sub _boolean_widget {
498 0     0     my($self, $frame, $opt) = @_;
499 0           ($self->_fix_layout($frame, "Checkbutton",
500             -variable => $self->varref($opt)))[0];
501             }
502              
503             sub _boolean_checkmark_widget {
504             # XXX hangs with Tk800.014?!
505 0     0     my($self, $frame, $opt) = @_;
506 0           _create_checkmarks($frame);
507 0           ($self->_fix_layout($frame, "Checkbutton",
508             -variable => $self->varref($opt),
509             -image => $CHECKMARK_OFF,
510             -selectimage => $CHECKMARK_ON,
511             -indicatoron => 0,
512             ))[0];
513             }
514              
515             sub _number_widget {
516 0     0     my($self, $frame, $opt) = @_;
517             ($self->_fix_layout($frame, "Scale",
518             -orient => 'horizontal',
519             -from => $opt->[OPTEXTRA]{'range'}[0],
520 0 0         -to => $opt->[OPTEXTRA]{'range'}[1],
521             -showvalue => 1,
522             -resolution => ($opt->[OPTTYPE] =~ /f/ ? 0 : 1),
523             -variable => $self->varref($opt)
524             ))[0];
525             }
526              
527             sub _integer_widget {
528 0     0     my($self, $frame, $opt) = @_;
529 0 0         if (exists $opt->[OPTEXTRA]{'range'}) {
530 0           $self->_number_widget($frame, $opt);
531             } else {
532 0           $self->_string_widget($frame, $opt, -restrict => "=i");
533             }
534             }
535              
536             sub _float_widget {
537 0     0     my($self, $frame, $opt) = @_;
538 0 0         if (exists $opt->[OPTEXTRA]{'range'}) {
539 0           $self->_number_widget($frame, $opt);
540             } else {
541 0           $self->_string_widget($frame, $opt, -restrict => "=f");
542             }
543             }
544              
545             sub _list_widget {
546 0     0     my($self, $frame, $opt) = @_;
547 0 0 0       if ($opt->[OPTEXTRA]{'strict'} && grep { ref $_ eq 'ARRAY' } @{$opt->[OPTEXTRA]{'choices'}}) {
  0            
  0            
548 0           $self->_optionmenu_widget($frame, $opt);
549             } else {
550 0           $self->_browseentry_widget($frame, $opt);
551             }
552             }
553              
554             sub _browseentry_widget {
555 0     0     my($self, $frame, $opt) = @_;
556 0           require Tk::BrowseEntry;
557 0           my %args = (-variable => $self->varref($opt));
558 0 0         if ($opt->[OPTEXTRA]{'strict'}) {
559 0           $args{-state} = "readonly";
560             }
561 0           my $w = $frame->BrowseEntry(%args);
562 0           my %mapping;
563 0           my @optlist = @{$opt->[OPTEXTRA]{'choices'}};
  0            
564 0 0         unshift @optlist, $opt->[DEFVAL] if defined $opt->[DEFVAL];
565 0           my $o;
566             my %seen;
567 0           foreach $o (@optlist) {
568 0 0         if (!$seen{$o}) {
569 0           $w->insert("end", $o);
570 0           $seen{$o}++;
571             }
572             }
573 0           $w;
574             }
575              
576             sub _optionmenu_widget {
577 0     0     my($self, $frame, $opt) = @_;
578 0           require Tk::Optionmenu;
579 0           my $varref = $self->varref($opt);
580             # Have to remember value, otherwise Optionmenu would overwrite it...
581 0           my $value = $$varref;
582             my %args = (-variable => $varref,
583 0           -options => $opt->[OPTEXTRA]{'choices'},
584             );
585 0           my $w = $frame->Optionmenu(%args);
586 0 0         if (defined $value) {
587 0           my $label = $value;
588 0           for my $choice (@{ $opt->[OPTEXTRA]{'choices'} }) {
  0            
589 0 0 0       if (ref $choice eq 'ARRAY' && $choice->[1] eq $value) {
590 0           $label = $choice->[0];
591             }
592             }
593 0           $w->setOption($label, $value);
594             }
595 0           $w;
596             }
597              
598             sub _string_widget {
599 0     0     my($self, $frame, $opt, %args) = @_;
600 0 0         if (exists $opt->[OPTEXTRA]{'choices'}) {
601 0           $self->_list_widget($frame, $opt);
602             } else {
603             my($e, $ee) = $self->_fix_layout
604             ($frame, "Entry",
605             (defined $opt->[OPTEXTRA]{'length'}
606 0 0         ? (-width => $opt->[OPTEXTRA]{'length'}) : ()),
607             -textvariable => $self->varref($opt));
608 0 0 0       if ($args{-restrict} || defined $opt->[OPTEXTRA]{'maxsize'}) {
609 0     0     my $restrict_int = sub { $_[0] =~ /^([+-]?\d+|)$/ };
  0            
610             my $restrict_float = sub {
611 0     0     $_[0] =~ /^(|([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)$/
612 0           };
613             my $restrict_len = sub {
614 0     0     length $_[0] <= $opt->[OPTEXTRA]{'maxsize'}
615 0           };
616 0           eval {
617             $ee->configure
618             (-validate => "all",
619             -vcmd => sub {
620             ($args{-restrict} ne "=i" || $restrict_int->($_[0]))
621             &&
622             ($args{-restrict} ne "=f" || $restrict_float->($_[0]))
623             &&
624 0 0 0 0     (!defined $opt->[OPTEXTRA]{'maxsize'} || $restrict_len->($_[0]))
      0        
      0        
      0        
625 0           });
626             };
627 0 0         warn $@ if $@;
628             }
629 0           $e;
630             }
631             }
632              
633             sub _dir_select {
634 0     0     my($top, $curr_dir) = @_;
635              
636 0 0         if ($top->can("chooseDirectory")) {
637 0           return $top->chooseDirectory(-initialdir => $curr_dir);
638             }
639              
640 0 0         if (eval { require Tk::DirSelect; Tk::DirSelect->VERSION("1.03"); 1 }) {
  0            
  0            
  0            
641 0           return $top->DirSelect(-directory => $curr_dir)->Show;
642             }
643              
644 0           require Tk::DirTree;
645 0           my $t = $top->Toplevel;
646 0           $t->title("Choose directory:");
647 0           my $ok = 0; # flag: "1" means OK, "-1" means cancelled
648              
649             # Create Frame widget before the DirTree widget, so it's always visible
650             # if the window gets resized.
651 0           my $f = $t->Frame->pack(-fill => "x", -side => "bottom");
652              
653 0           my $d;
654             $d = $t->Scrolled('DirTree',
655             -scrollbars => 'osoe',
656             -width => 35,
657             -height => 20,
658             -selectmode => 'browse',
659             -exportselection => 1,
660 0     0     -browsecmd => sub { $curr_dir = shift;
661 0 0         if ($^O ne 'MSWin32') {
662 0           $curr_dir =~ s|^//|/|; # bugfix
663             }
664             },
665              
666             # With this version of -command a double-click will
667             # select the directory
668 0     0     -command => sub { $ok = 1 },
669              
670             # With this version of -command a double-click will
671             # open a directory. Selection is only possible with
672             # the Ok button.
673             #-command => sub { $d->opencmd($_[0]) },
674 0           )->pack(-fill => "both", -expand => 1);
675             # Set the initial directory
676 0           $d->chdir($curr_dir);
677              
678             $f->Button(-text => 'Ok',
679 0     0     -command => sub { $ok = 1 })->pack(-side => 'left');
  0            
680             $f->Button(-text => 'Cancel',
681 0     0     -command => sub { $ok = -1 })->pack(-side => 'left');
  0            
682 0     0     $t->OnDestroy(sub { $ok = -1 });
  0            
683 0           $f->waitVariable(\$ok);
684 0 0         $t->destroy if Tk::Exists($t);
685 0 0         if ($ok == 1) {
686 0           $curr_dir;
687             } else {
688 0           undef;
689             }
690             }
691              
692             sub _filedialog_widget {
693 0     0     my($self, $frame, $opt, %args) = @_;
694 0 0         my $subtype = (exists $args{'-subtype'} ? $args{'-subtype'} : 'file');
695 0           my $topframe = $frame->Frame;
696 0           my $e;
697 0 0         if (exists $opt->[OPTEXTRA]{'choices'}) {
698 0           require Tk::BrowseEntry;
699 0           $e = $topframe->BrowseEntry(-variable => $self->varref($opt));
700 0           my @optlist = @{$opt->[OPTEXTRA]{'choices'}};
  0            
701 0 0         unshift(@optlist, $opt->[DEFVAL]) if defined $opt->[DEFVAL];
702 0           my $o;
703 0           foreach $o (@optlist) {
704 0           $e->insert("end", $o);
705             }
706             } else {
707 0 0         if (!eval '
708             use Tk::PathEntry;
709             my $real_e;
710             ($e, $real_e) = $self->_fix_layout($topframe, "PathEntry",
711             -textvariable => $self->varref($opt));
712             # XXX Escape is already used for cancelling Tk::Getopt
713             $real_e->bind("<$_>" => sub { $real_e->Finish }) for (qw/Return/);
714             1;
715             ') {
716 0           ($e) = $self->_fix_layout($topframe, "Entry",
717             -textvariable => $self->varref($opt));
718             }
719             }
720 0           $e->pack(-side => 'left');
721              
722             my $b = $topframe->Button
723             (_get_browse_args($topframe),
724             -command => sub {
725 0     0     require File::Basename;
726 0           my($fd, $filedialog);
727 0 0         if ($Tk::VERSION >= 800) {
728 0 0         if ($subtype eq 'dir') {
    0          
    0          
729 0           $fd = '_dir_select';
730             } elsif ($subtype eq 'savefile') {
731 0           $fd = 'getSaveFile';
732             } elsif ($subtype eq 'file') {
733 0           $fd = 'getOpenFile';
734             } else {
735 0           die "Unknown subtype <$subtype>";
736             }
737             } else {
738 0           $fd = 'FileDialog';
739 0           eval {
740 0 0         die "nope" if $subtype eq 'dir';
741 0           require Tk::FileDialog;
742             };
743 0 0         if ($@) {
744 0           require Tk::FileSelect;
745 0           $fd = 'FileSelect';
746             }
747             # XXX set FileDialog options via $opt->[3]{'filedialog_opt'}
748 0 0         if ($fd eq 'FileDialog') {
749 0           $filedialog = $topframe->FileDialog
750             (-Title => 'Select file');
751             } else {
752 0           $filedialog = $topframe->FileSelect;
753             }
754             }
755 0           my($dir, $base, $file);
756 0           my $act_val = $ {$self->varref($opt)};
  0            
757 0 0         if ($act_val) {
758 0           $dir = File::Basename::dirname($act_val);
759 0           $base = File::Basename::basename($act_val);
760 0 0         $dir = '.' if (!-d $dir);
761              
762 0 0         if ($fd =~ /^get(Open|Save)File$/) {
    0          
    0          
763 0           $file = $topframe->$fd(-initialdir => $dir,
764             -initialfile => $base,
765             -title => 'Select file',
766             # XXX erst ab 800.013 (?)
767             # -force => 1,
768             );
769             } elsif ($fd eq '_dir_select') {
770 0           $file = _dir_select($topframe, $dir);
771             } elsif ($fd eq 'FileDialog') {
772 0           $file = $filedialog->Show(-Path => $dir,
773             -File => $base);
774             } else {
775 0 0         if ($subtype eq 'dir') {
776 0           $file = $filedialog->Show(-directory => $dir,
777             -verify => [qw(-d)],
778             );
779             } else {
780 0           $file = $filedialog->Show(-directory => $dir);
781             }
782             }
783             } else {
784 0 0         if ($fd =~ /^get(Open|Save)File$/) {
    0          
785 0           $file = $topframe->$fd(-title => 'Select file');
786             } elsif ($fd eq '_dir_select') {
787 0           require Cwd;
788 0           $file = _dir_select($topframe, Cwd::getcwd());
789             } else {
790 0 0         if ($subtype eq 'dir') {
791 0           $file = $filedialog->Show(-verify => [qw(-d)]);
792             } else {
793 0           $file = $filedialog->Show;
794             }
795             }
796             }
797 0 0 0       if (defined $file && $file ne "") {
798 0           $ {$self->varref($opt)} = $file;
  0            
799             }
800 0           });
801 0           $b->pack(-side => 'left');
802 0           $topframe;
803             }
804              
805             sub _geometry_widget {
806 0     0     my($self, $frame, $opt) = @_;
807             my($topframe, $e) = $self->_fix_layout
808             ($frame,
809             'Entry',
810             (defined $opt->[OPTEXTRA]{'length'}
811 0 0         ? (-width => $opt->[OPTEXTRA]{'length'}) : ()),
812             -textvariable => $self->varref($opt));
813             $topframe->Button(_get_curr_geometry_args($topframe),
814             -command => sub {
815 0     0     my $mw = $frame->MainWindow;
816 0           $e->delete(0, "end");
817 0           $e->insert("end", $mw->geometry);
818             },
819 0           )->pack(-side => "left");
820 0           $topframe;
821             }
822              
823             sub _color_widget {
824 0     0     return shift->_string_widget(@_);
825              
826             # XXX funktioniert leider nicht...
827 0           my($self, $frame, $opt) = @_;
828             my($topframe, $e) = $self->_fix_layout
829             ($frame,
830             'Entry',
831             (defined $opt->[OPTEXTRA]{'length'}
832 0 0         ? (-width => $opt->[OPTEXTRA]{'length'}) : ()),
833             -textvariable => $self->varref($opt));
834 0 0         if ($frame->can("chooseColor")) {
835             $topframe->Button(-text => "...",
836             -padx => 0, -pady => 0,
837             -command => sub {
838 0     0     my $color = $frame->chooseColor;
839             # (-initialcolor => $e->get);
840 0 0         return unless defined $color;
841 0           $e->delete(0, "end");
842 0           $e->insert("end", $color);
843             },
844 0           )->pack(-side => "left");
845             }
846 0           $topframe;
847             }
848              
849             sub _font_widget {
850 0     0     my($self, $frame, $opt) = @_;
851             my($topframe, $e) = $self->_fix_layout
852             ($frame,
853             'Entry',
854             (defined $opt->[OPTEXTRA]{'length'}
855 0 0         ? (-width => $opt->[OPTEXTRA]{'length'}) : ()),
856             -textvariable => $self->varref($opt));
857 0 0         if (eval {require Tk::Font; require Tk::FontDialog; 1}) {
  0            
  0            
  0            
858             $topframe->Button(-text => "...",
859             -padx => 0, -pady => 0,
860             -command => sub {
861 0     0     my $font = $frame->FontDialog
862             (-initfont => $e->get)->Show;
863 0 0         return unless defined $font;
864 0           $e->delete(0, "end");
865 0           $e->insert("end", $font->Pattern);
866             },
867 0           )->pack(-side => "left");
868             }
869 0           $topframe;
870             }
871              
872             # Creates one page of the Notebook widget
873             # Arguments:
874             # $current_page: Frame for drawing
875             # $optnote: Notebook widget
876             # $current_top: title of Notebook page
877             # $optlist: list of options for this Notebook page
878             # $balloon: Balloon widget
879             # $msglist: (optional) list of messages for this Notebook page
880             sub _create_page {
881 0     0     my($self, $current_page,
882             $optnote, $current_top, $optlist,
883             $balloon, $msglist) = @_;
884 0 0         $current_page = $optnote->{$current_top} if !defined $current_page;
885 0           my $opt;
886 0           my $row = -1;
887              
888 0           my $msgobj;
889 0 0 0       if (ref $msglist and
      0        
890             exists $msglist->{$current_top} and
891             $msglist->{$current_top} ne "") {
892 0           $row++;
893 0           $msgobj = $current_page->Label(-text => $msglist->{$current_top},
894             -justify => "left",
895             )->grid(-row => $row, -column => 0,
896             -columnspan => 3);
897             }
898              
899 0           foreach $opt (@{$optlist->{$current_top}}) {
  0            
900 0           my $f = $current_page;
901 0           $row++;
902 0 0         if (_is_separator($opt)) {
903 0           my $separator = $f->Frame(-height => 2,
904             )->grid(-row => $row,
905             -column => 0,
906             -columnspan => 3,
907             -pady => 3,
908             -padx => 3,
909             -sticky => "ew");
910 0           $separator->configure
911             (-fg => $separator->cget(-bg),
912             -bg => $separator->Darken($separator->cget(-bg), 60));
913 0           next;
914             }
915              
916 0           my $label;
917             my $w;
918 0 0         if (exists $opt->[OPTEXTRA]{'label'}) {
919 0           $label = $opt->[OPTEXTRA]{'label'};
920             } else {
921 0           $label = $opt->[OPTNAME];
922 0 0 0       if ($label =~ /^(.*)-(.*)$/ && $1 eq $current_top) {
923 0           $label = $2;
924             }
925             }
926 0           my $lw = $f->Label(-text => $label)->grid(-row => $row, -column => 0,
927             -sticky => 'w');
928 0 0 0       if (exists $opt->[OPTEXTRA]{'widget'}) {
    0 0        
    0 0        
    0 0        
    0 0        
929             # own widget
930 0           $w = &{$opt->[OPTEXTRA]{'widget'}}($self, $f, $opt);
  0            
931             } elsif (defined $opt->[OPTTYPE] &&
932             $opt->[OPTTYPE] eq '!' or $opt->[OPTTYPE] eq '') {
933 0           $w = $self->_boolean_widget($f, $opt); # XXX _checkmark_
934             } elsif (defined $opt->[OPTTYPE] && $opt->[OPTTYPE] =~ /i/) {
935 0           $w = $self->_integer_widget($f, $opt);
936             } elsif (defined $opt->[OPTTYPE] && $opt->[OPTTYPE] =~ /f/) {
937 0           $w = $self->_float_widget($f, $opt);
938             } elsif (defined $opt->[OPTTYPE] && $opt->[OPTTYPE] =~ /s/) {
939             my $subtype = (defined $opt->[OPTEXTRA] &&
940             exists $opt->[OPTEXTRA]{'subtype'} ?
941 0 0 0       $opt->[OPTEXTRA]{'subtype'} : "");
942 0 0 0       if ($subtype eq 'file' ||
    0 0        
    0          
    0          
943             $subtype eq 'savefile' ||
944             $subtype eq 'dir') {
945 0           $w = $self->_filedialog_widget($f, $opt, -subtype => $subtype);
946             } elsif ($subtype eq 'geometry') {
947 0           $w = $self->_geometry_widget($f, $opt);
948             } elsif ($subtype eq 'color') {
949 0           $w = $self->_color_widget($f, $opt);
950             } elsif ($subtype eq 'font') {
951 0           $w = $self->_font_widget($f, $opt);
952             } else {
953 0           $w = $self->_string_widget($f, $opt);
954             }
955             } else {
956 0           warn "Can't generate option editor entry for $opt->[OPTNAME]";
957             }
958 0 0         if (defined $w) {
959 0           $w->grid(-row => $row, -column => 1, -sticky => 'w');
960             }
961 0 0 0       if (exists $opt->[OPTEXTRA]{'help'} && defined $balloon) {
962 0 0         $balloon->attach($w, -msg => $opt->[OPTEXTRA]{'help'})
963             if defined $w;
964 0 0         $balloon->attach($lw, -msg => $opt->[OPTEXTRA]{'help'})
965             if defined $lw;
966             }
967 0 0         if (exists $opt->[OPTEXTRA]{'longhelp'}) {
968             $f->Button(-text => '?',
969             -padx => 1,
970             -pady => 1,
971             -command => sub {
972             my $t = $f->Toplevel
973 0     0     (-title => $self->{_string}{"helpfor"}
974             . " $label");
975 0           $t->Message(-text => $opt->[OPTEXTRA]{'longhelp'},
976             -justify => 'left')->pack;
977             $t->Button(-text => 'OK',
978 0           -command => sub { $t->destroy }
979 0           )->pack;
980 0           $t->Popup(-popover => "cursor");
981 0           })->grid(-row => $row, -column => 2, -sticky => 'w');
982             }
983             }
984 0           $current_page->grid('columnconfigure', 3, -weight => 1);
985 0           $current_page->grid('rowconfigure', ++$row, -weight => 1);
986             }
987              
988             sub _do_undo {
989 0     0     my($self, $undo_options) = @_;
990 0           my $opt;
991 0           foreach $opt ($self->_opt_array) {
992 0 0         next if $opt->[OPTEXTRA]{'nogui'};
993 0 0         if (exists $undo_options->{$opt->[OPTNAME]}) {
994 0           my $ref = ref $self->varref($opt);
995 0 0         if ($ref eq 'ARRAY') {
    0          
    0          
996 0           my @swap = @ {$self->varref($opt)};
  0            
997 0           @ {$self->varref($opt)} = @{ $undo_options->{$opt->[OPTNAME]} };
  0            
  0            
998 0           @{ $undo_options->{$opt->[OPTNAME]}} = @swap;
  0            
999             } elsif ($ref eq 'HASH') {
1000 0           my %swap = % {$self->varref($opt)};
  0            
1001 0           % {$self->varref($opt)} = %{ $undo_options->{$opt->[OPTNAME]} };
  0            
  0            
1002 0           %{ $undo_options->{$opt->[OPTNAME]}} = %swap;
  0            
1003             } elsif ($ref eq 'SCALAR') {
1004 0           my $swap = $ {$self->varref($opt)};
  0            
1005 0           $ {$self->varref($opt)} = $undo_options->{$opt->[OPTNAME]};
  0            
1006 0           $undo_options->{$opt->[OPTNAME]} = $swap;
1007             } else {
1008 0           die "Invalid reference type for option $opt->[OPTNAME]";
1009             }
1010             }
1011             }
1012             }
1013              
1014             sub option_dialog {
1015 0     0 1   my($self, $top, %a) = @_;
1016 0           my $button_pressed;
1017 0           $a{'-buttonpressed'} = \$button_pressed;
1018 0           $a{'-wait'} = 1;
1019 0           $self->option_editor($top, %a);
1020 0           $button_pressed;
1021             }
1022              
1023             sub option_editor {
1024 0     0 1   my($self, $top, %a) = @_;
1025 0           my $callback = delete $a{'-callback'};
1026 0           my $nosave = delete $a{'-nosave'};
1027 0           my $buttons = delete $a{'-buttons'};
1028 0   0       my $toplevel = delete $a{'-toplevel'} || 'Toplevel';
1029 0           my $pack = delete $a{'-pack'};
1030 0           my $transient = delete $a{'-transient'};
1031 0           my $use_statusbar = delete $a{'-statusbar'};
1032 0           my $wait = delete $a{'-wait'};
1033 0   0       my $string = delete $a{'-string'} || {};
1034             my $delay_page_create = (exists $a{'-delaypagecreate'}
1035 0 0         ? delete $a{'-delaypagecreate'}
1036             : 1);
1037 0           my $page = delete $a{'-page'};
1038 0           my $button_pressed;
1039 0 0         if (exists $a{'-buttonpressed'}) {
1040 0 0         if (ref $a{'-buttonpressed'} ne "SCALAR") {
1041 0           die "The value for the -buttonpressed option has to be a SCALAR reference, not a " . ref($a{'-buttonpressed'}) . "\n";
1042             }
1043 0           $button_pressed = delete $a{'-buttonpressed'};
1044             } else {
1045             # dummy
1046 0           $button_pressed = \do { my $dummy };
  0            
1047             }
1048             {
1049 0           my %defaults = ('optedit' => 'Option editor',
  0            
1050             'undo' => 'Undo',
1051             'lastsaved' => 'Last saved',
1052             'save' => 'Save',
1053             'defaults' => 'Defaults',
1054             'ok' => 'OK',
1055             'apply' => 'Apply',
1056             'cancel' => 'Cancel',
1057             'helpfor' => 'Help for:',
1058             'oksave' => 'OK',
1059             );
1060 0           for my $key (keys %defaults) {
1061 0 0         next if exists $string->{$key};
1062 0           $string->{$key} = $defaults{$key};
1063             }
1064             }
1065 0           $self->{_string} = $string;
1066              
1067 0 0         if (defined $page) {
1068 0           $self->{'raised'} = $page;
1069             }
1070              
1071             # store old values for undo
1072 0           my %undo_options;
1073             my $opt;
1074 0           foreach $opt ($self->_opt_array) {
1075 0 0         next if $opt->[OPTEXTRA]{'nogui'};
1076 0           my $ref = ref $self->varref($opt);
1077 0 0         if ($ref eq 'ARRAY') {
    0          
    0          
1078 0           @{ $undo_options{$opt->[OPTNAME]} } = @ {$self->varref($opt)};
  0            
  0            
1079             } elsif ($ref eq 'HASH') {
1080 0           %{ $undo_options{$opt->[OPTNAME]} } = % {$self->varref($opt)};
  0            
  0            
1081             } elsif ($ref eq 'SCALAR') {
1082 0           $undo_options{$opt->[OPTNAME]} = $ {$self->varref($opt)};
  0            
1083             } else {
1084 0           die "Invalid reference type for option $opt->[OPTNAME]";
1085             }
1086             }
1087              
1088 0           require Tk;
1089              
1090 0           my $dont_use_notebook = 1;
1091 0           foreach $opt (@{$self->{'opttable'}}) {
  0            
1092 0 0         if (ref $opt ne 'ARRAY') { # found header
1093 0           undef $dont_use_notebook;
1094 0           last;
1095             }
1096             }
1097 0 0         if (!$dont_use_notebook) {
1098 0           eval { require Tk::NoteBook };
  0            
1099 0 0         $dont_use_notebook = 1 if $@;
1100             }
1101              
1102 0           my $dont_use_balloon;
1103 0           eval { require Tk::Balloon };
  0            
1104 0 0         $dont_use_balloon = 1 if $@;
1105              
1106 0           my $cmd = '$top->' . $toplevel . '(%a)';
1107 0           my $opt_editor = eval $cmd;
1108 0 0         die "$@ while evaling $cmd" if $@;
1109 0 0         $opt_editor->transient($transient) if $transient;
1110 0           eval { $opt_editor->configure(-title => $string->{optedit}) };
  0            
1111              
1112 0 0         my $opt_notebook = ($dont_use_notebook ?
1113             $opt_editor->Frame :
1114             $opt_editor->NoteBook(-ipadx => 6, -ipady => 6));
1115 0           $self->{Frame} = $opt_notebook;
1116              
1117 0           my($statusbar, $balloon);
1118 0 0         if (!$dont_use_balloon) {
1119 0 0         if ($use_statusbar) {
1120 0           $statusbar = $opt_editor->Label;
1121             }
1122 0 0         $balloon = $opt_notebook->Balloon($use_statusbar
1123             ? (-statusbar => $statusbar)
1124             : ());
1125             }
1126              
1127 0           my $optlist = {};
1128 0           my $msglist = {};
1129 0           my $current_top;
1130 0 0         if ($dont_use_notebook) {
1131 0           $current_top = $string->{'optedit'};
1132 0           foreach $opt ($self->_opt_array) {
1133 0           push(@{$optlist->{$current_top}}, $opt)
1134 0 0         if !$opt->[OPTEXTRA]{'nogui'};
1135             }
1136             # XXX message missing
1137 0           $self->_create_page($opt_notebook, undef, $current_top,
1138             $optlist, $balloon);
1139             } else {
1140 0           my @opttable = @{$self->{'opttable'}};
  0            
1141 0 0         unshift(@opttable, $string->{'optedit'})
1142             if ref $opttable[OPTNAME] eq 'ARRAY'; # put head
1143              
1144 0           my $page_create_page;
1145 0           foreach $opt (@opttable) {
1146 0 0 0       if (ref $opt ne 'ARRAY') {
    0          
1147 0 0 0       if (!$delay_page_create && $page_create_page) {
1148 0           $page_create_page->();
1149 0           undef $page_create_page;
1150             }
1151              
1152 0           my $label = $opt;
1153 0           $current_top = lc($label);
1154 0           my $c = $current_top;
1155 0           $optlist->{$c} = [];
1156 0           $msglist->{$c} = "";
1157 0           my $page_f;
1158             $page_create_page = sub {
1159 0     0     $self->_create_page
1160             ($page_f,
1161             $opt_notebook, $c,
1162             $optlist, $balloon, $msglist);
1163 0           };
1164 0 0         $page_f = $opt_notebook->add
1165             ($c,
1166             -label => $label,
1167             -anchor => 'w',
1168             ($delay_page_create?(-createcmd => $page_create_page):()),
1169             );
1170             } elsif ($opt->[OPTNAME] eq '' && !_is_separator($opt)) {
1171 0           $msglist->{$current_top} = $opt->[DEFVAL];
1172             } else {
1173 0           push @{$optlist->{$current_top}}, $opt
1174 0 0         if !$opt->[OPTEXTRA]{'nogui'};
1175             }
1176             }
1177 0 0 0       if (!$delay_page_create && $page_create_page) {
1178 0           $page_create_page->();
1179 0           undef $page_create_page;
1180             }
1181              
1182             }
1183              
1184 0           require Tk::Tiler;
1185 0           my $f;
1186             $f = $opt_editor->Tiler
1187             (-rows => 1,
1188             -columns => 1,
1189             -yscrollcommand => sub {
1190 0     0     my $bw = $f->cget(-highlightthickness);
1191 0 0         return if (!$f->{Sw});
1192 0           my $nenner = int(($f->Width-2*$bw)/$f->{Sw});
1193 0 0         return if (!$nenner);
1194 0           my $rows = @{$f->{Slaves}}/$nenner;
  0            
1195 0 0 0       return if (!$rows or !int($rows));
1196 0 0         if ($rows/int($rows) > 0) {
1197 0           $rows = int($rows)+1;
1198             }
1199             $f->GeometryRequest($f->Width,
1200 0           2*$bw+$rows*$f->{Sh});
1201 0           });
1202             $f->bind('' => sub {
1203 0 0   0     if ($f->y + $f->height > $opt_editor->height) {
1204 0           $opt_editor->geometry($opt_editor->width .
1205             "x" .
1206             ($f->height+$f->y));
1207             }
1208 0           });
1209 0           my @tiler_b;
1210              
1211             my %allowed_button;
1212 0 0         if ($buttons) {
1213 0 0         if (ref $buttons ne 'ARRAY') {
1214 0           undef $buttons;
1215             } else {
1216 0           %allowed_button = map { ($_ => 1) } @$buttons;
  0            
1217             }
1218             }
1219              
1220 0 0 0       if (!$buttons || $allowed_button{'ok'}) {
1221             my $ok_button
1222             = $f->Button(-text => $string->{'ok'},
1223             -underline => 0,
1224             -command => sub {
1225 0     0     $self->process_options(\%undo_options, 1);
1226 0 0         if (!$dont_use_notebook) {
1227 0           $self->{'raised'} = $opt_notebook->raised();
1228             }
1229 0           $opt_editor->destroy;
1230 0           $$button_pressed = 'ok';
1231             }
1232 0           );
1233 0           push @tiler_b, $ok_button;
1234             }
1235              
1236 0 0         if ($allowed_button{'oksave'}) {
1237             my $ok_button
1238             = $f->Button(-text => $string->{'oksave'},
1239             -underline => 0,
1240             -command => sub {
1241 0     0     $top->Busy;
1242 0           eval {
1243 0           $self->save_options;
1244 0           $self->process_options(\%undo_options, 1);
1245 0 0         if (!$dont_use_notebook) {
1246 0           $self->{'raised'} = $opt_notebook->raised();
1247             }
1248             };
1249 0           my $err = $@;
1250 0           $top->Unbusy;
1251 0 0         if ($err) {
1252 0           $self->my_die($err, 'safe');
1253             }
1254 0           $opt_editor->destroy;
1255 0           $$button_pressed = 'ok';
1256             }
1257 0           );
1258 0           push @tiler_b, $ok_button;
1259             }
1260              
1261 0 0 0       if (!$buttons || $allowed_button{'apply'}) {
1262             my $apply_button
1263             = $f->Button(-text => $string->{'apply'},
1264             -command => sub {
1265 0     0     $self->process_options(\%undo_options, 1);
1266             }
1267 0           );
1268 0           push @tiler_b, $apply_button;
1269             }
1270            
1271 0           my $cancel_button;
1272 0 0 0       if (!$buttons || $allowed_button{'cancel'}) {
1273             $cancel_button
1274             = $f->Button(-text => $string->{'cancel'},
1275             -command => sub {
1276 0     0     $self->_do_undo(\%undo_options);
1277 0 0         if (!$dont_use_notebook) {
1278 0           $self->{'raised'} = $opt_notebook->raised();
1279             }
1280 0           $opt_editor->destroy;
1281 0           $$button_pressed = 'cancel';
1282             }
1283 0           );
1284 0           push @tiler_b, $cancel_button;
1285             }
1286              
1287 0 0 0       if (!$buttons || $allowed_button{'undo'}) {
1288             my $undo_button
1289             = $f->Button(-text => $string->{'undo'},
1290             -command => sub {
1291 0     0     $self->_do_undo(\%undo_options);
1292             }
1293 0           );
1294 0           push @tiler_b, $undo_button;
1295             }
1296              
1297 0 0         if ($self->{'filename'}) {
1298 0 0 0       if (!$buttons || $allowed_button{'lastsaved'}) {
1299             my $lastsaved_button
1300             = $f->Button(-text => $string->{'lastsaved'},
1301             -command => sub {
1302 0     0     $top->Busy;
1303 0           $self->load_options;
1304 0           $top->Unbusy;
1305             }
1306 0           );
1307 0           push @tiler_b, $lastsaved_button;
1308             }
1309              
1310 0 0 0       if (!$nosave && (!$buttons || $allowed_button{'save'})) {
      0        
1311 0           my $save_button;
1312             $save_button
1313             = $f->Button(-text => $string->{'save'},
1314             -command => sub {
1315 0     0     $top->Busy;
1316 0           eval { $self->save_options };
  0            
1317 0 0         if ($@ =~ /No Data::Dumper/) {
1318 0           $save_button->configure(-state => 'disabled');
1319             }
1320 0           $top->Unbusy;
1321             }
1322 0           );
1323 0           push @tiler_b, $save_button;
1324             }
1325             }
1326              
1327 0 0 0       if (!$buttons || $allowed_button{'defaults'}) {
1328             my $def_button
1329             = $f->Button(-text => $string->{'defaults'},
1330             -command => sub {
1331 0     0     $self->set_defaults;
1332             }
1333 0           );
1334 0           push @tiler_b, $def_button;
1335             }
1336              
1337 0           $f->Manage(@tiler_b);
1338              
1339 0 0         &$callback($self, $opt_editor) if $callback;
1340              
1341 0 0 0       if (!$dont_use_notebook && defined $self->{'raised'}) {
1342 0           $self->raise_page($self->{'raised'});
1343             }
1344              
1345 0     0     $opt_editor->bind('' => sub { $cancel_button->invoke });
  0            
1346              
1347 0           $f->pack(-fill => 'x', -side => "bottom");
1348 0           $opt_notebook->pack(-expand => 1, -fill => 'both');
1349 0 0         if (defined $statusbar) {
1350 0           $statusbar->pack(-fill => 'x', -anchor => 'w');
1351             }
1352              
1353 0 0         if ($opt_editor->can('Popup')) {
1354 0           $opt_editor->withdraw;
1355 0           $opt_editor->Popup;
1356             }
1357 0 0         if ($wait) {
1358 0 0         if ($pack) {
1359 0           $opt_editor->pack(@$pack);
1360             }
1361 0           my $wait_var = 1;
1362 0     0     $opt_editor->OnDestroy(sub { undef $wait_var });
  0            
1363 0 0         $opt_editor->waitVisibility unless $opt_editor->ismapped;
1364 0           $opt_editor->grab;
1365 0           $opt_editor->waitVariable(\$wait_var);
1366             }
1367              
1368 0           $opt_editor;
1369             }
1370              
1371             sub _create_checkmarks {
1372 0     0     my $w = shift;
1373              
1374 0 0         $CHECKMARK_ON = $w->Photo(-data => <
1375             R0lGODdhDgAOAIAAAP///1FR+ywAAAAADgAOAAACM4SPFplGIXy0yDQK4aNFZAIlhI8QEQkUACKC
1376             4EMERFAI3yg+wb+ICEQjMeGjRWQahfCxAAA7
1377             EOF
1378             unless $CHECKMARK_ON;
1379 0 0         $CHECKMARK_OFF = $w->Photo(-data => <
1380             R0lGODdhDgAOAIAAAAAAAP///ywAAAAADgAOAAACDYyPqcvtD6OctNqrSAEAOw==
1381             EOF
1382             unless $CHECKMARK_OFF;
1383             }
1384              
1385             sub _get_browse_args {
1386 0     0     my $w = shift;
1387 0 0         if (!defined $FILE_IMAGE) {
1388 0           require Tk::Pixmap;
1389 0           $FILE_IMAGE = $w->Pixmap(-file => Tk->findINC("openfolder.xpm"));
1390 0 0         $FILE_IMAGE = 0 if (!$FILE_IMAGE);
1391             }
1392 0 0         if ($FILE_IMAGE) {
1393 0           (-image => $FILE_IMAGE);
1394             } else {
1395 0           (-text => "Browse...");
1396             }
1397             }
1398              
1399             sub _get_curr_geometry_args {
1400 0     0     my $w = shift;
1401 0 0         if (!defined $CURR_GEOMETRY_IMAGE) {
1402 0           require Tk::Photo;
1403 0           $CURR_GEOMETRY_IMAGE = $w->Photo(-file => Tk->findINC("win.xbm"));
1404 0 0         $CURR_GEOMETRY_IMAGE = 0 if (!$CURR_GEOMETRY_IMAGE);
1405             }
1406 0 0         if ($CURR_GEOMETRY_IMAGE) {
1407 0           (-image => $CURR_GEOMETRY_IMAGE);
1408             } else {
1409 0           (-text => "Geom.");
1410             }
1411             }
1412              
1413             sub raise_page {
1414 0     0 0   my($self, $page) = @_;
1415 0           my $opt_notebook = $self->{Frame};
1416 0           $page = lc $page; # always lowercase in NoteBook internals
1417 0           $opt_notebook->raise($page);
1418             }
1419              
1420             1;
1421              
1422             __END__