<> <> <> <> <> DIRECTORY Atom USING [GetPropFromList, PutPropOnList], Carets USING [ResumeCarets, SuspendCarets], ColorDisplay USING [height, width], InputFocus USING [GetInputFocus, SetInputFocus], List USING [Sort, CompareProc], Menus USING [CopyMenu, Menu], 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 Atom, Carets, ColorDisplay, InputFocus, List, Menus, MessageWindow, Process, ViewerBLT, ViewerEvents, ViewerLocks, ViewerOps, WindowManager EXPORTS ViewerOps, ViewerSpecs, WindowManagerPrivate SHARES Menus, ViewerClasses, ViewerEvents, ViewerOps, ViewerLocks = BEGIN OPEN ViewerClasses, ViewerSpecs, ViewerOps, WindowManager; fatalViewersError: ERROR = 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: BOOL _ TRUE] RETURNS [new: Viewer] = BEGIN ENABLE UNWIND => NULL; topLevel: BOOL; defaultPosition: BOOL; 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 ~defaultPosition AND new.column#static THEN ERROR fatalViewersError; -- illegal positioning 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 BEGIN new.iconic _ FALSE; new.visible _ ~new.parent.iconic; new.column _ new.parent.column; END; IF topLevel THEN new.border _ TRUE; 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 ~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 { <> 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 paint AND (~topLevel OR ~defaultPosition OR new.iconic) THEN PaintViewer[new, all]; [] _ ViewerEvents.ProcessEvent[create, new, FALSE]; END; DestroyViewer: PUBLIC PROC [viewer: Viewer, paint: BOOL _ TRUE] = BEGIN ENABLE UNWIND => NULL; IF viewer.destroyed THEN RETURN; -- already destroyed ; nop IF viewer=NIL THEN ERROR fatalViewersError; InternalDestroyViewer[viewer, paint, FALSE]; END; InternalDestroyViewer: PROC [viewer: Viewer, paint, replace: BOOL] = BEGIN 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]; BottomUpRecursivelyDestroyViewerAndChildren[viewer]; IF viewer.link#NIL THEN BEGIN IF viewer.link.link=viewer THEN BEGIN viewer.link.link _ NIL; IF viewer.link.parent=NIL THEN onlyLink _ viewer.link; END ELSE FOR v: Viewer _ viewer.link.link, v.link UNTIL v.link=viewer DO REPEAT FINISHED => v.link _ viewer.link; ENDLOOP; viewer.link _ NIL; END; IF parent#NIL THEN BEGIN 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; END ELSE IF viewer.iconic THEN BEGIN 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]; END 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]; <> IF onlyLink#NIL THEN PaintViewer[onlyLink, caption]; [] _ ViewerEvents.ProcessEvent[destroy, viewer, FALSE]; END; BottomUpRecursivelyDestroyViewerAndChildren: PROC [viewer: Viewer] = BEGIN LockedBottomUpEtc: PROC = BEGIN 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]; <> viewer.parent _ NIL; viewer.name _ NIL; viewer.menu _ NIL; viewer.props _ NIL; viewer.child _ NIL; viewer.data _ NIL; END; ViewerLocks.CallUnderWriteLock[LockedBottomUpEtc, viewer]; END; TopViewer: PUBLIC PROC [viewer: Viewer, paint: BOOL _ TRUE] = BEGIN ENABLE UNWIND => NULL; LockedTopViewer: PROC = { noop: BOOL _ FALSE; myTopY: INTEGER ~ viewer.wy+viewer.wh; IF viewer.parent#NIL THEN RETURN WITH ERROR fatalViewersError; -- top level viewers 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 => RETURN WITH ERROR fatalViewersError; -- 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]; END; BottomViewer: PUBLIC PROC [viewer: Viewer, paint: BOOL _ TRUE] = BEGIN ENABLE UNWIND => NULL; LockedBottomViewer: PROC = { noop: BOOL _ FALSE; IF viewer.parent#NIL THEN RETURN WITH ERROR fatalViewersError; -- top level viewers SELECT viewer.column FROM left, right => IF viewer.wy = openBottomY THEN noop _ TRUE; color => IF viewer.wy = 0 THEN noop _ TRUE; ENDCASE => RETURN WITH ERROR fatalViewersError; -- 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]; END; MoveAboveViewer: PUBLIC PROC [altered, static: Viewer, paint: BOOL _ TRUE] = BEGIN LockedMoveAboveViewer: PROC = BEGIN 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]; END; IF static.offDeskTop THEN ChangeColumn[static, left]; IF altered.offDeskTop THEN ChangeColumn[altered, left]; ViewerLocks.CallUnderColumnLock[LockedMoveAboveViewer, altered.column]; END; MoveBelowViewer: PUBLIC PROC [altered, static: Viewer, paint: BOOL _ TRUE] = BEGIN LockedMoveBelowViewer: PROC = BEGIN 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 BEGIN altered.sibling _ static; rootViewerTree[altered.column] _ altered; END ELSE BEGIN 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; END; InternalComputeColumn[altered.column, paint]; END; IF static.offDeskTop THEN ChangeColumn[static, left]; IF altered.offDeskTop THEN ChangeColumn[altered, left]; ViewerLocks.CallUnderColumnLock[LockedMoveBelowViewer, altered.column]; END; ReplaceViewer: PUBLIC PROC [new, old: Viewer] = BEGIN 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]; END; MoveBoundary: PUBLIC PROC [newLeftWidth: INTEGER _ openLeftWidth, newBottomY: INTEGER _ openBottomY] = BEGIN 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}; }; Carets.SuspendCarets[]; ViewerLocks.CallUnderViewerTreeLock[LockedMoveBoundary]; WaitForPaintingToFinish[]; Carets.ResumeCarets[]; END; ChangeColumn: PUBLIC PROC [viewer: Viewer, newColumn: Column] = BEGIN ENABLE UNWIND => Carets.ResumeCarets[]; 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 BEGIN 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}; END; <<-- non iconic case>> IF ~viewer.iconic THEN BEGIN UnlinkViewer[viewer]; viewer.column _ newColumn; LinkViewer[viewer]; ResetPaintCache[]; InternalComputeColumn[oldColumn, paint]; InternalComputeColumn[newColumn, paint]; END; <<-- 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]; END; OpenIcon: PUBLIC PROC [icon: Viewer, closeOthers: BOOL _ FALSE, bottom: BOOL _ TRUE, paint: BOOL _ TRUE] = BEGIN LockedOpenIcon: PROC = BEGIN 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 END; IF ViewerEvents.ProcessEvent[open, icon, TRUE].abort THEN RETURN; IF ViewerLocks.ColumnWedged[icon.column].wedged THEN { <> 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]; END; CloseViewer: PUBLIC PROC [viewer: Viewer, paint: BOOL _ TRUE] = BEGIN ENABLE UNWIND => NULL; LockedCloseViewer: PROC = { SetClosed: EnumProc = {v.visible _ FALSE}; IF viewer.iconic OR viewer.column=static THEN RETURN; IF iconSlotsFree=0 THEN BEGIN MessageWindow.Append["No space for another icon! Try deleting some viewers.", TRUE]; MessageWindow.Blink; RETURN; END; 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]; END; SwapIconAndViewer: PUBLIC PROC [icon, openViewer: Viewer, paint: BOOL _ TRUE] = BEGIN -- this isn't locked at all! IF ~icon.iconic OR openViewer.iconic THEN RETURN WITH ERROR fatalViewersError; 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]; END; GrowViewer: PUBLIC PROC [viewer: Viewer, paint: BOOL _ TRUE] = BEGIN 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}; 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]; 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]}; [] _ ViewerEvents.ProcessEvent[grow, viewer, FALSE]; END; GrowExtra: ViewerEvents.EventProc = BEGIN -- [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]; END; ComputeColumn: PUBLIC PROC [column: Column, paint: BOOL _ TRUE] = BEGIN ENABLE UNWIND => NULL; LockedComputeColumn: PROC = {InternalComputeColumn[column, paint]}; ViewerLocks.CallUnderColumnLock[LockedComputeColumn, column]; END; InternalComputeColumn: PROC [column: Column, paint, icons: BOOL _ TRUE] = BEGIN <> force: BOOL _ FALSE; 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 BEGIN totalRequested _ totalRequested + DesiredHeight[v]; requests _ requests + 1; END; ENDLOOP}; ResetPaintCache[]; -- clients are assuming that this will happen IF column # static AND openViewers # 0 THEN { <> < column height, ignore requests for more than average height.>> <> <<>> [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: 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]; END; ColumnInfo: PROC[column: Column] RETURNS[totalSpace, leftX, width, bottomY, nextY: INTEGER] = INLINE BEGIN 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; END; iconSlots: CARDINAL = (iconRightX-iconLeftX)/(iconWidth+iconSpacing); iconSlotsFree: CARDINAL _ iconRows*iconSlots; iconSlotsArray: ARRAY [0..iconRows*iconSlots) OF BOOL _ ALL[FALSE]; RemoveIcon: PROCEDURE[icon: Viewer, fillSlot: BOOLEAN _ FALSE] = BEGIN 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]]}; END; PositionIcon: PROC [viewer: Viewer, slot: INTEGER _ 0] = BEGIN 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] = BEGIN 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; END; 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]; END; SetIconPosition: PROC [icon: Viewer] = INLINE BEGIN SetViewerPosition[icon, (icon.position MOD iconSlots)*(iconWidth+iconSpacing)+iconLeftX, iconBottomY+((icon.position/iconSlots)*(iconHeight+iconSpacing)), iconWidth, iconHeight]; END; MoveViewer: PUBLIC PROC [viewer: Viewer, x, y: INTEGER, w, h: INTEGER, paint: BOOL _ TRUE] = BEGIN OPEN viewer; ENABLE UNWIND => Carets.ResumeCarets[]; LockedMoveViewer: PROC = { oldX, oldY, oldW, oldH: INTEGER; oldX _ wx; oldY _ wy; oldW _ ww; oldH _ wh; SetViewerPosition[viewer, x, y, w, h]; IF ~paint OR parent # NIL THEN RETURN; GreyScreen[oldX, oldY, oldW, oldH, column=color AND ~iconic, iconic]}; Carets.SuspendCarets[]; ViewerLocks.CallUnderWriteLock[LockedMoveViewer, viewer]; IF paint THEN PaintViewer[IF parent=NIL THEN viewer ELSE parent, all]; Carets.ResumeCarets[]; END; EstablishViewerPosition: PUBLIC PROC [viewer: Viewer, x,y,w,h: INTEGER] = BEGIN LockedEstablishViewerPosition: PROC = {SetViewerPosition[viewer, x, y, w, h]}; ViewerLocks.CallUnderWriteLock[LockedEstablishViewerPosition, viewer]; END; SetViewerPosition: PROC [viewer: Viewer, x,y,w,h: INTEGER] = BEGIN OPEN viewer; vbs: INTEGER = IF border THEN windowBorderSize ELSE 0; ResetPaintCache[viewer]; wx _ x; wy _ y; ww _ w; wh _ h; ch _ wh - (vbs+vbs) - (IF column=static OR parent#NIL THEN 0 ELSE captionHeight); IF menu#NIL THEN ch _ ch - (menu.linesUsed*menuHeight); cx _ wx + vbs + (IF scrollable THEN scrollBarW ELSE 0); cy _ wy + vbs; cw _ ww - (cx-wx) - vbs; END; LinkViewer: PROC [new: Viewer, bottom: BOOL _ TRUE] = BEGIN column: Column ~ ViewerColumn[new]; IF rootViewerTree[column]=NIL OR bottom THEN BEGIN new.sibling _ rootViewerTree[column]; rootViewerTree[column] _ new; END ELSE FOR v: Viewer _ rootViewerTree[column], v.sibling UNTIL v.sibling=NIL DO REPEAT FINISHED => v.sibling _ new; ENDLOOP; END; UnlinkViewer: PROC [old: Viewer] = BEGIN 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; END; LinkChild: PROC [parent, child: Viewer, bottom: BOOL _ TRUE] = BEGIN IF parent.child=NIL OR bottom THEN BEGIN child.sibling _ parent.child; parent.child _ child; END ELSE FOR v: Viewer _ parent.child, v.sibling UNTIL v.sibling=NIL DO REPEAT FINISHED => v.sibling _ child; ENDLOOP; END; CountViewers: PROC [c: Column] RETURNS [count: INTEGER] = BEGIN count _ 0; FOR v: Viewer _ rootViewerTree[c], v.sibling UNTIL v=NIL DO count _ count+1; ENDLOOP; END; KillInputFocus: PROC [viewer: Viewer] = BEGIN focus: Viewer _ InputFocus.GetInputFocus[].owner; WHILE focus # NIL DO IF focus # viewer THEN focus _ focus.parent ELSE {InputFocus.SetInputFocus[]; EXIT}; ENDLOOP; END; UnWedgedColumn: PROC RETURNS[unwedged: Column] = INLINE BEGIN 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]; END; [] _ ViewerEvents.RegisterEventProc[GrowExtra, grow, NIL, TRUE]; [] _ ViewerEvents.RegisterEventProc[GrowExtra, grow, NIL, FALSE]; [] _ ViewerEvents.RegisterEventProc[GrowExtra, destroy]; END.