# # $Id: CardPile.pm,v 4.9 2002/07/21 06:49:52 kono Exp $ # package CardPlay::CardPile; use strict; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $CardPileTag ); require Exporter; require AutoLoader; @ISA = qw(Exporter AutoLoader CardPlay::SimpleCardPile ); @EXPORT = qw( ); $VERSION = "0.2"; use Tk; use Carp; use CardPlay::Card; use CardPlay::SimpleCardPile; use CardPlay::CardPileUndo; # use CardPlay::CardBoard; $CardPlay::CardPileTag = 'cardpile000'; sub new { my $type = shift; my %arg = @_; my $self; $self = CardPlay::SimpleCardPile->new(@_); defined($arg{'-tag'}) or $self->{'-tag'} = $CardPlay::CardPileTag++; defined($arg{'-deltax'}) or $self->{'-deltax'} = 0; defined($arg{'-deltay'}) or $self->{'-deltay'} = $CardPlay::Card::deltax; defined($arg{'-contents'}) or $self->{'-contents'} = []; $self->{'-bind'} = {}; bless $self; } sub bind { my ($self) = shift; my (%args) = @_; my ($c) = $self->{'-canvas'}; my ($tag) = $self->{'-tag'}; my ($bind) = $self->{'-bind'}; if ($#_ == -1) { # print STDERR "binding...\n"; foreach my $key ( keys %{$bind} ) { $c->bind($tag, $key => $bind->{$key}); } } else { foreach my $key ( keys %args ) { if (defined( $args{$key} )) { $bind->{$key} = $args{$key}; } } } } sub movable { my ($self) = shift; my ($c) = $self->{'-canvas'}; $self->bind('<1>' => sub {$self->plot_down(@_)}); # $self->bind('<2>' => sub {$self->hide(@_)}); # $self->bind('<3>' => sub {$self->open(@_)}); $self->bind('<ButtonRelease-1>' => sub {shift->dtag('selected')}); $self->bind('<B1-Motion>' => sub {$self->plot_move(@_)}); $self->bind(); } sub unbind { my ($self) = shift; my ($c) = $self->{'-canvas'}; my ($tag) = $self->{'-tag'}; foreach my $key ( keys %{$self->{'-bind'}} ) { $c->bind($tag, $key => ""); } } sub display { my ($self,%args) = @_; $self->merge_args(%args); my ($tag) = $self->{'-tag'}; my ($x) = defined($self->{'-x'})?$self->{'-x'}:0; my ($y) = defined($self->{'-y'})?$self->{'-y'}:0; my ($dx) = $self->{'-deltax'}; my ($dy) = $self->{'-deltay'}; my ($xx,$yy); my ($ctag,$basetag); my ($first) = 0; my ($c) = $self->{'-canvas'} or carp "needs -canvas\n"; if (defined( $self->{'-base'} )) { if (defined( $self->{'-basetag'} )) { ($xx,$yy) = $c->coords($self->{'-basetag'}); if($xx != $x || $yy != $y) { $c->move($self->{'-basetag'},$x-$xx,$y-$yy); } } else { $self->{'-basetag'} = $basetag = $c->create('rectangle',$x,$y,$x+$CardPlay::Card::cx,$y+$CardPlay::Card::cy); $c->addtag($tag, withtag => $basetag); } } foreach my $i ( @{$self->{-contents}} ) { $first = $i if (! $first); if (defined( $i->{'-tag'})) { ($xx,$yy) = $i->coords(); if($xx != $x || $yy != $y) { $c->move($i->{'-tag'},$x-$xx,$y-$yy); } } else { $i->display('-x'=>$x,'-y'=>$y); $c->addtag($tag, withtag => ($i->{'-tag'})); } $i->raise(); $x += $dx; $y += $dy; } # if (defined( $self->{'-basetag'} ) && $first) { # $c->lower($self->{'-basetag'},$first->{'-tag'}); # } if ($self->{'-movable'}) { $self->movable(); } elsif ($self->{'-splitable'}) { $self->splitable(); } if ($self->{'-mergeable'}) { $self->mergeable(); } if ($self->{'-auto_open'}) { $self->open(); } $self->bind(); return $self; } sub raise { my ($self) = shift; my ($c) = $self->{'-canvas'}; my ($tag) = $self->{'-tag'}; $c->raise($tag); } sub plot_down { my ($self) = shift; my ($w) = @_; my ($e) = $w->XEvent; my ($x, $y) = ($e->x, $e->y); my ($tag) = $self->{'-tag'}; $w->dtag('selected'); $w->addtag('selected', withtag => $self->{'-tag'}); $w->raise($tag); $self->{'-lastX'} = $x; $self->{'-lastY'} = $y; } # end plot_down sub plot_move { my ($self) = shift; my ($w) = @_; my $e = $w->XEvent; my ($x, $y) = ($e->x, $e->y); $w->move('selected', $x-$self->{'-lastX'}, $y-$self->{'-lastY'}); $self->{'-lastX'} = $x; $self->{'-lastY'} = $y; } # end plot_move sub hide { my ($self) = @_; my ($card) = $self->last(); if (defined($card)) { if ($card->{'-state'} eq 'open') { $card->hide(); } } } sub open { my ($self) = @_; my ($card) = $self->last(); if (defined($card)) { if ($card->{'-state'} eq 'hide') { $card->open(); } } } sub splitable { my ($self) = shift; my ($c) = $self->{'-canvas'}; my ($tag) = $self->{'-tag'}; my ($i,$j); $j = 0; foreach $i ( @{$self->{-contents}} ) { my ($jj) = $j; $c->bind($i->{'-tag'}, '<1>' => sub {$self->split_event($jj,@_)}); $j++; } # $self->bind('<2>' => sub {$self->hide(@_)}); # $self->bind('<3>' => sub {$self->open(@_)}); # $self->bind('<ButtonRelease-1>' => sub {$self->merge_event(@_)}); # $self->bind('<B1-Motion>' => sub {$self->plot_move(@_)}); $self->bind(); } sub unsplitable { my ($self) = shift; my ($c) = $self->{'-canvas'}; my ($tag) = $self->{'-tag'}; my ($i); foreach $i ( @{$self->{-contents}} ) { $c->bind($i->{'-tag'}, '<1>' => ""); } $self->unbind(); } sub clear { my ($self) = @_; my ($card); foreach $card ( @{$self->{'-contents'}} ) { $card->clear; } $self->SUPER::clear(); $self->unbind(); # $self->{'-canvas'}->delete($self->{'-tag'}); if (defined($self->{'-mergeable'})) { $self->{'-board'}->delete_drag($self); } } sub DESTROY { my ($self) = @_; $self->unbind(); $self->{'-canvas'}->dtag($self->{'-tag'}); if (defined($self->{'-mergeable'})) { $self->{'-board'}->delete_drag($self); } 1; } sub mergeable { my ($self) = shift; $self->{'-mergeable'}=1; $self->{'-board'}->add_drag($self); } sub merge { my ($self,$cardpile) = @_; return if ($cardpile==0); my ($contents) = $cardpile->{'-contents'}; my ($c) = $self->{'-canvas'}; if (defined($self->{'-reverse_merge'})) { $cardpile->reverse(); } if (! $c ) { push(@{$self->{-contents}},@{$cardpile->{-contents}}); $cardpile=''; return; } $c->addtag($self->{'-tag'}, withtag => $cardpile->{'-tag'}); $c->dtag($cardpile->{'-tag'},$cardpile->{'-tag'}); push(@{$self->{-contents}},@{$cardpile->{-contents}}); $self->display(); $cardpile = ''; # if ($self-> {'-splitable'}) { # this has been done by display # $self->splitable(); # } } sub merge_event { my ($self,$w) = @_; my ($e) = $w->XEvent; my ($x, $y) = ($e->x, $e->y); my ($draggedPile); my ($c) = $self->{'-canvas'}; # print STDERR "merge_event ";$self->print; if (! $self->{'-prevSplit'}) { # print STDERR "reenter merge_event..\n"; return; } if (defined($self->{'-board'})) { return 0 if ($self->{'-board'}->{'-auto_playing'}) ; $self->{'-board'}->log_commit(0); } $w->dtag('selected'); my $cx = $c->canvasx($x); my $cy = $c->canvasy($y); if (defined(($draggedPile = $self->{'-board'}->check_drag($cx,$cy)))) { # print STDERR "drag on ";$draggedPile->print; if (! $draggedPile->can('can_merge')) { $draggedPile->do_merge($self); $draggedPile->auto_play(); return; } # before checking prevSplit==$self, try intelligent move my $ret = $draggedPile->can_merge($self); if ($ret == 1) { # yes we can merge if ($draggedPile eq $self->{'-prevSplit'}) { # but this is myself $self->unsplit(); return; } $draggedPile->do_merge($self); $draggedPile->auto_play(); return; } elsif ($ret == 2) { # can_merge done everything forget unsplit $self->{'-prevSplit'}=0; $draggedPile->auto_play(); return; } # go into unsplit } $self->unsplit(); } sub auto_play { my ($self) = @_; my ($board) = $self->{'-board'}; return if (! $board); if ($board->{'-auto_play'} && $board->can('auto_play') && ! $board->{'-auto_playing'}) { $board->{'-auto_playing'}=1; $board->auto_play(); $board->{'-auto_playing'}=0; } } sub do_merge { my ($self,$cardpile) = @_; # merge with undo log and -auto_open processing my ($prevSplit) = $cardpile->{'-prevSplit'}; # usually self merge is not allowed but we don't prohibit it here $self->merge($cardpile); $self->mergeable() if ($self->{'-mergeable'}); if (defined($self->{'-board'})) { $self->{'-board'}->log_move( CardPlay::CardPileUndo->new( -from=>$prevSplit, -to=>$self, -at=> - ($#{$cardpile->{'-contents'}}+1))); } $cardpile->{'-prevSplit'}=0; if ($prevSplit->{'-auto_open'}) { $prevSplit->open(); } return; } sub split { my ($self,$i) = @_; my ($new); my ($tag,$j); my ($x,$y); my ($c) = $self->{'-canvas'}; my (@contents); if ($i>=0) { @contents = splice(@{$self->{'-contents'}},$i); } else { @contents = splice(@{$self->{'-contents'}},$#{$self->{'-contents'}}+$i+1); } return 0 if (! @contents); $new = CardPlay::CardPile->new(-contents=>\@contents,-canvas=>$c); $new->{'-deltax'} = $self->{'-deltax'}; $new->{'-deltay'} = $self->{'-deltay'}; if (defined($self->{'-reverse_merge'})) { $new->reverse(); } return $new if (! defined($c)); if (defined($self->{'-board'})) { $new->{'-board'} = $self->{'-board'}; } # push event traces $self's tag, so exchage it $self->unbind(); ($self->{'-tag'},$new->{'-tag'}) = ($new->{'-tag'},$self->{'-tag'}); foreach $j ( @{$self->{'-contents'}} ) { $tag = $j->{'-tag'}; $c->addtag($self->{'-tag'},withtag=>$tag); } $c->dtag($self->{'-tag'},$new->{'-tag'}); if ($self->{'-basetag'}) { $c->dtag($self->{'-basetag'},$new->{'-tag'}); $c->addtag($self->{'-tag'},withtag=>$self->{'-basetag'}); } $self->bind(); if ($self->{'-splitable'}) { $new->unsplitable(); } return $new; } sub split_event { my ($self,$i,$w) = @_; # print STDERR "split_event ";$self->print; my ($e) = $w->XEvent; my ($lx, $ly) = ($e->x, $e->y); if (defined($self->{'-board'})) { return 0 if ($self->{'-board'}->{'-auto_playing'}) ; } return if ($self->{'-prevSplit'}); # reentering... return if ($self->can('can_split') && ! $self->can_split($i)); # print STDERR "splited... ",$new->print; my ($new) = $self->split($i); return if (! $new); $new->{'-prevSplit'} = $self; $new->{'-lastX'} = $lx; $new->{'-lastY'} = $ly; $new->movable(); $w->bind($new->{'-tag'}, '<ButtonRelease-1>' => sub {$new->merge_event(@_)}); $w->dtag('selected'); $w->addtag('selected',withtag => $new->{-tag}); $w->raise($new->{-tag}); } sub do_split { my ($self,$i) = @_; my ($new) = $self->split($i); return if (! $new); $new->{'-prevSplit'} = $self; $new->raise() if (defined($self->{'-contents'})); return $new; } sub unsplit { my ($self) = @_; my ($prevSplit) = $self->{'-prevSplit'}; if ($prevSplit) { # print STDERR "unsplit ",$prevSplit->print," merged ",$self->print; $prevSplit->merge($self); $self->{'-prevSplit'} = 0; } else { print STDERR "lost prevSplit\n"; } # we should destory myself } sub coords { my ($self) = @_; my ($first) = $self->first(); my ($x,$y); if (defined($first)) { ($x,$y) = $first->coords(); return ($x,$y); } elsif (defined($self->{'-basetag'})) { ($x,$y) = $self->{'-canvas'}->coords($self->{'-basetag'}); return ($x,$y); } return (undef,undef); } sub last_coords { my ($self) = @_; my ($last) = $self->last(); my ($x,$y); if (defined($last)) { ($x,$y) = $last->coords(); return ($x,$y); } elsif (defined($self->{'-basetag'})) { ($x,$y) = $self->{'-canvas'}->coords($self->{'-basetag'}); return ($x-$self->{'-deltax'},$y-$self->{'-deltay'}); } return (undef,undef); } sub bbox { my ($self) = @_; return $self->{'-canvas'}->bbox($self->{'-tag'}); } 1; # end