<> <> <> <> <> <<>> DIRECTORY Carets USING [ResumeCarets, SuspendCarets], ColorWorld USING [TurnOffColor, TurnOnColor], Cursors USING [CursorType, GetCursor, SetCursor], InputFocus USING [CaptureButtons, inputEnabled, ReleaseButtons, WindowManagerTIPTable], InterminalExtra USING [InsertAction], Imager USING [black, Color, Context, DoSave, IntegerClipRectangle, IntegerMaskRectangle, MakeStipple, SetColor, white], Menus USING [ActionRec, allShifts, allTriggers, EntryRec, Menu, MenuRec, RegisterMenu], MenusPrivate USING [ClearMenu, DrawMenu, HitMenu, MarkMenu, ReComputeWindowMenus, ViewerlessAddMenu, ViewerMenus], MessageWindow USING [Append, Blink], TIPUser USING [TIPScreenCoords, TIPScreenCoordsRec], UserTerminal USING [BlinkDisplay], ViewerClasses USING [Column, NotifyProc, ScrollOp, Viewer], ViewerOps USING [AcquireContext, Adjust, ChangeColumn, CloseViewer, DestroyViewer, EnumerateViewers, EnumProc, GrowViewer, InitialiseColorPainting, InvisiblePaint, MouseInViewer, PaintViewer, ReleaseContext, TopViewer], ViewerSpecs USING [captionHeight, scrollBarW, windowBorderSize], WindowManager USING [ScreenPos], WindowManagerPrivate USING []; WindowManagerImpl: CEDAR MONITOR IMPORTS Carets, ColorWorld, Cursors, Imager, InputFocus, InterminalExtra, Menus, MenusPrivate, MessageWindow, UserTerminal, ViewerOps EXPORTS WindowManager, WindowManagerPrivate SHARES InputFocus, ViewerClasses, ViewerOps = BEGIN OPEN ViewerSpecs; Viewer: TYPE = ViewerClasses.Viewer; Zone: TYPE = {none, menu, scroll, hscroll, caption, client}; CursorZone: PROC[v: Viewer, mousePos: TIPUser.TIPScreenCoords] RETURNS[Zone] = { <> x: INTEGER = mousePos.mouseX; y: INTEGER = mousePos.mouseY; IF v.parent=NIL AND v.column#static THEN { ymax: INTEGER = v.wh; -- top of caption (= top of window) ymin: INTEGER = ymax-captionHeight; -- bottom of caption IF y IN[ymin..ymax) THEN RETURN[caption]; }; IF v.scrollable THEN { ymin: INTEGER = v.cy-v.wy; -- bottom of scrollbar (= bottom of client area) ymax: INTEGER = ymin+v.ch; -- top of scrollbar (= top of client area) IF x IN[0..scrollBarW) AND y IN[ymin..ymax) THEN RETURN[scroll]; }; IF v.hscrollable THEN { ymin: INTEGER = v.cy-v.wy+v.ch; -- bottom of scrollbar (= top of client area) ymax: INTEGER = ymin+scrollBarW; -- top of scrollbar IF y IN[ymin..ymax) THEN RETURN[hscroll]; }; IF v.menus#NIL THEN { m: MenusPrivate.ViewerMenus = NARROW[v.menus]; IF x IN[m.x..m.x+m.w) AND y IN[m.y..m.y+m.h) THEN RETURN[menu]; }; RETURN[none]; }; feedbackZone: Zone _ none; feedbackViewer: Viewer _ NIL; PostNewFeedback: PROC[viewer: Viewer, zone: Zone] = { old, new: BOOL _ TRUE; SELECT feedbackZone FROM scroll => RemoveScrollFeedback[feedbackViewer]; < RemoveHScrollFeedback[feedbackViewer];>> menu => RemoveMenuFeedback[feedbackViewer]; caption => RemoveCaptionFeedback[feedbackViewer]; ENDCASE => old _ FALSE; -- no old feedback IF old THEN { InputFocus.ReleaseButtons[]; feedbackViewer _ NIL; }; SELECT feedbackZone _ zone FROM scroll => PostScrollFeedback[viewer]; < PostHScrollFeedback[viewer];>> menu => PostMenuFeedback[viewer]; caption => PostCaptionFeedback[viewer]; ENDCASE => { SetC[textPointer]; new _ FALSE }; -- no new feedback IF new THEN { InputFocus.CaptureButtons[ ProcessWindowResults, InputFocus.WindowManagerTIPTable, viewer]; feedbackViewer _ viewer; }; }; ProcessWindowResults: PUBLIC ViewerClasses.NotifyProc = { <<[self: Viewer, input: LIST OF REF ANY]>> <> zone: Zone _ feedbackZone; newZone: BOOL _ FALSE; shift, control: BOOL _ FALSE; mousePos: TIPUser.TIPScreenCoords; viewer: Viewer _ self; -- the current viewer FOR l: LIST OF REF ANY _ input, l.rest UNTIL l=NIL DO WITH l.first SELECT FROM z: TIPUser.TIPScreenCoords => { client: BOOL _ FALSE; mousePos _ z; IF feedbackZone#none THEN [viewer, client] _ ViewerOps.MouseInViewer[mousePos]; zone _ IF client OR viewer=NIL THEN none ELSE CursorZone[viewer, mousePos]; newZone _ zone#feedbackZone OR (viewer#feedbackViewer AND feedbackViewer#NIL); }; z: ATOM => { IF newZone THEN PostNewFeedback[viewer, zone]; SELECT zone FROM -- zone specific ops scroll => SELECT z FROM $M => SetC[scrollUpDown]; $RU => HitScroll[viewer, mousePos.mouseY, up, shift, control]; $RD, $RM => SetC[scrollUp]; $YU => HitScroll[viewer, mousePos.mouseY, thumb, shift, control]; $YD, $YM => SetC[scrollRight]; $BU => HitScroll[viewer, mousePos.mouseY, down, shift, control]; $BD, $BM => SetC[scrollDown]; $Control => control _ TRUE; $Shift => shift _ TRUE; ENDCASE => NULL; menu => SELECT z FROM $RU => MenusPrivate.HitMenu[NARROW[viewer.menus], viewer, mousePos, IF shift THEN shiftLeftUp ELSE leftUp]; $YU => MenusPrivate.HitMenu[NARROW[viewer.menus], viewer, mousePos, IF shift THEN shiftMiddleUp ELSE middleUp]; $BU => MenusPrivate.HitMenu[NARROW[viewer.menus], viewer, mousePos, IF shift THEN shiftRightUp ELSE rightUp]; $RM, $BM, $YM => {MenusPrivate.MarkMenu[NARROW[viewer.menus], viewer, mousePos]}; $RD, $BD, $YD => MenusPrivate.MarkMenu[NARROW[viewer.menus], viewer, mousePos]; $Control => control _ TRUE; $Shift => shift _ TRUE; ENDCASE => NULL; caption => SELECT z FROM $RU => MenusPrivate.HitMenu[windowMenus, viewer, mousePos, IF shift THEN shiftLeftUp ELSE leftUp]; $YU => MenusPrivate.HitMenu[windowMenus, viewer, growPos, IF shift THEN shiftMiddleUp ELSE middleUp]; $BU => MenusPrivate.HitMenu[windowMenus, viewer, closePos, IF shift THEN shiftRightUp ELSE rightUp]; $RM, $RD => MenusPrivate.MarkMenu[windowMenus, viewer, mousePos]; $BM, $BD => MenusPrivate.MarkMenu[windowMenus, viewer, closePos]; $YM, $YD => MenusPrivate.MarkMenu[windowMenus, viewer, growPos]; $Control => control _ TRUE; $Shift => shift _ TRUE; ENDCASE => NULL; ENDCASE; }; ENDCASE => TRUSTED {UserTerminal.BlinkDisplay[]}; ENDLOOP; }; AlterColumn: PROC [v: Viewer, mx: INTEGER] = { right: BOOL ~ mx >= v.ww/2; column: ViewerClasses.Column _ SELECT v.column FROM left => IF right THEN right ELSE IF colorDisplayOn THEN color ELSE right, right => IF ~right THEN left ELSE IF colorDisplayOn THEN color ELSE left, color => IF right THEN right ELSE left, ENDCASE => ERROR; ViewerOps.ChangeColumn[v, column]; }; SetC: PROC [cursor: Cursors.CursorType] = INLINE {IF waitCount=0 AND Cursors.GetCursor[]#cursor THEN Cursors.SetCursor[cursor]}; HitScroll: PROC [v: Viewer, y: INT, op: ViewerClasses.ScrollOp, shift, control: BOOL] = { RemoveScrollFeedback[v]; <> y _ v.ch-y; -- client coords IF v.class.scroll#NIL THEN [] _ v.class.scroll[v, op, SELECT op FROM up, down => y, ENDCASE => (100*(y+1))/v.ch, -- percent shift, control]; PostScrollFeedback[v]; }; scrollVisible: Imager.Color = Imager.MakeStipple[122645B]; scrollInvisible: Imager.Color = Imager.MakeStipple[100040B]; PostScrollFeedback: PROC[v: Viewer] = { top, bottom: INTEGER; IF v.class.scroll=NIL THEN RETURN; IF ~v.init THEN RETURN; -- avoid a race condition bug Carets.SuspendCarets[]; [top, bottom] _ v.class.scroll[v, query, 0]; IF top IN[0..100] AND bottom IN[0..100] THEN { context: Imager.Context _ ViewerOps.AcquireContext[v.parent, v.column=color ! ViewerOps.InvisiblePaint => GOTO Punt]; vbs: INTEGER = (IF v.border THEN windowBorderSize ELSE 0); baseY, baseX: INTEGER; relY1, relY2: INT; -- so won't overflow <> <> <> <> <<} ELSE>> baseY _ v.wy + vbs; baseX _ v.wx + vbs; relY1 _ baseY; relY2 _ (LONG[100-bottom]*v.ch)/100+baseY; Imager.SetColor[context, scrollInvisible]; Imager.IntegerMaskRectangle[context, baseX, relY1, scrollBarW, relY2-relY1]; relY1 _ relY2; relY2 _ relY2 + (LONG[bottom-top]*v.ch)/100; Imager.SetColor[context, scrollVisible]; Imager.IntegerMaskRectangle[context, baseX, relY1, scrollBarW, relY2-relY1]; Imager.SetColor[context, scrollInvisible]; Imager.IntegerMaskRectangle[context, baseX, relY2, scrollBarW, baseY+v.ch-relY2]; ViewerOps.ReleaseContext[context]; EXITS Punt => NULL; }; Carets.ResumeCarets[]; SetC[scrollUpDown]; }; RemoveScrollFeedback: PROC[v: Viewer] = { Carets.SuspendCarets[]; IF v#NIL THEN { context: Imager.Context _ ViewerOps.AcquireContext[v.parent, v.column=color ! ViewerOps.InvisiblePaint => GOTO Punt]; baseX, baseY: INTEGER; vbs: INTEGER = (IF v.border THEN windowBorderSize ELSE 0); <> <> <> <> <> <<} ELSE>> baseY _ v.wy + vbs; baseX _ v.wx + vbs; Imager.SetColor[context, Imager.white]; Imager.IntegerMaskRectangle[context, baseX, baseY, scrollBarW, v.ch]; ViewerOps.ReleaseContext[context]; EXITS Punt => NULL; }; Carets.ResumeCarets[]; }; InternalDrawCaptionMenu: INTERNAL PROC[v: Viewer, guard: BOOL] = { IF v.visible AND NOT v.iconic THEN { context: Imager.Context _ ViewerOps.AcquireContext[v.parent, v.column=color ! ViewerOps.InvisiblePaint => GOTO Punt]; DrawCaption: PROC = { x: INTEGER _ v.wx+windowBorderSize; y: INTEGER _ v.wy+v.wh-captionHeight; guardDestroy: BOOL _ FALSE; IF guard THEN guardDestroy _ v.guardDestroy OR (v.newVersion AND v.link=NIL AND NOT v.saveInProgress); Imager.IntegerClipRectangle[context, x, y, v.ww-(2*windowBorderSize), captionHeight]; Imager.SetColor[context, Imager.white]; Imager.IntegerMaskRectangle[context, x, y, v.ww, captionHeight]; Imager.SetColor[context, Imager.black]; MenusPrivate.ReComputeWindowMenus[v, guardDestroy, colorDisplayOn]; MenusPrivate.DrawMenu[v, windowMenus, context]; }; Imager.DoSave[context, DrawCaption]; ViewerOps.ReleaseContext[context]; EXITS Punt => NULL; }; }; DrawCaptionMenu: PUBLIC ENTRY PROC[v: Viewer, guard: BOOL] = { ENABLE UNWIND => NULL; IF v=feedbackViewer THEN InternalDrawCaptionMenu[v, guard]; }; PostCaptionFeedback: ENTRY PROC[v: Viewer] = { ENABLE UNWIND => NULL; InternalDrawCaptionMenu[v, TRUE]; SetC[bullseye]; }; RemoveCaptionFeedback: ENTRY PROC[v: Viewer] = { ENABLE UNWIND => NULL; IF v#NIL THEN { MenusPrivate.ClearMenu[windowMenus, v, FALSE]; ViewerOps.PaintViewer[v, caption]; }; }; PostMenuFeedback: PROC[v: Viewer] = { SetC[bullseye]; }; RemoveMenuFeedback: PROC[v: Viewer] = { menus: MenusPrivate.ViewerMenus = NARROW[v.menus]; MenusPrivate.ClearMenu[menus, v]; }; waitCount: PUBLIC INTEGER _ 0; WaitCursor: PUBLIC ENTRY PROC [cursor: Cursors.CursorType _ hourGlass] = { ENABLE UNWIND => NULL; IF InputFocus.inputEnabled THEN Cursors.SetCursor[cursor]; waitCount _ waitCount + 1; }; UnWaitCursor: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; waitCount _ MAX[0, waitCount - 1]; IF waitCount=0 THEN RestoreCursor[]; }; RestoreCursor: PUBLIC PROC = { IF InputFocus.inputEnabled THEN TRUSTED{InterminalExtra.InsertAction[[contents: deltaMouse[[0,0]]]]} }; StartColorViewers: PUBLIC PROC [screenPos: WindowManager.ScreenPos, bitsPerPixel: CARDINAL] = { IF colorDisplayOn THEN StopColorViewers[]; colorDisplayOn _ ColorWorld.TurnOnColor[bitsPerPixel, (screenPos=left)]; IF ~colorDisplayOn THEN { MessageWindow.Append["Sorry, you don't have a color display.", TRUE]; MessageWindow.Blink[]; RETURN; }; ViewerOps.InitialiseColorPainting[]; }; StopColorViewers: PUBLIC PROC = { DoColorViewer: ViewerOps.EnumProc = { IF v.column=color THEN { ViewerOps.CloseViewer[v]; ViewerOps.ChangeColumn[v, left]; }; }; IF ~colorDisplayOn THEN RETURN; ViewerOps.EnumerateViewers[DoColorViewer]; ColorWorld.TurnOffColor[]; colorDisplayOn _ FALSE; }; BuildWindowMenus: PROC = { Menus.RegisterMenu[windowDestroyMenu]; Menus.RegisterMenu[windowGuardedDestroyMenu]; Menus.RegisterMenu[windowMovementMenu]; Menus.RegisterMenu[windowColorMenu]; Menus.RegisterMenu[windowSizeMenu]; <> MenusPrivate.ViewerlessAddMenu[$windowDestroyMenu]; MenusPrivate.ViewerlessAddMenu[$windowGuardedDestroyMenu]; MenusPrivate.ViewerlessAddMenu[$windowMovementMenu]; MenusPrivate.ViewerlessAddMenu[$windowColorMenu]; MenusPrivate.ViewerlessAddMenu[$windowSizeMenu]; <> <> growPos _ NEW[TIPUser.TIPScreenCoordsRec _ [0, FALSE, 0]]; closePos _ NEW[TIPUser.TIPScreenCoordsRec _ [0, FALSE, 0]]; }; colorDisplayOn: PUBLIC BOOL _ FALSE; -- color display status windowMenus: PUBLIC MenusPrivate.ViewerMenus; growPos: PUBLIC TIPUser.TIPScreenCoords; closePos: PUBLIC TIPUser.TIPScreenCoords; windowDestroyMenu: Menus.Menu = NEW[Menus.MenuRec _ [ name: $windowDestroyMenu, beginsActive: TRUE, breakBefore: FALSE, breakAfter: FALSE, notify: ViewerMenuNotifier, entries: LIST[ NEW[Menus.EntryRec _ [ name: "Destroy", actions: LIST[NEW[Menus.ActionRec _ [ triggers: Menus.allTriggers, input: LIST[$DestroyViewer] ]]] ]] ] ]]; windowGuardedDestroyMenu: Menus.Menu = NEW[Menus.MenuRec _ [ name: $windowGuardedDestroyMenu, beginsActive: TRUE, breakBefore: FALSE, breakAfter: FALSE, notify: ViewerMenuNotifier, entries: LIST[ NEW[Menus.EntryRec _ [ name: "Destroy", guarded: TRUE, actions: LIST[NEW[Menus.ActionRec _ [ triggers: Menus.allTriggers, input: LIST[$DestroyViewer], popupDoc: "Destroy the Viewer", guardResponse: "Edits will be discarded..." ]]] ]] ] ]]; windowMovementMenu: Menus.Menu = NEW[Menus.MenuRec _ [ name: $windowMovementMenu, beginsActive: TRUE, breakBefore: FALSE, breakAfter: FALSE, notify: ViewerMenuNotifier, entries: LIST[ NEW[Menus.EntryRec _ [ name: "Adjust", actions: LIST[NEW[Menus.ActionRec _ [ triggers: Menus.allTriggers, input: LIST[$AdjustViewer], popupDoc: "Adjust Viewer Size" ]]] ]], NEW[Menus.EntryRec _ [ name: "Top", actions: LIST[NEW[Menus.ActionRec _ [ triggers: Menus.allTriggers, input: LIST[$MoveViewerToTop], popupDoc: "Move Viewer To Top Of Column" ]]] ]], NEW[Menus.EntryRec _ [ name: "Left", displayData: "", actions: LIST[ NEW[Menus.ActionRec _ [ triggers: Menus.allShifts, input: LIST[$MoveViewerToLeftColumn, $GrowViewer], popupDoc: "Move To Left Column and Grow" ]], NEW[Menus.ActionRec _ [ triggers: Menus.allTriggers, input: LIST[$MoveViewerToLeftColumn], popupDoc: "Move Viewer To Left Column" ]] ] ]], NEW[Menus.EntryRec _ [ name: "Right", displayData: "", actions: LIST[ NEW[Menus.ActionRec _ [ triggers: Menus.allShifts, input: LIST[$MoveViewerToRightColumn, $GrowViewer], popupDoc: "Move To Right Column and Grow" ]], NEW[Menus.ActionRec _ [ triggers: Menus.allTriggers, input: LIST[$MoveViewerToRightColumn], popupDoc: "Move Viewer To Right Column" ]] ] ]] ] ]]; windowColorMenu: Menus.Menu = NEW[Menus.MenuRec _ [ name: $windowColorMenu, beginsActive: TRUE, breakBefore: FALSE, breakAfter: FALSE, notify: ViewerMenuNotifier, entries: LIST[ NEW[Menus.EntryRec _ [ name: "Color", actions: LIST[ NEW[Menus.ActionRec _ [ triggers: Menus.allShifts, input: LIST[$MoveViewerToColorColumnAndGrow, $GrowViewer], popupDoc: "Move To Color Display and Grow" ]], NEW[Menus.ActionRec _ [ triggers: Menus.allTriggers, input: LIST[$MoveViewerToColorColumn], popupDoc: "Move Viewer To Color Display" ]] ] ]] ] ]]; windowSizeMenu: Menus.Menu = NEW[Menus.MenuRec _ [ name: $windowSizeMenu, beginsActive: TRUE, breakBefore: FALSE, breakAfter: FALSE, notify: ViewerMenuNotifier, entries: LIST[ NEW[Menus.EntryRec _ [ name: "Grow", actions: LIST[ NEW[Menus.ActionRec _ [ triggers: Menus.allTriggers, input: LIST[$GrowViewer], popupDoc: "Grow the Viewer" ]] ] ]], NEW[Menus.EntryRec _ [ name: "Close", actions: LIST[ NEW[Menus.ActionRec _ [ triggers: Menus.allTriggers, input: LIST[$CloseViewer], popupDoc: "Close the Viewer" ]] ] ]] ] ]]; <> <<[name: $windowDebugMenu, >> <> <> <> <> <> <<["+Debug", FALSE, NIL, LIST[>> <<[LIST[all],LIST[$IncreaseDebug],"Increase debugging level", "", NIL,NIL,NIL] >> <<] ],>> <<["-Debug", FALSE, NIL, LIST[>> <<[LIST[all],LIST[$DecreaseDebug],"Decrease debugging level", "", NIL,NIL,NIL] >> <<] ],>> <<["NoBug", FALSE, NIL, LIST[>> <<[LIST[all],LIST[$NoDebug],"Turn Off Debugging", "", NIL,NIL,NIL] >> <<] ]>> <<]>> <<];>> <<>> ViewerMenuNotifier: ViewerClasses.NotifyProc = { <<[self: Viewer, input: LIST OF REF ANY]>> FOR current: LIST OF REF ANY _ input, current.rest UNTIL current = NIL DO SELECT current.first FROM $DestroyViewer => Destroy[self]; $CloseViewer => ViewerOps.CloseViewer[self]; $GrowViewer => ViewerOps.GrowViewer[self]; $MoveViewerToTop => ViewerOps.TopViewer[self]; $AdjustViewer => ViewerOps.Adjust[self]; $MoveViewerToLeftColumn => ViewerOps.ChangeColumn[self, left]; $MoveViewerToRightColumn => ViewerOps.ChangeColumn[self, right]; $MoveViewerToColorColumn => MoveToColorColumn[viewer: self, doGrow: FALSE]; $MoveViewerToColorColumnAndGrow => MoveToColorColumn[viewer: self, doGrow:TRUE]; <<$IncreaseDebug => MenusPrivate.AlterDebuggingLevel[amount: 1, relative: TRUE];>> <<$DecreaseDebug => MenusPrivate.AlterDebuggingLevel[amount: -1, relative: TRUE];>> <<$NoDebug => MenusPrivate.AlterDebuggingLevel[amount: 0, relative: FALSE];>> ENDCASE => ERROR; ENDLOOP; }; Destroy: PROC[viewer: Viewer] = { IF ~viewer.inhibitDestroy THEN ViewerOps.DestroyViewer[viewer] ELSE {MessageWindow.Append["Sorry, this viewer can not be destroyed.", TRUE]; MessageWindow.Blink[]}; }; MoveToColorColumn: PRIVATE PROC[viewer: Viewer, doGrow: BOOL _ FALSE] = { IF colorDisplayOn THEN { ViewerOps.ChangeColumn[viewer, color]; IF doGrow THEN ViewerOps.GrowViewer[viewer]} ELSE { MessageWindow.Append["Sorry, the color display is not available.", TRUE]; MessageWindow.Blink[]; }; }; BuildWindowMenus[]; END.