DIRECTORY Imager USING [black, Color, Context, DoSave, IntegerClipRectangle, IntegerMaskRectangle, IntegerSetXY, MakeStipple, SetColor, ShowCharacters, white], Menus, MenusPrivate, MessageWindow USING [Append], Process USING [Detach, Milliseconds, MsecToTicks, priorityNormal, SetPriority, SetTimeout], Real USING [RoundC], Rope USING [Equal, FromRefText, ROPE], TIPUser USING [TIPScreenCoords], UserProfile USING [Boolean, CallWhenProfileChanges, Number, ProfileChangedProc], VFonts USING [defaultFont, FONT, FontAscent, FontHeight, RopeWidth], ViewerBLT USING [ChangeMenuHeight], ViewerClasses, ViewerOps USING [EnumProc, EnumerateViewers, InvertForMenus, PaintViewer], ViewerSpecs USING [captionHeight, guardOffset, guardHeight, menuHeight, windowBorderSize], WindowManager USING [RestoreCursor], WindowManagerPrivate USING [closePos, DrawCaptionMenu, growPos, windowMenus]; MenusImpl: CEDAR MONITOR LOCKS entry USING entry: EntryState IMPORTS Imager, MessageWindow, Process, Real, Rope, UserProfile, VFonts, ViewerBLT, ViewerOps, WindowManager, WindowManagerPrivate EXPORTS Menus, MenusPrivate SHARES ViewerOps = BEGIN OPEN Menus, MenusPrivate; ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; menuFont: VFonts.FONT _ VFonts.defaultFont; fontHeight: INTEGER; baselineOffset: INTEGER; registeredMenus: LIST OF Menu _ NIL; widthFudge: INTEGER = 3; targetNotFound: PUBLIC SIGNAL = CODE; MenuNameEqual: PROC[a, b: ATOM] RETURNS[BOOL] = INLINE { RETURN[a=b] }; MarkMenu: PUBLIC PROC [menus: ViewerMenus, parent: Viewer, mousePos: TIPUser.TIPScreenCoords] = { entry: EntryState _ ResolveEntry[menus, mousePos]; IF menus.inverted # entry THEN { IF menus.inverted # NIL THEN InvertEntry[menus,parent]; menus.inverted _ entry; IF menus.inverted # NIL THEN InvertEntry[menus,parent]; }; }; HitMenu: PUBLIC PROC[menus: ViewerMenus, parent: Viewer, mousePos: TIPUser.TIPScreenCoords, trigger: Trigger] = { entry: EntryState _ ResolveEntry[menus, mousePos]; hit: BOOL _ menus.inverted#NIL AND entry=menus.inverted; IF menus.inverted#NIL THEN InvertEntry[menus, parent]; -- take down menus.inverted _ NIL; IF hit THEN ProcessMenuHit[entry, menus, parent, trigger]; }; ProcessMenuHit: ENTRY PROC [entry: EntryState, menus: ViewerMenus, parent: Viewer, trigger: Trigger] = { SELECT entry.guardState FROM guarded => { response: REF ANY _ NIL; entry.guardState _ arming; TRUSTED {Process.Detach[FORK ArmMenuProc[entry, menus, parent]]}; response _ FindAction[entry, trigger].guardResponse; IF response#NIL THEN TRUSTED {Process.Detach[FORK GuardResponse[response]]}; }; arming=> NULL; -- no action armed => { IF entry.commonData.guarded THEN entry.guardState _ guarded; MenuPusher[entry, menus, parent, trigger, FALSE]; }; ENDCASE; }; ArmMenuProc: ENTRY PROC [entry: EntryState, menus: ViewerMenus, parent: Viewer] = BEGIN menuWaitCondition: CONDITION; TRUSTED {Process.SetTimeout[@menuWaitCondition, Process.MsecToTicks[armingTime]]}; WAIT menuWaitCondition; IF entry.guardState = arming THEN BEGIN entry.guardState _ armed; RedrawMenu[parent, menus, entry]; TRUSTED {Process.SetTimeout[@menuWaitCondition, Process.MsecToTicks[armedTime]]}; WAIT menuWaitCondition; END; IF entry.guardState#guarded THEN BEGIN entry.guardState _ guarded; RedrawMenu[parent, menus, entry]; END; END; MenuPusher: PROC[entry: EntryState, menus: ViewerMenus, parent: Viewer, trigger: Trigger, normalPriority: BOOL _ TRUE] = BEGIN changeOccurred: BOOLEAN _ FALSE; action: Action = FindAction[entry, trigger]; notifyProc: ViewerClasses.NotifyProc _ FindViewerMenu[menus,entry].commonData.notify; IF notifyProc = NIL THEN { notifyProc _ parent.class.notify; } ELSE { }; entry.greyed _ TRUE; RedrawMenu[parent, menus, entry]; IF normalPriority THEN TRUSTED {Process.SetPriority[Process.priorityNormal]}; IF notifyProc # NIL THEN notifyProc[parent, action.input]; entry.greyed _ FALSE; RedrawMenu[parent, menus, entry]; changeOccurred _ FALSE; FOR m: LIST OF MenuName _ action.makeActive, m.rest UNTIL m = NIL DO MakeActive[viewer: parent, menu: m.first, paint: FALSE]; changeOccurred _ TRUE; ENDLOOP; FOR m: LIST OF MenuName _ action.makeInactive, m.rest UNTIL m = NIL DO MakeInactive[viewer: parent, menu: m.first, paint: FALSE]; changeOccurred _ TRUE; ENDLOOP; FOR m: LIST OF MenuName _ action.toggle, m.rest UNTIL m = NIL DO Toggle[viewer: parent, menu: m.first, paint: FALSE]; changeOccurred _ TRUE; ENDLOOP; IF changeOccurred THEN RePaintBecauseOfMenuChange[parent]; WindowManager.RestoreCursor[]; END; RedrawMenu: PROC [viewer: Viewer, menus: ViewerMenus, entry: EntryState] = BEGIN IF menus = WindowManagerPrivate.windowMenus THEN WindowManagerPrivate.DrawCaptionMenu[viewer, FALSE] ELSE ViewerOps.PaintViewer[viewer: viewer, hint: menu, whatChanged: entry]; END; GuardResponse: PROC [response: REF ANY] = BEGIN WITH response SELECT FROM response: ROPE => MessageWindow.Append[response, TRUE]; response: REF UnGuardRec => response.proc[response.data]; ENDCASE => ERROR; -- not valid response END; ClearMenu: PUBLIC PROC [menus: ViewerMenus, parent: Viewer, paint: BOOL _ TRUE] = BEGIN IF menus.inverted#NIL THEN BEGIN IF paint THEN InvertEntry[menus, parent]; menus.inverted _ NIL; END; END; DrawSingleEntry: PROC [viewer: Viewer, context: Imager.Context, entry: EntryState, clearFirst: BOOLEAN _ TRUE] = { myGrey: Imager.Color = Imager.MakeStipple[001010B]; lowerLeftX: INTEGER _ viewer.wx + entry.xPos; lowerLeftY: INTEGER _ viewer.wy + entry.yPos; IF clearFirst THEN { Imager.SetColor[context, Imager.white]; Imager.IntegerMaskRectangle[context, lowerLeftX - widthFudge, lowerLeftY, entry.width + (2*widthFudge), fontHeight-1]; Imager.SetColor[context, Imager.black]; }; IF entry.greyed THEN { Imager.SetColor[context, myGrey]; Imager.IntegerMaskRectangle[context, lowerLeftX - widthFudge, lowerLeftY, entry.width + (2*widthFudge), fontHeight-1]; Imager.SetColor[context, Imager.black]; }; WITH entry.commonData.displayData SELECT FROM r: ROPE => { Imager.IntegerSetXY[context, lowerLeftX, lowerLeftY + baselineOffset]; Imager.ShowCharacters[context, r, VFonts.defaultFont]; }; user: REF DrawingRec => { CallUserDrawProc: PROC = { Imager.IntegerClipRectangle[context, lowerLeftX-widthFudge, lowerLeftY, entry.width+(2*widthFudge), fontHeight-1]; [] _ user.proc[op: draw, context: context, clientData: user.data]; }; Imager.IntegerSetXY[context, lowerLeftX, lowerLeftY]; Imager.DoSave[context, CallUserDrawProc]; }; ENDCASE => ERROR; IF entry.commonData.guarded AND entry.guardState # armed THEN { OPEN ViewerSpecs; Imager.IntegerMaskRectangle[context, lowerLeftX - 1, lowerLeftY + baselineOffset+guardOffset, entry.width + 2, guardHeight]; }; }; DrawMenu: PUBLIC PROC [v: Viewer, menus: ViewerMenus, context: Imager.Context, whatChanged: REF ANY _ NIL] = { WITH whatChanged SELECT FROM entry: EntryState => DrawSingleEntry[v, context, entry]; ENDCASE => { FOR m: LIST OF ViewerMenu _ menus.list, m.rest UNTIL m = NIL DO IF m.first.active THEN FOR e: LIST OF EntryState _ m.first.entries, e.rest UNTIL e = NIL DO DrawSingleEntry[v, context, e.first, FALSE]; ENDLOOP; ENDLOOP; menus.inverted _ NIL; }; }; resolveCacheMenus: ViewerMenus _ NIL; resolveCacheMenu: ViewerMenu _ NIL; rce: EntryState _ NIL; -- stands for ResolveCacheEntry ResolveEntry: PROC [menus: ViewerMenus, mousePos: TIPUser.TIPScreenCoords] RETURNS [entry: EntryState] = { IF resolveCacheMenus = menus THEN IF resolveCacheMenu.active THEN { IF mousePos.mouseX IN [rce.xPos..(rce.xPos + rce.width)] AND mousePos.mouseY IN [rce.yPos..(rce.yPos+(fontHeight-1))] THEN RETURN[rce]; }; FOR m: LIST OF ViewerMenu _ menus.list, m.rest UNTIL m = NIL DO IF NOT m.first.active THEN LOOP; FOR e: LIST OF EntryState _ m.first.entries, e.rest UNTIL e = NIL DO IF mousePos.mouseX IN [e.first.xPos..(e.first.xPos + e.first.width)] AND mousePos.mouseY IN [e.first.yPos..(e.first.yPos+(fontHeight-1))] THEN { resolveCacheMenus _ menus; resolveCacheMenu _ m.first; rce _ e.first; RETURN[e.first]; }; ENDLOOP; ENDLOOP; RETURN[NIL]; }; InvertEntry: PROC [menus: ViewerMenus, parent: Viewer] = { IF ~parent.destroyed AND ~parent.iconic THEN { entry: EntryState _ menus.inverted; ViewerOps.InvertForMenus[parent, (parent.wx+entry.xPos)-widthFudge, parent.wy+entry.yPos, entry.width+(2*widthFudge), fontHeight-1]; }; }; ComputeFontInfo: PROC = BEGIN ascent: INTEGER = VFonts.FontAscent[menuFont]; fontHeight _ VFonts.FontHeight[menuFont]; baselineOffset _ Real.RoundC[fontHeight-ascent]; END; FindAction: PROC[entry: EntryState, trigger: Trigger] RETURNS [Action] = { FOR list: LIST OF Action _ entry.commonData.actions, list.rest UNTIL list=NIL DO action: Action = list.first; IF action.triggers[trigger] THEN RETURN[action]; ENDLOOP; ERROR; }; FindViewerMenu: PROC [menus: ViewerMenus, entry: EntryState] RETURNS [menu: ViewerMenu] = { FOR l: LIST OF ViewerMenu _ menus.list, l.rest UNTIL l = NIL DO FOR e: LIST OF EntryState _ l.first.entries, e.rest UNTIL e = NIL DO IF e.first = entry THEN RETURN[l.first]; ENDLOOP; ENDLOOP; RETURN[NIL]; }; ValidateMenu: PROC[menu: Menu] = { FOR entries: LIST OF Entry _ menu.entries, entries.rest UNTIL entries=NIL DO entry: Entry = entries.first; IF entry.displayData=NIL THEN entry.displayData _ entry.name ELSE { WITH entry.displayData SELECT FROM dispData: REF TEXT => entry.displayData _ Rope.FromRefText[dispData]; okRope: ROPE => NULL; okProc: REF DrawingRec => NULL; ENDCASE => ERROR; -- invalid data type for DisplayData field. }; FOR actions: LIST OF Action _ entry.actions, actions.rest UNTIL actions=NIL DO action: Action = actions.first; IF action.guardResponse=NIL THEN NULL ELSE { WITH action.guardResponse SELECT FROM textRef: REF TEXT => action.guardResponse _ Rope.FromRefText[textRef]; okRope: ROPE => NULL; okRec: REF UnGuardRec => NULL; ENDCASE => ERROR; -- invalid data type for guardResponse field. }; ENDLOOP; ENDLOOP; }; RegisterMenu: PUBLIC PROC [menu: Menu] = { ValidateMenu[menu]; registeredMenus _ CONS[menu, registeredMenus]; }; AlreadyRegistered: PUBLIC PROC [name: MenuName] RETURNS [registered: BOOLEAN] = { FOR m: LIST OF Menu _ registeredMenus, m.rest UNTIL m = NIL DO IF MenuNameEqual[m.first.name, name] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; ReRegisterMenu: PUBLIC PROC [menu: Menu, paint: BOOL _ TRUE] = { newMenu: ViewerMenuRec; MenuDefinitonChanged: ViewerOps.EnumProc = { menus: ViewerMenus = NARROW[v.menus]; FOR m: LIST OF ViewerMenu _ menus.list, m.rest UNTIL m = NIL DO IF MenuNameEqual[menu.name, m.first.commonData.name] THEN m.first _ NEW[ViewerMenuRec _ newMenu]; ENDLOOP; IF paint THEN RePaintBecauseOfMenuChange[v]; }; ValidateMenu[menu]; FOR m: LIST OF Menu _ registeredMenus, m.rest UNTIL m = NIL DO IF MenuNameEqual[m.first.name, menu.name] THEN { m.first _ menu; newMenu _ MakeNewViewerMenuRec[m.first]; ViewerOps.EnumerateViewers[MenuDefinitonChanged]; RETURN; }; ENDLOOP; ERROR targetNotFound; }; GetRegisteredMenu: PUBLIC PROC [name: MenuName] RETURNS [Menu] = { FOR m: LIST OF Menu _ registeredMenus, m.rest UNTIL m = NIL DO menu: Menu = m.first; IF MenuNameEqual[menu.name, name] THEN RETURN[menu]; ENDLOOP; ERROR targetNotFound; }; ClearMenus: PUBLIC PROC [viewer: Viewer, paint: BOOL _ TRUE] = { viewer.menus _ NIL; IF paint THEN RePaintBecauseOfMenuChange[viewer]; }; AddMenu: PUBLIC PROC [viewer: Viewer, name: MenuName, paint: BOOL _ TRUE, addBefore: MenuName _ NIL] = { menus: ViewerMenus = NARROW[viewer.menus]; IF menus = NIL THEN { IF addBefore # NIL THEN SIGNAL targetNotFound; viewer.menus _ NEW[ViewerMenusRec _ [list: LIST[MakeNewViewerMenu[name]]]]; } ELSE IF addBefore # NIL THEN { prev: LIST OF ViewerMenu _ NIL; foundIt: BOOLEAN _ FALSE; FOR m: LIST OF ViewerMenu _ menus.list, m.rest UNTIL m = NIL DO IF MenuNameEqual[m.first.commonData.name, name] THEN { prev.rest _ CONS[MakeNewViewerMenu[name], m]; foundIt _ TRUE; }; prev _ m; ENDLOOP; IF NOT foundIt THEN SIGNAL targetNotFound; } ELSE IF GetRegisteredMenu[name].breakBefore THEN { prev: LIST OF ViewerMenu _ NIL; FOR m: LIST OF ViewerMenu _ menus.list, m.rest UNTIL m = NIL DO prev _ m; ENDLOOP; prev.rest _ LIST[MakeNewViewerMenu[name]]; } ELSE { prev: LIST OF ViewerMenu _ NIL; FOR m: LIST OF ViewerMenu _ menus.list, m.rest UNTIL m = NIL OR ((prev # NIL AND prev.first.commonData.breakAfter = FALSE) AND m.first.commonData.breakBefore) DO prev _ m; ENDLOOP; prev.rest _ CONS[MakeNewViewerMenu[name],prev.rest]; }; IF paint THEN RePaintBecauseOfMenuChange[viewer]; }; ViewerlessAddMenu: PUBLIC PROC [name: MenuName, addBefore: MenuName _ NIL, paint: BOOL _ TRUE] = { IF WindowManagerPrivate.windowMenus = NIL THEN { IF addBefore # NIL THEN SIGNAL targetNotFound; WindowManagerPrivate.windowMenus _ NEW[ViewerMenusRec]; WindowManagerPrivate.windowMenus.list _ LIST[MakeNewViewerMenu[name]]; } ELSE IF addBefore # NIL THEN { prev: LIST OF ViewerMenu _ NIL; foundIt: BOOLEAN _ FALSE; FOR m: LIST OF ViewerMenu _ WindowManagerPrivate.windowMenus.list, m.rest UNTIL m = NIL DO IF MenuNameEqual[m.first.commonData.name, name] THEN { prev.rest _ CONS[MakeNewViewerMenu[name], m]; foundIt _ TRUE; }; prev _ m; ENDLOOP; IF NOT foundIt THEN SIGNAL targetNotFound; } ELSE IF GetRegisteredMenu[name].breakBefore THEN { prev: LIST OF ViewerMenu _ NIL; FOR m: LIST OF ViewerMenu _ WindowManagerPrivate.windowMenus.list, m.rest UNTIL m = NIL DO prev _ m; ENDLOOP; prev.rest _ LIST[MakeNewViewerMenu[name]]; } ELSE { prev: LIST OF ViewerMenu _ NIL; FOR m: LIST OF ViewerMenu _ WindowManagerPrivate.windowMenus.list, m.rest UNTIL m = NIL OR ((prev # NIL AND prev.first.commonData.breakAfter = FALSE) AND m.first.commonData.breakBefore) DO prev _ m; ENDLOOP; prev.rest _ CONS[MakeNewViewerMenu[name],prev.rest]; }; }; MakeNewViewerMenu: PROC[name: MenuName] RETURNS[ViewerMenu] = { FOR m: LIST OF Menu _ registeredMenus, m.rest UNTIL m = NIL DO IF MenuNameEqual[m.first.name, name] THEN RETURN[NEW[ViewerMenuRec _ MakeNewViewerMenuRec[m.first]]]; ENDLOOP; ERROR targetNotFound; }; MakeNewViewerMenuRec: PROC[menu: Menu] RETURNS[ViewerMenuRec] = { RETURN[[commonData: menu, active: menu.beginsActive, entries: MakeNewViewerMenuEntries[menu]]]; }; MakeNewViewerMenuEntries: PROC[menu: Menu] RETURNS[LIST OF EntryState] = { reversedList: LIST OF EntryState _ NIL; answer: LIST OF EntryState _ NIL; FOR e: LIST OF Entry _ menu.entries, e.rest UNTIL e = NIL DO reversedList _ CONS[NEW[EntryStateRec _ [commonData: e.first]], reversedList]; ENDLOOP; FOR e: LIST OF EntryState _ reversedList, e.rest UNTIL e = NIL DO IF e.first.commonData.guarded THEN e.first.guardState _ guarded; answer _ CONS[e.first, answer]; ENDLOOP; RETURN[answer]; }; MakeActive: PUBLIC PROC [viewer: Viewer, menu: MenuName, paint: BOOL _ TRUE] = { menus: ViewerMenus = NARROW[viewer.menus]; FOR m: LIST OF ViewerMenu _ menus.list, m.rest UNTIL m = NIL DO IF MenuNameEqual[m.first.commonData.name, menu] THEN { IF NOT m.first.active THEN { m.first.active _ TRUE; IF paint THEN RePaintBecauseOfMenuChange[viewer]; }; RETURN; }; ENDLOOP; SIGNAL targetNotFound; }; MakeInactive: PUBLIC PROC [viewer: Viewer, menu: MenuName, paint: BOOL _ TRUE] = { menus: ViewerMenus = NARROW[viewer.menus]; FOR m: LIST OF ViewerMenu _ menus.list, m.rest UNTIL m = NIL DO IF MenuNameEqual[m.first.commonData.name, menu] THEN { IF m.first.active THEN { m.first.active _ FALSE; IF paint THEN RePaintBecauseOfMenuChange[viewer]; }; RETURN; }; ENDLOOP; SIGNAL targetNotFound; }; Toggle: PUBLIC PROC [viewer: Viewer, menu: MenuName, paint: BOOL _ TRUE] = { menus: ViewerMenus = NARROW[viewer.menus]; FOR m: LIST OF ViewerMenu _ menus.list, m.rest UNTIL m = NIL DO IF MenuNameEqual[m.first.commonData.name, menu] THEN { m.first.active _ NOT m.first.active; IF paint THEN RePaintBecauseOfMenuChange[viewer]; RETURN; }; ENDLOOP; SIGNAL targetNotFound; }; MenuInViewer: PUBLIC PROC[viewer: Viewer, menu: MenuName] RETURNS[exists: BOOLEAN] = { menus: ViewerMenus = NARROW[viewer.menus]; FOR m: LIST OF ViewerMenu _ menus.list, m.rest UNTIL m = NIL DO IF MenuNameEqual[m.first.commonData.name, menu] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; wrap: BOOLEAN _ TRUE; -- TRUE iff user wants menu entries ALWAYS to be displayed wrapIndent: INTEGER _ (menuHLeading*4); -- indention on non-first lines of wrapped menu tooNarrowToWrapMenus: INTEGER _ 150; -- don't wrap if the window is ridiculously narrow ReEstablishUserProfileParameters: PUBLIC UserProfile.ProfileChangedProc = { wrap _ UserProfile.Boolean[key: "ViewerMenusWrap", default: TRUE]; wrapIndent _ UserProfile.Number[key: "ViewerMenusWrapIndent", default: (menuHLeading*4)]; tooNarrowToWrapMenus _ UserProfile.Number["ViewerMenusTooNarrowToWrap", 150]; }; RecomputeMenus: PUBLIC PROC [v: Viewer] RETURNS[heightDelta: INTEGER] = { OPEN ViewerSpecs; menus: ViewerMenus = NARROW[v.menus]; originalHeight: INTEGER; prevLineHadBreakafter: BOOLEAN _ TRUE; -- starting true forces first line to move down wbs: INTEGER _ IF v.border THEN windowBorderSize ELSE 0; TopLeftCornerX: INTEGER _ wbs; TopLeftCornerY: INTEGER _ v.wh-captionHeight; CurrentX: INTEGER _ TopLeftCornerX + menuHLeading; CurrentY: INTEGER _ TopLeftCornerY; IF menus=NIL OR v.iconic THEN RETURN[0]; originalHeight _ menus.h; FOR m: LIST OF ViewerMenu _ menus.list, m.rest UNTIL m = NIL DO IF NOT m.first.active THEN LOOP; IF prevLineHadBreakafter OR m.first.commonData.breakBefore THEN { CurrentX _ TopLeftCornerX + menuHLeading; CurrentY _ CurrentY - menuHeight; }; prevLineHadBreakafter _ m.first.commonData.breakAfter; FOR e: LIST OF EntryState _ m.first.entries, e.rest UNTIL e = NIL DO e.first.width _ WITH e.first.commonData.displayData SELECT FROM r: ROPE => VFonts.RopeWidth[r], user: REF DrawingRec => user.proc[op: query, clientData: user.data] ENDCASE => ERROR; IF wrap AND (v.ww > tooNarrowToWrapMenus) THEN IF (CurrentX + e.first.width) > (v.ww - wbs) THEN { CurrentX _ TopLeftCornerX + wrapIndent; CurrentY _ CurrentY - menuHeight; }; e.first.xPos _ CurrentX; e.first.yPos _ CurrentY; CurrentX _ CurrentX + e.first.width + menuHSpace; ENDLOOP; ENDLOOP; menus.x _ TopLeftCornerX; menus.y _ CurrentY; menus.w _ v.ww - (2 * wbs); menus.h _ TopLeftCornerY - CurrentY; RETURN[menus.h - originalHeight]; }; ReComputeWindowMenus: PUBLIC PROC [v: Viewer, guard: BOOL, color: BOOL] = { OPEN ViewerSpecs; CurrentX: INTEGER _ menuHLeading + windowBorderSize; CurrentY: INTEGER ~ v.wh - captionHeight; -- all have same Y coordinate WindowManagerPrivate.windowMenus.x _ windowBorderSize; WindowManagerPrivate.windowMenus.y _ v.wh - captionHeight; WindowManagerPrivate.windowMenus.w _ v.ww - (2 * windowBorderSize); WindowManagerPrivate.windowMenus.h _ captionHeight; IF guard THEN { SetDisplay[$windowDestroyMenu, FALSE]; SetDisplay[$windowGuardedDestroyMenu, TRUE]; } ELSE { SetDisplay[$windowDestroyMenu, TRUE]; SetDisplay[$windowGuardedDestroyMenu, FALSE]; }; IF color THEN SetDisplay[$windowColorMenu, TRUE] ELSE SetDisplay[$windowColorMenu, FALSE]; FOR m: LIST OF ViewerMenu _ WindowManagerPrivate.windowMenus.list, m.rest UNTIL m = NIL DO IF NOT m.first.active THEN LOOP; FOR e: LIST OF EntryState _ m.first.entries, e.rest UNTIL e = NIL DO e.first.width _ WITH e.first.commonData.displayData SELECT FROM r: ROPE => VFonts.RopeWidth[r], user: REF DrawingRec => user.proc[op: query, clientData: user.data] ENDCASE => ERROR; e.first.xPos _ CurrentX; e.first.yPos _ CurrentY; IF Rope.Equal[e.first.commonData.name, "Grow"] THEN { WindowManagerPrivate.growPos.mouseX _ e.first.xPos+1; WindowManagerPrivate.growPos.color _ FALSE; WindowManagerPrivate.growPos.mouseY _ e.first.yPos+1; }; IF Rope.Equal[e.first.commonData.name, "Close"] THEN { WindowManagerPrivate.closePos.mouseX _ e.first.xPos+1; WindowManagerPrivate.closePos.color _ FALSE; WindowManagerPrivate.closePos.mouseY _ e.first.yPos+1; }; CurrentX _ CurrentX + e.first.width + menuHSpace; ENDLOOP; ENDLOOP; }; SetDisplay: PROC [name: MenuName, setting: BOOL] = { FOR l: LIST OF ViewerMenu _ WindowManagerPrivate.windowMenus.list, l.rest UNTIL l = NIL DO IF MenuNameEqual[l.first.commonData.name, name] THEN { l.first.active _ setting; RETURN; }; ENDLOOP; ERROR; }; RePaintBecauseOfMenuChange: PROC[v: Viewer] = { ViewerBLT.ChangeMenuHeight[v, RecomputeMenus[v]]; }; ComputeFontInfo[]; UserProfile.CallWhenProfileChanges[ReEstablishUserProfileParameters]; 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 Last Edited by: Pausch, August 25, 1983 10:29 am Last Edited by: Wyatt, October 21, 1983 12:07 pm assert: state=arming IF notifyProc = NIL THEN Debug[msg: "using NIL class notifyproc", waitForLeftShift: TRUE, level: 2] ELSE Debug[msg: "using NON-NIL class notifyproc", waitForLeftShift: TRUE, level: 2] Debug[msg: "using specially specified notifyproc", waitForLeftShift: TRUE, level: 2]; this is where we actually tell the client about the menu-invoked function: separate procedure because it sets a clipping rectangle NB: the menus parm need not be the menus for the viewer; it could also be the windowManger caption menu Debug[msg: IO.PutFR["drawing menus of height: %d", IO.int[menus.h]], level: 2]; this has to go FAST, so we cache the last entry. In order for this cache to be legit, we have to save a pointer to the entry, which is the cached entry, the menu (TYPE ViewerMenu) that contains it, and the ViewerMenus that contained the menu. If we just tested against the entry, we could get fooled by an entry in a menu that had been made inactive. Cache test: if cache fails, we test everybody. now we must reset the cache: This is here so that user-created entries that have displayData fields of type REF TEXT can be converted to ROPE. If the compiler ever defaults quoted text strings, this can be safely removed. This is here so that user-created entries that have displayData fields of type REF TEXT can be converted to ROPE. If the compiler ever defaults quoted text strings, this can be safely removed. [v: Viewer] RETURNS [BOOL _ TRUE] place in the slot before 'addBefore' place at the end of the list if it wants its own separate line. place it at the first place without a 'breakAfter' that has a 'breakBefore' following it provided for convenience of BuildWindowMenus in WindowManagerImpl place in the slot before 'addBefore' place at the end of the list if it wants its own separate line. place it at the first place without a 'breakAfter' that has a 'breakBefore' following it The user-profilible menu options: This routine is called whenever the user profile changes: NOTE: the returned value is the change in the menu's height as a result of recomputation. This is important so that the client space may be adjusted appropriately. NOTE: menus coordinates are computed and stored RELATIVE to the viewer IF wrap THEN Debug[msg: IO.PutFR["RecomputeMenus for %g ==> wrap is true", IO.rope[ menus.list.first.commonData.name]], level: 2] ELSE Debug[msg: IO.PutFR["RecomputeMenus for %g ==> wrap is false", IO.rope[menus.list.first.commonData.name]], level: 2]; wrap by just skipping down to the next line Debug[msg: "we just decided to wrap!", level: 2]; Debug[msg: IO.PutFR["menu height deterimined to be: %d, a difference of: %d",IO.int[TopLeftCornerY - CurrentY], IO.int[menus.h - originalHeight] ],level: 2]; menus coordinates are computed and stored RELATIVE to the viewer Debug["ReComputeWindowMenus entered", TRUE, 3]; Debug["ReComputeWindowMenus: guard is true", TRUE, 4]; Debug["ReComputeWindowMenus: guard is false", TRUE, 4]; hack for special clicking GROW and CLOSE ViewerOps.PaintViewer[viewer: v, hint: menu]; currentDebugLevel: INTEGER _ 3; AlterDebuggingLevel: PUBLIC PROC [amount: INTEGER, relative: BOOL _ TRUE] = { string: ROPE; IF relative THEN currentDebugLevel _ currentDebugLevel + amount ELSE currentDebugLevel _ amount; string _ IO.PutFR["Debugging Level Set to %d", IO.int[currentDebugLevel]]; Debug[string, TRUE, currentDebugLevel]; }; Debug: PUBLIC PROC [msg: ROPE, waitForLeftShift: BOOL _ TRUE, level: INTEGER _ 1] = TRUSTED { keys: LONG POINTER TO Interminal.KeyState = LOOPHOLE[UserTerminal.keyboard]; IF currentDebugLevel >= level THEN { MessageWindow.Append[msg, TRUE]; IF waitForLeftShift THEN { MessageWindow.Append[" ... HIT LEFT SHIFT TO CONTINUE", FALSE]; WHILE keys.bits[LeftShift]#down DO ENDLOOP; WHILE keys.bits[LeftShift]#up DO ENDLOOP; MessageWindow.Clear[]; }; }; }; -- Debug ΚŒ– "cedar" style˜JšΟc&™&Jš-™-J™-J™0J™0J˜šΟk ˜ Jšœžœ‰˜•J˜J˜ Jšœžœ ˜JšœžœN˜[Jšœžœ ˜Jšœžœžœ˜&Jšœžœ˜ Jšœ žœ?˜PJšœžœžœ%˜DJšœ žœ˜#J˜Jšœ žœ;˜JJšœ žœI˜ZJšœžœ˜$Jšœžœ3˜MJ˜—Jš œ žœžœžœžœ˜Jšžœ#žœžœžœ˜7Jšžœ˜—Jšžœžœ˜J˜J˜—š Ÿœžœžœžœžœ˜@Jšœ˜šΟbœ˜,Jšœžœžœ™"Jšœžœ ˜%š žœžœžœ!žœžœž˜?Jšžœ3žœ žœ˜aJšžœ˜—Jšžœžœ˜,J˜—J˜š žœžœžœ žœžœž˜>šžœ(žœ˜0Jšœ˜Jšœ(˜(Jšœ1˜1Jšžœ˜J˜—Jšžœ˜—Jšžœ˜J˜J˜—šŸœžœžœžœ ˜Bš žœžœžœ žœžœž˜>J˜Jšžœ žœžœ˜4Jšžœ˜—Jšžœ˜J˜J˜—š Ÿ œžœžœžœžœ˜@Jšœžœ˜Jšžœžœ$˜1J˜J˜—š Ÿœžœžœ)žœžœžœ˜hJšœžœ˜*šžœ žœžœ˜Jšžœ žœžœžœ˜.Jšœžœžœ˜KJ˜—šžœžœ žœžœ˜Jšœ$™$Jšœžœžœžœ˜Jšœ žœžœ˜š žœžœžœ!žœžœž˜?šžœ.žœ˜6Jšœ žœ˜-Jšœ žœ˜J˜—J˜ Jšžœ˜—Jšžœžœ žœžœ˜*J˜—šžœžœ%žœ˜2Jšœ?™?Jšœžœžœžœ˜š žœžœžœ!žœžœž˜?J˜ Jšžœ˜—Jšœ žœ˜*J˜—šžœ˜JšœX™XJšœžœžœžœ˜šžœžœžœ!žœžœžœ žœžœ$žœžœ!ž˜‘J˜ Jšžœ˜—Jšœ žœ$˜4J˜—Jšžœžœ$˜1J˜J˜—š Ÿœžœžœ(žœ žœžœ˜bJšœA™Ašžœ$žœžœ˜0Jšžœ žœžœžœ˜.Jšœ$žœ˜8Jšœ(žœ˜FJ˜—šžœžœ žœžœ˜Jšœ$™$Jšœžœžœžœ˜Jšœ žœžœ˜š žœžœžœ<žœžœž˜Zšžœ.žœ˜6Jšœ žœ˜-Jšœ žœ˜J˜—J˜ Jšžœ˜—Jšžœžœ žœžœ˜*J˜—šžœžœ%žœ˜2Jšœ?™?Jšœžœžœžœ˜š žœžœžœ<žœžœž˜ZJ˜ Jšžœ˜—Jšœ žœ˜*J˜—šžœ˜JšœX™XJšœžœžœžœ˜šžœžœžœ<žœžœžœ žœžœ$žœžœ!ž˜ΌJ˜ Jšžœ˜—Jšœ žœ$˜4J˜—J˜J˜—šŸœžœžœ˜?š žœžœžœ žœžœž˜>šžœ#ž˜)Jšžœžœ1˜;—Jšžœ˜—Jšžœ˜J˜J˜—šŸœžœ žœ˜Ašžœ.˜4Jšœ*˜*—J˜J˜—š Ÿœžœ žœžœžœ˜JJšœžœžœžœ˜'Jšœžœžœžœ˜!š žœžœžœžœžœž˜