PropToolImpl.mesa
Copyright Ó 1991, 1992 by Xerox Corporation. All rights reserved.
Created by Ken Pier, July 7, 1992 10:23 am PDT
DIRECTORY
Atom, AtomButtons, CedarProcess, Commander, EmbeddedButtons, InputFocus, Imager, IO, IOTioga, List, MJSContainers, NodeProps, Rope, SimpleFeedback, TiogaAccessViewers, TiogaActive, TiogaOps, TypeScript, ViewerClasses, ViewerOps, PropRegistry, ViewerTools;
PropToolImpl: CEDAR PROGRAM
IMPORTS Atom, AtomButtons, CedarProcess, Commander, EmbeddedButtons, InputFocus, Imager, IO, IOTioga, List, MJSContainers, NodeProps, Rope, SimpleFeedback, TiogaAccessViewers, TiogaActive, TiogaOps, TypeScript, ViewerOps, PropRegistry, ViewerTools = BEGIN
Viewer: TYPE = ViewerClasses.Viewer;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Doc: TYPE = PropRegistry.Doc;
Key: TYPE = PropRegistry.Key;
Target: TYPE = PropRegistry.Target;
ForkData: TYPE = REF ForkDataObj;
ForkDataObj: TYPE = RECORD [
clientData: REF ANY,
event: LIST OF REF ANY
];
ToolData: TYPE = REF ToolDataObj;
ToolDataObj: TYPE = RECORD [
container, messages, listViewer, nameViewer, valueViewer, historyViewer: Viewer,
savedTarget: Target,
lastOp: ATOM
];
PrintHerald: PROC [r: Rope.ROPE, look: CHAR, t1, t2: Viewer, clearFirst: BOOL ¬ TRUE] = {
IF t1#NIL THEN {
TypeScript.ChangeLooks[t1, look];
IF clearFirst THEN TypeScript.PutRope[t1, cr];
TypeScript.PutRope[t1, r];
TypeScript.ChangeLooks[t1, ' ]; -- clear looks
};
IF t2#NIL THEN {
TypeScript.ChangeLooks[t2, look];
IF clearFirst THEN TypeScript.Reset[t2];
TypeScript.PutRope[t2, r];
TypeScript.ChangeLooks[t2, ' ]; -- clear looks
};
};
PrintRope: PROC [typescript: Viewer, text: Viewer, r: ROPE, clearFirst: BOOL ¬ FALSE] = {
IF typescript#NIL THEN {
IF clearFirst THEN TypeScript.PutRope[typescript, "\n"];
TypeScript.PutRope[typescript, r];
};
IF text#NIL THEN {
IF TypeScript.IsATypeScript[text] THEN {
IF clearFirst THEN TypeScript.Reset[text];
TypeScript.PutRope[text, r];
}
ELSE {
ViewerTools.SetContents[text, IF clearFirst THEN r ELSE Rope.Concat[ViewerTools.GetContents[text], r] ];
};
};
};
FocusInTool: PROC [in: Viewer, toolData: ToolData] RETURNS [inTool: BOOL] = {
RETURN[in=toolData.listViewer OR in=toolData.nameViewer OR in=toolData.valueViewer OR in=toolData.historyViewer OR in=toolData.messages];
};
SetCharProp: PROC [stream: STREAM, key: ATOM, val: REF] ~ {
old: IOTioga.PropList ~ IOTioga.GetCharProps[stream];
new: IOTioga.PropList ~ IOTioga.PropPut[old, key, val];
IOTioga.SetCharProps[stream, new];
};
theButtonDataKey: ATOM ~ $ButtonData;
theButtonDataRope: ROPE ~ "Poppy1
Class: PopUpButton
MessageHandler: PropTool
Menu: (
(( StuffAndGet <ButtonText>) \"StuffAndGet\" \"Stuffs this button name into the Name field and does Name!\")
)
Feedback: (
(MouseMoved <SetCursor bullseye>)
)";
theButtonDataValue: REF ~ NodeProps.DoSpecs[theButtonDataKey, theButtonDataRope];
ListProc: PROC [toolData: ToolData] = {
focus: InputFocus.Focus ¬ InputFocus.GetInputFocus[];
from: Viewer ¬ IF focus#NIL THEN focus.owner ELSE NIL;
viewerClass: ATOM ¬ IF from#NIL THEN from.class.flavor ELSE NIL;
typescript: Viewer ¬ toolData.historyViewer;
val: ROPE ¬ ViewerTools.GetContents[viewer: toolData.valueViewer];
PrintHerald["List: ", heraldLook, typescript, toolData.messages];
PrintRope[NIL, toolData.listViewer, "", TRUE]; -- clear viewer even if error occurs
BEGIN
msg: ROPE;
success: BOOL ¬ FALSE;
t: Target;
props: LIST OF PropRegistry.Key;
rClass: PropRegistry.RegistryClass;
IF from=NIL THEN GOTO NoFocus;
IF FocusInTool[from, toolData] THEN GOTO MoveFocus; -- NO AUTOMATIC RETARGETING
IF (rClass ¬ PropRegistry.GetRegistered[viewerClass])=NIL THEN GOTO NoClass;
IF rClass.getTarget#NIL THEN [success, t] ¬ rClass.getTarget[doc: from, hint: val];
IF NOT success THEN GOTO NoTarget;
toolData.savedTarget ¬ t;
toolData.lastOp ¬ $ListHit;
IF rClass.listProps=NIL THEN GOTO NoListProps;
props ¬ rClass.listProps[doc: from, hint: val];
SELECT TRUE FROM
props=NIL => {
msg ¬ "No properties found";
PrintRope[typescript, toolData.listViewer, msg, TRUE];
};
ENDCASE => {
s: IO.STREAM ¬ IOTioga.CreateTiogaAccessStream[];
IOTioga.SetLooks[s, buttonLooks];
FOR keys: LIST OF PropRegistry.Key ¬ props, keys.rest UNTIL keys=NIL DO
pName: ROPE = Atom.GetPName[keys.first];
msg ¬ Rope.Cat[msg, " ", pName];
s.PutRope[" "];
now tell the stream what the coming char props are
SetCharProp[s, theButtonDataKey, theButtonDataValue]; -- ButtonData
s.PutRope[pName]; -- now put the prop name
SetCharProp[s, theButtonDataKey, NIL]; -- clear ButtonData
ENDLOOP;
TiogaAccessViewers.WriteViewer[IOTioga.WriterFromStream[s], toolData.listViewer];
PrintRope[typescript, NIL, msg, TRUE];
};
PrintRope[NIL, toolData.messages, " Done", FALSE];
EXITS
NoFocus => PrintRope[typescript, toolData.messages, "No input focus - Bogus"];
MoveFocus => PrintRope[typescript, toolData.messages, "Input focus in PropTool - Bogus"];
NoClass => PrintRope[typescript, toolData.messages, IO.PutFR1["Unknown registry class: %g - Bogus", IO.rope[IF viewerClass=NIL THEN "NIL" ELSE Atom.GetPName[viewerClass] ] ]];
NoTarget => PrintRope[typescript, toolData.messages, "No target in focus viewer - Bogus"];
NoListProps => PrintRope[typescript, toolData.messages, IO.PutFR1["No List proc in registry class: %g - Bogus", IO.rope[IF viewerClass=NIL THEN "NIL" ELSE Atom.GetPName[viewerClass] ] ]];
END;
};
GetProc: PROC [toolData: ToolData] = {
focus: InputFocus.Focus ¬ InputFocus.GetInputFocus[];
from: Viewer ¬ IF focus#NIL THEN focus.owner ELSE NIL;
viewerClass: ATOM ¬ IF from#NIL THEN from.class.flavor ELSE NIL;
typescript: Viewer ¬ toolData.historyViewer;
propName: ROPE ¬ ViewerTools.GetContents[viewer: toolData.nameViewer];
val: ROPE ¬ ViewerTools.GetContents[viewer: toolData.valueViewer];
PrintHerald[IO.PutFR1["Get: %g ", IO.rope[propName] ], heraldLook, typescript, toolData.messages];
BEGIN
prop, error, msg: ROPE;
success: BOOL ¬ FALSE;
t: Target;
rClass: PropRegistry.RegistryClass;
IF from=NIL THEN GOTO NoFocus;
IF FocusInTool[from, toolData] THEN GOTO MoveFocus;
IF Rope.Equal[propName, NIL] THEN GOTO NoProp;
IF (rClass ¬ PropRegistry.GetRegistered[viewerClass])=NIL THEN GOTO NoClass;
IF rClass.getTarget#NIL THEN [success, t] ¬ rClass.getTarget[doc: from, hint: val];
IF NOT success THEN GOTO NoTarget;
toolData.savedTarget ¬ t;
toolData.lastOp ¬ $GetHit;
IF rClass.getProp=NIL THEN GOTO NoGetProp;
[prop, error] ¬ rClass.getProp[key: Atom.MakeAtom[propName], doc: from, hint: val];
msg ¬ SELECT TRUE FROM
error#NIL => IO.PutFR1["ERROR: %g", IO.rope[error] ],
prop=NIL => IO.PutFR1["Property %g not found", IO.rope[propName] ],
ENDCASE => IO.PutFR1["%g", IO.rope[prop] ]; -- don't insert extra space or formatting
PrintRope[typescript, toolData.valueViewer, msg, TRUE];
PrintRope[NIL, toolData.messages, " Done", FALSE];
EXITS
NoFocus => PrintRope[typescript, toolData.messages, "No input focus - Bogus"];
MoveFocus => PrintRope[typescript, toolData.messages, "Input focus in PropTool - Bogus"];
NoProp => PrintRope[typescript, toolData.messages, "No property name - Bogus"];
NoClass => PrintRope[typescript, toolData.messages, IO.PutFR1["Unknown registry class: %g - Bogus", IO.rope[IF viewerClass=NIL THEN "NIL" ELSE Atom.GetPName[viewerClass] ] ]];
NoTarget => PrintRope[typescript, toolData.messages, "No target in focus viewer - Bogus"];
NoGetProp => PrintRope[typescript, toolData.messages, IO.PutFR1["No Get proc in registry class: %g - Bogus", IO.rope[IF viewerClass=NIL THEN "NIL" ELSE Atom.GetPName[viewerClass] ] ]];
END;
};
SetProc: PROC [toolData: ToolData] = {
focus: InputFocus.Focus ¬ InputFocus.GetInputFocus[];
from: Viewer ¬ IF focus#NIL THEN focus.owner ELSE NIL;
viewerClass: ATOM ¬ IF from#NIL THEN from.class.flavor ELSE NIL;
typescript: Viewer ¬ toolData.historyViewer;
propName: ROPE ¬ ViewerTools.GetContents[viewer: toolData.nameViewer];
propVal: ROPE ¬ ViewerTools.GetContents[viewer: toolData.valueViewer];
PrintHerald[IO.PutFR1["Set: %g ", IO.rope[propName] ], heraldLook, typescript, toolData.messages];
BEGIN
success: BOOL ¬ FALSE;
t: Target;
rClass: PropRegistry.RegistryClass;
IF from=NIL THEN GOTO NoFocus;
IF FocusInTool[from, toolData] THEN GOTO MoveFocus;
IF Rope.Equal[propName, NIL] THEN GOTO NoProp;
IF Rope.Equal[propVal, NIL] THEN GOTO NoVal;
IF (rClass ¬ PropRegistry.GetRegistered[viewerClass])=NIL THEN GOTO NoClass;
IF rClass.getTarget#NIL THEN [success, t] ¬ rClass.getTarget[doc: from, hint: NIL];
IF NOT success THEN GOTO NoTarget;
toolData.savedTarget ¬ t;
toolData.lastOp ¬ $SetHit;
IF rClass.setProp=NIL THEN GOTO NoSetProp;
rClass.setProp[key: Atom.MakeAtom[propName], doc: from, hint: NIL, prop: propVal, edited: TRUE];
PrintRope[typescript, toolData.messages, IO.PutFR1["Property %g added", IO.rope[propName] ]];
EXITS
NoFocus => PrintRope[typescript, toolData.messages, "No input focus - Bogus"];
MoveFocus => PrintRope[typescript, toolData.messages, "Input focus in PropTool - Bogus"];
NoProp => PrintRope[typescript, toolData.messages, "No property name - Bogus"];
NoVal => PrintRope[typescript, toolData.messages, "No property value - Bogus"];
NoClass => PrintRope[typescript, toolData.messages, IO.PutFR1["Unknown registry class: %g - Bogus", IO.rope[IF viewerClass=NIL THEN "NIL" ELSE Atom.GetPName[viewerClass] ] ]];
NoTarget => PrintRope[typescript, toolData.messages, "No target in focus viewer - Bogus"];
NoSetProp => PrintRope[typescript, toolData.messages, IO.PutFR1["No Set proc in registry class: %g - Bogus", IO.rope[IF viewerClass=NIL THEN "NIL" ELSE Atom.GetPName[viewerClass] ] ]];
END;
};
RemoveProc: PROC [toolData: ToolData] = {
focus: InputFocus.Focus ¬ InputFocus.GetInputFocus[];
from: Viewer ¬ IF focus#NIL THEN focus.owner ELSE NIL;
viewerClass: ATOM ¬ IF from#NIL THEN from.class.flavor ELSE NIL;
typescript: Viewer ¬ toolData.historyViewer;
nameViewer: Viewer ¬ toolData.nameViewer;
propName: ROPE ¬ ViewerTools.GetContents[viewer: nameViewer];
PrintHerald[IO.PutFR1["Remove: %g ", IO.rope[propName] ], heraldLook, typescript, toolData.messages];
BEGIN
success: BOOL ¬ FALSE;
t: Target;
rClass: PropRegistry.RegistryClass;
IF from=NIL THEN GOTO NoFocus;
IF FocusInTool[from, toolData] THEN GOTO MoveFocus;
IF Rope.Equal[propName, NIL] THEN GOTO NoProp;
IF (rClass ¬ PropRegistry.GetRegistered[viewerClass])=NIL THEN GOTO NoClass;
IF rClass.getTarget#NIL THEN [success, t] ¬ rClass.getTarget[doc: from, hint: NIL];
IF NOT success THEN GOTO NoTarget;
toolData.savedTarget ¬ t;
toolData.lastOp ¬ $RemoveHit;
IF rClass.remProp=NIL THEN GOTO NoRemProp;
rClass.remProp[key: Atom.MakeAtom[propName], doc: from, hint: NIL, edited: TRUE];
PrintRope[typescript, toolData.messages, IO.PutFR1["Property %g removed", IO.rope[propName] ]];
EXITS
NoFocus => PrintRope[typescript, toolData.messages, "No input focus - Bogus"];
MoveFocus => PrintRope[typescript, toolData.messages, "Input focus in PropTool - Bogus"];
NoProp => PrintRope[typescript, toolData.messages, "No property name - Bogus"];
NoClass => PrintRope[typescript, toolData.messages, IO.PutFR1["Unknown registry class: %g - Bogus", IO.rope[IF viewerClass=NIL THEN "NIL" ELSE Atom.GetPName[viewerClass] ] ]];
NoTarget => PrintRope[typescript, toolData.messages, "No target in focus viewer - Bogus"];
NoRemProp => PrintRope[typescript, toolData.messages, IO.PutFR1["No Remove proc in registry class: %g - Bogus", IO.rope[IF viewerClass=NIL THEN "NIL" ELSE Atom.GetPName[viewerClass] ] ]];
END;
};
RetargetProc: PROC [toolData: ToolData] RETURNS [success: BOOL ¬ FALSE]= {
viewerClass: ATOM;
BEGIN
msg: ROPE;
rClass: PropRegistry.RegistryClass;
PrintHerald["Retarget: ", heraldLook, toolData.historyViewer, toolData.messages];
IF toolData.savedTarget.v=NIL OR NOT ISTYPE[toolData.savedTarget.v, Viewer] THEN GOTO NoSavedTarget
ELSE {
v: Viewer ¬ NARROW[toolData.savedTarget.v];
viewerClass ¬ v.class.flavor;
IF (rClass ¬ PropRegistry.GetRegistered[viewerClass])=NIL THEN GOTO NoClass;
IF rClass.setTarget=NIL THEN GOTO NoSetTarget;
[success, msg] ¬ rClass.setTarget[doc: v, hint: NIL, t: toolData.savedTarget]; -- NIL targetData is OK (class-specific interpretation)
PrintRope[toolData.historyViewer, toolData.messages, IF success THEN " Done" ELSE IO.PutFR1[" failed: %g", IO.rope[msg]], FALSE];
};
EXITS
NoSavedTarget => PrintRope[toolData.historyViewer, toolData.messages, "No saved target - can't retarget"];
NoClass => PrintRope[toolData.historyViewer, toolData.messages, IO.PutFR1["Unknown registry class: %g - Bogus", IO.rope[IF viewerClass=NIL THEN "NIL" ELSE Atom.GetPName[viewerClass] ] ]];
NoSetTarget => PrintRope[toolData.historyViewer, toolData.messages, IO.PutFR1["No SetTarget proc in registry class: %g - Bogus", IO.rope[IF viewerClass=NIL THEN "NIL" ELSE Atom.GetPName[viewerClass] ] ]];
END;
};
RetryProc: PROC [toolData: ToolData] = {
success: BOOL ¬ FALSE;
success ¬ RetargetProc[toolData];
IF success THEN DoButtons[toolData, LIST[toolData.lastOp] ];
};
NotesProc: PROC [toolData: ToolData] = {
TypeScript.Reset[toolData.messages];
TypeScript.PutRope[toolData.messages, " "]; -- kludge
TypeScript.Flush[toolData.messages]; -- force repaint
};
NameProc: PROC [toolData: ToolData] = {
ViewerTools.SetSelection[toolData.nameViewer];
};
ValueProc: PROC [toolData: ToolData] = {
ViewerTools.SetSelection[toolData.valueViewer];
};
HistoryProc: PROC [toolData: ToolData] = {
ViewerTools.SetSelection[toolData.historyViewer, selectAll];
};
selectAll: ViewerTools.SelPos ¬ NEW[ViewerTools.SelPosRec ¬ [
start: 0,
length: LAST[INT],
pendingDelete: TRUE,
caretPos: before
] ];
heraldLook: CHAR = 'b;
buttonLook: ROPE = "i";
buttonLooks: IOTioga.Looks = IOTioga.LooksFromRope[buttonLook];
buttonFont: Imager.Font = Imager.FindFontScaled["xerox/tiogafonts/helvetica14", 1.0];
cr: ROPE = "\n";
entryHeight: INTEGER = 15;
valueHeight: INTEGER = 240;
textX: INTEGER = 70;
gap: INTEGER = 2;
StuffAndGet: PROC [toolData: ToolData, r: ROPE] = {
PrintRope[NIL, toolData.nameViewer, r, TRUE]; -- put the button name in the Name viewer
DoButtons[toolData, LIST[$GetHit] ]; -- and fake a Name! button press
};
HandleButtons: AtomButtons.HandleButtonProc ~ {
PROC [clientData: REF ANY, event: LIST OF REF ANY];
forkData: ForkData ¬ NEW[ForkDataObj ¬ [clientData, event] ];
[] ¬ CedarProcess.Fork[action: HandleButtonsInternal, data: forkData];
};
HandleButtonsInternal: CedarProcess.ForkableProc ~ {
PROC [data: REF] RETURNS [results: REFNIL]
forkData: ForkData ¬ NARROW[data];
DoButtons[toolData: NARROW[forkData.clientData], event: forkData.event];
};
DoButtons: PROC [toolData: ToolData, event: LIST OF REF ANY] ~ {
hit: ATOM ¬ NARROW[event.first];
SELECT hit FROM
$StuffAndGet => StuffAndGet[toolData, NARROW[event.rest.first]]; -- ROPE with prop name
$ListHit => ListProc[toolData];
$GetHit => GetProc[toolData];
$SetHit => SetProc[toolData];
$RemoveHit => RemoveProc[toolData];
$RetargetHit => [] ¬ RetargetProc[toolData];
$RetrytHit => RetryProc[toolData];
$NameHit => NameProc[toolData];
$ValueHit => ValueProc[toolData];
$HistoryHit => HistoryProc[toolData];
$MessagesHit => NotesProc[toolData];
ENDCASE => SimpleFeedback.Append[$MessageWindow, oneLiner, $Error, IO.PutFR1["PropTool saw unknown event: %g", IO.atom[NARROW[event.first]] ]];
};
ListViewerNotify: EmbeddedButtons.NotifyProc = {
PROC [buttonInfo: ButtonInfo, events: LIST OF REF ANY, targetViewer: Viewer, applicationData: REF] RETURNS [result: REF ← NIL];
newEvent: LIST OF REF ANY ¬ List.Append[events]; -- make a copy of the eventList, because EmbeddedButtons resuses the original
HandleButtons[applicationData, newEvent];
};
CreatePropTool: Commander.CommandProc = {
PROC [cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL];
nextX, nextY: INTEGER ¬ 0;
activeDoc: EmbeddedButtons.ActiveDoc;
toolData: ToolData ¬ NEW[ToolDataObj];
messages: AtomButtons.ButtonLineEntry ¬ [button["Notes!", LIST[LIST[$MessagesHit]], -1, FALSE, buttonFont]];
list: AtomButtons.ButtonLineEntry ¬ [button["List!", LIST[LIST[$ListHit]], -1, FALSE, buttonFont]];
get: AtomButtons.ButtonLineEntry ¬ [button["Get!", LIST[LIST[$GetHit]], -1, FALSE, buttonFont]];
add: AtomButtons.ButtonLineEntry ¬ [button["Set!", LIST[LIST[$SetHit]], -1, FALSE, buttonFont]];
remove: AtomButtons.ButtonLineEntry ¬ [button["Remove!", LIST[LIST[$RemoveHit]], -1, FALSE, buttonFont]];
retarget: AtomButtons.ButtonLineEntry ¬ [button["Retarget!", LIST[LIST[$RetargetHit]], -1, FALSE, buttonFont]];
retry: AtomButtons.ButtonLineEntry ¬ [button["Retry!", LIST[LIST[$RetrytHit]], -1, FALSE, buttonFont]];
name: AtomButtons.ButtonLineEntry ¬ [button["Name!", LIST[LIST[$NameHit]], -1, FALSE, buttonFont]];
value: AtomButtons.ButtonLineEntry ¬ [button["Value!", LIST[LIST[$ValueHit]], -1, FALSE, buttonFont]];
history: AtomButtons.ButtonLineEntry ¬ [button["History!", LIST[LIST[$HistoryHit]], -1, FALSE, buttonFont]];
toolData.container ¬ MJSContainers.Create[viewerFlavor: $VanillaMJSContainer, info: [name: "PropTool", scrollable: TRUE, data: toolData], paint: FALSE];
nextX ¬ AtomButtons.BuildButtonLine[container: toolData.container, x: nextX, y: nextY, clientData: toolData, handleProc: HandleButtons, entries: LIST[messages], lineHeight: entryHeight];
toolData.messages ¬ TypeScript.Create[info: [parent: toolData.container, wx: textX, wy: nextY, wh: entryHeight, border: TRUE, scrollable: TRUE], paint: FALSE];
MJSContainers.ChildXBound[toolData.container, toolData.messages];
ViewerTools.SetContents[toolData.messages, "Messages go here", FALSE];
nextY ¬ nextY + entryHeight + gap;
nextX ¬ 0;
nextX ¬ AtomButtons.BuildButtonLine[container: toolData.container, x: nextX, y: nextY, clientData: toolData, handleProc: HandleButtons, entries: LIST[list], lineHeight: entryHeight];
toolData.listViewer ¬ ViewerOps.CreateViewer[flavor: $Text, info: [parent: toolData.container, wx: textX, wy: nextY, wh: entryHeight, border: TRUE, scrollable: TRUE], paint: FALSE];
MJSContainers.ChildXBound[toolData.container, toolData.listViewer];
ViewerTools.SetContents[toolData.listViewer, "List results go here", FALSE];
activeDoc ¬ TiogaActive.LookupDoc[toolData.listViewer];
EmbeddedButtons.LinkDocToApplication[doc: activeDoc, target: $PropTool, targetViewer: toolData.listViewer, applicationData: toolData, notifyProc: ListViewerNotify];
TiogaOps.Interpret[toolData.listViewer, LIST[$ActivityOn]];
nextY ¬ nextY + entryHeight + gap;
nextX ¬ 0;
nextX ¬ AtomButtons.BuildButtonLine[container: toolData.container, x: nextX, y: nextY, clientData: toolData, handleProc: HandleButtons, entries: LIST[get, add, remove, retarget, retry], lineHeight: entryHeight ];
nextY ¬ nextY + entryHeight + gap;
nextX ¬ 0;
nextX ¬ AtomButtons.BuildButtonLine[container: toolData.container, x: nextX, y: nextY, clientData: toolData, handleProc: HandleButtons, entries: LIST[name], lineHeight: entryHeight ];
toolData.nameViewer ¬ ViewerOps.CreateViewer[flavor: $Text, info: [parent: toolData.container, wx: textX, wy: nextY, wh: entryHeight, border: TRUE, scrollable: TRUE], paint: FALSE];
MJSContainers.ChildXBound[toolData.container, toolData.nameViewer];
ViewerTools.SetContents[toolData.nameViewer, "Property names go here", FALSE];
nextY ¬ nextY + entryHeight + gap;
nextX ¬ 0;
nextX ¬ AtomButtons.BuildButtonLine[container: toolData.container, x: nextX, y: nextY, clientData: toolData, handleProc: HandleButtons, entries: LIST[value], lineHeight: entryHeight ];
toolData.valueViewer ¬ ViewerOps.CreateViewer[flavor: $Text, info: [parent: toolData.container, wx: textX, wy: nextY, wh: valueHeight, border: TRUE, scrollable: TRUE], paint: FALSE];
MJSContainers.ChildXBound[toolData.container, toolData.valueViewer];
ViewerTools.SetContents[toolData.valueViewer, "Property values go here", FALSE];
nextY ¬ nextY + valueHeight + gap;
nextX ¬ 0;
nextX ¬ AtomButtons.BuildButtonLine[container: toolData.container, x: nextX, y: nextY, clientData: toolData, handleProc: HandleButtons, entries: LIST[history], lineHeight: entryHeight ];
toolData.historyViewer ¬ TypeScript.Create[info: [parent: toolData.container, wx: textX, wy: nextY, border: TRUE, scrollable: TRUE], paint: FALSE];
MJSContainers.ChildXBound[toolData.container, toolData.historyViewer];
MJSContainers.ChildYBound[toolData.container, toolData.historyViewer];
PrintHerald["Welcome to the Property Tool of June 11, 1991", heraldLook, toolData.historyViewer, NIL, TRUE];
ViewerOps.PaintViewer[viewer: toolData.container, hint: all, clearClient: TRUE, whatChanged: NIL];
};
Commander.Register["PropTool", CreatePropTool, "Creates a tool to access properties in diverse applications"];
END.