PreViewImpl.mesa
Last edited by Ken Pier; May 15, 1986 9:55:26 am PDT
DIRECTORY
AIS USING [CloseFile],
Atom USING [GetPName],
BasicTime USING [GMT],
BiScrollers USING [BiScroller, ClientCoords, ClientDataOfViewer, QuaBiScroller],
Convert USING [RopeFromReal],
FS USING [Error],
FunctionCache USING [Cache, CompareProc, GlobalCache, Insert, Lookup],
Geom2D USING [ExtremaOfRect, id, Transform, Rect, Vec],
GriffinImageUtils USING [GriffinToImagerCalls],
Imager USING [black, ClipRectangle, Color, ConcatT, Context, Error, MaskBox, MaskRectangle, MaskVector, Scale2T, ScaleT, SetColor, SetSampledColor, SetStrokeJoint, SetStrokeWidth, TranslateT, DoSaveAll],
ImagerArtwork USING [PasteArtwork, Points],
ImagerBackdoor USING [DrawBits, MakeStipple],
ImagerInterpress USING [Close, Create, DoPage, Ref],
ImagerMemory USING [GetContextSize, NewMemoryContext, Replay],
ImagerPixelMap USING [DeviceRectangle, PixelMap, Window],
ImagerTransformation USING [PostTranslate, Rectangle, Scale, Transformation, TransformRectangle],
InputFocus USING [GetInputFocus, SetInputFocus],
Interpress USING [DoPage],
MessageWindow USING [Append, Blink],
PDFileReader USING [Close, Handle],
PDImageReader USING [Handle, InterpretImage, ResetToImage],
PreView USING [AISData, AISState, BBoxOp, bsStyle, Data, GData, IPData, PaintOp, PDData, PressData, PVFeedback, PVListRemove, ResetProcess],
PrincOpsUtils USING [],
Process USING [GetCurrent],
Real USING [LargestNumber],
Rope USING [Concat, ROPE],
SafeStorage USING [ReclaimCollectibleObjects],
ShowPress USING [Close, DrawPressPage, Handle, ShowPressError],
ViewerClasses USING [DestroyProc, GetProc, NotifyProc, PaintProc, Viewer],
ViewerGroupLocks USING [CallRootUnderWriteLock],
ViewerLocks USING [Wedged],
ViewerPrivate USING [PaintClient];
PreViewImpl:
CEDAR
MONITOR
IMPORTS AIS, Atom, BiScrollers, Convert, FS, FunctionCache, Geom2D, GriffinImageUtils, Imager, ImagerArtwork, ImagerBackdoor, ImagerInterpress, ImagerMemory, ImagerPixelMap, ImagerTransformation, Interpress, InputFocus, MessageWindow, PDFileReader, PDImageReader, PreView, Process, Rope, SafeStorage, ShowPress, ViewerGroupLocks, ViewerLocks, ViewerPrivate
PProc: TYPE = PROC [Imager.Context];
Data: TYPE = PreView.Data;
PressData: TYPE = PreView.PressData;
PDData: TYPE = PreView.PDData;
IPData: TYPE = PreView.IPData;
AISData: TYPE = PreView.AISData;
GData: TYPE = PreView.GData;
CacheKey: TYPE = REF CacheKeyRep;
CacheKeyRep:
TYPE =
RECORD [
fileName: Rope.ROPE,
created: BasicTime.GMT,
pageNumber: INTEGER
];
globalCache: FunctionCache.Cache ← FunctionCache.GlobalCache[];
screenResolution: REAL = 72.0; -- points per inch
pointsPerMica: REAL = 72.0/2540.0;
micasPerPoint: REAL = 2540.0/72.0;
pointsPerMeter: REAL = 72.0/.0254;
metersPerPoint: REAL = .0254/72.0;
grey: CARDINAL ← 122645B;
xorStipple: Imager.Color = ImagerBackdoor.MakeStipple[stipple: grey, xor: TRUE];
borderWidth: REAL ← 2.0;
fudge: REAL ← 2.0;
messageWindowFeedback: BOOLEAN ← FALSE;
Message:
PROC [x, y:
REAL] = {
MessageWindow.Append[Convert.RopeFromReal[x], TRUE];
MessageWindow.Append[", ", FALSE];
MessageWindow.Append[Convert.RopeFromReal[y], FALSE];
};
PVNotify:
PUBLIC ViewerClasses.NotifyProc = {
-- [self: Viewer, input: LIST OF REF ANY]
ENABLE UNWIND => NULL;
mouseX, mouseY: REAL ← 0.0;
data: Data ← NARROW[BiScrollers.ClientDataOfViewer[self]];
FOR list:
LIST
OF
REF
ANY ← input, list.rest
UNTIL list =
NIL
DO
WITH list.first
SELECT
FROM
x:
ATOM =>
SELECT x
FROM
$Abort => {
IF data.bBox.active THEN PVFeedback[data: data, v: self, op: remove]; -- remove grey bbox.
data.bBox^ ← []; -- initialization default values cancel current clipping rectangle
};
$Move => {
IF InputFocus.GetInputFocus[].owner#self THEN InputFocus.SetInputFocus[self];
IF data.bBox.mode = waitingForFirstPoint
THEN {
data.bBox.mode ← waitingForSecondPoint;
data.bBox.x0 ← mouseX;
data.bBox.y0 ← mouseY;
data.bBox.active ← TRUE;
};
data.bBox.rect ← [x: MIN[mouseX, data.bBox.x0], y: MIN[mouseY, data.bBox.y0], w: ABS[mouseX-data.bBox.x0], h: ABS[mouseY-data.bBox.y0]];
PVFeedback[data: data, v: self, op: change, x: mouseX, y: mouseY]; -- repaint grey bbox.
};
ENDCASE => NULL;
z: BiScrollers.ClientCoords => {
--TYPE = REF Vec
mouseX ← z.x; -- mouseX in BiScroller coords
mouseY ← z.y; -- mouseY in BiScroller coords
IF messageWindowFeedback THEN Message[mouseX, mouseY];
};
ENDCASE => ERROR;
ENDLOOP;
};
PVFeedback:
PUBLIC
PROC [data: Data, v: ViewerClasses.Viewer, x, y:
REAL ← 0.0, op: PreView.PaintOp ← paint] = {
Action:
PROC [context: Imager.Context] = {
-- this context in viewer coords
Show:
PROC [x1, y1, x2, y2:
REAL] = {
rect: ImagerTransformation.Rectangle;
IF x1 > x2 THEN {t: REAL ← x2; x2 ← x1; x1 ← t};
IF y1 > y2 THEN {t: REAL ← y2; y2 ← y1; y1 ← t};
rect ← ImagerTransformation.TransformRectangle[m: ctv, r: [x: x1, y: y1, w: x2-x1, h: y2-y1]];
x1 ← rect.x; y1 ← rect.y; x2 ← rect.x+rect.w; y2 ← rect.y+rect.h;
Imager.MaskRectangle[context, [x1-5.0, y1-5.0, 5.0, y2-y1+10.0]]; -- left side
Imager.MaskRectangle[context, [x2, y1-5.0, 5.0, y2-y1+10.0]]; -- right side
Imager.MaskRectangle[context, [x1, y1-5.0, x2-x1, 5.0]]; -- top side
Imager.MaskRectangle[context, [x1, y2, x2-x1, 5.0]]; -- bottom side
};
IF data.bBox.lastX=x AND data.bBox.lastY=y AND op#paint THEN RETURN; -- no change
Imager.SetColor[context, xorStipple];
SELECT op
FROM
paint => {
Show[data.bBox.rect.x, data.bBox.rect.y, data.bBox.rect.x+data.bBox.rect.w, data.bBox.rect.y+data.bBox.rect.h];
};
remove => {
Show[data.bBox.lastX, data.bBox.lastY, data.bBox.x0, data.bBox.y0]; --remove the old box
data.bBox.lastX ← data.bBox.lastY ← Real.LargestNumber;
};
change => {
IF data.bBox.lastX#Real.LargestNumber THEN Show[data.bBox.lastX, data.bBox.lastY, data.bBox.x0, data.bBox.y0]; --remove the old box
Show[x, y, data.bBox.x0, data.bBox.y0]; -- to show the new box
data.bBox.lastX ← x; data.bBox.lastY ← y;
};
ENDCASE => ERROR;
};
ctv: ImagerTransformation.Transformation ← PreView.bsStyle.GetTransforms[BiScrollers.QuaBiScroller[v]].clientToViewer;
DirectPaint[viewer: v, action: Action];
};
DoFileOps:
PUBLIC
PROC [op: PreView.BBoxOp, viewer: ViewerClasses.Viewer, data: Data, fileName: Rope.
ROPE, clip:
BOOL ←
TRUE] = {
ctv: ImagerTransformation.Transformation ← PreView.bsStyle.GetTransforms[BiScrollers.QuaBiScroller[viewer]].clientToViewer;
rectV: ImagerTransformation.Rectangle ← ImagerTransformation.TransformRectangle[m: ctv, r: [x: data.bBox.rect.x, y: data.bBox.rect.y, w: data.bBox.rect.w, h: data.bBox.rect.h]];
SELECT op
FROM
stuff => {
-- stuff requires clip to be TRUE and bBox valid
DoStuff:
PROC [context: Imager.Context] = {
DoIt:
PROC = {
context.ConcatT[ctv];
DoPainting[context: context, data: data, viewer: viewer];
};
context.TranslateT[t: [-rectV.x, -rectV.y]];
Imager.DoSaveAll[context, DoIt];
IF data.stuffWithBorders
THEN {
context.SetStrokeWidth[borderWidth];
context.SetColor[Imager.black];
context.MaskVector [ [rectV.x, rectV.y], [rectV.x, rectV.y+rectV.h] ]; -- left side
context.MaskVector [ [rectV.x, rectV.y], [rectV.x+rectV.w, rectV.y] ]; -- bottom
context.MaskVector [ [rectV.x+rectV.w, rectV.y], [rectV.x+rectV.w, rectV.y+rectV.h] ]; -- right side
context.MaskVector [ [rectV.x, rectV.y+rectV.h], [rectV.x+rectV.w, rectV.y+rectV.h] ]; -- top
};
};
ImagerArtwork.PasteArtwork[action: DoStuff, bounds: [0, 0, rectV.w, rectV.h], m: ImagerArtwork.Points[], clip: TRUE];
};
ipMaster => {
DoIPMaster:
PROC [context: Imager.Context] = {
context.ScaleT[metersPerPoint];
IF clip
THEN {
context.TranslateT[t: [-rectV.x, -rectV.y]];
context.ClipRectangle[rectV];
};
context.ConcatT[ctv];
DoPainting[context: context, data: data, viewer: viewer];
};
r: ImagerInterpress.Ref ← ImagerInterpress.Create[fileName: fileName !
FS.Error => {
MessageWindow.Append[message: error.explanation, clearFirst: TRUE];
GOTO Quit;
};];
ImagerInterpress.DoPage[self: r, action: DoIPMaster ! Imager.Error => {
MessageWindow.Append[message: Rope.Concat[error.explanation, " creating IPMaster"], clearFirst: TRUE]; CONTINUE;};];
ImagerInterpress.Close[r];
};
ENDCASE;
data.bBox^ ← []; -- causes initialization of data.bBox values to inactive state
};
DirectPaint:
PROC [viewer: ViewerClasses.Viewer, action: PProc] = {
CallLocked: PROC = { ViewerPrivate.PaintClient[viewer, action ! ABORTED => CONTINUE] };
IF viewer = NIL OR viewer.destroyed OR viewer.paintingWedged THEN RETURN;
ViewerGroupLocks.CallRootUnderWriteLock[CallLocked, viewer
! ViewerLocks.Wedged => {viewer.paintingWedged ← TRUE; CONTINUE}];
};
DoPainting:
PROC [context: Imager.Context, data: Data, viewer: ViewerClasses.Viewer] = {
data.abort ← FALSE; -- need this to clean up any PD abort bits that were set before opening the viewer
WITH data
SELECT
FROM
ipData: IPData => PaintIP[context: context, ipData: ipData]; --clobbers context variables, but that is OK
pressData: PressData => PaintPress[context: context, pressData: pressData];
pdData: PDData => PaintPD[context: context, pdData: pdData, viewer: viewer];
aisData: AISData => PaintAIS[context: context, aisData: aisData];
gData: GData => PaintGriffin[context: context, gData: gData];
ENDCASE => ERROR;
data.abort ← FALSE; --just in case you returned here due to data.abort set while PD painting
};
PaintIP:
PROC [context: Imager.Context, ipData: IPData] = {
SELECT
TRUE
FROM
ipData.switches['M] => {
-- M switch means use no ImagerMemory
context.SetColor[Imager.black];
context.ScaleT[pointsPerMeter]; -- establish IP coord system
Interpress.DoPage[master: ipData.ipMaster, page: ipData.pageNumber, context: context];
};
ipData.pageInMem=ipData.pageNumber => {
-- iMemContext is current
ImagerMemory.Replay[c: ipData.iMemContext, into: context];
};
ipData.pageInMem#ipData.pageNumber => {
-- memory context may be cached
PageCompareProc: FunctionCache.CompareProc = {
PROC [argument: Domain] RETURNS [good: BOOL];
RETURN[NARROW[argument, CacheKey]^=cacheKey^];
};
cacheKey: CacheKey ← NEW[CacheKeyRep ← [ipData.fileInfo.fullFName, ipData.fileInfo.created, ipData.pageNumber]];
IF (ipData.iMemContext ←
NARROW[FunctionCache.Lookup[x: globalCache, compare: PageCompareProc, clientID: $PVPage].value, Imager.Context])#
NIL
THEN {
--hit
ImagerMemory.Replay[c: ipData.iMemContext, into: context];
ipData.pageInMem ← ipData.pageNumber;
}
ELSE {
-- miss
ipData.iMemContext ← ImagerMemory.NewMemoryContext[];
ipData.iMemContext.SetColor[Imager.black];
ipData.iMemContext.ScaleT[pointsPerMeter]; -- establish IP coord system
Interpress.DoPage[master: ipData.ipMaster, page: ipData.pageNumber, context: ipData.iMemContext];
FunctionCache.Insert[x: globalCache, argument: cacheKey, value: ipData.iMemContext, size: ImagerMemory.GetContextSize[ipData.iMemContext], clientID: $PVPage];
ipData.pageInMem ← ipData.pageNumber;
ImagerMemory.Replay[c: ipData.iMemContext, into: context];
};
};
ENDCASE => NULL;
};
PaintPress:
PROC [context: Imager.Context, pressData: PressData] = {
context.SetColor[Imager.black];
context.ScaleT[pointsPerMica]; -- establish Press coord system
ShowPress.DrawPressPage[context: context, show: pressData.presshandle, pageNumber: pressData.pageNumber
! ShowPress.ShowPressError => {
SELECT code
FROM
$NoSuchPage => {IF pressData.pageNumber > 0 THEN {pressData.pageNumber ← pressData.pageNumber-1; RETRY};};
ENDCASE => {
MessageWindow.Append[Atom.GetPName[code], TRUE];
MessageWindow.Blink[];
CONTINUE;
};
}
];
};
PaintPD:
PROC [context: Imager.Context, pdData: PDData, viewer: ViewerClasses.Viewer] = {
PaintIt:
PROC [self: ViewerClasses.Viewer, whatChanged:
REF
ANY] = {
remember that this routine may be called many times and should not do destructive operations on the context state
pdData: PDData ← NARROW[BiScrollers.ClientDataOfViewer[self]];
WITH whatChanged
SELECT
FROM
handle: PDImageReader.Handle => {
t: ImagerPixelMap.PixelMap = handle.pixelMap;
w: ImagerPixelMap.DeviceRectangle = ImagerPixelMap.Window[t];
IF w.sSize > 0
THEN {
context.SetColor[Imager.black];
ImagerBackdoor.DrawBits[context: context, base: handle.pixelMap.refRep.pointer, wordsPerLine: handle.pixelMap.refRep.rast, sMin: t.sMin, fMin: t.fMin, fSize: t.fSize, sSize: t.sSize, tx: w.fMin, ty: pdData.pdhandle.herald.imageSSize-w.sMin+w.sSize];
};
};
ENDCASE => ERROR;
};
IF pdData.scalePD THEN context.Scale2T[pdData.scaleFactors]; -- do this only once (outside of paintAction)
Reset the handle in pdData to the image in pdData.pageNumber, then interpret the image
IF PDImageReader.ResetToImage[pdData: pdData]#NIL THEN IF NOT pdData.abort THEN [] ← PDImageReader.InterpretImage[handle: pdData.pdhandle, viewer: viewer, paintAction: PaintIt]; --this guy eventually calls PaintIt.
};
PaintAIS:
PROC [context: Imager.Context, aisData: AISData] = {
state: PreView.AISState ← aisData.state;
IF state.active
THEN {
Imager.SetSampledColor[context: context, m: ImagerTransformation.Scale[1.0], pa: state.pa, colorOperator: state.op];
Imager.MaskBox[context, [0,0, state.pixels, state.scans]];
};
};
PaintGriffin:
PROC [context: Imager.Context, gData: GData] = {
SELECT
TRUE
FROM
gData.switches['M] => {
-- M switch means use no ImagerMemory
context.ScaleT[pointsPerMica]; -- establish Griffin coord. system
context.SetStrokeJoint[round]; -- avoid miter problems
GriffinImageUtils.GriffinToImagerCalls[context, gData.image];
};
gData.pageInMem=gData.pageNumber => {
-- iMemContext is current
ImagerMemory.Replay[c: gData.iMemContext, into: context];
};
gData.pageInMem#gData.pageNumber => {
-- memory context may be cached
PageCompareProc: FunctionCache.CompareProc = {
PROC [argument: Domain] RETURNS [good: BOOL];
RETURN[NARROW[argument, CacheKey]^=cacheKey^];
};
cacheKey: CacheKey ← NEW[CacheKeyRep ← [gData.fileInfo.fullFName, gData.fileInfo.created, gData.pageNumber]];
IF (gData.iMemContext ←
NARROW[FunctionCache.Lookup[x: globalCache, compare: PageCompareProc, clientID: $PVPage].value, Imager.Context])#
NIL
THEN {
--hit
ImagerMemory.Replay[c: gData.iMemContext, into: context];
gData.pageInMem ← gData.pageNumber;
}
ELSE {
-- miss
gData.iMemContext ← ImagerMemory.NewMemoryContext[];
gData.iMemContext.ScaleT[pointsPerMica]; -- establish Griffin coord. system
gData.iMemContext.SetStrokeJoint[round]; -- avoid miter problems
GriffinImageUtils.GriffinToImagerCalls[gData.iMemContext, gData.image];
FunctionCache.Insert[x: globalCache, argument: cacheKey, value: gData.iMemContext, size: ImagerMemory.GetContextSize[gData.iMemContext], clientID: $PVPage];
gData.pageInMem ← gData.pageNumber;
ImagerMemory.Replay[c: gData.iMemContext, into: context];
};
};
ENDCASE => NULL;
};
PVPaint:
PUBLIC
ENTRY ViewerClasses.PaintProc =
TRUSTED {
[self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL];
ENABLE UNWIND => NULL;
data: Data ← NARROW[BiScrollers.ClientDataOfViewer[self]];
data.process ← Process.GetCurrent[];
DoPainting[context: context, data: data, viewer: self ! UNWIND => PreView.ResetProcess[data]];
IF data.bBox.active THEN PreView.PVFeedback[data: data, v: self, op: paint ! UNWIND => PreView.ResetProcess[data]];
PreView.ResetProcess[data]; -- this call is to a PreViewTool ENTRY PROC so it is synchronized with PreViewTool.StopIfYouCan
};
PVDestroy:
PUBLIC
ENTRY ViewerClasses.DestroyProc = {
[self: Viewer]
ENABLE UNWIND => NULL;
data: Data ← NARROW[BiScrollers.ClientDataOfViewer[self]];
PreView.PVListRemove[ref: data];
WITH data
SELECT
FROM
ipData: IPData => {
ipData.ipMaster ← NIL; -- Interpress.Close doesn't exist
};
pressData: PressData => {
ShowPress.Close[pressData.presshandle];
pressData.presshandle ← NIL;
};
pdData: PDData => {
PDFileReader.Close[pdData.pdhandle];
pdData.pdhandle ← NIL;
pdData.image ← NIL;
};
aisData: AISData => {
AIS.CloseFile[aisData.state.openFile];
aisData.state.openFile ← NIL;
aisData.state.pa ← NIL;
aisData.state.op ← NIL;
};
gData: GData => {
gData.image ← NIL;
};
ENDCASE => ERROR; -- can't happen
SafeStorage.ReclaimCollectibleObjects[suspendMe: FALSE]; -- good citizenship
};
This next PROC is for the benefit of TSetter, which will call it when the InputFocus is in the previewer and the TSetter GET button is clicked
PVGetName:
PUBLIC ViewerClasses.GetProc = {
--[self: Viewer, op: ATOM] RETURNS [data: REF ANY]
d: Data ← NARROW[BiScrollers.ClientDataOfViewer[self]];
RETURN[d.container.name];
};
PVBasicTransformProc:
PUBLIC
PROC [bs: BiScrollers.BiScroller]
RETURNS [t: Geom2D.Transform] = {
BiScrollers.TransformGenerator
iv: ViewerClasses.Viewer ← bs.style.QuaViewer[bs: bs, inner: TRUE];
data: Data ← NARROW[BiScrollers.ClientDataOfViewer[iv]];
t ← Geom2D.id.PostTranslate[[0, iv.ch - MAX[data.pageHeight, data.pageWidth]]];
t ← Geom2D.id.PostTranslate[[0, iv.ch - data.pageHeight]];
Make client <0, max> appear at the upper left corner of viewer.
};
PVExtremaProc:
PUBLIC
PROC [clientData:
REF
ANY, direction: Geom2D.Vec]
RETURNS [min, max: Geom2D.Vec]
--BiScrollers.ExtremaProc-- = {
This proc is required by BiScrollers to return the extremes of the displayed data
data: Data ← NARROW[clientData];
area: Geom2D.Rect ← [x: 0.0, y: 0.0, w: data.pageWidth, h: data.pageHeight];
[min, max] ← Geom2D.ExtremaOfRect[r: area, n: direction];
};
END.