<> <> <> <> <<>> DIRECTORY Atom USING [GetPropFromList, PutPropOnList], Carets USING [ResumeCarets, SuspendCarets], ColorDisplay USING [height, width], Cursors USING [InvertCursor], InputFocus USING [GetInputFocus, SetInputFocus], List USING [Sort, CompareProc], Menus USING [CopyMenu, Menu], MessageWindow USING [Append, Blink, Clear], Process USING [Detach, GetPriority, Priority, priorityForeground, SetPriority], RefTab USING [Create, Fetch, Ref, Store], Rope USING [Compare, ROPE], RTOS USING [GetCurrent, RegisterCedarProcess, UnregisterCedarProcess], SafeStorage USING [NewZone], TIPUser USING [TIPScreenCoords], ViewerEvents USING [EventProc, ProcessEvent, RegisterEventProc], ViewerOps, ViewerLocks, ViewerClasses, ViewerSpecs, WindowManager USING [colorDisplayOn], WindowManagerPrivate; ViewerOpsImpl: CEDAR PROGRAM IMPORTS Atom, Carets, ColorDisplay, Cursors, InputFocus, List, Menus, MessageWindow, Process, RefTab, Rope, RTOS, SafeStorage, ViewerEvents, ViewerLocks, ViewerOps, WindowManager EXPORTS ViewerOps, ViewerSpecs, WindowManagerPrivate SHARES Menus, ViewerClasses, ViewerEvents, ViewerOps, ViewerLocks = BEGIN OPEN ViewerClasses, ViewerSpecs, ViewerOps, WindowManager; vZ: ZONE _ SafeStorage.NewZone[quantized]; fatalViewersError: ERROR = CODE; openLeftWidth: PUBLIC INTEGER _ 600; openRightLeftX: PUBLIC INTEGER _ openLeftLeftX+openLeftWidth; openRightWidth: PUBLIC INTEGER _ screenW-openLeftWidth; openBottomY: PUBLIC INTEGER _ iconHeight+iconSpacing; classTable: RefTab.Ref _ RefTab.Create[mod:50]; -- hold viewer classes rootViewerTree: PUBLIC ARRAY Column OF Viewer _ ALL[NIL]; RegisterViewerClass: PUBLIC PROC [flavor: ViewerFlavor, class: ViewerClass] = { class.flavor _ flavor; [] _ RefTab.Store[classTable, flavor, class]}; <> FetchViewerClass: PUBLIC PROC [flavor: ViewerFlavor] RETURNS [ViewerClass] = {IF flavor = $TypeScript THEN flavor _ $Typescript; RETURN[NARROW[RefTab.Fetch[classTable, flavor].val]]}; <> 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]; new.init _ TRUE}; new _ vZ.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 THEN IF topLevel AND defaultPosition AND ~new.iconic THEN InternalPaintColumn[new.column] ELSE 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 paintParent, paintColumn: BOOL _ FALSE; parent, filler: Viewer; x,y,w,h: INTEGER; fillerX, fillerY: INTEGER; onlyLink: Viewer; -- set only if the only link 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 [filler, fillerX, fillerY] _ RemoveIcon[icon: viewer, fillSlot: TRUE]; IF filler # NIL THEN GreyScreen[fillerX, fillerY, iconWidth, iconHeight, FALSE, TRUE]; END ELSE {InternalComputeColumn[viewer.column]; paintColumn _ paint}; }; IF ViewerEvents.ProcessEvent[destroy, viewer, TRUE] THEN RETURN; KillInputFocus[viewer]; ViewerLocks.CallUnderWriteLock[LockedDestroyViewer, viewer]; IF paintParent THEN PaintViewer[parent, all]; IF paintColumn THEN InternalPaintColumn[viewer.column]; <> IF filler # NIL THEN ViewerOps.PaintViewer[filler, all]; IF onlyLink#NIL AND (onlyLink.iconic#viewer.iconic OR onlyLink.column#viewer.column) 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.file _ NIL; viewer.menu _ NIL; viewer.props _ NIL; viewer.child _ NIL; viewer.data _ NIL; END; ViewerLocks.CallUnderWriteLock[LockedBottomUpEtc, viewer]; END; EnumerateViewers: PUBLIC PROC [enum: EnumProc] = BEGIN <> FOR c: Column DECREASING IN Column DO -- decreasing so will try static viewers last v: Viewer _ rootViewerTree[c]; next: Viewer; UNTIL v=NIL DO next _ v.sibling; IF ~enum[v] THEN RETURN; v _ next; ENDLOOP; ENDLOOP; END; EnumerateChildren: PUBLIC PROC [viewer: Viewer, enum: EnumProc] = BEGIN v: Viewer _ viewer.child; next: Viewer; WHILE v#NIL DO next _ v.sibling; EnumerateChildren[v, enum]; IF ~enum[v] THEN RETURN; v _ next; 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; FindViewer: PUBLIC PROC [name: Rope.ROPE] RETURNS [viewer: Viewer] = BEGIN MatchName: EnumProc = BEGIN IF Rope.Compare[name, v.name, FALSE]=equal THEN BEGIN viewer _ v; RETURN[FALSE]; END ELSE RETURN[TRUE]; END; EnumerateViewers[MatchName]; END; SaveViewer: PUBLIC PROC [viewer: Viewer] = BEGIN list: LIST OF Viewer; LockedStart: PROC = { FOR v: Viewer _ viewer, v.link DO v.saveInProgress _ TRUE; PaintViewer[v, caption, FALSE]; list _ CONS[v, list]; IF v.link = NIL OR v.link = viewer THEN EXIT; ENDLOOP}; LockedStop: PROC = { FOR saved: LIST OF Viewer _ list, saved.rest UNTIL saved = NIL DO v: Viewer = saved.first; IF v.destroyed THEN LOOP; v.newVersion _ v.newFile _ v.saveInProgress _ FALSE; PaintViewer[v, caption, FALSE]; [] _ ViewerEvents.ProcessEvent[save, v, FALSE]; ENDLOOP}; FOR v: Viewer _ viewer, v.link DO IF ViewerEvents.ProcessEvent[save, v, TRUE].abort THEN RETURN; IF v.link = NIL OR v.link = viewer THEN EXIT; ENDLOOP; <> LockedStart[]; IF viewer.class.save#NIL THEN viewer.class.save[viewer]; LockedStop[]; END; RestoreViewer: PUBLIC PROC [viewer: Viewer] = BEGIN DoOne: PROC [v: Viewer] = INLINE BEGIN KillInputFocus[v]; IF v.class.init # NIL THEN v.class.init[v]; v.newVersion _ v.newFile _ FALSE; PaintViewer[v, all]; END; DoOne[viewer]; IF viewer.link#NIL THEN FOR v: Viewer _ viewer.link, v.link UNTIL v=viewer DO DoOne[v]; ENDLOOP; END; SetMenu: PUBLIC PROC [viewer: Viewer, menu: Menus.Menu _ NIL, paint: BOOL _ TRUE] = BEGIN sameSize: BOOL = (menu#NIL AND viewer.menu#NIL AND viewer.menu.linesUsed=menu.linesUsed); IF viewer.iconic THEN paint _ FALSE; IF viewer.parent#NIL THEN ERROR; viewer.menu _ IF menu=NIL THEN NIL ELSE Menus.CopyMenu[menu]; IF ~sameSize AND paint THEN EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh]; IF paint THEN PaintViewer[viewer, IF sameSize THEN menu ELSE all]; END; IndicateNewVersion: PUBLIC PROC [viewer: Viewer] = BEGIN link1, link2: Viewer; LockedNewVersion: PROC = { FOR v: Viewer _ viewer, v.link DO v.newVersion _ TRUE; PaintViewer[v, caption]; [] _ ViewerEvents.ProcessEvent[edit, v, FALSE]; IF v.link = NIL OR v.link = viewer THEN EXIT; ENDLOOP}; FOR v: Viewer _ viewer, v.link DO IF ViewerEvents.ProcessEvent[edit, v, TRUE].abort THEN RETURN; IF v.link = NIL OR v.link = viewer THEN EXIT; ENDLOOP; IF viewer # NIL THEN link1 _ viewer.link; IF link1 # NIL THEN link2 _ link1.link; ViewerLocks.CallUnderReadLocks[LockedNewVersion, viewer, link1, link2]; END; SetNewFile: PUBLIC PROC [viewer: Viewer] = BEGIN DoOne: PROC [v: Viewer] = BEGIN v.newFile _ TRUE; PaintViewer[v, caption]; END; DoOne[viewer]; IF viewer.link#NIL THEN FOR v: Viewer _ viewer.link, v.link UNTIL v=viewer DO DoOne[v]; ENDLOOP; 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]}; IF viewer.offDeskTop THEN ChangeColumn[viewer, left]; ViewerLocks.CallUnderColumnLock[LockedTopViewer, viewer.column]; IF paint THEN InternalPaintColumn[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]}; IF viewer.offDeskTop THEN ChangeColumn[viewer, left]; ViewerLocks.CallUnderColumnLock[LockedBottomViewer, viewer.column]; IF paint THEN InternalPaintColumn[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 {paint _ FALSE; RETURN}; IF altered.column#static.column OR static.iconic THEN {paint _ FALSE; RETURN}; UnlinkViewer[altered]; altered.sibling _ static.sibling; static.sibling _ altered; InternalComputeColumn[altered.column]; END; IF static.offDeskTop THEN ChangeColumn[static, left]; IF altered.offDeskTop THEN ChangeColumn[altered, left]; ViewerLocks.CallUnderColumnLock[LockedMoveAboveViewer, altered.column]; IF paint THEN InternalPaintColumn[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 {paint _ FALSE; RETURN}; IF altered.column#static.column OR static.iconic THEN {paint _ FALSE; 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]; END; IF static.offDeskTop THEN ChangeColumn[static, left]; IF altered.offDeskTop THEN ChangeColumn[altered, left]; ViewerLocks.CallUnderColumnLock[LockedMoveBelowViewer, altered.column]; IF paint THEN InternalPaintColumn[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 }; KillInputFocus[old]; IF old.offDeskTop THEN ChangeColumn[old, left]; IF new.offDeskTop THEN ChangeColumn[new, left]; ViewerLocks.CallUnderColumnLocks[LockedReplaceViewer, ViewerColumn[new], ViewerColumn[old]]; PaintViewer[new, all]; END; OpenIcon: PUBLIC PROC [icon: Viewer, closeOthers: BOOL _ FALSE, bottom: BOOL _ TRUE, paint: BOOL _ TRUE] = BEGIN filler: Viewer; fillerX, fillerY: INTEGER; 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]; [filler, fillerX, fillerY] _ 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 filler # NIL THEN GreyScreen[fillerX, fillerY, iconWidth, iconHeight, FALSE, TRUE]; InternalComputeColumn[icon.column]; 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]; IF filler # NIL THEN ViewerOps.PaintViewer[filler, all]; IF closeOthers THEN GrowViewer[icon, paint] ELSE IF paint THEN InternalPaintColumn[icon.column]; [] _ 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]; }; 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 { InternalPaintColumn[viewer.column]; PaintViewer[viewer, all]}; [] _ ViewerEvents.ProcessEvent[close, viewer, FALSE]; END; GrowViewer: PUBLIC PROC [viewer: Viewer, paint: BOOL _ TRUE] = BEGIN inThisColumn: LIST OF Viewer; 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 ViewerOps.PaintViewer[v.first, all]; ENDLOOP; ViewerOps.PaintViewer[viewer, all]}; [] _ 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; 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; MoveBoundary: PUBLIC PROC [newLeftWidth: INTEGER _ openLeftWidth, newBottomY: INTEGER _ openBottomY] = BEGIN ENABLE UNWIND => NULL; LockedMoveBoundary: PROC = { ResetPaintCache[]; openLeftWidth _ newLeftWidth; openRightLeftX _ openLeftLeftX+openLeftWidth; openRightWidth _ screenW-openLeftWidth; openBottomY _ newBottomY; GreyScreen[0, 0, screenW, openLeftTopY, FALSE, FALSE]; InternalComputeColumn[left]; InternalComputeColumn[right]; }; Carets.SuspendCarets[]; ViewerLocks.CallUnderViewerTreeLock[LockedMoveBoundary]; InternalPaintColumn[left]; InternalPaintColumn[right]; FOR v: Viewer _ rootViewerTree[static], v.sibling UNTIL v=NIL DO IF v.iconic THEN TRUSTED {Process.Detach[FORK PaintViewer[v, all]]}; ENDLOOP; WaitForPaintingToFinish[]; Carets.ResumeCarets[]; END; ChangeColumn: PUBLIC PROC [viewer: Viewer, newColumn: Column] = BEGIN ENABLE UNWIND => Carets.ResumeCarets[]; oldColumn: Column; paintIcon: BOOL _ FALSE; paintColumns: BOOL _ FALSE; LockedChangeColumn: PROC = { SetColumn: EnumProc = {v.column _ viewer.column} ; oldColumn _ viewer.column; IF oldColumn = newColumn THEN RETURN; <> IF viewer.iconic THEN BEGIN IF oldColumn # static THEN { [] _ RemoveIcon[viewer]; GreyScreen[viewer.wx, viewer.wy, viewer.ww, viewer.wh, viewer.column=color AND ~viewer.iconic, viewer.iconic]}; 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]; InternalComputeColumn[oldColumn]; InternalComputeColumn[newColumn]; paintColumns _ TRUE; 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]; Carets.SuspendCarets[]; ResetPaintCache[]; IF paintIcon THEN PaintViewer[viewer, all]; IF paintColumns THEN BEGIN IF oldColumn # static THEN InternalPaintColumn[oldColumn]; IF newColumn # static THEN InternalPaintColumn[newColumn]; END; Carets.ResumeCarets[]; [] _ ViewerEvents.ProcessEvent[changeColumn, viewer, FALSE]; END; ComputeColumn: PUBLIC PROC [column: Column, paint: BOOL _ TRUE] = BEGIN ENABLE UNWIND => NULL; LockedComputeColumn: PROC = {InternalComputeColumn[column]}; ViewerLocks.CallUnderColumnLock[LockedComputeColumn, column]; IF paint THEN InternalPaintColumn[column]; END; InternalComputeColumn: PROC [column: Column] = 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 THEN RETURN; IF openViewers=0 THEN RETURN; <> < 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; END; InternalPaintColumn: PROC [column: Column] = BEGIN totalSpace, leftX, width, nextY: INTEGER; Carets.SuspendCarets[]; IF rootViewerTree[column] = NIL AND column # static THEN { [totalSpace, leftX, width, , nextY] _ ColumnInfo[column]; GreyScreen[leftX, nextY, width, totalSpace, column=color, FALSE]; IF column # color AND openBottomY < iconTopY THEN -- paint icons FOR v: Viewer _ rootViewerTree[static], v.sibling UNTIL v=NIL DO IF v.iconic AND ~v.offDeskTop THEN TRUSTED {Process.Detach[FORK PaintViewer[v, all]]}; ENDLOOP} ELSE FOR v: Viewer _ rootViewerTree[column], v.sibling UNTIL v=NIL DO IF ~v.offDeskTop THEN TRUSTED {Process.Detach[FORK PaintViewer[v, all]]}; ENDLOOP; WaitForPaintingToFinish[]; Carets.ResumeCarets[]; 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; SetOpenHeight: PUBLIC PROC [viewer: Viewer, clientHeight: INTEGER] = BEGIN overhead: INTEGER _ captionHeight + (IF viewer.border THEN windowBorderSize ELSE 0); IF viewer.menu#NIL THEN overhead _ overhead+(viewer.menu.linesUsed*menuHeight)+menuBarHeight; viewer.openHeight _ clientHeight+overhead; 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] RETURNS[filler: Viewer, oldX, oldY: INTEGER] = BEGIN slot: INTEGER _ 0; FindIcon: ViewerOps.EnumProc = { IF v.position = slot THEN {filler _ v; RETURN[FALSE]} ELSE RETURN[TRUE]}; 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[FindIcon]; IF filler = NIL THEN RETURN; oldX _ filler.wx; oldY _ filler.wy; iconSlotsArray[filler.position] _ FALSE; iconSlotsArray[icon.position] _ TRUE; filler.position _ icon.position; SetIconPosition[filler]; END; PositionIcon: PROC [viewer: Viewer] = BEGIN 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 BEGIN iconSlotsArray[slot] _ TRUE; RETURN; END; ENDLOOP ELSE FOR n: CARDINAL IN [0..iconSlots) DO slot _ m*iconSlots+n; IF ~iconSlotsArray[slot] THEN BEGIN iconSlotsArray[slot] _ TRUE; RETURN; END; ENDLOOP; ENDLOOP; END; viewer.position _ FindIconSlot[viewer.column]; SetIconPosition[viewer]; iconSlotsFree _ iconSlotsFree-1; 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.CallUnderReadLock[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; 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; SaveAllEdits: PUBLIC PROC = BEGIN <> old: Process.Priority = Process.GetPriority[]; Save: EnumProc = BEGIN Cursors.InvertCursor[]; IF (v.newVersion OR v.newFile) AND v.class.save # NIL THEN v.class.save[v, TRUE ! ANY => CONTINUE]; v.newVersion _ v.newFile _ FALSE; IF v.icon=dirtyDocument THEN v.icon _ document; IF v.link#NIL THEN FOR t: Viewer _ v.link, t.link UNTIL t=v DO t.newVersion _ t.newFile _ FALSE; ENDLOOP; Cursors.InvertCursor[]; RETURN[TRUE]; END; IF old>Process.priorityForeground THEN TRUSTED BEGIN -- called from CoPilot Process.SetPriority[Process.priorityForeground]; RTOS.RegisterCedarProcess[RTOS.GetCurrent[]]; END; EnumerateViewers[Save]; IF old>Process.priorityForeground THEN TRUSTED BEGIN -- called from CoPilot RTOS.UnregisterCedarProcess[RTOS.GetCurrent[]]; Process.SetPriority[old]; END; END; PaintEverything: PUBLIC PROC = BEGIN OPEN Process; ResetPaintCache[]; Carets.SuspendCarets[]; GreyScreen[0, 0, 9999, 9999, FALSE, FALSE]; ComputeColumn[static]; ComputeColumn[left]; ComputeColumn[right]; IF WindowManager.colorDisplayOn THEN ComputeColumn[color]; WaitForPaintingToFinish[]; Carets.ResumeCarets[]; END; UserToScreenCoords: PUBLIC PROC [self: Viewer, vx, vy: INTEGER _ 0] RETURNS [sx, sy: INTEGER] = BEGIN invert: BOOL; UNTIL self=NIL DO -- compute enclosing viewer offsets invert _ IF self.parent=NIL THEN self.class.coordSys=top ELSE self.class.coordSys#self.parent.class.coordSys; vx _ vx + self.cx; IF invert THEN vy _ Top2Bottom[self, vy]; vy _ vy + self.cy; self _ self.parent; ENDLOOP; RETURN [vx, vy]; END; MouseInViewer: PUBLIC PROC [tsc: TIPUser.TIPScreenCoords] RETURNS [viewer: Viewer, client: BOOL] = BEGIN ENABLE UNWIND => NULL; x: INTEGER _ tsc.mouseX; y: INTEGER _ tsc.mouseY; invert: BOOL; TopLevelHit: PROC RETURNS [Viewer] = BEGIN FOR c: Column DECREASING IN Column DO FOR v: Viewer _ rootViewerTree[c], v.sibling UNTIL v=NIL DO IF ViewerHit[v] THEN RETURN[v]; ENDLOOP; REPEAT FINISHED => RETURN[NIL]; ENDLOOP; END; ViewerHit: PROC [v: Viewer] RETURNS [BOOL] = INLINE {RETURN[x IN [v.wx..v.wx+v.ww) AND y IN [v.wy..v.wy+v.wh) AND (IF tsc.color THEN (v.column=color AND ~v.iconic) ELSE (v.column#color OR v.iconic))]}; Client: PROC RETURNS [BOOL] = INLINE {RETURN[y IN [viewer.cy..viewer.cy+viewer.ch) AND x IN [viewer.cx..viewer.cx+viewer.cw)]}; CheckViewersChildren: PROC [v: Viewer] RETURNS [BOOL] = BEGIN ex, ey: INTEGER; EmbeddedViewerHit: PROC [v: Viewer] RETURNS [BOOL] = INLINE {RETURN[ex IN [v.wx..v.wx+v.ww) AND ey IN [v.wy..v.wy+v.wh)]}; ex _ x - v.cx; ey _ y - v.cy; invert _ IF v.parent=NIL THEN v.class.coordSys=top ELSE v.class.coordSys#v.parent.class.coordSys; IF invert THEN ey _ Bottom2Top[v, ey]; FOR v _ v.child, v.sibling UNTIL v=NIL DO IF EmbeddedViewerHit[v] THEN BEGIN <> x _ ex; y _ ey; viewer _ v; RETURN[TRUE]; END; ENDLOOP; RETURN[FALSE]; END; IF (viewer _ TopLevelHit[]) = NIL THEN RETURN [NIL, FALSE]; IF viewer.child#NIL AND ~viewer.iconic AND Client[] THEN WHILE viewer.child#NIL AND CheckViewersChildren[viewer] DO ENDLOOP; client _ Client[]; invert _ IF viewer.parent=NIL THEN client AND viewer.class.coordSys=top ELSE viewer.class.coordSys#viewer.parent.class.coordSys; <> tsc.mouseX _ x - (IF client THEN viewer.cx ELSE viewer.wx); y _ y - (IF client THEN viewer.cy ELSE viewer.wy); tsc.mouseY _ IF ~invert THEN y ELSE IF client THEN Top2Bottom[viewer, y] ELSE viewer.wh-y; END; [] _ ViewerEvents.RegisterEventProc[GrowExtra, grow, NIL, TRUE]; [] _ ViewerEvents.RegisterEventProc[GrowExtra, grow, NIL, FALSE]; [] _ ViewerEvents.RegisterEventProc[GrowExtra, destroy]; END.