Controlling User Input "I'm sick of writing documentation." Bob Ainsbury Introduction The Toolkit includes three major units to help you prompt the user for input: totIO1, totIO2 and totIO3. These units provide more than 20 different input objects to accommodate all of your user input needs. Objects may be used individually to prompt a user for input, or combined together to build a powerful input form or dialog box. There are objects to prompt for strings, numbers, dates, word-wrapping memo fields, radio buttons, check boxes and lists. In the unlikely event that there is no object type to support your specific needs, you can create your own customized input objects. Two Quick Examples The Toolkit form input facilities are powerful and extensive, but you don't need to understand every feature and facility to use them! Here are a couple of examples to prove the point. The following example, DEMIO1.PAS, is a simple program which prompts the user to enter a real number. program DemoIOOne; {demIO1 - single field input} Uses DOS, CRT, totFAST, totIO1, totIO2; Var Price: FixedRealIOOBJ; begin ClrScr; with Price do begin Init(35,5,8,2); SetLabel('How much was the doppleganger? '); SetValue(250.0); SetMinMax(0.1,12250.0); SetRules(EraseDefault); Activate; Writeln;writeln('You entered ',GetValue); 11-2 User's Guide -------------------------------------------------------------------------------- Done; end; end. Let's analyze the program. At the top of the program is the declaration of a variable called Price, which is of type FixedRealIOOBJ. All form input fields have object names ending in "IOOBJ", which stands for input-output object. The FixedRealIOOBJ object is for the input of real numbers which have a fixed number of decimal places. In the main part of the program, only the three bold statements are mandatory. The Init statement initializes the field. The four parame- ters represent the (X,Y) coordinate of the first character in the field, the number of whole digits, and the number of decimal places, respectively. In this example, the field will be located in column 35, row 5, with 8 significant digits, and 2 decimal places. The Activate command instructs the Toolkit to display the field and wait for user input. The Done method disposes of the object. The other statements are used to customize the field. Any input field can have a label, and usually, the label is displayed to the immediate left of the field. The SetLabel method assigns a label to the input field. By default, the field will have an initial value of zero. The SetValue method is used to assign an initial default value of 250.0. As you may have guessed, the SetMinMax method ensures that the user inputs a number within a specified range. Figure 11.1 shows the error message generated by the user when an invalid number is entered. The SetRules method instructs the Toolkit to erase the default value if the user first presses a non-editing key, e.g. a number. The function method GetValue returns the value entered by the user. Figure 11.1 [SCREEN] Automatic Input Validation The second example, DEMIO2.PAS, shows how to combine a number of fields into a full screen input form: program DemoIOTwo; {demIO2 - full field input} Uses DOS, CRT, totFAST, totIO1, totIO2; Controlling User Input 11-3 -------------------------------------------------------------------------------- Var Name: LateralIOOBJ; Phone: PictureIOOBJ; Price: FixedRealIOOBJ; Keys: ControlkeysIOOBJ; Manager: FormOBJ; Result: tAction; procedure InitVars; {} begin with Name do begin Init(35,5,20,40); SetLabel('Vendor Name'); end; with Phone do begin Init(35,7,'(###) ###-####'); SetLabel('Tel'); end; with Price do begin Init(35,9,8,2); SetLabel('Unit Price'); SetValue(250.0); SetMinMax(0.1,12250.0); SetRules(EraseDefault); end; Keys.Init; end; {InitVars} begin ClrScr; Screen.TitledBox(15,3,65,11,76,79,78,2,' Quicky Input Demo '); Screen.WriteCenter(25,white,'Press TAB to switch fields and press ESC or F10 to end'); InitVars; with Manager do begin Init; AddItem(Keys); AddItem(Name); AddItem(Phone); AddItem(Price); Result := Go; if Result = Finished then {update the database..} else 11-4 User's Guide -------------------------------------------------------------------------------- {call Esc routine}; end; end. In this example, there are three input fields which prompt the user to input a name, a telephone number and a unit price. The object Keys, of type ControlKeysIOOBJ, is used to control which keys allow the user to switch between fields and terminate the input session. By default, the following keys are supported [KEYCAP], [KEYCAP], [KEYCAP] and [KEYCAP]. Every full screen input form must be controlled by a form manager of type FormOBJ. In the example, the instance Manager is used. The procedure InitVars is called to initialize all the input fields using methods similar to the ones used in the previous example. In the body of the main program, the Manager instance is initialized, and each of the IO objects is included in the form by calling the method AddI- tem. Finally, the function method Go is called to instruct the Toolkit to process user input. The method returns a member of the enumerated type tAction, which indicates whether the user escaped or ended nor- mally - in this case by pressing [KEYCAP]. Figure 11.2 illustrates the output generated by this program. Note that the first field can literally scroll, allowing the user to input data which is longer than the visible field. Figure 11.2 [SCREEN] Full Screen Input The Object Hierarchy The totIO units make extensive use of inheritance and polymorphism, and this is reflected in the object hierarchy (see figure 11.3). Controlling User Input 11-5 -------------------------------------------------------------------------------- Figure 11.3 totIO Object Hierarchy 11-6 User's Guide -------------------------------------------------------------------------------- At first, the object hierarchy may seem daunting, but there really isn't much to it. Many of the objects are abstract, and these are shaded gray in the figure. Remember that you should not create instances of abstract objects -- use a descendant. All you have to decide is which object fits your input needs, and then create an instance. For example, if you want to prompt a user for a date, use the DateIOOBJ, or if you want to prompt for a number, choose RealIOOBJ, FixedRealIOOBJ, or IntIOOBJ. It is often difficult to determine which methods are applicable to which objects, especially with a deeply structured object hierarchy. Part 3: Flash Cards lists, in one place, all the methods available for each object in the IO hierarchy. Listed below is a brief description of each input object: StringIOOBJ Basic string input. Provides methods to force the case of text, and to justify text when the user moves to another field. PictureIOOBJ String input which supports a special field mask or picture to control input. The following special format characters govern input: ! Forces letters to upper case # Only accepts numbers @ Only accepts letters and punctuation * Accepts any character For example, the field mask '(###) ###-####' is ideal for US telephone numbers. LateralIOOBJ String input which supports lateral scrolling. For example, the field width can be set to 20 characters wide, but the user might be allowed to enter up to 40 characters. DateIOOBJ Input of dates. The Toolkit supports eight different date formats, and this field will automatically verify that the date entered is valid, and, option- ally, within a specified range. IntIOOBJ Whole number input. Use this object when you want to obtain a whole number, i.e. shortint, byte, integer, word, longint. Provides methods to ensure input is within a specified range. RealIOOBJ Real number input. By using Toolkit compiler direc- tives (discussed on page 3.9), this object can get input of single, double, extended, and comp reals. Provides methods to ensure input is within a speci- fied range. Controlling User Input 11-7 -------------------------------------------------------------------------------- FixedRealIOOBJ Like RealIOOBJ, this object is for the input of real numbers. The object is similar to contemporary data- base packages - there are a fixed number of decimal places, and when the user presses [KEYCAP] the cursor jumps to the right of the decimal place. HexIOOBJ Just for the engineers, this accepts the input of hexadecimal numbers! WWArrayIOOBJ A multi-line word-wrapping field. A one-dimensional string array is assigned to the field. The contents of the array are updated with the user's input. WWLinkIOOBJ Also a multi-line word-wrapping field. A DLLOBJ instance is assigned to the field. The linked list is updated with the user's input. ArrayIOOBJ A scrollable list of options from which the user may select one. The displayed list is based on the contents of a one-dimensional string array. LinkIOOBJ A scrollable list based on the contents of a DLLOBJ instance. CheckIOOBJ A check box field, where the user can check any item in the list. RadioIOOBJ A radio button field where the user can select any one item in the list. StripIOOBJ A "Turbo 6.0 like" button which the user can select, e.g. buttons for Edit, Save, Cancel, etc. Strip3dIOOBJ The same as StripIOOBJ, except that a small 3-D shadow is drawn behind the strip. ButttonIOOBJ A "Norton Utilities-like" box button. HotKeyIOOBJ HotkeyIOOBJ are invisible fields, used to trap for special keys. For example, the key [KEYCAP] might terminate the input session. ControlKeysIOOBJ Defines which keys are used to move between fields, and which keys terminate the input session. Figure 11.4 shows a not-so-useful screen which uses every input type! The on-disk demo file DEMIO3.PAS created the display. Note: All the display colors used by the input objects are con- trolled by the global instance pointer IOTOT. IOTOT controls all the common display characteristics for input fields. This makes it 11-8 User's Guide -------------------------------------------------------------------------------- easy to change the overall look and feel of your program, by just changing the IOTOT values. IOTOT will be discussed in detail later in the chapter (page 11-40). Figure 11.4 [SCREEN] Every Field Type Common Field Methods All the Toolkit IO objects are derived from the base object ItemIOOBJ. This abstract object defines the following fundamental methods which are shared by all the objects: SetActiveStatus(Selectable:boolean); The Toolkit allows you to display fields in a form which are not always accessible, i.e. the user cannot land the cursor on the field. Fields which are displayed, but cannot be highlighted, are referred to as inactive fields. By default, all fields are active, but the method SetActiveStatus can be used to control activity status. Pass True to activate the field, or False to deactivate it. SetHotkey(HK:word); Every field can be optionally selected by pressing a Hotkey. You can assign a hotkey to any field with the method SetHotKey. The passed parameter is the Toolkit key code (refer to the table on page 6-3) that represents the key which will activate the field. For example, the statement SetHotKey(305) assigns the key [KEYCAP] to the object. When the user presses [KEYCAP], the cursor will jump to this field. Be sure to assign different hotkeys to each field. To inform the user of the hotkey, you can use the SetLabel method with the embedded character '~' to highlight the key, e.g. SetLabel('~N~ame');. SetID(ID:word); By default, every field has an ID of zero. You can assign each field a different ID with the SetID method. This ID is used to indicate the highlighted field when a user presses a help function, or when a field raises a signal to indicate that other fields need to be refreshed. The topics of Help and Signals are discussed later. In addition to these basic methods, all visible fields share the fol- lowing two methods: Controlling User Input 11-9 -------------------------------------------------------------------------------- SetLabel(Lbl:string); Every visible field can have a label. The label is usually displayed to the immediate left of the input field. When the field is highlighted, the label is drawn in a different color to signify that the field is active. Use the SetLabel command to specify a string label for any field. Note that the use of the embedded control character '~' is sup- ported. Refer to the discussion of the Screen.WriteHi method on page 5-3 for further information. SetMessage(X,Y:byte; Msg:string); The Toolkit can display an optional message at some location on the screen when the field is highlighted. When the user leaves the field, the message is erased. (Note: the old screen contents which were cov- ered by the message are not restored.) Use the SetMessage method to identify the location of the message and the message text. Listed below is the demo program DEMIO4.PAS, which is a variation on the program DEMIO2.PAS, discussed earlier. The usability has been enhanced by adding informative (!) messages, and the user can jump to the various fields by pressing hotkeys. Figure 11.5 shows the resultant display. program DemoIOFour; {demIO4 - using hotkeys, labels, and messages} Uses DOS, CRT, totFAST, totIO1, totIO2; Var Name: LateralIOOBJ; Phone: PictureIOOBJ; Price: FixedRealIOOBJ; Keys: ControlkeysIOOBJ; Manager: FormOBJ; Result: tAction; procedure InitVars; {} begin with Name do begin Init(35,5,20,40); SetLabel('Vendor ~N~ame'); SetHotkey(305); {Alt-N} SetMessage(22,11,'Enter the vendor''s name, 40 chars Max'); end; with Phone do begin 11-10 User's Guide -------------------------------------------------------------------------------- Init(35,7,'(###) ###-####'); SetLabel('~T~el'); SetHotkey(276); {Alt-T} SetMessage(22,11,'Enter the vendor''s phone number'); end; with Price do begin Init(35,9,8,2); SetLabel('Unit ~P~rice'); SetHotKey(281); {Alt-P} SetMessage(22,11,'Enter the unit price in dollars'); end; Keys.Init; end; {InitVars} begin ClrScr; Screen.TitledBox(15,3,65,12,76,79,78,2,' Quicky Input Demo '); Screen.WriteCenter(25,white,'Press TAB to switched fields and press ESC or F10 to end'); InitVars; with Manager do begin Init; AddItem(Keys); AddItem(Name); AddItem(Phone); AddItem(Price); Result := Go; if Result = Finished then {update the database..} else {call Esc routine}; end; end. Figure 11.5 [SCREEN] Using Labels & Messages Field Types The Object hierarchy illustrates the interrelated nature of the totIO objects. The objects are loosely organized into the following groups: Controlling User Input 11-11 -------------------------------------------------------------------------------- String StringIOOBJ (totIO2) PictureIOOBJ (totIO2) LateralIOOBJ (totIO2) Number IntIOOBJ (totIO2) RealIOOBJ (totIO2) FixedRealIOOBJ (totIO2) HexIOOBJ (totIO2) Date DateIOOBJ (totIO2) Check/Radio CheckIOOBJ (totIO1) RadioIOOBJ (totIO1) Lists ArrayIOOBJ (totIO2) LinkIOOBJ (totIO2) Wordwrap WWArrayIOOBJ (totIO3) WWLinkIOOBJ (totIO3) Buttons StripIOOBJ (totIO1) Strip3dIOOBJ (totIO1) ButtonIOOBJ (totIO1) Hotkeys HotkeyIOOBJ (totIO1) ControlKeysIOOBJ (totIO1) The objects which fall into the category of single line objects are the String, Number and Date object groups. These objects are similar in nature, and share the following common methods: SetIns(InsOn: boolean); Pass True to set the field initially in insert mode, or False to be in overtype mode. SetRules(Rules:byte); The rules control some of the field's editing properties. The following constants are declared in the totIO1 unit: NoRules This is the default state, with no editing rules active. AllowNull Use this rule to instruct the Toolkit to accept an empty or null number field, even though the SetMinMax method has been used to confine the acceptable input within a range. SuppressZero This rule suppresses the display of the default field value in number fields, when the value is zero, i.e. the field is empty, rather than showing "0" or "0.0". 11-12 User's Guide -------------------------------------------------------------------------------- EraseDefault When active, this rule forces the emptying of the field's default value, when a user highlights the field and presses a non-editing key. JumpIfFull This rule forces the user to the next field, as soon as the current field is filled. To specify multiple rules, simply sum the rules. For example, the following statement will enforce three of the rules: SetRules(AllowNull+EraseDefault+JumpIfFull); SetDispChar(Ch:char); Normally, the key pressed by the user is displayed in the input field. However, for security, you may want to echo a different character to the screen. Use this method to suppress the display of the typed char- acters. The Toolkit will use the character Ch to identify that a char- acter has been pressed. This method is ideal for password fields. Note that the field stores the actual typed characters, as this method only affects the way the display is updated. Pass a character of ' ' (space) to instruct the Toolkit to echo the typed characters. SetPadChar(Pad:char); This method controls which character is used to pad out the empty part of the input field. By default, the character used is defined in the IOTOT object (discussed later). With the exception of the FixedRealIOOBJ, the single line input fields also share the following two methods: SetJust(Just:tJust); When the user leaves the current field, the Toolkit can automatically justify the field data. This method controls the justification. The totSTR unit includes the declaration of an enumerated type tJust, which has the following members: JustLeft, JustCenter and JustRight. (Sounds like a good name for a breakfast cereal!) By default, fields are left justified. SetCursor(Curs:tCursPos); Every time the user jumps to a new field, the Toolkit has to decide where to position the cursor. This method controls the cursor position- ing for each field. The totIO1 unit includes the declaration of an enumerated type tJust, which has the following members: CursLeft, CursRight and CursPrev. Use CursLeft and CursRight to force the cursor Controlling User Input 11-13 -------------------------------------------------------------------------------- to the leftmost or rightmost character position respectively. The Cur- sPrev setting (default) instructs the Toolkit to position the cursor where it was the last time the field was edited. String Fields The three string field objects (StringIOOBJ, PictureIOOBJ and Lateral- IOOBJ) are all located in the totIO2 unit. All these objects are descended from the abstract object VisibleIOOBJ, and share the following methods, which were described earlier: SetActiveStatus(Selectable:boolean); SetHotkey(HK:word); SetID(ID:word); SetLabel(Lbl:string); SetMessage(X,Y:byte;Msg:string); Activate; In addition, the following methods are shared by all three objects: SetCase(Cas:tCase); The totSTR unit includes the declaration of an enumerated type tCase which has the following members: Lower, Upper, Proper and Leave. This method can be used to control whether the Toolkit automatically adjusts the case field string when the user leaves the field. Set the case to Leave if you want the string to remain the same case as when it was entered. SetForceCase(On:boolean); The SetForceCase method is used to control whether the case of the string input is adjusted while the user is typing in the string. Pass a True parameter to force the case adjustment (as specified with the method SetCase) every time a character is pressed. Pass a False parame- ter if you want the string to be adjusted only when the user moves to another field. SetValue(Str:string); This method assigns a default value to the field. This value will be displayed when the field is first displayed. GetValue: string; This method should be called (after the user has completed the input session) to determine which string was entered. 11-14 User's Guide -------------------------------------------------------------------------------- Done; This method disposes of the memory consumed by the object, and should be called after all user input is completed. StringIOOBJ The StringIOOBJ is used to obtain string input from the user. The user may enter any alphanumeric character, including upper ASCII and inter- national characters. The only method specific to StringIOOBJ is the important Init method. The syntax of Init is as follows: Init(X,Y,FieldLen:byte); This method must be called first. The first two parameters indicate the (X,Y) coordinate of the first character in the field. The third parame- ter specifies the field length. LateralIOOBJ The LateralIOOBJ object is very similar to StringIOOBJ, except that the field can scroll horizontally, allowing the user to input more charac- ters than are visible in the field. LateralIOOBJ has its own Init method as follows: Init(X,Y,Fieldlen,MaxLen:byte); This method must be called first. The first two parameters indicate the (X,Y) coordinate of the first character in the field, the third parame- ter specifies the visible field length, and the fourth parameter speci- fies the maximum length of the input string, i.e. the scrollable field length. PictureIOOBJ The PictureIOOBJ object gives you character by character control of the input data, and allows you to embed non-editable characters into the field. When you initialize the object using the Init method (discussed later), you must specify a string which represents the field picture. The pic- ture is comprised of non-editable characters and the following four, pre-defined, format characters: # Allows the input of the characters (0-9 . -) and indicates that only numbers may be input. Controlling User Input 11-15 -------------------------------------------------------------------------------- @ Allows only letters of the English alphabet and punctuation characters. * Allows any character the user can find. ! Converts all alphabetical characters to upper case. Any other characters embedded in the picture are treated as fixed and for display only. For example, the picture '(###) ###-####' would be used for the input of US telephone numbers, and the picture '@@@####' might be used for a seven character part number which comprises three letters and four dig- its. The field picture is specified in the Init method as follows: Init(X,Y:byte; Picture:string); This method must be called first. The first two parameters indicate the (X,Y) coordinate of the first character in the field, and the third parameter specifies the field picture. Two other methods are provided to give even more control over which characters can be input. You can either specify the characters which are allowable, or the characters which are not allowable. The following two methods serve this purpose: SetDisAllowChar(Str:string); When this method is used, the Toolkit will not allow the user to input any of the characters specified in the Str. For example, SetDisAllow- Char('\:'); will not allow the user to input a backslash or colon, regardless of the field picture. SetAllowChar(Str:string); This method is the opposite of the previous one, and it is used to identify all the characters which the Toolkit will allow the user to enter. For example, SetAllowChar('AaBbCc0123456789'); will only allow the user to input numbers, or the characters A, B and C. Only use one or the other of these methods. If you only want to stop the user from entering a few specific characters, use SetDisAllowChar. On the other hand, if you only want to allow a few characters to be input, use the SetAllowChar method. 11-16 User's Guide -------------------------------------------------------------------------------- The function method GetValue will return the condensed string excluding the format characters. For example, a telephone number would be returned as 10 digits with no brackets or spaces. The following method will return the fully formatted string, including all embedded format characters: GetPicValue:string; Returns the string entered by the user, including all formatting char- acters. Note: the totStr unit includes the function PicFormat which returns a formatted string. Refer to chapter 14: String Handling for further information. String Field Examples Listed below is an extract of the demo program DEMIO5, which focuses on string fields. Figure 11.6 shows an example of the display generated by the full program. procedure InitVars; {} begin with Field1 do begin Init(40,3,10); SetLabel('Field 1 (StringIOOBJ)'); end; with Field2 do begin Init(40,5,10); SetLabel('Field 2 (StringIOOBJ)'); SetCase(upper); SetValue('hello'); SetRules(EraseDefault+JumpIfFull); end; with Field3 do begin Init(40,7,15,30); SetLabel('Field 3 (LateralIOOBJ)'); end; with Field4 do begin Init(40,9,15,30); Controlling User Input 11-17 -------------------------------------------------------------------------------- SetLabel('Field 4 (LateralIOOBJ)'); SetCase(Upper); SetForcecase(True); SetCursor(CursLeft); end; with Field5 do begin Init(40,11,'(###) ###-####'); SetLabel('Field 5 (PictureIOOBJ)'); end; with Field6 do begin Init(40,13,'!!!***@@@###'); SetLabel('Field 6 (PictureIOOBJ)'); SetDisAllowChar('aAbBcC123@!'); SetRules(EraseDefault); end; with Field7 do begin Init(40,15,10); SetLabel('Field 7 (StringIOOBJ)'); SetDispChar('#'); end; end; {InitVars} Figure 11.6 [SCREEN] Using String Fields Don't forget that IO objects can be used individually, to prompt the user with a single field. Listed below is the demo program DEMIO6.PAS, which prompts the user to enter a string. program DemoIOSix; {demIO6 - single string field input} Uses DOS, CRT, totFAST, totIO1, totIO2, totSTR; var Field: StringIOOBJ; begin with Field do begin Init(40,5,10); SetLabel('Field (StringIOOBJ)'); SetCase(upper); SetValue('hello'); 11-18 User's Guide -------------------------------------------------------------------------------- SetRules(EraseDefault+JumpIfFull); clrscr; Activate; writeln;writeln('You entered ',GetValue); Done; end; end. Number Fields The totIO2 unit includes the following four objects for creating number fields: IntIOOBJ, RealIOOBJ, FixedRealIOOBJ and HEXIOOBJ. All these objects are descended from the abstract object CharIOOBJ, and share the following methods which were described earlier: SetActiveStatus(Selectable:boolean); SetHotkey(HK:word); SetID(ID:word); SetLabel(Lbl:string); SetMessage(X,Y:byte;Msg:string); SetIns(InsOn:boolean); SetRules(Rules:byte); SetDispChar(Ch:char); SetPadChar(Pad:char); Activate; Done; Except for FixedRealIOOBJ, the number fields also support the following two methods: SetJust(Just:tJust); SetCursor(Curs:tCursPos); IntIOOBJ The IntIOOBJ object should be used to prompt for whole numbers, i.e. with a type of byte, word, shortint, integer and longint. The following additional methods are supported by IntIOOBJ objects: Init(X,Y,Len:byte); This method must be called first. The first two parameters indicate the (X,Y) coordinate of the first character in the field. The third parame- ter specifies the field length. SetMinMax(Min,Max:longint); Controlling User Input 11-19 -------------------------------------------------------------------------------- The Toolkit can automatically ensure that the user input falls within a specified range. Use the method SetMinMax to set an input range. Pass two zeros to turn off input validation. If the user tries to input an invalid value, the Toolkit displays a pop-up message explaining the problem (see figure 11.1), and forces the user to input a valid number. SetValue(Val:longint); Use this method to set an initial or default value in the field. GetValue: longint; This function method returns the value input by the user. RealIOOBJ This object is very similar to IntIOOBJ, except that it is designed for the input of real numbers, i.e. numbers with one or more decimal places. Note: The Toolkit supports all the real types, i.e. real, single, double, extended and comp. By default, all these types are mapped to the base type REAL. In other words, if you declare a variable of type EXTENDED, the Toolkit re-maps it so that Turbo Pascal treats it like a basic REAL. If you do want to support all the additional precision reals, edit the file TOTFLAGS.INC and adjust the conditional defines FLOAT and FLOATEM. Refer to page 3-9 for further information. In addition to the common methods, the following methods are supported by RealIOOBJ objects: Init(X,Y,Len:byte); This method must be called first. The first two parameters indicate the (X,Y) coordinate of the first character in the field. The third parame- ter specifies the field length. SetMinMax(Min,Max:extended); The Toolkit can automatically ensure that the user input falls within a specified range. Use the method SetMinMax to set an input range. Pass two zeros to suppress input validation. If the user tries to input an invalid value, the Toolkit displays a pop-up message explaining the problem (see figure 11.1), and forces the user to input a valid number. 11-20 User's Guide -------------------------------------------------------------------------------- SetValue(Val:extended); Use this method to set an initial or default value in the field. SetENotation(On:boolean); This method controls whether the user is allowed to enter the number in E-notation, e.g. 4.435623E+12. Pass True to allow the user to input E-notation, or False to disallow it. GetValue: extended; This function method returns the value input by the user. FixedRealIOOBJ You may have used some commercial database or financial software pro- grams which provide special cursor control when the user is inputting real numbers. For example, when the user presses [KEYCAP], the cursor automatically jumps to the right of the fixed decimal place. The Fixe- dRealIOOBJ object brings this capability to the Toolkit. (The cursor and key reactions emulate the FoxPro database package - we liked it, so we copied it!) FixedRealIOOBJ objects provide the following additional methods: Init(X,Y,Whole,DP:byte); This method must be called first. The first two parameters indicate the (X,Y) coordinate of the first character in the field. The third parame- ter specifies the maximum number of whole numbers, and the last parame- ter specifies the maximum number of decimal places. SetMinMax(Min,Max:extended); The Toolkit can automatically ensure that the user input falls within a specified range. Use the method SetMinMax to set an input range. Pass two zeros to suppress input validation. If the user tries to input an invalid value, the Toolkit displays a pop-up message explaining the problem (see figure 11.1), and forces the user to input a valid number. SetValue(Val:extended); Use this method to set an initial or default value in the field. GetValue: extended; This function method returns the value input by the user. Controlling User Input 11-21 -------------------------------------------------------------------------------- HEXIOOBJ If you want a user to enter HEX numbers, HEXIOOBJ is the object for you. HEXIOOBJ is very similar to IntIOOBJ, with the main difference that HEX allows the user to input numbers and the letters A, B, C, D, E and F. As well as the common number methods, HEXIOOBJ supports the following methods: Init(X,Y,Len:byte); This method must be called first. The first two parameters indicate the (X,Y) coordinate of the first character in the field. The third parame- ter specifies the field length. SetMinMax(Min,Max:longint); The Toolkit can automatically ensure that the user input falls within a specified range. Use the method SetMinMax to set an input range. Pass two zeros to turn off input validation. SetValue(Val:longint); Use this method to set an initial or default value in the field. GetValue: longint; This function method returns the value input by the user. Note: in Turbo Pascal HEX numbers are preceded by a '$' character. For example, the following method calls have the same effect: SetValue($FF); SetValue(255); Formatting Number Fields The objects IntIOOBJ, RealIOOBJ and FixedREALIOOBJ all provide optional formatting capabilities, which can be used to control the format of the displayed number when the user leaves the field, i.e. when the user is editing, the number is unformatted, but when the user exits the field, the number can be formatted in a variety of styles. For example, the number -123456.78 might be formatted as $123,456.78 CR. If you want formatted number fields, you must call the following method: 11-22 User's Guide -------------------------------------------------------------------------------- InitFormat; This method instructs the object to format the field when the user leaves the field. The totSTR unit includes the object FmtNumberOBJ, which is designed to format numbers. The FmtNumber object includes the following methods: SetPrefixSuffix(P,S:char); SetSign(S:tSign); SetSeparators(P,T,D:char); SetJustification(J:tJust); These methods are described in detail in chapter 14: String Handling. Refer to this chapter for further information. Behind the scenes, when the number method InitFormat is called, the Toolkit creates a private FmtNumberOBJ instance. The number method For- matPtr can be used to access the FmtNumberOBJ instance using the fol- lowing syntax: FormatPtr^.[method] For example, the following code fragment formats an integer field with a '$' prefix and embedded commas. var Field: IntIOOBJ; begin with Field do begin Init; InitFormat; FormatPtr^.SetPrefixSuffix('$',#0); FormatPtr^.SetSeparators(' ',',','.'); {...} end; end. Setting the Default Format The totIO2 unit includes FmtNumberTOT, a pointer to an object instance of type FmtNumberOBJ. This instance defines the default formatting that will be applied to real and integer fields during full-screen input. Whenever a number field object's method InitFormat is called, the field's initial formats are copied from FmtNumberTOT. Controlling User Input 11-23 -------------------------------------------------------------------------------- You can control each field's default value by setting FmtNumberTOT to specifically meet your needs, before you call the InitFormat method. For example, if you want all your number fields to be formatted with a suffix of FR, set the default with the following statement: FmtNumberTOT.SetPrefixSuffix('','FR'); All number objects initialized after this statement will assume the FR suffix. Number Field Examples Back on the first page of this chapter you saw an example of how to prompt the user for a real number using FixedRealIOOBJ. Take another look at page 11.1 and see if the statements make more sense now! Listed below is an extract of the demo program DEMIO8.PAS which illus- trates how to use many of the methods discussed in this section. Figure 11.7 shows an example of the display generated by the full program. procedure InitVars; {} begin with Field1 do begin Init(40,3,5); SetLabel('Field 1 (IntIOOBJ)'); end; with Field2 do begin Init(40,5,5); InitFormat; SetLabel('Field 2 (IntIOOBJ)'); SetValue(69); SetRules(EraseDefault+JumpIfFull); end; with Field3 do begin Init(40,7,10); SetLabel('Field 3 (RealIOOBJ)'); end; with Field4 do begin Init(40,9,15); InitFormat; FormatPtr^.SetSeparators('*',',','.'); FormatPtr^.SetPrefixSuffix('','FR'); FormatPtr^.SetJustification(JustRight); SetLabel('Field 4 (RealIOOBJ)'); 11-24 User's Guide -------------------------------------------------------------------------------- SetCursor(CursLeft); end; with Field5 do begin Init(40,11,8,2); SetLabel('Field 5 (FixedRealIOOBJ)'); end; with Field6 do begin Init(40,13,5); SetLabel('Field 6 (HEXIOOBJ)'); end; Keys.Init; end; {InitVars} Figure 11.7 [SCREEN] Using Number Fields Date Fields The DateIOOBJ object is used to prompt the user to input a date. The Toolkit supports eight different date formats. The totDATE unit includes an enumerated type tDate which has the following members: MMDDYY, MMDDYYY, MMYY, MMYYYY, DDMMYY, DDMMYYYY, YYMMDD, YYYYMMDD. Whenever you work with dates, you must identify the desired date for- mat. Refer to chapter 13: Managing Dates for a full description of all the date manipulation routines which complement DateIOOBJ. There are a wealth of functions to convert dates to and from Julian, Gregorian and string formats. For example, the JultoStr and StrtoJul functions con- vert between Julian and string formats. DateIOOBJ is a descendant of CharIOOBJ, and inherits the following, previously described, methods: SetActiveStatus(Selectable:boolean); SetHotkey(HK:word); SetID(ID:word); SetLabel(Lbl:string); SetMessage(X,Y:byte;Msg:string); SetIns(InsOn:boolean); SetRules(Rules:byte); SetDispChar(Ch:char); SetPadChar(Pad:char); Activate; Done; Controlling User Input 11-25 -------------------------------------------------------------------------------- The DateIOOBJ object manipulates dates in Julian format, where dates are specified as longints. DateIOOBJ includes the following methods: Init(X,Y:byte; DateFmt:tDate); This method must be called first. The first two parameters indicate the (X,Y) coordinate of the first character in the field. The third parame- ter is a member of the enumerated type tDate, and indicates which for- mat of date is to be input, e.g. MMDDYY or DDMMYY. SetMinMax(Min,Max:longint); The Toolkit will automatically verify if proper dates are entered, i.e. that the month is between 1 and 12, and the days are valid for the specified month. The method SetMinMax can also be used to specify a date range. Pass two zeros to turn off input validation. If the user tries to input an invalid date or an out of range date, the Toolkit displays a pop-up message explaining the problem (see figure 11.8), and forces the user to input a valid number. SetValue(Date: longint); Use this method to set an initial or default value in the field. The date is passed as a Julian (longint) value. GetValue:longint; This function method returns the Julian date value input by the user. Listed below is the demo program DEMIO9.PAS which prompts the user for the input of a single date. Figure 11.8 is an example of the date validation performed by the Toolkit. program DemoIONine; {demIO9 - single date field input} Uses DOS, CRT, totIO1, totIO2, totDate; Var Birthday: DateIOOBJ; begin ClrScr; with Birthday do begin Init(35,5,MMDDYY); SetLabel('When is your next birthday? '); Activate; 11-26 User's Guide -------------------------------------------------------------------------------- Writeln; writeln('You entered the Julian date',GetValue); writeln('i.e. ',JultoStr(GetValue,MMDDYY)); Done; end; end. Figure 11.8 [SCREEN] Single Date Input Listed below is an extract of the demo program DEMIO10.PAS which illus- trates how to use many of the methods discussed in this section. Figure 11.9 shows an example of the display generated by the full program. procedure InitVars; {} begin with Field1 do begin Init(40,3,MMDDYY); SetLabel('Field 1 (MMDDYY)'); end; with Field2 do begin Init(40,5,MMDDYY); SetLabel('Field 2 (MMDDYY)'); SetValue(TodayInJul); SetRules(EraseDefault+JumpIfFull); end; with Field3 do begin Init(40,7,DDMMYYYY); SetLabel('Field 3 (DDMMYYYY)'); end; with Field4 do begin Init(40,9,DDMMYYYY); SetLabel('Field 4 (DDMMYYYY)'); SetMinMax(GregtoJul(3,1,1992),GregtoJul(3,31,1992)); SetCursor(CursLeft); end; Keys.Init; end; {InitVars} Controlling User Input 11-27 -------------------------------------------------------------------------------- Figure 11.9 [SCREEN] Using Date Fields Check Boxes and Radio Buttons Two common elements of contemporary input forms are check boxes and radio buttons. Both these objects provide ways of choosing items from lists of options. Figure 11.10 (listed later in the section) illus- trates both types of objects. A check box object provides a list of options with each item having its own check box displayed to the left of the item, e.g. [X] Toast. Any number of items in the list can be selected. An item's selection status is toggled by hitting the [KEYCAP] or by clicking the mouse cursor on it. Selected items have an X in the adjacent check box. Radio buttons are similar to check boxes, except that only one item can be selected. When an item is selected, the previously selected item is deselected. The selected item has a dot in the "button" next to it, e.g. (.) Ham Sandwich. The Toolkit objects CheckIOOBJ and RadioIOOBJ are located in the totIO1 unit, and are descended from VisibleIOOBJ. Both objects, therefore, inherit the following, previously described, methods: SetActiveStatus(Selectable:boolean); SetHotkey(HK:word); SetID(ID:word); SetLabel(Lbl:string); SetMessage(X,Y:byte;Msg:string); Activate; Done; The objects are part of the MultLineIOOBJ object family and also inherit the following method: SetBoxOn(On:boolean); All multi-line objects can be optionally displayed in a box. By default, the objects are not displayed in a box. To activate the box display, pass a True parameter. Both objects share the following common methods: Init(X1,Y1,width,depth:byte; Title:string); 11-28 User's Guide -------------------------------------------------------------------------------- This method should be called first. The first two parameters specify the (X,Y) coordinate of the top left of the object. The third and fourth parameters specify the width and depth of the object in charac- ters. The width should be large enough to accommodate the longest item, and leave room for the box, if the box is active. The depth should be large enough to accommodate the title, all the items and, optionally, the box. AddItem(Str:string; HK:word; Selected:boolean); This method adds an item to the list. The first parameter specifies the text to be displayed - don't forget that embedded '~' characters can be used to highlight text (see page 5-3 for further information). The second parameter is an optional hot key code, which the user can press to jump to this item and automatically select/deselect it. Pass a value of zero if you don't want to specify a hotkey. The third parameter indicates whether the item is selected or not. You can add as many items as will fit in the object's initialized dimensions. Note: the generic method SetHotKey is used to specify the hotkey for the entire object. If the user presses the generic hotkey, the object will be selected, and the last active item will be high- lighted. The individual item hotkeys allow the user to both select the object, and immediately activate the specific item. The demo program DEMIO12.PAS, discussed later, illustrates hotkeys. CheckIOOBJ CheckIOOBJ provides the following two methods for setting and checking the status of each item: SetValue(Item:byte; selected:boolean); This method is used to change the selection status of an item after it has been added with AddItem. The first parameter specifies the item number, and the second boolean parameter should be set to True to select the item, or false to deselect it. GetValue(Item:byte):boolean; This boolean function method returns True if the specified item is selected. Controlling User Input 11-29 -------------------------------------------------------------------------------- RadioIOOBJ RadioIOOBJ has the following methods for setting and checking which item is selected: SetValue(Item:byte); Only one item in a radio button object can be selected. Use this method (after you have added all the items) to specify which item is selected. GetValue:byte; This function returns the number of the selected item. Examples Listed below is the demo program DEMIO11.PAS illustrating a single radio button field. program DemoIOEleven; {demIO11 - single Radio Button input} Uses DOS, CRT, totFAST, totINPUT, totIO1, totIO2; Var Bool: RadioIOOBJ; begin ClrScr; with Bool do begin Init(35,12,20,5,'Sex?'); SetBoxOn(True); AddItem('~M~ale',77,true); AddItem('~F~emale',70,false); Mouse.Show; Activate; Mouse.Hide; gotoxy(1,20); if GetValue = 1 then writeln('You are male!') else writeln('Hi, I''m Bob.'); Done; end; end. 11-30 User's Guide -------------------------------------------------------------------------------- Listed below is an extract of the demo program DEMIO12.PAS, which illustrates how to combine mulitiple fields in a dialog box. Figure 11.10 shows an example of the display generated by the full program. procedure InitVars; {} begin with Field1 do begin Init(17,5,25,4,'Options'); AddItem('~C~ase sensitive',67,false); AddItem('~W~hole words only',87,false); AddItem('~R~egular expression',82,false); end; with Field2 do begin Init(17,10,25,3,'Scope'); AddItem('~G~lobal',71,true); AddItem('~S~elected text',83,false); end; with Field3 do begin Init(45,5,17,3,'Direction'); AddItem('Forwar~d~',68,true); AddItem('~B~ackward',66,false); end; with Field4 do begin Init(45,10,17,3,'Origin'); AddItem('~F~rom cursor',70,false); AddItem('~E~ntire scope',69,true); end; Keys.Init; end; {InitVars} Figure 11.10 [SCREEN] A Custom Dialog Box List Fields Radio buttons allow the user to select a single item from a list. Sometimes, however, you may have too many items in the list to display all together. The ListOOBJ object family can be used to display scroll- able lists, and these are located in the totIO2 unit. Controlling User Input 11-31 -------------------------------------------------------------------------------- ListIOOBJ is an abstract object, and you should only use the descen- dants ArrayIOOBJ and LinkIOOBJ. These objects display the contents of a string array and a DLLOBJ linked list, respectively. Both these objects are descendant from MultiLineIOOBJ, and inherit the following, pre- viously discussed, methods: SetActiveStatus(Selectable:boolean); SetHotkey(HK:word); SetID(ID:word); SetLabel(Lbl:string); SetMessage(X,Y:byte;Msg:string); SetBoxOn(On:boolean); Activate; Done; Both items share the following common Init method: Init(X1,Y1,width,depth:byte; Title:string); This method should be called first. The first two parameters specify the (X,Y) coordinate of the top left of the object. The third and fourth parameters specify the width and depth of the object in charac- ters. The width should be large enough to accommodate the longest item, a scroll bar (if there are too many items to display at once) and leave room for the box (if the box is active). The depth should be large enough to accommodate the title, at least one item and, optionally, the box. Having initialized the object, the next task is to call the AssignList method. AssignList instructs the Toolkit where to locate the data for the items in the list. Each object has its own AssignList method. ArrayIOOBJ The Toolkit gets the contents of the list from a string array, which you must create separately. Having created the array, you must call the following method: AssignList(var StrArray; Total:longint; StrLength:byte); AssignList identifies the string array that will be displayed. The four parameters are the string array, the total number of elements in the array, and the length of each string in the array. The string length parameter must reflect the string length of the array when it was declared, not the maximum length of any string assigned to the array. 11-32 User's Guide -------------------------------------------------------------------------------- Listed below is the demo program DEMIO13.PAS, which illustrates how to build an ArrayIOOBJ field. program DemoIOThirteen; {demIO13 - single ArrayIOOBJ input} Uses DOS, CRT, totFAST, totIO1, totIO2; Var MyList: array[1..10] of string[20]; ListField: ArrayIOOBJ; procedure FillArray; {} begin MyList[1] := 'Monitor'; MyList[2] := 'Keyboard'; MyList[3] := 'Mouse'; MyList[4] := 'Light Pen'; MyList[5] := 'Microphone'; MyList[6] := 'LCD O/H Panel'; MyList[7] := 'Modem'; MyList[8] := 'Printer'; MyList[9] := 'CD Rom'; MyList[10] := 'Toolkit'; end; {FillArray} begin ClrScr; FillArray; with ListField do begin Init(35,5,15,6,'Peripherals'); AssignList(MyList,10,20); Activate; gotoxy(1,20); writeln('You chose item: ',GetValue,' - ',MyList[GetValue]); Done; end; end. LinkIOOBJ This object accesses a DLLOBJ object, to determine which items to dis- play in the list. You must create a DLLOBJ instance, populate the list, and then call the following AssignList method: Controlling User Input 11-33 -------------------------------------------------------------------------------- AssignList(var LinkList: DLLOBJ); This method is passed a DLLOBJ instance, or any instance of an object descended from DLLOBJ, e.g. StrDLLOBJ. Listed below is the demo program DEMIO14.PAS, which illustrates how to build a LinkIOOBJ field. program DemoIOFourteen; {demIO14 - single LinkIOOBJ input} Uses DOS, CRT, totFAST, totIO1, totIO2, totLINK; Var MyList: StrDLLOBJ; ListField: LinkIOOBJ; procedure FillList; {} var Retcode: integer; begin with MyList do begin Init; Retcode := Add('Monitor'); Retcode := Add('Keyboard'); Retcode := Add('Mouse'); Retcode := Add('Light Pen'); Retcode := Add('Microphone'); Retcode := Add('LCD O/H Panel'); Retcode := Add('Modem'); Retcode := Add('Printer'); Retcode := Add('CD Rom'); Retcode := Add('Toolkit'); end; end; {FillList} begin ClrScr; FillList; with ListField do begin Init(35,5,15,6,'Peripherals'); AssignList(MyList); Activate; gotoxy(1,20); writeln('You chose item: ',GetValue,' - ', MyList.GetStr(MyList.NodePtr(GetValue),0,0)); Done; 11-34 User's Guide -------------------------------------------------------------------------------- MyList.Done; end; end. Both the examples produce identical displays, see figure 11.11 below. Figure 11.11 [SCREEN] Displaying a List Word Wrapping Fields Some of the most powerful, but easy to use, form objects are the Word- WrapIOOBJ objects. These objects can be used to display scrollable memo fields. The user can type in multiple lines of text with full editing, and the object provides automatic word wrapping. WordWrapIOOBJ is an abstract object, and you should only use the descendants WWArrayIOOBJ and WWLinkIOOBJ. Like the List objects described in the last section, these objects display the contents of a string array and a DLLOBJ linked list, respectively. Both objects are descendant from MultiLineIOOBJ, and inherit the following, previously discussed, methods: SetActiveStatus(Selectable:boolean); SetHotkey(HK:word); SetID(ID:word); SetLabel(Lbl:string); SetMessage(X,Y:byte;Msg:string); SetBoxOn(On:boolean); SetIns(InsOn:boolean); Activate; Done; Both items share the following common Init method: Init(X1,Y1,width,lines:byte; Title:string); This method should be called first. The first two parameters specify the (X,Y) coordinate of the top left of the object. The third parameter specifies the width of the object in characters, and the fourth parame- ter specifies the number of lines of text to display. The last parame- ter is an optional title. Controlling User Input 11-35 -------------------------------------------------------------------------------- Having initialized the object, the next task is to call the AssignList method. AssignList instructs the Toolkit where to locate the data for the text. Each object has its own AssignList method, and these are explained in a later section. If you want to pre-load the field with some text, you can take advan- tage of the following auto-word wrapping method: WrapFull; This method adjusts the contents of the source data structure, i.e. the string array or linked list, by word wrapping the existing text. When you add text to the data structures you do not need to accurately word wrap the text, but you should make sure that there is always one space at the end of the line. This space separates the last word on one line from the first word on the next line. Sometimes, you may want to display a word wrap field, but not allow the user to edit the text. The following method controls whether editing is allowed: SetAllowEdit(On:Boolean); Pass true to enable editing (default). When editing is disabled, the user may still scroll the text, but it cannot be changed. When the word wrap fields are used on their own, i.e. by calling acti- vate rather than as part of a form, the user can end the input by pressing F10. This "end edit" key can be assigned to another key using the following method: SetEndKey(K:word); Specifies the key which will end the input session when the field is used individually rather than as part of a form. The default is F10. WWArrayIOOBJ The Toolkit gets the text contents from a string array, which you must create separately. Having created the array, you must call the follow- ing method: AssignList(var StrArray; Total:longint; StrLength:byte); 11-36 User's Guide -------------------------------------------------------------------------------- AssignList identifies the string array that will be displayed. The three parameters are the string array, the total number of elements in the array, and the length of each string in the array. The string length parameter must reflect the string length of the array when it was declared, not the maximum length of any string assigned to the array. Note: the Toolkit is unable to extend the size of the array. You must, therefore, ensure the array is large enough to accommodate all the text the user is allowed to enter. If you want an unlim- ited number of lines, or you want the Toolkit to extend the lines used, take advantage of the WWlinkOBJ described next. Listed below is the demo program DEMIO15.PAS, which illustrates how to build an WWArrayIOOBJ field. program DemoIOFifteen; {demIO15 - single WWArrayIOOBJ input} Uses DOS, CRT, totFAST, totIO1, totIO3, totINPUT; Var MyList: array[1..10] of string[60]; WWField: WWArrayIOOBJ; procedure FillArray; {} begin FillChar(MyList,sizeof(MyList),#0); MyList[1] := 'It seems like we have to work at innocence '; MyList[2] := 'and being pure, and at the same time we have '; MyList[3] := 'to work at being successful so that we have '; MyList[4] := 'an understanding as to what the rest of the '; MyList[5] := 'world is up to.'; MyList[6] := ''; MyList[7] := 'Brother Anthony Fiore'; end; {FillArray} begin ClrScr; Screen.WriteCenter(1,15,'Press F10 to finish'); FillArray; Mouse.Show; with WWField do begin Init(5,7,65,7,'A Quote'); AssignList(MyList,10,60); Controlling User Input 11-37 -------------------------------------------------------------------------------- WrapFull; Activate; gotoxy(1,20); Done; end; Mouse.Hide; end. WWLinkIOOBJ This object accesses a DLLOBJ object to determine which items to dis- play in the list. You must create a DLLOBJ instance, populate the list, and then call the following AssignList method: AssignList(var LinkList: DLLOBJ; Max:integer); This method is passed a DLLOBJ instance, or any instance of an object descended from DLLOBJ, e.g. StrDLLOBJ. Max specifies the maximum number of lines by which the Toolkit can extend the list. Listed below is the demo program DEMIO16.PAS, which illustrates how to build a WWLinkIOOBJ field. program DemoIOSixteen; {demIO16 - single WWLinkIOOBJ input} Uses DOS, CRT, totFAST, totIO1, totIO3, totINPUT, totLINK; Var MyList: StrDLLOBJ; WWField: WWLinkIOOBJ; procedure FillList; {} var Retcode: integer; begin with MyList do begin init; Retcode := Add('It seems like we have to work at innocence '); Retcode := Add('and being pure, and at the same time we have '); Retcode := Add('to work at being successful so that we have '); Retcode := Add('an understanding as to what the rest of the '); Retcode := Add('world is up to.'); Retcode := Add(''); Retcode := Add('Brother Anthony Fiore'); end; end; {FillList} 11-38 User's Guide -------------------------------------------------------------------------------- begin ClrScr; Screen.WriteCenter(1,15,'Press F10 to finish'); FillList; Mouse.Show; with WWField do begin Init(5,7,65,7,'A Quote'); AssignList(MyList,40); WrapFull; Activate; gotoxy(1,20); MyList.Done; Done; end; Mouse.Hide; end. The display generated by both of the demo programs is identical, and is illustrated in figure 11.12. Figure 11.12 [SCREEN] A Word Wrap Field Button Fields Buttons are for use with full screen input forms. They provide a way for the user to invoke an event. For example, an input form may have a SAVE button and a CANCEL button. A button can be selected by tabbing to the button and pressing [KEYCAP], pressing the button hotkey, or by clicking the mouse cursor on the button. The Toolkit provides three button objects in the totIO1 unit: Stri- pIOOBJ, Strip3dIOOBJ, and ButtonIOOBJ. All three objects function iden- tically, and the differences are purely cosmetic. The StripIOOBJ object provides a single-line button, Strip3dIOOBJ is a single line button with a black shadow, and ButtonIOOBJ displays the button text sur- rounded by a box. Refer back to figure 11.4 (on page 11-8) for examples of each button type. All three button objects are descended from VisibleIOOBJ and share the following, previously described, methods: Controlling User Input 11-39 -------------------------------------------------------------------------------- SetActiveStatus(Selectable:boolean); SetHotkey(HK:word); SetID(ID:word); SetLabel(Lbl:string); SetMessage(X,Y:byte;Msg:string); Done; While the SetLabel method is technically available, it is not normally used, because the button text acts as a label. The totIO1 unit includes the declaration of the enumerated type tAc- tion, which has the following members: None, NextField, PrevField, Fin- ished, Escaped, Refresh, Signal, Enter, Help, Stop1..Stop9. All these members are used internally by the Toolkit to control events during full screen input. The following members should be used with buttons: Finished Use with buttons to indicate a standard end of a form editing session, e.g. buttons like "OK", "Done", "Proceed", "Save". Escaped Use with buttons that allow the user to exit without processing the edited data, e.g. buttons like "Cancel", "Abort". Help Use with a "Help" button. When selected, the Toolkit will automatically invoke a context sensi- tive help system (discussed later). Stop1..Stop9 These buttons function like Finished. They should be used to successfully end the form edit session. They are provided so that you may provide multiple finished buttons. Based on which button is selected, you may process the edited data differ- ently. For example, you may create the following three buttons: "Save & Quit" - Finished "Save & Edit Next" - Stop1 "Save & Backup File" - Stop2 Whichever button is selected, the edit session will end, and the FormOBJ (discussed later) will return the tAction member selected. All three buttons share the following method: Init(X,Y:byte;Tit:string;Act:tAction); 11-40 User's Guide -------------------------------------------------------------------------------- This method initializes the button and must be called first. The first two parameters specify the upper left (X,Y) coordinate of the button. The third parameter is the title which will be displayed in the button. The title may include embedded highlight ('~') characters. The fourth parameter is a member of the enumerated type tAction, which indicates what action to take when the button is selected. Listed below is an extract of the program DEMIO17.PAS. This program is an enhancement to DEMIO12.PAS. Two buttons have been added to the dia- log box. Compare the display of the new program, shown in figure 11.13, with the button-less version in figure 11.10 (page 11-30). procedure InitVars; {} begin {...} OK.Init(23,14,' ~O~K ',Finished); OK.SetHotkey(79); Cancel.Init(36,14,' C~a~ncel ',Escaped); Cancel.SetHotkey(65); {...} end; {InitVars} Figure 11.13 [SCREEN] 3D Strip Buttons Controlling Defaults with IOTOT One of the basic approaches in the Toolkit is to default as much as possible! This saves you the chore of setting dozens of parameters like display colors every time you want to perform a simple task. You can, of course, change any of the default values to meet your specific needs. All the colors and default settings for user input are established by the instance IOTOT^. IOTOT is a pointer to an object of type InputOBJ and is designed specifically to give you control of the input defaults. There are methods for setting display colors, field rules, the case of string input, whether the fields are initially in insert mode, and so on. To modify the defaults, all you have to do is call one of the IOTOT^ methods. Before analyzing the detailed method syntax, you need to be aware of the wealth of colors used by the Toolkit. Most display set- tings require four different color attributes to be specified. The Controlling User Input 11-41 -------------------------------------------------------------------------------- first two attributes are used when the field is highlighted, i.e. when the user is editing the field, and the second two attributes are used when the field is not highlighted. You may recall that the Toolkit can display any string in two colors using the method WriteHi (discussed on page 5-3). Many of the strings in the IO objects also support the two colors capability, e.g. field labels, button text, and check boxes. That is why the color setting methods need to use two colors when the field is highlighted, and two colors when it isn't, making a total of four colors for each category. Don't forget that the Toolkit uses a combined foreground/back- ground attribute to define each color. Refer to page 3.11 for further information. The following methods can be used to change the Toolkit IO defaults: SetColLabel(Off,OffHot,On,OnHot:byte); Specifies the display attributes for labels. The first two parameters specify the display attributes when the field is not highlighted. The first parameter is the normal attribute, and the second attribute is the color used after the embedded highlight character, e.g. ~. The third and fourth parameters specify the attributes to use when the field is highlighted. SetColButton(Off,OffHot,On,OnHot:byte); Specifies the four display attributes for button text. SetColGroup(Off,OffHot,On,OnHot:byte); Specifies the four color attributes to use with radio button and check box fields. SetColList(Off,OffHot,On,OnHot:byte); Specifies the four colors to use for list fields. SetColField(Off,On,Mask,Inactive:byte); Specifies the colors to use with standard string fields. The first parameter is the display color when the field is not highlighted, the second parameter is the color when the field is highlighted, the third parameter is the color to display formatting characters in PictureIOOBJ fields, and the fourth parameter is the default display color for any inactive (i.e. non-selectable) field. 11-42 User's Guide -------------------------------------------------------------------------------- Note: For consistency, the Toolkit does not allow you to change the display attributes of an individual field on an input form. The attributes are always derived from IOTOT at display time. You must use the methods SetColLabel, SetColButton, SetColGroup, Set- ColList, and SetColField to change the display attributes of all the field types in the relevant category. SetColMsg(Col:byte); Sets the color of the message which is displayed when the field is highlighted. Specify an attribute of zero, if you want the current display attribute to be used. SetIns(On:boolean); Controls whether the input fields are initially in insert (True) or overtype (False) mode. SetRules(Rules:byte); Sets the default field rules (refer to page 11-12 for further informa- tion). SetPadChar(Pad:char); When a field value does not entirely fill the allotted field, the unused characters are represented by displaying a pad character. By default, this value is set to a dot (ASCII characters 250). This method is used to change the pad character, and one of the most common alter- natives is a space, e.g. IOTOT^.SetPadChar(' ');. SetJust(Just:tJust); This method is used to set the default justification of the string fields (see page 11.13). SetCursor(Curs: tCursPos); This method controls where the cursor is positioned when an input field is highlighted (see page 11.13). SetCase(Cas:tCase); This method can be used to control whether the Toolkit automatically adjusts the case of any of the string field objects, when the user leaves the field (see page 11.13). Controlling User Input 11-43 -------------------------------------------------------------------------------- SetForceCase(On:boolean); The SetForceCase method is used to control whether the case of the string input is adjusted while the user is typing in the string. Pass a True parameter to force the case adjustment (as specified with the method SetCase) every time a character is pressed. Pass a False parame- ter if you want the string to be adjusted only when the user moves to another field. The following function methods return information about the current default settings: LabelCol(Element:byte): byte; This function method returns the current attribute for displaying field labels. Pass a parameter with a value between 1 and 4 to indicate which attribute you want to determine. The parameter values represent the following colors: 1 Not highlighted normal attribute. 2 Not highlighted high attribute. 3 Highlighted normal attribute. 4 Highlighted high attribute. ButtonCol(Element:byte): byte; Returns the attribute byte of the Button text. GroupCol(Element: byte): byte; Returns the attribute byte of radio button and check box fields. ListCol(Element:byte): byte; Returns the attribute byte of the list fields. FieldCol(Element:byte): byte; Returns the attribute byte of the string fields. Pass a parameter with a value between 1 and 4 to indicate which attribute you want to deter- mine. The parameter values represent the following colors: 1 Highlighted attribute. 2 Not highlighted attribute. 3 Attribute of mask character in picture fields. 4 Attribute of inactive (non-selectable) fields. MessageCol: byte; 11-44 User's Guide -------------------------------------------------------------------------------- Returns the attribute used for writing the highlighted field's message. InputPadChar: char; Returns that character which is used to fill a field when the input is shorter than the field length. InputJust: tJust; Returns the default string justification (see page 11-13) InputCursorLoc: tCursPos; Returns the default cursor position (see page 11-13). InputCase: tCase; Returns the default case setting for string fields (see page 11-13). InputForceCase: boolean; Returns true if user input is forced to a specific case when each character is input. If you want to reset all the IOTOT settings back to the original Tool- kit defaults, all you have to do is call the SetDefaults method as follows: IOTOT^.SetDefaults; By looking at the actual Toolkit code for the SetDefaults method, you can quickly determine the default values. procedure InputOBJ.SetDefaults; {} begin if Monitor^.ColorOn then {color System} begin SetColLabel(78,76,79,76); SetColButton(32,46,47,46); SetColGroup(48,62,63,62); SetColList(48,62,31,30); SetColField(48,31,23,71); end else begin SetColLabel(112,126,127,126); SetColButton(32,46,47,46); SetColGroup(48,62,63,62); Controlling User Input 11-45 -------------------------------------------------------------------------------- SetColList(48,62,31,30); SetColField(48,31,23,24); end; SetColMsg(0); vInputPad := chr(250); vCase := Leave; vForceCase := false; vInputJust := JustLeft; vCursorLoc := CursPrev; vInsert := false; vRules := AllowNull; end; {InputOBJ.SetDefaults} Form Management In this section, the various techniques for combining a collection of fields into a form will be explained. You have already learned the complicated stuff! All you have to do is create a FormOBJ instance and tell it which field object to include in the form. FormOBJ is the form manager and is located in the totIO1 unit. To build a form, initialize the FormOBJ instance, add the fields that will create the form, and instruct the Toolkit to process the user input. The following FormOBJ methods perform these services: Init; This method initializes the FormOBJ instance, and should always be called first. AddItem(var NewItem: ItemIOOBJ); This method should be called once for every field on the form. The only passed parameter is an object descended from ItemIOOBJ, i.e. any of the objects discussed so far in this chapter. Go:tAction; Having added all the items, call the function method Go to activate the form, and process the user's input. The function returns one of the following members of the enumerated type tAction: Finished, Escaped, Stop1 through Stop9. The value returned will depend on which button or key the user pressed. Based on the return value, you can decide how to process the user-entered data. 11-46 User's Guide -------------------------------------------------------------------------------- In addition to the standard user input fields, the totIO1 unit includes a special object, ControlKeysIOOBJ, which is used to control how the user moves between fields and terminates input. Every form you create should include a ControlKeysIOOBJ instance. ControlKeysIOOBJ objects provide the following three methods: Init; This method initializes the object and must be called first. The object actually provides the Toolkit with four special keys: the key to move on to the next field, the key to move back to the previous field, the key to finish the edit session, and the key to abort the edit session. When the ControlIOOBJ object is initialized, these keys are set to [KEYCAP], [KEYCAP], [KEYCAP] and [KEYCAP], respectively. SetKeys(Next,Prev,Fin,Esc:word); This method is used to override the default keys. The method is passed the four key codes which represent the keys for moving to the next field, moving back to the previous field, to finish editing, and to abort editing. For example, the statement SetKeys(336,328,335,27); will set the keys to [KEYCAP], [KEYCAP], [KEYCAP] and [KEYCAP]. Be careful which keys you select. For example, if you have a word wrap field, you probably don't want to use the up and down arrows. Each time the user presses the down arrow to edit the next line, he/she will jump to the next field! Done; This should be called to dispose of the object when it is no longer required. Listed below is the example DEMIO2.PAS, which was first introduced at the beginning of the chapter. It shows how to use some of these basic FormOBJ methods. Refer back to figure 11.2 on page 11-4 to see the resultant output. program DemoIOTwo; {demIO2 - full field input} Uses DOS, CRT, totFAST, totIO1, totIO2; Controlling User Input 11-47 -------------------------------------------------------------------------------- Var Name: LateralIOOBJ; Phone: PictureIOOBJ; Price: FixedRealIOOBJ; Keys: ControlkeysIOOBJ; Manager: FormOBJ; Result: tAction; procedure InitVars; {} begin with Name do begin Init(35,5,20,40); SetLabel('Vendor Name'); end; with Phone do begin Init(35,7,'(###) ###-####'); SetLabel('Tel'); end; with Price do begin Init(35,9,8,2); SetLabel('Unit Price'); SetValue(250.0); SetMinMax(0.1,12250.0); SetRules(EraseDefault); end; Keys.Init; end; {InitVars} begin ClrScr; Screen.TitledBox(15,3,65,11,76,79,78,2,' Quicky Input Demo '); Screen.WriteCenter(25,white,'Press TAB to switched fields and press ESC or F10 to end'); InitVars; with Manager do begin Init; AddItem(Keys); AddItem(Name); AddItem(Phone); AddItem(Price); Result := Go; if Result = Finished then {update the database..} else 11-48 User's Guide -------------------------------------------------------------------------------- {call Esc routine}; end; end. The order in which the fields are added using AddItem will be the order in which the user will jump from field to field. The following FormOBJ method can be used to control which field is highlighted when the Go method is called: SetActiveItem(ID:word); This method instructs the Toolkit on which field to highlight first. The only parameter is the field ID of the field to be highlighted. Don't forget that any field can have a unique ID by calling the method SetID. By default, all fields have an ID of zero. Hotkey Fields By now, you should know that any field can have a hotkey assigned with the method SetHotKey. There is one remaining type of field that has not yet been discussed. Namely, HotkeyIOOBJ objects. These fields are "in- visible" and are used to add special Hotkeys to the form. HotkeyIOBJ objects only have the following two methods: Init(HK:word;Act:tAction); A HotkeyIOOBJ object has only two properties. It has a keycode, which represents the key that must be pressed to invoke it, and it has an action code of type tAction, i.e. None, NextField, PrevField, Finished, Escaped, Refresh, Signal, Enter, Help, Stop1..Stop9. Whenever the user presses the assigned hotkey, the specified action is invoked. For exam- ple, if you want to assign the key [KEYCAP] (in addition to [KEYCAP]) to end the edit session, you would initialize a HotkeyIOOBJ instance as follows: Init(301,Finished); Done; This disposes of the object. Moveable Forms The object WinFormOBJ is a descendant of FormOBJ, and provides the same services with one important enhancement. WinFormOBJ manages an input Controlling User Input 11-49 -------------------------------------------------------------------------------- form on a moveable window. It is ideal for creating moveable dialog boxes. The Toolkit itself takes advantage of WinFormOBJ to create the pop-up message objects and the moveable directory dialog box. WinFormOBJ inherits the following methods from FormOBJ: Init; AddItem(var NewItem: ItemIOOBJ); Done; Additionally, WinFormOBJ has a function method Win which returns a pointer to the MoveWinOBJ instance on which the form is built. To modify the window size and other properties, just call Win^.method, where method is any MoveWinOBJ instance. If you are going to create a window-based form using WinFormOBJ, be sure to create each individual field objects with relative (X,Y) coor- dinates. In other words, the top left of the form window will be at coordinates (1,1). Important Note: before calling the method Go, call the method Draw. This instructs the WinFormOBJ object to display the window and save the underlying screen contents. Refer to the on-disk example DEMIO18.PAS for a full example illustrat- ing the WinFormOBJ object. DEMIO18.PAS is actually an enhancement to the program DEMIO17.PAS, creating a text-search dialog box. Determining User Input When the user has edited the form, you probably want to know what data was entered! First, be sure to check the tAction code returned by the FormOBJ method Go. This will indicate whether the user escaped, or really wanted the data processed. The only form input objects which update the original data source are the word wrapping objects WWArrayIOOBJ and WWLinkIOOBJ. The Toolkit automatically modifies the data passed to these objects. In all other cases, you should call each individual object's GetValue method to determine the user's input. Review the demo programs DEMIO5,8,10,13,14 to see examples of how to determine user input. Also, the demo programs in the last section More Examples! provide still further examples. 11-50 User's Guide -------------------------------------------------------------------------------- Advanced Techniques For the majority of situations, you have learned all you need to know to master form input. However, the Toolkit provides another level of sophistication for developers who really want to customize their input forms. Although this section is called Advanced Features, it does not mean that these features are complicated. The only reason for separa- ting them into a special section is because you will probably use them less frequently. There follows descriptions of how to intercept every character that is pressed, how to validate individual fields before allowing the user to leave, and how to implement a help system. The concept of raising sig- nals between dependent fields is also introduced. Intercepting Every Character Press Many of the Toolkit's units include character hooks, which provide a way for you to nominate a procedure or function to be called every time a character is pressed. This allows you to intercept each character and if appropriate, invoke a special routine. You can even change the value of the character pressed. The FormOBJ and WinFormOBJ objects also pro- vide a character hook facility. A character hook is an external procedure which is called every time a key or mouse button is pressed. To utilize the character hook facility, all you have to do is create a function following some specific rules, and then call the FormOBJ method SetCharHook to instruct the Toolkit to use your function. For a function to be eligible as a character hook it must adhere to the following rules: Rule 1 The function must be declared as a FAR function. This can be achieved by preceding the function with a {$F+} compiler directive, and following the function with a {$F-} direc- tive. Alternatively, Turbo 6 users can use the new keyword FAR following the function statement. Rule 2 The function must be declared with four passed parameters. Parameter one must be a variable parameter of type word. This parameter indicates which key the user just pressed, and you may change the value of this parameter to return a different key. The second and third parameters must be variable parameters of type byte, and they represent the X and Y coordinates of the mouse at the time the key was pressed. The fourth parameter is a variable, and must be of type word. This parameter indicates the field ID of the currently active field. This field can be changed to another ID if you want the user to jump to a different field. Controlling User Input 11-51 -------------------------------------------------------------------------------- Rule 3 The function must return a value of type tAction. This is an enumerated type which indicates to the Toolkit how to proceed. The members of the enumerated type are: None, NextField, PrevField, Finished, Escaped, Refresh, Signal, Enter, Help, Stop1..Stop9. If you want the edit session to terminate, return Finished, Escaped, or Stop1 through Stop9. If you have changed, inserted, or deleted any items in the visible list, return Refresh. The Toolkit will then re- display the entire form contents. Rule 4 The function must be at the root level, i.e. the function cannot be nested within another procedure or function. The following function declaration follows these rules: {$F+} function MyCharHook(var K:word; var X,Y: byte; var FieldID:word): tAction; begin ...{function statements} MyCharHook := NextField; end; {$F-} The following method SetCharHook is then called to instruct the Toolkit to call your function every time a key is pressed: SetCharHook(Func:CharFunc); This method is passed the function name of a function declared using the rules outlined above, e.g. SetCharHook(MyCharHook);. Listed below is an extract from the demo program DEMIO19.PAS. This file is a variation on our old favorite DEMIO2.PAS. In this case, a charac- ter hook has been added to intercept the key [KEYCAP]. If this key is pressed, a message showing the current time is displayed. Notice that if [KEYCAP] is pressed, the hook function replaces the actual key with a value of 0. This stops the active field from trying to process the key - a zero key is ignored by the Toolkit. Finally, the hook function returns a value of None, indicating that the form manager does not need to take special action. {$F+} function ShowTime(var K:word; var X,Y:byte; var ID:word):tAction; {} var Msg: MessageOBJ; begin if K = 276 then {Alt-T} 11-52 User's Guide -------------------------------------------------------------------------------- begin with Msg do begin Init(1,'The Time M''Lord'); Addline(''); AddLine(padcenter(CurrentTime,25,' ')); AddLine(''); Show; Done; end; K := 0; end; ShowTime := none; end; {ShowTime} {$F-} begin {...} with Manager do begin Init; AddItem(Keys); AddItem(Name); AddItem(Phone); AddItem(Price); SetCharHook(ShowTime); Result := Go; {...} end; end. Validating Input As well as a character hook, the FormOBJ objects provide leave field and enter field hooks. These functions are called when the user tries to leave a field, and when the user tries to enter a field. The leave field hook provides a way to ensure the contents of a field are valid before moving to another field. The enter field hook can be used to take some action before entering a field, e.g. display a warning mes- sage, or move the user to another field if a specific condition is not met. Like the character hook, both field hooks must adhere to the following common rules: Rule 1 The function must be declared as a FAR function. Controlling User Input 11-53 -------------------------------------------------------------------------------- Rule 2 The function must return a value of type tAction. This is an enumerated type, which indicates to the Toolkit how to proceed. Rule 3 The function must be at the root level. The hooked procedure's passed parameters are different for each hook type, as follows: Leave The function must be declared with one variable passed parameter of type word. This parameter represents the field ID of the field the user is leaving. This parameter can be changed to another field to instruct the Toolkit to jump to a different field. Enter The function must be declared with two passed parameters. The first parameter is a variable parameter of type word. This parameter identifies the ID of the field the user is about to enter. This parameter may be modified, to instruct the Toolkit to jump to a different field. The second parame- ter is of type word, and it represents the ID of the field the user just left. Listed below are two code sample procedures which adhere to the rules for leave and enter field hooks. {$F+} function MyEnterHook(var NewID:word; LastID: word):tAction begin ...{function statements} MyEnterHook := none; end; {$F-} {$F+} function MyLeaveHook(var LastID: word):tAction begin ...{function statements} MyLeaveHook := none; end; {$F-} The FormOBJ methods SetLeaveHook or SetEnterHook can then be called to instruct the Toolkit to call the specified function whenever a user tries to leave or enter a field. 11-54 User's Guide -------------------------------------------------------------------------------- Context Sensitive Help The fourth category of FormOBJ user hooks is the Help hook. The Help hook provides a way of implementing context sensitive help into your form. To implement a help hook, all you have to do is create a procedure following some specific rules, and then call the FormOBJ method SetHel- pHook to instruct the Toolkit to use your function. For a function to be eligible as a character hook it must adhere to the following rules: Rule 1 The procedure must be declared as a FAR procedure. Rule 2 The procedure must be declared with a single passed parame- ter of type word. This parameter identifies the ID of the highlighted field at the time the user requested help. Rule 3 The procedure must be at the root level. The following code fragment shows a procedure which adheres to these rules. {$F+} function MyHelpHook(LastID: word); begin ...{help display statements} end; {$F-} If you go to the trouble of creating a help system, you ought to give the user some way of requesting help! The two best ways to add help are with a hotkey and with a help button. Either way is easy, and you ought to consider using both. The following code fragment shows how to imple- ment them: var HelpBut: Strip3dIOOBJ; F1key: HotkeyIOOBJ; begin {...} HelpBut.Init(34,12,' ~H~elp ',Help); HelpBut.SetHotkey(291); HelpBut.SetID(HelpID); F1Key.Init(315,Help); {...} end. Controlling User Input 11-55 -------------------------------------------------------------------------------- Notice that the help button is assigned an ID of HelpID. HelpID is a constant defined in the totIO1 unit. The likelihood is that the user wants help on the highlighted field, and doesn't actually want to move to the help button. By assigning an object an ID of HelpID, the Toolkit knows not to swap to it when it is selected with a mouse button or hotkey. Refer to the demo program DEMIO20.PAS for a full example of how to implement a help system. This program is a further enhancement to the search dialog box demo program discussed earlier. Figure 11.14 illus- trates the display generated when the user asks for help. Figure 11.14 [SCREEN] Add a Help System Creating Descendant Objects The FormOBJ object provides four different hooks for customizing the form input to meet your specific needs. If you think there may be an OOP way to achieve the same results you are right. You can create a descendent object from either FormOBJ or WinFormOBJ, and replace the following virtual methods: function CharTask(var K:word; var X,Y:byte;var FieldID:word):tAction; function LeaveTask(var FieldID:word):tAction; function EnterTask(var NewID:word; OldID;word):tAction; procedure HelpTask(ID:word); The passed parameters to these virtual methods are exactly the same as the ones described earlier for the hooks. Refer to chapter 9: Managing Lists page 9-32 for a comparison of hooks and virtual methods. In some special circumstances you will want to create fields which interact. A good example can be found in the Toolkit object DirWinOBJ (discussed in chapter 10). This object has three main fields - a filename input field, a list of matching files, and a list of directo- ries and drives. If the user enters a new file mask, the file list needs to be refreshed. Similarly, if the user changes directories, the file list and the directory list need to be updated. The solution is for the modified field to raise a signal when changes need to be made to other fields. When a field raises a signal, the Toolkit passes the signal to every other field in turn, and each of these fields may itself raise signals. Every input field object has the following virtual methods: 11-56 User's Guide -------------------------------------------------------------------------------- procedure RaiseSignal(var TheSig:tSignal); procedure HandleSignal(var BaseSig:tSignal; var NewSig:tSignal); procedure ShutDownSignal(var BaseSig:tSignal); Refer to Part 2: Extending the Toolkit for further information about creating input objects which raise and handle signals. More Examples! There are several additional on-disk examples included in the Toolkit. These examples combine many of the features described in this chapter to provide powerful input routines. For example, IODEM21.PAS shows how you might use the objects to create a database access program, and IODEM22.PAS illustrates how you can use PgUp and PgDn to implement a double input screen.