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   242 use v5.14;
  26         91  
32 26     26   1327 use strictures;
  26         101  
  26         130  
33 26     26   3188 no indirect 'fatal';
  26         109  
  26         109  
34 26     26   1210 no multidimensional;
  26         287  
  26         108  
35 26     26   769 use warnings 'once';
  26         196  
  26         671  
36              
37 26     26   171 use Carp;
  25         43  
  25         1140  
38 25     25   12136 use Storable ();
  25         59029  
  25         1116  
39              
40             our $VERSION = '0.23';
41              
42 25     25   9232 use UI::Various::language::en;
  25         93  
  25         1963  
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   129 use constant _ROOT_PACKAGE_ => substr(__PACKAGE__, 0, rindex(__PACKAGE__, "::"));
  25         177  
  25         2075  
70              
71 25         4199 use constant UI_ELEMENTS =>
72 25     25   115 qw(Box Button Check Dialog Input Listbox Main Radio Text Window);
  25         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 25     25   155 use constant GUI_PACKAGES => qw(Tk);
  25         45  
  25         1177  
95 25     25   966 use constant TERM_PACKAGES => qw(Curses RichTerm);
  25         76  
  25         1210  
96 25     25   123 use constant FINAL_PACKAGE => 'PoorTerm';
  25         77  
  25         1070  
97 25     25   109 use constant UNIT_TEST_PACKAGE => '_Zz_Unit_Test'; # only used in test regexp;
  25         176  
  25         1056  
98             # currently supported languages:
99 25     25   194 use constant LANGUAGES => qw(en de);
  25         167  
  25         1459  
100              
101             # logging levels (with 2 aliases):
102 25         7041 use constant LOG_LEVELS =>
103 25     25   156 qw(FATAL ERROR WARN INFO DEBUG_1 DEBUG_2 DEBUG_3 DEBUG_4);
  25         72  
104              
105             # which package identifier must checked with which Perl module:
106 25         1170 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   936 );
  25         74  
116              
117 25     25   105 use constant PACKAGES => (GUI_PACKAGES, TERM_PACKAGES);
  25         316  
  25         28794  
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   3696 my ($pkg, $rh_options) = @_;
156 340         466 local $_;
157              
158             # checks (using standard croak during initialisation only!):
159 340 100       728 ref($pkg) and
160             fatal('bad_usage_of__1_pkg_is__2', __PACKAGE__, ref($pkg));
161 339 100       701 $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         33552 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         1288 my $caller = (caller())[0];
170 338 100       2105 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     1554 unless (defined(caller(4)) and (caller(4))[0] eq 'Pod::Coverage')
176             {
177             defined $UI->{ui} or
178 299 100       2535 fatal('ui_various_core_must_be_1st_used_from_ui_various');
179 298         21407 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         43 $rh_options->{use} = [];
188             }
189             }
190              
191             # check options:
192 41         218 my @packages = PACKAGES;
193 41         57 my $stderr = 0;
194 41         52 my $include = 'all';
195 41 100       253 if (defined $rh_options)
196             {
197 38 100       101 ref($rh_options) eq 'HASH' or
198             fatal('options_must_be_specified_as_hash');
199 37         184 foreach (sort keys %$rh_options)
200             {
201 59 100       174 if ($_ eq 'use')
    100          
    100          
    100          
    100          
202             {
203 23 100       86 ref($rh_options->{$_}) eq 'ARRAY' or
204             fatal('use_option_must_be_an_array_reference');
205 22         866 foreach my $ui (@{$rh_options->{$_}})
  22         87  
206             {
207 7 100       52 $ui =~ m/$re_packages/o or
208             fatal('unsupported_ui_package__1', $ui);
209             }
210 21         36 @packages = @{$rh_options->{$_}};
  21         85  
211             }
212             elsif ($_ eq 'include')
213 20         43 { $include = $rh_options->{$_}; }
214             elsif ($_ eq 'log')
215             {
216 11         27 my $level = uc($rh_options->{$_});
217 11 100       163 defined $log_level{$level} or
218             fatal('undefined_logging_level__1', $level);
219 10         30 logging($rh_options->{$_});
220             }
221             elsif ($_ eq 'language')
222             {
223             $rh_options->{$_} =~ m/$re_languages/o or
224 4 100       22 fatal('unsupported_language__1', $rh_options->{$_});
225 3         107 language($rh_options->{$_});
226             }
227             elsif ($_ eq 'stderr')
228             {
229 8 100       34 $rh_options->{$_} =~ m/^[0-3]$/ or
230             fatal('stderr_not_0_1_2_or_3');
231 7         46 $stderr = $rh_options->{$_};
232             }
233             else
234             {
235 3         12 fatal('unknown_option__1', $_);
236             }
237             }
238             }
239              
240             # now check which package can actually be used:
241 34 100       114 $ENV{UI} and unshift @packages, $ENV{UI};
242 34         850 push @packages, FINAL_PACKAGE;
243 34         111 foreach my $use (@packages)
244             {
245 60 100 100     19637 next if $use =~ m/$re_gui_packages/o and not $ENV{DISPLAY};
246 54         110 my $uipkg = $ui_map{$use};
247 54         166 debug(1, 'testing: ', $use, ' / ', $uipkg);
248 54 100       2719 if (eval "require $uipkg")
249             {
250 34         77191 info('using__1_as_ui', $use);
251 34         197 $UI->{using} = $use;
252 34 50       349 $UI->{is_gui} = $use =~ m/$re_gui_packages/o ? 1 : 0;
253 34         109 $UI->{ui} = _ROOT_PACKAGE_ . '::' . $use;
254 34         176 last;
255             }
256             }
257              
258             # now we really know how to STDERR (e.g. for value 1):
259 34         175 stderr($stderr);
260              
261             # finally we can import the automatically included modules:
262 34 100       128 if (ref($include) eq '')
263             {
264 23 100       65 if ($include eq 'all')
    100          
265 16         64 { $include = [ UI_ELEMENTS ]; }
266             elsif ($include eq 'none')
267 4         816 { $include = []; }
268             else
269 7         54 { $include = [ $include ]; }
270             }
271 34 100       106 ref($include) eq 'ARRAY' or
272             fatal('include_option_must_be_an_array_reference_or_a_scalar');
273 33         60 foreach (@{$include})
  33         222  
274             {
275 173         409 $_ = _ROOT_PACKAGE_ . '::' . $_;
276 173 100       7833 unless (eval "require $_")
277 3         148 { fatal('unsupported_ui_element__1__2', $_, $@); }
278 172         14238 $_->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 2502 my ($new_language) = @_;
296              
297 8 100       114 if (defined $new_language)
298             {
299 7 100       43 if ($new_language !~ m/$re_languages/o)
300 4         40 { error('unsupported_language__1', $new_language); }
301             else
302             {
303 5         19 $UI->{language} = $new_language;
304 5         48 local $_ = _ROOT_PACKAGE_ . '::language::' . $new_language;
305 5         1292 eval "require $_"; # require with variable needs eval!
306 5         65 $_ .= '::T';
307 25     25   199 no strict 'refs';
  25     2   174  
  25         59141  
308 5         23 $UI->{T} = \%$_;
309             }
310             }
311 8         25 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 2336 my ($new_level) = @_;
327              
328 18 100       42 if (defined $new_level)
329             {
330 17         32 local $_ = $log_level{uc($new_level)};
331 17 100       212 if (defined $_)
332 16         34 { $UI->{log} = $_; }
333             else
334 3         5 { error('undefined_logging_level__1', $new_level); }
335             }
336 18         155 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 1638 my ($new_value) = @_;
355              
356 65 100       407 if (defined $new_value)
357             {
358 63 100       436 if ($new_value !~ m/^[0-3]$/)
359             {
360 3         42 error('stderr_not_0_1_2_or_3');
361             }
362             else
363             {
364 62 100       1300 if ($new_value == 1)
365             {
366 4 50       55 $new_value = $UI->{is_gui} ? 0 : 2;
367             }
368 62 100       189 if ($new_value != $UI->{stderr})
369             {
370 16 100 100     59 if ($UI->{stderr} == 0 and not defined $orgerr)
371             {
372 7 100       165 unless (open $orgerr, '>&', \*STDERR)
373             {
374             # errors can't use standard messaging here:
375 3         23 print "\n***** can't duplicate STDERR: $! *****\n";
376 3         79 die;
377             }
378             }
379 15         242 close STDERR;
380 15 100       48 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   249 $orgerr);
  7 100       1049  
  7         159  
  7         4053  
385             # uncoverable branch true
386 15 50       1840 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         8 print "\n***** can't redirect STDERR: $! *****\n";
393             }
394 15         84 binmode(STDERR, ':utf8');
395 15 100 100     73 if ($UI->{stderr} == 2 and $new_value == 0)
396             {
397 5         47 print STDERR $UI->{messages};
398             }
399 15         998 $UI->{messages} = '';
400 15         73 $UI->{stderr} = $new_value;
401             }
402             }
403             }
404 64         467 return $UI->{stderr};
405             }
406             }
407             END {
408 25     25   785094 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 33477 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 174     174 1 998 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 479 my $message_id = shift;
483 46         111 local $_ = sprintf(msg($message_id), @_); # using $_ to allow debugging
484 46         5626 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 79     79 1 504 sub error($;@) { _message(1, @_); }
526 5     5 1 12 sub warning($;@) { _message(2, @_); }
527 41     41 1 2673 sub info($;@) { _message(3, @_); }
528              
529             sub _message($$;@)
530             {
531 121     121   201 my $level = shift;
532 121 100       433 return undef if $UI->{log} < $level;
533              
534 83         178 my $message_id = shift;
535 83         249 local $_ = msg($message_id);
536 83 100       348 $_ = sprintf($_, @_) unless $_ eq $message_id;
537 83 100 100     380 if ($level < 3 and $_ !~ m/\n\z/)
538 62         4255 { carp($_); }
539             else
540 21         236 { warn $_; }
541 83         40341 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 338     340 1 2815 my $level = shift;
575 338 100 100     2026 unless ($level =~ m/^\d$/ and $level > 0)
576             {
577 2         7 error('bad_debug_level__1', $level);
578 2         7 return;
579             }
580 336 100       962 return if $UI->{log} < $level + 3;
581 3         6 local $_ = ' ' x --$level;
582 3         8 my $message = join('', @_);
583 3         7 $message =~ s/\n\z//;
584 3         4 $message =~ s/\n/\n\t$_/g;
585 3         12 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 299     301 1 3042 my ($message_id) = @_;
613              
614 299 100 100     1334 if (defined $UI->{T}{$message_id} and $UI->{T}{$message_id} ne '')
615             {
616 294         1221 return $UI->{T}{$message_id};
617             }
618             # for missing message we try a fallback to English, if possible:
619 5 100       12 if ($UI->{language} ne 'en')
620             {
621 2         5 warning('message__1_missing_in__2', $message_id, $UI->{language});
622             defined $UI::Various::language::en::T{$message_id}
623 2 100       10 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 163     165 1 7154318 local ($Storable::Deparse, $Storable::Eval) = (1, 1);
685 163     6   5585 my $attributes = Storable::dclone(shift);
  4     6   157  
  4     6   25  
  4     6   11  
  4     6   174  
  4     6   19  
  4     6   11  
  4     6   515  
  4     6   21  
  4     6   11  
  4     6   393  
  4     6   21  
  4     6   114  
  4     6   56  
  4     6   99  
  4     6   2638  
  4     6   124  
  4     2   21  
  4     2   10  
  4     2   131  
  4     2   17  
  4     2   8  
  4     2   406  
  4     2   22  
  4     2   8  
  4         260  
  4         16  
  4         92  
  4         24  
  4         106  
  4         1874  
  4         104  
  4         17  
  4         8  
  4         101  
  4         16  
  4         7  
  4         370  
  4         21  
  4         6  
  4         250  
  4         91  
  4         83  
686 163         462 my $re_allowed_parameters = shift;
687 163         704 my $self = shift;
688 163   100     741 my $class = ref($self) || $self;
689 163         293 local $_;
690              
691             # sanity checks:
692 163 100       507 ref($attributes) eq 'HASH'
693             or fatal('invalid_parameter__1_in_call_to__2',
694             '$attributes', (caller(1))[3]);
695 162 100       366 ref($re_allowed_parameters) eq ''
696             or fatal('invalid_parameter__1_in_call_to__2',
697             '$re_allowed_parameters', (caller(1))[3]);
698 161 100       1417 $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 159         3298 $class =~ s/.*:://;
704 159         470 $self = bless $attributes, ui() . '::' . $class;
705              
706             # handle optional initial attribute values:
707 159         277 my $parameters = {};
708 159 100       1135 if (@_ == 1)
    100          
709             {
710 24 100       80 if (ref($_[0]) eq 'HASH')
    100          
711 22         37 { $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         5 fatal('invalid_object__1_in_call_to__2',
717             ref($_[0]), (caller(1))[3]);
718             }
719             }
720             elsif (@_ % 2 != 0)
721             {
722 1         8 fatal('odd_number_of_parameters_in_initialisation_list_of__1',
723             (caller(1))[3]);
724             }
725             else
726             {
727 134         331 $parameters = {@_};
728             }
729 156         509 foreach my $key (keys %$parameters)
730             {
731 160 100       2276 $key =~ m/$re_allowed_parameters/
732             or fatal('invalid_parameter__1_in_call_to__2',
733             $key, (caller(1))[3]);
734 159 100       1765 if ($self->can("_$key"))
    100          
735 2         8 { $_ = "_$key"; $_ = $self->$_($parameters->{$key}); }
  2         15  
736             elsif ($self->can($key))
737 156         559 { $_ = $self->$key($parameters->{$key}); }
738             else
739 1         3 { $attributes->{$key} = $parameters->{$key}; }
740             }
741 154         558 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 4368     4370 1 7174 my $attribute = shift;
814 4368         4057 my $sub_set = shift;
815 4368         3918 my $self = shift;
816              
817             # sanity checks:
818 4368 100       10346 $self->isa((caller(0))[0])
819             or fatal('invalid_object__1_in_call_to__2',
820             ref($self), (caller(1))[3]);
821 4365 100 100     39664 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 4364 100       5677 if (exists $_[0])
827             {
828 292         330 my $val = shift;
829 292 100       566 local $_ = ref($val) eq 'SCALAR' ? $$val : $val;
830 292 100       426 if (defined $sub_set)
831 204 100       405 { defined &$sub_set($self, @_) or return $self->{$attribute}; }
832 283 100       502 if (ref($val) eq 'SCALAR')
833             {
834 3         6 $$val = $_;
835             # Curses needs to keep track of the references:
836 3 50       114 $self->can('_reference') and $self->_reference($val);
837             }
838             else
839 280         317 { $val = $_; }
840 283         639 $self->{$attribute} = $val;
841             }
842             return (ref($self->{$attribute}) eq 'SCALAR'
843 10         42 ? ${$self->{$attribute}}
844 4355 100       11560 : $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 1209 my $attribute = shift;
914 7         12 my $sub_set = shift;
915 7         12 my $self = shift;
916              
917             # sanity checks:
918 7 100       17 $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     106 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         9 my $val = shift;
927 5 100       13 local $_ = ref($val) eq 'SCALAR' ? $$val : $val;
928 5 100       11 if (defined $sub_set)
929 3 100       6 { defined &$sub_set($self, @_) or return $self->{$attribute}; }
930 4 100       14 if (ref($val) eq 'SCALAR')
931             {
932 2         3 $$val = $_;
933             # Curses needs to keep track of the references:
934 2 100       12 $self->can('_reference') and $self->_reference($val);
935             }
936             else
937 2         3 { $val = $_; }
938 4         17 $self->{$attribute} = $val;
939             return (ref($self->{$attribute}) eq 'SCALAR'
940 2         6 ? ${$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 2311 my $attribute = shift;
983 617         632 my $self = shift;
984              
985             # sanity checks:
986 617 100       1418 $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         11 ? ${$self->{$attribute}}
992 616 100       5769 : $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 2450 my $attribute = shift;
1042 24         121 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       537 if (exists $_[0])
1051             {
1052 11 100       41 unless (ref($_[0]) eq 'SCALAR')
1053             {
1054 4         13 error('_1_attribute_must_be_a_2_reference',
1055             $attribute, 'SCALAR');
1056 4         11 return undef;
1057             }
1058 7         22 my $varref = shift;
1059 7         15 $self->{$attribute} = $varref;
1060             # Curses needs to keep track of the references:
1061 7 100       62 $self->can('_reference') and $self->_reference($varref);
1062             }
1063 19         35 return ${$self->{$attribute}};
  19         86  
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 3464 { my $dummy = ''; return \$dummy; }
  30         3358  
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