/usr/local/CPAN/Tk-MDI/Tk/MDI/ChildWindow.pm


package Tk::MDI::ChildWindow;

use strict;

my $unfocusedTitleBg = 'lightgray';      # bg color when no focus
my $focusedTitleBg   = 'papayawhip';       # default color when focused

my %changeCursorHash = 
	(
		n  => [qw/size_ns    top_side/],
		w  => [qw/size_we   left_side/],
		e  => [qw/size_we  right_side/],
		s  => [qw/size_ns bottom_side/],

		nw => [qw/size_nw_se     top_left_corner/],
		se => [qw/size_nw_se bottom_right_corner/],
		sw => [qw/size_ne_sw  bottom_left_corner/],
		ne => [qw/size_ne_sw    top_right_corner/],
	);

sub new {
	my $self  = shift;
	my $class = ref($self) || $self;

	my $obj = bless {} => $class;

	my %args  = @_;
	$obj->{PARENT}    = delete $args{-parent} or die "No Parent Frame";
	$obj->{PARENTOBJ} = delete $args{-parentobj} or die "No Parent Object";
	$obj->{NAME}     = $args{-name};
	$obj->{FOCUSBG}   = $args{-titlebg} || $focusedTitleBg;
	$obj->{MINFRAME}  = $args{-minframe};

	$obj->{EXTBD} = $obj->{PARENTOBJ}->_getExtBD;
	$obj->{INTBD} = $obj->{PARENTOBJ}->_getIntBD;
	$obj->{MINIMIZEDHEIGHT}=23;
	$obj->{MINIMIZEDWIDTH}=120;
	$obj->{CASCADEHEIGHT}=400;
	$obj->{CASCADEWIDTH}=500;

	my $x = delete $args{-x} || 0;
	my $y = delete $args{-y} || 0;
	$x=$y=0 unless ($x == $y);
	my $h = delete $args{-height} || $obj->{CASCADEHEIGHT};
	my $w = delete $args{-width} || $obj->{CASCADEWIDTH};

	$obj->{EXTERNAL} = $obj->{PARENT}->Frame(
				-relief=>'raised',
				-bd=>$obj->{EXTBD}
				)->place(-x=>$x,-y=>$y,-width=>$w,-height=>$h);

	$obj->{INTERNAL} = $obj->{EXTERNAL}->Frame(
				-relief=>'flat',
				-bd=>$obj->{INTBD},
				)->pack(qw/-fill both -expand 1/);

	$obj->{TITLE} = $obj->{INTERNAL}->Frame(
				-relief=>'flat', 
				-height=>$obj->{MINIMIZEDHEIGHT},
				-bd=>0,
				-bg => $obj->{FOCUSBG},
				)->pack(qw/-fill x -expand 0/);

	$obj->{MAIN} = $obj->{INTERNAL}->Frame(
				-container => 1,
				)->pack(qw/-fill both -expand 1/);

	# for some reason I need an eval here to get it to work on winblowZ.
	$obj->{TOPLEVEL} = $obj->{PARENT}->Toplevel(-use => eval $obj->{MAIN}->id);

	$obj->{TITLELABEL} = $obj->{TITLE}->Label(
				-text => $obj->{NAME},
				-bg   => $obj->{FOCUSBG},
				-anchor => 'w',
				)->grid(qw/-row 0 -column 0 -sticky nesw/);

	# populate the title.
	$obj->{CLOSEBUTTON}=$obj->{TITLE}->Button(
				-image=>$obj->{PARENTOBJ}->{IMAGES}->{close},
				-command => sub { $obj->_killWindow(1) },
				)->grid(qw/-row 0 -column 3 -sticky ew -padx 2/);
                         
	# We put the Restore button directly beneath the minimize and maximize buttons.
	# This means only we only need a Tk::raise to toggle between them
	$obj->{RESTORE_FROM_MAX}=$obj->{TITLE}->Button(
				-image=>$obj->{PARENTOBJ}->{IMAGES}->{restore},
				-command => sub { $obj->_restoreFromMax },
				)->grid(qw/-row 0 -column 2 -sticky ew/);       

	$obj->{MAXBUTTON}=$obj->{TITLE}->Button(
				-image=>$obj->{PARENTOBJ}->{IMAGES}->{maximize},
				-command => sub { $obj->_maximizeWindow },
				)->grid(qw/-row 0 -column 2 -sticky ew/);

	$obj->{RESTORE_FROM_MIN}=$obj->{TITLE}->Button(
				-image=>$obj->{PARENTOBJ}->{IMAGES}->{restore},
				-command => sub { $obj->_restoreFromMin },
				)->grid(qw/-row 0 -column 1 -sticky ew/);

	$obj->{MINBUTTON}=$obj->{TITLE}->Button(
				-image=>$obj->{PARENTOBJ}->{IMAGES}->{minimize},
				-command => sub { $obj->_minimizeWindow },
				)->grid(qw/-row 0 -column 1 -sticky ew/);

	$obj->{TITLE}->gridColumnconfigure(0,-weight=>1);
	$obj->{TITLE}->raise;

	foreach my $b(qw/CLOSEBUTTON MAXBUTTON RESTORE_FROM_MIN RESTORE_FROM_MAX MINBUTTON/){
		$obj->{$b}->bind('<Enter>',[sub {$obj->{$b}->configure(qw/-relief ridge/)}]);
		$obj->{$b}->bind('<Leave>',[sub {$obj->{$b}->configure(qw/-relief raised/)}]);
	}

	$obj->{ISMAX}     = 0; # is it currently maximized?
	$obj->{ISMIN}     = 0; # is it currently minimized?
	$obj->{ISSHADED}  = 0; # is it currently shaded?
	$obj->{HASFOCUS}  = 0; # is it currently focused?
	$obj->{WASMIN}    = 0; # was the last position held a minimized position?
	$obj->{WASMAX}    = 0; # was the last position held a maximized position?
	$obj->{MYSLOT}    = 0; # Holds the current array numbers of a minimized window.

	# create default bindings.
	$obj->_createDefaultBindings;

	# Give the Toplevel an OnDestroy hook to delete the object itself!
	# This will allow the user to destroy child windows programmatically.
	# We don't want _killWindow to be executed twice if the user actually just
	# clicked on the 'X' close button - so pass a zero flag to the sub to show
	# that this OnDestroy hook was called.
	$obj->{TOPLEVEL}->OnDestroy(sub{$obj->_killWindow(0)});

	return $obj;
}

sub _checkCursorPosition
{
	my ($obj,$first)=@_;
	my ($cX,$cY)=$obj->{EXTERNAL}->pointerxy;
	my $posn = $obj->_find_location($cX,$cY);
		if ($first){
			$obj->_changeCursor($posn);
			$obj->{OLDPOSN}=$posn;
			return;
		}
		if ($obj->{OLDPOSN} ne $posn){
			$obj->_changeCursor($posn);
			$obj->{OLDPOSN}=$posn;
			return;
		}
}

sub _find_location
{
	my ($obj,$point_rootx,$point_rooty)=@_;
	my $wi=$obj->{EXTERNAL}->width;
	my $he=$obj->{EXTERNAL}->height;
	my $wx=$point_rootx-($obj->{EXTERNAL}->rootx);
	my $wy=$point_rooty-($obj->{EXTERNAL}->rooty);
	my ($x,$y)=('','');

	if ($wx<=20){
		$x='w';
	}
	elsif($wx>=$wi-20){
		$x='e';
	}
	if ($wy<=20){
		$y='n';
	}
	elsif($wy>=$he-20){
	$y='s';
	}
	return $y.$x;
}

sub _changeCursor
{
	my ($obj,$p)=@_;
	$obj->{EXTERNAL}->configure(-cursor => $changeCursorHash{$p}[$Tk::platform ne 'MSWin32']);;
}

sub _createDefaultBindings {
	my $obj = shift;

	# Toplevel focus - haven't figured these out yet!!
	$obj->{TOPLEVEL}->bind('<Enter>'       => [sub {$obj->_enterToplevel}]);
	$obj->{TOPLEVEL}->bind('<Leave>'       => [sub {$obj->_leaveToplevel}]);

	# bindings for focussing, dragging and (un)shading the title bar.
	for my $o (qw/TITLE TITLELABEL/) {
		$obj->{$o}->bind('<ButtonPress-1>'   => [sub { $obj->_button1Press }]);
		$obj->{$o}->bind('<ButtonRelease-1>' => [sub { $obj->_button1Release }]);	
		$obj->{$o}->bind('<B1-Motion>'       => [sub { $obj->_button1Drag  }]);
		$obj->{$o}->bind('<ButtonPress-3>' => [sub { $obj->_shadeWindow  }]);
	}

		# bindings for focussing, changing cursors and resizing the window.
	$obj->_bindExternal;
	$obj->{EXTERNAL}->bind('<ButtonPress-1>'    =>[sub  { $obj->_expandWinPress  }]);

	$obj->{DIRECTION} = {
			's'  =>     [0,1,0,0],
			'sw' =>     [0,1,1,0],
			'se' =>     [0,1,0,1],
			'n'  =>     [1,0,0,0],
			'nw' =>     [1,0,1,0],
			'ne' =>     [1,0,0,1],
			'w'  =>     [0,0,1,0],
			'e'  =>     [0,0,0,1],
			};
}

sub _bindExternal
{
	#this callback is invoked at the end of a window resize to reset the 
	#bindings which were cancelled at the onset of resize
	#Called from _expandWinRelease

	my $obj=shift;
	$obj->{EXTERNAL}->bind('<Enter>'=> [sub { $obj->_enterExternal }]);
	$obj->{INTERNAL}->bind('<Enter>'=> [sub { $obj->_enterInternal }]);
	$obj->{INTERNAL}->bind('<Leave>'=> [sub { $obj->_enterExternal }]);
	$obj->{EXTERNAL}->bind('<Leave>'=> [sub { $obj->_leaveExternal }]);
}

sub _unbindExternal
{
	#This callback stops all bindings to the window being resized. This is only
	#temporary - as the bindings are replaced at the end of a resize. Why do this?
	#Because sometimes if you move the mouse too quickly enter and leave events will
	#occur during a resize - and we want neither!
	#Called from _expandWinPress
	
	my $obj=shift;
	$obj->{EXTERNAL}->bind('<Enter>'=> '');
	$obj->{INTERNAL}->bind('<Enter>'=> '');
	$obj->{INTERNAL}->bind('<Leave>'=> '');
	$obj->{EXTERNAL}->bind('<Leave>'=> '');
}

sub _leaveExternal
{
	my $obj=shift;
	if ($obj->{PARENTOBJ}->cget(-focus) eq 'strict'){
		$obj->_unfocusIt;
		$obj->{PARENTOBJ}->_unfocusMe($obj->{NAME});
	}

	$obj->{EXTERNAL}->configure(-cursor => 'left_ptr');
	$obj->{EXTERNAL}->bind('<Motion>','');
}

sub _enterExternal
{
	my $obj=shift;
	$obj->_focusIt if ($obj->{PARENTOBJ}->cget(-focus) ne 'click');
	$obj->_checkCursorPosition(1);  
	$obj->{EXTERNAL}->bind('<Motion>',[sub{$obj->_checkCursorPosition(0)}]);
}

sub _enterInternal
{
	my $obj=shift;
	$obj->{EXTERNAL}->configure(-cursor => 'left_ptr');
	$obj->{EXTERNAL}->bind('<Motion>','');
}

sub _enterToplevel
{
	my $obj = shift;

	if ($obj->{PARENTOBJ}->cget(-focus) ne 'click'){
		$obj->_focusIt unless ($obj->{HASFOCUS});
		#foreach my $child ($obj->{TOPLEVEL}->children){
		#       print "CHILD: $child\n";
		#       $child->bind('<Enter>',sub{$child->Tk::EnterFocus});
		#}
	}
	else{
		$obj->{TOPLEVEL}->bind('<ButtonPress-1>' => [sub {$obj->_focusIt}]);
	}
}

sub _leaveToplevel
{
	# my $obj = shift;
	#  $obj->{TOPLEVEL}->bind('<ButtonPress-1>' => '');
}

sub _expandWinPress 
{
	my $obj = shift;

	$obj->{PARENTOBJ}->_win32confineCursor if ($obj->{PARENTOBJ}->_confineMethod eq "win32");
	$obj->_focusIt;

	my @dir = @{$obj->{DIRECTION}->{$obj->{OLDPOSN} }};

	my ($x, $y)   = $obj->{PARENT}->pointerxy;
	my $w = $obj->{EXTERNAL}->width;
	my $h = $obj->{EXTERNAL}->height;

	my $sx = $obj->{EXTERNAL}->x();
	my $sy = $obj->{EXTERNAL}->y();
	$obj->{TEMP} = [$x, $y, $sx, $sy, $w, $h];
	
	$obj->_setShadow($sx,$sy) if ($obj->{PARENTOBJ}->cget(-shadow));
	$obj->_unbindExternal;
	$obj->{EXTERNAL}->bind('<Motion>','');
	
	$obj->{PARENTOBJ}->bind('all','<Motion>',[sub {$obj->_expandWinDrag(@dir,0)}]);
	$obj->{PARENTOBJ}->bind('all','<ButtonRelease-1>',[sub {$obj->_expandWinRelease(@dir,1)}]);

	#Turn off shaded feature as you are resizing
	$obj->{ISSHADED}=0;
}

sub _expandWinRelease {
	my $obj = shift;
	$obj->{PARENTOBJ}->bind('all','<Motion>','');
	$obj->_expandWinDrag(@_);
	$obj->{PARENTOBJ}->_win32releaseCursor if ($obj->{PARENTOBJ}->_confineMethod eq "win32");

	$obj->{PARENTOBJ}->bind('all','<ButtonRelease-1>','');	  
	$obj->_bindExternal;
}

sub _expandWinDrag {
	my ($obj, $top, $bottom, $left, $right, $final) = @_;
	my $l = $obj->{TEMP};

	$obj->_warpToConfine if ($obj->{PARENTOBJ}->_confineMethod eq "unix");
		
	my ($x, $y) = $obj->{PARENT}->pointerxy;
		#warn "NOW $x: $y";
	my $dw = my $dh = my $dx = my $dy = my $YLimit = my $XLimit = 0;

	if ($top) {
		$dh -= $y - $l->[1];
		$dy += $y - $l->[1];
		$YLimit = $l->[3]+$l->[5]-$obj->{MINIMIZEDHEIGHT};
	}
	elsif ($bottom) {
		$dh += $y - $l->[1];
	}
	if ($left) {
		$dw -= $x - $l->[0];
		$dx += $x - $l->[0];
		$XLimit = $l->[2]+$l->[4]-$obj->{MINIMIZEDWIDTH};
	}
	elsif ($right) {
		$dw += $x - $l->[0];
	}

	my $cX = $l->[2]+$dx;
	$cX = $XLimit if ( $left && $cX > $XLimit);

	my $cY = $l->[3]+$dy;
	$cY = $YLimit if ( $top && $cY > $YLimit);

	my $cH = $l->[5]+$dh;
	$cH = $obj->{MINIMIZEDHEIGHT} if ($cH < $obj->{MINIMIZEDHEIGHT});

	my $cW = $l->[4]+$dw;
	$cW = $obj->{MINIMIZEDWIDTH} if ($cW < $obj->{MINIMIZEDWIDTH}); 

	if ($obj->{PARENTOBJ}->cget(-shadow) && not $final){
		$obj->_resizeShadow($cX,$cY,$cW,$cH);
	}
	else{
		$obj->{EXTERNAL}->place(
				-x => $cX,
				-y => $cY,
				-width  => $cW,
				-height => $cH,
				);
		$obj->_hideShadow if ($obj->{PARENTOBJ}->cget(-shadow));
	}       
}

sub _shadeWindow {
	my $obj = shift;
	return if ($obj->{ISMIN});
	return if ($obj->{ISMAX});

	if ($obj->{ISSHADED}) {
		# it is shaded. Unshade it.
		$obj->{EXTERNAL}->place(
				-height => $obj->{ISSHADED}
				);
		$obj->{ISSHADED} = 0;
	}
	else {
		$obj->{ISSHADED} = $obj->{EXTERNAL}->height;
		$obj->{EXTERNAL}->place(
				-height => $obj->{MINIMIZEDHEIGHT}
				);
	}
}

sub _killWindow {
	my ($obj,$flag) = @_;

	# If $flag is an affirmative boolean then the 'X' button was clicked.
	# In this case we make sure the OnDestroy hook gets cancelled - so 
	# this subroutine does not get called twice.

	$obj->{TOPLEVEL}->OnDestroy([sub{}]) if ($flag);

	$obj->{PARENTOBJ}->IwasUnMaximized($obj) if ($obj->{ISMAX});# turn off max flag in MDI.pm
	$obj->{PARENTOBJ}->IwasUnMinimized($obj) if ($obj->{ISMIN});# open the minimized slot
	$obj->{PARENTOBJ}->_destroyMe($obj); # bye bye world!
	$obj->{EXTERNAL}->destroy;
	undef $obj;
}

sub _minimizeWindow
{
	my $obj = shift;

	if ($obj->{ISMIN}){
		$obj->_unfocusIt;
		return;
	}
	# back-up placement info only if coming from an UN-maximized position.
	unless ($obj->{ISMAX}){
		my %info = $obj->{EXTERNAL}->placeInfo;
		$obj->{SAVEDINFO}=\%info;
	}
	# force a button change..
	$obj->{RESTORE_FROM_MIN}->raise($obj->{MINBUTTON});
	$obj->{MAXBUTTON}->raise($obj->{RESTORE_FROM_MAX});

	# if minimized from a maximised position?
	if ($obj->{ISMAX}){
		$obj->{PARENTOBJ}->IwasUnMaximized($obj);
	}

	my ($r,$c) = $obj->{PARENTOBJ}->_findMinimizeSlot($obj);
	my $h = $obj->{PARENT}->height;

	# disallow resize by removing the borders
	$obj->{EXTERNAL}->configure(-bd=>0);
	$obj->{INTERNAL}->configure(-bd=>0);
	$obj->_unfocusIt;
	#now show ONLY the titlebar
	$obj->{EXTERNAL}->placeForget;
	$obj->{EXTERNAL}->place(
				-relx=>$c/5,
				-rely => 1,
				-y    => -($r)*($obj->{MINIMIZEDHEIGHT}-$obj->{EXTBD}-$obj->{INTBD}),
				-height=>$obj->{MINIMIZEDHEIGHT}-$obj->{EXTBD}-$obj->{INTBD},
				-relwidth=>0.2,
				);

	$obj->{MYSLOT} = $r.'~'.$c;
	$obj->{ISMAX}  = 0;
	$obj->{ISMIN}  = 1;
	$obj->{WASMIN} = 0;
	#$obj->{PARENTOBJ}->IwasMinimized($obj);
}

sub _restoreFromMin
{
	# Restore after a minimize..
	my $obj = shift;
	$obj->_restore;
	$obj->{PARENTOBJ}->IwasUnMinimized($obj);
	$obj->_focusIt;

}

sub _restore
{
	my $obj = shift;
	#force correct buttons...       
	$obj->{MINBUTTON}->raise($obj->{RESTORE_FROM_MIN});
	$obj->{MAXBUTTON}->raise($obj->{RESTORE_FROM_MAX});

	#allow resize by replacing the borders
	$obj->{EXTERNAL}->configure(-bd=>$obj->{EXTBD});
	$obj->{INTERNAL}->configure(-bd=>$obj->{INTBD});
	$obj->{EXTERNAL}->placeForget;
	$obj->{EXTERNAL}->place(%{$obj->{SAVEDINFO}});
	$obj->{ISMIN} = 0;
	$obj->{ISMAX} = 0;
	$obj->{WASMIN}= 0;      
}

sub _restoreFromMax
{
	my $obj = shift;
	$obj->_restore;
	$obj->{PARENTOBJ}->IwasUnMaximized($obj);
	$obj->_focusIt;
}

sub _maximizeWindow
{
	my $obj = shift;
	if ($obj->{ISMAX}){
		$obj->_focusIt;
		return;
	}
	if ( $obj->{ISMIN} ){
		#then we are maximizing from a minimize position
		$obj->{MINBUTTON}->raise($obj->{RESTORE_FROM_MIN});
		$obj->{PARENTOBJ}->IwasUnMinimized($obj);
		$obj->{WASMIN}=1;
	}
	else {
		# we are coming from a placed position
		# back up placement info.
		my %info = $obj->{EXTERNAL}->placeInfo;
		$obj->{SAVEDINFO}=\%info;
	}

	$obj->{ISMAX} = 1;
	$obj->{ISMIN} = 0;

	#disallow resize by zeroing the borders
	$obj->{EXTERNAL}->configure(-bd=>0);
	$obj->{INTERNAL}->configure(-bd=>0);
	$obj->{RESTORE_FROM_MAX}->raise($obj->{MAXBUTTON});

	#now finally maximize it..
	$obj->{EXTERNAL}->placeForget;
	$obj->{EXTERNAL}->place(
				-in=>$obj->{PARENTOBJ}->{MAINFRAME},
				-relx=>0, -rely=>0,
				-relheight => 1.0,
				-relwidth  => 1.0,
				);
	$obj->{PARENTOBJ}->IwasMaximized($obj);
	$obj->_focusIt;
}

sub _revert
{
	my $obj = shift;
	if ($obj->{WASMIN}){
		$obj->_minimizeWindow;
	}
	else{
		$obj->_restoreFromMax;
	}
}


sub _button1Press {
	my $obj = shift;

	#disallow move on a minimized or maximized window
	return if ($obj->{ISMAX} or $obj->{ISMIN});

	$obj->{PARENTOBJ}->_win32confineCursor if ($obj->{PARENTOBJ}->_confineMethod eq "win32");
	$obj->_focusIt;
	
	my ($x, $y) = $obj->{EXTERNAL}->pointerxy;
	my %info    = $obj->{EXTERNAL}->placeInfo;
	my ($w,$h)  = ($obj->{EXTERNAL}->width,$obj->{EXTERNAL}->height);
	my $ox      = $info{'-x'};
	my $oy      = $info{'-y'};

	$obj->{TEMP} = [$x, $y, $ox, $oy, $w, $h];
	$obj->_setShadow($ox,$oy) if ($obj->{PARENTOBJ}->cget(-shadow));
}

sub _button1Release{
	my $obj = shift;
	#disallow move on a minimized or maximized window
	return if ($obj->{ISMAX} or $obj->{ISMIN});

	$obj->{PARENTOBJ}->_win32releaseCursor if ($obj->{PARENTOBJ}->_confineMethod eq "win32");
	#$obj->resizePane; # not yet implemented
	my ($xN, $yN) = $obj->{EXTERNAL}->pointerxy;
	$obj->_hideShadow if ($obj->{PARENTOBJ}->cget(-shadow));
	$obj->{PARENTOBJ}->update;
	$obj->{EXTERNAL}->place(
			-x => $obj->{TEMP}[2] + $xN - $obj->{TEMP}[0],
			-y => $obj->{TEMP}[3] + $yN - $obj->{TEMP}[1]
				);

}

sub _button1Drag {
	my $obj = shift;
	#disallow move on a minimized or maximized window
	return if ($obj->{ISMAX} or $obj->{ISMIN});

	$obj->_warpToConfine if ($obj->{PARENTOBJ}->_confineMethod eq "unix");
	my ($xN, $yN) = $obj->{EXTERNAL}->pointerxy;

	if ($obj->{PARENTOBJ}->cget(-shadow)){
		my $X = $obj->{TEMP}[2] + $xN - $obj->{TEMP}[0];
		my $Y = $obj->{TEMP}[3] + $yN - $obj->{TEMP}[1];
		$obj->_moveShadow($X,$Y);       
	}
	else {
		$obj->{EXTERNAL}->place(
				-x => $obj->{TEMP}[2] + $xN - $obj->{TEMP}[0],
				-y => $obj->{TEMP}[3] + $yN - $obj->{TEMP}[1]);
	}
}

sub _setShadow{
	my ($obj,$X,$Y) = @_;
	my $bd=$obj->{EXTBD};
	$bd=1 unless ($bd);
	my ($ts,$bs,$ls,$rs) = $obj->{PARENTOBJ}->_getShadowRefs;

	$ts->place(
		-x=>$X,
		-y=>$Y,
		-width=>$obj->{TEMP}[4],
		-height=>$bd
	);
	$bs->place(
		-x=>$X,
		-y=>$Y+$obj->{TEMP}[5]-$bd,
		-width=>$obj->{TEMP}[4],
		-height=>$bd
	);
	$ls->place(
		-x=>$X,
		-y=>$Y,
		-width=>$bd,
		-height=>$obj->{TEMP}[5]
	);
	$rs->place(
		-x=>$X+$obj->{TEMP}[4]-$bd,
		-y=>$Y,
		-width=>$bd,
		-height=>$obj->{TEMP}[5]);

	foreach ($ts,$bs,$ls,$rs){
		$_->raise;
	}
}

sub _moveShadow
{
	my ($obj,$X,$Y) = @_;
	my $bd=$obj->{EXTBD};
	$bd=1 unless ($bd);

	my ($ts,$bs,$ls,$rs) = $obj->{PARENTOBJ}->_getShadowRefs;

	$ts->place(
		-x=>$X,
		-y=>$Y
	);
	$bs->place(
		-x=>$X,
		-y=>$Y+$obj->{TEMP}[5]-$bd
	);
	$ls->place(
		-x=>$X,
		-y=>$Y
	);
	$rs->place(
		-x=>$X+$obj->{TEMP}[4]-$bd,
		-y=>$Y
	);
}

sub _resizeShadow
{
	my ($obj,$X,$Y,$w,$h)=@_;
	my $bd=$obj->{EXTBD};
	$bd=1 unless ($bd);

	my ($ts,$bs,$ls,$rs) = $obj->{PARENTOBJ}->_getShadowRefs;
	$ts->place(
		-x=>$X,
		-y=>$Y,
		-width=>$w
	);
	$bs->place(
		-x=>$X,
		-y=>$Y+$h-$bd,
		-width=>$w
	);
	$ls->place(
		-x=>$X,
		-y=>$Y,
		-height=>$h
	);
	$rs->place(
		-x=>$X+$w-$bd,
		-y=>$Y,
		-height=>$h
	);
}

sub _hideShadow
{
	my $obj = shift;
	#Hide the shadow frame
	foreach ($obj->{PARENTOBJ}->_getShadowRefs){
		$_->place(-x=>-50,-y=>-50,-width=>10,-height=>10);
	}
}

sub _menuFocus
{
	# This only gets called if someone clicked on the menu
	# to bring up the child. Standard MDI is to not only bring
	# up the window - but to maximize it if indeed others are maximized.
	# If window has focus then minimize it - but NOT if it is currently
	# maximized.

	my $obj = shift;

	return if ($obj->{ISMAX} and $obj->{HASFOCUS});
	my ($existsMaxWindow) = $obj->{PARENTOBJ}->_isWindowMaxed;

	if ($obj->{ISMIN}){
		if ($existsMaxWindow){
			$obj->_maximizeWindow;
		}
		else {
			$obj->_restoreFromMin;
		}
	}
	elsif ($obj->{HASFOCUS} and not $existsMaxWindow){
		$obj->_minimizeWindow;
	}
	else {
		if ($existsMaxWindow){
			$obj->_maximizeWindow;
		}
		else {
			$obj->_focusIt;
		}
	}
}

sub _warpToConfine
{
	my $obj = shift;
	my ($rx, $ry) = $obj->{PARENT}->pointerxy;
		#warn "$rx: $ry";
	my ($wi, $he) = ($obj->{PARENT}->width, $obj->{PARENT}->height);
	my ($rootx,$rooty)=($obj->{PARENT}->rootx,$obj->{PARENT}->rooty);
	my ($px,$py)=($rx-$rootx,$ry-$rooty);
	my $warpneeded=0;

	if ($px <= 0) {
		$px = 1;
		$warpneeded=1;
	}
	elsif ($px >= $wi) {
		$px = $wi - 1;
		$warpneeded=1;
	}

	if ($py <= 0) {
		$py = 1;
		$warpneeded=1;
	}
	elsif ($py >= $he) {
		$py = $he - 1;
		$warpneeded=1;
	}

	# Wow! Huge memory leak here if the '-when' is not set.
	# I had 60 meg core dumps..didn't quite figure out why yet?
	if ($warpneeded){
		#warn "Warping: $px $py";
		$obj->{PARENT}->eventGenerate(
				"<Motion>",
				-when=>'head',
				-x => $px,
				-y => $py,
				-warp => 1
				);
		$obj->{PARENT}->idletasks;		
	}
}

sub _focusIt {
	my $obj=shift;

	return if ($obj->{ISMIN}); #Don't focus a minimized window
	$obj->{PARENTOBJ}->_focusMe($obj->{NAME});
	$obj->{HASFOCUS} = 1;
	$obj->{TITLE}     ->configure(-bg => $obj->{FOCUSBG});
	$obj->{TITLELABEL}->configure(-bg => $obj->{FOCUSBG});
	$obj->{EXTERNAL}  ->raise;
	# somehow need to get packed widgets the focus.
	#foreach ($obj->{TOPLEVEL}->children){
	#    $_->Tk::focus;     
	#}

}

sub _unfocusIt {
	my $obj = shift;

	if ($obj->{HASFOCUS}) {
		$obj->{TITLE}     ->configure(-bg => $unfocusedTitleBg);
		$obj->{TITLELABEL}->configure(-bg => $unfocusedTitleBg);
		$obj->{HASFOCUS}   = 0;
	}
}

sub mainFrame { $_[0]->{TOPLEVEL} }

sub _TileMe
{
	#avoids relx/rely bug.
	my ($obj, $x, $y, $w, $h) = @_;

	$obj->{EXTERNAL}->place(
				-x => $x,
				-y => $y,
				-width  => $w,
				-height => $h,
	);
	$obj->{EXTERNAL}->raise;
}

sub _name   { return $_[0]->{NAME}  }

sub _isMin  { return $_[0]->{ISMIN} }

sub _wasMin { return $_[0]->{WASMIN}}

sub _isMax  { return $_[0]->{ISMAX} }

sub _mySlot { return $_[0]->{MYSLOT}}

1;