<<>> <> <> <> <> <> <> 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, nColumns, 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 main, 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]; SnapshotsRep: TYPE ~ RECORD[SEQUENCE c: Column OF RECORD[before, after: ColumnSnapshot]]; snapshots: REF SnapshotsRep ¬ NEW[SnapshotsRep[ViewerSpecs.nColumns]]; 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.