File Coverage

blib/lib/EBook/Gutenberg/Dialog.pm
Criterion Covered Total %
statement 18 117 15.3
branch 1 30 3.3
condition 0 12 0.0
subroutine 7 24 29.1
pod 13 13 100.0
total 39 196 19.9


line stmt bran cond sub pod time code
1             package EBook::Gutenberg::Dialog;
2 2     2   35 use 5.016;
  2         7  
3             our $VERSION = '1.00';
4 2     2   11 use strict;
  2         21  
  2         63  
5 2     2   10 use warnings;
  2         3  
  2         187  
6              
7 2     2   13 use Exporter qw(import);
  2         3  
  2         219  
8              
9             our @EXPORT_OK = qw(
10             DIALOG_OK
11             DIALOG_CANCEL
12             DIALOG_HELP
13             DIALOG_EXTRA
14             DIALOG_ITEM_HELP
15             DIALOG_TIMEOUT
16             DIALOG_ERROR
17             DIALOG_ESC
18             );
19              
20             our %EXPORT_TAGS = (
21             codes => [ qw(
22             DIALOG_OK
23             DIALOG_CANCEL
24             DIALOG_HELP
25             DIALOG_EXTRA
26             DIALOG_ITEM_HELP
27             DIALOG_TIMEOUT
28             DIALOG_ERROR
29             DIALOG_ESC
30             ) ],
31             );
32              
33 2     2   12 use File::Temp qw(tempfile);
  2         4  
  2         168  
34              
35             use constant {
36 2         4177 DIALOG_OK => 0,
37             DIALOG_CANCEL => 1,
38             DIALOG_HELP => 2,
39             DIALOG_EXTRA => 3,
40             DIALOG_ITEM_HELP => 4,
41             DIALOG_TIMEOUT => 5,
42             DIALOG_ERROR => 6,
43             DIALOG_ESC => 7,
44 2     2   12 };
  2         3  
45              
46             @ENV{ qw(
47             DIALOG_OK
48             DIALOG_CANCEL
49             DIALOG_HELP
50             DIALOG_EXTRA
51             DIALOG_ITEM_HELP
52             DIALOG_TIMEOUT
53             DIALOG_ERROR
54             DIALOG_ESC
55             ) } = (
56             DIALOG_OK,
57             DIALOG_CANCEL,
58             DIALOG_HELP,
59             DIALOG_EXTRA,
60             DIALOG_ITEM_HELP,
61             DIALOG_TIMEOUT,
62             DIALOG_ERROR,
63             DIALOG_ESC
64             );
65              
66             my %MTHD_OPTS = (
67             title => [ '--title %s', 1 ],
68             ok_label => [ '--ok-label %s', 1 ],
69             yes_label => [ '--yes-label %s', 1 ],
70             cancel_label => [ '--cancel-label %s', 1 ],
71             no_label => [ '--no-label %s', 1 ],
72             extra_button => [ '--extra-button', 0 ],
73             extra_label => [ '--extra-label %s', 1 ],
74             help_button => [ '--help-button', 0 ],
75             help_label => [ '--help-label %s', 1 ],
76             erase_on_exit => [ '--erase-on-exit', 0 ],
77             );
78              
79             my %ATTR_OPTS = (
80             backtitle => [ '--backtitle %s', 1 ],
81             );
82              
83             my $TMP = do {
84             my ($fh, $fn) = tempfile;
85             close $fh;
86             $fn;
87             };
88              
89             sub _quote {
90              
91 0     0     my $str = shift;
92              
93 0           $str =~ s/(["\\`"\$])/\\$1/g;
94              
95 0           return qq("$str");
96              
97             }
98              
99             sub _cmd {
100              
101 0     0     my $cmd = shift;
102              
103 0           system "$cmd 2>$TMP";
104              
105 0 0         open my $fh, '<', $TMP
106             or die "Failed to open $TMP for reading: $!\n";
107 0           my $text = do { local $/ = undef; <$fh> };
  0            
  0            
108 0           close $fh;
109              
110 0           return ($? >> 8, $text);
111              
112             }
113              
114             sub _build_dialog_cmd {
115              
116 0     0     my $self = shift;
117 0           my $param = shift;
118 0           my $args = shift;
119              
120 0           my $cmd = "$self->{ backend } ";
121              
122 0           for my $k (keys %ATTR_OPTS) {
123 0 0         next unless defined $self->{ $k };
124             $cmd .= $ATTR_OPTS{ $k }->[1]
125 0 0         ? sprintf "$ATTR_OPTS{ $k }->[0] ", _quote($self->{ $k })
126             : "$ATTR_OPTS{ $k }->[0] ";
127             }
128              
129 0           for my $k (keys %$param) {
130 0 0         next unless exists $MTHD_OPTS{ $k };
131             $cmd .= $MTHD_OPTS{ $k }->[1]
132 0 0         ? sprintf "$MTHD_OPTS{ $k }->[0] ", _quote($param->{ $k })
133             : "$MTHD_OPTS{ $k }->[0] ";
134             }
135              
136 0           $cmd .= join ' ', map { _quote($_) } @$args;
  0            
137              
138 0           $cmd =~ s/ $//;
139              
140 0           return $cmd;
141              
142             }
143              
144             sub _backend_ok {
145              
146 0     0     my $self = shift;
147 0   0       my $backend = shift // $self->{ backend };
148              
149 0           qx/$backend -v 2>&1/;
150              
151 0           return $? == 0;
152              
153             }
154              
155             sub new {
156              
157 0     0 1   my $class = shift;
158 0           my %param = @_;
159              
160 0           my $self = {};
161              
162 0   0       $self->{ backend } = $param{ backend } // 'dialog';
163 0   0       $self->{ backtitle } = $param{ backtitle } // undef;
164              
165 0           bless $self, $class;
166              
167 0 0         unless ($self->_backend_ok) {
168 0           die "Failed to initialize dialog interface: $self->{ backend } is not available\n";
169             }
170              
171 0           return $self;
172              
173             }
174              
175             sub backend {
176              
177 0     0 1   my $self = shift;
178              
179 0           return $self->{ backend };
180              
181             }
182              
183             sub set_backend {
184              
185 0     0 1   my $self = shift;
186 0           my $backend = shift;
187              
188 0 0         unless ($self->_backend_ok($backend)) {
189 0           die "$backend is not available";
190             }
191              
192 0           $self->{ backend } = $backend;
193              
194             }
195              
196             sub backtitle {
197              
198 0     0 1   my $self = shift;
199              
200 0           return $self->{ backtitle };
201              
202             }
203              
204             sub set_backtitle {
205              
206 0     0 1   my $self = shift;
207 0           my $backtitle = shift;
208              
209 0           $self->{ backtitle } = $backtitle;
210              
211             }
212              
213             sub form {
214              
215 0     0 1   my $self = shift;
216 0           my @args = @_;
217 0 0         my $param = ref $args[-1] eq 'HASH' ? pop @args : {};
218              
219 0           my $cmd = $self->_build_dialog_cmd(
220             $param,
221             [ '--form', @args ],
222             );
223              
224 0           my ($rv, $rt) = _cmd($cmd);
225              
226 0           return ($rv, [ $rt =~ m/([^\n]*)\n/g ]);
227              
228             }
229              
230             sub infobox {
231              
232 0     0 1   my $self = shift;
233 0           my @args = @_;
234 0 0         my $param = ref $args[-1] eq 'HASH' ? pop @args : {};
235              
236 0           my $cmd = $self->_build_dialog_cmd(
237             $param,
238             [ '--infobox', @args ],
239             );
240              
241 0           my $rv = _cmd($cmd);
242              
243 0           return $rv;
244              
245             }
246              
247             sub inputbox {
248              
249 0     0 1   my $self = shift;
250 0           my @args = @_;
251 0 0         my $param = ref $args[-1] eq 'HASH' ? pop @args : {};
252              
253 0           my $cmd = $self->_build_dialog_cmd(
254             $param,
255             [ '--inputbox', @args ],
256             );
257              
258 0           my ($rv, $rt) = _cmd($cmd);
259 0           chomp $rt;
260              
261 0           return ($rv, $rt);
262              
263             }
264              
265             sub menu {
266              
267 0     0 1   my $self = shift;
268 0           my @args = @_;
269 0 0         my $param = ref $args[-1] eq 'HASH' ? pop @args : {};
270              
271 0           my $cmd = $self->_build_dialog_cmd(
272             $param,
273             [ '--menu', @args ],
274             );
275              
276 0           my ($rv, $rt) = _cmd($cmd);
277 0           chomp $rt;
278              
279 0           return ($rv, $rt);
280              
281             }
282              
283             sub msgbox {
284              
285 0     0 1   my $self = shift;
286 0           my @args = @_;
287 0 0         my $param = ref $args[-1] eq 'HASH' ? pop @args : {};
288              
289 0           my $cmd = $self->_build_dialog_cmd(
290             $param,
291             [ '--msgbox', @args ],
292             );
293              
294 0           my ($rv, $rt) = _cmd($cmd);
295              
296 0           return $rv;
297              
298             }
299              
300             sub textbox {
301              
302 0     0 1   my $self = shift;
303 0           my @args = @_;
304 0 0         my $param = ref $args[-1] eq 'HASH' ? pop @args : {};
305              
306 0           my $cmd = $self->_build_dialog_cmd(
307             $param,
308             [ '--textbox', @args ],
309             );
310              
311 0           my ($rv, $rt) = _cmd($cmd);
312              
313 0           return $rv;
314              
315             }
316              
317             sub yesno {
318              
319 0     0 1   my $self = shift;
320 0           my @args = @_;
321 0 0         my $param = ref $args[-1] eq 'HASH' ? pop @args : {};
322              
323 0           my $cmd = $self->_build_dialog_cmd(
324             $param,
325             [ '--yesno', @args ],
326             );
327              
328 0           my ($rv, $rt) = _cmd($cmd);
329              
330 0           return $rv;
331              
332             }
333              
334             sub pager {
335              
336 0     0 1   my $self = shift;
337 0           my $file = shift;
338 0   0       my $pgr = shift // $ENV{ PAGER } // 'less';
      0        
339              
340 0           $file = _quote($file);
341              
342 0           return system "$pgr $file";
343              
344             }
345              
346             END {
347 2 50   2   3347 unlink $TMP if -e $TMP;
348             }
349              
350             1;
351              
352             =head1 NAME
353              
354             EBook::Gutenberg::Dialog - Interface to dialog command.
355              
356             =head1 SYNOPSIS
357              
358             use EBook::Gutenberg::Dialog;
359              
360             my $dialog = EBook::Gutenberg::Dialog->new(backtitle => 'gutenberg');
361              
362             =head1 DESCRIPTION
363              
364             B is the L interface to the Unix
365             L program. This is developer documentation, for user documentation
366             you should consult the L manual.
367              
368             =head1 METHODS
369              
370             =over 4
371              
372             =item $dialog = EBook::Gutenberg::Dialog->new(%params)
373              
374             Returns a newly blessed B object.
375              
376             The following are valid C<%params> fields.
377              
378             =over 4
379              
380             =item backend
381              
382             The backend dialog command to use. Defaults to L.
383              
384             =item backtitle
385              
386             String to use for the C<--backtitle> option. Unset by default.
387              
388             =back
389              
390             =back
391              
392             =head2 Accessors
393              
394             =over 4
395              
396             =item $backend = $dialog->backend
397              
398             =item $dialog->set_backend($backend)
399              
400             Getter/setter for the backend attribute.
401              
402             =item $backtitle = $dialog->backtitle
403              
404             =item $dialog->set_backtitle($backtitle)
405              
406             Getter/setter for the backtitle attribute.
407              
408             =back
409              
410             =head2 Widgets
411              
412             These methods correspond to C widget options. Each method accepts a
413             hashref as an optional final argument that can contain the following fields:
414              
415             =over 4
416              
417             =item title
418              
419             =item ok_label
420              
421             =item yes_label
422              
423             =item cancel_label
424              
425             =item no_label
426              
427             =item extra_button
428              
429             =item extra_label
430              
431             =item help_button
432              
433             =item help_label
434              
435             =item erase_on_exit
436              
437             =back
438              
439             Each correspond to a C option.
440              
441             =over 4
442              
443             =item ($rv, $forms) = $dialog->form($text, $height, $width, $list_height, [ $ly, $lx, $i1, $iy, $ix, $flen, $ilen ] ..., [ \%param ])
444              
445             =item $rv = $dialog->infobox($text, $height, $width, [ $init ], [ \%param ])
446              
447             =item ($rv, $in) = $dialog->inputbox($text, $height, $width, $menu_height, [ $tag, $item ] ..., [ \%param ])
448              
449             =item ($rv, $item) = $dialog->menu($text, $height, $width, $menu_height, [ $tag, $item ] ..., [ \%param ])
450              
451             =item $rv = $dialog->msgbox($text, $height, $width, [ \%param ])
452              
453             =item $rv = $dialog->textbox($file, $height, $width, [ \%param ])
454              
455             =item $rv = $dialog->yesno($text, $height, $width, [ \%param ])
456              
457             =item $rv = $dialog->pager($file, [ $pgr ])
458              
459             C uses a given pager to read C<$file>. It does not actually correspond
460             to a C widget.
461              
462             =back
463              
464             =head1 EXPORTS
465              
466             =over 4
467              
468             =item :codes
469              
470             C return code constants.
471              
472             =over 4
473              
474             =item DIALOG_OK
475              
476             =item DIALOG_CANCEL
477              
478             =item DIALOG_HELP
479              
480             =item DIALOG_EXTRA
481              
482             =item DIALOG_ITEM_HELP
483              
484             =item DIALOG_TIMEOUT
485              
486             =item DIALOG_ERROR
487              
488             =item DIALOG_ESC
489              
490             =back
491              
492             =back
493              
494             =head1 AUTHOR
495              
496             Written by Samuel Young, Esamyoung12788@gmail.comE.
497              
498             This project's source can be found on its
499             L. Comments and pull
500             requests are welcome!
501              
502             =head1 COPYRIGHT
503              
504             Copyright (C) 2025 Samuel Young
505              
506             This program is free software: you can redistribute it and/or modify
507             it under the terms of the GNU General Public License as published by
508             the Free Software Foundation, either version 3 of the License, or
509             (at your option) any later version.
510              
511             =head1 SEE ALSO
512              
513             L, L
514              
515             =cut
516              
517             # vim: expandtab shiftwidth=4