[comp.windows.news] Pie menus & Tab windows for TNT

hopkins@sun.com (Don Hopkins) (05/23/91)

Here's an implementation of pie menus and tab windows the the NeWS
toolkit, version TNT2.0fcs or TNT3.0beta. (TNT2.0 runs under OpenWindows
2.0 and is a separately distributed product.)  This distribution includes
the files "pie.ps", "tab.ps", and "winit.ps". Put them in a directory
somewhere, adjust the file names in "winit.ps", and put something like:

(tnt/win/winit.ps) LoadFile pop

into your ".user.ps" file. This will take effect next time you start
another window server. If you want to try them out right now, you can
simply "psh" the files "pie.ps" and "win.ps" in that order. 

For a demo, just type the following (if you're running V3, you'll also
have to do the beginpackage stuff):

psh
/demo ClassTabBaseWindow send

When you get some tab windows on the screen, try pressing the menu (right)
button over them and play around with the various functions. You can press
the point (left) or adjust (middle) buttons over the tab, and move the tab
or the window around in various ways. Try dragging the window by the
header, and stretching the resize corners of course. Don't forget to see
what happens while you're moving or resizing, when you press down the meta
key, the alt key, and even the shift key (if you have cycles to burn)! 

When you get sick of the spiffy animation of pie menus spinning and
flipping and flying out of the screen, there is an easy way to supress it.
When your muscles can remember the direction of the item you want, you can
mouse ahead into the menu, and the display will be supressed -- you won't
even see the menu at all! To mouse ahead, press and hold the menu button,
drag in the direction you want, then release, in a smooth quick gesture.
You don't have to drag very far, but the further out you move, the better
your control of the direction!  You can even quickly select from nested
submenus this way. You will know you're doing the right thing when you see
just the outline of the wedge that you're mousing ahead into. 

The spiffy special effects you see when you don't mouse ahead are actually
negative reinforcement designed to make you *want* to mouse ahead to avoid
seeing the menu display, which is the whole point of using pie menus!
Pretty soon you will be totally sick of the spiffy animation but it will
not matter because you will mouse ahead all the time and never see it! But
if you really want to turn it off, so the menus pop up fast, you can set
the /Spiffy? flag to false. Or if you want them to animate even longer,
there's a variable you can frob for that, too.

TNT programmers can use ClassTabBaseWindow and ClassTabPopupWindow just
like ClassBaseWindow and ClassPopupWindow to manage any TNT application.
ClassPieMenuCanvas is a canvas subclass that allows you to pop up a
pie menu with the right button. You can mix it in with other canvas
subclasses you want to be "piemenuable". ClassPieMenu is pretty much like
ClassMenu in its application programmer interface, but not its user
interface!

Please send any comments and suggestions to "hopkins@sun.com". 

Take a look and feel free!

	-Don

==== winit.ps: cut here ================================================
% Autoloads for pie menus and tab windows. 
% Load this from ".user.ps".
% Make sure the file names are correct, relative to the directory
% you started the server in!
%
systemdict begin

  openwinversion 0 get 51 eq
  dup { % V3?
    /TNT 3 0 findpackage begin
    currentdict beginautoload
  } if

  [ /ClassPieMenu				(tnt/win/pie.ps)
    /ClassPieMenuService			(tnt/win/pie.ps)
    /ClassPieMenuCanvas				(tnt/win/pie.ps)
    /ClassTabWindow				(tnt/win/tab.ps)
    /ClassTabBaseWindow				(tnt/win/tab.ps)
    /ClassTabPopupWindow			(tnt/win/tab.ps)
    /ClassPieMenuWindowManager			(tnt/win/tab.ps)
  ] DefineAutoLoads

  { % V3?
    currentdict endautoload end
  } if

end % systemdict

==== pie.ps: cut here ==================================================
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Pie Menus for the NeWS toolkit
% Version 3.0.1
%
% Copyright (C) 1991, by Don Hopkins. All rights reserved.
% This program is provided for unrestricted use, provided that this 
% copyright message is preserved. There is no warranty, and no author 
% or distributer accepts responsibility for any damage caused by this 
% program. 
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This file implements pie menus for the NeWS toolkit.
% It should work with TNT2.0fcs on OW2.0fcs and TNT3.0beta on OW3.0beta.
% This code and the ideas behind it were developed over time by Don Hopkins 
% at the University of Maryland, UniPress Software, and Sun Microsystems. 
% Pie menus and tab windows and NOT patented or restricted, and the
% interface and algorithms may be freely copied and improved upon. 
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

openwinversion 0 get 51 eq { % XXX: V3
    /NeWS 3 0 findpackage beginpackage
    /TNTCore 3 0 findpackage beginpackage
    /TNT 3 0 findpackage dup beginpackage
    dup beginautoload begin
        /CVSEC { aload pop 1000000 div add } def
	/SUBTIMEVAL { timeval subtimeval } def
} { % XXX: V2
    systemdict begin
        /CVSEC { 60 mul } def
	/SUBTIMEVAL { sub } def
} ifelse

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ClassPieMenu
%

/ClassPieMenu [ClassCanvas ClassControl] [
    /Direction /Distance
    /SliceWidth /Radius
    /CurrentValue /PaintedValue
] classbegin

% Canvas defaults:

    /SaveBehind		true def
    /Transparent	false def
    /Mapped		false def
    /Retained		false def

% Class variables:

    /BaseMenu?		false def
    /SkipNextDamage?	false def
    /Label		null def
    /ItemList		nullarray def
    /ItemListValid?	false def
    /Invoker		null def	% Object that invoked the menu
    /Border		3 def
    /Gap		6 def
    /Pad		1 def
    /InactiveRadius	4 def
    /RadiusMin		15 def
    /RadiusExtra	2 def
    /RadiusStep		2 def
    /Clockwise?		true def
    /InitialAngle	90 def % up
    /Sliced?		true def
    /Spiffy?		true def
    /ThrowSec		.25 def

    /TextFont		/LucidaSans-Bold findfont 12 scalefont
			false printermatchfont
			/ISOLatin1Encoding encodefont def

% Existential stuff:

    /NewInit {	% parent => instance
	/NewInit super send
	/Radius 0 def
	GlobalEventMgr /activate self send
    } def

    /destroy { % - => -
        null /setinvoker self send
	/destroy super send
    } def

% Label stuff:

    /label { % - => (label)
	Label
    } def

    /setlabel { % (label) => -
	/Label exch promote
    } def

% Item stuff:

    /List { % - => ItemList
        ItemList
    } def

    /Move { % x y item => -
        begin 
            /ItemY exch def
            /ItemX exch def
        end
    } def

    /Size { % item | self => width height
        dup self eq {
            /size exch send
        }{
           begin ItemWidth ItemHeight end
        } ifelse
    } def

    /Location { % item | self => x y
        dup self eq {
            pop 0 0
        }{
            begin ItemX ItemY end
        } ifelse
    } def

    /ResolveReference { % self | item-index => self | item
        dup type /integertype eq {
            ItemList exch get
        } if
    } def

    /setitemlist { % [item0 ...] => -
        /invalidate self send
        /ItemListValid? unpromote
       dup length dup array /ItemList exch promote
       1 sub 0 1 3 2 roll {
	    2 copy get
	    /NewItem self send
	    ItemList 3 1 roll put
	    pause
	} for
	pop
    } def

    /insertitem { % item-index item
        /invalidate self send
        /ItemListValid? unpromote
        /NewItem self send
        ItemList 3 1 roll arrayinsert
        /ItemList exch promote
    } def

    /deleteitem { % item-index => -
        /invalidate self send
        /ItemListValid? unpromote
        ItemList exch arraydelete
        /ItemList exch promote
    } def

    /replaceitem { % item-index item => -
        /invalidate self send
        /ItemListValid? unpromote
        /NewItem self send
        ItemList 3 1 roll 			% il i# i
	put					
    } def

    /appenditem { % item => -
        /invalidate self send
        /ItemListValid? unpromote
        /NewItem self send

        ItemList exch arrayappend
        /ItemList exch promote 
    } def   

    % Translate an external item representation to an internal item dict.
    % di can be a display item, or a dictionary (isobject? = false).
    % The dictionary must contain the keys /DisplayItem and /SubMenu
    % or /Notifier.
    %
    /NewItem { % string | [displayitem] | [di notify] | [di submenu] => item
	dictbegin
	    dup type /stringtype eq {		% string
		/DisplayItem exch def		% -
	    }{					%  [ di stuff(opt.) ]
		dup 0 get /DisplayItem exch def
		dup length 1 eq {pop}{
		    1 get dup isobject? {
			/SubMenu exch def
		    }{
			/Notifier exch def
		    } ifelse
		} ifelse
            } ifelse
	dictend					% item
    } def     

    /itemsize { % item-index => width height
        /?validate self send
        ItemList exch get begin
            ItemWidth ItemHeight
        end
    } def

    /itembbox { % item-index => x y width height
        /?validate self send
        ItemList exch get begin
            ItemX ItemY ItemWidth ItemHeight
        end
    } def

    /itemlocation { % item-index => x y 
        /?validate self send
        ItemList exch get begin
            ItemX ItemY
        end
    } def

    /itemlist { % - => [ item0 ... ]
        /itemcount self send dup array
        0 1 4 -1 roll 1 sub {
            2 copy /item self send
            exch 4 1 roll put
            pause
        } for
    } def

    /itemcount { % - => n
        ItemList length
    } def

    /item { % index => string | [displayitem] | [di notify] | [di submenu]
	/Item self send				% dict
	dup /DisplayItem get /stringtype ne	% dict bool1
	1 index /Notifier known			% dict bool1 bool2
	2 index /SubMenu known			% dict bool1 bool2 bool3
	2 copy 5 2 roll or or {			% dict bool2 bool3
	    2 copy or {				% dict bool2 bool3
		pop exch dup 3 -1 roll		% dict dict bool2
		{ /Notifier } { /SubMenu } ifelse get % dict notify|submenu
		exch /DisplayItem get		% notify|submenu di
		exch 2 array astore		% [di notify|submenu]
	    }{					% dict false false
		pop pop /DisplayItem get 1 array astore % [di]
	    } ifelse
	}{					% dict bool2 bool3
	    pop pop
	    /DisplayItem get			% string
	} ifelse
    } def

    /Item { % item-index => item
        ItemList exch get
    } def

    /pointtoitem { % x y => item-index true | false
        /?validate self send
	Radius dup xysub
	/SetCurrentValue self send
	CurrentValue dup null eq { pop false } { true } ifelse
    } def

    /pointinitem? { % x y <item index> => boolean
        /?validate self send
        /PointInItem? self send
    } def
        
    /PointInItem? { % x y <item index> => boolean
	3 1 roll /pointtoitem self send eq
    } def

% Layout:

    /clockwise? { % - => clockwise
        Clockwise?
    } def

    /setclockwise { % clockwise => -
        /Clockwise? exch promote
	/invalidate self send
    } def

    /initialangle { % - => initialangle
        InitialAngle
    } def

    /setinitialangle { % initialangle => -
        /InitialAngle exch promote
	/invalidate self send
    } def

    /radiusmin { % - => radiusmin
        RadiusMin
    } def

    /setradiusmin { % radiusmin => -
        /RadiusMin exch promote
	/invalidate self send
    } def

    /inactiveradius { % - => inactiveradius
	InactiveRadius
    } def

    /setinactiveradius { % inactiveradius => -
	/InactiveRadius exch promote
	/invalidate self send
    } def

    /sliced? { % - => bool
        Sliced?
    } def

    /setsliced { % bool => -
        /Sliced? exch promote
    } def

    /minsize { % - => w h
        /?validate self send
	Radius dup add dup
    } def

    /validate { % - => -
	/Layout self send
	/validate super send
	gsave
	    Parent setcanvas
	    /minsize self send
	    2 copy /size self send
	    3 -1 roll ne 3 1 roll ne or {
	        /location self send 4 2 roll /reshape self send
	    } { pop pop } ifelse
	    /Valid? true def % XXX
	grestore
    } def

    /Layout { % - => -
	PieGSave self setcanvas

	    % Deflate the menu.
	    /Radius 0 def
	    % Figure the slice width.
	    /SliceWidth 360 /itemcount self send 1 max div def
	    % Point the initial slice in the initial angle.
	    /ThisAngle InitialAngle store

	    % Loop through the items, validating each one.
	    ItemList {
		begin % item

		    % Measure the label.
		    /DisplayItem load DisplayItemSize
		    /ItemHeight exch def
		    /ItemWidth exch def

		    % Remember the angle and the direction.
		    /Angle ThisAngle def
		    /DX Angle cos def
		    /DY Angle sin def

		    % Figure the offset from the tip of the inner radius 
		    % spoke to the lower left label corner, according to 
		    % the direction of the item.
		    %
		    % Labels at the very top (bottom) are centered on their
		    % bottom (top) edge. Labels to the left (right) are
		    % centered on their right (left) edge.
		    %
		    DX abs .05 lt { % tippy top or bippy bottom

			% Offset to the North or South edge of the label.
			/XOffset ItemWidth -.5 mul def
			/YOffset
			    DY 0 lt {ItemHeight neg} {0} ifelse
			def

		    } { % left or right

			% Offset to the East or West edge of the label.
			/XOffset
			    DX 0 lt {ItemWidth neg} {0} ifelse
			def
			/YOffset ItemHeight -.5 mul def

		    } ifelse

		    % Twist around to the next item.
		    /ThisAngle
		        ThisAngle SliceWidth
			Clockwise? {sub} {add} ifelse
			NormalAngle
		    store

		end % item
	    } forall

	    % Figure the inner label radius, at least enough to prevent 
	    % the labels from overlapping.
	    /LabelRadius RadiusMin def
	    /itemcount self send 3 gt { % No sweat if 3 or less.

		% Check each item label against its following neighbor.
		0 1 /itemcount self send 1 sub {

		    /I exch def
		    /NextI I 1 add /itemcount self send mod def

		    % See if these two labels overlap.
		    % If they do, keep pushing the label radius out
		    % by RadiusStep until they don't.
		    {   I /CalcRect self send
			NextI /CalcRect self send
			rectsoverlap not {exit} if % They don't overlap!

			% They overlap. Push them out a notch and try again.
			/LabelRadius LabelRadius RadiusStep add def
		    } loop

		} for
		% Now that we've gone around once checking each pair,
		% none of them overlap any more!
	    } if

	    % Add in some more space to be nice.
	    /LabelRadius LabelRadius RadiusExtra add def

	    % Now we need to calculate the outer radius, based on the radius
	    % of the farthest label corner. During the loop, Radius actually
	    % holds the square of the radius, since we're comparing it against
	    % squared label corner radii anyway. 
	    /Radius LabelRadius dup mul def
	    ItemList {
		begin % item

		    % Remember the location to center the label edge.
		    /x DX LabelRadius mul def
		    /y DY LabelRadius mul def

		    % Remember the location of the label's SouthWest corner.
		    /ItemX x XOffset add round def
		    /ItemY y YOffset add round def

		    % Figure the distance of the label's farthest corner.
		    % This is easy 'cause we can fold all the labels into
		    % the NorthEast quadrant and get the same result.
		    DX abs .05 lt { % tippy top or bippy bottom

			% (|x|,|y|) is South edge: radius^2 of NorthEast corner
			x abs ItemWidth .5 mul add dup mul
			y abs ItemHeight add dup mul add

		    } { % left or right

			% (|x|,|y|) is West edge: radius^2 of NorthEast corner
			x abs ItemWidth add dup mul
			y abs ItemHeight .5 mul add dup mul add

		    } ifelse

		    % Remember the maximum corner radius seen so far.
		    Radius max /Radius exch store
		end % item
	    } forall

	    % Take the square root and add some extra space.
	    /Radius
		Radius sqrt Gap add Border add ceiling cvi
	    store

	grestore % Whew, we're done! Time to party!
    } def

    /CalcRect { % item-number => x y w h
	/Item self send begin
	    LabelRadius DX mul XOffset add Pad sub
	    LabelRadius DY mul YOffset add Pad sub
	    ItemWidth Pad dup add add ItemHeight Pad dup add add
	end
    } def

    /NormalAngle { % angle => angle
	dup 0 lt {
	    dup 360 sub 360 div cvi 360 mul sub
	} if
	dup 360 ge {
	    dup 360 div cvi 360 mul sub
	} if
    } def

    /PieGSave { % - => -
	gsave
	    self setcanvas
	    Radius dup translate
	    TextFont setfont
    } def

% Painting:

    /FixAll { % - => -
	SkipNextDamage? {
	    /SkipNextDamage? unpromote
	    damagepath newpath
	} {
	    /FixAll super send
        } ifelse
    } def

    /Paint { % - => -
        gsave
	    TextFont setfont
	    Radius dup translate
	    /PaintFrame self send
	    /PaintItems self send
	    true CurrentValue PaintSlice
	grestore
    } def

    /PaintFrame { % - => -
	0 0 Radius 1 add 45 225 arc closepath
	0 0 Radius Border sub 45 225 arc closepath
	3D? { BG0 } { 2DFG } ifelse setcolor eofill
	0 0 Radius 1 add 225 45 arc closepath
	0 0 Radius Border sub 225 45 arc closepath
	3D? { BG3 } { 2DFG } ifelse setcolor eofill
	0 0 Radius Border sub .9 add 0 360 arc closepath
	BackgroundColor setcolor fill
    } def

    /PaintItems { % - => -
	ForegroundColor setcolor
	ItemList {
	    begin
		ItemX ItemY moveto
		/DisplayItem load DisplayItemPaint
		Sliced? {
		    newpath
		    Angle SliceWidth .5 mul sub
		    matrix currentmatrix
		    exch rotate
		    InactiveRadius 0 moveto
		    LabelRadius Gap sub 0 lineto
		    ForegroundColor setcolor
		    stroke
		    setmatrix
		} if
	    end
	} forall
    } def

    /PaintCurrentValue { % - => -
	false PaintedValue PaintSlice
	true CurrentValue PaintSlice
	/PaintedValue CurrentValue store
    } def

    % Paint highlighting on a menu slice. If it's null, then do nothing.
    %
    /PaintSlice { % draw key => -
	dup null eq {pop pop} {
	    PieGSave 
		10 dict begin % localdict
		    exch /Hilite? exch def
		    /Item self send begin
			% Highlight the key
			Hilite? {
			    -2 ItemX ItemY ItemWidth ItemHeight insetrect
			    3D? { true Paint3DBox } { false Paint2DBox } ifelse
			    ForegroundColor setcolor
			} {
			    BackgroundColor setcolor
			    -3 ItemX ItemY ItemWidth ItemHeight insetrect
			    rectpath fill
			    ForegroundColor setcolor
			} ifelse
			ItemX ItemY moveto /DisplayItem load
		    end % keydict
		end % localdict
		DisplayItemPaint
	    grestore
	} ifelse
    } def

    /path { % x y w h => -
	ovalpath
    } def

% Tracking:

    /showat { % posname event => -
	/BaseMenu? true def
	Parent setcanvas
	dup 3 1 roll begin			% event posname
	    XLocation YLocation			% event posname x y
	end
	/popup self send			% event
	/PieStart self send		%
    } def

    /popup { % posname x y => -
        /?validate self send
	/PaintedValue null def
	/CurrentValue null def
	Parent setcanvas
	Radius dup xysub
	/move self send
	/totop self send
	pop
    } def

    /?Reveal {
        Mapped not { /Reveal self send } if
    } def

    /Reveal { % - => -
	/SkipNextDamage? true promote
	/ClearOverlay self send
	gsave Parent setcanvas
	    10 dict begin % localdict
		% Force menu on screen
		/bbox self send rect2points		% x0 y0 x1 y1
		/y1 exch def /x1 exch def		% x0 y0
		/y0 exch def /x0 exch def		%
		/size Parent send			% w h
		/h exch def /w exch def			%

		x0 0 lt {
		    x0 neg				% dx=-x0
		} {
		    x1 w gt {
			w x1 sub			% dx=w-x1
		    } { 0 } ifelse			% dx=0
		} ifelse				% dx

		y0 0 lt {
		    y0 neg				% dx dy=-y0
		} {
		    y1 h gt {
			h y1 sub			% dx dy=h-y1
		    } { 0 } ifelse			% dx dy=0
		} ifelse				% dx dy
	    end % localdict

	    2 copy 0 eq exch 0 eq and {
		pop pop					%
	    } {						% dx dy
		2 copy /location self send xyadd	% dx dy dx+x dy+y
		/move self send				% dx dy
		/CurrentMenu PieMenuService send
		self ne { pop pop } {
		    currentcursorlocation xyadd		% dx+cx dy+cy
		    setcursorlocation			%
		} ifelse
	    } ifelse

	    Spiffy? {
		/ThrowPie self send
	    } {
		/map self send
		/paint self send
	    } ifelse
	grestore
    } def

    /ThrowPie { % - => -
	{ 100 dict begin gsave
	    /c self newcanvas def
	    c /Mapped false put
	    c /Transparent false put
	    c /Retained true put
	    self setcanvas
	    clippath
	    c reshapecanvas
	    c setcanvas
	    /PaintedValue null store
	    Radius dup translate
	    TextFont setfont
	    /PaintFrame self send
	    /PaintItems self send
	    /map self send
	    self setcanvas
	    Radius dup translate

	    /top? /CurrentMenu PieMenuService send self eq def
	    /t0 currenttime def
	    /spin 0 def
	    top? {
		currentcursorlocation
		1 index dup mul 1 index dup mul add sqrt
		InactiveRadius le {
		    /spin 360 def
		    pop pop 90
		} { exch atan } ifelse
	    } {
		Direction
	    } ifelse
	    /a exch def
	    {   /i
		  currenttime t0 SUBTIMEVAL CVSEC ThrowSec div
		  .9 mul .1 add
		def
		i 1 ge { exit } if
		gsave
		    a rotate
		    i i mul .02 max
		    i .02 max
		    scale
		    1 i sub spin mul a sub rotate
		    Radius neg dup translate
		    newpath Radius dup dup 0 360 arc closepath
		    clip newpath
		    c imagecanvas
		grestore
	    } loop

	    Radius neg dup translate
	    c imagecanvas c /Retained false put

	    self setcanvas Radius dup translate
	    top? {
		currentcursorlocation /SetCurrentValue self send
	    } if
	    /PaintCurrentValue self send
	} fork pop
    } def

    /popdown { % event notify? => -
	/unmap self send
	{ /PieStop self send } { /PieCancel self send } ifelse
	BaseMenu? {
	    currentcanvas
		/CurrentClient PieMenuService send dup setcanvas
		self /PieMenuStop 3 -1 roll send
		/BaseMenu? unpromote
	    setcanvas
	} if
	/PaintedValue null store
    } def

    /UnPaintValue { % - => -
	PaintedValue null ne {
	    false PaintedValue PaintSlice
	    /PaintedValue null store
	} if
    } def

    /PieStart { % event => -
    	/PieMotion self send
    } def

    /PieMotion { % event => -
	PieGSave
	    begin XLocation YLocation end
	    /SetCurrentValue self send
	    Mapped {
		PaintedValue CurrentValue ne {
		    /PaintCurrentValue self send
		} if
	    } {
	        Overlay setcanvas erasepage
	        CurrentValue null ne {
		    /location self send Radius dup xyadd translate
		    0 0 moveto
		    0 0 Radius
		    ItemList CurrentValue get /Angle get
		    SliceWidth .5 mul sub
		    dup SliceWidth add
		    arc
		    closepath
		    stroke
		} if		
	    } ifelse
	grestore
    } def

    /PieStop { % event => -
        /PieMotion self send
        /ClearOverlay self send
	CurrentValue null ne {
	    CurrentValue /NotifyItem self send
	} if
    } def

    /PieCancel { % event => -
        pop /ClearOverlay self send
    } def

    /Overlay { % - => can
        Parent createoverlay
	/Overlay 1 index promote
    } def

    /ClearOverlay { % - => -
        /Overlay promoted? {
	    Overlay setcanvas erasepage
	    /Overlay unpromote
	} if
    } def

    /Notifier {pop pop} def

    /NotifyItem { % index => -
	dup /Item self send				% index child
	begin /Notifier load end			% index proc
	/ExecuteNotifier self send			% -
    } def

    /SetCurrentValue { % dx dy => -
	/Distance
	    2 index cvr dup mul 2 index cvr dup mul add sqrt def
	Distance 0 eq { pop pop 0 } { neg exch atan } ifelse
	/Direction exch def
	/CurrentValue
	    Distance InactiveRadius le { null } {
		SliceWidth .5 mul  InitialAngle
		Clockwise? { add } { sub } ifelse
		Direction add NormalAngle
		SliceWidth div cvi
	    } ifelse
	def
    } def

    /submenu { % event => false | menu true
        gsave
	    self setcanvas
	    begin XLocation YLocation end
	    /pointtoitem self send {			% index
		[1 index] /setvalue self send
		/Item self send
		dup /SubMenu known {
		    /SubMenu get true
		} {
		    pop false
		} ifelse
	    } {
		false
	    } ifelse
	grestore
    } def

    /invoker { % - => object
	Invoker
    } def

    /setinvoker { % object|null => -
	dup Invoker ne {
	    Invoker null ne {
		Invoker self /removeclient ObsoleteService send
	    } if
	    /Invoker exch soften def
	    Invoker null ne {
	    	Invoker self /addclient ObsoleteService send
	    } if
	} {
	    pop
	} ifelse
    } def

    /HandleObsoleteTarget { % object => -
	dup Invoker eq {
	    null /setinvoker self send
	} if
	/HandleObsoleteTarget super send
    } def

    /eventmgr { % - => process
	/eventmgr Invoker send
    } def

    /target { % - => object
	/target super send dup null eq {
	    pop /DefaultTarget self send
	} if
    } def

    /DefaultTarget { % - => object
	Invoker null eq {null} {
	    /target /understands? Invoker send {
		/target Invoker send
	    }{
		Invoker
	    } ifelse
	} ifelse
    } def

% Demo:

    /demo {
	userdict begin
	    /pie0 framebuffer /new ClassPieMenu send def
	    [ (a)(b)(c)(d)(e)(f)(g)[(h) {{beep}fork pop pop pop}]
	    ] /setitemlist pie0 send
	    /pie framebuffer /new ClassPieMenu send def
	    [ [(foo...) pie0] (bar) (baz) [(yow) {{beep}fork pop pop pop}]
	    ] /setitemlist pie send
	    /can framebuffer /new ClassPieMenuCanvas send def
	    /minsize { 200 200 } /promote can send
	    pie /setpiemenu can send
	    /win can framebuffer /new ClassBaseWindow send def
	    (Pie Menu Demo) /setlabel win send
	    /mgr /new ClassEventMgr send def
	    mgr /activate win send
	    /place win send /map win send
	end % userdict
    } def

classend def

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ClassPieMenuService
%

/ClassPieMenuService [
    ClassNotifyInterest ClassFullScreenInterest
] [
    /CurrentMenu	% which canvas we're currently tracking in
    /MenuList		% array of active menus not including CurrentMenu
    /ButtonDict		% which up/downs to watch for during tracking
    /PressedDownAt	% [x y] of cursor at button down

    % The state machine:
    /MouseDragged
    /UpTransition
    /DownTransition
]
classbegin

% Class variables:

    /PointName PointButton def
    /AdjustName AdjustButton def
    /MenuName MenuButton def

% Methods:

    % intialize the menu interest; create and install its dependents
    %
    /NewInit { % - => -
	/Exclusivity true def
	/Priority MenuButtonPriority .9 add def
		% ensures that dependent interest in mouse-down is higher
	growabledict /DownTransition TriggerName
	    /NewInit super send

	/CurrentMenu null def
	/MenuList nullarray def
	/ButtonDict 10 dict def
	/RevealEvent createevent def
	RevealEvent /Name /PieMenuReveal put
	CreateDependents

	self /addclient GlobalEventMgr send
    } def
    
    /destroy { % - => -
	Active? { /CancelNotify self send } if
    } def

    % the Default NotifyInterest name
    %
    /TriggerName 2 dict dup begin
	MenuName {
	    /trigger /SendInContext 2 index /Interest get send
	} def
	AdjustName MenuName load def
    end def

    % Override: Popdown all menus, remove any references to them.
    %
    /CancelNotify { % - => -
	CurrentMenu null ne {
	    createevent false /popdown CurrentMenu send
	    /CurrentMenu null def
	} if
	MenuList {
	    createevent false /popdown 4 -1 roll send
	} forall
	/MenuList nullarray def
    	/CancelNotify super send
    } def

    /NullActionUnblock { % event => -
	pop unblockinputqueue
    } def

    % Include the stopped/Unwind stuff in the executable matches rather
    % than having to include it in every procedure that might get stored
    % as one of the DownTransition/UpTransition/MouseDragged methods.
    %
    /MenuAction 2 dict dup begin
	/DownTransition {
	    {{gsave DownTransition grestore} stopped {grestore Unwind} if}
	    1 index /Interest get /NotifyInterest get send
	} def
	/UpTransition {
	    {{gsave UpTransition grestore} stopped {grestore Unwind} if}
	    1 index /Interest get /NotifyInterest get send
	} def
    end def

    /MotionName 1 dict dup begin
	/MouseDragged {
	    {{gsave MouseDragged grestore} stopped {grestore Unwind} if}
	    1 index /Interest get /NotifyInterest get send
	} def
    end def

    /RevealName 1 dict dup begin
        /PieMenuReveal {
	    {{gsave Reveal grestore} stopped {grestore Unwind} if}
	    1 index /Interest get /NotifyInterest get send
	} def
    end def

    % Create a set of dependent interests that can manage a menu
    %
    /CreateDependents { % - => -
	/MenuClick
	    null MenuAction ButtonDict DepCreate
	    dup /Priority MenuButtonPriority 1 add put
	    dup /Synchronous true put
	    dup /Exclusivity true put
	    pop
	/MenuMotion
	    null null MotionName DepCreate
	    dup /Priority MenuButtonPriority 1 add put
	    dup /Synchronous true put
	    dup /Exclusivity true put
	    pop
	/MenuReveal
	    CurrentClient null RevealName DepCreate
	    dup /Priority MenuButtonPriority 1 add put
	    dup /Synchronous true put
	    dup /Exclusivity true put
	    pop
    } def

    % Utility that ensures ButtonDict contains only the given name.
    %
    /ButtonDictDef { % name => -
    	ButtonDict MenuName undef
    	ButtonDict PointName undef
    	ButtonDict AdjustName undef
    	ButtonDict exch dup put
    } def

    /ActivateDependents { % event => -
	CurrentClient /Center 3 -1 roll		% invoker posname event
	/PieMenuStart CurrentClient send {	% invoker posname event pie
	    % REMIND: Placeholder for null menu.  For now, just swallow the
	    % event.  Later, want to change it to start a tracker with no
	    % menu showing, and add an interface to allow belated installation
	    % of the menu associated with this mouse-down event.
	    dup null ne {
		/activatefullscreen self send
		1 index 5 1 roll		% ev invoker posname ev pie
		/StartMenuTracking self send	% event
		/ActivateDependents super send	% -
	    }{					% invoke posname event null
	    	pop pop pop pop
	    	/CancelNotify self send
	    } ifelse
	}{					% invoker posname event
	    /CancelNotify self send
	    dup /Canvas null put
	    % Let event continue up the canvas tree; the Canvas field got
	    % set to this particular canvas when it was distributed to it.
	    % REMIND: Do we need to be hairy like the ReceptionService and
	    % avoid having modified the Canvas field in the first place (to
	    % avoid marking the event as /Synthetic)?
	    redistributeevent
	    pop pop
	} ifelse
    } def

    /DeactivateDependents { % - => -
	/deactivatefullscreen self send
	RevealEvent recallevent
	/DeactivateDependents super send
    } def


    /Reveal { % event => -
        pop unblockinputqueue
	/RevealNow self send
    } def

    /RevealNow { % - => -
	RevealEvent recallevent
	null blockinputqueue
	null MenuList aload pop CurrentMenu
	{   dup null eq { pop exit } if
	    /?Reveal exch send
	} loop
	unblockinputqueue
    } def

    openwinversion 0 get 51 eq { % XXX: V3
	/RevealDelay [0 750000] def
	/RevealLater { % - => -
	    RevealEvent dup recallevent
	    dup /TimeStamp
	      currenttime RevealDelay [0 0] addtimeval
	    put
	    sendevent
	} def
    } { % XXX: V2
	/RevealDelay .5 60 div def
	/RevealLater { % - => -
	    RevealEvent dup recallevent
	    dup /TimeStamp
	      currenttime RevealDelay add
	    put
	    sendevent
	} def
    } ifelse

    /StartMenuTracking { % invoker posname event pie => -
	/CurrentMenu exch def
	/MenuList 0 array def			% invoker posname event

	gsave
	    dup /Name get ButtonDictDef

	    /showat CurrentMenu send		% invoker
	    /RevealLater self send

	    /setinvoker CurrentMenu send	%

	    /framebufferof CurrentMenu send setcanvas
	    dup /Coordinates get /PressedDownAt exch promote

	    /MouseDragged /PieTrack load def
	    /UpTransition /CheckClick load def

	    /DownTransition /NullActionUnblock load def
	grestore
    } def

    /PieTrack { % event => -
	CurrentMenu dup null eq { pop pop } {
	  dup setcanvas
	  /PieMotion exch send
	} ifelse
	unblockinputqueue
    } def

    /ClickDown { % event => -
	CurrentMenu null eq { pop } {
	    dup /Name get MenuName ne {
		CurrentMenu setcanvas
		/Radius CurrentMenu send dup setcursorlocation
		% the cursor dissappears in V2 lego, so we force it to appear:
		CurrentMenu /Cursor 2 copy get put
	    } if
	    /MouseDragged /PieTrack load def
	    /UpTransition /CheckClick load def
	    /DownTransition /NullActionUnblock load def
	    % Watch for uptransitions only on the button that just went down
	    dup /Name get ButtonDictDef
	    /framebufferof CurrentMenu send setcanvas
	    dup /Coordinates get /PressedDownAt exch promote
	    CurrentMenu   				% event menu
	    dup setcanvas
	    /PieStart exch send			%
	} ifelse
    	unblockinputqueue
    } def

    /CheckClick { % event => -
	/MouseDragged /PieTrack load def
	/UpTransition /NullActionUnblock load def
	/DownTransition /ClickDown load def
	% If we keep tracking we want to watch for either button going down
	ButtonDict PointName dup put
	ButtonDict AdjustName dup put
	ButtonDict MenuName dup put

	CurrentMenu setcanvas
	dup /PieTrack self send

	dup /Name get MenuName ne
	CurrentMenu /CurrentValue get null eq  and {
	    MenuList length 0 eq {
		/CancelNotify self send
		unblockinputqueue
	        pop
	    } {
		dup false /popdown CurrentMenu send
		/CurrentMenu MenuList dup length 1 sub get def
		/MenuList MenuList 0 1 index length 1 sub getinterval def
		/PieTrack self send
	    } ifelse
	} {
	    dup /submenu CurrentMenu send {
		CurrentMenu /setinvoker 2 index send	% event submenu
		/MenuList MenuList CurrentMenu arrayappend def
		/CurrentMenu exch def			% event

		/framebufferof CurrentMenu send setcanvas
		begin XLocation YLocation end		%
		2 copy
		/Default 3 1 roll /popup CurrentMenu send

		% Was this the result of a click? If so, map now.
		% Otherwise we were moving with the button down, so map later.
		PressedDownAt aload pop xysub abs exch abs max
		4 le {
		    /RevealNow self send
		} {
		    /RevealLater self send
		} ifelse
	    } {
		MenuList length 0 eq {
		    { CurrentValue null eq Mapped not and
		    } CurrentMenu send {
			/RevealNow self send
			pop
		    } {
			/ExecDone self send
		    } ifelse
		} {
		    /ExecDone self send
		} ifelse
	    } ifelse
	    unblockinputqueue
	} ifelse
    } def

    /ExecDone { % event => -
	CurrentMenu setcanvas
	dup true /popdown CurrentMenu send
	dup MenuList arrayreverse {			% ev ev menu
	    false /popdown 3 -1 roll send		% ev
	    dup
	} forall					% ev ev
	pop						% ev

	/MenuList nullarray def
	/CurrentMenu null def
	/untrigger self send
    } def

classend def

systemdict /PieMenuService 2 copy known {	% dict name
    2 copy get type /eventtype ne		% dict name buildIt?
} { true } ifelse				% dict name buildIt?
{ /new ClassPieMenuService send put		%
} { pop pop } ifelse				%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ClassPieMenuCanvas
%

/ClassPieMenuCanvas ClassCanvas []
classbegin
    /PieMenuable? true def

    /PieMenu null def

    /activate {
	/activate super send
	EventMgr null ne {
	   PieMenuable? {self /addclient PieMenuService send} if
	} if
    } def

    /deactivate {
	EventMgr null ne {
	    PieMenuable? {self /removeclient PieMenuService send} if
	} if
	/deactivate super send
    } def

    /piemenuable? { % - => bool
	PieMenuable?
    } def

    /setpiemenuable { % bool => -
	EventMgr null ne {
	    self 1 index {/addclient} {/removeclient} ifelse
	    PieMenuService send
	} if
	/PieMenuable? exch def 
    } def

    /PieMenuStart { % invoker pos event => invoker' pos' event' pie true |
		    % invoker pos event => invoker pos event false
	PieMenu dup null eq { pop false } {
	    /InitPieMenu self send
	    true
	} ifelse
    } def

    /PieMenuStop { % pie => -
	pop
    } def

    /InitPieMenu % invoker pos event pie => invoker' pos' event' pie' =>
        nullproc
    def

    /piemenu { % - => pie
	PieMenu
    } def

    /setpiemenu { % pie => -
	/PieMenu exch promote
    } def

classend def

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

openwinversion 0 get 51 eq { % XXX: V3
    currentdict endautoload end % TNT
    endpackage endpackage endpackage
} { % XXX: V2
    end % systemdict
} ifelse

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

==== tab.ps: cut here ==================================================
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Tab Windows for the NeWS toolkit
% Version 3.0.1
%
% Copyright (C) 1991, by Don Hopkins. All rights reserved.
% This program is provided for unrestricted use, provided that this 
% copyright message is preserved. There is no warranty, and no author 
% or distributer accepts responsibility for any damage caused by this 
% program. 
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This file implements a pie menu tab window manager for the NeWS toolkit.
% It should work with TNT2.0fcs on OW2.0fcs and TNT3.0beta on OW3.0beta.
% This code and the ideas behind it were developed over time by Don Hopkins 
% at the University of Maryland, UniPress Software, and Sun Microsystems. 
% Pie menus and tab windows and NOT patented or restricted, and the
% interface and algorithms may be freely copied and improved upon. 
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

openwinversion 0 get 51 eq { % XXX: V3
    /NeWS 3 0 findpackage beginpackage
    /TNTCore 3 0 findpackage beginpackage
    /TNT 3 0 findpackage dup beginpackage
    dup beginautoload begin
} { % XXX: V2
    systemdict begin
} ifelse

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ClassTabWindow
%

/ClassTabWindow [ClassWindow ClassPieMenuCanvas]
dictbegin
    /TabEdge /West def
    /TabPosition 1 def
dictend
classbegin

    /Hands? true def

    /tabedge { % - => /West|/East|/North|/South
	TabEdge
    } def

    /settabedge { % /West|/East|/North|/South => -
	/TabEdge exch store
	/invalidate self send
    } def

    /changetabedge { % edge => -
	gsave
	    Parent setcanvas
	    /bbox self send /unfittab self send
	    5 -1 roll /settabedge self send
	    /fittab self send
	    /reshape self send
	grestore
    } def

    /tabposition { % - => 0..1
	TabPosition
    } def

    /settabposition { % 0..1 => -
	/TabPosition exch store
	/invalidate self send
    } def

    /changetabposition { % 0..1 => -
	gsave
	    Parent setcanvas
	    /bbox self send
	    5 -1 roll /settabposition self send
	    /reshape self send
	grestore
    } def

    /NInset { % - => n
        /NInset super send
	TabEdge /North eq { TabSize exch pop add } if
	self /NInset 2 index put
    } def

    /SInset { % - => n
        /SInset super send
	TabEdge /South eq { TabSize exch pop add } if
	self /SInset 2 index put
    } def

    /EInset { % - => n
        /EInset super send
	TabEdge /East eq { TabSize pop add } if
	self /EInset 2 index put
    } def

    /WInset { % - => n
        /WInset super send
	TabEdge /West eq { TabSize pop add } if
	self /WInset 2 index put
    } def

    /TabSize { 100 25 } def

    /EdgeTabXYDict 4 dict def
    EdgeTabXYDict begin
	/North { % width height => x y
	    TabSize xysub
	    exch TabPosition mul round exch % x y
	} def
	/South { % width height => x y
	    pop TabSize pop sub TabPosition mul round % x
	    0 % x 0
	} def
	/East { % width height => x y
	    TabSize xysub TabPosition mul round % x y
	} def
	/West { % width height => x y
	    exch pop 0 exch % 0 height
	    TabSize exch pop sub TabPosition mul round % 0 y
	} def
    end % EdgeTabXYDict

    /TabXY { % - => x y
        gsave
	    /opened? self send {
	        /size self send
	        //EdgeTabXYDict TabEdge get exec
	    } { 0 0 } ifelse
	    2 copy 2 packedarray cvx
	    self exch /TabXY exch put
	grestore
    } def

    /TabPathDict 4 dict def
    TabPathDict begin
	/West { % - => -
	    tw 0 moveto
	    0 h th sub TabPosition mul round rlineto
	    tw neg 0 rlineto
	    0 th rlineto
	    tw 0 rlineto
	    tw h lineto
	    w h lineto
	    w 0 lineto
	    closepath
	} def
	/East { % - => -
	    h th sub TabPosition mul round
	    0 0 moveto
	    0 h rlineto
	    w tw sub 0 rlineto
	    0 1 index th add h sub
	    rlineto
	    tw 0 rlineto
	    0 th neg rlineto
	    tw neg 0 rlineto
	    0 exch neg rlineto
	    closepath
	} def
	/North { % - => -
	    0 0 moveto
	    0 h th sub rlineto
	    w tw sub TabPosition mul round 0 rlineto
	    0 th rlineto
	    tw 0 rlineto
	    0 th neg rlineto
	    w h th sub lineto
	    w 0 lineto
	    closepath
	} def
	/South { % - => -
	    0 th moveto
	    0 h th sub rlineto
	    w 0 rlineto
	    0 h th sub neg rlineto
	    w tw sub TabPosition mul round tw add
	    th lineto
	    0 th neg rlineto
	    tw neg 0 rlineto
	    0 th rlineto
	    closepath
	} def
    end % TabPathDict

    /TabPath { % x y w h => -
	10 dict begin % tempdict
	    /mat matrix currentmatrix def
	    /minsize self send xymax
	    /h exch def /w exch def
	    TabSize /th exch def /tw exch def
	    translate
	    //TabPathDict TabEdge get exec
	    mat setmatrix
	end % tempdict
    } def

    /path { % x y w h => -
        /TabPath self send
    } def

    /EdgeUnfitDict 4 dict def
    EdgeUnfitDict begin
	/West { % x y w h => x' y' w' h'
	    4 -1 roll TabSize pop add 4 1 roll
	    exch TabSize pop sub exch
	} def
	/East { % x y w h => x' y' w' h'
	    exch TabSize pop sub exch
	} def
	/North { % x y w h => x' y' w' h'
	    TabSize exch pop sub
	} def
	/South { % x y w h => x' y' w' h'
	    3 -1 roll TabSize exch pop add 3 1 roll
	    TabSize exch pop sub
	} def
    end % EdgeUnfitDict

    % given tabbed frame bbox, returns bbox of frame w/out tab
    %
    /unfittab { % x y w h => x' y' w' h'
        //EdgeUnfitDict TabEdge get exec
    } def

    /EdgeFitDict 4 dict def
    EdgeFitDict begin
	/West { % x y w h => x' y' w' h'
	    4 -1 roll TabSize pop sub 4 1 roll
	    exch TabSize pop add exch
	} def
	/East { % x y w h => x' y' w' h'
	    exch TabSize pop add exch
	} def
	/North { % x y w h => x' y' w' h'
	    TabSize exch pop add
	} def
	/South { % x y w h => x' y' w' h'
	    3 -1 roll TabSize exch pop sub 3 1 roll
	    TabSize exch pop add
	} def
    end

    % given untabbed frame bbox, returns bbox of frame with tab
    %
    /fittab { % x' y' w' h' => x y w h
        //EdgeFitDict TabEdge get exec
    } def

    /invalidate { % - => -
        /NInset unpromote
        /SInset unpromote
        /EInset unpromote
        /WInset unpromote
	/TabSize unpromote
	/TabXY unpromote
	/invalidate super send
    } def

    /InReshapeArea? { % event => bool
        matrix currentmatrix
	/bbox self send /unfittab self send
	4 2 roll translate
	
	4 -1 roll begin XLocation YLocation end		% w h X Y
	4 2 roll
	ReshapeSize dup xysub				% X Y w h

	4 copy ReshapeSize dup pointinrect?		% X Y w h bool
	4 index 4 index 0 0 ReshapeSize dup pointinrect? or
	4 index 4 index 0 4 index ReshapeSize dup pointinrect? or
	4 index 4 index 4 index 0 ReshapeSize dup pointinrect? or

	5 1 roll pop pop pop pop
	exch setmatrix
    } def

    /PaintReshape { % - => -
	GraphicFont setfont
	matrix currentmatrix
	/bbox self send /unfittab self send
	4 2 roll translate
	exch ReshapeSize sub exch % w-reshape h
	0 1 index
	3D? {
	    BG0 setcolor 2 copy moveto (\130) show
	    BG setcolor  2 copy moveto (\132) show
	    BG3 setcolor moveto (\131) show
	    2 copy
	    BG0 setcolor 2 copy moveto (\133) show
	    BG setcolor  2 copy moveto (\135) show
	    BG3 setcolor moveto (\134) show
	    1 index ReshapeSize
	    BG0 setcolor 2 copy moveto (\136) show
	    BG setcolor  2 copy moveto (\140) show
	    BG3 setcolor moveto (\137) show
	    0 ReshapeSize
	    BG0 setcolor 2 copy moveto (\141) show
	    BG setcolor  2 copy moveto (\143) show
	    BG3 setcolor moveto (\142) show
	} {
	    2DFG setcolor
	    moveto (\242) show
	    2 copy moveto (\243) show
	    1 index ReshapeSize
	    moveto (\244) show
	    0 ReshapeSize
	    moveto (\245) show
        } ifelse
	pop pop
	setmatrix
    } def

    /PaintLabelBackground { % - => -
	WInset SInset
	/size self send
	EInset NInset xysub 3 index 3 index xysub	% x y w h
	3 -1 roll add exch HeaderHeight HeaderGap add
	/opened? self send { 1 add } if
	BackgroundColor setcolor
	rectpath fill
    } def

    /reshape { % x y w h => -
	/reshape super send
	/?validate self send
    } def

    /minsize { % - => w h
        /minsize super send
	/TabSize self send xymax
    } def

    /PaintFocus { % - => -
        WInset SInset
	/size self send						% w h
	EInset NInset xysub 3 index 3 index xysub		% x y w h
	3 -1 roll add exch HeaderHeight
	-1 2 xyadd

	/ClickToType? self send {
	    4 2 roll 1 add 4 2 roll
	    3D? {true Paint3DBox} {2DFG setcolor rectpath fill} ifelse
	} { % x y w h
	    3 index ReshapeSize BorderEdge sub add
	    3 index 2 index add 1 add
	    3 index ReshapeSize BorderEdge sub dup add sub
	    3D? {Paint3DLine} {2 rectpath 2DFG setcolor fill} ifelse
    
    	    pop
	    3D? {Paint3DLine} {2 rectpath fill} ifelse
	} ifelse
    } def

    /PaintTab { % - => -
	gsave
	    TabXY translate
	    newpath 0 0 TabSize rectpath clip newpath
	    BackgroundColor FillCanvas
	    ForegroundColor BorderStroke StrokeCanvas
	    /Label load dup null eq {pop} {
		ForegroundColor setcolor FooterFont setfont
		TabSize pop    					% dspitm w
		1 index DisplayItemSize pop sub 2 div 4 max	% dspitm x
		4 moveto DisplayItemPaint
	    } ifelse
	grestore
    } def

    /Paint { % - => -
	/PaintBackground self send
	/PaintTab self send
	/PaintBorder self send
	Reshape? {/PaintReshape self send} if
	Label?   {false /PaintHeader self send} if
	Footer?  {false /PaintFooter self send} if
    } def

    /PaintLabel { % erase? => -
	/Label load 

	WInset NInset /size self send			% ? di x y w h
	EInset NInset xysub 3 index 3 index xysub	% ? di x y w h
	3 -1 roll add HeaderGap add exch HeaderHeight

	VisualState /Busy eq {
	    ForegroundColor setcolor
	    Gray12Stipple 5 copy pop //rectpath StipplePath
	} if

	Close? Pin? or {
	    Close? {CloseSize ClosePad add} {PinSize PinPad add} ifelse
	    5 2 roll 3 index sub
	    5 2 roll exch add
	    4 1 roll
	} if
	gsave % for clip
	    TextFont setfont				% ? di x y w h
	    4 copy rectpath clip			% ? di x y w h
	    6 -1 roll {
	        ClickToType? Focus? and {
		    3D? {BG2} {2DFG} ifelse
	        } {BackgroundColor} ifelse
	        setcolor clippath fill
	    } if					% di x y w h
	    4 2 roll					% di w h x y
	    HeaderPad add moveto			% di w h
	    pop 1 index DisplayItemSize pop		% di w swidth
	    2 copy lt {pop pop} {
		sub 2.2 div 0 rmoveto % note: 2++ to round toward left
	    } ifelse					% di
	    3D? not ClickToType? Focus? and and
	        {BackgroundColor} {ForegroundColor} ifelse
	    setcolor DisplayItemPaint
	grestore
    } def

    /PaintPin { % pinned? => -
	% REMIND: simplify!
	GraphicFont setfont
	{103} {100} ifelse

%	WInset PinX BorderEdge sub add
	WInset PinPad add
	/size self send exch pop NInset sub
	HeaderHeight add HeaderPad add

	2 copy PinSize HeaderHeight HeaderPad 2 mul sub neg % i x y x y w h
	rectpath
	3D? {
	    BG setcolor fill	% i x y
	    BG0 setcolor 2 copy moveto 2 index cvis show
	    BG2 setcolor 2 copy moveto 2 index 2 add cvis show
	    BG3 setcolor moveto 1 add cvis show
	} {
	    2DBG setcolor fill	% i x y
	    2DFG setcolor moveto
	    100 eq { (\23) } { (\24) } ifelse show
	} ifelse
    } def

    /InCloseArea? { % event => bool
	begin XLocation YLocation end			% X Y

	/size self send exch pop NInset sub
	HeaderGap add					% X Y y

	Close? {
	    CloseX exch CloseSize			% X Y x y w
	} {
	    PinX exch PinSize				% X Y x y w
	} ifelse
	HeaderHeight
	pointinrect?					% bool
    } def

    /CloseX { % - => closex
        WInset /CloseX super send BorderEdge sub add
    } def

    /PinX { % - => pinx
        WInset /PinX super send BorderEdge sub add
    } def

    /HeaderDeltaY { % - => headerdeltay
        NInset HeaderGap sub
    } def

    /PaintClose { % down? => -
	GraphicFont setfont

	CloseX
	/size self send exch pop NInset sub
	HeaderHeight add HeaderGap add
	3D? {
	    3 copy moveto {BG3} {BG0} ifelse setcolor (\63) show
	    3 copy moveto {BG0} {BG3} ifelse setcolor (\64) show
	    3 copy moveto {BG2} {BG} ifelse setcolor (\65) show

	    4 -4 xyadd

	    2 copy moveto BG3 setcolor (\55) show
	    2 copy moveto BG0 setcolor (\56) show
	    2 copy moveto BG2 setcolor (\57) show
	    pop pop pop
	} {
	    2DBG setcolor 1 index 1 add 1 index moveto (\164) show
	    2DFG setcolor moveto { (\27) } { (\26) } ifelse show
        } ifelse
    } def

    /PaintFooter { % erase? => -
	/LeftFooter load /RightFooter load		% ? l r
	WInset FooterGap add				% ? l r x
	/size self send pop				% ? l r x w
	1 index sub EInset FooterGap add sub		% ? l r x w
	exch						% ? l r w x
	SInset FooterPad sub FooterHeight sub		% ? l r w x y
	6 -1 roll {
	    2 copy 4 index FooterHeight
	    BackgroundColor setcolor rectpath
	    fill
	} if

	FooterFont setfont
	ForegroundColor setcolor
	2 copy moveto 5 -1 roll DisplayItemPaint	% r w x y
	3 1 roll					% r y w x 
	add 2 index DisplayItemSize pop sub exch	% r x y
	moveto DisplayItemPaint
    } def

    /InTabArea? { % event => bool
	/opened? self send {
	    begin XLocation YLocation end			% X Y
	    TabXY TabSize
	    pointinrect?					% bool
	} {
	    pop true
	} ifelse
    } def

    /TrackStart { % event => /Default true
        dup /Name get AdjustButton ne {
	    /TrackStart super send
	} {
	    gsave
	        self setcanvas
		dup InTabArea? not { /TrackStart super send } {
		    /WindowTracking? true store
		    /opened? self send
		    TrackDict begin
			{   /Start /TabStart load def
			    /Motion /TabMotion load def
			    /Stop /TabStop load def
			} {
			    /Start /ShowWhereStart load def
			    /Motion NullNotify def
			    /Stop /ShowWhereStop load def
			} ifelse
		    end
		    [exch TrackDict /Start get self]
		    /sendmanager EventMgr send
		    /Default true
		} ifelse
	    grestore
        } ifelse
    } def

% Window sliding stuff:

    /MoveStart { % event => -
	true /inhibitfocus ClassFocus send
        gsave
	    TrackDict begin
	        self setcanvas
		/edge
		    1 index InTabArea?
		    opened? self send not or {
		        TabEdge {
			    /North /South { /Horizontal }
			    /East /West { /Vertical }
			} case
		    } { /All } ifelse
	        def
	        /Can Parent createoverlay def
		Can setcanvas
		InitTracking
		/TrackHasMoved? false store 
		dup begin
		    /TrackStartXY [ XLocation YLocation ] cvx store
		end
		TrackEvent				%
		/downx x def /downy y def
		/downname name def
	    end % TrackDict
	grestore
    } def

    /MoveMotion { % event => -
        gsave
	    TrackDict begin
		TrackHasMoved? {
		    Can setcanvas
		    TrackEvent				%
		    AdjustEdge
		    e /Shift /modifierdown? ClassKeyboard send {
			FinishAdjustEdge
		    } if
		    pop
		    PreviewAdjustEdge
		} {
		    Can setcanvas
		    begin XLocation YLocation end	%
		    TrackStartXY xysub
		    abs MoveThresh ge exch abs MoveThresh ge or {
			/TrackHasMoved? true store
		    } if
		} ifelse
	    end % TrackDict
	grestore
    } def

    /MoveStop { % event => -
        dup /MoveMotion self send		% event
	TrackHasMoved? {
	    pop					%
	    gsave
		TrackDict begin
		    Can setcanvas erasepage
		    FinishAdjustEdge
		end % TrackDict
	    grestore
openwinversion 0 get 51 eq { % XXX: V3
	        timeval
} { % XXX: V2
	        0
} ifelse
	} {					% event
	    dup /HandleSingleClick self send	% event
openwinversion 0 get 51 eq { % XXX: V3
	        /TimeStamp get			% time
} { % XXX: V2
	        /TimeStampMS get		% time
} ifelse
	} ifelse				% time
	/TrackStopTime exch store		%
	TrackDict cleanoutdict
        false /inhibitfocus ClassFocus send
    } def

% BBox dragging stuff:

    /BBoxStart { % event => -
	true /inhibitfocus ClassFocus send
        gsave
	    TrackDict begin
	        /Can Parent createoverlay def
		Can setcanvas
		InitTracking
		x x0 sub abs  x x1 sub abs  lt
		y y0 sub abs  y y1 sub abs  lt { % South
		    { /SouthWest } { /SouthEast } ifelse
		} { % North
		    { /NorthWest } { /NorthEast } ifelse
		} ifelse
		/edge exch def
		TrackEvent
		/downx x def /downy y def
		/downname name def
	    end % TrackDict
	grestore
    } def

    /BBoxMotion { % event => -
        gsave
	    TrackDict begin
		Can setcanvas
		TrackEvent
		AdjustEdge
		e /Shift /modifierdown? ClassKeyboard send {
		    FinishAdjustEdge
		} if
		pop
		PreviewAdjustEdge
	    end % TrackDict
	grestore
    } def

    /BBoxStop { % event => -
        dup /BBoxMotion self send
	gsave
	    TrackDict begin
		Can setcanvas erasepage
		FinishAdjustEdge
	    end % TrackDict
	grestore
	TrackDict cleanoutdict
        false /inhibitfocus ClassFocus send
    } def

% Tab dragging stuff:

    /TabStart { % event => -
	true /inhibitfocus ClassFocus send
	gsave
	    /TrackStartXY [
		2 index begin XLocation YLocation end
	    ] cvx store
	    /TrackHasMoved? false store
	    Parent createoverlay dup setcanvas			% event can
	    /bbox self send /unfittab self send rect2points	% e c w s e n
	    TrackDict begin
		/north exch def /east exch def			% e c w s
		/south exch def /west exch def			% e c
		/Can exch def					% e
	    end % TrackDict
	grestore
	/TabMotion self send					%
    } def

    /TabMotion { % event => -
	gsave
	    TrackDict begin
		Can setcanvas
		begin /x XLocation /y YLocation end def def
		TrackHasMoved? not {
		    x y TrackStartXY xysub
		    abs MoveThresh ge exch abs MoveThresh ge or {
			/TrackHasMoved? true store
		    } if
		} if
		TrackHasMoved? {
		    TabEdge {
		      /West {
			x west gt {
			  y north gt { /North /settabedge self send } {
			    y south lt { /South /settabedge self send } {
			      x east gt { /East /settabedge self send } if
			    } ifelse
			  } ifelse
			} if
		      }
		      /East {
			x east lt {
			  y north gt { /North /settabedge self send } {
			    y south lt { /South /settabedge self send } {
			      x west lt { /West /settabedge self send } if
			    } ifelse
			  } ifelse
			} if
		      }
		      /North {
			y north lt {
			  x west lt { /West /settabedge self send } {
			    x east gt { /East /settabedge self send } {
			      y south lt { /South /settabedge self send } if
			    } ifelse
			  } ifelse
			} if
		      }
		      /South {
			y south gt {
			  x west lt { /West /settabedge self send } {
			    x east gt { /East /settabedge self send } {
			      y north gt { /North /settabedge self send } if
			    } ifelse
			  } ifelse
			} if
		      }
		    } case

		    % XXX: clean this up
		    TabEdge {
		      /West /East {
			y south sub
			TabSize exch pop .5 mul sub
			north south sub TabSize exch pop sub 1 max div
			0 max 1 min
		      }
		      /North /South {
			x west sub
			TabSize pop .5 mul sub
			east west sub TabSize pop sub 1 max div
			0 max 1 min
		      }
		    } case
		    /settabposition self send
		} if
		west south east 2 index sub north 2 index sub % x y w h
	    end % TrackDict
	    /fittab self send
	    4 copy /TabPath self send
	    4 2 roll -1 1 xyadd 4 2 roll /TabPath self send
	    erasepage
	    stroke
	grestore
    } def

    /TabStop { % event => -
        /TabMotion self send			%
	gsave
	    TrackHasMoved? {
		TrackDict begin
		    Can setcanvas erasepage
		    west south			% x y
		    east 2 index sub		% x y w
		    north 2 index sub		% x y w h
		end % TrackDict
		/fittab self send
		/minsize self send		% x y w h mw mh
		3 -1 roll max		% x y w mw H
		3 1 roll max		% x y H W
		exch			% x y W H
		/reshape self send		%
	    } {
	        TrackDict /Can get setcanvas erasepage
	    } ifelse
	grestore
	TrackDict cleanoutdict
        false /inhibitfocus ClassFocus send
    } def

    /UpdateTab {
	/PaintTab self send
	/PaintBorder self send
    } def

    /ShowWhereStart { % event => -
        pop
    } def

    /ShowWhereStop { % event => -
        pop
    } def

% Window Placement:

    /PlaceDelta { % - => dx dy
        0 TabSize neg exch pop
    } def

    % XXX: V2
    /place { % - => -
	gsave Parent dup setcanvas
	    /TNTPlaceXY known not {
		Parent /TNTPlaceXY [
		    0 /size Parent send exch pop
		] cvx put
	    } if
	    /reshaped? self send  		% bool
	    Parent /TNTPlaceXY get exec		% bool x y
	    2 index {
		/size self send			% bool x y w h
	    } {
		/preferredsize self send	% bool x y w h
	    } ifelse				% bool x y-h w h
	    3 -1 roll 1 index sub 3 1 roll		

	    4 copy rect2points			% bool x y-h w h x y-h x1 y1
	    4 -1 roll 0 lt 4 -1 roll 0 lt or {	% bool x y-h w h x1 y1
		pop pop true			% bool x y-h w h true
	    } {					% bool x y-h w h x1 y1
		/size Parent send		% bool x y-h w h x1 y1 W h
		3 -1 roll lt			% bool x y-h w h x1 W H<y1
		3 1 roll gt or			% bool x y-h w h H<y1|x1>W
	    } ifelse				% bool x y-h w h init?

	    {   Parent /TNTPlaceXY [
		    0 /size Parent send exch pop
		    PlaceDelta xyadd
		] cvx put			% bool x y-h w h

		4 2 roll			% bool w h x y-h
		pop pop				% bool w h
		0 /size Parent send exch pop
		2 index sub			% bool w h x y-h
		4 2 roll			% bool x y-h w h
	    } {					% bool x y-h w h
		3 index 3 index			% bool x y-h w h x y-h
		2 index add			% bool x y-h w h x y
		PlaceDelta xyadd		% bool x y-h w h x' y'
		2 array astore cvx		% bool x y-h w h {x' y'}
		Parent /TNTPlaceXY 3 -1 roll put% bool x y-h w h
	    } ifelse

	    5 -1 roll {				% x y-h w h
		pop pop /move self send
	    } {					% x y-h w h
		/reshape self send
	    } ifelse
	grestore
    } def

% Random utilities:

    /?Open { % - => -
        /opened? self send not {
	    /ToggleOpened self send
	} if
    } def

openwinversion 0 get 51 eq { % XXX: V3
    /flashwindow { % - => -
	{   /Selected? Selected? not def /Focus? Focus? not def
	    /paintwindow self send
	    [0 500000] sleep
	    /Selected? Selected? not def /Focus? Focus? not def
	    /paintwindow self send
	} fork pop
    } def
} { % XXX: V2
    /flashwindow { % - => -
	{   /Selected? Selected? not def /Focus? Focus? not def
	    /paintwindow self send
	    .5 60 div sleep
	    /Selected? Selected? not def /Focus? Focus? not def
	    /paintwindow self send
	} fork pop
    } def
} ifelse

% ClassPieMenuWindowManager mixin hooks:

    /TrackBBox { % x y w h => x' y' w' h'
	/unfittab self send
    } def

    /UnTrackBBox { % x y w h => x' y' w' h'
        /fittab self send
    } def

    /demo {
	ClassName /ClassTabWindow eq {
	    /demo ClassTabBaseWindow send
	    /demo ClassTabPopupWindow send
	} {
	    /demo super send
	} ifelse
    } def

classend def

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ClassPieMenuWindowManager
%

/ClassPieMenuWindowManager ClassCanvas []
classbegin

% ClassPieMenuWindowManager mixin hooks:

%   /TrackBBox % x y w h => x' y' w' h'
%   /UnTrackBBox % x y w h => x' y' w' h'

% Pie Menu Definitions:

/FBFindMenu where { pop } { % XXX: V2
    % Utility for managing shared menus with multiple framebuffers.
    % The proc should create and return a menu whose parent is
    % "/framebufferof self send". The dict is the cache of menus
    % keyed by framebuffer. The menu for the object's framebuffer is 
    % looked up in the cache, created and stashed away if needed,
    % and returned.
    %
    /FBFindMenu { % proc dict => menu
	/framebufferof self send		% proc dict fb
	2 copy known {
	    get exch pop			% menu
	} {					% proc dict fb
	    3 -1 roll cvx exec			% dict fb menu
	    dup 4 1 roll put			% menu
	} ifelse
    } def

} ifelse

    /Parent { framebuffer } /installmethod ClassCanvas send

    /PieMenu { % - => menu
	/MakeWindowPieMenu WindowPieMenus /FBFindMenu self send
    } def

    /EdgeItemList [
        /Icon findfont 1 scalefont begin
	    [ [ { /size eq {11  7}
		  { [(\243) -2 -1] DisplayItemPaint }
		ifelse } currentdict ] ]
	    [ [ { /size eq {15 15}
		  { [(\237)  0 -1] DisplayItemPaint }
		ifelse } currentdict ] ]
	    [ [ { /size eq { 7 11}
		  { [(\244)  0 -1] DisplayItemPaint }
		ifelse } currentdict ] ]
	    [ [ { /size eq {15 15}
		  { [(\241)  0 -1] DisplayItemPaint }
		ifelse } currentdict ] ]
	    [ [ { /size eq {11  7}
		  { [(\243) -2 -1] DisplayItemPaint }
		ifelse } currentdict ] ]
	    [ [ { /size eq {15 15}
		  { [(\242)  0 -1] DisplayItemPaint }
		ifelse } currentdict ] ]
	    [ [ { /size eq { 7 11}
		  { [(\244)  0 -1] DisplayItemPaint }
		ifelse } currentdict ] ]
	    [ [ { /size eq {15 15}
		  { [(\240)  0 -1] DisplayItemPaint }
		ifelse } currentdict ] ]
	end
     ] def

     /SlideItemList [
	/ZapfDingbats findfont 35 scalefont begin
	    [ [ (\327) currentdict ]
		{ null blockinputqueue
		  exch pop /VMoveFromUser /sendtarget 2 index send } ]
	    [ [ (\326) currentdict ]
		{ null blockinputqueue
		  exch pop /HMoveFromUser /sendtarget 2 index send } ]
	    [ [ (\327) currentdict ]
		{ null blockinputqueue
		  exch pop /VMoveFromUser /sendtarget 2 index send } ]
	    [ [ (\326) currentdict ]
		{ null blockinputqueue
		  exch pop /HMoveFromUser /sendtarget 2 index send } ]
	end
    ] def

    /WindowPieMenus growabledict def

    /MakeWindowPieMenu { % - => menu
        /framebufferof self send /new ClassPieMenu send
	[   [ (Front!)
	        { exch pop /FrontFromUser /sendtarget 2 index send } ]
	    [ (Zoom!)
	        { exch pop /ZoomFromUser /sendtarget 2 index send } ]
	    [ ((Grab))
	        /framebufferof self send /new ClassPieMenu send
		[ /Icon findfont 1 scalefont begin
		    [ [ { /size eq {11  7}
			  { [(\243) -2 -1] DisplayItemPaint }
			ifelse } currentdict ] ]
		    [ [ { /size eq {15 15}
			  { [(\237)  0 -1] DisplayItemPaint }
			ifelse } currentdict ] ]
		    [ [ { /size eq { 7 11}
			  { [(\244)  0 -1] DisplayItemPaint }
			ifelse } currentdict ] ]
		    [ [ { /size eq {15 15}
			  { [(\241)  0 -1] DisplayItemPaint }
			ifelse } currentdict ] ]
		    [ [ { /size eq {11  7}
			  { [(\243) -2 -1] DisplayItemPaint }
			ifelse } currentdict ] ]
		    [ [ { /size eq {15 15}
			  { [(\242)  0 -1] DisplayItemPaint }
			ifelse } currentdict ] ]
		    [ [ { /size eq { 7 11}
			  { [(\244)  0 -1] DisplayItemPaint }
			ifelse } currentdict ] ]
		    [ [ { /size eq {15 15}
			  { [(\240)  0 -1] DisplayItemPaint }
			ifelse } currentdict ] ]
		end ] /setitemlist 2 index send
		/Icon findfont 1 scalefont /settextfont 2 index send
		{ null blockinputqueue
		  exch pop /AdjustEdgeFromUser /sendtarget 2 index send
		} /setnotifier 2 index send
		20 /setradiusmin 2 index send ]
	    [ ((Frame))
		/framebufferof self send /new ClassPieMenu send
		[   [ (Quit!)
		        { exch pop /QuitFromUser /sendtarget 2 index send } ]
		    [ (Props...)
		        { exch pop /PropsFromUser /sendtarget 2 index send } ]
		] /setitemlist 2 index send ]
	    [ (Back!)
	        { exch pop /BackFromUser /sendtarget 2 index send } ]
	    [ (Paint!)
	        { exch pop /RefreshFromUser /sendtarget 2 index send } ]
	    [ ((Push))
		/framebufferof self send /new ClassPieMenu send
		[   [ [ { /size eq {64 35} { (\271) DisplayItemPaint } ifelse
		        } /Icon findfont ]
			{ exch pop /PlaceFromUser /sendtarget 2 index send } ]
		    [ [ (\054) /ZapfDingbats findfont 50 scalefont ]
			{ null blockinputqueue
		          exch pop /MoveFromUser /sendtarget 2 index send } ]
		    [ [ (\327) /ZapfDingbats findfont 40 scalefont ]
			{ null blockinputqueue
			  exch pop /VMoveFromUser /sendtarget 2 index send } ]
		    [ [ (\326) /ZapfDingbats findfont 40 scalefont ]
			{ null blockinputqueue
			  exch pop /HMoveFromUser /sendtarget 2 index send } ]
		] /setitemlist 2 index send
		5 /setradiusmin 2 index send ]
	    [ (Icon!)
	        { exch pop /CloseFromUser /sendtarget 2 index send } ]
	] /setitemlist 2 index send
    } def

% Pie Menu Handlers:

    /PieMenuStart { % piename event => piename event <pie true> | false
        /PieMenuStart super send dup {
	    % Let the right button pop up the regular frame menu if the
	    % event is in the close button, by refusing pie tracking.
	    Close? {
	        gsave
		    self setcanvas
		    2 index InCloseArea? {
		      pop pop false
		    } if
		grestore
	    } if
	} if
    } def

    /PlaceFromUser { % ctl => -
	/place self send
    } def

    /FrontFromUser { % ctl => -
	pop /totop self send
    } def

    /VMoveFromUser { % ctl => -
	pop /Vertical /ClickAdjustEdge self send
    } def

    /HMoveFromUser { % ctl => -
	pop /Horizontal /ClickAdjustEdge self send
    } def

    /MoveFromUser { % ctl => -
	pop /All /ClickAdjustEdge self send
    } def
 
    /AdjustEdgeFromUser { % ctl => -
        /value exch send 0 get
	{   /North /NorthEast /East /SouthEast
            /South /SouthWest /West /NorthWest
	} exch get
	/?Open self send
	/ClickAdjustEdge self send
     } def

    /AdjustEdgeDict 20 dict def
    AdjustEdgeDict begin
	/North {
	    /y1 y1 dy add store
	} def
	/South {
	    /y0 y0 dy add store
	} def
	/East {
	    /x1 x1 dx add store
	} def
	/West {
	    /x0 x0 dx add store
	} def
	/NorthEast { North East } def
	/NorthWest { North West } def
	/SouthEast { South East } def
	/SouthWest { South West } def
	/Vertical { North South } def
	/Horizontal { East West } def
	/All { North South East West } def
    end % AdjustEdgeDict

    /AdjustEdge { % - => -
	AdjustEdgeDict begin
	    edge cvx exec
	end
    } def

    /GrabInterest { % - => interest
	createevent
	dup /Name [ PointButton AdjustButton MenuButton /MouseDragged ] put
	dup /Priority 1000 put
	dup /IsPreChild true put
	dup /Exclusivity true put
	/GrabInterest 1 index store
    } def

    /ClickAdjustEdge { % edge => -
        { gsave TrackDict begin
	    /edge exch def
	    /Can Parent createoverlay def
	    Can setcanvas

	    GrabInterest expressinterest
	    unblockinputqueue

	    InitTracking
	    PreviewAdjustEdge

	    {   awaitevent TrackEvent
		action /DownTransition eq { exit } if
		PreviewAdjustEdge
	    } loop

	    /downx x def /downy y def
	    /downname name def

	    {   AdjustEdge 
		e /Shift /modifierdown? ClassKeyboard send {
		    FinishAdjustEdge
		} if
		pop
		PreviewAdjustEdge
		awaitevent TrackEvent
		name downname eq
		action /UpTransition eq  and {
		    exit
		} if
	    } loop

	    Can setcanvas erasepage
	    FinishAdjustEdge
	    TrackDict cleanoutdict
	    clear
	} fork /ProcessName (ClickAdjustEdge tracker) put
	pop
    } def

    /InitTracking { % - => -
	/startx lasteventx def
	/starty lasteventy def
	/bbox self send
        /TrackBBox self send
	rect2points
	/y1 exch def /x1 exch def /y0 exch def /x0 exch def
	/x startx def /y starty def
	/x' startx def /y' starty def
	/xe 0 def /ye 0 def
%	/Arrow Quiver dup length random mul floor cvi get cvis store
    } def

    /SlowTrackSpeed .1 def
    /FastTrackSpeed 4 def

    /TrackEvent { % event => -
        /e 1 index def
	Can setcanvas
	{   /Meta /modifierdown? self send	% event slow?
	    exch /FunctionAlt /modifierdown? self send% s? ev fast?
	    3 -1 roll				% ev s? f?
	} ClassKeyboard send			% ev s? f?
	{ { 0 } { SlowTrackSpeed } ifelse }
	{ { FastTrackSpeed } { 1 } ifelse }
	ifelse					% ev speed
	/x' x /y' y				% ev sp /x' x' /y' y'
	6 -1 roll begin				% sp /x' x' /y' y'
	    XLocation YLocation Name Action	% sp /x' x' /y' y' x y n a
	end
	/action exch def   /name exch def	% sp /x' x' /y' y' x y
	/y exch def   /x exch def		% sp /x' x' /y' y'
	def def					% speed
	dup x x' sub mul			% speed dx
	/dx exch def				% speed
	y y' sub mul				% dy
	/dy exch def				%
    } def

    /EdgeOffsetDict growabledict def
    EdgeOffsetDict begin
	/North		{.5  1} def
	/South		{.5  0} def
	/East		{ 1 .5} def
	/West		{ 0 .5} def
	/NorthEast	{ 1  1} def
	/NorthWest	{ 0  1} def
	/SouthEast	{ 1  0} def
	/SouthWest	{ 0  0} def
	/Vertical	{.5 .5} def
	/Horizontal	//Vertical def
	/All		//Vertical def
    end % EdgeOffsetDict

    /EdgeLen 50 def
    /EdgeGap 10 def

    /Quiver [
        42 43 49 50 165 167 212 213 217 219 220 221 222 223 224 225
	226 227 228 229 230 231 232 233 234 235 236 237 238 239 241
	242 243 245 248 250 251 253 254
    ] def
    /Arrow [ 43 ] cvas def
    /ding /ZapfDingbats findfont def
    /s 50 def

    /EastArrowFont
      ding [ 1 0 0 1 0 0 ] makefont s scalefont def
    /WestArrowFont
      ding [ -1 0 0 1 0 0 ] makefont s scalefont def
    /NorthArrowFont
      ding [ 0 1 -1 0 0 0 ] makefont s scalefont def
    /SouthArrowFont
      ding [ 0 -1 -1 0 0 0 ] makefont s scalefont def

    /hfr .35 def
    /vfr .35 def

    /NorthHand { % x y => -
	NorthArrowFont setfont
	s hfr mul -1 xyadd moveto Arrow show
    } def

    /SouthHand { % x y => -
	SouthArrowFont setfont
	s hfr mul 1 xyadd moveto Arrow show
    } def

    /EastHand { % x y => -
	EastArrowFont setfont
	-1 s vfr neg mul xyadd moveto Arrow show
    } def

    /WestHand { % x y => -
	WestArrowFont setfont
	1 s vfr neg mul xyadd moveto Arrow show
    } def

    /PreviewAdjustEdge { % - => -
%	/Arrow Quiver dup length random mul floor cvi get cvis store

	x0 y0 x1 y1 points2rect			% x0 y0 x1 y1
	/UnTrackBBox self send
	4 copy /path self send
	4 2 roll -1 1 xyadd 4 2 roll		% x0-1 y0+1 x1 y1
	/path self send				%
	erasepage
	edge {
	    /All {
		Hands? {
		    x0 x1 add .5 mul
		    y0 y1 add .5 mul
		    1 index y0 SouthHand
		    1 index y1 NorthHand
		    x0 1 index WestHand
		    x1 1 index EastHand
		    pop pop
		} if
	    }
	    /Horizontal {
		x0 EdgeGap sub y0 moveto	EdgeLen neg 0 rlineto
		x1 EdgeGap add y0 moveto	EdgeLen 0 rlineto
		x0 EdgeGap sub y1 1 add moveto	EdgeLen neg 0 rlineto
		x1 EdgeGap add y1 1 add moveto	EdgeLen 0 rlineto

		Hands? {
		    y0 y1 add .5 mul
		    x0 1 index WestHand
		    x1 exch EastHand
		} if
	    }
	    /Vertical {
		x0 1 sub y0 EdgeGap sub moveto	0 EdgeLen neg rlineto
		x1 y0 EdgeGap sub moveto	0 EdgeLen neg rlineto
		x0 1 sub y1 EdgeGap add moveto	0 EdgeLen rlineto
		x1 y1 EdgeGap add moveto	0 EdgeLen rlineto

		Hands? {
		    x0 x1 add .5 mul
		    dup y0 SouthHand
		    y1 NorthHand
		} if
	    }
	    /North {
		x0 1 sub y1 EdgeGap add moveto	0 EdgeLen rlineto
		x1 y1 EdgeGap add moveto	0 EdgeLen rlineto

		Hands? {
		    x0 x1 add .5 mul y1 2 copy NorthHand SouthHand
		} if 
	    }
	    /South {
		x0 1 sub y0 EdgeGap sub moveto	0 EdgeLen neg rlineto
		x1 y0 EdgeGap sub moveto	0 EdgeLen neg rlineto

		Hands? {
		    x0 x1 add .5 mul y0 2 copy NorthHand SouthHand
		} if
	    }
	    /East {
		x1 EdgeGap add y0 moveto	EdgeLen 0 rlineto
		x1 EdgeGap add y1 1 add moveto	EdgeLen 0 rlineto
		
		Hands? {
		    x1 y0 y1 add .5 mul 2 copy EastHand WestHand
		} if
	    }
	    /West {
		x0 EdgeGap sub y0 moveto	EdgeLen neg 0 rlineto
		x0 EdgeGap sub y1 1 add moveto	EdgeLen neg 0 rlineto

		Hands? {
		    x0 y0 y1 add .5 mul 2 copy EastHand WestHand
		} if
	    }
	    /NorthEast {
		x1 EdgeGap add y1 EdgeGap add
		2 copy moveto EdgeLen neg 0 rlineto
		moveto 0 EdgeLen neg rlineto
		x1 EdgeGap sub y1 EdgeGap sub
		2 copy moveto EdgeLen neg 0 rlineto
		moveto 0 EdgeLen neg rlineto

		Hands? {
		    x0 x1 add .5 mul y1 2 copy NorthHand SouthHand
		    x1 y0 y1 add .5 mul 2 copy EastHand WestHand
		} if
	    }
	    /NorthWest {
		x0 EdgeGap sub y1 EdgeGap add
		2 copy moveto EdgeLen 0 rlineto
		moveto 0 EdgeLen neg rlineto
		x0 EdgeGap add y1 EdgeGap sub
		2 copy moveto EdgeLen 0 rlineto
		moveto 0 EdgeLen neg rlineto

		Hands? {
		    x0 x1 add .5 mul y1 2 copy NorthHand SouthHand
		    x0 y0 y1 add .5 mul 2 copy EastHand WestHand
		} if
	    }
	    /SouthEast {
		x1 EdgeGap add y0 EdgeGap sub
		2 copy moveto EdgeLen neg 0 rlineto
		moveto 0 EdgeLen rlineto
		x1 EdgeGap sub y0 EdgeGap add
		2 copy moveto EdgeLen neg 0 rlineto
		moveto 0 EdgeLen rlineto

		Hands? {
		    x0 x1 add .5 mul y0 2 copy NorthHand SouthHand
		    x1 y0 y1 add .5 mul 2 copy EastHand WestHand
		} if
	    }
	    /SouthWest {
		x0 EdgeGap sub y0 EdgeGap sub
		2 copy moveto EdgeLen 0 rlineto
		moveto 0 EdgeLen rlineto
		x0 EdgeGap add y0 EdgeGap add
		2 copy moveto EdgeLen 0 rlineto
		moveto 0 EdgeLen rlineto

		Hands? {
		    x0 x1 add .5 mul y0 2 copy NorthHand SouthHand
		    x0 y0 y1 add .5 mul 2 copy EastHand WestHand
		} if
	    }
	} case

	stroke
	pause
    } def

    /FinishAdjustEdge { % - => -
	gsave
	    x0 y0 x1 y1 points2rect
	    /UnTrackBBox self send
	    Parent setcanvas
	    edge dup /Vertical eq 1 index /Horizontal eq or exch /All eq or {
		pop pop /move self send
	    } {
		/reshape self send
%		currentprocess /eventmgr self send eq {
%		    /invalidate self send /damage self send
%		} if
	    } ifelse
	grestore
    } def

% Demo:

    /ClassBaseWindow { ClassTabBaseWindow } def
    /ClassPopupWindow { ClassTabPopupWindow } def

    /demo {
	userdict begin
	    framebuffer setcanvas

	    1 1 4 { /i exch def
	        /demo super send pop
		/win exch def
		i (Tab Demo #%) sprintf /setlabel win send
		userdict win dup put
		/QuitFromUser {
		    userdict self undef /QuitFromUser super send
		} /installmethod win send
	    } for
	    currentdict /win undef
	end
    } def

classend def

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ClassTabBaseWindow
%

/ClassTabBaseWindow [
    ClassPieMenuWindowManager ClassTabWindow ClassBaseWindow
] []
classbegin

    /IconFont /LucidaSans findfont 10 scalefont 
	      false printermatchfont
    	      /ISOLatin1Encoding encodefont def

    /setlabel { % - => -
	/IconLabel promoted? not {
	    dup /seticonlabel self send
	} if
        /setlabel super send
    } def

    /seticonlabel { % - => -
        /seticonlabel super send
	/opened? self send /valid? self send and {
	    /UpdateTab self send
	} if
    } def

    /IconSize { % - => w h
        /TabSize super send
    } def

    /IconImage null def

    /DefaultIconSize { % - => w h
        /TabSize super send
    } def

    /IconXY { % - => x y
        /location self send
	/TabXY self send xyadd
    } def

    /PaintIcon { % - => -
        /PaintTab self send
    } def

    /path { % x y w h => -
	Opened? {
	    /TabPath self send
        } {
	    rectpath
	} ifelse
    } def

    /ToggleOpened { % - => - 
	/Opened? Opened? not def
	gsave
	    Parent setcanvas
	    Opened? {
		/location self send			% x y

		/TabSize unpromote
		/TabXY unpromote

		BaseWindowBBox				% x y wx wy ww wh
		4 2 roll pop pop			% x y ww wh

		2 copy 6 2 roll				% ww wh x y ww wh 
		//EdgeTabXYDict TabEdge get exec	% ww wh x y tx ty
		xysub 4 2 roll				% wx wy ww wh

		/reshape self send
		/clientlist self send {/map exch send} forall
		/Valid? true def
	    } {
		/BaseWindowBBox [ /bbox self send ] cvx def
		IconXY IconSize /reshape self send
		/clientlist self send {/unmap exch send} forall
	    } ifelse
	grestore
    } def

    /ShowWhereStart { % event => -
	gsave
	    TrackDict /Can Parent createoverlay dup setcanvas put
	    /location self send			% x y
	    BaseWindowBBox			% x y wx wy ww wh
	    4 2 roll pop pop			% x y ww wh

	    2 copy 6 2 roll			% ww wh x y ww wh 
	    //EdgeTabXYDict TabEdge get exec	% ww wh x y tx ty
	    xysub 4 2 roll			% wx wy ww wh
	    4 copy /TabPath self send
	    4 2 roll -1 1 xyadd 4 2 roll /TabPath self send
	    stroke
	grestore
    } def

    /ShowWhereStop { % event => -
	gsave
	    TrackDict /Can get setcanvas
	    erasepage
	    TrackDict cleanoutdict
	grestore
    } def

classend def

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ClassTabPopupWindow
%

/ClassTabPopupWindow [
    ClassPieMenuWindowManager ClassTabWindow ClassPopupWindow
] []
classbegin

    /ToggleOpened { % - => -
	/ToggleOpened super send
	/TabXY unpromote
	/opened? self send { /damage self send } if
    } def

classend def

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

openwinversion 0 get 51 eq { % XXX: V3
    currentdict endautoload end % TNT
    endpackage endpackage endpackage
} { % XXX: V2
    end % systemdict
} ifelse

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

==== End of file: cut here =============================================