File Coverage

blib/lib/UI/Various/core.pm
Criterion Covered Total %
statement 314 314 100.0
branch 161 166 98.1
condition 29 30 100.0
subroutine 66 66 100.0
pod 17 17 100.0
total 587 593 99.4


line stmt bran cond sub pod time code
1             package UI::Various::core;
2              
3             # Author, Copyright and License: see end of file
4              
5             =head1 NAME
6              
7             UI::Various::core - core functions of L
8              
9             =head1 SYNOPSIS
10              
11             # This module should never be used directly!
12             # It is used indirectly via the following:
13             use UI::Various;
14              
15             =head1 ABSTRACT
16              
17             This module is the main worker module for the L package.
18              
19             =head1 DESCRIPTION
20              
21             The documentation of this module is mainly intended for developers of the
22             package itself.
23              
24             Basically the module is a singleton providing a set of functions to be used
25             by the other modules of L.
26              
27             =cut
28              
29             #########################################################################
30              
31 26     26   252 use v5.14;
  26         101  
32 26     26   1537 use strictures;
  26         118  
  26         160  
33 26     26   3437 no indirect 'fatal';
  26         118  
  26         117  
34 26     26   1340 no multidimensional;
  26         276  
  26         128  
35 26     26   900 use warnings 'once';
  26         223  
  26         783  
36              
37 26     26   185 use Carp;
  25         44  
  25         1204  
38 25     25   14704 use Storable ();
  25         67536  
  25         1115  
39              
40             our $VERSION = '0.22';
41              
42 25     25   10518 use UI::Various::language::en;
  25         101  
  25         2316  
43              
44             #########################################################################
45              
46             =head1 EXPORT
47              
48             No data structures are exported, the core module is only accessed via its
49             functions (and initialised with the L
50             UI::Various package> method indirectly called via C).
51              
52             =cut
53              
54             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
55              
56             require Exporter;
57              
58             our @ISA = qw(Exporter);
59             # 1st row: public functions of the package UI::Various
60             # 2nd/3rd row: internal functions of the package UI::Various
61             our @EXPORT = qw(language logging stderr using
62             fatal error warning info debug msg
63             construct access set get access_varref dummy_varref);
64              
65             #########################################################################
66             #
67             # internal constants and data:
68              
69 25     25   148 use constant _ROOT_PACKAGE_ => substr(__PACKAGE__, 0, rindex(__PACKAGE__, "::"));
  25         200  
  25         2157  
70              
71 25         4806 use constant UI_ELEMENTS =>
72 25     25   139 qw(Box Button Check Dialog Input Listbox Main Radio Text Window);
  25         511  
73              
74             our @CARP_NOT =
75             ( _ROOT_PACKAGE_,
76             map {( _ROOT_PACKAGE_ . '::' . $_ )}
77             (qw(core base container),
78             map {( $_, "Tk::$_", "Curses::$_", "RichTerm::$_", "PoorTerm::$_" )}
79             UI_ELEMENTS)
80             );
81              
82             # global data-structure holding internal configuration:
83             my $UI =
84             {
85             log => 1, # see constant array LOG_LEVELS below
86             language => 'en',
87             stderr => 0, # 0: immediate, 2: on exit, 3: suppress
88             messages => '', # stored messages
89             T # reference to all text strings
90             => \%UI::Various::language::en::T,
91             };
92              
93             # currently supported packages (GUI, terminal-based and last-resort):
94 25     25   188 use constant GUI_PACKAGES => qw(Tk);
  25         49  
  25         1487  
95 25     25   1112 use constant TERM_PACKAGES => qw(Curses RichTerm);
  25         87  
  25         1470  
96 25     25   137 use constant FINAL_PACKAGE => 'PoorTerm';
  25         137  
  25         1203  
97 25     25   126 use constant UNIT_TEST_PACKAGE => '_Zz_Unit_Test'; # only used in test regexp;
  25         200  
  25         1288  
98             # currently supported languages:
99 25     25   218 use constant LANGUAGES => qw(en de);
  25         170  
  25         1742  
100              
101             # logging levels (with 2 aliases):
102 25         8643 use constant LOG_LEVELS =>
103 25     25   190 qw(FATAL ERROR WARN INFO DEBUG_1 DEBUG_2 DEBUG_3 DEBUG_4);
  25         44  
104              
105             # which package identifier must checked with which Perl module:
106 25         1381 use constant PACKAGE_MAP =>
107             ('Tk' => 'Tk',
108             'Curses' => 'Curses::UI',
109             # note that both *Term use only Perl core modules, so both should load
110             # successful with those examples here:
111             'RichTerm' => 'Term::ANSIColor',
112             'PoorTerm' => 'Term::ReadLine',
113             # this dummy package is only used for failing unit tests:
114             '_Zz_Unit_Test' => 'ZZ::Unit::Test',
115 25     25   1101 );
  25         91  
116              
117 25     25   128 use constant PACKAGES => (GUI_PACKAGES, TERM_PACKAGES);
  25         81  
  25         34127  
118              
119             my $re_languages = '^' . join('|', LANGUAGES) . '$';
120             my %log_level = ();
121             {
122             my $n = 0;
123             %log_level = map { ($_ => $n++) } LOG_LEVELS;
124             }
125             $log_level{WARNING} = $log_level{WARN};
126             $log_level{INFORMATION} = $log_level{INFO};
127              
128             #########################################################################
129             #########################################################################
130              
131             =head1 METHODS and FUNCTIONS
132              
133             =cut
134              
135             #########################################################################
136              
137             =head2 B - initialisation of L package
138              
139             see L
140             UI::Various package>
141              
142             Otherwise this method just exports the core functions to our other modules.
143              
144             =cut
145              
146             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
147             {
148             my $re_packages =
149             '^' . join('|', PACKAGES, FINAL_PACKAGE, UNIT_TEST_PACKAGE) . '$';
150             my $re_gui_packages = '^' . join('|', GUI_PACKAGES) . '$';
151             my %ui_map = PACKAGE_MAP;
152              
153             sub import($;%)
154             {
155 340     340   4889 my ($pkg, $rh_options) = @_;
156 340         590 local $_;
157              
158             # checks (using standard croak during initialisation only!):
159 340 100       855 ref($pkg) and
160             fatal('bad_usage_of__1_pkg_is__2', __PACKAGE__, ref($pkg));
161 339 100       821 $pkg eq __PACKAGE__ or
162             fatal('bad_usage_of__1_as__2', __PACKAGE__, $pkg);
163              
164             # manual export as we use own import method:
165 338         40240 UI::Various::core->export_to_level(1, $pkg, @EXPORT);
166              
167             # unless during initialisation in main module we ignore options and
168             # check only that we are already initialised:
169 338         1590 my $caller = (caller())[0];
170 338 100       2603 unless ($caller eq _ROOT_PACKAGE_)
171             {
172             # Q&D: special exception to avoid failing "testpodcoverage":
173             # uncoverable branch true
174             # uncoverable condition false
175 299 50 66     1762 unless (defined(caller(4)) and (caller(4))[0] eq 'Pod::Coverage')
176             {
177             defined $UI->{ui} or
178 299 100       2878 fatal('ui_various_core_must_be_1st_used_from_ui_various');
179 298         26381 return;
180             }
181             else # else needed for correct coverage handling
182             {
183             # needed for the "require" in other modules' "testpodcoverage",
184             # in addition it sometimes is counted by coverage without being
185             # run at all (that's what the 'not' is for):
186             # uncoverable not statement
187 2         49 $rh_options->{use} = [];
188             }
189             }
190              
191             # check options:
192 41         268 my @packages = PACKAGES;
193 41         76 my $stderr = 0;
194 41         68 my $include = 'all';
195 41 100       281 if (defined $rh_options)
196             {
197 38 100       126 ref($rh_options) eq 'HASH' or
198             fatal('options_must_be_specified_as_hash');
199 37         286 foreach (sort keys %$rh_options)
200             {
201 59 100       259 if ($_ eq 'use')
    100          
    100          
    100          
    100          
202             {
203 23 100       96 ref($rh_options->{$_}) eq 'ARRAY' or
204             fatal('use_option_must_be_an_array_reference');
205 22         941 foreach my $ui (@{$rh_options->{$_}})
  22         99  
206             {
207 7 100       105 $ui =~ m/$re_packages/o or
208             fatal('unsupported_ui_package__1', $ui);
209             }
210 21         56 @packages = @{$rh_options->{$_}};
  21         102  
211             }
212             elsif ($_ eq 'include')
213 20         50 { $include = $rh_options->{$_}; }
214             elsif ($_ eq 'log')
215             {
216 11         33 my $level = uc($rh_options->{$_});
217 11 100       189 defined $log_level{$level} or
218             fatal('undefined_logging_level__1', $level);
219 10         35 logging($rh_options->{$_});
220             }
221             elsif ($_ eq 'language')
222             {
223             $rh_options->{$_} =~ m/$re_languages/o or
224 4 100       44 fatal('unsupported_language__1', $rh_options->{$_});
225 3         124 language($rh_options->{$_});
226             }
227             elsif ($_ eq 'stderr')
228             {
229 8 100       46 $rh_options->{$_} =~ m/^[0-3]$/ or
230             fatal('stderr_not_0_1_2_or_3');
231 7         54 $stderr = $rh_options->{$_};
232             }
233             else
234             {
235 3         16 fatal('unknown_option__1', $_);
236             }
237             }
238             }
239              
240             # now check which package can actually be used:
241 34 100       152 $ENV{UI} and unshift @packages, $ENV{UI};
242 34         964 push @packages, FINAL_PACKAGE;
243 34         127 foreach my $use (@packages)
244             {
245 60 100 100     23010 next if $use =~ m/$re_gui_packages/o and not $ENV{DISPLAY};
246 54         143 my $uipkg = $ui_map{$use};
247 54         205 debug(1, 'testing: ', $use, ' / ', $uipkg);
248 54 100       3159 if (eval "require $uipkg")
249             {
250 34         89592 info('using__1_as_ui', $use);
251 34         233 $UI->{using} = $use;
252 34 50       423 $UI->{is_gui} = $use =~ m/$re_gui_packages/o ? 1 : 0;
253 34         141 $UI->{ui} = _ROOT_PACKAGE_ . '::' . $use;
254 34         248 last;
255             }
256             }
257              
258             # now we really know how to STDERR (e.g. for value 1):
259 34         218 stderr($stderr);
260              
261             # finally we can import the automatically included modules:
262 34 100       137 if (ref($include) eq '')
263             {
264 23 100       78 if ($include eq 'all')
    100          
265 16         91 { $include = [ UI_ELEMENTS ]; }
266             elsif ($include eq 'none')
267 4         970 { $include = []; }
268             else
269 7         61 { $include = [ $include ]; }
270             }
271 34 100       131 ref($include) eq 'ARRAY' or
272             fatal('include_option_must_be_an_array_reference_or_a_scalar');
273 33         54 foreach (@{$include})
  33         320  
274             {
275 173         495 $_ = _ROOT_PACKAGE_ . '::' . $_;
276 173 100       9740 unless (eval "require $_")
277 3         164 { fatal('unsupported_ui_element__1__2', $_, $@); }
278 172         16615 $_->import;
279             }
280             }
281             }
282              
283             #########################################################################
284              
285             =head2 B - get or set currently used language
286              
287             internal implementation of L
288             get or set currently used language>
289              
290             =cut
291              
292             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
293             sub language(;$)
294             {
295 8     8 1 3352 my ($new_language) = @_;
296              
297 8 100       144 if (defined $new_language)
298             {
299 7 100       62 if ($new_language !~ m/$re_languages/o)
300 4         48 { error('unsupported_language__1', $new_language); }
301             else
302             {
303 5         23 $UI->{language} = $new_language;
304 5         40 local $_ = _ROOT_PACKAGE_ . '::language::' . $new_language;
305 5         1298 eval "require $_"; # require with variable needs eval!
306 5         78 $_ .= '::T';
307 25     25   231 no strict 'refs';
  25     2   204  
  25         69044  
308 5         30 $UI->{T} = \%$_;
309             }
310             }
311 8         33 return $UI->{language};
312             }
313              
314             #########################################################################
315              
316             =head2 B - get or set currently used logging-level
317              
318             internal implementation of L
319             get or set currently used logging-level>
320              
321             =cut
322              
323             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
324             sub logging(;$)
325             {
326 18     18 1 3639 my ($new_level) = @_;
327              
328 18 100       56 if (defined $new_level)
329             {
330 17         45 local $_ = $log_level{uc($new_level)};
331 17 100       210 if (defined $_)
332 16         46 { $UI->{log} = $_; }
333             else
334 3         7 { error('undefined_logging_level__1', $new_level); }
335             }
336 18         189 return (LOG_LEVELS)[$UI->{log}];
337             }
338              
339             #########################################################################
340              
341             =head2 B - get or set currently used handling of output
342              
343             internal implementation of L
344             get or set currently used handling of output>
345              
346             =cut
347              
348             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
349             {
350             my $orgerr = undef;
351              
352             sub stderr(;$)
353             {
354 65     65 1 2333 my ($new_value) = @_;
355              
356 65 100       380 if (defined $new_value)
357             {
358 63 100       469 if ($new_value !~ m/^[0-3]$/)
359             {
360 3         32 error('stderr_not_0_1_2_or_3');
361             }
362             else
363             {
364 62 100       1140 if ($new_value == 1)
365             {
366 4 50       65 $new_value = $UI->{is_gui} ? 0 : 2;
367             }
368 62 100       228 if ($new_value != $UI->{stderr})
369             {
370 16 100 100     79 if ($UI->{stderr} == 0 and not defined $orgerr)
371             {
372 7 100       245 unless (open $orgerr, '>&', \*STDERR)
373             {
374             # errors can't use standard messaging here:
375 3         28 print "\n***** can't duplicate STDERR: $! *****\n";
376 3         105 die;
377             }
378             }
379 15         267 close STDERR;
380 15 100       58 my $rop = $new_value == 0 ? '>&' : '>>';
381             my $rc =
382             open STDERR, $rop, ($new_value == 3 ? '/dev/null' :
383             $new_value == 2 ? \$UI->{messages} :
384 15 100   9   397 $orgerr);
  7 100       1533  
  7         189  
  7         5517  
385             # uncoverable branch true
386 15 50       2629 if ($rc == 0)
387             {
388             # errors can't use standard messaging here (like
389             # above we have a paradox; the statement is covered
390             # while the branch is not):
391             # uncoverable not statement
392 2         10 print "\n***** can't redirect STDERR: $! *****\n";
393             }
394 15         106 binmode(STDERR, ':utf8');
395 15 100 100     89 if ($UI->{stderr} == 2 and $new_value == 0)
396             {
397 5         46 print STDERR $UI->{messages};
398             }
399 15         950 $UI->{messages} = '';
400 15         95 $UI->{stderr} = $new_value;
401             }
402             }
403             }
404 64         533 return $UI->{stderr};
405             }
406             }
407             END {
408 25     25   773215 stderr(0);
409             }
410              
411             #########################################################################
412              
413             =head2 B - get currently used UI as text string
414              
415             internal implementation of L
416             currently used UI>
417              
418             =cut
419              
420             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
421             sub using()
422             {
423 105     105 1 41685 return $UI->{using};
424             }
425              
426             #########################################################################
427              
428             =head2 B - get currently used UI
429              
430             $interface = UI::Various::core::ui();
431              
432             =head3 example:
433              
434             $_ = UI::Various::core::ui() . '::Main::_init';
435             { no strict 'refs'; &$_($self); }
436              
437             =head3 description:
438              
439             This function returns the full name of the currently used user interface,
440             e.g. to access its methods.
441              
442             =head3 returns:
443              
444             full name of UI
445              
446             =cut
447              
448             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
449             sub ui(;$)
450             {
451 173     173 1 945 return $UI->{ui};
452             }
453              
454             #########################################################################
455              
456             =head2 B - abort with error message
457              
458             fatal($message_id, @message_data);
459              
460             =head3 example:
461              
462             fatal('bad_usage_of__1_as__2', __PACKAGE__, $pkg);
463             fatal('UI__Various__core_must_be_1st_used_from_UI__Various');
464              
465             =head3 parameters:
466              
467             $message_id ID of the text or format string in language module
468             @message_data optional additional text data for format string
469              
470             =head3 description:
471              
472             This function looks up the format (or simple) string passed in
473             C<$message_id> in the text hash of the currently used language, formats it
474             together with the C<@message_data> with sprintf and passes it on to
475             C>.
476              
477             =cut
478              
479             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
480             sub fatal($;@)
481             {
482 46     46 1 477 my $message_id = shift;
483 46         144 local $_ = sprintf(msg($message_id), @_); # using $_ to allow debugging
484 46         7260 croak($_);
485             }
486              
487             #########################################################################
488              
489             =head2 B / B / B - print error / warning / info message
490              
491             error($message_id, @message_data);
492             warning($message_id, @message_data);
493             info($message_id, @message_data);
494              
495             =head3 example:
496              
497             warning(1, 'message__1_missing_in__2', $message_id, $UI->{language});
498              
499             =head3 parameters:
500              
501             $message_id ID of the text or format string in language module
502             @message_data optional additional text data for format string
503              
504             =head3 description:
505              
506             If the current logging level is lower than C / C / C
507             these function do nothing. Otherwise they print the formatted message using
508             C<_message>.
509              
510             C<_message> has logging level to be printed as additional 1st parameter. It
511             checks the logging level, looks up the format (or simple) string passed in
512             C<$message_id> in the text hash of the currently used language, formats the
513             latter together with the C<@message_data> with sprintf and passes it on to
514             C> (in case of errors or warnings) or C>
515             (in case of informational messages).
516              
517             =head3 returns:
518              
519             always C (to allow something like C indicating
520             the error to the caller)
521              
522             =cut
523              
524             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
525 78     78 1 762 sub error($;@) { _message(1, @_); }
526 5     5 1 13 sub warning($;@) { _message(2, @_); }
527 41     41 1 3496 sub info($;@) { _message(3, @_); }
528              
529             sub _message($$;@)
530             {
531 120     120   264 my $level = shift;
532 120 100       454 return undef if $UI->{log} < $level;
533              
534 82         230 my $message_id = shift;
535 82         321 local $_ = msg($message_id);
536 82 100       457 $_ = sprintf($_, @_) unless $_ eq $message_id;
537 82 100 100     496 if ($level < 3 and $_ !~ m/\n\z/)
538 61         5209 { carp($_); }
539             else
540 21         349 { warn $_; }
541 82         46744 return undef;
542             }
543              
544             #########################################################################
545              
546             =head2 B - print debugging message
547              
548             debug($level, @message);
549              
550             =head3 example:
551              
552             debug(1, __PACKAGE__, '::new');
553              
554             =head3 parameters:
555              
556             $level debug-level of the message (>= 1)
557             @message the text to be printed
558              
559             =head3 description:
560              
561             If the current logging level is lower than C (with C being the
562             C<$level> specified in the call) this function does nothing. Otherwise it
563             prints the given text. Note that debugging messages are always English, so
564             they can be added / removed / changed anytime without bothering about the
565             C modules. Also note that debug messages are printed
566             with C> and prefixed with C and some blanks
567             according to the debug-level.
568              
569             =cut
570              
571             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
572             sub debug($$;@)
573             {
574 337     339 1 3531 my $level = shift;
575 337 100 100     2386 unless ($level =~ m/^\d$/ and $level > 0)
576             {
577 2         8 error('bad_debug_level__1', $level);
578 2         9 return;
579             }
580 335 100       1121 return if $UI->{log} < $level + 3;
581 3         10 local $_ = ' ' x --$level;
582 3         7 my $message = join('', @_);
583 3         8 $message =~ s/\n\z//;
584 3         6 $message =~ s/\n/\n\t$_/g;
585 3         16 warn "DEBUG\t", $_, $message, "\n";
586             }
587              
588             #########################################################################
589              
590             =head2 B - look-up text for currently used language
591              
592             $message = msg($message_id);
593              
594             =head3 example:
595              
596             $_ = sprintf(msg($message_id), @_);
597              
598             =head3 parameters:
599              
600             $message_id ID of the text or format string in language module
601              
602             =head3 description:
603              
604             This method looks up the format (or simple) string passed in C<$message_id>
605             in the text hash of the currently used language and returns it.
606              
607             =cut
608              
609             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
610             sub msg($)
611             {
612 298     300 1 4534 my ($message_id) = @_;
613              
614 298 100 100     1731 if (defined $UI->{T}{$message_id} and $UI->{T}{$message_id} ne '')
615             {
616 293         1479 return $UI->{T}{$message_id};
617             }
618             # for missing message we try a fallback to English, if possible:
619 5 100       19 if ($UI->{language} ne 'en')
620             {
621 2         7 warning('message__1_missing_in__2', $message_id, $UI->{language});
622             defined $UI::Various::language::en::T{$message_id}
623 2 100       14 and return $UI::Various::language::en::T{$message_id};
624             }
625 4         16 error('message__1_missing_in__2', $message_id, 'en');
626 4         18 return $message_id;
627             }
628              
629             #########################################################################
630              
631             =head2 B - common constructor for UI elements
632              
633             $ui_element = UI::Various::Element->new(%attributes);
634              
635             =head3 example:
636              
637             $ui_element = UI::Various::Element->new();
638             $ui_element = UI::Various::Element->new(attr1 => $val1, attr2 => $val2);
639             $ui_element = UI::Various::Element->new({attr1 => $val1, attr2 => $val2});
640              
641             =head3 parameters:
642              
643             %attributes optional hash with initial attribute values
644              
645             =head3 description:
646              
647             This function contains the common constructor code of all UI element classes
648             ( C). Initial values can either be passed as an array
649             of key/value pairs or as a single reference to a hash containing those
650             key/value pairs. Note that if the class defines a (private) setter method
651             C<_attr> (tried 1st) or a (public) accessor C (tried 2nd), it is used
652             to assign the value before falling back to a simple assignment.
653              
654             The internal implementation has the following interface:
655              
656             $self = construct($attributes, $re_allowed_params, $self, @_);
657              
658             It is used like this:
659              
660             sub new($;\[@$])
661             {
662             return construct({ DEFAULT_ATTRIBUTES },
663             '^(?:' . join('|', ALLOWED_PARAMETERS) . ')$',
664             @_);
665             }
666              
667             The additional parameters are:
668              
669             $attributes reference to hash with default attributes
670             $re_allowed_params regular expression matching all allowed parameters
671              
672             $self name of class or reference to other element of class
673             @_ parameters passed to caller's C
674              
675             =head3 returns:
676              
677             blessed new UI element
678              
679             =cut
680              
681             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
682             sub construct($$@) # not $$$@, that may put $self in wrong context!
683             {
684 162     164 1 7066340 local ($Storable::Deparse, $Storable::Eval) = (1, 1);
685 162     6   6533 my $attributes = Storable::dclone(shift);
  4     6   183  
  4     6   31  
  4     6   10  
  4     6   198  
  4     6   28  
  4     6   15  
  4     6   700  
  4     6   26  
  4     6   6  
  4     6   422  
  4     6   33  
  4     6   154  
  4     6   73  
  4     6   106  
  4     6   3034  
  4     6   180  
  4     2   26  
  4     2   10  
  4     2   171  
  4     2   24  
  4     2   7  
  4     2   495  
  4     2   29  
  4     2   9  
  4         312  
  4         19  
  4         115  
  4         71  
  4         135  
  4         2558  
  4         164  
  4         31  
  4         10  
  4         137  
  4         20  
  4         7  
  4         420  
  4         23  
  4         7  
  4         270  
  4         171  
  4         101  
686 162         472 my $re_allowed_parameters = shift;
687 162         291 my $self = shift;
688 162   100     724 my $class = ref($self) || $self;
689 162         303 local $_;
690              
691             # sanity checks:
692 162 100       548 ref($attributes) eq 'HASH'
693             or fatal('invalid_parameter__1_in_call_to__2',
694             '$attributes', (caller(1))[3]);
695 161 100       491 ref($re_allowed_parameters) eq ''
696             or fatal('invalid_parameter__1_in_call_to__2',
697             '$re_allowed_parameters', (caller(1))[3]);
698 160 100       1461 $self->isa((caller(0))[0])
699             or fatal('invalid_object__1_in_call_to__2',
700             ref($self), (caller(1))[3]);
701              
702             # create (correct!) object:
703 158         3507 $class =~ s/.*:://;
704 158         506 $self = bless $attributes, ui() . '::' . $class;
705              
706             # handle optional initial attribute values:
707 158         329 my $parameters = {};
708 158 100       602 if (@_ == 1)
    100          
709             {
710 24 100       87 if (ref($_[0]) eq 'HASH')
    100          
711 22         41 { $parameters = $_[0]; }
712             elsif (ref($_[0]) eq '')
713 1         4 { fatal('invalid_scalar__1_in_call_to__2', $_[0], (caller(1))[3]); }
714             else
715             {
716 1         8 fatal('invalid_object__1_in_call_to__2',
717             ref($_[0]), (caller(1))[3]);
718             }
719             }
720             elsif (@_ % 2 != 0)
721             {
722 1         3 fatal('odd_number_of_parameters_in_initialisation_list_of__1',
723             (caller(1))[3]);
724             }
725             else
726             {
727 133         388 $parameters = {@_};
728             }
729 155         541 foreach my $key (keys %$parameters)
730             {
731 157 100       2809 $key =~ m/$re_allowed_parameters/
732             or fatal('invalid_parameter__1_in_call_to__2',
733             $key, (caller(1))[3]);
734 156 100       1745 if ($self->can("_$key"))
    100          
735 2         6 { $_ = "_$key"; $_ = $self->$_($parameters->{$key}); }
  2         13  
736             elsif ($self->can($key))
737 153         590 { $_ = $self->$key($parameters->{$key}); }
738             else
739 1         4 { $attributes->{$key} = $parameters->{$key}; }
740             }
741 153         605 return $self;
742             }
743              
744             #########################################################################
745              
746             =head2 B - common accessor for UI elements
747              
748             $value = $ui_element->attribute();
749             $ui_element->attribute($value);
750              
751             =head3 parameters:
752              
753             $value optional value to be set
754              
755             =head3 description:
756              
757             This function contains the common accessor code of all UI element classes (
758             C) aka implementing a combined standard getter /
759             setter. When it's called with a value, the attribute is set. In all cases
760             the current (after modification, if applicable) value is returned. If the
761             value is a SCALAR reference it is stored as reference but returned as value.
762              
763             The internal implementation has the following interface:
764              
765             $value = access($attribute, $sub_set, $self, $new_value);
766              
767             It is used like this:
768              
769             sub attribute($;$)
770             {
771             return access('attribute', sub{ ... }, @_);
772             }
773              
774             or simply
775              
776             sub attribute($;$)
777             {
778             return access('attribute', undef, @_);
779             }
780              
781             The additional parameters are:
782              
783             $attribute name of the attribute
784             $sub_set optional reference to a subroutine called when
785             the function is used as a setter (see below)
786              
787             $self reference to the class object
788             @_ the optional new value and possible other parameters
789             passed to C<$sub_set>
790              
791             The optional subroutine gets the new value passed in C<$_> and must return
792             the value to be set in C<$_> as well. To allow for complicated tests and/or
793             side-effects it gets C<$self> and possible additional parameters passed in
794             C<@_>. The return value of the subroutine itself decides, if the attribute
795             is modified: If it's C, the previous value is kept. In all other
796             cases the attribute gets the new value as defined in C<$_>. Note that the
797             subroutine gets the value even in case of a SCALAR reference.
798              
799             If no additional code is needed, the parameter can be C as in the 2nd
800             example above.
801              
802             =head3 returns:
803              
804             the current value of the attribute (SCALAR references are dereferenced)
805              
806             =cut
807              
808             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
809             sub access($$@) # not $$$;@, that may put $self in wrong context!
810             {
811             # additional parameter "attribute" is much cheaper than "(caller(0))[3]"
812             # followed by "s/^.*::_?//":
813 4365     4367 1 7270 my $attribute = shift;
814 4365         4633 my $sub_set = shift;
815 4365         4677 my $self = shift;
816              
817             # sanity checks:
818 4365 100       11769 $self->isa((caller(0))[0])
819             or fatal('invalid_object__1_in_call_to__2',
820             ref($self), (caller(1))[3]);
821 4362 100 100     44655 defined $sub_set and ref($sub_set) ne 'CODE'
822             and fatal('invalid_parameter__1_in_call_to__2',
823             '$sub_set', (caller(1))[3]);
824              
825             # handle setter part, if applicable:
826 4361 100       6599 if (exists $_[0])
827             {
828 289         429 my $val = shift;
829 289 100       624 local $_ = ref($val) eq 'SCALAR' ? $$val : $val;
830 289 100       482 if (defined $sub_set)
831 201 100       494 { defined &$sub_set($self, @_) or return $self->{$attribute}; }
832 281 100       559 if (ref($val) eq 'SCALAR')
833             {
834 3         7 $$val = $_;
835             # Curses needs to keep track of the references:
836 3 50       55 $self->can('_reference') and $self->_reference($val);
837             }
838             else
839 278         384 { $val = $_; }
840 281         680 $self->{$attribute} = $val;
841             }
842             return (ref($self->{$attribute}) eq 'SCALAR'
843 10         41 ? ${$self->{$attribute}}
844 4353 100       13565 : $self->{$attribute});
845             }
846              
847             #########################################################################
848              
849             =head2 B - common setter for UI elements
850              
851             $ui_element->attribute($value);
852              
853             =head3 parameters:
854              
855             $value mandatory value to be set
856              
857             =head3 description:
858              
859             This function contains the common setter code of all UI element classes (
860             C). Basically it's an accessor with a mandatory value
861             to be set. Like C> it
862             returns the updated value. If the value is a SCALAR reference it is
863             stored as reference but returned as value.
864              
865             The internal implementation has the following interface:
866              
867             $value = set($attribute, $sub_set, $self, $new_value);
868              
869             It is used like this:
870              
871             sub _attribute($$)
872             {
873             return set('attribute', sub{ ...; }, @_);
874             }
875              
876             or simply
877              
878             sub _attribute($$)
879             {
880             return set('attribute', undef, @_);
881             }
882              
883             The additional parameters are:
884              
885             $attribute name of the attribute
886             $sub_set optional reference to a subroutine called within the
887             setter
888              
889             $self name of class or reference to other element of class
890             @_ the new value and possible other parameters passed
891             to C<$sub_set>
892              
893             The optional subroutine gets the new value passed in C<$_> and must return
894             the value to be set in C<$_> as well. To allow for complicated tests and/or
895             side-effects it gets C<$self> and possible additional parameters passed in
896             C<@_>. The return value of the subroutine itself decides, if the attribute
897             is modified: If it's C, the previous value is kept. In all other
898             cases the attribute gets the new value as defined in C<$_>. Note that the
899             subroutine gets the value even in case of a SCALAR reference.
900              
901             If no additional code is needed, the parameter can be C as in the 2nd
902             example above.
903              
904             =head3 returns:
905              
906             the new value of the attribute (SCALAR references are dereferenced)
907              
908             =cut
909              
910             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
911             sub set($$@) # not $$$@, that may put $self in wrong context!
912             {
913 7     9 1 1145 my $attribute = shift;
914 7         13 my $sub_set = shift;
915 7         14 my $self = shift;
916              
917             # sanity checks:
918 7 100       20 $self->isa((caller(0))[0])
919             or fatal('invalid_object__1_in_call_to__2',
920             ref($self), (caller(1))[3]);
921 6 100 100     131 defined $sub_set and ref($sub_set) ne 'CODE'
922             and fatal('invalid_parameter__1_in_call_to__2',
923             '$sub_set', (caller(1))[3]);
924              
925             # handle setter part, if applicable:
926 5         8 my $val = shift;
927 5 100       11 local $_ = ref($val) eq 'SCALAR' ? $$val : $val;
928 5 100       9 if (defined $sub_set)
929 3 100       7 { defined &$sub_set($self, @_) or return $self->{$attribute}; }
930 4 100       25 if (ref($val) eq 'SCALAR')
931             {
932 2         3 $$val = $_;
933             # Curses needs to keep track of the references:
934 2 100       17 $self->can('_reference') and $self->_reference($val);
935             }
936             else
937 2         3 { $val = $_; }
938 4         20 $self->{$attribute} = $val;
939             return (ref($self->{$attribute}) eq 'SCALAR'
940 2         7 ? ${$self->{$attribute}}
941 4 100       17 : $self->{$attribute});
942             }
943              
944             #########################################################################
945              
946             =head2 B - common getter for UI elements
947              
948             $value = $ui_element->attribute();
949              
950             =head3 description:
951              
952             This function contains the common getter code of all UI element classes (
953             C), implementing a very simple getter returning the
954             current value of the attribute (but still with all sanity checks). Note
955             that if the attribute is a SCALAR reference it is nonetheless returned as
956             value. (If you really need the reference itself, access it directly as
957             C<$ui_element->{attribute}>.)
958              
959             The internal implementation has the following interface:
960              
961             $value = get($attribute, $self);
962              
963             It is used like this:
964              
965             sub attribute($) { return get('attribute', @_); }
966              
967             The additional parameters are:
968              
969             $attribute name of the attribute
970              
971             $self name of class or reference to other element of class
972              
973             =head3 returns:
974              
975             the current value of the attribute (SCALAR references are dereferenced)
976              
977             =cut
978              
979             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
980             sub get($@) # not $$, that may put $self in wrong context!
981             {
982 617     619 1 2071 my $attribute = shift;
983 617         722 my $self = shift;
984              
985             # sanity checks:
986 617 100       1569 $self->isa((caller(0))[0])
987             or fatal('invalid_object__1_in_call_to__2',
988             ref($self), (caller(1))[3]);
989              
990             return (ref($self->{$attribute}) eq 'SCALAR'
991 3         18 ? ${$self->{$attribute}}
992 616 100       6456 : $self->{$attribute});
993             }
994              
995             #########################################################################
996              
997             =head2 B - special accessor for UI elements needing SCALAR ref.
998              
999             $value = $ui_element->attribute();
1000             $ui_element->attribute(\$variable);
1001              
1002             =head3 parameters:
1003              
1004             $variable optional SCALAR reference to be set
1005              
1006             =head3 description:
1007              
1008             This function contains a variant of the common accessor L
1009             common accessor for UI elements> that is used by attributes needing a SCALAR
1010             reference to a variable. Those still always return the current value of the
1011             variable when used as getter, but the setter directly uses the SCALAR
1012             reference.
1013              
1014             The internal implementation has the following interface (note the missing
1015             subroutine):
1016              
1017             $value = access_varref($attribute, $self, $new_value);
1018              
1019             It is used like this:
1020              
1021             sub attribute($;$)
1022             {
1023             return access_varref('attribute', @_);
1024             }
1025              
1026             The additional parameters are:
1027              
1028             $attribute name of the attribute
1029             $self reference to the class object
1030             $r_variable the optional SCALAR reference
1031              
1032             =head3 returns:
1033              
1034             the current value of the attribute (the SCALAR reference is dereferenced)
1035              
1036             =cut
1037              
1038             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1039             sub access_varref($@) # not $$$;@, that may put $self in wrong context!
1040             {
1041 24     26 1 2627 my $attribute = shift;
1042 24         133 my $self = shift;
1043              
1044             # sanity checks:
1045 24 100       65 $self->isa((caller(0))[0])
1046             or fatal('invalid_object__1_in_call_to__2',
1047             ref($self), (caller(1))[3]);
1048              
1049             # handle setter part, if applicable:
1050 23 100       507 if (exists $_[0])
1051             {
1052 11 100       48 unless (ref($_[0]) eq 'SCALAR')
1053             {
1054 4         16 error('_1_attribute_must_be_a_2_reference',
1055             $attribute, 'SCALAR');
1056 4         16 return undef;
1057             }
1058 7         12 my $varref = shift;
1059 7         12 $self->{$attribute} = $varref;
1060             # Curses needs to keep track of the references:
1061 7 100       121 $self->can('_reference') and $self->_reference($varref);
1062             }
1063 19         35 return ${$self->{$attribute}};
  19         95  
1064             }
1065              
1066             #########################################################################
1067              
1068             =head2 B - create a dummy SCALAR reference
1069              
1070             $scalar = dummy_varref();
1071              
1072             =head3 description:
1073              
1074             This function returns a SCALAR reference to a dummy variable initialised
1075             with an empty string. Note that each call returns a reference to a
1076             different variable. The function can be used to initialise C
1077             constants.
1078              
1079             =head3 returns:
1080              
1081             a scalar reference to an empty variable
1082              
1083             =cut
1084              
1085             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1086 2         3 BEGIN {
1087             sub dummy_varref()
1088 30     32 1 3423 { my $dummy = ''; return \$dummy; }
  30         4029  
1089             }
1090              
1091             # TODO L8R: add option to disable sanity checks
1092              
1093             1;
1094              
1095             #########################################################################
1096             #########################################################################
1097              
1098             =head1 SEE ALSO
1099              
1100             L
1101              
1102             =head1 LICENSE
1103              
1104             Copyright (C) Thomas Dorner.
1105              
1106             This library is free software; you can redistribute it and/or modify it
1107             under the same terms as Perl itself. See LICENSE file for more details.
1108              
1109             =head1 AUTHOR
1110              
1111             Thomas Dorner Edorner (at) cpan (dot) orgE
1112              
1113             =cut