# Author:  Chao-Kuei Hung
# For more info, including license, please see doc/index.html

package RecDialog;

use strict;
use Carp;
use RecCanvas;
use base qw(Tk::Toplevel);

# see perldoc Tk::mega and perldoc Tk::composite
Construct Tk::Widget 'RecDialog';

sub Populate {
    my ($self, $args) = @_;

    $self->{"#canvas"} = delete $args->{-canvas};
    my ($lowest, $c);
    foreach $c (@{ $self->{"#canvas"} }) {
	$lowest = $c if (not defined $lowest or
	    $c->cget(-elevation) < $lowest->cget(-elevation) );
	$c->configure(-coordinator=>$self);
    }
    $self->{"#lowest_canvas"} = $lowest;
#    my (@ml) = $ml ? (-maxlevel=>$ml) : ();
#    my ($buttons) = delete $args->{-buttons};
    $self->{"#slevel"} = (delete $args->{-slevel} or 0); # scale level

    $self->SUPER::Populate($args);
    $self->Component("Frame", "menubar", -relief=>"raised", -bd=>2);
    $self->Subwidget("menubar")->pack(-side=>"top", -fill=>"x");
    $self->Component("Frame", "statusbar", -relief=>"sunken", -bd=>2);
    $self->Subwidget("statusbar")->pack(-side=>"bottom", -fill=>"x");
    $self->Component("Frame", "timebar", -relief=>"sunken", -bd=>2);
    $self->Subwidget("timebar")->pack(-side=>"bottom", -fill=>"x");
#    my ($c) = $self->Scrolled("RecCanvas", -scrollbars=>"osow", @ml);
#    $self->Advertise("worksp"=>$c);
#    $self->Subwidget("worksp")->pack(-side=>"top", -fill=>"both",
#	-expand=>"yes");

    $self->Subwidget("timebar")->Component("Scale", "timeknob",
	-orient=>"horizontal", -showvalue=>0, # -width=>"3m",
	-command=>sub { $self->timeknob_seek($_[0]); }
    );
    $self->Subwidget("timebar")->Subwidget("timeknob")->pack(-side=>"right",
	-expand=>1, -fill=>"both");
    $self->Subwidget("timebar")->Component("Label", "clock",
	-width=>$self->{"#slevel"}*6+6);
#        -textvariable=>\${ $c->Subwidget("reccanvas") }{"#now"} );
    $self->Subwidget("timebar")->Subwidget("clock")->pack(-side=>"left", -fill=>"both");

#    $self->Delegates(DEFAULT => $self->Subwidget("worksp"));
    $self->ConfigSpecs(
	-recorder=>[qw(METHOD recorder Recorder 0)],
	-clockstring=>[qw(PASSIVE clockstring ClockString), \&_clock_string_],
	-bg=>[qw(DESCENDANTS background Background)],
#	DEFAULT=>[$self->Subwidget("worksp")]
    );
}

sub incr_other_canvases {
    my ($self, $initiator) = @_;
    my ($rc);
    $initiator = $initiator->cget(-name);
    foreach $rc (@{ $self->{"#canvas"} }) {
	# next if $rc eq $initiator;
	# wrong! $rc might also be the scrolled version of $initiator
	next if $rc->cget(-name) eq $initiator;
	$rc->no_op();
    }
}

sub mark_other_canvases {
    my ($self, $initiator, $absolute_level) = @_;
    my ($rc);
    $initiator = $initiator->cget(-name);
    foreach $rc (@{ $self->{"#canvas"} }) {
	# next if $rc eq $initiator;
	# wrong! $rc might also be the scrolled version of $initiator
#my ($t) = $rc->Subwidget("scrolled");
#print " [elev", $t->{-elevation}, "] : $t->{'#now'}";
	next if $rc->cget(-name) eq $initiator;
	$rc->set_mark($absolute_level - $rc->cget(-elevation), "final");
    }
#print "\n";
}

sub seek_bkwd_at_level {
    my ($self, $level) = @_;
    my ($tk, $rc, $pos);
#    my ($rc) = $self->Subwidget("worksp")->Subwidget("reccanvas");
    $tk = $self->Subwidget("timebar")->Subwidget("timeknob");
    $rc = $self->{"#lowest_canvas"};
    $pos = $rc->find_backward_stop($level);
    $tk->set($rc->get_mark_at($pos, $self->{"#slevel"}));
    $tk->update();
    # no need to $rc->time_seek -- $tk->timeknob_seek will be
    # automatically invoked and will do just that
    $self->_update_status_();
}

sub seek_fwd_at_level {
    my ($self, $level) = @_;
    my ($tk, $rc, $pos);
    $tk = $self->Subwidget("timebar")->Subwidget("timeknob");
    $rc = $self->{"#lowest_canvas"};
    $pos = $rc->find_forward_stop($level);
    $tk->set($rc->get_mark_at($pos, $self->{"#slevel"}));
    $tk->update();
    # no need to $rc->time_seek -- $tk->timeknob_seek will be
    # automatically invoked and will do just that
    $self->_update_status_();
}

sub timeknob_seek {
    my ($self, $target) = @_;
    my ($tk, $rc);
    $tk = $self->Subwidget("timebar")->Subwidget("timeknob");
    foreach $rc (@{ $self->{"#canvas"} }) {
	$rc->time_seek($rc->find_position($self->{"#slevel"}, $target));
    }
    $tk->set($target) if ($target != $tk->get()); # avoid deep recursion
    $tk->update();
    $self->_update_status_();
}

sub _clock_string_ {
    my ($self) = @_;
    my ($r, $s, $rc, $now);
    $rc = $self->{"#canvas"}[0];
    $now = [ $rc->get_mark() ];
    $s = [ $rc->relative_mark($now) ];
#    $now = [ reverse @$now ]; pop @$now;
    $s = [ reverse @$s ]; pop @$s;
    return join(".", @$s) # . "\n" . join(".", @$now);
#    foreach $rc (@{ $self->{"#canvas"} }) {
#	$now = [ $rc->get_mark() ];
#	$s = [ reverse $rc->relative_mark($now) ];
##print "($now)";
##	$s = join ".", $rc->get_mark_at($now);
#	push @$r, join ".", @$s; #@{$s}[1..$#$s];
#    }
#    return join "\n", @$r;
}

sub _update_status_ {
    my ($self) = @_;
    $self->Subwidget("timebar")->Subwidget("clock")->configure(
	-text => $self->{Configure}{-clockstring}->($self)
    );
}

sub recorder {
    my ($self, $state) = @_;
    my ($tk) = $self->Subwidget("timebar")->Subwidget("timeknob");
    my ($rc) = $self->{"#lowest_canvas"};
    my ($lv) = $self->{"#slevel"};
    $tk->configure(-to=>$rc->total_marks($lv));
    $tk->set($rc->get_mark($lv));

    # $tk->update;
    # Uncomment this line in case the Scale widget gets confused
    # after the -to configuration and the set operation.
    # (Might have been just an artifact of bugs in my old code.) 

    foreach $rc (@{ $self->{"#canvas"} }) {
	$rc->configure(-recorder=>$state);
    }
}

if ($0 =~ /RecDialog.pm$/) {
# being tested as a stand-alone program, so run test code.

use Tk;
my ($main, $rc, $rd, $mb);
$main = MainWindow->new();
$rc = $main->Scrolled("RecCanvas", -scrollbars=>"osow",
    -elevation=>4, -maxlevel=>6, -width=>300, -height=>200);
$rc->pack(-expand=>"yes", -fill=>"both");
$rd = $main->RecDialog(-slevel=>4, -canvas=>[$rc]);
$rd->protocol("WM_DELETE_WINDOW", sub { exit; });
$mb = $rd->Subwidget("menubar");
$mb->{file} = $mb->Menubutton(
    -text=>"File", -tearoff=>0, -menuitems=>[
	["command"=>"Quit", -command=>sub { exit; } ]
    ]
);
$mb->{x1} = $mb->Button(-text=>"|<-", -command=>
    sub { $rd->seek_bkwd_at_level(6); } );
$mb->{x2} = $mb->Button(-text=>"<<-", -command=>
    sub { $rd->seek_bkwd_at_level(5); } );
$mb->{x3} = $mb->Button(-text=>"<--", -command=>
    sub { $rd->seek_bkwd_at_level(4); } );
$mb->{x4} = $mb->Button(-text=>"-->", -command=>
    sub { $rd->seek_fwd_at_level(4); } );
$mb->{x5} = $mb->Button(-text=>"->>", -command=>
    sub { $rd->seek_fwd_at_level(5); } );
$mb->{x6} = $mb->Button(-text=>"->|", -command=>
    sub { $rd->seek_fwd_at_level(6); } );
$mb->{file}->pack(@{$mb}{qw(x1 x2 x3 x4 x5 x6)}, -side=>"left");

my ($i, $j, $block);
for ($i=0; $i<4; ++$i) {
    for ($j=0; $j<4; ++$j) {
	$block->[$i][$j] = $rc->createRectangle(
	    $j*40+20, $i*30+15, $j*40+40, $i*30+30);
	$rc->itemconfigure($block->[$i][$j], -outline=>"hidden");
    }
}
$rd->configure(-recorder=>1);
for ($i=0; $i<4; ++$i) {
    for ($j=0; $j<4; ++$j) {
	$rc->itemconfigure($block->[$i][$j], -outline=>"cyan");
	$rc->itemconfigure($block->[$i][$j], -outline=>"yellow");
	$rc->itemconfigure($block->[$i][$j], -outline=>"magenta");
	$rc->set_mark(0);
    }
    $rc->set_mark(1);
}
$rd->configure(-recorder=>0);
MainLoop();

}

1;

