File Coverage

blib/lib/Term/ReadLine/Simple.pm
Criterion Covered Total %
statement 78 516 15.1
branch 18 276 6.5
condition 2 81 2.4
subroutine 25 44 56.8
pod 4 12 33.3
total 127 929 13.6


line stmt bran cond sub pod time code
1             package Term::ReadLine::Simple;
2              
3 4     4   93966 use warnings;
  4         8  
  4         126  
4 4     4   24 use strict;
  4         6  
  4         77  
5 4     4   89 use 5.008003;
  4         22  
6              
7             our $VERSION = '0.307';
8              
9 4     4   21 use Carp qw( croak carp );
  4         6  
  4         277  
10 4     4   6613 use Encode qw( encode );
  4         52751  
  4         393  
11 4     4   35 use List::Util qw( any );
  4         10  
  4         462  
12              
13 4     4   3534 use Encode::Locale qw();
  4         17452  
  4         94  
14 4     4   2811 use Unicode::GCString qw();
  4         75209  
  4         114  
15              
16 4     4   2405 use Term::ReadLine::Simple::Constants qw( :rl );
  4         12  
  4         1178  
17              
18             my $Plugin_Package;
19              
20             BEGIN {
21 4 50   4   20 if ( $^O eq 'MSWin32' ) {
22 0         0 require Term::ReadLine::Simple::Win32;
23 0         0 $Plugin_Package = 'Term::ReadLine::Simple::Win32';
24             }
25             else {
26 4         2242 require Term::ReadLine::Simple::Linux;
27 4         10596 $Plugin_Package = 'Term::ReadLine::Simple::Linux';
28             }
29             }
30              
31 1     1 0 23 sub ReadLine { 'Term::ReadLine::Simple' }
32       1 0   sub IN {}
33       1 0   sub OUT {}
34       1 0   sub MinLine {}
35 1     1 0 6 sub Attribs { {} }
36 1     1 0 6 sub Features { { no_features => 1 } }
37       1 0   sub addhistory {}
38       1 0   sub ornaments {}
39              
40              
41             sub new {
42 3     3 1 1678 my $class = shift;
43 3         7 my ( $name ) = @_;
44 3         11 my $self = bless {
45             name => $name,
46             }, $class;
47 3         13 $self->__set_defaults();
48 3         25 $self->{plugin} = $Plugin_Package->new();
49 3         50 return $self;
50             }
51              
52              
53             sub DESTROY {
54 3     3   1082 my ( $self ) = @_;
55 3         13 $self->__reset_term();
56             }
57              
58              
59             sub __set_defaults {
60 3     3   5 my ( $self ) = @_;
61             # compat : undef ok
62             # reinit_encoding: undef ok
63             # no_echo : false ok
64 3         15 $self->{default} = '';
65              
66             # prompt : undef ok
67             # mark_curr: false ok
68             # back : undef ok
69 3         7 $self->{auto_up} = 0;
70 3         7 $self->{back} = '';
71 3         6 $self->{confirm} = '<<';
72 3         9 $self->{ro} = [];
73             }
74              
75              
76             sub __validate_options {
77 53     53   80 my ( $self, $opt, $valid ) = @_;
78 53 50       113 if ( ! defined $opt ) {
79 0         0 $opt = {};
80 0         0 return;
81             }
82 53         305 my $sub = ( caller( 1 ) )[3];
83 53         290 $sub =~ s/^.+::([^:]+)\z/$1/;
84              
85             ####
86 53 0 33     169 if ( exists $opt->{sep} && ! $self->{called_sep_message} && $sub ne 'readline' ) { # remove
      33        
87 0         0 print " '$sub' called with the option 'sep'.\n";
88 0         0 print " The option 'sep' has been removed.\n";
89 0         0 print " Write an email to cuer2s\x{0040}gmail.com with the subject\n";
90 0         0 print " 'keep opt sep' if you want back the option 'sep'.\n";
91 0         0 print " Press ENTER to continue: ";
92 0         0 ;
93 0         0 $self->{called_sep_message} = 1;
94             }
95             ####
96              
97 53         153 for my $key ( keys %$opt ) {
98 73 100       169 if ( ! exists $valid->{$key} ) {
99 1         95 croak $sub . ": '$key' is not a valid option name";
100             }
101 72 100       151 if ( ! defined $opt->{$key} ) {
102 11         23 next;
103             }
104 61 100       127 if ( ref $opt->{$key} ) {
105 15 50       37 if ( $valid->{$key} eq 'ARRAY' ) {
106 0         0 next;
107             }
108 15         1393 croak $sub . ": option '$key' : a reference is not a valid value.";
109             }
110 46 100       96 if ( $valid->{$key} eq '' ) {
111 23         47 next;
112             }
113 23 100       238 if ( $opt->{$key} !~ m/^$valid->{$key}\z/x ) {
114 9         906 croak $sub . ": option '$key' : '$opt->{$key}' is not a valid value.";
115             }
116             }
117             }
118              
119              
120             sub __init_term {
121 0     0   0 my ( $self ) = @_;
122 0         0 $self->{plugin}->__set_mode();
123 0 0       0 if ( $self->{reinit_encoding} ) {
124 0         0 Encode::Locale::reinit( $self->{reinit_encoding} );
125             }
126             }
127              
128              
129             sub __reset_term {
130 3     3   6 my ( $self ) = @_;
131 3 50       11 delete $self->{enter_row} if exists $self->{enter_row};
132 3 50       11 delete $self->{enter_col} if exists $self->{enter_col};
133 3 50       12 if ( defined $self->{plugin} ) {
134 3         26 $self->{plugin}->__reset_mode();
135             }
136             }
137              
138              
139             sub config {
140 53     53 1 18457 my ( $self, $opt ) = @_;
141 53 50       135 if ( defined $opt ) {
142 53 50       140 if ( ref $opt ne 'HASH' ) {
143 0         0 croak "config: the (optional) argument must be a HASH reference";
144             }
145 53         351 my $valid = {
146             no_echo => '[ 0 1 2 ]',
147             compat => '[ 0 1 ]',
148             reinit_encoding => '',
149             default => '',
150             prompt => '',
151             back => '',
152             confirm => '',
153             auto_up => '[ 0 1 2 ]',
154             mark_curr => '[ 0 1 ]',
155             ro => 'ARRAY',
156             ####
157             sep => '', # remove
158             ####
159             };
160 53         138 $self->__validate_options( $opt, $valid );
161 28         68 for my $option ( keys %$opt ) {
162 42         161 $self->{$option} = $opt->{$option};
163             }
164             }
165             }
166              
167              
168             sub readline {
169 0     0 1   my ( $self, $prompt, $opt ) = @_;
170 0 0         if ( defined $prompt ) {
171 0 0         croak "readline: a reference is not a valid prompt." if ref $prompt;
172             }
173             else {
174 0           $prompt = '';
175             }
176 0 0         if ( defined $opt ) {
177 0 0         if ( ! ref $opt ) {
    0          
178 0           $opt = { default => $opt };
179             }
180             elsif ( ref $opt ne 'HASH' ) {
181 0           croak "readline: the (optional) second argument must be a string or a HASH reference";
182             }
183             }
184 0           my $valid = {
185             no_echo => '[ 0 1 2 ]',
186             default => '',
187             };
188 0           $self->__validate_options( $opt, $valid );
189 0 0         $opt->{default} = $self->{default} if ! defined $opt->{default};
190 0 0         $opt->{no_echo} = $self->{no_echo} if ! defined $opt->{no_echo};
191 0           $self->{sep} = '';
192 0           my $list = [ [ $prompt, $self->{default} ] ];
193 0           $self->{curr_row} = 0;
194 0           $self->{length_key}[0] = Unicode::GCString->new( $prompt )->columns;
195 0           $self->{len_longest_key} = $self->{length_key}[0];
196 0           $self->{length_prompt} = $self->{len_longest_key} + length $self->{sep};
197 0           my $str = Unicode::GCString->new( $opt->{default} );
198 0           my $pos = $str->length();
199 0           local $| = 1;
200 0           $self->__init_term();
201              
202 0           while ( 1 ) {
203 0 0         if ( $self->{beep} ) {
204 0           $self->{plugin}->__beep();
205 0           $self->{beep} = 0;
206             }
207 0           my ( $term_width ) = $self->{plugin}->__term_buff_size();
208 0           $self->{avail_width} = $term_width - 1;
209 0           $self->{avail_width_value} = $self->{avail_width} - $self->{length_prompt};
210 0           $self->__print_readline( $opt, $list, $str, $pos );
211 0           my $key = $self->{plugin}->__get_key();
212 0 0         if ( ! defined $key ) {
213 0           $self->__reset_term();
214 0           carp "EOT: $!";
215 0           return;
216             }
217 0 0         next if $key == NEXT_get_key;
218 0 0         next if $key == KEY_TAB;
219 0 0 0       if ( $key == KEY_BSPACE || $key == CONTROL_H ) {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0          
    0          
220 0 0         if ( $pos ) {
221 0           $pos--;
222 0           $str->substr( $pos, 1, '' );
223             }
224             else {
225 0           $self->{beep} = 1;
226             }
227             }
228             elsif ( $key == CONTROL_U ) {
229 0 0         if ( $pos ) {
230 0           $str->substr( 0, $pos, '' );
231 0           $pos = 0;
232             }
233             else {
234 0           $self->{beep} = 1;
235             }
236             }
237             elsif ( $key == CONTROL_K ) {
238 0 0         if ( $pos < $str->length() ) {
239 0           $str->substr( $pos, $str->length() - $pos, '' );
240             }
241             else {
242 0           $self->{beep} = 1;
243             }
244             }
245             elsif ( $key == VK_DELETE || $key == CONTROL_D ) {
246 0 0         if ( $str->length() ) {
247 0 0         if ( $pos < $str->length() ) {
248 0           $str->substr( $pos, 1, '' );
249             }
250             else {
251 0           $self->{beep} = 1;
252             }
253             }
254             else {
255 0           print "\n";
256 0           $self->__reset_term();
257 0           return;
258             }
259             }
260             elsif ( $key == VK_RIGHT || $key == CONTROL_F ) {
261 0 0         if ( $pos < $str->length() ) {
262 0           $pos++;
263             }
264             else {
265 0           $self->{beep} = 1;
266             }
267             }
268             elsif ( $key == VK_LEFT || $key == CONTROL_B ) {
269 0 0         if ( $pos ) {
270 0           $pos--;
271             }
272             else {
273 0           $self->{beep} = 1;
274             }
275             }
276             elsif ( $key == VK_END || $key == CONTROL_E ) {
277 0 0         if ( $pos < $str->length() ) {
278 0           $pos = $str->length();
279             }
280             else {
281 0           $self->{beep} = 1;
282             }
283             }
284             elsif ( $key == VK_HOME || $key == CONTROL_A ) {
285 0 0         if ( $pos > 0 ) {
286 0           $pos = 0;
287             }
288             else {
289 0           $self->{beep} = 1;
290             }
291             }
292             elsif ( $key == VK_UP || $key == VK_DOWN ) {
293 0           $self->{beep} = 1;
294             }
295             else {
296 0           $key = chr $key;
297 0           utf8::upgrade $key;
298 0 0 0       if ( $key eq "\n" || $key eq "\r" ) { #
299 0           print "\n";
300 0           $self->__reset_term();
301 0 0 0       if ( $self->{compat} || ! defined $self->{compat} && $ENV{READLINE_SIMPLE_COMPAT} ) {
      0        
302 0           return encode( 'console_in', $str->as_string );
303             }
304 0           return $str->as_string;
305             }
306             else {
307 0           $str->substr( $pos, 0, $key );
308 0           $pos++;
309             }
310             }
311             }
312             }
313              
314              
315             sub __print_readline {
316 0     0     my ( $self, $opt, $list, $str, $pos ) = @_;
317 0           my $print_str = $str->copy;
318 0           my $print_pos = $pos;
319 0           my $n = 1;
320 0           my ( $b, $e );
321 0           while ( $print_str->columns > $self->{avail_width_value} ) {
322 0 0         if ( $print_str->substr( 0, $print_pos )->columns > $self->{avail_width_value} / 4 ) {
323 0           $print_str->substr( 0, $n, '' );
324 0           $print_pos -= $n;
325 0           $b = 1;
326             }
327             else {
328 0           $print_str->substr( -$n, $n, '' );
329 0           $e = 1;
330             }
331             }
332 0 0         if ( $b ) {
333 0           $print_str->substr( 0, 1, '<' );
334             }
335 0 0         if ( $e ) {
336 0           $print_str->substr( $print_str->length(), 1, '>' );
337             }
338 0           my $key = $self->__padded_or_trimed_key( $list, $self->{curr_row} );
339 0           $self->{plugin}->__clear_line();
340 0 0         if ( $opt->{mark_curr} ) {
341 0           $self->{plugin}->__mark_current();
342 0           print "\r", $key;
343 0           $self->{plugin}->__reset();
344             }
345             else {
346 0           print "\r", $key;
347             }
348 0           my $sep = $self->{sep};
349 0 0   0     if ( any { $_ == $self->{curr_row} - @{$self->{pre_list}} } @{$opt->{ro}} ) {
  0            
  0            
  0            
350 0           $sep = $self->{sep_ro};
351             }
352 0 0         if ( $opt->{no_echo} ) {
353 0 0         if ( $opt->{no_echo} == 2 ) {
354 0           print $sep;
355 0           return;
356             }
357 0           print $sep, '*' x $print_str->length(), "\r";
358             }
359             else {
360 0           print $sep, $print_str->as_string, "\r";
361             }
362 0           $self->{plugin}->__right( $self->{length_prompt} + $print_str->substr( 0, $print_pos )->columns );
363              
364             }
365              
366              
367             sub __length_longest_key {
368 0     0     my ( $self, $list ) = @_;
369 0           my $len = []; #
370 0           my $longest = 0;
371 0           for my $i ( 0 .. $#$list ) {
372 0           my $gcs = Unicode::GCString->new( $list->[$i][0] );
373 0           $len->[$i] = $gcs->columns;
374 0 0         if ( $i < @{$self->{pre_list}} ) {
  0            
375 0           next;
376             }
377 0 0         $longest = $len->[$i] if $len->[$i] > $longest;
378             }
379 0           $self->{len_longest_key} = $longest;
380 0           $self->{length_key} = $len;
381             }
382              
383              
384             sub __prepare_size {
385 0     0     my ( $self, $opt, $list, $maxcols, $maxrows ) = @_;
386 0           $self->{avail_width} = $maxcols - 1;
387 0           $self->{avail_height} = $maxrows;
388 0 0         if ( defined $opt->{main_prompt} ) {
389 0           $self->{avail_height}--;
390             }
391 0 0         if ( @$list > $self->{avail_height} ) {
392 0           $self->{pages} = int @$list / ( $self->{avail_height} - 1 );
393 0 0         if ( @$list % ( $self->{avail_height} - 1 ) ) {
394 0           $self->{pages}++;
395             }
396 0           $self->{avail_height}--;
397             }
398             else {
399 0           $self->{pages} = 1;
400             }
401 0           return;
402             }
403              
404              
405             sub __gcstring_and_pos {
406 0     0     my ( $self, $list ) = @_;
407 0           my $default = $list->[$self->{curr_row}][1];
408 0 0         if ( ! defined $default ) {
409 0           $default = '';
410             }
411 0           my $str = Unicode::GCString->new( $default );
412 0           return $str, $str->length();
413             }
414              
415              
416             sub __print_current_row {
417 0     0     my ( $self, $opt, $list, $str, $pos ) = @_;
418 0           $self->{plugin}->__clear_line();
419 0 0         if ( $self->{curr_row} < @{$self->{pre_list}} ) {
  0            
420 0           $self->{plugin}->__reverse();
421 0           print $list->[$self->{curr_row}][0];
422 0           $self->{plugin}->__reset();
423             }
424             else {
425 0           $self->__print_readline( $opt, $list, $str, $pos );
426 0           $list->[$self->{curr_row}][1] = $str->as_string;
427             }
428             }
429              
430              
431             sub __print_row {
432 0     0     my ( $self, $opt, $list, $idx ) = @_;
433 0 0         if ( $idx < @{$self->{pre_list}} ) {
  0            
434 0           return $list->[$idx][0];
435             }
436             else {
437 0 0         my $val = defined $list->[$idx][1] ? $list->[$idx][1] : '';
438 4     4   4136 $val =~ s/\p{Space}/ /g;
  4         43  
  4         61  
  0            
439 0           $val =~ s/\p{C}//g;
440 0           my $sep = $self->{sep};
441 0 0   0     if ( any { $_ == $idx - @{$self->{pre_list}} } @{$opt->{ro}} ) {
  0            
  0            
  0            
442 0           $sep = $self->{sep_ro};
443             }
444             return
445             $self->__padded_or_trimed_key( $list, $idx ) . $sep .
446 0           $self->__unicode_trim( Unicode::GCString->new( $val ), $self->{avail_width_value} );
447             }
448             }
449              
450              
451             sub __write_screen {
452 0     0     my ( $self, $opt, $list ) = @_;
453 0           print join "\n", map { $self->__print_row( $opt, $list, $_ ) } $self->{begin_row} .. $self->{end_row};
  0            
454 0 0         if ( $self->{pages} > 1 ) {
455 0 0         if ( $self->{avail_height} - ( $self->{end_row} + 1 - $self->{begin_row} ) ) {
456 0           print "\n" x ( $self->{avail_height} - ( $self->{end_row} - $self->{begin_row} ) - 1 );
457             }
458 0           $self->{page} = int( $self->{end_row} / $self->{avail_height} ) + 1;
459 0           my $page_number = sprintf '- Page %d/%d -', $self->{page}, $self->{pages};
460 0 0         if ( length $page_number > $self->{avail_width} ) {
461 0           $page_number = substr sprintf( '%d/%d', $self->{page}, $self->{pages} ), 0, $self->{avail_width};
462             }
463 0           print "\n", $page_number;
464 0           $self->{plugin}->__up( $self->{avail_height} - ( $self->{curr_row} - $self->{begin_row} ) );
465             }
466             else {
467 0           $self->{page} = 1;
468 0           my $up_curr = $self->{end_row} - $self->{curr_row};
469 0           $self->{plugin}->__up( $up_curr );
470             }
471             }
472              
473              
474             sub __write_first_screen {
475 0     0     my ( $self, $opt, $list, $curr_row ) = @_;
476 0 0         if ( $self->{len_longest_key} > $self->{avail_width} / 3 ) {
477 0           $self->{len_longest_key} = int( $self->{avail_width} / 3 );
478             }
479 0           my $len_separator = Unicode::GCString->new( $self->{sep} )->columns;
480 0 0         if ( @{$opt->{ro}} ) {
  0            
481 0           my $tmp = Unicode::GCString->new( $self->{sep_ro} )->columns;
482 0 0         $len_separator = $tmp if $tmp > $len_separator;
483             }
484 0           $self->{length_prompt} = $self->{len_longest_key} + $len_separator;
485 0           $self->{avail_width_value} = $self->{avail_width} - $self->{length_prompt};
486 0 0         $self->{curr_row} = $opt->{auto_up} == 2 ? $curr_row : @{$self->{pre_list}};
  0            
487 0           $self->{begin_row} = 0;
488 0           $self->{end_row} = ( $self->{avail_height} - 1 );
489 0 0         if ( $self->{end_row} > $#$list ) {
490 0           $self->{end_row} = $#$list;
491             }
492 0 0         if ( defined $opt->{main_prompt} ) {
493 0           print $opt->{main_prompt}, "\n";
494             }
495 0           $self->__write_screen( $opt, $list );
496             }
497              
498              
499             sub fill_form {
500 0     0 1   my ( $self, $orig_list, $opt ) = @_;
501 0 0         if ( ! defined $orig_list ) {
    0          
502 0           croak "'fill_form' called with no argument.";
503             }
504             elsif ( ref $orig_list ne 'ARRAY' ) {
505 0           croak "'fill_form' requires an ARRAY reference as its argument.";
506             }
507 0 0 0       if ( defined $opt && ref $opt ne 'HASH' ) {
508 0           croak "'fill_form': the (optional) second argument must be a HASH reference";
509             }
510 0           my $valid = {
511             prompt => '',
512             back => '',
513             confirm => '',
514             auto_up => '[ 0 1 2 ]',
515             mark_curr => '[ 0 1 ]',
516             ro => 'ARRAY',
517             ####
518             sep => '', # remove
519             ####
520             };
521 0           my $list = [ map { [ @$_ ] } @$orig_list ];
  0            
522 0           $self->__validate_options( $opt, $valid );
523 0 0         $opt->{prompt} = $self->{prompt} if ! defined $opt->{prompt};
524 0 0         $opt->{back} = $self->{back} if ! defined $opt->{back};
525 0 0         $opt->{confirm} = $self->{confirm} if ! defined $opt->{confirm};
526 0 0         $opt->{auto_up} = $self->{auto_up} if ! defined $opt->{auto_up};
527 0 0         $opt->{ro} = $self->{ro} if ! defined $opt->{ro};
528 0           $opt->{main_prompt} = $opt->{prompt};
529 0           $self->{sep} = ': ';
530 0           $self->{sep_ro} = '| ';
531 0           $self->{pre_list} = [ [ $opt->{confirm} ] ];
532 0 0         if ( length $opt->{back} ) {
533 0           unshift @{$self->{pre_list}}, [ $opt->{back} ];
  0            
534             }
535 0           unshift @$list, @{$self->{pre_list}};
  0            
536 0           $self->__length_longest_key( $list );
537 0           $self->__init_term();
538 0           local $| = 1;
539 0           my ( $maxcols, $maxrows ) = $self->{plugin}->__term_buff_size();
540 0           $self->__prepare_size( $opt, $list, $maxcols, $maxrows );
541 0           $self->__write_first_screen( $opt, $list, 0 );
542 0           my ( $str, $pos ) = $self->__gcstring_and_pos( $list );
543              
544 0           LINE: while ( 1 ) {
545 0           my $locked = 0;
546 0 0   0     if ( any { $_ == $self->{curr_row} - @{$self->{pre_list}} } @{$opt->{ro}} ) {
  0            
  0            
  0            
547 0           $locked = 1;
548             }
549 0 0         if ( $self->{beep} ) {
550 0           $self->{plugin}->__beep();
551 0           $self->{beep} = 0;
552             }
553             else {
554 0           $self->__print_current_row( $opt, $list, $str, $pos );
555             }
556 0           my $key = $self->{plugin}->__get_key();
557 0 0         if ( ! defined $key ) {
558 0           $self->__reset_term();
559 0           carp "EOT: $!";
560 0           return;
561             }
562 0 0         next if $key == NEXT_get_key;
563 0 0         next if $key == KEY_TAB;
564 0           my ( $tmp_maxcols, $tmp_maxrows ) = $self->{plugin}->__term_buff_size();
565 0 0 0       if ( $tmp_maxcols != $maxcols || $tmp_maxrows != $maxrows && $tmp_maxrows < ( @$list + 1 ) ) {
      0        
566 0           ( $maxcols, $maxrows ) = ( $tmp_maxcols, $tmp_maxrows );
567 0           $self->__prepare_size( $opt, $list, $maxcols, $maxrows );
568 0           $self->{plugin}->__clear_screen();
569 0           $self->__write_first_screen( $opt, $list, 1 );
570 0           ( $str, $pos ) = $self->__gcstring_and_pos( $list );
571             }
572 0 0 0       if ( $key == KEY_BSPACE || $key == CONTROL_H ) {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
573 0 0         if ( $locked ) {
    0          
574 0           $self->{beep} = 1;
575             }
576             elsif ( $pos ) {
577 0           $pos--;
578 0           $str->substr( $pos, 1, '' );
579             }
580             else {
581 0           $self->{beep} = 1;
582             }
583             }
584             elsif ( $key == CONTROL_U ) {
585 0 0         if ( $locked ) {
    0          
586 0           $self->{beep} = 1;
587             }
588             elsif ( $pos ) {
589 0           $str->substr( 0, $pos, '' );
590 0           $pos = 0;
591             }
592             else {
593 0           $self->{beep} = 1;
594             }
595             }
596             elsif ( $key == CONTROL_K ) {
597 0 0         if ( $locked ) {
    0          
598 0           $self->{beep} = 1;
599             }
600             elsif ( $pos < $str->length() ) {
601 0           $str->substr( $pos, $str->length() - $pos, '' );
602             }
603             else {
604 0           $self->{beep} = 1;
605             }
606             }
607             elsif ( $key == VK_DELETE || $key == CONTROL_D ) {
608 0 0         if ( $str->length() ) {
609 0 0         if ( $locked ) {
    0          
610 0           $self->{beep} = 1;
611             }
612             elsif ( $pos < $str->length() ) {
613 0           $str->substr( $pos, 1, '' );
614             }
615             else {
616 0           $self->{beep} = 1;
617             }
618             }
619             else {
620 0           print "\n";
621 0           $self->__reset_term();
622 0           return;
623             }
624             }
625             elsif ( $key == VK_RIGHT ) {
626 0 0         if ( $pos < $str->length() ) {
627 0           $pos++;
628             }
629             else {
630 0           $self->{beep} = 1;
631             }
632             }
633             elsif ( $key == VK_LEFT ) {
634 0 0         if ( $pos ) {
635 0           $pos--;
636             }
637             else {
638 0           $self->{beep} = 1;
639             }
640             }
641             elsif ( $key == VK_END || $key == CONTROL_E ) {
642 0 0         if ( $pos < $str->length() ) {
643 0           $pos = $str->length();
644             }
645             else {
646 0           $self->{beep} = 1;
647             }
648             }
649             elsif ( $key == VK_HOME || $key == CONTROL_A ) {
650 0 0         if ( $pos > 0 ) {
651 0           $pos = 0;
652             }
653             else {
654 0           $self->{beep} = 1;
655             }
656             }
657             elsif ( $key == VK_UP ) {
658 0 0         if ( $self->{curr_row} == 0 ) {
659 0           $self->{beep} = 1;
660             }
661             else {
662 0           $self->{curr_row}--;
663 0           ( $str, $pos ) = $self->__gcstring_and_pos( $list );
664 0 0         if ( $self->{curr_row} >= $self->{begin_row} ) {
665 0           $self->__reset_previous_row( $opt, $list, $self->{curr_row} + 1 );
666 0           $self->{plugin}->__up( 1 );
667             }
668             else {
669 0           $self->__print_previous_page( $opt, $list );
670             }
671             }
672             }
673             elsif ( $key == VK_DOWN ) {
674 0 0         if ( $self->{curr_row} == $#$list ) {
675 0           $self->{beep} = 1;
676             }
677             else {
678 0           $self->{curr_row}++;
679 0           ( $str, $pos ) = $self->__gcstring_and_pos( $list );
680 0 0         if ( $self->{curr_row} <= $self->{end_row} ) {
681 0           $self->__reset_previous_row( $opt, $list, $self->{curr_row} - 1 );
682 0           $self->{plugin}->__down( 1 );
683             }
684             else {
685 0           $self->{plugin}->__up( $self->{end_row} - $self->{begin_row} );
686 0           $self->__print_next_page( $opt, $list );
687             }
688             }
689             }
690             elsif ( $key == VK_PAGE_UP || $key == CONTROL_B ) {
691 0 0         if ( $self->{page} == 1 ) {
692 0 0         if ( $self->{curr_row} == 0 ) {
693 0           $self->{beep} = 1;
694             }
695             else {
696 0           $self->__reset_previous_row( $opt, $list, $self->{curr_row} );
697 0           $self->{plugin}->__up( $self->{curr_row} );
698 0           $self->{curr_row} = 0;
699 0           ( $str, $pos ) = $self->__gcstring_and_pos( $list );
700             }
701             }
702             else {
703 0           $self->{plugin}->__up( $self->{curr_row} - $self->{begin_row} );
704 0           $self->{curr_row} = $self->{begin_row} - $self->{avail_height};
705 0           ( $str, $pos ) = $self->__gcstring_and_pos( $list );
706 0           $self->__print_previous_page( $opt, $list );
707             }
708             }
709             elsif ( $key == VK_PAGE_DOWN || $key == CONTROL_F ) {
710 0 0         if ( $self->{page} == $self->{pages} ) {
711 0 0         if ( $self->{curr_row} == $#$list ) {
712 0           $self->{beep} = 1;
713             }
714             else {
715 0           $self->__reset_previous_row( $opt, $list, $self->{curr_row} );
716 0           $self->{plugin}->__down( $self->{end_row} - $self->{curr_row} );
717 0           $self->{curr_row} = $self->{end_row};
718 0           ( $str, $pos ) = $self->__gcstring_and_pos( $list );
719             }
720             }
721             else {
722 0           $self->{plugin}->__up( $self->{curr_row} - $self->{begin_row} );
723 0           $self->{curr_row} = $self->{end_row} + 1;
724 0           ( $str, $pos ) = $self->__gcstring_and_pos( $list );
725 0           $self->__print_next_page( $opt, $list );
726             }
727             }
728             else {
729 0           $key = chr $key;
730 0           utf8::upgrade $key;
731 0 0 0       if ( $key eq "\n" || $key eq "\r" ) { #
732 0           my $up = $self->{curr_row} - $self->{begin_row};
733 0 0         $up += 1 if $opt->{main_prompt};
734 0 0         if ( $list->[$self->{curr_row}][0] eq $opt->{back} ) {
    0          
735 0           $self->{plugin}->__up( $up );
736 0           $self->{plugin}->__clear_lines_to_end_of_screen();
737 0           $self->__reset_term();
738 0           return;
739             }
740             elsif ( $list->[$self->{curr_row}][0] eq $opt->{confirm} ) {
741 0           $self->{plugin}->__up( $up );
742 0           $self->{plugin}->__clear_lines_to_end_of_screen();
743 0           $self->__reset_term();
744 0           splice @$list, 0, @{$self->{pre_list}};
  0            
745 0 0 0       if ( $self->{compat} || ! defined $self->{compat} && $ENV{READLINE_SIMPLE_COMPAT} ) {
      0        
746 0           return [ map { [ $_->[0], encode( 'console_in', $_->[1] ) ] } @$list ];
  0            
747             }
748 0           return $list;
749             }
750 0 0         if ( $opt->{auto_up} == 2 ) {
    0          
751 0 0         if ( $self->{curr_row} == 0 ) {
752 0           $self->{beep} = 1;
753             }
754             else {
755 0           $self->{plugin}->__up( $up );
756 0           $self->{plugin}->__clear_lines_to_end_of_screen();
757 0           ( $str, $pos ) = $self->__write_first_screen( $opt, $list, 0 );
758 0           ( $str, $pos ) = $self->__gcstring_and_pos( $list );
759             }
760             }
761             elsif ( $self->{curr_row} == $#$list ) {
762 0           $self->{plugin}->__up( $up );
763 0           $self->{plugin}->__clear_lines_to_end_of_screen();
764 0           ( $str, $pos ) = $self->__write_first_screen( $opt, $list, scalar @{$self->{pre_list}} );
  0            
765 0           ( $str, $pos ) = $self->__gcstring_and_pos( $list );
766 0           $self->{enter_col} = $pos;
767 0           $self->{enter_row} = $self->{curr_row};
768             }
769             else {
770 0 0         if ( $opt->{auto_up} == 1 ) {
771 0 0 0       if ( defined $self->{enter_row} && $self->{enter_row} == $self->{curr_row}
      0        
      0        
772             && defined $self->{enter_col} && $self->{enter_col} == $pos
773             ) {
774 0           $self->{beep} = 1;
775 0           next;
776             }
777             else {
778 0           delete $self->{enter_row};
779 0           delete $self->{enter_col};
780             }
781             }
782 0           $self->{curr_row}++;
783 0           ( $str, $pos ) = $self->__gcstring_and_pos( $list );
784 0 0         if ( $self->{curr_row} <= $self->{end_row} ) {
785 0           $self->__reset_previous_row( $opt, $list, $self->{curr_row} - 1 );
786 0           $self->{plugin}->__down( 1 );
787             }
788             else {
789 0           $self->{plugin}->__up( $up );
790 0           $self->__print_next_page( $opt, $list );
791             }
792             }
793             }
794             else {
795 0 0         if ( $locked ) {
796 0           $self->{beep} = 1;
797             }
798             else {
799 0           $str->substr( $pos, 0, $key );
800 0           $pos++;
801             }
802             }
803             }
804             }
805             }
806              
807              
808             sub __reset_previous_row {
809 0     0     my ( $self, $opt, $list, $idx ) = @_;
810 0           $self->{plugin}->__clear_line();
811 0           print $self->__print_row( $opt, $list, $idx );
812             }
813              
814              
815             sub __print_next_page {
816 0     0     my ( $self, $opt, $list ) = @_;
817 0           $self->{begin_row} = $self->{end_row} + 1;
818 0           $self->{end_row} = $self->{end_row} + $self->{avail_height};
819 0 0         $self->{end_row} = $#$list if $self->{end_row} > $#$list;
820 0           $self->{plugin}->__clear_lines_to_end_of_screen();
821 0           $self->__write_screen( $opt, $list );
822             }
823              
824              
825             sub __print_previous_page {
826 0     0     my ( $self, $opt, $list ) = @_;
827 0           $self->{end_row} = $self->{begin_row} - 1;
828 0           $self->{begin_row} = $self->{begin_row} - $self->{avail_height};
829 0 0         $self->{begin_row} = 0 if $self->{begin_row} < 0;
830 0           $self->{plugin}->__clear_lines_to_end_of_screen();
831 0           $self->__write_screen( $opt, $list );
832             }
833              
834              
835             sub __padded_or_trimed_key {
836 0     0     my ( $self, $list, $idx ) = @_;
837 0           my $unicode;
838 0           my $key_length = $self->{length_key}[$idx];
839 0           my $key = $list->[$idx][0];
840 0           $key =~ s/\p{Space}/ /g;
841 0           $key =~ s/\p{C}//g;
842 0 0         if ( $key_length > $self->{len_longest_key} ) {
    0          
843 0           my $gcs = Unicode::GCString->new( $key );
844 0           $unicode = $self->__unicode_trim( $gcs, $self->{len_longest_key} );
845             }
846             elsif ( $key_length < $self->{len_longest_key} ) {
847 0           $unicode = " " x ( $self->{len_longest_key} - $key_length ) . $key;
848             }
849             else {
850 0           $unicode = $key;
851             }
852 0           return $unicode;
853             }
854              
855              
856             sub __unicode_trim {
857 0     0     my ( $self, $gcs, $len ) = @_;
858 0 0         if ( $gcs->columns <= $len ) {
859 0           return $gcs->as_string;
860             }
861 0           my $pos = $gcs->pos;
862 0           $gcs->pos( 0 );
863 0           my $cols = 0;
864 0           my $gc;
865 0           while ( defined( $gc = $gcs->next ) ) {
866 0 0         if ( ( $len - 3 ) < ( $cols += $gc->columns ) ) {
867 0           my $ret = $gcs->substr( 0, $gcs->pos - 1 );
868 0           $gcs->pos( $pos );
869 0           return $ret->as_string . '...';
870             }
871             }
872             }
873              
874              
875             # use utf8;
876             # use open qw( :std :utf8 );
877              
878             # use Term::ReadLine::Simple;
879             # use Term::ReadLine; # 1.14
880             # use Devel::Peek;
881              
882             # my $default = 'ü'; # "\x{fc}"
883             # character read with readline: 'ä' # "\x{e4}"
884              
885              
886             #-----------------------------------------------------------
887              
888             # my $tr = Term::ReadLine->new( 'Stub' ); # default not supported
889              
890             #-----------------------------------------------------------
891              
892             # my $tr = Term::ReadLine->new( 'Perl' );
893             # my $line = $tr->readline( ': ' );
894              
895             ### ä "\303\244"\0
896              
897             #-----------------------------------------------------------
898              
899             # my $tr = Term::ReadLine->new( 'Perl' );
900             # my $line = $tr->readline( ': ', $default );
901              
902             ### üä "\303\274\303\203\302\244"\0 [UTF8 "\x{fc}\x{c3}\x{a4}"]
903              
904             #-----------------------------------------------------------
905              
906             # my $tr = Term::ReadLine->new( 'Perl', *STDIN, *STDOUT );
907             # my $line = $tr->readline( ': ', $default );
908              
909             ### üä "\303\274\303\244"\0 [UTF8 "\x{fc}\x{e4}"]
910              
911             #-----------------------------------------------------------
912              
913             # my $tr = Term::ReadLine->new( 'Gnu 1.26' );
914             # my $line = $tr->readline( ': ', $default );
915              
916             ### üä "\303\274\303\244"\0
917              
918             #-----------------------------------------------------------
919              
920             # my $tr = Term::ReadLine->new( 'Gnu 1.26', *STDIN, *STDOUT );
921             # my $line = $tr->readline( ': ', $default );
922              
923             ### üä "\303\274\303\244"\0
924              
925             #-----------------------------------------------------------
926              
927             # my $tr = Term::ReadLine::Simple->new();
928             # $tr->config( { compat => 0 } );
929             # my $line = $tr->readline( ': ', $default );
930              
931             ### üä "\303\274\303\244"\0 [UTF8 "\x{fc}\x{e4}"]
932              
933             #-----------------------------------------------------------
934              
935             # my $tr = Term::ReadLine::Simple->new();
936             # $tr->config( { compat => 1 } );
937             # my $line = $tr->readline( ': ', $default );
938              
939             ### üä "\303\274\303\244"\0
940              
941              
942             1;
943              
944             __END__