ViewerOpsImplA.mesa
Copyright Ó 1985, 1986, 1987, 1991 by Xerox Corporation. All rights reserved.
Plass, May 4, 1983 11:27 am
Russ Atkinson (RRA) June 18, 1985 0:58:25 am PDT
Doug Wyatt, January 19, 1987 11:59:30 pm PST
Michael Plass, March 17, 1992 9:38 am PST
Christian Jacobi, May 25, 1990 1:16 pm PDT
Last tweaked by Mike Spreitzer on May 18, 1988 6:34:16 pm PDT
Bier, January 29, 1989 3:07:02 pm PST
Willie-s, May 24, 1991 1:46 pm PDT
DIRECTORY
Atom USING [GetPropFromList, PutPropOnList],
Carets USING [ResumeCarets, SuspendCarets],
InputFocus USING [GetInputFocus, SetInputFocus],
List USING [CompareProc, Sort],
Menus USING [CopyMenu],
MessageWindow USING [Append, Blink, Clear],
MessageWindowBackdoor USING [AllocateStaticArea],
Rope USING [ROPE],
ViewerBLT USING [InternalPaintColumn],
ViewerClasses USING [Column, Viewer, ViewerClass, ViewerFlavor, ViewerRec],
ViewerEvents USING [EventProc, ProcessEvent, RegisterEventProc],
ViewerForkers USING [ForkPaint],
ViewerLocks USING [CallUnderColumnLock, CallUnderColumnLocks, CallUnderViewerTreeLock, CallUnderWriteLock, Wedged],
ViewerOps USING [AddProp, BottomViewer, CloseViewer, ComputeColumn, EnumerateChildren, EnumerateViewers, EnumProc, FetchProp, FetchViewerClass, OpenIcon, PaintViewer, ViewerColumn],
ViewerPrivate,
ViewerSpecs,
WindowManager USING [colorDisplayOn];
ViewerOpsImplA:
CEDAR
MONITOR
-- locks icon slot table and props field --
IMPORTS Atom, Carets, InputFocus, List, Menus, MessageWindow, MessageWindowBackdoor, ViewerBLT, ViewerEvents, ViewerForkers, ViewerLocks, ViewerOps, ViewerPrivate, ViewerSpecs, WindowManager
EXPORTS ViewerOps, ViewerPrivate
SHARES Menus, ViewerClasses, ViewerEvents, ViewerOps, ViewerLocks
= BEGIN OPEN ViewerClasses, ViewerSpecs, ViewerOps, WindowManager;
fatalViewersError: ERROR = CODE;
rootViewerTree:
PUBLIC
REF ViewerPrivate.RootViewerTreeRep ¬
NEW[ViewerPrivate.RootViewerTreeRep[
VAL[4]]];
rootViewerTree[column] is the bottom viewer in the column
AddProp:
PUBLIC
ENTRY
PROC [viewer: Viewer, prop:
ATOM, val:
REF
ANY] = {
ENABLE UNWIND => NULL;
IF viewer#
NIL
THEN
viewer.props ¬ Atom.PutPropOnList[viewer.props, prop, val]
};
FetchProp:
PUBLIC
PROC [viewer: Viewer, prop:
ATOM]
RETURNS [val:
REF
ANY] = {
RETURN[Atom.GetPropFromList[viewer.props, prop]]
};
CreateViewer:
PUBLIC
PROC [flavor: ViewerFlavor, info: ViewerRec ¬ [], paint:
BOOL ¬
TRUE]
RETURNS [new: Viewer] = {
ENABLE UNWIND => NULL;
topLevel: BOOL ¬ FALSE; -- to keep the compiler happy
defaultPosition: BOOL ¬ FALSE;
LockedCreateViewer:
PROC = {
class: ViewerClasses.ViewerClass = FetchViewerClass[flavor];
IF class=NIL THEN ERROR fatalViewersError; -- class not implemented
defaultPosition ¬ (new.wx=0) AND (new.wy=0) AND (new.ww=0) AND (new.wh=0);
IF new.parent=NIL AND new.column#static THEN defaultPosition ¬ TRUE; -- insist
new.class ¬ class;
topLevel ¬ (new.parent=NIL);
IF topLevel THEN new.border ¬ TRUE;
IF topLevel AND new.column#static THEN new.caption ¬ TRUE;
IF class.scroll=NIL THEN new.scrollable ¬ FALSE;
IF class.hscroll=NIL THEN new.hscrollable ¬ FALSE;
IF new.tipTable=NIL THEN new.tipTable ¬ class.tipTable;
IF new.icon=unInit THEN new.icon ¬ class.icon;
IF topLevel
THEN {
IF new.column=static THEN new.iconic ¬ FALSE;
IF NOT iconSlotsUsed<maxIcons THEN new.iconic ¬ FALSE;
}
ELSE {
new.iconic ¬ FALSE;
new.visible ¬ new.parent.visible AND NOT new.parent.iconic;
new.column ¬ new.parent.column;
};
IF new.column#static
AND new.parent=
NIL
AND new.menu=
NIL
AND new.class.menu#NIL THEN new.menu ¬ Menus.CopyMenu[new.class.menu];
IF topLevel THEN LinkViewer[new, ~new.iconic] ELSE LinkChild[new.parent, new];
IF NOT 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 OR new.spare5<<topRow>> THEN InternalComputeColumn[new.column, paint];
new.init ¬ TRUE;
};
new ¬ NEW[ViewerRec ¬ info];
IF ViewerEvents.ProcessEvent[create, new, TRUE] THEN RETURN[NIL]; -- Gulp!!!
IF ViewerPrivate.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 {
column: Column ~ IF new.iconic THEN static ELSE new.column;
ViewerLocks.CallUnderColumnLock[LockedCreateViewer, column];
}
ELSE ViewerLocks.CallUnderWriteLock[LockedCreateViewer, new.parent];
IF paint
AND (new.iconic
OR
NOT(topLevel
AND defaultPosition))
THEN
ViewerOps.PaintViewer[new, all];
[] ¬ ViewerEvents.ProcessEvent[create, new, FALSE];
};
DestroyViewer:
PUBLIC
PROC [viewer: Viewer, paint:
BOOL ¬
TRUE] = {
IF viewer=NIL OR viewer.destroyed THEN RETURN; -- already destroyed; nop
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: BOOL ¬ FALSE;
LockedDestroyViewer:
PROC = {
parent ¬ viewer.parent; -- copy parent; smashed below
x ¬ viewer.wx; y ¬ viewer.wy; w ¬ viewer.ww; h ¬ viewer.wh;
KillInputFocus[viewer];
IF viewer.iconic THEN ViewerPrivate.GreyWindow[viewer]; -- erase the icon
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;
};
SELECT
TRUE
FROM
parent #
NIL => {
SELECT
TRUE
FROM
parent.child=viewer => parent.child ¬ viewer.sibling;
viewer.sibling#
NIL
OR parent#viewer =>
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;
ENDCASE;
viewer.sibling ¬ NIL;
paintParent ¬ paint;
};
viewer.iconic => {
IF replace
THEN
RETURN;
used by ReplaceViewer to prevent the slot from being reused
RemoveIcon[icon: viewer, fillSlot: TRUE];
};
ENDCASE => InternalComputeColumn[viewer.column, paint];
};
IF ViewerEvents.ProcessEvent[destroy, viewer, TRUE] THEN RETURN;
KillInputFocus[viewer];
IF viewer.parent=
NIL
THEN {
column: Column ~ IF viewer.iconic THEN static ELSE viewer.column;
ViewerLocks.CallUnderColumnLock[LockedDestroyViewer, column];
}
ELSE ViewerLocks.CallUnderWriteLock[LockedDestroyViewer, viewer.parent];
IF paintParent THEN ViewerOps.PaintViewer[parent, all];
the following are always painted because the caller isn't likely to know to repaint them
IF onlyLink#NIL THEN ViewerOps.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.file ¬ NIL;
viewer.menu ¬ NIL; viewer.props ¬ NIL; viewer.child ¬ NIL;
viewer.data ¬ NIL;
};
ViewerLocks.CallUnderWriteLock[LockedBottomUpEtc, viewer];
};
TopViewer:
PUBLIC
PROC [viewer: Viewer, paint:
BOOL ¬
TRUE] = {
LockedTopViewer:
PROC = {
IF viewer.parent#NIL THEN RETURN; -- top level viewers only
SELECT viewer.column
FROM
left, right => IF (viewer.wy+viewer.wh)=ViewerSpecs.openTopY THEN RETURN;
color => IF (viewer.wy+viewer.wh)=ViewerSpecs.colorScreenHeight THEN RETURN;
static => NULL;
ENDCASE => RETURN; -- not a sequenced column
UnlinkViewer[viewer];
LinkViewer[viewer, FALSE];
InternalComputeColumn[viewer.column, paint];
};
IF viewer.offDeskTop THEN ChangeColumn[viewer, left];
ViewerLocks.CallUnderColumnLock[LockedTopViewer, viewer.column];
};
BottomViewer:
PUBLIC
PROC [viewer: Viewer, paint:
BOOL ¬
TRUE] = {
LockedBottomViewer:
PROC = {
IF viewer.parent#NIL THEN RETURN; -- top level viewers only
SELECT viewer.column
FROM
left, right => IF viewer.wy=ViewerSpecs.openBottomY THEN RETURN;
color => IF viewer.wy=0 THEN RETURN;
static => NULL;
ENDCASE => RETURN; -- not a sequenced column
UnlinkViewer[viewer];
LinkViewer[viewer, TRUE];
InternalComputeColumn[viewer.column, paint];
};
IF viewer.offDeskTop THEN ChangeColumn[viewer, left];
ViewerLocks.CallUnderColumnLock[LockedBottomViewer, viewer.column];
};
MoveAboveViewer:
PUBLIC
PROC [altered, static: Viewer, paint:
BOOL ¬
TRUE] = {
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:
BOOL ¬
TRUE] = {
LockedMoveBelowViewer:
PROC = {
IF altered.parent#NIL OR static.parent#NIL THEN RETURN;
IF altered.column#static.column OR static.iconic THEN RETURN;
IF altered.destroyed OR static.destroyed THEN RETURN; -- DKW: until locking is right
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] = {
LockedReplaceViewer:
PROC = {
IF old.parent#
NIL
OR new.parent#
NIL
OR new.column#old.column
THEN
RETURN WITH ERROR fatalViewersError; -- 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 new.iconic THEN ViewerOps.PaintViewer[new, all];
};
MoveBoundary:
PUBLIC
PROC [newLeftWidth, newBottomY:
INTEGER] = {
LockedMoveBoundary:
PROC = {
oldBottomY: INTEGER ~ ViewerSpecs.openBottomY;
oldLeftWidth: INTEGER ~ ViewerSpecs.openLeftWidth;
ViewerPrivate.SetOpenLeftWidth[newLeftWidth];
ViewerPrivate.SetOpenBottomY[newBottomY];
IF ViewerSpecs.openLeftWidth > oldLeftWidth
THEN {
InternalComputeColumn[right, TRUE, FALSE];
InternalComputeColumn[left, TRUE, FALSE];
}
ELSE {
InternalComputeColumn[left, TRUE, FALSE];
InternalComputeColumn[right, TRUE, FALSE];
};
IF ViewerSpecs.openBottomY#oldBottomY
THEN {
-- repaint icons
ViewerPrivate.GreyScreen[main, 0, 0, ViewerSpecs.bwScreenWidth, ViewerSpecs.openBottomY];
FOR v: Viewer ¬ rootViewerTree[static], v.sibling
UNTIL v =
NIL
DO
IF v.offDeskTop OR NOT v.iconic THEN LOOP;
ForkPaintViewerAll[v];
ENDLOOP;
};
};
inner:
PROC = {
ViewerLocks.CallUnderViewerTreeLock[LockedMoveBoundary];
ViewerPrivate.WaitForPaintingToFinish[];
};
WithCaretsOff[inner];
};
ChangeColumn:
PUBLIC
PROC [viewer: Viewer, newColumn: Column] = {
oldColumn: Column;
paint: BOOL = TRUE;
paintIcon: BOOL ¬ FALSE;
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 {
RemoveIcon[viewer];
ViewerPrivate.GreyWindow[viewer];
};
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];
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];
};
inner:
PROC = {
IF ViewerPrivate.ColumnWedged[newColumn].wedged
THEN {
newColumn ¬ UnWedgedColumn[];
IF newColumn = static THEN RETURN;
};
ViewerLocks.CallUnderColumnLocks[LockedChangeColumn, ViewerColumn[viewer], newColumn];
IF paintIcon THEN ViewerOps.PaintViewer[viewer, all];
};
IF ViewerEvents.ProcessEvent[changeColumn, viewer, TRUE].abort THEN RETURN;
WithCaretsOff[inner];
[] ¬ ViewerEvents.ProcessEvent[changeColumn, viewer, FALSE];
};
OpenIcon:
PUBLIC
PROC [icon: Viewer,
closeOthers:
BOOL ¬
FALSE, bottom:
BOOL ¬
TRUE, paint:
BOOL ¬
TRUE] = {
LockedOpenIcon:
PROC = {
ENABLE UNWIND => NULL;
SetOpen: EnumProc = {v.visible ¬ TRUE};
IF ~icon.iconic THEN {paint ¬ closeOthers ¬ FALSE; RETURN};
KillInputFocus[icon];
closeOthers ¬ (closeOthers AND (CountViewers[icon.column]#0));
ViewerPrivate.GreyWindow[icon];
UnlinkViewer[icon];
icon.iconic ¬ FALSE;
LinkViewer[icon, bottom];
EnumerateChildren[icon, SetOpen];
RemoveIcon[icon: icon, fillSlot: NOT closeOthers];
icon.position ¬ 0;
IF closeOthers THEN GrowViewer[icon, paint];
InternalComputeColumn[icon.column, paint];
IF paint THEN MessageWindow.Clear[]; -- flush old messages
};
inner:
PROC = {
IF ViewerPrivate.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];
};
IF ViewerEvents.ProcessEvent[open, icon, TRUE].abort THEN RETURN;
WithCaretsOff[inner ! ViewerLocks.Wedged =>
GO
TO ignore];
[] ¬ ViewerEvents.ProcessEvent[open, icon, FALSE];
EXITS ignore => {};
};
CloseViewer:
PUBLIC
PROC [viewer: Viewer, paint:
BOOL ¬
TRUE] = {
ENABLE UNWIND => NULL;
LockedCloseViewer:
PROC = {
SetClosed: EnumProc = {v.visible ¬ FALSE};
IF viewer.iconic OR viewer.column=static THEN RETURN;
IF
NOT iconSlotsUsed<maxIcons
THEN {
MessageWindow.Append["No space for another icon! Try deleting some viewers.", TRUE];
MessageWindow.Blink;
RETURN;
};
KillInputFocus[viewer];
UnlinkViewer[viewer];
viewer.iconic ¬ TRUE;
LinkViewer[viewer];
EnumerateChildren[viewer, SetClosed];
PositionIcon[viewer];
InternalComputeColumn[viewer.column, paint];
};
inner:
PROC = {
KillInputFocus[viewer];
IF viewer.offDeskTop THEN ChangeColumn[viewer, left];
ViewerLocks.CallUnderColumnLocks[LockedCloseViewer, viewer.column, static];
IF paint THEN ViewerOps.PaintViewer[viewer, all];
};
IF ViewerEvents.ProcessEvent[close, viewer, TRUE].abort THEN RETURN;
WithCaretsOff[inner];
[] ¬ ViewerEvents.ProcessEvent[close, viewer, FALSE];
};
SwapIconAndViewer:
PUBLIC
PROC [icon, openViewer: Viewer, paint:
BOOL ¬
TRUE] = {
DO
col: Column = openViewer.column;
retry: BOOL ¬ FALSE;
inner:
PROC = {
IF openViewer.column # col OR icon.column # col THEN {retry ¬ TRUE; RETURN};
IF ~icon.iconic OR openViewer.iconic THEN RETURN;
KillInputFocus[icon];
KillInputFocus[openViewer];
OpenIcon[icon: icon, paint: FALSE];
MoveAboveViewer[icon, openViewer, FALSE];
CloseViewer[openViewer, FALSE];
ComputeColumn[col];
};
IF ~icon.iconic OR openViewer.iconic THEN RETURN;
IF ViewerPrivate.ColumnWedged[col].wedged THEN RETURN;
IF ViewerPrivate.ColumnWedged[icon.column].wedged THEN RETURN;
IF icon.column#col THEN {ChangeColumn[icon, col]; LOOP};
ViewerLocks.CallUnderColumnLocks[inner, col, static];
IF NOT retry THEN EXIT;
ENDLOOP;
};
GrowViewer:
PUBLIC
PROC [viewer: Viewer, paint:
BOOL ¬
TRUE] = {
inThisColumn: LIST OF Viewer;
LockedComputeColumn: PROC = {InternalComputeColumn[viewer.column, paint]};
LockedGrowViewer:
PROC = {
FOR v: Viewer ¬ rootViewerTree[viewer.column], v.sibling
WHILE v #
NIL
DO
IF v # viewer THEN inThisColumn ¬ CONS[v, inThisColumn];
ENDLOOP;
};
inner:
PROC = {
IF viewer.offDeskTop THEN ChangeColumn[viewer, left];
ViewerLocks.CallUnderColumnLocks[LockedGrowViewer, viewer.column, static];
FOR v:
LIST
OF Viewer ¬ inThisColumn, v.rest
UNTIL v =
NIL
DO
ViewerOps.CloseViewer[v.first, FALSE]; -- outside the lock because of KillInputFocus
ENDLOOP;
IF paint
AND inThisColumn #
NIL
THEN {
FOR v:
LIST
OF Viewer ¬ inThisColumn, v.rest
UNTIL v =
NIL
DO
IF v.first.iconic THEN ViewerOps.PaintViewer[v.first, all];
ENDLOOP;
ViewerLocks.CallUnderColumnLock[LockedComputeColumn, viewer.column];
};
};
IF viewer.iconic THEN {ViewerOps.OpenIcon[viewer, TRUE, paint]; RETURN};
IF ViewerEvents.ProcessEvent[grow, viewer, TRUE].abort THEN RETURN;
WithCaretsOff[inner];
[] ¬ 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
SELECT
TRUE
FROM
v.first.destroyed => {};
v.first = viewer => ViewerOps.BottomViewer[viewer: viewer, paint: FALSE];
v.first.iconic
AND v.first.column = viewer.column =>
ViewerOps.OpenIcon[icon: v.first, paint: FALSE];
ENDCASE;
ENDLOOP;
ViewerOps.ComputeColumn[viewer.column];
};
inner:
PROC = {
ViewerLocks.CallUnderColumnLocks[LockedGrowExtra, viewer.column, static];
};
IF event = destroy AND viewer.parent # NIL THEN RETURN;
IF event = destroy AND (viewer.iconic OR viewer.column = static) THEN RETURN;
WithCaretsOff[inner];
};
ComputeColumn:
PUBLIC
PROC [column: Column, paint:
BOOL ¬
TRUE] = {
ENABLE UNWIND => NULL;
LockedComputeColumn:
PROC = {
InternalComputeColumn[column, paint];
};
inner:
PROC = {
ViewerLocks.CallUnderColumnLock[LockedComputeColumn, column];
};
WithCaretsOff[inner];
};
InternalComputeColumn:
PROC [column: Column, paint, icons:
BOOL ¬
TRUE] = {
monitored access to window chain
openViewers: INTEGER = CountViewers[column];
SELECT
TRUE
FROM
(openViewers = 0) => NULL;
(column = static) => {
The static column contains the buttons in the top row of the Viewers World. If this World has been resized, these buttons will be moved so as to stay flush right. Note that if the Viewers World has just been shrunk, some of these buttons may end up at negative wx values (e.g., off the screen).
mw: Viewer ~ ViewerPrivate.messageWindow;
IF mw #
NIL
THEN {
-- don't do anything if the message window is not there yet.
toplist: LIST OF Viewer ¬ NIL;
SetViewerPosition[
viewer: mw,
x: 0,
y: ViewerSpecs.bwScreenHeight-ViewerSpecs.messageWindowHeight,
w: ViewerSpecs.bwScreenWidth,
h: ViewerSpecs.messageWindowHeight
];
FOR v: Viewer ¬ rootViewerTree[column], v.sibling
UNTIL v=
NIL
DO
SELECT
TRUE
FROM
v.offDeskTop => NULL;
v=mw => NULL;
v.iconic => SetIconPosition[v];
v.spare5 => {
toplist ¬ CONS[v, toplist];
};
ENDCASE => NULL;
ENDLOOP;
WHILE toplist #
NIL
DO
v: Viewer ¬ toplist.first;
x, y, w, h: INTEGER;
[x, y, w, h] ← MessageWindowBackdoor.AllocateStaticArea[v.ww];
SetViewerPosition[v, x, y, w, h];
toplist ¬ toplist.rest;
ENDLOOP;
};
};
ENDCASE => {
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.
force: BOOL ¬ FALSE;
largestViewer: Viewer;
totalRequested, requests: INTEGER ¬ 0;
minHeight: INTEGER ~ captionHeight;
leftX, width, bottomY, nextY: INTEGER;
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;
};
[x: leftX, w: width, y: bottomY, h: totalSpace] ¬ ColumnInfo[column];
nextY ¬ bottomY;
normalHeight ¬ totalSpace/openViewers;
AddUpRequests[LAST[INTEGER]/2];
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:
INTEGER ¬
SELECT
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:
PROC[column: Column]
RETURNS[x, y, w, h:
INTEGER] = {
SELECT column
FROM
left =>
RETURN[
x: ViewerSpecs.openLeftLeftX, w: ViewerSpecs.openLeftWidth,
y: ViewerSpecs.openBottomY, h: ViewerSpecs.openTopY-ViewerSpecs.openBottomY
];
right =>
RETURN[
x: ViewerSpecs.openRightLeftX, w: ViewerSpecs.openRightWidth,
y: ViewerSpecs.openBottomY, h: ViewerSpecs.openTopY-ViewerSpecs.openBottomY
];
color =>
RETURN[
x: 0, w: ViewerSpecs.colorScreenWidth,
y: 0, h: ViewerSpecs.colorScreenHeight
];
ENDCASE => ERROR;
};
maxIcons: NAT ~ 16*32;
IconSlotsArray: TYPE ~ PACKED ARRAY [0..maxIcons) OF BOOL;
iconSlotsArray: REF IconSlotsArray ¬ NEW[IconSlotsArray ¬ ALL[FALSE]];
iconSlotsUsed: [0..maxIcons] ¬ 0;
ClaimIconSlot:
ENTRY
PROC [slot: [0..maxIcons)]
RETURNS [
BOOL] ~ {
ENABLE UNWIND => NULL;
IF
NOT iconSlotsArray[slot]
THEN {
iconSlotsUsed ¬ iconSlotsUsed + 1;
iconSlotsArray[slot] ¬ TRUE;
RETURN [TRUE]
};
RETURN [FALSE]
};
UpdateIconSlot:
ENTRY
PROC [slot: [0..maxIcons), value:
BOOL] ~ {
ENABLE UNWIND => NULL;
IF iconSlotsArray[slot] THEN iconSlotsUsed ¬ iconSlotsUsed - 1;
IF value THEN iconSlotsUsed ¬ iconSlotsUsed + 1;
iconSlotsArray[slot] ¬ value;
};
RemoveIcon:
PROC [icon: Viewer, fillSlot:
BOOL ¬
FALSE] = {
rows: NAT ~ ViewerSpecs.iconRows;
columns: NAT ~ ViewerSpecs.iconColumns;
filler: Viewer;
slot: INTEGER ¬ 0;
FindSlotIcon: ViewerOps.EnumProc = {
IF v.offDeskTop THEN RETURN;
IF v.iconic AND v.position=slot THEN { filler ¬ v; RETURN[FALSE] };
};
IF icon.offDeskTop THEN RETURN;
UpdateIconSlot[icon.position, FALSE];
IF NOT fillSlot THEN RETURN;
FOR m:
NAT
DECREASING
IN ((icon.position/columns)..rows)
DO
IF icon.column = right
THEN
FOR n:
NAT
DECREASING
IN [0..columns)
DO
slot ¬ m*columns + n;
IF iconSlotsArray[slot] THEN EXIT;
ENDLOOP
ELSE
FOR n:
NAT
IN [0..columns)
DO
slot ¬ m*columns + n;
IF iconSlotsArray[slot] THEN EXIT;
ENDLOOP;
ENDLOOP;
IF slot = 0 OR NOT iconSlotsArray[slot] THEN RETURN;
ViewerOps.EnumerateViewers[FindSlotIcon];
IF filler = NIL THEN RETURN;
ViewerPrivate.GreyWindow[filler];
UpdateIconSlot[filler.position, FALSE];
filler.position ¬ icon.position;
UpdateIconSlot[filler.position, TRUE];
SetIconPosition[filler];
ForkPaintViewerAll[filler];
};
PositionIcon:
PROC [viewer: Viewer, slot:
INTEGER ¬ 0] = {
rows: NAT ~ ViewerSpecs.iconRows;
columns: NAT ~ ViewerSpecs.iconColumns;
slotIcon: Viewer ¬ NIL;
FindSlotIcon: ViewerOps.EnumProc = {
IF v.offDeskTop THEN RETURN;
IF v.iconic AND v.position=slot THEN {slotIcon ¬ v; RETURN[FALSE]};
};
FindIconSlot:
PROC [side: Column]
RETURNS [slot:
CARDINAL] = {
FOR m:
CARDINAL
IN [0..rows)
DO
IF side=right
THEN
FOR n:
CARDINAL
DECREASING
IN [0..columns)
DO
slot ¬ m*columns+n;
IF ClaimIconSlot[slot] THEN RETURN;
ENDLOOP
ELSE
FOR n:
CARDINAL
IN [0..columns)
DO
slot ¬ m*columns+n;
IF ClaimIconSlot[slot] THEN RETURN;
ENDLOOP;
ENDLOOP;
};
IF slot # 0
AND iconSlotsArray[slot]
THEN {
-- move the icon in the slot
ViewerOps.EnumerateViewers[FindSlotIcon];
IF slotIcon #
NIL
THEN {
slotIcon.position ¬ FindIconSlot[slotIcon.column];
SetIconPosition[slotIcon];
};
};
viewer.position ¬ IF slot # 0 THEN slot ELSE FindIconSlot[viewer.column];
UpdateIconSlot[viewer.position, TRUE];
SetIconPosition[viewer];
};
SetIconPosition:
PROC [icon: Viewer] = {
columns: NAT ~ ViewerSpecs.iconColumns;
position: NAT ~ icon.position;
r: NAT ~ position/columns;
c: NAT ~ position MOD columns;
x: INTEGER ~ ViewerSpecs.iconLeftX+c*ViewerSpecs.iconColumnWidth;
y: INTEGER ~ ViewerSpecs.iconBottomY+r*ViewerSpecs.iconRowHeight;
SetViewerPosition[icon, x, y, iconWidth, iconHeight];
};
MoveViewer:
PUBLIC
PROC [viewer: Viewer, x, y, w, h:
INTEGER, paint:
BOOL ¬
TRUE] ~ {
parent: Viewer ~ viewer.parent;
LockedMoveViewer:
PROC = {
IF paint AND parent=NIL THEN ViewerPrivate.GreyWindow[viewer];
SetViewerPosition[viewer, x, y, w, h];
};
inner:
PROC = {
ViewerLocks.CallUnderWriteLock[LockedMoveViewer, viewer];
IF paint THEN ViewerOps.PaintViewer[IF parent=NIL THEN viewer ELSE parent, all];
};
WithCaretsOff[inner];
};
EstablishViewerPosition:
PUBLIC
PROC [viewer: Viewer, x, y, w, h:
INTEGER] ~ {
LockedEstablishViewerPosition:
PROC = {
SetViewerPosition[viewer, x, y, w, h];
};
inner:
PROC = {
ViewerLocks.CallUnderWriteLock[LockedEstablishViewerPosition, viewer];
};
WithCaretsOff[inner];
};
SetViewerPosition:
PROC [viewer: Viewer, x, y, w, h:
INTEGER] ~ {
oldcw: INTEGER ~ viewer.cw; oldch: INTEGER ~ viewer.ch;
IF w<0 THEN w ¬ 0; IF h<0 THEN h ¬ 0;
viewer.wx ¬ x;
viewer.wy ¬ y;
viewer.ww ¬ w;
viewer.wh ¬ h;
IF viewer.iconic
THEN {
viewer.cx ¬ 0;
viewer.cy ¬ 0;
viewer.cw ¬ w;
viewer.ch ¬ h;
}
ELSE {
xmin: INTEGER ¬ 0; xmax: INTEGER ¬ w;
ymin: INTEGER ¬ 0; ymax: INTEGER ¬ h;
IF viewer.border
THEN {
size: INTEGER ~ ViewerSpecs.windowBorderSize;
xmin ¬ xmin+size; xmax ¬ xmax-size;
ymin ¬ ymin+size; ymax ¬ ymax-size;
};
IF viewer.caption
THEN
ymax ¬ h-ViewerSpecs.captionHeight;
IF viewer.menu#
NIL
THEN {
lines: NAT ~ viewer.menu.linesUsed;
ymax ¬ ymax-lines*ViewerSpecs.menuHeight;
ymax ¬ ymax-ViewerSpecs.menuBarHeight;
};
IF viewer.scrollable
THEN
xmin ¬ xmin+ViewerSpecs.scrollBarW;
IF viewer.hscrollable
THEN
ymin ¬ ymin+ViewerSpecs.scrollBarW;
IF xmax<xmin THEN xmin ¬ xmax ¬ 0;
IF ymax<ymin THEN ymin ¬ ymax ¬ 0;
viewer.cx ¬ xmin; viewer.cw ¬ xmax-xmin;
viewer.cy ¬ ymin; viewer.ch ¬ ymax-ymin;
};
IF viewer.class.adjust#
NIL
AND (viewer.cw#oldcw
OR viewer.ch#oldch)
THEN
[] ¬ viewer.class.adjust[viewer];
};
LinkViewer:
PROC [new: Viewer, bottom:
BOOL ¬
TRUE] = {
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] = {
column: Column ~ ViewerColumn[old];
IF old.parent =
NIL
THEN {
A top-level viewer, so a single chain terminated by NIL
root: Viewer ¬ rootViewerTree[column];
IF old=root
THEN rootViewerTree[column] ¬ old.sibling
ELSE
FOR v: Viewer ¬ root, v.sibling
WHILE v #
NIL
DO
IF v.sibling = old THEN {v.sibling ¬ old.sibling; EXIT};
ENDLOOP;
}
ELSE {
Child of a viewer (circular chain)
parent: Viewer ¬ old.parent;
FOR v: Viewer ¬ parent, v.sibling
WHILE v #
NIL
DO
IF v.sibling = old
THEN {
IF parent.child = old THEN parent.child ¬ old.sibling;
v.sibling ¬ old.sibling;
EXIT};
ENDLOOP;
};
old.sibling ¬ NIL;
};
LinkChild:
PROC [parent, child: Viewer, bottom:
BOOL ¬
TRUE] = {
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] = {
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 NOT ViewerPrivate.ColumnWedged[unwedged].wedged THEN RETURN;
ENDLOOP;
RETURN[static];
};
ForkPaintViewerAll:
PROC [viewer: Viewer] =
TRUSTED {
ViewerForkers.ForkPaint[viewer, all];
};
WithCaretsOff:
PROC [inner:
PROC] = {
Carets.SuspendCarets[];
inner[ ! UNWIND => Carets.ResumeCarets[]];
Carets.ResumeCarets[];
};
[] ¬ ViewerEvents.RegisterEventProc[GrowExtra, grow, NIL, TRUE];
[] ¬ ViewerEvents.RegisterEventProc[GrowExtra, grow, NIL, FALSE];
[] ¬ ViewerEvents.RegisterEventProc[GrowExtra, destroy];
END.