DIRECTORY
GriffinColor USING [StringToColor],
GriffinData USING [DataRec],
GriffinDisplay USING [BkgndColor, BkgndColorRec, BoxFill, DrawCaption, InvertBox],
GriffinKernel USING [ClipBox, DataRec],
GriffinMenu USING [MenuHandle, MenuHandleProc, MenuItem, MenuItemHandle, MenuProc],
GriffinObject USING [ForAllObjects, MenuOrientation, Object, ObjectProc, ReplotBoxFromObject, StartObject],
GriffinPoint USING [ScrPt, ScrRealPt, X, Y],
GriffinRefresh USING [EraseAndSave, MarkObject],
GriffinStyle USING [Style, StyleHandle],
GriffinText USING [GetBoundingBox],
GriffinViewer USING [DoPaint, PaintProc],
Imager USING [ConstantColor, Context],
ImagerColor USING [ColorFromGray],
ImagerFont USING [Extents, Find, FontBoundingBox],
Real USING [RoundI],
Rope USING [Length, ROPE];
GriffinMenuImpl:
CEDAR
PROGRAM
IMPORTS GriffinColor, GriffinDisplay, GriffinObject, GriffinRefresh, GriffinText, GriffinViewer, ImagerColor, ImagerFont, Real, Rope
EXPORTS GriffinMenu, GriffinKernel = BEGIN
Data: TYPE = REF DataRec;
DataRec: PUBLIC TYPE = GriffinData.DataRec;
ROPE: TYPE = Rope.ROPE;
X: NAT = GriffinPoint.X;
Y: NAT = GriffinPoint.Y;
menuLineWidth: INTEGER = 4;
twoLineWidth: INTEGER = 2*menuLineWidth;
colorLineWidth: INTEGER = 2;
menuMargin: INTEGER = 4;
menuGrey: Imager.ConstantColor = ImagerColor.ColorFromGray[.5];
menuWhite: Imager.ConstantColor = ImagerColor.ColorFromGray[0];
menuGrey: GriffinDisplay.BkgndColor; -- initialized in InitMenuStyle
menuWhite: GriffinDisplay.BkgndColor;-- initialized in InitMenuStyle
menuStyle: GriffinStyle.StyleHandle ← NIL; -- initialized in InitMenuStyle
menuHeight, itemHeight: INTEGER ← 0; -- initialized in InitMenuStyle
CreateMenu:
PUBLIC
PROC [data: Data, orientation: GriffinObject.MenuOrientation, tl: GriffinPoint.ScrPt, title:
ROPE]
RETURNS [GriffinMenu.MenuHandle] = {
menu: GriffinMenu.MenuHandle ← NARROW[GriffinObject.StartObject[data, menu]];
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 => {
menu.br[X] ← tl[X] + twoLineWidth;
menu.br[Y] ← tl[Y] - menuLineWidth;
};
horizontal => {
menu.br[X] ← tl[X] + menuLineWidth;
menu.br[Y] ← tl[Y] - menuHeight;
};
ENDCASE;
IF title # NIL AND Rope.Length[title] > 0
THEN AddMenuItem[menu, title, NullMenuProc].inverted ← TRUE;
RETURN[menu]
};
NullMenuProc: GriffinMenu.MenuProc = { };
ItemWidth:
PROC [string:
ROPE]
RETURNS [
INTEGER] = {
tl, br: GriffinPoint.ScrPt;
[tl, br] ← GriffinText.GetBoundingBox[string, menuStyle, [0, 0]];
RETURN[br[X]-tl[X]+menuMargin];
};
AddMenuItem:
PUBLIC
PROC [menu: GriffinMenu.MenuHandle, string:
ROPE, proc: GriffinMenu.MenuProc]
RETURNS [GriffinMenu.MenuItemHandle] = {
data: Data ← menu.data;
first: BOOLEAN ← FALSE;
rover: GriffinMenu.MenuItemHandle;
newitem: GriffinMenu.MenuItemHandle ← NEW[GriffinMenu.MenuItem];
newitem^ ← [link:
NIL, menu: menu, selected:
FALSE, inverted:
FALSE,
tl: , br: , string: NIL, proc: proc];
IF menu.head=NIL THEN { first ← TRUE; menu.head ← newitem }
ELSE {
FOR rover ← NARROW[menu.head], rover.link UNTIL rover.link = NIL
DO ENDLOOP; --find end
rover.link ← newitem;
};
SELECT menu.orientation
FROM
vertical => {
width: INTEGER ← IF menu=data.colorMenu OR menu=data.lineColorMenu THEN colorLineWidth ELSE menuLineWidth;
newitem.tl[X] ← menu.tl[X] + menuLineWidth;
newitem.tl[Y] ← menu.br[Y] - width;
};
horizontal => {
width: INTEGER ← IF menu=data.colorMenu OR menu=data.lineColorMenu THEN colorLineWidth ELSE menuLineWidth;
newitem.tl[X] ← menu.br[X] + menuLineWidth;
newitem.tl[Y] ← menu.tl[Y] - width;
};
ENDCASE;
newitem.string ← string;
SELECT menu.orientation
FROM
vertical => {
lineWidth: INTEGER ← IF menu=data.colorMenu OR menu=data.lineColorMenu THEN colorLineWidth ELSE menuLineWidth;
oldWidth: INTEGER ← menu.br[X] - menu.tl[X] - twoLineWidth;
width: INTEGER ← ItemWidth[string];
IF first OR menu.tl[X] + width + twoLineWidth > menu.br[X]
THEN ChangeMenuWidth[menu, width]
ELSE newitem.br[X] ← newitem.tl[X]+oldWidth;
newitem.br[Y] ← newitem.tl[Y] - itemHeight;
menu.br[Y] ← newitem.br[Y] - lineWidth;
};
horizontal => {
lineWidth: INTEGER ← IF menu=data.colorMenu OR menu=data.lineColorMenu THEN colorLineWidth ELSE menuLineWidth;
newitem.br[X] ← newitem.tl[X] + ItemWidth[string];
menu.br[X] ← newitem.br[X] + lineWidth;
newitem.br[Y] ← newitem.tl[Y] - itemHeight;
};
ENDCASE;
IF menu.visible THEN GriffinRefresh.EraseAndSave[menu];
RETURN[newitem];
};
ChangeMenuWidth:
PROC [menu: GriffinMenu.MenuHandle, width:
INTEGER] = {
WidenMenu: GriffinMenu.MenuProc = {
item.br[X] ← item.tl[X] + width;
};
IF menu.orientation # vertical THEN ERROR;
ForAllMenuItems[menu, WidenMenu];
menu.br[X] ← menu.tl[X] + width + twoLineWidth;
IF menu.visible THEN GriffinRefresh.EraseAndSave[menu];
};
ForAllMenus:
PUBLIC
PROC [data: Data, proc: GriffinMenu.MenuHandleProc] = {
IsMenu: GriffinObject.ObjectProc = {
WITH object
SELECT
FROM
mref: REF GriffinObject.Object[menu] => proc[mref];
ENDCASE;
};
GriffinObject.ForAllObjects[data, IsMenu];
};
ForAllMenuItems:
PUBLIC
PROC [menu: GriffinMenu.MenuHandle, proc: GriffinMenu.MenuProc] = {
rover: GriffinMenu.MenuItemHandle;
FOR rover ← NARROW[menu.head], rover.link UNTIL rover = NIL
DO proc[rover] ENDLOOP;
};
PlotMenu:
PUBLIC
PROC [menu: GriffinMenu.MenuHandle, dc: Imager.Context] = {
Show: GriffinMenu.MenuProc = { ShowItem[item, dc] };
data: Data ← menu.data;
GriffinDisplay.BoxFill[menu.tl, menu.br, menuGrey, data.clipBox, dc];
ForAllMenuItems[menu, Show];
};
ShowMenu:
PUBLIC
PROC [menu: GriffinMenu.MenuHandle] = {
IF
NOT menu.visible
THEN {
menu.visible ← TRUE;
GriffinRefresh.MarkObject[menu];
};
};
HideMenu:
PUBLIC
PROC [menu: GriffinMenu.MenuHandle] = {
IF menu.visible
THEN {
menu.visible ← FALSE;
GriffinRefresh.EraseAndSave[menu];
};
};
ShowItem:
PROC [item: GriffinMenu.MenuItemHandle, dc: Imager.Context] = {
data: Data ← item.menu.data;
clipBox: GriffinKernel.ClipBox ← data.clipBox;
GriffinDisplay.BoxFill[item.tl, item.br, menuWhite, clipBox, dc];
GriffinDisplay.DrawCaption[MenuAnchorPoint[item], item.string, menuStyle, clipBox, dc];
IF item.inverted THEN GriffinDisplay.InvertBox[item.tl, item.br, clipBox, dc];
};
OverWhichItem:
PUBLIC
PROC [data: Data, pt: GriffinPoint.ScrPt]
RETURNS [GriffinMenu.MenuItemHandle] = {
IsOverMenu:
PROC [m: GriffinMenu.MenuHandle] = {
IF m.visible AND pt[X] IN [m.tl[X]..m.br[X]] AND pt[Y] IN [m.br[Y]..m.tl[Y]]
THEN lastover ← m;
};
LastItemOver: GriffinMenu.MenuProc = {
IF pt[X] IN [item.tl[X] .. item.br[X]] AND pt[Y] IN [item.br[Y] .. item.tl[Y]]
THEN lastitem ← item;
};
lastover: GriffinMenu.MenuHandle ← NIL;
lastitem: GriffinMenu.MenuItemHandle ← NIL;
ForAllMenus[data, IsOverMenu];
IF lastover = NIL THEN RETURN[NIL];
ForAllMenuItems[lastover, LastItemOver];
RETURN[lastitem]
};
IsOverItem:
PUBLIC
PROC [pt: GriffinPoint.ScrPt, item: GriffinMenu.MenuItemHandle]
RETURNS [
BOOLEAN] = {
RETURN[pt[X] IN [item.tl[X]..item.br[X]] AND pt[Y] IN [item.br[Y]..item.tl[Y]]];
};
BugItem:
PUBLIC
PROC [item: GriffinMenu.MenuItemHandle] = { item.proc[item] };
WhichMenu: PUBLIC PROC [item: GriffinMenu.MenuItemHandle]
RETURNS [GriffinMenu.MenuHandle] = {
RETURN[item.menu] };
IsSelected:
PUBLIC
PROC [item: GriffinMenu.MenuItemHandle]
RETURNS [
BOOLEAN] = {
RETURN[item.selected] };
MenuString:
PUBLIC
PROC [item: GriffinMenu.MenuItemHandle]
RETURNS [
ROPE] = {
RETURN[item.string] };
MenuAnchorPoint:
PROC [item: GriffinMenu.MenuItemHandle]
RETURNS [pt: GriffinPoint.ScrRealPt] = {
pt[X] ← (item.br[X]+item.tl[X])/2;
pt[Y] ← item.tl[Y]-menuMargin/2;
};
SetMenuString:
PUBLIC
PROC [item: GriffinMenu.MenuItemHandle, string:
ROPE] = {
menu: GriffinMenu.MenuHandle = item.menu;
width: INTEGER ← 0;
FindWidest: GriffinMenu.MenuProc = {
w: INTEGER ← ItemWidth[item.string];
IF w>width THEN width ← w;
};
item.string ← string;
IF menu.visible THEN GriffinRefresh.EraseAndSave[menu];
ForAllMenuItems[menu, FindWidest];
ChangeMenuWidth[menu, width];
IF menu.visible THEN GriffinRefresh.EraseAndSave[menu];
};
SelectOnly:
PUBLIC GriffinMenu.MenuProc = {
ForAllMenuItems[item.menu, Deselect];
Select[item];
};
Select:
PUBLIC GriffinMenu.MenuProc = {
item.selected ← TRUE;
ClearMenuItem[item]
};
Deselect:
PUBLIC GriffinMenu.MenuProc = {
IF item.selected
THEN {
item.selected ← FALSE;
ClearMenuItem[item];
};
};
HighlightMenuItem:
PUBLIC GriffinMenu.MenuProc = {
data: Data ← item.menu.data;
wasInverted: BOOLEAN = item.inverted;
PProc: GriffinViewer.PaintProc = {
GriffinObject.ReplotBoxFromObject[item.tl, item.br, item.menu, dc];
};
item.inverted ← ~ item.selected;
IF item.menu.visible AND wasInverted # item.inverted THEN GriffinViewer.DoPaint[data.viewer, PProc];
};
ClearMenuItem:
PUBLIC GriffinMenu.MenuProc = {
data: Data ← item.menu.data;
wasInverted: BOOLEAN = item.inverted;
PProc: GriffinViewer.PaintProc = {
GriffinObject.ReplotBoxFromObject[item.tl, item.br, item.menu, dc];
};
item.inverted ← item.selected;
IF item.menu.visible AND wasInverted # item.inverted
THEN GriffinViewer.DoPaint[data.viewer, PProc];
};
called only once when Griffin STARTS.
InitMenuStyle:
PUBLIC
PROC = {
extents: ImagerFont.Extents;
menuStyle ← NEW[GriffinStyle.Style];
menuStyle.color ← GriffinColor.StringToColor["black"];
menuStyle.fillcolor ← GriffinColor.StringToColor["grey"];
menuStyle.backgndcolor ← GriffinColor.StringToColor["white"];
menuStyle.anchor ← center;
menuStyle.stringRotation ← or0;
menuStyle.font ← ImagerFont.Find["Xerox/TiogaFonts/Cream10"];
extents ← ImagerFont.FontBoundingBox[menuStyle.font];
itemHeight ← Real.RoundI[extents.descent+extents.ascent]+menuMargin;
menuHeight ← itemHeight+twoLineWidth;
menuGrey ← NEW[GriffinDisplay.BkgndColorRec ← [] ];
menuGrey.constant ← ImagerColor.ColorFromGray[0.5];
menuWhite ← NEW[GriffinDisplay.BkgndColorRec ← [] ];
menuWhite.constant ← ImagerColor.ColorFromGray[0];
};