<> <> <> <> <> DIRECTORY Atom USING [GetPropFromList, PutPropOnList], Carets USING [ResumeCarets, SuspendCarets], InputFocus USING [GetInputFocus, SetInputFocus], List USING [CompareProc, Sort], Menus USING [CopyMenu, Menu], MessageWindow USING [Append, Blink, Clear], 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 PROGRAM IMPORTS Atom, Carets, InputFocus, List, Menus, MessageWindow, 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 ARRAY Column OF Viewer _ ALL[NIL]; <> AddProp: PUBLIC PROC [viewer: Viewer, prop: ATOM, val: REF ANY] = { 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; 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 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> 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] = { ENABLE UNWIND => NULL; IF viewer.destroyed THEN RETURN; -- already destroyed; nop IF viewer=NIL THEN ERROR fatalViewersError; 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; <> 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]; <> 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]; <> 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; 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; 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[bw, 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 { <> 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> 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 { <> 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] = { <> 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 { totalRequested _ totalRequested + DesiredHeight[v]; requests _ requests + 1; }; ENDLOOP; }; IF column # static AND openViewers # 0 THEN { <> < column height, ignore requests for more than average height.>> <> [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 ~ 12*16; IconSlotsArray: TYPE ~ PACKED ARRAY [0..maxIcons) OF BOOL; iconSlotsArray: REF IconSlotsArray _ NEW[IconSlotsArray _ ALL[FALSE]]; iconSlotsUsed: [0..maxIcons] _ 0; 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; iconSlotsUsed _ iconSlotsUsed-1; iconSlotsArray[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]; iconSlotsArray[filler.position] _ FALSE; filler.position _ icon.position; iconSlotsArray[filler.position] _ TRUE; SetIconPosition[filler]; ForkPaintViewerAll[filler]; }; PositionIcon: PROC [viewer: Viewer, slot: INTEGER _ 0] = { rows: NAT ~ ViewerSpecs.iconRows; columns: NAT ~ ViewerSpecs.iconColumns; slotIcon: Viewer; 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 NOT iconSlotsArray[slot] THEN RETURN; ENDLOOP ELSE FOR n: CARDINAL IN [0..columns) DO slot _ m*columns+n; IF NOT 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; iconSlotsUsed _ iconSlotsUsed+1; 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 v.sibling _ new; ENDLOOP; }; UnlinkViewer: PROC [old: Viewer] = { column: Column ~ ViewerColumn[old]; IF old.parent = NIL THEN { <> 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 { <> 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.