CDCommandOpsImpl.mesa
Copyright © 1984, 1986 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi, July 12, 1984 3:49:16 pm PDT
Last Edited by: Christian Jacobi, September 4, 1986 3:45:42 pm PDT
DIRECTORY
Atom,
CDBasics,
CD,
CDInstances,
CDCallSpecific,
CDCommandOps,
CDCommandOpsExtras,
CDCommandOpsExtras2,
CDCommandOpsExtras3,
CDEvents,
CDLayers,
CDOps,
CDPopUpMenus,
CDPrivate,
CDProperties,
CDSequencer,
CDValue,
CDViewer,
Convert,
IO,
PopUpMenus,
Process,
RefTab,
Rope,
SymTab,
TerminalIO,
ViewerClasses USING [Viewer],
ViewerOps USING [GetViewer];
CDCommandOpsImpl:
CEDAR
MONITOR
IMPORTS Atom, CD, CDBasics, CDInstances, CDCallSpecific, CDCommandOps, CDEvents, CDLayers, CDOps, CDPopUpMenus, CDProperties, CDSequencer, CDValue, CDViewer, Convert, IO, PopUpMenus, Process, RefTab, Rope, SymTab, TerminalIO, ViewerOps
EXPORTS CDCommandOps, CDCommandOpsExtras, CDCommandOpsExtras2, CDCommandOpsExtras3 =
BEGIN
-- -- -- -- -- --
--generic
ProbablyLastChar:
PROC [r: Rope.
ROPE]
RETURNS [
CHAR] = {
l: INT = r.Length[];
IF l<=0 THEN RETURN ['@];
RETURN [r.Fetch[l-1]]
};
-- -- -- -- -- --
--ImplementSpecificCommand
Entry: TYPE = RECORD [key: ATOM, text: Rope.ROPE←NIL, x: REF];
tableKey: REF = NEW[ATOM←$tableKey]; -- indirect to make inaccessible
gTable: SymTab.Ref = SymTab.Create[];
GetTTable:
PROC [t:
CD.Technology]
RETURNS [table: SymTab.Ref] = {
IF t=NIL THEN table←gTable
ELSE {
x: REF ← CDValue.Fetch[boundTo: t, key: tableKey, propagation: technology];
IF x=
NIL
THEN {
x ← SymTab.Create[7];
CDValue.Store[boundTo: t, key: tableKey, value: x]
};
table ← NARROW[x]
}
};
GetEntry:
PROC [tipKey: Rope.
ROPE, tech:
CD.Technology]
RETURNS [entry:
REF Entry←
NIL] = {
IF tipKey.Length[]>=2
THEN {
x: REF;
found: BOOL ← FALSE;
tipKey ← tipKey.Substr[len: tipKey.Length[]];
IF tech#NIL THEN [found, x] ← SymTab.Fetch[GetTTable[tech], tipKey];
IF NOT found THEN [found, x] ← SymTab.Fetch[gTable, tipKey];
IF found
THEN
WITH x SELECT FROM e: REF Entry => entry𡤎 ENDCASE => NULL
}
};
GeneralCommand:
PROC [comm: CDSequencer.Command] = {
n: NAT𡤀
t: Rope.ROPE = Atom.GetPName[comm.key];
entry: REF Entry = GetEntry[t, comm.design.technology];
IF entry=
NIL
THEN {
TerminalIO.WriteRopes["unknown command: ", t];
}
ELSE {
TerminalIO.WriteRopes[entry.text, " "];
SELECT ProbablyLastChar[t]
FROM
'S => {
TerminalIO.WriteRope["selected "];
n ← CDCallSpecific.CallForSelected[design: comm.design, objectSpecific: entry.key, x: entry.x];
};
'P => {
TerminalIO.WriteRope["pointed "];
n ← CDCallSpecific.CallForPointed[design: comm.design, point: comm.pos, objectSpecific: entry.key, x: entry.x];
};
'A => {
TerminalIO.WriteRope["all "];
n ← CDCallSpecific.CallForAll[design: comm.design, objectSpecific: entry.key, x: entry.x];
};
'X => {
TerminalIO.WriteRope["(if 1 selected) "];
n ← CDCallSpecific.CallIfOneSelected[design: comm.design, objectSpecific: entry.key, x: entry.x];
};
'F => {
TerminalIO.WriteRope["(first selected) "];
n ← CDCallSpecific.CallForOneSelected[design: comm.design, objectSpecific: entry.key, x: entry.x];
};
ENDCASE => TerminalIO.WriteRope["bad modifier"];
};
TerminalIO.WriteF["\n%gobjects handled\n", [integer[n]]];
};
ImplementSpecificCommand:
PUBLIC PROC [specificAtom:
ATOM, text: Rope.
ROPE←
NIL, tipBase: Rope.
ROPE←
NIL, useFor: Rope.
ROPE←
NIL, x:
REF←
NIL, technology:
CD.Technology←
NIL] = {
--Implements a command which is executed by using CDCallSpecific
--specificAtom: handled through to select CDCallSpecific command
--text: logged; defaults to tipBase
--tipBase: How command is called in tiptable; defaults to specificAtom
--useFor: Suffix letters appended to tipBase getting the tip table entry
--x: handled through to CDCallSpecific
--technology: NIL => all technologies
t: SymTab.Ref = GetTTable[technology];
entry: REF Entry;
EachKey: Rope.ActionType
-- PROC [c: CHAR] RETURNS [quit: BOOL ← FALSE] -- = {
SELECT c
FROM
'P, 'S, 'A, 'F, 'X => {
fiddledTip: Rope.ROPE ← Rope.Concat[tipBase, Rope.FromChar[c]];
[] ← SymTab.Store[t, fiddledTip, entry];
CDSequencer.ImplementCommand[Atom.MakeAtom[fiddledTip], GeneralCommand, technology];
}
ENDCASE => NULL
};
--set up defaults
IF useFor=NIL THEN useFor ← "PS";
IF tipBase=NIL THEN tipBase ← Atom.GetPName[specificAtom];
IF text=NIL THEN text ← tipBase;
SELECT ProbablyLastChar[text]
FROM
'\n, ' => text ← text.Substr[len: text.Length[]-1];
ENDCASE => NULL;
--do it
entry ← NEW[Entry ← [key: specificAtom, text: text, x: x]];
[] ← Rope.Map[base: useFor, action: EachKey];
};
-- -- -- -- -- --
--TheInstance
TheInstance:
PUBLIC PROC[comm: CDSequencer.Command, text: Rope.
ROPE←
NIL]
RETURNS [inst:
CD.Instance←
NIL] = {
--extracts the application given a command
--if returned application is nil, all the messages are made and caller should return quiet;
--if returned application is not nil; text line is written and object is there
multiple: BOOL;
key: Rope.ROPE ← Atom.GetPName[comm.key];
IF text=NIL THEN text ← key;
SELECT ProbablyLastChar[text]
FROM
'\n, ' => text ← text.Substr[len: text.Length[]-1];
ENDCASE => NULL;
TerminalIO.WriteRopes[text, " "];
SELECT ProbablyLastChar[key]
FROM
'S => {
TerminalIO.WriteRope["selected\n"];
[inst, multiple] ← CDOps.SelectedInstance[comm.design];
IF multiple THEN {TerminalIO.WriteRope[" multiple selection\n"]; RETURN [NIL]};
IF inst=NIL THEN TerminalIO.WriteRope[" no selection\n"];
};
'P => {
TerminalIO.WriteRope["pointed\n"];
inst ← CDInstances.InstanceAt[CDOps.InstList[comm.design], comm.pos];
IF inst=NIL THEN TerminalIO.WriteRope[" no pointed application\n"];
};
'X => {
TerminalIO.WriteRope["(if 1 selected)\n"];
[inst, multiple] ← CDOps.SelectedInstance[comm.design];
IF multiple THEN {TerminalIO.WriteRope[" multiple selection\n"]; RETURN [NIL]};
IF inst=NIL THEN TerminalIO.WriteRope[" no selection\n"];
};
'F => {
-- and specially 'F
TerminalIO.WriteRope["(first selected)\n"];
[inst, multiple] ← CDOps.SelectedInstance[comm.design];
IF inst=NIL THEN TerminalIO.WriteRope[" no selection\n"];
};
ENDCASE => {
-- same as 'S
TerminalIO.WriteRope["selected\n"];
[inst, multiple] ← CDOps.SelectedInstance[comm.design];
IF multiple THEN {TerminalIO.WriteRope[" multiple selection\n"]; RETURN [NIL]};
IF inst=NIL THEN TerminalIO.WriteRope[" no selection\n"];
};
IF inst#
NIL
THEN {
IF inst.ob=NIL THEN {inst←NIL; TerminalIO.WriteRope[" bad object\n"]};
};
};
-- -- -- -- -- --
--Vanilla stuff
BoundingBox:
PUBLIC
PROC [design:
CD.Design, onlySelected:
BOOL]
RETURNS [r:
CD.Rect ← CDBasics.empty] = {
IF onlySelected THEN r ← CDInstances.BoundingRectO[CDOps.InstList[design], TRUE]
ELSE
FOR l:
LIST
OF
CD.PushRec ← design.actual, l.rest
WHILE l#
NIL
DO
r ← CDBasics.Surround[r, CDInstances.BoundingRectO[
NARROW[l.first.dummyCell.ob.specificRef, CD.CellPtr].contents ]]
ENDLOOP;
};
InstRope:
PUBLIC
PROC[inst:
CD.Instance, verbosity:
INT𡤀]
RETURNS [r: Rope.
ROPE] = {
IF inst=NIL THEN r ← "nil instance"
ELSE {
r ←
IF inst.ob=NIL THEN "nil object"
ELSE IF inst.ob.class.describeInst#NIL THEN inst.ob.class.describeInst[inst]
ELSE CDOps.ObjectInfo[inst.ob];
IF verbosity>0
THEN {
WITH CDProperties.GetInstanceProp[inst, $SignalName]
SELECT
FROM
n: Rope.ROPE => r ← Rope.Cat[r, " ", n];
a: ATOM => r ← Rope.Cat[r, " ", Atom.GetPName[a]];
ENDCASE => NULL;
};
};
RETURN [Rope.Cat["(", r, ")"]]
};
ToRope:
PUBLIC
PROC [x:
REF, whenFailed:
REF←
NIL]
RETURNS [rope: Rope.
ROPE←
NIL] = {
WITH x
SELECT
FROM
r: Rope.ROPE => rope ← r;
rt: REF TEXT => rope ← Rope.FromRefText[rt];
ri: REF INT => rope ← Convert.RopeFromInt[ri^];
a: ATOM => rope ← Atom.GetPName[a];
l: CDPrivate.LayerRef => rope ← CDOps.LayerName[l.number];
ob: CD.Object => rope ← CDOps.ObjectInfo[ob];
inst: CD.Instance => rope ← inst.ob.class.describeInst[inst];
d: CD.Design => rope ← d.name;
t: CD.Technology => rope ← t.name;
rc: REF LONG CARDINAL => rope ← Convert.RopeFromCard[rc^];
ri: REF INTEGER => rope ← Convert.RopeFromInt[ri^];
ri: REF NAT => rope ← Convert.RopeFromInt[ri^];
rc: REF CARDINAL => rope ← Convert.RopeFromCard[rc^];
ENDCASE =>
SELECT whenFailed
FROM
NIL => rope ← NIL;
$Interactive => {
RopeNeeded: SIGNAL [ ref: REF REF ] = CODE;
refRef: REF REF = NEW[REF ← x];
TerminalIO.WriteRope["please enter a ROPE using the debugger"];
SIGNAL RopeNeeded[refRef];
rope ← ToRope[refRef^ ! RopeNeeded => ERROR];
};
ENDCASE => rope ← ToRope[whenFailed];
};
LambdaRope:
PUBLIC
PROC [n:
CD.Number, lambda:
CD.Number𡤁]
RETURNS [Rope.
ROPE] = {
IF n MOD lambda = 0 THEN RETURN IO.PutFR1[" %g", IO.int[n/lambda]]
ELSE {
r: Rope.ROPE ← " (";
IF n<0 THEN {n ← ABS[n]; r ← " -("};
IF n/lambda>0 THEN r ← IO.PutFR["%0g%0g+", IO.rope[r], IO.int[n/lambda]];
RETURN [IO.PutFR["%0g%0g/%0g)", IO.rope[r], IO.int[n MOD lambda], IO.int[lambda]]];
}
};
-- -- -- -- -- --
--CallWithResource
reCheck: CONDITION ← [timeout: Process.MsecToTicks[1000]];
Enter:
ENTRY
PROC [resource:
REF, wait:
BOOL, design:
REF]
RETURNS [ok:
BOOL] = {
IF design=NIL THEN design ← $NIL;
DO
ok ← RefTab.Insert[resourceTab, resource, design];
IF ok OR ~wait THEN EXIT;
WAIT reCheck;
ENDLOOP
};
Leave:
ENTRY
PROC [resource:
REF] = {
[] ← RefTab.Delete[resourceTab, resource];
BROADCAST reCheck
};
CallWithResource:
PUBLIC
PROC [proc:
PROC[CDSequencer.Command], comm: CDSequencer.Command, resource:
REF, abortFlag:
REF
BOOL←
NIL, waitIfBusy:
BOOL ←
FALSE]
RETURNS [skipped:
BOOL←TRUE] = {
--Monitoring commands using global resourceTab
--proc will be called with comm as parameter, but is skipped if resource is already in use
--resource: typically atom; every resource is called only once at a time
--abortFlag: will be set to TRUE if an abort event occurs while execution of proc
--the procedure message on Terminal if it skipped the call, or, if abortFlag is true on return
ENABLE UNWIND => Leave[resource];
design: CD.Design = IF comm#NIL THEN comm.design ELSE NIL;
IF ~Enter[resource, waitIfBusy, design]
THEN
TerminalIO.WriteRope[" not reentrant; skipped\n"]
ELSE {
previous: REF ← CDValue.Fetch[design, abortFlagKey];
CDValue.Store[boundTo: design, key: abortFlagKey, value: abortFlag];
IF abortFlag#NIL THEN abortFlag^ ← FALSE;
proc[comm];
IF abortFlag#NIL AND abortFlag^ THEN TerminalIO.WriteRope[" aborted\n"] ELSE skipped ← FALSE;
CDValue.Store[boundTo: design, key: abortFlagKey, value: previous];
Leave[resource];
}
};
PlaceInst:
PUBLIC PROC [design:
CD.Design, ob:
CD.Object, commHint:
REF←
NIL]
RETURNS [inst:
CD.Instance] = {
GetGrid:
PROC [design:
CD.Design, commHint:
REF←
NIL]
RETURNS [g:
CD.Number𡤀] = {
--figures out grid used for a particular design or viewer
v: ViewerClasses.Viewer ← GetViewer[design, commHint];
IF v#
NIL
THEN {
WITH ViewerOps.GetViewer[v, $Grid]
SELECT
FROM
ri: REF INT => g ← ri^;
ENDCASE => NULL;
};
IF g<=0 THEN g ← design.technology.lambda;
};
GetViewer:
PROC [design:
CD.Design, commHint:
REF←
NIL]
RETURNS [ViewerClasses.Viewer←
NIL] = {
--figures out "the" viewer issued for a design
v: ViewerClasses.Viewer←NIL;
vL: CDViewer.ViewerList←NIL;
WITH commHint
SELECT
FROM
v2: ViewerClasses.Viewer => v ← v2;
comm: CDSequencer.Command =>
IF comm.design=design OR comm.design=NIL THEN v ← CDViewer.GetViewer[comm];
ENDCASE => NULL;
IF v#NIL AND CDViewer.DesignOf[v]=design THEN RETURN [v];
vL ← CDViewer.ViewersOf[design];
IF vL#NIL AND CDViewer.DesignOf[vL.first]=design THEN RETURN [vL.first];
};
lambda: CD.Number ← design.technology.lambda;
grid: CD.Number ← MAX[1, GetGrid[design, commHint]];
space, w1, w2: CD.Number;
bb: CD.Rect ← CDCommandOps.BoundingBox[design];
IF ~CDBasics.NonEmpty[bb] THEN bb ← [0, 0, 0, 0];
w1 ← MAX[lambda*5, bb.x2-bb.x1];
w2 ← MAX[lambda*5, CD.InterestSize[ob].x];
space ← MIN[w1, w2]/8+MAX[w1, w2]/80+lambda*4;
inst ← CDInstances.NewInstI[ob: ob,
location: [(bb.x2+space+grid-1)/grid*grid, (bb.y1+grid-1)/grid*grid]
];
CDOps.IncludeInstance[design, inst];
};
AbortEvent: CDEvents.EventProc = {
--PROC [event: REF, design: CD.Design, x: REF] RETURNS [dont: BOOLLSE]
EachResource: RefTab
.EachPairAction = {
--PROC [key: Key, val: Val] RETURNS [quit: BOOLEAN]
quit ← FALSE;
IF design=
NIL
OR design=val
OR val=$NIL
THEN
WITH CDValue.Fetch[boundTo: val, key: abortFlagKey]
SELECT
FROM
abortFlag: REF BOOL => abortFlag^ ← TRUE;
ENDCASE => NULL;
}; -- of EachResource
[] ← RefTab.Pairs[resourceTab, EachResource]
};
cnt: INT ← 0;
RegisterWithMenu:
PUBLIC PROC [menu:
REF←
NIL, entry: Rope.
ROPE←
NIL, doc: Rope.
ROPE←
NIL, key:
ATOM←
NIL, proc: CDSequencer.CommandProc←
NIL, queue: CDSequencer.QueueMethod𡤍oQueue, tech:
CD.Technology←
NIL] = {
IF key=NIL THEN key ← Atom.MakeAtom[IO.PutFR1["a%g", IO.int[cnt ← cnt+1]]];
IF proc#
NIL
THEN
CDSequencer.ImplementCommand[key: key, proc: proc, queue: queue, technology: tech];
IF menu#
NIL
THEN
[] ← PopUpMenus.Entry[CDPopUpMenus.GetMenu[menu], entry, NIL, key, doc];
};
SetCurrentLayer:
PROC [comm: CDSequencer.Command] = {
props: CDProperties.PropRef ← GetMyProps[comm.key];
x: REF ← CDProperties.GetProp[props, comm.design.technology.key];
IF x=NIL THEN x ← CDProperties.GetProp[props, $all];
WITH x
SELECT
FROM
lora:
LIST
OF
REF
ANY => {
lay: CD.Layer ← CD.errorLayer;
w: INT ← -1;
WITH lora.first
SELECT
FROM
l: REF CD.Layer => lay ← l^;
ENDCASE => NULL;
IF lora.rest#
NIL
THEN
WITH lora.rest.first
SELECT
FROM
i: REF INT => w ← i^
ENDCASE => NULL;
IF w>=0 THEN CDLayers.SetLayerWidth[comm.design, lay, w];
CDLayers.SetCurrentLayer[comm.design, lay];
TerminalIO.WriteRopes["set default layer: ", CDOps.LayerName[lay], "\n"];
};
ENDCASE => TerminalIO.WriteF["command %g failed\n", [atom[comm.key]]];
};
myKey: REF INT ← NEW[INT];
GetMyProps:
ENTRY
PROC [a:
ATOM]
RETURNS [props: CDProperties.PropRef←NIL] = {
ENABLE UNWIND => NULL;
IF a=NIL THEN RETURN WITH ERROR CD.Error[noResource];
WITH CDProperties.GetAtomProp[a, myKey]
SELECT
FROM
pr: CDProperties.PropRef => RETURN [pr];
ENDCASE => NULL;
props ← CDProperties.InitPropRef[];
CDProperties.PutAtomProp[a, myKey, props];
};
RegisterCurrentLayerCommand:
PUBLIC PROC [key:
ATOM, layer:
CD.Layer, tech:
CD.Technology, w:
CD.Number←-1] = {
props: CDProperties.PropRef ← GetMyProps[key];
tKey: ATOM ← IF tech#NIL THEN tech.key ELSE $all;
CDProperties.PutProp[props, tKey, LIST[NEW[CD.Layer←layer], NEW[INT←w]]];
CDSequencer.ImplementCommand[key: key, proc: SetCurrentLayer, queue: doQueue, technology: tech];
};
resourceTab: RefTab.Ref ~ RefTab.Create[3];
abortFlagKey: REF ← resourceTab;
CDEvents.RegisterEventProc[$Abort, AbortEvent];
END.