The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package LifeWidget;

use Qt 2.0;
use Qt::slots ('nextGeneration()', 'clear()');

@ISA = qw(Qt::Frame);

$SCALE = 10;
$MAXSIZE = 50;
$MINSIZE = 10;
$BORDER = 5;

sub pos2index {
    my $x = shift;
    return int(($x - $BORDER) / $SCALE + 1);
}

sub index2pos {
    my $i = shift;
    return int(($i - 1) * $SCALE + $BORDER);
}

sub maxCol { shift->{'maxi'} }
sub maxRow { shift->{'maxj'} }

sub new {
    my $self = shift->SUPER::new(@_);

    $self->{'maxi'} = $self->{'maxj'} = 50;
    $self->setMinimumSize($MINSIZE * $SCALE + 2 * $BORDER,
			  $MINSIZE * $SCALE + 2 * $BORDER);
    $self->setMaximumSize($MAXSIZE * $SCALE + 2 * $BORDER,
			  $MAXSIZE * $SCALE + 2 * $BORDER );
    $self->setSizeIncrement($SCALE, $SCALE);

    $self->clear();
    $self->resize($self->{'maxi'} * $SCALE + 2 * $BORDER,
		  $self->{'maxj'} * $SCALE + 2 * $BORDER);

    return $self;
}


sub clear {
    my $self = shift;
    $self->{'current'} = 0;
    for(my $t = 0; $t < 2; $t++) {
	for(my $i = 0; $i < $MAXSIZE + 2; $i++) {
	    for (my $j = 0; $j < $MAXSIZE + 2; $j++) {
		$self->{'cells'}[$t][$i][$j] = 0;
	    }
	}
    }

    $self->repaint();
}


sub resizeEvent {
    my $self = shift;
    my $e = shift;
    $self->{'maxi'} = ($e->size()->width()  - 2 * $BORDER) / $SCALE;
    $self->{'maxj'} = ($e->size()->height() - 2 * $BORDER) / $SCALE;
}


sub setPoint {
    my $self = shift;
    my($i, $j) = @_;
    return if $i < 1 || $i > $self->{'maxi'} ||
	      $j < 1 || $j > $self->{'maxi'};
    $self->{'cells'}[$self->{'current'}][$i][$j] = 1;
    $self->repaint(index2pos($i), index2pos($j), $SCALE, $SCALE, 0);
}


sub mouseHandle {
    my $self = shift;
    my $pos = shift;
    my $i = pos2index($pos->x());
    my $j = pos2index($pos->y());
    $self->setPoint($i, $j);
}

sub mouseMoveEvent {
    my $self = shift;
    my $e = shift;
    $self->mouseHandle($e->pos());
}

sub mousePressEvent {
    my $self = shift;
    my $e = shift;
    $self->mouseHandle($e->pos()) if $e->button() == Qt::LeftButton;
}

sub nextGeneration() {
    my $self = shift;
    my(@cells) = @{$self->{'cells'}};
    my $current = $self->{'current'};

    for(my $i = 1; $i <= $MAXSIZE; $i++) {
	for (my $j = 1; $j <= $MAXSIZE; $j++ ) {
	    my $t = $cells[$current][$i - 1][$j - 1]
	    + $cells[$current][$i - 1][$j]
	    + $cells[$current][$i - 1][$j + 1]
	    + $cells[$current][$i][$j - 1]
	    + $cells[$current][$i][$j + 1]
	    + $cells[$current][$i + 1][$j - 1]
	    + $cells[$current][$i + 1][$j]
	    + $cells[$current][$i + 1][$j + 1];

	    $cells[!$current][$i][$j] = ( $t == 3 ||
				      $t == 2 && $cells[$current][$i][$j]);
	}
    }
    $self->{'current'} = !$current;
    $self->repaint(0);
}

sub paintEvent {
    my $self = shift;
    my $cells = $self->{'cells'};
    my $e = shift;
    my $starti = pos2index($e->rect()->left());
    my $stopi  = pos2index($e->rect()->right());
    my $startj = pos2index($e->rect()->top());
    my $stopj  = pos2index($e->rect()->bottom());

    $stopi = $self->{'maxi'} if $stopi > $self->{'maxi'};
    $stopj = $self->{'maxj'} if $stopj > $self->{'maxj'};

    my $paint = Qt::Painter->new($self);
    for(my $i = $starti; $i <= $stopi; $i++) {
	for(my $j = $startj; $j <= $stopj; $j++) {
#	    print "getting [$self->{'current'}][$i][$j]\n";
	    if($cells->[$self->{'current'}][$i][$j]) {
		my $ipos = index2pos($i);
		my $jpos = index2pos($j);
		$paint->drawShadePanel($ipos, $jpos,
				$SCALE - 1, $SCALE - 1,
$self->colorGroup());
#,0,1,undef);
	    } elsif($cells->[!$self->{'current'}][$i][$j]) {
		$paint->eraseRect(index2pos($i), index2pos($j),
				 $SCALE - 1, $SCALE - 1);
	    }
	}
    }
    $self->drawFrame($paint);
}