File: MBWindows.mesa
Contents: package implementing set of buttons in viewer when there are too many things to actually create buttons for all. Client passes procedures to go forward and backward through enumeration, and ScrollableButtonsImpl implements scrolling, calls client button proc on appropriate thing.
Last Edited by: Cattell, October 5, 1983 9:19 am
DIRECTORY Buttons, MBQueue, ViewerClasses, Rope;
MBWindows: CEDAR DEFINITIONS =
BEGIN
Viewer: TYPE = ViewerClasses.Viewer;
EnumProc: TYPE =
PROC[enum: REF ANY] RETURNS [label: Rope.ROPE, thing: REF ANY];
Returns next or previous element of the enumeration.
CountProc: TYPE =
PROC[v: Viewer, enum: REF ANY] RETURNS [INT];
Estimate size of enumeration. Used only for feedback so may be approximate.
ThumbProc: TYPE =
PROC[v: Viewer, enum: REF ANY, where: NAT] RETURNS [newEnum: REF ANY];
Use to set enumeration to particular position quickly; may return new enum.
The where parameter is a percentage between 0 and 100.
CreateMBWindow: PROC[
next, prev: EnumProc, -- to go forward and backward in the enumeration
thumb: ThumbProc, -- to create an enumeration set to a particular position
count: CountProc, -- to give total size of enumeration
buttonProc: Buttons.ButtonProc, -- called with the thing associated with button
info: ViewerClasses.ViewerRec← [], -- allows client to fill in ViewerRec fields
initialScroll: INT← 0, -- what percentage of scroll to have for initial display
q: MBQueue.Queue← NIL] -- MBQueue under which to synchronize (doesn't yet work)
RETURNS [viewer: Viewer];
Client's buttonProc must be prepared to be called with an empty button (clientData and label NIL), as there will always be one screenheight of buttons even if they're not all occupied. It should do nothing in this case. Client's next proc should return [NIL, NIL] when there are no more items to enumerate. It may be called again after that, in which case it should continue to return [NIL, NIL]. Similarly for prev proc at beginning of list. Client's count procedure is used only for providing feedback in scroll bar, so it need not be exactly right. MBWindows will tolerate some inconsistency in the client's procedures, e.g. enumerating the same item twice when change from next to prev proc.
Notice we do not need an initial REF ANY for the enumeration; it is created by calling the thumb procedure with where=initialScroll and enum=NIL. Note also that we allow the thumb procedure to create a new enumeration, while the next and prev procedures are expected to make any modification to the enum they are passed in place (they may modify it, because MBWindows has a REF to it).
Note: if the MBWindow is nested inside another viewer, then the parent field should be set by passing a parent in the info field, and the client should call Containers.ChildYBound so that the MBWindow's size is set automatically with changes in the parent. See example below.
END.
An example of the use of MBWindows (from Squirrel, DomainNutImpl):
To create the MBWindow:
parent.scrollable← FALSE; -- Scrollbar will be provided by MBWindow
mbw← MBWindows.CreateMBWindow[
count: GetThingCount,
thumb: DomainThumb,
next: DomainNextProc,
prev: DomainPrevProc,
buttonProc: ProcessEntitySelection,
info: [parent: parent, wx: 0, wy: 0, ww: parent.ww, wh: 800, border: FALSE],
q: NIL ];
Containers.ChildYBound[container: parent, child: mbw];
GetThingCount: MBWindows.CountProc = {
parent: Viewer← GetTopLevel[v];
dInfo: DomainInfo← NARROW[ ViewerOps.FetchProp[parent, $DomainInfo] ];
RETURN[EntitySetSize[DomainSubset[dInfo.domain, "", "\177"], FALSE]];
};
DomainNextProc: MBWindows.EnumProc = TRUSTED
BEGIN es: EntitySet← LOOPHOLE[enum];
e: Entity← NextEntity[es];
RETURN[IF e=NIL THEN NIL ELSE NameOf[e], e];
END;
DomainPrevProc: MBWindows.EnumProc = TRUSTED
BEGIN es: EntitySet← LOOPHOLE[enum];
e: Entity← PrevEntity[es];
RETURN[IF e=NIL THEN NIL ELSE NameOf[e], e];
END;
DomainThumb: MBWindows.ThumbProc = TRUSTED
BEGIN es: EntitySet← LOOPHOLE[enum];
dInfo: DomainInfo = NARROW[ViewerOps.FetchProp[GetTopLevel[v], $DomainInfo]];
DB.ReleaseEntitySet[es];
IF where<10 THEN
es← DB.DomainSubset[dInfo.domain, "", "\177", First]
ELSE IF where>90 THEN {
es← DB.DomainSubset[dInfo.domain, "", "\177", Last];
[]← PrevEntity[es]; []← PrevEntity[es]; []← PrevEntity[es]}
ELSE {
Get there the hard way, though NextEntity calls
es← DB.DomainSubset[dInfo.domain, "", "\177", First];
THROUGH [0..where*GetThingCount[v, es]/100) DO []← NextEntity[es] ENDLOOP;
};
RETURN[es]
END;
GetTopLevel: PROCEDURE [v: Viewer] RETURNS [ancestor: Viewer] = {
FOR ancestor ← v, ancestor.parent UNTIL ancestor.parent=NIL DO ENDLOOP;
};