FILE: BiScrollersButtonless.Mesa
Last Edited by: Spreitzer, April 15, 1985 9:52:23 am PST
Implements BiScrollerStyle "Buttonless".
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.