CDCommandOpsImpl.mesa
Copyright © 1984, 1986 by Xerox Corporation. All rights reserved.
by Christian Jacobi, July 12, 1984 3:49:16 pm PDT
last edited Christian Jacobi, March 28, 1986 5:11:34 pm PST
Last Edited by: Jacobi June 2, 1986 5:14:49 pm PDT
DIRECTORY
Atom,
CDBasics,
CD,
CDInstances,
CDCallSpecific,
CDCommandOps,
CDEvents,
CDMenus,
CDOps,
CDPrivate,
CDProperties,
CDSequencer,
CDValue,
IO,
RefTab,
Rope,
SymTab,
TerminalIO;
CDCommandOpsImpl: CEDAR MONITOR
IMPORTS Atom, CDBasics, CDInstances, CDCallSpecific, CDEvents, CDMenus, CDOps, CDProperties, CDSequencer, CDValue, IO, RefTab, Rope, SymTab, TerminalIO
EXPORTS CDCommandOps =
BEGIN
-- -- -- -- -- --
--generic
ProbablyLastChar: PROC [r: Rope.ROPE] RETURNS [CHAR] =
BEGIN
l: INT = r.Length[];
IF l<=0 THEN RETURN ['@];
RETURN [r.Fetch[l-1]]
END;
-- -- -- -- -- --
--ImplementSpecificCommand
Entry: TYPE = RECORD [key: ATOM, text: Rope.ROPENIL, 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] =
BEGIN
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]
}
END;
GetEntry: PROC [tipKey: Rope.ROPE, tech: CD.Technology] RETURNS [entry: REF Entry←NIL] =
BEGIN
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
}
END;
GeneralCommand: PROC [comm: CDSequencer.Command] =
BEGIN
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.WriteRope["\n "];
TerminalIO.WriteInt[n]; TerminalIO.WriteRope[" objects handled\n"];
END;
ImplementSpecificCommand: PUBLIC PROC [specificAtom: ATOM, text: Rope.ROPENIL, tipBase: Rope.ROPENIL, useFor: Rope.ROPENIL, x: REFNIL, 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
BEGIN
t: SymTab.Ref = GetTTable[technology];
entry: REF Entry;
EachKey: Rope.ActionType -- PROC [c: CHAR] RETURNS [quit: BOOL ← FALSE] -- =
BEGIN
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
END;
--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];
END;
-- -- -- -- -- --
--TheInstance
TheInstance: PUBLIC PROC[comm: CDSequencer.Command, text: Rope.ROPENIL] 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
BEGIN
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"]};
};
END;
-- -- -- -- -- --
--Vanilla stuff
BoundingBox: PUBLIC PROC [design: CD.Design, onlySelected: BOOL] RETURNS [r: CD.Rect ← CDBasics.empty] =
BEGIN
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;
END;
InstRope: PUBLIC PROC[inst: CD.Instance, verbosity: INT𡤀] RETURNS [r: Rope.ROPE] =
BEGIN
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, ")"]]
END;
ToRope: PUBLIC PROC [x: REF, whenFailed: REFNIL] RETURNS [rope: Rope.ROPE←NIL] =
BEGIN
WITH x SELECT FROM
r: Rope.ROPE => rope ← r;
rt: REF TEXT => rope ← Rope.FromRefText[rt];
i: REF INT => rope ← IO.PutFR["%0g", IO.int[i^]];
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;
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];
END;
LambdaRope: PUBLIC PROC [n: CD.Number, lambda: CD.Number𡤁] RETURNS [Rope.ROPE] =
BEGIN
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]]];
}
END;
-- -- -- -- -- --
--CallWithResource
reCheck: CONDITION;
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 BOOLNIL, waitIfBusy: BOOLFALSE] 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
BEGIN
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];
}
END;
AbortEvent: CDEvents.EventProc = {
--PROC [event: REF, design: CD.Design, x: REF] RETURNS [dont: BOOL�LSE]
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]
};
InitMenus: PROC [] = {
[] ← CDMenus.CreateMenu["Programs on Rects", $RectProgramMenu];
[] ← CDMenus.CreateMenu["Additional Programs", $ProgramMenu];
[] ← CDMenus.CreateMenu["Other Programs", $OtherProgramMenu];
[] ← CDMenus.CreateMenu["Cell (s)", $CellMenu];
[] ← CDMenus.CreateMenu["Global menu", $GlobalMenu];
[] ← CDMenus.CreateMenu["Input / Output", $IOMenu];
[] ← CDMenus.CreateMenu["Directory options", $DirectoryMenu];
[] ← CDMenus.CreateMenu["Viewer options", $ViewerMenu];
[] ← CDMenus.CreateMenu["Special commands", $SpecialMenu];
[] ← CDMenus.CreateMenu["Hard copy", $HardCopyMenu];
[] ← CDMenus.CreateMenu["Display options", $DisplayMenu];
[] ← CDMenus.CreateMenu["Import and remote", $ImportMenu];
[] ← CDMenus.CreateMenu["Generator options", $GeneratorMenu];
[] ← CDMenus.CreateMenu["Text & Properties", $TextPropertyMenu];
CDMenus.ImplementCommandToCallMenu[$RectProgramMenu, $RectProgramMenu];
CDMenus.ImplementCommandToCallMenu[$ProgramMenu, $ProgramMenu];
CDMenus.ImplementCommandToCallMenu[$OtherProgramMenu, $OtherProgramMenu];
CDMenus.ImplementCommandToCallMenu[$CellMenu, $CellMenu];
CDMenus.ImplementCommandToCallMenu[$GlobalMenu, $GlobalMenu];
CDMenus.ImplementCommandToCallMenu[$IOMenu, $IOMenu];
CDMenus.ImplementCommandToCallMenu[$DirectoryMenu, $DirectoryMenu];
CDMenus.ImplementCommandToCallMenu[$ViewerMenu, $ViewerMenu];
CDMenus.ImplementCommandToCallMenu[$DisplayMenu, $DisplayMenu];
CDMenus.ImplementCommandToCallMenu[$SpecialMenu, $SpecialMenu];
CDMenus.ImplementCommandToCallMenu[$HardCopyMenu, $HardCopyMenu];
CDMenus.ImplementCommandToCallMenu[$ImportMenu, $ImportMenu];
CDMenus.ImplementCommandToCallMenu[$TextPropertyMenu, $TextPropertyMenu];
};
resourceTab: RefTab.Ref = RefTab.Create[3];
abortFlagKey: REF resourceTab;
CDEvents.RegisterEventProc[$Abort, AbortEvent];
InitMenus[];
END.