Menus.mesa; Written by Randy Pausch
Menus are collections of entries. Entries are selectable regions at the top of viewers that can cause TIP-like actions to be passed to notifyProcs. Menus are useful for manipulating sets of entries as a group. Menus can be created and registered with the viewers package (after which they are referred to by their unique rope "name"), and then attached to viewers. Each viewer has a list of menus associated with it, and at any given time each menu may be "active" in that viewer (visible to the user and ready to have its entries selected), or "inactive", in which case it exists in the viewer's list of menus but is currently not displayed to the user. For example, the standard Tioga viewer has three menus: the "main" one, which is initially active, and the "Levels" and "Places" menus, which are initially inactive.
Last Edited by: Pausch, August 26, 1983 10:18 am
Last Edited by: Wyatt, October 10, 1983 6:42 pm
DIRECTORY
Imager USING [Context],
Rope USING [ROPE],
ViewerClasses USING [NotifyProc, Viewer];
Menus: CEDAR DEFINITIONS = BEGIN
Viewer: TYPE = ViewerClasses.Viewer;
ROPE: TYPE = Rope.ROPE;
Trigger: TYPE = {leftUp, middleUp, rightUp, shiftLeftUp, shiftMiddleUp, shiftRightUp};
TriggerEnabled: TYPE = BOOLFALSE;
TriggerSet: TYPE = PACKED ARRAY Trigger OF TriggerEnabled; -- SET OF Trigger
allTriggers: TriggerSet = ALL[TRUE];
allNonShifts: TriggerSet = [leftUp: TRUE, middleUp: TRUE, rightUp: TRUE];
allShifts: TriggerSet = [shiftLeftUp: TRUE, shiftMiddleUp: TRUE, shiftRightUp: TRUE];
noTriggers: TriggerSet = ALL[FALSE];
Menu entries may specify different user actions as their triggers. Depending on the state of the shift key and which mouse button was used, different results may be obtained from clicking a menu entry. In addition, there is a standard way (holding control down and clicking) for users to request a list of all possible functions for a menu entry. A TriggerSet specifies which triggers will invoke an action.
targetNotFound: SIGNAL;
Some calls require locating entries by their names. This signal is raised if an entry doesn't exist.
MenuName: TYPE = ATOM;
Menu: TYPE = REF MenuRec;
MenuRec: TYPE = RECORD[
name: MenuName,
beginsActive: BOOLTRUE,
breakBefore: BOOLTRUE,
breakAfter: BOOLTRUE,
notify: ViewerClasses.NotifyProc ← NIL,
entries: LIST OF Entry ← NIL
];
A 'menu' is a set of entries defined and manipulated as a group. A menu is uniquely identified by its name after it is registered with the viewers package. This is all designed for ease in 'passively' describing menus as data structures. If beginsActive is TRUE, the menu will be accessible to the user when the menu is added to a viewer. Each viewer has a list of menus associated with it, and they are "laid out" like text, as many as will fit onto a line. breakBefore and breakAfter can be used to force "line breaks" in the menus. When the user clicks a menu entry, the 'notify' procedure is called with the input actions for that entry; if notify=NIL, the notify proc of the viewer owning the menu will be called.
Entry: TYPE = REF EntryRec;
EntryRec: TYPE = RECORD[
name: ROPE,
guarded: BOOLEANFALSE,
displayData: REF ANYNIL, -- if supplied, must narrow to ROPE or REF DrawingRec
actions: LIST OF Action ← NIL
];
Menu entries are uniquely identified by name. If 'guarded' is TRUE, the user will have to click the entry twice in a short period of time to cause action actually to occur. If something other than the name of the entry should be displayed, this can be done by making 'displayData' either a ROPE (which will be displayed instead of the name), or a REF DrawingRec, which will draw the entry itself.
Action: TYPE = REF ActionRec;
ActionRec: TYPE = RECORD[
Menus may have more than one action associated with them. Each action has a record with the list of triggers that cause the action, what to pass to the notify proc, and a ROPE to describe the action for use in the user interface. 'guardResponse' is used after the first mouse hit; the rope associated with the trigger is shown in the message window, or the indicated procedure is called. 'makeActive', 'makeInactive', and 'toggle' specify the menus to affect when the trigger is handled; this makes it easy to describe typical menu actions in the data structure, rather than writing code to handle making menus active and inactive.
triggers: TriggerSet, -- triggers that invoke this action
input: LIST OF REF ANYNIL, -- input to be passed to the notify proc
popupDoc: ROPE ← NIL,
guardResponse: REF ANYNIL, -- if supplied, must narrow to ROPE or REF UnGuardRec
makeActive: LIST OF MenuName ← NIL,
makeInactive: LIST OF MenuName ← NIL,
toggle: LIST OF MenuName ← NIL
];
DrawingRec: TYPE = RECORD [
proc: DrawingProc, -- procedure to call to do the drawing
data: REF ANYNIL -- will be passed as the 'clientData' parameter to the procedure
];
UnGuardRec: TYPE = RECORD [
proc: UnGuardProc, -- procedure to call when menu becomes unguarded
data: REF ANYNIL -- will be passed as the 'clientData' parameter to the procedure
];
MenuEntryDisplayOp: TYPE = {query, draw};
If op is 'query' the client should return the width he would like to have. (Height will be ViewerSpecs.menuHeight). If 'op' is 'draw', the procedure should draw into the passed context to display the entry on the screen.
DrawingProc: TYPE = PROC [op: MenuEntryDisplayOp, context: Imager.Context ← NIL, clientData: REF ANYNIL] RETURNS[width: INTEGER ← 64];
UnGuardProc: TYPE = PROC [clientData: REF ANYNIL];
operations to register menus with the viewers package:
RegisterMenu: PROC [menu: Menu];
The menu data is recorded by the viewers package and associated with the name in the record. All future references may be made through the name alone.
AlreadyRegistered: PROC [name: MenuName] RETURNS [registered: BOOLEAN];
returns TRUE iff a menu with the given name has been registered.
ReRegisterMenu: PROC [menu: Menu, paint: BOOLTRUE];
The previous definition of the menu is forgotten, and the new one takes its place. If 'paint' is true, all viewers containing the menu will be found and repainted with hint=menu.
GetRegisteredMenu: PROC [name: MenuName] RETURNS [menu: Menu];
The definition for the menu is returned
operations to attach menus to viewers:
ClearMenus: PROC [viewer: Viewer, paint: BOOLTRUE];
Any menus that had been associated with the viewer are no longer attached to the viewer. They are still registered with the viewers package, however. If 'paint' is true, the equivalent of a 'PaintViewer[viewer,hint=caption] will be done.
AddMenu: PROC [viewer: Viewer, name: MenuName,
paint: BOOLTRUE, addBefore: MenuName ← NIL];
The menu registered under the name 'name' is added to the viewer's list of menus. If 'addBefore' is non-NIL, the new menu will be added in the list before the menu with name 'addBefore'. If 'paint' is true, the equivalent of a 'PaintViewer[viewer,hint=caption] will be done.
MakeActive: PROC [viewer: Viewer, menu: MenuName, paint: BOOLTRUE];
'menu' is made accessible to the human user.
MakeInactive: PROC [viewer: Viewer, menu: MenuName, paint: BOOLTRUE];
'menu' is made inaccessible to the human user.
Toggle: PROC [viewer: Viewer, menu: MenuName, paint: BOOLTRUE];
If 'menu' is currently active, it is made inactive. Otherwise, it is made active.
MenuInViewer: PROC [viewer: Viewer, menu: MenuName] RETURNS [exists: BOOLEAN];
Returns TRUE if the menu already exists in the viewer, FALSE otherwise.
operations to parse menu descriptions:
ParseDescription: PROC [def: ROPE, prevlist: LIST OF Menu ← NIL, errorFile: ROPENIL]
RETURNS [LIST OF Menu];
The rope is parsed for multiple menu descriptions, as laid out in MenuBNF.tioga. Any errors are written to "errorFile." If 'prevlist' is not NIL, the returned list will be added to and "layered" on top of 'prevlist'. If 'errorFile' is NIL, errors will be written to "menus.errlog". If any errors are found, NIL will be returned.
END.