package Hexed::Term; use strict; use warnings; use include; use Hexed::Util; use Curses; # Initialize the window and pad. sub init { my ($class, $dat) = @_; # Ten thousand lines is QUITE a bit. We probably won't run out. $dat->{Pad} = newpad(CONFIG->termlines, $dat->{Width} + 1); $dat->{OffY} = 0; # for the pad $dat->{Prompt} = $dat->{Opts}{Prompt}; $dat->{Total} = 0; delete $dat->{Opts}; return bless $dat, $class; } # Add a message to the window. sub msg { my ($self, $msg) = @_; my @lines = wrap($self->{Width}, $msg); my $were = $#{ $self->{Msgs} }; push @{ $self->{Msgs} }, @lines; # Split by formatting tags. $self->{Pad}->standend; foreach my $line (@lines) { my $x = 0; foreach my $string (split(/(<[^>]+>)/, $line)) { next unless $string; # that split is weird if ($string =~ m/<([^>]+)>/) { $self->{Pad}->attron( paint $1 ); } else { # A normal part of the string. $self->{Pad}->addstr($self->{Total}, $x, $string); $x += length($string); } } $self->{Total}++; } # We must always reserve one line for the prompt. Thus, we can have a max of # height - 1 lines. If there's a prompt, that is. if ($#{ $self->{Msgs} } > $self->{Height} - $self->{Prompt}) { # Remove as many lines as we need. $self->{OffY} += $#{ $self->{Msgs} } - $self->{Height} + $self->{Prompt}; splice @{ $self->{Msgs} }, 0, $#{ $self->{Msgs} } - $self->{Height} + $self->{Prompt}; } return @lines; } # Display the messages in the scrolling region. sub draw { my $self = shift; $self->{Win}->refresh if shift; # fullscreen HexedUI stuff kills border $self->{Pad}->prefresh( $self->{OffY}, 0, # in the pad/grid $self->{Y1}, $self->{X1}, $self->{Y2}, $self->{X2} ); return 1; } # Perform a readline-esque input. sub prompt { my ($self, $prompt, $limit) = @_; die "Can't prompt on a promptless terminal!\n" unless $self->{Prompt}; my $y = $self->{Total}; my $x = 0; # relative in the grid, that is $limit = $self->{Width} - length($prompt) if $limit eq "line"; # We don't operate on the "window" like we once did. Instead, we directly # mess with the pad -- this prevents some window/pad conflicts. No big # chance, just $y is huge and we call draw, not refresh. my $win = $self->{Pad}; my @orig = ($y, $x); curs_set(1); my @in = (); my $pntr = 0; # Draw the prompt. $win->addstr($y, $x, $prompt); $x += length($prompt); # Our cursor wandered off, McGonagoll, I swear! Wait, this joke is old... $self->draw; my ($start, $end) = ($x, $x); # Our cursor can't go past these points until ((my $key = getch) eq "\n") { if ($key eq 260) { # Left next if $x == $start; # Boundary? $x--; $pntr--; $win->move($y, $x); $self->draw; } elsif ($key eq 261) { # Right next if $x == $end; # Boundary? $x++; $pntr++; $win->move($y, $x); $self->draw; } elsif ($key eq 263) { # Backspace next if $pntr == 0; # Anything there to erase? splice(@in, --$pntr, 1); $win->delch($y, --$x); $self->draw; $end--; } elsif ($key eq 330) { # Delete next if $start == $end; # Anything there to erase? splice(@in, $pntr, 1); $win->delch($y, $x); $self->draw; $end--; } elsif ($key eq 262) { # Home next if $x == $start; # Already there $x = $start; $pntr = 0; $win->move($y, $x); $self->draw; } elsif ($key eq 360) { # End next if $x == $end; # Already there $x = $end; $pntr = $#in + 1; $win->move($y, $x); $self->draw; } else { next if scalar @in == $limit; if ($x == $end) { # Are we at the end? $win->addch($y, $x++, $key); $self->draw; $end++; $in[$pntr] = $key; $pntr++; } else { # Or are we inserting? splice(@in, $pntr, 0, $key); # @in is correct. Make the display and cursor and such reflect that. foreach my $X ($start .. $end) { $win->addch($y, $X, $in[$X - $start]); } $end++; $x++; $pntr++; $win->move($y, $x); $self->draw; } } } # If we're a temporary prompt... curs_set(0); my $in = join "", @in; $self->msg($prompt . $in); return $in; } 42;