CDMenusImpl.mesa 
Copyright © 1984, 1986 by Xerox Corporation.  All rights reserved.
Created by: Ch. Jacobi, September 18, 1984 3:24:53 pm PDT
Last Edited by: Ch. Jacobi, March 25, 1986 1:41:47 pm PST
 
DIRECTORY   
Atom,
List,
CD USING [Technology],
CDMenus,
CDProperties,
CDSequencer,
IO,
RefTab,
Rope,
TerminalIO;
 
CDMenusImpl: 
CEDAR 
MONITOR
IMPORTS Atom, List, CDProperties, CDSequencer, IO, RefTab, Rope, TerminalIO
EXPORTS CDMenus = 
 
BEGIN
globalTab: RefTab.Ref = RefTab.Create[];
Menu: 
TYPE = 
RECORD [
label: Rope.ROPE←NIL,
ropeL: LIST OF Rope.ROPE←NIL, --ropeL and keyL are always used parallel
keyL: LIST OF REF←NIL
];
 
CreateMenu: 
PUBLIC 
PROC [label: Rope.
ROPE←
NIL, globalKey: 
ATOM←
NIL] 
RETURNS [menu: 
REF] =
BEGIN
menu ← NEW[Menu←[label: label]];
IF globalKey#NIL THEN [] ← RefTab.Store[globalTab, globalKey, menu];
END;
 
RefToMenu: 
PROC [keyOrMenu: 
REF] 
RETURNS [menu: 
REF Menu←
NIL] =
BEGIN
WITH keyOrMenu 
SELECT 
FROM
a: 
ATOM => {
x: REF; found: BOOL;
[found, x] ← globalTab.Fetch[a];
menu ← NARROW[x]
};
 
rm: REF Menu => menu ← rm;
ENDCASE => menu ← NIL;
 
END;
 
GetMenu: 
PUBLIC 
PROC [globalKey: 
ATOM] 
RETURNS [menu: 
REF] =
BEGIN
menu ← RefToMenu[globalKey]
END;
 
CreateEntry: 
PUBLIC 
PROC [menu: 
REF, entry: Rope.
ROPE←
NIL, key: 
REF] =
--menu:  an ATOM => fetch for menu with globalKey
--   a menu => use menu
--key:  an ATOM => use CDSequencer
--   a menu => use menu
BEGIN
m: REF Menu = RefToMenu[menu];
IF m=NIL THEN RETURN;
--check for defaults on entry
IF Rope.IsEmpty[entry] 
THEN
WITH key 
SELECT 
FROM
a: ATOM => entry ← Atom.GetPName[a];
rm: REF Menu => entry ← rm.label;
ENDCASE => NULL;
 
 
IF key#
NIL 
THEN {
--include or replace the entry
IF m.ropeL=
NIL 
OR m.keyL=
NIL 
THEN {
m.ropeL ← LIST[entry];
m.keyL ← LIST[key];
}
 
ELSE {
rL: LIST OF Rope.ROPE ← m.ropeL;
kL: LIST OF REF ← m.keyL;
--ASSERT rL # NIL
DO
IF Rope.Equal[rL.first, entry] 
THEN {
kL.first ← key;
RETURN
}; 
 
IF rL.rest=
NIL OR kL.rest=
NIL 
THEN {
rL.rest ← LIST[entry];
kL.rest ← LIST[key];
RETURN
};
 
rL ← rL.rest; --not nil
kL ← kL.rest;
ENDLOOP
 
}
 
}
 
ELSE { 
--key=NIL
--remove the entry
IF m.ropeL=NIL OR m.keyL=NIL THEN RETURN;
IF Rope.Equal[m.ropeL.first, entry] 
THEN {
m.ropeL ← m.ropeL.rest;
m.keyL ← m.keyL.rest
}
 
ELSE {
rL: LIST OF Rope.ROPE ← m.ropeL; -- not NIL
kL: LIST OF REF ← m.keyL;
--ASSERT rL # NIL
DO
IF rL.rest=NIL OR kL.rest=NIL THEN RETURN
ELSE { 
IF Rope.Equal[rL.rest.first, entry] 
THEN {
rL.rest ← rL.rest.rest;
kL.rest ← kL.rest.rest;
RETURN
}; 
 
rL ← rL.rest;
kL ← kL.rest;
};
 
ENDLOOP
 
}
 
}
 
END;
 
CallMenu: 
PUBLIC 
PROC [menu: 
REF] 
RETURNS [key: 
REF←NIL] =
--the atom wil be changed to the menu atom
BEGIN
n: INT;
m: REF Menu = RefToMenu[menu];
x: REF;
IF m=
NIL 
THEN {
TerminalIO.WriteRope["**menu not handled\n"];
RETURN
};
 
--now call the menu
IF ~Rope.IsEmpty[m.label] 
THEN {
TerminalIO.WriteRopes["menu ", m.label, " "];
};
 
IF m.ropeL=
NIL 
THEN {
TerminalIO.WriteRope["  no entry for menu\n"];
RETURN [NIL]
};
 
n ← TerminalIO.RequestSelection[m.label, m.ropeL];
IF n=0 
THEN {
TerminalIO.WriteRope["  discarded\n"];
RETURN [NIL]
};
 
WITH x ← List.NthElement[m.keyL, n] 
SELECT 
FROM
mm: 
REF Menu => {
TerminalIO.WriteRope[" -> "];
key ← CallMenu[mm];
};
 
ENDCASE => {
TerminalIO.WriteRope["selects "];
TRUSTED{TerminalIO.WriteRope[NARROW[List.NthElement[LOOPHOLE[m.ropeL], n]]]};
TerminalIO.WriteRope["\n"];
key ← x;
};
 
 
END;
 
CallMenuAndExecute: 
PUBLIC 
PROC [menu: 
REF, comm: CDSequencer.Command] =
--the atom will be changed to the menu atom
BEGIN
key: REF = CallMenu[menu].key;
WITH key 
SELECT 
FROM
a: ATOM => CDSequencer.ExecuteCommand[comm: comm, key: a];
ENDCASE => IF key#NIL THEN TerminalIO.WriteRope["** bad menu; not a command\n"]; 
 
END;
 
MenuCommand: 
PROC [comm: CDSequencer.Command] =
BEGIN 
WITH CDProperties.GetAtomProp[from: comm.key, prop: menuProperty] 
SELECT 
FROM
menuList: Atom.PropList => {
menu: REF ← Atom.GetPropFromList[menuList, comm.design.technology.key];
IF menu=NIL THEN menu ← Atom.GetPropFromList[menuList, $any];
CallMenuAndExecute[menu, comm];
};
 
ENDCASE => TerminalIO.WriteRope["**menu not executed\n"];
 
END;
 
ImplementCommandToCallMenu: 
PUBLIC 
ENTRY PROC[key: 
ATOM, menu: 
REF, technology: 
CD.Technology←
NIL] = 
BEGIN ENABLE UNWIND => NULL;
techKey: ATOM = ( IF technology=NIL THEN $any ELSE technology.key );
menuList: Atom.PropList ← NIL;
WITH CDProperties.GetAtomProp[from: key, prop: menuProperty] 
SELECT 
FROM
ml: Atom.PropList => menuList←ml;
ENDCASE => NULL;
 
menuList ← Atom.PutPropOnList[propList: menuList, prop: techKey, val: menu];
CDProperties.PutAtomProp[onto: key, prop: menuProperty, val: menuList];
CDSequencer.ImplementCommand[key, MenuCommand, technology, dontQueue];
END;
 
count: INT ← 0;
ImplementEntryCommand: 
PUBLIC 
PROC [menu: 
REF, entry: Rope.
ROPE ← 
NIL, p: CDSequencer.CommandProc, key: 
ATOM ← 
NIL, queue: CDSequencer.QueueMethod ← doQueue, technology: 
CD.Technology ←
 NIL] = 
--For conveniance only; in all technologies.
--Creates an entry line into the menu and sets it up to call the command p.
BEGIN
IF key=NIL THEN key ← Atom.MakeAtom[IO.PutFR1["a%g", IO.int[count ← count+1]]];
CDSequencer.ImplementCommand[key: key, proc: p, queue: queue, technology: technology];
CreateEntry[menu: menu, entry: entry, key: key];
END;
 
menuProperty: REF ATOM = NEW[ATOM ← $menuProperty]; --no accessible
END.