<> <> <> <> DIRECTORY Carets USING [ResumeCarets, SuspendCarets], IconManager USING [selectedIcon], Icons USING [DrawIcon], Imager USING [black, ClipIntRectangle, ClipView, Context, DoSave, MaskIntRectangle, MaskCharacters, Create, IntScaleT, Reset, SetColor, SetIntCP, SetView, IntTranslateT, white], Menus USING [], MenusPrivate USING [DrawMenu], Process USING [Detach, GetCurrent, MsecToTicks, Pause, Ticks], Rope USING [ROPE], VFonts USING [defaultFont, RopeWidth], ViewerOps, ViewerClasses, ViewerLocks USING [CallUnderReadLock, CallUnderWriteLock, ColumnWedged, Wedged], ViewerSpecs, ViewersStallNotifier, ViewerTools USING [GetSelectedViewer]; ViewerPaintImpl: CEDAR MONITOR IMPORTS Carets, Imager, IconManager, Icons, MenusPrivate, Process, VFonts, ViewerLocks, ViewerTools, ViewerOps, ViewerSpecs EXPORTS ViewerOps, ViewersStallNotifier SHARES Menus, ViewerClasses, ViewerLocks = BEGIN OPEN Imager, ViewerClasses, ViewerOps, ViewerSpecs; InvisiblePaint: PUBLIC SIGNAL = CODE; PaintDocumentHeader: PROC [viewer: Viewer, context: Context, clear: BOOL, hint: PaintHint, whatChanged: REF ANY] = BEGIN OPEN viewer; wbs: INTEGER = IF viewer.border THEN windowBorderSize ELSE 0; ClipIntRectangle[context, [wx, wy, ww, wh]]; IF hint#menu THEN BEGIN IF viewer.class.caption#NIL THEN BEGIN -- client-painted caption CallClient: PROC = BEGIN ClipIntRectangle[context, [wx+wbs, wy+wh-captionHeight-wbs, ww-(2*wbs), wh-wbs]]; viewer.class.caption[viewer, context]; END; DoSave[context, CallClient]; END ELSE BEGIN headerW: INTEGER _ VFonts.RopeWidth[name]; IF viewer.saveInProgress THEN headerW _ headerW + sipW ELSE IF viewer.newFile THEN headerW _ headerW + newFW ELSE IF viewer.newVersion THEN headerW _ headerW + newVW; IF viewer.link#NIL THEN headerW _ headerW + linkW; headerW _ MIN[headerW, ww]; MaskIntRectangle[context, [wx, wy+wh-captionHeight, ww, captionHeight]]; SetColor[context, white]; SetIntCP[context, [wx+((ww-headerW)/2), wy+wh-captionHeight+4]]; MaskCharacters[context, VFonts.defaultFont, name]; IF viewer.saveInProgress THEN MaskCharacters[context, VFonts.defaultFont, sipT] ELSE IF viewer.newFile THEN MaskCharacters[context, VFonts.defaultFont, newFT] ELSE IF viewer.newVersion THEN MaskCharacters[context, VFonts.defaultFont, newVT]; IF viewer.link#NIL THEN MaskCharacters[context, VFonts.defaultFont, linkT]; END; END; IF menu#NIL AND hint#caption THEN BEGIN height: INTEGER _ menu.linesUsed*menuHeight; IF whatChanged=NIL THEN BEGIN IF ~clear THEN BEGIN SetColor[context, white]; MaskIntRectangle[context, [wx+wbs, wy+wh-captionHeight-height, ww-(2*wbs), height]]; END; SetColor[context, black]; MaskIntRectangle[context, [wx, wy+wh-captionHeight-height-windowBorderSize,ww, menuBarHeight]]; END; MenusPrivate.DrawMenu[viewer.menu, context, wx+wbs, wy+wh-menuHeight-captionHeight, whatChanged]; END; END; PaintIcon: PROC [viewer: Viewer, hint: PaintHint, whatChanged: REF ANY _ NIL] = BEGIN context: Context; label: Rope.ROPE; IF viewer.parent#NIL THEN RETURN; context _ AcquireContext[viewer, FALSE]; -- all icons on b&w display <> IF viewer.icon=private THEN viewer.class.paint[viewer, context, whatChanged, hint=all] ELSE IF hint=all OR hint=caption THEN BEGIN DrawIcon: PROC = {Icons.DrawIcon[flavor: viewer.icon, context: context, label: label]}; IF viewer.icon=document AND (viewer.newVersion OR viewer.newFile) THEN viewer.icon _ dirtyDocument ELSE IF viewer.icon=dirtyDocument AND NOT (viewer.newVersion OR viewer.newFile) THEN viewer.icon _ document; label _ NARROW[ViewerOps.FetchProp[viewer, $IconLabel]]; IF label = NIL THEN label _ viewer.name; DoSave[context, DrawIcon]; IF IconManager.selectedIcon=viewer THEN BEGIN SetColor[context, $XOR]; MaskIntRectangle[context, [0, 0, iconWidth, iconHeight]]; END; END; ReleaseContext[context]; END; SetIconLabel: PUBLIC PROC [viewer: Viewer, label: Rope.ROPE] = BEGIN ViewerOps.AddProp[viewer, $IconLabel, label]; END; RecursivelyPaintViewers: PROC [viewer: Viewer, hint: PaintHint, clearClient: BOOL, clear: BOOL, rect: PaintRectangle, whatChanged: REF ANY] = BEGIN OPEN viewer; vx, vy: INTEGER; context: Context _ NIL; bitmapY: INTEGER _ 0; FOR v: Viewer _ viewer, v.parent UNTIL v=NIL DO -- visibility test IF v.parent#NIL THEN BEGIN IF (v.wy+v.wh < 0) OR (v.wy > v.parent.ch) THEN RETURN; IF (v.ww+v.ww < 0) OR (v.wx > v.parent.cw) THEN RETURN; END ENDLOOP; IF hint#client THEN BEGIN ENABLE UNWIND => IF context#NIL THEN ReleaseContext[context]; PaintHeader: PROC = {PaintDocumentHeader[viewer, context, clear, hint, whatChanged]}; context _ AcquireContext[parent, viewer.column=color]; IF ~viewer.visible OR viewer.destroyed THEN SIGNAL InvisiblePaint; IF clearClient AND ~clear AND hint=all THEN BEGIN SetColor[context, white]; MaskIntRectangle[context, [wx, wy, ww, wh]]; SetColor[context, black]; clear _ TRUE; END; IF border AND hint=all THEN BEGIN wbs: INTEGER ~ windowBorderSize; MaskIntRectangle[context, [wx, wy, ww, wbs]]; MaskIntRectangle[context, [wx, wy, wbs, wh]]; MaskIntRectangle[context, [wx+ww-wbs, wy, wbs, wh]]; MaskIntRectangle[context, [wx, wy+wh-wbs, ww, wbs]]; END; IF parent=NIL AND viewer.column#static THEN DoSave[context, PaintHeader]; ReleaseContext[context]; END; IF hint=all OR hint=client THEN BEGIN ENABLE UNWIND => IF context#NIL THEN ReleaseContext[context]; PaintClient: PROC = {class.paint[viewer, context, whatChanged, clear]}; context _ NIL; context _ AcquireContext[viewer, viewer.column=color]; IF ~viewer.visible OR viewer.destroyed THEN SIGNAL InvisiblePaint; IF rect#NIL AND class.bltContents = none THEN clearClient _ TRUE; IF clearClient AND ~clear THEN BEGIN SetColor[context, white]; MaskIntRectangle[context, [0, 0, cw, ch]]; SetColor[context, black]; IF rect # NIL THEN whatChanged _ NIL; rect _ NIL; clear _ TRUE; END; IF class.paint#NIL THEN BEGIN IF clientContext THEN PaintClient[] ELSE DoSave[context, PaintClient]; END; ReleaseContext[context]; END; <> IF rect # NIL AND viewer.parent = NIL THEN { IF viewer.cx >= rect.x AND (viewer.cx + viewer.cw <= rect.x + rect.w) AND viewer.cy >= rect.y AND (viewer.cy + viewer.ch <= rect.y + rect.h) THEN RETURN}; <> IF hint=all OR clear THEN <> FOR v: Viewer _ viewer.child, v.sibling UNTIL v=NIL DO IF rect # NIL THEN { -- don't paint if it is completely within the blt <> IF viewer.class.coordSys = top THEN [vx, vy] _ ViewerOps.UserToScreenCoords[viewer, v.wx, v.wy+v.wh] ELSE [vx, vy] _ ViewerOps.UserToScreenCoords[viewer, v.wx, v.wy]; IF vx >= rect.x AND (vx + v.ww <= rect.x + rect.w) AND vy >= rect.y AND (vy + v.wh <= rect.y + rect.h) THEN LOOP}; RecursivelyPaintViewers[v, all, FALSE, clear, rect, whatChanged]; ENDLOOP; END; PaintViewer: PUBLIC PROC [viewer: Viewer, hint: PaintHint _ client, clearClient: BOOL _ TRUE, whatChanged: REF ANY _ NIL] = BEGIN LocalPaintViewer: PROC = { ENABLE ABORTED => {Carets.ResumeCarets[]; CONTINUE}; rect: PaintRectangle; IF ~viewer.visible OR viewer.destroyed THEN RETURN; IF viewer.iconic THEN {PaintIcon[viewer, hint, whatChanged]; RETURN}; Carets.SuspendCarets[]; IF ISTYPE[whatChanged, PaintRectangle] THEN rect _ NARROW[whatChanged]; RecursivelyPaintViewers[viewer, hint, clearClient AND ~(hint=menu OR hint=caption), FALSE, rect, whatChanged ! InvisiblePaint => CONTINUE]; Carets.ResumeCarets[]}; ViewerLocks.CallUnderReadLock[LocalPaintViewer, viewer ! ViewerLocks.Wedged => CONTINUE]; END; PaintVector: TYPE = REF PaintVectorRec; PaintVectorRec: TYPE = RECORD [ next: PaintVector _ NIL, viewer: Viewer _ NIL, inUse: BOOL _ FALSE, resetOnRelease: BOOL _ FALSE, color: BOOL _ FALSE, process: UNSAFE PROCESS _ NIL, context: Context _ NIL, previous: PaintVector _ NIL]; nVectors: CARDINAL = 5; usedVectors: CARDINAL _ 0; PaintVectorAvailable: CONDITION; paintingLock: CARDINAL _ 0; vectorRoot: PaintVector _ NIL; Init: ENTRY PROC = BEGIN new: PaintVector; FOR n: CARDINAL IN [0..nVectors) DO new _ NEW[PaintVectorRec]; new.context _ Create[$LFDisplay]; new.next _ vectorRoot; IF vectorRoot#NIL THEN vectorRoot.previous _ new; vectorRoot _ new; ENDLOOP; newFW _ VFonts.RopeWidth[newFT]; newVW _ VFonts.RopeWidth[newVT]; linkW _ VFonts.RopeWidth[linkT]; sipW _ VFonts.RopeWidth[sipT]; END; colorInit: BOOL _ FALSE; InitialiseColorPainting: PUBLIC PROC = BEGIN colorInit _ TRUE; ViewerOps.GreyScreen[0, 0, 9999, 9999, TRUE, FALSE]; END; ZapVector: INTERNAL PROC [p: PaintVector] = BEGIN oldContext: Context _ p.context; p.context _ Create[$LFDisplay]; p.viewer _ NIL; p.inUse _ FALSE; p.process _ NIL; IF ~p.color THEN usedVectors _ usedVectors - 1; SetView[oldContext, [0,0,0,0]]; -- smash old BROADCAST PaintVectorAvailable; END; UnWedgePainter: PUBLIC ENTRY PROC [process: PROCESS, kind: ATOM] = BEGIN ENABLE UNWIND => NULL; FOR p: PaintVector _ vectorRoot, p.next UNTIL p=NIL DO IF p.inUse AND p.process=process THEN ZapVector[p]; ENDLOOP; END; UnWedge: ENTRY PROC [process: PROCESS, kind: ATOM] = BEGIN <> FOR p: PaintVector _ vectorRoot, p.next UNTIL p=NIL DO IF p.inUse THEN ZapVector[p]; ENDLOOP; usedVectors _ 0; -- make sure paintingLock _ 0; -- make sure BROADCAST PaintVectorAvailable; END; DisablePainting: PUBLIC ENTRY PROC [wait: BOOL _ TRUE] = BEGIN ENABLE UNWIND => NULL; InternalDisablePainting[wait]; END; InternalDisablePainting: INTERNAL PROC [wait: BOOL _ TRUE] = INLINE BEGIN IF wait THEN WHILE usedVectors#0 DO WAIT PaintVectorAvailable; ENDLOOP; paintingLock _ paintingLock+1; END; EnablePainting: PUBLIC ENTRY PROC = BEGIN ENABLE UNWIND => NULL; InternalEnablePainting[]; END; InternalEnablePainting: INTERNAL PROC = INLINE BEGIN IF paintingLock#0 THEN paintingLock _ paintingLock-1; <> IF paintingLock=0 THEN BROADCAST PaintVectorAvailable; END; DisablePaintingUnWedged: INTERNAL PROC [wait: BOOL _ TRUE] = INLINE BEGIN AllWedged: INTERNAL PROC RETURNS[BOOL] = INLINE BEGIN FOR p: PaintVector _ vectorRoot, p.next UNTIL p=NIL DO IF ~p.inUse THEN LOOP; IF p.viewer = NIL THEN RETURN[FALSE]; -- context = screen IF ~ViewerLocks.ColumnWedged[ViewerColumn[p.viewer]].wedged THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; END; IF wait THEN WHILE usedVectors#0 DO IF AllWedged[] THEN EXIT ELSE WAIT PaintVectorAvailable; ENDLOOP; paintingLock _ paintingLock+1; END; WaitForPaintingToFinish: PUBLIC ENTRY PROC = BEGIN <> WHILE usedVectors#0 DO WAIT PaintVectorAvailable ENDLOOP; END; ResetPaintCache: PUBLIC ENTRY PROC [viewer: Viewer _ NIL, wait: BOOL _ TRUE] = BEGIN ENABLE UNWIND => NULL; DisablePaintingUnWedged[wait]; FOR p: PaintVector _ vectorRoot, p.next UNTIL p=NIL DO IF p.inUse THEN BEGIN IF p.viewer = NIL OR ~ViewerLocks.ColumnWedged[ViewerColumn[p.viewer]].wedged THEN IF wait THEN RETURN WITH ERROR fatalViewersError; p.resetOnRelease _ TRUE; END ELSE IF p.viewer#NIL THEN BEGIN Reset[p.context]; p.viewer _ NIL; END; ENDLOOP; InternalEnablePainting[]; END; fatalViewersError: ERROR = CODE; ReleaseContext: PUBLIC ENTRY PROC [free: Context] = BEGIN ENABLE UNWIND => NULL; CleanUp: INTERNAL PROC [p: PaintVector] = BEGIN p.inUse _ FALSE; IF usedVectors=0 THEN RETURN WITH ERROR fatalViewersError ELSE usedVectors _ usedVectors-1; IF p.resetOnRelease THEN BEGIN Reset[p.context]; p.viewer _ NIL; p.resetOnRelease _ FALSE; END; p.process _ NIL; BROADCAST PaintVectorAvailable; END; IF free=NIL THEN RETURN WITH ERROR fatalViewersError; FOR p: PaintVector _ vectorRoot, p.next UNTIL p=NIL DO IF free=p.context THEN {CleanUp[p]; EXIT}; ENDLOOP; END; AcquireContext: PUBLIC ENTRY PROC [viewer: Viewer, color: BOOL _ FALSE] RETURNS [allocated: Context] = BEGIN ENABLE UNWIND => NULL; bestFit: PaintVector; nil: BOOL _ FALSE; MakeFirst: INTERNAL PROC [pv: PaintVector] = INLINE BEGIN -- LRU caching scheme IF pv=vectorRoot THEN RETURN; pv.previous.next _ pv.next; IF pv.next#NIL THEN pv.next.previous _ pv.previous; pv.next _ vectorRoot; vectorRoot.previous _ pv; vectorRoot _ pv; END; IF color THEN BEGIN IF ~colorInit THEN ERROR fatalViewersError; END; UNTIL usedVectors openBottomY THEN BEGIN ClipIcon[context]; ClipView[context, [wx, wy, ww, wh], FALSE]; IntTranslateT[context, wx, wy]; --xlate since ClipView does no x,y translation END ELSE SetView[context, [wx, wy, ww, wh]]; END ELSE IF viewer.parent=NIL THEN BEGIN OPEN viewer; SetView[context, [cx, cy, cw, ch]]; IF class.coordSys=top THEN BEGIN IntTranslateT[context, 0, ch]; IntScaleT[context, 1, -1]; END; END ELSE BEGIN RecursivelyPushParent[viewer]; IF invert THEN BEGIN IntTranslateT[context, x, y+viewer.ch]; IntScaleT[context, 1, -1]; END ELSE IntTranslateT[context, x, y]; END; END; InvertForMenus: PUBLIC PROC [viewer: Viewer, x,y: INTEGER, w,h: INTEGER] = BEGIN context: Context _ AcquireContext[viewer.parent, viewer.column=color]; SetColor[context, $XOR]; MaskIntRectangle[context, [viewer.wx+x, y, MIN[w,viewer.ww-x], h]]; ReleaseContext[context]; END; ClipIcon: PROC [context: Context] = BEGIN <> ClipOpenViewer: ViewerOps.EnumProc = BEGIN IF ~v.iconic AND (v.column=right OR v.column=left) THEN ClipView[context, [v.wx, v.wy, v.ww, v.wh], TRUE]; END; ViewerOps.EnumerateViewers[ClipOpenViewer]; END; BlinkIcon: PUBLIC PROC [viewer: Viewer, count, interval: NAT] = BEGIN IF interval >= 1000 THEN interval _ interval/1000; IF viewer.offDeskTop THEN ViewerOps.ChangeColumn[viewer, left]; IF count = 1 THEN BlinkProcess[viewer, count, interval] ELSE TRUSTED {Process.Detach[FORK BlinkProcess[viewer, count, interval]]}; END; BlinkProcess: PROC [viewer: Viewer, count, n: NAT] = BEGIN howOften: Process.Ticks = Process.MsecToTicks[500]; Blink: PROC = BEGIN OPEN viewer; context: Context; IF NOT visible OR destroyed THEN RETURN; context _ AcquireContext[viewer, IF iconic THEN FALSE ELSE column=color]; SetColor[context, $XOR]; MaskIntRectangle[context, [0, 0, viewer.ww, viewer.wh]]; Process.Pause[Process.MsecToTicks[400]]; MaskIntRectangle[context, [0, 0, viewer.ww, viewer.wh]]; ReleaseContext[context]; Process.Pause[Process.MsecToTicks[350]]; -- so can be called back-to-back END; DO ViewerLocks.CallUnderWriteLock[Blink, viewer]; IF count = 1 THEN RETURN; IF count > 0 THEN count _ count - 1; FOR i: NAT IN [0..2*n) DO -- blink every n seconds, but wake up every half second. Process.Pause[howOften]; IF viewer.destroyed OR ~viewer.iconic OR viewer = ViewerTools.GetSelectedViewer[] THEN RETURN; ENDLOOP; ENDLOOP; END; newVT: Rope.ROPE = " [New Version]"; newVW: INTEGER; newFT: Rope.ROPE = " [New File]"; newFW: INTEGER; linkT: Rope.ROPE = " [Split]"; linkW: INTEGER; sipT: Rope.ROPE = " [Saving...]"; sipW: INTEGER; Init[]; ViewerOps.GreyScreen[0, 0, 9999, 9999, FALSE, FALSE]; END.