<> <> <> <> <> DIRECTORY Carets USING [ResetCarets, ResumeCarets, SuspendCarets], Imager USING [black, Color, Context, IntegerExcludeRectangle, IntegerMaskRectangle, MakeStipple, MoveSurfaceRectangle, SetColor, white], Process USING [Detach], ViewerBLT USING [], ViewerClasses USING [BltRule, Column, Viewer, PaintRectangle, PaintRectangleRec, cymax, wxmax, wxmin, wymax, wymin, xmax, xmin, ymax, ymin], ViewerLocks USING [CallUnderWriteLock], ViewerOps USING [AcquireContext, ColumnInfo, EnumerateViewers, EnumProc, EstablishViewerPosition, PaintHint, PaintViewer, ReleaseContext, WaitForPaintingToFinish], ViewerSpecs USING [openBottomY, openLeftLeftX, openLeftWidth, openRightLeftX, windowBorderSize], WindowManagerPrivate USING [rootViewerTree]; ViewerBLTImpl: CEDAR PROGRAM IMPORTS Carets, Imager, Process, ViewerClasses, ViewerLocks, ViewerOps, ViewerSpecs, WindowManagerPrivate EXPORTS ViewerBLT, ViewerOps SHARES ViewerOps, ViewerClasses = BEGIN BltRule: TYPE = ViewerClasses.BltRule; Column: TYPE = ViewerClasses.Column; Viewer: TYPE = ViewerClasses.Viewer; Box: TYPE = RECORD[x, y, w, h: INTEGER]; ColumnSnapshot: TYPE = REF ColumnSequence; ColumnSequence: TYPE = RECORD[SEQUENCE size: NAT OF RECORD[ viewer: Viewer, box: Box, painted: BOOL _ FALSE]]; snapshots: ARRAY Column OF RECORD[before, after: ColumnSnapshot]; ChangeMenuHeight: PUBLIC PROC[viewer: Viewer, delta: INTEGER] = { LockedChangeMenuHeight: PROC = { oldBox, newBox: Box; <> IF viewer.menus = NIL OR delta = 0 THEN RETURN; oldBox _ [viewer.cx, viewer.cy, viewer.cw, viewer.ch]; ViewerOps.EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh]; newBox _ [viewer.cx, viewer.cy, viewer.cw, viewer.ch]; BLTViewer[viewer, oldBox, newBox]; }; ViewerLocks.CallUnderWriteLock[LockedChangeMenuHeight, viewer]; }; ForkPaintViewer: PROC[viewer: Viewer, hint: ViewerOps.PaintHint _ all, clearClient: BOOL _ TRUE, whatChanged: REF ANY _ NIL] = { p: PROCESS = FORK ViewerOps.PaintViewer[viewer, hint, clearClient, whatChanged]; TRUSTED{Process.Detach[p]}; }; ColumnHead: PROC[column: Column] RETURNS[Viewer] = INLINE { RETURN[WindowManagerPrivate.rootViewerTree[column]] }; InternalPaintColumn: PUBLIC PROC[column: Column, paintIcons: BOOL _ TRUE] = { oldIndex: CARDINAL; oldSnapshot: ColumnSnapshot; newSnapshot: ColumnSnapshot; invalidTop, invalidBottom: INTEGER; IF column = static THEN { FOR v: Viewer _ ColumnHead[column], v.sibling UNTIL v=NIL DO IF ~v.offDeskTop THEN ForkPaintViewer[v]; ENDLOOP; RETURN}; IF ColumnHead[column] = NIL THEN { height, leftX, width, bottomY: INTEGER; [totalSpace: height, leftX: leftX, width: width, bottomY: bottomY] _ ViewerOps.ColumnInfo[column]; GreyScreen[leftX, bottomY, width, height, column=color, FALSE]; IF paintIcons AND column # color THEN -- paint newly visible icons FOR v: Viewer _ ColumnHead[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; ForkPaintViewer[v]; 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 _ ColumnHead[column], v.sibling UNTIL v=NIL DO ForkPaintViewer[v]; ENDLOOP; ViewerOps.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 ForkPaintViewer[newSnapshot[i].viewer]; ENDLOOP; Carets.ResetCarets[]; ViewerOps.WaitForPaintingToFinish[]; Carets.ResumeCarets[]; }; MaskBox: PROC[context: Imager.Context, xmin, ymin, xmax, ymax: INTEGER] = INLINE { Imager.IntegerMaskRectangle[context: context, x: xmin, y: ymin, w: xmax-xmin, h: ymax-ymin]; }; BLTViewer: PROC[viewer: Viewer, oldBox, newBox: Box] = { rect: ViewerClasses.PaintRectangle _ NIL; hBLT, vBLT: BltRule; w, h: INTEGER; context: Imager.Context _ NIL; fromX, fromY, toX, toY: INTEGER; wbs: INTEGER _ IF viewer.border THEN ViewerSpecs.windowBorderSize ELSE 0; header: INTEGER = viewer.wymax - viewer.cymax; <<-- 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 => {ForkPaintViewer[viewer]; 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 => {ForkPaintViewer[viewer]; RETURN}; { ENABLE UNWIND => IF context#NIL THEN ViewerOps.ReleaseContext[context]; context _ ViewerOps.AcquireContext[NIL, viewer.column=color]; rect _ NEW[ViewerClasses.PaintRectangleRec _ [flavor: blt, x: toX, y: toY, w: w, h: 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 viewer.wymin < rect.ymin THEN -- some on the bottom MaskBox[context, viewer.wxmin, viewer.wymin, viewer.wxmax, rect.ymin]; IF viewer.wymax > rect.ymax THEN -- some on the top MaskBox[context, viewer.wxmin, rect.ymax, viewer.wxmax, viewer.wymax]; IF viewer.wxmin < rect.xmin THEN -- some on the left MaskBox[context, viewer.wxmin, viewer.wymin, rect.xmin, viewer.wymax]; IF viewer.wxmax > rect.xmax THEN -- some on the right MaskBox[context, rect.xmax, viewer.wymin, viewer.wxmax, viewer.wymax]; Imager.SetColor[context, Imager.black]; IF viewer.border AND (oldBox.w#newBox.w OR oldBox.h#newBox.h) THEN { -- draw border MaskBox[context, viewer.wxmin, viewer.wymin, viewer.wxmax, viewer.wymin+wbs]; MaskBox[context, viewer.wxmin, viewer.wymin, viewer.wxmin+wbs, viewer.wymax]; MaskBox[context, viewer.wxmax-wbs, viewer.wymin, viewer.wxmax, viewer.wymax]; MaskBox[context, viewer.wxmin, viewer.wymax-wbs, viewer.wxmax, viewer.wymax]; }; ViewerOps.ReleaseContext[context]; }; IF viewer.wxmin < rect.xmin OR viewer.wxmax > rect.xmax OR (viewer.wymax - header) < rect.y OR (viewer.wymax > rect.ymax) THEN { -- repaint header ViewerOps.PaintViewer[viewer, caption, FALSE]; ViewerOps.PaintViewer[viewer, menu, FALSE]; }; IF oldBox.w # newBox.w OR oldBox.h # newBox.h THEN -- shape has changed ForkPaintViewer[viewer, client, FALSE, rect]; <> <> <> }; 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 _ ColumnHead[column], v.sibling UNTIL v = NIL DO index: CARDINAL = length; length _ length+1; 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[index] _ [v, [v.wx, v.wy, v.ww, v.wh]]; ENDLOOP; }; ClearSnapshot: PROC[snapshot: ColumnSnapshot] = INLINE { 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]; }; screenGrey: Imager.Color = Imager.MakeStipple[104042B]; GreyScreen: PUBLIC PROC [x,y,w,h: INTEGER, color, icon: BOOL, stipple: Imager.Color _ NIL] = { context: Imager.Context _ ViewerOps.AcquireContext[NIL, color]; IF icon AND y+h > ViewerSpecs.openBottomY THEN ClipIcon[context]; Imager.SetColor[context, IF stipple=NIL THEN screenGrey ELSE stipple]; Imager.IntegerMaskRectangle[context, x, y, w, h]; ViewerOps.ReleaseContext[context]; IF icon THEN RETURN; IF color THEN ClearSnapshot[snapshots[color].before] ELSE { OPEN ViewerSpecs; IF x + w > openRightLeftX THEN ClearSnapshot[snapshots[right].before]; IF x < openLeftLeftX + openLeftWidth THEN ClearSnapshot[snapshots[left].before] }; }; ClipIcon: PROC [context: Imager.Context] = { <> ClipOpenViewer: ViewerOps.EnumProc = { IF ~v.iconic AND (v.column=right OR v.column=left) THEN Imager.IntegerExcludeRectangle[context, v.wx, v.wy, v.ww, v.wh]; }; ViewerOps.EnumerateViewers[ClipOpenViewer]; }; END. <> <> <> <> <> <> <> <> <> <<};>> <> <<};>> <<>> <> <> <> <> <> <<[vX, vY] _ ViewerOps.UserToScreenCoords[viewer, 0,>> <> <0 THEN vY ELSE vY-nLines;>> <0 THEN vY-nLines ELSE vY;>> <> <> <> <> <> <> <> <> <> <> <<];>> <> <<};>> <<>> <> <> <<};>>