PPreViewImpl.mesa
Copyright Ó 1989, 1992 by Xerox Corporation. All rights reserved.
Pier, January 4, 1991 4:28 pm PST
Carl Hauser, August 18, 1989 4:35:28 pm PDT
Michael Plass, March 25, 1992 12:20 pm PST
DIRECTORY
BasicTime, BiScrollers, FunctionCache, FS, Geom2D, Imager, ImagerArtwork, ImagerBackdoor, ImagerColor, ImagerInterpress, ImagerMemory, ImagerPixel, ImagerPixelArray, ImagerTransformation, InputFocus, InterpressInterpreter, IO, MessageWindow, PPreView, PPreViewClient, RasterEncodingStandardIO, Real, Rope, SF, Vector2, ViewerAbort, ViewerClasses, ViewerGroupLocks, ViewerLocks, ViewerPrivate, ViewerTools;
PPreViewImpl: CEDAR MONITOR
IMPORTS BiScrollers, FunctionCache, FS, Geom2D, Imager, ImagerArtwork, ImagerBackdoor, ImagerColor, ImagerInterpress, ImagerMemory, ImagerPixel, ImagerPixelArray, ImagerTransformation, InputFocus, InterpressInterpreter, IO, MessageWindow, PPreView, PPreViewClient, Real, Rope, SF, Vector2, ViewerAbort, ViewerGroupLocks, ViewerLocks, ViewerPrivate, ViewerTools
EXPORTS PPreView ~ BEGIN
Data: TYPE ~ PPreView.Data;
IPData: TYPE ~ PPreView.IPData;
AISData: TYPE ~ PPreView.AISData;
RESData: TYPE ~ PPreView.RESData;
PaintOp: TYPE ~ PPreView.PaintOp;
Context: TYPE ~ Imager.Context;
Rectangle: TYPE ~ Imager.Rectangle;
Transformation: TYPE ~ ImagerTransformation.Transformation;
Viewer: TYPE ~ ViewerClasses.Viewer;
Vec: TYPE ~ Geom2D.Vec;
PProc: TYPE ~ PROC [Context];
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
pointsPerInch: REAL ~ 72.0;
oneOverPointsPerInch: REAL ~ 1.0/72.0;
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;
useMessageWindow: BOOL ¬ FALSE;
Message: PROC [message: Rope.ROPE, clearFirst, blink: BOOL ¬ TRUE] ~ {
MessageWindow.Append[Rope.Concat["PreView: ", message], clearFirst];
IF blink THEN MessageWindow.Blink[];
};
RectangleFromXYs: PUBLIC PROC [x0, y0, x1, y1: REAL] RETURNS [r: Imager.Rectangle] ~ {
IF x0 > x1 THEN {t: REAL ¬ x0; x0 ¬ x1; x1 ¬ t};
IF y0 > y1 THEN {t: REAL ¬ y0; y0 ¬ y1; y1 ¬ t};
r ¬ [x0, y0, x1-x0, y1-y0];
};
PVNotify: PUBLIC ViewerClasses.NotifyProc ~ {-- [self: Viewer, input: LIST OF REF ANY]
ENABLE UNWIND => NULL;
NewMouse: PROC ~ {
IF InputFocus.GetInputFocus[].owner # self THEN InputFocus.SetInputFocus[self];
IF b.all
THEN {
dif: Imager.VEC ¬ Vector2.Sub[[mouseX, mouseY], b.origin];
b.p0 ¬ Vector2.Add[b.p0, dif];
b.p1 ¬ Vector2.Add[b.p1, dif];
b.origin ¬ [mouseX, mouseY];
}
ELSE {
IF b.x0 THEN b.p0.x ¬ mouseX ELSE IF b.x1 THEN b.p1.x ¬ mouseX;
IF b.y0 THEN b.p0.y ¬ mouseY ELSE IF b.y1 THEN b.p1.y ¬ mouseY;
};
b.rect ¬ RectangleFromXYs[b.p0.x, b.p0.y, b.p1.x, b.p1.y];
PVFeedback[data: data, v: self, op: change]; --paint grey box
};
mouseX, mouseY: REAL ¬ 0.0;
data: Data ¬ NARROW[BiScrollers.ClientDataOfViewer[self]];
b: PPreView.BBoxState ¬ data.bBox;
IF data.painting THEN RETURN; -- ignore input while painting
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 b.active THEN PVFeedback[data: data, v: self, op: remove]; -- kill grey bbox
b­ ¬ []; -- initialization default values cancel current clipping rectangle
};
$Down => {  -- determine user interaction (corner, edge, or center?)
once: BOOL ¬ FALSE;
wOver4, hOver4: REAL;
b.x0 ¬ b.x1 ¬ b.y0 ¬ b.y1 ¬ b.all ¬ FALSE;
IF NOT b.active
THEN {
b.x1 ¬ b.y1 ¬ TRUE;
b.p0 ¬ b.p1 ¬ [mouseX, mouseY];
b.active ¬ TRUE;     -- turn it on and leave it on
RETURN;
};
IF b.p0.x > b.p1.x THEN {t: REAL ¬ b.p0.x; b.p0.x ¬ b.p1.x; b.p1.x ¬ t};
IF b.p0.y > b.p1.y THEN {t: REAL ¬ b.p0.y; b.p0.y ¬ b.p1.y; b.p1.y ¬ t};
wOver4 ¬ (b.p1.x-b.p0.x)/4;
hOver4 ¬ (b.p1.y-b.p0.y)/4;
SELECT mouseX FROM
< b.p0.x+wOver4 => b.x0 ¬ TRUE;
> b.p1.x-wOver4 => b.x1 ¬ TRUE;
ENDCASE => once ¬ TRUE;
SELECT mouseY FROM
< b.p0.y+hOver4 => b.y0 ¬ TRUE;
> b.p1.y-hOver4 => b.y1 ¬ TRUE;
ENDCASE => IF once THEN {
b.all ¬ TRUE;
b.origin ¬ [mouseX, mouseY];
};
NewMouse[];
};
$Move => NewMouse[];
$EndMove, $ShowSelection => PPreViewClient.ShowSelection[data, self];
ENDCASE => NULL;
z: BiScrollers.ClientCoords => { -- TYPE = REF Vec
mouseX ¬ z.x; -- mouseX in BiScroller coords
mouseY ¬ z.y; -- mouseY in BiScroller coords
IF useMessageWindow
THEN Message[IO.PutFR["%g, %g", IO.real[z.x], IO.real[z.y]], TRUE, FALSE];
};
ENDCASE => Message["unknown input Notify operation"];
ENDLOOP;
};
PVFeedback: PUBLIC PROC [data: Data, v: Viewer, op: PPreView.PaintOp ¬ paint] ~ {
ENABLE UNWIND => data.bBox­ ¬ []; -- kill selection in desperation
Action: PROC [context: Context] ~ { -- this context in viewer coords
Show: PROC [r: Rectangle, updateDimensions: BOOL ¬ FALSE] ~ {
ctv: Transformation ¬ PPreViewClient.GetTransformation[v];
r ¬ ImagerTransformation.TransformRectangle[m: ctv, r: r];
Imager.MaskRectangle[context, [r.x-5.0, r.y-5.0, 5.0, r.h+10.0]]; -- left side
Imager.MaskRectangle[context, [r.x+r.w, r.y-5.0, 5.0, r.h+10.0]]; -- right side
Imager.MaskRectangle[context, [r.x, r.y-5.0, r.w, 5.0]];    -- top side
Imager.MaskRectangle[context, [r.x, r.y+r.h, r.w, 5.0]];    -- bottom side
IF updateDimensions THEN {
ViewerTools.SetContents[data.selectionWViewer, IO.PutFR1["%6.3f",
IO.real[oneOverPointsPerInch*r.w]]];
ViewerTools.SetContents[data.selectionHViewer, IO.PutFR1["%6.3f",
IO.real[oneOverPointsPerInch*r.h]]];
};
};
IF data.bBox.prev = data.bBox.rect AND op = change THEN RETURN; -- no change
Imager.SetColor[context, xorStipple];
SELECT op FROM
paint => Show[data.bBox.rect];
remove => {
IF data.abort
THEN data.abort ¬ FALSE
ELSE IF data.bBox.prev # [0., 0., 0., 0.] THEN Show[data.bBox.prev]; -- remove old box
data.bBox.prev ¬ [0.0, 0.0, 0.0, 0.0];
};
change => {
IF data.abort
THEN data.abort ¬ FALSE
ELSE IF data.bBox.prev # [0., 0., 0., 0.] THEN Show[data.bBox.prev]; -- remove old box
Show[data.bBox.rect, TRUE];  -- show new box
};
ENDCASE => Message["unknown feedback operation"];
data.bBox.prev ¬ data.bBox.rect;
};
DirectPaint[viewer: v, action: Action];
};
DoFileOps: PUBLIC PROC [
op: PPreView.BBoxOp, viewer: Viewer, data: Data, fileName: Rope.ROPE, clip: BOOL ¬ TRUE]
~ {
ctv: Transformation ¬ PPreViewClient.GetTransformation[viewer];
r: Rectangle ¬ ImagerTransformation.TransformRectangle[m: ctv, r: data.bBox.rect];
clipRect: REF Rectangle ¬ IF NOT clip THEN NIL
ELSE NEW[Rectangle ¬ ImagerTransformation.InverseTransformRectangle[ctv, r]];
SELECT op FROM
stuff => { -- stuff requires clip to be TRUE and bBox valid
DoStuff: PROC [context: Context] ~ {
DoIt: PROC ~ {
Imager.ConcatT[context, ctv];
DoPainting[context: context, data: data, viewer: viewer, clipRect: clipRect];
};
Imager.TranslateT[context, [-r.x, -r.y]];
Imager.DoSaveAll[context, DoIt];
IF data.stuffWithBorders THEN {
Imager.SetStrokeWidth[context, borderWidth];
Imager.SetGray[context, 1];
Imager.MaskVector[context, [r.x, r.y], [r.x, r.y+r.h]];    -- left side
Imager.MaskVector[context, [r.x, r.y], [r.x+r.w, r.y]];    -- bottom side
Imager.MaskVector[context, [r.x+r.w, r.y], [r.x+r.w, r.y+r.h]]; -- right side
Imager.MaskVector[context, [r.x, r.y+r.h], [r.x+r.w, r.y+r.h]];  -- top side
};
};
m: Transformation ¬ ImagerArtwork.Points[];
ImagerArtwork.PasteArtwork[DoStuff, [0, 0, r.w, r.h], m, TRUE, data.stuffWithFit];
};
ipMaster => {
DoIPMaster: PROC [context: Context] ~ {
Imager.ScaleT[context, metersPerPoint];
IF clip THEN {
Imager.TranslateT[context, [-r.x, -r.y]];
Imager.ClipRectangle[context, r];
};
Imager.ConcatT[context, ctv];
DoPainting[context: context, data: data, viewer: viewer, clipRect: clipRect];
};
ref: ImagerInterpress.Ref ¬ ImagerInterpress.Create[fileName: fileName ! FS.Error => {
Message[Rope.Concat[error.explanation, " creating IPMaster"]];
GOTO Quit;
}];
ImagerInterpress.DoPage[self: ref, action: DoIPMaster ! Imager.Error => {
Message[Rope.Concat[error.explanation, " creating IPMaster"]];
ImagerInterpress.Close[ref];
GOTO Quit;
}];
ImagerInterpress.Close[ref];
EXITS
Quit => data.abort ¬ TRUE;
};
ENDCASE;
};
DirectPaint: PROC [viewer: 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: Context, data: Data, viewer: Viewer, clipRect: REF Rectangle ¬ NIL]
~ {
IF data.clientPaint # NIL
THEN [] ¬ data.clientPaint[viewer, context, data.clientData, TRUE]
ELSE WITH data SELECT FROM
ipData: IPData => PaintIP[context: context, ipData: ipData]; --kills context variables, but ok
resData: RESData => PaintRES[context: context, resData: resData, clipRect: clipRect];
aisData: AISData => PaintAIS[context: context, aisData: aisData, clipRect: clipRect];
pressData: PressData => PaintPress[context: context, pressData: pressData];
pdData: PDData => PaintPD[context: context, pdData: pdData, viewer: viewer];
gData: GData => PaintGriffin[context: context, gData: gData];
pvData: PVData => PaintGG[context: context, pvData: pvData];
ENDCASE => Message["unknown paint operation"];
};
PaintIP: PROC [context: Context, ipData: IPData] ~ {
ENABLE {
Imager.Error => {
Message[Rope.Concat[error.explanation, ". Use -M switch to preview this file."]];
GOTO Null; -- return cleanly from painting
};
Imager.Warning => RESUME;
};
IF ipData.ipMaster # NIL THEN SELECT TRUE FROM
ipData.switches['M] => { -- M switch means use no ImagerMemory
selected: BOOL ¬ FALSE;
Imager.SetGray[context, 1];
Imager.ScaleT[context, pointsPerMeter]; -- establish IP coord system
InterpressInterpreter.DoPage[ipData.ipMaster, ipData.pageNumber, context, PPreView.IPLogError];
};
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[];
Imager.SetGray[ipData.iMemContext, 1];
Imager.ScaleT[ipData.iMemContext, pointsPerMeter]; -- establish IP coord system
InterpressInterpreter.DoPage[
ipData.ipMaster, ipData.pageNumber, ipData.iMemContext, PPreView.IPLogError];
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;
EXITS
Null => NULL;
};
<<PaintPress: PROC [context: Context, pressData: PressData] ~ {
Imager.SetGray[context, 1];
Imager.ScaleT[context, pointsPerMica]; -- establish Press coord system
ShowPress.DrawPressPage[context, pressData.presshandle, pressData.pageNumber
! ShowPress.ShowPressError => {
Message[Atom.GetPName[code]];
pressData.abort ¬ TRUE;
CONTINUE;
}];
};
>>
<<PaintPD: PROC [context: Context, pdData: PDData, viewer: Viewer] ~ {
PaintIt: PROC [self: Viewer, whatChanged: REF ANY] ~ {
This routine may be called many times; it shouldn't do destructive operations on context state
pdData: PDData ¬ NARROW[BiScrollers.ClientDataOfViewer[self]];
WITH whatChanged SELECT FROM
handle: PDImageReader.Handle => Imager.DrawBitmap[
context: context, bitmap: handle.bitmap, position: [0,pdData.pdhandle.herald.imageSSize]];
ENDCASE => ERROR;
};
IF pdData.scalePD
THEN Imager.Scale2T[context, pdData.scaleFactors];-- do 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 [] ¬ PDImageReader.InterpretImage[handle: pdData.pdhandle, viewer: viewer, paintAction: PaintIt]; --this guy eventually calls PaintIt.
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.
};
>>
ClipPixelArray: PROC [pa: ImagerPixelArray.PixelArray, clipRectangle: Rectangle]
RETURNS [new: ImagerPixelArray.PixelArray]
~ {
MaxSample: PROC [i: NAT] RETURNS [ImagerPixel.Sample] ~ {
RETURN [ImagerPixelArray.MaxSampleValue[pa, i]]
};
paRect: Rectangle ¬ ImagerTransformation.InverseTransformRectangle[m: pa.m, r: clipRectangle];
paBox: SF.Box = SF.Intersect[[max: [pa.sSize, pa.fSize]], [min: [s: Real.Floor[paRect.x], f: Real.Floor[paRect.y]], max: [s: Real.Ceiling[paRect.x+paRect.w], f: Real.Ceiling[paRect.y+paRect.h]]]];
pixelMap: ImagerPixel.PixelMap = ImagerPixel.NewPixelMap[samplesPerPixel: pa.samplesPerPixel, box: paBox, maxSample: MaxSample];
FOR i: NAT IN [0..pixelMap.samplesPerPixel) DO
ImagerPixelArray.Transfer[pa: pa, i: i, s: paBox.min.s, f: paBox.min.f, dst: pixelMap[i], dstMin: paBox.min, size: SF.Size[paBox]];
ENDLOOP;
new ¬ ImagerPixelArray.FromPixelMap[pixelMap: pixelMap, box: paBox, scanMode: [slow: right, fast: up], immutable: TRUE];
new.m ¬ ImagerTransformation.PreTranslate[pa.m, [paBox.min.s, paBox.min.f]];
};
PaintAIS: PROC [context: Context, aisData: AISData, clipRect: REF Rectangle] ~ {
state: PPreView.AISState ¬ aisData.state;
pa: ImagerPixelArray.PixelArray ¬ state.pa;
IF state.active THEN {
IF clipRect#NIL THEN { pa ¬ ClipPixelArray[pa, clipRect­] };
IF state.op = NIL
THEN {
Imager.SetGray[context, 1];
Imager.MaskPixel[context: context, pa: pa]
}
ELSE Imager.DrawSampledColor[context: context, pa: pa, colorOperator: state.op];
};
};
PaintRES: PROC [context: Context, resData: RESData, clipRect: REF Rectangle] ~ {
res: RasterEncodingStandardIO.RES = resData.image;
maskImage: ImagerPixelArray.PixelArray ¬ res.maskImage;
colorImage: ImagerPixelArray.PixelArray ¬ res.colorImage;
MaskPABounds: PROC [pa: ImagerPixelArray.PixelArray] ~ {
Inner: PROC ~ {
Imager.ConcatT[context, pa.m];
Imager.MaskRectangle[context, [0, 0, pa.sSize, pa.fSize]];
};
Imager.DoSave[context, Inner];
};
IF clipRect#NIL THEN {
IF maskImage # NIL THEN maskImage ¬ ClipPixelArray[maskImage, clipRect­];
IF colorImage # NIL THEN colorImage ¬ ClipPixelArray[colorImage, clipRect­];
};
SELECT TRUE FROM
colorImage # NIL AND maskImage=NIL AND res.colorOperator # NIL AND res.colorOperator.samplesPerPixelIn = 1 AND ImagerPixelArray.MaxSampleValue[colorImage, 0] = 1 => {
sample: CARDINAL ¬ 0;
Pixel: PROC [NAT] RETURNS [REAL] ~ { RETURN [sample] };
maskImage ¬ colorImage;
colorImage ¬ NIL;
sample ¬ 0;
Imager.SetColor[context, ImagerColor.ColorFromPixel[res.colorOperator, Pixel]];
MaskPABounds[maskImage];
sample ¬ 1;
Imager.SetColor[context, ImagerColor.ColorFromPixel[res.colorOperator, Pixel]];
};
colorImage = NIL OR res.colorOperator = NIL => {
Imager.SetGray[context, 1];
};
ENDCASE => { Imager.SetSampledColor[context, colorImage, NIL, res.colorOperator] };
IF maskImage=NIL
THEN Imager.MaskRectangle[context, ImagerTransformation.TransformRectangle[m: colorImage.m, r: [0, 0, colorImage.sSize, colorImage.fSize]]]
ELSE Imager.MaskPixel[context, maskImage];
};
<<PaintGriffin: PROC [context: Context, gData: GData] ~ {
SELECT TRUE FROM
gData.switches['M] => { -- M switch means use no ImagerMemory
Imager.ScaleT[context, pointsPerMica]; -- establish Griffin coord. system
Imager.SetStrokeJoint[context, 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, Context])#NIL THEN { --hit
ImagerMemory.Replay[c: gData.iMemContext, into: context];
gData.pageInMem ¬ gData.pageNumber;
}
ELSE { -- miss
gData.iMemContext ¬ ImagerMemory.NewMemoryContext[];
Imager.ScaleT[gData.iMemContext, pointsPerMica]; -- establish Griffin coord. system
Imager.SetStrokeJoint[gData.iMemContext, 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;
};
>>
<<PaintGG: PROC [context: Context, pvData: PVData] ~ {
SELECT TRUE FROM
pvData.switches['M] => { -- M switch means use no ImagerMemory
Imager.ScaleT[context, pointsPerInch]; -- establish GG coord. system
GGForPV.DrawData[context, pvData.data];
};
pvData.pageInMem=pvData.pageNumber => { -- iMemContext is current
ImagerMemory.Replay[c: pvData.iMemContext, into: context];
};
pvData.pageInMem#pvData.pageNumber => { -- memory context may be cached
PageCompareProc: FunctionCache.CompareProc ~ {
PROC [argument: Domain] RETURNS [good: BOOL];
RETURN[NARROW[argument, CacheKey]­ = cacheKey­];
};
cacheKey: CacheKey ¬ NEW[CacheKeyRep ¬
[pvData.fileInfo.fullFName, pvData.fileInfo.created, pvData.pageNumber]];
IF (pvData.iMemContext ¬ NARROW[FunctionCache.Lookup[x: globalCache, compare: PageCompareProc, clientID: $PVPage].value, Context])#NIL
THEN { --hit
ImagerMemory.Replay[c: pvData.iMemContext, into: context];
pvData.pageInMem ¬ pvData.pageNumber;
}
ELSE { -- miss
pvData.iMemContext ¬ ImagerMemory.NewMemoryContext[];
Imager.ScaleT[pvData.iMemContext, pointsPerInch]; -- establish GG coord. system
GGForPV.DrawData[pvData.iMemContext, pvData.data];
FunctionCache.Insert[x: globalCache, argument: cacheKey, clientID: $PVPage, size: ImagerMemory.GetContextSize[pvData.iMemContext], value: pvData.iMemContext];
pvData.pageInMem ¬ pvData.pageNumber;
ImagerMemory.Replay[c: pvData.iMemContext, into: context];
};
};
ENDCASE => NULL;
};
>>
PVPaint: PUBLIC ENTRY ViewerClasses.PaintProc ~ TRUSTED {
[self: Viewer, context: Context, whatChanged: REF ANY, clear: BOOL];
ENABLE ABORTED => GOTO Finished; -- return and unlock viewers
data: Data ¬ NARROW[BiScrollers.ClientDataOfViewer[self]];
BEGIN
ENABLE UNWIND => data.painting ¬ FALSE; -- abort is set, painting gets cleared
PVPaintWithAbort: PROC ~ TRUSTED {
data.abort ¬ data.painting ¬ TRUE;
DoPainting[context: context, data: data, viewer: self];
data.abort ¬ data.painting ¬ FALSE;
IF data.bBox.active THEN PVFeedback[data: data, v: self, op: paint];
};
ViewerAbort.CallWithAbortEnabled[self, PVPaintWithAbort];
END;
EXITS
Finished => NULL;
};
PVDestroy: PUBLIC ENTRY ViewerClasses.DestroyProc ~ { -- [self: Viewer]
ENABLE UNWIND => NULL;
data: Data ¬ NARROW[BiScrollers.ClientDataOfViewer[self]];
WITH data SELECT FROM
ipData: IPData => {
IF ipData.ipMaster # NIL THEN InterpressInterpreter.Close[master: ipData.ipMaster];
ipData.ipMaster ¬ NIL;
};
resData: RESData => {
resData.image ¬ NIL;
};
aisData: AISData => {
AIS.CloseFile[aisData.state.openFile];
aisData.state.openFile ← NIL;
aisData.state.pa ¬ NIL;
aisData.state.op ¬ NIL;
};
pressData: PressData => {
ShowPress.Close[pressData.presshandle];
pressData.presshandle ← NIL;
};
pdData: PDData => {
PDFileReader.Close[pdData.pdhandle];
pdData.pdhandle ← NIL;
pdData.image ← NIL;
};
gData: GData => {
gData.image ← NIL;
};
pvData: PVData => {
pvData.data ← NIL;
};
ENDCASE => Message["unknown destroy operation"];
SafeStorage.ReclaimCollectibleObjects[suspendMe: FALSE]; -- good citizenship
};
PVGetName: PUBLIC ViewerClasses.GetProc ~ {
[self: Viewer, op: ATOM] RETURNS [data: REF ANY]
This PROC for the benefit of XTSetter, which calls this when the InputFocus is in the previewer and the XTSetter GET button is clicked
RETURN[NARROW[BiScrollers.ClientDataOfViewer[self], Data].container.file];
};
PVBasicTransformProc: PUBLIC PROC [bs: BiScrollers.BiScroller] RETURNS [Transformation] ~ {
BiScrollers.TransformGenerator
actionArea: Viewer ¬ bs.style.QuaViewer[bs: bs, inner: TRUE];
data: Data ¬ NARROW[BiScrollers.ClientDataOfViewer[actionArea]];
Put the center of the client specified size in the middle of the window.
height: REAL ¬ data.pageHeight;
width: REAL ¬ data.pageWidth;
ww: REAL ¬ actionArea.ww;
wh: REAL ¬ actionArea.wh;
RETURN[ImagerTransformation.Translate[[(ww-width)/2.0, (wh-height)/2.0]]];
};
PVExtremaProc: PUBLIC PROC [clientData: REF ANY, direction: Vec] RETURNS [min, max: 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.
..
Old Preview.tip
SELECT TRIGGER FROM
Mouse WHILE Red Down => Coords, Move;
Red Down => SELECT ENABLE FROM
Yellow Down => Abort;
ENDCASE => Coords, Move;
Yellow Down => Abort;
Red Up => EndMove;
Blue Up => ShowSelection;
ENDCASE.