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 27     27   244 use v5.14;
  27         107  
32 27     27   1576 use strictures;
  27         116  
  27         130  
33 27     27   3411 no indirect 'fatal';
  27         126  
  27         130  
34 27     27   1261 no multidimensional;
  27         282  
  27         170  
35 27     27   868 use warnings 'once';
  27         215  
  27         806  
36              
37 27     27   174 use Carp;
  26         45  
  26         1251  
38 26     26   14119 use Storable ();
  26         67558  
  26         1106  
39              
40             our $VERSION = '0.24';
41              
42 26     26   9976 use UI::Various::language::en;
  26         100  
  26         2183  
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 26     26   149 use constant _ROOT_PACKAGE_ => substr(__PACKAGE__, 0, rindex(__PACKAGE__, "::"));
  26         196  
  26         2131  
70              
71 26         6177 use constant UI_ELEMENTS =>
72 26     26   138 qw(Box Button Check Dialog Input Listbox Main Optionmenu Radio Text Window);
  26         473  
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 26     26   183 use constant GUI_PACKAGES => qw(Tk);
  26         53  
  26         1455  
95 26     26   1060 use constant TERM_PACKAGES => qw(Curses RichTerm);
  26         95  
  26         1410  
96 26     26   125 use constant FINAL_PACKAGE => 'PoorTerm';
  26         105  
  26         1182  
97 26     26   121 use constant UNIT_TEST_PACKAGE => '_Zz_Unit_Test'; # only used in test regexp;
  26         204  
  26         1337  
98             # currently supported languages:
99 26     26   222 use constant LANGUAGES => qw(en de);
  26         175  
  26         1730  
100              
101             # logging levels (with 2 aliases):
102 26         4247 use constant LOG_LEVELS =>
103 26     26   1362 qw(FATAL ERROR WARN INFO DEBUG_1 DEBUG_2 DEBUG_3 DEBUG_4);
  26         2556  
104              
105             # which package identifier must checked with which Perl module:
106 26         1473 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 26     26   1081 );
  26         90  
116              
117 26     26   130 use constant PACKAGES => (GUI_PACKAGES, TERM_PACKAGES);
  26         85  
  26         34751  
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 362     362   4309 my ($pkg, $rh_options) = @_;
156 362         533 local $_;
157              
158             # checks (using standard croak during initialisation only!):
159 362 100       800 ref($pkg) and
160             fatal('bad_usage_of__1_pkg_is__2', __PACKAGE__, ref($pkg));
161 361 100       784 $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 360         37237 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 360         1444 my $caller = (caller())[0];
170 360 100       2545 unless ($caller eq _ROOT_PACKAGE_)
171             {
172             # Q&D: special exception to avoid failing "testpodcoverage":
173             # uncoverable branch true
174             # uncoverable condition false
175 320 50 66     1598 unless (defined(caller(4)) and (caller(4))[0] eq 'Pod::Coverage')
176             {
177             defined $UI->{ui} or
178 320 100       2857 fatal('ui_various_core_must_be_1st_used_from_ui_various');
179 319         24827 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         45 $rh_options->{use} = [];
188             }
189             }
190              
191             # check options:
192 42         251 my @packages = PACKAGES;
193 42         90 my $stderr = 0;
194 42         91 my $include = 'all';
195 42 100       208 if (defined $rh_options)
196             {
197 39 100       108 ref($rh_options) eq 'HASH' or
198             fatal('options_must_be_specified_as_hash');
199 38         207 foreach (sort keys %$rh_options)
200             {
201 61 100       232 if ($_ eq 'use')
    100          
    100          
    100          
    100          
202             {
203 24 100       88 ref($rh_options->{$_}) eq 'ARRAY' or
204             fatal('use_option_must_be_an_array_reference');
205 23         958 foreach my $ui (@{$rh_options->{$_}})
  23         93  
206             {
207 7 100       57 $ui =~ m/$re_packages/o or
208             fatal('unsupported_ui_package__1', $ui);
209             }
210 22         34 @packages = @{$rh_options->{$_}};
  22         99  
211             }
212             elsif ($_ eq 'include')
213 21         51 { $include = $rh_options->{$_}; }
214             elsif ($_ eq 'log')
215             {
216 11         30 my $level = uc($rh_options->{$_});
217 11 100       178 defined $log_level{$level} or
218             fatal('undefined_logging_level__1', $level);
219 10         33 logging($rh_options->{$_});
220             }
221             elsif ($_ eq 'language')
222             {
223             $rh_options->{$_} =~ m/$re_languages/o or
224 4 100       21 fatal('unsupported_language__1', $rh_options->{$_});
225 3         117 language($rh_options->{$_});
226             }
227             elsif ($_ eq 'stderr')
228             {
229 8 100       35 $rh_options->{$_} =~ m/^[0-3]$/ or
230             fatal('stderr_not_0_1_2_or_3');
231 7         51 $stderr = $rh_options->{$_};
232             }
233             else
234             {
235 3         13 fatal('unknown_option__1', $_);
236             }
237             }
238             }
239              
240             # now check which package can actually be used:
241 35 100       162 $ENV{UI} and unshift @packages, $ENV{UI};
242 35         973 push @packages, FINAL_PACKAGE;
243 35         103 foreach my $use (@packages)
244             {
245 61 100 100     20401 next if $use =~ m/$re_gui_packages/o and not $ENV{DISPLAY};
246 55         113 my $uipkg = $ui_map{$use};
247 55         175 debug(1, 'testing: ', $use, ' / ', $uipkg);
248 55 100       2884 if (eval "require $uipkg")
249             {
250 35         86761 info('using__1_as_ui', $use);
251 35         225 $UI->{using} = $use;
252 35 50       343 $UI->{is_gui} = $use =~ m/$re_gui_packages/o ? 1 : 0;
253 35         129 $UI->{ui} = _ROOT_PACKAGE_ . '::' . $use;
254 35         181 last;
255             }
256             }
257              
258             # now we really know how to STDERR (e.g. for value 1):
259 35         175 stderr($stderr);
260              
261             # finally we can import the automatically included modules:
262 35 100       121 if (ref($include) eq '')
263             {
264 23 100       84 if ($include eq 'all')
    100          
265 16         65 { $include = [ UI_ELEMENTS ]; }
266             elsif ($include eq 'none')
267 4         946 { $include = []; }
268             else
269 7         66 { $include = [ $include ]; }
270             }
271 35 100       91 ref($include) eq 'ARRAY' or
272             fatal('include_option_must_be_an_array_reference_or_a_scalar');
273 34         51 foreach (@{$include})
  34         217  
274             {
275 189         462 $_ = _ROOT_PACKAGE_ . '::' . $_;
276 189 100       9484 unless (eval "require $_")
277 3         159 { fatal('unsupported_ui_element__1__2', $_, $@); }
278 188         15022 $_->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 2617 my ($new_language) = @_;
296              
297 8 100       126 if (defined $new_language)
298             {
299 7 100       50 if ($new_language !~ m/$re_languages/o)
300 4         42 { error('unsupported_language__1', $new_language); }
301             else
302             {
303 5         23 $UI->{language} = $new_language;
304 5         45 local $_ = _ROOT_PACKAGE_ . '::language::' . $new_language;
305 5         1358 eval "require $_"; # require with variable needs eval!
306 5         73 $_ .= '::T';
307 26     26   188 no strict 'refs';
  26     2   200  
  26         68985  
308 5         25 $UI->{T} = \%$_;
309             }
310             }
311 8         24 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 2807 my ($new_level) = @_;
327              
328 18 100       52 if (defined $new_level)
329             {
330 17         36 local $_ = $log_level{uc($new_level)};
331 17 100       218 if (defined $_)
332 16         36 { $UI->{log} = $_; }
333             else
334 3         5 { error('undefined_logging_level__1', $new_level); }
335             }
336 18         176 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 67     67 1 1888 my ($new_value) = @_;
355              
356 67 100       330 if (defined $new_value)
357             {
358 65 100       455 if ($new_value !~ m/^[0-3]$/)
359             {
360 3         40 error('stderr_not_0_1_2_or_3');
361             }
362             else
363             {
364 64 100       1346 if ($new_value == 1)
365             {
366 4 50       62 $new_value = $UI->{is_gui} ? 0 : 2;
367             }
368 64 100       219 if ($new_value != $UI->{stderr})
369             {
370 16 100 100     60 if ($UI->{stderr} == 0 and not defined $orgerr)
371             {
372 7 100       192 unless (open $orgerr, '>&', \*STDERR)
373             {
374             # errors can't use standard messaging here:
375 3         30 print "\n***** can't duplicate STDERR: $! *****\n";
376 3         104 die;
377             }
378             }
379 15         260 close STDERR;
380 15 100       47 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   297 $orgerr);
  7 100       1350  
  7         196  
  7         4942  
385             # uncoverable branch true
386 15 50       2288 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         9 print "\n***** can't redirect STDERR: $! *****\n";
393             }
394 15         92 binmode(STDERR, ':utf8');
395 15 100 100     73 if ($UI->{stderr} == 2 and $new_value == 0)
396             {
397 5         56 print STDERR $UI->{messages};
398             }
399 15         1146 $UI->{messages} = '';
400 15         84 $UI->{stderr} = $new_value;
401             }
402             }
403             }
404 66         562 return $UI->{stderr};
405             }
406             }
407             END {
408 26     26   763085 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 114     114 1 39603 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 182     182 1 1013 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 584 my $message_id = shift;
483 46         136 local $_ = sprintf(msg($message_id), @_); # using $_ to allow debugging
484 46         6902 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 86     86 1 594 sub error($;@) { _message(1, @_); }
526 5     5 1 13 sub warning($;@) { _message(2, @_); }
527 42     42 1 3281 sub info($;@) { _message(3, @_); }
528              
529             sub _message($$;@)
530             {
531 129     129   239 my $level = shift;
532 129 100       444 return undef if $UI->{log} < $level;
533              
534 90         275 my $message_id = shift;
535 90         257 local $_ = msg($message_id);
536 90 100       453 $_ = sprintf($_, @_) unless $_ eq $message_id;
537 90 100 100     477 if ($level < 3 and $_ !~ m/\n\z/)
538 67         5369 { carp($_); }
539             else
540 23         357 { warn $_; }
541 90         50605 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 348     350 1 3363 my $level = shift;
575 348 100 100     2370 unless ($level =~ m/^\d$/ and $level > 0)
576             {
577 2         5 error('bad_debug_level__1', $level);
578 2         9 return;
579             }
580 346 100       1050 return if $UI->{log} < $level + 3;
581 3         7 local $_ = ' ' x --$level;
582 3         7 my $message = join('', @_);
583 3         7 $message =~ s/\n\z//;
584 3         4 $message =~ s/\n/\n\t$_/g;
585 3         15 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 312     314 1 3545 my ($message_id) = @_;
613              
614 312 100 100     1668 if (defined $UI->{T}{$message_id} and $UI->{T}{$message_id} ne '')
615             {
616 307         1466 return $UI->{T}{$message_id};
617             }
618             # for missing message we try a fallback to English, if possible:
619 5 100       13 if ($UI->{language} ne 'en')
620             {
621 2         6 warning('message__1_missing_in__2', $message_id, $UI->{language});
622             defined $UI::Various::language::en::T{$message_id}
623 2 100       11 and return $UI::Various::language::en::T{$message_id};
624             }
625 4         9 error('message__1_missing_in__2', $message_id, 'en');
626 4         12 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 170     172 1 7341407 local ($Storable::Deparse, $Storable::Eval) = (1, 1);
685 170     6   6721 my $attributes = Storable::dclone(shift);
  4     6   159  
  4     6   32  
  4     6   17  
  4     6   196  
  4     6   21  
  4     6   6  
  4     6   613  
  4     6   21  
  4     6   7  
  4     6   380  
  4     6   26  
  4     6   166  
  4     6   40  
  4     6   110  
  4     6   3062  
  4     6   149  
  4     2   24  
  4     2   16  
  4     2   160  
  4     2   22  
  4     2   9  
  4     2   498  
  4     2   22  
  4     2   13  
  4         310  
  4         46  
  4         112  
  4         35  
  4         138  
  4         2406  
  4         120  
  4         28  
  4         9  
  4         136  
  4         24  
  4         8  
  4         386  
  4         26  
  4         8  
  4         293  
  4         132  
  4         90  
686 170         482 my $re_allowed_parameters = shift;
687 170         309 my $self = shift;
688 170   100     806 my $class = ref($self) || $self;
689 170         290 local $_;
690              
691             # sanity checks:
692 170 100       687 ref($attributes) eq 'HASH'
693             or fatal('invalid_parameter__1_in_call_to__2',
694             '$attributes', (caller(1))[3]);
695 169 100       514 ref($re_allowed_parameters) eq ''
696             or fatal('invalid_parameter__1_in_call_to__2',
697             '$re_allowed_parameters', (caller(1))[3]);
698 168 100       1835 $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 166         3756 $class =~ s/.*:://;
704 166         568 $self = bless $attributes, ui() . '::' . $class;
705              
706             # handle optional initial attribute values:
707 166         308 my $parameters = {};
708 166 100       616 if (@_ == 1)
    100          
709             {
710 24 100       105 if (ref($_[0]) eq 'HASH')
    100          
711 22         69 { $parameters = $_[0]; }
712             elsif (ref($_[0]) eq '')
713 1         13 { fatal('invalid_scalar__1_in_call_to__2', $_[0], (caller(1))[3]); }
714             else
715             {
716 1         17 fatal('invalid_object__1_in_call_to__2',
717             ref($_[0]), (caller(1))[3]);
718             }
719             }
720             elsif (@_ % 2 != 0)
721             {
722 1         9 fatal('odd_number_of_parameters_in_initialisation_list_of__1',
723             (caller(1))[3]);
724             }
725             else
726             {
727 141         442 $parameters = {@_};
728             }
729 163         536 foreach my $key (keys %$parameters)
730             {
731 168 100       2940 $key =~ m/$re_allowed_parameters/
732             or fatal('invalid_parameter__1_in_call_to__2',
733             $key, (caller(1))[3]);
734 167 100       1725 if ($self->can("_$key"))
    100          
735 3         11 { $_ = "_$key"; $_ = $self->$_($parameters->{$key}); }
  3         15  
736             elsif ($self->can($key))
737 163         636 { $_ = $self->$key($parameters->{$key}); }
738             else
739 1         8 { $attributes->{$key} = $parameters->{$key}; }
740             }
741 161         614 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 4476     4478 1 7662 my $attribute = shift;
814 4476         4619 my $sub_set = shift;
815 4476         4700 my $self = shift;
816              
817             # sanity checks:
818 4476 100       11760 $self->isa((caller(0))[0])
819             or fatal('invalid_object__1_in_call_to__2',
820             ref($self), (caller(1))[3]);
821 4473 100 100     46354 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 4472 100       6714 if (exists $_[0])
827             {
828 302         386 my $val = shift;
829 302 100       635 local $_ = ref($val) eq 'SCALAR' ? $$val : $val;
830 302 100       508 if (defined $sub_set)
831 213 100       426 { defined &$sub_set($self, @_) or return $self->{$attribute}; }
832 291 100       594 if (ref($val) eq 'SCALAR')
833             {
834 3         8 $$val = $_;
835             # Curses needs to keep track of the references:
836 3 50       50 $self->can('_reference') and $self->_reference($val);
837             }
838             else
839 288         348 { $val = $_; }
840 291         614 $self->{$attribute} = $val;
841             }
842             return (ref($self->{$attribute}) eq 'SCALAR'
843 10         44 ? ${$self->{$attribute}}
844 4461 100       13288 : $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 8     10 1 1225 my $attribute = shift;
914 8         14 my $sub_set = shift;
915 8         18 my $self = shift;
916              
917             # sanity checks:
918 8 100       29 $self->isa((caller(0))[0])
919             or fatal('invalid_object__1_in_call_to__2',
920             ref($self), (caller(1))[3]);
921 7 100 100     159 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 6         14 my $val = shift;
927 6 100       17 local $_ = ref($val) eq 'SCALAR' ? $$val : $val;
928 6 100       16 if (defined $sub_set)
929 3 100       8 { defined &$sub_set($self, @_) or return $self->{$attribute}; }
930 5 100       25 if (ref($val) eq 'SCALAR')
931             {
932 2         4 $$val = $_;
933             # Curses needs to keep track of the references:
934 2 100       15 $self->can('_reference') and $self->_reference($val);
935             }
936             else
937 3         9 { $val = $_; }
938 5         20 $self->{$attribute} = $val;
939             return (ref($self->{$attribute}) eq 'SCALAR'
940 2         8 ? ${$self->{$attribute}}
941 5 100       25 : $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 636     638 1 2288 my $attribute = shift;
983 636         717 my $self = shift;
984              
985             # sanity checks:
986 636 100       1698 $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         12 ? ${$self->{$attribute}}
992 635 100       6855 : $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 2669 my $attribute = shift;
1042 24         141 my $self = shift;
1043              
1044             # sanity checks:
1045 24 100       59 $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       470 if (exists $_[0])
1051             {
1052 11 100       53 unless (ref($_[0]) eq 'SCALAR')
1053             {
1054 4         14 error('_1_attribute_must_be_a_2_reference',
1055             $attribute, 'SCALAR');
1056 4         14 return undef;
1057             }
1058 7         16 my $varref = shift;
1059 7         17 $self->{$attribute} = $varref;
1060             # Curses needs to keep track of the references:
1061 7 100       78 $self->can('_reference') and $self->_reference($varref);
1062             }
1063 19         32 return ${$self->{$attribute}};
  19         88  
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 3666 { my $dummy = ''; return \$dummy; }
  30         3897  
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