<Viewing>BiScrollers.DF>> <> <<>> <> DIRECTORY Cursors, TIPUser, Icons, InputFocus, ViewerClasses, ViewerOps, Graphics, Geom2D, BiScrollers, RealFns, Real; BiScrollersImpl: CEDAR MONITOR IMPORTS Graphics, Cursors, TIPUser, InputFocus, VO:ViewerOps, Geom2D, Real EXPORTS BiScrollers = BEGIN OPEN BiScrollers; Number: TYPE = Geom2D.Number; ViewerClass: TYPE = ViewerClasses.ViewerClass; Axis: TYPE = Geom2D.Axis; Vec: TYPE = Geom2D.Vec; Area: TYPE = Geom2D.Area; AreaRef: TYPE = REF Area; BiScroller: PUBLIC TYPE = REF BiScrollerObject; BiScrollerObject: PUBLIC TYPE = RECORD [ class: BiScrollerClass, 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 cw, ch: INTEGER _ 0 --last time we looked in the viewer-- ]; BiScrollerClass: PUBLIC TYPE = REF BiScrollerClassRec; BiScrollerClassRec: PUBLIC TYPE = RECORD [ viewerClass: ViewerClass, notify: ViewerClasses.NotifyProc, paint: ViewerClasses.PaintProc, extrema: ExtremaProc, init: ViewerClasses.InitProc, finish: LIST OF REF ANY, cursor: Cursors.CursorType, mayStretch: BOOLEAN]; 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]; StartProc: TYPE = PROC [mv, bc: Vec, bs: BiScroller] RETURNS [Area, BOOLEAN]; FinishProc: TYPE = PROC [bs: BiScroller] RETURNS [Areas]; CursorProc: TYPE = PROC [mv, bc: Vec] RETURNS [Cursors.CursorType]; TrackProc: TYPE = PROC [mv, bc: Vec, bs: BiScroller] RETURNS [Area]; NewTransformProc: TYPE = PROC [old: Transform, mv, bc: Vec, bs: BiScroller] RETURNS [new: Transform]; Areas: TYPE = REF AreasRec; AreasRec: TYPE = RECORD [next: Areas, area: Area]; State: TYPE = {Normal, MovingWindow, ToCentering, ScaleDoubling, ScaleHalving, ScaleRandoming, ResettingScale, Centering, Lefting, Righting, Topping, Bottoming, ResettingAndCentering, Client}; ActiveState: TYPE = State[MovingWindow..Client]; awake: BOOLEAN _ FALSE; state: State _ Normal; mice: ARRAY ActiveState OF Mouse _ ALL[NIL]; resetScaleCursor, centerCursor, resetAllCursor, pointUpRight, pointUpLeft, pointDownRight, pointDownLeft: Cursors.CursorType; NewBiScrollerClass: PUBLIC PROC [ flavor: ATOM, extrema: ExtremaProc, notify: ViewerClasses.NotifyProc _ NIL, paint: ViewerClasses.PaintProc _ NIL, modify: ViewerClasses.ModifyProc _ NIL, destroy: ViewerClasses.DestroyProc _ NIL, copy: ViewerClasses.CopyProc _ NIL, set: ViewerClasses.SetProc _ NIL, get: ViewerClasses.GetProc _ NIL, init: ViewerClasses.InitProc _ NIL, finish: LIST OF REF ANY _ NIL, save: ViewerClasses.SaveProc _ NIL, tipTable: TIPUser.TIPTable _ NIL, icon: Icons.IconFlavor _ document, cursor: Cursors.CursorType _ textPointer, mayStretch: BOOLEAN] RETURNS [bsc: BiScrollerClass] = BEGIN vc: ViewerClass; t: TIPUser.TIPTable _ TIPUser.InstantiateNewTIPTable["BiScroller.TIP"]; IF tipTable # NIL THEN BEGIN t.mouseTicks _ MIN[t.mouseTicks, tipTable.mouseTicks]; t.opaque _ FALSE; t.link _ tipTable; END; vc _ NEW [ViewerClasses.ViewerClassRec _ [ flavor: flavor, notify: NotifyBiScroller, paint: PaintBiScroller, modify: modify, destroy: destroy, copy: copy, set: set, get: get, save: save, tipTable: t, icon: icon, cursor: cursor]]; VO.RegisterViewerClass[flavor: flavor, class: vc]; bsc _ NEW [BiScrollerClassRec _ [ viewerClass: vc, notify: notify, paint: paint, extrema: extrema, init: init, finish: finish, cursor: cursor, mayStretch: mayStretch]]; END; CreateBiScroller: PUBLIC PROC [class: BiScrollerClass, info: ViewerClasses.ViewerRec _ [], paint: BOOLEAN _ TRUE] RETURNS [new: BiScroller] = BEGIN paintFirst: BOOL = paint AND info.iconic; new _ NEW[BiScrollerObject _ [class: class, clientData: info.data, t: Geom2D.id]]; info.data _ new; new.viewer _ VO.CreateViewer[flavor: class.viewerClass.flavor, info: info, paint: paintFirst]; new.t _ Geom2D.id.Translate[new.viewer.cw/2, new.viewer.ch/2]; new.u _ new.t.Inverse[]; IF class.init # NIL THEN class.init[new.viewer]; IF NOT paintFirst THEN VO.ComputeColumn[column: VO.ViewerColumn[new.viewer], paint: paint]; END; Destroy: PUBLIC PROC [bs: BiScroller] RETURNS [BiScroller] = BEGIN VO.DestroyViewer[bs.viewer]; bs.viewer _ NIL; RETURN [NIL]; END; AddChild: PUBLIC PROC [to: BiScroller, what: Viewer, x, y: REAL _ 0, useTheseCoords: BOOLEAN _ FALSE, paint: BOOLEAN _ TRUE] = BEGIN my: Vec; c: Child _ NEW [ChildObject _ [next: to.children, it: what, where: IF useTheseCoords THEN [x, y] ELSE [what.wx, what.wy] ]]; to.children _ c; my _ to.t.MapVec[c.where]; VO.MoveViewer[viewer: what, x: RI[my.x], y: RI[my.y], w: what.ww, h: what.wh, paint: paint]; END; DeleteChild: PUBLIC PROC [of: BiScroller, who: Viewer] = BEGIN 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]}; of.children _ Filter[of.children]; END; IsBiScroller: PUBLIC PROC [ra: REF ANY] RETURNS [BOOLEAN] = {RETURN [ISTYPE[ra, BiScroller]]}; NarrowToBiScroller: PUBLIC PROC [ra: REF ANY] RETURNS [BiScroller] = {RETURN [NARROW[ra]]}; QuaViewer: PUBLIC PROC [bs: BiScroller] RETURNS [Viewer] = {RETURN [bs.viewer]}; QuaBiScroller: PUBLIC PROC [v: Viewer] RETURNS [BiScroller] = {RETURN [NARROW[v.data]]}; ViewerIsABiScroller: PUBLIC PROC [v: Viewer] RETURNS [BOOLEAN] = {RETURN [ISTYPE[v.data, BiScroller]]}; ClientDataOf: PUBLIC PROC [bs: BiScroller] RETURNS [REF ANY] = {RETURN [bs.clientData]}; BoundaryOf: PUBLIC PROC [bs: BiScroller] RETURNS [VecList] = BEGIN RETURN [Geom2D.MapVecs[bs.u, LIST[[0, 0], [bs.viewer.cw, 0], [bs.viewer.cw, bs.viewer.ch], [0, bs.viewer.ch]]]]; END; BoundaryExtrema: PUBLIC PROC [bs: BiScroller, direction: Vec] RETURNS [min, max: Vec] = BEGIN vl: VecList _ BoundaryOf[bs]; e: Geom2D.ExtremaRec _ direction.Extreme[vl.first, direction.Extreme[vl.rest.first, direction.Extreme[vl.rest.rest.first, direction.StartExtreme[vl.rest.rest.rest.first]]]]; RETURN [e.minV, e.maxV]; END; NotifyClient: PROC [bs: BiScroller, input: LIST OF REF ANY, giveUp: BOOLEAN] = BEGIN i, o, l: LIST OF REF ANY _ NIL; IF giveUp THEN GiveUp[bs]; IF bs.class.notify = NIL THEN RETURN; 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 => {cc: ClientCoords _ NEW [Vec _ bs.u.MapVec[[z.mouseX, z.mouseY]]]; l.first _ cc}; ENDCASE; ENDLOOP; {bs.class.notify[bs.viewer, o]}; END; NotifyBiScroller: ViewerClasses.NotifyProc = BEGIN ENABLE UNWIND => {InputFocus.ReleaseButtons[]; awake _ FALSE}; bs: BiScroller _ NARROW[self.data]; IF input = NIL THEN NotifyClient[bs, input, TRUE] ELSE SELECT input.first FROM $BSscaleRandom => Work[bs, ScaleRandoming, input.rest]; $BSscaleDouble => Work[bs, ScaleDoubling, input.rest]; $BSscaleHalve => Work[bs, ScaleHalving, input.rest]; $BSresetScale => Work[bs, ResettingScale, input.rest]; $BSmoveWindow => Work[bs, MovingWindow, input.rest]; $BStoCenter => Work[bs, ToCentering, input.rest]; $BScenter => Work[bs, Centering, input.rest]; $BSresetCenter => Work[bs, ResettingAndCentering, input.rest]; $BSleft => Work[bs, Lefting, input.rest]; $BSright => Work[bs, Righting, input.rest]; $BStop => Work[bs, Topping, input.rest]; $BSbottom => Work[bs, Bottoming, input.rest]; $BSClient => Work[bs, Client, input.rest]; $BSClientEnd => {Work[bs, Client, input.rest]; Sleep[bs]}; $BSgiveUp => GiveUp[bs]; $BSeatIt => NULL; ENDCASE => NotifyClient[bs, input, TRUE]; END; GiveUp: PROCEDURE [bs: BiScroller] = BEGIN Hello: ENTRY PROC RETURNS [BOOLEAN] = {oldState _ state; IF NOT awake THEN RETURN [TRUE]; DoSleep[bs]; RETURN [FALSE]}; oldState: State; IF Hello[] THEN RETURN; IF oldState = Normal THEN RETURN; Finish[bs, mice[oldState]]; END; first, next: Area; Finish: PROC [bs: BiScroller, mouse: Mouse] = BEGIN IF mouse.finish # NIL THEN BEGIN changed: Areas _ mouse.finish[bs]; IF changed # NIL THEN VO.PaintViewer[ viewer: bs.viewer, hint: client, clearClient: FALSE, whatChanged: changed]; END; END; Work: PROC [bs: BiScroller, why: ActiveState, input: LIST OF REF ANY] = BEGIN mv, bc: Vec; at: TIPUser.TIPScreenCoords _ NARROW[input.first]; who: Mouse _ mice[why]; bc _ [bs.viewer.cw/2, bs.viewer.ch/2]; IF NOT WakeUp[bs] THEN BEGIN v: Viewer; c: BOOLEAN; [v, c] _ VO.MouseInViewer[at]; IF v # bs.viewer OR NOT c THEN {Sleep[bs]; Finish[bs, who]; RETURN}; END; mv _ [at.mouseX, at.mouseY]; IF why # state THEN BEGIN IF state IN ActiveState THEN Finish[bs, mice[state]]; state _ why; IF who.start # NIL THEN BEGIN paint: BOOLEAN; [first, paint] _ who.start[mv, bc, bs]; IF paint THEN VO.PaintViewer[viewer: bs.viewer, hint: client, clearClient: FALSE, whatChanged: NewArea[first]]; next _ first; END END; IF who.cursor # NIL THEN Cursors.SetCursor[bs.viewer.class.cursor _ who.cursor[mv, bc]]; IF who.track # NIL THEN BEGIN new: Area _ who.track[mv, bc, bs]; VO.PaintViewer[viewer: bs.viewer, hint: client, clearClient: FALSE, whatChanged: AreaDiff[new, next]]; next _ new; END; IF why = Client THEN NotifyClient[bs, input, FALSE] ELSE IF input.rest.first = $Doit THEN BEGIN Sleep[bs]; oldT _ bs.t; bcUsed _ bc; ChangeTransform[bs, nuT _ who.newTransform[bs.t, mv, bc, bs]]; END ELSE IF input.rest.first # $Idle THEN ERROR; END; oldT, nuT: Transform; bcUsed: Vec; WakeUp: ENTRY PROC [bs: BiScroller] RETURNS [BOOLEAN] = BEGIN IF awake THEN RETURN [FALSE]; {InputFocus.CaptureButtons[proc: bs.viewer.class.notify, tip: bs.viewer.tipTable, viewer: bs.viewer]}; RETURN [awake _ TRUE]; END; Sleep: ENTRY PROC [bs: BiScroller] = {DoSleep[bs]}; DoSleep: PROC [bs: BiScroller] = BEGIN awake _ FALSE; state _ Normal; InputFocus.ReleaseButtons[]; Cursors.SetCursor[bs.viewer.class.cursor _ bs.class.cursor]; END; windowT: Transform; MoveWindowStart: StartProc = BEGIN cxmin, cxmax, cymin, cymax: Number; cx, cy, dx, dy: Number; [cxmin, cxmax] _ GetLimits[bs, X]; [cymin, cymax] _ GetLimits[bs, Y]; [windowT.a, windowT.e] _ Squeeze[cxmin, cxmax, 0, bs.viewer.cw]; [windowT.d, windowT.f] _ Squeeze[cymin, cymax, 0, bs.viewer.ch]; windowT.b _ windowT.c _ 0; cx _ (cxmin+cxmax)/2; cy _ (cymin+cymax)/2; dx _ bs.viewer.cw/2; dy _ bs.viewer.ch/2; RETURN [windowT.MapArea[[cx-dx, cy-dy, cx+dx, cy+dy]], TRUE]; END; MoveWindowTrack: TrackProc = BEGIN RETURN [mv.Minus[bc].Displace[first]]; END; MoveWindowFinish: FinishProc = {RETURN [AreaDiff[next, first]]}; MoveWindowTransform: NewTransformProc = BEGIN u: Transform _ windowT.Inverse[]; RETURN [old.TranslateV[Geom2D.Minus[[bs.viewer.cw/2, bs.viewer.ch/2], u.MapVec[mv]]]]; END; Indicate: PROC [a, b: Vec, w, h: REAL] RETURNS [Area] = BEGIN adx: Number _ ABS[a.x - b.x]; ady: Number _ ABS[a.y - b.y]; IF a.Minus[b] = [0, 0] THEN RETURN [[a.x, a.y, a.x, a.y]]; IF adx < 2 THEN RETURN [SortArea[[0, a.y, w, b.y]]]; IF adx < ady/10 THEN RETURN [SortArea[[0, a.y, w, b.y]]]; IF ady < 2 THEN RETURN [SortArea[[a.x, 0, b.x, h]]]; IF ady < adx/10 THEN RETURN [SortArea[[a.x, 0, b.x, h]]]; RETURN [SortArea[[a.x, a.y, b.x, b.y]]]; END; SortArea: PROC [in: Area] RETURNS [out: Area] = INLINE {RETURN [[MIN[in.xmin, in.xmax], MIN[in.ymin, in.ymax], MAX[in.xmin, in.xmax], MAX[in.ymin, in.ymax]]]}; PtToCenterStart: StartProc = {RETURN [Indicate[mv, bc, bs.viewer.cw, bs.viewer.ch], TRUE]}; PtToCenterTrack: TrackProc = {RETURN [Indicate[mv, bc, bs.viewer.cw, bs.viewer.ch]]}; PtToCenterFinish: FinishProc = {RETURN [NewArea[next]]}; PtToCenterCursor: CursorProc = BEGIN d: Vec _ bc.Minus[mv]; 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; PtToCenterTransform: NewTransformProc = BEGIN delta: Vec _ bc.Minus[mv]; adx: Number _ ABS[delta.x]; ady: Number _ ABS[delta.y]; IF adx < 2 OR adx < ady/10 THEN delta.x _ 0 ELSE IF ady < 2 OR ady < adx/10 THEN delta.y _ 0; RETURN [old.TranslateV[delta]]; END; ScaleDoubleCursor: CursorProc = {RETURN [pointUp]}; ScaleDoubleTransform: NewTransformProc = {RETURN [old.Translate[-bc.x, -bc.y].ScaleT[2].TranslateV[bc]]}; ScaleHalveCursor: CursorProc = {RETURN [pointDown]}; ScaleHalveTransform: NewTransformProc = {RETURN [old.Translate[-bc.x, -bc.y].ScaleT[1.0/2.0].TranslateV[bc]]}; ScaleRandomStart: StartProc = BEGIN d: Vec _ mv.Minus[bc]; adx: Number _ ABS[d.x]; ady: Number _ ABS[d.y]; slope _ IF adx = 0 THEN 1 ELSE ady/adx; RETURN [[bc.x-adx, bc.y-ady, bc.x+adx, bc.y+ady], FALSE]; END; slope: Number; ScaleRandomTrack: TrackProc = BEGIN d: Vec _ mv.Minus[bc]; adx: Number _ ABS[d.x]; ady: Number _ IF bs.class.mayStretch THEN ABS[d.y] ELSE adx*slope; RETURN [[bc.x-adx, bc.y-ady, bc.x+adx, bc.y+ady]]; END; ScaleRandomFinish: FinishProc = {RETURN [AreaDiff[next, first]]}; ScaleRandomTransform: NewTransformProc = BEGIN ndx, ndy, odx, ody, sx, sy: Number; ndx _ next.xmax - next.xmin; ndy _ next.ymax - next.ymin; odx _ first.xmax - first.xmin; ody _ first.ymax - first.ymin; sx _ IF ndx=0 OR odx=0 THEN 1 ELSE ndx/odx; sy _ IF ndy=0 OR ody=0 THEN 1 ELSE ndy/ody; IF NOT bs.class.mayStretch THEN sx _ sy _ (sx+sy)/2; RETURN [old.Translate[-bc.x, -bc.y].ScaleTXY[sx, sy].TranslateV[bc]]; END; ResetScaleCursor: CursorProc = {RETURN [resetScaleCursor]}; ResetScaleTransform: NewTransformProc = {s, ne, nf: Number; new _ old.Translate[-bc.x, -bc.y]; s _ new.a*new.d - new.b*new.c; IF ABS[s] = 0 THEN RETURN [old]; ne _ (new.e*new.d-new.f*new.c)/s; nf _ (new.f*new.a-new.e*new.b)/s; new _ [1, 0, 0, 1, ne, nf]; new _ new.TranslateV[bc]}; CenterCursor: CursorProc = {RETURN [centerCursor]}; CenterTransform: NewTransformProc = BEGIN xmin, xmax, ymin, ymax: Number; [xmin, xmax] _ GetLimits[bs, X]; [ymin, ymax] _ GetLimits[bs, Y]; RETURN [old.Translate[bc.x - (xmin+xmax)/2, bc.y - (ymin+ymax)/2]]; END; LeftingCursor: CursorProc = {RETURN [scrollLeft]}; LeftTransform: NewTransformProc = BEGIN xmin: Number; [xmin, ] _ GetLimits[bs, X]; RETURN [old.Translate[-xmin, 0]]; END; BottomingCursor: CursorProc = {RETURN [scrollDown]}; BottomTransform: NewTransformProc = BEGIN ymin: Number; [ymin, ] _ GetLimits[bs, Y]; RETURN [old.Translate[0, -ymin]]; END; RightingCursor: CursorProc = {RETURN [scrollRight]}; RightTransform: NewTransformProc = BEGIN xmax: Number; [, xmax] _ GetLimits[bs, X]; RETURN [old.Translate[bs.viewer.cw - xmax, 0]]; END; ToppingCursor: CursorProc = {RETURN [scrollUp]}; TopTransform: NewTransformProc = BEGIN ymax: Number; [, ymax] _ GetLimits[bs, Y]; RETURN [old.Translate[0, bs.viewer.ch - ymax]]; END; ResetCenterCursor: CursorProc = {RETURN [resetAllCursor]}; ResetCenterTransform: NewTransformProc = BEGIN xmin, xmax, ymin, ymax: Vec; [xmin, xmax] _ bs.class.extrema[bs.clientData, [1, 0]]; [ymin, ymax] _ bs.class.extrema[bs.clientData, [0, 1]]; RETURN [Geom2D.id.TranslateV[bc.Minus[ [(xmin.x+xmax.x)/2, (ymin.y+ymax.y)/2]]]]; END; TextCursor: CursorProc = {RETURN [textPointer]}; ClientFinish: FinishProc = {NotifyClient[bs, bs.class.finish, FALSE]; RETURN [NIL]}; PaintBiScroller: ViewerClasses.PaintProc = BEGIN bs: BiScroller _ NARROW[self.data]; IF (IF whatChanged = NIL THEN FALSE ELSE ISTYPE[whatChanged, Areas]) THEN BEGIN Graphics.SetStipple[context, 122645B]; [] _ Graphics.SetPaintMode[context, invert]; FOR a: Areas _ NARROW[whatChanged], a.next WHILE a # NIL DO Graphics.DrawBox[context, a.area]; ENDLOOP; RETURN; END; Graphics.Translate[context, bs.t.e, bs.t.f]; Graphics.Concat[context, bs.t.a, bs.t.b, bs.t.c, bs.t.d]; bs.class.paint[self, context, whatChanged, clear]; END; GetLimits: PROC [bs: BiScroller, axis: Axis] RETURNS [pmin, pmax: Number] = BEGIN norm, min, max: Vec; SELECT axis FROM X => norm _ [bs.t.d, -bs.t.b]; Y => norm _ [-bs.t.c, bs.t.a]; ENDCASE => ERROR; [min, max] _ bs.class.extrema[bs.clientData, norm]; min _ bs.t.MapVec[min]; max _ bs.t.MapVec[max]; SELECT axis FROM X => RETURN [min.x, max.x]; Y => RETURN [min.y, max.y]; ENDCASE => ERROR; END; Fit: PUBLIC PROC [bs: BiScroller, paint: BOOLEAN _ TRUE, mayStretch: BOOLEAN _ FALSE] = BEGIN new: Transform _ Geom2D.id; cxmin, cxmax, cymin, cymax: Number; [cxmin, cxmax] _ GetLimits[bs, X]; [cymin, cymax] _ GetLimits[bs, Y]; [new.a, new.e] _ Squeeze[cxmin, cxmax, 0, bs.viewer.cw]; [new.d, new.f] _ Squeeze[cymin, cymax, 0, bs.viewer.ch]; IF NOT mayStretch THEN BEGIN IF new.a < new.d THEN BEGIN new.d _ new.a; new.f _ CenterRange[cymin, cymax, 0, bs.viewer.ch, new.d]; END ELSE IF new.d < new.a THEN BEGIN new.a _ new.d; new.e _ CenterRange[cxmin, cxmax, 0, bs.viewer.cw, new.a]; END; END; ChangeTransform[bs, Geom2D.Concat[bs.t, new], paint]; END; CenterRange: PROC [umin, umax, tmin, tmax, s: Number] RETURNS [o: Number] = BEGIN RETURN [(tmin+tmax - (umin+umax)*s)/2]; END; RI: PROC [REAL] RETURNS [INTEGER] = Real.RoundI; GetTransform: PUBLIC PROC [bs: BiScroller] RETURNS [t: Transform] = {t _ bs.t}; ChangeTransform: PUBLIC PROC [bs: BiScroller, new: Transform, paint: BOOLEAN _ TRUE] = BEGIN Doit: ENTRY PROC [t, u: Transform] = {bs.u _ u; bs.t _ t}; inv: Transform _ new.Inverse[]; FOR c: Child _ bs.children, c.next UNTIL c = NIL DO nu: Vec; nu _ new.MapVec[c.where]; VO.EstablishViewerPosition[c.it, RI[nu.x], RI[nu.y], c.it.ww, c.it.wh]; ENDLOOP; Doit[new, inv]; IF paint THEN VO.PaintViewer[viewer: bs.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; NewArea: PROC [a: Area, next: Areas _ NIL] RETURNS [Areas] = {RETURN [NEW [AreasRec _ [next: next, area: a]]]}; AreaDiff: PROC [a, b: Area] RETURNS [d: Areas] = BEGIN y: Number; IF b.ymin < a.ymin THEN {c: Area _ a; a _ b; b _ c}; 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 [NewArea[a, NewArea[b]]]; IF a.ymax > b.ymax THEN d _ NewArea[[a.xmin, y _ b.ymax, a.xmax, a.ymax]] ELSE d _ NewArea[[b.xmin, y _ a.ymax, b.xmax, b.ymax]]; RETURN [NewArea[[b.xmin, b.ymin, a.xmin, y], NewArea[[b.xmax, b.ymin, a.xmax, y], NewArea[[a.xmin, a.ymin, a.xmax, b.ymin], d]]]]; END; Setup: PROCEDURE = BEGIN resetAllCursor _ Cursors.NewCursor[hotX: 8, hotY: 8, bits: [ 146667B, 124442B, 146662B, 124242B, 126662B, 000000B, 000000B, 000000B, 004440B, 012440B, 016440B, 012440B, 012660B, 000000B, 000000B, 000000B]]; centerCursor _ Cursors.NewCursor[hotX: 8, hotY: 8, bits: [ 071721B, 105031B, 101031B, 101625B, 101023B, 105023B, 071721B, 000000B, 000000B, 175736B, 021021B, 021021B, 021636B, 021022B, 021021B, 021721B]]; resetScaleCursor _ Cursors.NewCursor[hotX: 8, hotY: 8, bits: [ 146667B, 124442B, 146662B, 124242B, 126662B, 000000B, 000000B, 000000B, 155646B, 111244B, 151646B, 051244B, 155266B, 000000B, 000000B, 000000B]]; 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[MovingWindow] _ NEW [MouseRec _ [start: MoveWindowStart, track: MoveWindowTrack, finish: MoveWindowFinish, cursor: TextCursor, newTransform: MoveWindowTransform]]; mice[ToCentering] _ NEW [MouseRec _ [start: PtToCenterStart, track: PtToCenterTrack, finish: PtToCenterFinish, cursor: PtToCenterCursor, newTransform: PtToCenterTransform]]; mice[ScaleDoubling] _ NEW [MouseRec _ [cursor: ScaleDoubleCursor, newTransform: ScaleDoubleTransform]]; mice[ScaleHalving] _ NEW [MouseRec _ [cursor: ScaleHalveCursor, newTransform: ScaleHalveTransform]]; mice[ScaleRandoming] _ NEW [MouseRec _ [start: ScaleRandomStart, track: ScaleRandomTrack, finish: ScaleRandomFinish, cursor: TextCursor, newTransform: ScaleRandomTransform]]; mice[ResettingScale] _ NEW [MouseRec _ [cursor: ResetScaleCursor, newTransform: ResetScaleTransform]]; mice[Centering] _ NEW [MouseRec _ [cursor: CenterCursor, newTransform: CenterTransform]]; mice[Lefting] _ NEW [MouseRec _ [cursor: LeftingCursor, newTransform: LeftTransform]]; mice[Righting] _ NEW [MouseRec _ [cursor: RightingCursor, newTransform: RightTransform]]; mice[Topping] _ NEW [MouseRec _ [cursor:ToppingCursor, newTransform: TopTransform]]; mice[Bottoming] _ NEW [MouseRec _ [cursor: BottomingCursor, newTransform: BottomTransform]]; mice[ResettingAndCentering] _ NEW [MouseRec _ [cursor: ResetCenterCursor, newTransform: ResetCenterTransform]]; mice[Client] _ NEW [MouseRec _ [finish: ClientFinish]]; END; Setup[]; END.