DIRECTORY CedarProcess USING [SetPriority], Imager USING [black, Color, Context, MaskRectangleI, SetColor, SetFont, SetXRelI, SetXYI, ShowRope, white], ImagerFont USING [Extents, FontBoundingBox], ImagerBackdoor USING [MakeStipple], Menus USING [armedTime, armingTime, ClickProc, Menu, MenuEntry, MenuEntryRec, MenuRec, MenuLine, MouseButton], MessageWindow USING [Append], Process USING [Detach, Milliseconds, MsecToTicks, SetTimeout], Real USING [RoundC], Rope USING [Equal, FromRefText, ROPE], TIPUser USING [TIPScreenCoords], VFonts USING [defaultFont, Font, StringWidth], ViewerClasses USING [Viewer], ViewerOps USING [PaintViewer], ViewerPrivate USING [DrawCaptionMenu, InvertForMenus, menuHLeading, menuHSpace, windowMenu], ViewerSpecs USING [menuHeight], WindowManager USING [RestoreCursor]; MenusImpl: CEDAR MONITOR LOCKS entry USING entry: MenuEntry IMPORTS CedarProcess, Imager, ImagerBackdoor, ImagerFont, MessageWindow, Process, Real, Rope, VFonts, ViewerOps, ViewerPrivate, ViewerSpecs, WindowManager EXPORTS Menus, ViewerPrivate SHARES Menus, ViewerOps = BEGIN OPEN Menus; Viewer: TYPE ~ ViewerClasses.Viewer; menuFont: VFonts.Font _ VFonts.defaultFont; fontHeight: INTEGER _ 0; baselineOffset: INTEGER _ 0; margin: INTEGER = 3; CreateMenu: PUBLIC PROC [lines: MenuLine _ 1] RETURNS [menu: Menu] ~ { RETURN[NEW[MenuRec _ [linesUsed: lines]]] }; 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, menuFont] ]]; }; CopyEntry: PUBLIC PROC [oldEntry: MenuEntry] RETURNS [newEntry: MenuEntry] = { RETURN[NEW[MenuEntryRec _ oldEntry^]]; }; SetGuarded: PUBLIC PROC [entry: MenuEntry, guard: BOOL] = { entry.guarded _ guard; entry.guardState _ IF guard THEN guarded ELSE armed; }; SetClientData: PUBLIC PROC [entry: MenuEntry, newData: REF ANY] RETURNS [oldData: REF ANY] = { oldData _ entry.clientData; entry.clientData _ newData; }; SetDocumentation: PUBLIC PROC [entry: MenuEntry, newDoc: REF ANY] RETURNS [oldDoc: REF ANY] = { oldDoc _ entry.documentation; entry.documentation _ newDoc; }; 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 _ ViewerPrivate.menuHLeading; FOR e: MenuEntry _ menu.lines[line].link, e.link UNTIL e=NIL DO e.xPos _ e.xPos + entry.width + ViewerPrivate.menuHSpace; ENDLOOP; }; AppendMenuEntry: PUBLIC PROC [menu: Menu, entry: MenuEntry, line: MenuLine _ 0] = { entry.link _ NIL; IF menu.lines[line]=NIL THEN {entry.xPos _ ViewerPrivate.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+ViewerPrivate.menuHSpace; e.link _ entry}; ENDLOOP; }; targetNotFound: PUBLIC SIGNAL = CODE; ReplaceMenuEntry: PUBLIC PROC [menu: Menu, oldEntry: MenuEntry, newEntry: MenuEntry _ NIL] = { l: MenuLine; x: INTEGER _ ViewerPrivate.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; IF menu.lines[l]#NIL THEN menu.lines[l].xPos _ ViewerPrivate.menuHLeading; FOR e: MenuEntry _ menu.lines[l], e.link UNTIL e=NIL DO e.xPos _ x; x _ x+e.width+ViewerPrivate.menuHSpace; ENDLOOP; }; ChangeNumberOfLines: PUBLIC PROC [menu: Menu, newLines: MenuLine] = { menu.linesUsed _ newLines; }; GetNumberOfLines: PUBLIC PROC [menu: Menu] RETURNS [lines: MenuLine] = { RETURN[menu.linesUsed]; }; SetLine: PUBLIC PROC [menu: Menu, line: MenuLine, entryList: MenuEntry] = { menu.lines[line] _ entryList; menu.linesUsed _ MAX[menu.linesUsed, line+1]; }; GetLine: PUBLIC PROC [menu: Menu, line: MenuLine] RETURNS [entryList: MenuEntry] = { RETURN[menu.lines[line]]; }; MarkMenu: PUBLIC PROC [menu: Menus.Menu, parent: ViewerClasses.Viewer, mousePos: TIPUser.TIPScreenCoords] = { delta: INTEGER ~ (menu.y-mousePos.mouseY); IF delta >= 0 THEN { row: NAT ~ NAT[delta]/ViewerSpecs.menuHeight; newEntry: MenuEntry _ ResolveEntry[menu, row, mousePos.mouseX]; SELECT TRUE FROM newEntry=NIL => { IF menu.inverted#NIL THEN InvertEntry[menu, parent]; menu.inverted _ NIL; }; newEntry#menu.inverted => { IF menu.inverted#NIL THEN InvertEntry[menu, parent]; menu.inverted _ newEntry; menu.lineInverted _ row; InvertEntry[menu, parent]; }; ENDCASE; }; }; HitMenu: PUBLIC PROC [menu: Menus.Menu, parent: ViewerClasses.Viewer, mousePos: TIPUser.TIPScreenCoords, button: MouseButton, shift, control: BOOL _ FALSE] = { delta: INTEGER ~ (menu.y-mousePos.mouseY); IF delta >= 0 THEN { row: NAT ~ NAT[delta]/ViewerSpecs.menuHeight; 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 CedarProcess.SetPriority[normal]; 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=ViewerPrivate.windowMenu THEN ViewerPrivate.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 ~ ImagerBackdoor.MakeStipple[001010B]; DrawMenu: PUBLIC PROC [menu: Menu, context: Imager.Context, x, y: INTEGER, whatChanged: REF ANY _ NIL] = { quitEarly: BOOL _ FALSE; ey: INTEGER _ y; menu.x _ x; menu.y _ y; Imager.SetFont[context, menuFont]; FOR row: NAT IN [0..menu.linesUsed) DO ey _ ey-ViewerSpecs.menuHeight; Imager.SetXYI[context, x+ViewerPrivate.menuHLeading, ey+baselineOffset]; 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[context, Imager.white]; Imager.MaskRectangleI[context, ex-margin, ey, e.width+margin*2, fontHeight]; quitEarly _ TRUE; } ELSE { Imager.SetXRelI[context, e.width+ViewerPrivate.menuHSpace]; LOOP }; }; IF e.greyed THEN { Imager.SetColor[context, myGrey]; Imager.MaskRectangleI[context, ex-margin, ey, e.width+margin*2, fontHeight]; }; Imager.SetColor[context, Imager.black]; Imager.ShowRope[context, e.name]; IF e.guarded AND e.guardState#armed THEN { Imager.MaskRectangleI[context, ex-1, ey+baselineOffset+3, e.width+2, 1]; }; IF quitEarly THEN RETURN; Imager.SetXRelI[context, ViewerPrivate.menuHSpace]; ENDLOOP; 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 { menuHeight: INTEGER ~ ViewerSpecs.menuHeight; x: INTEGER ~ menu.inverted.xPos-margin; w: INTEGER ~ menu.inverted.width+(2*margin); y: INTEGER ~ menu.y-menu.lineInverted*menuHeight; ViewerPrivate.InvertForMenus[parent, x, y, w, -menuHeight]; }; }; ComputeFontInfo: PROC = { extents: ImagerFont.Extents ~ ImagerFont.FontBoundingBox[menuFont]; fontHeight _ Real.RoundC[extents.descent+extents.ascent]; baselineOffset _ Real.RoundC[extents.descent]; }; ComputeFontInfo[]; END. ΒMenusImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Doug Wyatt, May 24, 1985 9:49:00 am PDT Russ Atkinson (RRA) June 11, 1985 4:15:15 pm PDT assert: state=arming Κ– "Mesa" style˜codešœ™Kšœ Οmœ1™Kšœžœ ˜Kšœžœžœ˜&Kšœžœ˜ Kšœžœ"˜.Kšœžœ ˜Kšœ žœ˜KšœžœI˜\Kšœ žœ˜Kšœžœ˜$—K˜š Πbl œžœžœžœžœ˜;Kšžœ“˜šKšžœ˜Kšžœ˜Kšœžœžœ˜—K˜Kšœžœ˜$K˜K˜+Kšœ žœ˜Kšœžœ˜Kšœžœ˜K˜šΟn œžœžœžœ˜FKšžœžœ˜)Kšœ˜K˜—š œžœžœ žœ˜9K˜ š žœ žœ žœžœž˜5K˜K˜Kšžœ˜—Kšœ˜K˜—š œžœžœ˜.K˜'K˜Kšœžœ˜šžœ!žœžœž˜2Kšžœžœžœžœ˜OKšžœžœ˜7Kšœžœ˜Kšœžœžœ žœ˜BKšžœ˜—Kšœ˜K˜—š  œžœžœ žœžœžœžœžœžœžœžœžœ žœžœžœ˜½šœžœ˜K˜ K˜ K˜K˜K˜ K˜Kšœ žœ žœ žœ˜/K˜)K˜—šœ˜K˜——š  œžœžœžœ˜NKšžœžœ˜&Kšœ˜K˜—š  œžœžœžœ˜;Kšœ˜Kšœžœžœ žœ˜4Kšœ˜K˜—š  œžœžœžœžœžœ žœžœ˜^Kšœ˜Kšœ˜Kšœ˜K˜—š œžœžœžœžœžœ žœžœ˜_Kšœ˜Kšœ˜Kšœ˜K˜—š   œžœžœžœžœ˜YKš žœžœžœžœžœ˜š žœ žœ žœžœž˜6šžœ&žœžœž˜7Kšžœžœžœ˜0Kšžœ˜—Kšžœ˜—Kšœ˜K˜—š œžœžœ7˜SK˜K˜K˜(šžœ.žœžœž˜?Kšœ9˜9Kšžœ˜—Kšœ˜K˜—š œžœžœ7˜SKšœ žœ˜šžœž˜KšžœD˜Hš žœžœ)žœžœž˜Dšžœžœ˜Kšœ5˜5Kšœ˜—Kšžœ˜——Kšœ˜K˜—Kšœžœžœžœ˜%K˜š œžœžœ9žœ˜^K˜ Kšœžœ˜(Kšœžœžœ˜š žœžœ žœžœž˜,šžœžœ˜ šžœ ž˜Kšžœ#˜'šžœ˜K˜K˜Kšœ˜——Kšžœ˜Kšœ˜—š žœžœ&žœžœž˜Ašžœžœ˜šžœ ž˜Kšžœ˜šžœ˜K˜K˜Kšœ˜——Kšœžœ˜ Kšžœ˜Kšœ˜—Kšžœ˜—Kšžœžœžœ˜Kšžœžœžœ˜)Kšžœ˜—Kšžœžœžœ1˜Jšžœ&žœžœž˜7K˜ Kšœ'˜'Kšžœ˜—Kšœ˜K˜—š œžœžœ%˜EKšœ˜Kšœ˜K˜—š œžœžœžœ˜HKšžœ˜Kšœ˜K˜—š œžœžœ7˜KKšœ/žœ˜KKšœ˜K˜—š œžœžœžœ˜TKšžœ˜Kšœ˜K˜—š œžœžœX˜mKšœžœ˜*šžœ žœ˜Kšœžœžœ˜-K˜?šžœžœž˜šœ žœ˜Kšžœžœžœ˜4Kšœžœ˜K˜—šœ˜Kšžœžœžœ˜4K˜K˜K˜K˜—Kšžœ˜—K˜—Kšœ˜K˜—š  œžœžœzžœžœ˜ŸKšœžœ˜*šžœ žœ˜Kšœžœžœ˜-K˜žœžœ˜SKšœžœ˜K˜ K˜Kšœ˜K˜—š  œžœ3˜Cšžœ˜ Kšžœ'žœ˜1Kšžœ+žœ ˜;—Kšœ˜K˜—š œžœžœžœžœ žœžœžœžœžœ2žœžœ˜“šžœžœž˜Kšœžœžœ0žœ˜CKšœžœD˜LKšœ žœžœ˜2Kšžœžœ‘˜,—Kšœ˜K˜—š   œžœžœ3žœžœ˜Yš žœžœžœžœžœ˜(Kšžœžœ˜(Kšœžœ˜Kšœ˜—Kšœ˜K˜—Kšœ žœžœžœ‘,˜LK˜Kšœ;˜;K˜š œžœžœ-žœžœžœžœ˜kKšœ žœžœ˜Kšœžœ˜K˜ K˜ K˜"šžœžœžœž˜&Kšœ˜KšœH˜Hšžœ(žœžœž˜9Kšœžœ ˜šžœ žœžœ˜šžœžœ˜K˜'KšœL˜LKšœ žœ˜Kšœ˜—Kšžœ?žœ˜JKšœ˜—šžœ žœ˜K˜!KšœL˜LKšœ˜—K˜'K˜!šžœ žœžœ˜*KšœH˜HKšœ˜—Kšžœ žœžœ˜Kšœ3˜3Kšžœ˜—Kšžœ˜—Kšœžœ˜Kšœ˜K˜—š  œžœžœžœ˜PKš žœžœžœžœžœžœ˜4š žœ)žœžœžœ ž˜FKšžœžœžœžœ˜0Kšžœ˜—Kšžœžœ˜ Kšœ˜K˜—š  œžœ!˜2šžœžœžœ˜.Kšœ žœ˜-Kšœžœ˜'Kšœžœ"˜,Kšœžœ'˜1Kšœ;˜;K˜—Kšœ˜K˜—š œžœ˜KšœC˜CKšœ9˜9Kšœ.˜.Kšœ˜K˜—K˜K˜Kšžœ˜—…—+~:Z