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; [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. κViewerOpsImpl.mesa; Written by S. McGregor Edited by McGregor on March 4, 1983 10:52 am Edited by Maxwell, February 7, 1983 3:41 pm Edited by Plass, May 4, 1983 11:27 am let the Viewer package know about a new class of viewer. class information from an existing viewer class. not locked, but these are not ordinary times! IF new.column = static THEN { -- create a viewer in the middle of the screen new.iconic _ FALSE; new.wx _ 1; -- prevent call to InternalComputeColumn LockedCreateViewer[]; new.column _ left; -- fool SetViewerPosition into leaving room for menu SetViewerPosition[new, 300, 100, 500, 500]; new.column _ static; ViewerOps.PaintViewer[new, all]; RETURN}}; 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) Safe in that caller can move the enumerated viewer in the tree in which case all viewers get seen at least once Not called under locks so viewers can be saved when the column is locked. not locked, but these are not ordinary times! IF icon.column = static THEN { -- create a window in the middle of the screen LockedOpenIcon[]; icon.column _ left; -- to fool SetViewerPosition into leaving room for menu SetViewerPosition[icon, 300, 100, 500, 500]; icon.column _ static; ViewerOps.PaintViewer[icon, all]; RETURN}}; iconic case non iconic case hyperspace 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. poor man's crash recovery embedded frames are client info relative overwrite screen coords record Κ.– "Mesa" style˜JšΟc*™*Jšœ™,J™+J™%J™šΟk ˜ Jšœžœ"˜,Jšœžœ˜+Jšœ žœ˜#Jšœžœ˜Jšœ žœ ˜0Jšœžœ˜Jšœžœ˜Jšœžœ˜+JšœžœB˜OJšœžœ˜)Jšœžœ žœ˜Jšžœžœ<˜FJšœ žœ ˜Jšœžœ˜ Jšœ žœ.˜@J˜ J˜ J˜J˜ Jšœžœ˜%J˜J˜—Jšœž ˜J˜JšžœežœB˜²Jšžœ-˜4Jšžœ=˜CJ˜Jšžœžœ6˜@J˜Jšœžœ"˜*J˜Jšœžœžœ˜ J˜Jšœžœžœ˜$Jšœžœžœ˜=šœžœžœ˜7J˜—Jšœ žœžœ˜5J˜šœ0˜GJ˜—Jš œžœžœžœ žœžœ˜9J˜šΟnœžœžœ/˜OJ˜J˜.Jš8™8J˜—šŸœžœžœžœ˜LJšœžœžœ˜3Jšžœžœ)˜6Jš0™0J˜—J˜š Ÿ œžœžœ5žœžœ˜[Jšžœžœ˜Jšžœžœžœ˜Jšœ žœ˜Jšœžœ˜šŸœžœ˜J˜Jšžœ ˜$Jšžœ˜—Jšœ,žœ˜3Jšžœ˜J˜—š Ÿ œžœžœžœžœž˜GJšžœžœžœ˜Jšžœžœžœ˜;Jšžœžœžœžœ˜+Jšœ%žœ˜,Jšžœ˜J˜—šŸœžœ"žœž˜JJšœžœžœ˜'Jšœ˜Jšœ žœ˜Jšœžœ˜Jšœ˜.J˜šŸœžœ˜Jšœ˜5J˜;J˜J˜J˜4J˜šžœ žœžœž˜šžœžœž˜%Jšœžœ˜Jšžœžœžœ˜6Jšž˜—šžœžœ&žœž˜DJšžœžœ˜(Jšžœ˜—Jšœžœ˜Jšžœ˜J˜—šžœžœžœž˜Jšžœžœ˜9š žœžœžœžœž˜0šžœ+žœžœž˜?Jšžœžœžœžœ%˜UJšžœ˜——Jšœžœ˜Jšœ˜Jšž˜—šžœžœžœž˜ Jšœžœžœ˜6Jšžœ žœžœΟi;˜VJšœ@žœ˜FJš žœ žœžœ5žœžœ˜VJšž˜—Jšžœ=˜AJ˜—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˜J˜—J˜šŸœžœžœž˜6Jšo™oš žœ ž œžœžœ-˜SJ˜J˜ šžœžœž˜J˜Jšžœ žœžœ˜J˜ Jšžœ˜—Jšžœ˜—Jšžœ˜J˜—šŸœžœžœ$ž˜GJ˜Jšœ ž˜ šžœžœž˜J˜J˜Jšžœ žœžœ˜J˜ Jšžœ˜—Jšžœ˜J˜—š Ÿ œžœ žœ žœž˜?J˜ šžœ*žœžœž˜;Jšœ˜Jšžœ˜—Jšžœ˜J˜—š Ÿ œžœžœ žœžœž˜Jšœž˜šžœžœžœž˜5J˜ Jšžœžœ˜Jšž˜—Jšžœžœžœ˜Jšžœ˜—J˜Jšžœ˜J˜—J˜šŸ œžœžœž˜0Jšœžœžœ˜šŸ œžœ˜šžœž˜!Jšœžœ˜Jšœžœ˜Jšœžœ ˜Jš žœ žœžœžœžœ˜-Jšžœ˜ ——šŸ œžœ˜š žœžœžœžœ žœž˜AJšœ˜Jšžœ žœžœ˜Jšœ.žœ˜4Jšœžœ˜Jšœ(žœ˜/Jšžœ˜ J˜——šžœž˜!Jšžœ$žœžœžœ˜>Jš žœ žœžœžœžœ˜-Jšžœ˜—J˜JšœI™IJšœ˜Jšžœžœžœ˜8Jšœ ˜ J˜Jšžœ˜J˜—šŸ œžœžœž˜3šŸœžœžœž˜&Jšœ˜Jšžœžœžœ˜+Jšœžœ˜!J˜Jšžœ˜—J˜šžœ žœž˜Jšžœ!žœ žœ žœ˜H—Jšžœ˜J˜J˜—š Ÿœžœžœ%žœ žœžœž˜Yš œ žœ žœžœ žœž˜2J˜&—Jšžœžœ žœ˜$Jšžœžœžœžœ˜ Jš œžœžœžœžœžœ˜=šžœ žœž˜J˜L—Jš žœžœžœ žœžœ˜BJšžœ˜J˜—šŸœžœžœž˜8J˜šŸœžœ˜šžœž˜!Jšœžœ˜J˜Jšœ(žœ˜/Jš žœ žœžœžœžœ˜-Jšžœ˜ —J˜—šžœž˜!Jšžœ$žœžœžœ˜>Jš žœ žœžœžœžœ˜-Jšžœ˜—J˜Jšžœ žœžœ˜)Jšžœ žœžœ˜'J˜JšœG˜GJšžœ˜J˜—šŸ œžœžœž˜0šŸœžœž˜Jšœ žœ˜J˜Jšžœ˜—J˜šžœ žœž˜Jšžœ!žœ žœ žœ˜H—Jšžœ˜J˜—J˜š Ÿ œžœžœžœžœž˜CJšžœžœžœ˜šŸœžœ˜Jšœžœžœ˜Jšœžœ˜&Jš žœžœžœžœžœžœ˜Sšžœž˜Jšœ žœžœžœ˜3Jšœ žœžœžœ˜5Jš œ žœ žœžœžœžœ˜NJš žœžœžœžœ˜I—Jšžœžœ žœžœ˜%J˜Jšœžœ˜Jšœ&˜&J˜—Jšžœžœ˜5Jšœ@˜@Jšžœžœ$˜1Jšžœ˜J˜—š Ÿ œžœžœžœžœž˜FJšžœžœžœ˜šŸœžœ˜Jšœžœžœ˜Jš žœžœžœžœžœžœ˜Sšžœž˜Jšœžœžœžœ˜;Jšœ žœžœžœ˜-Jš žœžœžœžœ˜I—Jšžœžœ žœžœ˜%J˜Jšœžœ˜Jšœ&˜&J˜—Jšžœžœ˜5JšœC˜CJšžœžœ$˜1Jšžœ˜J˜—š Ÿœžœžœ"žœžœž˜RšŸœžœž˜#Jšžœžœžœžœžœ žœžœ˜HJš žœžœžœ žœžœ˜NJ˜J˜!J˜J˜&Jšžœ˜—J˜Jšžœžœ˜5Jšžœžœ˜7JšœG˜GJšžœžœ%˜2Jšžœ˜J˜—š Ÿœžœžœ"žœžœž˜RšŸœžœž˜#Jšžœžœžœžœžœ žœžœž˜HJš žœžœžœ žœžœ˜NJ˜šžœ'žœž˜3J˜Jšœ)˜)Jšž˜—šžœž˜ J˜šžœ7žœž˜SJšžœžœ˜Jšžœ˜—J˜J˜Jšžœ˜—Jšœ&˜&Jšžœ˜J˜—Jšžœžœ˜5Jšžœžœ˜7JšœG˜GJšžœžœ%˜2Jšžœ˜J˜—šŸ œžœžœž˜5šŸœžœ˜š žœ žœžœ žœžœž˜AJšžœžœžœ ˜E—J˜J˜J˜J˜J˜J˜J˜ J˜J˜ J˜J˜7Jšœžœžœ˜FJ˜—J˜Jšžœžœ˜/Jšžœžœ˜/šœ6˜6Jšœ&˜&—J˜Jšžœ˜J˜—J˜š Ÿœž œžœžœ žœžœ˜TJšœžœžœž˜J˜Jšœžœ˜šŸœžœž˜Jšžœžœžœ˜Jšœ!žœ˜'Jšžœžœžœžœ˜;Jšœ˜Jšœ˜Jšœžœ ˜>Jšœ˜Jšœžœ˜J˜J˜!JšœL˜LJ˜Jšœ.˜EJšœ4žœžœ˜AJš žœ žœžœ5žœžœ˜VJšœ#˜#Jšžœžœ˜:Jšžœ˜J˜—Jšžœ'žœžœžœ˜AJ˜šžœ.žœ˜6Jšœ-™-J˜"Jšžœžœ˜.šžœžœ.™MJšœ™Jšœ7™KJšœ,™,Jšœ™Jšœ!™!Jšžœ™ —J˜—Jšœ˜Jšžœžœ˜1šœF˜FJ˜—Jšžœ žœžœ$˜8Jšžœ žœ˜+Jšžœžœžœ"˜4J˜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šœ˜J˜—Jšœ.žœ˜5Jšžœ˜J˜—š Ÿ œžœžœžœžœž˜DJšœžœžœ˜šŸœžœ˜šžœ6žœžœž˜IJšžœ žœžœ˜8Jšžœ˜ ——J˜Jšžœžœžœ žœ˜HJ˜Jšžœ)žœžœžœ˜CJ˜Jšžœžœ˜5JšœJ˜Jš žœžœžœžœžœž˜=Jšœžœ-˜UJšžœ˜ —J˜šžœžœžœžœ˜&š žœžœžœžœžœž˜=J˜$Jšžœ˜—J˜$J˜—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˜—š Ÿœžœžœ#žœžœ˜OJšžœ˜"Jš žœžœžœžœžœžœ˜NJšœ˜Jšœ˜Jšžœžœ'˜LJšœžœ˜#Jšœ"žœ˜)Jšœžœ˜Jšœ˜Jšžœ˜J˜—šŸ œžœžœžœ˜AJšœ žœž˜*Jšžœžœžœ˜šŸœžœ˜J˜J˜J˜-J˜'J˜Jšœ(žœžœ˜6J˜J˜J˜—J˜J˜J˜šœ8˜8J˜—J˜J˜šžœ/žœžœž˜@Jšžœ žœžœžœ˜DJšžœ˜J˜—J˜J˜Jšžœ˜J˜—šŸ œžœžœ'ž˜EJšžœžœ˜'J˜Jšœ žœžœ˜Jšœžœžœ˜šŸœžœ˜J˜2J˜Jšžœžœžœ˜%J™ šžœžœž˜šžœžœ˜Jšœ˜šœ7˜7Jšœžœ"˜9——šžœžœ˜?Jšžœ žœžœžœ˜=—Jšœ˜šžœžœ˜Jšœ˜Jšœ žœ˜—Jšžœ˜—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šžœ žœ˜+šžœžœž˜Jšžœžœ ˜:Jšžœžœ ˜:Jšžœ˜—J˜J˜Jšœ5žœž˜Jšžœžœž˜4Jšœžœ˜šΟbœ˜ Jšžœžœžœžœžœžœžœ˜IJ˜—J˜"Jšœ žœ˜&Jšžœžœ žœžœ˜š žœžœž œžœ'ž˜Eš žœžœžœžœž œžœž˜KJ˜Jšžœžœžœ˜"Jšž˜—š žœžœžœžœž˜)J˜Jšžœžœžœ˜"Jšžœ˜—Jšžœ˜—Jšžœ žœžœžœ˜1J˜%Jšžœ žœžœžœ˜J˜J˜Jšœ"žœ˜(Jšœ žœ˜%Jšœ ˜ Jšœ˜Jšžœ˜J˜—šŸ œžœž˜+J˜š Ÿ œžœžœžœž˜Bšžœžœžœž˜#š žœ žœžœžœž œžœž˜BJ˜šžœžœž˜#Jšœžœ˜Jšžœ˜Jšžœ˜—Jšž˜—š žœžœžœžœž˜)J˜šžœžœž˜#Jšœžœ˜Jšžœ˜Jšžœ˜—Jšžœ˜—Jšžœ˜—Jšžœ˜J˜—J˜.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šœE˜EJšžœ˜J˜—šŸœžœžœ˜Jšœžœ˜!Jšžœ˜—J˜Jšžœžœ˜ Jšžœ˜—šžœ žœž œ˜KJšœ0˜0Jšžœžœ˜-Jšžœ˜—J˜šžœ žœž œ˜KJšžœžœ˜/Jšœ˜Jšžœ˜—Jšžœ˜J˜—š Ÿœžœžœžœžœ ˜2J˜J˜Jšœžœžœ˜+Jšœ˜J˜J˜Jšžœžœ˜:J˜J˜Jšžœ˜J˜—šŸœžœžœžœ˜CJšžœ žœž˜!Jšœžœ˜ šžœžœžœ#˜5šœ žœ žœžœ˜8Jšžœ0˜4—J˜Jšžœžœ˜)J˜J˜Jšžœ˜—Jšžœ ˜Jšžœ˜J˜—šŸ œžœžœ˜9Jšžœžœž˜.Jšžœžœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜ J˜šŸ œžœžœ ž˜*šžœ ž œžœž˜%šžœ*žœžœž˜;Jšžœžœžœ˜Jšžœ˜—Jšžœžœžœžœ˜Jšžœ˜—Jšžœ˜—J˜š Ÿ œžœ žœžœž˜3š œžœžœžœžœž˜=Jš œžœ žœžœ žœžœ˜WJ˜——š Ÿœžœžœžœž˜$šœžœžœ"ž˜1Jšœžœ$˜(J˜——š Ÿœžœ žœžœž˜=Jšœžœ˜J˜š Ÿœžœ žœžœž˜;Jš œžœžœžœžœ˜>J˜—J˜J˜šœ žœ žœžœ˜2Jšžœ*˜.—Jšžœžœ˜&J˜šžœžœžœž˜)šžœžœž˜"Jš(™(J˜J˜J˜ Jšžœžœ˜ Jšžœ˜—Jšžœ˜—Jšžœžœ˜Jšžœ˜J˜—Jš žœžœžœžœžœžœ˜;J˜š žœžœžœžœ žœžœž˜OJšžœžœžœ˜,J˜—J˜š œ žœžœžœžœ˜GJšžœ4˜8J˜—Jš™Jšœžœžœ žœ ˜;Jšœ žœžœ žœ ˜2Jš œ žœ žœžœžœžœžœ ˜ZJ˜Jšžœ˜J˜—J˜Jšœ5žœžœ˜@Jšœ5žœžœ˜AJšœ8˜8Jšžœ˜J˜J˜—…—‡ŠΌŠ