|
|
|
|
||
|
DEFINITION MODULE DMEditFields; (******************************************************************* Module DMEditFields ('Dialog Machine' DM_V3.0) Copyright (c) 1987-2006 by Alex Itten, Andreas Fischlin and ETH Zurich. Purpose Manages modeless dialogs in any window offered by DMWindows. Remarks Modeless dialogs allow the user to make entries any time he/she wishes, i.e. the user may turn his/her attention in the middle of data entries to other activities before actually completing the dialog by requesting the system to do something with the data entered. EditFields allows the user to enter data to an application program. The entering of values for all elementary data types, characters, strings, numbers (integers, cardinals real), and so called controls (pushbuttons, radiobuttons, checkboxes scrollbars are supported. Method: Any edit field may be defined by means of declarative procedure calls. They define size and position of editing fields and return a variable for later references to it. Fields may be used for the entry of characters, strings, and multi lined text, integers, cardinals, reals, and other input (e.g. pushbuttons, analog input by means of radio buttons, checkboxes and scrollbars). positions within the window are specified in the local window pixel coordinate system (see DMWindows and DMWindIO). DMEditFields uses the current window font for text in string based edit fields and the system font in buttons. Once the specification of an edit item has been made, it is displayed on the screen and the dialog with the user is handled automatically by the Dialog Machine. In Contrast to DMEntryForms, which offers so-called modal dialogs, DMEditFields supports modeless dialogs. In this form of dialog the completion of an user input is not defined exactly and there is no obvious moment for testing for proper syntax and correct range. For this reason in contrast to DMEntryForms the client program must get the values entered by the user and hereby specify the moment of testing. DMEditFields will then test the input and display appropriate error messages. DMEditFields provides an automatic update mechanism for all exported items. But they are not protected from beeing overwritten, so this is your own responsibility. To support full text editing in text fields there exists the module DMMakeTextFields, which represents an extension of this module. This module belongs to the 'Dialog Machine'. Programming o Design Alex Itten 18/01/1987 Andreas Fischlin 23/11/1989 o Implementation Alex Itten 12/08/1987 Andreas Fischlin 23/11/1989 ETH Zurich Systems Ecology CHN E 35.1 Universitaetstrasse 16 8092 Zurich SWITZERLAND URLs: <mailto:RAMSES@env.ethz.ch> <http://www.sysecol.ethz.ch> <http://www.sysecol.ethz.ch/SimSoftware/RAMSES> Last revision of definition: 06/09/1995 AF *******************************************************************) FROM SYSTEM IMPORT ADDRESS; FROM DMWindows IMPORT Window, WindowFrame; (*************************************) (*##### Dialog Declarations #####*) (*************************************) TYPE EditItem; (* Type used to reference an edit item. A variable of this type has to be associated with any edit item and its value will be initialized by any declarative procedure. *) RadioBut; (* Type used to reference a radio button *) ItemType = (charField, stringField, textField, cardField, intField, realField, pushButton, radioButtonSet, checkBox, scrollBar); VAR EditFieldsDone: BOOLEAN; (* returns success of any declaration *) notInstalledEditItem: EditItem; notInstalledRadioBut: RadioBut; (* Read only variables which may be used for variables of type EditItem or RadioBut to denote that the item has not yet been installed. It is a good programming practice to assign this value to all variables of type EditItem or RadioBut during the initialization phase. *) (***************************************************************** The following procedures provide mechanisms to declare within a window so-called edit fields. Edit fields support the entry and editing of elementary data types, i.e. CHAR, strings (1 or several lines of text), INTEGER, CARDINAL, and REAL (in form of a textual field or scrollbars), BOOLEAN (check box), sets (radio buttons) (number data types are also supported with long variants). The so-called make procedures used to declare edit fields typically define fields with a width fw (in char cells) at a particular position within the window given in pixel coordinates. Such edit fields will be displayed by an enclosing, rectangular frame. Every make procedure returns a variable of type EditItem which you must use for later references. After the making of an edit item, the 'Dialog Machine' will automatically display and update it as needed. Furthermore the 'Dialog Machine' provides all editing within these fields without any further action of the client program up to the moment, whent the client program wishes to actually use the values the user has entered. At that time the 'Dialog Machine' will test the data entries for correct syntax and range constraints as specified for each field during its declaration or making. More details on this behavior can be found in comments below, e.g. in the comment on procedure IsChar. For numbers the syntax is that of Modula-2 and the ranges of the accepted values may be provided by entering appropriate values for the interval boundaries (the interval boundaries are also legal values). An open interval may be created by assigning to either of the boundary values the system dependent values MIN(Type) or MAX(Type). The application must always provide a default value, which is displayed when the edit item is drawn on screen for the first time. IMPORTANT NOTE: Due to an implementation restriction you must not call any make procedure within a window restore procedure, since it will trigger an additional update event, which would lead to a never ending chain of update events. The need to call this routine in a restore procedure arises usually, if you wish to modify the position of an edit item within a window which has changed its size. Use a redefine handler (see module DMWindows procedure AddWindowHandler) to program such a task. *****************************************************************) PROCEDURE MakeCharField(u: Window; VAR ei: EditItem; x,y: INTEGER; ch: CHAR; charset: ARRAY OF CHAR); (* Editing of a single character. However, the character ch will only be accepted if its value is equal to one of the characters contained in the string charset (tests are made case sensitive, hence to allow for case insensitive entry you must specify in charset all possibilities, e.g. "AaBbCcDd..." etc.). If any character is acceptable, pass an empty charset (""). Normally exactly one character must be entered by the user, i.e. not none nor more than one character are acceptable. There can be made an exception to this rule so that no entry becomes acceptable too, by listing the character ASCII DEL (177C) as the very first in charset. *) PROCEDURE MakeStringField(u: Window; VAR ei: EditItem; x,y: INTEGER; fw: CARDINAL; string: ARRAY OF CHAR); (* any string may be edited *) PROCEDURE MakeTextField(u: Window; VAR ei: EditItem; x,y: INTEGER; fw,lines: CARDINAL; text: ARRAY OF CHAR); (* Text fields differ from string fields only inasmuch as they offer more than a single line , i.e. 'lines' lines of editing. There is an automatic word wrap arround and scrolling mechanism available. However note, for a full fledged editing of texts there are other routines available from module DMMakeTextFields, which allows to easily implement a full text editor. *) PROCEDURE MakeCardField(u: Window; VAR ei: EditItem; x,y: INTEGER; fw: CARDINAL; (*maximum number of digits expected*) card: CARDINAL; minCard,maxCard: CARDINAL); PROCEDURE MakeLongCardField(u: Window; VAR ei: EditItem; x,y: INTEGER; fw: CARDINAL; (*maximum number of digits expected*) card: LONGCARD; minCard,maxCard: LONGCARD); (* The cardinal is only accepted if its syntax is legal and if card falls within subrange [minCard..maxCard] *) PROCEDURE MakeIntField(u: Window; VAR ei: EditItem; x,y: INTEGER; fw: CARDINAL; (*max number of digits+1 expected*) int: INTEGER; minInt,maxInt: INTEGER); PROCEDURE MakeLongIntField(u: Window; VAR ei: EditItem; x,y: INTEGER; fw: CARDINAL; (*max number of digits+1 expected*) int: LONGINT; minInt,maxInt: LONGINT); (* The integer is only accepted if its syntax is legal and if it falls within subrange [minInt..maxInt] *) PROCEDURE MakeRealField(u: Window; VAR ei: EditItem; x,y: INTEGER; fw: CARDINAL; (*max number of digits+4 expected*) real: REAL; minReal,maxReal: REAL); PROCEDURE MakeLongRealField(u: Window; VAR ei: EditItem; x,y: INTEGER; fw: CARDINAL; (*max number of digits+4 expected*) real: LONGREAL; minReal,maxReal: LONGREAL); (* Real is only accepted if its syntax is legal and if its value falls within interval [minReal..maxReal] *) (***************************************************************** Graphical data entry facilities found in most graphical user interfaces, like scroll bars, check boxes etc., are supported by the following objects. *****************************************************************) PROCEDURE MakePushButton(u: Window; VAR ei: EditItem; x,y: INTEGER; buttonWidth: CARDINAL; (*in char cells*) buttonText: ARRAY OF CHAR; pushButtonAction: PROC); (* The pushbutton labelled with the text buttonText (displayed in the middle of the pushbutton in system font) is placed with its lower left point at x and y. buttonWidth defines the witdh of the pushbutton in character cells (ca. 8 pixel). If the pushbutton is pressed (mouse click within the button), the associated procedure pushButtonAction is called. *) PROCEDURE UseAsDefaultButton(pushButton: EditItem); (* The edit item pushButton is used as the so-called default button in a window during a modal dialog as run by procedure UseWindowModally from module DMWindows. The default button has a thicker border and can be activated by pressing the Enter key or the Return key (the latter only in case that no MakeTextField is present) as an alternative to the activation by the mouse. *) PROCEDURE BeginRadioButtonSet(u: Window; VAR ei: EditItem); (* Radio buttons offers a selection of "one choice out of n possibilities". So every radio button is associated with its logical group called a "radio button set". This set is defined by BeginRadioButonSet and EndRadioButtonSet. Between them you may define as many RadioButtons as you like. Only complete RadioButtonSets may be removed or disabled, not any radio button seperatedly. *) PROCEDURE AddRadioButton(VAR radButt: RadioBut; x,y: INTEGER; text: ARRAY OF CHAR); (* The radio button is placed with its lower left point at x and y. The string text is written to the right of the radio button. *) PROCEDURE EndRadioButtonSet(selRadioButton: RadioBut); (* The initially selected button is defined by passing its reference variable radButt to selRadioButton. *) PROCEDURE MakeCheckBox(u: Window; VAR ei: EditItem; x,y: INTEGER; text: ARRAY OF CHAR; boxChecked: BOOLEAN); (* The check box is placed with its lower left point at x,y and the text is written to the right of the check box. The initial selection is determined by the value assigned to variable boxChecked. *) TYPE Direction = (horizontal, vertical); PROCEDURE MakeScrollBar(u: Window; VAR ei: EditItem; x, y, length: INTEGER; sbd: Direction; minVal,maxVal: REAL; smallStep, bigStep: REAL; curVal: REAL; actionProc: PROC); (* Scrollbars are located by specifying their lower left point (x,y), their direction (sbd) and their length. A minimum length of 60 pixel must be specified for correct appearance and the width is always set to 16 pixel. smallStep defines the intervall which the thumb (white caret in the scrollbar) is moved, when the user clicks within the up or down arrow. bigStep is used when he clicks inside the grey (dotted) "page-up" or "page-down" region. The initial setting of the scroll box is determined by the value assigned to curVal. The action procedure is called once or repeatedly when the user keeps the mouse button pressed. This procedure may be used for example to write the current value anywhere on the screen using standart window IO procedures offered by DMWindIO. *) (************************************************) (*##### EditItem Access and Activation #####*) (************************************************) PROCEDURE EditItemExists(ei: EditItem) : BOOLEAN; (* returns TRUE if edit item ei exists. *) PROCEDURE RadioButtonExists(rb: RadioBut) : BOOLEAN; (* returns TRUE if radio button rb exists. *) PROCEDURE EditItemLevel(ei: EditItem): CARDINAL; (* Returns the level of the sub-program on which the edit item ei has been created. If ei does not exist DMSystem.startUpLevel-1, i.e. 0 is returned. *) PROCEDURE RadioButtonLevel(rb: RadioBut): CARDINAL; (* Returns the level of the sub-program on which the radio button rb has been created. If rb does not exist DMSystem.startUpLevel-1, i.e. 0 is returned. *) (* Each edit item is initially enabled so the user may fully edit their current values. For temporarily disabling resp. reenabling of the editing of items the following two procedures are offered. Note, for easier recognition by the user disabled items are displayed differently, i.e. for string based edit fields such as char, string, text, and all number fields, their frame is dotted; for push buttons, for check-boxes and radio buttons their text is dimmed, for scroll bars their grey-dotted area is blanked and the scroll box is no longer visible. Note however, that the programmer has still full access to disabled items via the Set- and Get/Is-procedures (see below). Only the user has restricted access, i.e. he/she cannot modify the current content of a disabled item. However, he/she can still access the content of a disabled item for transferring it into the clipboard, i.e. the user can make selections in fields which contain text and copy the selected characters into the clipboard by pressing the command-key simultaneously with the key "C". *) PROCEDURE DisableItem(ei: EditItem); PROCEDURE EnableItem(ei: EditItem); PROCEDURE IsEnabled(ei: EditItem): BOOLEAN; PROCEDURE SelectField(ei: EditItem); PROCEDURE ClearFieldSelection (u: Window); (* For string based edit fields the entire content of the field may be selected (hilited) respectively deselected by these procedures. You may use these procedures in a keyboard handler (see DMMaster) to use other keys than the tab-key to allow him/her to change the current selected edit field via the keyboard instead of clicking with the mouse. For example, in tables the "User Interface Guidelines" which are published by AppleŠ recommend to use a combination of the option and one of the arrow keys to skip to adjacent fields. SelectField of a new field implies always the deselection of an eventually previously selected field. *) (***********************************************************) (*##### Associating Client Objects with EditItems #####*) (***********************************************************) PROCEDURE AttachEditFieldObject(ei: EditItem; obj: ADDRESS); PROCEDURE EditFieldObject(ei: EditItem): ADDRESS; (* Attaches to the edit item ei an object in memory at address obj. The attach allows the calling program to associate its own objects, e.g. a data structure with an edit item for reference to that object. Typically an edit handler may profit from this mechanism, since it returns the edit item in which the user event has taken place, which allows the programer to access directly the associated object by calling procedure EditFieldObject from within the edit handler procedure without having frist to search the object. Attach NIL to detach an object. *) (**********************************************) (*##### Altering Values in EditItems #####*) (**********************************************) (* The current setting or value of edit items may also be changed under program control. The reference variable ei is used for specifing the item. The edit item will be redrawn to reflect the change. If ei does not exist then the current value remains unchanged. *) PROCEDURE SetChar(ei: EditItem; newCh:CHAR); PROCEDURE SetString(ei: EditItem; newStr: ARRAY OF CHAR); PROCEDURE SetText(ei: EditItem; VAR text: ARRAY OF CHAR); PROCEDURE SetCardinal(ei: EditItem; newValue: CARDINAL); PROCEDURE SetLongCardinal(ei: EditItem; newValue: LONGCARD); PROCEDURE SetInteger(ei: EditItem; newValue: INTEGER); PROCEDURE SetLongInteger(ei: EditItem; newValue: LONGINT); PROCEDURE SetReal(ei: EditItem; newValue: REAL); PROCEDURE SetLongReal(ei: EditItem; newValue: LONGREAL); PROCEDURE SetRadioButtonSet(ei: EditItem; selRadioButton: RadioBut); PROCEDURE SetCheckBox(ei: EditItem; boxChecked: BOOLEAN); PROCEDURE SetScrollBar(ei: EditItem; r: REAL); (************************************************) (*##### Fetching Values from EditItems #####*) (************************************************) PROCEDURE GetEditItemType(ei: EditItem; VAR it: ItemType); PROCEDURE IsChar(ei: EditItem; VAR ch:CHAR): BOOLEAN; (* If ei exists and its type is charField, the procedure tests whether exactly one character has been entered and whether it is contained in charset as specified during field installation (for more informations about how legal characters are tested please refer to the comments of procedure MakeCharField). If the test is passed IsChar returns TRUE, else it will display an error message, asking the user to modify the character to match a character from charset and then return FALSE. In the latter case the charField will also be selected to give the user a hint, which field was accessed; an important information in case of multiple edit fields within the same window. If ei does not exist or is not of the proper type an error message will be displayed. *) PROCEDURE GetString(ei: EditItem; VAR string: ARRAY OF CHAR); (* Procedure GetString returns the content from a stringField. All characters entered are accepted. In case that ei does not exist or is not of the proper type an error message will be displayed. *) PROCEDURE GetText(ei: EditItem; VAR text: ARRAY OF CHAR); (* Procedure GetText returns the text from a textField. All characters entered are accepted. In case that ei does not exist or is not of the proper type an error message will be displayed. *) PROCEDURE IsCardinal(ei: EditItem; VAR c: CARDINAL): BOOLEAN; PROCEDURE IsLongCardinal(ei: EditItem; VAR c: LONGCARD): BOOLEAN; (* If ei exists and its type is cardField, this procedure tests the current field content for correct syntax and range as specified during field installation. If the test is passed the procedure returns TRUE, else it will display an error message and then return FALSE similar as described under procedure IsChar. In case that the ei does not exist or is not of the proper type an error message will be displayed. *) PROCEDURE IsInteger(ei: EditItem; VAR i: INTEGER): BOOLEAN; PROCEDURE IsLongInteger(ei: EditItem; VAR i: LONGINT): BOOLEAN; (* If ei exists and its type is intField, this procedure tests the current field content for correct syntax and range as specified during field installation. If the test is passed the procedure returns TRUE, else it will display an error message and then return FALSE similar as described under procedure IsChar. In case that ei does not exist or is not of the proper type an error message will be displayed. *) PROCEDURE IsReal(ei: EditItem; VAR r: REAL): BOOLEAN; PROCEDURE IsLongReal(ei: EditItem; VAR r: LONGREAL): BOOLEAN; (* If ei exists and its type is realField, this procedure tests the current field content for correct syntax and range as specified during field installation. If the test is passed the procedure returns TRUE, else it will display an error message and then return FALSE similar as described under procedure IsChar. In case that ei does not exist or is not of the proper type an error message will be displayed. *) PROCEDURE GetRadioButtonSet(ei: EditItem; VAR selRadioButton: RadioBut); (* If ei is of type radioButtonSet the currently selected radio button is returned. In case that ei does not exist or is not of the proper type an error message will be displayed. *) PROCEDURE GetCheckBox(ei: EditItem; VAR boxChecked: BOOLEAN); (* If ei is of type checkBox the current state of the checkbox, i.e. whether it is checked (TRUE) or not (FALSE) is returned. In case that ei does not exist or is not of the proper type an error message will be displayed. *) PROCEDURE GetScrollBar(ei: EditItem; VAR r: REAL); (* If ei is of type scrollbar, the value r corresponding to the current setting of the scrollbar is returned. In case that ei does not exist or is not of the proper type an error message will be displayed. *) (***************************************************************** Modeless Dialog Modeless dialog allows the user to make his entries any time he wishes, i.e. he may turn his attention in the middle of data entries to other activities before he actually terminates the dialog by requesting the system to do something with his data entered during the dialog. DMEditFields offers two possibilities to sample the entered input: 1. When an application needs the entered value to do the job the user has told him, it can get it by the following procedures: GetChar, GetString, GetInteger, GetCardinal, GetReal, GetCheckbox and GetRadioButtonSet (see below). DMEditFields tests in this moment the input for proper syntax and correct range. In case of an error DMEditFields displays an appropriate error message like this: +-----------------------------------------------------+ | | | Your entry "1.2b43" is syntactically incorrect ! | | | | Please enter a number of type REAL... | | | | +=========+ | | | O K | | | +=========+ | | | +-----------------------------------------------------+ The Get…(…) routine returnes FALSE and the returned value is UNDEFINED (it remaines unchanged). In this case you should cancel the current task. The owner window of the erroneous edit field will be brought to the front if necessary and erroneous EditField itself will be selected (hilited) and the user may correct the input while the program is in the same state as before. If there are more than one input needed for a specific task then you may use the following construction: IF Get…(…) AND Get…(…) AND ... THEN DoTheTask END; (* IF *) MODULA-2 evaluates the second and following expressions only if the first or all previous calls have returned TRUE. This method guarantees that no more than one error message will be displayed before the user may correct the input. 2. To prevent confusing situations, the application may test the input either when the input window is removed from front or if the user selects another string based edit field (e.g. presing the tab-key or clicking inside another field). For the first case the application can call Get…(…) inside a mouse handler routine of kind "RemoveFromFront" offered by DMMaster. For the second case this module offers a handler mechanism called an "EditHandler" (see below). It reports (only) elementary input activities such as a mouse click (mouse-up event) or pressing a key (a key-down event). The handler routine returns the engaged edit item and you may determine its type by calling the procedure GetEditItemType (see below). Knowing the item type you are enabled to get the value with one of the procedures listed above. The program structure may look like this: =============== Example =============== MODULE MyProgram; … FROM DMWindows IMPORT Window, CreateWindow,…; FROM DMEditFields IMPORT notInstalledEditItem, EditItem, ItemType, RadioBut,… InstallEditHandler, GetItemType, GetReal, MakeRealField, GetRadioButtonSet,…; FROM DMMaster IMPORT RunDialogMachine,…; … (* global variables *) VAR myWindow : Window; (* owner window of edit fields *) lastEditField, (* last active (string based) field *) myMakeRealField, (* an EditField for REAL's *) myRadioButtonSet : EditItem; (* a radio button set (see above) *) rb,rb2 : RadioBut; (* with two radio buttons *) … PROCEDURE CheckIfFieldChanged(VAR ei: EditItem); VAR it: ItemType; BEGIN GetEditItemType(ei,it); IF (ei <> lastEditField) THEN (* first test if it is a string based edit field and test their value by Get…(…) procedures *) IF (it = realField) AND GetReal(ei,r) THEN … ELSIF (it = …) AND Get…(ei,…) THEN (* test other string based fields *) … END; ELSE (* Get input from so called "controls" *) IF (it = radioButtonSet) THEN GetRadioButtonSet(ei,crb) … END; (* IF *) … END; (* IF *) IF (it <= realField) OR (lastEditField = notInstalledEditItem) THEN lastEditField:= ei (* init or adjust global variable *) END; (* IF *) END CheckIfFieldChanged; … PROCEDURE SetUpWindow; BEGIN … CreateWindow(myWindow,…); … MakeRealField(myWindow,myMakeRealField,…,100.0,100.0,200.0); BeginRadioButtonSet(myWindow,myRadioButonSet); AddRadioButton(rb1,…,"AddRadioButton 1"); AddRadioButton(rb2,…,"AddRadioButton 2"); EndRadioButtonSet(rb2); … InstallEditHandler(myWindow, CheckIfFieldChanged); … END SetUpWindow; … BEGIN (* MAIN *) myWindow:= notExistingWindow; lastEditField := notInstalledEditItem; myMakeRealField:= notInstalledEditItem; myRadioButtonSet:= notInstalledRadioBut; SetUpWindow; … RunDialogMachine; END MyProgram. ============= End of example ============= CAUTION: Be awared that in string based edit fields it is possible, that the user enters intermediate syntactically uncorrect strings or values which are outside the specified ranges. In this case the calling of a Get…(…) procedure within an edit handler will cause a (eventually unwanted) error message. With the following EditHandler it would be impossible to enter a correct number if the user has deleted the input once. It will produce an error message after every key stroke: ============== Bad EditHandler ============== PROCEDURE BadEditHandler(VAR ei: EditItem); VAR it: ItemType; r: REAL; BEGIN GetEditItemType(ei,it); CASE it OF realField : IF GetReal(ei,r) THEN … END; | … END; (* CASE *) END BadEditHandler; =========== End of bad EditHandler =========== *****************************************************************) TYPE EditHandler = PROCEDURE(EditItem); (* EditHandler will be called if an elementary user dialog is finished (e.g. mouse buttom released) or a key pressed. Activities on every type of edit items are reported except those on pushbuttons. If you want to select specific parts of an EditField in the EditHandler procedure, this can be done using the procedure DMMakeTextFields.SetSelection. Selecting is possible for (Long)Card, (Long)Int, (Long)Real, String and Text Fields. Three cases are to be distinguished depending on procedure arguments: - beforeCh ≤ afterCh : The characters in between are selected; - beforeCh > afterCh ≥ 0 : The cursor is inserted at afterCh; - beforeCh > afterCh < 0 : The characters from afterCh to the end of the field are selected. *) PROCEDURE InstallEditHandler(u: Window; eh: EditHandler); PROCEDURE GetEditHandler(u: Window; VAR eh: EditHandler); (* at a time every window may support only one EditHandler routine *) (**********************************) (*##### EditItem Removal #####*) (**********************************) PROCEDURE RemoveEditItem(VAR ei: EditItem); (* Remove the edit item ei from the screen and distroyes its associated memory representation. "notInstalledEditItem" is assigned to ei. IMPORTANT NOTE: Due to an implementation restriction you must not call this procedure within a window restore procedure, since RemoveEditItem will trigger an additional update event, which leads to a never ending chain of update events. The need to call this routine in a restore procedure arises usually, if you wish to modify the position of an edit item within a window which has changed its size. Use a redefine handler (see module DMWindows procedure AddWindowHandler) to program such a task. *) PROCEDURE RemoveAllEditItems(u: Window); (* Removes every edit item which is associated with the window u. (DMWindows RemoveWindow calls this procedure when a window is removed from the screen). *) END DMEditFields.
|
||
|
|
|