ViewRec.Mesa
Last Edited by: Maxwell, November 16, 1982 3:28 pm
Mike Spreitzer August 26, 1986 4:37:45 pm PDT
DIRECTORY AMTypes, List, Rope, ViewerClasses, VFonts;
ViewRec: CEDAR DEFINITIONS
IMPORTS VFonts =
BEGIN
Familiar Types:
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
TypedVariable: TYPE = AMTypes.TypedVariable;
Type: TYPE = AMTypes.Type;
Creating RecordViewers:
The following procedure creates a RecordViewer on the referent of "agg". For simple usage, default all other arguments.
ViewRef: PROC [
agg: REF ANY,
specs: BindingList ← NIL,
See section on binding lists.
label: ROPENIL,
A label to put in the upper left corner of the viewer. NIL means don't label. You can force following sub-viewers to be below the label by making the label end in CR; the CR won't appear in the text of the label.
otherStuff: OtherStuffProc ← NIL,
A way to include other viewers in the created RecordViewer; they go after the label and before anything else.
toButt: Closure ← [],
"toButt" is invoked when CONTROL-RETURN is typed in a Value Viewer by the User.
parent: RecordViewer ← NIL,
The "parent" is independant of the Viewers ancestry; it is used for finding a FeedBack Window.
asElement: EltHandle ← NIL,
If ViewRef is being applied to an aggregate logically embedded in another aggregate which is itself being viewed with ViewRec, pass the EltHandle for "agg" in "asElement" (to make Next and Previous work correctly).
sample: BOOLTRUE,
"sample" causes this RecordViewer to be put on a list of RV's to be sampled in the background to keep its display up to date.
createOptions: CreateOptions ← [],
For control of layout, and a few other things, which are done at create time; default gives reasonable behavior.
viewerInit: ViewerClasses.ViewerRec ← [],
"viewerInit" is for the usual Viewers initing stuff, except:
for ww:
the RecordViewer lays itself out to the inner width of the Viewer created,
unless ww#0 and viewerInit.parent=NIL, in which case ww is used,
or unless ww=0 and (viewerInit.parent#NIL OR iconic), in which case createOptions.minRecordViewerWidth is used.
for name:
If name = NIL, then we try GetName[agg].
wDir: ROPENIL,
The working directory to be current for procedures invoked from this RecordViewer; NIL means use working directory current at create time (now).
paint: BOOLTRUE
if "paint", then painted when done.
]
RETURNS [rv: RecordViewer];
NotAnAggregate: ERROR;
raised by the create procs if not handed a record, structure (unpainted record), sequence, or array.
OtherStuffProc: TYPE = PROC [in: Viewer] RETURNS [stuff: LIST OF Viewer];
Closure: TYPE = RECORD [
Proc: PROC [data: REF ANY] ← NIL,
data: REF ANYNIL
];
EltHandle: TYPE = REF ANY; --type system lets us down
A handle on the display of an element of an aggregate.
RecordViewer: TYPE = REF RecordViewerPrivate;
RecordViewerPrivate: TYPE;
ViewTV: PROC [agg: TypedVariable,
specs: BindingList ← NIL,
label: ROPENIL,
otherStuff: OtherStuffProc ← NIL,
toButt: Closure ← [],
parent: RecordViewer ← NIL,
asElement: EltHandle ← NIL,
sample: BOOLTRUE,
createOptions: CreateOptions ← [],
viewerInit: ViewerClasses.ViewerRec ← [],
wDir: ROPENIL,
paint: BOOLTRUE]
RETURNS [rv: RecordViewer];
ViewTV is just like ViewRef, except it takes a TypedVariable for the subject.
ViewInterface: PROC [name: ROPE,
specs: BindingList ← NIL,
label: ROPE ← NIL,
otherStuff: OtherStuffProc ← NIL,
toButt: Closure ← [],
parent: RecordViewer ← NIL,
asElement: EltHandle ← NIL,
sample: BOOLTRUE,
createOptions: CreateOptions ← [],
viewerInit: ViewerClasses.ViewerRec ← [],
wDir: ROPENIL,
paint: BOOLTRUE]
RETURNS [RecordViewer];
makes a top-level RecordViewer on an Interface Record from a DEFINITIONs module
ViewSelf: PROC;
invokes ViewInterface on ViewRec
CreateOptions:
These control layout, and a few other things, which are done at create time.
CreateOptions: TYPE = RECORD [
immProcFont: VFonts.Font ← NIL, --NIL means use Viewers default
preProcFont: VFonts.Font ← NIL, --font for prepared procedure names
doitFont: VFonts.Font ← NIL, --for the Doit button in Proc's args rec
stateFont: VFonts.Font ← NIL, --for the State label in Proc's args rec
nameFont: VFonts.Font ← NIL, --for first half of name:value pairs
labelFont: VFonts.Font ← NIL, --for the label of the RecordViewer
bordElts: BOOLFALSE,  --draw border around every Name-Value pair
bordRecs: BOOLTRUE,  --border a Value if it is a RECORD
bordImmProcs: BOOLTRUE, --border immediate PROCEDUREs
bordPreProcs: BOOLTRUE, --border prepared PROCEDUREs
bordDoit: BOOLTRUE,  --border the Doit button in a Proc's args rec
bordState: BOOLFALSE, --border the State label in a Proc's args
vSep: CARDINAL ← 2,  --spacing between lines of stuff
hSep: CARDINAL ← 5,  --spacing between things on a line
vPad: CARDINAL ← 3,  --height of Value boxes, beyond font height
hPad: CARDINAL ← 1,  --width of Value boxes, beyond string width
nvSep: CARDINAL ← 3,  --seperation between Name and Value
vStilts: INTEGER ← 1,  --artificially lower the Name Label this much
feedBackHeight: CARDINAL ← 40, --how high the FeedBack Window is
minFeedBackWidth: CARDINAL ← 100, --unless height is 0, of course
minRecordWidth: CARDINAL ← 100, --lower bound for RECORDs
defaultTargetWidth: CARDINAL ← 400,
relayoutable: BOOLTRUE,
mayInitiateRelayout: BOOLTRUE,
maxEltsHeight: CARDINAL ← 290, --limits display for elements
maxArgsHeight: CARDINAL ← 310, --limits PROC arg display height
doAllRecords: BOOLFALSE,--if TRUE, every record is judged simple enough to attempt to display; components will still be judged individually
exclusiveProcs: BOOLTRUE, --makes procs mutually exclusive
holdOff: BOOLFALSE,  --prevents actually calling PROCs
highlightSelectedProc: BOOLTRUE, --causes current Proc Button to display Black on Grey
other: List.AList ← NIL  --for expansion
];
RightFont: PROC [f: VFonts.Font] RETURNS [rf: VFonts.Font]
= INLINE {RETURN [IF f = NIL THEN VFonts.defaultFont ELSE f]};
BindingLists:
What are Binding Lists?
Binding lists provide a way to say random things about components of an aggregate:
A Value binding will cause that component to be initialized to the given value; furthermore, this prevents that component from making the aggregate it is part of too complicated to handle.
Setting "dontAssign" prevents this intialization.
Setting "visible" FALSE will prevent the bound component from appearing on the screen.
Setting "editable" FALSE will ensure that it will not be editable.
A Notify binding causes ViewRec to call the given NotifyClientProc whenever ViewRec changes the value of that component (and maybe some other times as well).
A TryRecognizer binding is how clients may specify their own ways of handling components (see the section on "Introducing new ways of displaying things").
Group bindings are the method by which components of nested aggregates are addressed.
the "altSublist" applies to the return record of procedures; the "sublist" applies to embedded aggregates and the argument record of procedures.
BindingList: TYPE = LIST OF Binding;
Binding: TYPE = RECORD [
name: Id,
it: SELECT type: BindingType FROM
Value => [val: TypedVariable, visible, editable, dontAssign: BOOLFALSE],
Notify => [notify: NotifyClientProc, clientData: REF ANYNIL],
TryRecognizer => [recognizer: Recognizer],
Group => [sublist, altSublist: BindingList ← NIL],
ENDCASE];
BindingType: TYPE = {Value, Notify, TryRecognizer, Group};
Id: TYPE = REF ANY; --actually UNION [ROPE, REF TEXT, REF INT (origin 1) (negatives mean count from end), all];
BadID: ERROR [id: Id];
raised by create procs and GetEltHandle.
all: Id;
Int: TYPE = REF INT;
NotifyClientProc: TYPE = PROC [clientData: REF ANY];
BindAllOfATypeFromTVs: PROC [
aggType, eltType: Type,
name: ROPENIL,
b: Binding --fill the "it" field with what you want
] RETURNS [BindingList];
returns a binding list where everything in "aggType" whose type = "eltType", and whose name = "name", gets bound according to the b.it;
IF name = NIL, THEN the name test is ignored.
BindAllOfATypeFromRefs: PROC [rec, handle: REF ANY, name: ROPENIL, visible, editable, dontAssign: BOOLFALSE]
RETURNS [BindingList];
Uses BindAllOfATypeFromTVs to make Value Bindings. The value used is the REFERENT of handle (NOT handle itself). For instance, to bind all Viewer variables to the value in v1, say "handle: NEW [Viewer ← v1]", not "handle: v1".
BindAllOfANameInType: PROC [agType: Type, name: ROPE, b: Binding] RETURNS [bl: BindingList];
BindAllOfANameFromRef: PROC [agInst: REF ANY, name: ROPE, val: REF ANY, visible, editable, dontAssign: BOOLFALSE] RETURNS [bl: BindingList];
BindingListAppend: PROC [a, b: BindingList] RETURNS [c: BindingList];
This is destructive (i.e. non-functional: it may alter CDR of last CONS cell in "a").
Things to do with RecordViewers once they have been created:
GetWorkingDirectory: PROC [rv: RecordViewer] RETURNS [wDir: ROPE];
SetWorkingDirectory: PROC [rv: RecordViewer, new: ROPE] RETURNS [old: ROPE];
ReLayout: PROC [rv: RecordViewer, targetWidth: INTEGER ← 0, paint, forkResize: BOOL];
If ideal height changes, forkResize tells whether to fork the operation of letting Viewers know this (meaningful only for an rv that's a top-level viewer).
SampleRV: PROC [rv: RecordViewer];
Ensures that the current values are displayed
RedisplayElt: PROC [eh: REF ANY];
Ensures that the current value of this component is displayed.
CallSelectedProc: PROC [in: RecordViewer] RETURNS [refused: BOOL];
NoSelectedProc: ERROR;
DisplayMessage: PROC [rv: RecordViewer, msg: ROPE];
The msg is displayed wherever messages from this RecordViewer are displayed; the message is appended to the others already there, unless it is:
clearMessagePlace: READONLY ROPE;
this message will clear out wherever messages are going to.
SetUserAbort: PROC [who: REF ANY, newLevel: CARDINAL ← 100];
If who gets a RecordViewer, one of its procedure invocations (if any) is the subject;
If who gets an EltHandle, the running invocation of that procedure is the subject.
IncrementUserAbort: PROC [who: REF ANY, deltaLevel: INTEGER];
TestUserAbort: PROC [who: REF ANY] RETURNS [abort: CARDINAL];
TestAndMaybeResetUserAbort: PROC [who: REF ANY, threshold: CARDINAL ← 100] RETURNS [abort: CARDINAL --will be 0 or >= threshold--];
If it was >= threshold, it is reset to 0, and the old value returned; otherwise it is left as it was, and 0 is returned.
ProcessAbort: PROC [who: REF ANY] RETURNS [found: BOOL];
Calls Process.Abort on the running proc, if any.
GetEltHandle: PROC [rv: RecordViewer, name: Path] RETURNS [eh: EltHandle];
Path: TYPE = LIST OF Id; --outermost first
NotFound: ERROR [name: Path];
GetEltHandle raises NotFound[tail(i)[name]] when lost at i'th step.
SIMULA-style coersions between CLASSes:
RVQuaViewer: PROC [RecordViewer] RETURNS [Viewer];
ViewerIsRV: PROC [Viewer] RETURNS [BOOL];
ViewerQuaRV: PROC [Viewer] RETURNS [RecordViewer];
IsRV: PROC [REF ANY] RETURNS [BOOL];
NarrowToRV: PROC [REF ANY] RETURNS [RecordViewer];
For the fun of it:
Help: PROC;
opens a viewer on the document
GetName: PROC [tv: TypedVariable --record or structure--] RETURNS [name: ROPE];
Finds a field named "name", if any, and returns contents as rope, if possible.
Returns NIL if not possible.
Introducing new ways of displaying things:
RegisterRecognizerByType: PROC [r: Recognizer, end: AddPlace, type: Type--unreduced--, reductions: Reductions];
RegisterRecognizerBeforeReductions: PROC [r: Recognizer, end: AddPlace, applyBefore: Reductions];
RegisterRecognizerToApplyAfterAll: PROC [r: Recognizer, end: AddPlace];
Reductions: TYPE = {EquivalenceClass, StripSubranges, TypeClass};
AddPlace: TYPE = {Front, Back--of list of others already there--};
Recognizer: TYPE =
PROC [
t: Type,  --unreduced
onlyRecognize: BOOL, --don't bother to produce the handler and handlerData
specs: BindingList, --what will apply to this element
createOptions: CreateOptions--for aggregate containing this elt
]
RETURNS [
IKnowYou: BOOL,
handler: Handler,
handlerData: REF ANYNIL];
Handler: TYPE = REF ANY; --actually UNION {SimpleHandler, ComplexHandler}
SimpleHandler: TYPE = REF SimpleHandlerRep;
SimpleHandlerRep: TYPE = RECORD [
Parse: ParseProc, --NIL => read-only operation
UnParse: UnParseProc,
Max: MaxProc,
Butt: ButtProc ← NIL--NIL => can't increment or decrement
];
ParseProc: TYPE = PROC [asRope: ROPE, tv: TypedVariable, targType: Type, handlerData: REF ANY] RETURNS [ok: BOOL];
UnParseProc: TYPE = PROC [tv: TypedVariable, targType: Type, handlerData: REF ANY] RETURNS [asRope: ROPE];
MaxProc: TYPE = PROC [tv: TypedVariable, targType: Type, handlerData: REF ANY] RETURNS [maxWidthNeeded: INTEGER, lines: REAL ← 1];
ButtProc: TYPE = PROC [tv: TypedVariable, targType: Type, handler: SimpleHandler, handlerData: REF ANY, which: {Succ, Pred}] RETURNS [new: TypedVariable, msg: ROPENIL];
ComplexHandler: TYPE = REF ComplexHandlerRep;
ComplexHandlerRep: TYPE = RECORD [
producer: ComplexProducer,
Responsible for putting up the viewer for the element.
relayouter: ReLayouter ← NIL,
updater: Updater,
Called when change of value detected.
elementGiver: ElementGiver ← NIL
If this element is an aggregate, this is for getting its elements.
];
ComplexProducer: TYPE = PROC [tv: TypedVariable, context: Context, handlerData: REF ANY] RETURNS [v: Viewer, clientData: REF ANY, sampleable: BOOLTRUE];
ReLayouter: TYPE = PROC [v: Viewer, maxWidth: INTEGER, handlerData, clientData: REF ANY];
Updater: TYPE = PROC [tv: TypedVariable, v: Viewer, handlerData, clientData: REF ANY];
ElementGiver: TYPE = PROC [agg: TypedVariable, which: Id, handlerData, clientData: REF ANY] RETURNS [eh: EltHandle];
Context: TYPE = RECORD [
main: Viewer,
for: RecordViewer,
maxWidth: INTEGER,
name: ROPE,
thisElement: EltHandle,
createOptions: CreateOptions,
notifies: NotifyList,
bindings: BindingList,
toButt: Closure];
NotifyList: TYPE = LIST OF NotifyRequest;
NotifyRequest: TYPE = RECORD [proc: NotifyClientProc, clientData: REF ANY];
FinishPendingBusiness: PROC RETURNS [okToProceed: BOOL];
Upon user action in a Complex Viewer, first call this proc to get permission to handle it.
SetPendingBusiness: PROC [proc: FinishProc ← NIL, data: REF ANYNIL];
FinishProc: TYPE = PROC [data: REF ANY] RETURNS [okToProceed: BOOL];
RecognizeRope, RecognizeAtom, RecognizeNumber, RecognizeEnumeration, RecognizeRecord, RecognizeSequence--also works on ARRAYs--: Recognizer;
The standard ones; already registered.
RecognizeBoolShort: Recognizer;
Displays booleans as simply T or F.
Non-standard; not initially registered.
SimpleEnough: PROC [rt: --an aggregate--Type, specs: BindingList, createOptions: CreateOptions] RETURNS [ok: BOOL, count--of visible elements--: CARDINAL];
For controling the sampling process:
BehaviorOptions: TYPE = RECORD [
delayParms: DelayParms ← [] --controls delay in sampling process
];
These parameters determine the number of milliseconds to pause:
DelayParms: TYPE = RECORD [
min: REAL ← 200,
max: REAL ← 3000,
offset: REAL ← 0,
dActive: REAL ← 0,
dExtant: REAL ← 0,
dElt: REAL ← 0,
dMicroseconds: REAL ← 4E-3, --wait 4 times as long as you work
priority: DelayPriority ← Normal];
DelayPriority: TYPE = {Normal, Background, Foreground};
delayed: READONLY REAL; --running average of sample time, weights are z**n
z: REAL;
behavior: BehaviorOptions;
these are the bits actually used
SetBehavior: PROC [newBehavior: BehaviorOptions ← []];
to set them all at once
END.