<<>> <> <> <> <<>> 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 ) \"StuffAndGet\" \"Stuffs this button name into the Name field and does Name!\") ) Feedback: ( (MouseMoved ) )"; 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[" "]; <> 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 ~ { <> forkData: ForkData ¬ NEW[ForkDataObj ¬ [clientData, event] ]; [] ¬ CedarProcess.Fork[action: HandleButtonsInternal, data: forkData]; }; HandleButtonsInternal: CedarProcess.ForkableProc ~ { <> 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 = { <> 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 = { <> 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.