File Coverage

blib/lib/Win32/GUI/XMLBuilder.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             ###############################################################################
2             #
3             # Win32::GUI::XMLBuilder
4             #
5             # 14 Dec 2003 by Blair Sutton
6             #
7             # Version: 0.39 (25th January 2007)
8             #
9             # Copyright (c) 2003-2007 Blair Sutton. All rights reserved.
10             # This program is free software; you can redistribute it and/or
11             # modify it under the same terms as Perl itself.
12             #
13             ###############################################################################
14            
15             package Win32::GUI::XMLBuilder;
16            
17 1     1   24820 use strict;
  1         2  
  1         122  
18             require Exporter;
19             our $VERSION = 0.39;
20             our @ISA = qw(Exporter);
21            
22             our $AUTHOR = "Blair Sutton - 2007 - Win32::GUI::XMLBuilder - $VERSION";
23            
24 1     1   1799 use XML::Twig;
  0            
  0            
25             use Win32::GUI qw(WS_CAPTION WS_SIZEBOX WS_EX_CONTROLPARENT WS_CHILD DS_CONTROL WS_VISIBLE WS_VSCROLL WS_TABSTOP);
26            
27             use Win32::GUI::BitmapInline ();
28             our $ICON = newIcon Win32::GUI::BitmapInline( q(
29             AAABAAEAICAAAAEAGACoDAAAFgAAACgAAAAgAAAAQAAAAAEAGAAAAAAAAAAAAEgAAABIAAAAAAAA
30             AAAAAAD/////////////////////////////////////////////////////////////////////
31             ////////////////////////////////////////////////////////////////////////////
32             ///////////////////////////////////////cyfzJrfvcyfzcyfzJrfv////////////cyfzJ
33             rfvcyfz////////////JrfvJrfvcyfz/////////////////////////////////////////////
34             //////////+UW/ZeCfJeCfJeCfJwJfNeCfL////t5P6CQPVeCfJeCfJeCfJwJfPt5P7///9eCfJe
35             CfKUW/b////////////////////////////////////////////////////t5P5eCfJeCfKCQPVw
36             JfNeCfJeCfL///+md/heCfJeCfKUW/ZeCfJeCfKmd/j///9eCfJeCfKUW/b/////////////////
37             ///////////////////////////////////JrfteCfJeCfLJrfuUW/ZeCfJeCfL///+UW/ZeCfJe
38             CfL///9eCfJeCfKUW/b///9eCfJeCfKUW/b/////////////////////////////////////////
39             ///////////JrfteCfJeCfLJrfuCQPVeCfJeCfL///+UW/ZeCfJeCfL///9eCfJeCfKUW/b///9e
40             CfJeCfKUW/b////////////////////////////////////////////////////JrfteCfJeCfLJ
41             rfteCfJeCfJeCfL///+UW/ZeCfJeCfL///9eCfJeCfKUW/b///9eCfJeCfKUW/b/////////////
42             ///////////////////////////////////////JrfteCfJeCfLJrfvJrfvJrfvJrfv///+UW/Ze
43             CfJeCfL///9eCfJeCfKUW/b///9eCfJeCfKUW/b/////////////////////////////////////
44             ///////////////JrfteCfJeCfLJrfu4kvmUW/aUW/b///+UW/ZeCfJeCfL///9eCfJeCfKUW/b/
45             //9eCfJeCfKUW/b////////////////////////////////////////////////////JrfteCfJe
46             CfLJrfuUW/ZeCfJeCfL///+UW/ZeCfJeCfL///9eCfJeCfKUW/b///9eCfJeCfKUW/b/////////
47             ///////////////////////////////////////////JrfteCfJeCfKmd/iCQPVeCfJwJfP///+U
48             W/ZeCfJeCfL///9eCfJeCfKUW/b///9eCfJeCfKUW/b/////////////////////////////////
49             //////////////////////9wJfNeCfJeCfJeCfJeCfLJrfv///+UW/ZeCfJeCfL///9eCfJeCfKU
50             W/b///9eCfJeCfKUW/b/////////////////////////////////////////////////////////
51             //+4kvmUW/aUW/bcyfz///////+4kvmUW/aUW/b///+UW/aUW/a4kvn///+UW/aUW/a4kvn/////
52             ///////////HyceOko7///////////+Oko7Hycf///////+Oko7///+Oko7///////+Oko7////H
53             ycdVW1Vyd3Lj5OPHycdVW1VVW1VVW1WOko7///////////////////////////9yd3I5QDn/////
54             //////85QDlyd3L///////9VW1X///9VW1X///////9VW1XHycdVW1X////j5ONVW1X///9yd3Lj
55             5OP///////////////////////////////////85QDmOko6qrar///+qraqOko5VW1X///////9V
56             W1X///9VW1X///////9VW1X///////////////9VW1X///////9yd3Lj5OP/////////////////
57             ///////////j5ONVW1Xj5ONVW1X///9yd3Lj5ONVW1Xj5OP///9VW1X///9VW1X///////9VW1X/
58             ///////HyceOko5VW1X///////////9yd3Lj5OP///////////////////////+qraqqrar///9V
59             W1X///9VW1X///+qraqqrar///9VW1X///8ACQCOko6Oko5yd3L////////Hycdyd3Kqrar/////
60             ///////j5ONyd3L///////////////////////9VW1X///////+Oko6Oko6Oko7///////9VW1X/
61             ///Hycf////HycfHyceqrar////Hyceqrar///////9VW1XHycfHycf////j5ONVW1X/////////
62             //////////////9VW1X////////j5OMdJR3j5OP///////9VW1X///+Oko7/////////////////
63             //////9VW1VVW1U5QDnj5OP///9VW1VVW1U5QDnj5OP////////////////////6WmT+5Ob/////
64             ///////////////7dn/8rLL////6WmT9ycz////////6WmT7kZj////////6WmT9ycz////9ycz6
65             WmT6WmT6WmT6WmT6WmT6WmT////////////////6WmT4IzH+5Ob////////////7kZj3Bxf+5Ob/
66             ///5Pkv6WmT////////3Bxf3Bxf+5Ob////6WmT6WmT////+5Ob3Bxf5Pkv6WmT6WmT6WmT6WmT9
67             ycz////////////////6WmT4IzH+5Ob////+5Ob4IzH7kZj////////7dn/5Pkv////////3Bxf3
68             Bxf7dn/////7dn/5Pkv////////3Bxf7kZj////////////////////////////////////////6
69             WmT4IzH+5Ob7dn/4IzH////////////8rLL3Bxf////8rLL3Bxf8rLL3Bxf+5Ob8rLL3Bxf/////
70             ///6WmT6WmT////////////////////////////////////////////6WmT4IzH3Bxf8rLL/////
71             ///////+5Ob3Bxf9ycz8rLL3Bxf////4IzH7dn/+5Ob3Bxf9ycz////7dn/4IzH/////////////
72             ///////////////////////////////////4IzH3Bxf+5Ob////////////////4IzH7kZj8rLL3
73             Bxf////7kZj3Bxf+5Ob4IzH7kZj////8rLL3Bxf/////////////////////////////////////
74             ///////8rLL3Bxf6WmT4IzH+5Ob////////////6WmT6WmT7dn/5Pkv////////4IzH7dn/6WmT6
75             WmT////+5Ob3Bxf8rLL////////////////////////////////////////4IzH7dn/////6WmT4
76             IzH+5Ob////////7kZj4IzH6WmT6WmT////////7kZj3Bxf7dn/4IzH////////3Bxf7kZj/////
77             ///////////////////////////////7kZj4IzH+5Ob////////6WmT4IzH+5Ob////8rLL3Bxf5
78             Pkv6WmT////////////4IzH5Pkv3Bxf////////6WmT6WmT/////////////////////////////
79             ///+5Ob3Bxf7kZj////////////////6WmT4IzH+5Ob////3Bxf3Bxf7kZj////////////7kZj3
80             Bxf3Bxf8rLL////7dn/4IzH////////////////////////////////+5Ob8rLL/////////////
81             ///////////8rLL9ycz////8rLL8rLL+5Ob////////////////8rLL8rLL+5Ob////+5Ob8rLL/
82             ////////////////////////////////////////////////////////////////////////////
83             //////////////////////////////////////////////////////////////////////////8A
84             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
85             AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
86             AAAAAAAAAAAAAAAAAA==
87             ) );
88            
89             =head1 NAME
90            
91             XMLBuilder - Build Win32::GUIs using XML.
92            
93             =head1 SYNOPSIS
94            
95             use Win32::GUI::XMLBuilder;
96            
97             my $gui = Win32::GUI::XMLBuilder->new({file=>"file.xml"});
98             my $gui = Win32::GUI::XMLBuilder->new(*DATA);
99            
100             Win32::GUI::Dialog;
101            
102             sub test {
103             $gui->{Status}->Text("testing 1 2 3..");
104             }
105            
106             ...
107            
108             __END__
109            
110            
111             ..
112            
113            
114             =head1 DEPENDENCIES
115            
116             XML::Twig
117             Win32::GUI
118            
119             =head1 DESCRIPTION
120            
121             This module allows Win32::GUIs to be built using XML.
122             For examples on usage please look in samples/ directory.
123            
124             =head1 XML SYNTAX
125            
126             XMLBuilder will parse an XML file or string that contains elements
127             that describe a Win32::GUI object.
128            
129             All XML documents must be enclosed in .. elements and each
130             separate GUI window must be enclosed in .. elements.
131             To create a N-tier window system one might use a construction similar to: -
132            
133            
134            
135             ...
136            
137            
138             ...
139            
140            
141             ...
142            
143            
144            
145             =head1 ATTRIBUTES
146            
147             Elements can additionally be supplemented with attributes that describe its
148             corresponding Win32::GUI object's properties such as top, left, height and
149             width. These properties usually include those provided as standard in each
150             Win32::GUI class. I.e.
151            
152            
153            
154             Elements that require referencing in your code should be given a name attribute.
155             An element with attribute: -
156            
157            
158            
159             can be called as $gui->{'MyButton'} and event subroutines called using MyButton_Click.
160             From within an XML string the element must be called by $self->{'MyButton'}.
161            
162             Attributes can contain Perl code or variables and generally any attribute that
163             contains the variable '$self' or starts with 'exec:' will be evaluated. This is useful
164             when one wants to create dynamically sizing windows: -
165            
166            
167             left='0' top='0'
168             width='400' height='200'
169             >
170            
171             left='0' top='$self->{W}->ScaleHeight-$self->{S}->Height'
172             width='$self->{W}->ScaleWidth' height='$self->{S}->Height'
173             />
174            
175            
176             =head1 SPECIAL SUBSTITUTION VARIABLES
177            
178             If an attribute contains the string %P% then it is subsituted with $self->{}. Where
179             is the name of the current elements parent. It is useful when specifying child
180             dimensions where the parent is nameless.
181            
182             =head1 SPECIFYING DIMENSIONS
183            
184             'pos' and 'size' attributes are supported but converted to top, left, height and width
185             attributes on parsing. I suggest using the attribute dim='left,top,width,height' instead
186             (not an array but an list with brackets).
187            
188             =cut
189            
190             sub expandDimensions {
191             my ($self, $e) = @_;
192            
193             if (exists $e->{'att'}->{'pos'}) {
194             if ($e->{'att'}->{'pos'} =~ m/^\[\s*(.+)\s*,\s*(.+)\s*\]$/) {
195             ($e->{'att'}->{'top'}, $e->{'att'}->{'left'}) = ($1, $2);
196             delete $e->{'att'}->{'pos'};
197             } else {
198             $self->debug("Failed to parse pos '$e->{att}->{pos}', should have format '[top, left]'");
199             }
200             }
201            
202             if (exists $e->{'att'}->{'size'}) {
203             if ($e->{'att'}->{'size'} =~ m/^\[\s*(.+)\s*,\s*(.+)\s*\]$/) {
204             ($e->{'att'}->{'width'}, $e->{'att'}->{'height'}) = ($1, $2);
205             delete $e->{'att'}->{'size'};
206             } else {
207             $self->debug("Failed to parse size '$e->{att}->{size}', should have format '[width, height]'");
208             }
209             }
210            
211             if (exists $e->{'att'}->{'dim'}) {
212             if ($e->{'att'}->{'dim'} =~ m/^\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*$/) {
213             ($e->{'att'}->{'left'}, $e->{'att'}->{'top'}, $e->{'att'}->{'width'}, $e->{'att'}->{'height'}) = ($1, $2, $3, $4);
214             delete $e->{'att'}->{'dim'};
215             } else {
216             $self->debug("Failed to parse dim '$e->{att}->{dim}', should have format 'left, top, width, height'");
217             }
218             }
219            
220             return $e;
221             }
222            
223             =head1 AUTO-RESIZING
224            
225             Win32::GUI::XMLBuilder will autogenerate an onResize NEM method by reading in values for top, left, height and width.
226             This will work sufficiently well provided you use values that are dynamic such as $self->{PARENT_WIDGET}->Width,
227             $self->{PARENT_WIDGET}->Height for width, height attributes respectively when creating new widget elements.
228            
229             =cut
230            
231             sub genresize {
232             my ($self, $name) = @_;
233            
234             my $coderef = eval "{
235             package main; no strict;
236             sub {
237             foreach (\@{\$self->{_worder_}{$name}}) {
238             my \$width = eval \$self->{_width_}{$name}{\$_};
239             \$self->debug(\"\$_: Width to \$self->{_width_}{$name}{\$_} = \$width\");
240             \$self->{\$_}->Width(\$width) if \$_ ne '$name';
241             }
242             foreach (\@{\$self->{_horder_}{$name}}) {
243             my \$height = eval \$self->{_height_}{$name}{\$_};
244             \$self->debug(\"\$_: Height to \$self->{_height_}{$name}{\$_} = \$height\");
245             \$self->{\$_}->Height(\$height) if \$_ ne '$name';
246             }
247            
248             foreach (\@{\$self->{_lorder_}{$name}}) {
249             my \$left = eval \$self->{_left_}{$name}{\$_};
250             \$self->debug(\"\$_: Left to \$self->{_left_}{$name}{\$_} = \$left\");
251             \$self->{\$_}->Left(\$left) if \$_ ne '$name';
252             }
253             foreach (\@{\$self->{_torder_}{$name}}) {
254             my \$top = eval \$self->{_top_}{$name}{\$_};
255             \$self->debug(\"\$_: Top to \$self->{_top_}{$name}{\$_} = \$top\");
256             \$self->{\$_}->Top(\$top) if \$_ ne '$name';
257             }
258             }
259             }"; print STDERR $@ if $@;
260            
261             return $coderef;
262             }
263            
264             =head1 NEM EVENTS
265            
266             NEM events are supported. When specifying a NEM event such as onClick one must use $self syntax to specify current
267             Win32::GUI::XMLBuilder object in anonymous subroutines. An attribute of notify='1' is added automatically when an
268             NEM event is called. One can alo specify other named subroutines by name, but do not prefix with an ampersand! i.e.
269            
270             onClick='my_sub' [CORRECT]
271             onClick='&my_sub' [INCORRECT]
272            
273             =head1 SIMPLE POSITION AND SIZE
274            
275             If no dimensions are given for an element whose direct parent is of a Top level widget type such as Window or DialogBox,
276             it will assume a top and left of zero and and a width and height of its parent. I.e.
277            
278             dim='0, 0, $self->{PARENT}->ScaleWidth, $self->{PARENT}->ScaleHeight'
279            
280             =cut
281            
282             my $qrTop = qr/(Window|DialogBox|MDIFrame|MDIClient|MDIChild)$/;
283             my $qrFile = qr/(Icon|Bitmap|Cursor)$/;
284             my $qrNoParent = qr/(Font|Class|Pen|Brush)$/;
285             my $qrNoDim = qr/(NotifyIcon)$/;
286             my $qrLRWidgets = qr/(Grid|DIBitmap|AxWindow|Scintilla|ScintillaPerl)$/;
287            
288             sub evalhash {
289             my ($self, $e) = @_;
290            
291             $e = $self->expandDimensions($e);
292             my %in = %{$e->{'att'}};
293             my %out;
294            
295             my $parent = $self->getParent($e);
296            
297             foreach my $k (sort keys %in) {
298             $in{$k} =~ s/%P%/\$self->{$parent}/g; # sub %P% for parent
299             if ($k =~ /^on[A-Z]/) {
300             $out{-notify} = 1;
301             if ($in{$k} =~ /^\s*sub\s*\{.*\}\s*/s) {
302             $out{-$k} = eval "{ package main; no strict; use Win32::GUI(); ".$in{$k}."}"; print STDERR $@ if $@;
303             } else {
304             $out{-$k} = $in{$k};
305             }
306             } elsif ($in{$k} =~ /\$self|(^\s*exec:)/) {
307             (my $eval = $in{$k}) =~ s/(^\s*exec:)//;
308             $out{-$k} = eval "{ package main; no strict; use Win32::GUI(); ".$eval."}"; print STDERR $@ if $@;
309             } else {
310             $out{-$k} = $in{$k};
311             }
312            
313             $self->debug("\t-$k : $in{$k} -> $out{-$k}");
314             }
315            
316             if (defined $parent) {
317            
318             if (!$in{_nowidth_}) {
319             if (exists $in{width} && $in{width} ne '') {
320             $self->{_width_}{$parent}{$out{-name}} = $in{width};
321             push @{$self->{_worder_}{$parent}}, $out{-name};
322             } elsif ($e->gi !~ /^$qrNoDim/ && ref($self->{$e->parent->{'att'}->{'name'}}) =~ /^Win32::GUI::$qrTop/) {
323             $self->{_width_}{$parent}{$out{-name}} = "\$self->{$parent}->ScaleWidth"; # since we know $parent must be direct ancestor
324             push @{$self->{_worder_}{$parent}}, $out{-name};
325             $out{-width} = eval "{ package main; no strict; use Win32::GUI(); \$self->{$parent}->ScaleWidth }"; print STDERR $@ if $@;
326             }
327             }
328            
329             if (!$in{_noheight_}) {
330             if (exists $in{height} && $in{height} ne '') {
331             $self->{_height_}{$parent}{$out{-name}} = $in{height};
332             push @{$self->{_horder_}{$parent}}, $out{-name};
333             } elsif ($e->gi !~ /^$qrNoDim/ && ref($self->{$e->parent->{'att'}->{'name'}}) =~ /^Win32::GUI::$qrTop/) {
334             $self->{_height_}{$parent}{$out{-name}} = "\$self->{$parent}->ScaleHeight";
335             push @{$self->{_horder_}{$parent}}, $out{-name};
336             $out{-height} = eval "{ package main; no strict; use Win32::GUI(); \$self->{$parent}->ScaleHeight }"; print STDERR $@ if $@;
337             }
338             }
339            
340             if (!$in{_noleft_}) {
341             if (exists $in{left} && $in{left} ne '') {
342             $self->{_left_}{$parent}{$out{-name}} = $in{left};
343             push @{$self->{_lorder_}{$parent}}, $out{-name};
344             } elsif ($e->gi !~ /^$qrNoDim/ && ref($self->{$e->parent->{'att'}->{'name'}}) =~ /^Win32::GUI::$qrTop/) {
345             $self->{_left_}{$parent}{$out{-name}} = "0";
346             push @{$self->{_lorder_}{$parent}}, $out{-name};
347             $out{-left} = 0;
348             }
349             }
350            
351             if (!$in{_notop_}) {
352             if (exists $in{top} && $in{top} ne '') {
353             $self->{_top_}{$parent}{$out{-name}} = $in{top};
354             push @{$self->{_torder_}{$parent}}, $out{-name};
355             } elsif ($e->gi !~ /^$qrNoDim/ && ref($self->{$e->parent->{'att'}->{'name'}}) =~ /^Win32::GUI::$qrTop/) {
356             $self->{_top_}{$parent}{$out{-name}} = "0";
357             push @{$self->{_torder_}{$parent}}, $out{-name};
358             $out{-top} = 0;
359             }
360             }
361             }
362            
363             return %out;
364             }
365            
366             sub getParent {
367             my ($self, $e) = @_;
368            
369             if (ref $e ne 'XML::Twig::Elt' || $e->level == 1) {
370             return undef;
371             }
372            
373             my $xmlparent = $e->parent(sub {
374             return ref($self->{$_[0]->{'att'}->{'name'}}) =~ /^Win32::GUI::$qrTop/
375             });
376            
377             # should return undef if no parent found!
378             return $xmlparent->{'att'}->{'name'};
379             }
380            
381             =head1 AUTO WIDGET NAMING
382            
383             Win32::GUI::XMLBuilder will autogenerate a name for a wiget if a 'name' attribute is not
384             provided. The current naming convention is Widget_Class_N where N is a number. For example
385             Button_1, Window_23, etc...
386            
387             =cut
388            
389             sub genname {
390             my ($self, $e) = @_;
391             if (!exists $e->{'att'}->{'name'} || $e->{'att'}->{'name'} eq '') {
392             my $i = 0;
393             while () {
394             if (!exists $self->{$e->gi.'_'.$i}) {
395             $e->set_att(name=>$e->gi.'_'.$i);
396             last;
397             }
398             $i++;
399             }
400             }
401             return $e->{'att'}->{'name'};
402             }
403            
404             =head1 ENVIRONMENT VARIABLES
405            
406             =over 4
407            
408             =item WIN32GUIXMLBUILDER_DEBUG
409            
410             Setting this to 1 will produce logging.
411            
412             =cut
413            
414             sub debug {
415             my $self = shift;
416             print "$_[0]\n" if $ENV{WIN32GUIXMLBUILDER_DEBUG};
417             }
418            
419             sub error {
420             my $self = shift;
421             my $sub = (caller(1))[3];
422             my $line = (caller(1))[2];
423             $self->debug("$sub error on line $line: $^E $!");
424             print STDERR "$sub error on line $line: $^E $!\n";
425             }
426            
427             =head1 METHODS
428            
429             =over 4
430            
431             =item new({file=>$file}) or new($xmlstring)
432            
433             =cut
434            
435             sub new {
436             my $this = shift;
437             my $self = {};
438             $self->{_show_} = undef; # ...{parent} = COMMAND
439             $self->{_width_} = undef; # ...{parent}{child}
440             $self->{_worder_} = undef; # ...{parent} = (child1, child2, ...)
441             $self->{_height_} = undef; # ...{parent}{child}
442             $self->{_horder_} = undef; # ...{parent} = (child1, child2, ...)
443             $self->{_left_} = undef; # ...{parent}{child}
444             $self->{_lorder_} = undef; # ...{parent} = (child1, child2, ...)
445             $self->{_top_} = undef; # ...{parent}{child}
446             $self->{_torder_} = undef; # ...{parent} = (child1, child2, ...)
447             $self->{_menuid_} = 1; # menu id counter (see Win32/GUI.pm/MakeMenu)
448            
449             bless($self, (ref($this) || $this));
450            
451             my $s = new XML::Twig(
452             TwigHandlers => {
453             Script => sub { $self->WGXPre(@_) },
454             PreExec => sub { $self->WGXPre(@_) },
455             WGXPre => sub { $self->WGXPre(@_) },
456             }
457             );
458            
459             if (ref($_[0]) eq 'HASH') {
460             $self->debug("processing file ${$_[0]}{file}");
461             $s->parsefile(${$_[0]}{file})
462             }
463             else {
464             $s->parse($_[0])
465             }
466            
467             my $t = new XML::Twig;
468             $t->parse($s->sprint);
469             my $root = $t->root;
470             foreach ($root->children()) {
471             #$self->debug($_->{'att'}->{'name'});
472             #$self->debug($_->gi);
473             next if $_->gi eq 'WGXPost' or $_->gi eq 'PostExec';
474            
475             if (exists &{$_->gi}) {
476             &{\&{$_->gi}}($self, $t, $_);
477             }
478             elsif ($_->gi =~ /^$qrTop/) {
479             $self->_GenericTop($t, $_);
480             }
481             elsif ($_->gi =~ /^$qrFile/) {
482             $self->_GenericFile($t, $_);
483             }
484             elsif ($_->gi =~ /^$qrNoParent/) {
485             $self->_GenericNoParent($t, $_);
486             }
487             }
488            
489             foreach (sort keys %{$self->{_show_}}) {
490             $self->debug("show widget $_ with command ${$self->{_show_}}{$_}");
491            
492             if (${$self->{_show_}}{$_} =~ /\$self|(^\s*exec:)/) {
493             (my $eval = ${$self->{_show_}}{$_}) =~ s/(^\s*exec:)//;
494             ${$self->{_show_}}{$_} = eval "{ package main; no strict; use Win32::GUI(); ".$eval."}"; print STDERR $@ if $@;
495             }
496            
497             $self->{$_}->Show(${$self->{_show_}}{$_});
498             }
499            
500             my $u = new XML::Twig(
501             TwigHandlers => {
502             PostExec => sub { $self->WGXPost(@_) },
503             WGXPost => sub { $self->WGXPost(@_) },
504             }
505             );
506            
507             $u->parse($t->sprint);
508            
509             return $self;
510             }
511            
512            
513             =head1 SUPPORTED WIDGETS - ELEMENTS
514            
515             Most Win32::GUI widgets are supported and general type widgets can added without any modification
516             being added to this module.
517            
518             =over 4
519            
520             =item
521            
522             The element is parsed before GUI construction and is useful for defining subroutines
523             and global variables. Code is wrapped in a { package main; no strict; .. } so that if subroutines
524             are created they can contain variables in your program including Win32::GUI::XMLBuilder instances.
525             The current Win32::GUI::XMLBuilder instance can also be accessed outside subroutines as $self.
526             If any data is returned it must be valid XML that will be parsed once by the WGXPost phase, see below.
527            
528             Since you may need to use a illegal XML characters within this element such as
529            
530             < less than (<)
531             > greater than (>)
532             & ampersand (&)
533             ' apostrophe (')
534             " quotation mark (")
535            
536             you can use the alternative predefined entity reference or enclose this data in a "" section.
537             Please look at the samples and read http://www.w3schools.com/xml/xml_cdata.asp.
538            
539             The element was previously called . The tag is deprecated but remains
540             only for backward compatibility and will be removed in a later release.
541            
542             =cut
543            
544             sub WGXPre {
545             my ($self, $t, $e) = @_;
546            
547             $self->debug($e->text);
548             my $ret = eval "{ package main; no strict; ".$e->text."}";
549             print STDERR "$@" if $@;
550             $self->debug($ret);
551             $e->set_text('');
552             my $pcdata= XML::Twig::Elt->new(XML::Twig::ENT, $ret);
553             $pcdata->paste($e);
554             $e->erase();
555             }
556            
557             =item
558            
559             The element is parsed during GUI construction and allows code to be inserted at arbitrary points in the code.
560             It otherwise behaves exactly the same as and can be used to place _Resize subroutines. If any data is returned
561             it must be valid XML that will be parsed once by the WGXPost phase, see below.
562            
563             =cut
564            
565             sub WGXExec { WGXPre(@_) }
566            
567             =item
568            
569             The element is parsed after GUI construction and allows code to be included at the end of an XML file.
570             It otherwise behaves exactly the same as and can be used to place _Resize subroutines.
571            
572             The element was previously called . The
573             tag is deprecated but remains only for backward compatibility and will be removed in a later release.
574            
575             =cut
576            
577             sub WGXPost {
578             my ($self, $t, $e) = @_;
579            
580             $self->debug($e->text);
581             my $ret = eval "{ package main; no strict; ".$e->text."}";
582             print STDERR "$@" if $@;
583             $self->debug($ret);
584             }
585            
586             =item , and elements.
587            
588             The element allows you to specify an Icon for your program.
589            
590            
591            
592             The element allows you to specify an Bitmap for your program.
593            
594            
595            
596             The element allows you to specify an Cursor for your program.
597            
598            
599            
600             =cut
601            
602             sub _GenericFile {
603             my ($self, $t, $e) = @_;
604             my $widget = $e->gi;
605             my $name = $self->genname($e);
606             my $file = $e->{'att'}->{'file'} !~ /\$/ ? $e->{'att'}->{'file'} : eval $e->{'att'}->{'file'};;
607            
608             $self->debug("\n$widget (_GenericFile): $name");
609             $self->debug("file -> $file");
610             $self->{$name} = eval "new Win32::GUI::$widget('$file')" || $self->error;
611             }
612            
613             =item
614            
615            
616            
617            
618            
619            
620            
621             =cut
622            
623             sub ImageList {
624             my ($self, $t, $e) = @_;
625             my $name = $self->genname($e);
626             my %opt = $self->evalhash($e);
627             my $width = $opt{-width} || 16;
628             my $height = $opt{-height} || 16;
629             my $initial = $e->children_count();
630             my $growth = $opt{-growth} || (2 * $initial);
631            
632             $self->debug("\nImageList: $name");
633             $self->{$name} = new Win32::GUI::ImageList($width, $height, 0, $initial, $growth) || $self->error;
634            
635             foreach ($e->children()) {
636             my %chopt = $self->evalhash($_);
637             if (exists $chopt{-bitmap}) {
638             $self->{$name}->Add($chopt{-bitmap}, $chopt{-mask});
639             $self->debug($chopt{-bitmap});
640             } elsif (exists $chopt{-icon}) {
641             $self->{$name}->AddIcon($chopt{-icon});
642             $self->debug($chopt{-icon});
643             }
644             }
645             }
646            
647             =item
648            
649             Allows you to create a font for use in your program.
650            
651            
652             size='8'
653             face='Arial'
654             bold='1'
655             italic='0'
656             />
657            
658             You might call this in a label element using something like this: -
659            
660            
661             text='some text'
662             font='$self->{Bold}'
663             ...
664             />.
665            
666             =item
667            
668             You can create a element,
669            
670            
671            
672             that can be applied to a . The name of a class must be unique
673             over all instances of Win32::GUI::XMLBuilder instances!
674            
675             Typically one might add an icon to your application using a Class element, i.e.
676            
677            
678            
679            
680            
681            
682            
683             =cut
684            
685             sub _GenericNoParent {
686             my ($self, $t, $e) = @_;
687             my $widget = $e->gi;
688             my $name = $self->genname($e);
689            
690             $self->debug("\n$widget (_GenericNoParent): $name");
691             $self->{$name} = eval "new Win32::GUI::$widget(\$self->evalhash(\$e))" || $self->error;
692             }
693            
694             =item
695            
696             Creates a menu system. Submenus can be nested many times more deeply than using MakeMenu. Although
697             one can use Item elements throughout the structure it is more readable to use the Button attribute
698             when a new nest begins. I.e.
699            
700            
701            
702            
703            
704            
705            
706             and
707            
708            
709            
710            
711            
712            
713            
714             are equivalent but the former is more true to what is happening under the hood. One can generally pass
715             a Button to TrackPopupMenu and a Button handle to MDIClient's windowmenu attribute.
716            
717             A separator line can be specified by setting the separator attribute to 1.
718            
719             One can also use NEM events directly as attributes such as onClick (or OEM events by using
720             PopupMenu_Click), etc..
721            
722            
723            
724            
725            
726            
727            
728            
729            
730            
731            
732             See the menus.xml for an extensive example in the samples/ directory.
733            
734             =cut
735            
736             sub WGXMenu {
737             my ($self, $t, $e) = @_;
738             my $name = $self->genname($e);
739            
740             $self->debug("\nWGXMenu: $name");
741             $self->{$name} = new Win32::GUI::Menu() || $self->error;
742            
743             foreach my $button ($e->children()) {
744             next if $button->gi !~ /^(Item|Button)$/;
745             $self->WGXMenu_Button($button, $name);
746             }
747             }
748            
749             sub WGXMenu_Button {
750             my ($self, $e, $parent) = @_;
751             my $name = $self->genname($e);
752             $e->{'att'}->{'id'} = $self->{_menuid_}++;
753            
754             $self->debug("\nWGXMenu_Button: $name");
755             $self->{$name} = $self->{$parent}->AddMenuButton($self->evalhash($e));
756            
757             foreach my $item ($e->children()) {
758             $item->{'att'}->{'id'} = $self->{_menuid_}++;
759             my $iname = $self->genname($item);
760             if ($item->gi eq 'Button' || $item->children_count()) {
761             $self->{'submenu'.$self->{_menuid_}} = new Win32::GUI::Menu();
762             my $bname = $self->WGXMenu_Button($item, 'submenu'.$self->{_menuid_});
763             $item->{'att'}->{'submenu'} = $self->{$bname};
764             $self->{$name}->AddMenuItem($self->evalhash($item));
765             } elsif ($item->gi eq 'Item') {
766             $self->{$iname} = $self->{$name}->AddMenuItem($self->evalhash($item));
767             }
768             }
769             return $name;
770             }
771            
772             =item
773            
774             Creates a menu system. The amount of '>'s prefixing a text label specifies the menu items
775             depth. A value of text '-' (includes '>-', '>>-', etc) creates a separator line. To access
776             named menu items one must use the menu widgets name, i.e. $gui->{PopupMenu}->{SelectAll},
777             although one can access an event by its name, i.e. SelectAll_Click. One can also use NEM
778             events directly as attributes such as onClick, etc..
779            
780            
781            
782            
783            
784            
785            
786            
787            
788            
789            
790            
791             See the makemenu.xml example in the samples/ directory. The MakeMenu element suffers from
792             the limitation of being only able to nest menus to 2 layers. This is inherent from the
793             underlying Win32::GUI module. I would suggest using the more configurable WGXMenu above.
794            
795             The element was previously called . The tag is deprecated but remains
796             only for backward compatibility and will be removed in a later release. Please try to update
797             your code to use MakeMenu instead.
798            
799             =cut
800            
801             sub Menu { MakeMenu(@_) }
802            
803             sub MakeMenu {
804             my ($self, $t, $e) = @_;
805             my $name = $self->genname($e);
806            
807             $self->debug("\nMenu: $name");
808             my @m;
809             foreach ($e->children()) {
810             next if $_->gi ne 'Item';
811             $_->{'att'}->{'name'} = '0' if ! exists $_->{'att'}->{'name'};
812             my $label = $_->{'att'}->{'text'};
813             $self->debug("Text: $label");
814             delete $_->{'att'}->{'text'}; # prevents preformated text becoming label
815             push @m, $label, { $self->evalhash($_) };
816             }
817             $self->{$name} = Win32::GUI::MakeMenu(@m) || $self->error;
818             }
819            
820             =item
821            
822             Creates a key accelerator table.
823            
824            
825            
826            
827            
828            
829            
830            
831             =cut
832            
833             sub AcceleratorTable {
834             my ($self, $t, $e) = @_;
835             my $name = $self->genname($e);
836            
837             $self->debug("\nAcceleratorTable: $name");
838             my @a;
839             foreach ($e->children()) {
840             my $key = $_->{'att'}->{'key'};
841             my $sub = $_->{'att'}->{'sub'};
842             if ($sub =~ /^\s*sub\s*\{.*\}\s*/) {
843             $sub = eval "{ package main; no strict; use Win32::GUI(); ".$sub."}"; print STDERR $@ if $@;
844             } else {
845             $sub = \&{'::'.$sub};
846             }
847             $self->debug("$key -> $sub");
848             push @a, $key, $sub;
849             }
850             $self->{$name} = new Win32::GUI::AcceleratorTable(@a) || $self->error;
851             }
852            
853            
854             =item
855            
856             The element creates a top level widget. In addition to standard
857             Win32::GUI::Window attributes it also has a 'show=n' attribute. This instructs XMLBuilder
858             to give the window a Show(n) command on invocation.
859            
860            
861            
862             NOTE: Since the onResize event is defined automatically for the this element one must set
863             the attribute 'eventmodel' to 'both' to allow _Event events to be caught!
864            
865             =item
866            
867             is very similar to , except that by default it cannot be resized and it
868             doesn't have the minimize and maximize buttons.
869            
870             =item
871            
872             The element creates a Multiple Document Interface. It has a similar behaviour
873             to the attribute. PLease see the MDI.xml sample.
874            
875             =cut
876            
877             sub _GenericTop {
878             my ($self, $t, $e) = @_;
879             my $widget = $e->gi;
880             my $name = $self->genname($e); # should this be allowed?
881             my $show = $e->{'att'}->{'show'};
882            
883             $self->debug("\n$widget (_GenericTop): $name");
884             $self->{$name} = eval "new Win32::GUI::$widget(\$self->evalhash(\$e))" || $self->error;
885             $self->{$name}->SetEvent('Resize', $self->genresize($name));
886            
887             ${$self->{_show_}}{$name} = $show eq '' ? 1 : $show;
888            
889             foreach ($e->children()) {
890             if (exists &{$_->gi}) {
891             &{\&{$_->gi}}($self, $t, $_);
892             } else {
893             $self->_Generic($t, $_);
894             }
895             }
896             }
897            
898             =item
899            
900             A WGXPanel is a shorthand for a Window element with popstyles WS_CAPTION, WS_SIZEBOX and WS_EX_CONTROLPARENT
901             and pushstyles WS_CHILD, DS_CONTROL and WS_VISIBLE. It is useful for grouping controls together.
902            
903            
904             ...
905            
906            
907             =cut
908            
909             sub WGXPanel {
910             my ($self, $t, $e) = @_;
911             my $name = $self->genname($e);
912             my $parent = $self->getParent($e);
913             my $show = $e->{'att'}->{'show'};
914            
915             $self->debug("\nWGXPanel: $name; Parent: $parent");
916            
917             $e->{'att'}->{'parent'} = $self->{$parent};
918             $e->{'att'}->{'popstyle'} = WS_CAPTION()|WS_SIZEBOX()|WS_EX_CONTROLPARENT();
919             $e->{'att'}->{'pushstyle'} = WS_CHILD()|DS_CONTROL()|WS_VISIBLE();
920            
921             $self->debug("\nWGXPanel: $name");
922             $self->{$name} = eval "new Win32::GUI::Window(\$self->evalhash(\$e))" || $self->error;
923             $self->{$name}->SetEvent('Resize', $self->genresize($name));
924            
925             ${$self->{_show_}}{$name} = $show eq '' ? 1 : $show;
926            
927             foreach ($e->children()) {
928             if (exists &{$_->gi}) {
929             &{\&{$_->gi}}($self, $t, $_);
930             } else {
931             $self->_Generic($t, $_);
932             }
933             }
934            
935             }
936            
937            
938             =item
939            
940             Creates a TreeView. These can be nested deeply using the sub element . Please look at the
941             treeview.pl example in the samples/ directory.
942            
943            
944            
945            
946            
947            
948             etc...
949            
950             ...
951            
952            
953             =cut
954            
955             sub TreeView {
956             my ($self, $t, $e) = @_;
957             my $name = $self->genname($e);
958             my $parent = $self->getParent($e);
959            
960             $self->debug("\nTreeView: $name; Parent: $parent");
961             $self->{$name} = $self->{$parent}->AddTreeView($self->evalhash($e)) || $self->error;
962            
963             if($e->children_count()) {
964             $self->TreeView_Item($e, $name);
965             }
966             }
967            
968             sub TreeView_Item {
969             my ($self, $e, $parent) = @_;
970             my $name = $e->{'att'}->{'name'};
971             foreach my $item ($e->children()) {
972             next if $item->gi ne 'Item';
973             my $iname = $item->{'att'}->{'name'};
974             $self->debug("Item: $iname; Parent: $name");
975             $item->{'att'}->{'parent'} = "\$self->{$name}" if $name ne $parent;
976             $self->{$iname} = $self->{$parent}->InsertItem($self->evalhash($item));
977             if($item->children_count()) {
978             $self->TreeView_Item($item, $parent);
979             }
980             }
981             }
982            
983             =item
984            
985             Generate a combobox with drop down items specified with the elements. In addition
986             to standard attributes for Win32::GUI::Combobox there is also a 'dropdown' attribute that
987             automatically sets the 'pushstyle' to 'WS_VISIBLE()|0x3|WS_VSCROLL()|WS_TABSTOP()'. In 'dropdown'
988             mode an element has the additional attribute 'default'.
989            
990             =cut
991            
992             sub Combobox {
993             my ($self, $t, $e) = @_;
994             my $name = $self->genname($e);
995             my $parent = $self->getParent($e);
996            
997             $self->debug("\nCombobox: $name; Parent: $parent");
998            
999             $e->{'att'}->{'pushstyle'} = WS_VISIBLE()|0x3|WS_VSCROLL()|WS_TABSTOP() if $e->{'att'}->{'dropdown'};
1000            
1001             $self->{$name} = $self->{$parent}->AddCombobox($self->evalhash($e)) || $self->error;
1002            
1003             my $default;
1004             if($e->children_count()) {
1005             foreach my $item ($e->children()) {
1006             next if $item->gi ne 'Item';
1007             my $text = $item->{'att'}->{'text'};
1008             $default = $text if $item->{'att'}->{'default'};
1009             $self->debug("Item: $text");
1010             $self->{$name}->InsertItem($text);
1011             }
1012             }
1013            
1014             $self->{$name}->Select($self->{$name}->FindStringExact($default)) if $default;
1015             }
1016            
1017             =item
1018            
1019             Generate a listbox with drop down items specified with the elements. In addition
1020             to standard attributes for Win32::GUI::Listbox there is also a 'dropdown' attribute that
1021             automatically sets the 'pushstyle' to 'WS_CHILD()|WS_VISIBLE()|1'. In 'dropdown' mode an element has
1022             the additional attribute 'default'.
1023            
1024             =cut
1025            
1026             sub Listbox {
1027             my ($self, $t, $e) = @_;
1028             my $name = $self->genname($e);
1029             my $parent = $self->getParent($e);
1030            
1031             $self->debug("\nListbox: $name; Parent: $parent");
1032             $e->{'att'}->{'pushstyle'} = $e->{'att'}->{'dropdown'} ? WS_VSCROLL()|WS_CHILD()|WS_VISIBLE()|1 : WS_VSCROLL()|WS_VISIBLE()|WS_CHILD();
1033             $self->{$name} = $self->{$parent}->AddListbox($self->evalhash($e)) || $self->error;
1034            
1035             # $self->{$name}->SendMessage(0x0195, 201, 0);
1036            
1037             my $default;
1038             if($e->children_count()) {
1039             foreach my $item ($e->children()) {
1040             next if $item->gi ne 'Item';
1041             my $text = $item->{'att'}->{'text'};
1042             $default = $text if $item->{'att'}->{'default'};
1043             $self->debug("Item: $text");
1044             $self->{$name}->AddString($text);
1045             }
1046             }
1047            
1048             $self->{$name}->Select($self->{$name}->FindStringExact($default)) if $default;
1049             }
1050            
1051             =item
1052            
1053             See rebar.xml example in samples/ directory.
1054            
1055             =cut
1056            
1057             sub Rebar {
1058             my ($self, $t, $e) = @_;
1059             my $name = $self->genname($e);
1060             my $parent = $self->getParent($e);
1061            
1062             $self->debug("\nRebar: $name; Parent: $parent");
1063             $self->{$name} = $self->{$parent}->AddRebar($self->evalhash($e)) || $self->error;
1064             foreach my $item ($e->children()) {
1065             my $bname = $self->genname($item);
1066             $self->debug("Band: $bname");
1067            
1068             my $f;
1069             $f->{'att'}->{'parent'} = $self->{$parent};
1070             $f->{'att'}->{'popstyle'} = WS_CAPTION()|WS_SIZEBOX();
1071             $f->{'att'}->{'pushstyle'} = WS_CHILD();
1072             # push non-Band attributes into Window class
1073             foreach (keys %{$item->{'att'}}) {
1074             if ($_ !~ /^(image|index|bitmap|child|foreground|background|width|minwidth|minheight|text|style)$/) {
1075             $f->{'att'}->{$_} = $item->{'att'}->{$_};
1076             }
1077             }
1078             $self->debug("Window: $bname");
1079             $self->{$bname} = new Win32::GUI::Window($self->evalhash($f)) || $self->error;
1080             $item->{'att'}->{'child'} = $self->{$bname};
1081            
1082             $self->{$bname}->SetEvent('Resize', $self->genresize($bname));
1083            
1084             foreach ($item->children()) {
1085             $self->debug($_->{'att'}->{'name'});
1086             $self->debug($_->gi);
1087            
1088             if (exists &{$_->gi}) {
1089             &{\&{$_->gi}}($self, $t, $_);
1090             } else {
1091             $self->_Generic($t, $_);
1092             }
1093             }
1094            
1095             $self->{$name}->InsertBand($self->evalhash($item));
1096             }
1097             }
1098            
1099             =item
1100            
1101             A TabStrip can be created using the following structure: -
1102            
1103            
1104            
1105            
1106            
1107            
1108            
1109             ..other elements, etc...
1110            
1111            
1112            
1113             See wizard_tabstrip.xml example in samples/ directory.
1114            
1115             =item
1116            
1117             A TabFrame should behave identically to a TabStrip. TabFrame is no longer supported
1118             and will be removed from a future release. Please try to update your code to use
1119             TabStrip instead.
1120            
1121             =cut
1122            
1123             sub TabFrame { TabStrip(@_); }
1124            
1125             sub TabStrip {
1126             my ($self, $t, $e) = @_;
1127             my $name = $self->genname($e);
1128             my $parent = $self->getParent($e);
1129            
1130             $e->{'att'}->{'onChange'} = "sub {
1131             my \$i;
1132             for (\$i = 0; \$i < \$_[0]->Count; \$i++) {
1133             \$self->{\$self->{$name}->{\$i}}->Show(\$_[0]->SelectedItem == \$i ? 1 : 0);
1134             }
1135             }";
1136            
1137             $self->debug("\nTabStrip: $name; Parent: $parent");
1138             $self->{$name} = $self->{$parent}->AddTabStrip($self->evalhash($e)) || $self->error;
1139             my $tabcount = 0;
1140             foreach my $item ($e->children()) {
1141             my $bname = $self->genname($item);
1142             $self->debug("Tab: $bname");
1143            
1144             my $f;
1145             $f->{'att'}->{'parent'} = $self->{$parent};
1146             $f->{'att'}->{'popstyle'} = WS_CAPTION()|WS_SIZEBOX()|WS_EX_CONTROLPARENT();
1147             $f->{'att'}->{'pushstyle'} = WS_CHILD()|DS_CONTROL();
1148             $f->{'att'}->{'pushstyle'} |= WS_VISIBLE() if $tabcount == 0;
1149            
1150             $self->{$name}->InsertItem($self->evalhash($item));
1151            
1152             # push non-Item attributes into Window class
1153             foreach (keys %{$item->{'att'}}) {
1154             if ($_ !~ /^(image|index|text)$/) {
1155             $f->{'att'}->{$_} = $item->{'att'}->{$_};
1156             }
1157             }
1158            
1159             ($f->{'att'}->{'left'}, $f->{'att'}->{'top'}, $f->{'att'}->{'width'}, $f->{'att'}->{'height'}) = (
1160             $self->{$name}->Left + ($self->{$name}->DisplayArea)[0],
1161             $self->{$name}->Top + ($self->{$name}->DisplayArea)[1],
1162             ($self->{$name}->DisplayArea)[2],
1163             ($self->{$name}->DisplayArea)[3],
1164             );
1165            
1166             $self->debug("Window: $bname");
1167             $self->{$bname} = new Win32::GUI::Window($self->evalhash($f)) || $self->error;
1168             $self->{$bname}->SetEvent('Resize', $self->genresize($bname));
1169            
1170             $self->{_left_}{$parent}{$bname} = "\$self->{$name}->Left + (\$self->{$name}->DisplayArea)[0]";
1171             $self->{_top_}{$parent}{$bname} = "\$self->{$name}->Top + (\$self->{$name}->DisplayArea)[1]";
1172             $self->{_width_}{$parent}{$bname} = "(\$self->{$name}->DisplayArea)[2]";
1173             $self->{_height_}{$parent}{$bname} = "(\$self->{$name}->DisplayArea)[3]";
1174            
1175             push @{$self->{_worder_}{$parent}}, $bname;
1176             push @{$self->{_horder_}{$parent}}, $bname;
1177             push @{$self->{_lorder_}{$parent}}, $bname;
1178             push @{$self->{_torder_}{$parent}}, $bname;
1179            
1180            
1181             $self->{$name}->{$tabcount} = $bname; # stash index to name mapping!
1182            
1183             foreach ($item->children()) {
1184             $self->debug($_->{'att'}->{'name'});
1185             $self->debug($_->gi);
1186            
1187             if (exists &{$_->gi}) {
1188             &{\&{$_->gi}}($self, $t, $_);
1189             } else {
1190             $self->_Generic($t, $_);
1191             }
1192             }
1193            
1194             $tabcount++;
1195             }
1196             }
1197            
1198            
1199             =item
1200            
1201             A WGXSplitter can be created using the following structure: -
1202            
1203            
1204            
1205            
1206            
1207            
1208            
1209             ..other elements, etc...
1210            
1211            
1212            
1213             The reason this is called a WGXSplitter is because it does not exist as a super-class
1214             to a Splitter object. It's width dimension for example holds the complete width of both
1215             panes and its splitterwidth ...
1216            
1217             See splitter.xml example in samples/ directory.
1218            
1219             =cut
1220            
1221             sub WGXSplitter {
1222             my ($self, $t, $e) = @_;
1223             my $name = $self->genname($e);
1224             my $parent = $self->getParent($e);
1225            
1226             $e = $self->expandDimensions($e);
1227            
1228             if (exists $e->{'att'}->{'range'}) {
1229             if ($e->{'att'}->{'range'} =~ m/^\s*(.+)\s*,\s*(.+)\s*$/) {
1230             ($e->{'att'}->{'min'}, $e->{'att'}->{'max'}) = ($1, $2);
1231             delete $e->{'att'}->{'range'};
1232             } else {
1233             $self->debug("Failed to parse range '$e->{'att'}->{'range'}', should have format '[min, max]'");
1234             }
1235             }
1236            
1237             my ($LEFT, $TOP, $WIDTH, $HEIGHT);
1238             if($e->{'att'}->{'horizontal'}) {
1239             $e->{'att'}->{'_notop_'} = 1;
1240             $e->{'att'}->{'_noheight_'} = 1;
1241             $TOP = $e->{'att'}->{'top'};
1242             $e->{'att'}->{'top'} = "exec:".join('+', map { s/^exec://; $_ } $TOP, $e->{'att'}->{'start'});
1243             $HEIGHT = $e->{'att'}->{'height'};
1244             $e->{'att'}->{'height'} = $e->{'att'}->{'splittersize'};
1245             $e->{'att'}->{'min'} = "exec:".join('+', map { s/^exec://; $_ } $TOP, $e->{'att'}->{'min'}) if exists $e->{'att'}->{'min'};
1246             $e->{'att'}->{'max'} = "exec:".join('+', map { s/^exec://; $_ } $TOP, $e->{'att'}->{'max'}) if exists $e->{'att'}->{'max'};
1247             $e->{'att'}->{'onRelease'} = "sub {
1248             \$self->{\$self->{$name}->{0}}->Move(\$_[0]->Left, $TOP);
1249             \$self->{\$self->{$name}->{0}}->Resize(\$_[0]->Width, \$_[1] - $TOP);
1250             \$self->{\$self->{$name}->{1}}->Move(\$_[0]->Left, \$_[1] + \$_[0]->Height);
1251             \$self->{\$self->{$name}->{1}}->Resize(\$_[0]->Width, $HEIGHT - \$_[0]->Height - \$_[1] + $TOP);
1252             }";
1253             } else {
1254             $e->{'att'}->{'_noleft_'} = 1;
1255             $e->{'att'}->{'_nowidth_'} = 1;
1256             $LEFT = $e->{'att'}->{'left'};
1257             $e->{'att'}->{'left'} = "exec:".join('+', map { s/^exec://; $_ } $LEFT, $e->{'att'}->{'start'});
1258             $WIDTH = $e->{'att'}->{'width'};
1259             $e->{'att'}->{'width'} = $e->{'att'}->{'splittersize'};
1260             $e->{'att'}->{'min'} = "exec:".join('+', map { s/^exec://; $_ } $LEFT, $e->{'att'}->{'min'}) if exists $e->{'att'}->{'min'};
1261             $e->{'att'}->{'max'} = "exec:".join('+', map { s/^exec://; $_ } $LEFT, $e->{'att'}->{'max'}) if exists $e->{'att'}->{'max'};
1262             $e->{'att'}->{'onRelease'} = "sub {
1263             \$self->{\$self->{$name}->{0}}->Move($LEFT, \$_[0]->Top);
1264             \$self->{\$self->{$name}->{0}}->Resize(\$_[1] - $LEFT, \$_[0]->Height);
1265             \$self->{\$self->{$name}->{1}}->Move(\$_[1] + \$_[0]->Width, \$_[0]->Top);
1266             \$self->{\$self->{$name}->{1}}->Resize($WIDTH - \$_[0]->Width - \$_[1] + $LEFT, \$_[0]->Height);
1267             }";
1268             }
1269            
1270             $self->debug("\nWGXSplitter: $name; Parent: $parent");
1271             $self->{$name} = $self->{$parent}->AddSplitter($self->evalhash($e)) || $self->error;
1272             my $tabcount = 0;
1273             foreach my $item ($e->children()) {
1274             my $bname = $self->genname($item);
1275             $self->debug("Pane: $bname");
1276            
1277             my $f;
1278             $f->{'att'}->{'parent'} = $self->{$parent};
1279             $f->{'att'}->{'popstyle'} = WS_CAPTION()|WS_SIZEBOX()|WS_EX_CONTROLPARENT();
1280             $f->{'att'}->{'pushstyle'} = WS_CHILD()|DS_CONTROL()|WS_VISIBLE();
1281            
1282            
1283             # push attributes into Window class
1284             foreach (keys %{$item->{'att'}}) {
1285             $f->{'att'}->{$_} = $item->{'att'}->{$_};
1286             }
1287            
1288             $self->debug("Window: $bname");
1289             $self->{$bname} = new Win32::GUI::Window($self->evalhash($f)) || $self->error;
1290             $self->{$bname}->SetEvent('Resize', $self->genresize($bname));
1291            
1292             if($e->{'att'}->{'horizontal'}) {
1293             $self->{_left_}{$parent}{$bname} = $tabcount == 0 ? "\$self->{$name}->Left" : "\$self->{$name}->Left";
1294             $self->{_top_}{$parent}{$bname} = $tabcount == 0 ? "$TOP" : "\$self->{$name}->Top + \$self->{$name}->Height";
1295             $self->{_width_}{$parent}{$bname} = $tabcount == 0 ? "\$self->{$name}->Width" : "\$self->{$name}->Width";
1296             $self->{_height_}{$parent}{$bname} = $tabcount == 0 ? "\$self->{$name}->Top - $TOP" : "$HEIGHT - \$self->{$name}->Top - \$self->{$name}->Height + $TOP";
1297             } else {
1298             $self->{_left_}{$parent}{$bname} = $tabcount == 0 ? "$LEFT" : "\$self->{$name}->Left + \$self->{$name}->Width";
1299             $self->{_top_}{$parent}{$bname} = $tabcount == 0 ? "\$self->{$name}->Top" : "\$self->{$name}->Top";
1300             $self->{_width_}{$parent}{$bname} = $tabcount == 0 ? "\$self->{$name}->Left - $LEFT" : "$WIDTH - \$self->{$name}->Width - \$self->{$name}->Left + $LEFT";
1301             $self->{_height_}{$parent}{$bname} = $tabcount == 0 ? "\$self->{$name}->Height" : "\$self->{$name}->Height";
1302             }
1303            
1304             push @{$self->{_worder_}{$parent}}, $bname;
1305             push @{$self->{_horder_}{$parent}}, $bname;
1306             push @{$self->{_lorder_}{$parent}}, $bname;
1307             push @{$self->{_torder_}{$parent}}, $bname;
1308            
1309             $self->{$name}->{$tabcount} = $bname; # stash index to name mapping!
1310            
1311             foreach ($item->children()) {
1312             $self->debug($_->{'att'}->{'name'});
1313             $self->debug($_->gi);
1314            
1315             if (exists &{$_->gi}) {
1316             &{\&{$_->gi}}($self, $t, $_);
1317             } else {
1318             $self->_Generic($t, $_);
1319             }
1320             }
1321            
1322             $tabcount++;
1323             }
1324             }
1325            
1326             =item
1327            
1328             Allows you to create a timer for use in your program.
1329            
1330            
1331            
1332             =cut
1333            
1334             sub Timer {
1335             my ($self, $t, $e) = @_;
1336             my $name = $self->genname($e);
1337             my $parent = $self->getParent($e);
1338             my $elapse = $e->{'att'}->{'elapse'};
1339            
1340             $self->debug("\nTimer: $name, $elapse, ($parent)");
1341             $self->{$name} = new Win32::GUI::Timer($self->{$parent}, $name, $elapse) || $self->error;
1342             }
1343            
1344             =item Generic Elements
1345            
1346             Any widget not explicitly mentioned above can be generated by using its name
1347             as an element id. For example a Button widget can be created using: -
1348            
1349            
1350             text='Push Me'
1351             left='20' top='0'
1352             width='80' height='20'
1353             />
1354            
1355             =cut
1356            
1357             sub _Generic {
1358             my ($self, $t, $e) = @_;
1359             my $widget = $e->gi;
1360             my $name = $self->genname($e);
1361             my $parent = $self->getParent($e);
1362            
1363             $self->debug("\n$widget (_Generic): $name; Parent: $parent");
1364             if ($widget =~ /^$qrLRWidgets/) {
1365             $e->{'att'}->{'parent'} = "\$self->{$parent}";
1366             $self->{$name} = eval "new Win32::GUI::$widget(\$self->evalhash(\$e))" || $self->error;
1367             }
1368             elsif ($widget =~ /^$qrTop/) {
1369             $e->{'att'}->{'parent'} = "\$self->{$parent}";
1370             $self->{$name} = eval "new Win32::GUI::$widget(\$self->evalhash(\$e))" || $self->error;
1371             $self->{$name}->SetEvent('Resize', $self->genresize($name));
1372             }
1373             else {
1374             $self->{$name} = eval "new Win32::GUI::$widget(\$self->{$parent}, \$self->evalhash(\$e))" || $self->error;
1375             }
1376            
1377             foreach ($e->children()) {
1378             if (exists &{$_->gi}) {
1379             &{\&{$_->gi}}($self, $t, $_);
1380             } else {
1381             $self->_Generic($t, $_);
1382             }
1383             }
1384             }
1385            
1386             1;