FILE: BiScrollersButtonned.Mesa
Last Edited by: Spreitzer, April 23, 1985 1:35:20 pm PST
Pier, March 29, 1985 1:23:37 pm PST
Implements BiScrollerStyle "Buttonned".
DIRECTORY BiScrollers, Cursors, Geom2D, Graphics, Icons, InputFocus, Menus, Real, TIPUser, ViewerClasses, ViewerOps;
BiScrollersButtonned: CEDAR MONITOR
IMPORTS BiScrollers, Cursors, Geom2D, Graphics, InputFocus, Menus, Real, 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 [
clientClass: ViewerClass,
menu: Menus.Menu];
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 = 102041B;
graym: CARDINAL = 122645B;
grayh: CARDINAL = 11110B;
buttonnedStyle: 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
]];
containerFlavor: ATOM ← $ButtonnedContainer;
NewBiScrollerClass: PROC [cc: ClassCommon] RETURNS [bsc: BiScrollerClass] =
BEGIN
vc: ViewerClass;
IF cc.vanilla = NIL THEN cc.vanilla ← GenID;
vc ← NEW [ViewerClasses.ViewerClassRec ← [
flavor: cc.flavor,
notify: NotifyBiScroller,
paint: PaintClient,
modify: cc.modify,
destroy: cc.destroy,
copy: cc.copy,
set: cc.set,
get: cc.get,
save: cc.save,
tipTable: cc.tipTable,
icon: cc.icon,
cursor: cc.cursor]];
ViewerOps.RegisterViewerClass[flavor: vc.flavor, class: vc];
bsc ← NEW [BiScrollerClassRep ← [
style: buttonnedStyle,
common: cc,
rep: NEW [ButtonnedClassRep ← [
clientClass: vc,
menu: cc.menu
]]
]];
END;
CreateBiScroller: PROC [class: BiScrollerClass, info: ViewerClasses.ViewerRec ← [], paint: BOOLEANTRUE] RETURNS [new: BiScroller] =
BEGIN
bdc: ButtonnedClass ← NARROW[class.rep];
paintFirst: BOOL = paint AND info.iconic;
bd: Buttonned ← NEW[ButtonnedRep ← [class: bdc,
clientData: info.data,
t: Geom2D.id]];
clientInfo: ViewerClasses.ViewerRec;
info.data ← new ← NEW [BiScrollerRep ← [
style: class.style,
class: class,
rep: bd]];
bd.container ← ViewerOps.CreateViewer[flavor: containerFlavor, info: info, paint: paintFirst];
bd.h ← ViewerOps.CreateViewer[flavor: $BiScrollBarX, info: [
name: "X scrolling", parent: bd.container,
wx: indent, wy: 0, ww: bd.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];
bd.v ← ViewerOps.CreateViewer[flavor: $BiScrollBarY, info: [
name: "Y scrolling", parent: bd.container,
wx: 0, wy: indent, ww: indent, wh: bd.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 ← bd.container;
clientInfo.border ← FALSE;
bd.client ← ViewerOps.CreateViewer[flavor: bdc.clientClass.flavor, info: clientInfo, paint: FALSE];
[] ← ComputeClientBounds[new, bd, FALSE];
ChangeTransform[new, class.common.vanilla[new], FALSE];
SetBS[bd.container, new];
IF class.common.init # NIL THEN class.common.init[bd.client];
IF NOT paintFirst THEN ViewerOps.ComputeColumn[column: ViewerOps.ViewerColumn[bd.container], paint: paint];
END;
Destroy: PROC [bs: BiScroller] RETURNS [BiScroller] =
BEGIN
bd: Buttonned ← NARROW[bs.rep];
ViewerOps.DestroyViewer[bd.container];
bd.container ← NIL;
RETURN [NIL];
END;
AddChild: PROC [to: BiScroller, what: Viewer, x, y: REAL ← 0, useTheseCoords: BOOLEANFALSE, paint: BOOLEANTRUE] =
BEGIN
bd: Buttonned ← NARROW[to.rep];
my: Vec;
c: Child ← NEW [ChildObject ← [
next: bd.children,
it: what,
where: IF useTheseCoords THEN [x, y] ELSE [what.wx, what.wy]
]];
bd.children ← c;
my ← bd.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
bd: 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]};
bd.children ← Filter[bd.children];
END;
QuaViewer: PROC [bs: BiScroller, inner: BOOLFALSE] RETURNS [v: Viewer] =
{bd: Buttonned ← NARROW[bs.rep];
v ← IF inner THEN bd.client ELSE bd.container};
ClientDataOf: PROC [bs: BiScroller] RETURNS [ra: REF ANY] =
{bd: Buttonned ← NARROW[bs.rep];
ra ← bd.clientData};
ViewportOf: PROC [bs: BiScroller] RETURNS [VecList] =
BEGIN
bd: Buttonned ← NARROW[bs.rep];
RETURN [Geom2D.MapVecs[bd.u,
LIST[
[0, 0],
[bd.client.cw, 0],
[bd.client.cw, bd.client.ch],
[0, bd.client.ch]]]];
END;
ComputeClientBounds: PROC [bs: BiScroller, bd: Buttonned, adjust: BOOL] RETURNS [changed: BOOL] = {
make scrollbars stretch all the way across
IF changed ← (bd.cw # bd.container.cw OR bd.ch # bd.container.ch) THEN {
client: Vec --the point that stays fixed, in client coords--;
IF adjust THEN {
client ← bd.u.MapVec[[
bs.class.common.preserve[X]*bd.client.cw,
bs.class.common.preserve[Y]*bd.client.ch]];
};
bd.cw ← bd.container.cw;
bd.ch ← bd.container.ch;
SetViewerPosition[bd.h, indent, 0, bd.cw-indent, indent];
SetViewerPosition[bd.v, 0, indent, indent, bd.ch-indent];
SetViewerPosition[bd.client, indent, indent, bd.cw-indent, bd.ch-indent];
IF adjust THEN {
Align[
bs: bs,
client: [coord[client.x, client.y]],
viewer: [fraction[
bs.class.common.preserve[X],
bs.class.common.preserve[Y]]],
paint: FALSE];
};
};
};
NotifyBiScroller: PROC [self: Viewer, input: LIST OF REF ANY] --ViewerClasses.NotifyProc-- = {
bs: BiScroller ← NARROW[self.data];
bd: Buttonned ← NARROW[bs.rep];
i, o, l: LIST OF REF ANYNIL;
IF self # bd.client THEN ERROR;
IF bs.class.common.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 # bd.client OR NOT inClient THEN {
bd.clientWantsActive ← FALSE;
SetActive[bd];
bs.class.common.notify[bd.client, bs.class.common.finish];
RETURN};
};
l.first ← NEW [Vec ← bd.u.MapVec[[z.mouseX, z.mouseY]]];
};
ENDCASE;
ENDLOOP;
bs.class.common.notify[self, o];
};
SetActive: PROC [bd: Buttonned] = {
wasAwake: Viewer ← awake;
IF bd.clientWantsActive THEN {
IF awake = NIL THEN {
awake ← bd.client;
InputFocus.CaptureButtons[proc: bd.client.class.notify, tip: bd.client.tipTable, viewer: bd.client];
};
}
ELSE {
IF awake = bd.client THEN {
awake ← NIL;
InputFocus.ReleaseButtons[];
};
};
IF wasAwake # bd.client AND wasAwake # NIL THEN ERROR;
};
PaintClient: PROC [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] --ViewerClasses.PaintProc-- = {
bs: BiScroller ← NARROW[self.data];
bd: Buttonned ← NARROW[bs.rep];
Graphics.Translate[context, bd.t.e, bd.t.f];
Graphics.Concat[context, bd.t.a, bd.t.b, bd.t.c, bd.t.d];
bs.class.common.paint[self, context, whatChanged, clear];
};
SetButtonsCapturedness: PROC [bs: BiScroller, captured: BOOL] = {
bd: Buttonned ← NARROW[bs.rep];
bd.clientWantsActive ← captured;
SetActive[bd]};
InitBiScrollBar: PROC [self: Viewer] --ViewerClasses.InitProc-- =
BEGIN
bsb: BiScrollBar ← NARROW[self.data];
bsb.viewer ← self;
END;
PaintBiScrollBar: PROC [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] --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;
NotifyBiScrollBarProc: TYPE = PROC [bsb: BiScrollBar, bs: BiScroller, bd: 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, bd: Buttonned] = {
bs: BiScroller ← bsb.parent;
r: Range ← NEW[RangeObject];
beginW, endW, beginZ, endZ, deltaW: Number;
SELECT bsb.axis FROM
X => {[beginW, endW] ← ViewLimitsOfImage[bs, X]; beginZ𡤀 endZ�.client.cw};
Y => {[beginW, endW] ← ViewLimitsOfImage[bs, Y]; beginZ𡤀 endZ�.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, bd, Increase, TRUE];
IF input.first = $Idle THEN NULL
ELSE IF input.first = $Doit THEN
BEGIN
Relax[bsb];
Move[bs, bd, SELECT bsb.axis FROM
X => [0 - mouse.mouseX, 0],
Y => [0, bsb.viewer.ch - mouse.mouseY],
ENDCASE => ERROR];
Indicate[bsb, bd];
END
ELSE ERROR;
RETURN [input.rest];
END;
DecreaseBiScrollBar: NotifyBiScrollBarProc =
BEGIN
IF bsb.state # Decrease THEN WakeUpBiScrollBar[bsb, bd, Decrease, TRUE];
IF input.first = $Idle THEN NULL
ELSE IF input.first = $Doit THEN
BEGIN
Relax[bsb];
Move[bs, bd, SELECT bsb.axis FROM
X => [mouse.mouseX, 0],
Y => [0, mouse.mouseY - bsb.viewer.ch],
ENDCASE => ERROR];
Indicate[bsb, bd];
END
ELSE ERROR;
RETURN [input.rest];
END;
ThumbBiScrollBar: NotifyBiScrollBarProc =
BEGIN
IF bsb.state # Random THEN WakeUpBiScrollBar[bsb, bd, 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] ← ViewLimitsOfImage[bs, X];
Relax[bsb];
Move[bs, bd, [Foo[0, bd.client.cw, mouse.mouseX], 0]]};
Y => {[cmin, cmax] ← ViewLimitsOfImage[bs, Y];
Relax[bsb];
Move[bs, bd, [0, Foo[0, bd.client.ch, mouse.mouseY]]]};
ENDCASE => ERROR;
Indicate[bsb, bd];
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;
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
bd: Buttonned ← NARROW[bs.rep];
Doit: ENTRY PROC [t, u: Transform] = {bd.u ← u; bd.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 ← bd.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: bd.client, hint: client];
END;
InitContainer: PROC [self: Viewer] --ViewerClasses.InitProc-- = {
bs: BiScroller ← NARROW[self.data];
bd: Buttonned ← NARROW[bs.rep];
IF bd.class.menu # NIL AND self.parent = NIL THEN self.menu ← bd.class.menu.CopyMenu[];
self.icon ← bs.class.common.icon;
};
PaintContainer: PROC [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] --ViewerClasses.PaintProc-- = {
bs: BiScroller ← NARROW[self.data];
bd: Buttonned ← NARROW[bs.rep];
IF clear AND ComputeClientBounds[bs, bd, TRUE] THEN ViewerOps.ResetPaintCache[self, FALSE];
Don't ask me, I copied it from ContainersImpl
};
Setup: PROCEDURE =
BEGIN
ViewerOps.RegisterViewerClass[flavor: containerFlavor,
class: NEW [ViewerClasses.ViewerClassRec ← [
flavor: containerFlavor,
init: InitContainer,
paint: PaintContainer
]]];
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.