ViewerOpsImplA.mesa; Written by S. McGregor
Edited by McGregor on July 28, 1983 5:49 pm
Edited by Plass, May 4, 1983 11:27 am
Last Edited by: Maxwell, June 3, 1983 2:46 pm
Last Edited by: Pausch, August 22, 1983 4:30 pm
Last Edited by: Wyatt, November 10, 1983 3:03 pm
DIRECTORY
Carets USING [ResumeCarets, SuspendCarets],
ColorDisplay USING [height, width],
InputFocus USING [GetInputFocus, SetInputFocus],
List USING [Sort, CompareProc],
Menus USING [Menu, SetMenu],
MenusPrivate USING [EstablishHeader],
MessageWindow USING [Append, Blink, Clear],
Process USING [Detach],
Rope USING [ROPE],
ViewerBLT USING [InternalPaintColumn],
ViewerEvents USING [EventProc, ProcessEvent, RegisterEventProc],
ViewerOps,
ViewerLocks,
ViewerClasses,
ViewerSpecs,
WindowManager USING [colorDisplayOn],
WindowManagerPrivate;
ViewerOpsImplA: CEDAR PROGRAM
IMPORTS Carets, ColorDisplay, InputFocus, List, Menus, MenusPrivate, MessageWindow, Process, ViewerBLT, ViewerEvents, ViewerLocks, ViewerOps, WindowManager
EXPORTS ViewerOps, ViewerSpecs, WindowManagerPrivate
SHARES ViewerClasses, ViewerEvents, ViewerOps, ViewerLocks
= BEGIN OPEN ViewerClasses, ViewerSpecs, ViewerOps, WindowManager;
ErrorType: TYPE = {ok, classNotImplemented, illegalPositioning, nilViewer, notTopLevel, nonSequencedColumn, cantReplace, cantSwap};
fatalViewersError: ERROR[error: ErrorType] = CODE;
openLeftWidth: PUBLIC INTEGER ← initialOpenLeftWidth; -- RFP changed from simply ← 600
openRightLeftX: PUBLIC INTEGER ← openLeftLeftX+openLeftWidth;
openRightWidth: PUBLIC INTEGER ← screenW-openLeftWidth;
openBottomY: PUBLIC INTEGER ← iconHeight+iconSpacing;
rootViewerTree: PUBLIC ARRAY Column OF Viewer ← ALL[NIL];
CreateViewer: PUBLIC PROC [flavor: ViewerFlavor, info: ViewerRec ← [], paint: BOOLTRUE]
RETURNS [new: Viewer] = {
topLevel: BOOL;
defaultPosition: BOOL;
error: ErrorType ← ok;
LockedCreateViewer: PROC = {
class: ViewerClasses.ViewerClass = FetchViewerClass[flavor];
IF class=NIL THEN { error ← classNotImplemented; RETURN };
defaultPosition ← (new.wx=0) AND (new.wy=0) AND (new.ww=0) AND (new.wh=0);
IF new.parent=NIL AND ~defaultPosition AND new.column#static THEN
{ error ← illegalPositioning; RETURN };
new.class ← class;
topLevel ← (new.parent=NIL);
new.scrollable ← new.scrollable AND (class.scroll#NIL);
IF new.tipTable=NIL THEN new.tipTable ← class.tipTable;
IF new.icon=unInit THEN new.icon ← class.icon;
IF topLevel THEN new.iconic ← (new.iconic AND new.column#static AND new.column#color
AND iconSlotsFree#0) -- override if illegal or too many
ELSE {
new.iconic ← FALSE;
new.visible ← ~new.parent.iconic;
new.column ← new.parent.column;
};
IF topLevel THEN new.border ← TRUE;
WITH new.class.menu SELECT FROM
menu: Menus.Menu => Menus.SetMenu[new, menu, FALSE];
ENDCASE;
IF topLevel THEN LinkViewer[new, ~new.iconic] ELSE LinkChild[new.parent, new];
IF ~defaultPosition THEN SetViewerPosition[new, new.wx, new.wy, new.ww, new.wh];
IF new.class.init#NIL THEN new.class.init[new];
IF topLevel THEN IF new.iconic THEN PositionIcon[new]
ELSE IF defaultPosition THEN InternalComputeColumn[new.column, paint];
new.init ← TRUE};
new ← NEW[ViewerRec ← info];
IF ViewerEvents.ProcessEvent[create, new, TRUE] THEN RETURN[NIL]; -- Gulp!!!
IF ViewerLocks.ColumnWedged[ViewerColumn[new]].wedged THEN {
not locked, but these are not ordinary times!
column: Column ← UnWedgedColumn[];
IF column # static THEN new.column ← column};
IF new.parent=NIL
THEN IF new.iconic
THEN ViewerLocks.CallUnderColumnLock[LockedCreateViewer, static]
ELSE ViewerLocks.CallUnderColumnLock[LockedCreateViewer, new.column]
ELSE ViewerLocks.CallUnderWriteLock[LockedCreateViewer, new.parent];
IF error#ok THEN ERROR fatalViewersError[error];
IF paint AND (~topLevel OR ~defaultPosition OR new.iconic) THEN PaintViewer[new, all];
[] ← ViewerEvents.ProcessEvent[create, new, FALSE];
};
DestroyViewer: PUBLIC PROC [viewer: Viewer, paint: BOOLTRUE] = {
ENABLE UNWIND => NULL;
IF viewer.destroyed THEN RETURN; -- already destroyed ; nop
IF viewer=NIL THEN ERROR fatalViewersError[nilViewer];
InternalDestroyViewer[viewer, paint, FALSE];
};
InternalDestroyViewer: PROC [viewer: Viewer, paint, replace: BOOL] = {
parent: Viewer;
x,y,w,h: INTEGER;
onlyLink: Viewer; -- set only if the only link
paintParent, paintColumn: BOOLFALSE;
LockedDestroyViewer: PROC = {
parent ← viewer.parent; -- copy parent; smashed below
x ← viewer.wx; y ← viewer.wy; w ← viewer.ww; h ← viewer.wh;
KillInputFocus[viewer];
BottomUpRecursivelyDestroyViewerAndChildren[viewer];
IF viewer.link#NIL THEN {
IF viewer.link.link=viewer THEN {
viewer.link.link ← NIL;
IF viewer.link.parent=NIL THEN onlyLink ← viewer.link;
}
ELSE FOR v: Viewer ← viewer.link.link, v.link UNTIL v.link=viewer DO
REPEAT FINISHED => v.link ← viewer.link;
ENDLOOP;
viewer.link ← NIL;
};
IF parent#NIL THEN {
IF parent.child=viewer THEN parent.child ← viewer.sibling
ELSE IF viewer.sibling#NIL OR parent#viewer THEN
FOR test: Viewer ← parent.child, test.sibling UNTIL test=NIL DO
IF test.sibling#NIL AND test.sibling=viewer THEN test.sibling ← test.sibling.sibling;
ENDLOOP;
viewer.sibling ← NIL;
paintParent ← paint;
}
ELSE IF viewer.iconic THEN {
GreyScreen[x, y, w, h, FALSE, TRUE]; -- erase the icon
IF replace THEN RETURN; -- used by ReplaceViewer to prevent the slot from being reused
RemoveIcon[icon: viewer, fillSlot: TRUE];
}
ELSE InternalComputeColumn[viewer.column, paint];
};
IF ViewerEvents.ProcessEvent[destroy, viewer, TRUE] THEN RETURN;
KillInputFocus[viewer];
ViewerLocks.CallUnderWriteLock[LockedDestroyViewer, viewer];
IF paintParent THEN PaintViewer[parent, all];
the following are always painted because the caller isn't likely to know to repaint them
IF onlyLink#NIL THEN PaintViewer[onlyLink, caption];
[] ← ViewerEvents.ProcessEvent[destroy, viewer, FALSE];
};
BottomUpRecursivelyDestroyViewerAndChildren: PROC [viewer: Viewer] = {
LockedBottomUpEtc: PROC = {
child, next: Viewer;
viewer.visible ← FALSE;
viewer.destroyed ← TRUE;
IF viewer.class.destroy#NIL THEN viewer.class.destroy[viewer];
child ← viewer.child;
WHILE child # NIL DO
[] ← ViewerEvents.ProcessEvent[destroy, child, TRUE]; -- what would abort mean?
BottomUpRecursivelyDestroyViewerAndChildren[child];
[] ← ViewerEvents.ProcessEvent[destroy, child, FALSE];
next ← child.sibling;
child.sibling ← NIL;
child ← next;
ENDLOOP;
IF viewer.parent=NIL THEN UnlinkViewer[viewer];
help out the garbage collector (.sibling handled by caller and above)
viewer.parent ← NIL; viewer.name ← NIL;
viewer.menu ← NIL; viewer.props ← NIL; viewer.child ← NIL;
viewer.data ← NIL;
};
ViewerLocks.CallUnderWriteLock[LockedBottomUpEtc, viewer];
};
TopViewer: PUBLIC PROC [viewer: Viewer, paint: BOOLTRUE] = {
error: ErrorType ← ok;
LockedTopViewer: PROC = {
noop: BOOLFALSE;
myTopY: INTEGER = viewer.wy+viewer.wh;
IF viewer.parent#NIL THEN { error ← notTopLevel; RETURN };
SELECT viewer.column FROM
left  => IF myTopY = openLeftTopY THEN noop ← TRUE;
right  => IF myTopY = openRightTopY THEN noop ← TRUE;
color  => IF myTopY = LOOPHOLE[ColorDisplay.height, INTEGER] THEN noop ← TRUE;
ENDCASE => { error ← nonSequencedColumn; RETURN }; -- not a sequenced column
IF noop THEN {paint ← FALSE; RETURN};
UnlinkViewer[viewer];
LinkViewer[viewer, FALSE];
InternalComputeColumn[viewer.column, paint]};
IF viewer.offDeskTop THEN ChangeColumn[viewer, left];
ViewerLocks.CallUnderColumnLock[LockedTopViewer, viewer.column];
IF error#ok THEN ERROR fatalViewersError[error];
};
BottomViewer: PUBLIC PROC [viewer: Viewer, paint: BOOLTRUE] = {
error: ErrorType ← ok;
LockedBottomViewer: PROC = {
noop: BOOLFALSE;
IF viewer.parent#NIL THEN { error ← notTopLevel; RETURN };
SELECT viewer.column FROM
left, right => IF viewer.wy = openBottomY THEN noop ← TRUE;
color   => IF viewer.wy = 0 THEN noop ← TRUE;
ENDCASE => { error ← nonSequencedColumn; RETURN }; -- not a sequenced column
IF noop THEN {paint ← FALSE; RETURN};
UnlinkViewer[viewer];
LinkViewer[viewer, TRUE];
InternalComputeColumn[viewer.column, paint]};
IF viewer.offDeskTop THEN ChangeColumn[viewer, left];
ViewerLocks.CallUnderColumnLock[LockedBottomViewer, viewer.column];
IF error#ok THEN ERROR fatalViewersError[error];
};
MoveAboveViewer: PUBLIC PROC [altered, static: Viewer, paint: BOOLTRUE] = {
LockedMoveAboveViewer: PROC = {
IF altered.parent#NIL OR static.parent#NIL THEN RETURN;
IF altered.column#static.column OR static.iconic THEN RETURN;
UnlinkViewer[altered];
altered.sibling ← static.sibling;
static.sibling ← altered;
InternalComputeColumn[altered.column, paint];
};
IF static.offDeskTop THEN ChangeColumn[static, left];
IF altered.offDeskTop THEN ChangeColumn[altered, left];
ViewerLocks.CallUnderColumnLock[LockedMoveAboveViewer, altered.column];
};
MoveBelowViewer: PUBLIC PROC [altered, static: Viewer, paint: BOOLTRUE] = {
LockedMoveBelowViewer: PROC = {
IF altered.parent#NIL OR static.parent#NIL THEN RETURN;
IF altered.column#static.column OR static.iconic THEN RETURN;
UnlinkViewer[altered];
IF static=rootViewerTree[altered.column] THEN {
altered.sibling ← static;
rootViewerTree[altered.column] ← altered;
}
ELSE {
inFront: Viewer;
FOR v: Viewer ← rootViewerTree[altered.column], v.sibling UNTIL v.sibling=static DO
REPEAT FINISHED => inFront ← v;
ENDLOOP;
altered.sibling ← static;
inFront.sibling ← altered;
};
InternalComputeColumn[altered.column, paint];
};
IF static.offDeskTop THEN ChangeColumn[static, left];
IF altered.offDeskTop THEN ChangeColumn[altered, left];
ViewerLocks.CallUnderColumnLock[LockedMoveBelowViewer, altered.column];
};
ReplaceViewer: PUBLIC PROC [new, old: Viewer] = {
error: ErrorType ← ok;
LockedReplaceViewer: PROC = {
IF old.parent#NIL OR new.parent#NIL OR new.column#old.column THEN
{ error ← cantReplace; RETURN }; -- must be same level and column
KillInputFocus[old];
UnlinkViewer[new];
new.sibling ← old.sibling;
old.sibling ← new;
new.iconic ← old.iconic;
new.position ← old.position;
new.openHeight ← old.openHeight;
new.column ← old.column;
new.offDeskTop ← old.offDeskTop;
new.visible ← old.visible;
SetViewerPosition[new, old.wx, old.wy, old.ww, old.wh];
InternalDestroyViewer[old, FALSE, TRUE]; -- doesn't free the icon slot
IF ~new.iconic THEN InternalComputeColumn[new.column];
};
KillInputFocus[old];
IF old.offDeskTop THEN ChangeColumn[old, left];
IF new.offDeskTop THEN ChangeColumn[new, left];
ViewerLocks.CallUnderColumnLock[LockedReplaceViewer, ViewerColumn[old]];
IF error#ok THEN ERROR fatalViewersError[error];
IF new.iconic THEN ViewerOps.PaintViewer[new, all];
};
MoveBoundary: PUBLIC PROC [newLeftWidth: INTEGER ← openLeftWidth,
newBottomY: INTEGER ← openBottomY] = {
ENABLE UNWIND => NULL;
LockedMoveBoundary: PROC = {
oldBottomY: INTEGER ← openBottomY;
oldLeftWidth: INTEGER ← openLeftWidth;
ResetPaintCache[];
openBottomY ← newBottomY;
openLeftWidth ← newLeftWidth;
openRightLeftX ← openLeftLeftX+openLeftWidth;
openRightWidth ← screenW-openLeftWidth;
IF oldLeftWidth < newLeftWidth THEN {
InternalComputeColumn[right, TRUE, FALSE];
InternalComputeColumn[left, TRUE, FALSE]}
ELSE {
InternalComputeColumn[left, TRUE, FALSE];
InternalComputeColumn[right, TRUE, FALSE]};
IF newBottomY # oldBottomY THEN { -- repaint icons
GreyScreen[0, 0, screenW, newBottomY, FALSE, TRUE];
FOR v: Viewer ← rootViewerTree[static], v.sibling UNTIL v = NIL DO
IF ~v.iconic OR v.offDeskTop THEN LOOP;
TRUSTED {Process.Detach[FORK PaintViewer[v, all]]};
ENDLOOP};
IF oldLeftWidth < newLeftWidth THEN {
InternalComputeColumn[right, FALSE, FALSE];
InternalComputeColumn[left, FALSE, FALSE]}
ELSE {
InternalComputeColumn[left, FALSE, FALSE];
InternalComputeColumn[right, FALSE, FALSE]};
PaintEverything;
};
Carets.SuspendCarets[];
ViewerLocks.CallUnderViewerTreeLock[LockedMoveBoundary];
WaitForPaintingToFinish[];
Carets.ResumeCarets[];
};
ChangeColumn: PUBLIC PROC [viewer: Viewer, newColumn: Column] = {
ENABLE UNWIND => Carets.ResumeCarets[];
oldColumn: Column;
paint: BOOL = TRUE;
paintIcon: BOOLFALSE;
LockedChangeColumn: PROC = {
SetColumn: EnumProc = {v.column ← viewer.column} ;
oldColumn ← viewer.column;
IF oldColumn = newColumn THEN RETURN;
IF newColumn = static AND ~viewer.iconic THEN CloseViewer[viewer];
-- iconic case
IF viewer.iconic THEN {
IF oldColumn # static THEN {OPEN viewer;
RemoveIcon[viewer];
GreyScreen[wx, wy, ww, wh, column=color, TRUE]};
IF oldColumn # static AND newColumn # static -- not offDeskTop
THEN newColumn ← IF viewer.column=right THEN left ELSE right;
viewer.column ← newColumn;
IF newColumn # static THEN {PositionIcon[viewer]; paintIcon ← TRUE};
};
-- non iconic case
IF ~viewer.iconic THEN {
UnlinkViewer[viewer];
viewer.column ← newColumn;
LinkViewer[viewer];
ResetPaintCache[];
InternalComputeColumn[oldColumn, paint];
InternalComputeColumn[newColumn, paint];
};
-- hyperspace
IF oldColumn = static THEN viewer.offDeskTop ← FALSE;
IF newColumn = static THEN {
SetViewerPosition[viewer, 2000, 2000, 10, 10];
viewer.offDeskTop ← TRUE};
EnumerateChildren[viewer, SetColumn];
};
IF ViewerEvents.ProcessEvent[changeColumn, viewer, TRUE].abort THEN RETURN;
IF ViewerLocks.ColumnWedged[newColumn].wedged THEN {
newColumn ← UnWedgedColumn[];
IF newColumn = static THEN RETURN};
ViewerLocks.CallUnderColumnLocks[LockedChangeColumn,
ViewerColumn[viewer], newColumn];
IF paintIcon THEN PaintViewer[viewer, all];
[] ← ViewerEvents.ProcessEvent[changeColumn, viewer, FALSE];
};
OpenIcon: PUBLIC PROC [icon: Viewer, closeOthers: BOOLFALSE, bottom: BOOLTRUE,
paint: BOOLTRUE] = {
LockedOpenIcon: PROC = {
ENABLE UNWIND => NULL;
SetOpen: EnumProc = {v.visible ← TRUE};
IF ~icon.iconic THEN {paint ← closeOthers ← FALSE; RETURN};
KillInputFocus[icon];
ResetPaintCache[];
closeOthers ← (closeOthers AND (CountViewers[icon.column]#0));
UnlinkViewer[icon];
icon.iconic ← FALSE;
LinkViewer[icon, bottom];
EnumerateChildren[icon, SetOpen];
RemoveIcon[icon: icon, fillSlot: ~closeOthers];
icon.position ← 0;
icon.ww ← icon.wh ← 0; -- so won't be clipped out in GreyScreen below
GreyScreen[icon.wx, icon.wy, iconWidth, iconHeight, FALSE, TRUE];
IF closeOthers THEN GrowViewer[icon, paint];
InternalComputeColumn[icon.column, paint];
IF paint THEN MessageWindow.Clear[]; -- flush old messages
};
IF ViewerEvents.ProcessEvent[open, icon, TRUE].abort THEN RETURN;
IF ViewerLocks.ColumnWedged[icon.column].wedged THEN {
not locked, but these are not ordinary times!
column: Column ← UnWedgedColumn[];
IF column # static THEN icon.column ← column};
KillInputFocus[icon];
IF icon.offDeskTop THEN ChangeColumn[icon, left];
ViewerLocks.CallUnderColumnLocks[LockedOpenIcon, icon.column, static];
[] ← ViewerEvents.ProcessEvent[open, icon, FALSE];
};
CloseViewer: PUBLIC PROC [viewer: Viewer, paint: BOOLTRUE] = {
ENABLE UNWIND => NULL;
LockedCloseViewer: PROC = {
SetClosed: EnumProc = {v.visible ← FALSE};
IF viewer.iconic OR viewer.column=static THEN RETURN;
IF iconSlotsFree=0 THEN {
MessageWindow.Append["No space for another icon! Try deleting some viewers.", TRUE];
MessageWindow.Blink;
RETURN;
};
KillInputFocus[viewer];
ResetPaintCache[];
UnlinkViewer[viewer];
viewer.iconic ← TRUE;
LinkViewer[viewer];
EnumerateChildren[viewer, SetClosed];
PositionIcon[viewer];
InternalComputeColumn[viewer.column, paint];
};
IF ViewerEvents.ProcessEvent[close, viewer, TRUE].abort THEN RETURN;
KillInputFocus[viewer];
IF viewer.offDeskTop THEN ChangeColumn[viewer, left];
ViewerLocks.CallUnderColumnLocks[LockedCloseViewer, viewer.column, static];
IF paint THEN PaintViewer[viewer, all];
[] ← ViewerEvents.ProcessEvent[close, viewer, FALSE];
};
SwapIconAndViewer: PUBLIC PROC [icon, openViewer: Viewer, paint: BOOLTRUE] =
{ -- this isn't locked at all!
IF ~icon.iconic OR openViewer.iconic THEN ERROR fatalViewersError[cantSwap];
KillInputFocus[icon];
KillInputFocus[openViewer];
IF icon.column#openViewer.column THEN ChangeColumn[icon, openViewer.column];
OpenIcon[icon: icon, paint: FALSE];
MoveAboveViewer[icon, openViewer, FALSE];
CloseViewer[openViewer, FALSE];
ComputeColumn[icon.column];
PaintViewer[openViewer, all];
};
GrowViewer: PUBLIC PROC [viewer: Viewer, paint: BOOLTRUE] = {
LockedGrowViewer: PROC = {
v, next: Viewer ← NIL;
v ← rootViewerTree[viewer.column];
WHILE v # NIL DO
next ← v.sibling;
IF v # viewer THEN {
ViewerOps.CloseViewer[v, FALSE]; -- must be under a lock!
IF paint THEN ViewerOps.PaintViewer[v, all]}; -- just paint the icon
v ← next;
ENDLOOP;
InternalComputeColumn[viewer.column, paint]};
IF viewer.iconic THEN {ViewerOps.OpenIcon[viewer, TRUE, paint]; RETURN};
IF ViewerEvents.ProcessEvent[grow, viewer, TRUE].abort THEN RETURN;
IF viewer.offDeskTop THEN ChangeColumn[viewer, left];
FOR v: Viewer ← rootViewerTree[viewer.column], v.sibling WHILE v # NIL DO
IF v # viewer THEN KillInputFocus[v]; -- done here to prevent deadlock
ENDLOOP;
ViewerLocks.CallUnderColumnLocks[LockedGrowViewer, viewer.column, static];
-- you MUST acquire the static column here, since CloseViewer needs it.
[] ← ViewerEvents.ProcessEvent[grow, viewer, FALSE];
};
GrowExtra: ViewerEvents.EventProc = {
[viewer: Viewer, event: ViewerEvent, before: BOOL]
LockedGrowExtra: PROC = {
closedViewers: LIST OF Viewer; -- closed with the last grow
inThisColumn: LIST OF Viewer; -- viewers currently in the column
compare: List.CompareProc = {RETURN[IF NARROW[ref1, Viewer].wy >
NARROW[ref2, Viewer].wy THEN less ELSE greater]};
ListColumn: ViewerOps.EnumProc = {
IF ~v.iconic AND v.column=viewer.column
THEN inThisColumn ← CONS[v, inThisColumn]};
IF before THEN { -- cons up a list of viewers currently in the column
ViewerOps.EnumerateViewers[ListColumn];
TRUSTED {inThisColumn ← LOOPHOLE[List.Sort[LOOPHOLE[inThisColumn], compare]]};
ViewerOps.AddProp[viewer, $InThisColumn, inThisColumn];
IF event = grow THEN RETURN};
closedViewers ← NARROW[ViewerOps.FetchProp[viewer, $ClosedViewers]];
inThisColumn ← NARROW[ViewerOps.FetchProp[viewer, $InThisColumn]];
ViewerOps.AddProp[viewer, $ClosedViewers, inThisColumn];
ViewerOps.AddProp[viewer, $InThisColumn, NIL];
IF inThisColumn = NIL OR inThisColumn.rest # NIL THEN RETURN; -- first grow
FOR v: LIST OF Viewer ← closedViewers, v.rest WHILE v # NIL DO
IF v.first.destroyed THEN NULL
ELSE IF v.first = viewer THEN ViewerOps.BottomViewer[viewer: viewer, paint: FALSE]
ELSE IF v.first.iconic AND v.first.column = viewer.column
THEN ViewerOps.OpenIcon[icon: v.first, paint: FALSE];
ENDLOOP;
ViewerOps.ComputeColumn[viewer.column]};
IF event = destroy AND viewer.parent # NIL THEN RETURN;
IF event = destroy AND (viewer.iconic OR viewer.column = static) THEN RETURN;
ViewerLocks.CallUnderColumnLocks[LockedGrowExtra, viewer.column, static];
};
ComputeColumn: PUBLIC PROC [column: Column, paint: BOOLTRUE] = {
ENABLE UNWIND => NULL;
LockedComputeColumn: PROC = {InternalComputeColumn[column, paint]};
ViewerLocks.CallUnderColumnLock[LockedComputeColumn, column];
};
InternalComputeColumn: PROC [column: Column, paint, icons: BOOLTRUE] = {
monitored access to window chain
force: BOOLFALSE;
largestViewer: Viewer;
totalRequested, requests: INTEGER ← 0;
minHeight: INTEGER ~ captionHeight;
leftX, width, bottomY, nextY: INTEGER;
openViewers: INTEGER = CountViewers[column];
normalHeight, extra, default, totalSpace: INTEGER ← 0;
DesiredHeight: PROC [v: Viewer] RETURNS [INTEGER] = INLINE
{RETURN[--IF v.position#0 THEN v.position ELSE-- v.openHeight]};
AddUpRequests: PROC [cutoff: INTEGER] = {
largestHeight: INTEGER ← 0;
totalRequested ← requests ← 0;
FOR v: Viewer ← rootViewerTree[column], v.sibling UNTIL v=NIL DO
IF DesiredHeight[v] > largestHeight THEN {
largestHeight ← DesiredHeight[v]; largestViewer ← v};
IF DesiredHeight[v]#0 AND DesiredHeight[v] <= cutoff THEN {
totalRequested ← totalRequested + DesiredHeight[v];
requests ← requests + 1;
};
ENDLOOP};
ResetPaintCache[]; -- clients are assuming that this will happen
IF column # static AND openViewers # 0 THEN {
If there are no requests, divide the column evenly among the viewers.
If total requested > column height, ignore requests for more than average height.
If total requested < column height AND requests = viewers, put extra on largest viewer.
[totalSpace, leftX, width, bottomY, nextY] ← ColumnInfo[column];
normalHeight ← totalSpace/openViewers;
AddUpRequests[totalSpace];
IF totalRequested+((openViewers-requests)*minHeight) > totalSpace THEN {
force ← TRUE; AddUpRequests[normalHeight]};
IF openViewers=requests THEN extra ← totalSpace-totalRequested
ELSE {default ← (totalSpace-totalRequested)/(openViewers-requests);
extra ← (totalSpace-totalRequested) MOD (openViewers-requests)};
FOR v: Viewer ← rootViewerTree[column], v.sibling UNTIL v=NIL DO
height: INTEGERSELECT TRUE FROM
DesiredHeight[v]=0 => default,
force AND DesiredHeight[v] > normalHeight => default,
ENDCASE => DesiredHeight[v];
IF openViewers = requests AND largestViewer # NIL
THEN {IF v = largestViewer THEN height ← height + extra}
ELSE {IF nextY=bottomY THEN height ← height + extra};
SetViewerPosition[v, leftX, nextY, width, height];
nextY ← nextY + v.wh;
ENDLOOP};
IF paint THEN ViewerBLT.InternalPaintColumn[column, icons];
};
ColumnInfo: PUBLIC PROC[column: Column]
RETURNS[totalSpace, leftX, width, bottomY, nextY: INTEGER] = {
SELECT column FROM
left => {
totalSpace ← openLeftTopY-openBottomY;
bottomY ← nextY ← openBottomY;
leftX ← openLeftLeftX;
width ← openLeftWidth};
right => {
totalSpace ← openRightTopY-openBottomY;
bottomY ← nextY ← openBottomY;
leftX ← openRightLeftX;
width ← openRightWidth};
color => {
totalSpace ← ColorDisplay.height;
bottomY ← nextY ← 0;
leftX ← 0;
width ← ColorDisplay.width};
ENDCASE => ERROR;
};
iconSlots: CARDINAL = (iconRightX-iconLeftX)/(iconWidth+iconSpacing);
iconSlotsFree: CARDINAL ← iconRows*iconSlots;
iconSlotsArray: ARRAY [0..iconRows*iconSlots) OF BOOLALL[FALSE];
RemoveIcon: PROC[icon: Viewer, fillSlot: BOOLEANFALSE] = {
filler: Viewer;
slot: INTEGER ← 0;
FindSlotIcon: ViewerOps.EnumProc = {
IF ~v.iconic OR v.offDeskTop THEN RETURN[TRUE];
IF v.position = slot THEN {filler ← v; RETURN[FALSE]} ELSE RETURN[TRUE]};
IF icon.offDeskTop THEN RETURN;
iconSlotsFree ← iconSlotsFree + 1;
iconSlotsArray[icon.position] ← FALSE;
IF NOT fillSlot THEN RETURN;
FOR m: INTEGER DECREASING IN ((icon.position/iconSlots)..iconRows) DO
IF icon.column = right THEN FOR n: CARDINAL DECREASING IN [0..iconSlots) DO
slot ← m*iconSlots + n;
IF iconSlotsArray[slot] THEN EXIT;
ENDLOOP
ELSE FOR n: CARDINAL IN [0..iconSlots) DO
slot ← m*iconSlots + n;
IF iconSlotsArray[slot] THEN EXIT;
ENDLOOP;
ENDLOOP;
IF slot = 0 OR ~iconSlotsArray[slot] THEN RETURN;
ViewerOps.EnumerateViewers[FindSlotIcon];
IF filler = NIL THEN RETURN;
GreyScreen[filler.wx, filler.wy, iconWidth, iconHeight, FALSE, TRUE];
iconSlotsArray[filler.position] ← FALSE;
filler.position ← icon.position;
iconSlotsArray[filler.position] ← TRUE;
SetIconPosition[filler];
TRUSTED {Process.Detach[FORK PaintViewer[filler, all]]};
};
PositionIcon: PROC [viewer: Viewer, slot: INTEGER ← 0] = {
slotIcon: Viewer;
FindSlotIcon: ViewerOps.EnumProc = {
IF ~v.iconic OR v.offDeskTop THEN RETURN[TRUE];
IF v.position = slot THEN {slotIcon ← v; RETURN[FALSE]} ELSE RETURN[TRUE]};
FindIconSlot: PROC [side: Column] RETURNS [slot: CARDINAL] = {
FOR m: CARDINAL IN [0..iconRows) DO
IF side=right THEN FOR n: CARDINAL DECREASING IN [0..iconSlots) DO
slot ← m*iconSlots+n;
IF ~iconSlotsArray[slot] THEN RETURN;
ENDLOOP
ELSE FOR n: CARDINAL IN [0..iconSlots) DO
slot ← m*iconSlots+n;
IF ~iconSlotsArray[slot] THEN RETURN;
ENDLOOP;
ENDLOOP;
};
IF slot # 0 AND iconSlotsArray[slot] THEN { -- move the icon in the slot
ViewerOps.EnumerateViewers[FindSlotIcon];
slotIcon.position ← FindIconSlot[slotIcon.column];
iconSlotsArray[slotIcon.position] ← TRUE;
SetIconPosition[slotIcon]};
viewer.position ← IF slot # 0 THEN slot ELSE FindIconSlot[viewer.column];
iconSlotsArray[viewer.position] ← TRUE;
iconSlotsFree ← iconSlotsFree-1;
SetIconPosition[viewer];
};
SetIconPosition: PROC [icon: Viewer] = INLINE {
SetViewerPosition[icon, (icon.position MOD iconSlots)*(iconWidth+iconSpacing)+iconLeftX,
iconBottomY+((icon.position/iconSlots)*(iconHeight+iconSpacing)), iconWidth,
iconHeight];
};
MoveViewer: PUBLIC PROC [viewer: Viewer, x, y, w, h: INTEGER,
paint: BOOLTRUE] = {
ENABLE UNWIND => Carets.ResumeCarets[];
LockedMoveViewer: PROC = {
oldX, oldY, oldW, oldH: INTEGER;
oldX ← viewer.wx; oldY ← viewer.wy; oldW ← viewer.ww; oldH ← viewer.wh;
SetViewerPosition[viewer, x, y, w, h];
IF ~paint OR viewer.parent # NIL THEN RETURN;
GreyScreen[oldX, oldY, oldW, oldH, viewer.column=color AND ~viewer.iconic, viewer.iconic];
};
Carets.SuspendCarets[];
ViewerLocks.CallUnderWriteLock[LockedMoveViewer, viewer];
IF paint THEN PaintViewer[IF viewer.parent=NIL THEN viewer ELSE viewer.parent, all];
Carets.ResumeCarets[];
};
EstablishViewerPosition: PUBLIC PROC [viewer: Viewer, x, y, w, h: INTEGER] = {
LockedEstablishViewerPosition: PROC = {SetViewerPosition[viewer, x, y, w, h]};
ViewerLocks.CallUnderWriteLock[LockedEstablishViewerPosition, viewer];
};
SetViewerPosition: PROC[viewer: Viewer, x, y, w, h: INTEGER] = { OPEN ViewerSpecs;
vbs: INTEGER = IF viewer.border THEN windowBorderSize ELSE 0;
xmin, xmax, ymin, ymax: INTEGER; -- limits of client area, relative to the viewer window
viewer.wx ← x; viewer.wy ← y; viewer.ww ← w; viewer.wh ← h;
ymax ← MenusPrivate.EstablishHeader[viewer]; -- Note: EstablishHeader uses ww and wh!
ymin ← vbs; xmin ← vbs; xmax ← w-vbs;
Leave space for a horizontal scrollbar, if any.
If there is a menu, the horizontal scrollbar region overlaps the menu bar.
IF viewer.hscrollable THEN {
IF viewer.menu#NIL THEN ymax ← ymax+menuBarHeight;
ymax ← ymax-scrollBarW;
};
Leave space for a vertical scrollbar, if any.
If there is a border, the vertical scrollbar region overlaps the left border.
IF viewer.scrollable THEN xmin ← scrollBarW;
viewer.cx ← x+xmin; viewer.cy ← y+ymin;
viewer.cw ← xmax-xmin; viewer.ch ← ymax-ymin;
ResetPaintCache[viewer];
};
LinkViewer: PROC [new: Viewer, bottom: BOOLTRUE] = {
column: Column ~ ViewerColumn[new];
IF rootViewerTree[column]=NIL OR bottom THEN {
new.sibling ← rootViewerTree[column];
rootViewerTree[column] ← new;
}
ELSE FOR v: Viewer ← rootViewerTree[column], v.sibling UNTIL v.sibling=NIL DO
REPEAT FINISHED => v.sibling ← new;
ENDLOOP;
};
UnlinkViewer: PROC [old: Viewer] = {
root: Viewer;
column: Column ~ ViewerColumn[old];
IF old=rootViewerTree[column] THEN
{rootViewerTree[column] ← old.sibling; old.sibling ← NIL; RETURN};
root ← IF old.parent=NIL THEN rootViewerTree[column] ELSE old.parent.child;
IF old=root THEN old.parent.child ← old.sibling;
FOR v: Viewer ← root, v.sibling UNTIL v.sibling=old DO
REPEAT FINISHED => v.sibling ← v.sibling.sibling;
ENDLOOP;
old.sibling ← NIL;
};
LinkChild: PROC [parent, child: Viewer, bottom: BOOLTRUE] = {
IF parent.child=NIL OR bottom THEN {
child.sibling ← parent.child;
parent.child ← child;
}
ELSE FOR v: Viewer ← parent.child, v.sibling UNTIL v.sibling=NIL DO
REPEAT FINISHED => v.sibling ← child;
ENDLOOP;
};
CountViewers: PROC [c: Column] RETURNS [count: INTEGER] = {
count ← 0;
FOR v: Viewer ← rootViewerTree[c], v.sibling UNTIL v=NIL DO
count ← count+1;
ENDLOOP;
};
KillInputFocus: PROC [viewer: Viewer] = {
focus: Viewer ← InputFocus.GetInputFocus[].owner;
WHILE focus # NIL DO
IF focus # viewer THEN focus ← focus.parent
ELSE {InputFocus.SetInputFocus[]; EXIT};
ENDLOOP;
};
UnWedgedColumn: PROC RETURNS[unwedged: Column] = INLINE {
FOR unwedged IN Column DO
IF unwedged = static THEN LOOP; -- save the static column for last
IF unwedged = color AND ~WindowManager.colorDisplayOn THEN LOOP;
IF ~ViewerLocks.ColumnWedged[unwedged].wedged THEN RETURN;
ENDLOOP;
RETURN[static];
};
[] ← ViewerEvents.RegisterEventProc[GrowExtra, grow, NIL, TRUE];
[] ← ViewerEvents.RegisterEventProc[GrowExtra, grow, NIL, FALSE];
[] ← ViewerEvents.RegisterEventProc[GrowExtra, destroy];
END.