-- color menu
-- MStone December 11, 1980 7:15 PM
-- Tiberi January 25, 1980 1:37 PM
DIRECTORY
MenuDefs: FROM "MenuDefs",
ControllerDefs: FROM "ControllerDefs",
StringDefs: FROM "StringDefs",
ScreenDefs: FROM "ScreenDefs",
StyleDefs: FROM "StyleDefs",
RefreshDefs: FROM "RefreshDefs",
GriffinFontDefs: FROM "GriffinFontDefs",
PointDefs: FROM "PointDefs",
ObjectDefs: FROM "ObjectDefs"
USING [MenuOrientation, StartObject, ObjectProc,
ForAllObjects, ReplotBoxFromObject],
GriffinMemoryDefs: FROM "GriffinMemoryDefs";
Menu: PROGRAM
IMPORTS StringDefs, ObjectDefs, GriffinFontDefs,
GriffinMemoryDefs, ScreenDefs, ControllerDefs,
RefreshDefs, PointDefs
EXPORTS MenuDefs SHARES MenuDefs =
BEGIN OPEN MenuDefs, ObjectDefs;
X: INTEGER = PointDefs.X;
Y: INTEGER = PointDefs.Y;
menuLineWidth: INTEGER = 3;
menuMargin: INTEGER = 4;
menuTopMargin: INTEGER = 1;
menuStyle: StyleDefs.StyleHandle ← NIL;
menuFont: GriffinFontDefs.FontDescriptor;
menuHeight: INTEGER ← 0;
menuBaseLine: INTEGER ← 0;
menuGrey: POINTER TO ARRAY [0..3] OF CARDINAL ← NIL;
black: POINTER TO ARRAY [0..3] OF CARDINAL ← NIL;
twoLineWidth: INTEGER = 2*menuLineWidth;
MenuStyle: PUBLIC PROCEDURE RETURNS [StyleDefs.StyleHandle] =
BEGIN
RETURN [menuStyle]
END;
CreateMenu: PUBLIC PROCEDURE [orientation: MenuOrientation, tl: PointDefs.ScrPt, title: STRING] RETURNS [menu: MenuHandle] =
BEGIN
menu ← LOOPHOLE [StartObject [menu], MenuHandle];
-- a MenuHandle is a POINTER TO menu Object
menu.head ← NIL;
menu.validEncoding ← TRUE;
menu.style ← menuStyle;
menu.orientation ← orientation;
menu.visible ← FALSE;
menu.tl ← tl; -- leave space for enclosing box
SELECT orientation FROM
vertical =>
BEGIN
menu.br [X] ← tl [X] + twoLineWidth-1;
menu.br [Y] ← tl [Y] + menuLineWidth-1;
END;
horizontal =>
BEGIN
menu.br [X] ← tl [X] + menuLineWidth-1;
menu.br [Y] ← tl [Y] + twoLineWidth + menuHeight;
END;
ENDCASE;
IF title # NIL AND title.length > 0
THEN AddMenuItem [menu, title, NullMenuProc].inverted ← TRUE;
END;
NullMenuProc: MenuProc = BEGIN END;
ItemWidth: PROCEDURE [string:STRING] RETURNS [INTEGER] = INLINE
BEGIN
RETURN [PointDefs.ObjValToScrVal[GriffinFontDefs.StringWidth [string,
@menuFont, menuStyle.orientation]] + menuMargin - 1];
END;
AddMenuItem: PUBLIC PROCEDURE [menu: MenuHandle, string: STRING, proc: MenuProc] RETURNS [MenuItemHandle] =
BEGIN
newitem: MenuItemHandle;
rover: MenuItemHandle;
first: BOOLEAN ← FALSE;
newitem ← GriffinMemoryDefs.Allocate [SIZE [MenuItem]];
newitem↑ ← [link: NIL, menu: menu, selected: FALSE, inverted: FALSE,
tl: , br: , string: NIL, proc: proc];
IF menu.head=NIL
THEN BEGIN first ← TRUE; menu.head ← newitem END
ELSE BEGIN
FOR rover ← menu.head, rover.link UNTIL rover.link = NIL
DO ENDLOOP; --find end
rover.link ← newitem
END;
SELECT menu.orientation FROM
vertical =>
BEGIN
newitem.tl [X] ← menu.tl [X] + menuLineWidth;
newitem.tl [Y] ← menu.br [Y] + 1;
END;
horizontal =>
BEGIN
newitem.tl [X] ← menu.br [X] + 1;
newitem.tl [Y] ← menu.tl [Y] + menuLineWidth;
END;
ENDCASE;
newitem.string ← GriffinMemoryDefs.AllocateString [string.length];
StringDefs.AppendString [newitem.string, string];
SELECT menu.orientation FROM
vertical =>
BEGIN
width: INTEGER ← ItemWidth[string];
IF first OR menu.tl[X] + width + twoLineWidth > menu.br [X]
THEN ChangeMenuWidth[menu, ItemWidth[string]]
ELSE newitem.br [X] ← menu.br [X] - menuLineWidth;
newitem.br [Y] ← newitem.tl [Y] + menuHeight;
menu.br [Y] ← newitem.br [Y] + menuLineWidth;
END;
horizontal =>
BEGIN
newitem.br [X] ← newitem.tl [X] + ItemWidth[string];
newitem.br [Y] ← menu.br [Y] - menuLineWidth;
menu.br [X] ← newitem.br [X] + menuLineWidth
END;
ENDCASE;
IF menu.visible THEN RefreshDefs.EraseAndSave[menu];
RETURN [newitem];
END;
ChangeMenuWidth: PROCEDURE [menu: MenuHandle, width: INTEGER] =
BEGIN OPEN ObjectDefs;
WidenMenu: MenuProc =
BEGIN
item.br [X] ← item.tl [X] + width;
END;
IF menu.orientation # vertical THEN ERROR;
ForAllMenuItems [menu, WidenMenu];
menu.br [X] ← menu.tl [X] + width + twoLineWidth;
IF menu.visible THEN RefreshDefs.EraseAndSave[menu];
END;
ForAllMenus: PUBLIC PROCEDURE [proc: PROCEDURE [menu: MenuHandle]] =
BEGIN OPEN ObjectDefs;
IsMenu: ObjectProc =
BEGIN
WITH object: obj SELECT FROM
menu => proc[@object];
ENDCASE;
END;
ForAllObjects[IsMenu];
END;
ForAllMenuItems: PUBLIC PROCEDURE [menu: MenuHandle, proc: MenuProc] =
BEGIN
rover: MenuItemHandle;
FOR rover ← menu.head, rover.link UNTIL rover = NIL
DO proc [rover] ENDLOOP;
END;
PlotMenu: PUBLIC PROCEDURE [menu: MenuHandle] =
BEGIN
tl, br: PointDefs.ScrPt;
ScreenDefs.EraseBoxBW[menu.tl, menu.br];
ScreenDefs.SetFunction[replace];
ScreenDefs.SetFillParms[menuGrey,0];
menu.visible ← TRUE;
--top edge:
tl ← menu.tl;
br [X] ← menu.br[X];
br [Y] ← tl [Y] + menuLineWidth - 1;
ScreenDefs.BoxFill [tl, br];
--left edge:
br [X] ← tl [X] + menuLineWidth - 1;
br [Y] ← menu.br [Y];
tl ← menu.tl;
ScreenDefs.BoxFill [tl, br];
-- the right edge:
tl [X] ← menu.br [X] - menuLineWidth + 1;
tl [Y] ← menu.tl [Y];
br ← menu.br;
ScreenDefs.BoxFill [tl, br];
-- the bottom edge:
tl [X] ← menu.tl [X];
tl [Y] ← menu.br [Y] - menuLineWidth + 1;
br ← menu.br;
ScreenDefs.BoxFill [tl, br];
ForAllMenuItems [menu, ShowItem];
END;
ShowMenu: PUBLIC PROCEDURE [menu: MenuHandle] =
BEGIN
menu.visible ← TRUE;
RefreshDefs.PlotAndMark[menu];
END;
HideMenu: PUBLIC PROCEDURE [menu: MenuHandle] =
BEGIN
menu.visible ← FALSE;
RefreshDefs.EraseAndSave[menu];
END;
ShowItem: PUBLIC MenuProc =
BEGIN
tl, br: PointDefs.ScrPt;
screen: ScreenDefs.ScreenPtr ← ScreenDefs.GetCurrentScreen[];
tl ← item.tl;
br ← IF item.menu.orientation = vertical
THEN [item.br[X], item.br[Y] + menuLineWidth]
ELSE [item.br[X] + menuLineWidth, item.br[Y]];
IF tl[X] >screen.rx OR tl[Y] >screen.by
OR br[X] < screen.lx OR br[Y] < screen.ty
THEN RETURN;
IF item.menu.orientation = vertical
THEN tl [Y] ← item.br [Y]+1 -- bottom edges
ELSE tl [X] ← item.br [X]+1; -- right edges
ScreenDefs.SetFunction[replace];
ScreenDefs.SetFillParms[menuGrey,0];
ScreenDefs.BoxFill [tl, br];
GriffinFontDefs.DisplayString [item.string,
MenuAnchorPoint[item],
StyleDefs.Anchor [center],
StyleDefs.Orientation [or0], @menuFont];
IF item.inverted THEN
BEGIN
ScreenDefs.InvertBox[item.tl, item.br];
END;
END;
OverWhichItem: PUBLIC PROCEDURE [pt: PointDefs.ScrPt] RETURNS [lastitem: MenuItemHandle] =
BEGIN
lastover: MenuHandle ← NIL;
IsOverMenu: PROCEDURE[m: MenuHandle] =
BEGIN
IF m.visible
AND pt [X] IN [m.tl[X]..m.br[X]]
AND pt [Y] IN [m.tl[Y]..m.br[Y]]
THEN lastover ← m;
END;
LastItemOver: MenuProc =
BEGIN
IF pt [X] IN [item.tl [X] .. item.br [X]] AND
pt [Y] IN [item.tl [Y] .. item.br [Y]] THEN lastitem ← item
END;
lastitem ← NIL;
ForAllMenus[IsOverMenu];
IF lastover = NIL THEN RETURN [NIL];
-- which menu item?
ForAllMenuItems[lastover, LastItemOver];
END;
IsOverItem: PUBLIC PROCEDURE [pt: PointDefs.ScrPt,
item: MenuItemHandle] RETURNS [BOOLEAN] =
BEGIN
IF pt [X] IN [item.tl [X]..item.br [X]]
AND pt [Y] IN [item.tl [Y]..item.br [Y]]
THEN RETURN [TRUE]
ELSE RETURN [FALSE];
END;
BugItem: PUBLIC PROCEDURE [item: MenuItemHandle] =
BEGIN item.proc [item] END;
--questions about menus:
WhichMenu: PUBLIC PROCEDURE [item: MenuItemHandle]
RETURNS [menu: MenuHandle] =
BEGIN RETURN [item.menu] END;
IsSelected: PUBLIC PROCEDURE [item: MenuItemHandle] RETURNS [BOOLEAN] =
BEGIN RETURN [item.selected] END;
MenuString: PUBLIC PROCEDURE [item: MenuItemHandle] RETURNS [STRING] =
BEGIN RETURN [item.string] END;
-- the string returned might go away sometime!
MenuAnchorPoint: PUBLIC PROCEDURE [item: MenuItemHandle]
RETURNS [pt: PointDefs.ScrPt] =
BEGIN
pt[X] ← (item.br[X]+item.tl[X])/2 + 1;
pt[Y] ← item.tl[Y] + menuTopMargin;
END;
--changing things:
SetMenuString: PUBLIC PROCEDURE [item: MenuItemHandle, string: STRING] =
BEGIN
menu: MenuHandle = item.menu;
width: INTEGER ← 0;
FindWidest: MenuProc =
BEGIN
w: INTEGER ← ItemWidth[item.string];
IF w>width THEN width ← w;
END;
GriffinMemoryDefs.FreeString[item.string];
item.string ← GriffinMemoryDefs.AllocateString [string.length];
StringDefs.AppendString [item.string, string];
IF menu.visible THEN RefreshDefs.EraseAndSave[menu];
ForAllMenuItems [menu, FindWidest];
ChangeMenuWidth[menu, width];
IF menu.visible THEN RefreshDefs.EraseAndSave[menu];
END;
SelectOnly: PUBLIC MenuProc =
BEGIN
ForAllMenuItems[item.menu, Deselect];
Select[item];
END;
Select: PUBLIC MenuProc =
BEGIN
item.selected ← TRUE;
ClearMenuItem[item]
END;
Deselect: PUBLIC MenuProc =
BEGIN
IF item.selected THEN
BEGIN
item.selected ← FALSE;
ClearMenuItem[item];
END;
END;
HighlightMenuItem: PUBLIC MenuProc =
BEGIN
wasInverted: BOOLEAN = item.inverted;
item.inverted ← ~ item.selected;
IF item.menu.visible AND wasInverted # item.inverted
THEN ObjectDefs.ReplotBoxFromObject[item.tl, item.br, item.menu];
END;
ClearMenuItem: PUBLIC MenuProc =
BEGIN
wasInverted: BOOLEAN = item.inverted;
item.inverted ← item.selected;
IF item.menu.visible AND wasInverted # item.inverted
THEN ObjectDefs.ReplotBoxFromObject[item.tl, item.br, item.menu];
END;
InitMenuStyle: PROCEDURE =
BEGIN OPEN GriffinFontDefs, StringDefs;
currentFont: FontDescriptorHandle;
best: FontDescriptorHandle ← NIL;
nameInSet: PROC[s: STRING] RETURNS[BOOLEAN] = {
RETURN[
EquivalentString[s, "Cream"] OR
EquivalentString[s, "Helvetica"] OR
EquivalentString[s, "TimesRoman"] OR
EquivalentString[s, "Gacha"] OR
EquivalentString[s, "SmallTalk"]];
};
findCream10: PROC[font: FontDescriptorHandle] = {
IF font.rotation=Rot0Degrees AND
EquivalentString[font.name, "Cream"] AND font.points=10
THEN best ← font;
};
findAny10InSet: PROC[font: FontDescriptorHandle] = {
IF font.rotation=Rot0Degrees AND
font.points=10 AND nameInSet[font.name] THEN best ← font;
};
findAnyInSet: PROC[font: FontDescriptorHandle] = {
IF font.rotation=Rot0Degrees AND nameInSet[font.name]
THEN best ← font;
};
findAny: PROC[font: FontDescriptorHandle] = {
IF font.rotation=Rot0Degrees AND best#NIL THEN best ← font;
};
menuStyle ← ControllerDefs.CurrentStyleRecord[];
--pick a menu font. Try for Cream 10, then one of the nameset above
--Then, find the first font that meets one of those names
--default to the first font found
ForAllFonts[findCream10];
IF best=NIL THEN ForAllFonts[findAny10InSet];
IF best=NIL THEN ForAllFonts[findAnyInSet];
IF best=NIL THEN ForAllFonts[findAny];
IF best=NIL THEN StartupFontError[nofonts];
currentFont ← ControllerDefs.CurrentFontDescriptor[];
currentFont↑ ← best↑;
BEGIN OPEN menuStyle;
color ← [0,0,128]; -- 50% grey
fillcolor ← [0,0,128];
backgndcolor ← [0,0,128];
anchor ← center;
orientation ← or0;
END;
menuStyle ← ControllerDefs.CurrentStyle[];
menuFont ← ControllerDefs.FontWithNumber[menuStyle.fontid];
menuHeight ← PointDefs.ObjValToScrVal[GriffinFontDefs.MaxHeight
[@menuFont]] + menuTopMargin-1;
menuBaseLine ← GriffinFontDefs.BaseLine [@menuFont];
menuGrey ← ControllerDefs.GreyOfColor[menuStyle.color];
black ← ControllerDefs.GreyOfColor[[0,0,0]];
END;
InitMenuStyle;
END.