File Coverage

blib/lib/Term/ReadLine/Simple.pm
Criterion Covered Total %
statement 80 530 15.0
branch 18 282 6.3
condition 0 78 0.0
subroutine 26 46 56.5
pod 4 12 33.3
total 128 948 13.5


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