<> <> <> <> DIRECTORY Carets USING [ResumeCarets, SuspendCarets], Imager USING [black, Color, Context, MaskRectangleI, SetColor, white], ImagerBackdoor USING [MoveViewRectangle], Menus USING [ChangeNumberOfLines], ViewerBLT USING [], ViewerClasses USING [Column, HBltRule, PaintProc, PaintRectangle, PaintRectangleRec, VBltRule, Viewer], ViewerForkers USING [ForkCall, ForkPaint], ViewerLocks USING [CallUnderWriteLock], ViewerOps USING [EstablishViewerPosition, FetchViewerClass, PaintViewer, UserToScreenCoords], ViewerPrivate USING [GreyScreen, PaintScreen, rootViewerTree, Screen, ViewerScreen], ViewerSpecs USING [captionHeight, colorScreenHeight, colorScreenWidth, openBottomY, openLeftLeftX, openLeftWidth, openRightLeftX, openRightWidth, openTopY, windowBorderSize]; ViewerBLTImpl: CEDAR PROGRAM IMPORTS Carets, Imager, ImagerBackdoor, Menus, ViewerForkers, ViewerLocks, ViewerOps, ViewerPrivate, ViewerSpecs EXPORTS ViewerBLT SHARES Menus, ViewerOps, ViewerClasses = BEGIN OPEN ViewerClasses, ViewerSpecs; disable: BOOL _ FALSE; ChangeNumberOfLines: PUBLIC PROC[viewer: Viewer, newLines: [0..5)] = { LockedChangeLines: PROC = { oldBox, newBox: Box; IF viewer.menu = NIL THEN RETURN; oldBox _ [viewer.wx+viewer.cx, viewer.wy+viewer.cy, viewer.cw, viewer.ch]; Menus.ChangeNumberOfLines[viewer.menu, newLines]; ViewerOps.EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh]; newBox _ [viewer.wx+viewer.cx, viewer.wy+viewer.cy, viewer.cw, viewer.ch]; BLTViewer[viewer, oldBox, newBox]; }; ViewerLocks.CallUnderWriteLock[LockedChangeLines, viewer]; }; InternalPaintColumn: PUBLIC PROC [column: Column, paintIcons: BOOL _ TRUE] = { oldIndex: CARDINAL; oldSnapshot: ColumnSnapshot; newSnapshot: ColumnSnapshot; invalidTop, invalidBottom: INTEGER; IF column = static THEN { FOR v: Viewer _ ViewerPrivate.rootViewerTree[column], v.sibling UNTIL v=NIL DO IF ~v.offDeskTop THEN ForkPaintViewerAll[v]; ENDLOOP; RETURN }; Carets.SuspendCarets[]; { ENABLE UNWIND => Carets.ResumeCarets[]; IF ViewerPrivate.rootViewerTree[column] = NIL THEN { height, leftX, width, bottomY: INTEGER; [height, leftX, width, bottomY, ] _ ColumnInfo[column]; ViewerPrivate.GreyScreen[IF column=color THEN color ELSE bw, leftX, bottomY, width, height]; ClearSnapshot[snapshots[column].before]; IF paintIcons AND column # color THEN -- paint newly visible icons FOR v: Viewer _ ViewerPrivate.rootViewerTree[static], v.sibling UNTIL v=NIL DO IF ~v.iconic OR v.offDeskTop THEN LOOP; IF v.wy + v.wh < bottomY THEN LOOP; IF v.wx + v.ww < leftX THEN LOOP; IF v.wx > leftX + width THEN LOOP; ForkPaintViewerAll[v]; ENDLOOP; GO TO done; }; oldSnapshot _ snapshots[column].before; newSnapshot _ SnapshotColumn[column]; snapshots[column].before _ newSnapshot; snapshots[column].after _ oldSnapshot; IF oldSnapshot = NIL OR disable THEN { FOR v: Viewer _ ViewerPrivate.rootViewerTree[column], v.sibling UNTIL v=NIL DO ForkPaintViewerAll[v]; ENDLOOP; GO TO done; }; <> <> <> <<>> invalidTop _ -1; -- top of section that has been blitted over invalidBottom _ 1000; -- bottom of section that has been blitted over <> FOR i: CARDINAL IN [0..newSnapshot.size) DO IF newSnapshot[i].viewer = NIL THEN EXIT; oldIndex _ GetIndex[oldSnapshot, newSnapshot[i].viewer]; IF oldIndex = oldSnapshot.size THEN LOOP; -- a new viewer <<>> <> IF oldSnapshot[oldIndex].box.y + oldSnapshot[oldIndex].box.h > invalidBottom AND oldSnapshot[oldIndex].box.y < invalidTop THEN LOOP; <<>> <> IF newSnapshot[i].box.y + newSnapshot[i].box.h > oldSnapshot[oldIndex].box.y + oldSnapshot[oldIndex].box.h THEN LOOP; IF newSnapshot[i].box # oldSnapshot[oldIndex].box THEN { BLTViewer[newSnapshot[i].viewer, oldSnapshot[oldIndex].box, newSnapshot[i].box]; invalidTop _ MAX[invalidTop, newSnapshot[i].box.y + newSnapshot[i].box.h]; invalidBottom _ MIN[invalidBottom, newSnapshot[i].box.y]; }; newSnapshot[i].painted _ TRUE; ENDLOOP; <> FOR i: CARDINAL DECREASING IN [0..newSnapshot.size) DO IF newSnapshot[i].viewer = NIL THEN LOOP; IF newSnapshot[i].painted THEN LOOP; oldIndex _ GetIndex[oldSnapshot, newSnapshot[i].viewer]; IF oldIndex = oldSnapshot.size THEN LOOP; -- a new viewer <> IF oldSnapshot[oldIndex].box.y + oldSnapshot[oldIndex].box.h > invalidBottom AND oldSnapshot[oldIndex].box.y < invalidTop THEN LOOP; <> IF newSnapshot[1].viewer # NIL AND -- more than one viewer in column newSnapshot[i].box.y < oldSnapshot[oldIndex].box.y THEN LOOP; IF newSnapshot[i].box # oldSnapshot[oldIndex].box THEN { BLTViewer[newSnapshot[i].viewer, oldSnapshot[oldIndex].box, newSnapshot[i].box]; invalidTop _ MAX[invalidTop, newSnapshot[i].box.y + newSnapshot[i].box.h]; invalidBottom _ MIN[invalidBottom, newSnapshot[i].box.y]; }; newSnapshot[i].painted _ TRUE; ENDLOOP; FOR i: CARDINAL IN [0..newSnapshot.size) DO IF newSnapshot[i].viewer = NIL THEN EXIT; IF ~newSnapshot[i].painted THEN ForkPaintViewerAll[newSnapshot[i].viewer]; ENDLOOP; EXITS done => {}; }; Carets.ResumeCarets[]; }; BLTViewer: PROC [viewer: Viewer, oldBox, newBox: Box] = { rect: PaintRectangle; hBLT: HBltRule; vBLT: VBltRule; w, h, header: INTEGER; fromX, fromY, toX, toY: INTEGER; wbs: INTEGER _ IF viewer.border THEN windowBorderSize ELSE 0; header _ viewer.wh - (viewer.cy + viewer.ch); <> w _ MIN[oldBox.w, newBox.w]; h _ MIN[oldBox.h, newBox.h]; hBLT _ IF oldBox.w = newBox.w THEN left ELSE viewer.class.bltH; SELECT hBLT FROM left, center, right => { <> IF oldBox.w # newBox.w THEN w _ w - wbs; -- don't include right border toX _ newBox.x; fromX _ oldBox.x; }; ENDCASE => {ForkPaintViewerAll[viewer]; RETURN}; vBLT _ IF oldBox.h = newBox.h THEN top ELSE viewer.class.bltV; SELECT vBLT FROM top => { IF oldBox.h # newBox.h THEN h _ h - wbs; -- don't include bottom border fromY _ oldBox.y + oldBox.h - h; toY _ newBox.y + newBox.h - h; }; bottom => { h _ h - header; -- don't include top header fromY _ oldBox.y; toY _ newBox.y; }; center => { fromY _ oldBox.y + wbs + oldBox.h/2 - h/2; toY _ newBox.y + wbs + newBox.h/2 - h/2; h _ h - header - wbs; -- don't include top or bottom }; ENDCASE => {ForkPaintViewerAll[viewer]; RETURN}; { OPEN viewer; action: PROC [context: Imager.Context] ~ { rect _ NEW[PaintRectangleRec _ [x: toX, y: toY, w: w, h: h]]; IF fromX # toX OR fromY # toY AND w >=0 AND h >= 0 THEN ImagerBackdoor.MoveViewRectangle[context: context, width: w, height: h, fromX: fromX, fromY: fromY, toX: toX, toY: toY]; <<-- erase remainder>> Imager.SetColor[context, Imager.white]; IF wy < rect.y THEN -- some on the bottom Imager.MaskRectangleI[context, wx, wy, ww, rect.y - wy]; IF rect.h >=0 AND wy+wh > rect.y+rect.h THEN -- some on the top Imager.MaskRectangleI[context, wx, rect.y+rect.h, ww, (wy+wh) - (rect.y+rect.h)]; IF wx < rect.x THEN -- some on the left Imager.MaskRectangleI[context, wx, wy, rect.x - wx, wh]; IF rect.w >=0 AND wx+ww > rect.x+rect.w THEN -- some on the right Imager.MaskRectangleI[context, rect.x+rect.w, wy, (wx+ww) - (rect.x+rect.w), wh]; Imager.SetColor[context, Imager.black]; IF viewer.border AND (oldBox.w # newBox.w OR oldBox.h # newBox.h) THEN { Imager.MaskRectangleI[context, wx, wy, ww, wbs]; Imager.MaskRectangleI[context, wx, wy, wbs, wh]; Imager.MaskRectangleI[context, wx+ww-wbs, wy, wbs, wh]; Imager.MaskRectangleI[context, wx, wy+wh-wbs, ww, wbs]; }; }; ViewerPrivate.PaintScreen[ViewerPrivate.ViewerScreen[viewer], action]; }; IF viewer.wx < rect.x OR (viewer.wx + viewer.ww > rect.x + rect.w) OR (viewer.wy + viewer.wh - header) < rect.y OR (viewer.wy + viewer.wh > rect.y + rect.h) THEN { <> ViewerOps.PaintViewer[viewer, caption, FALSE]; ViewerOps.PaintViewer[viewer, menu, FALSE]; }; IF viewer.menu # NIL THEN { <> viewer.menu.x _ wbs; viewer.menu.y _ viewer.wh-captionHeight; }; <> ViewerForkers.ForkCall[viewer, RepaintViewer, NEW[RepaintActionData _ [viewer: viewer, xChanged: oldBox.w # newBox.w, yChanged: oldBox.h # newBox.h, rect: rect]]]; }; RepaintActionData: TYPE = RECORD [ <> viewer: Viewer, xChanged: BOOL, yChanged: BOOL, rect: PaintRectangle]; RepaintViewer: PROC [data: REF] = { WITH data SELECT FROM mine: REF RepaintActionData => { viewer: Viewer _ mine.viewer; ViewerOps.PaintViewer[viewer, client, FALSE, mine.rect]; <> <> IF ContainerPaint = NIL THEN ContainerPaint _ ViewerOps.FetchViewerClass[$Container].paint; IF viewer.class.paint = ContainerPaint THEN RepaintBoundViewers[viewer, mine.rect, mine.xChanged, mine.yChanged]; }; ENDCASE; }; RepaintBoundViewers: PROC[viewer: Viewer, rect: PaintRectangle, xChanged, yChanged: BOOL] = { xBound: LIST OF Viewer _ NARROW[viewer.class.get[viewer, $XBound]]; yBound: LIST OF Viewer _ NARROW[viewer.class.get[viewer, $YBound]]; IF xChanged THEN { <> FOR list: LIST OF Viewer _ xBound, list.rest WHILE list # NIL DO v: Viewer _ list.first; IF v.class.flavor = $Text OR v.class.flavor = $Typescript THEN {ViewerOps.PaintViewer[v, all]; LOOP}; <> ViewerOps.PaintViewer[v, all, FALSE, rect]; IF v.class.paint = ContainerPaint THEN { doubleBounded: BOOL _ FALSE; IF yChanged THEN FOR l: LIST OF Viewer _ yBound, l.rest WHILE l # NIL DO IF l.first = v THEN {doubleBounded _ TRUE; EXIT}; ENDLOOP; RepaintBoundViewers[v, rect, TRUE, doubleBounded]}; ENDLOOP; }; IF yChanged THEN { <> FOR list: LIST OF Viewer _ yBound, list.rest WHILE list # NIL DO v: Viewer _ list.first; painted: BOOL _ FALSE; FOR list: LIST OF Viewer _ xBound, list.rest WHILE list # NIL DO IF list.first = v THEN {painted _ TRUE; EXIT}; ENDLOOP; IF painted THEN LOOP; <> ViewerOps.PaintViewer[v, all, FALSE, rect]; IF v.class.paint = ContainerPaint THEN RepaintBoundViewers[v, rect, FALSE, TRUE]; ENDLOOP; }; }; WithinRect: PROC[v: Viewer, rect: PaintRectangle] RETURNS[BOOL] = { vx, vy: INTEGER; IF v.parent.class.topDownCoordSys THEN [vx, vy] _ ViewerOps.UserToScreenCoords[v.parent, v.wx, v.wy+v.wh] ELSE [vx, vy] _ ViewerOps.UserToScreenCoords[v.parent, v.wx, v.wy]; RETURN[vx >= rect.x AND (vx + v.ww <= rect.x + rect.w) AND vy >= rect.y AND (vy + v.wh <= rect.y + rect.h)]; }; ContainerPaint: ViewerClasses.PaintProc _ NIL; ColumnInfo: PROC[column: Column] RETURNS[totalSpace, leftX, width, bottomY, nextY: INTEGER] = INLINE { SELECT column FROM left => { totalSpace _ openTopY-openBottomY; bottomY _ nextY _ openBottomY; leftX _ openLeftLeftX; width _ openLeftWidth; }; right => { totalSpace _ openTopY-openBottomY; bottomY _ nextY _ openBottomY; leftX _ openRightLeftX; width _ openRightWidth; }; color => { totalSpace _ colorScreenHeight; bottomY _ nextY _ 0; leftX _ 0; width _ colorScreenWidth; }; ENDCASE => ERROR; }; ColumnSnapshot: TYPE ~ REF ColumnSequence; ColumnSequence: TYPE ~ RECORD[SEQUENCE size: NAT OF ColumnRec]; ColumnRec: TYPE ~ RECORD[viewer: Viewer, box: Box, painted: BOOL _ FALSE]; Box: TYPE = RECORD[x, y, w, h: INTEGER]; snapshots: ARRAY Column OF RECORD[before, after: ColumnSnapshot]; SnapshotColumn: PROC[column: Column] RETURNS[snapshot: ColumnSnapshot] = { length: CARDINAL _ 0; snapshot _ snapshots[column].after; IF snapshot = NIL THEN snapshot _ NEW[ColumnSequence[10]]; ClearSnapshot[snapshot]; FOR v: Viewer _ ViewerPrivate.rootViewerTree[column], v.sibling UNTIL v = NIL DO IF length = snapshot.size THEN { -- make a bigger snapshot temp: ColumnSnapshot _ NEW[ColumnSequence[snapshot.size + 10]]; FOR i: CARDINAL IN [0..snapshot.size) DO temp[i] _ snapshot[i]; ENDLOOP; snapshot _ temp; }; snapshot[length] _ [v, [v.wx, v.wy, v.ww, v.wh]]; length _ length + 1; ENDLOOP; }; ClearSnapshot: PROC[snapshot: ColumnSnapshot] = { IF snapshot = NIL THEN RETURN; FOR i: CARDINAL IN [0..snapshot.size) DO snapshot[i].viewer _ NIL; ENDLOOP; }; GetIndex: PROC[snapshot: ColumnSnapshot, viewer: Viewer] RETURNS[CARDINAL] = { FOR i: CARDINAL IN [0..snapshot.size) DO IF snapshot[i].viewer = NIL THEN EXIT; IF snapshot[i].viewer = viewer THEN RETURN[i]; ENDLOOP; RETURN[snapshot.size]; }; Invalidate: PUBLIC PROC = { ClearSnapshot[snapshots[left].before]; ClearSnapshot[snapshots[right].before]; ClearSnapshot[snapshots[color].before]; }; ForkPaintViewerAll: PROC [viewer: Viewer] = TRUSTED { ViewerForkers.ForkPaint[viewer, all]; }; END. <<>> <> <> <> <> <> <<[vX, vY] _ ViewerOps.UserToScreenCoords[viewer, 0,>> <> <0 THEN vY ELSE vY-nLines;>> <0 THEN vY-nLines ELSE vY;>> <> <> <> <> <> <> <> <> <> <> <<];>> <> <<};>> <<>> <> <> <<};>> <<>>