DIRECTORY BiScrollers, Cursors, Geom2D, Graphics, Icons, InputFocus, Real, RealFns, TIPUser, ViewerClasses, ViewerOps; BiScrollersButtonless: CEDAR MONITOR IMPORTS BiScrollers, Cursors, Geom2D, Graphics, InputFocus, Real, TIPUser, ViewerOps = 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; 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 cw, ch: INTEGER _ 0, --last time we looked in the viewer-- clientWantsActive: BOOL _ FALSE, state: State _ Normal ]; ButtonlessClass: TYPE = REF ButtonlessClassRep; ButtonlessClassRep: TYPE = RECORD [ viewerClass: ViewerClass, notify: ViewerClasses.NotifyProc, paint: ViewerClasses.PaintProc, extrema: ExtremaProc, init: ViewerClasses.InitProc, finish: LORA, cursor: Cursors.CursorType, mayStretch, offsetsMustBeIntegers: BOOL]; 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: Buttonless] RETURNS [Area, BOOLEAN]; FinishProc: TYPE = PROC [bs: Buttonless] RETURNS [Areas]; CursorProc: TYPE = PROC [mv, bc: Vec] RETURNS [Cursors.CursorType]; TrackProc: TYPE = PROC [mv, bc: Vec, bs: Buttonless] RETURNS [Area]; NewTransformProc: TYPE = PROC [old: Transform, mv, bc: Vec, bs: Buttonless] 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}; ActiveState: TYPE = State[MovingWindow .. ResettingAndCentering]; awake: Viewer _ NIL; mice: ARRAY ActiveState OF Mouse _ ALL[NIL]; resetScaleCursor, centerCursor, resetAllCursor, pointUpRight, pointUpLeft, pointDownRight, pointDownLeft: Cursors.CursorType; buttonlessStyle: BiScrollerStyle _ NEW [BiScrollerStyleRep _ [ NewBiScrollerClass: NewBiScrollerClass, CreateBiScroller: CreateBiScroller, Destroy: Destroy, Fit: Fit, GetTransforms: GetTransforms, ChangeTransform: ChangeTransform, AddChild: AddChild, DeleteChild: DeleteChild, SetButtonsCapturedness: SetButtonsCapturedness, BoundaryOf: BoundaryOf, QuaViewer: QuaViewer, ClientDataOf: ClientDataOf ]]; NewBiScrollerClass: 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: LORA _ NIL, save: ViewerClasses.SaveProc _ NIL, tipTable: TIPUser.TIPTable _ NIL, icon: Icons.IconFlavor _ document, cursor: Cursors.CursorType _ textPointer, mayStretch: BOOL _ TRUE, offsetsMustBeIntegers: BOOL _ FALSE] 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]]; ViewerOps.RegisterViewerClass[flavor: flavor, class: vc]; bsc _ NEW [BiScrollerClassRep _ [ style: buttonlessStyle, rep: NEW [ButtonlessClassRep _ [ viewerClass: vc, notify: notify, paint: paint, extrema: extrema, init: init, finish: finish, cursor: cursor, mayStretch: mayStretch, offsetsMustBeIntegers: offsetsMustBeIntegers]] ]]; 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; bs: Buttonless _ NEW [ButtonlessRep _ [class: bsc, clientData: info.data, t: Geom2D.id]]; info.data _ new _ NEW [BiScrollerRep _ [ style: class.style, class: class, rep: bs]]; bs.viewer _ ViewerOps.CreateViewer[flavor: bsc.viewerClass.flavor, info: info, paint: paintFirst]; ChangeTransform[new, Geom2D.id.Translate[bs.viewer.cw/2, bs.viewer.ch/2], FALSE]; IF bsc.init # NIL THEN bsc.init[bs.viewer]; IF NOT paintFirst THEN ViewerOps.ComputeColumn[column: ViewerOps.ViewerColumn[bs.viewer], paint: paint]; 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.MapVec[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]}; BoundaryOf: 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; NotifyClient: PROC [bl: Buttonless, input: LORA] = { IF bl.class.notify # NIL THEN bl.class.notify[bl.viewer, input]; }; 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; mv: Vec; ToClient: PROC = { IF bl.state # Normal THEN GiveUp[bl]; IF bl.class.notify # NIL THEN bl.class.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[bl, mice[bl.state]]; Sleep[bl]; IF didWant THEN NotifyClient[bl, bl.class.finish]; RETURN}; }; l.first _ NEW [Vec _ bl.u.MapVec[mv _ [z.mouseX, z.mouseY]]]; }; ENDCASE; ENDLOOP; input _ o; IF input = NIL THEN ToClient[] ELSE SELECT input.first FROM $BSscaleRandom => Work[bs, bl, mv, ScaleRandoming, input.rest]; $BSscaleDouble => Work[bs, bl, mv, ScaleDoubling, input.rest]; $BSscaleHalve => Work[bs, bl, mv, ScaleHalving, input.rest]; $BSresetScale => Work[bs, bl, mv, ResettingScale, input.rest]; $BSmoveWindow => Work[bs, bl, mv, MovingWindow, input.rest]; $BStoCenter => Work[bs, bl, mv, ToCentering, input.rest]; $BScenter => Work[bs, bl, mv, Centering, input.rest]; $BSresetCenter => Work[bs, bl, mv, ResettingAndCentering, input.rest]; $BSleft => Work[bs, bl, mv, Lefting, input.rest]; $BSright => Work[bs, bl, mv, Righting, input.rest]; $BStop => Work[bs, bl, mv, Topping, input.rest]; $BSbottom => Work[bs, bl, mv, Bottoming, input.rest]; $BSgiveUp => GiveUp[bl]; $BSeatIt => NULL; ENDCASE => ToClient[]; END; first, next: Area; Work: PROC [bs: BiScroller, bl: Buttonless, mv: Vec, why: ActiveState, input: LORA] = BEGIN bc: Vec; who: Mouse _ mice[why]; oldState: State _ bl.state; clientWanted: BOOL _ bl.clientWantsActive; bl.clientWantsActive _ FALSE; bl.state _ why; bc _ [bl.viewer.cw/2, bl.viewer.ch/2]; IF clientWanted THEN NotifyClient[bl, bl.class.finish]; WakeUp[bl]; IF why # oldState THEN BEGIN IF oldState IN ActiveState THEN Finish[bl, mice[oldState]]; IF who.start # NIL THEN BEGIN paint: BOOLEAN; [first, paint] _ who.start[mv, bc, bl]; IF paint THEN ViewerOps.PaintViewer[viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: NewArea[first]]; next _ first; END END; IF who.cursor # NIL THEN Cursors.SetCursor[bl.viewer.class.cursor _ who.cursor[mv, bc]]; IF who.track # NIL THEN BEGIN new: Area _ who.track[mv, bc, bl]; ViewerOps.PaintViewer[viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: AreaDiff[new, next]]; next _ new; END; IF input.rest.first = $Doit THEN BEGIN Sleep[bl]; oldT _ bl.t; bcUsed _ bc; ChangeTransform[bs, nuT _ who.newTransform[bl.t, mv, bc, bl]]; END ELSE IF input.rest.first # $Idle THEN ERROR; END; oldT, nuT: Transform; bcUsed: Vec; GiveUp: PROCEDURE [bs: Buttonless] = BEGIN WasIdle: ENTRY PROC RETURNS [BOOLEAN] = {oldState _ bs.state; IF bs.state = Normal THEN RETURN [TRUE]; DoSleep[bs]; RETURN [FALSE]}; oldState: State; IF WasIdle[] THEN RETURN; IF oldState = Normal THEN ERROR; Finish[bs, mice[oldState]]; END; Finish: PROC [bs: Buttonless, mouse: Mouse] = BEGIN IF mouse.finish # NIL THEN BEGIN changed: Areas _ mouse.finish[bs]; IF changed # NIL THEN ViewerOps.PaintViewer[viewer: bs.viewer, hint: client, clearClient: FALSE, whatChanged: changed]; END; END; Awake: ENTRY PROC [bs: Buttonless] RETURNS [b: BOOL] = {b _ awake = bs.viewer}; WakeUp: ENTRY PROC [bs: Buttonless] = {ENABLE UNWIND => {}; SetActive[bs]}; Sleep: ENTRY PROC [bs: Buttonless] = {ENABLE UNWIND => {}; DoSleep[bs]}; DoSleep: INTERNAL PROC [bs: Buttonless] = { bs.state _ Normal; Cursors.SetCursor[bs.viewer.class.cursor _ bs.class.cursor]; SetActive[bs]; }; SetActive: INTERNAL PROC [bs: Buttonless] = { wasAwake: Viewer _ awake; IF bs.clientWantsActive OR (bs.state # Normal) THEN { IF awake = NIL THEN { awake _ bs.viewer; InputFocus.CaptureButtons[proc: bs.viewer.class.notify, tip: bs.viewer.tipTable, viewer: bs.viewer]; }; } ELSE { IF awake = bs.viewer THEN { awake _ NIL; InputFocus.ReleaseButtons[]; }; }; IF NOT (wasAwake = bs.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[]}; 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]}; PaintBiScroller: PROC [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] --ViewerClasses.PaintProc-- = BEGIN bs: Buttonless _ NARROW[NARROW[self.data, BiScroller].rep]; 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: Buttonless, 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: PROC [bs: BiScroller, paint: BOOLEAN _ TRUE, mayStretch: BOOLEAN _ FALSE] = BEGIN bl: Buttonless _ NARROW[bs.rep]; new: Transform _ Geom2D.id; cxmin, cxmax, cymin, cymax: Number; [cxmin, cxmax] _ GetLimits[bl, X]; [cymin, cymax] _ GetLimits[bl, Y]; [new.a, new.e] _ Squeeze[cxmin, cxmax, 0, bl.viewer.cw]; [new.d, new.f] _ Squeeze[cymin, cymax, 0, bl.viewer.ch]; IF NOT mayStretch THEN BEGIN IF new.a < new.d THEN BEGIN new.d _ new.a; new.f _ CenterRange[cymin, cymax, 0, bl.viewer.ch, new.d]; END ELSE IF new.d < new.a THEN BEGIN new.a _ new.d; new.e _ CenterRange[cxmin, cxmax, 0, bl.viewer.cw, new.a]; END; END; ChangeTransform[bs, Geom2D.Concat[bl.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; GetTransforms: PROC [bs: BiScroller] RETURNS [t, u: Transform] = {bl: Buttonless _ NARROW[bs.rep]; t _ bl.t; u _ bl.u}; ChangeTransform: PROC [bs: BiScroller, new: Transform, paint: BOOLEAN _ TRUE] = BEGIN bl: Buttonless _ NARROW[bs.rep]; Doit: ENTRY PROC [t, u: Transform] = {bl.u _ u; bl.t _ t}; inv: Transform; IF bl.class.offsetsMustBeIntegers THEN {new.e _ RI[new.e]; new.f _ RI[new.f]}; inv _ new.Inverse[]; FOR c: Child _ bl.children, c.next UNTIL c = NIL DO nu: Vec; nu _ new.MapVec[c.where]; ViewerOps.EstablishViewerPosition[c.it, RI[nu.x], RI[nu.y], c.it.ww, c.it.wh]; ENDLOOP; Doit[new, inv]; 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; 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]]; RegisterStyle["Buttonless", buttonlessStyle]; END; Setup[]; END. ŠFILE: BiScrollersButtonless.Mesa Last Edited by: Spreitzer, March 28, 1985 3:41:47 pm PST Implements BiScrollerStyle "Buttonless". Κ^˜J™ J™8J™Jšœ(™(J˜codešΟk œm˜vK˜—šΠbxœœ˜$KšœM˜T—K˜Kšœœœ ˜K˜Kšœœ˜Kšœ œ˜.Kšœœ˜Kšœœ˜Kšœœ˜Kšœ œœ˜K˜Kšœ œœ˜%šœœœ˜Kšœ˜Kšœ œœ˜Kšœœ˜Kšœœ˜KšœΟc˜6KšœŸ!˜;KšœœŸ%˜;Kšœœœ˜ K˜K˜K˜—Kšœœœ˜/šœœœ˜#K˜K˜!K˜K˜K˜Kšœœ˜ K˜Kšœ#œ˜)K˜—Kšœœœ ˜Kšœ œœ'˜AK˜Kšœœœ ˜šœ œœœ˜0Kšœœ˜Kšœœ˜Kšœœ˜K˜ K˜—Kš Οn œœœœœ˜MKš  œœœœ ˜9Kš  œœœœ˜CKš  œœœœ˜DKš œœœ/œ˜eK˜Kšœœœ ˜Kšœ œœ˜2K˜šœœ­˜ΈK˜—Kšœ œ0˜AK˜Kšœœ˜Kš œœ œ œœ˜,K˜K˜}K˜šœ#œ˜>Kšœ'˜'Kšœ#˜#Kšœ˜Kšœ ˜ Kšœ˜Kšœ!˜!Kšœ˜Kšœ˜Kšœ/˜/Kšœ˜Kšœ˜Kšœ˜K˜—K˜š œ˜šœ˜Kšœœ˜ K˜Kšœ#œ˜'Kšœ!œ˜%Kšœ#œ˜'Kšœ%œ˜)Kšœœ˜#Kšœœ˜!Kšœœ˜!Kšœœ˜#Kšœœœ˜Kšœœ˜#Kšœœ˜!K˜"K˜)Kšœ œœ˜Kšœœœ˜$—Kšœ˜ Kš˜K˜K˜Gšœ œ˜Kš˜Kšœœ$˜6Kšœ œ˜K˜Kšœ˜—šœœ"˜*K˜K˜K˜K˜K˜K˜ K˜ K˜ K˜ K˜ K˜ K˜—Kšœ9˜9šœœ˜!Kšœ˜šœœ˜ K˜K˜K˜ K˜K˜ K˜K˜K˜Kšœ.˜.—K˜—Kšœ˜K˜—š  œœEœœœ˜†Kš˜Kšœœ ˜)Kšœ œ œ ˜)šœœ˜2K˜K˜—šœœ˜(Kšœ˜Kšœ ˜ Kšœ ˜ —Kšœb˜bKšœJœ˜QKšœ œœ˜+Kšœœ œR˜hKšœ˜K˜—š œœœ˜5Kš˜Kšœœ ˜ Kšœ#˜#Kšœ œ˜Kšœœ˜ Kšœ˜K˜—š œœ&œœœ œœ˜wKš˜Kšœœ ˜ K˜šœ œ#˜1K˜ šœœœ˜$Kšœ˜——Kšœ˜Kšœ˜šœ&œ œ˜˜>Kšœ<˜˜>Kšœ<˜˜>Kš˜—Kšœœœœ˜,Kšœ˜K˜—K˜K˜ K˜š œ œ˜$Kš˜š  œœœœœ˜'K˜Kšœœœœ˜(K˜ Kšœœ˜—K˜Kšœ œœ˜Kšœœœ˜ K˜Kšœ˜—K˜š œœ!˜-Kš˜šœœ˜Kš˜K˜"Kšœ œœEœ˜wKšœ˜—Kšœ˜—K˜Kš  œœœœœ˜OK˜Kš  œœœœœ˜KK˜Kš  œœœœœ˜HK˜š œœœ˜+Kšœ˜K˜K˜—šœ˜Kšœœ1˜8K˜—šœ˜Kšœœ˜K˜—šœ˜Kš˜K˜Kšœœ˜Kšœœ˜šœœ œ˜7Kšœœ œœœ œ œ ˜MKšœœ œœœ œ œ ˜PKš œœ œœ œœ˜GKšœœ œ œ˜7—Kšœ˜K˜—šœ'˜'Kš˜K˜Kšœœ ˜Kšœœ ˜Kšœ œœ ˜0Kšœ œœ ˜,Kšœ˜Kšœ˜K˜—Kšœ!œ ˜3K˜šœ(˜(Kšœœ9˜@K˜—Kšœ œ˜4K˜šœ'˜'Kšœœ?˜FK˜—šœ˜Kš˜K˜Kšœœ˜Kšœœ˜Kšœœ œœ ˜'Kšœ,œ˜9Kšœ˜—K˜K˜K˜šœ˜Kš˜K˜Kšœœ˜Kš œœœœœ ˜BKšœ,˜2Kšœ˜K˜—šœ˜Kšœœ˜!K˜—šœ(˜(Kš˜K˜#K˜K˜K˜K˜Kš œœœœœ ˜+Kš œœœœœ ˜+Kšœœœ˜4Kšœ?˜EKšœ˜K˜—Kšœ œ˜;K˜šœ'˜'K˜K˜"K˜Kšœœœœ˜ K˜!K˜!K˜K˜K˜—Kšœœ˜3K˜šœ#˜#Kš˜K˜Kšœœ˜ Kšœœ˜ Kšœ=˜CKšœ˜—K˜Kšœœ˜2K˜šœ!˜!Kš˜K˜ Kšœœ˜Kšœ˜!Kšœ˜—K˜Kšœœ˜4K˜šœ#˜#Kš˜K˜ Kšœœ˜Kšœ˜!Kšœ˜—K˜Kšœœ˜4K˜šœ"˜"Kš˜K˜ Kšœœ˜Kšœ)˜/Kšœ˜—K˜Kšœœ ˜0K˜šœ ˜ Kš˜K˜ Kšœœ˜Kšœ)˜/Kšœ˜—K˜Kšœ!œ˜:K˜šœ(˜(Kš˜K˜K˜7K˜7šœ ˜&K˜*—Kšœ˜K˜—Kšœœ˜0K˜š  œœ8œœ œŸœ˜€Kš˜Kšœœœ˜;Kšœœœœœœœ˜Dšœ˜ K˜&K˜,š œ œœœ˜;K˜"Kšœ˜—Kšœ˜Kšœ˜—K˜,K˜9K˜2Kšœ˜K˜—š  œœœ˜KKš˜K˜šœ˜Kšœ˜Kšœ˜Kšœœ˜—K˜3K˜K˜šœ˜Kšœœ˜Kšœœ˜Kšœœ˜—Kšœ˜—K˜š  œœœœœœ˜PKš˜Kšœœ ˜ K˜K˜#Kšœœ˜"Kšœœ˜"Kšœ8˜8Kšœ8˜8šœœ ˜Kš˜šœ˜Kš˜K˜Kšœ:˜:Kš˜—šœœ˜Kš˜K˜Kšœ:˜:Kšœ˜—Kšœ˜—Kšœ5˜5Kšœ˜—K˜š  œœ%œ˜KKš˜Kšœ!˜'Kšœ˜—K˜Kš œœœœœ˜0K˜š  œœœ˜@Kšœœ˜6—K˜š œœ)œœ˜OKš˜Kšœœ ˜ Kš œœœ*˜:K˜Kšœ œ œœ ˜NK˜šœ œœ˜3K˜K˜Kšœ(œœ˜NKšœ˜—K˜Kšœœ8˜EKšœ˜K˜—š œœ)˜6Kšœ˜!Kš˜K˜"Kšœ œœ˜.K˜K˜Kšœ˜K˜—š œœœœ ˜K˜#K˜#K˜#K˜%—˜