Compiler ObjectOps/n;bind griffin;griffin
mstone November 10, 1980 5:29 PM
Last Edited by: Stone, July 6, 1985 4:20:34 pm PDT
DIRECTORY
GriffinDefs: FROM "GriffinDefs",
Rope USING [ROPE, Equal],
MenuDefs: FROM "MenuDefs",
ObjectDefs: FROM "ObjectDefs",
RealFns: FROM "RealFns",
StyleDefs: FROM "StyleDefs",
PointDefs: FROM "PointDefs",
OpDefs: FROM "OpDefs",
XFormDefs: FROM "XFormDefs",
GriffinMemoryDefs: FROM "GriffinMemoryDefs",
RefreshDefs: FROM "RefreshDefs",
GriffinMenusInterface,
RelationDefs: FROM "RelationDefs";
ObjectOps: CEDAR PROGRAM
IMPORTS GriffinDefs, MenuDefs, ObjectDefs, Rope, GriffinMemoryDefs, RealFns, XFormDefs, PointDefs, RefreshDefs, RelationDefs
EXPORTS GriffinDefs, OpDefs, GriffinMenusInterface =
BEGIN OPEN ObjectDefs,PointDefs,GriffinMemoryDefs;
xftype: {map,scale,scalex,scaley,rotate};
oltype: {top,bottom,up1,down1};
TL,BR: ScrPt;
ROPE: TYPE = Rope.ROPE;
MarkObject: ObjectProc = {RefreshDefs.MarkObject[obj]};
EraseAndSave: ObjectProc = {RefreshDefs.EraseAndSave[obj]};
OverallMBB: ObjectProc=
BEGIN
IF obj.tl[X]<TL[X] THEN TL[X] ← obj.tl[X];
IF obj.tl[Y]<TL[Y] THEN TL[Y] ← obj.tl[Y];
IF obj.br[X] >BR[X] THEN BR[X] ← obj.br[X];
IF obj.br[Y] >BR[Y] THEN BR[Y] ← obj.br[Y];
END;
AnySelected: PROCEDURE RETURNS[BOOLEAN] =
BEGIN
anyselected: BOOLEANFALSE;
IsSelected: ObjectProc=BEGIN
anyselected ← TRUE;
END;
ForAllSelectedDo[IsSelected];
RETURN[anyselected];
END;
Delete: PUBLIC MenuDefs.MenuProc =
BEGIN
DeleteObjs: ObjectProc=BEGIN
DeSelectObject[obj];
RefreshDefs.EraseAndSave[obj];
obj.deleted ← TRUE;
END;
IF AnySelected[] THEN BEGIN
ExpungeObjects[];
ForAllSelectedDo[DeleteObjs];
RefreshDefs.RestoreScreen[];
END;
END;
Undo: PUBLIC MenuDefs.MenuProc =
BEGIN
Undelete: ObjectProc = BEGIN
IF ~obj.deleted THEN RETURN;
obj.deleted ← FALSE;
IF obj.cluster # 0 THEN [] ← SelectCluster[obj.cluster]
ELSE [] ← SelectObject[obj];
RefreshDefs.MarkObject[obj];
END;
ForAllObjects[Undelete];
RefreshDefs.RestoreScreen[];
END;
Transfer: PUBLIC MenuDefs.MenuProc =
BEGIN
view: View ← IF objectMenu.view=main THEN alternate ELSE main;
token: ObjectHandle ← NIL;
TransferObjs: ObjectProc=BEGIN
RefreshDefs.EraseAndSave[obj];
IF (token ← ReturnSelectToken[obj]) # NIL
THEN BEGIN
RefreshDefs.EraseAndSave[token];
token.view ← view;
END;
obj.view ← view;
END;
IF ~AnySelected[] THEN GriffinDefs.UserMessage["Please make a selection"];
ForAllSelectedDo[TransferObjs];
RefreshDefs.RestoreScreen[];
END;
SelObj: TYPE = RECORD[up,down: REF SelObj, item: ObjectHandle];
ChangeOverlap: PUBLIC MenuDefs.MenuProc =
BEGIN
string: ROPE ← MenuDefs.MenuString[item];
topObj,bottomObj: REF SelObj ← NIL;
ptr,next: REF SelObj ← NIL;
ListSelected: ObjectProc=BEGIN
selObj: REF SelObj 𡤌Zone.NEW[SelObj];
selObj^ ← [up: NIL,down: topObj,item: obj];
IF bottomObj=NIL THEN bottomObj ← topObj ← selObj
ELSE BEGIN topObj.up ← selObj; topObj ← selObj; END;
END;
Up: PROCEDURE=BEGIN
selObj: REF SelObj ←topObj;
FOR selObj ← topObj, selObj.down UNTIL selObj=NIL DO
FlipUpObject[selObj.item];
ENDLOOP;
END;
Down: PROCEDURE=BEGIN
selObj: REF SelObj ;
FOR selObj ← bottomObj, selObj.up UNTIL selObj=NIL DO
FlipDownObject[selObj.item];
ENDLOOP;
END;
Bottom: PROCEDURE=BEGIN
selObj: REF SelObj ←topObj;
FOR selObj ← topObj, selObj.down UNTIL selObj=NIL DO
SinkObject[selObj.item];
ENDLOOP;
END;
Top: PROCEDURE=BEGIN
selObj: REF SelObj ;
FOR selObj ← bottomObj, selObj.up UNTIL selObj=NIL DO
FloatObject[selObj.item];
ENDLOOP;
END;
make a list of all items affected. Work in different order for different cases
IF ~AnySelected[] THEN GriffinDefs.UserMessage["Please make a selection"];
ForAllSelectedDo[ListSelected];
IF bottomObj=NIL THEN RETURN; --none found
oltype ← SELECT TRUE FROM
Rope.Equal[string,"Top",FALSE] => top,
Rope.Equal[string,"Bottom",FALSE] => bottom,
Rope.Equal[string,"Up One",FALSE] => up1,
Rope.Equal[string,"Down One",FALSE] => down1,
ENDCASE => ERROR;
SELECT oltype FROM
top => Top[];
bottom => Bottom[];
up1 => Up[];
down1 => Down[];
ENDCASE;
ForAllSelectedDo[MarkObject];
RefreshDefs.RestoreScreen;
ptr ← bottomObj;
UNTIL ptr=NIL DO
next ←ptr.up;
ptr.up ← NIL;
ptr ← next;
ENDLOOP;
END;
XForm: PUBLIC MenuDefs.MenuProc =
BEGIN
ENABLE XFormDefs.ProblemWithXForms => BEGIN
IF Rope.Equal[string,"Singular transform",FALSE] THEN
SIGNAL GriffinDefs.UserMessage["Transform will flatten picture"]
ELSE SIGNAL GriffinDefs.UserMessage["ProblemWith Transform"];
END;
string: ROPE ← MenuDefs.MenuString[item];
cps: PointDefs.ObjPtSequence ← ReadCPs[];
matrix: XFormDefs.XFMDescriptor ← CZone.NEW[XFormDefs.XFormMatrix];
clusterRelation: RelationDefs.Relation;
MoveObjs: ObjectProc=BEGIN
MoveObject[obj,ObjToScr[cps[0]],ObjToScr[cps[1]]];
END;
CopyObjs: ObjectProc=TRUSTED BEGIN
OPEN RelationDefs;
newobj: ObjectHandle;
newcluster: ClusterID;
IF NOT obj.selected OR NOT Visible[obj] THEN RETURN;
newobj ← CopyObject[obj];
IF obj.cluster # 0 THEN
BEGIN
IF Right[clusterRelation,obj.cluster] = notFound
THEN BEGIN
newcluster ← GetNextClusterID[];
AddPair[clusterRelation, obj.cluster,newcluster];
END
ELSE newcluster ← Right[clusterRelation,obj.cluster];
RefreshDefs.EraseAndSave[ReturnSelectToken[obj]];
newobj.cluster ← newcluster;
END
ELSE BEGIN
[] ← SelectObject[newobj];
RefreshDefs.EraseAndSave[ReturnSelectToken[obj]];
DeSelectObject[obj];
END; 
END;
SelectNewClusters: PROCEDURE[leftPart, rightPart: CARDINAL] =
BEGIN
selects new clusters formed after XForm with copy
[] ← SelectCluster[rightPart];
DeSelectCluster[leftPart];
END;
XFormObjs: ObjectProc=BEGIN
ObjectDefs.XFormObject[obj,matrix];
END;
IF cps=NIL OR cps.length <=1 THEN BEGIN
GriffinDefs.UserMessage["All transforms need at least two points"];
RETURN;
END;
IF ~AnySelected[] THEN GriffinDefs.UserMessage["Please make a selection"];
XFormDefs.InitXForms[matrix];
xftype ← SELECT TRUE FROM
Rope.Equal[string,"Map",FALSE] => map,
Rope.Equal[string,"Scale",FALSE] => scale,
Rope.Equal[string,"ScaleX",FALSE] => scalex,
Rope.Equal[string,"ScaleY",FALSE] => scaley,
Rope.Equal[string,"Rotate",FALSE] => rotate,
ENDCASE => ERROR;
SELECT xftype FROM
map => Map[matrix];
scale => Scale[matrix,both];
scalex => Scale[matrix,xonly];
scaley => Scale[matrix,yonly];
rotate => Rotate[matrix];
ENDCASE;
RefreshDefs.EraseAndSaveAllCPs[];
DeleteAllCPs[];
will use the selection
IF MenuDefs.IsSelected[copy] THEN
TRUSTED BEGIN
clusterRelation ← RelationDefs.CreateRelation[];
ForAllObjectsThroughObject[CopyObjs, GetTopPictureObj[]];
RelationDefs.ForAllPairs[clusterRelation,SelectNewClusters];
RelationDefs.DestroyRelation[clusterRelation];
END
ELSE ForAllSelectedDo[ EraseAndSave];
IF cps.length=2 AND xftype=map THEN BEGIN
ForAllSelectedDo[MoveObjs];
END
ELSE BEGIN
ForAllSelectedDo[XFormObjs];
END;
ForAllSelectedDo[ MarkObject];
RefreshDefs.RestoreScreen[];
END;
Toggle: PUBLIC MenuDefs.MenuProc =
BEGIN OPEN MenuDefs;
IF IsSelected[item] THEN Deselect[item] ELSE Select[item];
END;
Rotate: PROCEDURE[matrix: XFormDefs.XFMDescriptor] =
BEGIN OPEN XFormDefs;
cps: PointDefs.ObjPtSequence ← ReadCPs[];
npts: INTEGER ← (IF cps=NIL THEN 0 ELSE cps.length);
dorig: ObjPt ← cps[0];
negdorig: ObjPt ← [-cps[0][X],-cps[0][Y]];
dx,dy,theta: REAL;
IF npts>3 THEN GriffinDefs.UserMessage["Only 2 and 3 point rotate is valid"];
dx ← cps[1][X]-cps[0][X];
dy ← cps[1][Y]-cps[0][Y];
theta ← RealFns.ArcTan[dy,dx];
IF npts>=3 THEN BEGIN
theta1: REAL;
dx ← cps[2][X]-cps[0][X];
dy ← cps[2][Y]-cps[0][Y];
theta1 ← RealFns.ArcTan[dy,dx];
theta ← theta1-theta;
END;
Translate[dorig,matrix];
Rotate[theta,z,matrix];
Translate[negdorig,matrix];
END;
will mirror. Imagine a pinned point and a new point
Scale: PROCEDURE[matrix: XFormDefs.XFMDescriptor,direction: {both,xonly,yonly}] =
BEGIN OPEN XFormDefs;
cps: PointDefs.ObjPtSequence ← ReadCPs[];
npts: INTEGER ← (IF cps=NIL THEN 0 ELSE cps.length);
sx,sy: REAL;
dorig: ObjPt ← cps[0];
tl,br: ObjPt;
negdorig: ObjPt ← [-cps[0][X],-cps[0][Y]];
dx1,dy1,dx2,dy2: REAL;
IF npts>3 THEN GriffinDefs.UserMessage["Only 2 and 3 point scale is valid"];
scale max dimension into 2 pt span
IF npts=2 THEN BEGIN
TL ← [77777B,77777B]; --init for OverallMBB
BR ← [0,0];
ForAllSelectedDo[OverallMBB];
tl ← ScrToObj[TL];
br ← ScrToObj[BR];
dx1 ← br[X]-tl[X];
dy1 ← tl[Y]-br[Y]; --object space
dx2 ← ABS[cps[1][X]-cps[0][X]];
dy2 ← ABS[cps[1][Y]-cps[0][Y]];
END
assume 3 points
ELSE BEGIN
dx2 ← cps[2][X]-cps[0][X];
dy2 ← cps[2][Y]-cps[0][Y];
dx1 ← cps[1][X]-cps[0][X];
dy1 ← cps[1][Y]-cps[0][Y];
END;
Translate[dorig,matrix];
SELECT direction FROM
both => BEGIN
IF dx1=0 OR dy1=0 THEN GriffinDefs.UserMessage["Transform will flatten picture"];
sx ← dx2/dx1;
sy ← dy2/dy1;
Scale[[sx,sy],matrix];
END;
xonly => BEGIN
IF dx1=0 THEN GriffinDefs.UserMessage["Transform will flatten picture"];
sx ← dx2/dx1;
Scale[[sx,1],matrix];
END;
yonly => BEGIN
IF dy1=0 THEN GriffinDefs.UserMessage["Transform will flatten picture"];
sy ← dy2/dy1;
Scale[[1,sy],matrix];
END;
ENDCASE;
Translate[negdorig,matrix];
END;
Map: PROCEDURE[matrix: XFormDefs.XFMDescriptor] =
BEGIN OPEN XFormDefs;
cps: PointDefs.ObjPtSequence ← ReadCPs[];
npts: INTEGER ← (IF cps=NIL THEN 0 ELSE cps.length);
pts: PointDefs.ObjPtSequence;
dorig: ObjPt ← cps[0];
negdorig: ObjPt ← [-cps[0][X],-cps[0][Y]];
IF npts>6 THEN GriffinDefs.UserMessage["Maximum of 6 points for map"];
SELECT npts FROM
0,1 NOP, 2 is a move, which is done above
=3 => BEGIN
pts ← CZone.NEW[ObjPtSequenceRec[4]];
pts[0] ← cps[0];
pts[1] ← cps[1];
pts[2] ← cps[0];
pts[3] ← cps[2];
npts ← 4;
END;
=4 => pts ← cps;
=5 => BEGIN
pts ← CZone.NEW[ObjPtSequenceRec[6]];
pts[0] ← cps[0];
pts[1] ← cps[1];
pts[2] ← cps[2];
pts[3] ← cps[0];
pts[4] ← cps[3];
pts[5] ← cps[4];
npts ← 6;
END;
ENDCASE=> pts ← cps;
Translate[dorig,matrix];
SELECT npts FROM
=4 => XForm4Pts[pts,matrix];
>=6 => XForm6Pts[pts,matrix];
ENDCASE;
Translate[negdorig,matrix];
END;
Cluster: PUBLIC MenuDefs.MenuProc =
BEGIN
id: ClusterID ← GetNextClusterID[];
called: BOOLEANFALSE;
token: ObjectHandle ← NIL;
clu: ObjectProc=BEGIN
obj.cluster ← id;
called ← TRUE;
IF token # NIL
THEN token ← DeleteObject[token];
token ← ReturnSelectToken[obj];
RefreshDefs.EraseAndSave[token];
END;
IF ~AnySelected[] THEN GriffinDefs.UserMessage["Please make a selection"];
ForAllSelectedDo[clu];
IF called THEN BEGIN
GriffinDefs.ShowUserMessage["New cluster made"];
RefreshDefs.RestoreScreen[];
END;
END;
UnCluster: PUBLIC MenuDefs.MenuProc =
BEGIN
called: BOOLEANFALSE;
ctoken: ObjectHandle;
clu: ObjectProc=BEGIN
select: ObjectHandle;
IF obj.cluster IN [0..OpenCluster] THEN RETURN;
obj.cluster ← 0;
called ← TRUE;
IF (ctoken ← ReturnSelectToken[obj]) # NIL THEN
BEGIN
RefreshDefs.EraseAndSave[ctoken];
RETURN;
END;
obj.selected ← FALSE;
select ← SelectObject[obj];
RefreshDefs.MarkObject[select];
END;
IF ~AnySelected[] THEN GriffinDefs.UserMessage["Please make a selection"];
ForAllSelectedDo[clu];
IF called THEN GriffinDefs.ShowUserMessage["Cluster unmade"];
END;
DeselectAll: PUBLIC MenuDefs.MenuProc =
BEGIN
DS: ObjectProc = BEGIN
RefreshDefs.EraseAndSave[ReturnSelectToken[obj]];
DeSelectObject[obj];
END;
ObjectDefs.ForAllSelectedDo[DS];
RefreshDefs.RestoreScreen[];
END;
SelectAll: PUBLIC MenuDefs.MenuProc =
BEGIN OPEN ObjectDefs;
SelectObjs: ObjectProc=BEGIN
token: ObjectHandle ← IF obj.cluster = 0
THEN SelectObject[obj]
ELSE SelectCluster[obj.cluster];
IF token#NIL THEN RefreshDefs.MarkObject[token];
END;
ForAllVisibleObjects[SelectObjs];
RefreshDefs.RestoreScreen[];
END;
make a translated copy of indicated object or set, given xy,xy
CopyObjects: PUBLIC PROCEDURE[Down,Up: ScrPt] =
BEGIN OPEN ObjectDefs;
new: ObjectHandle ← GetObjectHandle[Down];
selected: BOOLEAN;
id: ClusterID ← 0;
co: ObjectProc = BEGIN
IF selected THEN BEGIN
RefreshDefs.EraseAndSave[ReturnSelectToken[obj]];
IF id = 0 THEN DeSelectObject[obj];
END;
obj ← CopyObject[obj] ;
obj.cluster ← id;
MoveObject[obj,Down,Up];
RefreshDefs.MarkObject[obj];
IF selected AND id = 0 THEN [] ← SelectObject[obj];
clusters are selected below.
END;
IF new=NIL THEN RETURN;
selected ← new.selected;
IF new.objectType=menu OR new.objectType=token OR new.cluster=OpenCluster
THEN RETURN;
click doesn't got thru move/refresh stuff
IF ABS[Down[X]-Up[X]]<1 AND ABS[Down[Y]-Up[Y]]<1 THEN RETURN;
IF new.cluster=0
THEN [] ← co[new] -- not a clustered object
ELSE BEGIN
id ← GetNextClusterID[];
ForAllInCluster[new.cluster,co];
IF selected THEN BEGIN
DeSelectCluster[new.cluster];
[] ← SelectCluster[id];
END;
END;
RefreshDefs.RestoreScreen[];
END;
MoveObjects: PUBLIC PROCEDURE[Down,Up: ScrPt] =
BEGIN
object: ObjectDefs.ObjectHandle ← ObjectDefs.GetObjectHandle[Down] ;
move: ObjectProc = BEGIN
RefreshDefs.EraseAndSave[obj];
ObjectDefs.MoveObject[obj,Down,Up];
RefreshDefs.MarkObject[obj];
END;
IF object=NIL THEN RETURN;
click doesn't got thru move/refresh stuff
IF ABS[Down[X]-Up[X]]<1 AND ABS[Down[Y]-Up[Y]]<1 THEN RETURN;
IF object.cluster=OpenCluster THEN RETURN; --unfortuneate
IF object.cluster=0 THEN [] ← move[object] ELSE ForAllInCluster[object.cluster,move];
RefreshDefs.RestoreScreen[];
END;
PlaceControlPoint: PUBLIC PROCEDURE[pt: ScrPt] =
BEGIN
top: ObjectHandle ← GetObjectHandle[pt];
IF top=NIL OR top.objectType#menu THEN ObjectDefs.AddToken[pt, CP]
END;
DeleteControlPoint: PUBLIC PROCEDURE[Down,Up: ScrPt] =
BEGIN OPEN ObjectDefs;
DelCP: ObjectProc = TRUSTED BEGIN
IF obj=NIL THEN RETURN;
WITH obj SELECT FROM
token=>
IF tokenType = CP THEN BEGIN
RefreshDefs.EraseAndSave[obj];
[]�leteObject[obj];
END;
ENDCASE;
END;
IF NOT ForAllInBoxDo[Down, Up, DelCP] THEN
ForObjectPointedAtDo[Down, Up, DelCP];
RefreshDefs.RestoreScreen[];
END;
DeselectObjects: PUBLIC PROCEDURE[Down,Up: ScrPt] =
BEGIN
Desel: ObjectProc = BEGIN
IF obj=NIL OR obj.objectType=token OR obj.objectType=menu THEN RETURN;
IF obj.cluster = 0 THEN [] ← DS[obj]
ELSE ForAllInCluster[obj.cluster, DS];
END;
DS: ObjectProc = BEGIN
RefreshDefs.EraseAndSave[ReturnSelectToken[obj]];
DeSelectObject[obj];
END;
IF NOT ForAllPictureObjectsInBoxDo[Down, Up, Desel]
THEN ForObjectPointedAtDo[Down, Up, Desel];
RefreshDefs.RestoreScreen[];
END;
SelectObjects: PUBLIC PROCEDURE[Down,Up: ScrPt] =
BEGIN OPEN ObjectDefs,RefreshDefs;
Sel: ObjectProc = BEGIN
IF obj=NIL OR obj.objectType=token OR obj.objectType=menu OR obj.cluster=OpenCluster
THEN RETURN;
[] ← S[obj];
END;
S: ObjectProc = BEGIN
obj ← IF obj.cluster = 0 THEN SelectObject[obj]
ELSE SelectCluster[obj.cluster];
get the token, may return NIL if already selected
if clustered, will select whole cluster
IF obj#NIL THEN MarkObject[obj];
END;
IF NOT ForAllPictureObjectsInBoxDo[Down, Up, Sel]
THEN ForObjectPointedAtDo[Down, Up, Sel];
RefreshDefs.RestoreScreen[];
END;
ForObjectPointedAtDo: PROCEDURE[Down,Up: ScrPt,proc: ObjectProc] =
BEGIN
upObj,downObj: ObjectHandle;
upObj ← GetObjectHandle[Up];
IF upObj=NIL THEN RETURN;
downObj ← GetObjectHandle[Down];
IF upObj=downObj OR downObj=NIL THEN [] ← proc[upObj] ;
END;
objectMenu, xformMenu, overlapMenu: MenuDefs.MenuHandle←NIL;
copy: MenuDefs.MenuItemHandle←NIL;
ToggleObjectMenu: PUBLIC PROC = {
IF objectMenu.visible THEN MenuDefs.HideMenu[objectMenu]
ELSE MenuDefs.ShowMenu[objectMenu];
RefreshDefs.RestoreScreen[];
};
ToggleTransformMenu: PUBLIC PROC = {
IF xformMenu.visible THEN MenuDefs.HideMenu[xformMenu]
ELSE MenuDefs.ShowMenu[xformMenu];
RefreshDefs.RestoreScreen[];
};
ToggleOverlapMenu: PUBLIC PROC = {
IF overlapMenu.visible THEN MenuDefs.HideMenu[overlapMenu]
ELSE MenuDefs.ShowMenu[overlapMenu];
RefreshDefs.RestoreScreen[];
};
StartObjectMenus: PUBLIC PROC = {
objectMenu ← MenuDefs.CreateMenu[horizontal, [100,600], NIL];
[]←MenuDefs.AddMenuItem[objectMenu,"Delete",Delete];
[]←MenuDefs.AddMenuItem[objectMenu,"Undo",Undo];
[]←MenuDefs.AddMenuItem[objectMenu,"Select all",SelectAll];
[]←MenuDefs.AddMenuItem[objectMenu,"Deselect all",DeselectAll];
[]←MenuDefs.AddMenuItem[objectMenu,"Cluster",Cluster];
[]←MenuDefs.AddMenuItem[objectMenu,"Uncluster",UnCluster];
[]←MenuDefs.AddMenuItem[objectMenu,"Transfer",Transfer];
xformMenu ← MenuDefs.CreateMenu[vertical, [400,400], NIL];
[]←MenuDefs.AddMenuItem[xformMenu,"Map",XForm];
[]←MenuDefs.AddMenuItem[xformMenu,"Scale",XForm];
[]←MenuDefs.AddMenuItem[xformMenu,"ScaleX",XForm];
[]←MenuDefs.AddMenuItem[xformMenu,"ScaleY",XForm];
[]←MenuDefs.AddMenuItem[xformMenu,"Rotate",XForm];
copy ← MenuDefs.AddMenuItem[xformMenu,"Use Copy",Toggle];
overlapMenu ← MenuDefs.CreateMenu[vertical, [200,250], NIL];
[]←MenuDefs.AddMenuItem[overlapMenu,"Top",ChangeOverlap];
[]←MenuDefs.AddMenuItem[overlapMenu,"Bottom",ChangeOverlap];
[]←MenuDefs.AddMenuItem[overlapMenu,"Up One",ChangeOverlap];
[]←MenuDefs.AddMenuItem[overlapMenu,"Down One",ChangeOverlap];
};
END.