<> <> <> <<>> <> DIRECTORY BiScrollers, Cursors, Geom2D, Imager, ImagerBackdoor, ImagerBox, ImagerPath, ImagerTransformation, InputFocus, Real, RealFns, TIPUser, ViewerClasses, Vector2, ViewerOps; BiScrollersButtonless: CEDAR MONITOR IMPORTS BiScrollers, Cursors, Geom2D, Imager, ImagerBackdoor, ImagerBox, ImagerPath, ImagerTransformation, InputFocus, Real, RealFns, 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; 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; NewBiScrollerClass: PROC [cc: ClassCommon] RETURNS [bsc: BiScrollerClass] = BEGIN viewerClass: ViewerClass; t: TIPUser.TIPTable _ TIPUser.InstantiateNewTIPTable["BiScroller.tip"]; IF cc.vanilla = NIL THEN cc.vanilla _ GenID; IF cc.bsUserAction = NIL THEN cc.bsUserAction _ DoBSUserAction; IF cc.tipTable # NIL THEN BEGIN t.mouseTicks _ MIN[t.mouseTicks, cc.tipTable.mouseTicks]; t.opaque _ FALSE; t.link _ cc.tipTable; END; 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] --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 WasIdle: ENTRY PROC RETURNS [BOOLEAN] = {oldState _ bl.state; IF bl.state = Normal THEN RETURN [TRUE]; DoSleep[bl]; RETURN [FALSE]}; oldState: State; 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; 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 _ Cursors.NewCursor[hotX: -15, hotY: 0, bits: [ 000037B, 000177B, 000777B, 001477B, 002077B, 000176B, 000346B, 000704B, 001614B, 003410B, 007020B, 016000B, 034000B, 070000B, 160000B, 140000B]]; pointUpLeft _ Cursors.NewCursor[hotX: 0, hotY: 0, bits: [ 174000B, 177000B, 177600B, 176300B, 176040B, 077000B, 063400B, 021600B, 030700B, 010340B, 004160B, 000070B, 000034B, 000016B, 000007B, 000003B]]; pointDownRight _ Cursors.NewCursor[hotX: -15, hotY: -15, bits: [ 140000B, 160000B, 070000B, 034000B, 016000B, 007020B, 003410B, 001614B, 000704B, 000346B, 000176B, 002077B, 001477B, 000777B, 000177B, 000037B]]; pointDownLeft _ Cursors.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.