DIRECTORY Carets USING [ResumeCarets, SuspendCarets], ColorDisplay USING [height, width], InputFocus USING [GetInputFocus, SetInputFocus], List USING [Sort, CompareProc], Menus USING [AddMenu], MenusPrivate USING [RecomputeMenus, ViewerMenus], MessageWindow USING [Append, Blink, Clear], Process USING [Detach], Rope USING [ROPE], ViewerBLT USING [InternalPaintColumn], ViewerEvents USING [EventProc, ProcessEvent, RegisterEventProc], ViewerOps, ViewerLocks, ViewerClasses, ViewerSpecs, WindowManager USING [colorDisplayOn], WindowManagerPrivate; ViewerOpsImplA: CEDAR PROGRAM IMPORTS Carets, ColorDisplay, InputFocus, List, Menus, MenusPrivate, MessageWindow, Process, ViewerBLT, ViewerEvents, ViewerLocks, ViewerOps, WindowManager EXPORTS ViewerOps, ViewerSpecs, WindowManagerPrivate SHARES ViewerClasses, ViewerEvents, ViewerOps, ViewerLocks = BEGIN OPEN ViewerClasses, ViewerSpecs, ViewerOps, WindowManager; ErrorType: TYPE = {ok, classNotImplemented, illegalPositioning, nilViewer, notTopLevel, nonSequencedColumn, cantReplace, cantSwap}; fatalViewersError: ERROR[error: ErrorType] = CODE; openLeftWidth: PUBLIC INTEGER _ initialOpenLeftWidth; -- RFP changed from simply _ 600 openRightLeftX: PUBLIC INTEGER _ openLeftLeftX+openLeftWidth; openRightWidth: PUBLIC INTEGER _ screenW-openLeftWidth; openBottomY: PUBLIC INTEGER _ iconHeight+iconSpacing; rootViewerTree: PUBLIC ARRAY Column OF Viewer _ ALL[NIL]; CreateViewer: PUBLIC PROC [flavor: ViewerFlavor, info: ViewerRec _ [], paint: BOOL _ TRUE] RETURNS [new: Viewer] = { topLevel: BOOL; defaultPosition: BOOL; error: ErrorType _ ok; LockedCreateViewer: PROC = { class: ViewerClasses.ViewerClass = FetchViewerClass[flavor]; IF class=NIL THEN { error _ classNotImplemented; RETURN }; 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 _ illegalPositioning; RETURN }; 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 { new.iconic _ FALSE; new.visible _ ~new.parent.iconic; new.column _ new.parent.column; }; IF topLevel THEN new.border _ TRUE; IF new.column#static AND new.parent=NIL AND new.menus=NIL AND new.class.menus#NIL THEN FOR m: LIST OF ATOM _ new.class.menus, m.rest UNTIL m = NIL DO Menus.AddMenu[viewer: new, name: m.first, paint: FALSE]; ENDLOOP; IF topLevel THEN LinkViewer[new, ~new.iconic] ELSE LinkChild[new.parent, new]; IF ~defaultPosition THEN SetViewerPosition[new, new.wx, new.wy, new.ww, new.wh]; IF new.class.init#NIL THEN new.class.init[new]; IF topLevel THEN IF new.iconic THEN PositionIcon[new] ELSE IF defaultPosition THEN InternalComputeColumn[new.column, paint]; new.init _ TRUE}; new _ NEW[ViewerRec _ info]; IF ViewerEvents.ProcessEvent[create, new, TRUE] THEN RETURN[NIL]; -- Gulp!!! IF ViewerLocks.ColumnWedged[ViewerColumn[new]].wedged THEN { column: Column _ UnWedgedColumn[]; IF column # static THEN new.column _ column}; IF new.parent=NIL THEN IF new.iconic THEN ViewerLocks.CallUnderColumnLock[LockedCreateViewer, static] ELSE ViewerLocks.CallUnderColumnLock[LockedCreateViewer, new.column] ELSE ViewerLocks.CallUnderWriteLock[LockedCreateViewer, new.parent]; IF error#ok THEN ERROR fatalViewersError[error]; IF paint AND (~topLevel OR ~defaultPosition OR new.iconic) THEN 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[nilViewer]; 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]; 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; }; IF parent#NIL THEN { 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; } ELSE IF viewer.iconic THEN { GreyScreen[x, y, w, h, FALSE, TRUE]; -- erase the icon IF replace THEN RETURN; -- used by ReplaceViewer to prevent the slot from being reused RemoveIcon[icon: viewer, fillSlot: TRUE]; } ELSE InternalComputeColumn[viewer.column, paint]; }; IF ViewerEvents.ProcessEvent[destroy, viewer, TRUE] THEN RETURN; KillInputFocus[viewer]; ViewerLocks.CallUnderWriteLock[LockedDestroyViewer, viewer]; IF paintParent THEN PaintViewer[parent, all]; IF onlyLink#NIL THEN PaintViewer[onlyLink, caption]; [] _ ViewerEvents.ProcessEvent[destroy, viewer, FALSE]; }; 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.menus _ NIL; viewer.props _ NIL; viewer.child _ NIL; viewer.data _ NIL; }; ViewerLocks.CallUnderWriteLock[LockedBottomUpEtc, viewer]; }; TopViewer: PUBLIC PROC [viewer: Viewer, paint: BOOL _ TRUE] = { error: ErrorType _ ok; LockedTopViewer: PROC = { noop: BOOL _ FALSE; myTopY: INTEGER = viewer.wy+viewer.wh; IF viewer.parent#NIL THEN { error _ notTopLevel; RETURN }; 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 => { error _ nonSequencedColumn; RETURN }; -- not a sequenced column IF noop THEN {paint _ FALSE; RETURN}; UnlinkViewer[viewer]; LinkViewer[viewer, FALSE]; InternalComputeColumn[viewer.column, paint]}; IF viewer.offDeskTop THEN ChangeColumn[viewer, left]; ViewerLocks.CallUnderColumnLock[LockedTopViewer, viewer.column]; IF error#ok THEN ERROR fatalViewersError[error]; }; BottomViewer: PUBLIC PROC [viewer: Viewer, paint: BOOL _ TRUE] = { error: ErrorType _ ok; LockedBottomViewer: PROC = { noop: BOOL _ FALSE; IF viewer.parent#NIL THEN { error _ notTopLevel; RETURN }; SELECT viewer.column FROM left, right => IF viewer.wy = openBottomY THEN noop _ TRUE; color => IF viewer.wy = 0 THEN noop _ TRUE; ENDCASE => { error _ nonSequencedColumn; RETURN }; -- not a sequenced column IF noop THEN {paint _ FALSE; RETURN}; UnlinkViewer[viewer]; LinkViewer[viewer, TRUE]; InternalComputeColumn[viewer.column, paint]}; IF viewer.offDeskTop THEN ChangeColumn[viewer, left]; ViewerLocks.CallUnderColumnLock[LockedBottomViewer, viewer.column]; IF error#ok THEN ERROR fatalViewersError[error]; }; 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; 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] = { error: ErrorType _ ok; LockedReplaceViewer: PROC = { IF old.parent#NIL OR new.parent#NIL OR new.column#old.column THEN { error _ cantReplace; RETURN }; -- 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 error#ok THEN ERROR fatalViewersError[error]; IF new.iconic THEN ViewerOps.PaintViewer[new, all]; }; MoveBoundary: PUBLIC PROC [newLeftWidth: INTEGER _ openLeftWidth, newBottomY: INTEGER _ openBottomY] = { ENABLE UNWIND => NULL; LockedMoveBoundary: PROC = { oldBottomY: INTEGER _ openBottomY; oldLeftWidth: INTEGER _ openLeftWidth; ResetPaintCache[]; openBottomY _ newBottomY; openLeftWidth _ newLeftWidth; openRightLeftX _ openLeftLeftX+openLeftWidth; openRightWidth _ screenW-openLeftWidth; IF oldLeftWidth < newLeftWidth THEN { InternalComputeColumn[right, FALSE, FALSE]; InternalComputeColumn[left, FALSE, FALSE]} ELSE { InternalComputeColumn[left, FALSE, FALSE]; InternalComputeColumn[right, FALSE, FALSE]}; PaintEverything; }; Carets.SuspendCarets[]; ViewerLocks.CallUnderViewerTreeLock[LockedMoveBoundary]; WaitForPaintingToFinish[]; Carets.ResumeCarets[]; }; ChangeColumn: PUBLIC PROC [viewer: Viewer, newColumn: Column] = { ENABLE UNWIND => Carets.ResumeCarets[]; oldColumn: Column; paint: BOOL = TRUE; paintIcon: BOOL _ FALSE; LockedChangeColumn: PROC = { SetColumn: EnumProc = {v.column _ viewer.column} ; oldColumn _ viewer.column; IF oldColumn = newColumn THEN RETURN; IF newColumn = static AND ~viewer.iconic THEN CloseViewer[viewer]; IF viewer.iconic THEN { IF oldColumn # static THEN {OPEN viewer; RemoveIcon[viewer]; GreyScreen[wx, wy, ww, wh, column=color, TRUE]}; IF oldColumn # static AND newColumn # static -- not offDeskTop THEN newColumn _ IF viewer.column=right THEN left ELSE right; viewer.column _ newColumn; IF newColumn # static THEN {PositionIcon[viewer]; paintIcon _ TRUE}; }; IF ~viewer.iconic THEN { UnlinkViewer[viewer]; viewer.column _ newColumn; LinkViewer[viewer]; ResetPaintCache[]; InternalComputeColumn[oldColumn, paint]; InternalComputeColumn[newColumn, paint]; }; IF oldColumn = static THEN viewer.offDeskTop _ FALSE; IF newColumn = static THEN { SetViewerPosition[viewer, 2000, 2000, 10, 10]; viewer.offDeskTop _ TRUE}; EnumerateChildren[viewer, SetColumn]; }; IF ViewerEvents.ProcessEvent[changeColumn, viewer, TRUE].abort THEN RETURN; IF ViewerLocks.ColumnWedged[newColumn].wedged THEN { newColumn _ UnWedgedColumn[]; IF newColumn = static THEN RETURN}; ViewerLocks.CallUnderColumnLocks[LockedChangeColumn, ViewerColumn[viewer], newColumn]; IF paintIcon THEN PaintViewer[viewer, all]; [] _ ViewerEvents.ProcessEvent[changeColumn, viewer, FALSE]; }; 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]; ResetPaintCache[]; closeOthers _ (closeOthers AND (CountViewers[icon.column]#0)); UnlinkViewer[icon]; icon.iconic _ FALSE; LinkViewer[icon, bottom]; EnumerateChildren[icon, SetOpen]; RemoveIcon[icon: icon, fillSlot: ~closeOthers]; icon.position _ 0; icon.ww _ icon.wh _ 0; -- so won't be clipped out in GreyScreen below GreyScreen[icon.wx, icon.wy, iconWidth, iconHeight, FALSE, TRUE]; IF closeOthers THEN GrowViewer[icon, paint]; InternalComputeColumn[icon.column, paint]; IF paint THEN MessageWindow.Clear[]; -- flush old messages }; IF ViewerEvents.ProcessEvent[open, icon, TRUE].abort THEN RETURN; IF ViewerLocks.ColumnWedged[icon.column].wedged THEN { column: Column _ UnWedgedColumn[]; IF column # static THEN icon.column _ column}; KillInputFocus[icon]; IF icon.offDeskTop THEN ChangeColumn[icon, left]; ViewerLocks.CallUnderColumnLocks[LockedOpenIcon, icon.column, static]; [] _ ViewerEvents.ProcessEvent[open, icon, FALSE]; }; 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 iconSlotsFree=0 THEN { MessageWindow.Append["No space for another icon! Try deleting some viewers.", TRUE]; MessageWindow.Blink; RETURN; }; KillInputFocus[viewer]; ResetPaintCache[]; UnlinkViewer[viewer]; viewer.iconic _ TRUE; LinkViewer[viewer]; EnumerateChildren[viewer, SetClosed]; PositionIcon[viewer]; InternalComputeColumn[viewer.column, paint]; }; IF ViewerEvents.ProcessEvent[close, viewer, TRUE].abort THEN RETURN; KillInputFocus[viewer]; IF viewer.offDeskTop THEN ChangeColumn[viewer, left]; ViewerLocks.CallUnderColumnLocks[LockedCloseViewer, viewer.column, static]; IF paint THEN PaintViewer[viewer, all]; [] _ ViewerEvents.ProcessEvent[close, viewer, FALSE]; }; SwapIconAndViewer: PUBLIC PROC [icon, openViewer: Viewer, paint: BOOL _ TRUE] = { -- this isn't locked at all! IF ~icon.iconic OR openViewer.iconic THEN ERROR fatalViewersError[cantSwap]; 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]; PaintViewer[openViewer, all]; }; GrowViewer: PUBLIC PROC [viewer: Viewer, paint: BOOL _ TRUE] = { LockedGrowViewer: PROC = { v, next: Viewer _ NIL; v _ rootViewerTree[viewer.column]; WHILE v # NIL DO next _ v.sibling; IF v # viewer THEN { ViewerOps.CloseViewer[v, FALSE]; -- must be under a lock! IF paint THEN ViewerOps.PaintViewer[v, all]}; -- just paint the icon v _ next; ENDLOOP; InternalComputeColumn[viewer.column, paint]}; 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]; FOR v: Viewer _ rootViewerTree[viewer.column], v.sibling WHILE v # NIL DO IF v # viewer THEN KillInputFocus[v]; -- done here to prevent deadlock ENDLOOP; ViewerLocks.CallUnderColumnLocks[LockedGrowViewer, viewer.column, static]; [] _ ViewerEvents.ProcessEvent[grow, viewer, FALSE]; }; GrowExtra: ViewerEvents.EventProc = { 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]; }; ComputeColumn: PUBLIC PROC [column: Column, paint: BOOL _ TRUE] = { ENABLE UNWIND => NULL; LockedComputeColumn: PROC = {InternalComputeColumn[column, paint]}; ViewerLocks.CallUnderColumnLock[LockedComputeColumn, column]; }; 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}; ResetPaintCache[]; -- clients are assuming that this will happen IF column # static AND openViewers # 0 THEN { [totalSpace, leftX, width, bottomY, nextY] _ ColumnInfo[column]; normalHeight _ totalSpace/openViewers; AddUpRequests[totalSpace]; IF totalRequested+((openViewers-requests)*minHeight) > totalSpace THEN { force _ TRUE; AddUpRequests[normalHeight]}; IF openViewers=requests THEN extra _ totalSpace-totalRequested ELSE {default _ (totalSpace-totalRequested)/(openViewers-requests); extra _ (totalSpace-totalRequested) MOD (openViewers-requests)}; FOR v: Viewer _ rootViewerTree[column], v.sibling UNTIL v=NIL DO height: INTEGER _ SELECT TRUE FROM DesiredHeight[v]=0 => default, force AND DesiredHeight[v] > normalHeight => default, ENDCASE => DesiredHeight[v]; IF openViewers = requests AND largestViewer # NIL THEN {IF v = largestViewer THEN height _ height + extra} ELSE {IF nextY=bottomY THEN height _ height + extra}; SetViewerPosition[v, leftX, nextY, width, height]; nextY _ nextY + v.wh; ENDLOOP}; IF paint THEN ViewerBLT.InternalPaintColumn[column, icons]; }; ColumnInfo: PUBLIC PROC[column: Column] RETURNS[totalSpace, leftX, width, bottomY, nextY: INTEGER] = { 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; }; iconSlots: CARDINAL = (iconRightX-iconLeftX)/(iconWidth+iconSpacing); iconSlotsFree: CARDINAL _ iconRows*iconSlots; iconSlotsArray: ARRAY [0..iconRows*iconSlots) OF BOOL _ ALL[FALSE]; RemoveIcon: PROC[icon: Viewer, fillSlot: BOOLEAN _ FALSE] = { filler: Viewer; slot: INTEGER _ 0; FindSlotIcon: ViewerOps.EnumProc = { IF ~v.iconic OR v.offDeskTop THEN RETURN[TRUE]; IF v.position = slot THEN {filler _ v; RETURN[FALSE]} ELSE RETURN[TRUE]}; IF icon.offDeskTop THEN RETURN; iconSlotsFree _ iconSlotsFree + 1; iconSlotsArray[icon.position] _ FALSE; IF NOT fillSlot THEN RETURN; FOR m: INTEGER DECREASING IN ((icon.position/iconSlots)..iconRows) DO IF icon.column = right THEN FOR n: CARDINAL DECREASING IN [0..iconSlots) DO slot _ m*iconSlots + n; IF iconSlotsArray[slot] THEN EXIT; ENDLOOP ELSE FOR n: CARDINAL IN [0..iconSlots) DO slot _ m*iconSlots + n; IF iconSlotsArray[slot] THEN EXIT; ENDLOOP; ENDLOOP; IF slot = 0 OR ~iconSlotsArray[slot] THEN RETURN; ViewerOps.EnumerateViewers[FindSlotIcon]; IF filler = NIL THEN RETURN; GreyScreen[filler.wx, filler.wy, iconWidth, iconHeight, FALSE, TRUE]; iconSlotsArray[filler.position] _ FALSE; filler.position _ icon.position; iconSlotsArray[filler.position] _ TRUE; SetIconPosition[filler]; TRUSTED {Process.Detach[FORK PaintViewer[filler, all]]}; }; PositionIcon: PROC [viewer: Viewer, slot: INTEGER _ 0] = { slotIcon: Viewer; FindSlotIcon: ViewerOps.EnumProc = { IF ~v.iconic OR v.offDeskTop THEN RETURN[TRUE]; IF v.position = slot THEN {slotIcon _ v; RETURN[FALSE]} ELSE RETURN[TRUE]}; FindIconSlot: PROC [side: Column] RETURNS [slot: CARDINAL] = { FOR m: CARDINAL IN [0..iconRows) DO IF side=right THEN FOR n: CARDINAL DECREASING IN [0..iconSlots) DO slot _ m*iconSlots+n; IF ~iconSlotsArray[slot] THEN RETURN; ENDLOOP ELSE FOR n: CARDINAL IN [0..iconSlots) DO slot _ m*iconSlots+n; IF ~iconSlotsArray[slot] THEN RETURN; ENDLOOP; ENDLOOP; }; IF slot # 0 AND iconSlotsArray[slot] THEN { -- move the icon in the slot ViewerOps.EnumerateViewers[FindSlotIcon]; slotIcon.position _ FindIconSlot[slotIcon.column]; iconSlotsArray[slotIcon.position] _ TRUE; SetIconPosition[slotIcon]}; viewer.position _ IF slot # 0 THEN slot ELSE FindIconSlot[viewer.column]; iconSlotsArray[viewer.position] _ TRUE; iconSlotsFree _ iconSlotsFree-1; SetIconPosition[viewer]; }; SetIconPosition: PROC [icon: Viewer] = INLINE { SetViewerPosition[icon, (icon.position MOD iconSlots)*(iconWidth+iconSpacing)+iconLeftX, iconBottomY+((icon.position/iconSlots)*(iconHeight+iconSpacing)), iconWidth, iconHeight]; }; MoveViewer: PUBLIC PROC [viewer: Viewer, x, y, w, h: INTEGER, paint: BOOL _ TRUE] = { ENABLE UNWIND => Carets.ResumeCarets[]; LockedMoveViewer: PROC = { oldX, oldY, oldW, oldH: INTEGER; oldX _ viewer.wx; oldY _ viewer.wy; oldW _ viewer.ww; oldH _ viewer.wh; SetViewerPosition[viewer, x, y, w, h]; IF ~paint OR viewer.parent # NIL THEN RETURN; GreyScreen[oldX, oldY, oldW, oldH, viewer.column=color AND ~viewer.iconic, viewer.iconic]; }; Carets.SuspendCarets[]; ViewerLocks.CallUnderWriteLock[LockedMoveViewer, viewer]; IF paint THEN PaintViewer[IF viewer.parent=NIL THEN viewer ELSE viewer.parent, all]; Carets.ResumeCarets[]; }; EstablishViewerPosition: PUBLIC PROC [viewer: Viewer, x, y, w, h: INTEGER] = { LockedEstablishViewerPosition: PROC = {SetViewerPosition[viewer, x, y, w, h]}; ViewerLocks.CallUnderWriteLock[LockedEstablishViewerPosition, viewer]; }; SetViewerPosition: PROC [viewer: Viewer, x, y, w, h: INTEGER] = { OPEN ViewerSpecs; vbs: INTEGER = IF viewer.border THEN windowBorderSize ELSE 0; xmin: INTEGER _ x+vbs; -- left edge of client area ymin: INTEGER _ y+vbs; -- bottom edge of client area xmax: INTEGER _ x+w-vbs; -- right edge of client area ymax: INTEGER _ y+h-vbs; -- top edge of client area viewer.wx _ x; viewer.wy _ y; viewer.ww _ w; viewer.wh _ h; IF viewer.parent=NIL AND viewer.column#static THEN ymax _ y+h-captionHeight; IF viewer.menus#NIL THEN { [] _ MenusPrivate.RecomputeMenus[viewer]; ymax _ ymax-NARROW[viewer.menus, MenusPrivate.ViewerMenus].h-menuBarHeight; }; IF viewer.hscrollable THEN { IF viewer.menus#NIL THEN ymax _ ymax+menuBarHeight; ymax _ ymax-scrollBarW; }; IF viewer.scrollable THEN xmin _ x+scrollBarW; viewer.cx _ xmin; viewer.cy _ ymin; viewer.cw _ xmax-xmin; viewer.ch _ ymax-ymin; ResetPaintCache[viewer]; }; LinkViewer: PROC [new: Viewer, bottom: BOOL _ TRUE] = { column: Column ~ ViewerColumn[new]; IF rootViewerTree[column]=NIL OR bottom THEN { new.sibling _ rootViewerTree[column]; rootViewerTree[column] _ new; } ELSE FOR v: Viewer _ rootViewerTree[column], v.sibling UNTIL v.sibling=NIL DO REPEAT FINISHED => v.sibling _ new; ENDLOOP; }; UnlinkViewer: PROC [old: Viewer] = { 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; }; 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] = INLINE { 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]; }; [] _ ViewerEvents.RegisterEventProc[GrowExtra, grow, NIL, TRUE]; [] _ ViewerEvents.RegisterEventProc[GrowExtra, grow, NIL, FALSE]; [] _ ViewerEvents.RegisterEventProc[GrowExtra, destroy]; END. <ViewerOpsImplA.mesa; Written by S. McGregor Edited by McGregor on July 28, 1983 5:49 pm Edited by Plass, May 4, 1983 11:27 am Last Edited by: Maxwell, June 3, 1983 2:46 pm Last Edited by: Pausch, August 22, 1983 4:30 pm Last Edited by: Wyatt, October 26, 1983 11:40 am add menus to the viewer if the class has a list of names of menus to add not locked, but these are not ordinary times! the following are always painted because the caller isn't likely to know to repaint them help out the garbage collector (.sibling handled by caller and above) IF oldLeftWidth < newLeftWidth THEN { InternalComputeColumn[right, TRUE, FALSE]; InternalComputeColumn[left, TRUE, FALSE]} ELSE { InternalComputeColumn[left, TRUE, FALSE]; InternalComputeColumn[right, TRUE, FALSE]}; IF newBottomY # oldBottomY THEN { -- repaint icons GreyScreen[0, 0, screenW, newBottomY, FALSE, TRUE]; FOR v: Viewer _ rootViewerTree[static], v.sibling UNTIL v = NIL DO IF ~v.iconic OR v.offDeskTop THEN LOOP; TRUSTED {Process.Detach[FORK PaintViewer[v, all]]}; ENDLOOP}; -- iconic case -- non iconic case -- hyperspace not locked, but these are not ordinary times! -- you MUST acquire the static column here, since CloseViewer needs it. [viewer: Viewer, event: ViewerEvent, before: BOOL] 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. If this is a top level viewer (other than MessageWindow or an icon), leave space for a caption. If there is a border, the caption area overlaps the top border. If the viewer has a menu, leave space for the menu and the bar beneath. *** Note: RecomputeMenus uses viewer.wh and viewer.ww; it also assumes that the viewer has a caption!! *** Leave space for a horizontal scrollbar, if any. If there is a menu, the horizontal scrollbar overlaps the menu bar. Leave space for a vertical scrollbar, if any. If there is a border, the vertical scrollbar overlaps the left border. Κ!– "cedar" style˜JšΟc+™+Jšœ™+J™%J™-Jš/™/Jš0™0J˜šΟk ˜ Jšœžœ˜+Jšœ žœ˜#Jšœ žœ ˜0Jšœžœ˜Jšœžœ ˜Jšœ žœ˜1Jšœžœ˜+Jšœžœ ˜Jšœžœžœ˜Jšœ žœ˜&Jšœ žœ.˜@J˜ J˜ J˜J˜ Jšœžœ˜%J˜—J˜Jšœžœž˜Jšžœ”˜›Jšžœ-˜4Jšžœ4˜:Jšœžœžœ6˜BJ˜Jšœ žœt˜ƒJšœžœžœ˜2J˜Jšœžœžœ ˜VJšœžœžœ˜=Jšœžœžœ˜7Jšœ žœžœ˜5J˜Jš œžœžœžœ žœžœ˜9J˜š Οn œžœžœ5žœžœ˜[Jšžœ˜Jšœ žœ˜Jšœžœ˜Jšœ˜šŸœžœ˜J˜Jšœ1žœ˜8Jšžœ˜——J˜Jšžœ žœžœ˜NJšžœžœ8˜PJšžœžœžœ˜/Jšžœ žœžœ žœ˜5Jšžœžœžœ*˜FJšœ žœ˜—J˜Jšœžœ˜J˜Jš žœ(žœžœžœžœ ˜LJ˜šžœ4žœ˜J˜J˜šžœ žœž˜Jšœ/žœ˜OJšœ3˜3Jšœ/žœ˜6J˜Jšœžœ˜J˜ Jšžœ˜J˜—Jšžœžœžœ˜/JšE™EJšœžœžœ˜'Jšœžœžœžœ˜;Jšœžœ˜Jšœ˜J˜—Jšœ;˜;Jšœ˜—J˜š Ÿ œžœžœžœžœ˜?Jšœ˜šŸœžœ˜Jšœžœžœ˜Jšœžœ˜&Jšžœžœžœžœ˜:šžœž˜Jšœ žœžœžœ˜3Jšœ žœžœžœ˜5Jš œ žœ žœžœžœžœ˜NJšžœ"žœ˜L—Jšžœžœ žœžœ˜%J˜Jšœžœ˜Jšœ-˜-J˜—Jšžœžœ˜5Jšœ@˜@Jšžœ žœžœ˜0Jšœ˜J˜—š Ÿ œžœžœžœžœ˜BJšœ˜šŸœžœ˜Jšœžœžœ˜Jšžœžœžœžœ˜:šžœž˜Jšœžœžœžœ˜;Jšœ žœžœžœ˜-Jšžœ"žœ˜L—Jšžœžœ žœžœ˜%J˜Jšœžœ˜Jšœ-˜-J˜—Jšžœžœ˜5JšœC˜CJšžœ žœžœ˜0Jšœ˜J˜—š Ÿœžœžœ"žœžœ˜NšŸœžœ˜Jš žœžœžœžœžœžœ˜7Jšžœžœžœžœ˜=J˜J˜!J˜J˜-Jšœ˜—J˜Jšžœžœ˜5Jšžœžœ˜7JšœG˜GJšœ˜J˜—š Ÿœžœžœ"žœžœ˜NšŸœžœ˜Jš žœžœžœžœžœžœ˜7Jšžœžœžœžœ˜=J˜šžœ'žœ˜/J˜Jšœ)˜)Jšœ˜—šžœ˜J˜šžœ7žœž˜SJšžœžœ˜Jšžœ˜—J˜J˜Jšœ˜—Jšœ-˜-Jšœ˜J˜—Jšžœžœ˜5Jšžœžœ˜7JšœG˜GJšœ˜J˜—šŸ œžœžœ˜1Jšœ˜šŸœžœ˜š žœ žœžœ žœžœž˜AJšœžœ ˜A—J˜J˜J˜J˜J˜J˜J˜ J˜J˜ J˜J˜7Jšœžœžœ˜FJšžœ žœ#˜6J˜—J˜Jšžœžœ˜/Jšžœžœ˜/JšœH˜HJšžœ žœžœ˜0Jšžœ žœ!˜3Jšœ˜J˜—J˜šŸ œžœžœžœ˜AJšœ žœ˜&Jšžœžœžœ˜šŸœžœ˜Jšœ žœ˜"Jšœžœ˜&J˜J˜J˜J˜-J˜'šžœžœ™%Jšœžœžœ™*Jšœžœžœ™)—šžœ™Jšœžœžœ™)Jšœžœžœ™+—šžœžœ™2Jšœ&žœžœ™3šžœ/žœžœž™BJšžœ žœžœžœ™'Jšžœžœ™3Jšžœ™ ——šžœžœ˜%Jšœžœžœ˜+Jšœžœžœ˜*—šžœ˜Jšœžœžœ˜*Jšœžœžœ˜,—J˜J˜—J˜J˜J˜šœ8˜8J˜—J˜J˜Jšœ˜J˜—šŸ œžœžœ(˜AJšžœžœ˜'J˜Jšœžœžœ˜Jšœ žœžœ˜šŸœžœ˜J˜2J˜Jšžœžœžœ˜%Jšžœžœžœ˜BJ™šžœžœ˜šžœžœžœ˜(Jšœ˜Jšœ)žœ˜1—šžœžœ˜?Jšžœ žœžœžœ˜=—Jšœ˜Jšžœžœ$žœ˜DJšœ˜—Jšœ™šžœžœ˜J˜J˜J˜J˜J˜(J˜(Jšœ˜—Jšœ ™ Jšžœžœžœ˜5šžœžœ˜J˜.Jšœžœ˜J˜—J˜%J˜J˜—J˜Jšžœ1žœžœžœ˜KJ˜šžœ,žœ˜4J˜Jšžœžœžœ˜#—J˜šœ5˜5Jšœ!˜!J˜—Jšžœ žœ˜+J˜Jšœ5žœ˜Jšœ˜Jšœžœ˜J˜J˜!Jšœ/˜/J˜Jšœ.˜EJšœ4žœžœ˜AJšžœ žœ˜,Jšœ*˜*Jšžœžœ˜:Jšœ˜J˜—Jšžœ'žœžœžœ˜AJ˜šžœ.žœ˜6Jšœ-™-J˜"Jšžœžœ˜.J˜—Jšœ˜Jšžœžœ˜1šœF˜FJ˜—Jšœ+žœ˜2Jšœ˜J˜—š Ÿ œžœžœžœžœ˜AJšžœžœžœ˜šŸœžœ˜Jšœ#žœ˜*Jšžœžœžœžœ˜5šžœžœ˜JšœOžœ˜UJ˜Jšžœ˜Jšœ˜—Jšœ˜J˜J˜Jšœžœ˜J˜J˜%Jšœ˜J˜,J˜J˜—Jšžœ*žœžœžœ˜DJ˜J˜Jšžœžœ˜5JšœK˜Kšžœžœ˜'J˜—Jšœ.žœ˜5Jšœ˜J˜—š Ÿœžœžœ#žœžœ˜OJšœ˜Jšžœžœžœžœ˜LJšœ˜Jšœ˜Jšžœžœ'˜LJšœžœ˜#Jšœ"žœ˜)Jšœžœ˜Jšœ˜Jšœ˜Jšœ˜J˜—š Ÿ œžœžœžœžœ˜@šŸœžœ˜Jšœžœ˜Jšœ"˜"šžœžœž˜J˜šžœ žœ˜Jšœžœ˜9Jšžœžœ!˜D—J˜ Jšžœ˜ —Jšœ-˜-—J˜Jšžœžœžœ žœ˜HJšžœ)žœžœžœ˜CJšžœžœ˜5J˜šžœ6žœžœž˜IJšžœ žœ ˜FJšžœ˜ —JšœJ˜JJšœG™GJ˜Jšœ,ž˜4Jšœ˜J˜—šœ&˜&Jšœ-žœ™2šŸœžœ˜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˜š Ÿ œžœžœžœžœ˜CJšžœžœžœ˜JšŸœžœ*˜CJšœ=˜=Jšœ˜J˜—šŸœžœ žœžœ˜KJš ™ Jšœžœžœ˜J˜Jšœžœ˜&Jšœ žœ˜#Jšœžœ˜&Jšœ žœ˜,Jšœ*žœ˜6š Ÿ œžœ žœžœž˜:Jšœžœ(œ˜@—šŸ œžœ žœ˜*Jšœžœ˜Jšœ˜šžœ/žœžœž˜@šžœ"žœ˜*Jšœ5˜5—šžœžœžœ˜;J˜3J˜Jšœ˜—Jšžœ˜ —J˜—Jšœ-˜AJšžœžœžœ˜-˜JšœE™EJšœQ™QJšœY™YJ™J˜@Jšœ&˜&Jšœ˜šžœ@žœ˜HJšœžœ˜+—šžœžœ#˜?šžœ?˜CJšœ$žœ˜@——J˜šžœ/žœžœž˜@šœžœžœžœž˜"Jšœ˜Jšœžœ,˜5Jšžœ˜—šžœžœžœ˜2Jšžœžœžœ˜8Jšžœžœžœ˜5—J˜2J˜Jšžœ˜ ——J˜Jšžœžœ.˜;Jšœ˜J˜—šŸ œžœžœ˜'Jšžœ+žœ˜>šžœž˜šœ ˜ J˜&J˜J˜Jšœ˜—šœ ˜ J˜'J˜J˜Jšœ˜—šœ ˜ J˜!J˜J˜ Jšœ˜—Jšžœžœ˜—Jšœ˜—J˜Jšœ žœ2˜EJšœžœ˜-š œžœžœžœžœžœ˜CJ˜—šŸ œžœžœžœ˜=J˜Jšœžœ˜š‘ œ˜$Jš žœ žœžœžœžœ˜/Jšžœžœžœžœžœžœžœ˜I—J˜Jšžœžœžœ˜J˜"Jšœ žœ˜&Jšžœžœ žœžœ˜š žœžœž œžœ'ž˜Eš žœžœžœžœž œžœž˜KJ˜Jšžœžœžœ˜"Jšž˜—š žœžœžœžœž˜)J˜Jšžœžœžœ˜"Jšžœ˜—Jšžœ˜—Jšžœ žœžœžœ˜1J˜)Jšžœ žœžœžœ˜Jšœ8žœžœ˜EJšœ"žœ˜(Jšœ ˜ Jšœ"žœ˜'Jšœ˜Jšžœžœ˜8Jšœ˜J˜—šŸ œžœžœ ˜:J˜š‘ œ˜$Jš žœ žœžœžœžœ˜/Jšžœžœžœžœžœžœžœ˜K—šŸ œžœžœžœ˜>šžœžœžœž˜#š žœ žœžœžœž œžœž˜BJ˜Jšžœžœžœ˜%Jšž˜—š žœžœžœžœž˜)J˜Jšžœžœžœ˜%Jšžœ˜—Jšžœ˜—Jšœ˜—J˜šžœ žœžœ˜HJšœ)˜)J˜2Jšœ$žœ˜)Jšœ˜J˜—Jšœžœ žœžœ˜IJšœ"žœ˜'J˜ Jšœ˜Jšœ˜J˜—šŸœžœžœ˜/šœ'žœ.˜XJ˜LJ˜ —Jšœ˜—J˜šŸ œžœžœžœ˜=Jšœžœžœ˜Jšžœžœ˜'šŸœžœ˜Jšœžœ˜ JšœG˜GJšœ&˜&Jš žœžœžœžœžœ˜-Jšœ7žœ ˜ZJšœ˜—Jšœ˜Jšœ9˜9Jš žœžœ žœžœžœžœ˜TJ˜Jšœ˜J˜—šŸœžœžœžœ˜NJšŸœžœ+˜NJšœF˜FJšœ˜J˜—šŸœžœžœžœ ˜SJš œžœžœžœžœ˜=Jšœžœ ˜2Jšœžœ ˜4Jšœžœ ˜5Jšœžœ ˜3J˜J˜;J˜J™_J™?Jšžœžœžœžœ˜LJ˜J™Gšžœžœžœ˜J™jJ˜)Jšœ žœ9˜KJ˜—J˜J™/J™Cšžœžœ˜Jšžœžœžœ˜3Jšœ˜J˜—J˜J™-J™FJšžœžœ˜.J˜J˜QJ˜Jšœ˜—J˜šŸ œžœžœžœ˜7Jšœ#˜#šžœžœžœžœ˜.J˜%J˜Jšœ˜—š žœžœ/žœ žœž˜MJšžœžœ˜#Jšžœ˜—Jšœ˜J˜—šŸ œžœ˜$J˜ Jšœ#˜#šžœž˜"Jšœ5žœžœ˜B—Jš œžœ žœžœžœ˜KJšžœ žœ ˜0šžœžœž˜6Jšžœžœ"˜1Jšžœ˜—Jšœžœ˜Jšœ˜J˜—šŸ œžœ!žœžœ˜@šžœžœžœžœ˜$J˜J˜Jšœ˜—š žœžœ%žœ žœž˜CJšžœžœ˜%Jšžœ˜—Jšœ˜—J˜šŸ œžœ žœ žœ˜;J˜ šžœ*žœžœž˜;Jšœ˜Jšžœ˜—Jšœ˜J˜—šŸœžœ˜)Jšœ1˜1šžœ žœž˜šžœžœ˜+Jšžœžœ˜(—Jšžœ˜—Jšœ˜J˜—šŸœžœžœžœ˜9šžœ žœž˜Jšžœžœžœ"˜BJšžœžœžœžœ˜@Jšžœ,žœžœ˜:Jšžœ˜—Jšžœ ˜šœ˜J˜——J˜Jšœ5žœžœ˜@Jšœ5žœžœ˜AJšœ8˜8J˜Jšžœ˜J˜J™—…—gzΜ