<> <> <> <> DIRECTORY CD, CDMenuSpecials, PopUpMenus, Rope, SymTab, TerminalIO; CDMenuSpecialsImpl: CEDAR MONITOR IMPORTS PopUpMenus, Rope, SymTab, TerminalIO EXPORTS CDMenuSpecials = BEGIN SelectOneOf: PUBLIC PROC[ref: REF, label: Rope.ROPE, allowTypeIn: BOOL_FALSE] RETURNS [r: Rope.ROPE_NIL] = { cnt: INT _ 0; cntPerMenu: INT = 12; menu, firstMenu: PopUpMenus.Menu; x: REF_NIL; alreadyUsed: SymTab.Ref _ SymTab.Create[]; NewMenu: PROC [] RETURNS [m: PopUpMenus.Menu] = { m _ PopUpMenus.Create[header: label]; IF allowTypeIn THEN [] _ PopUpMenus.Entry[menu: m, entry: "", entryData: $type, doc: "type in rope to terminal"]; }; AddEntry: PROC [entry: Rope.ROPE] = { IF SymTab.Insert[alreadyUsed, entry, entry] THEN { IF (cnt MOD cntPerMenu)=(cntPerMenu-1) THEN { newMenu: PopUpMenus.Menu _ NewMenu[]; [] _ PopUpMenus.Entry[menu: menu, entry: "", entryData: newMenu, doc: "show more entries for this menu"]; menu _ newMenu; }; cnt _ cnt+1; [] _ PopUpMenus.Entry[menu: menu, entry: entry, entryData: entry]; }; }; EachSymTabEntry: SymTab.EachPairAction = { quit _ FALSE; AddEntry[key] }; menu _ firstMenu _ NewMenu[]; WITH ref SELECT FROM st: SymTab.Ref => [] _ SymTab.Pairs[st, EachSymTabEntry]; rl: LIST OF Rope.ROPE => FOR l: LIST OF Rope.ROPE _ rl, l.rest WHILE l#NIL DO AddEntry[l.first]; ENDLOOP; ENDCASE => NULL; IF menu#firstMenu THEN [] _ PopUpMenus.Entry[menu: menu, entry: "", entryData: firstMenu, doc: "show beginning entries again"]; IF cnt>0 THEN x _ PopUpMenus.Call[firstMenu] ELSE IF allowTypeIn THEN x _ $type; WHILE x#NIL AND ISTYPE[x, PopUpMenus.Menu] DO x _ PopUpMenus.Call[NARROW[x]]; ENDLOOP; IF x=$type THEN r _ TerminalIO.RequestRope[Rope.Concat[label, " >"] ! TerminalIO.UserAbort => CONTINUE] ELSE r _ NARROW[x]; }; END.