File Coverage

blib/lib/UI/Dialog/Backend.pm
Criterion Covered Total %
statement 186 479 38.8
branch 58 250 23.2
condition 33 179 18.4
subroutine 32 50 64.0
pod 18 29 62.0
total 327 987 33.1


line stmt bran cond sub pod time code
1             package UI::Dialog::Backend;
2             ###############################################################################
3             # Copyright (C) 2015 Kevin C. Krinke
4             #
5             # This library is free software; you can redistribute it and/or
6             # modify it under the terms of the GNU Lesser General Public
7             # License as published by the Free Software Foundation; either
8             # version 2.1 of the License, or (at your option) any later version.
9             #
10             # This library is distributed in the hope that it will be useful,
11             # but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13             # Lesser General Public License for more details.
14             #
15             # You should have received a copy of the GNU Lesser General Public
16             # License along with this library; if not, write to the Free Software
17             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18             ###############################################################################
19 6     6   117 use 5.006;
  6         20  
20 6     6   29 use strict;
  6         11  
  6         158  
21 6     6   28 use Carp;
  6         11  
  6         370  
22 6     6   29 use Cwd qw( abs_path );
  6         10  
  6         287  
23 6     6   31 use File::Basename;
  6         10  
  6         617  
24 6     6   174564 use Text::Wrap qw( wrap );
  6         19852  
  6         382  
25              
26             BEGIN {
27 6     6   35 use vars qw($VERSION);
  6         11  
  6         208  
28 6     6   17313 $VERSION = '1.11';
29             }
30              
31             #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
32             #: Constructor Method
33             #:
34              
35             #: not even really necessary as this class is inherited, and the constructor is
36             #: more often than not overridden by the backend inheriting it.
37             sub new {
38 0     0 0 0 my $proto = shift();
39 0   0     0 my $class = ref($proto) || $proto;
40 0 0       0 my $cfg = ((ref($_[0]) eq "HASH") ? $_[0] : (@_) ? { @_ } : {});
    0          
41 0         0 my $self = { '_opts' => $cfg };
42 0 0       0 $self->{'test_mode'} = $cfg->{'test_mode'} if exists $cfg->{'test_mode'};
43 0         0 $self->{'test_mode_result'} = '';
44 0         0 bless($self, $class);
45 0         0 return($self);
46             }
47              
48             #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
49             #: Accessory Methods
50             #:
51              
52             #: Return the path to the dialog variant binary
53             sub get_bin {
54 1 50   1 0 384 return $_[0]->{'_opts'}{'bin'} if defined $_[0]->{'_opts'}{'bin'};
55 0         0 return undef;
56             }
57              
58             #: Provide the API interface to nautilus
59             sub nautilus {
60 0     0 1 0 my $self = $_[0];
61 0   0     0 my $nautilus = $self->{'_nautilus'} || {};
62 0 0       0 unless (ref($nautilus) eq "UI::Dialog::Backend::Nautilus") {
63 0 0       0 if ($self->_find_bin('nautilus')) {
64 0 0       0 if (eval "require UI::Dialog::Backend::Nautilus; 1") {
65 0         0 require UI::Dialog::Backend::Nautilus;
66 0         0 $self->{'_nautilus'} = new UI::Dialog::Backend::Nautilus;
67             }
68             }
69             }
70 0         0 return($self->{'_nautilus'});
71             }
72              
73             #: Provide the API interface to osd_cat (aka: xosd)
74             sub xosd {
75 0     0 1 0 my $self = shift();
76 0 0       0 my @args = (@_ %2 == 0) ? (@_) : ();
77 0   0     0 my $xosd = $self->{'_xosd'} || {};
78 0 0       0 unless (ref($xosd) eq "UI::Dialog::Backend::XOSD") {
79 0 0       0 if ($self->_find_bin('osd_cat')) {
80 0 0       0 if (eval "require UI::Dialog::Backend::XOSD; 1") {
81 0         0 require UI::Dialog::Backend::XOSD;
82 0         0 $self->{'_xosd'} = new UI::Dialog::Backend::XOSD (@args);
83             }
84             }
85             }
86 0         0 return($self->{'_xosd'});
87             }
88              
89             #: Provide the API interface to notify-send
90             sub notify_send {
91 0     0 1 0 my $self = shift();
92 0 0       0 my @args = (@_ %2 == 0) ? (@_) : ();
93 0   0     0 my $notify_send = $self->{'_notify_send'} || {};
94 0 0       0 unless (ref($notify_send) eq "UI::Dialog::Backend::NotifySend") {
95 0 0       0 if ($self->_find_bin('notify-send')) {
96 0 0       0 if (eval "require UI::Dialog::Backend::NotifySend; 1") {
97 0         0 require UI::Dialog::Backend::NotifySend;
98 0         0 $self->{'_notify_send'} = new UI::Dialog::Backend::NotifySend (@args);
99             }
100             }
101             }
102 0         0 return($self->{'_notify_send'});
103             }
104              
105             #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
106             #: State Methods
107             #:
108              
109             #: enable altering of attributes
110             sub attr {
111 0     0 1 0 my $self = $_[0];
112 0         0 my $name = $_[1];
113 0 0       0 unless ($_[2]) {
114 0 0       0 return($self->{'_opts'}->{$name}) unless not $self->{'_opts'}->{$name};
115 0         0 return(undef());
116             }
117 0 0 0     0 if ($_[2] == 0 || $_[2] =~ /^NULL$/i) {
118 0         0 $self->{'_opts'}->{$name} = 0;
119             }
120             else {
121 0         0 $self->{'_opts'}->{$name} = $_[2];
122             }
123 0         0 return($self->{'_opts'}->{$name});
124             }
125              
126             #: return the last response data as an ARRAY
127             sub ra {
128 14     14 1 23 my $self = shift();
129 14 100       105 $self->_debug((join(" | ",(caller())))." > ra() > rset: ".((@_) ? "@_" : 'NULL'),3);
130 14 100       83 $self->{'_state'}->{'ra'} = ($_[0] =~ /^null$/i) ? [ 0 ] : [ @_ ] unless not @_;
    100          
131 14         28 my $aref = $self->{'_state'}->{'ra'};
132 14 50       36 ref($aref) eq "ARRAY" or $aref = [];
133 14         16 return(@{$aref});
  14         39  
134             }
135              
136             #: return the last response data as a SCALAR
137             sub rs {
138 12     12 1 17 my $self = shift();
139 12         20 my $rset = $_[0];
140 12 100       66 $self->_debug((join(" | ",(caller())))." > rs() > rset: ".(($rset) ? $rset : 'NULL'),3);
141 12 100       48 $self->{'_state'}->{'rs'} = ($rset =~ /^null$/i) ? 0 : $rset unless not $rset;
    100          
142 12         31 return($self->{'_state'}->{'rs'});
143             }
144              
145             #: return the last exit code as a SCALAR
146             sub rv {
147 11     11 1 15 my $self = shift();
148 11         18 my $rset = $_[0];
149 11 50       59 $self->_debug((join(" | ",(caller())))." > rv() > rset: ".(($rset) ? $rset : 'NULL'),3);
150 11 50       57 $self->{'_state'}->{'rv'} = ($rset =~ /^null$/i) ? '0' : $rset unless not $rset;
    50          
151 11         29 return($self->{'_state'}->{'rv'});
152             }
153              
154             #: report on the state of the last dialog variant execution.
155             sub state {
156 0     0 1 0 my $self = shift();
157 0   0     0 my $rv = $self->rv() || 0;
158 0   0     0 $self->_debug((join(" | ",(caller())))." > state() > is: ".($rv||'NULL'),2);
159 0 0 0     0 if ($rv == 1 or $rv == 129) {
    0 0        
    0          
    0          
    0          
    0          
160 0         0 return("CANCEL");
161             }
162             elsif ($rv == 2) {
163 0         0 return("HELP");
164             }
165             elsif ($rv == 3) {
166 0         0 return("EXTRA");
167             }
168             elsif ($rv == 254) {
169 0         0 return("ERROR");
170             }
171             elsif ($rv == 255) {
172 0         0 return("ESC");
173             }
174             elsif (not $rv or $rv =~ /^null$/i) {
175 0         0 return("OK");
176             }
177             else {
178 0         0 return("UNKNOWN(".$rv.")");
179             }
180             }
181              
182             #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
183             #: Preparation Methods
184             #
185              
186             #: construct a HASHREF for command {{tag}} substitutions
187             sub make_kvt {
188 10     10 0 16 my ($self,$args,$value) = @_;
189             return
190             {
191             literal => ($args->{'literal'} || 0),
192             width => ($args->{'width'}||'65'),
193 10   50     132 trust => ($args->{'trust-input'} || 0),
      50        
      100        
      50        
194             value => ($value || '')
195             };
196             }
197             sub make_kvl {
198 278     278 0 384 my ($self,$args,$value) = @_;
199             return
200             {
201             literal => 1,
202             width => ($args->{'width'}||'65'),
203 278   50     2312 trust => ($args->{'trust-input'} || 0),
      100        
      100        
204             value => ($value || '')
205             };
206             }
207              
208             #: Helper method to generate a base format string, accepts additional
209             #: strings which are considered trusted programmer template input.
210             sub prepare_format {
211 11     11 0 21 my $self = shift(@_);
212 11         25 my $args = shift(@_);
213             # start with our binary path
214 11         23 my $fmt = $self->{'_opts'}{'bin'};
215 11         37 $fmt = $self->append_format_check($args,$fmt,'title','--title {{title}}');
216 11         31 return $fmt;
217             }
218              
219             sub append_format {
220 51     51 0 86 my ($self,$fmt,$value) = @_;
221 51 50       91 if (ref($fmt) eq "SCALAR") {
222 0         0 $$fmt .= ' '.$value;
223             }
224             else {
225 51         123 $fmt .= ' '.$value;
226             }
227 51         130 return $fmt;
228             }
229              
230             #: simple test and if true; append value to format
231             sub append_format_check {
232 22     22 0 42 my ($self,$args,$fmt,$key,$value) = @_;
233 22 50 33     117 if (exists $args->{$key} and defined $args->{$key}) {
234 22         49 $fmt = $self->append_format($fmt,$value);
235             }
236 22         53 return $fmt;
237             }
238              
239             sub clean_format {
240 293     293 0 438 my ($self,$trust,$sref) = @_;
241 293 50       625 unless (ref($sref) eq "SCALAR") {
242 0         0 die("Programmer error. clean_format requires a SCALAR ref, found: ".ref($sref));
243             }
244 293         411 $$sref =~ s!\x00!!mg; # remove nulls
245 293 100       596 unless ($trust) {
246 268         363 $$sref =~ s!\`!'!mg;
247 268         323 $$sref =~ s!\$\(!\(!mg;
248 268         394 $$sref =~ s!\$!\\\$!mg;
249             }
250 293         370 $$sref =~ s!"!\\"!mg; # escape double-quotes
251 293         436 return $sref;
252             }
253              
254             #: Given a command string "format" and any key/value replacement pairs,
255             #: construct the exec'able command string.
256             sub prepare_command {
257 11     11 0 20 my $self = shift(@_);
258 11         16 my $args = shift(@_);
259 11         14 my $format = shift(@_);
260 11         32 my (%rpl_add) = @_;
261 11         17 my %rpl = ();
262 11         14 foreach my $key (keys %{$args}) {
  11         67  
263 275   100     1031 $rpl{$key} = $self->make_kvl($args,$args->{$key}||'');
264             }
265 11         46 foreach my $key (keys %rpl_add) {
266 13         41 $rpl{$key} = $rpl_add{$key};
267             }
268 11         50 foreach my $key (keys %rpl) {
269 275   100     964 my $value = $rpl{$key}->{value}||'';
270 275 100       530 if (ref($value) eq "ARRAY") {
271             #: menu, checklist, radiolist...
272 3         5 my $list = '';
273 3         6 foreach my $item (@{$value}) {
  3         5  
274 12 100       30 if (ref($item) eq "ARRAY") {
275             # checklist, radiolist...
276 4 50       6 if (@{$item} == 2) {
  4 0       11  
    0          
277 4         13 $self->clean_format( $rpl{$key}->{trust}, \$item->[0] );
278 4 100       37 $list .= ' "'.$item->[0].'" "'.($item->[1] ? 'on' : 'off').'"';
279 4         10 next;
280             }
281 0         0 elsif (@{$item} == 3) {
282 0         0 $self->clean_format( $rpl{$key}->{trust}, \$item->[0] );
283 0         0 $self->clean_format( $rpl{$key}->{trust}, \$item->[2] );
284 0 0 0     0 $list .= ' "'.$item->[0].'" "'.($item->[1] ? 'on' : 'off').'" "'.($item->[2]||1).'"';
285 0         0 next;
286             }
287 0         0 elsif (@{$item} == 4) {
288 0         0 $self->clean_format( $rpl{$key}->{trust}, \$item->[0] );
289 0         0 $self->clean_format( $rpl{$key}->{trust}, \$item->[2] );
290 0         0 $self->clean_format( $rpl{$key}->{trust}, \$item->[3] );
291 0 0 0     0 $list .= ' "'.$item->[0].'" "'.($item->[1] ? 'on' : 'off').'" "'.($item->[2]||1).'"';
292 0         0 $list .= ' "'.$item->[3].'"';
293 0         0 next;
294             }
295             }
296             # menu...
297 8         21 $self->clean_format( $rpl{$key}->{trust}, \$item );
298 8         18 $list .= ' "'.$item.'"';
299             }
300 3         30 $format =~ s!\{\{\Q${key}\E\}\}!${list}!mg;
301             }
302             else {
303 272 50 0     521 $value ||= '' unless defined $value;
304 272 100       736 $value = "$1" if $value =~ m!^(\d+)$!;
305 272 50       675 if (ref(\$value) eq "SCALAR") {
306 272 100 66     900 unless ($rpl{$key}->{'trust'}||$rpl{$key}->{literal}) {
307             $value = $self->_organize_text
308 9         33 ( $value, $rpl{$key}->{width}, $rpl{$key}->{'trust'} );
309             }
310 272         643 $self->clean_format( $rpl{$key}->{'trust'}, \$value );
311 272         2943 $format =~ s!\{\{\Q${key}\E\}\}!"${value}"!mg;
312             }
313             }
314             }
315 11         194 return $format;
316             }
317              
318              
319             #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
320             #: Execution Methods
321             #:
322              
323             sub is_unit_test_mode {
324 11     11 0 18 my ($self) = @_;
325             return 1
326             if ( exists $self->{'test_mode'}
327             &&
328             defined $self->{'test_mode'}
329             &&
330 11 50 33     108 $self->{'test_mode'}
      33        
331             );
332 0         0 return 0;
333             }
334             sub get_unit_test_result {
335 11     11 0 25 my ($self) = @_;
336 11         75 return $self->{'test_mode_result'};
337             }
338              
339             #: execute a simple command (return the exit code only);
340             sub command_state {
341 0     0 1 0 my $self = $_[0];
342 0         0 my $cmnd = $_[1];
343 0 0       0 if ($self->is_unit_test_mode()) {
344 0         0 $self->{'test_mode_result'} = $cmnd;
345 0         0 return 0;
346             }
347 0         0 $self->_debug("command: ".$cmnd,1);
348 0         0 system($cmnd . " 2>&1 > /dev/null");
349 0         0 my $rv = $? >> 8;
350 0         0 $self->_debug("command rv: ".$rv,2);
351 0         0 return($rv);
352             }
353              
354             #: execute a command and return the exit code and one-line SCALAR
355             sub command_string {
356 0     0 1 0 my $self = $_[0];
357 0         0 my $cmnd = $_[1];
358 0 0       0 if ($self->is_unit_test_mode()) {
359 0         0 $self->{'test_mode_result'} = $cmnd;
360 0 0       0 return (wantarray) ? (0,'') : '';
361             }
362 0         0 $self->_debug("command: ".$cmnd,1);
363 0         0 chomp(my $text = `$cmnd 2>&1`);
364 0         0 my $rv = $? >> 8;
365 0         0 $self->_debug("command rs: ".$rv." '".$text."'",2);
366 0 0       0 return($text) unless defined wantarray;
367 0 0       0 return (wantarray) ? ($rv,$text) : $text;
368             }
369              
370             #: execute a command and return the exit code and ARRAY of data
371             sub command_array {
372 0     0 1 0 my $self = $_[0];
373 0         0 my $cmnd = $_[1];
374 0 0       0 if ($self->is_unit_test_mode()) {
375 0         0 $self->{'test_mode_result'} = $cmnd;
376 0 0       0 return (wantarray) ? (0,[]) : [];
377             }
378 0         0 $self->_debug("command: ".$cmnd,1);
379 0         0 chomp(my $text = `$cmnd 2>&1`);
380 0         0 my $rv = $? >> 8;
381 0         0 $self->_debug("command ra: ".$rv." '".$text."'",2);
382 0 0       0 return([split(/\n/,$text)]) unless defined wantarray;
383 0 0       0 return (wantarray) ? ($rv,[split(/\n/,$text)]) : [split(/\n/,$text)];
384             }
385              
386             #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
387             #: Utility Methods
388             #:
389              
390              
391             #: make some noise
392             sub beep {
393 0     0 1 0 my $self = $_[0];
394 0         0 return($self->_beep(1));
395             }
396              
397             #: Clear terminal screen.
398             sub clear {
399 0     0 1 0 my $self = $_[0];
400 0         0 return($self->_clear(1));
401             }
402              
403             # word-wrap a line
404             sub word_wrap {
405 9     9 1 10 my $self = shift();
406 9   50     22 my $width = shift() || 65;
407 9   50     32 my $indent = shift() || "";
408 9   50     33 my $sub_indent = shift() || "";
409 9         12 $Text::Wrap::columns = $width;
410 9         30 my @strings = wrap($indent, $sub_indent, @_);
411 9         1218 return(@strings);
412             }
413              
414             # generate a temporary file name
415             sub gen_tempfile_name {
416 0     0 1 0 my $self = $_[0];
417 0   0     0 my $template = $self->{'_opts'}->{'tempfiletemplate'} || "UI_Dialog_tempfile_XXXXX";
418 0 0       0 if (eval("require File::Temp; 1")) {
419 6     6   7482 use File::Temp qw( tempfile );
  6         104345  
  6         21230  
420 0 0       0 my ($fh,$filename) = tempfile( UNLINK => 1 ) or croak( "Can't create tempfile: $!" );
421 0 0       0 if (wantarray) {
422 0         0 return($fh,$filename);
423             }
424             else {
425 0         0 close($fh); # actually required on win32
426 0         0 return($filename);
427             }
428 0         0 return($fh,$filename);
429             }
430             else {
431 0         0 my $mktemp = $self->_find_bin('mktemp');
432 0 0 0     0 if ($mktemp && -x $mktemp) {
433 0         0 chomp(my $tempfile = `$mktemp "$template"`);
434 0         0 return($tempfile);
435             }
436             else {
437             #pseudo-random filename coming up!
438 0         0 my $tempdir = "/tmp";
439 0 0       0 unless (-d $tempdir) {
440 0 0       0 if (-d "/var/tmp") {
441 0         0 $tempdir = "/var/tmp";
442             }
443             else {
444 0         0 $tempdir = ".";
445             }
446             }
447 0         0 $self->gen_random_string(5);
448 0         0 my $tempfile = "UI_Dialog_tempfile_".$self->gen_random_string(5);
449 0         0 while (-e $tempdir."/".$tempfile) {
450 0         0 $self->gen_random_string(5);
451 0         0 $tempfile = "UI_Dialog_tempfile_".$self->gen_random_string(5);
452             }
453 0         0 return($tempdir."/".$tempfile);
454             }
455             }
456             }
457              
458             # generate a random string as a (possibly) suitable failover option in the
459             # event that File::Temp is not installed and the 'mktemp' program does not
460             # exist in the path.
461             sub gen_random_string {
462 0     0 1 0 my $self = $_[0];
463 0   0     0 my $length = $_[1] || 5;
464 0         0 my $string = "";
465 0         0 my $counter = 0;
466 0         0 while ($counter < $length) {
467             # 33 - 127
468 0         0 my $num = rand(128);
469 0   0     0 while ($num < 33 or $num > 127) {
470 0         0 $num = rand(128);
471             }
472 0         0 $string .= chr($num);
473 0         0 $counter++;
474             }
475 0         0 return($string);
476             }
477              
478             #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
479             #: Widget Wrapping Methods
480             #:
481              
482             #:+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
483             #: file select
484             sub fselect {
485 0     0 1 0 my $self = shift();
486 0   0     0 my $caller = (caller(1))[3] || 'main';
487 0 0 0     0 $caller = ($caller =~ /^UI\:\:Dialog\:\:Backend\:\:/) ? ((caller(2))[3]||'main') : $caller;
488 0 0 0     0 if ($_[0] && $_[0] eq 'caller') {
489 0         0 shift(); $caller = shift();
  0         0  
490             }
491 0         0 my $args = $self->_pre($caller,@_);
492              
493 0         0 $self->rv('NULL');
494 0         0 $self->rs('NULL');
495 0         0 $self->ra('NULL');
496              
497 0         0 $self->_beep($args->{'beepbefore'});
498              
499 0         0 my $cwd = abs_path();
500 0   0     0 $args->{'path'} ||= abs_path();
501 0         0 my $path = $args->{'path'};
502 0 0 0     0 if (!$path || $path =~ /^(\.|\.\/)$/) {
503 0         0 $path = $cwd;
504             }
505 0         0 my $file;
506 0         0 my ($menu,$list) = ([],[]);
507 0   0     0 FSEL: while ($self->state() ne "ESC" && $self->state() ne "CANCEL") {
508 0 0       0 my $entries = ($args->{'dselect'}) ? ['[new directory]'] : ['[new file]'];
509 0         0 ($menu, $list) = $self->_list_dir($path,$entries);
510             $file = $self->menu(height=>$args->{'height'},width=>$args->{'width'},listheight=>($args->{'listheight'}||$args->{'menuheight'}),
511 0   0     0 title=>$args->{'title'},text=>$path,list=>$menu);
512 0 0       0 if ($self->state() eq "CANCEL") {
    0          
513 0         0 $self->rv(1);
514 0         0 $self->rs('NULL');
515 0         0 $self->ra('NULL');
516 0         0 last FSEL;
517             }
518             elsif ($file ne "") {
519 0 0 0     0 if ($list->[($file - 1 || 0)] =~ /^\[(new\sdirectory|new\sfile)\]$/) {
    0 0        
    0 0        
    0 0        
    0 0        
520 0         0 my $nfn;
521 0   0     0 while (!$nfn || -e $path."/".$nfn) {
522 0         0 $nfn = $self->inputbox(height=>$args->{'height'},width=>$args->{'width'},title=>$args->{'title'},
523             text=>'Enter a name (will have a base directory of: '.$path.')');
524 0 0 0     0 next FSEL if $self->state() eq "ESC" or $self->state() eq "CANCEL";
525 0 0       0 if (-e $path."/".$nfn) {
526 0         0 $self->msgbox(title=>'error',text=>$path."/".$nfn.' already exists! Choose another name please.');
527             }
528             }
529 0         0 $file = $path."/".$nfn;
530 0 0       0 $file =~ s!/$!! unless $file =~ m!^/$!;
531 0         0 $file =~ s!/\./!/!g; $file =~ s!/+!/!g;
  0         0  
532 0         0 last FSEL;
533             }
534             elsif ($list->[($file - 1 || 0)] eq "../") {
535 0         0 $path = dirname($path);
536             }
537             elsif ($list->[($file - 1 || 0)] eq "./") {
538 0         0 $file = $path;
539 0 0       0 $file =~ s!/$!! unless $file =~ m!^/$!;
540 0         0 $file =~ s!/\./!/!g; $file =~ s!/+!/!g;
  0         0  
541 0         0 last FSEL;
542             }
543             elsif (-d $path."/".$list->[($file - 1 || 0)]) {
544 0   0     0 $path = $path."/".$list->[($file - 1 || 0)];
545             }
546             elsif (-e $path."/".$list->[($file - 1 || 0)]) {
547 0   0     0 $file = $path."/".$list->[($file - 1 || 0)];
548 0 0       0 $file =~ s!/$!! unless $file =~ m!^/$!;
549 0         0 $file =~ s!/\./!/!g; $file =~ s!/+!/!g;
  0         0  
550 0         0 last FSEL;
551             }
552             }
553 0         0 $file = undef();
554 0         0 $path =~ s!(/*)!/!; $path =~ s!/\./!/!g;
  0         0  
555             }
556 0         0 $self->_beep($args->{'beepafter'});
557 0         0 my $rv = $self->rv();
558 0         0 $self->ra('NULL');
559 0 0 0     0 if ($rv && $rv >= 1) {
560 0         0 $self->rs('NULL');
561 0         0 return(0);
562             }
563             else {
564 0         0 $self->rs($file);
565 0         0 return($file);
566             }
567             }
568              
569             #:+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
570             #: directory selection
571             sub dselect {
572 0     0 1 0 my $self = shift();
573 0   0     0 my $caller = (caller(1))[3] || 'main';
574 0 0 0     0 $caller = ($caller =~ /^UI\:\:Dialog\:\:Backend\:\:/) ? ((caller(2))[3]||'main') : $caller;
575 0 0 0     0 if ($_[0] && $_[0] eq 'caller') {
576 0         0 shift(); $caller = shift();
  0         0  
577             }
578 0         0 my $args = $self->_pre($caller,@_);
579 0         0 my $dirname;
580 0         0 $self->rv('NULL');
581 0         0 $self->rs('NULL');
582 0         0 $self->ra('NULL');
583 0   0     0 while (not $dirname && $self->state() !~ /^(CANCEL|ESC|ERROR)$/) {
584 0         0 $dirname = $self->fselect(@_,'dselect',1);
585 0 0       0 if ($self->state() =~ /^(CANCEL|ESC|ERROR)$/) {
586 0         0 return(0);
587             }
588 0 0       0 unless (not $dirname) {
589             # if it's a directory or not exist (assume new dir)
590 0 0 0     0 unless (-d $dirname || not -e $dirname) {
591 0         0 $self->msgbox( text => $dirname . " is not a directory.\nPlease select a directory." );
592 0         0 $dirname = undef();
593             }
594             }
595             }
596 0   0     0 return($dirname||'');
597             }
598              
599              
600             #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
601             #: Backend Methods
602             #:
603              
604             sub _pre {
605 11     11   17 my $self = shift();
606 11         16 my $caller = shift();
607 11         32 my $args = $self->_merge_attrs(@_);
608 11         25 $args->{'caller'} = $caller;
609 11         18 my $class = ref($self);
610              
611 11         18 my $CODEREFS = $args->{'callbacks'};
612 11 50       30 if (ref($CODEREFS) eq "HASH") {
613 0         0 my $PRECODE = $CODEREFS->{'PRE'};
614 0 0       0 if (ref($PRECODE) eq "CODE") {
615 0         0 &$PRECODE($args,$self->state());
616             }
617             }
618              
619 11         34 $self->_beep($args->{'beepbefore'});
620 11         31 $self->_clear($args->{'clearbefore'});
621 11         32 return($args);
622             }
623              
624             sub _post {
625 10     10   14 my $self = shift();
626 10   50     23 my $args = shift() || {};
627 10         19 my $class = ref($self);
628              
629 10         24 $self->_beep($args->{'beepafter'});
630 10         24 $self->_clear($args->{'clearafter'});
631              
632 10         16 my $CODEREFS = $args->{'callbacks'};
633 10 50       25 if (ref($CODEREFS) eq "HASH") {
634 0         0 my $state = $self->state();
635 0 0       0 if ($state eq "OK") {
    0          
    0          
636 0         0 my $OKCODE = $CODEREFS->{'OK'};
637 0 0       0 if (ref($OKCODE) eq "CODE") {
638 0         0 &$OKCODE($args);
639             }
640             }
641             elsif ($state eq "ESC") {
642 0         0 my $ESCCODE = $CODEREFS->{'ESC'};
643 0 0       0 if (ref($ESCCODE) eq "CODE") {
644 0         0 &$ESCCODE($args);
645             }
646             }
647             elsif ($state eq "CANCEL") {
648 0         0 my $CANCELCODE = $CODEREFS->{'CANCEL'};
649 0 0       0 if (ref($CANCELCODE) eq "CODE") {
650 0         0 &$CANCELCODE($args);
651             }
652             }
653 0         0 my $POSTCODE = $CODEREFS->{'POST'};
654 0 0       0 if (ref($POSTCODE) eq "CODE") {
655 0         0 &$POSTCODE($args,$state);
656             }
657             }
658              
659 10         21 return(1);
660             }
661              
662              
663             #: indent and organize the text argument
664             sub _organize_text {
665 9     9   12 my $self = $_[0];
666 9         14 my $text = $_[1];
667 9   50     22 my $width = $_[2] || 65;
668 9 50 33     39 my $trust = (exists $_[3] && defined $_[3]) ? $_[3] : '0';
669 9         14 $width -= 4; # take account of borders?
670 9         11 my @array;
671              
672 9 50       29 if (ref($text) eq "ARRAY") {
    50          
673 0         0 push(@array,@{$text});
  0         0  
674             }
675             elsif ($text =~ /\\n/) {
676 0         0 @array = split(/\\n/,$text);
677             }
678             else {
679 9         27 @array = split(/\n/,$text);
680             }
681 9         43 $text = undef;
682              
683 9         38 @array = $self->word_wrap($width,"","",@array);
684              
685 9 50       28 if ($self->{'scale'}) {
686 0         0 foreach my $line (@array) {
687 0         0 my $s_line = $line;#$self->__TRANSLATE_CLEAN($line);
688 0         0 $s_line =~ s!\[A\=\w+\]!!gi;
689             $self->{'width'} = length($s_line) + 5
690             if ($self->{'width'} - 5) < length($s_line)
691 0 0 0     0 && (length($s_line) <= $self->{'max-scale'});
692             }
693             }
694              
695 9         17 foreach my $line (@array) {
696 9         10 my $pad;
697 9         23 $self->clean_format( $trust, \$line );
698 9         28 my $s_line = $self->_strip_text($line);
699 9 50       27 if ($line =~ /\[A\=(\w+)\]/i) {
700 0         0 my $align = $1;
701 0         0 $line =~ s!\[A\=\w+\]!!gi;
702 0 0 0     0 if (uc($align) eq "CENTER" || uc($align) eq "C") {
    0 0        
    0 0        
703 0         0 $pad = ((($self->{'_opts'}->{'width'} - 5) - length($s_line)) / 2);
704             }
705             elsif (uc($align) eq "LEFT" || uc($align) eq "L") {
706 0         0 $pad = 0;
707             }
708             elsif (uc($align) eq "RIGHT" || uc($align) eq "R") {
709 0         0 $pad = (($self->{'_opts'}->{'width'} - 5) - length($s_line));
710             }
711             }
712 9 50       17 if ($pad) {
713 0         0 $text .= (" " x $pad).$line."\n";
714             }
715             else {
716 9         22 $text .= $line."\n";
717             }
718             }
719 9         18 $text = $self->_strip_text($text);
720 9         17 chomp($text);
721 9         20 return($text);
722             }
723              
724             #: merge the arguments with the default attributes, and arguments override defaults.
725             sub _merge_attrs {
726 11     11   15 my $self = shift();
727 11 50       69 my $args = (@_ % 2) ? { @_, '_odd' } : { @_ };
728 11         25 my $defs = $self->{'_opts'};
729              
730 11         53 foreach my $def (keys(%$defs)) {
731             # default unless exists
732 209 100       596 $args->{$def} = $defs->{$def} unless exists $args->{$def};
733             }
734              
735             # alias 'filename' and 'file' to path
736             $args->{'path'} = (($args->{'filename'}) ? $args->{'filename'} :
737             ($args->{'file'}) ? $args->{'file'} :
738 11 100       64 ($args->{'path'}) ? $args->{'path'} : "");
    50          
    50          
739              
740 11   50     84 $args->{'clear'} = $args->{'clearbefore'} || $args->{'clearafter'} || $args->{'autoclear'} || 0;
741 11   50     79 $args->{'beep'} = $args->{'beepbefore'} || $args->{'beepafter'} || $args->{'autobeep'} || 0;
742 11         24 return($args);
743             }
744              
745             #: search through the given paths for a specific variant
746             sub _find_bin {
747 14     14   40 my $self = $_[0];
748 14         41 my $variant = $_[1];
749             $self->{'PATHS'} = ((ref($self->{'PATHS'}) eq "ARRAY") ? $self->{'PATHS'} :
750 14 0       60 ($self->{'PATHS'}) ? [ $self->{'PATHS'} ] :
    50          
751             [ '/bin', '/usr/bin', '/usr/local/bin', '/opt/bin' ]);
752 14         22 foreach my $PATH (@{$self->{'PATHS'}}) {
  14         46  
753 92 100       17063 return($PATH . '/' . $variant)
754             unless not -x $PATH . '/' . $variant;
755             }
756 10         90 return(0);
757             }
758              
759             #: clean the text arguments of all colour codes, alignments and attributes.
760             sub _strip_text {
761 18     18   22 my $self = $_[0];
762 18         28 my $text = $_[1];
763 18         31 $text =~ s!\\Z[0-7bBuUrRn]!!gmi;
764 18         21 $text =~ s!\[[AC]=\w+\]!!gmi;
765 18         22 $text =~ s!\[/?[BURN]\]!!gmi;
766 18         38 return($text);
767             }
768              
769             #: is this a BSD system?
770             sub _is_bsd {
771 0     0   0 my $self = shift();
772 0 0       0 return(1) if $^O =~ /bsd/i;
773 0         0 return(0);
774             }
775              
776             #: gather a list of the contents of a directory and return it in
777             #: two forms, one is the "simple" list of all the filenames and the
778             #: other is a 'menu' list corresponding to the simple list.
779             sub _list_dir {
780 0     0   0 my $self = shift();
781 0   0     0 my $path = shift() || return();
782 0         0 my $pref = shift();
783 0         0 my (@listing,@list);
784 0 0       0 if (opendir(GETDIR,$path)) {
785 0         0 my @dir_data = readdir(GETDIR);
786 0         0 closedir(GETDIR);
787 0 0       0 if ($pref) {
788 0         0 push(@listing,@{$pref});
  0         0  
789             }
790 0         0 foreach my $dir (sort(grep { -d $path."/".$_ } @dir_data)) {
  0         0  
791 0         0 push(@listing,$dir."/");
792             }
793 0         0 foreach my $item (sort(grep { !-d $path."/".$_ } @dir_data)) {
  0         0  
794 0         0 push(@listing,$item);
795             }
796 0         0 my $c = 1;
797 0         0 foreach my $item (@listing) {
798 0         0 push(@list,"$c",$item); $c++;
  0         0  
799             }
800 0         0 return(\@list,\@listing);
801             }
802             else {
803 0         0 return("failed to read directory: ".$path);
804             }
805             }
806              
807             sub _debug {
808 37     37   55 my $self = $_[0];
809 37   50     85 my $mesg = $_[1] || 'null debug message given!';
810 37   50     74 my $rate = $_[2] || 1;
811 37 50 33     131 return() unless $self->{'_opts'}->{'debug'} and $self->{'_opts'}->{'debug'} >= $rate;
812 0         0 chomp($mesg);
813 0         0 print STDERR "Debug: ".$mesg."\n";
814             }
815             sub _error {
816 0     0   0 my $self = $_[0];
817 0   0     0 my $mesg = $_[1] || 'null error message given!';
818 0         0 chomp($mesg);
819 0         0 print STDERR "Error: ".$mesg."\n";
820             }
821              
822             #: really make some noise
823             sub _beep {
824 21     21   23 my $self = $_[0];
825 21         29 my $beep = $_[1];
826 21 50       51 unless (not $beep) {
827 0 0       0 if (-x $self->{'_opts'}->{'beepbin'}) {
828 0         0 return(eval { system($self->{'_opts'}->{'beepbin'}); 1; });
  0         0  
  0         0  
829             }
830             else {
831 0 0 0     0 return (1) unless $ENV{'TERM'} && $ENV{'TERM'} ne "dumb";
832 0         0 print STDERR "\a";
833             }
834             }
835 21         30 return(1);
836             }
837              
838             #: The actual clear action.
839             sub _clear {
840 21     21   28 my $self = $_[0];
841 21   50     72 my $clear = $_[1] || 0;
842             # Useless with GUI based variants so we return here.
843             # Is the use of the "dumb" TERM appropriate? need feedback.
844 21 50 33     100 return (1) unless $ENV{'TERM'} && $ENV{'TERM'} ne "dumb";
845 21 50 33     92 unless (not $clear and not $self->{'_opts'}->{'autoclear'}) {
846 0   0     0 $self->{'_clear'} ||= `clear`;
847 0         0 print STDOUT $self->{'_clear'};
848             }
849 21         29 return(1);
850             }
851              
852              
853              
854             1;