DIRECTORY Atom, G2dZoom, Imager, ImagerInterpress, ImagerTransformation, IO, Process, Real, RefTab, Rope, TIPUser, ViewerClasses, ViewerOps, Vector2; G2dZoomImpl: CEDAR PROGRAM IMPORTS Atom, Imager, ImagerInterpress, ImagerTransformation, IO, Process, Real, RefTab, Rope, ViewerOps EXPORTS G2dZoom ~ BEGIN Rectangle: TYPE ~ Imager.Rectangle; Transformation: TYPE ~ Imager.Transformation; STREAM: TYPE ~ IO.STREAM; ROPE: TYPE ~ Rope.ROPE; VEC: TYPE ~ Vector2.VEC; Viewer: TYPE ~ ViewerClasses.Viewer; ViewerClass: TYPE ~ ViewerClasses.ViewerClass; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [m: Transformation]; Hijack: PUBLIC PROC [viewer: Viewer] ~ { IF viewer # NIL AND Atom.GetPropFromList[viewer.class.props, $Hijacked] = NIL THEN { old: ATOM ~ viewer.class.flavor; new: ATOM ~ Atom.MakeAtom[Rope.Concat[Atom.GetPName[old], "Zoom"]]; viewer.class ¬ IF RefTab.Fetch[x: flavorCache, key: new].found THEN NARROW[RefTab.Fetch[x: flavorCache, key: new].val] ELSE MakeFlavor[old, new]; Reset[viewer, FALSE]; -- reset transform in case viewer was previously commandeered }; }; Release: PUBLIC PROC [viewer: Viewer] ~ { IF viewer = NIL THEN RETURN; WITH Atom.GetPropFromList[viewer.class.props, viewer.class.flavor] SELECT FROM oldClass: ViewerClass => { -- restore to the previous class viewer.class ¬ oldClass; Repaint[viewer]; }; ENDCASE => NULL; }; SetTransform: PUBLIC PROC [viewer: Viewer, m: Transformation, repaint: BOOL ¬ TRUE] ~ { GetData[viewer].m ¬ m; IF repaint THEN Repaint[viewer]; }; Transform: PUBLIC PROC [ viewer: Viewer, translate: VEC, scale, rotate: REAL, repaint: BOOL ¬ TRUE] ~ { Reset[viewer, FALSE]; Rotate[viewer, rotate, FALSE]; Scale[viewer, scale, FALSE]; Translate[viewer, [translate.x, translate.y], repaint]; }; Scale: PUBLIC PROC [viewer: Viewer, s: REAL, repaint: BOOL ¬ TRUE] ~ { ImagerTransformation.ApplyPreScale[GetData[viewer].m, s]; IF repaint THEN Repaint[viewer]; }; Translate: PUBLIC PROC [viewer: Viewer, v: VEC, repaint: BOOL ¬ TRUE] ~ { ImagerTransformation.ApplyPostTranslate[GetData[viewer].m, [72.0*v.x, 72.0*v.y]]; IF repaint THEN Repaint[viewer]; }; Rotate: PUBLIC PROC [viewer: Viewer, degrees: REAL, repaint: BOOL ¬ TRUE] ~ { ImagerTransformation.ApplyPreRotate[GetData[viewer].m, degrees]; IF repaint THEN Repaint[viewer]; }; Reset: PUBLIC PROC [viewer: Viewer, repaint: BOOL ¬ TRUE] ~ { GetData[viewer].m ¬ ImagerTransformation.Scale[1]; IF repaint THEN Repaint[viewer]; }; flavorCache: RefTab.Ref ~ RefTab.Create[]; -- cache newly created viewer classes MakeFlavor: PROC [old, new: ATOM] RETURNS [class: ViewerClass] ~ { oldClass: ViewerClass ~ ViewerOps.FetchViewerClass[old]; class ¬ NEW[ViewerClasses.ViewerClassRec ¬ oldClass­]; class.props ¬ Atom.PutPropOnList[propList: class.props, prop: new, val: oldClass]; class.props ¬ Atom.PutPropOnList[propList: class.props, prop: $Hijacked, val: $True]; class.flavor ¬ new; class.notify ¬ MyNotify; class.paint ¬ MyPaint; class.bltV ¬ none; -- disable bitBlts, since Tioga would be confused as to what's on screen class.bltH ¬ none; ViewerOps.RegisterViewerClass[flavor: new, class: class]; [] ¬ RefTab.Store[x: flavorCache, key: new, val: class]; -- cache class for future use }; GetData: PROC [v: Viewer] RETURNS [d: Data] ~ { IF v # NIL THEN WITH ViewerOps.FetchProp[viewer: v, prop: v.class.flavor] SELECT FROM data: Data => d ¬ data; ENDCASE => { d ¬ NEW[DataRep ¬ [m: ImagerTransformation.Scale[1]]]; ViewerOps.AddProp[viewer: v, prop: v.class.flavor, val: d]; } ELSE d ¬ NEW[DataRep ¬ [m: ImagerTransformation.Scale[1]]]; }; GetClass: PROC [v: Viewer] RETURNS [ViewerClass] ~ { IF v = NIL THEN RETURN[NIL]; RETURN[NARROW[Atom.GetPropFromList[propList: v.class.props, prop: v.class.flavor]]]; }; Repaint: PROC [v: Viewer] ~ TRUSTED { Process.Detach[FORK ViewerOps.PaintViewer[viewer: v, hint: client, clearClient: TRUE]]; }; MyNotify: ViewerClasses.NotifyProc ~ { oldClass: ViewerClass ~ GetClass[self]; IF oldClass = NIL THEN RETURN; FOR l: LIST OF REF ANY ¬ input, l.rest UNTIL l = NIL DO WITH l.first SELECT FROM coords: TIPUser.TIPScreenCoords => { -- xform coordinates to pre-commandeered state old: VEC ~ [coords.mouseX, coords.mouseY]; data: Data ~ GetData[self]; new: VEC ~ ImagerTransformation.InverseTransform[data.m, old]; coords.mouseX ¬ Real.Round[new.x]; coords.mouseY ¬ Real.Round[new.y]; }; ENDCASE => NULL; ENDLOOP; oldClass.notify[self, input]; -- and pass on to original notify proc }; MyPaint: ViewerClasses.PaintProc ~ { oldClass: ViewerClass ~ GetClass[self]; Inner: PROC ~ { d: Data ~ GetData[self]; Imager.ConcatT[context: context, m: d.m]; -- add our own transformation quit ¬ oldClass.paint[self, context, whatChanged, clear]; -- paint with original paint proc }; IF oldClass # NIL THEN Imager.DoSave[context: context, action: Inner]; }; WriteTransform: PUBLIC PROC [viewer: Viewer, out: STREAM] ~ { m: Transformation ¬ GetData[viewer].m; f: ImagerTransformation.FactoredTransformation ¬ ImagerTransformation.Factor[m]; IO.PutF[out, "%lViewer Transformation%l\n", IO.rope["b"], IO.rope["B"]]; IO.PutF1[out, "\tRotate:\t%g\n", IO.real[f.r1]]; IO.PutF1[out, "\tScale:\t%g\n", IO.real[f.s.x]]; IO.PutF[out, "\tTranslate:\t%g, %g\n", IO.real[f.t.x], IO.real[f.t.y]]; }; ReadTransform: PUBLIC PROC [in: STREAM] RETURNS [m: Transformation] ~ { ENABLE IO.EndOfStream => CONTINUE; Eq: PROC [r1, r2: ROPE] RETURNS [b: BOOL] ~ {b ¬ Rope.Equal[r1, r2, FALSE]}; m ¬ ImagerTransformation.Scale[1.0]; DO rope: ROPE ¬ IO.GetCedarTokenRope[in].token; SELECT TRUE FROM Eq[rope, "Rotate"] => m ¬ ImagerTransformation.PreRotate[m, IO.GetReal[in]]; Eq[rope, "Scale"] => m ¬ ImagerTransformation.PreScale[m, IO.GetReal[in]]; Eq[rope, "Translate"] => { dx: REAL ¬ IO.GetReal[in]; dy: REAL ¬ IO.GetReal[in]; m ¬ ImagerTransformation.PostTranslate[m, [dx, dy]]; }; ENDCASE; ENDLOOP; }; IPOut: PUBLIC PROC [viewer: Viewer, fileName: ROPE] ~ { IF viewer # NIL AND fileName # NIL THEN { d: Data ~ GetData[viewer]; oldClass: ViewerClass ~ GetClass[viewer]; ref: ImagerInterpress.Ref ¬ ImagerInterpress.Create[fileName]; ContextProc: PROC [context: Imager.Context] ~ { Imager.ScaleT[context, Imager.metersPerPoint]; Imager.SetStrokeWidth[context, 1.0]; Imager.SetStrokeEnd[context, round]; Imager.TranslateT[context, [0.0, 0.5*11.0*Imager.pointsPerInch]]; Imager.ConcatT[context: context, m: d.m]; -- add our own transformation [] ¬ oldClass.paint[viewer, context, NIL, TRUE]; -- original paint proc }; ImagerInterpress.DoPage[ref, ContextProc]; ImagerInterpress.Close[ref]; }; }; END. .. MyScroll: PROC ViewerClasses.ScrollProc ~ { oldClass: ViewerClass ~ GetClass[self]; d: Data ~ GetData[self]; r: Rectangle ~ ImagerTransformation.InverseTransformRectangle[d.m, [0, 0, self.cw, self.ch]]; self.cw ¬ Real.Ceiling[r.w]; self.ch ¬ Real.Ceiling[r.h]; [top, bottom] ¬ oldClass.scroll[self, op, amount, shift, control]; }; Φ G2dZoomImpl.mesa Copyright Σ 1988, 1992 by Xerox Corporation. All rights reserved. Written by M. Plass, February 27, 1989 Bloomenthal, July 2, 1992 4:54 pm PDT Types Pan/Zoom a Viewer Override a viewer's paint and notify procs. New class's flavor depends on old class's flavor, check cache of flavorCache: Support class.scroll _ MyScroll; We'd like to be able to get the scrolling right, but haven't yet: save: ARRAY [0..2) OF INTEGER ~ [self.cw, self.ch]; r: Rectangle ~ ImagerTransformation.InverseTransformRectangle[d.m, [0, 0, self.cw, self.ch]]; self.cw _ Real.Ceiling[r.w]; self.ch _ Real.Ceiling[r.h]; self.cw _ save[0]; self.ch _ save[1]; IO A transformation is stored to or read from a stream according to the following format: Rotate: Scale: Translate: <[x, y: REAL]> The scrolling doesn't work right; neither did this attempted correction: save: ARRAY [0..2) OF INTEGER ~ [self.cw, self.ch]; self.cw _ save[0]; self.ch _ save[1]; Κ-–"cedarcode" style•NewlineDelimiter ™™Jšœ Οeœ6™BJ™&J™%—˜šΟk œ@žœJ˜•J˜——šΠln œžœž˜Jšžœ7žœ(˜hšžœ˜J˜——Jšœž˜headšΟl™Jšœ žœ˜$Jšœžœ˜-Jšžœžœžœžœ˜Jšžœžœžœ˜Jšžœžœ žœ˜Jšœ žœ˜&šœžœ˜/J˜—Jšœ žœžœ ˜Jšœ žœžœ˜-—š ™šΟnœžœžœ˜(J™+š žœ žœžœ7žœžœ˜TJšœžœ˜ J•StartOfExpansion([base: ROPE _ NIL, rest: ROPE _ NIL]šœžœ:˜CJ™Mšœžœ-˜>Jšžœžœ,˜7Jšžœ˜—JšœžœΟc>˜TJ˜—Jšœ˜J˜—š‘œžœžœ˜)Jšžœ žœžœžœ˜šžœ?žœž˜Nšœ’ ˜[viewer: ViewerClasses.Viewer, prop: ATOM, val: REF ANY]šœ;˜;Jšœ˜——Jšžœžœ/˜;—Jšœ˜J˜—š‘œžœ žœ˜4Jš žœžœžœžœžœ˜JšžœžœG˜TJ˜J˜—š‘œžœžœ˜%Jšœžœ=žœ˜WJ˜J˜—š‘œ˜&Jšœ'˜'Jšžœ žœžœžœ˜šžœžœžœžœžœžœžœž˜7šžœ žœž˜šœ%’.˜SJšœžœ"˜*J˜Jšœžœ6˜>J˜"J˜"Jšœ˜—Jšžœžœ˜—Jšžœ˜—Jšœ’&˜DJšœ˜J˜—š‘œ˜$Jšœ'˜'š‘œžœ˜J˜J–E[context: Imager.Context, m: ImagerTransformation.Transformation]™AJšœžœžœžœ™3J–D[m: ImagerTransformation.Transformation, r: ImagerBox.Rectangle]šœΟsœ £œ£œ3£œ£œ£œ£œ ™]Jšœ™Jšœ™Jšœ.’˜KJšœ:’!˜[Jšœ™Jšœ™Jšœ˜—J–+[context: Imager.Context, action: PROC]šžœ žœžœ0˜FJšœ˜——šΠkl™™VJšœžœ™Jšœžœ™Jšœžœ™J™—š‘œžœžœžœ˜=J˜&J˜PJšžœ*žœ žœ ˜HJšžœžœ ˜0Jšžœžœ˜0Jšžœ%žœžœ˜GJ˜J˜—š ‘ œžœžœžœžœ˜GJšžœžœžœ˜"Jš ‘œžœ žœžœžœžœ˜LJ˜$šž˜Jšœžœžœ˜,šžœžœž˜Jšœ<žœ˜LJšœ:žœ˜Jšœ˜Jšœžœžœ ˜Jšœžœžœ ˜J˜4J˜—Jšžœ˜—Jšžœ˜—J˜J˜—š‘œžœžœžœ˜7š žœ žœžœ žœžœ˜)Jšœ˜Jšœ)˜)J˜>š‘ œžœ˜/Jšœ.˜.J˜$J˜$JšœA˜AJšœ+’˜HJšœ%žœžœ’˜GJšœ˜—Jšœ*˜*Jšœ˜J˜—J˜——J˜Jšžœ˜˜J˜J™Hš‘œžœ˜+Jšœ'˜'J˜J–E[context: Imager.Context, m: ImagerTransformation.Transformation]šœžœžœžœ™3J–D[m: ImagerTransformation.Transformation, r: ImagerBox.Rectangle]šœ]˜]J˜J˜J˜BJšœ™Jšœ™Jšœ˜———…—ΰ-γ