ViewerBLTImpl.mesa; written by S. McGregor
Last Edited by McGregor, August 4, 1983 11:23 am
Last Edited by: Maxwell, May 24, 1983 11:53 am
Michael Plass, August 15, 1983 10:27 am
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: BOOLTRUE] =
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;
reset caret below relies on special deal with CaretsImpl
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: INTEGERIF 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: BOOLFALSE]];
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 CARDINALNIL] = 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
clips for overlapping top level windows given valid Context and Viewer
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.