File Coverage

lib/Wx/WidgetMaker.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Wx::WidgetMaker;
2              
3             require 5.006;
4              
5             our $VERSION = '0.11';
6              
7              
8 1     1   25569 use strict;
  1         3  
  1         39  
9 1     1   6 use warnings;
  1         1  
  1         35  
10 1     1   5 use Carp qw(carp confess);
  1         13  
  1         585  
11              
12 1     1   466 use Wx qw(:everything);
  0            
  0            
13              
14             use fields qw(_parent);
15              
16             # Some constants for consistency's sake
17             use constant wxDefaultID => -1;
18             use constant wxDefaultWidth => -1;
19             use constant wxDefaultHeight => -1;
20             use constant wxDefaultX => -1;
21             use constant wxDefaultY => -1;
22             use constant wxDefaultStyle => 0;
23             # Pointsize corresponding to h1, h2, etc.
24             use constant POINTSIZE => {
25             1 => 20, 2 => 16, 3 => 12, 4 => 10, 5 => 9, 6 => 8,
26             };
27             # Prefix added to some widget names so that params can avoid them
28             use constant MAGICPREFIX => '!!!#!!##!###';
29              
30              
31             sub new {
32             my $class = shift;
33             my ($parent) = _rearrange([qw(PARENT)], @_);
34              
35             _require_param_type(\$parent, 'Wx::Window', '-parent');
36              
37             my $self = bless {}, $class;
38             $self->{'_parent'} = $parent;
39              
40             return $self;
41             }
42              
43              
44             sub h1 {
45             my ($self, $text) = @_;
46             return $self->_h($text, 1);
47             }
48             sub h2 {
49             my ($self, $text) = @_;
50             return $self->_h($text, 2);
51             }
52             sub h3 {
53             my ($self, $text) = @_;
54             return $self->_h($text, 3);
55             }
56             sub h4 {
57             my ($self, $text) = @_;
58             return $self->_h($text, 4);
59             }
60             sub h5 {
61             my ($self, $text) = @_;
62             return $self->_h($text, 5);
63             }
64             sub h6 {
65             my ($self, $text) = @_;
66             return $self->_h($text, 6);
67             }
68              
69              
70             sub textfield {
71             my $self = shift;
72             my ($name, $default, $width, $maxlength, $id) =
73             _rearrange(['NAME', [qw(DEFAULT VALUE)], 'SIZE', 'MAXLENGTH', 'ID'], @_);
74             my ($textfield, $size);
75              
76             _require_param(\$name, '-name');
77             _init_param(\$default, '');
78             _init_param(\$id, wxDefaultID);
79              
80             if (defined $width) {
81             if ($width =~ /^\d+$/) {
82             $size = wxSIZE($width, wxDefaultHeight);
83             } else {
84             carp '-size argument ignored (not a whole number)';
85             }
86             }
87             _init_param(\$size, wxDefaultSize);
88              
89             # XXX: maxlength not implemented yet, need to set a validator
90              
91             $textfield = Wx::TextCtrl->new(
92             $self->{'_parent'}, $id, $default,
93             wxDefaultPosition, $size,
94             wxNO_BORDER, wxDefaultValidator, $name
95             );
96              
97             return $textfield;
98             }
99              
100              
101             sub password_field {
102             my $self = shift;
103             my ($name, $default, $width, $maxlength, $id) =
104             _rearrange(['NAME', [qw(DEFAULT VALUE)], 'SIZE', 'MAXLENGTH', 'ID'], @_);
105             my ($password_field, $size);
106              
107             _require_param(\$name, '-name');
108             _init_param(\$default, '');
109             _init_param(\$id, wxDefaultID);
110              
111             if (defined $width) {
112             if ($width =~ /^\d+$/) {
113             $size = wxSIZE($width, wxDefaultHeight);
114             } else {
115             carp '-size argument ignored (not a whole number)';
116             }
117             }
118             _init_param(\$size, wxDefaultSize);
119              
120             # XXX: maxlength not implemented yet, need to set a validator
121              
122             $password_field = Wx::TextCtrl->new(
123             $self->{'_parent'}, $id, $default,
124             wxDefaultPosition, $size,
125             wxTE_PASSWORD, wxDefaultValidator, $name
126             );
127              
128             return $password_field;
129             }
130              
131              
132             sub textarea {
133             my $self = shift;
134             my ($name, $default, $rows, $columns, $id) =
135             _rearrange(['NAME',[qw(DEFAULT VALUE)],'ROWS',[qw(COLS COLUMNS)], 'ID'], @_);
136             my ($textarea, $size);
137              
138             _require_param(\$name, '-name');
139             _init_param(\$default, '');
140             _init_param(\$id, wxDefaultID);
141              
142             if (defined $rows && defined $columns) {
143             unless ($rows =~ /^\d+$/) {
144             carp '-rows argument ignored (not a whole number)';
145             }
146             unless ($columns =~ /^\d+$/) {
147             carp '-columns argument ignored (not a whole number)';
148             }
149             if ($rows =~ /^\d+$/ && $columns =~ /^\d+$/) {
150             $size = wxSIZE($columns, $rows);
151             }
152             }
153             _init_param(\$size, wxSIZE(100, 50));
154              
155             $textarea = Wx::TextCtrl->new(
156             $self->{'_parent'}, $id, $default,
157             wxDefaultPosition, $size,
158             wxTE_MULTILINE, wxDefaultValidator, $name
159             );
160              
161             return $textarea;
162             }
163              
164              
165             sub popup_menu {
166             my $self = shift;
167             my ($name, $values, $default, $labels, $id) =
168             _rearrange(
169             ['NAME', [qw(VALUES VALUE)], [qw(DEFAULT DEFAULTS)], 'LABELS', 'ID'],
170             @_
171             );
172             my ($popup_menu);
173              
174             _require_param(\$name, '-name');
175             _require_param_type(\$values, 'ARRAY', '-values');
176             _init_param(\$default, $values->[0]);
177             _init_param(\$id, wxDefaultID);
178              
179             $popup_menu = Wx::Choice->new(
180             $self->{'_parent'}, $id, wxDefaultPosition, wxDefaultSize,
181             _make_labels($values, $labels),
182             wxDefaultStyle, wxDefaultValidator, $name
183             );
184             for (my $i = 0; $i < @$values; $i++) {
185             $popup_menu->SetClientData($i, $values->[$i]);
186             }
187             $popup_menu->SetStringSelection($default);
188              
189             return $popup_menu;
190             }
191              
192              
193             sub scrolling_list {
194             my $self = shift;
195             my ($name, $values, $default, $height, $multiple, $labels, $id) =
196             _rearrange(
197             ['NAME', [qw(VALUE VALUES)], [qw(DEFAULT DEFAULTS)],
198             'SIZE', 'MULTIPLE', 'LABELS', 'ID'],
199             @_
200             );
201             my ($scrolling_list, @labels, $style, $size);
202              
203             _require_param(\$name, '-name');
204             _require_param_type(\$values, 'ARRAY', '-values');
205             _init_param(\$default, $values->[0]);
206             _init_param(\$id, wxDefaultID);
207              
208             if (defined $height) {
209             if ($height =~ /^\d+$/) {
210             $size = wxSIZE(wxDefaultWidth, $height);
211             } else {
212             carp '-size argument ignored (not a whole number)';
213             }
214             }
215             _init_param(\$size, wxSIZE(wxDefaultWidth, 50));
216              
217             if (defined $multiple && $multiple) {
218             $style = wxLB_EXTENDED|wxLB_MULTIPLE;
219             } else {
220             $style = wxDefaultStyle;
221             }
222              
223             $scrolling_list = Wx::ListBox->new(
224             $self->{'_parent'}, $id, wxDefaultPosition, $size,
225             _make_labels($values, $labels),
226             wxCB_READONLY|$style, wxDefaultValidator, $name
227             );
228              
229             for (my $i = 0; $i < @$values; $i++) {
230             $scrolling_list->SetClientData($i, $values->[$i]);
231             }
232             $scrolling_list->SetStringSelection($default);
233              
234             return $scrolling_list;
235             }
236              
237              
238             sub checkbox_group {
239             my $self = shift;
240             my ($name, $values, $defaults, $linebreak, $labels, $rows, $columns,
241             $rowheaders, $colheaders, $nolabels) =
242             _rearrange(
243             ['NAME', [qw(VALUE VALUES)], [qw(DEFAULT DEFAULTS)],
244             'LINEBREAK', 'LABELS', 'ROWS', [qw(COLUMNS COLS)],
245             'ROWHEADERS', 'COLHEADERS', 'NOLABELS'],
246             @_
247             );
248              
249             confess 'method not yet implemented';
250             }
251              
252              
253             sub checkbox {
254             my $self = shift;
255             my ($name, $checked, $value, $label, $id) =
256             _rearrange(['NAME', [qw(CHECKED SELECTED ON)], 'VALUE', 'LABEL', 'ID'], @_);
257             my ($checkbox);
258              
259             _require_param(\$name, '-name');
260             _init_param(\$checked, 0);
261             _init_param(\$label, $name);
262             _init_param(\$id, wxDefaultID);
263              
264             $checkbox = Wx::CheckBox->new(
265             $self->{'_parent'}, $id, $label,
266             wxDefaultPosition, wxDefaultSize,
267             wxDefaultStyle, wxDefaultValidator, $name
268             );
269              
270             return $checkbox;
271             }
272              
273              
274             sub radio_group {
275             my $self = shift;
276             my ($name, $values, $default, $linebreak, $labels,
277             $rows, $columns, $rowheaders, $colheaders, $nolabels,
278             $caption, $id) =
279             _rearrange(
280             ['NAME', [qw(VALUES VALUE)], 'DEFAULT', 'LINEBREAK',
281             'LABELS', 'ROWS', [qw(COLUMNS COLS)],
282             'ROWHEADERS', 'COLHEADERS', 'NOLABELS', 'CAPTION', 'ID'],
283             @_
284             );
285             my ($radio_group, $style, @labels, $major_dimension);
286              
287             _require_param(\$name, '-name');
288             _require_param_type(\$values, 'ARRAY', '-values');
289             _init_param(\$default, $values->[0]);
290             _init_param(\$id, wxDefaultID);
291              
292             if (defined $nolabels && $nolabels) {
293             @labels = map {''} @labels;
294             } else {
295             @labels = @{ _make_labels($values, $labels) };
296             }
297              
298             _init_param(\$caption, '');
299              
300             _init_param(\$rows, 1);
301             _init_param(\$columns, 1);
302             if (defined $linebreak && $linebreak) {
303             $style = wxRA_SPECIFY_COLS;
304             $major_dimension = $columns;
305             } else {
306             $style = wxRA_SPECIFY_ROWS;
307             $major_dimension = $rows;
308             }
309              
310             $radio_group = Wx::RadioBox->new(
311             $self->{'_parent'}, $id, $caption,
312             wxDefaultPosition, wxDefaultSize,
313             \@labels, $major_dimension,
314             $style, wxDefaultValidator, $name
315             );
316              
317             return $radio_group;
318             }
319              
320              
321             sub submit {
322             my $self = shift;
323             my ($name, $value, $id) = _rearrange(['NAME', [qw(VALUE LABEL)], 'ID'], @_);
324             my ($button);
325              
326             _require_param(\$name, '-name');
327             _init_param(\$value, 'Submit');
328             _init_param(\$id, wxDefaultID);
329              
330             $button = Wx::Button->new(
331             $self->{'_parent'}, $id, $value,
332             wxDefaultPosition, wxDefaultSize, wxDefaultStyle,
333             wxDefaultValidator, $name
334             );
335              
336             return $button;
337             }
338              
339              
340             sub image_button {
341             my $self = shift;
342             my ($name, $src, $id) = _rearrange([qw(NAME SRC ID)], @_);
343             my ($button, $bitmap);
344              
345             _require_param(\$name, '-name');
346             _require_param(\$src, '-src');
347             _init_param(\$id, wxDefaultID);
348              
349             $bitmap = _bitmap($src);
350             $button = Wx::BitmapButton->new(
351             $self->{'_parent'}, $id, $bitmap,
352             wxDefaultPosition, wxDefaultSize, wxDefaultStyle,
353             wxDefaultValidator, $name
354             );
355              
356             return $button;
357             }
358              
359              
360             sub print {
361             my $self = shift;
362             my ($add, $sizer, $option, $flag, $border) =
363             _rearrange([qw(ADD SIZER OPTION FLAG BORDER)], @_);
364              
365             _init_param(\$add, '');
366             _init_param(\$option, 0);
367             _init_param(\$flag, 0);
368             _init_param(\$border, 0);
369              
370             if (defined $sizer) {
371             _require_param_type(\$sizer, 'Wx::Sizer', '-sizer');
372              
373             if (ref($add) eq 'ARRAY') {
374             foreach my $control (@$add) {
375             _require_param_type(\$control, ['Wx::Control', 'Wx::Sizer']);
376             $sizer->Add($control, $option, $flag, $border);
377             }
378             } else {
379             _require_param_type(\$add, ['Wx::Control', 'Wx::Sizer']);
380             $sizer->Add($add, $option, $flag, $border);
381             }
382              
383             return; # should be void context anyway
384             } else {
385             my $name = MAGICPREFIX . 'print';
386             return Wx::StaticText->new(
387             $self->{'_parent'}, wxDefaultID, $add,
388             wxDefaultPosition, wxDefaultSize, wxDefaultStyle,
389             $name
390             );
391             }
392             }
393              
394              
395             sub param {
396             my $self = shift;
397             my ($name) = _rearrange([qw(NAME)], @_);
398             my (@children);
399              
400             _init_param(\$name);
401              
402             @children = $self->{'_parent'}->GetChildren();
403              
404             if (defined $name) {
405             foreach my $child (@children) {
406             if ($child->GetName() eq $name) {
407             my $data = $self->_get_form_data($child);
408              
409             if (ref($data) eq 'ARRAY') {
410             if (wantarray) {
411             return @$data;
412             } else {
413             return $data->[0];
414             }
415             } else {
416             return $data;
417             }
418             }
419             }
420             } else {
421             my $prefix = MAGICPREFIX;
422             return grep {!/^$prefix/} map {$_->GetName()} @children;
423             }
424             }
425              
426              
427             ### PRIVATE ###
428              
429             sub _get_form_data {
430             my ($self, $child) = @_;
431             my ($value);
432              
433             local *isa = \&UNIVERSAL::isa;
434             undef $value;
435              
436             # XXX: should check if $child->Get* doesn't return a value
437             if (isa($child, 'Wx::TextCtrl')) {
438             $value = $child->GetValue();
439             } elsif (isa($child, 'Wx::Choice')) {
440             $value = $child->GetClientData($child->GetSelection());
441             } elsif (isa($child, 'Wx::ListBox')) {
442             $value = [ $child->GetClientData($child->GetSelections()) ];
443             } elsif (isa($child, 'Wx::RadioBox')) {
444             $value = $child->GetStringSelection();
445             } elsif (isa($child, 'Wx::CheckBox')) {
446             $value = $child->GetValue() || '';
447             } elsif (isa($child, 'Wx::Button') || isa($child, 'Wx::BitmapButton')) {
448             $value = $child->GetLabel();
449             }
450              
451             return $value;
452             }
453              
454             sub _page_width {
455             my $self = shift;
456             return $self->{'_parent'}->GetSize()->GetWidth();
457             }
458              
459             # Does h1, h2, ..., h6
460             sub _h {
461             my $self = shift;
462             my ($text, $num) = @_;
463             my ($textctrl, $font, $name);
464              
465             $name = MAGICPREFIX . "h$num";
466             $textctrl = Wx::StaticText->new(
467             $self->{'_parent'}, wxDefaultID, $text,
468             wxDefaultPosition, wxSIZE($self->_page_width(), wxDefaultHeight),
469             wxALIGN_LEFT, $name
470             );
471              
472             $font = Wx::Font->new(POINTSIZE->{$num}, wxDEFAULT, wxNORMAL, wxBOLD);
473             $textctrl->SetFont($font);
474              
475             return $textctrl;
476             }
477              
478              
479             # Note that the following are not class or object methods.
480             # There is no $self argument.
481              
482              
483             # Verify that the variable pointed to by reference $ptr
484             # is defined (i.e. was given as an argument in
485             # the calling subroutine. If $$ptr is undef,
486             # set $$ptr to $default (or undef if $default isn't
487             # supplied).
488             sub _init_param {
489             my ($ptr, $default) = @_;
490             undef $default unless defined $default;
491             $$ptr = $default unless defined $$ptr;
492             }
493              
494             # Verify that the variable pointed to by reference $ptr is
495             # an object of class $class. If $$ptr is undef or not an
496             # object of $class and $default is supplied, set $$ptr
497             # to $default (or undef if $default isn't supplied).
498             # Optional $arg is used for named parameters and only
499             # in the warning message to help with debugging.
500             sub _init_param_class {
501             my ($ptr, $default, $class, $arg) = @_;
502             undef $default unless defined $default;
503             _init_param(\$arg, '');
504              
505             if (defined $$ptr) {
506             unless (UNIVERSAL::isa($$ptr, $class)) {
507             carp "argument $arg ignored (not a $class)";
508             undef $$ptr;
509             }
510             }
511             _init_param($ptr, $default);
512             }
513              
514             # Require $$ptr to have been given as an argument
515             sub _require_param {
516             my ($ptr, $arg) = @_;
517             _init_param(\$arg, '');
518              
519             confess "$arg argument missing" unless defined $$ptr;
520             }
521              
522             # Require $$ptr to have been given and to be of type $types.
523             # $types can be an arrayref, in which case $$ptr has to
524             # be one of the types in the array.
525             sub _require_param_type {
526             my ($ptr, $types, $arg) = @_;
527             _init_param(\$arg, '');
528              
529             $types = [$types] unless ref($types) eq 'ARRAY';
530              
531             if (defined $$ptr) {
532             foreach my $type (@$types) {
533             return if UNIVERSAL::isa($$ptr, $type);
534             }
535             }
536              
537             confess "$arg argument invalid (not a '", join(" or '", @$types), "')";
538             }
539              
540             sub _no_op {
541             # No operation, in case you use something
542             # like `start_form' out of habit.
543             }
544              
545             # Convenience function for making labels from values
546             # (for things like popup_menu, radio_group, ...)
547             sub _make_labels {
548             my ($values, $labels) = @_;
549             my (@labels);
550              
551             foreach my $value (@$values) {
552             $labels->{$value} = $value unless defined $labels->{$value};
553             }
554             for (my $i = 0; $i < @$values; $i++) {
555             push @labels, $labels->{$values->[$i]};
556             }
557              
558             return \@labels;
559             }
560              
561             # This is CGI::Util::rearrange, swiped almost directly from CGI.pm.
562             # If the first parameter begins with '-', it rearranges the
563             # parameters so that you can use named parameters; otherwise,
564             # you pass the parameters in order.
565             # I didn't want to load CGI::Util just for this function,
566             # plus I removed the part where leftover arguments are
567             # made into HTML attributes.
568             sub _rearrange {
569             my ($order, @param) = @_;
570             my ($i, %pos, @result);
571              
572             return () unless @param;
573              
574             if (ref($param[0]) eq 'HASH') {
575             @param = %{ $param[0] };
576             } else {
577             return @param
578             unless defined($param[0]) && substr($param[0],0,1) eq '-';
579             }
580              
581             # map parameters into positional indices
582             $i = 0;
583             foreach (@$order) {
584             foreach (ref($_) eq 'ARRAY' ? @$_ : $_) { $pos{lc($_)} = $i; }
585             $i++;
586             }
587              
588             $#result = $#$order; # preextend
589             while (@param) {
590             my $key = lc(shift(@param));
591             $key =~ s/^\-//;
592             if (exists $pos{$key}) {
593             $result[$pos{$key}] = shift(@param);
594             }
595             }
596              
597             return @result;
598             }
599              
600             # Make a Bitmap from a filename
601             sub _bitmap {
602             my $filename = shift;
603             my ($type);
604              
605             carp "bitmap file not found" unless -r $filename;
606              
607             if ($filename =~ /\.bmp$/i) {
608             $type = wxBITMAP_TYPE_BMP;
609             } elsif ($filename =~ /\.gif$/i) {
610             $type = wxBITMAP_TYPE_GIF;
611             } elsif ($filename =~ /\.xbm$/i) {
612             $type = wxBITMAP_TYPE_XBM;
613             } elsif ($filename =~ /\.xpm$/i) {
614             $type = wxBITMAP_TYPE_XPM;
615             } elsif ($filename =~ /\.jpg$/i || $filename =~ /\.jpeg$/i) {
616             $type = wxBITMAP_TYPE_JPEG;
617             } elsif ($filename =~ /\.png$/i) {
618             $type = wxBITMAP_TYPE_PNG;
619             } elsif ($filename =~ /\.pcx$/i) {
620             $type = wxBITMAP_TYPE_PCX;
621             } elsif ($filename =~ /\.pnm$/i) {
622             $type = wxBITMAP_TYPE_PNM;
623             } elsif ($filename =~ /\.tif$/i || $filename =~ /\.tiff$/i) {
624             $type = wxBITMAP_TYPE_TIF;
625             } else {
626             undef $type; # well, we tried
627             }
628              
629             return Wx::Bitmap->new($filename, $type);
630             }
631              
632              
633              
634             1;
635              
636             __END__