CDMenusImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Created by: Ch. Jacobi, September 18, 1984 3:24:53 pm PDT
Last Edited by: Ch. Jacobi, November 8, 1984 10:30:13 am PST
DIRECTORY
Atom,
List,
CD USING [Technology],
CDMenus,
CDProperties,
CDSequencer,
RefTab,
Rope,
TerminalIO;
CDMenusImpl: CEDAR MONITOR
IMPORTS Atom, List, CDProperties, CDSequencer, RefTab, Rope, TerminalIO
EXPORTS CDMenus =
BEGIN
globalTab: RefTab.Ref = RefTab.Create[];
Menu: TYPE = RECORD [
label: Rope.ROPENIL,
ropeL: LIST OF Rope.ROPENIL, --ropeL and keyL are always used parallel
keyL: LIST OF REFNIL
];
CreateMenu: PUBLIC PROC [label: Rope.ROPENIL, globalKey: ATOMNIL] 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.ROPENIL, 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.WriteRope["menu "];
TerminalIO.WriteRope[m.label];
TerminalIO.WriteRope[" "];
};
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;
CallCommand: 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, as: a];
ENDCASE => IF key#NIL THEN TerminalIO.WriteRope["** bad menu; not a command\n"];
END;
MenuCommand: PROC [comm: CDSequencer.Command] =
BEGIN
WITH CDProperties.GetPropFromAtom[from: comm.a, 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];
CallCommand[menu, comm];
};
ENDCASE => TerminalIO.WriteRope["**menu not executed\n"];
END;
ImplementMenuCommand: PUBLIC ENTRY PROC[a: 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.GetPropFromAtom[from: a, prop: menuProperty] SELECT FROM
ml: Atom.PropList => menuList←ml;
ENDCASE => NULL;
menuList ← Atom.PutPropOnList[propList: menuList, prop: techKey, val: menu];
CDProperties.PutPropOnAtom[onto: a, prop: menuProperty, val: menuList];
CDSequencer.ImplementCommand[a, MenuCommand, technology, doQueue];
END;
menuProperty: REF ATOM = NEW[ATOM ← $menuProperty]; --no accessible
END.