DIRECTORY Imager USING [black, Context, MaskIntRectangle, MaskCharacters, GetIntCP, SetColor, SetIntCP, white], Menus, MenusPrivate, MessageWindow USING [Append], Process USING [Detach, Milliseconds, MsecToTicks, priorityNormal, SetPriority, SetTimeout], Real USING [RoundC], Rope USING [Equal, FromRefText, ROPE], SafeStorage USING [NewZone], TIPUser USING [TIPScreenCoords], VFonts USING [defaultFont, Font, FontAscent, FontHeight, RopeWidth], ViewerClasses, ViewerOps USING [InvertForMenus, PaintViewer], ViewerSpecs USING [menuHeight], WindowManager USING [RestoreCursor], WindowManagerPrivate USING [DrawCaptionMenu, windowMenu]; MenusImpl: CEDAR MONITOR LOCKS entry USING entry: MenuEntry IMPORTS Imager, Menus, MessageWindow, Process, Real, Rope, SafeStorage, VFonts, ViewerOps, WindowManager, WindowManagerPrivate EXPORTS Menus, MenusPrivate SHARES Menus, ViewerOps = BEGIN OPEN ViewerClasses, Menus, MenusPrivate; menuFont: VFonts.Font _ VFonts.defaultFont; fontHeight: INTEGER; baselineOffset: INTEGER; widthFudge: INTEGER = 3; mZ: ZONE _ SafeStorage.NewZone[quantized]; CopyMenu: PUBLIC PROC [old: Menu] RETURNS [new: Menu] = BEGIN new _ CreateMenu[old.linesUsed]; FOR l: MenuLine IN MenuLine UNTIL old.lines[l]=NIL DO new.lines[l] _ old.lines[l]; CopyLine[new, l]; ENDLOOP; END; CopyLine: PROC [menu: Menu, line: INTEGER] = BEGIN oldEntry: MenuEntry _ menu.lines[line]; newEntry: MenuEntry; menu.lines[line] _ NIL; FOR t: MenuEntry _ oldEntry, t.link UNTIL t=NIL DO IF menu.lines[line]=NIL THEN newEntry _ menu.lines[line]_mZ.NEW[MenuEntryRec _ t^] ELSE newEntry _ newEntry.link _ mZ.NEW[MenuEntryRec _ t^]; newEntry.greyed _ FALSE; newEntry.guardState _ IF newEntry.guarded THEN guarded ELSE armed; ENDLOOP; END; CreateEntry: PUBLIC PROC [name: Rope.ROPE, proc: ClickProc, clientData: REF ANY _ NIL, documentation: REF ANY _ NIL, fork: BOOL _ TRUE, guarded: BOOL _ FALSE] RETURNS [entry: MenuEntry] = BEGIN entry _ mZ.NEW[MenuEntryRec _ [ name: name, proc: proc, clientData: clientData, documentation: documentation, fork: fork, guarded: guarded, guardState: IF guarded THEN guarded ELSE armed, width: VFonts.RopeWidth[name, menuFont] ]]; END; FindEntry: PUBLIC PROC [menu: Menu, entryName: Rope.ROPE] RETURNS [entry: MenuEntry] = BEGIN IF menu=NIL THEN RETURN[NIL]; FOR l: MenuLine IN MenuLine UNTIL menu.lines[l]=NIL DO FOR e: MenuEntry _ menu.lines[l], e.link UNTIL e=NIL DO IF Rope.Equal[e.name, entryName] THEN RETURN[e]; ENDLOOP; ENDLOOP; END; InsertMenuEntry: PUBLIC PROC [menu: Menu, entry: MenuEntry, line: MenuLine _ 0] = BEGIN entry.link _ menu.lines[line]; menu.lines[line] _ entry; entry.xPos _ menuHLeading; FOR e: MenuEntry _ menu.lines[line].link, e.link UNTIL e=NIL DO e.xPos _ e.xPos + entry.width + menuHSpace; ENDLOOP; END; AppendMenuEntry: PUBLIC PROC [menu: Menu, entry: MenuEntry, line: MenuLine _ 0] = BEGIN entry.link _ NIL; IF menu.lines[line]=NIL THEN {entry.xPos _ menuHLeading; menu.lines[line] _ entry} ELSE FOR e: MenuEntry _ menu.lines[line], e.link UNTIL e.link=NIL DO REPEAT FINISHED => {entry.xPos _ e.xPos+e.width+menuHSpace; e.link _ entry}; ENDLOOP; END; targetNotFound: PUBLIC SIGNAL = CODE; ReplaceMenuEntry: PUBLIC PROC [menu: Menu, oldEntry: MenuEntry, newEntry: MenuEntry _ NIL] = BEGIN l: MenuLine; x: INTEGER _ menuHLeading; exit: BOOL _ FALSE; FOR l IN MenuLine UNTIL menu.lines[l]=NIL DO IF oldEntry=menu.lines[l] THEN BEGIN IF newEntry=NIL THEN menu.lines[l] _ menu.lines[l].link ELSE BEGIN newEntry.link _ oldEntry.link; menu.lines[l] _ newEntry; END; EXIT; END ELSE FOR e: MenuEntry _ menu.lines[l], e.link UNTIL e.link=NIL DO IF e.link=oldEntry THEN BEGIN IF newEntry=NIL THEN e.link _ e.link.link ELSE BEGIN newEntry.link _ oldEntry.link; e.link _ newEntry; END; exit _ TRUE; EXIT; END; ENDLOOP; IF exit THEN EXIT; REPEAT FINISHED => SIGNAL targetNotFound; ENDLOOP; menu.lines[l].xPos _ menuHLeading; FOR e: MenuEntry _ menu.lines[l], e.link UNTIL e=NIL DO e.xPos _ x; x _ x+e.width+menuHSpace; ENDLOOP; END; MenuRow: PROC [menu: Menu, viewer: ViewerClasses.Viewer, y: INTEGER] RETURNS [INTEGER] = INLINE BEGIN RETURN[IF menu.linesUsed=1 THEN 0 ELSE (menu.y+ViewerSpecs.menuHeight-y-viewer.wy)/ViewerSpecs.menuHeight]; END; MarkMenu: PUBLIC PROC [menu: Menus.Menu, parent: ViewerClasses.Viewer, mousePos: TIPUser.TIPScreenCoords] = BEGIN row: INTEGER _ MenuRow[menu, parent, mousePos.mouseY]; newEntry: MenuEntry _ ResolveEntry[menu, row, mousePos.mouseX]; IF newEntry=NIL THEN BEGIN IF menu.inverted#NIL THEN InvertEntry[menu, parent]; menu.inverted _ NIL; END ELSE IF newEntry#menu.inverted THEN BEGIN IF menu.inverted#NIL THEN InvertEntry[menu, parent]; menu.inverted _ newEntry; menu.lineInverted _ row; InvertEntry[menu, parent]; END; END; HitMenu: PUBLIC PROC [menu: Menus.Menu, parent: ViewerClasses.Viewer, mousePos: TIPUser.TIPScreenCoords, button: MouseButton, shift, control: BOOL _ FALSE] = BEGIN row: INTEGER _ MenuRow[menu, parent, mousePos.mouseY]; entry: MenuEntry _ ResolveEntry[menu, row, mousePos.mouseX]; hit: BOOL _ menu.inverted#NIL AND entry=menu.inverted; IF menu.inverted#NIL THEN InvertEntry[menu, parent]; -- take down menu.inverted _ NIL; IF hit THEN ProcessMenuHit[entry, menu, parent, button, shift, control]; END; ProcessMenuHit: ENTRY PROC [entry: MenuEntry, menu: Menus.Menu, parent: ViewerClasses.Viewer, button: MouseButton, shift, control: BOOL] = TRUSTED BEGIN SELECT entry.guardState FROM guarded => BEGIN entry.guardState _ arming; Process.Detach[FORK ArmMenuProc[entry, menu, parent]]; IF entry.documentation#NIL THEN Process.Detach[ FORK Document[entry.documentation, parent, entry.clientData, button, shift, control]]; END; arming=> NULL; -- no action armed => BEGIN IF entry.guarded THEN entry.guardState _ guarded; IF entry.fork THEN Process.Detach[FORK MenuPusher[entry, menu, parent, button, shift, control]] ELSE MenuPusher[entry, menu, parent, button, shift, control, FALSE]; END; ENDCASE; END; ArmMenuProc: ENTRY PROC [entry: MenuEntry, menu: Menus.Menu, parent: ViewerClasses.Viewer] = BEGIN menuWaitCondition: CONDITION; TRUSTED {Process.SetTimeout[@menuWaitCondition, Process.MsecToTicks[armingTime]]}; WAIT menuWaitCondition; IF entry.guardState = arming THEN BEGIN entry.guardState _ armed; RedrawMenu[parent, menu, entry]; TRUSTED {Process.SetTimeout[@menuWaitCondition, Process.MsecToTicks[armedTime]]}; WAIT menuWaitCondition; END; IF entry.guardState#guarded THEN BEGIN entry.guardState _ guarded; RedrawMenu[parent, menu, entry]; END; END; MenuPusher: PROC [entry: MenuEntry, menu: Menu, parent: ViewerClasses.Viewer, button: MouseButton, shift, control: BOOL _ FALSE, normalPriority: BOOL _ TRUE] = BEGIN entry.greyed _ TRUE; RedrawMenu[parent, menu, entry]; IF normalPriority THEN TRUSTED {Process.SetPriority[Process.priorityNormal]}; entry.proc[parent, entry.clientData, button, shift, control ! ABORTED => CONTINUE]; entry.greyed _ FALSE; RedrawMenu[parent, menu, entry]; WindowManager.RestoreCursor[]; END; RedrawMenu: PROC [viewer: Viewer, menu: Menu, entry: MenuEntry] = BEGIN IF menu=WindowManagerPrivate.windowMenu THEN WindowManagerPrivate.DrawCaptionMenu[viewer, FALSE] ELSE ViewerOps.PaintViewer[viewer, menu, entry=NIL, entry]; END; Document: PUBLIC PROC [info: REF ANY, parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: MouseButton _ red, shift, control: BOOL _ FALSE] = BEGIN WITH info SELECT FROM doc: REF TEXT => MessageWindow.Append[Rope.FromRefText[doc], TRUE]; doc: REF ClickProc => doc^[parent, clientData, mouseButton, shift, control]; doc: Rope.ROPE => MessageWindow.Append[doc, TRUE]; ENDCASE => ERROR; -- not valid documentation END; ClearMenu: PUBLIC PROC [menu: Menu, parent: ViewerClasses.Viewer, paint: BOOL _ TRUE] = BEGIN IF menu#NIL AND menu.inverted#NIL THEN BEGIN IF paint THEN InvertEntry[menu, parent]; menu.inverted _ NIL; END; END; myGrey: REF CARDINAL = NEW[CARDINAL _ 001010B]; DrawMenu: PUBLIC PROC [menu: Menu, context: Imager.Context, x, y: INTEGER, whatChanged: REF ANY _ NIL] = BEGIN OPEN Imager; line: INTEGER _ 0; quitEarly: BOOL _ FALSE; menu.x _ x; menu.y _ y; FOR row: INTEGER IN [0..menu.linesUsed) DO ey: INTEGER ~ y-line; SetIntCP[context, [x+menuHLeading, y+baselineOffset-line]]; FOR e: MenuEntry _ menu.lines[row], e.link UNTIL e=NIL DO ex: INTEGER ~ x+e.xPos; IF whatChanged#NIL THEN BEGIN IF e=whatChanged THEN BEGIN SetColor[context, white]; MaskIntRectangle[context, [ex-widthFudge, ey, e.width+widthFudge+widthFudge, fontHeight-1]]; SetColor[context, black]; quitEarly _ TRUE; END ELSE BEGIN SetIntCP[context, [GetIntCP[context].x+e.width+menuHSpace, GetIntCP[context].y]]; LOOP; END; END; IF e.greyed THEN BEGIN SetColor[context, myGrey]; MaskIntRectangle[context, [ex-widthFudge, ey, e.width+widthFudge+widthFudge, fontHeight-1]]; SetColor[context, black]; END; MaskCharacters[context, VFonts.defaultFont, e.name]; IF e.guarded AND e.guardState#armed THEN BEGIN by: INTEGER ~ ey+baselineOffset+2; MaskIntRectangle[context, [ex-1, by, e.width+2, 1]]; END; IF quitEarly THEN RETURN; SetIntCP[context, [GetIntCP[context].x+menuHSpace, GetIntCP[context].y]]; ENDLOOP; line _ line + ViewerSpecs.menuHeight; ENDLOOP; menu.inverted _ NIL; END; ResolveEntry: PROC [menu: Menu, line, x: INTEGER] RETURNS [entry: MenuEntry] = BEGIN IF line NOT IN [0..menu.linesUsed) THEN RETURN[NIL]; FOR e: MenuEntry _ menu.lines[line], e.link UNTIL e=NIL OR e.xPos>x DO IF x IN [e.xPos..e.xPos+e.width) THEN RETURN[e]; ENDLOOP; RETURN[NIL]; END; InvertEntry: PROC [menu: Menu, parent: Viewer] = BEGIN IF ~parent.destroyed AND ~parent.iconic THEN ViewerOps.InvertForMenus[parent, menu.inverted.xPos-widthFudge, menu.y - (ViewerSpecs.menuHeight*menu.lineInverted), menu.inverted.width+(2*widthFudge), fontHeight-1]; END; ComputeFontInfo: PROC = BEGIN ascent: INTEGER = VFonts.FontAscent[menuFont]; fontHeight _ VFonts.FontHeight[menuFont]; baselineOffset _ Real.RoundC[fontHeight-ascent]; END; ComputeFontInfo[]; END.  MenusImpl.mesa; Written by S. McGregor Edited by McGregor on August 4, 1983 11:22 am Last Edited by: Maxwell, May 24, 1983 7:48 am assert: state=arming Κ Τ– "cedar" style˜JšΟc&™&Jš-™-J™-J™šΟk ˜ JšœžœY˜eJ˜J˜ Jšœžœ ˜JšœžœN˜[Jšœžœ ˜Jšœžœžœ˜&Jšœ žœ ˜Jšœžœ˜ Jšœžœ8˜DJ˜Jšœ žœ˜.Jšœ žœ˜Jšœžœ˜$Jšœžœ˜9J˜—Jšœ ž œžœžœ˜;J˜šžœ@˜GJ˜6—Jšžœ˜Jšžœ˜J˜Jšžœžœ$˜.J˜J˜+Jšœ žœ˜Jšœžœ˜J˜Jšœ žœ˜J˜Jšœžœ"˜*J˜š Οnœžœžœ žœž˜=J˜ š žœ žœ žœžœž˜5J˜J˜Jšžœ˜—Jšžœ˜J˜—šŸœžœžœž˜2J˜'J˜Jšœžœ˜šžœ!žœžœž˜2Jšžœžœžœ žœ˜RJšžœžœ˜:Jšœžœ˜Jšœžœžœ žœ˜BJšžœ˜—Jšžœ˜J˜—šŸ œžœžœ žœžœžœžœ˜VJšœžœžœžœžœžœ žœžœ˜GJšžœž˜"šœ žœ˜J˜ J˜ J˜J˜J˜ J˜Jšœ žœ žœ žœ˜/J˜'J˜Jšžœ˜J˜——š Ÿ œžœžœžœžœ˜VJšž˜Jš žœžœžœžœžœ˜š žœ žœ žœžœž˜6šžœ&žœžœž˜7Jšžœžœžœ˜0Jšžœ˜—Jšžœ˜—Jšžœ˜J˜—šŸœžœžœ6ž˜WJ˜J˜J˜šžœ.žœžœž˜?J˜+Jšžœ˜—Jšžœ˜J˜—šŸœžœžœ6ž˜WJšœ žœ˜Jšžœžœžœ6˜Rš žœžœ)žœžœž˜DJšžœžœ=˜LJšžœ˜—Jšžœ˜J˜—Jšœžœžœžœ˜%J˜šŸœžœžœ"˜?Jšœžœž˜"J˜ Jšœžœ˜Jšœžœžœ˜š žœžœ žœžœž˜,šžœžœž˜$Jšžœ žœžœ#˜7šžœž˜ J˜J˜Jšžœ˜—Jšžœ˜Jšž˜—š žœžœ&žœžœž˜Ašžœžœž˜Jšžœ žœžœ˜)šžœž˜ J˜J˜Jšžœ˜—Jšœžœžœ˜Jšžœ˜—Jšžœ˜—Jšžœžœžœ˜Jšžœžœžœ˜)Jšžœ˜—J˜"šžœ&žœžœž˜7J˜ J˜Jšžœ˜—Jšžœ˜J˜—šŸœžœ/žœ˜DJšžœžœžœž˜ šžœžœžœž˜&J˜D—Jšžœ˜J˜—šŸœžœžœ1˜FJšœ%ž˜*Jšœžœ*˜6J˜?šžœ žœžœž˜Jšžœžœžœ˜4Jšœžœ˜Jšž˜—šžœžœžœž˜)Jšžœžœžœ˜4J˜J˜J˜Jšžœ˜—Jšžœ˜J˜—šŸœžœžœ1˜EJšœHžœžœž˜]Jšœžœ*˜6J˜žœžœ˜SJšœžœ˜J˜ J˜Jšžœ˜J˜—šŸ œžœ2ž˜Gšžœ&ž˜,Jšœ-žœ˜3—Jšžœ+žœ ˜;Jšžœ˜J˜—šŸœžœžœžœžœ žœžœžœžœžœ˜QJšœ0žœžœž˜Ešžœžœž˜Jšœžœžœ1žœ˜DJšœžœD˜LJšœ žœžœ˜3Jšžœžœ˜/—Jšžœ˜J˜—š Ÿ œžœžœ3žœžœ˜WJšž˜š žœžœžœžœžœž˜,Jšžœžœ˜(Jšœžœ˜Jšžœ˜—Jšžœ˜J˜—Jš œžœžœžœžœ ˜/J˜šŸœžœžœ-žœ˜JJšœ žœžœžœž˜#Jšžœ˜ Jšœžœ˜Jšœ žœžœ˜J˜ J˜ šžœžœžœž˜*Jšœžœ ˜J˜;šžœ(žœžœž˜9Jšœžœ ˜šžœ žœžœž˜šžœžœž˜J˜˜LJ˜—J˜Jšœ žœ˜Jšž˜—šžœž˜ JšœQ˜QJšžœ˜Jšžœ˜—Jšžœ˜—šžœ žœž˜J˜J˜\J˜Jšžœ˜—J˜4šžœ žœžœž˜.Jšœžœ˜"J˜4Jšžœ˜—Jšžœ žœžœ˜JšœFžœ˜IJšžœ˜—J˜%Jšžœ˜—Jšœžœ˜Jšžœ˜J˜—š Ÿ œžœžœžœž˜TJš žœžœžœžœžœžœ˜4š žœ)žœžœžœ ž˜FJšžœžœžœžœ˜0Jšžœ˜—Jšžœžœ˜ Jšžœ˜J˜—šŸ œžœ ž˜6šžœžœž˜,˜?J˜4J˜2——Jšžœ˜J˜—šŸœžœž˜Jšœžœ˜.J˜)Jšœ0˜0Jšžœ˜J˜—J˜J˜Jšžœ˜J˜J˜—…—&Ζ4: