#!/usr/local/bin/perl5.6.1 -w
# $Id: Dna2AbTubeV.pm,v 1.2 2003/10/24 10:49:26 jx1 Exp $

package Dna2AbTubeV;

use strict;
use warnings;
use Carp qw(croak cluck carp);
use Class::Struct;
use sigtrap qw(die untrapped normal-signals
               stack-trace any error-signals);
#use sigtrap qw( die KILL );

use Tk;
use Dna2AbTubeDataMgr;

my $setup = { title_f       => "*-times-bold-r-normal--*-180-*-*-p-*-*-*",
	      entry_f       => "-adobe-helvetica-medium-r-normal--17-120-100-100-p-88-iso8859-1",
	      button_f      => "-adobe-helvetica-bold-r-normal--17-120-100-100-p-92-iso8859-1",
	      menu_f        => "-adobe-helvetica-bold-r-normal--17-120-100-100-p-92-iso8859-1",
	      label_f       => "-adobe-helvetica-bold-r-normal--14-100-100-100-p-82-iso8859-1",
	      active_entry  => "pale green",
	      disabled_entry=> "grey85",
	      other_entry   => "white",
	      frame_bordrw  => '2',
	  };

my $titles = ['DNA Tube', 'Storage T1', 'Storage T2', 'Comment'];

my $pageLayout = { Column   => scalar @{$titles},
		   CWidth   => 15,
		   PageSz   => 4, #12
		 };

my $BITMAP_DIR = "$ENV{'BADGER'}/lib/platetrack/bitmaps";  

###########################################################################
 
unless (caller)
{
    use Dna2AbTubeV;

    my $mw = MainWindow->new();
    my $front =  Dna2AbTubeV -> new();
    $front -> init( $mw );

    $front-> OpFrame() -> pack( -in       => $mw,
				 -side     => 'top',
				 -anchor   => 'n',
				 -expand   => 1,
				 -fill     => 'x' );     
    MainLoop;
}


####    object definition    ####

struct Dna2AbTubeV =>
{
    Leader     => '$',
    OpFrame   => '$',
    TubeEntry  => '$',
    DNAcnt     => '$',
    PageNum    => '$',
    OpButtons  => '$', 
    DataEntry  => '$',
    DataPos    => '$',
    DataSource => '$', 
    #DataList => '$',
    DnaTubeOK => '$',
};


####    public methods    ####

sub init
{
    my $self = shift;
    my $parentw = shift; # as para

    my $op_frame = $parentw -> Frame( -relief	   => 'ridge', 
				      -borderwidth => $setup->{frame_bordrw},
				      -label => "Transfer DNA to Storage Tubes",
				      -labelFont => $setup->{title_f},
				      -labelPack => [ -side => 'top'] );

    $self -> DataPos( { CurRow => 0,
			CurPage => 1, 
			PageCnt => 1,
			PageInView => 1,
		      } );

    $self -> OpFrame( $op_frame );
    $self -> TubeEntry( createTubeEntry( $self ) );
    $self -> DNAcnt( createDnaCntLabel( $self ) );
    $self -> PageNum( createPageNumE( $self ) );
    createTitleGrid( $self );
    $self -> DataEntry( createDataGrid( $self ) );
    $self -> OpButtons( createOpButtons( $self ) );
#    createOpButtons( $self );

    # set leading Gui object
    my $leader = shift;
    if ( defined $leader )
    {
	$self->Leader( $leader );
    }
    
    eval
    {
	$self -> DataSource( Dna2AbTubeDataMgr->new(VLeader=>$leader)->init() );
    };
    if($@)
    {
	#if( $^W )
	{
	    print "$@\n";
	}

	my $msg = (split /\n/, $@)[0];
	print "Create BCreader error: $msg\n";
	#$leader -> updateWarning( $msg );
	croak $msg; #$@;
    }

#    print $self -> DataSource() -> BcReader()->{host}; #ABgeneHost();
#    print "\n";
    # ABgene host could be setup as env for user ...

    $self->DnaTubeOK( 0 );

    return $self;
}


sub updatePageNum
{
    my $self = shift;
    my $page = $self->PageNum();

#    if( @_ ) #clear display
#    {
#	${$page} = '';
#    }
#    else
#    {
	${$page} = $self->DataPos()->{PageInView}.' of '.$self->DataPos()->{PageCnt};
#    }
    print "${$page}\n";
    
}


sub addDataLineToGridV
{
    my $self = shift;
    #my $dataline = shift;
    my $ncol = $pageLayout->{Column};
    my $datapos = $self->DataPos();

    # whether a new page needed
    if( $datapos->{CurRow} >= $pageLayout->{PageSz} )
    {
	$self->updateDataPage();

	#update page number view
	$self->updatePageNum();

	# need to clear display first 
	$self->clearGridPage();
    }

#    if( $datapos->{CurPage} > 1 && 
#	$datapos->{CurRow} < 1 )
#    {
#	# start a new page
#
#	# need to clear display first 
#	$self->clearGridPage();
#
#	#update page number view
#	$self->updatePageNum();
#    }

    # go to current page 
    # if the viewing page is different 
    if( $datapos->{PageInView} != $datapos->{CurPage} )
    {
	$datapos->{PageInView} = $datapos->{CurPage};
	$self->updateDataView();
	$self->updatePageNum();
    }
    # otherwise, just add one line to the page
    else
    {
	my $irow = $datapos->{CurRow};
	my $dataline = $self->DataSource()->getLastDataLine();

	foreach my $icol ( 0 .. $ncol-1 )
	{
	    ${ $self->DataEntry()->[$irow]->[$icol] } = $dataline->[$icol];
        }
    }

    $datapos->{CurRow}++;
    
    $self->updateDNAcnt();
    print ${$self->DNAcnt}, " DNA tubes have been transferred.\n";

print "--: Current data position :--\n";
print "Updating page: ",  $datapos->{CurPage}, "\tUpdating row: ", $datapos->{CurRow}, "\n";
print "Viewing page: ", $datapos->{PageInView}, "\n";
print "\n";

    return;
}


sub clearGridPage
{
    my $self = shift;
    my $pgsz = $pageLayout->{PageSz};
    my $ncol = $pageLayout->{Column};

    my $data_entry = $self->DataEntry();
    my ($irow, $icol);
    foreach $irow ( 0 .. $pgsz-1 )
    {
	foreach $icol ( 0 .. $ncol-1 )
	{
	    ${ $data_entry->[$irow]->[$icol] } = '';
	}
    }
    
}

sub updateDataPage #called when a new page required
{
    my $self = shift;
    my $datapos = $self->DataPos();
    
    if( @_ ) #reset
    {
	$datapos->{CurPage} = 1;
	$datapos->{PageCnt} = 1;
    }
    else
    {
	$datapos->{CurPage}++;
	$datapos->{PageCnt}++;
    }

    $datapos->{CurRow} = 0;
    $datapos->{PageInView} = $datapos->{CurPage};

    print "Page count of data: ";
    print $datapos->{PageCnt};
    print "\n";
}


sub updateDNAcnt
{
    my $self = shift;
    my $cnt = $self->DNAcnt();
    
#    ${$cnt} = ($self->DataPos()->{PageCnt}-1) * $pageLayout->{PageSz}
#            + $self->DataPos()->{CurRow};
    if(@_)
    {
	${$cnt} = 0;
    }
    else
    {
	${$cnt} ++;
    }
}


sub updateDataView
{
    my $self = shift;
    my $data;
    my @datapage;

    my $datapos = $self->DataPos();
    my $page = $datapos->{PageInView};
    my $pgsz = $pageLayout->{PageSz};
    my $ncol = $pageLayout->{Column};

    if( @_ ) #clear
    {
	# 'undef' values in @datapage
	# safe enough to work on????
    }
    else
    {
	$data = $self -> DataSource() -> DataList(); 

#	print "--- data in source list ---\n";
#	foreach my $line (@{$data})
#	{
#	    foreach (@{$line})
#	    {
#		print $_, "\t";
#	    }
#	    print "\n";
#	}
# 	print "$page\n";

#    print "Data reference is: $data\n";
#    print "@{$data}\n";
#    print "One of the row data: @{$data->[10]}\n";

	@datapage = (@$data)[ ($page-1)*$pgsz .. $page*$pgsz-1 ]; #array of refs to array

	print "--- data in view ---\n";
	foreach my $line (@datapage)
	{
	    foreach (@{$line})
	    {
		print $_, "\t";
	    }
	    print "\n";
	}

#	if( $datapos->{CurPage} == $page )
#	{
#	    if( !$datapage[ $datapos->{CurRow} ]->[ $ncol-2 ] )
#	    {
#		foreach ( @{$datapage[ $datapos->{CurRow} ]} )
#		{
#		    $_ = undef();
#		}
#	    }
#        }
	 
    }
    #print "$#datapage\n";

    # display data page ...
    my ($irow, $icol);
    my $data_entry = $self->DataEntry();
    foreach $irow ( 0 .. $pgsz-1 )#$self->DataPos()->{CurRow} )
    {
	foreach $icol ( 0 .. $ncol-1 )
	{
#	    $self->DataEntry()->[$irow]->[$icol] -> 
#		configure( -textvariable => \$datapage[$irow]->[$icol],
#			   -background => $setup->{other_entry} );
	    ${ $data_entry->[$irow]->[$icol] } = $datapage[$irow]->[$icol];
	}
    }
}


sub end
{
    my $self = shift;
    $self->Leader( undef() );
    $self->DataSource( undef() );

    my $topframe = $self->OpFrame();
    if( defined $topframe )
    {
	$topframe -> packForget();
	$topframe -> parent() -> packPropagate(1);

	$self->OpFrame()->destroy();
	$self->OpFrame( undef() );
    }

    print "Dna2AbTubeV end process called.\n";
}


sub DESTROY
{
    my $self = shift;
    
    if( defined $self->OpFrame() )
    {
	$self->end();
    }
    print "Dna2AbTubeV destructor called.\n";
}


####    private procedures/functions    ####

sub createTubeEntry
{
    my $family = shift;
    my $op_frame = $family -> OpFrame();

    # create the 'look'
    my $tb_frame = $op_frame -> Frame( -label => '',
				       -labelFont => $setup->{label_f},
				       -labelPack => [ -side => 'top'] );

    my $tbf_gapL = $tb_frame -> Label( -text => '', 
				       -font => $setup->{label_f} )
	-> pack ( -side => 'bottom' );

    $tb_frame -> pack ( -side   => 'top',
			-expand => 1, 
			-fill   => 'x');

    my $DNAtube_frame = $tb_frame -> Frame();

    my $storeT1_frame = $tb_frame -> Frame();
    my $storeT2_frame = $tb_frame -> Frame();

    $DNAtube_frame -> pack ( -side   => 'top',
			     -expand => 1, 
			     -fill   => 'x');
    $storeT1_frame -> pack ( -side   => 'top',
			     -expand => 1, 
			     -fill   => 'x');
    $storeT2_frame -> pack ( -side   => 'top',
			     -expand => 1, 
			     -fill   => 'x');

    my $DNAtubeL = $DNAtube_frame -> Label( -width => $pageLayout->{CWidth},
					    -font => $setup->{button_f},
					    -text => 'DNA Tube ',
					    -justify => 'left',
					    -anchor => 'e'
					  ) -> pack( -side => 'left' );

    my $DNAtubeE = $DNAtube_frame -> Entry( -width => $pageLayout->{CWidth},
					    -font => $setup->{entry_f}
					  ) -> pack( -side => 'left' );

    my $storeT1L = $storeT1_frame -> Label( -width => $pageLayout->{CWidth},
					    -font => $setup->{button_f},
					    -text => 'Store Tube1 ',
					    -justify => 'left',
					    -anchor => 'e'
					  ) -> pack( -side => 'left' );

    my $storeT1E = $storeT1_frame -> Entry( -width => $pageLayout->{CWidth},
					    -font => $setup->{entry_f},
					    #-takefocus => 1,
					    -state => 'disabled'
					  ) -> pack( -side => 'left' ); 


    my $storeT2L = $storeT2_frame -> Label( -width => $pageLayout->{CWidth},
					    -font => $setup->{button_f},
					    -text => 'Store Tube2 ',
					    -justify => 'left',
					    -anchor => 'e'
					  ) -> pack( -side => 'left' );

    my $storeT2E = $storeT2_frame -> Entry( -width => $pageLayout->{CWidth},
					    -font => $setup->{entry_f},
					    -state => 'disabled'
					  ) -> pack( -side => 'left' ); 

    
    # binding events to entries
    $DNAtubeE -> bind( "<FocusIn>" => [\&DNAt_gotFocus, $family] );
    $DNAtubeE -> bind( "<Return>" => [\&DNAt_finishEntry, $family] );
    $DNAtubeE -> bind( "<Key-BackSpace>" => [\&entry_delete, $family, 'startline'] );
    $DNAtubeE -> bind( "<Key-Delete>" => [\&entry_delete, $family, 'startline'] );

    $storeT1E -> bind( "<FocusIn>" => [\&storeT_gotFocus, $family] );
    $storeT1E -> bind( "<Return>" => [\&storeT_finishEntry, $family] );
    $storeT1E -> bind( "<Key-BackSpace>" => [\&entry_delete, $family] );
    $storeT1E -> bind( "<Key-Delete>" => [\&entry_delete, $family] );

    $storeT2E -> bind( "<FocusIn>" => [\&storeT_gotFocus, $family] );
    $storeT2E -> bind( "<Return>" => [\&storeT_finishEntry, $family, 'endline'] ); 
    $storeT2E -> bind( "<Key-BackSpace>" => [\&entry_delete, $family] );
    $storeT2E -> bind( "<Key-Delete>" => [\&entry_delete, $family] );

    $DNAtubeE->focusForce();

    return [$DNAtubeE, $storeT1E, $storeT2E];
}


sub createDnaCntLabel
{
    my $family = shift;
    my $op_frame = $family -> OpFrame();

    my $cnt_frame = $op_frame -> Frame( -label => "DNA Tubes Total ",
				        -labelFont => $setup->{button_f},
				        -labelPack => [ -side => 'left']);

    $cnt_frame -> pack ( -side   => 'top',
			 -expand => 1, 
			 -fill   => 'x');

    my $dna_cnt = 0;
    my $cntlabel = $cnt_frame -> Label( -width => $pageLayout->{CWidth},
					-font => $setup->{entry_f},
					-textvariable => \$dna_cnt,
					-relief => 'sunken',
					-background => 'white'
				      ) -> pack( -side => 'left' );

    #return [$cntlabel, \$dna_cnt];
    return \$dna_cnt;
}


sub createPageNumE
{
    my $family = shift;
    my $op_frame = $family -> OpFrame();

    my $pnum_frame = $op_frame -> Frame( -label => "Page ",
					 -labelFont => $setup->{button_f},
					 -labelPack => [ -side => 'left'] );

    $pnum_frame -> pack ( -side   => 'top',
			  -anchor => 'e');

    #my $curp = 'No Data';
    #my $pnum_value = '1 of 1';
    my $pnum_value = ${\($family->DataPos()->{PageInView})}.' of '.${\($family->DataPos()->{PageCnt})};
    my $pnum_label = $pnum_frame -> Label( -width => 8,
					   -font => $setup->{entry_f},
					   -textvariable => \$pnum_value,
					   -relief => 'sunken',
					   -background => 'white'
					 ) -> pack( -side => 'right' );
    #return $pnum_label;
    return \$pnum_value;
}



sub createTitleGrid
{
    my $family = shift;
    my $op_frame = $family -> OpFrame();
    
    my $grid_frame = $op_frame -> Frame( ) -> pack ( -side   => 'top',
						     -expand => 0, 
						     -fill   => 'x');
    my $n_title = scalar @{$titles};
#    my $width = 15;

    my $titleLabel;

    foreach my $title_column (0 .. ($n_title-1))
    {
	$titleLabel->[$title_column] = $grid_frame->Label( -width => $pageLayout->{CWidth},
							   #-wraplength => $width,
							   -relief => 'sunken',
							   -text  => $titles->[$title_column],
							   -font => $setup->{button_f},
							   -background => 'white');

	$titleLabel->[$title_column] -> grid( -row => 0, 
					      -column => $title_column,  
					      -sticky => 'w' );
    }
}


sub createDataGrid
{
    my $family = shift;
    my $op_frame = $family -> OpFrame();
    
    my $data_frame = $op_frame -> Frame( ) -> pack ( -side   => 'top',
						     -expand => 0, 
						     -fill   => 'x');
    
    my $dataLabel;
    my $data_val;
    my $n_col = $pageLayout->{Column};
    foreach my $rowi (0 .. $pageLayout->{PageSz}-1)
    {
	my $dataline;

	foreach my $coli (0 .. ($n_col-1))
	{
	    my $data_point;

	    $dataLabel->[$rowi][$coli] = $data_frame->Label( -width => $pageLayout->{CWidth},
							     -relief => 'sunken',
							     -font => $setup->{entry_f},
							     -textvariable => \$data_point,
							     -background => $setup->{other_entry});
	    $dataLabel->[$rowi][$coli] -> grid( -row    => $rowi, 
						-column => $coli,
						-sticky => 'w' );
	    push @{$dataline}, \$data_point;
	}

	push @{$data_val}, $dataline;
    } # end outter 'foreach' loop 	

    #return $dataLabel;
    return $data_val;

} # End sub createDateGrid


sub createOpButtons
{
    my $family = shift;

    my $parentf = $family -> OpFrame();
    my $buttonf = $parentf -> Frame() ->  pack( -side     => 'bottom',
						-anchor   => 'n',
						-expand   => 1,
						-fill     => 'x' );

    my $quitB = $buttonf -> Button( -font             => $setup->{button_f},
				    -fg               => 'purple',
				    -activebackground => 'black',
				    -activeforeground => 'white',   
				    -text             => "Quit",
				    -command          => [\&quitButtonPressed, $family]
				  ) -> pack( -side => 'right' );  

    my $clearB = $buttonf -> Button( -font             => $setup->{button_f},
				     -fg               => 'purple',
				     -activebackground => 'black',
				     -activeforeground => 'white',   
				     -text             => "Clear",
				     -command          => [\&clearButtonPressed, $family]
				   ) -> pack( -side => 'right' );  

    my $updateB = $buttonf -> Button( -font             => $setup->{button_f},
				      -state            => 'disabled',
				      -fg               => 'purple',
				      -activebackground => 'black',
				      -activeforeground => 'white',   
				      -text             => "Update",
				      -command          => [\&updateButtonPressed, $family]
				    ) -> pack( -side => 'right' );  

    my $imgUpArrow = $buttonf -> Bitmap( -file => "$BITMAP_DIR/uarrw.xbm",
					 -maskfile => "$BITMAP_DIR/uarrw.xbm"
				        );
    my $upButton = $buttonf -> Button( -image   => $imgUpArrow,
				       -command => [\&upButtonPressed, $family]
				     )->pack(-side => 'left'); 

    my $imgDnArrow = $buttonf -> Bitmap( -file => "$BITMAP_DIR/darrw.xbm",
					 -maskfile => "$BITMAP_DIR/darrw.xbm"
				        );
    my $dnButton = $buttonf -> Button( -image   => $imgDnArrow, 
				       -command => [\&downButtonPressed, $family]
				     )->pack(-side => 'left'); 

#    my $scanB = $buttonf -> Button( -font             => $setup->{button_f},
#				    -fg               => 'purple',
#				    -activebackground => 'black',
#				    -activeforeground => 'white',   
#				    -text             => "Scan",
#				    -command          => [\&scanButtonPressed, $family]
#				  ) -> pack( -side => 'left' );  

    return [$updateB, $clearB];
}

sub enableButton
{
    my $updateB = shift;

    if( @_ ) #disabling
    {
	$updateB->configure( -state => 'disabled' );
    }
    else
    {
	$updateB->configure( -state => 'normal' );
    }
}

####    callbacks    ####

# DNA tube entry
sub DNAt_gotFocus
{
    my $entry = shift;
    my $family = shift;
    my $vleader = $family->Leader();

    $entry -> configure( -background => $setup->{active_entry},
			 -state      => 'normal');#, 
			 #-textvariabel => \$e_value);
    #$entry->delete( 0, 'end' );

    if( defined $vleader )
    {
	$vleader->updateMessage( "Please scan DNA tube." );
    }

#    $entry -> grab();
    return;
}

sub DNAt_finishEntry
{
    my $entry = shift;
    my $family = shift;
    
    my $datamgr = $family->DataSource();
    my $vleader = $family->Leader();

    my $e_value = $entry->get();
    $e_value =~ s/^\s+//;   # trim leading spaces
    $e_value =~ s/\s+$//;   # trim tailing spaces

    # basic validation of input
    if( $e_value =~ /^DT/ || $e_value =~ /^\d+$/ )
    {
	if( !$family->DnaTubeOK() )
	{
	    # disable UPDATE, CLEAR buttons
	    enableButton( $family->OpButtons()->[0], 'clear' );
	    enableButton( $family->OpButtons()->[1], 'clear' );
	    
	    # further verification, from data manager 
	    eval 
	    {
		$e_value = $datamgr->verifyDnaTube( $e_value );
	    };
	    if( $@ )
	    {
		## when tubeid not valid
		# extract error message
		my $msg = (split /\n/, $@)[0];
		
		if( defined $vleader )
		{
		    $vleader->updateWarning( $msg );
		}
		else
		{
		    print $@;
		}
		
		return;
	    }
	    else
	    {
		$entry -> configure( -textvariable => \$e_value );
	    }

	    #$entry -> grabRelease();
	    # mark DNA tube as been verified
	    $family->DnaTubeOK(1);
	}

	$entry -> configure( -background => $setup->{disabled_entry},
			     -state      => 'disabled' );

	# set focus to the next entry
	push ( @{ $family->TubeEntry() }, shift @{ $family->TubeEntry() } );

	$family->TubeEntry()->[0] -> focus();
    }
    else
    {
	my $msg = "$e_value - incorrect id type.";
	if( defined $vleader )
	{
	    $vleader->updateWarning( $msg );
	}
	#else
	{
	    print "$msg\n"; # if( $^W );
	}
    }

    return;
}


sub storeT_gotFocus
{
    my $entry = shift;
    my $family = shift;
    my $vleader = $family->Leader();

    print "In ABtube focus handling.\n"; # if( $^W );

    $entry -> configure( -background => $setup->{active_entry} ); #,
			 #-state      => 'normal');

    if( defined $vleader )
    {
	$vleader->updateMessage( "Please insert tube and press 'ENTER' key again.");
    }
    else
    {
	print "Please insert tube and press 'ENTER' key again.\n" if( $^W );
    }
    return;
}

sub storeT_finishEntry
{
    my $entry = shift;

    my $family = shift;
    my $data_src = $family->DataSource();
    my $vleader = $family->Leader();

#    $entry->parent()->focus(); # make entry loose focus
    my $e_value = $entry->get();

    if( $e_value eq '' )
    {
	# need to scan
        # call data manager for scanning and return tube id
	eval
	{
	    $e_value = $data_src->readAbTube();
	};
	if( $@ )
	{
	    my $msg = (split /\n/, $@)[0];
	    if( defined $vleader )
	    {
		$vleader->updateWarning( $msg );
	    }
	    else
	    {
		print "$msg\n"; # if( $^W );
	    }

	    $entry->parent()->focus();
	    $entry->focus();
	    return;
	}

	$entry -> configure( -textvariable => \$e_value );
    }
    else
    {
	# when ABtube already scanned

	if( @_ )
	{
	    # at the end of the input line
	    # 1.
	    # clear all entries and get ready for another line of input
	    my $tbEs = $family->TubeEntry();
	    foreach ( @{$tbEs} )
	    {
		$_ -> configure( -state => 'normal' );
		$_ -> delete( 0, 'end' );
		$_ -> configure( -state => 'disabled' );
	    }

	    # 2. 
	    # end of data line
	    # fetch data and display in data view
	    #my $dataline = $family->DataSource()->getLastDataLine();
	    #$family->addDataLineToGridV( $dataline );

	    $family->addDataLineToGridV();

	    # 4.
	    # enable update & clear button
	    enableButton( $family->OpButtons()->[0] );
	    enableButton( $family->OpButtons()->[1] );

	    # 5. ready to accept new DNA tube
	    $family->DnaTubeOK( 0 );
	}

	# a. 
	$entry -> configure( -background => $setup->{disabled_entry} ); #,

	# 3. & b.
	# rotate circular list
	push ( @{ $family->TubeEntry() }, shift @{ $family->TubeEntry() } );
	$family->TubeEntry()->[0] -> focus();
	
	#print "Other input paras: ", @_, "\n";
    }
    
    return;
}


sub entry_delete
{
    my $entry = shift;
    my $family = shift;
    my $vleader = $family->Leader();
    my $datasrc = $family->DataSource();

    print "program controled entry delete ... \n";

    my $e_value = $entry->get();
    if( $e_value )
    {
	print "need to remove data; \n";
	$entry->configure( -state => 'normal' );
	$entry->delete( 0, 'end' );
	$entry->configure( -state => 'disabled' );

	if( @_ ) # at start of entry list
	{
	    print "clearing entry ...\n";
	    $entry->configure( -state => 'normal' );
	    if( $family->DnaTubeOK() )
	    {
		print "removing last line of data ...\n";
		$datasrc->removeDataLine();
		$family->DnaTubeOK( 0 );
	    }
	}
	else
	{
	    print "removing 1 data item from last data line ...\n";
	    $datasrc->removeDataItem( $e_value );
	}
    }

    if( !@_ ) # not at start of entry list
    {
	print "Not at the start of input entry, moving backwards.\n";

	$entry->configure( -background => $setup->{disabled_entry} );

	my $tubeEs = $family->TubeEntry();
	unshift @{$tubeEs}, pop @{$tubeEs};
	$tubeEs->[0]->focus();
    }

    return;
}


# for buttons
sub upButtonPressed
{
    print "Request for previous page ......\n";

    my $family = shift;
    my $curP = $family->DataPos();
    my $lastline = $family->DataSource()->getLastDataLine();

    if( $curP->{PageInView} > 1 &&
	scalar @{$lastline} >= 3 )
    {
	print "Current display page: $curP->{PageInView} \n";
	$curP->{PageInView} --;
	print "Updated display page: ", $family->DataPos()->{PageInView}, "\n";
	
	$family -> updateDataView(); 
	$family -> updatePageNum();
    }  

    return;
}


sub downButtonPressed
{
    print "Request for next page ......\n";

    my $family = shift;
    my $curP = $family->DataPos();
    my $lastline = $family->DataSource()->getLastDataLine();

    if ( $curP->{PageCnt} > $curP->{PageInView} &&
	 scalar @{$lastline} >= 3 )
    {
	print "Current display page: $curP->{PageInView} \n";
	$curP->{PageInView} ++;
	print "Updated display page: ", $family->DataPos()->{PageInView}, "\n";
	
	$family -> updateDataView();
	$family -> updatePageNum();
    }

    return;
}


sub updateButtonPressed
{
    my $family = shift;
    my $vleader = $family->Leader();

    print "Update button pressed.\n";

    eval
    {
	$family->DataSource()->recordData();
    };
    if( $@ )
    {
	my $msg = (split $@, /\n/)[0];
	    
	if( defined $vleader )
	{
	    $vleader->updateWarning( $msg );
	}
	else
	{
	    print $@;
	}
    }
    else
    {
	# do we clear display here??
	#$family->clearButtonPressed();
	$vleader->updateMessage( "Database update successful." );
	
    }
    return;
}


sub clearButtonPressed
{
    # when 'clear', just clear the display and 
    # leave the DataSource intact

    my $family = shift;

    $family -> DataSource() -> resetData();
    $family -> updateDataView( 'clear' );
    $family -> updateDataPage( 'clear' );
    $family -> updateDNAcnt( 'clear' );
    $family -> updatePageNum( 'clear' );

    my $datapage = $family->DataPos();

#    print "Current page: ", $datapage->{CurPage}, "\n";
#    print "Current row: ", $datapage->{CurRow}, "\n";
#    print "Current page-in-view: ", $datapage->{PageInView}, "\n";

    #enableDbUpdate( $family->DbButton(), 'clear' );
}


sub quitButtonPressed
{
    my $family = shift;
    my $vleader = $family->Leader();

    if( defined $vleader )
    {
	$vleader->handleOpQuit();
    }

    $family->end();
    
    return;
}

1;     # just to keep perl happy
