File Coverage

blib/lib/Term/Choose.pm
Criterion Covered Total %
statement 45 774 5.8
branch 8 452 1.7
condition 0 189 0.0
subroutine 14 38 36.8
pod 2 2 100.0
total 69 1455 4.7


line stmt bran cond sub pod time code
1             package Term::Choose;
2              
3 3     3   212178 use warnings;
  3         24  
  3         136  
4 3     3   17 use strict;
  3         9  
  3         55  
5 3     3   45 use 5.10.0;
  3         9  
6              
7             our $VERSION = '1.760';
8 3     3   32 use Exporter 'import';
  3         6  
  3         156  
9             our @EXPORT_OK = qw( choose );
10              
11 3     3   18 use Carp qw( croak carp );
  3         4  
  3         172  
12              
13 3     3   1605 use Term::Choose::Constants qw( :all );
  3         6  
  3         870  
14 3     3   1371 use Term::Choose::LineFold qw( line_fold print_columns cut_to_printwidth );
  3         8  
  3         231  
15 3     3   1240 use Term::Choose::Screen qw( :all );
  3         62  
  3         3222  
16 3     3   3598 use Term::Choose::ValidateOptions qw( validate_options );
  3         9  
  3         642  
17              
18             my $Plugin;
19              
20             BEGIN {
21 3 50   3   48 if ( $^O eq 'MSWin32' ) {
22 0         0 require Win32::Console::ANSI;
23 0         0 require Term::Choose::Win32;
24 0         0 $Plugin = 'Term::Choose::Win32';
25             }
26             else {
27 3         1855 require Term::Choose::Linux;
28 3         2644 $Plugin = 'Term::Choose::Linux';
29             }
30             }
31              
32             END {
33 3 50   3   3400 if ( $? == 255 ) {
34 0 0       0 if( $^O eq 'MSWin32' ) {
    0          
35 0         0 my $input = Win32::Console->new( Win32::Console::constant( "STD_INPUT_HANDLE", 0 ) );
36 0         0 $input->Mode( 0x0001|0x0002|0x0004 );
37 0         0 $input->Flush;
38             }
39             elsif ( TERM_READKEY ) {
40 0         0 Term::ReadKey::ReadMode( 'restore' );
41             }
42             else {
43 0         0 system( "stty sane" );
44             }
45 0         0 print "\n", clear_to_end_of_screen;
46 0         0 print show_cursor;
47             }
48             }
49              
50              
51             sub new {
52 115     115 1 43010 my $class = shift;
53 115         211 my ( $opt ) = @_;
54 115 50       283 croak "new: called with " . @_ . " arguments - 0 or 1 arguments expected" if @_ > 1;
55 115         231 my $instance_defaults = _defaults();
56 115 100       265 if ( defined $opt ) {
57 113 50       264 croak "new: the (optional) argument must be a HASH reference" if ref $opt ne 'HASH';
58 113         208 validate_options( _valid_options(), $opt, 'new' );
59 113         541 for my $key ( keys %$opt ) {
60 152 100       417 $instance_defaults->{$key} = $opt->{$key} if defined $opt->{$key};
61             }
62             }
63 115         223 my $self = bless $instance_defaults, $class;
64 115         872 $self->{backup_instance_defaults} = { %$instance_defaults };
65 115         527 $self->{plugin} = $Plugin->new();
66 115         603 return $self;
67             }
68              
69              
70             sub _valid_options {
71             return {
72 113     113   2382 beep => '[ 0 1 ]',
73             clear_screen => '[ 0 1 ]',
74             codepage_mapping => '[ 0 1 ]',
75             hide_cursor => '[ 0 1 ]',
76             index => '[ 0 1 ]',
77             mouse => '[ 0 1 ]',
78             order => '[ 0 1 ]',
79             alignment => '[ 0 1 2 ]',
80             color => '[ 0 1 2 ]',
81             include_highlighted => '[ 0 1 2 ]',
82             layout => '[ 0 1 2 ]',
83             page => '[ 0 1 2 ]',
84             search => '[ 0 1 2 ]',
85             keep => '[ 1-9 ][ 0-9 ]*',
86             ll => '[ 1-9 ][ 0-9 ]*',
87             max_cols => '[ 1-9 ][ 0-9 ]*',
88             max_height => '[ 1-9 ][ 0-9 ]*',
89             max_width => '[ 1-9 ][ 0-9 ]*',
90             default => '[ 0-9 ]+',
91             pad => '[ 0-9 ]+',
92             margin => 'Array_Int',
93             mark => 'Array_Int',
94             meta_items => 'Array_Int',
95             no_spacebar => 'Array_Int',
96             tabs_info => 'Array_Int',
97             tabs_prompt => 'Array_Int',
98             skip_items => 'Regexp',
99             empty => 'Str',
100             footer => 'Str',
101             info => 'Str',
102             prompt => 'Str',
103             undef => 'Str',
104             busy_string => 'Str',
105             };
106             };
107              
108              
109             sub _defaults {
110             return {
111 115     115   1100 alignment => 0,
112             beep => 0,
113             clear_screen => 0,
114             codepage_mapping => 0,
115             color => 0,
116             #default => undef,
117             empty => '',
118             #footer => undef,
119             hide_cursor => 1,
120             include_highlighted => 0,
121             index => 0,
122             info => '',
123             keep => 5,
124             layout => 1,
125             #ll => undef,
126             #margin => undef,
127             #mark => undef,
128             #max_cols => undef,
129             #max_height => undef,
130             #max_width => undef,
131             #meta_items => undef,
132             mouse => 0,
133             #no_spacebar => undef,
134             order => 1,
135             pad => 2,
136             page => 1,
137             #prompt => undef,
138             search => 1,
139             #skip_items => undef,
140             #tabs_info => undef,
141             #tabs_prompt => undef,
142             undef => '',
143             #busy_string => undef,
144             };
145             }
146              
147              
148             sub __copy_orig_list {
149 0     0     my ( $self, $orig_list_ref ) = @_;
150 0 0         if ( $self->{ll} ) {
151 0           $self->{list} = $orig_list_ref;
152             }
153             else {
154 0           $self->{list} = [ @$orig_list_ref ];
155 0 0         if ( $self->{color} ) {
156 0           $self->{orig_list} = $orig_list_ref;
157             }
158 0           for ( @{$self->{list}} ) {
  0            
159 0 0         if ( ! $_ ) {
160 0 0         $_ = $self->{undef} if ! defined $_;
161 0 0         $_ = $self->{empty} if ! length $_;
162             }
163 0 0         if ( $self->{color} ) {
164 0           s/\x{feff}//g;
165 0           s/\e\[[\d;]*m/\x{feff}/g;
166             }
167 0           s/\t/ /g;
168 0           s/\v+/\ \ /g;
169             # \p{Cn} might not be up to date and remove assigned codepoints
170             # therefore only \p{Noncharacter_Code_Point}
171 0           s/[\p{Cc}\p{Noncharacter_Code_Point}\p{Cs}]//g;
172             }
173             }
174             }
175              
176              
177             sub __length_list_elements {
178 0     0     my ( $self ) = @_;
179 0           my $list = $self->{list};
180 0 0         if ( $self->{ll} ) {
181 0           $self->{col_width} = $self->{ll};
182             }
183             else {
184 0           my $length_elements = [];
185 0           my $longest = 0;
186 0           for my $i ( 0 .. $#$list ) {
187 0           $length_elements->[$i] = print_columns( $list->[$i] );
188 0 0         $longest = $length_elements->[$i] if $length_elements->[$i] > $longest;
189             }
190 0           $self->{width_elements} = $length_elements;
191 0           $self->{col_width} = $longest;
192             }
193             }
194              
195              
196             sub __init_term {
197 0     0     my ( $self ) = @_;
198             my $config = {
199             mode => 'ultra-raw',
200             mouse => $self->{mouse},
201             hide_cursor => $self->{hide_cursor},
202 0           };
203 0           $self->{mouse} = $self->{plugin}->__set_mode( $config );
204             }
205              
206              
207             sub __reset_term {
208 0     0     my ( $self, $clear_choose ) = @_;
209 0 0         if ( defined $self->{plugin} ) {
210 0           $self->{plugin}->__reset_mode( { mouse => $self->{mouse}, hide_cursor => $self->{hide_cursor} } );
211             }
212 0 0         if ( $clear_choose ) {
213 0           my $up = $self->{i_row} + $self->{count_prompt_lines};
214 0 0         print up( $up ) if $up;
215 0           print "\r" . clear_to_end_of_screen();
216             }
217 0 0         if ( exists $self->{backup_instance_defaults} ) { # backup_instance_defaults exists if ObjectOriented
218 0           my $instance_defaults = $self->{backup_instance_defaults};
219 0           for my $key ( keys %$self ) {
220 0 0 0       if ( $key eq 'plugin' || $key eq 'backup_instance_defaults' ) {
    0          
221 0           next;
222             }
223             elsif ( exists $instance_defaults->{$key} ) {
224 0           $self->{$key} = $instance_defaults->{$key};
225             }
226             else {
227 0           delete $self->{$key};
228             }
229             }
230             }
231             }
232              
233              
234             sub __get_key {
235 0     0     my ( $self ) = @_;
236 0           my $key;
237 0 0         if ( defined $self->{skip_items} ) {
238 0           my $idx = $self->{rc2idx}[$self->{pos}[ROW]][$self->{pos}[COL]];
239 0 0         if ( $self->{list}[$idx] =~ $self->{skip_items} ) {
240 0           $key = $self->Term::Choose::Opt::SkipItems::__key_skipped();
241             }
242             }
243 0 0         if ( ! defined $key ) {
244 0           $key = $self->{plugin}->__get_key_OS( $self->{mouse} );
245             }
246 0 0         return $key if ref $key ne 'ARRAY';
247 0           return $self->Term::Choose::Opt::Mouse::__mouse_info_to_key( @$key );
248             }
249              
250              
251             sub __modify_options {
252 0     0     my ( $self ) = @_;
253 0 0 0       if ( defined $self->{max_cols} && $self->{max_cols} == 1 ) {
254 0           $self->{layout} = 2;
255             }
256 0 0 0       if ( length $self->{footer} && $self->{page} != 2 ) {
257 0           $self->{page} = 2;
258             }
259 0 0 0       if ( $self->{page} == 2 && ! $self->{clear_screen} ) {
260 0           $self->{clear_screen} = 1;
261             }
262 0 0 0       if ( $self->{max_cols} && $self->{layout} == 1 ) {
263 0           $self->{layout} = 0;
264             }
265 0 0         if ( ! defined $self->{prompt} ) {
266 0 0         $self->{prompt} = defined $self->{wantarray} ? 'Your choice:' : 'Close with ENTER';
267             }
268 0 0         if ( defined $self->{margin} ) {
269 0           ( $self->{margin_top}, $self->{margin_right}, $self->{margin_bottom}, $self->{margin_left} ) = @{$self->{margin}};
  0            
270 0 0         if ( ! defined $self->{tabs_prompt} ) {
271 0           $self->{tabs_prompt} = [ $self->{margin_left}, $self->{margin_left}, $self->{margin_right} ];
272             }
273 0 0         if ( ! defined $self->{tabs_info} ) {
274 0           $self->{tabs_info} = [ $self->{margin_left}, $self->{margin_left}, $self->{margin_right} ];
275             }
276             }
277             }
278              
279              
280             sub choose {
281 0 0   0 1   if ( ref $_[0] ne __PACKAGE__ ) {
282 0           my $ob = __PACKAGE__->new();
283 0           delete $ob->{backup_instance_defaults};
284 0           return $ob->__choose( @_ );
285             }
286 0           my $self = shift;
287 0           return $self->__choose( @_ );
288             }
289              
290              
291             sub __choose {
292 0     0     my $self = shift;
293 0           my ( $orig_list_ref, $opt ) = @_;
294 0 0 0       croak "choose: called with " . @_ . " arguments - 1 or 2 arguments expected" if @_ < 1 || @_ > 2;
295 0 0         croak "choose: the first argument must be an ARRAY reference" if ref $orig_list_ref ne 'ARRAY';
296 0 0         if ( defined $opt ) {
297 0 0         croak "choose: the (optional) second argument must be a HASH reference" if ref $opt ne 'HASH';
298 0           validate_options( _valid_options(), $opt, 'choose' );
299 0           for my $key ( keys %$opt ) {
300 0 0         $self->{$key} = $opt->{$key} if defined $opt->{$key};
301             }
302             }
303 0 0         if ( ! @$orig_list_ref ) {
304 0           return;
305             }
306 0           local $\ = undef;
307 0           local $, = undef;
308 0           local $| = 1;
309 0 0         if ( defined $self->{busy_string} ) {
310 0           print "\r" . clear_to_end_of_line();
311 0           print $self->{busy_string};
312             }
313 0           $self->{wantarray} = wantarray;
314 0           $self->__modify_options();
315 0 0         if ( $self->{mouse} ) {
316 0           require Term::Choose::Opt::Mouse;
317             }
318 0 0         if ( $^O eq "MSWin32" ) {
319 0 0         print $opt->{codepage_mapping} ? "\e(K" : "\e(U";
320             }
321 0           $self->__copy_orig_list( $orig_list_ref );
322 0           $self->__length_list_elements();
323 0 0         if ( defined $self->{skip_items} ) {
324 0           require Term::Choose::Opt::SkipItems;
325 0           $self->Term::Choose::Opt::SkipItems::__prepare_default();
326             }
327 0 0         if ( exists $ENV{TC_RESET_AUTO_UP} ) {
328 0           $ENV{TC_RESET_AUTO_UP} = 0;
329             }
330             local $SIG{INT} = sub {
331 0     0     $self->__reset_term();
332 0           exit;
333 0           };
334 0           $self->__init_term();
335 0           ( $self->{term_width}, $self->{term_height} ) = get_term_size();
336 0           $self->__wr_first_screen();
337 0           my $fast_page = 10;
338 0 0         if ( $self->{pp_count} > 10_000 ) {
339 0           $fast_page = 20;
340             }
341 0           my $saved_pos;
342              
343 0           GET_KEY: while ( 1 ) {
344 0           my $key = $self->__get_key();
345 0 0         if ( ! defined $key ) {
346 0           $self->__reset_term( 1 );
347 0           carp "EOT: $!";
348 0           return;
349             }
350 0           $self->{pressed_key} = $key;
351 0           my ( $new_width, $new_height ) = get_term_size();
352 0 0 0       if ( $new_width != $self->{term_width} || $new_height != $self->{term_height} ) {
353 0 0         if ( $self->{ll} ) {
354 0           $self->__reset_term( 0 );
355 0           return -1;
356             }
357 0           ( $self->{term_width}, $self->{term_height} ) = ( $new_width, $new_height );
358 0           $self->__copy_orig_list( $orig_list_ref );
359 0           $self->{default} = $self->{rc2idx}[$self->{pos}[ROW]][$self->{pos}[COL]];
360 0 0 0       if ( $self->{wantarray} && @{$self->{marked}} ) {
  0            
361 0           $self->{mark} = $self->__marked_rc2idx();
362             }
363 0           my $up = $self->{i_row} + $self->{count_prompt_lines};
364 0 0         print up( $up ) if $up;
365 0           print "\r" . clear_to_end_of_screen();
366 0           $self->__wr_first_screen();
367 0           next GET_KEY;
368             }
369 0 0         next GET_KEY if $key == NEXT_get_key;
370 0 0         next GET_KEY if $key == KEY_Tilde;
371 0 0 0       if ( exists $ENV{TC_RESET_AUTO_UP} && $ENV{TC_RESET_AUTO_UP} == 0 ) {
372 0 0 0       if ( $key != LINE_FEED && $key != CARRIAGE_RETURN ) {
373 0           $ENV{TC_RESET_AUTO_UP} = 1;
374             }
375             }
376 0           my $page_step = 1;
377 0 0         if ( $key == VK_INSERT ) {
    0          
378 0 0         $page_step = $fast_page if $self->{first_page_row} - $fast_page * $self->{avail_height} >= 0;
379 0           $key = VK_PAGE_UP;
380             }
381             elsif ( $key == VK_DELETE ) {
382 0 0         $page_step = $fast_page if $self->{last_page_row} + $fast_page * $self->{avail_height} <= $#{$self->{rc2idx}};
  0            
383 0           $key = VK_PAGE_DOWN;
384             }
385 0 0 0       if ( $saved_pos && $key != VK_PAGE_UP && $key != CONTROL_B && $key != VK_PAGE_DOWN && $key != CONTROL_F ) {
      0        
      0        
      0        
386 0           $saved_pos = undef;
387             }
388             # $self->{rc2idx} holds the new list (AoA) formatted in "__list_idx2rc" appropriate to the chosen layout.
389             # $self->{rc2idx} does not hold the values directly but the respective list indexes from the original list.
390             # If the original list would be ( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' ) and the new formatted list should be
391             # a d g
392             # b e h
393             # c f
394             # then the $self->{rc2idx} would look like this
395             # 0 3 6
396             # 1 4 7
397             # 2 5
398             # So e.g. the second value in the second row of the new list would be $self->{list}[ $self->{rc2idx}[1][1] ].
399             # On the other hand the index of the last row of the new list would be $#{$self->{rc2idx}}
400             # or the index of the last column in the first row would be $#{$self->{rc2idx}[0]}.
401              
402 0 0 0       if ( $key == VK_DOWN || $key == KEY_j ) {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0          
    0          
403 0 0 0       if ( ! $self->{rc2idx}[$self->{pos}[ROW]+1]
404             || ! $self->{rc2idx}[$self->{pos}[ROW]+1][$self->{pos}[COL]]
405             ) {
406 0           $self->__beep();
407             }
408             else {
409 0           $self->{pos}[ROW]++;
410 0 0         if ( $self->{pos}[ROW] <= $self->{last_page_row} ) {
411 0           $self->__wr_cell( $self->{pos}[ROW] - 1, $self->{pos}[COL] );
412 0           $self->__wr_cell( $self->{pos}[ROW] , $self->{pos}[COL] );
413             }
414             else {
415 0           $self->{first_page_row} = $self->{last_page_row} + 1;
416 0           $self->{last_page_row} = $self->{last_page_row} + $self->{avail_height};
417 0 0         $self->{last_page_row} = $#{$self->{rc2idx}} if $self->{last_page_row} > $#{$self->{rc2idx}};
  0            
  0            
418 0           $self->__wr_screen();
419             }
420             }
421             }
422             elsif ( $key == VK_UP || $key == KEY_k ) {
423 0 0         if ( $self->{pos}[ROW] == 0 ) {
424 0           $self->__beep();
425             }
426             else {
427 0           $self->{pos}[ROW]--;
428 0 0         if ( $self->{pos}[ROW] >= $self->{first_page_row} ) {
429 0           $self->__wr_cell( $self->{pos}[ROW] + 1, $self->{pos}[COL] );
430 0           $self->__wr_cell( $self->{pos}[ROW] , $self->{pos}[COL] );
431             }
432             else {
433 0           $self->{last_page_row} = $self->{first_page_row} - 1;
434 0           $self->{first_page_row} = $self->{first_page_row} - $self->{avail_height};
435 0 0         $self->{first_page_row} = 0 if $self->{first_page_row} < 0;
436 0           $self->__wr_screen();
437             }
438             }
439             }
440             elsif ( $key == KEY_TAB || $key == CONTROL_I ) { # KEY_TAB == CONTROL_I
441 0 0 0       if ( $self->{pos}[ROW] == $#{$self->{rc2idx}}
  0            
442 0           && $self->{pos}[COL] == $#{$self->{rc2idx}[$self->{pos}[ROW]]}
443             ) {
444 0           $self->__beep();
445             }
446             else {
447 0 0         if ( $self->{pos}[COL] < $#{$self->{rc2idx}[$self->{pos}[ROW]]} ) {
  0            
448 0           $self->{pos}[COL]++;
449 0           $self->__wr_cell( $self->{pos}[ROW], $self->{pos}[COL] - 1 );
450 0           $self->__wr_cell( $self->{pos}[ROW], $self->{pos}[COL] );
451             }
452             else {
453 0           $self->{pos}[ROW]++;
454 0 0         if ( $self->{pos}[ROW] <= $self->{last_page_row} ) {
455 0           $self->{pos}[COL] = 0;
456 0           $self->__wr_cell( $self->{pos}[ROW] - 1, $#{$self->{rc2idx}[$self->{pos}[ROW] - 1]} );
  0            
457 0           $self->__wr_cell( $self->{pos}[ROW] , $self->{pos}[COL] );
458             }
459             else {
460 0           $self->{first_page_row} = $self->{last_page_row} + 1;
461 0           $self->{last_page_row} = $self->{last_page_row} + $self->{avail_height};
462 0 0         $self->{last_page_row} = $#{$self->{rc2idx}} if $self->{last_page_row} > $#{$self->{rc2idx}};
  0            
  0            
463 0           $self->{pos}[COL] = 0;
464 0           $self->__wr_screen();
465             }
466             }
467             }
468             }
469             elsif ( $key == KEY_BSPACE || $key == KEY_BTAB || $key == CONTROL_H ) { # KEY_BTAB == CONTROL_H
470 0 0 0       if ( $self->{pos}[COL] == 0 && $self->{pos}[ROW] == 0 ) {
471 0           $self->__beep();
472             }
473             else {
474 0 0         if ( $self->{pos}[COL] > 0 ) {
475 0           $self->{pos}[COL]--;
476 0           $self->__wr_cell( $self->{pos}[ROW], $self->{pos}[COL] + 1 );
477 0           $self->__wr_cell( $self->{pos}[ROW], $self->{pos}[COL] );
478             }
479             else {
480 0           $self->{pos}[ROW]--;
481 0 0         if ( $self->{pos}[ROW] >= $self->{first_page_row} ) {
482 0           $self->{pos}[COL] = $#{$self->{rc2idx}[$self->{pos}[ROW]]};
  0            
483 0           $self->__wr_cell( $self->{pos}[ROW] + 1, 0 );
484 0           $self->__wr_cell( $self->{pos}[ROW] , $self->{pos}[COL] );
485             }
486             else {
487 0           $self->{last_page_row} = $self->{first_page_row} - 1;
488 0           $self->{first_page_row} = $self->{first_page_row} - $self->{avail_height};
489 0 0         $self->{first_page_row} = 0 if $self->{first_page_row} < 0;
490 0           $self->{pos}[COL] = $#{$self->{rc2idx}[$self->{pos}[ROW]]};
  0            
491 0           $self->__wr_screen();
492             }
493             }
494             }
495             }
496             elsif ( $key == VK_RIGHT || $key == KEY_l ) {
497 0 0         if ( $self->{pos}[COL] == $#{$self->{rc2idx}[$self->{pos}[ROW]]} ) {
  0            
498 0           $self->__beep();
499             }
500             else {
501 0           $self->{pos}[COL]++;
502 0           $self->__wr_cell( $self->{pos}[ROW], $self->{pos}[COL] - 1 );
503 0           $self->__wr_cell( $self->{pos}[ROW], $self->{pos}[COL] );
504             }
505             }
506             elsif ( $key == VK_LEFT || $key == KEY_h ) {
507 0 0         if ( $self->{pos}[COL] == 0 ) {
508 0           $self->__beep();
509             }
510             else {
511 0           $self->{pos}[COL]--;
512 0           $self->__wr_cell( $self->{pos}[ROW], $self->{pos}[COL] + 1 );
513 0           $self->__wr_cell( $self->{pos}[ROW], $self->{pos}[COL] );
514             }
515             }
516             elsif ( $key == VK_PAGE_UP || $key == CONTROL_P ) {
517 0 0         if ( $self->{first_page_row} <= 0 ) {
518 0           $self->__beep();
519             }
520             else {
521 0           $self->{first_page_row} = $self->{avail_height} * ( int( $self->{pos}[ROW] / $self->{avail_height} ) - $page_step );
522 0           $self->{last_page_row} = $self->{first_page_row} + $self->{avail_height} - 1;
523 0 0         if ( $saved_pos ) {
524 0           $self->{pos}[ROW] = $saved_pos->[ROW] + $self->{first_page_row};
525 0           $self->{pos}[COL] = $saved_pos->[COL];
526 0           $saved_pos = undef;
527             }
528             else {
529 0           $self->{pos}[ROW] -= $self->{avail_height} * $page_step;
530             }
531 0           $self->__wr_screen();
532             }
533             }
534             elsif ( $key == VK_PAGE_DOWN || $key == CONTROL_N ) {
535 0 0         if ( $self->{last_page_row} >= $#{$self->{rc2idx}} ) {
  0            
536 0           $self->__beep();
537             }
538             else {
539 0           my $backup_p_begin = $self->{first_page_row};
540 0           $self->{first_page_row} = $self->{avail_height} * ( int( $self->{pos}[ROW] / $self->{avail_height} ) + $page_step );
541 0           $self->{last_page_row} = $self->{first_page_row} + $self->{avail_height} - 1;
542 0 0         $self->{last_page_row} = $#{$self->{rc2idx}} if $self->{last_page_row} > $#{$self->{rc2idx}};
  0            
  0            
543 0 0 0       if ( $self->{pos}[ROW] + $self->{avail_height} > $#{$self->{rc2idx}}
  0            
544 0           || $self->{pos}[COL] > $#{$self->{rc2idx}[$self->{pos}[ROW] + $self->{avail_height}]}
545             ) {
546 0           $saved_pos = [ $self->{pos}[ROW] - $backup_p_begin, $self->{pos}[COL] ];
547 0           $self->{pos}[ROW] = $#{$self->{rc2idx}};
  0            
548 0 0         if ( $self->{pos}[COL] > $#{$self->{rc2idx}[$self->{pos}[ROW]]} ) {
  0            
549 0           $self->{pos}[COL] = $#{$self->{rc2idx}[$self->{pos}[ROW]]};
  0            
550             }
551             }
552             else {
553 0           $self->{pos}[ROW] += $self->{avail_height} * $page_step;
554             }
555 0           $self->__wr_screen();
556             }
557             }
558             elsif ( $key == VK_HOME || $key == CONTROL_A ) {
559 0 0 0       if ( $self->{pos}[COL] == 0 && $self->{pos}[ROW] == 0 ) {
560 0           $self->__beep();
561             }
562             else {
563 0           $self->{pos}[ROW] = 0;
564 0           $self->{pos}[COL] = 0;
565 0           $self->{first_page_row} = 0;
566 0           $self->{last_page_row} = $self->{first_page_row} + $self->{avail_height} - 1;
567 0 0         $self->{last_page_row} = $#{$self->{rc2idx}} if $self->{last_page_row} > $#{$self->{rc2idx}};
  0            
  0            
568 0           $self->__wr_screen();
569             }
570             }
571             elsif ( $key == VK_END || $key == CONTROL_E ) {
572 0 0 0       if ( $self->{order} == 1 && $self->{idx_of_last_col_in_last_row} < $#{$self->{rc2idx}[0]} ) {
  0            
573 0 0 0       if ( $self->{pos}[ROW] == $#{$self->{rc2idx}} - 1
  0            
574 0           && $self->{pos}[COL] == $#{$self->{rc2idx}[$self->{pos}[ROW]]}
575             ) {
576 0           $self->__beep();
577             }
578             else {
579 0   0       $self->{first_page_row} = @{$self->{rc2idx}} - ( @{$self->{rc2idx}} % $self->{avail_height} || $self->{avail_height} );
  0            
580 0           $self->{pos}[ROW] = $#{$self->{rc2idx}} - 1;
  0            
581 0           $self->{pos}[COL] = $#{$self->{rc2idx}[$self->{pos}[ROW]]};
  0            
582 0 0         if ( $self->{first_page_row} == $#{$self->{rc2idx}} ) {
  0            
583 0           $self->{first_page_row} = $self->{first_page_row} - $self->{avail_height};
584 0           $self->{last_page_row} = $self->{first_page_row} + $self->{avail_height} - 1;
585             }
586             else {
587 0           $self->{last_page_row} = $#{$self->{rc2idx}};
  0            
588             }
589 0           $self->__wr_screen();
590             }
591             }
592             else {
593 0 0 0       if ( $self->{pos}[ROW] == $#{$self->{rc2idx}}
  0            
594 0           && $self->{pos}[COL] == $#{$self->{rc2idx}[$self->{pos}[ROW]]}
595             ) {
596 0           $self->__beep();
597             }
598             else {
599 0   0       $self->{first_page_row} = @{$self->{rc2idx}} - ( @{$self->{rc2idx}} % $self->{avail_height} || $self->{avail_height} );
  0            
600 0           $self->{last_page_row} = $#{$self->{rc2idx}};
  0            
601 0           $self->{pos}[ROW] = $#{$self->{rc2idx}};
  0            
602 0           $self->{pos}[COL] = $#{$self->{rc2idx}[$self->{pos}[ROW]]};
  0            
603 0           $self->__wr_screen();
604             }
605             }
606             }
607             elsif ( $key == KEY_q || $key == CONTROL_Q ) {
608 0           $self->__reset_term( 1 );
609 0           return;
610             }
611             elsif ( $key == CONTROL_C ) {
612 0           $self->__reset_term( 1 );
613 0           print STDERR "^C\n";
614 0           exit 1;
615             }
616             elsif ( $key == LINE_FEED || $key == CARRIAGE_RETURN ) { # LINE_FEED == CONTROL_J, CARRIAGE_RETURN == CONTROL_M # ENTER key
617 0 0         if ( length $self->{search_info} ) {
618 0           require Term::Choose::Opt::Search;
619 0           $self->Term::Choose::Opt::Search::__search_end();
620 0           next GET_KEY;
621             }
622 0   0       my $opt_index = $self->{index} || $self->{ll};
623 0           my $list_idx = $self->{rc2idx}[$self->{pos}[ROW]][$self->{pos}[COL]];
624 0 0         if ( ! defined $self->{wantarray} ) {
    0          
625 0           $self->__reset_term( 1 );
626 0           return;
627             }
628             elsif ( $self->{wantarray} ) {
629 0 0         if ( $self->{include_highlighted} == 1 ) {
    0          
630 0           $self->{marked}[$self->{pos}[ROW]][$self->{pos}[COL]] = 1;
631             }
632             elsif ( $self->{include_highlighted} == 2 ) {
633 0           my $chosen = $self->__marked_rc2idx();
634 0 0         if ( ! @$chosen ) {
635 0           $self->{marked}[$self->{pos}[ROW]][$self->{pos}[COL]] = 1;
636             }
637             }
638 0 0 0       if ( defined $self->{meta_items} && ! $self->{marked}[$self->{pos}[ROW]][$self->{pos}[COL]] ) {
639 0           for my $meta_item ( @{$self->{meta_items}} ) {
  0            
640 0 0         if ( $meta_item == $list_idx ) {
641 0           $self->{marked}[$self->{pos}[ROW]][$self->{pos}[COL]] = 1;
642 0           last;
643             }
644             }
645             }
646 0           my $chosen = $self->__marked_rc2idx();
647 0           $self->__reset_term( 1 );
648 0 0         return $opt_index ? @$chosen : @{$orig_list_ref}[@$chosen];
  0            
649             }
650             else {
651 0 0         my $chosen = $opt_index ? $list_idx : $orig_list_ref->[$list_idx];
652 0           $self->__reset_term( 1 );
653 0           return $chosen;
654             }
655             }
656             elsif ( $key == KEY_SPACE ) {
657 0 0         if ( $self->{wantarray} ) {
658 0           my $list_idx = $self->{rc2idx}[$self->{pos}[ROW]][$self->{pos}[COL]];
659 0           my $locked = 0;
660 0 0 0       if ( defined $self->{no_spacebar} || defined $self->{meta_items} ) {
661 0 0         for my $no_spacebar ( @{$self->{no_spacebar}||[]}, @{$self->{meta_items}||[]} ) {
  0 0          
  0            
662 0 0         if ( $list_idx == $no_spacebar ) {
663 0           ++$locked;
664 0           last;
665             }
666             }
667             }
668 0 0         if ( $locked ) {
669 0           $self->__beep();
670             }
671             else {
672 0           $self->{marked}[$self->{pos}[ROW]][$self->{pos}[COL]] = ! $self->{marked}[$self->{pos}[ROW]][$self->{pos}[COL]];
673 0           $self->__wr_cell( $self->{pos}[ROW], $self->{pos}[COL] );
674             }
675             }
676             else {
677 0           $self->__beep();
678             }
679             }
680             elsif ( $key == CONTROL_SPACE ) {
681 0 0         if ( $self->{wantarray} ) {
682 0           for my $i ( 0 .. $#{$self->{rc2idx}} ) {
  0            
683 0           for my $j ( 0 .. $#{$self->{rc2idx}[$i]} ) {
  0            
684 0           $self->{marked}[$i][$j] = ! $self->{marked}[$i][$j];
685             }
686             }
687 0 0         if ( $self->{skip_items} ) {
688 0           $self->Term::Choose::Opt::SkipItems::__unmark_skip_items();
689             }
690 0 0         if ( defined $self->{no_spacebar} ) {
691 0           $self->__marked_idx2rc( $self->{no_spacebar}, 0 );
692             }
693 0 0         if ( defined $self->{meta_items} ) {
694 0           $self->__marked_idx2rc( $self->{meta_items}, 0 );
695             }
696              
697 0           $self->__wr_screen();
698             }
699             else {
700 0           $self->__beep();
701             }
702             }
703             elsif ( $key == CONTROL_F && $self->{search} ) {
704 0           require Term::Choose::Opt::Search;
705 0 0         if ( $self->{ll} ) {
706 0           $ENV{TC_POS_AT_SEARCH} = $self->{rc2idx}[$self->{pos}[ROW]][$self->{pos}[COL]];
707 0           $self->__reset_term( 0 );
708 0           return -13;
709             }
710 0 0         if ( length $self->{search_info} ) {
711 0           $self->Term::Choose::Opt::Search::__search_end();
712             }
713 0           $self->Term::Choose::Opt::Search::__search_begin();
714             }
715             else {
716 0           $self->__beep();
717             }
718             }
719             }
720              
721              
722             sub __beep {
723 0     0     my ( $self, $beep ) = @_;
724 0 0         if ( $beep ) {
725 0           print bell();
726             }
727             }
728              
729              
730             sub __prepare_info_and_prompt_lines {
731 0     0     my ( $self ) = @_;
732 0           my $info_w = $self->{term_width};
733 0 0 0       if ( $^O ne 'MSWin32' && $^O ne 'cygwin' ) {
734 0           $info_w += WIDTH_CURSOR;
735             }
736 0 0 0       if ( $self->{max_width} && $info_w > $self->{max_width} ) { ##
737 0           $info_w = $self->{max_width};
738             }
739 0           my @tmp_prompt;
740 0 0         if ( $self->{margin_top} ) {
741 0           push @tmp_prompt, ( '' ) x $self->{margin_top};
742             }
743 0 0         if ( length $self->{info} ) {
744 0   0       my $init = $self->{tabs_info}[0] // 0;
745 0   0       my $subseq = $self->{tabs_info}[1] // 0;
746 0   0       my $r_margin = $self->{tabs_info}[2] // 0;
747             push @tmp_prompt, line_fold(
748             $self->{info}, $info_w - $r_margin,
749 0           { init_tab => ' ' x $init, subseq_tab => ' ' x $subseq, color => $self->{color}, join => 0 }
750             );
751             }
752 0 0         if ( length $self->{prompt} ) {
753 0   0       my $init = $self->{tabs_prompt}[0] // 0;
754 0   0       my $subseq = $self->{tabs_prompt}[1] // 0;
755 0   0       my $r_margin = $self->{tabs_prompt}[2] // 0;
756             push @tmp_prompt, line_fold(
757             $self->{prompt}, $info_w - $r_margin,
758 0           { init_tab => ' ' x $init, subseq_tab => ' ' x $subseq, color => $self->{color}, join => 0 }
759             );
760             }
761 0 0         if ( length $self->{search_info} ) {
762 0 0         push @tmp_prompt, ( $self->{margin_left} ? ' ' x $self->{margin_left} : '' ) . $self->{search_info};
763             }
764 0           $self->{count_prompt_lines} = @tmp_prompt;
765 0 0         if ( ! $self->{count_prompt_lines} ) {
766 0           $self->{prompt_copy} = '';
767 0           return;
768             }
769 0           $self->{prompt_copy} = join( "\n\r", @tmp_prompt ) . "\n\r"; #
770             # \n\r -> stty 'raw' mode and Term::Readkey 'ultra-raw' mode don't translate newline to carriage_return/newline
771             }
772              
773              
774             sub __prepare_footer_line {
775 0     0     my ( $self ) = @_;
776 0 0         if ( exists $self->{footer_fmt} ) {
777 0           delete $self->{footer_fmt};
778             }
779 0           my $pp_total = int( $#{$self->{rc2idx}} / $self->{avail_height} ) + 1;
  0            
780 0 0 0       if ( $self->{page} == 0 ) {
    0          
781             # nothing to do
782             }
783             elsif ( $self->{page} == 1 && $pp_total == 1 ) {
784 0           $self->{avail_height}++;
785             }
786             else {
787 0           my $pp_total_width = length $pp_total;
788 0           $self->{footer_fmt} = '--- %0' . $pp_total_width . 'd/' . $pp_total . ' --- ';
789 0 0         if ( defined $self->{footer} ) {
790 0           $self->{footer_fmt} .= $self->{footer};
791             }
792 0 0         if ( print_columns( sprintf $self->{footer_fmt}, $pp_total ) > $self->{avail_width} ) { # color
793 0           $self->{footer_fmt} = '%0' . $pp_total_width . 'd/' . $pp_total;
794 0 0         if ( length( sprintf $self->{footer_fmt}, $pp_total ) > $self->{avail_width} ) {
795 0 0         $pp_total_width = $self->{avail_width} if $pp_total_width > $self->{avail_width};
796 0           $self->{footer_fmt} = '%0' . $pp_total_width . '.' . $pp_total_width . 's';
797             }
798             }
799             }
800 0           $self->{pp_count} = $pp_total;
801             }
802              
803              
804             sub __set_cell {
805 0     0     my ( $self, $list_idx ) = @_;
806 0           LOOP: for my $i ( 0 .. $#{$self->{rc2idx}} ) {
  0            
807 0           for my $j ( 0 .. $#{$self->{rc2idx}[$i]} ) {
  0            
808 0 0         if ( $list_idx == $self->{rc2idx}[$i][$j] ) {
809 0           $self->{pos} = [ $i, $j ];
810 0           last LOOP;
811             }
812             }
813             }
814 0           $self->{first_page_row} = $self->{avail_height} * int( $self->{pos}[ROW] / $self->{avail_height} );
815 0           $self->{last_page_row} = $self->{first_page_row} + $self->{avail_height} - 1;
816 0 0         $self->{last_page_row} = $#{$self->{rc2idx}} if $self->{last_page_row} > $#{$self->{rc2idx}};
  0            
  0            
817             }
818              
819              
820             sub __wr_first_screen {
821 0     0     my ( $self ) = @_;
822 0           $self->__avail_screen_size();
823 0           $self->__current_layout();
824 0           $self->__list_idx2rc();
825 0           $self->__prepare_footer_line();
826 0           $self->{first_page_row} = 0;
827 0           my $avail_height_idx = $self->{avail_height} - 1;
828 0 0         $self->{last_page_row} = $avail_height_idx > $#{$self->{rc2idx}} ? $#{$self->{rc2idx}} : $avail_height_idx;
  0            
  0            
829 0           $self->{i_row} = 0;
830 0           $self->{i_col} = 0;
831 0           $self->{pos} = [ 0, 0 ];
832 0           $self->{marked} = [];
833 0 0 0       if ( $self->{wantarray} && defined $self->{mark} ) {
834 0           $self->__marked_idx2rc( $self->{mark}, 1 );
835             }
836 0 0 0       if ( defined $self->{default} && $self->{default} <= $#{$self->{list}} ) {
  0            
837 0           $self->__set_cell( $self->{default} );
838             }
839 0 0         if ( $self->{clear_screen} ) {
840 0           print clear_screen();
841             }
842             else {
843 0           print "\r" . clear_to_end_of_screen();
844             }
845 0 0         if ( $self->{prompt_copy} ne '' ) {
846 0           print $self->{prompt_copy};
847             }
848 0           $self->__wr_screen();
849 0 0         if ( $self->{mouse} ) {
850 0           my $abs_cursor_y = $self->{plugin}->__get_cursor_row();
851 0           $self->{offset_rows} = $abs_cursor_y - 1 - $self->{i_row};
852             }
853             }
854              
855              
856             sub __wr_screen {
857 0     0     my ( $self ) = @_;
858 0           $self->__goto( 0, 0 );
859 0           print "\r" . clear_to_end_of_screen();
860 0 0         if ( defined $self->{footer_fmt} ) {
    0          
861 0           my $pp_line = sprintf $self->{footer_fmt}, int( $self->{first_page_row} / $self->{avail_height} ) + 1;
862 0 0         if ( $self->{margin_left} ) {
863 0           print right( $self->{margin_left} );
864             }
865 0           print "\n" x ( $self->{avail_height} );
866 0           print $pp_line . "\r";
867 0 0         if ( $self->{margin_bottom} ) {
868 0           print "\n" x $self->{margin_bottom};
869 0           print up( $self->{margin_bottom} );
870             }
871 0           print up( $self->{avail_height} );
872             }
873             elsif ( $self->{margin_bottom} ) {
874 0           my $count = ( $self->{last_page_row} - $self->{first_page_row} ) + $self->{margin_bottom};
875 0           print "\n" x $count;
876 0           print up( $count );
877             }
878 0 0         if ( $self->{margin_left} ) {
879 0           print right( $self->{margin_left} ); # left margin after each "\r"
880             }
881 0           my $pad_str = ' ' x $self->{pad};
882 0           for my $row ( $self->{first_page_row} .. $self->{last_page_row} ) {
883 0           my $line = $self->__prepare_cell( $row, 0 );
884 0 0         if ( $#{$self->{rc2idx}[$row]} ) { #
  0            
885 0           for my $col ( 1 .. $#{$self->{rc2idx}[$row]} ) {
  0            
886 0           $line = $line . $pad_str . $self->__prepare_cell( $row, $col );
887             }
888             }
889 0           print $line . "\n\r";
890 0 0         if ( $self->{margin_left} ) {
891 0           print right( $self->{margin_left} );
892             }
893             }
894 0           print up( $self->{last_page_row} - $self->{first_page_row} + 1 );
895             # relativ cursor pos: 0, 0
896 0           $self->__wr_cell( $self->{pos}[ROW], $self->{pos}[COL] );
897             }
898              
899              
900             sub __prepare_cell {
901 0     0     my( $self, $row, $col ) = @_;
902 0   0       my $is_current_pos = $row == $self->{pos}[ROW] && $col == $self->{pos}[COL];
903 0 0         my $emphasised = ( $self->{marked}[$row][$col] ? bold_underline() : '' ) . ( $is_current_pos ? reverse_video() : '' );
    0          
904 0           my $idx = $self->{rc2idx}[$row][$col];
905 0 0         if ( $self->{ll} ) {
906 0 0         if ( $self->{color} ) {
907 0           my $str = $self->{list}[$idx];
908 0 0         if ( $emphasised ) {
909 0 0 0       if ( $is_current_pos && $self->{color} == 1 ) {
910             # no color for the selected cell if color == 1
911 0           $str =~ s/(\e\[[\d;]*m)//g;
912             }
913             else {
914             # keep marked cells marked after color escapes
915 0           $str =~ s/(\e\[[\d;]*m)/${1}$emphasised/g;
916             }
917 0           $str = $emphasised . $str;
918             }
919 0           return $str . normal();
920             }
921             else {
922 0 0         if ( $emphasised ) {
923 0           return $emphasised . $self->{list}[$idx] . normal();
924             }
925             else {
926 0           return $self->{list}[$idx];
927             }
928             }
929             }
930             else {
931 0 0         my $str = $self->{current_layout} == -1 ? $self->{list}[$idx] : $self->__pad_str_to_colwidth( $idx );
932 0 0         if ( $self->{color} ) {
933 0           my @color;
934 0 0         if ( ! $self->{orig_list}[$idx] ) {
935 0 0         if ( ! defined $self->{orig_list}[$idx] ) {
    0          
936 0           @color = $self->{undef} =~ /(\e\[[\d;]*m)/g;
937             }
938             elsif ( ! length $self->{orig_list}[$idx] ) {
939 0           @color = $self->{empty} =~ /(\e\[[\d;]*m)/g;
940             }
941             }
942             else {
943 0           @color = $self->{orig_list}[$idx] =~ /(\e\[[\d;]*m)/g;
944             }
945 0 0         if ( $emphasised ) {
946 0           for ( @color ) {
947             # keep marked cells marked after color escapes
948 0           $_ .= $emphasised;
949             }
950 0           $str = $emphasised . $str . normal();
951 0 0 0       if ( $is_current_pos && $self->{color} == 1 ) {
952             # no color for the selected cell if color == 1
953 0           @color = ();
954 0           $str =~ s/\x{feff}//g;
955             }
956             }
957 0 0         if ( @color ) {
958 0           $str =~ s/\x{feff}/shift @color/ge;
  0            
959 0 0         if ( ! $emphasised ) {
960 0           $str .= normal();
961             }
962             }
963 0           return $str;
964             }
965             else {
966 0 0         if ( $emphasised ) {
967 0           $str = $emphasised . $str . normal();
968             }
969 0           return $str;
970             }
971             }
972             }
973              
974              
975             sub __wr_cell {
976 0     0     my( $self, $row, $col ) = @_;
977 0           my $idx = $self->{rc2idx}[$row][$col];
978 0 0         if ( $self->{current_layout} == -1 ) {
979 0           my $x = 0;
980 0 0         if ( $col > 0 ) {
981 0           for my $cl ( 0 .. $col - 1 ) {
982 0           my $i = $self->{rc2idx}[$row][$cl];
983 0           $x += $self->{width_elements}[$i] + $self->{pad};
984             }
985             }
986 0           $self->__goto( $row - $self->{first_page_row}, $x );
987 0           $self->{i_col} = $self->{i_col} + $self->{width_elements}[$idx];
988             }
989             else {
990 0           $self->__goto( $row - $self->{first_page_row}, $col * $self->{col_width_plus} );
991 0           $self->{i_col} = $self->{i_col} + $self->{col_width};
992             }
993 0           print $self->__prepare_cell( $row, $col );
994             }
995              
996              
997             sub __pad_str_to_colwidth {
998 0     0     my ( $self, $idx ) = @_;
999 0 0         if ( $self->{width_elements}[$idx] < $self->{col_width} ) {
    0          
1000 0 0         if ( $self->{alignment} == 0 ) {
    0          
    0          
1001 0           return $self->{list}[$idx] . ( " " x ( $self->{col_width} - $self->{width_elements}[$idx] ) );
1002             }
1003             elsif ( $self->{alignment} == 1 ) {
1004 0           return " " x ( $self->{col_width} - $self->{width_elements}[$idx] ) . $self->{list}[$idx];
1005             }
1006             elsif ( $self->{alignment} == 2 ) {
1007 0           my $all = $self->{col_width} - $self->{width_elements}[$idx];
1008 0           my $half = int( $all / 2 );
1009 0           return ( " " x $half ) . $self->{list}[$idx] . ( " " x ( $all - $half ) );
1010             }
1011             }
1012             elsif ( $self->{width_elements}[$idx] > $self->{col_width} ) {
1013 0 0         if ( $self->{col_width} > 6 ) {
1014 0           return cut_to_printwidth( $self->{list}[$idx], $self->{col_width} - 3 ) . '...';
1015             }
1016             else {
1017 0           return cut_to_printwidth( $self->{list}[$idx], $self->{col_width} );
1018             }
1019             }
1020             else {
1021 0           return $self->{list}[$idx];
1022             }
1023             }
1024              
1025              
1026             sub __goto {
1027 0     0     my ( $self, $newrow, $newcol ) = @_;
1028             # requires up, down, left or right to be 1 or greater
1029 0 0         if ( $newrow > $self->{i_row} ) {
    0          
1030 0           print down( $newrow - $self->{i_row} );
1031 0           $self->{i_row} = $newrow;
1032             }
1033             elsif ( $newrow < $self->{i_row} ) {
1034 0           print up( $self->{i_row} - $newrow );
1035 0           $self->{i_row} = $newrow;
1036             }
1037 0 0         if ( $newcol > $self->{i_col} ) {
    0          
1038 0           print right( $newcol - $self->{i_col} );
1039 0           $self->{i_col} = $newcol;
1040             }
1041             elsif ( $newcol < $self->{i_col} ) {
1042 0           print left( $self->{i_col} - $newcol );
1043 0           $self->{i_col} = $newcol;
1044             }
1045             }
1046              
1047              
1048             sub __avail_screen_size {
1049 0     0     my ( $self ) = @_;
1050 0           ( $self->{avail_width}, $self->{avail_height} ) = ( $self->{term_width}, $self->{term_height} );
1051 0 0         if ( $self->{margin_left} ) {
1052 0           $self->{avail_width} -= $self->{margin_left};
1053             }
1054 0 0         if ( $self->{margin_right} ) {
1055 0           $self->{avail_width} -= $self->{margin_right};
1056             }
1057 0 0 0       if ( $self->{margin_right} || ( $self->{col_width} > $self->{avail_width} && $^O ne 'MSWin32' && $^O ne 'cygwin' ) ) {
      0        
      0        
1058 0           $self->{avail_width} += WIDTH_CURSOR;
1059             # + WIDTH_CURSOR: use also the last terminal column if there is only one item-column;
1060             # with only one item-column the output doesn't get messed up if an item
1061             # reaches the right edge of the terminal on a non-MSWin32-OS
1062             }
1063 0 0 0       if ( $self->{max_width} && $self->{avail_width} > $self->{max_width} ) {
1064 0           $self->{avail_width} = $self->{max_width};
1065             }
1066 0 0         if ( $self->{avail_width} < 1 ) {
1067 0           $self->{avail_width} = 1;
1068             }
1069             #if ( $self->{ll} && $self->{ll} > $self->{avail_width} ) {
1070             # return -2;
1071             #}
1072 0           $self->__prepare_info_and_prompt_lines();
1073 0 0         if ( $self->{count_prompt_lines} ) {
1074 0           $self->{avail_height} -= $self->{count_prompt_lines};
1075             }
1076 0 0         if ( $self->{page} ) {
1077 0           $self->{avail_height}--;
1078             }
1079 0 0         if ( $self->{margin_bottom} ) {
1080 0           $self->{avail_height} -= $self->{margin_bottom};
1081             }
1082 0 0         if ( $self->{avail_height} < $self->{keep} ) {
1083 0 0         $self->{avail_height} = $self->{term_height} >= $self->{keep} ? $self->{keep} : $self->{term_height};
1084             }
1085 0 0 0       if ( $self->{max_height} && $self->{max_height} < $self->{avail_height} ) {
1086 0           $self->{avail_height} = $self->{max_height};
1087             }
1088             }
1089              
1090              
1091             sub __current_layout {
1092 0     0     my ( $self ) = @_;
1093 0           my $all_in_first_row;
1094 0 0 0       if ( $self->{layout} <= 1 && ! $self->{ll} && ! $self->{max_cols} ) {
      0        
1095 0           my $firstrow_width = 0;
1096 0           for my $list_idx ( 0 .. $#{$self->{list}} ) {
  0            
1097 0           $firstrow_width += $self->{width_elements}[$list_idx] + $self->{pad};
1098 0 0         if ( $firstrow_width - $self->{pad} > $self->{avail_width} ) {
1099 0           $firstrow_width = 0;
1100 0           last;
1101             }
1102             }
1103 0           $all_in_first_row = $firstrow_width;
1104             }
1105 0 0         if ( $all_in_first_row ) {
    0          
1106 0           $self->{current_layout} = -1;
1107             }
1108             elsif ( $self->{col_width} >= $self->{avail_width} ) {
1109 0           $self->{current_layout} = 2;
1110 0           $self->{col_width} = $self->{avail_width};
1111             }
1112             else {
1113 0           $self->{current_layout} = $self->{layout};
1114             }
1115 0           $self->{col_width_plus} = $self->{col_width} + $self->{pad};
1116             # 'col_width_plus' no effects if layout == 2
1117             }
1118              
1119              
1120             sub __list_idx2rc {
1121 0     0     my ( $self ) = @_;
1122 0           my $layout = $self->{current_layout};
1123 0           $self->{rc2idx} = [];
1124 0 0         if ( $layout == -1 ) {
    0          
1125 0           $self->{rc2idx}[0] = [ 0 .. $#{$self->{list}} ];
  0            
1126 0           $self->{idx_of_last_col_in_last_row} = $#{$self->{list}};
  0            
1127             }
1128             elsif ( $layout == 2 ) {
1129 0           for my $list_idx ( 0 .. $#{$self->{list}} ) {
  0            
1130 0           $self->{rc2idx}[$list_idx][0] = $list_idx;
1131 0           $self->{idx_of_last_col_in_last_row} = 0;
1132             }
1133             }
1134             else {
1135 0           my $tmp_avail_width = $self->{avail_width} + $self->{pad};
1136             # auto_format
1137 0 0         if ( $layout == 1 ) {
1138 0           my $tmc = int( @{$self->{list}} / $self->{avail_height} );
  0            
1139 0 0         $tmc++ if @{$self->{list}} % $self->{avail_height};
  0            
1140 0           $tmc *= $self->{col_width_plus};
1141 0 0         if ( $tmc < $tmp_avail_width ) {
1142 0           $tmc = int( $tmc + ( ( $tmp_avail_width - $tmc ) / 1.5 ) );
1143 0           $tmp_avail_width = $tmc;
1144             }
1145             }
1146             # order
1147 0           my $cols_per_row = int( $tmp_avail_width / $self->{col_width_plus} );
1148 0 0 0       if ( $self->{max_cols} && $cols_per_row > $self->{max_cols} ) {
1149 0           $cols_per_row = $self->{max_cols};
1150             }
1151 0 0         $cols_per_row = 1 if $cols_per_row < 1;
1152 0   0       $self->{idx_of_last_col_in_last_row} = ( @{$self->{list}} % $cols_per_row || $cols_per_row ) - 1;
1153 0 0         if ( $self->{order} == 1 ) {
1154 0           my $rows = int( ( @{$self->{list}} - 1 + $cols_per_row ) / $cols_per_row );
  0            
1155 0           my @rearranged_idx;
1156 0           my $begin = 0;
1157 0           my $end = $rows - 1 ;
1158 0           for my $c ( 0 .. $cols_per_row - 1 ) {
1159 0 0         --$end if $c > $self->{idx_of_last_col_in_last_row};
1160 0           $rearranged_idx[$c] = [ $begin .. $end ];
1161 0           $begin = $end + 1;
1162 0           $end = $begin + $rows - 1;
1163             }
1164 0           for my $r ( 0 .. $rows - 1 ) {
1165 0           my @temp_idx;
1166 0           for my $c ( 0 .. $cols_per_row - 1 ) {
1167 0 0 0       next if $r == $rows - 1 && $c > $self->{idx_of_last_col_in_last_row};
1168 0           push @temp_idx, $rearranged_idx[$c][$r];
1169             }
1170 0           push @{$self->{rc2idx}}, \@temp_idx;
  0            
1171             }
1172             }
1173             else {
1174 0           my $begin = 0;
1175 0           my $end = $cols_per_row - 1;
1176 0 0         $end = $#{$self->{list}} if $end > $#{$self->{list}};
  0            
  0            
1177 0           push @{$self->{rc2idx}}, [ $begin .. $end ];
  0            
1178 0           while ( $end < $#{$self->{list}} ) {
  0            
1179 0           $begin += $cols_per_row;
1180 0           $end += $cols_per_row;
1181 0 0         $end = $#{$self->{list}} if $end > $#{$self->{list}};
  0            
  0            
1182 0           push @{$self->{rc2idx}}, [ $begin .. $end ];
  0            
1183             }
1184             }
1185             }
1186             }
1187              
1188              
1189             sub __marked_idx2rc {
1190 0     0     my ( $self, $list_of_indexes, $boolean ) = @_;
1191 0           my $last_list_idx = $#{$self->{list}};
  0            
1192 0 0         if ( $self->{current_layout} == 2 ) {
1193 0           for my $list_idx ( @$list_of_indexes ) {
1194 0 0         if ( $list_idx > $last_list_idx ) {
1195 0           next;
1196             }
1197 0           $self->{marked}[$list_idx][0] = $boolean;
1198             }
1199 0           return;
1200             }
1201 0           my ( $row, $col );
1202 0           my $cols_per_row = @{$self->{rc2idx}[0]};
  0            
1203 0 0         if ( $self->{order} == 0 ) {
    0          
1204 0           for my $list_idx ( @$list_of_indexes ) {
1205 0 0         if ( $list_idx > $last_list_idx ) {
1206 0           next;
1207             }
1208 0           $row = int( $list_idx / $cols_per_row );
1209 0           $col = $list_idx % $cols_per_row;
1210 0           $self->{marked}[$row][$col] = $boolean;
1211             }
1212             }
1213             elsif ( $self->{order} == 1 ) {
1214 0           my $rows_per_col = @{$self->{rc2idx}};
  0            
1215 0           my $col_count_last_row = $self->{idx_of_last_col_in_last_row} + 1;
1216 0           my $last_list_idx_in_cols_full = $rows_per_col * $col_count_last_row - 1;
1217 0           my $first_list_idx_in_cols_short = $last_list_idx_in_cols_full + 1;
1218              
1219 0           for my $list_idx ( @$list_of_indexes ) {
1220 0 0         if ( $list_idx > $last_list_idx ) {
1221 0           next;
1222             }
1223 0 0         if ( $list_idx < $last_list_idx_in_cols_full ) {
1224 0           $row = $list_idx % $rows_per_col;
1225 0           $col = int( $list_idx / $rows_per_col );
1226             }
1227             else {
1228 0           my $rows_per_col_short = $rows_per_col - 1;
1229 0           $row = ( $list_idx - $first_list_idx_in_cols_short ) % $rows_per_col_short;
1230 0           $col = int( ( $list_idx - $col_count_last_row ) / $rows_per_col_short );
1231             }
1232 0           $self->{marked}[$row][$col] = $boolean;
1233             }
1234             }
1235             }
1236              
1237              
1238             sub __marked_rc2idx {
1239 0     0     my ( $self ) = @_;
1240 0           my $list_idx = [];
1241 0 0         if ( $self->{order} == 1 ) {
1242 0           for my $col ( 0 .. $#{$self->{rc2idx}[0]} ) {
  0            
1243 0           for my $row ( 0 .. $#{$self->{rc2idx}} ) {
  0            
1244 0 0         if ( $self->{marked}[$row][$col] ) {
1245 0           push @$list_idx, $self->{rc2idx}[$row][$col];
1246             }
1247             }
1248             }
1249             }
1250             else {
1251 0           for my $row ( 0 .. $#{$self->{rc2idx}} ) {
  0            
1252 0           for my $col ( 0 .. $#{$self->{rc2idx}[$row]} ) {
  0            
1253 0 0         if ( $self->{marked}[$row][$col] ) {
1254 0           push @$list_idx, $self->{rc2idx}[$row][$col];
1255             }
1256             }
1257             }
1258             }
1259 0           return $list_idx;
1260             }
1261              
1262              
1263             1;
1264              
1265              
1266             __END__