/usr/local/CPAN/DBIx-Informix-Perform/DBIx/Informix/Perform/Widgets/TextField.pm


package DBIx::Informix::Perform::Widgets::TextField;

use base 'Curses::Widgets::TextField';

use Curses;
use Curses::Widgets;

use constant 'KEY_DEL' => '330'; # dunno why not in Curses.

use 5.6.0;

our $OVERWRITE = 1;

sub input_key {
  # Process input a keystroke at a time.
  #
  # Usage:  $self->input_key($key);

  my $self = shift;
  my $in = shift;
  my $conf = $self->{CONF};
  my $mask = $$conf{MASK};
  my ($shift) = grep { $$conf{attrs}{$_} } qw(UPSHIFT DOWNSHIFT);
  my ($value, $pos, $max, $ro) = 
    @$conf{qw(VALUE CURSORPOS MAXLENGTH READONLY)};
  my @string = split(//, $value);

  # Process special keys
  if ($in eq "\cX") {		# ctrl-x = delete char forward
    return if $ro;
    if ($pos < length($value)) {
	splice(@string, $pos, 1);
	$value = join '', @string;
    } else {
      beep;
    }
  } elsif ($in eq KEY_RIGHT) {
    $pos < length($value) ? ++$pos : beep;
  } elsif ($in eq KEY_LEFT  or  $in eq KEY_BACKSPACE or $in eq "\cH") {
    $pos > 0 ? --$pos : beep;
  } elsif ($in eq KEY_HOME) {
    $pos = 0;
  } elsif ($in eq KEY_END) {
    $pos = length($value);
  } elsif ($in eq "\cD") {	# clear to end of field
      splice(@string, $pos, $#string-$pos+1);
      $value = join('', @string);
  } elsif ($in eq "\cU") {	# clear to beginning  (not part of Perform)
      $value = "";
  # Process other keys
  } else {

    return if $ro || $in !~ /^[[:print:]]$/;

    # Exit if it's a non-printing character
    return unless $in =~ /^[\w\W]$/;

    $in = uc($in)
	if $shift eq 'UPSHIFT';
    $in = lc($in)
	if $shift eq 'DOWNSHIFT';

    # Append to the end if the cursor's at the end
    if ($pos == length($value)) {
	# Reject if we're already at the max length
	if (defined $max && length($value) == $max) {
	    beep;
	    return;
	}
	$value .= $in;

    # Insert/replace the character at the cursor's position
    } elsif ($OVERWRITE) {
	splice(@string, $pos, 1, $in);
	$value = join('', @string);
    } elsif ($pos > 0) {
      @string = (@string[0..($pos - 1)], $in, @string[$pos..$#string]);
      $value = join('', @string);

    # Insert the character at the beginning of the string
    } else {
      $value = "$in$value";
    }

    # Increment the cursor's position
    ++$pos;

    # If just filled up and AUTONEXT is on, exit.
    $$conf{'EXIT'} = 1		# requires change to execute
	if (defined $max && length($value) == $max &&
	    $$conf{'AUTONEXT'});
  }

  # Save the changes
  @$conf{qw(VALUE CURSORPOS)} = ($value, $pos);
}


#  Overriding Curses::Widgets::execute
sub execute {
  my $self = shift;
  my $mwh = shift;
  my $conf = $self->{CONF};
  my $func = $$conf{'INPUTFUNC'} || \&scankey;
  my $fskeys = $$conf{'FOCUSSWITCH'};
  my $mkeys = $$conf{'FOCUSSWITCH_MACROKEYS'};
  my $key;

  $mkeys = [$mkeys] if (defined($mkeys) && ref($mkeys) ne 'ARRAY');
  my $regex = $mkeys ? ("([$fskeys]|" . join ('|', @$mkeys) . ")")
       :  "[$fskeys]";

  $self->draw($mwh, 1);

  while (1) {
    $key = &$func($mwh);
    if (defined $key) {
      if (defined $regex) {
	  return $key if ($key =~ /^$regex/ || ($fskeys =~ /\t/ &&
						$key eq KEY_STAB));
      }
      if ($key eq "\cA") {
	  $OVERWRITE = !$OVERWRITE;
	  #print STDERR "OVERWRITE = '$OVERWRITE'\n";
	  return $key;
      }
      $self->input_key($key);
    }
    $self->draw($mwh, 1);
    last if $$conf{'EXIT'};	# ADDED
  }
}


# Modify this to handle the pos-after-at-last-char case.
sub _content {
  my $self = shift;
  my $dwh = shift;
  my $cursor = shift;
  my $conf = $self->{CONF};
  my ($pos, $ts, $value, $border, $col) = 
    @$conf{qw(CURSORPOS TEXTSTART VALUE BORDER COLUMNS)};
  my $seg;

  # Trim the value if it exceeds the maximum length
  $value = substr($value, 0, $$conf{MAXLENGTH}) if $$conf{MAXLENGTH};

  # Turn on underlining (terminal-dependent) if no border is used
  $dwh->attron(A_UNDERLINE) unless $border;

  # Adjust the cursor position and text start if it's out of whack
  if ($pos > length($value)) {
    $pos = length($value);
  } elsif ($pos < 0) {
    $pos = 0;
  }
  if ($pos > $ts + $$conf{COLUMNS} - 1) {
#    $ts = $pos + 1 - $$conf{COLUMNS};
    $ts = $pos     - $$conf{COLUMNS};
  } elsif ($pos < $ts) {
    $ts = $pos;
  }
  $ts = 0 if $ts < 0;

  # Write the widget value (adjusting for horizontal scrolling)
  $seg = substr($value, $ts, $$conf{COLUMNS});
  $seg = '*' x length($seg) if $$conf{PASSWORD};
  $seg .= ' ' x ($$conf{COLUMNS} - length($seg));
  $dwh->addstr(0, 0, $seg);
  $dwh->attroff(A_BOLD);

  # Underline the field if no border is used
  $dwh->chgat(0, 0, $col, A_UNDERLINE, 
    select_colour(@$conf{qw(FOREGROUND BACKGROUND)}), 0) unless $border;

  # Save the textstart, cursorpos, and value in case it was tweaked
  @$conf{qw(TEXTSTART CURSORPOS VALUE)} = ($ts, $pos, $value);
}


sub _cursor {
  my $self = shift;
  my $dwh = shift;
  my $conf = $self->{CONF};

  
  # Display the cursor
  my $cpos = $$conf{CURSORPOS} - $$conf{TEXTSTART};
  my $attr = A_STANDOUT;
  if ($cpos >= $$conf{COLUMNS}) {
      $cpos--;
      $attr = A_REVERSE;
  }
  $dwh->chgat(0, $cpos, 1, $attr, 
    select_colour(@$conf{qw(FOREGROUND BACKGROUND)}), 0)
    unless $$conf{READONLY};

  # Restore the default settings
  $self->_restore($dwh);
}


1;