DIRECTORY BiScrollers, Cursors, Geom2D, Graphics, Icons, InputFocus, Real, RealFns, TIPUser, ViewerClasses, ViewerOps;

BiScrollersButtonless: CEDAR MONITOR
IMPORTS BiScrollers, Cursors, Geom2D, Graphics, InputFocus, Real, RealFns, 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,
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, data: REF ANY];

Areas: TYPE = REF AreasRec;
AreasRec: TYPE = RECORD [next: Areas, area: Area];

Paths: TYPE = REF PathsRep;
PathsRep: TYPE = RECORD [next: Paths, path: Graphics.Path, 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: Area;

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.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,
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], 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];
IF NOT paintFirst THEN ViewerOps.ComputeColumn[column: ViewerOps.ViewerColumn[bl.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]};

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.MapVec[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, input.rest];
$BScontract => Work[bs, bl, viewMouse, Contracting, input.rest];
$BSthumb => Work[bs, bl, viewMouse, Thumbing, input.rest];
$BSscrollToCenter => Work[bs, bl, viewMouse, ScrollToCentering, input.rest];
$BSscrollAlongClicks => Work[bs, bl, viewMouse, ScrollAlongClicksing, input.rest];
$BSgiveUp => GiveUp[bs, bl];
$BSeatIt => NULL;
ENDCASE => ToClient[];
END;

Work: PROC [bs: BiScroller, bl: Buttonless, viewMouse: Vec, why: ActiveState, 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, 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: Area;

ThumbStart: StartProc =
BEGIN
cxmin, cxmax, cymin, cymax: Number;
cx, cy, dx, dy: Number;
windowT: Transform;
[cxmin, cxmax] _ ViewLimitsOfImage[bs, X];
[cymin, cymax] _ ViewLimitsOfImage[bs, Y];
[windowT.a, windowT.e] _ Squeeze[cxmin, cxmax, 0, bl.viewer.cw];
[windowT.d, windowT.f] _ Squeeze[cymin, cymax, 0, bl.viewer.ch];
windowT.b _ windowT.c _ 0;
cx _ (cxmin+cxmax)/2; cy _ (cymin+cymax)/2;
dx _ bl.viewer.cw/2; dy _ bl.viewer.ch/2;
outline _ windowT.MapArea[[cx-dx, cy-dy, cx+dx, cy+dy]];
first _ viewMouse.Minus[viewCenter].Displace[outline];
ViewerOps.PaintViewer[
viewer: bl.viewer,
hint: client,
clearClient: FALSE,
whatChanged: NewArea[first]
];
next _ first;
END;

ThumbTrack: TrackProc =
BEGIN
new: Area _ viewMouse.Minus[viewCenter].Displace[outline];
ViewerOps.PaintViewer[viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: AreaDiff[new, next]];
next _ new;
END;

ThumbFinish: FinishProc = {
ViewerOps.PaintViewer[viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: NewArea[next]];
};

ThumbTransform: NewTransformProc =
BEGIN
Align[
bs,
[fraction[viewMouse.x/bl.viewer.cw, viewMouse.y/bl.viewer.ch]],
[fraction[0.5, 0.5]]
];
END;

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: Graphics.Path _ Graphics.NewPath[10];
diff: Vec;
t: Transform;
Do: PROC [x, y: REAL] = {
v: Vec _ t.MapVec[[x, y]];
IF first THEN Graphics.MoveTo[p, v.x, v.y] ELSE Graphics.LineTo[p, v.x, v.y];
first _ FALSE;
};
[from, to, ] _ AdjustScroll[from, to];
diff _ from.Minus[to];
t _ Geom2D.id
.RotateDegrees[RealFns.ArcTanDeg[diff.y, diff.x] - 90]
.ScaleT[diff.Length[]]
.TranslateV[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 [nFrom, nTo: Vec, changed: BOOL] = {
delta: Vec _ to.Minus[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;
nTo _ to;
nFrom _ nTo.Minus[delta];
};

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 _ Geom2D.Minus[
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 =
BEGIN
from, to: Vec;
[from, to, ] _ AdjustScroll[
ScrollFrom[viewMouse, viewCenter, data],
ScrollTo[viewMouse, viewCenter, data]];
Shift[bs, to.x-from.x, to.y-from.y];
END;

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, viewMouse.x, viewMouse.y];
END;

ScaleRandomTrack: TrackProc =
BEGIN
d: Vec _ viewMouse.Minus[scaleCenter];
adx: Number _ ABS[d.x];
ady: Number _ IF bs.class.common.mayStretch THEN ABS[d.y] ELSE adx*slope;
new: Area _ [scaleCenter.x-adx, scaleCenter.y-ady, scaleCenter.x+adx, scaleCenter.y+ady];
ViewerOps.PaintViewer[viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: AreaDiff[new, next]];
next _ new;
END;

ScaleRandomFinish: FinishProc = {
ViewerOps.PaintViewer[viewer: bl.viewer, hint: client, clearClient: FALSE, whatChanged: NewArea[next]];
};

ScaleRandomTransform: NewTransformProc =
BEGIN
viewer: Viewer _ bs.style.QuaViewer[bs, TRUE];
fromA, toA: Area;
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;
BoxScale[bs, fromA, toA];
END;

TextCursor: CursorProc = {RETURN [textPointer]};

PaintBiScroller: PROC [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] --ViewerClasses.PaintProc-- =
BEGIN
bs: BiScroller _ NARROW[self.data];
bl: Buttonless _ NARROW[bs.rep];
IF clear AND (self.cw # bl.cw OR self.ch # bl.ch) THEN {
client: Vec --the point that stays fixed, in client coords--;
client _ bl.u.MapVec[[
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 whatChanged # NIL THEN WITH whatChanged SELECT FROM
as: Areas => {
Graphics.SetStipple[context, 122645B];
[] _ Graphics.SetPaintMode[context, invert];
FOR a: Areas _ as, a.next WHILE a # NIL DO
Graphics.DrawBox[context, a.area];
ENDLOOP;
RETURN;
};
ps: Paths => {
Graphics.SetStipple[context, 122645B];
[] _ Graphics.SetPaintMode[context, invert];
FOR p: Paths _ ps, p.next WHILE p # NIL DO
SELECT p.op FROM
fill => Graphics.DrawArea[context, p.path];
stroke => Graphics.DrawStroke[context, p.path];
ENDCASE => ERROR;
ENDLOOP;
RETURN;
};
ENDCASE;
Graphics.Translate[context, bl.t.e, bl.t.f];
Graphics.Concat[context, bl.t.a, bl.t.b, bl.t.c, bl.t.d];
bs.class.common.paint[self, context, whatChanged, clear];
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 bs.class.common.offsetsMustBeIntegers THEN {new.e _ RI[new.e]; new.f _ RI[new.f]};
IF new.a*new.d - new.b*new.c = 0 THEN RETURN;
inv _ new.Inverse[];
FOR c: Child _ bl.children, c.next UNTIL c = NIL DO
nu: Vec;
nu _ new.MapVec[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[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
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.

���Š��FILE: BiScrollersButtonless.Mesa
Last Edited by: Spreitzer, April 15, 1985 9:52:23 am PST

Implements BiScrollerStyle "Buttonless".
Êï��˜�J™ J™8J™�Jšœ(™(J˜�codešÏk	œm˜vK˜�—šÐbxœœ˜$KšœV˜]—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šœ
œœ'˜AK˜�Kšœœœ
˜šœ
œœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜K˜Kšœœœœ˜K˜�—Kš
Ïn	œœœDœœ˜cKš
 
œœœ(œœ˜HKš 
œœœ$œœœ˜aKš
 	œœœDœœ˜cKš
 œœœDœœ˜jK˜�Kšœœœ
˜Kšœ
œœ˜2K˜�Kšœœœ
˜Kšœ
œœ8˜OK˜�šœœW˜bK˜�—Kšœ
œ"˜3K˜�Kšœœ˜Kš	œœ
œ	œœ˜,K˜�K˜MK˜�šœ#œ˜>Kšœ'˜'Kšœ#˜#Kšœ˜Kšœ˜Kšœ!˜!Kšœ˜Kšœ˜Kšœ/˜/Kšœ˜Kšœ˜Kšœ˜K˜—K˜�K˜K˜�š œœœ˜KKš˜K˜K˜GKšœœœ˜,šœœ˜Kš˜Kšœœ'˜9Kšœœ˜K˜Kšœ˜—šœœ"˜3K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜—KšœN˜Nšœœ˜!Kšœ˜K˜KšœœE˜MK˜—Kšœ˜K˜�—š
 œœEœœœ˜†Kš˜Kšœœ˜)Kšœœ	œ
˜)Kšœœ7˜Kšœœ˜(Kšœ˜Kšœ
˜
Kšœ
˜
—Kšœb˜bKšœ0œ˜7K˜K˜K˜Kšœœœ˜=KšœœœR˜hKšœ˜K˜�—š œœœ˜5Kš˜Kšœœ	˜ Kšœ#˜#Kšœœ˜Kšœœ˜
Kšœ˜K˜�—š œœ&œœœ	œœ˜wKš˜Kšœœ	˜ K˜šœœ#˜1K˜	šœœœ˜$Kšœ˜——Kšœ˜Kšœ˜šœ&œœ˜<K˜&—Kšœ˜K˜�—š œœ ˜1Kš˜Kšœœ	˜ š œœœ
˜)Kšœœœœœœ˜Kšœœœ
˜#K˜Kšœ˜—Kšœ"˜"Kšœ˜—K˜�š
 	œœœœœ˜HKšœœ
œ˜6—K˜�š
 œœœœœ˜7Kšœœ
œ˜:—K˜�š 
œœœ˜5Kš˜Kšœœ	˜ šœ˜šœ˜Kšœ˜Kšœ˜Kšœ˜——Kšœ˜—K˜�š œœ%˜7Kšœœœ;˜_Kšœ˜K˜�—š œœœŸœ˜QKš˜Kšœœ˜/Kšœœ	˜ Kšœ	œœœœœ˜K˜š œœ˜Kšœœ˜)Kšœœœ+˜O—šœœœ˜&Kšœœœœœ
œœ
œ
œ˜Lšœ	œ˜šœ˜šœœ˜K˜
Kšœ
œ˜K˜+šœœœ
œ˜'Kšœ	œ˜%Kšœœ˜Kšœ
œ
œ ˜?Kšœ
˜
Kšœ	œ˜%Kšœ˜—K˜—Kšœ
œ7˜DK˜—Kšœ˜—Kšœ˜—K˜
Kšœ	œœ˜šœœ
˜Kšœ<˜<Kšœ@˜@Kšœ:˜:KšœL˜LKšœR˜RKšœ˜Kšœœ˜Kšœ˜—Kšœ˜K˜�—š œœKœ˜\Kš˜K˜K˜K˜Kšœœ˜*Kšœœ˜Kšœ˜Kšœ.˜.Kšœœ˜*Kšœ˜šœ˜Kš˜Kšœ
œ
œ ˜?Kšœ
œœ4˜KKšœ˜—KšœœœY˜qKšœ
œœ3˜Kšœ˜ Kš˜Kšœ
˜
Kšœ!˜!Kšœ:˜:Kš˜—Kšœœœœ˜,Kšœ˜K˜�—K˜K˜K˜�š œ	œ#˜4Kš˜š
 œœœœœ˜'K˜Kšœœœœ˜(K˜Kšœœ˜—K˜Kšœœœ˜Kšœœœ˜ K˜Kšœ˜—K˜�š œœ1˜=Kš˜Kšœœœ!˜<Kšœ˜—K˜�Kš
 œœœœœ˜OK˜�Kš
 œœœœœ˜KK˜�Kš
 œœœœœ˜HK˜�š œœœ˜+Kšœ˜K˜<K˜Kšœ˜—K˜�š 	œœœ˜-K˜šœœœ˜5šœ	œœ˜Kšœ˜K˜dK˜—K˜—šœ˜šœœ˜Kšœœ˜K˜K˜—K˜—Kšœœœœœœ˜;K˜—K˜�š œœœ˜AKšœœ	˜ š œœœ˜Kšœ ˜ Kšœ˜—K˜—K˜�K˜K˜�šœ˜Kš˜K˜#K˜K˜Kšœ'œ˜*Kšœ'œ˜*K˜@K˜@K˜K˜+K˜)Kšœ8˜8Kšœ6˜6šœ˜Kšœ˜Kšœ
˜
Kšœ
œ˜Kšœ˜Kšœ˜—K˜
Kšœ˜K˜�—šœ˜Kš˜Kšœ:˜:KšœDœ$˜mK˜Kšœ˜K˜�—šœ˜KšœDœ˜gKšœ˜K˜�—šœ"˜"Kš˜˜K˜Kšœ?˜?K˜K˜—Kšœ˜K˜�—Kšœ	œ˜Kšœ
œ
˜Kšœœ˜Kšœ	œ˜"Kšœ
œ˜#K˜�š 	œœœ˜GKšœœœ˜K˜(K˜
K˜
š œœœ˜K˜Kšœœœ˜MKšœœ˜K˜—K˜&K˜˜
Kšœ6˜6Kšœ˜Kšœ˜—Kšœ˜Kšœ-˜-KšœC˜CKšœ+˜+Kšœ	˜	Kšœ,˜,Kšœ?˜?Kšœ.˜.Kšœ˜Kšœœ.˜9K˜—K˜�š œœœÏgœ¡œœ˜OKšœ˜Kšœœ
˜Kšœœ
˜Kšœœœ
˜<Kšœœœ
˜8Kš¡œ˜	Kš¡œ¡œ˜K˜—K˜�K˜K˜K˜�š
 
œœ$œœœ˜Ršœœ˜K˜K˜Kšœœ˜——K˜�š
 œœ$œœœ˜Nšœœ˜Kšœ˜Kšœ˜Kšœœ˜——K˜�šœ˜Kšœ˜Kšœœ˜Kšœ˜K˜�—šœ˜šœ˜Kšœ˜Kšœ
˜
Kšœ
œ˜šœ"˜"Kšœ(˜(Kšœ&˜&Kšœ	˜	—Kšœ˜—Kšœœ˜Kšœ˜K˜�—šœ˜KšœœœEœ˜yKšœ˜K˜�—šœ˜Kš˜šœ˜Kšœ&˜&Kšœ)˜)—Kšœœ˜Kšœœ˜šœœœ˜7Kšœœ	œœœ	œœ	˜MKšœœ	œœœ	œœ˜PKšœœ	œœ	œœ˜GKšœœ	œ
œ˜7—Kšœ˜K˜�—šœ#˜#Kš˜K˜šœ˜Kšœ(˜(Kšœ'˜'—K˜$Kšœ˜—K˜�K˜K˜K˜�šœ˜Kš˜Kšœ(œ˜.Kš	œœœœœ˜?Kšœ˜KšœD˜DKšœ˜—K˜�šœ˜Kš˜K˜&Kšœœ˜Kš	œœœœœ˜IKšœY˜YKšœDœ$˜mK˜Kšœ˜K˜�—šœ!˜!KšœDœ˜gKšœ˜K˜�—šœ(˜(Kš˜Kšœ(œ˜.K˜šœ˜K˜>K˜@Kšœœ˜—K˜Kšœ˜K˜�—Kšœœ˜0K˜�š œœ8œœ	œŸœ˜€Kš˜Kšœœ˜#Kšœœ	˜ šœœœœ˜8KšœŸ0œ˜=˜Kšœœ˜"Kšœœ
˜$—šœ˜Kšœ˜Kšœ$˜$Kšœ+œœ˜MKšœœ˜—K˜K˜K˜—šœœœœ
œ˜6˜K˜&K˜,šœœœ˜*K˜"Kšœ˜—Kšœ˜K˜—šœ˜K˜&K˜,šœœœ˜*šœ˜Kšœ+˜+Kšœ/˜/Kšœœ˜—Kšœ˜—Kšœ˜K˜—Kšœ˜—K˜,K˜9K˜9Kšœ˜—K˜�Kš
œœœœœ˜0K˜�š 
œœœ˜@Kšœœ˜6—K˜�š œœ)œœ˜OKš˜Kšœœ	˜ Kš œœœ*˜:K˜Kšœ'œ
œœ	˜UKšœœœ˜-K˜šœ œœ˜3K˜K˜šœ˜Kšœ)œœ˜RKšœœœ˜C—Kšœ˜—K˜Kšœœ8˜EKšœ˜K˜�—š œœ)˜6Kšœ˜!Kš˜K˜"Kšœœœ˜.K˜K˜Kšœ˜K˜�—š œœœœ
˜<Kšœœœ&˜2K˜�—š œœœ
˜0Kš˜K˜
Kšœœ˜4šœœœ˜!Kšœœœ˜"Kšœœœ˜"Kšœœœœ˜1—Kšœœ2˜IKšœ3˜7šœ&˜,K˜$K˜0—Kšœ˜K˜�—š œ	œ˜Kš˜˜<K˜#K˜#K˜#K˜%—˜9K˜#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˜—Kšœ-˜-Kšœ˜K˜�—K˜K˜�Kšœ˜K˜�—�…—����M´��e-��