DIRECTORY Imager, Interminal, IO, Menus, MenusPrivate, MessageWindow USING [Append, Clear], Process USING [Detach, Milliseconds, MsecToTicks, priorityNormal, SetPriority, SetTimeout], Real USING [RoundC], Rope USING [Equal, FromRefText, ROPE], UserTerminal, 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, menuHeight, windowBorderSize], WindowManager USING [RestoreCursor], WindowManagerPrivate USING [closePos, DrawCaptionMenu, growPos, windowMenus]; MenusImpl: CEDAR MONITOR LOCKS entry USING entry: EntryState IMPORTS IO, Imager, MessageWindow, Process, Real, Rope, UserProfile, UserTerminal, VFonts, ViewerBLT, ViewerOps, WindowManager, WindowManagerPrivate EXPORTS Menus, MenusPrivate SHARES ViewerOps = BEGIN OPEN Menus, MenusPrivate; Viewer: TYPE = ViewerClasses.Viewer; menuFont: VFonts.Font _ VFonts.defaultFont; fontHeight: INTEGER; baselineOffset: INTEGER; menuHLeading: INTEGER = 5; -- white space before first entry menuHSpace: INTEGER = 12; -- white space between entries registeredMenus: LIST OF InternalFormatMenuRef _ NIL; widthFudge: INTEGER = 3; targetNotFound: PUBLIC SIGNAL = CODE; 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: MenuEntryTrigger] = BEGIN 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]; END; ProcessMenuHit: ENTRY PROC [entry: EntryState, menus: ViewerMenus, parent: Viewer, trigger: MenuEntryTrigger] = TRUSTED BEGIN response: REF ANY; SELECT entry.guardState FROM guarded => BEGIN entry.guardState _ arming; Process.Detach[FORK ArmMenuProc[entry, menus, parent]]; response _ FindNotifyRecord[entry,trigger].guardResponse; IF response#NIL THEN Process.Detach[ FORK GuardResponse[response] ]; END; arming=> NULL; -- no action armed => BEGIN IF entry.commonData.guarded THEN entry.guardState _ guarded; MenuPusher[entry, menus, parent, trigger, FALSE]; END; ENDCASE; END; 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: MenuEntryTrigger, normalPriority: BOOL _ TRUE] = BEGIN changeOccurred: BOOLEAN _ FALSE; notifyRecord: EntryNotifyRecord _ FindNotifyRecord[entry,trigger]; notifyProc: ViewerClasses.NotifyProc _ FindViewerMenu[menus,entry].commonData.notify; IF notifyProc = NIL THEN { notifyProc _ parent.class.notify; 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] } ELSE Debug[msg: "using specially specified notifyproc", waitForLeftShift: TRUE, level: 2]; entry.greyed _ TRUE; RedrawMenu[parent, menus, entry]; IF normalPriority THEN TRUSTED {Process.SetPriority[Process.priorityNormal]}; IF notifyProc # NIL THEN notifyProc[parent, notifyRecord.notifyData]; entry.greyed _ FALSE; RedrawMenu[parent, menus, entry]; changeOccurred _ FALSE; FOR m: LIST OF Rope.ROPE _ notifyRecord.makeActive, m.rest UNTIL m = NIL DO MakeActive[viewer: parent, menu: m.first, paint: FALSE]; changeOccurred _ TRUE; ENDLOOP; FOR m: LIST OF Rope.ROPE _ notifyRecord.makeInActive, m.rest UNTIL m = NIL DO MakeInActive[viewer: parent, menu: m.first, paint: FALSE]; changeOccurred _ TRUE; ENDLOOP; FOR m: LIST OF Rope.ROPE _ notifyRecord.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.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] = { OPEN Imager; myGrey: REF CARDINAL = NEW[CARDINAL _ 001010B]; lowerLeftX: INTEGER _ viewer.wx + entry.xPos; lowerLeftY: INTEGER _ viewer.wy + entry.yPos; IF clearFirst THEN { SetColor[context, white]; MaskIntRectangle[context, [lowerLeftX - widthFudge, lowerLeftY, entry.width + (2*widthFudge), fontHeight-1]]; SetColor[context, black]; }; IF entry.greyed THEN { SetColor[context, myGrey]; MaskIntRectangle[context, [lowerLeftX - widthFudge, lowerLeftY, entry.width + (2*widthFudge), fontHeight-1]]; SetColor[context, black]; }; WITH entry.commonData.displayData SELECT FROM r: Rope.ROPE => { SetIntCP[context, [lowerLeftX, lowerLeftY + baselineOffset]]; MaskCharacters[context, VFonts.defaultFont, r]; }; user: REF DrawingRec => { CallUserDrawProc: PROC = { ClipIntRectangle[context, [lowerLeftX-widthFudge, lowerLeftY, entry.width+(2*widthFudge), fontHeight-1]]; [] _ user.proc[op: draw, ctx: context, clientData: user.data]; }; SetIntCP[context, [lowerLeftX, lowerLeftY]]; DoSave[context, CallUserDrawProc]; }; ENDCASE => ERROR; IF entry.commonData.guarded AND entry.guardState # armed THEN MaskIntRectangle[context, [lowerLeftX - 1, lowerLeftY + baselineOffset+2, entry.width + 2, 1]]; }; DrawMenu: PUBLIC PROC [v: Viewer, menus: ViewerMenus, context: Imager.Context, whatChanged: REF ANY _ NIL] = BEGIN Debug[msg: IO.PutFR["drawing menus of height: %d", IO.int[menus.h]], level: 2]; IF whatChanged # NIL THEN DrawSingleEntry[v, context, NARROW[whatChanged, EntryState]] ELSE { 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; }; END; 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; FindNotifyRecord: PROC [entry: EntryState, trigger: MenuEntryTrigger] RETURNS [answer: EntryNotifyRecord] = { FOR l: LIST OF EntryNotifyRecord _ entry.commonData.actions, l.rest UNTIL l = NIL DO FOR t: LIST OF MenuEntryTrigger _ l.first.trigger, t.rest UNTIL t = NIL DO IF t.first = trigger OR t.first = all THEN RETURN[l.first]; ENDLOOP; 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]; }; CreateInternalFormatMenu: PROC [menu: Menu] RETURNS [answer: InternalFormatMenuRef] = { reversedList: LIST OF EntryRef _ NIL; answer _ NEW[InternalFormatMenu]; answer.name _ menu.name; answer.beginsActive _ menu.beginsActive; answer.breakBefore _ menu.breakBefore; answer.breakAfter _ menu.breakAfter; answer.notify _ menu.notify; answer.entries _ NIL; FOR e: LIST OF Entry _ menu.entries, e.rest UNTIL e = NIL DO reversedList _ CONS[MakeNewEntry[e.first], reversedList]; ENDLOOP; FOR e: LIST OF EntryRef _ reversedList, e.rest UNTIL e = NIL DO answer.entries _ CONS[e.first, answer.entries]; ENDLOOP; RETURN[answer]; }; MakeNewEntry: PROC[entry: Entry] RETURNS[answer: EntryRef] = { answer _ NEW[Entry _ entry]; IF answer.displayData = NIL THEN answer.displayData _ answer.name ELSE WITH answer.displayData SELECT FROM dispData: REF TEXT => answer.displayData _ Rope.FromRefText[dispData]; okRope: Rope.ROPE => NULL; okProc: REF DrawingRec => NULL; ENDCASE => ERROR; -- invalid data type for DisplayData field. FOR t: LIST OF EntryNotifyRecord _ answer.actions, t.rest UNTIL t = NIL DO IF t.first.guardResponse # NIL THEN WITH t.first.guardResponse SELECT FROM textRef: REF TEXT => t.first.guardResponse _ Rope.FromRefText[textRef]; okRope: Rope.ROPE => NULL; okRec: REF UnGuardRec => NULL; ENDCASE => ERROR; -- invalid data type for guardResponse field. ENDLOOP; RETURN[answer]; }; CreateExternalFormatMenu: PROC [menu: InternalFormatMenuRef] RETURNS [Menu] = { reversedList: LIST OF Entry _ NIL; answer: REF Menu _ NEW[Menu]; answer.name _ menu.name; answer.beginsActive _ menu.beginsActive; answer.breakBefore _ menu.breakBefore; answer.breakAfter _ menu.breakAfter; answer.notify _ menu.notify; answer.entries _ NIL; FOR e: LIST OF EntryRef _ menu.entries, e.rest UNTIL e = NIL DO temp: EntryRef _ NEW[Entry _ e.first^]; reversedList _ CONS[temp^, reversedList]; ENDLOOP; FOR e: LIST OF Entry _ reversedList, e.rest UNTIL e = NIL DO answer.entries _ CONS[e.first, answer.entries]; ENDLOOP; RETURN[answer^]; }; RegisterMenu: PUBLIC PROC [menu: Menu] = { registeredMenus _ CONS[CreateInternalFormatMenu[menu], registeredMenus]; }; AlreadyRegistered: PUBLIC PROC [name: Rope.ROPE] RETURNS [registered: BOOLEAN] = { FOR m: LIST OF InternalFormatMenuRef _ registeredMenus, m.rest UNTIL m = NIL DO IF Rope.Equal[m.first.name, name] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; ReRegisterMenu: PUBLIC PROC [menu: Menu, paint: BOOL _ TRUE] = { newMenu: ViewerMenuRec; MenuDefinitonChanged: ViewerOps.EnumProc = { FOR m: LIST OF ViewerMenu _ NARROW[v.menus, ViewerMenus].list, m.rest UNTIL m = NIL DO IF Rope.Equal[menu.name, m.first.commonData.name] THEN m.first _ NEW[ViewerMenuRec _ newMenu]; ENDLOOP; IF paint THEN RePaintBecauseOfMenuChange[v]; }; FOR m: LIST OF InternalFormatMenuRef _ registeredMenus, m.rest UNTIL m = NIL DO IF Rope.Equal[m.first.name, menu.name] THEN { m.first _ CreateInternalFormatMenu[menu]; newMenu _ MakeNewViewerMenuRec[m.first]; ViewerOps.EnumerateViewers[MenuDefinitonChanged]; RETURN; }; ENDLOOP; SIGNAL targetNotFound; }; GetRegisteredMenu: PUBLIC PROC [name: Rope.ROPE] RETURNS [menu: Menu] = { FOR m: LIST OF InternalFormatMenuRef _ registeredMenus, m.rest UNTIL m = NIL DO IF Rope.Equal[m.first.name, menu.name] THEN RETURN[CreateExternalFormatMenu[m.first] ]; ENDLOOP; SIGNAL targetNotFound; }; ClearMenus: PUBLIC PROC [viewer: Viewer, paint: BOOL _ TRUE] = { viewer.menus _ NIL; IF paint THEN RePaintBecauseOfMenuChange[viewer]; }; AddMenu: PUBLIC PROC [viewer: Viewer, name: Rope.ROPE, paint: BOOL _ TRUE, addBefore: Rope.ROPE _ NIL] = { IF viewer.menus = NIL THEN { IF addBefore # NIL THEN SIGNAL targetNotFound; viewer.menus _ NEW[ViewerMenusRec]; NARROW[viewer.menus, ViewerMenus].list _ LIST[MakeNewViewerMenu[name]]; } ELSE IF addBefore # NIL THEN { prev: LIST OF ViewerMenu _ NIL; foundIt: BOOLEAN _ FALSE; FOR m: LIST OF ViewerMenu _ NARROW[viewer.menus, ViewerMenus].list, m.rest UNTIL m = NIL DO IF Rope.Equal[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 GetInternalFormatMenuRef[name].breakBefore THEN { prev: LIST OF ViewerMenu _ NIL; FOR m: LIST OF ViewerMenu _ NARROW[viewer.menus, ViewerMenus].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 _ NARROW[viewer.menus, ViewerMenus].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: Rope.ROPE, addBefore: Rope.ROPE _ 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 Rope.Equal[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 GetInternalFormatMenuRef[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: Rope.ROPE] RETURNS [answer: ViewerMenu] = { FOR m: LIST OF InternalFormatMenuRef _ registeredMenus, m.rest UNTIL m = NIL DO IF Rope.Equal[m.first.name, name] THEN RETURN[NEW[ViewerMenuRec _ MakeNewViewerMenuRec[m.first] ] ]; ENDLOOP; SIGNAL targetNotFound; }; MakeNewViewerMenuRec: PROC [data: InternalFormatMenuRef] RETURNS [answer: ViewerMenuRec] = { answer.commonData _ data; answer.active _ answer.commonData.beginsActive; answer.entries _ MakeNewViewerMenuEntries[data]; RETURN[answer]; }; MakeNewViewerMenuEntries: PROC [menu: InternalFormatMenuRef] RETURNS [answer: LIST OF EntryState] = { reversedList: LIST OF EntryState _ NIL; answer _ NIL; FOR e: LIST OF EntryRef _ 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: Rope.ROPE, paint: BOOL _ TRUE] = { FOR m: LIST OF ViewerMenu _ NARROW[viewer.menus, ViewerMenus].list, m.rest UNTIL m = NIL DO IF Rope.Equal[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: Rope.ROPE, paint: BOOL _ TRUE] = { FOR m: LIST OF ViewerMenu _ NARROW[viewer.menus, ViewerMenus].list, m.rest UNTIL m = NIL DO IF Rope.Equal[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: Rope.ROPE, paint: BOOL _ TRUE] = { FOR m: LIST OF ViewerMenu _ NARROW[viewer.menus, ViewerMenus].list, m.rest UNTIL m = NIL DO IF Rope.Equal[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: Rope.ROPE] RETURNS [exists: BOOLEAN] = { FOR m: LIST OF ViewerMenu _ NARROW[viewer.menus, ViewerMenus].list, m.rest UNTIL m = NIL DO IF Rope.Equal[m.first.commonData.name, menu] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; wrap: BOOLEAN _ TRUE; -- TRUE iff user wants menu entries to ALWAYS 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 ReEstablishUserProfileParamaters: 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] = { originalHeight: INTEGER; prevLineHadBreakafter: BOOLEAN _ TRUE; -- starting true forces first line to move down wbs: INTEGER _ IF v.border THEN ViewerSpecs.windowBorderSize ELSE 0; TopLeftCornerX: INTEGER _ wbs; TopLeftCornerY: INTEGER _ v.wh-ViewerSpecs.captionHeight; CurrentX: INTEGER _ TopLeftCornerX + menuHLeading; CurrentY: INTEGER _ TopLeftCornerY; IF v.menus=NIL OR v.iconic THEN RETURN[0]; originalHeight _ NARROW[v.menus, ViewerMenus].h; IF wrap THEN Debug[msg: IO.PutFR["ReComputeMenus for %g ==> wrap is true", IO.rope[ NARROW[v.menus, ViewerMenus].list.first.commonData.name]], level: 2] ELSE Debug[msg: IO.PutFR["ReComputeMenus for %g ==> wrap is false", IO.rope[NARROW[v.menus, ViewerMenus].list.first.commonData.name]], level: 2]; FOR m: LIST OF ViewerMenu _ NARROW[v.menus, ViewerMenus].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 - ViewerSpecs.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.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 { Debug[msg: "we just decided to wrap!", level: 2]; CurrentX _ TopLeftCornerX + wrapIndent; CurrentY _ CurrentY - ViewerSpecs.menuHeight; }; e.first.xPos _ CurrentX; e.first.yPos _ CurrentY; CurrentX _ CurrentX + e.first.width + menuHSpace; ENDLOOP; ENDLOOP; NARROW[v.menus, ViewerMenus].x _ TopLeftCornerX; NARROW[v.menus, ViewerMenus].y _ CurrentY; NARROW[v.menus, ViewerMenus].w _ v.ww - (2 * wbs); NARROW[v.menus, ViewerMenus].h _ TopLeftCornerY - CurrentY; Debug[msg: IO.PutFR["menu height deterimined to be: %d, a difference of: %d",IO.int[TopLeftCornerY - CurrentY], IO.int[NARROW[v.menus, ViewerMenus].h - originalHeight] ],level: 2]; RETURN[NARROW[v.menus, ViewerMenus].h - originalHeight]; }; ReComputeWindowMenus: PUBLIC PROC [v: Viewer, guard: BOOL, color: BOOL] = { CurrentX: INTEGER _ menuHLeading + ViewerSpecs.windowBorderSize; CurrentY: INTEGER ~ v.wh - ViewerSpecs.captionHeight; -- all have same Y coordinate WindowManagerPrivate.windowMenus.x _ ViewerSpecs.windowBorderSize; WindowManagerPrivate.windowMenus.y _ v.wh - ViewerSpecs.captionHeight; WindowManagerPrivate.windowMenus.w _ v.ww - (2 * ViewerSpecs.windowBorderSize); WindowManagerPrivate.windowMenus.h _ ViewerSpecs.captionHeight; Debug["ReComputeWindowMenus entered", TRUE, 3]; IF guard THEN { Debug["ReComputeWindowMenus: guard is true", TRUE, 4]; SetDisplay["windowDestroyMenu", FALSE]; SetDisplay["windowGuardedDestroyMenu", TRUE]; } ELSE { Debug["ReComputeWindowMenus: guard is false", TRUE, 4]; 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.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: Rope.ROPE, setting: BOOL] = { FOR l: LIST OF ViewerMenu _ WindowManagerPrivate.windowMenus.list, l.rest UNTIL l = NIL DO IF Rope.Equal[l.first.commonData.name, name] THEN { l.first.active _ setting; RETURN; }; ENDLOOP; ERROR; }; GetInternalFormatMenuRef: PROC [name: Rope.ROPE] RETURNS [answer: InternalFormatMenuRef] = { FOR m: LIST OF InternalFormatMenuRef _ registeredMenus, m.rest UNTIL m = NIL DO IF Rope.Equal[m.first.name, name] THEN RETURN[m.first]; ENDLOOP; ERROR; }; RePaintBecauseOfMenuChange: PROC [v: Viewer] = { ViewerBLT.ChangeMenuHeight[v, ReComputeMenus[v]]; }; AlterDebuggingLevel: PUBLIC PROC [amount: INTEGER, relative: BOOL _ TRUE] = { string: Rope.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]; }; currentDebugLevel: INTEGER _ 3; Debug: PUBLIC PROC [msg: Rope.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 ComputeFontInfo[]; UserProfile.CallWhenProfileChanges[ReEstablishUserProfileParamaters]; 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 assert: state=arming this is where we actually tell the client about the menu-invoked function: separate procedure because it sets a clipping rectangle draw line through guarded entries (constants of '2' and '1' look good, aren't used elsewhere) NB: the menus parm need not be the menus for the viewer; it could also be the windowManger caption menu 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: NOTE! the following line produces a compiler warning that can be safely ignored. note that CONS'ing builds the list in the wrong order, so we do it twice! (gross ...) the name should be used for displaydata if nothing special is specified. this is here so that user-created entries that have displaydata fields of TYPE REF text can be converted to Rope.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.ROPE. If the compiler ever defaults quoted text strings, this can be safely removed. note that CONS'ing builds the list in the wrong order, so we do it twice! (gross ...) [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: menu wrapping is User Profilible: this routine is called everytime the .profile gets edited 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 wrap by just skipping down to the next line menus coordinates are computed and stored RELATIVE to the viewer hack for special clicking GROW and CLOSE ViewerOps.PaintViewer[viewer: v, hint: menu]; Κ…– "cedar" style˜JšΟc&™&Jš-™-J™-J™0šΟk ˜ Jšœ˜J˜ Jšžœ˜J˜J˜ Jšœžœ˜$JšœžœN˜[Jšœžœ ˜Jšœžœžœ˜&Jšœ ˜ Jšœžœ˜ Jšœ žœ?˜PJšœžœ8˜DJšœ žœ˜#J˜Jšœ žœ;˜JJšœ žœ/˜@Jšœžœ˜$Jšœžœ3˜MJ˜—Jš œ žœžœžœžœ˜Jšœ žœ,˜9Jšžœ žœ˜,—Jšžœ˜J˜—š Ÿ œžœžœ-žœžœ˜QJšž˜šžœžœžœž˜ Jšžœžœ˜)Jšœžœ˜Jšžœ˜—Jšžœ˜J˜—šŸœžœJžœžœ˜rJšžœ˜ Jš œžœžœžœžœ ˜/Jšœ žœ˜-Jšœ žœ˜-šžœ žœ˜J˜Jšœm˜mJ˜J˜—šžœžœ˜J˜Jšœm˜mJ˜J˜—šžœžœž˜-šœžœ˜Jšœ=˜=Jšœ/˜/J˜—šœžœ˜šΠbnœžœ˜Jšœ7™7Jšœi˜iJšœ@˜@J˜—Jšœ,˜,Jšœ"˜"J˜—Jšžœžœ˜—Jšœ^™^Jšžœžœžœ`˜J˜J˜—š ŸœžœžœGžœžœžœž˜rJšœg™gJšœ žœ&žœ˜OJšžœžœžœžœ˜Všžœ˜š žœžœžœ"žœžœž˜@šžœžœ˜š žœžœžœ&žœžœž˜DJšœ%žœ˜,Jšžœ˜——Jšžœ˜Jšœžœ˜—J˜—Jšžœ˜—J˜Jšœ!žœ˜%Jšœžœ˜#Jšœžœ˜6šŸ œžœ9žœ˜jJšœΰ™ΰJ˜Jšœ ™ šžœžœžœžœ˜CJš žœžœ$žœžœ'žœžœ˜‡J˜—J˜Jšœ"™"š žœžœžœ!žœžœž˜?Jšžœžœžœžœ˜ š žœžœžœ&žœžœž˜Dš žœžœ0žœžœ/žœ˜Jšœ™Jšœ˜Jšœ˜J˜Jšžœ ˜J˜—Jšžœ˜—Jšžœ˜—Jšžœžœ˜ Jšœ˜—šŸ œžœ)˜:šžœžœžœ˜.J˜#Jšœ„˜„J˜—Jšœ˜J˜—šŸœžœž˜Jšœžœ˜.J˜)Jšœ0˜0Jšžœ˜J˜—šŸœžœ0žœ ˜mš žœžœžœ6žœžœž˜Tš žœžœžœ,žœžœž˜JJšœP™PJšžœžœžœžœ ˜;Jšžœ˜—Jšžœ˜—Jšžœ˜J˜J˜—šŸœžœ)žœ˜[š žœžœžœ!žœžœž˜?š žœžœžœ&žœžœž˜DJšžœžœžœ ˜(Jšžœ˜—Jšžœ˜—Jšžœžœ˜ J˜J˜—šŸœžœžœ$˜WJšœU™UJšœžœžœ žœ˜%Jšœ žœ˜!J˜Jšœ(˜(Jšœ&˜&Jšœ$˜$Jšœ˜Jšœžœ˜š žœžœžœžœžœž˜Jšœ žœ˜JšœH™HJšžœžœžœ!˜AJšžœžœžœž˜(šœΖ™ΖJšœ žœžœ6˜HJšœ žœžœ˜Jšœžœžœ˜ Jšžœ žœ+˜B—JšœΖ™Ζš žœžœžœ,žœžœž˜Jš žœžœžœžœžœž˜JJšœ žœžœ8˜IJšœ žœžœ˜Jšœžœžœ˜Jšžœ žœ-˜D—Jšžœ˜—Jšžœ ˜˜J˜—J˜—šŸœžœžœ ˜OJšœU™UJšœžœžœ žœ˜"Jšœžœžœ˜J˜Jšœ(˜(Jšœ&˜&Jšœ$˜$Jšœ˜Jšœžœ˜š žœžœžœ!žœžœž˜?Jšœžœ˜'Jšœžœ˜*Jšžœ˜—š žœžœžœžœžœž˜˜˜Jšžœ žœ2žœžœ?˜‘š žœžœžœžœ$žœžœž˜VJšžœžœžœžœ˜ šžœžœ žœ˜AJšœ)˜)J˜-J˜—Jšœ6˜6š žœžœžœ&žœžœž˜Dšœžœ žœž˜?Jšœžœ˜%Jšœžœ:˜CJšžœžœ˜—šžœžœž˜.šžœ+žœ˜3Jšœ+™+J˜1J˜'J˜-J˜——J˜J˜Jšœ1˜1Jšžœ˜—Jšžœ˜—Jšžœ*˜0Jšžœ$˜*Jšžœ,˜2Jšžœ5˜;Jš œ žœ@žœ!žœžœ7˜΄Jšžœžœ+˜8J˜J˜—š Ÿœžœžœžœ žœ˜LJšœ@™@Jšœ žœ/˜@Jšœ žœ%˜SJšœB˜BJšœF˜FJšœO˜OJšœ?˜?Jšœ&žœ˜/J˜šžœžœ˜Jšœ-žœ˜6Jšœ žœ˜'Jšœ'žœ˜-J˜—šžœ˜Jšœ.žœ˜7Jšœ žœ˜&Jšœ'žœ˜.J˜—Jšžœžœžœ˜1Jšžœžœ˜*J˜š žœžœžœ<žœžœž˜ZJšžœžœžœžœ˜ š žœžœžœ&žœžœž˜Dšœžœ žœž˜?Jšœžœ˜%Jšœžœ:˜CJšžœžœ˜—J˜J˜Jšœ(™(šžœ-žœ˜5Jšœ5˜5Jšœ%žœ˜+Jšœ5˜5J˜—šžœ.žœ˜6Jšœ6˜6Jšœ&žœ˜,Jšœ6˜6J˜—Jšœ1˜1Jšžœ˜—Jšžœ˜—J˜J˜—šŸ œžœ žœ žœ˜5š žœžœžœ<žœžœž˜Zšžœ+žœ˜3Jšœ˜Jšžœ˜J˜—Jšžœ˜—Jšžœ˜J˜J˜—šŸœžœ žœžœ$˜\š žœžœžœ1žœžœž˜OJšžœ žœžœ ˜8Jšžœ˜—Jšžœ˜J˜J˜—šŸœžœ˜0Jšœ1˜1Jšœ-™-Jšœ˜—J˜š Ÿœžœžœ žœ žœžœ˜MJšœ žœ˜Jšžœ žœ/˜?Jšžœ˜ Jšœ žœ$žœ˜JJšœžœ˜(J˜J˜—Jšœžœ˜šŸœžœžœ žœžœžœ žœ žœ˜cJš œžœžœžœžœ˜Lšžœžœ˜$Jšœžœ˜ šžœžœ˜Jšœ9žœ˜@šžœž˜"Jšžœ˜—šžœž˜ Jšžœ˜—Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜ —J˜J˜JšœE˜EJ˜Jšžœ˜J˜—…—]Ž†½