WindowManagerImpl.mesa; Written by S. McGregor
Edited by McGregor on August 16, 1983 4:49 pm
Last Edited by: Maxwell, February 8, 1983 3:58 pm
Last Edited by: Pausch, August 17, 1983 5:05 pm
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, ClipIntRectangle, Color, Context, DoSave, MaskIntRectangle, IntScaleT, SetColor, IntTranslateT, white],
Menus,
MenusPrivate USING [AlterDebuggingLevel, ClearMenu, DrawMenu, HitMenu, MarkMenu, ReComputeWindowMenus, ViewerlessAddMenu, ViewerMenus],
MessageWindow USING [Append, Blink],
TIPUser USING [TIPScreenCoords, TIPScreenCoordsRec],
UserTerminal USING [BlinkDisplay],
ViewerClasses,
ViewerOps USING [AcquireContext, Adjust, ChangeColumn, CloseViewer, DestroyViewer, EnumerateViewers, EnumProc, GrowViewer, InitialiseColorPainting, InvisiblePaint, MouseInViewer, PaintViewer, ReleaseContext, TopViewer],
ViewerSpecs USING [captionHeight, scrollBarW, windowBorderSize],
WindowManager,
WindowManagerPrivate;
WindowManagerImpl: CEDAR MONITOR
IMPORTS Carets, ColorWorld, Cursors, Imager, InputFocus, InterminalExtra, WindowManager, Menus, MenusPrivate,
MessageWindow, ViewerOps, UserTerminal
EXPORTS WindowManager, WindowManagerPrivate
SHARES InputFocus, ViewerClasses, ViewerOps =
BEGIN OPEN Cursors, ViewerClasses, ViewerSpecs;
Zone: TYPE = {none, menu, scroll, caption, client} ;
trackingState: Zone ← none;
CursorZone: PROC [v: Viewer, mousePos: TIPUser.TIPScreenCoords] RETURNS [z: Zone] = {
OPEN v;
m: MenusPrivate.ViewerMenus ← NARROW[v.menus];
RETURN[SELECT TRUE FROM
parent=NIL AND column#static AND mousePos.mouseY IN [wh-captionHeight..wh] => caption,
scrollable AND mousePos.mouseX IN [0..scrollBarW] AND mousePos.mouseY IN [0..ch] => scroll,
m#NIL AND mousePos.mouseX IN [m.x..m.x+m.w] AND mousePos.mouseY IN [m.y..m.y+m.h] => menu,
ENDCASE => none
]
};
ProcessWindowResults: PUBLIC PROC [self: Viewer, input: LIST OF REF ANY] = BEGIN
not monitored since notifier is synchronous
zone: Zone;
newZone: BOOL;
shift, control: BOOLFALSE;
mousePos: TIPUser.TIPScreenCoords;
FOR l: LIST OF REF ANY ← input, l.rest UNTIL l = NIL DO WITH l.first SELECT FROM
z: ATOM => BEGIN OPEN InputFocus;
IF newZone THEN SELECT trackingState FROM-- old feedback
scroll  => {ReleaseButtons[]; RemoveScrollFeedback[]};
menu  => {ReleaseButtons[]; MenusPrivate.ClearMenu[NARROW[feedbackViewer.menus],
feedbackViewer]; feedbackViewer ← NIL};
caption => {ReleaseButtons[]; RemoveCaptionFeedback[]};
ENDCASE;
IF newZone THEN SELECT zone FROM-- first time stuff
scroll  => {CaptureButtons[ProcessWindowResults, WindowManagerTIPTable,
self];
PostScrollFeedback[self]};
menu  => {CaptureButtons[ProcessWindowResults, WindowManagerTIPTable,
self];
feedbackViewer ← self;
SetC[bullseye]};
caption => {CaptureButtons[ProcessWindowResults, WindowManagerTIPTable,
self];
PostCaptionFeedback[self];
SetC[bullseye]};
ENDCASE => RestoreCursor[];
SELECT trackingState ← zone FROM-- zone specific ops
scroll  => SELECT z FROM
$M   => SetC[scrollUpDown];
$RU   => HitScroll[self, mousePos.mouseY, up, shift, control];
$RD, $RM => SetC[scrollUp];
$YU   => HitScroll[self, mousePos.mouseY, thumb, shift, control];
$YD, $YM => SetC[scrollRight];
$BU   => HitScroll[self, 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[self.menus],
self, mousePos, IF shift THEN shiftleftup ELSE leftup];
$YU   => MenusPrivate.HitMenu[NARROW[self.menus],
self, mousePos, IF shift THEN shiftmiddleup ELSE middleup];
$BU   => MenusPrivate.HitMenu[NARROW[self.menus],
self, mousePos, IF shift THEN shiftrightup ELSE rightup];
$RM, $BM, $YM
=> {MenusPrivate.MarkMenu[NARROW[self.menus],
self, mousePos]};
$RD, $BD, $YD
=> MenusPrivate.MarkMenu[NARROW[self.menus],
self, mousePos];
$Control  => control ← TRUE;
$Shift   => shift ← TRUE;
ENDCASE  => NULL;
caption  => SELECT z FROM
$RU   => MenusPrivate.HitMenu[windowMenus,
self, mousePos, IF shift THEN shiftleftup ELSE leftup];
$YU   => MenusPrivate.HitMenu[windowMenus,
self, growPos, IF shift THEN shiftmiddleup ELSE middleup];
$BU   => MenusPrivate.HitMenu[windowMenus,
self, closePos, IF shift THEN shiftrightup ELSE rightup];
$RM, $RD => MenusPrivate.MarkMenu[windowMenus,
self, mousePos];
$BM, $BD => MenusPrivate.MarkMenu[windowMenus,
self, closePos];
$YM, $YD => MenusPrivate.MarkMenu[windowMenus,
self, growPos];
$Control  => control ← TRUE;
$Shift   => shift ← TRUE;
ENDCASE  => NULL;
ENDCASE;
END;
z: TIPUser.TIPScreenCoords => {
client: BOOLFALSE;
mousePos ← z;
IF mousePos.mouseX = 0 --AND trackingState = scroll-- THEN mousePos.mouseX ← 1;
IF trackingState#none THEN [self, client] ← ViewerOps.MouseInViewer[mousePos];
zone ← IF client OR self=NIL THEN none ELSE CursorZone[self, mousePos];
newZone ← trackingState#zone OR (zone=caption AND self#feedbackViewer)
OR (zone=scroll AND self#feedbackViewer);
};
z: REF CHAR => TRUSTED {UserTerminal.BlinkDisplay[]};
ENDCASE => NULL;
ENDLOOP;
END;
AlterColumn: PROC [v: Viewer, mx: INTEGER] = BEGIN
column: ViewerClasses.Column;
right: BOOL ~ mx >= v.ww/2;
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];
END;
SetC: PROC [cursor: CursorType] = INLINE
{IF waitCount=0 AND GetCursor[]#cursor THEN SetCursor[cursor]};
HitScroll: PROC [v: Viewer, y: LONG INTEGER, op: ScrollOp, shift, control: BOOL] = BEGIN
RemoveScrollFeedback[];
IF v.parent=NIL OR v.class.coordSys#top THEN 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];
SetC[scrollUpDown]; -- put the cursor back
PostScrollFeedback[v];
END;
feedbackViewer: Viewer ← NIL;
visible: REF CARDINAL = NEW[CARDINAL ← 122645B];
inVisible: REF CARDINAL = NEW[CARDINAL ← 100040B];
PostScrollFeedback: PROC [v: Viewer] = BEGIN
context: Imager.Context;
baseY, baseX, top, bottom: INTEGER;
relY1, relY2: INT; -- so won't overflow
IF feedbackViewer#NIL OR v.class.scroll=NIL THEN RETURN;
IF ~v.init THEN RETURN; -- avoid a race condition bug
[top, bottom] ← v.class.scroll[v, query, 0];
IF top>100 OR bottom>100 THEN RETURN;
Carets.SuspendCarets[];
context ← ViewerOps.AcquireContext[v.parent, v.column=color
! ViewerOps.InvisiblePaint => GOTO Punt];
IF v.parent#NIL AND v.parent.class.coordSys=top THEN BEGIN-- flip origin
Imager.IntTranslateT[context, 0, v.parent.ch];
Imager.IntScaleT[context, 1, -1];
baseY ← v.parent.ch - v.wy - v.wh + (IF v.border THEN windowBorderSize ELSE 0);
END
ELSE baseY ← v.wy + (IF v.border THEN windowBorderSize ELSE 0);
baseX ← v.wx + (IF v.border THEN windowBorderSize ELSE 0);
relY1 ← baseY;
relY2 ← (LONG[100-bottom]*v.ch)/100+baseY;
Imager.SetColor[context, inVisible];
Imager.MaskIntRectangle[context, [baseX, relY1, scrollBarW, relY2-relY1]];
relY1 ← relY2;
relY2 ← relY2 + (LONG[bottom-top]*v.ch)/100;
Imager.SetColor[context, visible];
Imager.MaskIntRectangle[context, [baseX, relY1, scrollBarW, relY2-relY1]];
Imager.SetColor[context, inVisible];
Imager.MaskIntRectangle[context, [baseX, relY2, scrollBarW, baseY+v.ch-relY2]];
ViewerOps.ReleaseContext[context];
feedbackViewer ← v; Carets.ResumeCarets[];
EXITS Punt => {feedbackViewer ← NIL; Carets.ResumeCarets[]};
END;
RemoveScrollFeedback: PROC = BEGIN
context: Imager.Context;
baseX, baseY: INTEGER;
IF feedbackViewer=NIL THEN RETURN;
Carets.SuspendCarets[];
context ← ViewerOps.AcquireContext[feedbackViewer.parent, feedbackViewer.column=color
! ViewerOps.InvisiblePaint => GOTO Punt];
IF feedbackViewer.parent#NIL AND feedbackViewer.parent.class.coordSys=top THEN BEGIN
flip origin
Imager.IntTranslateT[context, 0, feedbackViewer.parent.ch];
Imager.IntScaleT[context, 1, -1];
baseY ← feedbackViewer.parent.ch - feedbackViewer.wy - feedbackViewer.wh +
(IF feedbackViewer.border THEN windowBorderSize ELSE 0);
END
ELSE baseY ← feedbackViewer.wy + (IF feedbackViewer.border THEN windowBorderSize ELSE 0);
baseX ← feedbackViewer.wx + (IF feedbackViewer.border THEN windowBorderSize ELSE 0);
Imager.SetColor[context, Imager.white];
Imager.MaskIntRectangle[context, [baseX, baseY, scrollBarW, feedbackViewer.ch]];
ViewerOps.ReleaseContext[context];
GOTO Punt;
EXITS Punt => {feedbackViewer ← NIL; Carets.ResumeCarets[]};
END;
DrawCaptionMenu: PUBLIC ENTRY PROC [v: Viewer, guard: BOOL] =
{ENABLE UNWIND => NULL; InternalDrawCaptionMenu[v, guard]};
InternalDrawCaptionMenu: INTERNAL PROC [v: Viewer, guard: BOOL] = BEGIN OPEN v;
context: Imager.Context;
DrawCaption: PROC = BEGIN
x, y: INTEGER;
x ← wx+windowBorderSize;
y ← wy+wh-captionHeight;
Imager.ClipIntRectangle[context, [x, y, ww-(2*windowBorderSize),captionHeight]];
Imager.SetColor[context, Imager.white];
Imager.MaskIntRectangle[context, [x, y, ww, captionHeight]];
Imager.SetColor[context, Imager.black];
IF guard THEN MenusPrivate.ReComputeWindowMenus[v, guardDestroy OR (newVersion AND link=NIL AND ~saveInProgress), colorDisplayOn]
ELSE MenusPrivate.ReComputeWindowMenus[v, FALSE, colorDisplayOn];
MenusPrivate.DrawMenu[v, windowMenus, context];
END;
IF feedbackViewer#v OR iconic OR ~visible THEN RETURN;
context ← ViewerOps.AcquireContext[parent, column=color
! ViewerOps.InvisiblePaint => GOTO Punt];
Imager.DoSave[context, DrawCaption];
ViewerOps.ReleaseContext[context];
EXITS Punt => NULL;
END;
PostCaptionFeedback: ENTRY PROC [v: Viewer] = BEGIN
ENABLE UNWIND => NULL;
IF feedbackViewer#v THEN BEGIN-- remove old
IF feedbackViewer#NIL THEN
MenusPrivate.ClearMenu[windowMenus, feedbackViewer, FALSE];
feedbackViewer ← v;
END;
InternalDrawCaptionMenu[v, TRUE];
END;
RemoveCaptionFeedback: ENTRY PROC = BEGIN
ENABLE UNWIND => NULL;
IF feedbackViewer#NIL THEN BEGIN
MenusPrivate.ClearMenu[windowMenus, feedbackViewer, FALSE];
ViewerOps.PaintViewer[feedbackViewer, caption];
feedbackViewer ← NIL;
END;
END;
waitCount: PUBLIC INTEGER ← 0;
WaitCursor: PUBLIC ENTRY PROC [cursor: Cursors.CursorType ← hourGlass] = BEGIN
ENABLE UNWIND => NULL;
IF InputFocus.inputEnabled THEN SetCursor[cursor];
waitCount ← waitCount + 1;
END;
UnWaitCursor: PUBLIC ENTRY PROC = BEGIN
ENABLE UNWIND => NULL;
waitCount ← MAX[0, waitCount - 1];
IF waitCount=0 THEN RestoreCursor[];
END;
RestoreCursor: PUBLIC PROC = TRUSTED
{IF InputFocus.inputEnabled THEN InterminalExtra.InsertAction[[contents: deltaMouse[[0,0]]]]};
StartColorViewers: PUBLIC PROC [screenPos: WindowManager.ScreenPos,
bitsPerPixel: CARDINAL] = BEGIN
IF colorDisplayOn THEN StopColorViewers[];
colorDisplayOn ← ColorWorld.TurnOnColor[bitsPerPixel, (screenPos=left)];
IF ~colorDisplayOn THEN BEGIN
MessageWindow.Append["Sorry, you don't have a color display.", TRUE];
MessageWindow.Blink[];
RETURN;
END;
ViewerOps.InitialiseColorPainting[];
END;
StopColorViewers: PUBLIC PROC = BEGIN
DoColorViewer: ViewerOps.EnumProc = BEGIN
IF v.column=color THEN BEGIN
ViewerOps.CloseViewer[v];
ViewerOps.ChangeColumn[v, left];
END;
END;
IF ~colorDisplayOn THEN RETURN;
ViewerOps.EnumerateViewers[DoColorViewer];
ColorWorld.TurnOffColor[];
colorDisplayOn ← FALSE;
END;
BuildWindowMenus: PROC = BEGIN
Menus.RegisterMenu[windowDestroyMenu];
Menus.RegisterMenu[windowGuardedDestroyMenu];
Menus.RegisterMenu[windowMovementMenu];
Menus.RegisterMenu[windowColorMenu];
Menus.RegisterMenu[windowSizeMenu];
Menus.RegisterMenu[windowDebugMenu];
MenusPrivate.ViewerlessAddMenu["windowDestroyMenu"];
MenusPrivate.ViewerlessAddMenu["windowGuardedDestroyMenu"];
MenusPrivate.ViewerlessAddMenu["windowMovementMenu"];
MenusPrivate.ViewerlessAddMenu["windowColorMenu"];
MenusPrivate.ViewerlessAddMenu["windowSizeMenu"];
MenusPrivate.ViewerlessAddMenu["windowDebugMenu"];
we have to actually instantiate these pointers to start pointing at SOMETHING
growPos ← NEW[TIPUser.TIPScreenCoordsRec ← [0, FALSE, 0]];
closePos ← NEW[TIPUser.TIPScreenCoordsRec ← [0, FALSE, 0]];
END;
colorDisplayOn: PUBLIC BOOLFALSE; -- color display status
windowMenus: PUBLIC MenusPrivate.ViewerMenus;
growPos: PUBLIC TIPUser.TIPScreenCoords;
closePos: PUBLIC TIPUser.TIPScreenCoords;
windowDestroyMenu: Menus.Menu =
[name: "windowDestroyMenu",
beginsActive: TRUE,
breakBefore: FALSE,
breakAfter: FALSE,
notify: ViewerMenuNotifier,
entries: LIST[
["Destroy", FALSE, NIL, LIST [
[LIST[all],LIST[$DestroyViewer],"Destroy the Viewer", "", NIL,NIL,NIL]
] ]
]
];
windowGuardedDestroyMenu: Menus.Menu =
[name: "windowGuardedDestroyMenu",
beginsActive: TRUE,
breakBefore: FALSE,
breakAfter: FALSE,
notify: ViewerMenuNotifier,
entries: LIST[
["Destroy", TRUE, NIL, LIST [
[LIST[all],LIST[$DestroyViewer],"Destroy the Viewer", "Edits will be discarded...", NIL,NIL,NIL]
] ]
]
];
windowMovementMenu: Menus.Menu =
[name: "windowMovementMenu",
beginsActive: TRUE,
breakBefore: FALSE,
breakAfter: FALSE,
notify: ViewerMenuNotifier,
entries: LIST[
["Adjust", FALSE, NIL, LIST[
[LIST[all], LIST[$AdjustViewer], "Adjust Viewer Size", "", NIL, NIL, NIL]
] ],
["Top", FALSE, NIL, LIST[
[LIST[all], LIST[$MoveViewerToTop], "Move Viewer To Top Of Column", "", NIL, NIL, NIL]
] ],
["Left", FALSE, "—", LIST[
[LIST[shiftleftup, shiftmiddleup, shiftrightup],
LIST[$MoveViewerToLeftColumn, $GrowViewer], "Move To Left Column and Grow ", "", NIL, NIL, NIL],
[LIST[all], LIST[$MoveViewerToLeftColumn], "Move Viewer To Left Column", "", NIL, NIL, NIL]
] ],
["Right", FALSE, "—", LIST[
[LIST[shiftleftup, shiftmiddleup, shiftrightup],
LIST[$MoveViewerToRightColumn, $GrowViewer], "Move To Right Column and Grow ", "", NIL, NIL, NIL],
[LIST[all], LIST[$MoveViewerToRightColumn], "Move Viewer To Right Column", "", NIL, NIL, NIL]
] ]
]
];
windowColorMenu: Menus.Menu =
[name: "windowColorMenu",
beginsActive: TRUE,
breakBefore: FALSE,
breakAfter: FALSE,
notify: ViewerMenuNotifier,
entries: LIST[
["Color", FALSE, NIL, LIST[
[LIST[shiftleftup, shiftmiddleup, shiftrightup],
LIST[$MoveViewerToColorColumnAndGrow], "Move To Color Display and Grow ", "", NIL, NIL, NIL],
[LIST[all], LIST[$MoveViewerToColorColumn], "Move Viewer To Color Display", "", NIL, NIL, NIL]
] ]
]
];
windowSizeMenu: Menus.Menu =
[name: "windowSizeMenu",
beginsActive: TRUE,
breakBefore: FALSE,
breakAfter: FALSE,
notify: ViewerMenuNotifier,
entries: LIST[
["Grow", FALSE, NIL, LIST[
[LIST[all],LIST[$GrowViewer],"Grow the Viewer", "", NIL,NIL,NIL]
] ],
["Close", FALSE, NIL, LIST[
[LIST[all],LIST[$CloseViewer],"Close the Viewer", "", NIL,NIL,NIL]
] ]
]
];
windowDebugMenu: Menus.Menu =
[name: "windowDebugMenu",
beginsActive: TRUE,
breakBefore: FALSE,
breakAfter: FALSE,
notify: ViewerMenuNotifier,
entries: LIST[
["+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: ViewerClasses.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: ViewerClasses.Viewer, doGrow: BOOLFALSE] = {
IF WindowManager.colorDisplayOn THEN {
ViewerOps.ChangeColumn[viewer, color];
IF doGrow THEN ViewerOps.GrowViewer[viewer]}
ELSE BEGIN
MessageWindow.Append["Sorry, the color display is not available.", TRUE];
MessageWindow.Blink[];
END;
};
BuildWindowMenus[];
END.