Author: bdonlan
Date: 2005-01-03 22:24:33 -0500 (Mon, 03 Jan 2005)
New Revision: 513
Added:
trunk/clients/termvisual/Term/
trunk/clients/termvisual/Term/Visual.pm
Modified:
trunk/
trunk/clients/termvisual/termvisual.pl
Log:
[EMAIL PROTECTED]: bdonlan | 2005-01-04T03:24:03.086981Z
Add modded term::visual; add a use lib to termvisual.pl to search the cwd
Property changes on: trunk
___________________________________________________________________
Name: svk:merge
- 1f59643a-e6e5-0310-bc24-f7d4c744f460:/haver/local/trunk:10318
27e50396-46e3-0310-8b22-ae223a1f35ce:/local:212
edfcd8bd-4ce7-0310-a97e-bb1efd40edf3:/local:238
+ 1f59643a-e6e5-0310-bc24-f7d4c744f460:/haver/local/trunk:10320
27e50396-46e3-0310-8b22-ae223a1f35ce:/local:212
edfcd8bd-4ce7-0310-a97e-bb1efd40edf3:/local:238
Added: trunk/clients/termvisual/Term/Visual.pm
===================================================================
--- trunk/clients/termvisual/Term/Visual.pm 2005-01-04 01:57:15 UTC (rev
512)
+++ trunk/clients/termvisual/Term/Visual.pm 2005-01-04 03:24:33 UTC (rev
513)
@@ -0,0 +1,2340 @@
+# $Id: Visual.pm,v 0.06 2003/01/14 23:00:18 lunartear Exp $
+# Copyrights and documentation are after __END__.
+package Term::Visual;
+use strict;
+use warnings;
+use vars qw($VERSION $REVISION $console);
+$VERSION = '0.06';
+$REVISION = do [EMAIL PROTECTED]: 0.06 $=~/\d+/g);sprintf"%d."."%02d"x$#r,@r};
+
+use Term::Visual::StatusBar;
+use POE qw(Wheel::Curses Wheel::ReadWrite );
+use Curses;
+use Carp;
+
+BEGIN {
+ my $debug_default = 0;
+
+ $debug_default++ if defined $ENV{TV_DEBUG};
+ defined &DEBUG or eval "sub DEBUG () { $debug_default }";
+
+ if (&DEBUG) {
+ my $debug_file = $ENV{TV_LOG_FILE} || 'term_visual.log';
+ defined &DEBUG_FILE or eval "sub DEBUG_FILE () { '$debug_file' }";
+ open ERRS, ">" . &DEBUG_FILE or croak "Can't open Debug file: $!";
+ }
+}
+
+### Term::Visual Constants.
+
+sub WINDOW () { 0 } # hash of windows and there properties
+sub WINDOW_REV () { 1 } # window name => id key value pair for reverse
lookups
+sub PALETTE () { 2 } # Palette Element
+sub PAL_COL_SEQ () { 3 } # Palette Color Sequence
+sub CUR_WIN () { 4 } # holds the current window id
+sub ERRLEVEL () { 5 } # Error Level boolean
+sub ALIAS () { 6 } # Visterm's Alias
+sub BINDINGS () { 7 } # key bindings
+sub COMMON_INPUT () { 8 } # Common input boolean
+
+### Palette Constants.
+
+sub PAL_PAIR () { 0 } # Index of the COLOR_PAIR in the palette.
+sub PAL_NUMBER () { 1 } # Index of the color number in the palette.
+sub PAL_DESC () { 2 } # Text description of the color.
+
+### Title line constants.
+
+sub TITLE_LINE () { 0 } # Where the title goes.
+sub TITLE_COL () { 0 }
+
+
+sub current_window {
+ if (DEBUG) { print ERRS " Enter current_window\n"; }
+ my $self = shift;
+ return $self->[CUR_WIN];
+}
+
+
+sub CREATE_WINDOW_ID {
+ if (DEBUG) { print ERRS "Enter CREATE_WINDOW_ID\n"; }
+ my $self = shift;
+ my $id = 0;
+ my @list = sort {$a <=> $b} keys %{$self->[WINDOW]};
+ if (@list) {
+ my $high_number = $list[$#list] + 1;
+ for my $i (0..$high_number) {
+ next if (defined $list[$i] && $i == $list[$i]);
+ $id = $i; last;
+ }
+ }
+ return $id;
+}
+
+
+### Mold the Object.
+
+sub new {
+ if (DEBUG) { print ERRS "Enter Visterm->new\n"; }
+ my $package = shift;
+ my %params = @_;
+ my $alias = delete $params{Alias};
+ my $errlevel = delete $params{Errlevel} || 0;
+ my $current_window = -1;
+
+ my $common_input = delete $params{Common_Input};
+ # These options only make sense if Common_Input is specified
+ my $tabcomplete = delete $params{Tab_Complete};
+ my $history_size = delete $params{History_Size};
+
+ my $self =
+ bless [ { }, # WINDOW stores window properties under each window id.
+ { }, # WINDOW_REV reverse window lookups.
+ { }, # Palette
+ 0, # Palette Color Sequence
+ $current_window,
+ $errlevel, # Visterms error level.
+ $alias,
+ { }, # BINDINGS
+ $common_input ? {
+ History_Position => -1,
+ History_Size => $history_size,
+ Command_History => [ ],
+ Data => "",
+ Data_Save => "",
+ Cursor => 0,
+ Cursor_Save => 0,
+ Tab_Complete => $tabcomplete,
+ Insert => 1,
+ Edit_Position => 0,
+ } : undef,
+ ], $package;
+
+ POE::Session->create
+ ( object_states =>
+ [ $self => # $_[OBJECT]
+ { _start => "start_terminal",
+ _stop => "terminal_stopped",
+ send_me_input => "register_input",
+ private_input => "got_curses_input",
+ got_stderr => "got_stderr",
+ shutdown => "shutdown",
+ } ],
+ args => [ $alias ],
+
+ );
+
+ return $self;
+}
+
+sub got_stderr {
+ my ($self, $kernel, $stderr_line) = @_[OBJECT, KERNEL, ARG0];
+ my $window_id = $self->[CUR_WIN];
+
+if (DEBUG) { print ERRS $stderr_line, "\n"; }
+
+ &print($self, $window_id,
+ "\0(stderr_bullet)" .
+ "2>" .
+ "\0(ncolor)" .
+ " " .
+ "\0(stderr_text)" .
+ $stderr_line );
+}
+
+sub start_terminal {
+ if (DEBUG) { print ERRS "Enter start_terminal\n"; }
+ my ($kernel, $heap, $object, $alias) = @_[KERNEL, HEAP, OBJECT, ARG0];
+
+ $kernel->alias_set( $alias );
+ $console = POE::Wheel::Curses->new( InputEvent => 'private_input');
+ use_default_colors();
+ my $old_mouse_events = 0;
+ mousemask(0, $old_mouse_events);
+
+ #TODO See about adding support for a wheel mouse after defining old mouse
+ # events above, so that copy/paste will work as expected.
+
+ ### Set Colors used by Visterm
+ _set_color( $object, stderr_bullet => "bright white on red",
+ stderr_text => "bright yellow on black",
+ ncolor => "white on black",
+ statcolor => "green on black",
+ st_frames => "bright cyan on blue",
+ st_values => "bright white on blue", );
+
+
+ ### Redirect STDERR into the terminal buffer.
+ use Symbol qw(gensym);
+
+ # Pipe STDERR to a readable handle.
+ my $read_stderr = gensym();
+
+ pipe($read_stderr, STDERR) or do
+ { open STDERR, ">&=2";
+ die "can't pipe STDERR: $!";
+ };
+
+ $heap->{stderr_reader} = POE::Wheel::ReadWrite->new
+ ( Handle => $read_stderr,
+ Filter => POE::Filter::Line->new(),
+ Driver => POE::Driver::SysRW->new(),
+ InputEvent => "got_stderr",
+ );
+
+}
+
+### create a curses window
+### TODO add error handling
+
+sub create_window {
+ if (DEBUG) { print ERRS "Enter create_window\n"; }
+ my $self = shift;
+ my %params = @_;
+ my $use_title = 1 unless defined $params{Use_Title};
+ my $use_status = 1 unless defined $params{Use_Status};
+ my $new_window_id = CREATE_WINDOW_ID($self);
+ my $window_name = $params{Window_Name} || $new_window_id;
+
+ my $input;
+ if ($self->[COMMON_INPUT]) {
+ $input = $self->[COMMON_INPUT];
+ }
+ else {
+ $input = {
+ History_Position => -1,
+ History_Size => 50,
+ Command_History => [ ],
+ Data => "",
+ Data_Save => "",
+ Cursor => 0,
+ Cursor_Save => 0,
+ Tab_Complete => undef,
+ Insert => 1,
+ Edit_Position => 0,
+ };
+ }
+ # Allow override of possible global options
+ if ($params{History_Size}) {
+ $input->{History_Size} = $params{History_Size};
+ }
+ if ($params{Tab_Complete}) {
+ $input->{Tab_Complete} = $params{Tab_Complete};
+ }
+
+ if (defined $new_window_id) {
+ if (DEBUG) { print ERRS "new_window_id is defined: $new_window_id\n"; }
+ if (!$self->[WINDOW]->{$new_window_id}) {
+ $self->[WINDOW]->{$new_window_id} =
+ { Buffer => [ ],
+ Buffer_Size => $params{Buffer_Size} || 500,
+ Input => $input,
+ Use_Title => $use_title,
+ Use_Status => $use_status,
+ Scrolled_Lines => 0,
+ Window_Id => $new_window_id,
+ Window_Name => $window_name };
+
+ my $winref = $self->[WINDOW]->{$new_window_id};
+
+ # Set the newly created window as the current window
+ $self->[CUR_WIN] = $new_window_id;
+
+ $self->[WINDOW_REV]->{$window_name} = $new_window_id;
+
+ # create the screen, statusbar, title, and entryline
+ # for this window instance
+
+ if ($winref->{Use_Title}) {
+ $winref->{Title_Start} = 0;
+ $winref->{Title_Height} = 1;
+ $winref->{Title} = $params{Title} || "";
+
+ $winref->{Screen_Start} = $winref->{Title_Start} + 1;
+
+ $winref->{Window_Title} = newwin( $winref->{Title_Height},
+ $COLS,
+ $winref->{Title_Start},
+ 0 );
+ # Should we die here??
+ die "No title!!" unless defined $winref->{Window_Title};
+
+ my $title = $winref->{Window_Title};
+
+ $title->bkgd($self->[PALETTE]->{st_frames}->[PAL_PAIR]);
+ $title->erase();
+ _refresh_title( $self, $new_window_id);
+ }
+
+ if ($winref->{Use_Status}) {
+ $winref->{Status_Height} = $params{Status_Height} || 2;
+ $winref->{Status_Start} = $LINES - $winref->{Status_Height} - 1;
+
+ #FIXME I think this got lost when the new design was implemented.
+ $winref->{Def_Status_Field} = [ ];
+
+ $winref->{Screen_End} = $winref->{Status_Start} - 1;
+
+ $winref->{Window_Status} = newwin( $winref->{Status_Height},
+ $COLS,
+ $winref->{Status_Start},
+ 0 );
+ my $status = $winref->{Window_Status};
+ if (DEBUG) { print ERRS $status, " <-status in create_window\n"; }
+ $status->bkgd($self->[PALETTE]->{st_frames}->[PAL_PAIR]);
+ $status->erase();
+ $status->noutrefresh();
+
+ $winref->{Status_Object} = Term::Visual::StatusBar->new();
+ set_status_format( $self, $new_window_id, %{$params{Status}});
+ $winref->{Status_Lines} = $winref->{Status_Object}->get();
+ if (DEBUG) { print ERRS "passed set_status_format in create_window\n";
}
+ }
+
+ if ($winref->{Use_Title} && $winref->{Use_Status}) {
+ $winref->{Screen_Height} =
+ $winref->{Screen_End} - $winref->{Screen_Start} + 1;
+ }
+
+ else {
+ $winref->{Screen_Start} = 0 unless defined $winref->{Screen_Start};
+ $winref->{Screen_End} = $LINES - 2 unless defined
$winref->{Screen_End};
+ $winref->{Screen_Height} =
+ $winref->{Screen_End} - $winref->{Screen_Start} + 1
+ unless defined $winref->{Screen_Height};
+ }
+
+ $winref->{Edit_Height} = 1;
+ $winref->{Edit_Start} = $LINES - 1;
+
+ $winref->{Buffer_Last} = $winref->{Buffer_Size} - 1;
+ $winref->{Buffer_First} = $winref->{Screen_Height} - 1;
+ $winref->{Buffer_Visible} = $winref->{Screen_Height} - 1;
+
+
+ $winref->{Window_Edit} = newwin( $winref->{Edit_Height},
+ $COLS,
+ $winref->{Edit_Start},
+ 0 );
+ my $edit = $winref->{Window_Edit};
+ $edit->scrollok(1);
+
+ $winref->{Window_Screen} = newwin( $winref->{Screen_Height},
+ $COLS,
+ $winref->{Screen_Start},
+ 0 );
+ my $screen = $winref->{Window_Screen};
+
+ $screen->bkgd($self->[PALETTE]->{ncolor}->[PAL_PAIR]);
+ $screen->erase();
+ $screen->noutrefresh();
+
+ $winref->{Buffer_Row} = $winref->{Buffer_Last};
+
+ $winref->{Buffer} = [("") x $winref->{Buffer_Size}];
+
+ _refresh_edit($self, $new_window_id);
+
+
+ # Flush updates.
+ doupdate();
+
+ return $new_window_id;
+
+ }
+ else {
+ if (DEBUG) { print ERRS "Window $params{Window_Name} already exists\n";
}
+ carp "Window $params{Window_Name} already exists";
+ }
+ }
+ else {
+ if (DEBUG) { print ERRS "Window $params{Window_Name} couldn't be
created\n"; }
+ croak "Window $params{Window_Name} couldn't be created";
+ }
+}
+
+
+### delete one or more windows ##TODO add error handling
+### $vt->delete_window($window_id);
+
+sub delete_window {
+ if (DEBUG) { print ERRS "Enter delete_window\n"; }
+ my $self = shift;
+ my $win;
+ for (@_) {
+ $win = $_;
+ my $name = get_window_name($self, $_);
+ delete $self->[WINDOW]->{$_};
+ delete $self->[WINDOW_REV]->{$name};
+ }
+ return unless defined $win;
+ my $new_win;
+ my $cur_win = $win;
+ # Select previous window
+ while (--$cur_win > 0) {
+ if (exists $self->[WINDOW]{$cur_win}) {
+ $new_win = $cur_win;
+ last;
+ }
+ }
+ # No previous window, select next window
+ unless (defined $new_win) {
+ $cur_win = $win;
+ while (++$cur_win <= keys %{$self->[WINDOW]}) {
+ if (exists $self->[WINDOW]{$cur_win}) {
+ $new_win = $cur_win;
+ last;
+ }
+ }
+ }
+ if (defined $new_win) {
+ change_window($self, $new_win);
+ }
+ elsif (DEBUG) {
+ print ERRS "We have no more windows!\n"
+ }
+}
+
+### check if a window exists
+
+sub validate_window {
+ if (DEBUG) { print ERRS "Enter validate_window\n"; }
+ my $self = shift;
+ my $query = shift;
+ if (DEBUG) { print ERRS "Validating: $query\n"; }
+ if ($query =~ /^\d+$/ && defined $self->[WINDOW]->{$query}) { return 1; }
+ elsif (defined $self->[WINDOW_REV]->{$query}) { return 1; }
+ else { return 0; }
+}
+
+### return a windows palette or a specific colorname's description
+### my %palette = $vt->get_palette(); # entire palette.
+### my ($color_desc, $another_desc) = $vt->get_palette($colorname,
$somecolor); # color desc.
+
+sub get_palette {
+ my $self = shift;
+ my @result;
+ if ($#_ >= 0) {
+ for (@_) { push( @result, $self->[PALETTE]->{$_}->[PAL_DESC] ); }
+ return @result;
+ }
+ else {
+ for my $key (keys %{$self->[PALETTE]}) {
+ push( @result, $key, $self->[PALETTE]->{$key}->[PAL_DESC]);
+ }
+ return @result;
+ }
+
+}
+
+### set the palette for a window
+
+sub set_palette {
+ if (DEBUG) { print ERRS "Enter set_palette\n"; }
+ my $self = shift;
+ if (DEBUG) { print ERRS "palette needs an even number of parameters\n" if @_
& 1; }
+ croak "palette needs an even number of parameters" if @_ & 1;
+ my %params = @_;
+ _set_color($self, %params);
+}
+
+sub get_window_name {
+ my $self = shift;
+ my $id = shift;
+ if ($id =~ /^\d+$/) {
+ return $self->[WINDOW]->{$id}->{Window_Name};
+ }
+ else {
+ if (DEBUG) { print ERRS "$id is not a Window ID\n"; }
+ croak "$id is not a Window ID";
+ }
+}
+
+sub get_window_id {
+ my $self = shift;
+ my $query = shift;
+ my $validity = validate_window($self, $query);
+ if ($validity) {
+ return $self->[WINDOW_REV]->{$query};
+ }
+ else {
+ if (DEBUG) { print ERRS "$query is not a Window Name\n"; }
+ croak "$query is not a Window Name";
+ }
+}
+
+### set the Title for a Window.
+
+sub set_title {
+ if (DEBUG) { print ERRS "Enter set_title\n"; }
+ my $self = shift;
+ my ($window_id, $title) = @_;
+ my $validity = validate_window($self, $window_id);
+ if ($validity) {
+ $self->[WINDOW]->{$window_id}->{Title} = $title;
+ if ($window_id == $self->[CUR_WIN]) {
+ _refresh_title( $self, $window_id );
+ doupdate();
+ }
+ }
+ else {
+ if (DEBUG) { print ERRS "Window $window_id is nonexistant\n"; }
+ croak "Window $window_id is nonexistant";
+ }
+}
+
+### get the Title for a Window.
+
+sub get_title {
+ my $self = shift;
+ my $window_id = shift;
+ my $validity = validate_window($self, $window_id);
+ if ($validity) {
+ return $self->[WINDOW]->{$window_id}->{Title};
+ }
+ else {
+ if (DEBUG) { print ERRS "Window $window_id is nonexistant\n"; }
+ croak "Window $window_id is nonexistant";
+ }
+}
+
+### print lines to window
+### a window_id must be given as the first argument.
+
+sub print {
+ if (DEBUG) { print ERRS "Enter print\n"; }
+ my $self = shift;
+ my $window_id = shift;
+ if (!validate_window($self, $window_id)) {
+ if (DEBUG) { print ERRS "Can't print to nonexistant Window $window_id\n"; }
+ croak "Can't print to nonexistant Window $window_id";
+ }
+
+ my @lines;
+ foreach my $l (@_) {
+ foreach my $ll (split(/\n/,$l)) {
+ $ll =~ s/\r//g;
+ push(@lines,$ll);
+ }
+ }
+
+ my $winref = $self->[WINDOW]->{$window_id};
+
+ foreach (@lines) {
+
+ # Start a new line in the scrollback buffer.
+
+ push @{$winref->{Buffer}}, "";
+ $winref->{Scrolled_Lines}++;
+ my $column = 1;
+
+ # Build a scrollback line. Stuff surrounded by \0() does not take
+ # up screen space, so account for that while wrapping lines.
+
+ my $last_color = "\0(ncolor)";
+ while (length) {
+
+ # Unprintable color codes.
+ if (s/^(\0\([^\)]+\))//) {
+ $winref->{Buffer}->[-1] .= $last_color = $1;
+ next;
+ }
+
+ # Wordwrap visible stuff.
+ if (s/^([^\0]+)//) {
+ my @words = split /(\s+)/, $1;
+ foreach my $word (@words) {
+ unless (defined $word) {
+ warn "undefined word";
+ next;
+ }
+
+ while ($column + length($word) >= $COLS) {
+ # maybe this word length should be configurable
+ if (length($word) > 20) {
+ # save the word
+ my $preword = $word;
+ # shorten the word to the end of the line
+ $word = substr($word,0,($COLS - $column));
+ # add the word
+ $winref->{Buffer}->[-1] .= "$word\0(ncolor)";
+ $word = '';
+
+ # put the last color on the next line and wrap
+ push @{$winref->{Buffer}}, $last_color;
+ $winref->{Scrolled_Lines}++;
+ # slice the unmodified word
+ $word = substr($preword,($COLS - $column));
+ $column = 1;
+ next;
+ } else {
+ $winref->{Buffer}->[-1] .= "\0(ncolor)";
+ push @{$winref->{Buffer}}, $last_color;
+ }
+ $winref->{Scrolled_Lines}++;
+ $column = 1;
+ next if $word =~ /^\s+$/;
+ }
+ $winref->{Buffer}->[-1] .= $word;
+ $column += length($word);
+ $word = '';
+ }
+ }
+ }
+ }
+
+ # Keep the scrollback buffer a tidy length.
+ splice(@{$winref->{Buffer}}, 0, @{$winref->{Buffer}} -
$winref->{Buffer_Size})
+ if @{$winref->{Buffer}} > $winref->{Buffer_Size};
+
+ # Refresh the buffer when it's all done.
+ if ($self->[CUR_WIN] == $window_id) {
+ _refresh_buffer($self, $window_id);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+}
+
+## Register key bindings
+
+sub bind {
+ my $self = shift;
+ carp "invalid arugments to ->bindings()" if @_ & 1;
+ my %bindings = @_;
+ for (keys %bindings) {
+ my $key = _parse_key($_)
+ or carp "Invalid escape sequence $_";
+ $self->[BINDINGS]{$key} = $bindings{$_};
+ }
+}
+
+## UnRegister key bindings
+
+sub unbind {
+ my $self = shift;
+ for (@_) {
+ my $key = _parse_key($_)
+ or carp "Invalid escape sequence $_";
+ delete $self->[BINDINGS]{$key};
+ }
+}
+
+sub _parse_key {
+ my ($key) = @_;
+ my $esc = '';
+ while ($key =~ s/^(A(?:lt)|C(?:trl)?)-//i) {
+ my $in = uc $1;
+ if (substr($in, 0, 1) eq 'C') {
+ $esc .= '^'
+ }
+ elsif (substr($in, 0, 1) eq 'A') {
+ $esc .= '^[';
+ }
+ else {
+ die "We should not get here: $_";
+ }
+ }
+
+ if (length($key) == 1) {
+ return $esc . $key;
+ }
+ else {
+ return $esc . "KEY_" . uc($key);
+ }
+}
+
+### Register an input handler thing.
+
+sub register_input {
+ if (DEBUG) { print ERRS "Enter register_input\n"; }
+ my ($kernel, $heap, $sender, $event) = @_[KERNEL, HEAP, SENDER, ARG0];
+
+ # Remember the remote session and the event it wants to receive with
+ # input. This saves the sender's ID (instead of a reference)
+ # because references mess with Perl's garbage collection.
+
+ $heap->{input_session} = $sender->ID();
+ $heap->{input_event} = $event;
+}
+
+### Get input from the Curses thing.
+
+sub got_curses_input {
+ if (DEBUG) { print ERRS "Enter got_curses_input\n"; }
+ my ($self, $kernel, $heap, $key) = @_[OBJECT, KERNEL, HEAP, ARG0];
+
+ my $window_id = $self->[CUR_WIN];
+ my $winref = $self->[WINDOW]->{$window_id};
+ $key = uc(keyname($key)) if $key =~ /^\d{2,}$/;
+ $key = uc(unctrl($key)) if $key lt " " or $key gt "~";
+
+ # If it's a meta key, save it.
+ if ($key eq '^[') {
+ $winref->{Input}{Prefix} .= $key;
+ return;
+ }
+
+ # If there was a saved prefix, recall it.
+ if ($winref->{Input}{Prefix}) {
+ $key = $winref->{Input}{Prefix} . $key;
+ $winref->{Input}{Prefix} = '';
+ }
+
+ ### Handle internal keystrokes here. Page up, down, arrow keys, etc.
+
+ # key bindings
+ if (exists $self->[BINDINGS]{$key} and $heap->{input_session}) {
+ $kernel->post( $heap->{input_session}, $self->[BINDINGS]{$key},
+ $key, $winref, $window_id
+ );
+ return;
+ }
+
+ # Beginning of line.
+ if ($key eq '^A' or $key eq 'KEY_HOME') {
+ if ($winref->{Input}{Cursor}) {
+ $winref->{Input}{Cursor} = 0;
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Back one character.
+ if ($key eq 'KEY_LEFT') {
+ if ($winref->{Input}{Cursor}) {
+ $winref->{Input}{Cursor}--;
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+ if (DEBUG) { print ERRS $key, "\n"; }
+ # Switch Windows to the left Shifted left arrow
+ #FIXME come up with a better fix. KEY_LEFT didnt work for me.
+ if ($key eq '�' or $key eq '^[KEY_LEFT') {
+ $window_id--;
+ change_window($self, $window_id );
+ return;
+ }
+
+ # Switch Windows to the right Shifted right arrow
+ #FIXME come up with a better fix. KEY_RIGHT didnt work for me.
+ if ($key eq '�' or $key eq '^[KEY_RIGHT') {
+ $window_id++;
+ change_window($self, $window_id );
+ return;
+ }
+
+ # Interrupt.
+ if ($key eq '^\\') {
+ &shutdown;
+ return;
+ }
+
+ # Delete a character.
+ if ($key eq '^D' or $key eq 'KEY_DC') {
+ if ($winref->{Input}{Cursor} < length($winref->{Input}{Data})) {
+ substr($winref->{Input}{Data}, $winref->{Input}{Cursor}, 1) = '';
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # End of line.
+ if ($key eq '^E' or $key eq 'KEY_LL') {
+ if ($winref->{Input}{Cursor} < length($winref->{Input}{Data})) {
+ $winref->{Input}{Cursor} = length($winref->{Input}{Data});
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Forward character.
+ if ($key eq '^F' or $key eq 'KEY_RIGHT') {
+ if ($winref->{Input}{Cursor} < length($winref->{Input}{Data})) {
+ $winref->{Input}{Cursor}++;
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Backward delete character.
+ if ($key eq '^H' or $key eq "^?" or $key eq 'KEY_BACKSPACE') {
+ if ($winref->{Input}{Cursor}) {
+ $winref->{Input}{Cursor}--;
+ substr($winref->{Input}{Data}, $winref->{Input}{Cursor}, 1) = '';
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Accept line.
+ if ($key eq '^J' or $key eq '^M') {
+ $kernel->post( $heap->{input_session}, $heap->{input_event},
+ $winref->{Input}{Data}, undef
+ );
+
+ # And enter the line into the command history.
+ command_history( $self, $window_id, 0 );
+ return;
+ }
+
+ # Kill to EOL.
+ if ($key eq '^K') {
+ if ($winref->{Input}{Cursor} < length($winref->{Input}{Data})) {
+ substr($winref->{Input}{Data}, $winref->{Input}{Cursor}) = '';
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Refresh screen.
+ if ($key eq '^L' or $key eq 'KEY_RESIZE') {
+
+ # Refresh the title line.
+ _refresh_title($self, $window_id);
+
+ # Refresh the status lines.
+ _refresh_status( $self, $window_id);
+
+ # Refresh the buffer.
+ _refresh_buffer($self, $window_id);
+
+ # Refresh the edit line.
+ _refresh_edit($self, $window_id);
+
+ # Flush updates.
+ doupdate();
+
+ return;
+ }
+
+ # Next in history.
+ if ($key eq '^N' ) {
+ command_history( $self, $window_id, 2 );
+ return;
+ }
+
+ # Previous in history.
+ if ($key eq '^P' ) {
+ command_history( $self, $window_id, 1 );
+ return;
+ }
+
+ # Display input status.
+ if ($key eq '^Q') {
+ &print( $self, $window_id, # <- can I do this better?
+ "\0(statcolor)******",
+ "\0(statcolor)*** cursor is at $winref->{Input}{Cursor}",
+ "\0(statcolor)*** input is: ``$winref->{Input}{Data}''",
+ "\0(statcolor)*** scrolled lines: $winref->{Scrolled_Lines}",
+ "\0(statcolor)*** screen height: " . $winref->{Screen_Height},
+ "\0(statcolor)*** buffer row: $winref->{Buffer_Row}",
+ "\0(statcolor)*** scrollback height: " .
scalar(@{$winref->{Buffer}}),
+ "\0(statcolor)******"
+ );
+ return;
+ }
+
+ # Transpose characters.
+ if ($key eq '^T') {
+ if ($winref->{Input}{Cursor} > 0 and $winref->{Input}{Cursor} <
length($winref->{Input}{Data})) {
+ substr($winref->{Input}{Data}, $winref->{Input}{Cursor}-1, 2) =
+ reverse substr($winref->{Input}{Data}, $winref->{Input}{Cursor}-1, 2);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Discard line.
+ if ($key eq '^U') {
+ if (length($winref->{Input}{Data})) {
+ $winref->{Input}{Data} = '';
+ $winref->{Input}{Cursor} = 0;
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Word rubout.
+ if ($key eq '^W' or $key eq '^[^H') {
+ if ($winref->{Input}{Cursor}) {
+ substr($winref->{Input}{Data}, 0, $winref->{Input}{Cursor}) =~
s/(\S*\s*)$//;
+ $winref->{Input}{Cursor} -= length($1);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # First in history.
+ if ($key eq '^[<') {
+ # TODO
+ return;
+ }
+
+ # Last in history.
+ if ($key eq '^[>') {
+ # TODO
+ return;
+ }
+
+ # Capitalize from cursor on. Requires uc($key)
+ if (uc($key) eq '^[C') {
+
+ # If there's text to capitalize.
+ if (substr($winref->{Input}{Data}, $winref->{Input}{Cursor}) =~
/^(\s*)(\S+)/) {
+
+ # Track leading space, and uppercase word.
+ my $space = $1; $space = '' unless defined $space;
+ my $word = ucfirst(lc($2));
+
+ # Replace text with the uppercase version.
+ substr( $winref->{Input}{Data},
+ $winref->{Input}{Cursor} + length($space), length($word)
+ ) = $word;
+
+ $winref->{Input}{Cursor} += length($space . $word);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Uppercase from cursor on. Requires uc($key)
+ if (uc($key) eq '^[U') {
+
+ # If there's text to uppercase.
+ if (substr($winref->{Input}{Data}, $winref->{Input}{Cursor}) =~
/^(\s*)(\S+)/) {
+
+ # Track leading space, and uppercase word.
+ my $space = $1; $space = '' unless defined $space;
+ my $word = uc($2);
+
+ # Replace text with the uppercase version.
+ substr( $winref->{Input}{Data},
+ $winref->{Input}{Cursor} + length($space), length($word)
+ ) = $word;
+
+ $winref->{Input}{Cursor} += length($space . $word);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Lowercase from cursor on. Requires uc($key)
+ if (uc($key) eq '^[L') {
+
+ # If there's text to uppercase.
+ if (substr($winref->{Input}{Data}, $winref->{Input}{Cursor}) =~
/^(\s*)(\S+)/) {
+
+ # Track leading space, and uppercase word.
+ my $space = $1; $space = '' unless defined $space;
+ my $word = lc($2);
+
+ # Replace text with the uppercase version.
+ substr( $winref->{Input}{Data},
+ $winref->{Input}{Cursor} + length($space), length($word)
+ ) = $word;
+
+ $winref->{Input}{Cursor} += length($space . $word);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Forward one word. Requires uc($key)
+ if (uc($key) eq '^[F') {
+ if (substr($winref->{Input}{Data}, $winref->{Input}{Cursor}) =~
/^(\s*\S+)/) {
+ $winref->{Input}{Cursor} += length($1);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Backward one word. This needs uc($key).
+ if (uc($key) eq '^[B') {
+ if (substr($winref->{Input}{Data}, 0, $winref->{Input}{Cursor}) =~
/(\S+\s*)$/) {
+ $winref->{Input}{Cursor} -= length($1);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Delete a word forward. This needs uc($key).
+ if (uc($key) eq '^[D') {
+ if ($winref->{Input}{Cursor} < length($winref->{Input}{Data})) {
+ substr($winref->{Input}{Data}, $winref->{Input}{Cursor}) =~
s/^(\s*\S*\s*)//;
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Transpose words. This needs uc($key).
+ if (uc($key) eq '^[T') {
+ my ($previous, $left, $space, $right, $rest);
+
+ if (substr($winref->{Input}{Data}, $winref->{Input}{Cursor}, 1) =~ /\s/) {
+ my ($left_space, $right_space);
+ ($previous, $left, $left_space) =
+ ( substr($winref->{Input}{Data}, 0, $winref->{Input}{Cursor}) =~
/^(.*?)(\S+)(\s*)$/
+ );
+ ($right_space, $right, $rest) =
+ ( substr($winref->{Input}{Data}, $winref->{Input}{Cursor}) =~
/^(\s+)(\S+)(.*)$/
+ );
+ $space = $left_space . $right_space;
+ }
+ elsif ( substr($winref->{Input}{Data}, 0, $winref->{Input}{Cursor}) =~
+ /^(.*?)(\S+)(\s+)(\S*)$/
+ ) {
+ ($previous, $left, $space, $right) = ($1, $2, $3, $4);
+ if (substr($winref->{Input}{Data}, $winref->{Input}{Cursor}) =~
/^(\S*)(.*)$/) {
+ $right .= $1 if defined $1;
+ $rest = $2;
+ }
+ }
+ elsif (substr($winref->{Input}{Data}, $winref->{Input}{Cursor}) =~
/^(\S+)(\s+)(\S+)(.*)$/
+ ) {
+ ($left, $space, $right, $rest) = ($1, $2, $3, $4);
+ if ( substr($winref->{Input}{Data}, 0, $winref->{Input}{Cursor}) =~
/^(.*?)(\S+)$/ ) {
+ $previous = $1;
+ $left = $2 . $left;
+ }
+ }
+ else {
+ return;
+ }
+
+ $previous = '' unless defined $previous;
+ $rest = '' unless defined $rest;
+
+ $winref->{Input}{Data} = $previous . $right . $space . $left . $rest;
+ $winref->{Input}{Cursor} = length($previous. $left . $space . $right);
+
+ _refresh_edit($self, $window_id);
+ doupdate();
+ return;
+ }
+
+ # Toggle insert mode.
+ if ($key eq 'KEY_IC') {
+ $winref->{Input}{Insert} = !$winref->{Input}{Insert};
+ return;
+ }
+ # If the window is scrolled up go back to the beginning.
+ if ($key eq 'KEY_SELECT') {
+ $winref->{Buffer_Row} = $winref->{Buffer_Last};
+ _refresh_buffer($self, $window_id);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ return;
+ }
+
+ # Scroll back a page.
+ if ($key eq 'KEY_PPAGE') {
+ if ($winref->{Buffer_Row} > $winref->{Buffer_First}) {
+ $winref->{Buffer_Row} -= $winref->{Screen_Height};
+ if ($winref->{Buffer_Row} < $winref->{Buffer_First}) {
+ $winref->{Buffer_Row} = $winref->{Buffer_First}
+ }
+ _refresh_buffer($self, $window_id);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Scroll forward a page.
+ if ($key eq 'KEY_NPAGE') {
+ if ($winref->{Buffer_Row} < $winref->{Buffer_Last}) {
+ $winref->{Buffer_Row} += $winref->{Screen_Height};
+ if ($winref->{Buffer_Row} > $winref->{Buffer_Last}) {
+ $winref->{Buffer_Row} = $winref->{Buffer_Last};
+ }
+ _refresh_buffer($self, $window_id);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Scroll back a line.
+ if ($key eq 'KEY_UP') {
+ if ($winref->{Buffer_Row} > $winref->{Buffer_First}) {
+ $winref->{Buffer_Row}--;
+ _refresh_buffer($self, $window_id);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ # Scroll forward a line.
+ if ($key eq 'KEY_DOWN') {
+ if ($winref->{Buffer_Row} < $winref->{Buffer_Last}) {
+ $winref->{Buffer_Row}++;
+ _refresh_buffer($self, $window_id);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ return;
+ }
+
+ if ($key eq "^I") {
+ if ($winref->{Input}{Tab_Complete}) {
+ my $left = substr($winref->{Input}{Data}, 0, $winref->{Input}{Cursor});
+ my $right = substr($winref->{Input}{Data}, $winref->{Input}{Cursor});
+ my @str = $winref->{Input}{Tab_Complete}->($left, $right);
+ my $complete_word = $1 if $left =~ /(\S+)\s*\z/;
+ $left =~ s/\Q$complete_word\E\s*\z// if $complete_word;
+ if (@str == 1) {
+ my $data = $left . $str[0];
+ $winref->{Input}{Data} = $data . $right;
+ $winref->{Input}{Cursor} = length $data;
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ elsif (@str) {
+ # complete to something they all have in common
+ my $shortest = '';
+ for (@str) {
+ if (!length($shortest) or length($_) < length $shortest) {
+ $shortest = $_;
+ }
+ }
+ my $i = length $shortest;
+ for (@str) {
+ while (substr($shortest, 0, $i) ne substr($_, 0, $i) and $i) {
+ $i--;
+ }
+ last unless $i;
+ }
+ if ($i) {
+ $winref->{Input}{Data} = $left . substr($shortest, 0, $i) . $right;
+ $winref->{Input}{Cursor} = length($left) + $i;
+ }
+ my $table = columnize(
+ Items => [EMAIL PROTECTED],
+ MaxWidth => $COLS
+ );
+ for (split /\n/, $table) {
+ &print($self, $window_id, $_);
+ }
+ }
+ }
+ return;
+ }
+
+ ### Not an internal keystroke. Add it to the input buffer.
+ #FIXME double check if this is needed...
+ $key = chr(ord($1)-64) if $key =~ /^\^([EMAIL PROTECTED])$/;
+
+ # Inserting or overwriting in the middle of the input.
+ if ($winref->{Input}{Cursor} < length($winref->{Input}{Data})) {
+ if ($winref->{Input}{Insert}) {
+ substr($winref->{Input}{Data}, $winref->{Input}{Cursor}, 0) = $key;
+ }
+ else {
+ substr($winref->{Input}{Data}, $winref->{Input}{Cursor}, length($key)) =
$key;
+ }
+ }
+
+ # Appending.
+ else {
+ $winref->{Input}{Data} .= $key;
+ }
+
+ $winref->{Input}{Cursor} += length($key);
+ _refresh_edit($self, $window_id);
+ doupdate();
+ return;
+}
+
+sub columnize {
+ croak "Arguments to columnize must be a hash" if @_ & 1;
+ my %opts = @_;
+
+ my $width = delete $opts{MaxWidth};
+ $width = 80 unless defined $width;
+ croak "Invalid width $width" if $width <= 0;
+
+ my $padding = delete $opts{Padding};
+ $padding = 2 unless defined $padding;
+ croak "Invalid padding $padding" if $padding < 0;
+
+ my $max_columns = delete $opts{MaxColumns};
+ $max_columns = 10 unless defined $max_columns;
+ croak "Invalid max columns $max_columns" if $max_columns <= 0;
+
+ my $items = delete $opts{Items};
+ croak "Items must be an array reference"
+ unless ref($items) eq 'ARRAY';
+
+ croak "Unknown arguments: '", join("', '", sort keys %opts), "'"
+ if keys %opts;
+
+ for my $i (reverse 2 .. $max_columns) {
+ my $n = 0;
+ my @cols;
+ my $num_rows = 0;
+ for (0 .. $#{$items}) {
+ push @{$cols[$n++]}, $items->[$_];
+ unless (($_ + 1) % $i) {
+ $n = 0;
+ $num_rows++;
+ }
+ }
+ my @long;
+ for $n (0 .. $#cols) {
+ for my $item (@{$cols[$n]}) {
+ if (!$long[$n] or length($item) > $long[$n]) {
+ $long[$n] = length $item;
+ }
+ }
+ }
+ my $total = 0;
+ for (@long) {
+ $total += $_ + $padding;
+ }
+ next if $total > $width;
+ my $table = '';
+ for (0 .. $num_rows) {
+ my $row;
+ for $n (0 .. $#cols) {
+ my $item = $cols[$n][$_];
+ last unless defined $item;
+ $row .= $item . (' ' x ($long[$n] - length($item) + $padding));
+ }
+ $table .= $row . "\n";
+ }
+ return $table;
+ last;
+ }
+ return join("\n", @$items) . "\n";
+}
+##FIXME Has this been replaced with _parse_key() ??
+my %ctrl_to_visible;
+BEGIN {
+ for (0..31) {
+ $ctrl_to_visible{chr($_)} = chr($_+64);
+ }
+}
+
+### Common thing. Refresh the buffer on the screen.
+## Pass in $self and a window_id
+
+sub _refresh_buffer {
+ if (DEBUG) { print ERRS "Enter _refresh_buffer\n"; }
+ my $self = shift;
+ my $window_id = shift;
+ my $winref = $self->[WINDOW]->{$window_id};
+ my $screen = $winref->{Window_Screen};
+
+ if ($window_id != $self->[CUR_WIN]) { return; }
+ # Adjust the buffer row to compensate for any scrolling we encounter
+ # while in scrollback.
+
+ if ($winref->{Buffer_Row} < $winref->{Buffer_Last}) {
+ $winref->{Buffer_Row} -= $winref->{Scrolled_Lines};
+ }
+
+ # Don't scroll up past the start of the buffer.
+
+ if ($winref->{Buffer_Row} < $winref->{Buffer_First}) {
+ $winref->{Buffer_Row} = $winref->{Buffer_First};
+ }
+
+ # Don't scroll down past the bottom of the buffer.
+
+ if ($winref->{Buffer_Row} > $winref->{Buffer_Last}) {
+ $winref->{Buffer_Row} = $winref->{Buffer_Last};
+ }
+
+ # Now splat the last N lines onto the screen.
+
+ $screen->erase();
+ $screen->noutrefresh();
+
+ $winref->{Scrolled_Lines} = 0;
+
+ my $screen_y = 0;
+ my $buffer_y = $winref->{Buffer_Row} - $winref->{Buffer_Visible};
+ while ($screen_y < $winref->{Screen_Height}) {
+ $screen->move($screen_y, 0);
+ $screen->clrtoeol();
+ $screen->noutrefresh();
+
+ next if $buffer_y < 0;
+ next if $buffer_y > $winref->{Buffer_Last};
+
+ my $line = $winref->{Buffer}->[$buffer_y]; # does this work?
+ my $column = 1;
+ while (length $line) {
+ if ($line =~ s/^\0\(blink_(on|off)\)//) {
+ if ($1 eq 'on') { $screen->attron(A_BLINK); }
+ if ($1 eq 'off') { $screen->attroff(A_BLINK); }
+ $screen->noutrefresh();
+ }
+
+ if ($line =~ s/^\0\(bold_(on|off)\)//) {
+ if ($1 eq 'on') { $screen->attron(A_BOLD); }
+ if ($1 eq 'off') { $screen->attroff(A_BOLD); }
+ $screen->noutrefresh();
+ }
+
+ if ($line =~ s/^\0\(underline_(on|off)\)//) {
+ if ($1 eq 'on') { $screen->attron(A_UNDERLINE); }
+ if ($1 eq 'off') { $screen->attroff(A_UNDERLINE); }
+ $screen->noutrefresh();
+ }
+
+ if ($line =~ s/^ \0 \( ([^\)]+) \) //x) {
+ $screen->attrset($self->[PALETTE]->{$1}->[PAL_PAIR]);
+ $screen->noutrefresh();
+ }
+ if ($line =~ s/^([^\0]+)//x) {
+
+ # TODO: This needs to be revised so it cuts off the last word,
+ # not omits it entirely.
+ # Has this been fixed already??
+ next if $column >= $COLS;
+ if ($column + length($1) > $COLS) {
+ my $word = $1;
+ substr($word, ($column + length($1)) - $COLS - 1) = '';
+ $screen->addstr($word);
+ }
+ else {
+ $screen->addstr($1);
+ }
+ $column += length($1);
+ $screen->noutrefresh();
+ }
+ }
+
+ $screen->attrset($self->[PALETTE]->{ncolor}->[PAL_PAIR]);
+ $screen->noutrefresh();
+ $screen->clrtoeol();
+ $screen->noutrefresh();
+ }
+ continue {
+ $screen_y++;
+ $buffer_y++;
+ }
+}
+
+# Internal function to set the color palette for a window.
+
+sub _set_color {
+ if (DEBUG) { print ERRS "Enter _set_color\n"; }
+ my $self= shift;
+# my $window_id = shift;
+# my $winref = $self->[WINDOW]->{$window_id};
+ my %params = @_;
+
+ my %color_table =
+ ( bk => COLOR_BLACK, black => COLOR_BLACK,
+ bl => COLOR_BLUE, blue => COLOR_BLUE,
+ br => COLOR_YELLOW, brown => COLOR_YELLOW,
+ fu => COLOR_MAGENTA, fuschia => COLOR_MAGENTA,
+ cy => COLOR_CYAN, cyan => COLOR_CYAN,
+ gr => COLOR_GREEN, green => COLOR_GREEN,
+ ma => COLOR_MAGENTA, magenta => COLOR_MAGENTA,
+ re => COLOR_RED, red => COLOR_RED,
+ wh => COLOR_WHITE, white => COLOR_WHITE,
+ ye => COLOR_YELLOW, yellow => COLOR_YELLOW,
+ de => -1, default => -1,
+ );
+
+ my %attribute_table =
+ ( al => A_ALTCHARSET,
+ alt => A_ALTCHARSET,
+ alternate => A_ALTCHARSET,
+ blink => A_BLINK,
+ blinking => A_BLINK,
+ bo => A_BOLD,
+ bold => A_BOLD,
+ bright => A_BOLD,
+ dim => A_DIM,
+ fl => A_BLINK,
+ flash => A_BLINK,
+ flashing => A_BLINK,
+ hi => A_BOLD,
+ in => A_INVIS,
+ inverse => A_REVERSE,
+ inverted => A_REVERSE,
+ invisible => A_INVIS,
+ inviso => A_INVIS,
+ lo => A_DIM,
+ low => A_DIM,
+ no => A_NORMAL,
+ norm => A_NORMAL,
+ normal => A_NORMAL,
+ pr => A_PROTECT,
+ prot => A_PROTECT,
+ protected => A_PROTECT,
+ reverse => A_REVERSE,
+ rv => A_REVERSE,
+ st => A_STANDOUT,
+ stand => A_STANDOUT,
+ standout => A_STANDOUT,
+ un => A_UNDERLINE,
+ under => A_UNDERLINE,
+ underline => A_UNDERLINE,
+ underlined => A_UNDERLINE,
+ underscore => A_UNDERLINE,
+ );
+
+
+ for my $color_name (keys %params) {
+
+ my $description = $params{$color_name};
+ my $foreground = 0;
+ my $background = 0;
+ my $attributes = 0;
+
+ # Which is an alias to foreground or background depending on what
+ # state we're in.
+ my $which = \$foreground;
+
+ # Clean up the color description.
+ $description =~ s/^\s+//;
+ $description =~ s/\s+$//;
+ $description = lc($description);
+
+ # Parse the description.
+ foreach my $word (split /\s+/, $description) {
+
+ # The word "on" means we're switching to background.
+ if ($word eq 'on') {
+ $which = \$background;
+ next;
+ }
+
+ # If it's a color name, combine its value with the foreground or
+ # background, whichever is currently selected.
+ if (exists $color_table{$word}) {
+ $$which |= $color_table{$word};
+ next;
+ }
+
+ # If it's an attribute, it goes with attributes.
+ if (exists $attribute_table{$word}) {
+ $attributes |= $attribute_table{$word};
+ next;
+ }
+
+ # Otherwise it's an error.
+ if (DEBUG) { print ERRS "unknown color keyword \"$word\"\n"; }
+ croak "unknown color keyword \"$word\"";
+ }
+
+ # If the palette already has that color, redefine it.
+ if (exists $self->[PALETTE]->{$color_name}) {
+ my $old_color_number = $self->[PALETTE]->{$color_name}->[PAL_NUMBER];
+ init_pair($old_color_number, $foreground, $background);
+ $self->[PALETTE]->{$color_name}->[PAL_PAIR] =
+ COLOR_PAIR($old_color_number) | $attributes;
+ }
+ else {
+ my $new_color_number = ++$self->[PAL_COL_SEQ];
+ init_pair($new_color_number, $foreground, $background);
+ $self->[PALETTE]->{$color_name} =
+ [ COLOR_PAIR($new_color_number) | $attributes, # PAL_PAIR
+ $new_color_number, # PAL_NUMBER
+ $description, # PAL_DESC
+ ];
+ }
+ }
+}
+
+### The terminal stopped. Remove the reference count for the remote
+### session.
+
+sub terminal_stopped {
+ if (DEBUG) { print ERRS "Enter terminal_stopped\n"; }
+ my ($kernel, $heap) = @_[KERNEL, HEAP];
+
+ if (defined $heap->{input_session}) {
+ delete $heap->{input_session};
+ }
+}
+
+sub change_window {
+ if (DEBUG) { print ERRS "change_window called\n"; }
+ my $self = shift;
+ my $window_id = shift;
+ my @list = sort {$a <=> $b} keys %{$self->[WINDOW]};
+
+ if (@list) {
+ if ($window_id == -1) {
+ $window_id = $list[$#list];
+ }
+ elsif ($window_id > $list[$#list]) {
+ $window_id = 0;
+ }
+ }
+
+ my $validity = validate_window($self, $window_id);
+ if ($validity) {
+ $self->[CUR_WIN] = $window_id;
+ update_window( $self, $window_id );
+ }
+}
+
+sub update_window {
+ my $self = shift;
+ my $window_id = shift;
+
+ _refresh_title( $self, $window_id );
+ _refresh_buffer( $self, $window_id );
+ _refresh_status( $self, $window_id );
+ _refresh_edit( $self, $window_id );
+ doupdate();
+}
+
+sub _refresh_title {
+ if (DEBUG) { print ERRS "Enter _refresh_title\n"; }
+ my ($self, $window_id) = @_;
+ my $winref = $self->[WINDOW]->{$window_id};
+ my $title = $winref->{Window_Title};
+
+ if ($window_id != $self->[CUR_WIN]) { return; }
+
+ $title->move(TITLE_LINE, TITLE_COL);
+ $title->attrset($self->[PALETTE]->{st_values}->[PAL_PAIR]);
+ $title->noutrefresh();
+ $title->addstr($winref->{Title}) unless !$winref->{Title};
+ $title->noutrefresh();
+ $title->clrtoeol();
+ $title->noutrefresh();
+ doupdate();
+}
+
+sub _refresh_edit {
+ if (DEBUG) { print ERRS "Enter _refresh_edit\n"; }
+ my $self = shift;
+ my $window_id = shift;
+ my $winref = $self->[WINDOW]->{$window_id};
+ my $edit = $winref->{Window_Edit};
+ my $visible_input = $winref->{Input}{Data};
+
+ # If the cursor is after the last visible edit position, scroll the
+ # edit window left so the cursor is back on-screen.
+
+ if ($winref->{Input}{Cursor} - $winref->{Input}{Edit_Position} >= $COLS) {
+ $winref->{Input}{Edit_Position} = $winref->{Input}{Cursor} - $COLS + 1;
+ }
+
+ # If the cursor is moving left of the middle of the screen, scroll
+ # things to the right so that both sides of the cursor may be seen.
+
+ elsif ($winref->{Input}{Cursor} - $winref->{Input}{Edit_Position} < ($COLS
>> 1)) {
+ $winref->{Input}{Edit_Position} = $winref->{Input}{Cursor} - ($COLS >> 1);
+ $winref->{Input}{Edit_Position} = 0 if $winref->{Input}{Edit_Position} < 0;
+ }
+
+ # If the cursor is moving right of the middle of the screen, scroll
+ # things to the left so that both sides of the cursor may be seen.
+
+ elsif ( $winref->{Input}{Cursor} <= length($winref->{Input}{Data}) - ($COLS
>> 1) + 1 ){
+ $winref->{Input}{Edit_Position} = $winref->{Input}{Cursor} - ($COLS >> 1);
+ }
+
+ # Condition $visible_input so it really is.
+ $visible_input = substr($visible_input, $winref->{Input}{Edit_Position},
$COLS-1);
+
+ $edit->attron(A_NORMAL);
+ $edit->erase();
+ $edit->noutrefresh();
+
+ while (length($visible_input)) {
+ if ($visible_input =~ /^[\x00-\x1f]/) {
+ $edit->attron(A_UNDERLINE);
+ while ($visible_input =~ s/^([\x00-\x1f])//) {
+ $edit->addstr($ctrl_to_visible{$1});
+ }
+ }
+ if ($visible_input =~ s/^([^\x00-\x1f]+)//) {
+ $edit->attroff(A_UNDERLINE);
+ $edit->addstr($1);
+ }
+ }
+
+ $edit->noutrefresh();
+ $edit->move( 0, $winref->{Input}{Cursor} - $winref->{Input}{Edit_Position} );
+ $edit->noutrefresh();
+}
+
+### Set or call command history lines.
+
+sub command_history {
+ if (DEBUG) { print ERRS "Enter command_history\n"; }
+ my $self = shift;
+ my $window_id = shift;
+ my $flag = shift;
+ my $winref = $self->[WINDOW]->{$window_id};
+
+ if ($flag == 0) { #add to command history
+
+ # Add to the command history. Discard the oldest item if the
+ # history size is bigger than our maximum length.
+
+ unshift(@{$winref->{Input}{Command_History}}, $winref->{Input}{Data});
+ pop(@{$winref->{Input}{Command_History}}) if
@{$winref->{Input}{Command_History}} > $winref->{Input}{History_Size};
+
+ # Reset the input, saved input, and history position. Repaint the
+ # edit box.
+
+ $winref->{Input}{Data_Save} = $winref->{Input}{Data} = "";
+ $winref->{Input}{Cursor_Save} = $winref->{Input}{Cursor} = 0;
+ $winref->{Input}{History_Position} = -1;
+
+ _refresh_edit($self, $window_id);
+ doupdate();
+
+ return;
+ }
+
+ if ($flag == 1) { # get last history 'KEY_UP'
+
+ # At <0 command history, we save the input and move into the
+ # command history. The saved input will be used in case we come
+ # back.
+
+ if ($winref->{Input}{History_Position} < 0) {
+ if (@{$winref->{Input}{Command_History}}) {
+ $winref->{Input}{Data_Save} = $winref->{Input}{Data};
+ $winref->{Input}{Cursor_Save} = $winref->{Input}{Cursor};
+ $winref->{Input}{Data} =
+
$winref->{Input}{Command_History}->[++$winref->{Input}{History_Position}];
+ $winref->{Input}{Cursor} = length($winref->{Input}{Data});
+
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+ }
+
+ # If we're not at the end of the command history, then we go
+ # farther back.
+
+ elsif ($winref->{Input}{History_Position} <
@{$winref->{Input}{Command_History}} - 1) {
+ $winref->{Input}{Data} =
$winref->{Input}{Command_History}->[++$winref->{Input}{History_Position}];
+ $winref->{Input}{Cursor} = length($winref->{Input}{Data});
+
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+
+ return;
+ }
+
+ if ($flag == 2) { # get next history 'KEY_DOWN'
+
+ # At 0th command history. Switch to saved input.
+ unless ($winref->{Input}{History_Position}) {
+ $winref->{Input}{Data} = $winref->{Input}{Data_Save};
+ $winref->{Input}{Cursor} = $winref->{Input}{Cursor_Save};
+ $winref->{Input}{History_Position}--;
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+
+ # At >0 command history. Move towards 0.
+ elsif ($winref->{Input}{History_Position} > 0) {
+ $winref->{Input}{Data} =
$winref->{Input}{Command_History}->[--$winref->{Input}{History_Position}];
+ $winref->{Input}{Cursor} = length($winref->{Input}{Data});
+ _refresh_edit($self, $window_id);
+ doupdate();
+ }
+
+ return;
+ }
+
+ warn "unknown flag $flag";
+}
+
+sub set_status_field {
+ if (DEBUG) { print ERRS "Enter set_status_field\n"; }
+ my $self = shift;
+ my $window_id = shift;
+ my $validity = validate_window($self, $window_id);
+ if ($validity) {
+ my $winref = $self->[WINDOW]->{$window_id};
+ my $status_obj = $winref->{Status_Object};
+ $winref->{Status_Lines} = $status_obj->set(@_);
+ _refresh_status($self, $window_id);
+ _refresh_edit($self, $window_id);
+ doupdate();
+
+ }
+}
+
+sub set_status_format {
+ if (DEBUG) { print ERRS "Enter set_status_format\n"; }
+ my $self = shift;
+ my $window_id = shift;
+ my %status_formats = @_;
+ if (DEBUG) { print ERRS %status_formats, " <-status_formats\n"; }
+ my $validity = validate_window($self, $window_id);
+ if ($validity) {
+ my $winref = $self->[WINDOW]->{$window_id};
+ my $status_obj = $winref->{Status_Object};
+ if (DEBUG) { print ERRS "calling status_obj->set_format\n"; }
+ $status_obj->set_format(%status_formats);
+ if (DEBUG) { print ERRS "calling status_obj->get\n"; }
+ $winref->{Status_Lines} = $status_obj->get();
+if (DEBUG) { print ERRS "calling refresh_status\n"; }
+ # Update the status line.
+ _refresh_status( $self, $window_id );
+if (DEBUG) { print ERRS "returned from refresh_status\n"; }
+ doupdate();
+ }
+}
+
+sub _refresh_status {
+ if (DEBUG) { print ERRS "Enter _refresh_status\n"; }
+ my ($self, $window_id) = (shift, shift);
+
+ if ($window_id != $self->[CUR_WIN]) { return; }
+
+ my ($row, $value);
+ my $winref = $self->[WINDOW]->{$window_id};
+ my $status = $winref->{Window_Status};
+ my @status_lines = @{$winref->{Status_Lines}};
+ while (@status_lines) {
+ if (DEBUG) { print ERRS "in main while loop of refresh_status\n"; }
+ $row = shift @status_lines;
+ $value = shift @status_lines;
+if (DEBUG) { print ERRS "$row <-row value-> $value\n"; }
+if (DEBUG) { print ERRS $status, "<-status ref\n"; }
+ $status->move( $row, 0 );
+
+ # Parse the value. Stuff surrounded by ^C is considered color
+ # names. This interferes with epic/mirc colors.
+
+ while (defined $value and length $value) {
+ if (DEBUG) { print ERRS "while defined value and length value in
refresh_status\n"; }
+ if ($value =~ s/^\0\(([^\)]+)\)//) {
+ if (DEBUG) { print ERRS "value matched", '^\0\(([^\)]+)\)', "\n"; }
+ $status->attrset($self->[PALETTE]->{$1}->[PAL_PAIR]);
+ $status->noutrefresh();
+ }
+ if ($value =~ s/^([^\0]+)//) {
+ if (DEBUG) { print ERRS "value matched", '^([^\0]+)', "\n"; }
+ $status->addstr($1);
+ $status->noutrefresh();
+ }
+ }
+ }
+
+ # Clear to the end of the line, and refresh the status bar.
+ $status->attrset($self->[PALETTE]->{st_frames}->[PAL_PAIR]);
+ $status->noutrefresh();
+ $status->clrtoeol();
+ $status->noutrefresh();
+
+}
+
+
+
+sub set_errlevel {}
+sub get_errlevel {}
+
+sub debug {
+ my $self = shift;
+ if (DEBUG) { for (@_) { print ERRS "$_\n";} }
+ else { carp "turn on debugging in Term::Visual or define sub
Term::Visual::DEBUG () { 1 }; before use Term::Visual; in your program"; }
+
+}
+
+sub shutdown {
+ $_[KERNEL]->alias_remove($_[OBJECT][ALIAS]);
+ delete $_[HEAP]->{stderr_reader};
+ undef $console;
+ if (defined $_[HEAP]->{input_session}) {
+ $_[KERNEL]->post( $_[HEAP]->{input_session}, $_[HEAP]->{input_event},
+ undef, 'interrupt' );
+ }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Term::Visual - split-terminal user interface
+
+=head1 SYNOPSIS
+
+ #!/usr/bin/perl -w
+ use strict;
+
+ use Term::Visual;
+
+ my $vt = Term::Visual->new( Alias => "interface",
+ Errlevel => 0 );
+
+ $vt->set_palette( mycolor => "magenta on black",
+ thiscolor => "green on black" );
+
+ my $window_id = $vt->create_window(
+ Window_Name => "foo",
+
+ Status => { 0 =>
+ { format => "template for status line 1",
+ fields => [qw( foo bar )] },
+ 1 =>
+ { format => "template for status line 2",
+ fields => [ qw( biz baz ) ] },
+ },
+
+ Buffer_Size => 1000,
+ History_Size => 50,
+
+ Use_Title => 0, # Don't use a titlebar
+ Use_Status => 0, # Don't use a statusbar
+
+ Title => "Title of foo" );
+
+ POE::Session->create
+ (inline_states => {
+ _start => \&start_handler,
+ got_term_input => \&term_input_handler,
+ }
+ );
+
+ sub _start_handler {
+ my $kernel = $_[KERNEL];
+
+ # Tell the terminal to send me input as "got_term_input".
+ $kernel->post( interface => send_me_input => "got_term_input" );
+
+ $vt->set_status_field( $window_id, bar => $value );
+
+ $vt->print( $window_id, "my Window ID is $window_id" );
+ }
+
+ sub term_input_handler {
+ my ($kernel, $heap, $input, $exception) = @_[KERNEL, HEAP, ARG0, ARG1];
+
+ # Got an exception. These are interrupt (^C) or quit (^\).
+ if (defined $exception) {
+ warn "got exception: $exception";
+ exit;
+ }
+ $vt->print($window_id, $input);
+ }
+
+ # Only use delete_window if using multiple windows.
+ $vt->delete_window( $window_id );
+
+ $vt->shutdown;
+
+
+=head1 DESCRIPTION
+
+Term::Visual is a "visual" terminal interface for curses applications.
+It provides the split-screen interface you may have seen in console
+based IRC and MUD clients.
+
+Term::Visual uses the POE networking and multitasking framework to support
+concurrent input from network sockets and the console, multiple
+timers, and more.
+
+=head1 PUBLIC METHODS
+
+Term::Visual->method();
+
+=over 2
+
+=item new
+
+Create and initialize a new instance of Term::Visual.
+
+ my $vt = Term::Visual->new(
+ Alias => "interface",
+ Common_Input => 1,
+ Tab_Complete => sub { ... },
+ Errlevel => 0 );
+
+Alias is a session alias for POE.
+
+Common_Input is an optional flag used
+ to globalize History_Position,
+ History_Size,
+ Command_History,
+ Data,
+ Data_Save,
+ Cursor,
+ Cursor_Save,
+ Tab_Complete,
+ Insert,
+ Edit_Position
+ in create_window();
+Thus all windows created will have common input.
+
+Tab_Complete is a handler for tab completion.
+
+ Tab_Complete => sub {
+ my $left = shift;
+ my @return;
+ my %complete = (
+ foo => "foobar ",
+ biz => "bizbaz ",
+ );
+ return $complete{$left};
+ }
+
+Tab_Complete is covered more indepth in the examples directory.
+
+Errlevel not implemented yet.
+
+Errlevel sets Term::Visual's error level.
+
+=item create_window
+
+ my $window_id = $vt->create_window( ... );
+
+Set the window's name
+
+ Window_Name => "foo"
+
+Set the Statusbar's format
+
+ Status => { 0 => # first statusline
+ { format => "\0(st_frames)" .
+ " [" .
+ "\0(st_values)" .
+ "%8.8s" .
+ "\0(st_frames)" .
+ "] " .
+ "\0(st_values)" .
+ "%s",
+ fields => [qw( time name )] },
+ 1 => # second statusline
+ { format => "foo %s bar %s",
+ fields => [qw( foo bar )] },
+ }
+
+Set the size of the scrollback buffer
+
+ Buffer_Size => 1000
+
+Set the command history size
+
+ History_Size => 50
+
+Set the title of the window
+
+ Title => "This is the Titlebar"
+
+Don't use Term::Visual's Titlebar.
+
+ Use_Title => 0
+
+Don't use Term::Visual's StatusBar.
+
+ Use_Status => 0
+
+No need to declare Use_Status or Use_Title if you want to use
+the Statusbar or Titlebar.
+
+
+=item send_me_input
+
+send_me_input is a handler Term::Visual uses to send the client input
+from a keyboard and mouse.
+
+create a handler for parsing the input in your POE Session.
+
+ POE::Session->create
+ (inline_states => {
+ _start => \&start_handler,
+ got_term_input => \&term_input_handler,
+ }
+ );
+
+POE's _start handler is a good place to tell Term::Visual how to send you
input.
+
+ sub start_handler {
+ my $kernel = $_[KERNEL];
+
+ # Tell the terminal to send me input as "got_term_input".
+ $kernel->post( interface => send_me_input => "got_term_input" );
+ ...
+ }
+
+Now create your "term_input_handler" to parse input.
+In this case we simply check for exceptions and print
+the input to the screen.
+
+ sub term_input_handler {
+ my ($kernel, $heap, $input, $exception) = @_[KERNEL, HEAP, ARG0, ARG1];
+
+ # Got an exception. These are interrupt (^C) or quit (^\).
+ if (defined $exception) {
+ warn "got exception: $exception";
+ exit;
+ }
+ $vt->print($window_id, $input);
+ }
+
+=item print
+
+Prints lines of text to the main screen of a window
+
+ $vt->print( $window_id, "this is a string" );
+
+ my @array = qw(foo bar biz baz);
+ $vt->print( $window_id, @array );
+
+=item current_window
+
+ my $current_window = $vt->current_window;
+
+ $vt->print( $current_window, "current window is $current_window" );
+
+=item get_window_name
+
+ my $window_name = $vt->get_window_name( $window_id );
+
+=item get_window_id
+
+ my $window_id = $vt->get_window_id( $window_name );
+
+=item delete_window
+
+ $vt->delete_window($window_id);
+
+or
+
+ $vt->delete_window(@window_ids);
+
+=item validate_window
+
+ my $validity = $vt->validate_window( $window_id );
+
+or
+
+ my $validity = $vt->validate_window( $window_name );
+
+ if ($validity) { do stuff };
+
+=item get_palette
+
+Return color palette or a specific colorname's description.
+
+ my %palette = $vt->get_palette();
+
+ my $color_desc = $vt->get_palette($colorname);
+
+ my ($foo, $bar) = $vt->get_palette($biz, $baz);
+
+=item set_palette
+
+Set the color palette or specific colorname's value.
+
+ $vt->set_palette( color_name => "color on color" );
+
+ $vt->set_palette( color_name => "color on color",
+ another => "color on color" );
+
+ NOTE: (ncolor, st_values, st_frames, stderr_text, stderr_bullet, statcolor)
+ are set and used by Term::Visual internally.
+ It is safe to redifine there values.
+
+=item set_title
+
+ $vt->set_title( $window_id, "This is the new Title" );
+
+=item get_title
+
+ my $title = $vt->get_title( $window_id );
+
+=item change_window
+
+Switch between windows
+
+ $vt->change_window( $window_id );
+
+ $vt->change_window( 0 );
+
+ ...
+
+ $vt->change_window( 1 );
+
+=item set_status_format
+
+ $vt->set_status_format( $window_id,
+ 0 => { format => "template for status line 1",
+ fields => [ qw( foo bar ) ] },
+ 1 => { format => "template for status line 2",
+ fields => [ qw( biz baz ) ] }, );
+
+=item set_status_field
+
+ $vt->set_status_field( $window_id, field => "value" );
+
+ $vt->set_status_field( $window_id, foo => "bar", biz => "baz" );
+
+=item columnize
+ columnize takes a list of text and formats it into
+ a columnized table.
+
+ columnize is used internally, but might be of use
+ externally as well.
+
+ Arguments given to columnize must be a hash.
+ key 'Items' must be an array reference.
+ The default value for Maxwidth may change to $COLS.
+
+ my $table = $vt->columnize(
+ Items => [EMAIL PROTECTED],
+ Padding => 2, # default value and optional
+ MaxColumns => 10, # default value and optional
+ MaxWidth => 80 # default value and optional
+ );
+
+=item bind
+
+ bind is used for key bindings.
+ our %Bindings = (
+ Up => 'history',
+ Down => 'history',
+ ...
+ );
+
+ $vt->bind(%Bindings);
+
+ sub handler_history {
+ my ($kernel, $heap, $key, $win) = @_[KERNEL, HEAP, ARG0, ARG2];
+ if ($key eq 'KEY_UP') {
+ $vt->command_history($win, 1);
+ }
+ else {
+ $vt->command_history($win, 2);
+ }
+ }
+
+ POE::Session->create(
+ inline_states => {
+ _start => \&handler_start,
+ _stop => \&handler_stop,
+ history => \&handler_history,
+ ...
+ }
+ );
+
+
+=item unbind
+ unbind a key
+
+ $vt->unbind('Up', 'Down');
+ $vt->unbind(keys %Bindings);
+
+=item debug
+ write to the debug file
+
+ $vt->debug("message");
+
+ Debugging must be turned on before using this.
+
+ change sub DEBUG () { 0 } to 1 or
+ add this to your program:
+ sub Term::Visual::DEBUG () { 1 }
+ use Term::Visual;
+
+=item shutdown
+ shutdown Term::Visual
+
+ $vt->shutdown();
+
+=back
+
+=head1 Internal Keystrokes
+
+=over 2
+
+=item Ctrl A or KEY_HOME
+
+Move to BOL.
+
+=item KEY_LEFT
+
+Back one character.
+
+=item Alt P or Esc KEY_LEFT
+
+Switch Windows decrementaly.
+
+=item Alt N or Esc KEY_RIGHT
+
+Switch Windows incrementaly.
+
+=item Alt K or KEY_END
+
+Not implemented yet.
+
+Kill a Window.
+
+=item Ctrl \
+
+Kill Term::Visual.
+
+=item Ctrl D or KEY_DC
+
+Delete a character.
+
+=item Ctrl E or KEY_LL
+
+Move to EOL.
+
+=item Ctrl F or KEY_RIGHT
+
+Forward a character.
+
+=item Ctrl H or KEY_BACKSPACE
+
+Backward delete character.
+
+=item Ctrl J or Ctrl M 'Return'
+
+Accept a line.
+
+=item Ctrl K
+
+Kill to EOL.
+
+=item Ctrl L or KEY_RESIZE
+
+Refresh screen.
+
+=item Ctrl N
+
+Next in history.
+
+=item Ctrl P
+
+Previous in history.
+
+=item Ctrl Q
+
+Display input status.
+
+=item Ctrl T
+
+Transpose characters.
+
+=item Ctrl U
+
+Discard line.
+
+=item Ctrl W
+
+Word rubout.
+
+=item Esc C
+
+Capitalize word to right of cursor.
+
+=item Esc U
+
+Uppercase WORD.
+
+=item Esc L
+
+Lowercase word.
+
+=item Esc F
+
+Forward one word.
+
+=item Esc B
+
+Backward one word.
+
+=item Esc D
+
+Delete a word forward.
+
+=item Esc T
+
+Transpose words.
+
+=item KEY_IC 'Insert'
+
+Toggle Insert mode.
+
+=item KEY_SELECT 'Home'
+
+If window is scrolled up, page all the way down.
+
+=item KEY_PPAGE 'Page Down'
+
+Scroll down a page.
+
+=item KEY_NPAGE 'Page Up'
+
+Scroll up a page.
+
+=item KEY_UP
+
+Scroll up a line.
+
+=item KEY_DOWN
+
+Scroll down a line.
+
+=back
+
+=head1 Author
+
+=over 2
+
+=item Charles Ayres
+
+
+Except where otherwise noted,
+Term::Visual is Copyright 2002, 2003, 2004 Charles Ayres. All rights reserved.
+Term::Visual is free software; you may redistribute it and/or modify
+it under the same terms as Perl itself.
+
+Questions and Comments can be sent to [EMAIL PROTECTED]
+
+Please send bug reports and wishlist items to:
+ http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Term-Visual
+
+=back
+
+=head1 Acknowledgments
+
+=over 2
+
+=item Rocco Caputo
+
+A Big thanks to Rocco Caputo.
+
+Rocco has contributed to the development
+of Term::Visual In many ways.
+
+Rocco Caputo <[EMAIL PROTECTED]>
+
+=back
+
+Thank you!
+
+=cut
Property changes on: trunk/clients/termvisual/Term/Visual.pm
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: trunk/clients/termvisual/termvisual.pl
===================================================================
--- trunk/clients/termvisual/termvisual.pl 2005-01-04 01:57:15 UTC (rev
512)
+++ trunk/clients/termvisual/termvisual.pl 2005-01-04 03:24:33 UTC (rev
513)
@@ -4,6 +4,8 @@
# Term::Visual Haver client
# This is Free Software, under the terms of the GPL.
+use lib qw(.);
+
use strict;
use warnings;
use Carp;