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]; 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; IF ~viewer.iconic THEN BEGIN UnlinkViewer[viewer]; viewer.column _ newColumn; LinkViewer[viewer]; ResetPaintCache[]; InternalComputeColumn[oldColumn, paint]; InternalComputeColumn[newColumn, paint]; END; 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 { [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.  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, June 20, 1983 2:51 pm not locked, but these are not ordinary times! the following are always painted because the caller isn't likely to know to repaint them help out the garbage collector (.sibling handled by caller and above) -- iconic case -- non iconic case -- hyperspace not locked, but these are not ordinary times! monitored access to window chain 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. Κ ξ– "cedar" style˜JšΟc+™+Jšœ™+J™%J™-Jš-™-J˜šΟk ˜ Jšœžœ"˜,Jšœžœ˜+Jšœ žœ˜#Jšœ žœ ˜0Jšœžœ˜Jšœžœ˜Jšœžœ˜+Jšœžœ ˜Jšœžœžœ˜Jšœ žœ˜&Jšœ žœ.˜@J˜ J˜ J˜J˜ Jšœžœ˜%J˜—J˜Jšœž ˜J˜JšžœŒ˜“Jšžœ-˜4Jšžœ=˜CJ˜Jšžœžœ6˜@J˜Jšœžœžœ˜ J˜Jšœžœžœ9˜VJšœžœžœ˜=Jšœžœžœ˜7Jšœ žœžœ˜5J˜Jš œžœžœžœ žœžœ˜9J˜š Οn œžœžœ5žœžœ˜[Jšžœžœ˜Jšžœžœžœ˜Jšœ žœ˜Jšœžœ˜šŸœžœ˜J˜J˜J˜šžœ žœž˜Jšœ/žœ˜OJšœ3˜3Jšœ/žœ˜6J˜Jšœžœ˜J˜ Jšžœ˜J˜—Jšžœžœžœ˜/JšE™EJšœžœžœ˜'Jšœžœžœžœ˜:Jšœžœ˜Jšžœ˜J˜—Jšœ;˜;Jšžœ˜—J˜š Ÿ œžœžœžœžœž˜CJšžœžœžœ˜šŸœžœ˜Jšœžœžœ˜Jšœžœ˜&Jš žœžœžœžœžœžœ˜Sšžœž˜Jšœ žœžœžœ˜3Jšœ žœžœžœ˜5Jš œ žœ žœžœžœžœ˜NJš žœžœžœžœ˜I—Jšžœžœ žœžœ˜%J˜Jšœžœ˜Jšœ-˜-J˜—Jšžœžœ˜5Jšœ@˜@Jšžœ˜J˜—š Ÿ œžœžœžœžœž˜FJšžœžœžœ˜šŸœžœ˜Jšœžœžœ˜Jš žœžœžœžœžœžœ˜Sšžœž˜Jšœžœžœžœ˜;Jšœ žœžœžœ˜-Jš žœžœžœžœ˜I—Jšžœžœ žœžœ˜%J˜Jšœžœ˜Jšœ-˜-J˜—Jšžœžœ˜5JšœC˜CJšžœ˜J˜—š Ÿœžœžœ"žœžœž˜RšŸœžœž˜#Jš žœžœžœžœžœžœ˜7Jšžœžœžœžœ˜=J˜J˜!J˜J˜-Jšžœ˜—J˜Jšžœžœ˜5Jšžœžœ˜7JšœG˜GJšžœ˜J˜—š Ÿœžœžœ"žœžœž˜RšŸœžœž˜#Jš žœžœžœžœž ˜7Jšžœžœžœžœ˜=J˜šžœ'žœž˜3J˜Jšœ)˜)Jšž˜—šžœž˜ J˜šžœ7žœž˜SJšžœžœ˜Jšžœ˜—J˜J˜Jšžœ˜—Jšœ-˜-Jšžœ˜J˜—Jšžœžœ˜5Jšžœžœ˜7JšœG˜GJšžœ˜J˜—šŸ œžœžœž˜5šŸœžœ˜š žœ žœžœ žœžœž˜AJšžœžœžœ ˜E—J˜J˜J˜J˜J˜J˜J˜ J˜J˜ J˜J˜7Jšœžœžœ˜FJšžœ žœ#˜6J˜—J˜Jšžœžœ˜/Jšžœžœ˜/JšœH˜HJšžœ žœ!˜3Jšžœ˜J˜—J˜šŸ œžœžœžœ˜AJšœ žœž˜*Jšžœžœžœ˜šŸœžœ˜Jšœ žœ˜"Jšœžœ˜&J˜J˜J˜J˜-J˜'šžœžœ˜%Jšœžœžœ˜*Jšœžœžœ˜)—šžœ˜Jšœžœžœ˜)Jšœžœžœ˜+—šžœžœ˜2Jšœ&žœžœ˜3šžœ/žœžœž˜BJšžœ žœžœžœ˜'Jšžœžœ˜3Jšžœ˜ ——J˜—J˜J˜J˜šœ8˜8J˜—J˜J˜Jšžœ˜J˜—šŸ œžœžœ'ž˜EJšžœžœ˜'J˜Jšœžœžœ˜Jšœ žœžœ˜šŸœžœ˜J˜2J˜Jšžœžœžœ˜%Jšžœžœžœ˜BJ™šžœžœž˜šžœžœžœ˜(Jšœ˜Jšœ)žœ˜1—šžœžœ˜?Jšžœ žœžœžœ˜=—Jšœ˜Jšžœžœ$žœ˜DJšžœ˜—Jšœ™šžœž ˜J˜J˜J˜J˜J˜(J˜(Jšžœ˜—Jšœ ™ Jšžœžœžœ˜5šžœžœ˜J˜.Jšœžœ˜J˜—J˜%J˜J˜—J˜Jšžœ1žœžœžœ˜KJ˜šžœ,žœ˜4J˜Jšžœžœžœ˜#—J˜šœ5˜5Jšœ!˜!J˜—Jšžœ žœ˜+J˜Jšœ5žœž˜Jšœ˜Jšœžœ˜J˜J˜!Jšœ/˜/J˜Jšœ.˜EJšœ4žœžœ˜AJšžœ žœ˜,Jšœ*˜*Jšžœžœ˜:Jšžœ˜J˜—Jšžœ'žœžœžœ˜AJ˜šžœ.žœ˜6Jšœ-™-J˜"Jšžœžœ˜.J˜—Jšœ˜Jšžœžœ˜1šœF˜FJ˜—Jšœ+žœ˜2Jšžœ˜J˜—š Ÿ œžœžœžœžœž˜EJšžœžœžœ˜šŸœžœ˜Jšœ#žœ˜*Jšžœžœžœžœ˜5šžœžœž˜JšœOžœ˜UJ˜Jšžœ˜Jšžœ˜—Jšœ˜J˜J˜Jšœžœ˜J˜J˜%Jšœ˜J˜,J˜J˜—Jšžœ*žœžœžœ˜DJ˜J˜Jšžœžœ˜5JšœK˜Kšžœžœ˜'J˜—Jšœ.žœ˜5Jšžœ˜J˜—š Ÿœžœžœ#žœžœ˜OJšžœ˜"Jš žœžœžœžœžœžœ˜NJšœ˜Jšœ˜Jšžœžœ'˜LJšœžœ˜#Jšœ"žœ˜)Jšœžœ˜Jšœ˜Jšžœ˜J˜—š Ÿ œžœžœžœžœž˜DJšœžœžœ˜JšŸœžœ1˜JšŸœžœ˜šžœ6žœžœž˜IJšžœ žœžœ˜8Jšžœ˜ ——J˜Jšžœžœžœ žœ˜HJ˜Jšžœ)žœžœžœ˜CJ˜Jšžœžœ˜5J˜JšœJ˜Jš žœžœžœžœžœž˜=Jšœžœ-˜UJšžœ˜ —J˜šžœžœžœžœ˜&š žœžœžœžœžœž˜=Jšžœžœ%˜;Jšžœ˜—JšœE˜EJ˜—Jšœ,ž˜4Jšžœ˜J˜—šœ$žœ˜*Jš0Πck˜5šŸœžœ˜Jšœžœžœ ˜;Jšœžœžœ "˜@šœžœžœžœ˜AJšžœžœžœ ˜1—šœ"˜"šžœ žœ˜)Jšžœžœ˜+—J˜—šžœžœ4˜EJšœ'˜'Jšžœžœ žœ˜NJšœ7˜7Jšžœžœžœ˜Jšœ˜—Jšœžœ.˜DJšœžœ-˜BJšœ8˜8Jšœ)žœ˜.Jš žœžœžœžœžœžœ ˜KJ˜š žœžœžœ žœžœž˜>Jšœžœž˜Icodešžœžœžœ/žœ˜Ršžœžœžœ ˜:Kšžœ*žœ˜5—Kšžœ˜—Jšœ(˜(—J˜Jš žœžœžœžœžœ˜7Jš žœžœžœžœžœ˜MJšœI˜IJ˜Jšžœ˜—J˜š Ÿ œžœžœžœžœž˜GJšžœžœžœ˜JšŸœžœ*˜CJšœ=˜=Jšžœ˜J˜—š Ÿœžœ žœžœž˜OJš ™ Jšœžœžœ˜J˜Jšœžœ˜&Jšœ žœ˜#Jšœžœ˜&Jšœ žœ˜,Jšœ*žœ˜6š Ÿ œžœ žœžœž˜:Jšœžœ(œ˜@—šŸ œžœ žœ˜*Jšœžœ˜Jšœ˜šžœ/žœžœž˜@šžœ"žœ˜*Jšœ5˜5—šžœžœžœž˜?J˜3J˜Jšžœ˜—Jšžœ˜ —J˜—Jšœ-˜AJšžœžœžœ˜-˜JšœE™EJšœQ™QJšœY™YJ™J˜@Jšœ&˜&Jšœ˜šžœ@žœ˜HJšœžœ˜+—šžœžœ#˜?šžœ?˜CJšœ$žœ˜@——J˜šžœ/žœžœž˜@šœžœžœžœž˜"Jšœ˜Jšœžœ,˜5Jšžœ˜—šžœžœžœ˜2Jšžœžœžœ˜8Jšžœžœžœ˜5—J˜2J˜Jšžœ˜ ——J˜Jšžœžœ.˜;Jšžœ˜J˜—šŸ œžœžœ+žœ˜^Jšž ˜ šžœž˜šœ ˜ J˜&J˜J˜Jšœ˜—šœ ˜ J˜'J˜J˜Jšœ˜—šœ ˜ J˜!J˜J˜ Jšœ˜—Jšžœžœ˜—Jšžœ˜—J˜Jšœ žœ2˜EJšœžœ˜-š œžœžœžœžœžœ˜CJ˜—š Ÿ œž œžœžœž˜FJ˜Jšœžœ˜š‘ œ˜$Jš žœ žœžœžœžœ˜/Jšžœžœžœžœžœžœžœ˜I—J˜Jšžœžœžœ˜J˜"Jšœ žœ˜&Jšžœžœ žœžœ˜š žœžœž œžœ'ž˜Eš žœžœžœžœž œžœž˜KJ˜Jšžœžœžœ˜"Jšž˜—š žœžœžœžœž˜)J˜Jšžœžœžœ˜"Jšžœ˜—Jšžœ˜—Jšžœ žœžœžœ˜1J˜)Jšžœ žœžœžœ˜Jšœ8žœžœ˜EJšœ"žœ˜(Jšœ ˜ Jšœ"žœ˜'Jšœ˜Jšžœžœ˜8Jšžœ˜J˜—šŸ œžœžœž˜>J˜š‘ œ˜$Jš žœ žœžœžœžœ˜/Jšžœžœžœžœžœžœžœ˜K—š Ÿ œžœžœžœž˜Bšžœžœžœž˜#š žœ žœžœžœž œžœž˜BJ˜Jšžœžœžœ˜%Jšž˜—š žœžœžœžœž˜)J˜Jšžœžœžœ˜%Jšžœ˜—Jšžœ˜—Jšžœ˜—J˜šžœ žœžœ˜HJšœ)˜)J˜2Jšœ$žœ˜)Jšœ˜J˜—Jšœžœ žœžœ˜IJšœ"žœ˜'J˜ Jšœ˜Jšžœ˜J˜—šŸœžœž ˜3šœ'žœ.˜XJ˜LJ˜ —Jšžœ˜—J˜š Ÿ œžœžœžœžœ˜FJš œžœžœžœžœ˜(Jšžœžœ˜'šŸœžœ˜Jšœžœ˜ Jšœ+˜+Jšœ&˜&Jš žœžœ žœžœžœ˜&Jšœ0žœ˜FJ˜—Jšœ˜J˜šœ9˜9J˜—Jš žœžœ žœžœžœžœ˜FJ˜Jšžœ˜J˜—šŸœžœžœžœ˜IJšž˜JšŸœžœ+˜NJšœF˜FJšžœ˜J˜—šŸœžœžœ˜