DIRECTORY Font USING [Extents, FONT, FontBoundingBox], Graphics USING [Context], Imager USING [Color, Context, MaskRectangleI, SetColor, SetXRelI, SetXYI, ShowRope, white], ImagerOps USING [ColorFromStipple, ImagerFromGraphics], Menus USING [armedTime, armingTime, ClickProc, CreateMenu, Menu, MenuEntry, MenuEntryRec, MenuLine, MouseButton], MenusPrivate USING [menuHLeading, menuHSpace], MessageWindow USING [Append], Process USING [Detach, Milliseconds, MsecToTicks, priorityNormal, SetPriority, SetTimeout], Real USING [RoundC], Rope USING [Equal, FromRefText, ROPE], TIPUser USING [TIPScreenCoords], VFonts USING [defaultFont, Font, StringWidth], ViewerClasses USING [Viewer], ViewerExtras USING [ImagerFont], ViewerOps USING [InvertForMenus, PaintViewer], ViewerSpecs USING [menuHeight], WindowManager USING [RestoreCursor], WindowManagerPrivate USING [DrawCaptionMenu, windowMenu]; MenusImpl: CEDAR MONITOR LOCKS entry USING entry: MenuEntry IMPORTS Font, Imager, ImagerOps, Menus, MessageWindow, Process, Real, Rope, VFonts, ViewerExtras, ViewerOps, WindowManager, WindowManagerPrivate EXPORTS Menus, MenusPrivate SHARES Menus, ViewerOps = BEGIN OPEN Menus, MenusPrivate; Viewer: TYPE ~ ViewerClasses.Viewer; menuVFont: VFonts.Font _ VFonts.defaultFont; menuFont: Font.FONT _ ViewerExtras.ImagerFont[menuVFont]; fontHeight: INTEGER; baselineOffset: INTEGER; widthFudge: INTEGER = 3; CopyMenu: PUBLIC PROC [old: Menu] RETURNS [new: Menu] = { 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; }; CopyLine: PROC [menu: Menu, line: INTEGER] = { 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]_NEW[MenuEntryRec _ t^] ELSE newEntry _ newEntry.link _ NEW[MenuEntryRec _ t^]; newEntry.greyed _ FALSE; newEntry.guardState _ IF newEntry.guarded THEN guarded ELSE armed; ENDLOOP; }; 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] = { entry _ NEW[MenuEntryRec _ [ name: name, proc: proc, clientData: clientData, documentation: documentation, fork: fork, guarded: guarded, guardState: IF guarded THEN guarded ELSE armed, width: VFonts.StringWidth[name, menuVFont] ]]; }; FindEntry: PUBLIC PROC [menu: Menu, entryName: Rope.ROPE] RETURNS [entry: MenuEntry] = { 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; }; InsertMenuEntry: PUBLIC PROC [menu: Menu, entry: MenuEntry, line: MenuLine _ 0] = { 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; }; AppendMenuEntry: PUBLIC PROC [menu: Menu, entry: MenuEntry, line: MenuLine _ 0] = { 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; }; targetNotFound: PUBLIC SIGNAL = CODE; ReplaceMenuEntry: PUBLIC PROC [menu: Menu, oldEntry: MenuEntry, newEntry: MenuEntry _ NIL] = { l: MenuLine; x: INTEGER _ menuHLeading; exit: BOOL _ FALSE; FOR l IN MenuLine UNTIL menu.lines[l]=NIL DO IF oldEntry=menu.lines[l] THEN { IF newEntry=NIL THEN menu.lines[l] _ menu.lines[l].link ELSE { newEntry.link _ oldEntry.link; menu.lines[l] _ newEntry; }; EXIT; } ELSE FOR e: MenuEntry _ menu.lines[l], e.link UNTIL e.link=NIL DO IF e.link=oldEntry THEN { IF newEntry=NIL THEN e.link _ e.link.link ELSE { newEntry.link _ oldEntry.link; e.link _ newEntry; }; exit _ TRUE; EXIT; }; 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; }; MenuRow: PROC [menu: Menu, viewer: ViewerClasses.Viewer, y: INTEGER] RETURNS [INTEGER] = INLINE { RETURN[IF menu.linesUsed=1 THEN 0 ELSE (menu.y+ViewerSpecs.menuHeight-y-viewer.wy)/ViewerSpecs.menuHeight]; }; MarkMenu: PUBLIC PROC [menu: Menus.Menu, parent: ViewerClasses.Viewer, mousePos: TIPUser.TIPScreenCoords] = { row: INTEGER _ MenuRow[menu, parent, mousePos.mouseY]; newEntry: MenuEntry _ ResolveEntry[menu, row, mousePos.mouseX]; IF newEntry=NIL THEN { IF menu.inverted#NIL THEN InvertEntry[menu, parent]; menu.inverted _ NIL; } ELSE IF newEntry#menu.inverted THEN { IF menu.inverted#NIL THEN InvertEntry[menu, parent]; menu.inverted _ newEntry; menu.lineInverted _ row; InvertEntry[menu, parent]; }; }; HitMenu: PUBLIC PROC [menu: Menus.Menu, parent: ViewerClasses.Viewer, mousePos: TIPUser.TIPScreenCoords, button: MouseButton, shift, control: BOOL _ FALSE] = { 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]; }; ProcessMenuHit: ENTRY PROC [entry: MenuEntry, menu: Menus.Menu, parent: ViewerClasses.Viewer, button: MouseButton, shift, control: BOOL] = TRUSTED { SELECT entry.guardState FROM guarded => { 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]]; }; arming=> NULL; -- no action armed => { 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]; }; ENDCASE; }; ArmMenuProc: ENTRY PROC [entry: MenuEntry, menu: Menus.Menu, parent: ViewerClasses.Viewer] = { menuWaitCondition: CONDITION; TRUSTED {Process.SetTimeout[@menuWaitCondition, Process.MsecToTicks[armingTime]]}; WAIT menuWaitCondition; IF entry.guardState = arming THEN { entry.guardState _ armed; RedrawMenu[parent, menu, entry]; TRUSTED {Process.SetTimeout[@menuWaitCondition, Process.MsecToTicks[armedTime]]}; WAIT menuWaitCondition; }; IF entry.guardState#guarded THEN { entry.guardState _ guarded; RedrawMenu[parent, menu, entry]; }; }; MenuPusher: PROC [entry: MenuEntry, menu: Menu, parent: ViewerClasses.Viewer, button: MouseButton, shift, control: BOOL _ FALSE, normalPriority: BOOL _ TRUE] = { 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[]; }; RedrawMenu: PROC [viewer: Viewer, menu: Menu, entry: MenuEntry] = { IF menu=WindowManagerPrivate.windowMenu THEN WindowManagerPrivate.DrawCaptionMenu[viewer, FALSE] ELSE ViewerOps.PaintViewer[viewer, menu, entry=NIL, entry]; }; Document: PUBLIC PROC [info: REF ANY, parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: MouseButton _ red, shift, control: BOOL _ FALSE] = { 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 }; ClearMenu: PUBLIC PROC [menu: Menu, parent: ViewerClasses.Viewer, paint: BOOL _ TRUE] = { IF menu#NIL AND menu.inverted#NIL THEN { IF paint THEN InvertEntry[menu, parent]; menu.inverted _ NIL; }; }; greyGuard: PUBLIC BOOL _ FALSE; -- grey text of guarded items else strikeout myGrey: Imager.Color ~ ImagerOps.ColorFromStipple[001010B]; DrawMenu: PUBLIC PROC [menu: Menu, context: Graphics.Context, x, y: INTEGER, whatChanged: REF ANY _ NIL] = { imager: Imager.Context ~ ImagerOps.ImagerFromGraphics[context]; line: INTEGER _ 0; quitEarly: BOOL _ FALSE; menu.x _ x; menu.y _ y; FOR row: INTEGER IN [0..menu.linesUsed) DO ey: INTEGER ~ y-line; Imager.SetXYI[imager, 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 { IF e=whatChanged THEN { Imager.SetColor[imager, Imager.white]; Imager.MaskRectangleI[context: imager, x: ex-widthFudge, y: ey, w: e.width+2*widthFudge, h: fontHeight-1]; quitEarly _ TRUE; } ELSE { Imager.SetXRelI[imager, e.width+menuHSpace]; LOOP }; }; IF e.greyed THEN { Imager.SetColor[imager, myGrey]; Imager.MaskRectangleI[context: imager, x: ex-widthFudge, y: ey, w: e.width+2*widthFudge, h: fontHeight-1]; }; Imager.ShowRope[imager, e.name]; IF e.guarded AND e.guardState#armed THEN { by: INTEGER ~ ey+baselineOffset+2; Imager.MaskRectangleI[context: imager, x: ex-1, y: by, w: e.width+2, h: 1]; }; IF quitEarly THEN RETURN; Imager.SetXRelI[imager, menuHSpace]; ENDLOOP; line _ line + ViewerSpecs.menuHeight; ENDLOOP; menu.inverted _ NIL; }; ResolveEntry: PROC [menu: Menu, line, x: INTEGER] RETURNS [entry: MenuEntry] = { 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]; }; InvertEntry: PROC [menu: Menu, parent: Viewer] = { 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]; }; ComputeFontInfo: PROC = { min, max: REAL; extents: Font.Extents ~ Font.FontBoundingBox[menuFont]; min _ -extents.descent; max _ extents.ascent; fontHeight _ Real.RoundC[max-min+1]; baselineOffset _ Real.RoundC[-min]; }; ComputeFontInfo[]; END. .MenusImpl.mesa Copyright c 1982, 1983, 1984 Xerox Corporation. All rights reserved. Edited by McGregor on October 26, 1982 2:31 pm Last Edited by: Maxwell, May 24, 1983 7:48 am Last Edited by: Paul Rovner, June 15, 1983 5:27 pm Doug Wyatt, September 4, 1984 3:27:22 pm PDT assert: state=arming Κ ’– "Mesa" style˜šΟc™Jšœ Οmœœ5™EJš.™.J™-J™2J™,—J™šΟk ˜ JšœŸœ Ÿœ˜,Jšœ Ÿœ ˜JšœŸœO˜[Jšœ Ÿœ(˜7JšœŸœf˜qJšœ Ÿœ˜.JšœŸœ ˜JšœŸœN˜[JšœŸœ ˜JšœŸœŸœ˜&JšœŸœ˜ JšœŸœ"˜.JšœŸœ ˜Jšœ Ÿœ˜ Jšœ Ÿœ˜.Jšœ Ÿœ˜JšœŸœ˜$JšœŸœ˜9J˜—Jšœ Ÿ œŸœŸœ˜;JšŸœ‰˜JšŸœ˜JšŸœ˜JšœŸœŸœ˜!J˜JšœŸœ˜$J˜J˜,JšœŸœ&˜9Jšœ Ÿœ˜JšœŸœ˜J˜Jšœ Ÿœ˜J˜J˜šΟnœŸœŸœ Ÿœ˜9J˜ š Ÿœ Ÿœ ŸœŸœŸ˜5J˜J˜JšŸœ˜—Jšœ˜J˜—š œŸœŸœ˜.J˜'J˜JšœŸœ˜šŸœ!ŸœŸœŸ˜2JšŸœŸœŸœŸœ˜OJšŸœŸœ˜7JšœŸœ˜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˜—š œŸœŸœ7˜SJ˜J˜J˜šŸœ.ŸœŸœŸ˜?J˜+JšŸœ˜—Jšœ˜J˜—š œŸœŸœ7˜SJšœ Ÿœ˜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ŸœŸœ˜YJšœŸœ*˜6J˜ŸœŸœ˜SJšœŸœ˜J˜ J˜Jšœ˜J˜—š  œŸœ3˜CšŸœ&Ÿ˜,Jšœ-Ÿœ˜3—JšŸœ+Ÿœ ˜;Jšœ˜J˜—š œŸœŸœŸœŸœ ŸœŸœŸœŸœŸœ˜QJšœ0ŸœŸœ˜AšŸœŸœŸ˜JšœŸœŸœ1Ÿœ˜DJšœŸœD˜LJšœ ŸœŸœ˜3JšŸœŸœ˜/—Jšœ˜J˜—š   œŸœŸœ3ŸœŸœ˜WJšœ˜š ŸœŸœŸœŸœŸœ˜(JšŸœŸœ˜(JšœŸœ˜Jšœ˜—Jšœ˜J˜—Jšœ ŸœŸœŸœ,˜LJ˜Jšœ;˜;J˜š œŸœŸœ/Ÿœ˜LJšœ ŸœŸœŸœ˜J˜?JšœŸœ˜Jšœ ŸœŸœ˜J˜ J˜ šŸœŸœŸœŸ˜*JšœŸœ ˜J˜=šŸœ(ŸœŸœŸ˜9JšœŸœ ˜šŸœ ŸœŸœ˜šŸœŸœ˜J˜&˜&J˜C—Jšœ Ÿœ˜Jšœ˜—JšŸœ0Ÿœ˜;Jšœ˜—šŸœ Ÿœ˜J˜ ˜&J˜C—Jšœ˜—J˜ šŸœ ŸœŸœ˜*JšœŸœ˜"J˜KJšœ˜—JšŸœ ŸœŸœ˜Jšœ$˜$JšŸœ˜—J˜%JšŸœ˜—JšœŸœ˜Jšœ˜J˜—š  œŸœŸœŸœ˜PJš ŸœŸœŸœŸœŸœŸœ˜4š Ÿœ)ŸœŸœŸœ Ÿ˜FJšŸœŸœŸœŸœ˜0JšŸœ˜—JšŸœŸœ˜ Jšœ˜J˜—š  œŸœ!˜2šŸœŸœŸ˜,˜?J˜4J˜2——Jšœ˜J˜—š œŸœ˜Jšœ Ÿœ˜Jšœ7˜7Jšœ˜J˜J˜$J˜#Jšœ˜J˜—J˜J˜JšŸœ˜J˜J˜—…—'–5f