<<>> <> <> <> <> <<>> <> DIRECTORY BiScrollers, Cursors, Geom2D, Imager, ImagerBackdoor, ImagerBox, ImagerPath, ImagerTransformation, InputFocus, MultiCursors, PFS, PFSNames, Real, RealFns, TIPLinking, TIPUser, ViewerClasses, Vector2, ViewerOps; BiScrollersButtonless: CEDAR MONITOR IMPORTS BiScrollers, Cursors, Geom2D, Imager, ImagerBackdoor, ImagerBox, ImagerPath, ImagerTransformation, InputFocus, MultiCursors, PFS, PFSNames, Real, RealFns, TIPLinking, TIPUser, Vector2, ViewerOps = BEGIN OPEN BiScrollers; Number: TYPE = Geom2D.Number; ViewerClass: TYPE = ViewerClasses.ViewerClass; Axis: TYPE = Geom2D.Axis; Vec: TYPE = Geom2D.Vec; Rect: TYPE = Geom2D.Rect; RectRef: TYPE = REF Rect; Buttonless: TYPE = REF ButtonlessRep; ButtonlessRep: TYPE = RECORD [ class: ButtonlessClass, clientData: REF ANY, viewer: Viewer ¬ NIL, children: Child ¬ NIL, t: Transform ¬ Geom2D.id, --Client to Viewer Transform u: Transform ¬ Geom2D.id, --Viewer coords*u = Client coords tp, up: Transform ¬ Geom2D.id, --previous transforms cw, ch: INTEGER ¬ 0, --last time we looked in the viewer-- clientWantsActive, initialized: BOOL ¬ FALSE, state: State ¬ Normal ]; ButtonlessClass: TYPE = REF ButtonlessClassRep; ButtonlessClassRep: TYPE = RECORD [ viewerClass: ViewerClass, cursor: Cursors.CursorType ]; Child: TYPE = REF ChildObject; ChildObject: TYPE = RECORD [next: Child, where: Vec, it: Viewer]; Mouse: TYPE = REF MouseRec; MouseRec: TYPE = RECORD [ start: StartProc ¬ NIL, finish: FinishProc ¬ NIL, cursor: CursorProc ¬ NIL, track: TrackProc ¬ NIL, newTransform: NewTransformProc, data: REF ANY ¬ NIL]; StartProc: TYPE = PROC [viewMouse, viewCenter: Vec, bs: BiScroller, bl: Buttonless, data: REF ANY]; FinishProc: TYPE = PROC [bs: BiScroller, bl: Buttonless, data: REF ANY]; CursorProc: TYPE = PROC [viewMouse, viewCenter: Vec, data: REF ANY] RETURNS [Cursors.CursorType]; TrackProc: TYPE = PROC [viewMouse, viewCenter: Vec, bs: BiScroller, bl: Buttonless, data: REF ANY]; NewTransformProc: TYPE = PROC [viewMouse, viewCenter: Vec, bs: BiScroller, bl: Buttonless, diff: BOOL, data: REF ANY]; Rects: TYPE = REF RectsRec; RectsRec: TYPE = RECORD [next: Rects, rect: Rect]; Paths: TYPE = REF PathsRep; PathsRep: TYPE = RECORD [next: Paths, path: ImagerPath.Trajectory, op: {fill, stroke}]; State: TYPE = {Normal, Thumbing, ScrollToCentering, ScrollAlongClicksing, Expanding, Contracting}; ActiveState: TYPE = State[Thumbing .. Contracting]; awake: Viewer ¬ NIL; mice: ARRAY ActiveState OF Mouse ¬ ALL[NIL]; pointUpRight, pointUpLeft, pointDownRight, pointDownLeft: Cursors.CursorType ¬ Cursors.CursorType[questionMark]; buttonlessStyle: BiScrollerStyle ¬ NEW [BiScrollerStyleRep ¬ [ NewBiScrollerClass: NewBiScrollerClass, CreateBiScroller: CreateBiScroller, Destroy: Destroy, GetTransforms: GetTransforms, ChangeTransform: ChangeTransform, AddChild: AddChild, DeleteChild: DeleteChild, SetButtonsCapturedness: SetButtonsCapturedness, ViewportOf: ViewportOf, QuaViewer: QuaViewer, ClientDataOf: ClientDataOf ]]; first, next: Rect; myWorkingDirectory: PFS.PATH ~ PFS.GetWDir[]; NewBiScrollerClass: PROC [cc: ClassCommon] RETURNS [bsc: BiScrollerClass] = BEGIN viewerClass: ViewerClass; tipFile: PFS.PATH ¬ PFSNames.ExpandName[PFS.PathFromRope["BiScroller.tip"], myWorkingDirectory]; t: TIPUser.TIPTable ¬ TIPUser.InstantiateNewTIPTable[PFS.RopeFromPath[tipFile]]; IF cc.vanilla = NIL THEN cc.vanilla ¬ GenID; IF cc.bsUserAction = NIL THEN cc.bsUserAction ¬ DoBSUserAction; IF cc.tipTable # NIL THEN [] ¬ TIPLinking.Append[t, cc.tipTable]; viewerClass ¬ NEW [ViewerClasses.ViewerClassRec ¬ [ flavor: cc.flavor, notify: NotifyBiScroller, paint: PaintBiScroller, modify: cc.modify, destroy: cc.destroy, copy: cc.copy, set: cc.set, get: cc.get, save: cc.save, caption: cc.caption, adjust: AdjustBiScroller, menu: cc.menu, tipTable: t, icon: cc.icon, cursor: cc.cursor]]; ViewerOps.RegisterViewerClass[flavor: viewerClass.flavor, class: viewerClass]; bsc ¬ NEW [BiScrollerClassRep ¬ [ style: buttonlessStyle, common: cc, rep: NEW [ButtonlessClassRep ¬ [viewerClass: viewerClass, cursor: cc.cursor]] ]]; END; CreateBiScroller: PROC [class: BiScrollerClass, info: ViewerClasses.ViewerRec ¬ [], paint: BOOLEAN ¬ TRUE] RETURNS [new: BiScroller] = BEGIN bsc: ButtonlessClass ¬ NARROW[class.rep]; paintFirst: BOOL = paint AND info.iconic; bl: Buttonless ¬ NEW [ButtonlessRep ¬ [class: bsc, clientData: info.data]]; info.data ¬ new ¬ NEW [BiScrollerRep ¬ [ style: class.style, class: class, rep: bl]]; bl.viewer ¬ ViewerOps.CreateViewer[flavor: bsc.viewerClass.flavor, info: info, paint: paintFirst]; ChangeTransform[new, class.common.vanilla[new], ignore, FALSE]; bl.cw ¬ bl.viewer.cw; bl.ch ¬ bl.viewer.ch; SetBS[bl.viewer, new]; IF class.common.init # NIL THEN class.common.init[bl.viewer]; bl.initialized ¬ TRUE; IF paintFirst THEN NULL ELSE IF bl.viewer.parent=NIL THEN ViewerOps.ComputeColumn[column: ViewerOps.ViewerColumn[bl.viewer], paint: paint] ELSE IF paint THEN ViewerOps.PaintViewer[bl.viewer, all]; END; Destroy: PROC [bs: BiScroller] RETURNS [BiScroller] = BEGIN bl: Buttonless ¬ NARROW[bs.rep]; ViewerOps.DestroyViewer[bl.viewer]; bl.viewer ¬ NIL; RETURN [NIL]; END; AddChild: PROC [to: BiScroller, what: Viewer, x, y: REAL ¬ 0, useTheseCoords: BOOLEAN ¬ FALSE, paint: BOOLEAN ¬ TRUE] = BEGIN bl: Buttonless ¬ NARROW[to.rep]; my: Vec; c: Child ¬ NEW [ChildObject ¬ [next: bl.children, it: what, where: IF useTheseCoords THEN [x, y] ELSE [what.wx, what.wy] ]]; bl.children ¬ c; my ¬ bl.t.Transform[c.where]; ViewerOps.MoveViewer[viewer: what, x: RI[my.x], y: RI[my.y], w: what.ww, h: what.wh, paint: paint]; END; DeleteChild: PROC [of: BiScroller, who: Viewer] = BEGIN bl: Buttonless ¬ NARROW[of.rep]; Filter: PROC [c: Child] RETURNS [Child] = {IF c = NIL THEN RETURN [NIL]; IF c.it = who THEN RETURN [c.next]; c.next ¬ Filter[c.next]; RETURN [c]}; bl.children ¬ Filter[bl.children]; END; QuaViewer: PROC [bs: BiScroller, inner: BOOL ¬ FALSE] RETURNS [Viewer] = {bl: Buttonless ¬ NARROW[bs.rep]; RETURN [bl.viewer]}; ClientDataOf: PROC [bs: BiScroller] RETURNS [REF ANY] = {bl: Buttonless ¬ NARROW[bs.rep]; RETURN [bl.clientData]}; ViewportOf: PROC [bs: BiScroller] RETURNS [VecList] = BEGIN bl: Buttonless ¬ NARROW[bs.rep]; RETURN [Geom2D.MapVecs[bl.u, LIST[[0, 0], [bl.viewer.cw, 0], [bl.viewer.cw, bl.viewer.ch], [0, bl.viewer.ch]]]]; END; FinishClient: PROC [bs: BiScroller, bl: Buttonless] = { IF bs.class.common.notify # NIL THEN bs.class.common.notify[bl.viewer, bs.class.common.finish]; }; NotifyBiScroller: PROC [self: Viewer, input: LORA, device, user, display: REF ANY] --ViewerClasses.NotifyProc-- = BEGIN bs: BiScroller ¬ NARROW[self.data, BiScroller]; bl: Buttonless ¬ NARROW[bs.rep]; i, o, l: LIST OF REF ANY ¬ NIL; viewMouse: Vec; ToClient: PROC = { IF bl.state # Normal THEN GiveUp[bs, bl]; IF bs.class.common.notify # NIL THEN bs.class.common.notify[bl.viewer, input]}; FOR i ¬ input, i.rest WHILE i # NIL DO l ¬ IF l = NIL THEN o ¬ CONS[i.first, NIL] ELSE l.rest ¬ CONS[i.first, NIL]; WITH l.first SELECT FROM z: TIPUser.TIPScreenCoords => { IF Awake[bl] THEN { v: Viewer; inClient: BOOL; [v, inClient] ¬ ViewerOps.MouseInViewer[z]; IF v # bl.viewer OR NOT inClient THEN { didWant: BOOL ¬ bl.clientWantsActive; bl.clientWantsActive ¬ FALSE; IF bl.state IN ActiveState THEN Finish[bs, bl, mice[bl.state]]; Sleep[bl]; IF didWant THEN FinishClient[bs, bl]; RETURN}; }; l.first ¬ NEW [Vec ¬ bl.u.Transform[viewMouse ¬ [z.mouseX, z.mouseY]]]; }; ENDCASE; ENDLOOP; input ¬ o; IF input = NIL THEN ToClient[] ELSE SELECT input.first FROM $BSexpand => Work[bs, bl, viewMouse, Expanding, FALSE, input.rest]; $BSexpandXY => Work[bs, bl, viewMouse, Expanding, TRUE, input.rest]; $BScontract => Work[bs, bl, viewMouse, Contracting, FALSE, input.rest]; $BScontractXY => Work[bs, bl, viewMouse, Contracting, TRUE, input.rest]; $BSthumb => Work[bs, bl, viewMouse, Thumbing, FALSE, input.rest]; $BSscrollToCenter => Work[bs, bl, viewMouse, ScrollToCentering, FALSE, input.rest]; $BSscrollAlongClicks => Work[bs, bl, viewMouse, ScrollAlongClicksing, FALSE, input.rest]; $BSgiveUp => GiveUp[bs, bl]; $BSeatIt => NULL; ENDCASE => ToClient[]; END; Work: PROC [bs: BiScroller, bl: Buttonless, viewMouse: Vec, why: ActiveState, diff: BOOL, input: LORA] = BEGIN viewCenter: Vec; who: Mouse ¬ mice[why]; oldState: State ¬ bl.state; clientWanted: BOOL ¬ bl.clientWantsActive; bl.clientWantsActive ¬ FALSE; bl.state ¬ why; viewCenter ¬ [bl.viewer.cw/2, bl.viewer.ch/2]; IF clientWanted THEN FinishClient[bs, bl]; WakeUp[bl]; IF why # oldState THEN BEGIN IF oldState IN ActiveState THEN Finish[bs, bl, mice[oldState]]; IF who.start # NIL THEN who.start[viewMouse, viewCenter, bs, bl, who.data]; END; IF who.cursor # NIL THEN Cursors.SetCursor[bl.viewer.class.cursor ¬ who.cursor[viewMouse, viewCenter, who.data]]; IF who.track # NIL THEN who.track[viewMouse, viewCenter, bs, bl, who.data]; IF input.rest.first = $Doit THEN BEGIN Sleep[bl]; oldT ¬ bl.t; bcUsed ¬ viewCenter; who.newTransform[viewMouse, viewCenter, bs, bl, diff, who.data]; END ELSE IF input.rest.first # $Idle THEN ERROR; END; oldT: Transform; bcUsed: Vec; GiveUp: PROCEDURE [bs: BiScroller, bl: Buttonless] = BEGIN oldState: State ¬ Normal; WasIdle: ENTRY PROC RETURNS [BOOLEAN] = {oldState ¬ bl.state; IF bl.state = Normal THEN RETURN [TRUE]; DoSleep[bl]; RETURN [FALSE]}; IF WasIdle[] THEN RETURN; IF oldState = Normal THEN ERROR; Finish[bs, bl, mice[oldState]]; END; Finish: PROC [bs: BiScroller, bl: Buttonless, mouse: Mouse] = BEGIN IF mouse.finish # NIL THEN mouse.finish[bs, bl, mouse.data]; END; Awake: ENTRY PROC [bl: Buttonless] RETURNS [b: BOOL] = {b ¬ awake = bl.viewer}; WakeUp: ENTRY PROC [bl: Buttonless] = {ENABLE UNWIND => {}; SetActive[bl]}; Sleep: ENTRY PROC [bl: Buttonless] = {ENABLE UNWIND => {}; DoSleep[bl]}; DoSleep: INTERNAL PROC [bl: Buttonless] = { bl.state ¬ Normal; Cursors.SetCursor[bl.viewer.class.cursor ¬ bl.class.cursor]; SetActive[bl]; }; SetActive: INTERNAL PROC [bl: Buttonless] = { wasAwake: Viewer ¬ awake; IF bl.clientWantsActive OR (bl.state # Normal) THEN { IF awake = NIL THEN { awake ¬ bl.viewer; InputFocus.CaptureButtons[proc: bl.viewer.class.notify, tip: bl.viewer.tipTable, viewer: bl.viewer]; }; } ELSE { IF awake = bl.viewer THEN { awake ¬ NIL; InputFocus.ReleaseButtons[]; }; }; IF NOT (wasAwake = bl.viewer OR wasAwake = NIL) THEN ERROR; }; SetButtonsCapturedness: PROC [bs: BiScroller, captured: BOOL] = { bl: Buttonless ¬ NARROW[bs.rep]; Doit: ENTRY PROC = { bl.clientWantsActive ¬ captured; SetActive[bl]}; Doit[]}; outline: Rect; ThumbStart: StartProc = BEGIN cxmin, cxmax, cymin, cymax: Number; cx, cy, dx, dy: Number; windowT: Geom2D.Trans; [cxmin, cxmax] ¬ ViewLimitsOfImage[bs, X]; [cymin, cymax] ¬ ViewLimitsOfImage[bs, Y]; [windowT.dxdx, windowT.dx] ¬ Squeeze[cxmin, cxmax, 0, bl.viewer.cw]; [windowT.dydy, windowT.dy] ¬ Squeeze[cymin, cymax, 0, bl.viewer.ch]; windowT.dydx ¬ windowT.dxdy ¬ 0; cx ¬ (cxmin+cxmax)/2; cy ¬ (cymin+cymax)/2; dx ¬ bl.viewer.cw/2; dy ¬ bl.viewer.ch/2; outline ¬ windowT.FromTrans[].TransformRectangle[[cx-dx, cy-dy, 2*dx, 2*dy]]; first ¬ Geom2D.Displace[viewMouse.Sub[viewCenter], outline]; ViewerOps.PaintViewer[ viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: NewRect[first] ]; next ¬ first; END; ThumbTrack: TrackProc = BEGIN new: Rect ¬ Geom2D.Displace[viewMouse.Sub[viewCenter], outline]; ViewerOps.PaintViewer[viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: RectDiff[new, next]]; next ¬ new; END; ThumbFinish: FinishProc = { ViewerOps.PaintViewer[viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: NewRect[next]]; }; ThumbTransform: NewTransformProc ~ { bs.class.common.bsUserAction[bs, LIST[$AlignFracs, NEW [Vec ¬ [viewMouse.x/bl.viewer.cw, viewMouse.y/bl.viewer.ch]], NEW [Vec ¬ [0.5, 0.5]], $TRUE, $TRUE]]}; arWidth: REAL ¬ 0.1; arHalfWidth: REAL ¬ arWidth/2; arHeadLen: REAL ¬ 0.4; rootTwo: REAL ¬ RealFns.SqRt[2.0]; rootHalf: REAL ¬ RealFns.SqRt[0.5]; MakeArrow: PROC [from, to: Vec, also: Paths] RETURNS [alles: Paths] = { first: BOOL ¬ TRUE; p: ImagerPath.Trajectory ¬ NIL; diff: Vec; t: Transform; Do: PROC [x, y: REAL] = { v: Vec ¬ t.Transform[[x, y]]; p ¬ IF first THEN ImagerPath.MoveTo[v] ELSE ImagerPath.LineTo[p, v]; first ¬ FALSE; }; [from, to, ] ¬ AdjustScroll[from, to]; diff ¬ from.Sub[to]; t ¬ Geom2D.id .PostRotate[RealFns.ArcTanDeg[diff.y, diff.x] - 90] .PostScale[diff.Length[]] .PostTranslate[to]; Do[arHalfWidth, 1.0]; Do[arHalfWidth, arWidth*rootTwo+arHalfWidth]; Do[(arHeadLen - arWidth)*rootHalf, (arHeadLen + arWidth)*rootHalf]; Do[arHeadLen*rootHalf, arHeadLen*rootHalf]; Do[0, 0]; Do[-arHeadLen*rootHalf, arHeadLen*rootHalf]; Do[(arWidth-arHeadLen)*rootHalf, (arWidth+arHeadLen)*rootHalf]; Do[-arHalfWidth, arWidth*rootTwo+arHalfWidth]; Do[-arHalfWidth, 1.0]; alles ¬ NEW [PathsRep ¬ [next: also, path: p, op: fill]]; }; AdjustScroll: PROC [from, to: Vec] RETURNS [ delta: Vec ¬ to.Sub[from]; adx: Number ¬ ABS[delta.x]; ady: Number ¬ ABS[delta.y]; IF changed ¬ (adx < 2 OR adx < ady/10) THEN delta.x ¬ 0 ELSE IF changed ¬ (ady < 2 OR ady < adx/10) THEN delta.y ¬ 0; }; scrollDown: Vec ¬ [0, 0]; oldArrow: Paths; ScrollFrom: PROC [viewMouse, viewCenter: Vec, data: REF ANY] RETURNS [from: Vec] = {from ¬ SELECT data FROM $toCenter => viewMouse, $alongClicks => scrollDown, ENDCASE => ERROR}; ScrollTo: PROC [viewMouse, viewCenter: Vec, data: REF ANY] RETURNS [to: Vec] = {to ¬ SELECT data FROM $toCenter => viewCenter, $alongClicks => viewMouse, ENDCASE => ERROR}; ScrollStart: StartProc = { scrollDown ¬ viewMouse; oldArrow ¬ NIL; }; ScrollTrack: TrackProc = { ViewerOps.PaintViewer[ viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: oldArrow ¬ MakeArrow[ ScrollFrom[viewMouse, viewCenter, data], ScrollTo[viewMouse, viewCenter, data], oldArrow] ]; oldArrow.next ¬ NIL; }; ScrollFinish: FinishProc = { IF oldArrow # NIL THEN ViewerOps.PaintViewer[viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: oldArrow]; }; ScrollCursor: CursorProc = BEGIN d: Vec ¬ Vector2.Sub[ ScrollTo[viewMouse, viewCenter, data], ScrollFrom[viewMouse, viewCenter, data]]; adx: Number ¬ ABS[d.x]; ady: Number ¬ ABS[d.y]; RETURN [IF d = [0, 0] THEN Cursors.CursorType[bullseye] ELSE IF adx < 2 OR adx < ady/10 THEN (IF d.y < 0 THEN pointDown ELSE pointUp) ELSE IF ady < 2 OR ady < adx/10 THEN (IF d.x < 0 THEN pointLeft ELSE pointRight) ELSE IF d.x > 0 THEN (IF d.y > 0 THEN pointUpRight ELSE pointDownRight) ELSE (IF d.y > 0 THEN pointUpLeft ELSE pointDownLeft)]; END; ScrollTransform: NewTransformProc ~ { from, to: Vec; [from, to, ] ¬ AdjustScroll[ ScrollFrom[viewMouse, viewCenter, data], ScrollTo[viewMouse, viewCenter, data]]; bs.class.common.bsUserAction[bs, LIST[$Shift, NEW [Vec ¬ to.Sub[from]]]]; RETURN}; slope: Number; scaleCenter: Vec; ScaleRandomStart: StartProc = BEGIN viewer: Viewer ¬ bs.style.QuaViewer[bs, TRUE]; slope ¬ IF viewer.cw = 0 THEN 1 ELSE REAL[viewer.ch]/viewer.cw; scaleCenter ¬ viewMouse; next ¬ first ¬ [viewMouse.x, viewMouse.y, 0, 0]; END; ScaleRandomTrack: TrackProc = BEGIN d: Vec ¬ viewMouse.Sub[scaleCenter]; adx: Number ¬ ABS[d.x]; ady: Number ¬ IF bs.class.common.mayStretch THEN ABS[d.y] ELSE adx*slope; new: Rect ¬ [scaleCenter.x-adx, scaleCenter.y-ady, 2*adx, 2*ady]; ViewerOps.PaintViewer[viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: RectDiff[new, next]]; next ¬ new; END; ScaleRandomFinish: FinishProc = { ViewerOps.PaintViewer[viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: NewRect[next]]; }; ScaleRandomTransform: NewTransformProc ~ { viewer: Viewer ¬ bs.style.QuaViewer[bs, TRUE]; fromA, toA: Rect; SELECT data FROM $Expand => {fromA ¬ next; toA ¬ [0, 0, viewer.cw, viewer.ch]}; $Contract => {toA ¬ next; fromA ¬ [0, 0, viewer.cw, viewer.ch]}; ENDCASE => ERROR; bs.class.common.bsUserAction[bs, LIST[$BoxScale, NEW[Rect ¬ fromA], NEW[Rect ¬ toA], IF diff THEN $FALSE ELSE $TRUE]]; }; TextCursor: CursorProc = {RETURN [textPointer]}; AdjustBiScroller: PROC [self: Viewer] RETURNS [adjusted: BOOL ¬ FALSE] --ViewerClasses.AdjustProc-- ~ { bs: BiScroller ~ NARROW[self.data]; bl: Buttonless ~ NARROW[bs.rep]; IF adjusted ¬ bl.initialized AND (self.cw # bl.cw OR self.ch # bl.ch) THEN { client: Vec --the point that stays fixed, in client coords--; client ¬ bl.u.Transform[[ bs.class.common.preserve[X]*bl.cw, bs.class.common.preserve[Y]*bl.ch]]; Align[ bs: bs, client: [coord[client.x, client.y]], viewer: [fraction[bs.class.common.preserve[X], bs.class.common.preserve[Y]]], paint: FALSE]; bl.cw ¬ self.cw; bl.ch ¬ self.ch; }; IF bs.class.common.adjust#NIL AND bs.class.common.adjust[self] THEN adjusted ¬ TRUE; RETURN}; PaintBiScroller: PROC [self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOL ¬ FALSE] --ViewerClasses.PaintProc-- = BEGIN bs: BiScroller ¬ NARROW[self.data]; bl: Buttonless ¬ NARROW[bs.rep]; IF whatChanged # NIL THEN WITH whatChanged SELECT FROM as: Rects => { Imager.SetColor[context, invertingGray]; FOR a: Rects ¬ as, a.next WHILE a # NIL DO Imager.MaskRectangle[context, a.rect]; ENDLOOP; RETURN [FALSE]; }; ps: Paths => { Imager.SetColor[context, invertingGray]; FOR p: Paths ¬ ps, p.next WHILE p # NIL DO SELECT p.op FROM fill => Imager.MaskFillTrajectory[context, p.path]; stroke => Imager.MaskStrokeTrajectory[context, p.path]; ENDCASE => ERROR; ENDLOOP; RETURN [FALSE]; }; ENDCASE; Imager.ConcatT[context, bl.t]; quit ¬ bs.class.common.paint[self, context, whatChanged, clear]; END; invertingGray: Imager.Color ¬ ImagerBackdoor.MakeStipple[0A5A5H, TRUE]; RI: PROC [REAL] RETURNS [INT] = Real.Round; GetTransforms: PROC [bs: BiScroller, age: TransformsAge ¬ current] RETURNS [clientToViewer, viewerToClient: Transform] = { bl: Buttonless ¬ NARROW[bs.rep]; SELECT age FROM current => RETURN [clientToViewer: bl.t, viewerToClient: bl.u]; previous => RETURN [clientToViewer: bl.tp, viewerToClient: bl.up]; ENDCASE => ERROR; }; ChangeTransform: PROC [bs: BiScroller, new: Transform, ageOp: AgeOp, paint: BOOL ¬ TRUE] = BEGIN bl: Buttonless ¬ NARROW[bs.rep]; inv: Transform; Doit: ENTRY PROC = { ENABLE UNWIND => NULL; SELECT ageOp FROM remember => {bl.up ¬ bl.u; bl.tp ¬ bl.t}; ignore => NULL; ENDCASE => ERROR; bl.u ¬ inv; bl.t ¬ new; }; IF bs.class.common.offsetsMustBeIntegers THEN new ¬ new.TranslateTo[[RI[new.c], RI[new.f]]]; IF new.a*new.e - new.b*new.d = 0 THEN RETURN; inv ¬ new.Invert[]; FOR c: Child ¬ bl.children, c.next UNTIL c = NIL DO nu: Vec; nu ¬ new.Transform[c.where]; IF paint THEN ViewerOps.EstablishViewerPosition[c.it, RI[nu.x], RI[nu.y], c.it.ww, c.it.wh] ELSE SetViewerPosition[c.it, RI[nu.x], RI[nu.y], c.it.ww, c.it.wh]; ENDLOOP; Doit[]; IF paint THEN ViewerOps.PaintViewer[viewer: bl.viewer, hint: client]; END; Squeeze: PROC [fromMin, fromMax, toMin, toMax: Number] RETURNS [scale, offset: Number] = BEGIN dFrom: Number ¬ fromMax - fromMin; IF dFrom = 0 THEN RETURN [1, (toMin+toMax)/2]; scale ¬ (toMax - toMin)/dFrom; offset ¬ toMin - scale*fromMin; END; NewRect: PROC [a: Rect, next: Rects ¬ NIL] RETURNS [Rects] = {RETURN [NEW [RectsRec ¬ [next: next, rect: a]]]}; NewBox: PROC [b: ImagerBox.Box, next: Rects ¬ NIL] RETURNS [Rects] = {RETURN [NEW [RectsRec ¬ [next: next, rect: ImagerBox.RectangleFromBox[b]]]]}; RectDiff: PROC [a, b: Rect] RETURNS [d: Rects] = BEGIN y: Number; A, B: ImagerBox.Box; IF b.y < a.y THEN {c: Rect ¬ a; a ¬ b; b ¬ c}; A ¬ ImagerBox.BoxFromRectangle[a]; B ¬ ImagerBox.BoxFromRectangle[b]; IF (IF A.ymax <= B.ymin THEN TRUE ELSE IF A.xmin >= B.xmax THEN TRUE ELSE IF A.xmax <= B.xmin THEN TRUE ELSE FALSE) THEN RETURN [NewRect[a, NewRect[b]]]; IF A.ymax > B.ymax THEN d ¬ NewBox[[A.xmin, y ¬ B.ymax, A.xmax, A.ymax]] ELSE d ¬ NewBox[[B.xmin, y ¬ A.ymax, B.xmax, B.ymax]]; RETURN [NewBox[[B.xmin, B.ymin, A.xmin, y], NewBox[[B.xmax, B.ymin, A.xmax, y], NewBox[[A.xmin, A.ymin, A.xmax, B.ymin], d]]]]; END; Setup: PROCEDURE = BEGIN pointUpRight ¬ MultiCursors.NewCursor[hotX: -15, hotY: 0, bits: [ 000037B, 000177B, 000777B, 001477B, 002077B, 000176B, 000346B, 000704B, 001614B, 003410B, 007020B, 016000B, 034000B, 070000B, 160000B, 140000B]]; pointUpLeft ¬ MultiCursors.NewCursor[hotX: 0, hotY: 0, bits: [ 174000B, 177000B, 177600B, 176300B, 176040B, 077000B, 063400B, 021600B, 030700B, 010340B, 004160B, 000070B, 000034B, 000016B, 000007B, 000003B]]; pointDownRight ¬ MultiCursors.NewCursor[hotX: -15, hotY: -15, bits: [ 140000B, 160000B, 070000B, 034000B, 016000B, 007020B, 003410B, 001614B, 000704B, 000346B, 000176B, 002077B, 001477B, 000777B, 000177B, 000037B]]; pointDownLeft ¬ MultiCursors.NewCursor[hotX: 0, hotY: -15, bits: [ 000003B, 000007B, 000016B, 000034B, 000070B, 004160B, 010340B, 030700B, 021600B, 063400B, 077000B, 176040B, 176300B, 177600B, 177000B, 174000B]]; mice[Thumbing] ¬ NEW [MouseRec ¬ [ start: ThumbStart, track: ThumbTrack, finish: ThumbFinish, cursor: TextCursor, newTransform: ThumbTransform]]; mice[ScrollToCentering] ¬ NEW [MouseRec ¬ [ start: ScrollStart, track: ScrollTrack, finish: ScrollFinish, cursor: ScrollCursor, newTransform: ScrollTransform, data: $toCenter]]; mice[ScrollAlongClicksing] ¬ NEW [MouseRec ¬ [ start: ScrollStart, track: ScrollTrack, finish: ScrollFinish, cursor: ScrollCursor, newTransform: ScrollTransform, data: $alongClicks]]; mice[Expanding] ¬ NEW [MouseRec ¬ [ start: ScaleRandomStart, track: ScaleRandomTrack, finish: ScaleRandomFinish, cursor: TextCursor, newTransform: ScaleRandomTransform, data: $Expand]]; mice[Contracting] ¬ NEW [MouseRec ¬ [ start: ScaleRandomStart, track: ScaleRandomTrack, finish: ScaleRandomFinish, cursor: TextCursor, newTransform: ScaleRandomTransform, data: $Contract]]; RegisterStyle["Buttonless", buttonlessStyle]; END; Setup[]; END.