--- iselect.pl 2007/10/25 11:22:18 1 +++ iselect.pl 2007/10/25 15:12:43 10 @@ -2,141 +2,200 @@ use strict; use Term::Screen; +use Carp qw/cluck/; use Data::Dump qw/dump/; my $data = <<'EOF'; First line -+first selectable -+ second selectable +{s}first selectable +{s}second selectable a space.... ...infinity and beyond -+foo -+bar +{s}foo +{s}bar bum + EOF open(my $ps, "ps ax |") || die "can't do ps ax: $!"; while(<$ps>) { - $data .= '+'.$_; - $data .= ' '.$_; + $data .= '{s}'.$_; + $data .= $_; } close($ps); +$data .= "\n--EOF--"; -my @lines = split(/\n/, $data); +my $scr; -my $scr = new Term::Screen || die "can't init Term::Screen"; -$scr->clrscr()->noecho(); +# leave sane terminal if script dies +$SIG{__DIE__} = sub { + eval { system('stty sane'); }; +}; + + +my @lines = split(/\n/, $data); -my $o = 0; # offset in original text +my $top_screen_line = 0; # offset in original text my $pos = 0; +# default: select first line +my $sel_pos = 0; +my $status_text = ''; +my $error_text = ''; + +my $status_lines = 3; + # find which lines are selectable in input file my $selectable_line; for my $l (0 .. $#lines) { - next if (length($lines[$l]) < 2); - my $foo = ' '; - if ($lines[$l] !~ m/^\s/o) { + if ($lines[$l] =~ s/^{s}//) { $selectable_line->{$l}++; - $foo = '*'; } - warn "$l: $foo $lines[$l]\n"; +} + +# select first selectable line + +if ( $selectable_line ) { + $pos = $sel_pos = (sort { $a <=> $b } keys %$selectable_line)[0]; + warn "selected first selectable line $sel_pos"; } sub full_line { my $t = shift; - my $l = length($t); + $t = '' unless defined $t; return $t . (" " x ($scr->cols - length($t))); } sub chunk { my $t = shift; - my $o = ''; - $o = substr($t,1,$scr->cols) if length($t) > 1; - return $o . ( ' ' x ( $scr->cols - length($o) - 1 ) ); + cluck "expected line" unless defined $t; + return substr($t,0,$scr->cols); +} + +sub redraw_line { + my ($l,$line) = @_; + + if ( defined $selectable_line->{ $l + $top_screen_line } ) { + $scr->at($l,0)->bold()->puts( full_line( chunk($line) ) )->normal(); + } else { + $scr->at($l,0)->puts( full_line( chunk($line) ) ); + } } sub redraw { - for my $l (0 .. $scr->rows) { - my $line = $lines[ $l + $o ] || ''; - next if (length($line) < 2); - if (substr($line,0,1) !~ m/^\s/o) { - $scr->at($l,0)->bold()->puts( full_line( chunk($line) ) )->normal(); - } else { - $scr->at($l,0)->puts( full_line( chunk($line) ) ); - } + for my $l (0 .. $scr->rows - $status_lines) { + my $line = $lines[ $l + $top_screen_line ]; + redraw_line( $l, $line ); last if ($l == $#lines); } + selected(0); } -# default: select first line -my $sel_pos = 0; -my $status_text = ''; -my $error_text = ''; -$pos = 0; - sub status { my $pcnt = int(($pos || 0) * 100 / ($#lines || 1)); my $pos_txt = sprintf('%d, %d%% ',$pos,$pcnt); - $scr->at($scr->rows - 2,0)->reverse()->puts( + $scr->at($scr->rows - $status_lines + 1,0)->reverse()->puts( sprintf(' %-'.($scr->cols - length($pos_txt) - 2).'s ',$status_text) .$pos_txt)->normal(); - $scr->at($scr->rows - 1,0)->puts( + $scr->at($scr->rows - $status_lines + 2,0)->puts( sprintf('%-'.$scr->cols.'s', $error_text) ); } sub selected { - my $d = shift || return; + my $d = shift || 0; - if ( $selectable_line->{ $pos } ) { - $scr->at($pos-$o,0)->bold()->puts( chunk($lines[$pos]) )->normal(); - } else { - $scr->at($pos-$o,0)->puts( chunk($lines[$pos]) )->normal(); - } - $pos += $d; + my $screen_line = $pos - $top_screen_line; - my $max_row = $scr->rows - 3; + redraw_line( $screen_line, $lines[$pos] ); - if ($pos < 1) { - $error_text = "Already at Begin."; - $pos = 0; + my $last_screen_line = $scr->rows - $status_lines; + + if ( $d < 0 && $screen_line == 0 ) { + if ( $pos > 0 ) { + $top_screen_line--; + $pos--; + } else { + $error_text = "Already at Begin."; + } redraw; - } elsif ($pos > $max_row) { - $o = $pos - $max_row; # put selected line on last - $error_text = "Already at End."; + } elsif ( $d > 0 && $screen_line == $last_screen_line ) { + if ( $pos < $#lines ) { + $top_screen_line++; + $pos++; + } else { + $error_text = "Already at End."; + } redraw; + } else { + $pos += $d; } - $scr->at($pos-$o,0)->reverse()->puts(chunk($lines[$pos]))->normal(); + my $line = $lines[$pos]; + if ( defined $selectable_line->{ $pos } ) { + $scr->at($pos - $top_screen_line,0)->reverse->bold()->puts( full_line( chunk($line) ) )->normal(); + $sel_pos = $pos; + } else { + $scr->at($pos - $top_screen_line,0)->reverse->puts( full_line( chunk($line) ) ); + $sel_pos = -1; + } status; } -$status_text = "let's see does it work?"; +$scr = new Term::Screen || die "can't init Term::Screen"; +$scr->clrscr()->noecho(); redraw; selected; while(my $key = $scr->getch()) { - $status_text = "key: $key pos: $pos sel_pos: $sel_pos"; $error_text = ""; + my $lines_on_screen = $scr->rows - $status_lines; + if ($key eq 'ku') { selected( -1 ); } elsif ($key eq 'kd') { selected( +1 ); + } elsif ($key eq 'pgup' ) { + # first line on screen? + if ( $pos == $top_screen_line ) { + $top_screen_line -= $lines_on_screen; + $top_screen_line = 0 if $top_screen_line < 0; + redraw; + } + selected( -( $pos - $top_screen_line ) ); + } elsif ($key eq 'pgdn' ) { + # last line on screen? + if ( $pos - $top_screen_line == $lines_on_screen ) { + $top_screen_line += $lines_on_screen; + $top_screen_line = $#lines - $lines_on_screen if $top_screen_line >= $#lines - $lines_on_screen; + redraw; + } + selected( $top_screen_line + $lines_on_screen - $pos ); + } + + $status_text = sprintf("pos: %-3d sel_pos: %-3d top_screen_line: %-3d", $pos, $sel_pos, $top_screen_line ); + if ( length($key) > 1 ) { + $status_text .= " key: $key"; + } else { + $status_text .= sprintf("key: %s [%03d][%02x]", $key =~ m/\w/ ? $key : '?' , ord($key), ord($key) ); } status; + # CTRL+L + redraw if ord($key) eq 0x0c; + exit if (lc($key) eq 'q'); }