<> <> <> <> DIRECTORY Carets USING [pCaretViewer, ResumeCarets, sCaretViewer, StartCaret, SuspendCarets], ColorDisplay USING [height, width], Imager USING [black, ClipIntRectangle, Context, MaskIntRectangle, MoveSurfaceRectangle, SetColor, white], Menus, Process USING [Detach], ViewerBLT, ViewerClasses, ViewerLocks USING [CallUnderWriteLock], ViewerOps, ViewerSpecs, WindowManagerPrivate USING [rootViewerTree]; ViewerBLTImpl: CEDAR PROGRAM IMPORTS Carets, ColorDisplay, Imager, Menus, Process, ViewerLocks, ViewerOps, ViewerSpecs, WindowManagerPrivate EXPORTS ViewerBLT, ViewerOps SHARES Menus, ViewerOps, ViewerClasses = BEGIN OPEN ViewerClasses, ViewerOps, ViewerSpecs, WindowManagerPrivate; ChangeNumberOfLines: PUBLIC PROC[viewer: Viewer, newLines: [0..5)] = BEGIN LockedChangeLines: PROC = BEGIN oldBox, newBox: Box; IF viewer.menu = NIL THEN RETURN; oldBox _ [viewer.cx, viewer.cy, viewer.cw, viewer.ch]; Menus.ChangeNumberOfLines[viewer.menu, newLines]; ViewerOps.EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh]; newBox _ [viewer.cx, viewer.cy, viewer.cw, viewer.ch]; BLTViewer[viewer, oldBox, newBox]; END; ViewerLocks.CallUnderWriteLock[LockedChangeLines, viewer]; END; InternalPaintColumn: PUBLIC PROC [column: Column, paintIcons: BOOL _ TRUE] = BEGIN oldIndex: CARDINAL; oldSnapshot: ColumnSnapshot; newSnapshot: ColumnSnapshot; invalidTop, invalidBottom: INTEGER; IF column = static THEN { FOR v: Viewer _ rootViewerTree[column], v.sibling UNTIL v=NIL DO IF ~v.offDeskTop THEN TRUSTED {Process.Detach[FORK PaintViewer[v, all]]}; ENDLOOP; RETURN}; IF rootViewerTree[column] = NIL THEN { height, leftX, width, bottomY: INTEGER; [height, leftX, width, bottomY, ] _ ColumnInfo[column]; GreyScreen[leftX, bottomY, width, height, column=color, FALSE]; IF paintIcons AND column # color THEN -- paint newly visible icons FOR v: Viewer _ 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; TRUSTED {Process.Detach[FORK PaintViewer[v, all]]}; ENDLOOP; RETURN}; oldSnapshot _ snapshots[column].before; newSnapshot _ SnapshotColumn[column]; snapshots[column].before _ newSnapshot; snapshots[column].after _ oldSnapshot; IF oldSnapshot = NIL THEN { Carets.SuspendCarets[]; FOR v: Viewer _ rootViewerTree[column], v.sibling UNTIL v=NIL DO TRUSTED {Process.Detach[FORK PaintViewer[v, all]]}; ENDLOOP; WaitForPaintingToFinish[]; Carets.ResumeCarets[]; RETURN}; Carets.SuspendCarets[]; <<-- try to save cycles by BLTing bit maps around. >> <<-- this algorithm assumes that the column is almost in the same order as it was before,>> <<-- except for creating, deleting, or moving at most one viewer.>> <<-- the algorithm should never blt an invalid bitmap.>> <<>> invalidTop _ -1; -- top of section that has been blitted over invalidBottom _ 1000; -- bottom of section that has been blitted over <<-- iterate from bottom to top of column, BLTing bit maps if the space is free>> 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 <<-- check to see if the bitmap is invalid>> IF oldSnapshot[oldIndex].box.y + oldSnapshot[oldIndex].box.h > invalidBottom AND oldSnapshot[oldIndex].box.y < invalidTop THEN LOOP; <<-- check to see if BLTing would wipe out a bitmap above>> 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; <<-- now do it from top to bottom, catching the ones we couldn't do before>> 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 <<-- check to see if the bitmap is invalid>> IF oldSnapshot[oldIndex].box.y + oldSnapshot[oldIndex].box.h > invalidBottom AND oldSnapshot[oldIndex].box.y < invalidTop THEN LOOP; <<-- check to see if BLTing would wipe out a bitmap below>> 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 TRUSTED { Process.Detach[FORK PaintViewer[newSnapshot[i].viewer, all]]}; ENDLOOP; <> Carets.StartCaret[Carets.pCaretViewer, -10000, -10000, primary]; -- reset the primary caret Carets.StartCaret[Carets.sCaretViewer, -10000, -10000, secondary]; -- reset the secondary caret WaitForPaintingToFinish[]; Carets.ResumeCarets[]; END; BLTViewer: PROC[viewer: Viewer, oldBox, newBox: Box] = BEGIN rect: PaintRectangle; hBLT, vBLT: BltRule; w, h, header: INTEGER; context: Imager.Context; fromX, fromY, toX, toY: INTEGER; wbs: INTEGER _ IF viewer.border THEN windowBorderSize ELSE 0; header _ (viewer.wy + viewer.wh) - (viewer.cy + viewer.ch); <<-- assume that the menu is the same for before and after>> w _ MIN[oldBox.w, newBox.w]; h _ MIN[oldBox.h, newBox.h]; IF (viewer.class.flavor = $Text OR viewer.class.flavor = $Typescript) THEN vBLT _ none ELSE vBLT _ viewer.class.bltContents; IF oldBox.w = newBox.w THEN vBLT _ top; SELECT vBLT FROM top, bottom, center => { -- later we will distinguish between left, right and center IF oldBox.w # newBox.w THEN w _ w - wbs; -- don't include right border toX _ newBox.x; fromX _ oldBox.x}; ENDCASE => TRUSTED {Process.Detach[FORK PaintViewer[viewer, all]]; RETURN}; hBLT _ IF oldBox.h = newBox.h THEN top ELSE viewer.class.bltContents; SELECT hBLT 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 => TRUSTED {Process.Detach[FORK PaintViewer[viewer, all]]; RETURN}; BEGIN OPEN viewer; ENABLE UNWIND => IF context#NIL THEN ReleaseContext[context]; context _ AcquireContext[NIL, viewer.column=color]; rect _ NEW[PaintRectangleRec _ [blt, toX, toY, w, h]]; IF fromX # toX OR fromY # toY THEN [] _ Imager.MoveSurfaceRectangle[ context, [fromX, fromY, w, h], [toX, toY]]; <<-- erase remainder>> Imager.SetColor[context, Imager.white]; IF wy < rect.y THEN -- some on the bottom Imager.MaskIntRectangle[context, [wx, wy, ww, rect.y - wy]]; IF wy+wh > rect.y+rect.h THEN -- some on the top Imager.MaskIntRectangle[context, [wx, rect.y+rect.h, ww, (wy+wh) - (rect.y+rect.h)]]; IF wx < rect.x THEN -- some on the left Imager.MaskIntRectangle[context, [wx, wy, rect.x - wx, wh]]; IF wx+ww > rect.x+rect.w THEN -- some on the right Imager.MaskIntRectangle[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.MaskIntRectangle[context, [wx, wy, ww, wbs]]; Imager.MaskIntRectangle[context, [wx, wy, wbs, wh]]; Imager.MaskIntRectangle[context, [wx+ww-wbs, wy, wbs, wh]]; Imager.MaskIntRectangle[context, [wx, wy+wh-wbs, ww, wbs]]}; ReleaseContext[context]; END; 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 { -- repaint header PaintViewer[viewer, caption, FALSE]; PaintViewer[viewer, menu, FALSE]}; IF oldBox.w # newBox.w OR oldBox.h # newBox.h -- shape has changed THEN TRUSTED {Process.Detach[FORK PaintViewer[viewer, client, FALSE, rect]]}; IF viewer.menu # NIL THEN { -- reset cached position viewer.menu.x _ viewer.wx + wbs; viewer.menu.y _ viewer.wy + viewer.wh - menuHeight - captionHeight}; 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; ColumnSnapshot: TYPE = REF ColumnSequence; ColumnSequence: TYPE = RECORD[SEQUENCE size: NAT OF 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] = BEGIN length: CARDINAL _ 0; snapshot _ snapshots[column].after; IF snapshot = NIL THEN snapshot _ NEW[ColumnSequence[10]]; ClearSnapshot[snapshot]; FOR v: Viewer _ 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; END; ClearSnapshot: PROC[snapshot: ColumnSnapshot] = INLINE BEGIN IF snapshot = NIL THEN RETURN; FOR i: CARDINAL IN [0..snapshot.size) DO snapshot[i].viewer _ NIL; ENDLOOP; END; GetIndex: PROC[snapshot: ColumnSnapshot, viewer: Viewer] RETURNS[CARDINAL] = BEGIN 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]; END; screenGrey: REF CARDINAL = NEW[CARDINAL _ 104042B]; GreyScreen: PUBLIC PROC [x,y,w,h: INTEGER, color, icon: BOOL, stipple: REF CARDINAL _ NIL] = BEGIN context: Imager.Context _ AcquireContext[NIL, color]; IF icon AND y+h > openBottomY THEN ClipIcon[context]; Imager.SetColor[context, IF stipple=NIL THEN screenGrey ELSE stipple]; Imager.MaskIntRectangle[context, [x, y, w, h]]; ReleaseContext[context]; IF icon THEN RETURN; IF color THEN ClearSnapshot[snapshots[color].before] ELSE { IF x + w > openRightLeftX THEN ClearSnapshot[snapshots[right].before]; IF x < openLeftLeftX + openLeftWidth THEN ClearSnapshot[snapshots[left].before]}; END; ClipIcon: PROC [context: Imager.Context] = BEGIN <> ClipOpenViewer: ViewerOps.EnumProc = BEGIN IF ~v.iconic AND (v.column=right OR v.column=left) THEN Imager.ClipIntRectangle[context, [v.wx, v.wy, v.ww, v.wh], TRUE]; END; ViewerOps.EnumerateViewers[ClipOpenViewer]; END; END . . . Glitch: PUBLIC PROC [viewer: Viewer, nLines: INTEGER] = BEGIN dY, sY, vY, vX: INTEGER; w: INTEGER _ viewer.cw; h: INTEGER _ viewer.ch-ABS[nLines]; dc: Imager.Context _ ViewerOps.AcquireContext[NIL]; [vX, vY] _ ViewerOps.UserToScreenCoords[viewer, 0, IF viewer.class.coordSys=top THEN 0 ELSE viewer.ch]; dY _ IF nLines>0 THEN vY ELSE vY-nLines; sY _ IF nLines>0 THEN vY-nLines ELSE vY; Imager.SetIntCP[dc, vX, dY]; Imager.DrawBitmap[ self: dc, bitmap: Imager.ScreenBitmap[], w: w, h: h, x: vX, y: ViewerSpecs.screenH-sY, xorigin: vX, yorigin: ViewerSpecs.screenH-sY ]; ViewerOps.ReleaseContext[dc]; END; VBLT: PUBLIC PROC [src: VPlace, dest: VPlace, srcw, srch: INTEGER] = BEGIN ERROR; -- not yet implemented END; END.