QSyntax12b.Scn.Fnt \ParcElemsAlloc Syntax24.Scn.FntSyntax10.Scn.FntSyntax12.Scn.Fnt09 StampElemsAlloc22 Feb 99 Z   9  Z0*LineElemsAlloc LinkElemsAlloc*X*6%*vXSyntax10b.Scn.Fnt*X*X*IY*zY*Y*kY*Y*Y* Z*"?Z* copy =>P @ NameCmdPar=> move => @ NameCmdPar=> deleteMNNN88f8  Chapter2   Chapter2([8#Syntax10.Scn.FntDynamic Attributes  Floating2*<[[*<[] pNameCmdElemTools.SetAttrParstaticresult Value '%staticresult.Value %s.Value'AddpNamesCmdPar#Syntax10.Scn.FntX@wpNamestaticresultCmdPar` pNameCmdElemTools.SetAttrPard DynValue '%d.DynValue %d.Value'AddpNamedCmdParDynValueXXXXXXX#Syntax10.Scn.FntX *   tp:`NameCmdParMenuLockedPanel.Menu.TextSyntax10.Scn.Fnt>@ TextFieldElemsNewNamenameCmdPar  P4ListElemsNewNametypeCmdPar#Syntax10.Scn.Fnt**Boolean String Integer Real LongReal Text 0StaticTextElemsNewNameCmdParName: P4ButtonElemsNewNameCmdElemTools.AddAttrPar'%name.Value' '%type.Value'iAdd P4NameCmdElemTools.DelAttrPar'%name.Value'iDelB4    Chapter2<[8#Syntax10.Scn.FntCommands  Floating2D8#Syntax10.Scn.FntElemTools.SetAttr2  Floating3 l3`[NametxtCmdParStaticText`[@ NameCmdElemTools.SetAttrPartxt Align #CaptionL`[@ NameCmdElemTools.SetAttrPartxt Align #CaptionC`[@ NameCmdElemTools.SetAttrPartxt Align #CaptionR  @ NameCmdElemTools.SetAttrPartxt Value #CaptionValue1  @ NameCmdElemTools.SetAttrPartxt Value #CaptionValue2  @ NameCmdElemTools.SetAttrPartxt Value StaticText Align LeftDefault  Floating2}U8#Syntax10.Scn.FntElemTools.DelAttr   Floating3*([  Floating2ek8#Syntax10.Scn.FntElemTool.Align  Floating3?U8  O  Floating28#Syntax10.Scn.FntElemTools.SetSize   Floating3IE.     Floating3  "NameCmdParButton`[P4NameCmdParButton  Floating28#Syntax10.Scn.FntElemTools.LogParam  Floating3  @ NameCmdElemTools.LogParamPar#Cmd '#Caption'Test it !      Floating28#Syntax10.Scn.FntElemTools.ParamToCaret  Floating3z  0 NameCmdPar~Syntax10i.Scn.Fnt5 0StaticTextElemsNewNameCmdParHTML-HelperSyntax10.Scn.Fnt @ ButtonElemsNewNameCmdElemTools.ParamToCaretParbold @ NameCmdElemTools.ParamToCaretParitalic @ NameCmdElemTools.ParamToCaretParimage @ NameCmdElemTools.ParamToCaretParlink @ NameCmdElemTools.ParamToCaretParunderline @ NameCmdElemTools.ParamToCaretParmark 1JGJ J1G1@   %А  0  Floating28#Syntax10.Scn.FntElemTools.ShowTabs   Floating3  Floating28#Syntax10.Scn.FntElemTools.SetTabPos  Floating3  Chapter2[8#Syntax10.Scn.Fnt Elems.Panel  Floating2(*<[*<[9   'p19NameCmdParMenuLockedPanel.Menu.TextZESyntax10.Scn.Fnt UAreaButtonElemsNewNameCmdElemTools.SetSizeParw hlSyntax10.Scn.FntP4P4PElemsAllocBM.6(ʦ?i???i????i?iiiiii?i?i?i???i?????????i????????i??i?ii?i?i?i????i???????i???????i????i?iiiiii?i??ii?i?i?i?iii?iiiiiiiiiiii?iiiiiii?iiiiiii?iiiiii?i???i????i?iiiiii?i瓓?i罓?i?i???i????i?iiiiii?i瓽?i罽?i?i???i????i?iiiiii?i?i?i XNameCmdElemTools.SetSizeParwlSyntax10.Scn.FntP4P4PElemsAllocBM.6(ʦ?i???i????i?iiiiii?i?i?i???i?????????i????????i??i?ii?i?i?i????i???????i???????i????i?iiiiii?i??ii?i?i?i?iii?iiiiiiiiiiii?iiiiiii?iiiiiii?iiiiii?i???i????i?iiiiii?i瓓?i罓?i?i???i????i?iiiiii?i瓽?i罽?i?i???i????i?iiiiii?i?i?i XNameCmdElemTools.SetSizeParhlSyntax10.Scn.FntP4P4PElemsAllocBM.6(ʦ?i???i????i?iiiiii?i?i?i???i?????????i????????i??i?ii?i?i?i????i???????i???????i????i?iiiiii?i??ii?i?i?i?iii?iiiiiiiiiiii?iiiiiii?iiiiiii?iiiiii?i???i????i?iiiiii?i瓓?i罓?i?i???i????i?iiiiii?i瓽?i罽?i?i???i????i?iiiiii?i?i?i VNameCmdElemTools.AlignParrightlSyntax10.Scn.FntP4P4PElemsAllocBM.6(ʦ?i???i????i?iiiiii?i?i?i???i?????????i????????i??i?ii?i?i?i????i???????i???????i????i?iiiiii?i??ii?i?i?i?iii?iiiiiiiiiiii?iiiiiii?iiiiiii?iiiiii?i???i????i?iiiiii?i瓓?i罓?i?i???i????i?iiiiii?i瓽?i罽?i?i???i????i?iiiiii?i?i?i WNameCmdElemTools.AlignPardownlSyntax10.Scn.FntP4P4PElemsAllocBM.6(ʦ?i???i????i?iiiiii?i?i?i???i?????????i????????i??i?ii?i?i?i????i???????i???????i????i?iiiiii?i??ii?i?i?i?iii?iiiiiiiiiiii?iiiiiii?iiiiiii?iiiiii?i???i????i?iiiiii?i瓓?i罓?i?i???i????i?iiiiii?i瓽?i罽?i?i???i????i?iiiiii?i?i?i UNameCmdElemTools.AlignParcenterlSyntax10.Scn.FntP4P4PElemsAllocBM.6(ʦ?i???i????i?iiiiii?i?i?i???i?????????i????????i??i?ii?i?i?i????i???????i???????i????i?iiiiii?i??ii?i?i?i?iii?iiiiiiiiiiii?iiiiiii?iiiiiii?iiiiii?i???i????i?iiiiii?i瓓?i罓?i?i???i????i?iiiiii?i瓽?i罽?i?i???i????i?iiiiii?i?i?i YNameCmdElemTools.AlignParuplSyntax10.Scn.FntP4P4PElemsAllocBM.6(ʦ?i???i????i?iiiiii?i?i?i???i?????????i????????i??i?ii?i?i?i????i???????i???????i????i?iiiiii?i??ii?i?i?i?iii?iiiiiiiiiiii?iiiiiii?iiiiiii?iiiiii?i???i????i?iiiiii?i瓓?i罓?i?i???i????i?iiiiii?i瓽?i罽?i?i???i????i?iiiiii?i?i?i TNameCmdElemTools.AlignParvcenterlSyntax10.Scn.FntP4P4PElemsAllocBM.6(ʦ?i???i????i?iiiiii?i?i?i???i?????????i????????i??i?ii?i?i?i????i???????i???????i????i?iiiiii?i??ii?i?i?i?iii?iiiiiiiiiiii?iiiiiii?iiiiiii?iiiiii?i???i????i?iiiiii?i瓓?i罓?i?i???i????i?iiiiii?i瓽?i罽?i?i???i????i?iiiiii?i?i?i WNameCmdElemTools.AlignParleftlSyntax10.Scn.FntP4P4PElemsAllocBM.6(ʦ?i???i????i?iiiiii?i?i?i???i?????????i????????i??i?ii?i?i?i????i???????i???????i????i?iiiiii?i??ii?i?i?i?iii?iiiiiiiiiiii?iiiiiii?iiiiiii?iiiiii?i???i????i?iiiiii?i瓓?i罓?i?i???i????i?iiiiii?i瓽?i罽?i?i???i????i?iiiiii?i?i?i @Bm ListElemsNewNameCmd#ValuePar@Syntax10.Scn.Fntq qStaticTextElemsNewNameCmdParAreaButtonq qNameCmdParButtonq qNameCmdParCheckBoxq qNameCmdParColorq qNameCmdParFrameq qNameCmdParListq qNameCmdParPanelq qNameCmdParPasswordq qNameCmdParRadioButtonq qNameCmdParStaticTextq qNameCmdParTextAreaq qNameCmdParTextFieldSyntax8.Scn.FntAreaButtonElems.Insert ButtonElems.Insert CheckBoxElems.Insert ColorElems.Insert FrameElems.Insert ListElems.Insert PanelElems.Insert TextFieldElems.InsertPW RadioButtonElems.Insert StaticTextElems.Insert TextAreaElems.Insert TextFieldElems.Insert ZSyntax12i.Scn.Fnt@ StaticTextElemsNewNameCmdParInsert @ NameCmdParAlignmentSyntax10b.Scn.Fnt0 ButtonElemsNewNameCmdPanel.OpenParInspector.PanelInspector0 NameCmdDocuments.OpenParDynamicAttributes.PanelDyn. Attributes@FrameElemsNew  NameCmdParTabStop@`P4TextFieldElemsNewNametabposCmdElemTools.SetTabPosPar#Value S NameCmdElemTools.SetTabPosParlastLastS NameCmdElemTools.SetTabPosParfirstFirstqNameCmdParPos:@`P4 NameCmdElemTools.ShowTabsParononP4 NameCmdElemTools.ShowTabsParoffoffIP4PopupElemsAlloc?KSyntax10.Scn.FntLinkElemsAllocElems.Text[b wv w 0wX9w9wXXww9*FFGD00# x z oz K      Chapter2[\8#Syntax10.Scn.FntMessages  Floating28#Syntax10.Scn.Fnt Elems.AttrMsg  Floating3'"  Syntax8i.Scn.Fnt a  5 8#Syntax10.Scn.Fnt EnumNames A 8#Syntax10.Scn.Fnt Elems.AttrMsg  w 0 GUE  Floating3 VR E  ( 5   < ?  Chapter2m\8#Syntax10.Scn.Fnt Procedures  Floating2 V8#Syntax10.Scn.FntElems.CreateElem4  Floating3  Floating28#Syntax10.Scn.FntElems.GetBooleanK  Floating3"4 h  Floating28#Syntax10.Scn.FntElems.SetBooleanG  Floating3! f  Floating28#Syntax10.Scn.FntElems.GetClassB  Floating3 <h  Floating28#Syntax10.Scn.FntElems.NamedElemF  Floating3X8#Syntax10.Scn.FntElems.CmdContext/  Floating28#Syntax10.Scn.FntElems.UpdateElem$  Floating36:  Floating2 S> 8#Syntax10.Scn.FntElems.NamedElem? 8#Syntax10.Scn.FntElems.GetString&8#Syntax10.Scn.Fnt Elems.Done" 9 8#Syntax10.Scn.FntElems.SetString.8#Syntax10.Scn.FntElems.UpdateElem  <  Chapter18#Syntax10.Scn.FntExtending Elems  Floating119g ^  2- IElem.Guide.Texth  Chapter218#Syntax10.Scn.Fnt A Simple Elem  Floating2j Zp*[ 6 5 > A` R $ , -  /K &u  Chapter2XG8#Syntax10.Scn.FntA resizeable Elem  Floating2%P" %m '   Chapter2cEj8#Syntax10.Scn.FntA complete Elem  Floating2 4 1 *[\8#Syntax10.Scn.Fnt Elems.ExecMsg?8FoldElemsNew[Syntax10.Scn.Fnt(g(X > msg.X0) & (X < msg.X0 + SHORT(e.W DIV DUnit)) & (Y > msg.Y0) & (Y < msg.Y0 + SHORT(e.H DIV DUnit)) 8 Sj P ) 8? & 6( @ Q . ( 8#Syntax10.Scn.Fnt Elems.AttrMsg* "^ %H  \0* , Elems Christoph Ertl,   Contents  0 Abstract  1 Introduction  2 Handling of Elems  1.1 Inserting Elems  1.2 Resizing Elems  3 Working with PanelElems  4 Basic Elems  4.1 AreaButtonElems  4.2 ButtonElems  4.3 CheckBoxElems  4.4 ColorElems  4.5 FrameElems  4.6 ListElems  4.7 PanelElems  4.8 PasswordElems  4.9 RadioButtonElems  4.10 StaticTextElems  4.11 TextAreaElems  4.12 TextFieldElems  5 Macros  6 How to control Elems  6.1 Inspector.Panel  6.2 Dynamic Attributes  6.3 Commands  6.4 Elems.Panel  6.5 Messages  6.6 Procedures  7 Implementing Elems  7.1 A simple Elem  7.2 A resizeable Elem  7.3 A complete Elem    0 Abstract The Elems package implements a collection of user-interface components. Elems.Elem is the base type of every component and is itself an extension of Texts.Elem. This allows to use every existing Oberon text element as a component in a user-interface panel as well as to use every user-interface component within an Oberon text. Text elements can be grouped for user interaction in so called Panels which again are extensions of Elems.Elem. A Panel is either shown in a window for itself, or within a text as a text element. With help of existing panels a user may reorganise existing or even create new GUI-panels without any programming. Oberon text elements normaly come with some limitations. Our extensions introduce the following new capabilities: - Resizing by the user. - Introduction of an element context (surrounding text), i.e. elements may be aware of their context. - Attributes of other Elems (in the same context) can be referenced with a uniform interface (via messages). - PanelElems allow text elements to be postitioned two-dimensionally within a rectangle (panel) in order to create user interfaces. As extensions of the class Elems several concrete elements (e.g. StaticText, TextField, Checkbox, Radiobutton, ...) were implemented. Note that no changes to the Oberon System have been done.  1 Introduction One of the major advantages of the PanelElems is that they are designed to hold common text elements. A new text element can be used in a panel without changes. The other side is that the Elems, implemented for usage with PanelElems, also can be placed in an Oberon text. Another advantage are the macros which enable the user to create complex parameters for any Oberon command.  2 Handling of Elems  2.1 Inserting Elems Each Elem can be inserted by setting the caret and then calling its insert command (e.g. ButtonElems.Insert). Syntax for inserting commands: ElemName.Insert name cmd par W H [Elem_specific_pars] W and H are only used if greater than 0. Sample: ButtonElems.Insert "" "System.Time" "" 100 0 "MyCaption" ~ A more comfortable way is to use the Elems.Panel and then the Inspector.Panel  2.2 Resizing Elems Each Elem has a sensitive area at the bottom right corner. By clicking into this area with MM and dragging the mouse, the Elem can be resized. Interclicking with ML or MR cancels resizing. Sample:   3 Working with PanelElems  The mouseclicks inside a panel fit to the Oberon mouseclicks. e.g: left sets the cursor, right selects an elem, ... click Action  left Sets the cursor right Selects an elem If no elem is hit a selection with a rubber band can be made middle Moves an elem if hit at its border Moves the selection if no elem is hit Resizes an elem if hit at its bottom-right corner Forwards the click to the hit elem left + middle Copies a selection to the cursor position right + middle Copies the current selection to the cursor position Moving the selection is also possible via the arrow-keys. The selection is moved one pixel. Therefore an exact positioning is possible.  4 Basic Elems  Each Elem has several attributes. The standard attributes are the following. Cmd: The command to be executed. Macros are possible. Par: The parameters of the command 'Cmd'. Macros are possible. Locked: With this attribute enabled the Elem cannot be resized. Some elems do have additional behavior if locked.   4.1 AreaButtonElems   Insert: AreaButtonElems.Insert name cmd par W H Generator: AreaButtonElems.New Sample: AreaButtonElems.Insert "" "System.Time" "" 0 0 An AreaButton is a button which can have various looks. To modify its look the AreaButton must be unlocked. In this mode the behavior of an AreaButton is like that of a PanelElem. To use the button it must be locked. Attributes Locked: Indicates the behavior.  4.2 ButtonElems   Insert: ButtonElems.Insert name cmd par W H caption Generator: ButtonElems.New Sample: ButtonElems.Insert "" "ElemTools.LogInput" "#Caption" 200 0 "TestButton" Clicking on a Button with MM starts the execution of the command given in the attribute 'Cmd'. Moving the mouse outside the Button or interclicking with MR or ML cancels the execution. Attributes Caption: The caption to be displayed.  4.3 CheckBoxElems   Insert: CheckBoxElems.Insert name cmd par W H checked Generator: CheckBoxElems.New Sample: CheckBoxElems.Insert "" "ElemTools.LogInput" "#Value" 0 0 "Y" A CheckBox represents a value which can be 'on' or 'off'. Clicking on a CheckBox with MM toggles the Value and starts the execution of the given Cmd. Moving the mouse outside the CheckBox or interclicking with MR or ML cancels the execution and the value is not toggled. Attributes Value: Indicates whether the Elem is 'on' or 'off'.  4.4 ColorElems   Insert: ColorElems.Insert name cmd par W H currentColor nrOfColors Generator: ColorElems.New Sample: ColorElems.Insert "" "Edit.ChangeColor" "#Value" 0 0 1 16 Clicking on the ColorElem causes a colortable to be opened. From this table a color can be selected. Clicking only inside the elem without moving the mouse to the table selects the previous set color. Attributes Value: The current set color (is also displayed inside the elem). NrOfColors: 16 or 256  4.5 FrameElems   Insert: FrameElems.Insert name cmd par W H 3D Generator: FrameElems.New Sample: FrameElems.Insert "" "" "" 0 0 "Y" Displays a frame. Useful only inside of PanelElems. This elem can be used to make logical groups of elems visible. Attributes Cmd: Not used. Par: Not used. 3D: Indicates whether the frame should be drawn in 3D style.  4.6 ListElems     Insert: ListElems.Insert name cmd par W H Generator: ListElems.New Sample: ListElems.Insert "" "Documents.Open" "'#Value'" Attributes Cmd: Command to be executed if a line is clicked with MM. Scrollbar: Indicates the presence of a scrollbar. Popup: If set the list pops up if it is clicked with MM. Combo: Behaves like popup, additionally the editing of the value is possible. Locked: If not set the contents of the list can be edited using MM + MR. Value: Currently activated line. Note !!: setting this Attribute causes the activation of a line holding this value. The containing text isn't changed. ValueT: The list's text. Line: The current line (Value represents the string at this line). ComboV: The text of the combo field as string. ComboVT: The text of the combo field as text. Sel: Selected lines as string (separated with blanks). SelLine: The number of the first selected line. SelT: Selected lines as text (separated with CR). PopupH: Indicates the maximal height of the popup-list in pixels. The size is calculated automatically until PopupH is reached. The width also depends on the text inside. The module ListElems provides a command called 'ListElems.MoveLine'. With this command simple operations on list lines are possible. ListElems.MoveLine 'fromListName' 'toListName' 'value' Deletes the line containing 'value' in the first list and appends a line with 'value' to the second one. If one list name is not given the action to this list is suppressed. Therefore three different behaviors are possible.  parameter behavior  Move 'A' 'B' '#Value' moves current line from list A to list B Copy '' 'B' '#Value' inserts '#Value' into list B Delete 'A' '' '#Value' deletes current line from list A Example:   4.7 PanelElems   Insert: PanelElems.Insert name cmd par W H Generator: PanelElems.New Sample: PanelElems.Insert "" "" "" 50 100 A Panel can hold textelements (although there exist some exceptions) and they can be placed freely. They also may overlap each other. Further information is found in the chapter 'Working with PanelElems'. Attributes Cmd: Command to be executed when the panel is loaded. The context is set to the panel's text, so parameters can refer elems placed inside the Panel. Border: The width of the border. Menu: Specifies a filename which should be used as menu. This attribute is a dynamic attribute, which must be added as described in the chapter 'Dynamic Attributes'. Locked: If set to 'on' the contents of the panel cannot be changed.  Sample of a PanelElem  4.8 PasswordElems   Insert: TextFieldElems.InsertPW name cmd par W H password Generator: TextFieldElems.NewPW Sample: TextFieldElems.InsertPW "" "ElemTools.LogInput" "#PValue" 0 0 "test" A PasswordElem behaves like a TextFieldElem which echoes only the '*' mark. Attributes PValue The password. This value is not displayed in the Inspector.Panel.  4.9 RadioButtonElems     Insert: RadioButtonElems.Insert name cmd par W H myValue value Generator: RadioButtonElems.New Sample: RadioButtonElems.Insert "sel" "ElemTools.LogInput" "#Value" 0 0 "one" "" RadioButtonElems.Insert "sel" "ElemTools.LogInput" "#Value" 0 0 "two" "" RadioButtons are grouped buttons that represent a set of options. Only one can be selected at a time. All RadioButtons representing the same option set, have to have the same name. Clicking on a RadioButton with MM sets the option (attribute 'Value') to the contents of the attribute 'MyValue' and starts the execution of the command given in the attribute 'Cmd'. Moving the mouse outside the RadioButton or interclicking with MR or ML cancels the execution and the option stays unchanged. An single RadioButton behaves like a switch, which only can be set to 'on'. Attributes Value: The value of the option represented. MyValue: The value of the elem (each Elem should have a different 'MyValue').  4.10 StaticTextElems  Insert: StaticTextElems.Insert name cmd par W H value align borderStyle transparent Generator: StaticTextElems.New Sample: StaticTextElems.Insert "" "System.Time" "" 300 0 "a simple text" "C" "3D" "Y" Displays a static text. Changes can only be made using the Inspector.Panel. Normally only used inside Panels. A StaticText has also the possibility to execute a command. Clicking on it with MM starts the execution of command given in the attribute 'Cmd'. Moving outside the StaticText or interclicking with MR or ML cancels the execution. If using a StaticText as description of a RadioButton or CheckBox it's useful to set the 'Cmd' attribute in that way that it also changes the RadioButton's setting. An example follows below. To realize this funcionality the ElemTools.SetAttr command is used. Attributes Cmd: Is called if the StaticText is clicked with MM. Par: Is used as parameter for Cmd. Value: The text to be displayed. Align: Alignment (Left, Center, Right). Border: Indicates the presence of a border. 3D: With this attribute enabled the border (if used) is drawn in 3D style. Transparent: If checked no background is drawn. Example:  The RadioButtons are called 'option' and their 'MyValue' attributes are set to 'one' and 'two'. Attributes of the StaticText with value 'One' Cmd: ElemTools.SetAttr Par: option 'Value' 'one' The CheckBox is called 'chkbox'. Attributes of the StaticText with value 'Checkbox Description' Cmd: ElemTools.SetAttr Par: chkbox 'Value' 'S'  4.11 TextAreaElems  Insert: TextAreaElems.Insert name cmd par W H Generator: TextAreaElems.New Sample: TextAreaElems.Insert "" "" "" 400 80 A TextArea is an element which holds a scrollable Oberon text. Attributes Cmd: Not used. Par: Not used. Value: The text of the element. Scrollbar: Indicates whether a scrollbar is displayed. ReadOnly: Indicates whether the text can be edited.  4.12 TextFieldElems  Insert: TextFieldElems.Insert name cmd par W H value Generator: TextFieldElems.New Sample: TextFieldElems.Insert "" "ElemTools.LogInput" "#Value" 0 0 "The Value" A TextFieldElem is an element which holds one line of an Oberon text. Pressing the return-key starts the execution of the command given in the attribute 'Cmd'. Attributes Value: The text to be edited. MaxLen: The maximum length (in characters) of the field (0 means no limitation).  5 Macros With the use of macros complex commands and parameters depending on several Elems in the same context are possible. On execution the macros are expanded to the current values. % reference to a foreign attribute. Syntax: %elemName.attributeName # reference to an own attribute. Syntax: #attributeName \ redefines the following character. \n is replaced with a carriage-return. \t is replaced with a tabulator. \s is replaced with the most recent selection. All other characters are written as they are and the backslash is suppressed. Therefore the other macro characters can be written as simple characters. ' is replaced with a double quote " and used to create String parameters blank one blank is always suppressed, thus a concatenation is possible Note: boolean-values are expanded to 'Y' and 'N'. Examples:  Attr Makro Result    Par %text1.Value The Value of the elem named text1.   Par '%text2.Value' The double quoted Value of the elem named text2.   Par %text3.Value .Mod The Value of the elem named text3 with an appended ".Mod".  Cmd #Caption The caption of the button is executed.  Example: MultiOpen Select a protocol:  http  ftp  file   The RadioButtons are called 'protocol' and their 'MyValue' attributes are set to 'http://', 'ftp://' and ''. The TextfieldElem is called 'file'. Attributes of the ButtonElem: Cmd: Documents.Open Par: '%protocol.Value %file.Value'  6 How to control Elems  6.1 Inspector.Panel The inspector is a special editor (Panel), which allows dynamic inspection and modification of the public attributes of an element. With help of the 'Inspect' button a selected element can be inspected. The modification can be applied to a selected element using the 'Apply' button.  Inspector.Panel  6.2 Dynamic Attributes As shown above, every Elem has its own attributes to set its behavior and look. To extend these settings there's the possibility to set additional attributes, so called 'dynamic attributes'. The major advantage of the dynamic attributes is, that values can be stored which otherwise would be stored in a global variable of an extra program or in an extra element. Setting and reading dynamic attributes is handled the same as with default attributes. They can be added with the DynamicAttributes.Panel or by using the ElemTools.AddAttr  command. To delete such an attribute the DynamicAttributes.Panel or the ElemTools.DelAttr  command can be used. Sample without dynamic attributes (value stored in additional elem)    Sample without dynamic attributes (value stored in dynamic attribute of the TextFieldElem)   (inspect the TextField to see the result)  DynamicAttributes.Panel  6.3 Commands ElemTools.SetAttr elemName attributeName Value {attributeName Value} An attribute with the name 'AttributeName' of the Elem with the name 'ElemName' is set to 'Value'. The Elem is searched in the same context. Note: Boolean values must be set using 'Y' or 'N'. Using the parameter 'S' inverts the state of a boolean value. Example:    ElemTools.DelAttr attributeName This command deletes the dynamic attribute with the name 'AttributeName' of the selected elem. More information about dynamic attributes is found in the chapter 'Dynamic Attributes' . ElemTools.Align [L | C | R] [U | V | D] This command requires a selection inside a Panel. All selected Elems are aligned to specified alignment relative to the first selected Elem. If only one Elem is selected then the alignment is used relative to the Panel which holds this Elem. L ... Left C ... Center R ... Right U ... Up V ... Center vertical D ... Down ElemTools.SetSize [W] [H] With this command all selected Elems are set to the same width and/or height according to the first selected Elem. If only one Elem is selected and this is placed inside a panel, then the size of the Panel is adjusted to enclose the element exactly. W ... Width H ... Height Example: ElemTools.SetSize W   ElemTools.LogParam This command writes its parameters to the System.Log. It's useful to find errors in complex parameters, as you can see the result with expanded macros. Example:  Attributes Cmd: ElemTools.LogParam Par: #Cmd '#Caption' The Result in the System.Log is: ---------------------------------- ElemTools.LogParam "Test it !" ---------------------------------- ElemTools.ParamToCaret This command writes his parameter to the caret position. Using this command, simple inserting functionality is possible. Example:  Attributes of the ButtonElem with caption 'bold' Cmd: ElemTools.ParamToCaret Par: ElemTools.ShowTabs "on" | "off" This command is used to switch between the normal display mode and the mode where all elements which have a tabstop display their tabstop number. Using this command, a Panel must be selected. ElemTools.SetTabPos "first" | "last" | pos Sets the tabstop position of the selected elem. This command should only be used for elems placed in panels, because the tabstop position of elements placed in text is given with the their order in this text.  6.4 Elems.Panel This Panel provides most of the Commands of chapter 5.3 in a GUI. Documents.Open Elems.Panel opens this Panel.  Elems.Panel  6.5 Messages Elems.AttrMsg With this Message the Attributes of an Elem can be get, set and enumerated. CONST (* name *) Bool = 7; Char = 6; Int = 3; Real = 4; LongReal = 5; String = 2; Set = 8; Text = 9; (* id *) get = 1; set = 2; enum = 3; enumProps = 4; TYPE EnumNames = PROCEDURE (name : ARRAY OF CHAR; class : INTEGER); AttrMsg = RECORD (Texts.ElemMsg) id : INTEGER; hasProps : BOOLEAN; enum: EnumNames; name: ARRAY 32 OF CHAR; class: INTEGER; s: ARRAY 256 OF CHAR; set: SET; b: BOOLEAN; c: CHAR; i: LONGINT; r: REAL; lr: LONGREAL; t: Texts.Text; END ; - id (get, set, enum, enumProps): Indicates that the attribute should be set or read or that all attributes should be enumerated. If id is set to 'enum' then the enum procedure must be called for each attribute. If id is set to 'enumProps' then the enum procedure must be called for each property of the attribute with name name. - hasProps: This field indicates whether the attribute has special properties. e.g: 'Align' attribute of a StaticText. If hasProps is set to TRUE the next AttrMsg to handle will be one with id set to 'enumProps'. - enum: The Procedure which is called with every Attribute if id is set to 'enum' - name: The name of the referenced attribute. - class (Bool, Int, Real, LongReal, Char, String, Set, Text): The class of the referenced Attribute. - s, set, b, i, r, lr, t : One of this fields is set, depending on the value of class. Note: if t is used the sender/receiver has to copy the text but not the elem.  1.Example: The 'Value' of the Elem called "test" in the current context has to be read and then set to "TestString"; VAR o : Elems.Elem; attr : Elems.AttrMsg; s : ARRAY 256 OF CHAR; BEGIN ...... (* get the elem *) o := Elems.NamedElem("test", Elems.CmdContext); IF o # NIL THEN (* get the value *) attr.id := Elems.get; attr.name := "Value"; o.handle(o, attr); IF Elems.Done THEN IF attr.class = Elems.String THEN COPY(attr.s, s) ELSE (* attr 'Value' is not of type String *) END ELSE (* elem o doesn't have an attribute called 'Value' *) END; (* set the value *) attr.id := Elems.set; attr.name := "Value"; attr.class := Elems.String; COPY("TestString", attr.s); o.handle(o, attr); Elems.UpdateElem(o); (* make changes visible *) ELSE (* the current context doesn't have an elem called 'test' *) END; ...... 2.Example: We want to dump all public attributes of a given element. PROCEDURE MyEnum (name: ARRAY OF CHAR; class : INTEGER); BEGIN Out.String(name); Out.Ln END MyEnum; VAR o : Elems.Elem; attr : Elems.AttrMsg; BEGIN ..... attr.id := Elems.enum; attr.enum := MyEnum; o.handle(o, attr); ......  6.6 Procedures The module Elems provides some procedures to work with text elements with a few simple instructions. Elems.CreateElem(generator : ARRAY OF CHAR) : Elem Creates an element using the commando 'generator' und returns it. If using this procedure to create elements there is no need to import the modul of the Elem. Elems.GetBoolean (e : Texts.Elem; name : ARRAY OF CHAR; VAR b : BOOLEAN); Reads the attribute 'name' of the Elem 'e'. It expects that the value is of type boolean. Elems.Done indicates the success of the operation. There exists a procedure for every supported attribute type. Elems.SetBoolean (e : Texts.Elem; name : ARRAY OF CHAR; b : BOOLEAN); Sets the attribute 'name' of the Elem 'e'. Elems.Done indicates the success of the operation. There exists a procedure for every supported attribute type. Elems.GetClass (e : Texts.Elem; name : ARRAY OF CHAR) : INTEGER; Returns the type of the attribute 'name' of element 'e'. This procedure is useful to use the correct type accessing an unknown attribute. If the attribute doesn't exist Elems.NoClass is returned. The returned value is one of the following: Elems.String, Elems.Int, Elems.Real, Elems.LongReal, Elems.Bool, Elems.Set, Elems.Text, Elems.NoClass Elems.NamedElem (name : ARRAY OF CHAR; context : Texts.Text) : Elem; Searches for an Elem with 'name' in 'context'. If no element is found NIL is returned. Usually the variable Elems.CmdContext is used for 'context'. Elems.CmdContext holds the context of the last command started. Elems.UpdateElem (e : Texts.Elem); Notifies all views that the Elem has to be redrawn. After changing an attribute of an Elem this procedure can be used to make the changes visible. Example: The 'Value' of the Elem called "test" in the current context has to be read and then set to "TestString"; VAR o : Elems.Elem; s : ARRAY 256 OF CHAR; BEGIN ...... (* get the elem *) o := Elems.NamedElem("test", Elems.CmdContext); IF o # NIL THEN (* get the value *) Elems.GetString(o, "Value", s); IF Elem.Done THEN ...... ELSE (* elem o doesn't have an attribute called 'Value' *) END; Elems.SetString(o, "Value", "TestString"); Elems.UpdateElem(o); (* make changes visible *) ELSE (* the current context doesn't have an elem called 'test' *) END; ......  7 Implementing Elems In this chapter only features resulting from the Elems project are discussed. Therefore it is recommended to be familiar with the examples covered in the Edit.Programming.Text. At first let's have a look at the module GUtils which provides some graphical functionality used in the Elems described above. GUtils provides procedures to draw strings, rectangles, frames and so on. That procedures do their output depending on the global variable device, which is either set to GUtils.display or GUtils.printer. The procedures expect that the coordinates are already adapted to actual device units. The procedure GUtils.Unit provides the adaption of coordinates to the depending units. Each procedure gets a parameter f of type Display.Frame. This frame, if set, is used to clip the output. Elems derived from Elems.ElemDesc should always use this feature, because the PanelElems expect this behavior of clipping. For further description of the module GUtils the balloon help can be used. The following examples will describe the stepwise implementation of an text element using the features of the modules contained in this package. The funcionality of this element is to display a counter which is incremented if the element is drawn.  7.1 A simple Elem This simple text element differs from a default Oberon text element in two ways. First it is derived from Elems.ElemDesc and therefore calls Elems.Handle for unhandled messages. The second difference is the usage of the features provided from GUtils. Therefore it is clipable at a frame and the attributes of the base class can be edited via the Inspector.Panel. MODULE CntElems; IMPORT Texts, TextFrames, TextPrinter, Display, Viewers, Files, Fonts, Elems, GU := GUtils; CONST DUnit = TextFrames.Unit; TYPE Elem* = POINTER TO ElemDesc; ElemDesc* = RECORD (Elems.ElemDesc) cnt* : INTEGER; delta* : INTEGER; END; PROCEDURE Draw (e : Elem; x, y, color: INTEGER; fnt : Fonts.Font; f : Display.Frame); VAR h, w, diy : INTEGER; str : ARRAY 10 OF CHAR; BEGIN e.cnt := (e.cnt + e.delta) MOD MAX(INTEGER); (* calculate device depending units *) (* the second parameter of GU.Unit is set TRUE because the size of an element is stored in display units *) h := GU.Unit(e.H, TRUE); w := GU.Unit(e.W, TRUE); (* draw the element using the frame for clipping *) GU.Frame(f, color, x, y, w, h, 1, Display.paint); IF h > GU.Unit(fnt.maxY-fnt.minY, FALSE) THEN diy := (h DIV 2) - (GU.Unit(fnt.minY + fnt.maxY, FALSE) DIV 2); Elems.IntToString(e.cnt, str); GU.String(f, str, x + GU.Unit(2, FALSE), y + diy, w - GU.Unit(4, FALSE), fnt, color, Display.paint, GU.right) END END Draw; PROCEDURE Handle* (e : Texts.Elem; VAR msg : Texts.ElemMsg); VAR copy: Elem; BEGIN WITH e : Elem DO WITH msg: TextFrames.DisplayMsg DO IF ~msg.prepare THEN GU.SetDevice(GU.display); (* set the correct device *) Draw(e, msg.X0, msg.Y0, msg.col, msg.fnt, msg.frame) (* as GU.display is the default device it must not be set back *) END | msg : TextPrinter.PrintMsg DO IF ~msg.prepare THEN GU.SetDevice(GU.printer); (* set the correct device *) Draw(e, msg.X0, msg.Y0, msg.col, msg.fnt, NIL); GU.SetDevice(GU.display) (* set back to the default device *) END | msg : Texts.CopyMsg DO IF msg.e = NIL THEN NEW(copy); msg.e := copy ELSE copy := msg.e(Elem) END; Elems.CopyElem(e, copy); (* copy all the members of the base class *) copy.cnt := 0; copy.delta := e.delta; msg(Texts.CopyMsg).e := copy; | msg: Texts.IdentifyMsg DO msg.mod := "CntElems"; msg.proc := "New" | msg: Texts.FileMsg DO Elems.Handle(e, msg); (* store all the members of the base class *) IF msg.id = Texts.load THEN Files.ReadInt(msg.r, e.delta); ELSIF msg.id = Texts.store THEN Files.WriteInt(msg.r, e.delta) END | msg : TextFrames.TrackMsg DO (* prevent resizing *) ELSE Elems.Handle(e, msg) (* use the default behavior of an Elems.Elem *) END END END Handle; PROCEDURE Init* (e : Elem); BEGIN Elems.Init(e); (* initilize the base class members *) e.handle := Handle; e.cnt := 0; e.delta := 1; e.H := LONG(Fonts.Default.maxY - Fonts.Default.minY + 5) * DUnit; e.W := 50 * DUnit END Init; PROCEDURE New*; VAR e : Elem; BEGIN NEW(e); Init(e); Texts.new := e; END New; PROCEDURE Insert*; VAR e : Elem; m : TextFrames.InsertElemMsg; BEGIN NEW(e); Init(e); m.e := e; Viewers.Broadcast(m) END Insert; END CntElems.  7.2 A resizeable Elem This text element doesn't handle the TextFrames.TrackMsg itself and therefore it is resizeable. It also reads parameters when inserted. MODULE CntElems; IMPORT Texts, TextFrames, TextPrinter, Display, Viewers, Files, Fonts, Elems, GU := GUtils; CONST DUnit = TextFrames.Unit; TYPE Elem* = POINTER TO ElemDesc; ElemDesc* = RECORD (Elems.ElemDesc) cnt* : INTEGER; delta* : INTEGER; END; PROCEDURE Draw (e : Elem; x, y, color: INTEGER; fnt : Fonts.Font; f : Display.Frame); VAR h, w, diy : INTEGER; str : ARRAY 10 OF CHAR; BEGIN e.cnt := (e.cnt + e.delta) MOD MAX(INTEGER); h := GU.Unit(e.H, TRUE); w := GU.Unit(e.W, TRUE); GU.Frame(f, color, x, y, w, h, 1, Display.paint); IF h > GU.Unit(fnt.maxY-fnt.minY, FALSE) THEN diy := (h DIV 2) - (GU.Unit(fnt.minY + fnt.maxY, FALSE) DIV 2); Elems.IntToString(e.cnt, str); GU.String(f, str, x + GU.Unit(2, FALSE), y + diy, w - GU.Unit(4, FALSE), fnt, color, Display.paint, GU.right) END END Draw; PROCEDURE Handle* (e : Texts.Elem; VAR msg : Texts.ElemMsg); VAR copy: Elem; BEGIN WITH e : Elem DO WITH msg: TextFrames.DisplayMsg DO IF ~msg.prepare THEN GU.SetDevice(GU.display); Draw(e, msg.X0, msg.Y0, msg.col, msg.fnt, msg.frame) END | msg : TextPrinter.PrintMsg DO IF ~msg.prepare THEN GU.SetDevice(GU.printer); Draw(e, msg.X0, msg.Y0, msg.col, msg.fnt, NIL); GU.SetDevice(GU.display) END | msg : Texts.CopyMsg DO IF msg.e = NIL THEN NEW(copy); msg.e := copy ELSE copy := msg.e(Elem) END; Elems.CopyElem(e, copy); copy.cnt := 0; copy.delta := e.delta; msg(Texts.CopyMsg).e := copy; | msg: Texts.IdentifyMsg DO msg.mod := "CntElems"; msg.proc := "New" | msg: Texts.FileMsg DO Elems.Handle(e, msg); IF msg.id = Texts.load THEN Files.ReadInt(msg.r, e.delta); ELSIF msg.id = Texts.store THEN Files.WriteInt(msg.r, e.delta) END ELSE Elems.Handle(e, msg) END END END Handle; PROCEDURE Init* (e : Elem); BEGIN Elems.Init(e); e.handle := Handle; e.cnt := 0; e.delta := 1; e.H := LONG(Fonts.Default.maxY - Fonts.Default.minY + 5) * DUnit; e.W := 50 * DUnit END Init; PROCEDURE New*; VAR e : Elem; BEGIN NEW(e); Init(e); Texts.new := e; END New; PROCEDURE Insert*; (* Name Cmd Par W H cntStart delta *) VAR e : Elem; m : TextFrames.InsertElemMsg; s : Texts.Scanner; BEGIN NEW(e); Init(e); Elems.GetPar(e, s); (* read parameters of the base class *) (* read own parameters *) IF ~s.eot & (s.class = Texts.Int) THEN e.cnt := SHORT(s.i); Texts.Scan(s); IF ~s.eot & (s.class = Texts.Int) THEN e.delta := SHORT(s.i) END END; m.e := e; Viewers.Broadcast(m) END Insert; END CntElems.  7.3 A complete Elem The following Elem handles the TextFrames.TrackMsg itself but is also resizeable. It also handles the Elems.AttrMsg to allow editing its attributes. Details of the Elems.AttrMsg are discussed in chapter 6.5. MODULE CntElems; IMPORT Texts, TextFrames, TextPrinter, Display, Viewers, Files, Fonts, Elems, GU := GUtils, Input, Oberon; CONST DUnit = TextFrames.Unit; MR = 0; MM = 1; ML = 2; TYPE Elem* = POINTER TO ElemDesc; ElemDesc* = RECORD (Elems.ElemDesc) cnt* : INTEGER; delta* : INTEGER; END; PROCEDURE Draw (e : Elem; x, y, color: INTEGER; fnt : Fonts.Font; f : Display.Frame); VAR h, w, diy : INTEGER; str : ARRAY 10 OF CHAR; BEGIN e.cnt := (e.cnt + e.delta) MOD MAX(INTEGER); h := GU.Unit(e.H, TRUE); w := GU.Unit(e.W, TRUE); GU.Frame(f, color, x, y, w, h, 1, Display.paint); IF h > GU.Unit(fnt.maxY-fnt.minY, FALSE) THEN diy := (h DIV 2) - (GU.Unit(fnt.minY + fnt.maxY, FALSE) DIV 2); Elems.IntToString(e.cnt, str); GU.String(f, str, x + GU.Unit(2, FALSE), y + diy, w - GU.Unit(4, FALSE), fnt, color, Display.paint, GU.right) END END Draw; PROCEDURE Track(e : Elem; msg : TextFrames.TrackMsg); VAR X, Y : INTEGER; keysum : SET; exec : Elems.ExecMsg; BEGIN keysum := msg.keys; REPEAT Input.Mouse(msg.keys, X, Y); keysum := keysum + msg.keys; Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, X, Y); UNTIL msg.keys = {}; IF  inside elem  & ~(MR IN keysum) THEN exec.e := e; exec.x := msg.X0; exec.y := msg.Y0; exec.f := msg.frame; exec.unload := ML IN keysum; e.handle(e, exec) (* tell the elem to execute its command; this message is handled in Elems.Handle *) END END Track; PROCEDURE HandleAttrMsg (e : Elem; VAR msg : Elems.AttrMsg); BEGIN Elems.Done := TRUE; (* indicate that attribute name is known; maybe it's set back in Elems.Handle *) IF msg.id = Elems.get THEN (* the elem is asked to return a value *) IF msg.name = "Counter" THEN msg.class := Elems.Int; msg.i := e.cnt ELSIF msg.name = "Delta" THEN msg.class := Elems.Int; msg.i := e.delta ELSE (* maybe an attribute of the base class is to be read *) Elems.Handle(e, msg) END; ELSIF msg.id = Elems.set THEN (* the elem is asked to set a value *) IF msg.name = "Counter" THEN IF msg.class = Elems.Int THEN e.cnt := SHORT(msg.i) END ELSIF msg.name = "Delta" THEN IF msg.class = Elems.Int THEN e.delta := SHORT(msg.i) END ELSE Elems.Handle(e, msg) (* maybe an attribute of the base class must be set *) END; ELSIF msg.id = Elems.enum THEN (* the elem is asked to enumerate the names of its attributes *) (* the names used in Elems.get, Elems.set and Elems.enum must be equal *) (* the Inspector.Panel displays the attributes in the order of the enumeration *) Elems.Handle(e, msg); msg.enum("Counter", Elems.Int); msg.enum("Delta", Elems.Int) ELSE Elems.Handle(e, msg) END END HandleAttrMsg; PROCEDURE Handle* (e : Texts.Elem; VAR msg : Texts.ElemMsg); VAR copy: Elem; BEGIN WITH e : Elem DO WITH msg: TextFrames.DisplayMsg DO IF ~msg.prepare THEN GU.SetDevice(GU.display); Draw(e, msg.X0, msg.Y0, msg.col, msg.fnt, msg.frame) END | msg : TextPrinter.PrintMsg DO IF ~msg.prepare THEN GU.SetDevice(GU.printer); Draw(e, msg.X0, msg.Y0, msg.col, msg.fnt, NIL); GU.SetDevice(GU.display) END | msg : Texts.CopyMsg DO IF msg.e = NIL THEN NEW(copy); msg.e := copy ELSE copy := msg.e(Elem) END; Elems.CopyElem(e, copy); copy.cnt := 0; copy.delta := e.delta; msg(Texts.CopyMsg).e := copy; | msg: Texts.IdentifyMsg DO msg.mod := "CntElems"; msg.proc := "New" | msg: Texts.FileMsg DO Elems.Handle(e, msg); IF msg.id = Texts.load THEN Files.ReadInt(msg.r, e.delta); ELSIF msg.id = Texts.store THEN Files.WriteInt(msg.r, e.delta) END | msg : TextFrames.TrackMsg DO Elems.Handle(e, msg); (* try to resize the elem *) IF msg.keys = {MM} THEN Track(e, msg) END (* if not resized handle own tracking *) | msg : Elems.AttrMsg DO HandleAttrMsg(e, msg) (* handle editing of attributes *) ELSE Elems.Handle(e, msg) END END END Handle; PROCEDURE Init* (e : Elem); BEGIN Elems.Init(e); e.handle := Handle; e.cnt := 0; e.delta := 1; e.H := LONG(Fonts.Default.maxY - Fonts.Default.minY + 5) * DUnit; e.W := 50 * DUnit END Init; PROCEDURE New*; VAR e : Elem; BEGIN NEW(e); Init(e); Texts.new := e; END New; PROCEDURE Insert*; (* Name Cmd Par W H cntStart delta *) VAR e : Elem; m : TextFrames.InsertElemMsg; s : Texts.Scanner; BEGIN NEW(e); Init(e); Elems.GetPar(e, s); IF ~s.eot & (s.class = Texts.Int) THEN e.cnt := SHORT(s.i); Texts.Scan(s); IF ~s.eot & (s.class = Texts.Int) THEN e.delta := SHORT(s.i) END END; m.e := e; Viewers.Broadcast(m) END Insert; END CntElems.  file: Elems.Text