The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Tk::DragDrop::XDNDDrop;
use strict;
use vars qw($VERSION);
$VERSION = sprintf '4.%03d', q$Revision: #6 $ =~ /\D(\d+)\s*$/;
use base qw(Tk::DragDrop::Rect);

sub XDND_PROTOCOL_VERSION () { 4 }

Tk::DragDrop->Type('XDND');

sub NewDrag
{
 my ($class,$token) = @_;
 $token->{$class} = {};
}

sub new
{
 my ($class,$token,$id,@prop) = @_;
 my $ver = $token->InternAtom(shift(@prop));
 # warn "XDND version $ver ".join(' ',@prop)."\n";
 $ver = XDND_PROTOCOL_VERSION if $ver > XDND_PROTOCOL_VERSION;
 my $site = bless { id => $id, token => $token, ver => $ver, state => 0, accept => \@prop}, $class;
 my $w = $token->parent;
 $w->BindClientMessage('XdndStatus',[$site => 'XdndStatus']);
 $w->BindClientMessage('XdndFinished',[$site => 'XdndFinished']);
 return $site;
}

sub Drop
{
 my ($site,$token,$seln,$e) = @_;
 my $w   = $token->parent;
 my $data = pack('LLLLL',oct($w->id),0,$e->t,0,0);
 $w->SendClientMessage('XdndDrop',$site->{id},32,$data);
}

sub FindSite
{
 my ($class,$token,$X,$Y) = @_;
 my $id = $token->PointToWindow($X,$Y);
 while ($id)
  {
   my @prop;
   Tk::catch { @prop = $token->property('get','XdndAware', $id) };
   if (!$@ && shift(@prop) eq 'ATOM')
    {
     my $hash = $token->{$class};
     my $site = $hash->{$id};
     if (!defined $site)
      {
       $site = $class->new($token,$id,@prop);
       $hash->{$id} = $site;
      }
     return $site;
    }
   $id = $token->PointToWindow($X,$Y,$id)
  }
 return undef;
}

sub Enter
{
 my ($site,$token,$e) = @_;
 my $w   = $token->parent;
 $token->InstallHandlers('XdndSelection');
 my $seln = $token->cget('-selection');
 my @targets = grep(!/^(TARGETS|MULTIPLE|TIMESTAMP)$/,reverse($token->SelectionGet('-selection'=> 'XdndSelection','TARGETS')));
 # print join(' ',@targets),"\n";
 my $flags   = ($site->{ver} << 24);
 my @atarg   = map($token->InternAtom($_),@targets);
 my $ntarg   = @atarg;
 if ($ntarg > 3)
  {
   $flags |= 1;
   $w->property('set','XdndTypeList','ATOM',32,\@atarg);
   splice(@atarg,3);
  }
 else
  {
   splice(@atarg,$ntarg,(0 x 3 - $ntarg));
  }
 unshift(@atarg,oct($w->id),$flags);
 # print join(' ',map(sprintf("%08X",$_),@atarg)),"\n";
 my $data = pack('LLLLL',@atarg);
 $w->SendClientMessage('XdndEnter',$site->{id},32,$data);
}

sub Leave
{
 my ($site,$token,$e) = @_;
 my $w   = $token->parent;
 my $data = pack('LLLLL',oct($w->id), 0, 0, 0, 0);
 $w->SendClientMessage('XdndLeave',$site->{id},32,$data);
}

sub Motion
{
 my ($site,$token,$e) = @_;
 my $X = $e->X;
 my $Y = $e->Y;
 my $w   = $token->parent;
 my $action = $token->InternAtom($site->{'action'} || 'XdndActionCopy');
 my @atarg = (oct($w->id),0,($X << 16) | $Y, $e->t, $action);
 # print join(' ',map(sprintf("%08X",$_),@atarg)),"\n";
 my $data = pack('LLLLL',@atarg);
 $w->SendClientMessage('XdndPosition',$site->{id},32,$data);
}

sub XdndFinished
{
 my ($site) = @_;
 my $token = $site->{token};
 # printf "XdndFinished $site\n",
 $token->Done;
}

sub XdndStatus
{
 my ($site) = @_;
 my $token = $site->{token};
 my $w   = $token->parent;
 my $event = $w->XEvent;
 my ($tid,$flags,$xy,$wh,$action) = unpack('LLLLL',$event->A);
 $action = $w->GetAtomName($action) if $action;
 $site->{flags} = $flags;
 $site->{'X'}   = $xy >> 16;
 $site->{'Y'}   = $xy & 0xFFFF;
 $site->{'width'}  = $wh >> 16;
 $site->{'height'} = $wh & 0xFFFF;
 #printf "XdndStatus $site targ=%x flags=%08X x=%d y=%d w=%d h=%d a=%s\n",
 #        $tid,$flags,$xy >> 16, $xy & 0xFFFF, $wh >> 16, $wh & 0xFFFF,$action;
 if ($flags & 1)
  {
   $token->AcceptDrop;
  }
 else
  {
   $token->RejectDrop;
  }
}


1;
__END__