FILE: BiScrollersButtonned.Mesa
Last Edited by: Spreitzer, November 10, 1985 5:34:12 pm PST
Pier, March 29, 1985 1:23:37 pm PST
Implements BiScrollerStyle "Buttonned".
DIRECTORY BiScrollers, Cursors, Geom2D, Imager, ImagerBackdoor, ImagerTransformation, InputFocus, Menus, Real, TIPUser, ViewerClasses, ViewerOps;
BiScrollersButtonned:
CEDAR
MONITOR
IMPORTS BiScrollers, Cursors, Geom2D, Imager, ImagerBackdoor, ImagerTransformation, 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;
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: BOOL ← FALSE
];
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: Imager.Color ← ImagerBackdoor.MakeStipple[8421H];
graym: Imager.Color ← ImagerBackdoor.MakeStipple[5A5AH];
grayh: Imager.Color ← ImagerBackdoor.MakeStipple[1248H];
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:
BOOLEAN ←
TRUE]
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;
clientInfo.menu ← NIL;
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:
BOOLEAN ←
FALSE, paint:
BOOLEAN ←
TRUE] =
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.Transform[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:
BOOL ←
FALSE]
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.Transform[[
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 ANY ← NIL;
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.Transform[[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: Imager.Context, whatChanged:
REF
ANY, clear:
BOOL]
RETURNS [quit:
BOOL ←
FALSE]
--ViewerClasses.PaintProc-- = {
bs: BiScroller ← NARROW[self.data];
bd: Buttonned ← NARROW[bs.rep];
context.ConcatT[bd.t];
quit ← 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: Imager.Context, whatChanged:
REF
ANY, clear:
BOOL]
RETURNS [quit:
BOOL ←
FALSE]
--ViewerClasses.PaintProc-- =
BEGIN
bsb: BiScrollBar ← NARROW[self.data];
Do:
PROCEDURE [c: Imager.Color, xmin, ymin, xmax, ymax: Number] =
{Imager.SetColor[context, c];
Imager.MaskRectangle[context, [xmin, ymin, xmax-xmin, ymax-ymin]]};
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, FALSE];
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, FALSE];
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, FALSE];
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.PostTranslate[by]];
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 [INT] = Real.Round;
GetTransforms:
PROC [bs: BiScroller]
RETURNS [clientToViewer, viewerToClient: Transform] = {
bsr: Buttonned ← NARROW[bs.rep];
clientToViewer ← bsr.t;
viewerToClient ← bsr.u};
ChangeTransform:
PROC [bs: BiScroller, new: Transform, paint:
BOOLEAN ←
TRUE] =
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 ← new.TranslateTo[[RI[new.c], RI[new.f]]];
IF new.a*new.e - new.b*new.d = 0 THEN RETURN;
inv ← new.Invert[];
FOR c: Child ← bd.children, c.next
UNTIL c =
NIL
DO
nu: Vec;
nu ← new.Transform[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;
};
AdjustContainer:
PROC [self: Viewer]
RETURNS [adjusted:
BOOL ←
FALSE]
--ViewerClasses.AdjustProc-- = {
bs: BiScroller ← NARROW[self.data];
bd: Buttonned ← NARROW[bs.rep];
adjusted ← bd.container # NIL AND ComputeClientBounds[bs, bd, TRUE];
};
SaveContainer:
PROC [self: Viewer, force:
BOOL ←
FALSE]
--ViewerClasses.SaveProc-- = {
bs: BiScroller ← NARROW[self.data];
bd: Buttonned ← NARROW[bs.rep];
IF bs.class.common.save # NIL THEN bs.class.common.save[self, force];
};
Setup:
PROCEDURE =
BEGIN
ViewerOps.RegisterViewerClass[flavor: containerFlavor,
class:
NEW [ViewerClasses.ViewerClassRec ← [
flavor: containerFlavor,
init: InitContainer,
adjust: AdjustContainer,
save: SaveContainer
]]];
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.