FILE: BiScrollersButtonned.Mesa
Last Edited by: Spreitzer, March 28, 1985 3:42:13 pm PST
Pier, March 25, 1985 5:30:32 pm PST
Implements BiScrollerStyle "Buttonned".
DIRECTORY BiScrollers, Cursors, Geom2D, Graphics, Icons, InputFocus, Menus, Real, RealFns, TIPUser, ViewerClasses, ViewerOps;
BiScrollersButtonned: CEDAR MONITOR
IMPORTS BiScrollers, Cursors, Geom2D, Graphics, InputFocus, Menus, Real, RealFns, TIPUser, ViewerOps
SHARES 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;
Buttonned: TYPE = REF ButtonnedRep;
ButtonnedRep: TYPE = RECORD [
class: ButtonnedClass,
clientData: REF ANY,
container, client: Viewer ← NIL,
children: Child ← NIL,
t, u: Transform ← Geom2D.id,
h, v: Viewer ← NIL, --horizontal and vertical BiScrollBars--
cw, ch: INTEGER ← 0, --of container, last time we looked--
clientWantsActive: BOOLFALSE
];
ButtonnedClass: TYPE = REF ButtonnedClassRep;
ButtonnedClassRep: TYPE = RECORD [
viewerClass: ViewerClass,
notify: ViewerClasses.NotifyProc,
paint: ViewerClasses.PaintProc,
extrema: ExtremaProc,
init: ViewerClasses.InitProc,
finish: LORA,
mayStretch, offsetsMustBeIntegers: BOOL];
Child: TYPE = REF ChildObject;
ChildObject: TYPE = RECORD [next: Child, where: Vec, it: Viewer];
Range: TYPE = REF RangeObject;
RangeObject: TYPE = RECORD [min, max: Number];
BiScrollBar: TYPE = REF BiScrollBarObject;
BiScrollBarObject: TYPE = RECORD [
viewer: Viewer ← NIL,
parent: BiScroller,
axis: Axis,
state: State ← Idle,
cursors: ARRAY State OF Cursors.CursorType];
State: TYPE = {Idle, Increase, Decrease, Random};
awake: Viewer ← NIL; --who has CapturedButtons
indent: INTEGER ← 11; --width of BiScrollBars
reset: Cursors.CursorType ← bullseye;
grayl: CARDINAL = 11110B;
graym: CARDINAL = 122645B;
grayh: CARDINAL = 102041B;
buttonnedStyle: 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
]];
menu: Menus.Menu ← NIL;
containerFlavor: ATOM ← $ButtonnedContainer;
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: LIST OF REF ANYNIL,
save: ViewerClasses.SaveProc ← NIL,
tipTable: TIPUser.TIPTable ← NIL,
icon: Icons.IconFlavor ← document,
cursor: Cursors.CursorType ← textPointer,
mayStretch: BOOLTRUE,
offsetsMustBeIntegers: BOOLFALSE]
RETURNS [bsc: BiScrollerClass] =
BEGIN
vc: ViewerClass ← NEW [ViewerClasses.ViewerClassRec ← [
flavor: flavor,
notify: NotifyBiScroller,
paint: PaintClient,
modify: modify,
destroy: destroy,
copy: copy,
set: set,
get: get,
save: save,
tipTable: tipTable,
icon: icon,
cursor: cursor]];
ViewerOps.RegisterViewerClass[flavor: flavor, class: vc];
bsc ← NEW [BiScrollerClassRep ← [
style: buttonnedStyle,
rep: NEW [ButtonnedClassRep ← [
viewerClass: vc,
notify: notify,
paint: paint,
extrema: extrema,
init: init,
finish: finish,
mayStretch: mayStretch,
offsetsMustBeIntegers: offsetsMustBeIntegers]]
]];
END;
CreateBiScroller: PROC [class: BiScrollerClass, info: ViewerClasses.ViewerRec ← [], paint: BOOLEANTRUE] RETURNS [new: BiScroller] =
BEGIN
bsc: ButtonnedClass ← NARROW[class.rep];
bs: Buttonned ← NEW[ButtonnedRep ← [class: bsc,
clientData: info.data,
t: Geom2D.id]];
clientInfo: ViewerClasses.ViewerRec;
info.data ← new ← NEW [BiScrollerRep ← [
style: class.style,
class: class,
rep: bs]];
bs.container ← ViewerOps.CreateViewer[flavor: containerFlavor, info: info, paint: FALSE];
bs.h ← ViewerOps.CreateViewer[flavor: $BiScrollBarX, info: [
name: "X scrolling", parent: bs.container,
wx: indent, wy: 0, ww: bs.container.cw-indent, wh: indent,
data: NEW[BiScrollBarObject ← [parent: new, axis: X, cursors: [
Increase: scrollLeft,
Decrease: scrollRight,
Random: pointUp,
Idle: scrollLeftRight]]],
scrollable: FALSE, border: FALSE], paint: FALSE];
bs.v ← ViewerOps.CreateViewer[flavor: $BiScrollBarY, info: [
name: "Y scrolling", parent: bs.container,
wx: 0, wy: indent, ww: indent, wh: bs.container.ch-indent,
data: NEW[BiScrollBarObject ← [parent: new, axis: Y, cursors: [
Increase: scrollUp,
Decrease: scrollDown,
Random: pointRight,
Idle: scrollUpDown]]],
scrollable: FALSE, border: FALSE], paint: FALSE];
clientInfo ← info;
clientInfo.parent ← bs.container;
clientInfo.border ← FALSE;
bs.client ← ViewerOps.CreateViewer[flavor: bsc.viewerClass.flavor, info: clientInfo, paint: FALSE];
ChangeTransform[new, Geom2D.id.Translate[bs.client.cw/2, bs.client.ch/2], FALSE];
[] ← ComputeClientBounds[bs];
IF bsc.init # NIL THEN bsc.init[bs.client];
IF paint THEN ViewerOps.PaintViewer[viewer: bs.container, hint: all];
END;
Destroy: PROC [bs: BiScroller] RETURNS [BiScroller] =
BEGIN
bsr: Buttonned ← NARROW[bs.rep];
ViewerOps.DestroyViewer[bsr.container];
bsr.container ← NIL;
RETURN [NIL];
END;
AddChild: PROC [to: BiScroller, what: Viewer, x, y: REAL ← 0, useTheseCoords: BOOLEANFALSE, paint: BOOLEANTRUE] =
BEGIN
tor: Buttonned ← NARROW[to.rep];
my: Vec;
c: Child ← NEW [ChildObject ← [
next: tor.children,
it: what,
where: IF useTheseCoords THEN [x, y] ELSE [what.wx, what.wy]
]];
tor.children ← c;
my ← tor.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
ofr: Buttonned ← 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]};
ofr.children ← Filter[ofr.children];
END;
QuaViewer: PROC [bs: BiScroller, inner: BOOLFALSE] RETURNS [v: Viewer] =
{bsr: Buttonned ← NARROW[bs.rep];
v ← IF inner THEN bsr.client ELSE bsr.container};
ClientDataOf: PROC [bs: BiScroller] RETURNS [ra: REF ANY] =
{bsr: Buttonned ← NARROW[bs.rep];
ra ← bsr.clientData};
BoundaryOf: PROC [bs: BiScroller] RETURNS [VecList] =
BEGIN
bsr: Buttonned ← NARROW[bs.rep];
RETURN [Geom2D.MapVecs[bsr.u,
LIST[
[0, 0],
[bsr.client.cw, 0],
[bsr.client.cw, bsr.client.ch],
[0, bsr.client.ch]]]];
END;
ComputeClientBounds: PROC [bs: Buttonned] RETURNS [changed: BOOL] = {
make scrollbars stretch all the way across
IF changed ← (bs.cw # bs.container.cw OR bs.ch # bs.container.ch) THEN {
bs.cw ← bs.container.cw;
bs.ch ← bs.container.ch;
SetViewerPosition[bs.h, indent, 0, bs.cw-indent, indent];
SetViewerPosition[bs.v, 0, indent, indent, bs.ch-indent];
SetViewerPosition[bs.client, indent, indent, bs.cw-indent, bs.ch-indent];
};
};
SetViewerPosition: PROC [v: Viewer, x, y, w, h: INTEGER] = {
v.wx ← x;
v.wy ← y;
v.ww ← w;
v.wh ← h;
v.ch ← h;
v.cw ← w;
v.cx ← x;
v.cy ← y;
};
NotifyBiScroller: PROC [self: Viewer, input: LIST OF REF ANY] --ViewerClasses.NotifyProc-- = {
bs: BiScroller ← NARROW[self.data];
bsr: Buttonned ← NARROW[bs.rep];
i, o, l: LIST OF REF ANYNIL;
IF self # bsr.client THEN ERROR;
IF bsr.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 => {
IF awake = self THEN {
v: Viewer;
inClient: BOOL;
[v, inClient] ← ViewerOps.MouseInViewer[z];
IF v # bsr.client OR NOT inClient THEN {
bsr.clientWantsActive ← FALSE;
SetActive[bsr];
IF bsr.class.notify # NIL THEN bsr.class.notify[bsr.client, bsr.class.finish];
RETURN};
};
l.first ← NEW [Vec ← bsr.u.MapVec[[z.mouseX, z.mouseY]]];
};
ENDCASE;
ENDLOOP;
bsr.class.notify[self, o];
};
SetActive: PROC [bs: Buttonned] = {
wasAwake: Viewer ← awake;
IF bs.clientWantsActive THEN {
IF awake = NIL THEN {
awake ← bs.client;
InputFocus.CaptureButtons[proc: bs.client.class.notify, tip: bs.client.tipTable, viewer: bs.client];
};
}
ELSE {
IF awake = bs.client THEN {
awake ← NIL;
InputFocus.ReleaseButtons[];
};
};
IF wasAwake # bs.client AND wasAwake # NIL THEN ERROR;
};
PaintClient: PROC [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] --ViewerClasses.PaintProc-- = {
bs: Buttonned ← NARROW[NARROW[self.data, BiScroller].rep];
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];
};
SetButtonsCapturedness: PROC [bs: BiScroller, captured: BOOL] = {
bsr: Buttonned ← NARROW[bs.rep];
bsr.clientWantsActive ← captured;
SetActive[bsr]};
Fit: PROC [bs: BiScroller, paint: BOOLEANTRUE, mayStretch: BOOLEANFALSE] =
BEGIN
bsr: Buttonned ← NARROW[bs.rep];
ChangeTransform[bs, FittingTransform[bsr, mayStretch], paint];
END;
FittingTransform: PROC [bs: Buttonned, mayStretch: BOOLEAN] RETURNS [fit: Transform] =
BEGIN
xmin, ymin, xmax, ymax: Vec;
dx, dy, sx, sy: Number;
[xmin, xmax] ← bs.class.extrema[bs.clientData, [1, 0]];
[ymin, ymax] ← bs.class.extrema[bs.clientData, [0, 1]];
dx ← xmax.x - xmin.x; dy ← ymax.y - ymin.y;
sx ← IF ABS[dx] = 0 THEN 1 ELSE bs.client.cw/dx;
sy ← IF ABS[dy] = 0 THEN 1 ELSE bs.client.ch/dy;
IF NOT mayStretch THEN sx ← sy ← MIN[sx, sy];
fit ← [sx, 0, 0, sy,
bs.client.cw/2 - sx*(xmax.x+xmin.x)/2,
bs.client.ch/2 - sy*(ymax.y+ymin.y)/2];
END;
InitBiScrollBar: ViewerClasses.InitProc =
BEGIN
bsb: BiScrollBar ← NARROW[self.data];
bsb.viewer ← self;
END;
PaintBiScrollBar: ViewerClasses.PaintProc =
BEGIN
bsb: BiScrollBar ← NARROW[self.data];
Do: PROCEDURE [c: CARDINAL, xmin, ymin, xmax, ymax: Number] =
{Graphics.SetStipple[context, c];
Graphics.DrawBox[context, [xmin, ymin, xmax, ymax]]};
IF whatChanged # NIL THEN WITH whatChanged SELECT FROM
r: Range => {
SELECT bsb.axis FROM
X => {Do[grayh, 0, 0, r.min*self.cw, self.ch];
Do[graym, r.min*self.cw, 0, r.max*self.cw, self.ch];
Do[grayl, r.max*self.cw, 0, self.cw, self.ch]};
Y => {Do[grayh, 0, 0, self.cw, r.min*self.ch];
Do[graym, 0, r.min*self.ch, self.cw, r.max*self.ch];
Do[grayl, 0, r.max*self.ch, self.cw, self.ch]};
ENDCASE => ERROR;
};
ENDCASE;
END;
GetLimits: PROC [bs: Buttonned, 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;
NotifyBiScrollBarProc: TYPE = PROC [bsb: BiScrollBar, bs: BiScroller, bsr: Buttonned, mouse: TIPUser.TIPScreenCoords, input: LIST OF REF ANY] RETURNS [LIST OF REF ANY];
WakeUpBiScrollBar: PROC [bsb: BiScrollBar, bs: Buttonned, to: State, indicate: BOOLEAN] =
BEGIN
IF awake = NIL THEN {
awake ← bsb.viewer;
InputFocus.CaptureButtons[proc: bsb.viewer.class.notify, tip: bsb.viewer.tipTable, viewer: bsb.viewer];
}
ELSE IF awake # bsb.viewer THEN ERROR;
bsb.state ← to;
Cursors.SetCursor[bsb.viewer.class.cursor ← bsb.cursors[to]];
IF indicate THEN Indicate[bsb, bs];
END;
Indicate: PROC [bsb: BiScrollBar, bs: Buttonned] = {
r: Range ← NEW[RangeObject];
beginW, endW, beginZ, endZ, deltaW: Number;
SELECT bsb.axis FROM
X => {[beginW, endW] ← GetLimits[bs, X]; beginZ𡤀 endZ ← bs.client.cw};
Y => {[beginW, endW] ← GetLimits[bs, Y]; beginZ𡤀 endZ ← bs.client.ch};
ENDCASE => ERROR;
deltaW ← endW - beginW;
IF ABS[deltaW] # 0 THEN r^ ← [
MAX[0.0, MIN[1.0, (beginZ - beginW)/deltaW]],
MAX[0.0, MIN[1.0, (endZ - beginW)/deltaW]]]
ELSE r^ ← [
IF beginW > beginZ THEN 0.0 ELSE 1.0,
IF beginW < endZ THEN 1.0 ELSE 0.0];
ViewerOps.PaintViewer[viewer: bsb.viewer, hint: client, clearClient: FALSE, whatChanged: r];
};
Sleep: PROC [bsb: BiScrollBar] =
BEGIN
IF awake # bsb.viewer THEN ERROR;
bsb.state ← Idle;
Cursors.SetCursor[bsb.viewer.class.cursor ← bsb.cursors[Idle]];
InputFocus.ReleaseButtons[];
awake ← NIL;
END;
Relax: PROC [bsb: BiScrollBar] = {
bsb.state ← Idle;
Cursors.SetCursor[bsb.viewer.class.cursor ← bsb.cursors[Idle]];
};
IncreaseBiScrollBar: NotifyBiScrollBarProc =
BEGIN
IF bsb.state # Increase THEN WakeUpBiScrollBar[bsb, bsr, Increase, TRUE];
IF input.first = $Idle THEN NULL
ELSE IF input.first = $Doit THEN
BEGIN
Relax[bsb];
Move[bs, bsr, SELECT bsb.axis FROM
X => [0 - mouse.mouseX, 0],
Y => [0, bsb.viewer.ch - mouse.mouseY],
ENDCASE => ERROR];
Indicate[bsb, bsr];
END
ELSE ERROR;
RETURN [input.rest];
END;
DecreaseBiScrollBar: NotifyBiScrollBarProc =
BEGIN
IF bsb.state # Decrease THEN WakeUpBiScrollBar[bsb, bsr, Decrease, TRUE];
IF input.first = $Idle THEN NULL
ELSE IF input.first = $Doit THEN
BEGIN
Relax[bsb];
Move[bs, bsr, SELECT bsb.axis FROM
X => [mouse.mouseX, 0],
Y => [0, mouse.mouseY - bsb.viewer.ch],
ENDCASE => ERROR];
Indicate[bsb, bsr];
END
ELSE ERROR;
RETURN [input.rest];
END;
ThumbBiScrollBar: NotifyBiScrollBarProc =
BEGIN
IF bsb.state # Random THEN WakeUpBiScrollBar[bsb, bsr, Random, TRUE];
IF input.first = $Idle THEN NULL
ELSE IF input.first = $Doit THEN
BEGIN
cmin, cmax: Number;
Foo: PROCEDURE [low, high, mouse: Number] RETURNS [Number] =
{RETURN [(low+high)/2 - (cmin + (mouse-low)*(cmax-cmin)/(high-low))]};
SELECT bsb.axis FROM
X => {[cmin, cmax] ← GetLimits[bsr, X];
Relax[bsb];
Move[bs, bsr, [Foo[0, bsr.client.cw, mouse.mouseX], 0]]};
Y => {[cmin, cmax] ← GetLimits[bsr, Y];
Relax[bsb];
Move[bs, bsr, [0, Foo[0, bsr.client.ch, mouse.mouseY]]]};
ENDCASE => ERROR;
Indicate[bsb, bsr];
END
ELSE ERROR;
RETURN [input.rest];
END;
Move: PROCEDURE [bs: BiScroller, bsr: Buttonned, by: Vec] =
BEGIN
ChangeTransform[bs, bsr.t.Translate[by.x, by.y]];
END;
NotifyBiScrollBar: PROC [self: Viewer, input: LIST OF REF ANY] --ViewerClasses.NotifyProc-- =
BEGIN ENABLE UNWIND => InputFocus.ReleaseButtons[];
bsb: BiScrollBar ← NARROW[self.data];
bs: BiScroller ← bsb.parent;
bsr: Buttonned ← NARROW[bs.rep];
mouse: TIPUser.TIPScreenCoords;
WHILE input # NIL DO
WITH input.first SELECT FROM
x: ATOM => SELECT x FROM
$HereToEdge => input ← IncreaseBiScrollBar[bsb, bs, bsr, mouse, input.rest];
$EdgeToHere => input ← DecreaseBiScrollBar[bsb, bs, bsr, mouse, input.rest];
$Thumb => input ← ThumbBiScrollBar[bsb, bs, bsr, mouse, input.rest];
ENDCASE => ERROR;
z: TIPUser.TIPScreenCoords => BEGIN
v: Viewer;
c: BOOLEAN;
mouse ← z;
IF awake = self THEN
BEGIN
[v, c] ← ViewerOps.MouseInViewer[mouse];
IF v # self OR NOT c THEN
{Sleep[bsb];
ViewerOps.PaintViewer[self, client, TRUE, NIL];
RETURN};
END
ELSE WakeUpBiScrollBar[bsb, bsr, Idle, TRUE];
input ← input.rest;
END;
ENDCASE => ERROR;
ENDLOOP;
END;
Scale: PROC [parent: REF ANY, clientData: REF ANYNIL, mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE] --Menus.MenuProc-- = {
v: Viewer ← NARROW[parent];
bs: BiScroller ← NARROW[v.data, BiScroller];
bsr: Buttonned ← NARROW[bs.rep];
vc: Vec ← ViewerCenter[bsr];
old: Transform ← bsr.t;
new: Transform;
new ← old.Translate[-vc.x, -vc.y];
new ← new.ScaleT[SELECT mouseButton FROM
red => 2.0,
yellow => 1.0/ZeroProtect[RealFns.SqRt[ABS[old.a*old.d - old.b*old.c]]],
blue => 1.0/2.0,
ENDCASE => ERROR];
new ← new.Translate[vc.x, vc.y];
ChangeTransform[bs, new];
};
ZeroProtect: PROC [r: REAL] RETURNS [r0: REAL] =
{r0 ← IF r = 0.0 THEN 1.0 ELSE r};
Rotate: PROC [parent: REF ANY, clientData: REF ANYNIL, mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE] --Menus.MenuProc-- = {
v: Viewer ← NARROW[parent];
bs: BiScroller ← NARROW[v.data, BiScroller];
bsr: Buttonned ← NARROW[bs.rep];
vc: Vec ← ViewerCenter[bsr];
old: Transform ← bsr.t;
new: Transform;
new ← old.Translate[-vc.x, -vc.y];
new ← new.RotateBy90s[SELECT mouseButton FROM
red => 1,
yellow => 2,
blue => 3,
ENDCASE => ERROR];
new ← new.Translate[vc.x, vc.y];
ChangeTransform[bs, new];
};
FitButton: PROC [parent: REF ANY, clientData: REF ANYNIL, mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE] --Menus.MenuProc-- = {
v: Viewer ← NARROW[parent];
bs: BiScroller ← NARROW[v.data];
bd: Buttonned ← NARROW[bs.rep];
ChangeTransform[bs, FittingTransform[bd, bd.class.mayStretch]];
};
Identity: PROC [parent: REF ANY, clientData: REF ANYNIL, mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE] --Menus.MenuProc-- = {
v: Viewer ← NARROW[parent];
bs: BiScroller ← NARROW[v.data];
bd: Buttonned ← NARROW[bs.rep];
ChangeTransform[bs, Geom2D.id];
};
Center: PROC [parent: REF ANY, clientData: REF ANYNIL, mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE] --Menus.MenuProc-- = {
v: Viewer ← NARROW[parent];
bs: BiScroller ← NARROW[v.data, BiScroller];
bsr: Buttonned ← NARROW[bs.rep];
new: Transform;
xmin, xmax, ymin, ymax: Number;
[xmin, xmax] ← GetLimits[bsr, X];
[ymin, ymax] ← GetLimits[bsr, Y];
new ← bsr.t.Translate[
bsr.client.cw/2-(xmin+xmax)/2,
bsr.client.ch/2-(ymin+ymax)/2];
ChangeTransform[bs, new];
};
ResetAndCenter: PROC [parent: REF ANY, clientData: REF ANYNIL, mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE] --Menus.MenuProc-- = {
v: Viewer ← NARROW[parent];
bs: BiScroller ← NARROW[v.data, BiScroller];
bsr: Buttonned ← NARROW[bs.rep];
new: Transform;
xmin, xmax, ymin, ymax: Number;
ChangeTransform[bs, Geom2D.id, FALSE];
[xmin, xmax] ← GetLimits[bsr, X];
[ymin, ymax] ← GetLimits[bsr, Y];
new ← bsr.t.Translate[
bsr.client.cw/2-(xmin+xmax)/2,
bsr.client.ch/2-(ymin+ymax)/2];
ChangeTransform[bs, new];
};
ViewerCenter: PROC [bs: Buttonned] RETURNS [vc: Vec] = {
vc ← [bs.client.cw/2, bs.client.ch/2];
};
RI: PROC [REAL] RETURNS [INTEGER] = Real.RoundI;
GetTransforms: PROC [bs: BiScroller] RETURNS [t, u: Transform] =
{bsr: Buttonned ← NARROW[bs.rep]; t ← bsr.t; u ← bsr.u};
ChangeTransform: PROC [bs: BiScroller, new: Transform, paint: BOOLEANTRUE] =
BEGIN
bsr: Buttonned ← NARROW[bs.rep];
Doit: ENTRY PROC [t, u: Transform] = {bsr.u ← u; bsr.t ← t};
inv: Transform;
IF bsr.class.offsetsMustBeIntegers THEN {new.e ← RI[new.e]; new.f ← RI[new.f]};
inv ← new.Inverse[];
FOR c: Child ← bsr.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: bsr.client, hint: client];
END;
PaintContainer: PROC [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] --ViewerClasses.PaintProc-- = {
bs: Buttonned ← NARROW[NARROW[self.data, BiScroller].rep];
IF ComputeClientBounds[bs] THEN ViewerOps.ResetPaintCache[self, FALSE];
Don't ask me, I copied it from ContainersImpl
};
Setup: PROCEDURE =
BEGIN
menu ← Menus.CreateMenu[];
menu.AppendMenuEntry[Menus.CreateEntry["Rotate", Rotate]];
menu.AppendMenuEntry[Menus.CreateEntry["Scale", Scale]];
menu.AppendMenuEntry[Menus.CreateEntry["Fit", FitButton]];
menu.AppendMenuEntry[Menus.CreateEntry["ResetAndCenter", ResetAndCenter]];
menu.AppendMenuEntry[Menus.CreateEntry["Center", Center]];
menu.AppendMenuEntry[Menus.CreateEntry["Identity", Identity]];
ViewerOps.RegisterViewerClass[flavor: containerFlavor,
class: NEW [ViewerClasses.ViewerClassRec ← [
flavor: containerFlavor,
paint: PaintContainer,
menu: menu
]]];
ViewerOps.RegisterViewerClass[flavor: $BiScrollBarX,
class: NEW [ViewerClasses.ViewerClassRec ← [
flavor: $BiScrollBarX,
init: InitBiScrollBar,
notify: NotifyBiScrollBar,
paint: PaintBiScrollBar,
tipTable: TIPUser.InstantiateNewTIPTable["Knob.TIP"],
cursor: scrollLeftRight]]];
ViewerOps.RegisterViewerClass[flavor: $BiScrollBarY,
class: NEW [ViewerClasses.ViewerClassRec ← [
flavor: $BiScrollBarY,
init: InitBiScrollBar,
notify: NotifyBiScrollBar,
paint: PaintBiScrollBar,
tipTable: TIPUser.InstantiateNewTIPTable["Knob.TIP"],
cursor: scrollUpDown]]];
RegisterStyle["Buttonned", buttonnedStyle];
[] ← SetDefaultStyle["Buttonned"];
END;
Setup[];
END.