<> <> <> <> <> <> 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], IO USING [PutFR], MessageWindow USING [Append, Blink], PDFileReader USING [Close, Handle], PDImageReader USING [Handle, InterpretImage, ResetToImage], PreView USING [AISData, AISState, BBoxOp, bsStyle, Data, GData, IPData, IPLogError, 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, IO, MessageWindow, PDFileReader, PDImageReader, PreView, Process, Rope, SafeStorage, ShowPress, ViewerGroupLocks, ViewerLocks, ViewerPrivate EXPORTS PreView = BEGIN 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 pointsPerInch: REAL = 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; 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. }; $EndMove => IF data.bBox.active THEN MessageWindow.Append[ message: IO.PutFR["Selection = [x: %g, y: %g, w: %g, h: %g] inches", [real[data.bBox.rect.x/pointsPerInch]], [real[data.bBox.rect.y/pointsPerInch]], [real[data.bBox.rect.w/pointsPerInch]], [real[data.bBox.rect.h/pointsPerInch]]], clearFirst: TRUE] ELSE MessageWindow.Append["No selection", TRUE]; 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, fit: data.stuffWithFit]; }; 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]; EXITS Quit => NULL; }; ENDCASE; <> }; 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, log: PreView.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 = { <> 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, log: PreView.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; }; 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] = { <> 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) <> 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 = { <> 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 }; <> 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] = { <> iv: ViewerClasses.Viewer _ bs.style.QuaViewer[bs: bs, inner: TRUE]; data: Data _ NARROW[BiScrollers.ClientDataOfViewer[iv]]; <> t _ Geom2D.id.PostTranslate[[0, iv.ch - data.pageHeight]]; < appear at the upper left corner of viewer.>> }; PVExtremaProc: PUBLIC PROC [clientData: REF ANY, direction: Geom2D.Vec] RETURNS [min, max: Geom2D.Vec] --BiScrollers.ExtremaProc-- = { <> 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.