                             Managing Input Forms

                            "Will that Gold product ever get finished?"
                                                    A. Spouse, 1995

Introduction

          Input/Output, or IO for short, is the heart of any program. As
     you are about to discover, it is also the heart of Gold!

          The Gold units which support IO forms are GOLDIO, GOLDIO2 and
     GOLDIO3. These units offer a full set of sophisticated input tools
     including the following:

          27 different field types support string, number and date
          input, along with radio buttons, checks boxes, lists and push
          buttons.

          Automated input validation, e.g. only accept numbers in the
          range 16 to 60; only accept dates in 1995; etc.

          Flexible picture fields allowing users to input telephone
          numbers, part numbers, etc.

          A bevy of user hooks allowing the developer to fine tune the
          form's behavior.

          Forms can be displayed in draggable windows.

          Exotic fields such as spinners and drop-down calendars are
          available.

          All validation and error messages are customizable allowing
     easy internationalization (that's localization for the politically
     correct among us). Multiple simultaneous forms are supported. For
     example, a secondary form can be displayed when a user clicks on a
     button in the main form. Fully supports the mouse. All colors are
     customizable. Fields can be grayed (or disabled) based on the
     values in other fields. Oh, that's enough, you get the idea.

Building a Form -- A Quick Recipe

          Time for an analogy. You don't need to be an accredited chef
     to cook breakfast (unless you happen to be French). The same goes
     for IO forms -- although there is a great deal that you need to
     learn to become a Form Jedi, you can still create very useful and
     functional forms by following an essentially simple recipe.

          Before you delve into the form creation process, you need to
     understand some of the basic principles. A form is a collection of
     fields bound together to give the user a single input screen or
     dialog. Fields have various uses. You might use a field to record a
     person's name or zip code, allowing the end user to type text
     directly into the field. Other fields don't accept normal data
     entry, such as radio buttons or lists -- the user makes a choice
     from a predefined set.

          If you follow a simple recipe and take the steps in the
     correct order, you can create a form in just a few minutes. Here is
     the recipe:

          Decide what data you want to garner from the user, and what
     the data types will be.

          e.g. 5 strings for the address, an integer for the age, and a
          string for the sex.

     Decide on the basic layout of the form.

          e.g. the form might have 5 fields (one below the other) to
          enter the user's name and address, and a numeric field to
          enter the person's age. Finally, there might be OK and Cancel
          buttons on the form. Activate a form.

          A Gold form can support multiple forms, and at any one time, a
     single form will be the active form, i.e. will have focus. All IO
     procedures and function calls are directed at the active form.

          Add the fields to the form.

          At this stage, Gold doesn't care about each field's properties
     -- just the order of the fields and the (X,Y) coordinate of each
     field.

          Define the properties of each field.

          You might set the first field, for example, to be a string
     field that accepts up to 30 alpha numeric characters. You will also
     tie fields to specific variables. When the user enters data in the
     field, the variable is automatically updated with the value
     displayed in the field.

     Add optional labels and messages to each field.

          e.g. You might add the left-justified label "Name:" to the
     first field .

     Assign appropriate values to the variables referenced by the form.

     Run the form and wait for user input.

     If the form is no longer required dispose of the fields.

          Figure 16.1 is the form created in DEMIO1.PAS. On the
     following page is a full listing of the program along with
     annotations showing each of the major steps in the recipe.

     Figure 16.1
     A Basic Form


     Let's analyze a few lines of code from the example.

     KwikAddField(1, 25,9);
     KwikAddField(2, 25,11);

          These lines add fields to the form. The first field is located
     at coordinates (25,9) and the second field is located at
     coordinates (25,11), i.e. two lines below the first line.

     StringField(1, Name, '******************************');
     SetLabel(1, LabelLeft,LabelLeft,'Full name:');

          These two lines define the properties of field one. The field
     is a string field, i.e. a standard field where the user can enter
     text or numbers. The variable associated with the field is named
     NAME, and this variable will be assigned the string that the user
     enters. The third parameter identifies the string picture. An
     asterisk "*" indicates that any alphanumeric character will be
     accepted in that position. The other picture characters are "!"
     which forces alphas to upper case, "#" which accepts only numbers,
     and "@" which accepts letters and punctuation. In this case, the
     field will be 30 characters wide because there are 30 asterisks in
     the picture. The second line adds a left justified label 'Full
     name:' to the field -- you can see this label in Figure 16.1.

     StringField(2, Tel, '(###) ###-####');
     SetLabel(2, LabelLeft,LabelLeft,'Tel:');

          These two lines define the properties of field one. This field
     is also a string but the picture is formatted for telephone number
     input (see Figure 16.1). The tel string will be updated with the
     user input. The second line adds a left justified label of 'Tel: '
     to the field.

     ButtonField(5,'   OK   ',finished);

          This line sets the fifth field to be a push button with the
     text OK. When the button is pressed, the form input will be
     terminated and the value Finished will be returned by the EditForm
     function.

     Result := EditForm(1);

          This line instructs Gold to display the form and wait for the
     user to conclude the input session. The function returns a value of
     type gAction to indicate how the user ended the session. In this
     example, the function might return Finished if the F10 key was
     pressed or the user selected the OK button, or Escaped if the user
     pressed the Esc key or selected the Cancel button.

          If you are the kind of programmer who likes to learn by
     example (that's all of you, by the way), investigate the demo files
     DEMIO2.PAS through DEMIO5.PAS. Each demo builds on the previous
     one, and adds another degree of sophistication to the form. By the
     last demo, the form is displayed in a window with field messages
     displayed on the status bar, radio buttons and ... well, just take
     a look for yourself.

Configuring the Form(s)

          Having learned the instant form recipe, it's time to take a
     more detailed look at the entire form management process. RTFM.

Creating Multiple Forms

          Gold allows an application to have multiple forms defined at
     one time, i.e. within the same scope. For example, form 1 might be
     a customer name and address form, and form 2 might be an order
     entry form. If you want to use more than one form at a time, you
     must instruct Gold to create the number of forms you will need
     using the following function:

     CreateForms(Count:byte);

          Instructs Gold to allocate enough memory for Count forms. Note
     that Count must be in the range 1 to MaxForms (which is defined in
     GOLDIO as 10). This should be called before any other IO procedures
     and functions.

          If you call any Gold IO procedure or function before you call
     CreateForms, Gold will assume that you will only be using one form,
     and will call CreateForms(1) internally. That is why the example on
     page 17-4 didn't use a CreateForms statement.

          It is important to realize that you do not need to create more
     than one form just because you intend to use more than one type of
     form in your application. You can use the same form for more than
     one purpose if you dispose of the first set of fields and redefine
     a new set of fields prior to displaying the revised form -- more
     about field disposing later.

          If you want to call CreateForms more than once in an
     application (and this will be rare), you must ensure that the you
     call DisposeForms before calling CreateForms a second time.

Giving a Form Focus

          If you don't make a call to CreateForms, then you don't need
     to worry about form focus. If your application creates more than
     one form, read on.

          The notion of focus is used in many parts of Gold: the top
     window on the desktop has focus, and in list applications, a
     specific single or double linked list has focus. The same goes for
     input forms. If you have more than one form in an application, Gold
     needs to know which form to work on, i.e. which form has focus.

     The following procedure sets the focus to a specified form:

     ActivateForm(FormNo:byte);

          Instructs Gold to direct all IO procedures and functions
     toward the specified form. The form must fall within the range 1 to
     the value specified in the call to CreateForms.

          The following code fragment shows how ActivateForm might be
     used to configure multiple forms:

          begin
             CreateForms(4);
             ActivateTable(1);
             ...{work on form 1}
             ActivateForm(2);
             ...{work on form 2}
          end;

Creating Dialogs, i.e. Forms in Windows

          If you are using forms (in windows) on the desktop, then refer
     to the section Launching Forms on the Desktop at the end of this
     chapter. The text below refers to forms created outside of the
     desktop environment.

          By default, the Gold form routines are configured to work full
     screen. If you want to display a form in a window you must advise
     Gold that the form will be displayed in a window before calling the
     field definition functions like AddField, StringField, etc.

          Use the procedure SetFormWindow (defined below) to configure
     the form to run in a window.

     SetFormWindow(X1,Y1,X2,Y2,style:byte);

          Instructs Gold to create a form in a window. The first four
     parameters define the window coordinates, and the fifth parameter
     defines the window style.

          Having set the form window, the function FormWinNum will
     return the window handle (or number). You can customize (and write
     to) a form window, just like any standard Gold window.

     The following code fragment is from the demo file DEMIO10.PAS:

          procedure SetFields;
          {}
          var I : Integer;
          begin
             CreateForms(1);
             ActivateForm(1);
             {define the form's window}
             SetFormWindow(15,4,67,22,1);
             WinSetTitle(FormWinNum,' Customer Comments ');
             WinSetType(FormWinNum,WMove);
             WinSetShowNum(FormWinNum,false);
             KwikAddField(1,10,2);
             KwikAddField(2,10,14);
             KwikAddField(3,35,14);
             KwikAddField(4,35,16);
             ...
          end;


          Gold will dispose of the window automatically when you call
     DisposeFields.

Customizing a Form's Appearance and Behavior

Overriding the Form Navigation Keys

          Most of a form's characteristics are controlled by the
     individual fields in a form. The keys used to jump from field to
     field, however, are set for the entire table.

          By default, the keystrokes to shift to the next field up,
     down, left and right are the Up cursor, Down cursor, Ctrl-Left
     cursor and Ctrl-Right cursor respectively. The de facto standard of
     using the Tab and Shift-Tab keys to move to the next and previous
     fields are also supported.

          The F10 key is assigned to be the finished key, Esc is the
     escaped key, and Ctrl-E is the erase field key.

          You can customize these keys using one of the following three
     procedures:

     AssignActionChars(Nxt,Prv,U,D,L,R,Fin,Esc,E: word);

          Defines the keys the user will use to navigate the form. These
     nine word parameters represent the key codes to move to the next
     field, the previous field, the field above the field below, the
     field to the left, the field to the right, the key to finish
     editing, the key to abort editing and the key to erase the entire
     field contents, respectively.

          Here is a little tip; if you pass a keycode of zero, the
     keystroke assignment for that key will not be changed. This
     provides a quick and easy way to change some, but not all, of the
     key codes. For example, the following statement will only redefine
     the up, down, left and right keys:

     AssignActionChars(0,0,278,288,294,275,0,0,0);

     AssignFinishChar(W:word);

          Provides a quick way to change the finished editing key, which
     defaults to F10.

     AllowEsc(On:boolean);

          Controls whether the user can Esc from the form. Pass TRUE to
     enable the Esc key, and FALSE to disable it.

Customizing Colors

          There are many different colors that might be used in a form.
     In fact there are 37 tint elements that affect forms and they are
     defined as follows:

          IOEditErase,
          IOEditNorm,
          IOEditHi,
          IOEditOff,
          IOButtonNorm,
          IOButtonNormHot,
          IOButtonHi,
          IOButtonHiHot,
          IOButtonOff,
          IOButtonDef,
          IOButtonDefHot,
          IOMessage,
          IOLabelNorm,
          IOLabelNormHot,
          IOLabelHi,
          IOLabelHiHot,
          IOLabelOff,
          IOWinBorder1,
          IOWinBorder2,
          IOWinTitle,
          IOWinBody,
          IOWinIcons,
          IOWinBorderOff,
          IOCheckArea,
          IOChoiceHi,
          IOChoiceHiHot,
          IOChoiceNorm,
          IOChoiceNormHot,
          IOChoiceOff,
          IOIcons,
          IOIcons2,
          IOListHi,
          IOListHiHot,
          IOListNorm,
          IOListNormHot,
          IOListOff,
          IOListScroll

          If you want to change the color of all the forms in your
     application, then you should call the GoldSetColor function and
     modify the Gold defaults, prior to calling any of the IO procedures
     and functions. If you want to change the colors for a specific
     form, then call the IOSetColor function which is defined as
     follows:

     IOSetColor(A:TintElement;C:byte);

     Changes the color of a single element of the active form.

          Listed below is an extract from the demo file DEMIO6.PAS which
     customizes the colors in a form.

          procedure CustomizeColors;
          {}
          begin
             IoSetColor(IOEditErase,WhiteonRed);
             IoSetColor(IOEditNorm,WhiteOnMagenta);
             IoSetColor(IOEditHi,YellowOnGreen);
             IoSetColor(IOEditOff,LightgrayOnMagenta);
             IoSetColor(IOButtonNorm,WhiteOnBlue);
             IoSetColor(IOButtonNormHot,YellowOnBlue);
             IoSetColor(IOButtonHi,WhiteOnRed);
             IoSetColor(IOButtonHiHot,YellowOnRed);
             IoSetColor(IOButtonOff,WhiteOnBlack);
             IoSetColor(IOButtonDef,LightgrayOnBlue);
             IoSetColor(IOButtonDefHot,LightcyanOnBlue);
             IoSetColor(IOMessage,WhiteOnBlack);
             ...
          end; { CustomizeColors }

Understanding Field Rules

          The field rules define the characteristics of the field. For
     example, whether the field is right justified or left justified.
     The rules are stored for each field individually, and the
     programmer can change any or all of the rules as desired.

     There are five different field rules, defined as follows:

          AllowNull - indicates the user is allowed to leave the field
               empty.
          SuppressZero - the field is displayed as blank in a number
               field if the value is zero.
          RightJustify - the field grows from right to left rather than
               the normal left to right.
          EraseDefault - when the user enters the field, the default (or
               previous value) is erased if the user presses a normal
               alphanumeric key.
          JumpIfFull - move to the next field to the right if the field
               is filled with data, i.e. if the user has entered
               sufficient characters to fill the field.

          These five values are defined as constants in GOLDIO.PAS, and
     can be summed to combine field rules. For example, a field may be
     defined as having rules AllowNull + SuppressZero. If one of the
     constants is omitted from a field rule's declaration, then that
     rule will not be enforced. In the example just mentioned, for
     example, the field would not be right justified.

          If you want none of the rules to apply, then specify the
     constant NoRules as the only field rule.

          To set the default rules for an entire form, call the
     following procedure before adding the fields:

     SetDefaultRules(Rules:word);

          Defines the default rules that will be applied to any fields
     subsequently created with AddField.

Miscellaneous Form Defaults

          The following procedures and functions are used to set other
     default characteristics of a form:

     SetMessageXY(X,Y:byte; InWindow: boolean);

          Defines the location of where field messages will be
     displayed. These messages provide the user with information about
     the field with focus. For example, a message might be "Enter an age
     in the range 15 to 65". If the form is being displayed in a window,
     and the message will be displayed inside the window, set the
     InWindow parameter to TRUE, otherwise set it to false.

     SetInsertMode(On:boolean);

          Controls whether the user is initially in insert or overtype
     mode. Pass TRUE to start the form in insert mode.

     SetValidation(Val:gValidate);

          Gold offers automatic field validation on numeric and date
     fields. The validation checks can be performed when the user moves
     from one field to the next, or when the user attempts to finish
     input (by clicking on the OK button, or pressing F10, for example).
     The procedure accepts one of the following two values:

     gValidate = (ValidatebyField,ValidateAtEnd);


Placing Fields in a Form

          Having defined the form's main properties, you need to place
     the fields on the form. At this point Gold doesn't need to know
     anything about the field except where it is positioned, and which
     fields will be adjacent to it.

     AddField(FieldID,DefU,DefD,DefL,DefR,DefX,DefY:byte);

          Adds a field to a form. The first parameter identifies the
     logical field number. This must be unique, and it is recommended
     that the fields are added in ascending numerical order. This same
     field identifier is used in subsequent calls to change the field's
     properties. The next four parameters identify the field ID of the
     fields which will be jumped to when the user attempts to navigate
     upward, downward, leftward and rightward, respectively. The final
     two parameters identify the (X,Y) coordinate of the first character
     of the field.

     KwikAddField(FieldID,DefX,DefY:byte);

          Performs the same function as AddField but leaves Gold to
     assign the field jumps. Upward and leftward will jump to the field
     whose ID is one less than the current field. Downward and rightward
     will jump to the field whose ID is one greater than the current
     field. Don't use this function to add the last field in a form,
     since there will not be a field with an ID greater than the current
     field -- use KwikAddLastField below.

     KwikAddLastField(FieldID,DefX,DefY:byte);

          Adds the last field to a form. See KwikAddField above.

          One family of fields, invisible fields, cannot be added with
     any of the three functions described above. They are not visible
     and so do not have (X,Y) coordinates, and the user never lands on
     or edits them. These fields are referred to as HotKey fields, and
     are added to the form using the following procedure:

     AddHotKeyField(FieldID:byte; Key:word; Action:gAction);

          Adds a hotkey (non-visible) field to a form. The first
     parameter identifies the field ID, the second parameter the word
     value of the hotkey, and the third value identifies the action code
     invoked when the user presses the specified key. Listed below is
     the enumerated type gAction defined in GOLDIO.PAS.

          gAction = (None,NextField,PrevField,NextForm,PrevForm,
          Refresh,Enter,Help,Stop1,Stop2,...Stop97,Stop98,Stop99,
          Finished,Cancel1,Cancel2,Cancel3,Cancel4,Cancel5,
          Cancel6,Cancel7,Cancel8,Cancel9,Escaped);

          The following three functions apply to all visible fields, and
     can be used to set field messages, labels and hotkeys:

     SetMessage(FieldID,X,Y:integer; Str : string);

          Defines the message which will be displayed when the field has
     focus. The message location is controlled by using the procedure
     SetMessageXY, discussed earlier.

     SetLabel(FieldID,X,Y:integer; Str : string);

          Identifies a label that will be printed when the form is
     displayed. In most forms each field will have a label. Although you
     can specify an explicit XY location, to literally put the label
     anywhere on the form, in most situations, use the constant
     LabelLeft or LabelTop to instruct Gold to automatically position
     the label relative to the field.

     SetHK(FieldID:byte; Hotkey: word);

          Defines the keystroke which a user can press to automatically
     jump to the field. The keystroke should normally correspond to a
     highlighted letter in the field label. For example, if the letter A
     is highlighted, the hotkey would be assigned as AltA, i.e. 286.

Individual Field Rules

          SetDefaultRules defines the default field input rules for a
     form. Individual field rules can be specified using the procedure
     FieldRules. In addition to identifying the field rules as discussed
     earlier, the FieldRules procedure can control the specific
     characters which the user will be allowed to input. These character
     rules go beyond the simple field masks such as @, *, ! and #.

          For example, the only valid characters for a field that
     identifies a warehouse part number may be "A B C 1 2 3 4 5 6".
     Alternatively, a field that is a filename may allow any
     alphanumeric character except the * and ? characters. Both of these
     conditions can be accommodated using the FieldRules procedure
     defined as follows:

     FieldRules(FieldID:byte;Rules:word;AChar:IOcharset;
                                                  DChar:IOcharset);

          Defines the field rules for a specific field. The second
     parameter identifies the field rules and can be any combination of
     AllowNull, SuppressZero, RightJustify, EraseDefault and JumpIfFull.
     The third parameter is of type set of char and identifies the
     allowable characters. These characters should be defined within
     square parentheses, e.g. ['A'..'C','1'..'6']. The constant [NoChar]
     can be used to indicate that no special character restrictions
     apply. The fourth parameter is also of type set of char and
     identifies disallowed characters, e.g. ['*','?']. If no characters
     are to be disallowed, use the constant [NoChar].

Controlling Field States

          The following two procedures are used to set and get the state
     of a field, i.e. whether the field is enabled, disabled or hidden.
     As you might imagine, hidden fields are not visible and cannot have
     focus. Disabled fields, however, are visible but are displayed in a
     grayed style, and cannot have focus. The gActiveState is an
     enumerated type defined in GOLDIO.PAS as follows:

     gActiveState = (FldOff, FldOn, FldHidden);

     FieldSetState(FieldID:byte; State:gActiveState);

     Sets the state of the field to enabled, disabled or hidden.

     FieldGetState(FieldID:byte):gActiveState;

     Returns the state of the specified field.

Defining Each Field's Properties

          Typical fields allow the user to enter text or numbers, but
     there are also radio buttons, check boxes, push buttons, etc.

          Having placed the fields on the form (with AddField or
     KwikAddField), you must now define each field's properties. For
     example, field 1 might accept text input, and field 2 might be a
     push button, etc. Gold includes 27 different field types for your
     programming pleasure and they are organized into the following
     categories:

          String Fields
          Number Fields
          Date Fields
          Push Buttons
          Radio Buttons and Check Boxes
          List Fields
          Multi-Line Edit Fields
          Hot Spots

Understanding Field Formats

          All the standard fields (i.e. string, number and date fields)
     accept a string variable called DefFormat. This string uses Gold's
     special mask characters to define the field's width and optionally
     any mask characters, i.e. characters which are displayed in the
     input field, but are non-editable, and the cursor automatically
     jumps past these mask characters.

     There are four pre-defined format characters in Gold:

          #    Allow 0..9, '.','-', and 'e' for scientific

          @    Only letters of the alphabet and punctuation

          *    Any character the user can find!

          !    Like @, but all characters are converted to uppercase

          Any other characters identified in the field's format string
     are treated as fixed and for display only. For example, if the
     input field is a telephone number, then the format might be defined
     as '(###) ###-####'. The user will only be allowed to input
     numbers, and after typing three numbers, the cursor will jump three
     positions to the right (to the next # character).

          As you may recall, every field is associated with an
     individual variable. Note that this variable is not updated with
     the formatting characters. For example, if the input field on the
     screen displayed "(409) 737-5472" the variable would be updated
     with the string "4097375472. This saves space if the variables are
     stored in a database.

          The GoldSTR unit contains the function PicFormat which is
     passed a string and a picture, and which returns the combined
     formatted string.

String Fields

          The string fields form the backbone of user input forms as
     they are used to gather string data from the user, e.g. names,
     addresses, part numbers, etc. Two different string field types are
     provided for your coding pleasure: a StringField is of fixed width
     and supports the formatting characters; the ScrollField allows the
     user to input a string that is wider than the displayed field
     width, but does not support special formatting characters. The
     fields are defined as follows:

     StringField(FieldID:byte;var Strvar:String;DefFormat:string);

          Basic input field allowing the user to input a string. The
     width of the field is defined by the DefFormat parameter. For
     example, a twelve character name field might be defined with a
     DefFormat of '************' or a shortcut would be to use the
     Replicate function, i.e. Replicate(12,'*');

     ScrollField(FieldID:byte; var Strvar:string;FieldL,MaxL:byte);

          Accepts string input and provides scrolling features, allowing
     the user to input more characters than the visible field width. Use
     this field type when you have limited space on the form, or when
     you want to allow the user to enter a large number of characters,
     and don't want a humongo field displayed. The last two parameters
     define the visible field width and the maximum number of characters
     which the user may input, respectively.

Number Fields

          As you might imagine, number fields should be used when you
     want the user to input a number. A variety of different field types
     are provided. The ByteField, WordField, IntegerField, LongIntField
     procedures all accept whole numbers, and are defined as follows:

     ByteField(FieldID:byte;var Bytevar:Byte;DefFormat:string;
                                                        Min,Max:byte);

     WordField(FieldID:byte;var Wordvar:Word;DefFormat:string;
                                                        Min,Max:word);

     IntegerField(FieldID:byte;var Integervar:Integer;DefFormat:string;
                                                      Min,Max:Integer);

     LongIntField(FieldID:byte;var LongIntvar:LongInt;DefFormat:string;
                                                    Min,Max : LongInt);

          The DefFormat passed to these four fields defines the field
     width and although mask characters are supported, you will
     typically use the # character to indicate that numbers should be
     input.

          Each of the four field types accepts Min and Max numbers
     indicating the valid number range that you want to impose on the
     user. For example, an age field might be set to accept ranges from
     age 12 to age 65. If one of the Min or Max values is non zero, Gold
     will automatically impose the range restrictions. The brighter
     among you may have deduced that the Min and Max should both be set
     to zero when you want to accept any number supported by the
     variable type.

          Some modern applications provide spinner icons to allow the
     user to quickly scroll through a number series. Gold provides the
     SpinLongField function for this purpose (defined below). If you
     want the user to input a whole number within a specific range, and
     offer spinners, then use SpinLongField, but remember that the
     variable must be of type LongInt.

     SpinLongField(FieldID:byte; var LongIntvar:LongInt;Width:byte;
                                         Min,Max,Increment : LongInt);

          Gold provides two field types for the entry of real numbers.
     Use FixedRealField to get input when there are a fixed number of
     decimal places required. For example, a FixedRealField would be
     ideal for inputting dollar values where two decimal places would be
     used for the cents. If, on the other hand, the number of decimal
     places is at the discretion of the user, use a RealField, because
     the user can enter the decimal point (or whatever obscure character
     your country may utilize for this purpose!) where appropriate.

     RealField(FieldID:byte;var Realvar:extended;DefFormat:string;
                                                      Min,Max:extended);

     FixedRealField(FieldID:byte; var Realvar:Extended;Whole,DP:byte;
                                                      Min,Max:extended);

          As the name suggests, the SpinRealField (defined below)
     provides real field input with spinners. The last parameter defines
     how much the input number will be incremented or decremented when
     the spinner is clicked.

     SpinRealField(FieldID:byte; var Realvar:extended;Whole,DP:byte;
                                                Min,Max,Delta:extended);

Date Fields

          While Gold won't make you more attractive to the opposite sex,
     it will allow you to get dates from your users! Gold supports eight
     different date formats, which are identified using the gDate
     enumerated type defined in GoldDate as follows (see Chapter 19):

     gDate = (MMDDYY,MMDDYYYY,MMYY,MMYYYY,DDMMYY,DDMMYYYY,
                                                      YYMMDD,YYYYMMDD);

          You can use one of four different field types to garner date
     information from the user. The traditional DateField allows the
     user to input a date using the keyboard, the traditional way. The
     SpinDateField adds spinners to the standard date field, allowing
     the user to easily scroll backwards and forwards through dates.

          If you want the user to optionally select the date from a
     pop-up calendar (and let's face it, who wouldn't?!), use the
     DropDateField. This field sports an arrow to the right of the
     field. When the user clicks on the arrow, a calendar is displayed.
     If you want spinners and a pop-up calendar, you guessed it, use the
     SpinDropDateField. These four fields are defined as follows:

     DateField(FieldID:byte;var Datevar:Dates;DateFormat:gDate;
                                      DefFormat:string;Min,Max : Dates);

     SpinDateField(FieldID:byte; var Datevar:Dates;
                    DateFormat:gDate;DefFormat:string; Min,Max : Dates);

     DropDateField(FieldID:byte; var Datevar:Dates;
                    DateFormat:gDate;DefFormat:string; Min,Max : Dates);

     SpinDropDateField(FieldID:byte;var Datevar:Dates; DateFormat:gDate;
                                     DefFormat:string; Min,Max : Dates);

          Each date field takes a variable of type dates. As with the
     number fields, the Min and Max variables define the earliest and
     latest dates which you will allow the user to input. (These dates
     are Julian; refer to Chapter 19 to find out more about Julian
     dates.) Specify a Min and Max of zero if you want to accept any
     valid date. Note that Gold will always ensure that the date is
     valid, e.g. November 31st, 1956 would not be accepted.

          Each of the four date fields accepts a DefFormat string. This
     parameter is primarily offered for backward compatibility and to
     provide support for other date formats not intrinsically supported
     by Gold. Typically, pass a null string as the DefFormat and Gold
     will assign the appropriate picture characters based on the
     DateFormat specified.

Push Buttons

          Push button fields don't accept direct user input, i.e. you
     can't enter data into a "button". Button fields, therefore, are not
     linked to a variable. Each button added to a form has an action
     code of type gAction. When the user presses the button, the
     button's action code is processed. This results in the form editing
     session finishing, and the action code returned by the form is the
     action code assigned to the button. You may recall that gAction is
     defined in the GoldIO unit as follows:

          gAction = (None,NextField,PrevField,NextForm,PrevForm,
          Refresh,Enter,Help,Stop1,Stop2,...Stop97,Stop98,Stop99,
          Finished,Cancel1,Cancel2,Cancel3,Cancel4,Cancel5,
          Cancel6,Cancel7,Cancel8,Cancel9,Escaped);

          Buttons can be added to a form using ButtonField or
     ButtonDefaultField, which are defined as follows:

     ButtonField(FieldID:byte; Face:string; Action:gAction);

     ButtonDefaultField(FieldID:byte; Face:string; Action:gAction);

          The second parameter identifies the text that will be
     displayed on the button, and the third parameter is the gAction
     code that will be returned by the form when the button is pressed.
     Use the ButtonDefaultField to identify a button which will be
     "pressed" when the user presses the Enter key -- there should only
     be one default button field on a form.

          The ButtonChangeSettings procedure (defined below) can be used
     to dynamically change the face and action code of a button. This
     might be used, for example, to change an "Edit" button to a "Save"
     button when the button is pressed.

     ButtonChangeSettings(FieldID:byte; Face:string; Action:gAction);


          The following code fragments are from the demo file
     DEMIO13.PAS which uses buttons to help the user tag and untag
     selections in another field.

          procedure SetFields;
          {}
          begin
             .....
             KwikAddField(1, 1,2);
             KwikAddField(2, 6,19);
             KwikAddField(3, 18,19);
             KwikAddField(4, 33,19);
             KwikAddField(5, 49,19);
             KwikAddLastField(6, 62,19);
             AssignHindHook(HindHook);
             {The List}
             FillTheList;
             ConfiguretheListFormat;
             ListAssignSLL(ListFormat,SourceList);
             WrapListField(1,75,1,14,ListFormat);     {Buttons}
             ButtonField(2,'  ~T~ag  ',Stop1);
             ButtonField(3,' Tag ~A~ll  ',Stop2);
             ButtonField(4,' ~U~ntag all ',Stop3);
             ButtonDefaultField(5,'   ~O~K   ',finished);
             ButtonField(6,' ~C~ancel ',escaped);
             SetHK(2,276); {Alt+T}
             SetHK(3,286); {Alt+A}
             SetHK(4,278); {Alt+U}
             SetHK(5,280); {Alt+O}
             SetHK(6,302); {Alt+C}
          end; {SetFields}

          repeat
             DisplayAllFields;
             EditAction := EditForm(1);
             case EditAction of
                Stop1: begin
                   ItemIsTagged := SLLGetTagState
                                    (ListFormat.ActiveNode);
                   SLLSetBit(SLLNodePtr(ListFormat.ActiveNode),
                             TagBit, not ItemIsTagged);
                   SLLSetBit(SLLNodePtr(ListFormat.ActiveNode),
                             ColBit, not ItemIsTagged);
                   end;
                Stop2: begin
                   SetTagAll(ListFormat,true);
                end;
                Stop3: begin
                   SetTagAll(ListFormat,false);
                end;
             end; {case}
             CheckTagButton;
          until EditAction in [Finished,Escaped];

          Note that the form is displayed in a repeat loop -- every time
     a button is pressed the EditForm function (discussed later) returns
     the value of the button pressed by the user. Although the user
     isn't aware of it, the form is being closed and opened many times
     during the edit session.

Radio Buttons and Check Boxes

          Two common elements of contemporary input forms are check
     boxes and radio buttons. Both these objects provide ways of
     choosing items from collections of options.

          A check box field 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 space bar (even
     with your forehead) 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 at any one time. 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.

Radio Buttons

          To define a radio button field, you must define the field
     using RadioField, and then call RadioAddItem once for each option
     in the field. These procedures are defined as follows:

     RadioField(FieldID,width,depth:byte; var SelectedItem:byte);

          The Width and Depth fields define the overall dimensions of
     the radio button field, in characters and lines, respectively --
     this is analogous to defining the groupbox in Windows (just a note
     for you Delphi jocks). The SelectedItem variable indicates which
     radio button is selected by the user. Be sure to assign a suitable
     value to this variable before running the form.

     RadioAddItem(FieldID,ItemX,ItemY:integer; Str,Msg:string;
                                                          ItemHK:word);

          Defines a specific item in a Radio field. Note the ItemX and
     ItemY parameters define the position of the item relative to the
     topleft of the Radio field (not the top left of the form).

CheckFields

          To define a check button field, you must define the field
     using CheckField, and then call CheckAddItem once for each option
     in the field. These procedures are defined as follows:

     CheckField(FieldID,width,depth:byte);

          The Width and Depth fields define the overall demensions of
     the radio button field, in characters and lines, respectively.

     CheckAddItem(FieldID,ItemX,ItemY:integer; Str,Msg:string;

     ItemHK:word;var gResult:boolean);

          Defines a specific item in a Check field. Note the ItemX and
     ItemY parameters define the position of the item relative to the
     topleft of the Radio field (not the top left of the form). The
     gResult variable is set to true when the item is checked, and false
     when it is not checked.

Disabling Radio or Check Button Items

          If you want one element of a radio button or check box field
     to be disabled, use CheckRadioSetActive which is defined as
     follows:

     CheckRadioSetActive(FieldID,ItemNum:integer;IsActive:boolean);

          Sets the enabled state of an individual radio button or check
     box item. The ItemNum parameter represents the individual element
     whose state will be changed -- the first added item has a value of
     1, the second a value of 2, and so on.

          The following code fragment is from the demo file DEMIO6.PAS,
     and show how to construct radio button and checkbox fields:

          {Radio Buttons}
          RadioField(9, 25,4,Cust.Radio);
          RadioAddItem(9, 1,1, 'Corporation','',0);
          RadioAddItem(9, 1,2, 'S Corporation','',0);
          RadioAddItem(9, 1,3, 'DBA','',0);
          RadioAddItem(9, 1,4, 'Individual','',0);
          SetLabel(9,LabelTop,LabelTop,'Business ~T~ype');
          SetHK(9,276);
          {Check Boxes}
          CheckField(10, 25,4);
          CheckAddItem(10, 1,1, 'Small Business','',0,
                       Cust.Check1);
          CheckAddItem(10, 1,2, 'Minority Owned','',0,
                       Cust.Check2);
          CheckAddItem(10, 1,3, 'English Owned','',0,
                       Cust.Check3);
          CheckAddItem(10, 1,4, 'Woman Owned','',0,Cust.Check4);
          CheckRadioSetActive(10,3,false);
          SetLabel(10,LabelTop,LabelTop,'~B~usiness Categeory');
          SetHK(10,304);

List Fields

          Gold supports basic list fields that can be created and
     defined in a couple of fields, as well as gut-busting lists which
     have all the power of the list engine defined in the GoldLIST unit.

Basic List Fields

          Creating a list field is a two step process. First use
     ListField (described below) to define the overall dimensions of the
     list.

     ListField(FieldID,width,depth:byte; var SelectedItem:integer);

          Identfies the width and depth of a list field. The
     SelectedItem variable will be updated with the one-based node
     number of the selected item.

          Individual items in the list can be added using the
     ListAddItem function, which is defined as follows:

     ListAddItem(FieldID:integer; Str:string);

     Adds the specified string to the list. That's all there is to it.

          If you want to add a set of fields in one fell swoop, use the
     ListKwikAddItem as follows:

     ListKwikAddItem(FieldID:integer; Str:string);

          Adds a series of items to a list field. All the items are
     passed in a single string, and the items are separated with the
     split bar character "|".

     The following code fragment shows KwikListAddItem in use:

          SpinDropListField(1,25,PrintInfo.TypeFaceID);
          ListKwikAddItem(1,'Adelaide|Arial MT|Bookman|Courier'
                     ListKwikAddItem(1,'Dom Casual |Freestyle|Script');
          ListKwikAddItem(1,'Helvetica|Juniper|Kidnap|Lithos');
          ListKwikAddItem(1,'Light|Times Roman|Zap Dingbobs');

          In addition to the standard rectangular list box, Gold can
     display lists using formats similar to the Window's ComboBox. Lists
     can be displayed in a drop-down box, as a spinner field, or both
     features can be combined into a single spin drop list field. These
     different formats can be implemented by using one of the following
     three procedures in place of the standard ListField procedure:

          DropListField(FieldID,width:byte; var SelectedItem:integer);
          SpinListField(FieldID,width:byte; var SelectedItem:integer);
          SpinDropListField(FieldID,width:byte;
                                            var SelectedItem:integer);

          Use the EditDropListField (defined below) if you want the user
     to be able to type text into the field or optionally select the
     text from a drop-down list box.

     EditDropListField(FieldID:byte; var Strvar:string;
                                                      FieldL,MaxL:byte);

          To change the contents of a list field "on the fly", you
     should call ListRebuild (defined below) and pass the list contents
     as a single string using the KwikAddField format.

     ListRebuild(FieldID:integer; Str:string);

          If you want to get the literal string of the list items
     selected by the user, you can use the ListGetString function which
     is defined as follows:

     ListGetString(FieldID:integer; EntryNo:integer): string;

Power List Fields

          To display lists in forms which have advanced features such as
     tagging, wrapping, etc., you'll need to create a string linked
     list, and then use one of the power list fields. The power list
     fields use the list engine defined in the GoldLIST unit. We suggest
     you also read Chapters 14 and 15 to gain a thorough understanding
     of Gold's power field support.

          To display a list defined in an StrLL (a simple string linked
     list), first create the field using ListField, SpinDropListField
     (or any of the other variations) and then use ListAssignStrLL to
     define the list contents.

     ListAssignStrLL(FieldID:integer; var SL:StringLL);

          To change the list contents "on the fly", use the following
     procedure in preference to ListRebuild:

     ListUpdateStrLL(FieldID:integer; var SL:StringLL);

          The most powerful list fields provide all the functionality of
     Gold's list windows as a field in a form. Before adding the list
     field to the form, using one of the three procedures listed below,
     first create a linked list and a ListCfg variable following the
     same principles as outlined in Chapter 14.

     WrapListField(FieldID,Colwidth,ColCount,RowCount:byte;
                                              var ListDetails: ListCfg);
     GridListField(FieldID,width,depth:byte;var ListDetails: ListCfg);
     BrowseField(FieldID,width,depth:byte; var ListDetails: ListCfg);

          Use the following two functions to ascertain the user's
     selection from one of these power fields, along with the last key
     that was pressed when the list had focus in the form:

     ListGetActivePick(FieldID:integer): integer;
     ListLastKey(FieldID:byte):word;

Multi-Line Edit Fields

          Chapter 15 described how to use Gold's editor to add editing
     capabilities to an application. The same editor engine can be
     accessed from a form by using a memo field. It goes without saying
     that you need to read Chapter 15 to get the most out of the memo
     field.

          To add a memo field to a form, create a memo data source, such
     as a linked list, create and set a MemoCfg variable and then call
     MemoField, which is defined as follows:

     MemoField(FieldID,width,depth:byte;var Memo:MemoCfg);

Hot Spots

     When is a field not a field? When it's a hotspot!

          A hot spot field is really an invisible button. When you click
     on a hot spot, Gold will end the form edit session and return the
     gAction code assigned to the hotspot. Define a hot spot field using
     the following procedure:

     HotspotField(FieldID:byte; Width,Depth: byte; Action:gAction);

          Although the field seems to have little value, you can use it
     in conjunction with plain text to create custom buttons. For
     example, you might draw four different boxes on the form and write
     some text within each box. These text boxes are not active parts of
     the form, but by adding hotspots which overlay the precise area of
     the box, you can respond to user clicks in the rectangle.

Running The Form

Using EditForm

          Once the form has been fully defined, use the EditForm
     function to pass control to the user and let them get on with the
     input. EditForm is defined as follows:

     EditForm(StartField:byte):gAction;

          Displays the active form and allows the user to edit data in
     the fields. The single passed parameter indicates the FieldID of
     the field which will be initially focused when the edit session
     begins. The function returns a value of type gAction indicating how
     the edit session was terminated. This may be Finished, Escaped, or
     any of the codes assigned to buttons on the form.

          You should check the value returned by EditForm to determine
     whether the user was aborting, or  the user wanted the edits
     committed.

     The following code fragment illustrates this technique:

          if EditForm(1) = Finished then
             SaveTheEdits
          else
             IgnoreTheEdits;

          The function FieldWithFocus returns the FieldID of the field
     with focus. If you are using EditForm in a loop, use FieldWithFocus
     to determine which field to give focus on the next iteration. The
     following code fragment (extracted from DEMDB1.PAS) demonstrates
     this technique:

          ActiveField := 13;
          repeat
             RecNum := X;
             if (DbGetNumRecs > 0) and (X > 0) then
                DatabaseToScreen(X);
             DisplayAllFields;
             LastAction := EditForm(ActiveField);
             ActiveField := FieldWithFocus;
             case LastAction of
                Stop1: begin
                          X := NdxGotoFirst;
                          ActiveField := 13;  { next }
                       end;
                Stop2: begin
                          X := NdxGotoPrev;
                          if X = 0 then
                          begin
                             X := NdxGotoLast;
                             PromptOK(' Top of file ','^Loop...')
                          end;
                       end;
                Stop3: begin
                          X := NdxGotoNext;
                          if X = 0 then
                          begin
                             X := NdxGotoFirst;
                             PromptOK(' End of file ','^Loo...');
                          end;
                       end;
                Stop4: begin
                          X := NdxGotoLast;
                          ActiveField := 12;  { prev }
                       end;
             end;  { case }
          until LastAction = Escaped;
          DisposeFields;
          DisposeForms;

          Old TTTimers might be familiar with the TTT5 procedure
     ProcessInput. This procedure is still supported. The function
     FormExitAction can be called to determine gAction value used to end
     the edit session.

Displaying The Form

          EditForm will automatically display the fields and labels in
     the form before commencing with the edit session. You can, however,
     display the form without passing control to the user by call the
     DisplayForm procedure, which is defined as follows:

     DisplayForm;

          Instructs Gold to display all the fields and labels associated
     with the active form.

          Behind the scenes, DisplayForm calls the two procedures
     DisplayAllLabels and DisplayAllFields. You can call these
     procedures individually if you prefer. For example, within an edit
     loop, you might want to call DisplayAllfields with each iteration
     of the loop because the data in the fields change each time around
     (see the code fragment on the previous page), but you might only
     call DisplayAllLabels once before starting the loop.

Closing and Disposing of a Form

          Behind the scenes, forms are created on the heap in a custom
     form of linked list. You must dispose of the memory used by a form
     when the form is no longer required.

          The primary way to dispose of a form is to call the procedure
     DisposeFields. This procedure will remove all the field data from
     memory and effectively erase all information associated with the
     active table.

          If you have called DisposeFields for a table, you can (at a
     later time) redefine the table with a new set of fields.

Using DisposeForms

          Gold maintains a list of information about each form, e.g.
     whether the form has any fields defined, is the form in a window,
     etc. When you have no further need for any of the forms, you can
     call DisposeForms to free every last drop of memory used by the
     forms.

Using Hooks in Forms

          Brilliant though Gold is, there will be occasions when the
     form routines provided do not meet your exact requirements. There
     are six different custom hooks which will allow you to precisely
     customize a form's behavior to meet your specific needs.

          The hooks provide you with information about the current state
     of the form and the task being performed, and allow you to
     influence the execution of the task. For example, the user may be
     attempting to leave field 2 (perhaps by pressing the tab key or
     clicking with the mouse on another field). You could use a leave
     field hook to trap for the field change event, and only permit the
     user to leave field 2 when the data in the field is satisfactory.

     Gold provides the following form hooks:

          Character Hook
               Called every time the user presses a character.

          Hind Hook
               Called after a every character has been processed.

          Insert Hook
               Called when the user presses the insert key.

          Leave Field Hook
               Called every time the user tries to leave one field and
               move to another.

          Enter Fields Hook
          Called every time the user tries to enter a field, i.e. gives
          a field focus.

          Finished Hook
               Called when the user tries to close the form.

Intercepting Every Character

          Two different hooks allow you to trap each character as it is
     processed. The character hook gives you a chance to review a
     character before it is processed by Gold, and the Hind hook is
     called after each character has been processed.

Character Hook

          For a procedure to be eligible as a form character hook, it
     must adhere to the following rules:

          The function must be declared as a far function at the root
          level. Refer to section Understanding Hooks in Chapter 3 for
          further information.

          The function must be declared with three passed parameters.
          The first parameter is a variable word parameter which
          indicates the key which is about to be processed. The second
          parameter is a variable byte parameter, indicating the ID of
          the field with focus. The third parameter is also a variable
          of type byte which can be used to return a Refresh code,
          instructing Gold on how to proceed.

     The following procedure declaration follows these rules:

          {$F+}
          procedure KeyPressCheck(var C : word;var CF:byte;
                                                      var Refresh:byte);
          begin
              If C = 315 then
              begin
                  Help;
                  C := 0;
              end;
          end;
          {$F-}

          The following procedure is then called to instruct Gold to
     call your procedure every time a character is about to be
     processed:

     AssignCharHook(Proc:CharHookProc);

     Here are a few tips about writing character hooks:

          If you want to trap for a special character (such as Alt-H),
     perform some custom task, and then stop Gold from trying to process
     this character, assign a value of zero to the character and Gold
     will ignore it.

          The Refresh parameter is used to instruct Gold to perform some
     special screen refreshing task. The constants (listed below) are
     declared in the GoldIO unit and should be used for this purpose.
     Assign one of these values to the third passed parameter if you
     want Gold to perform some special field refreshing operation, or if
     you want to end the edit session.

          RefreshNone    = 0;
          RefreshCurrent = 1;
          RefreshAll     = 2;
          RefreshOthers  = 3;
          EndInput       = 99;

Hind Hook

          Like the character hook, the hind hook is called every time a
     key is pressed by the user. The hind hook, however, is called after
     the character has been processed by Gold. One use for a hind hook
     provides is to write some text to the screen based on the data
     entered into other fields; the sub-totals on an invoice form, for
     example. A hind hook is often used to change the enabled/disabled
     state of a field based on the value of other fields, e.g. if a font
     check box is not checked, then the hind hook might disable or hide
     the font list box.

          For a procedure to be eligible as a form character hook, it
     must adhere to the following rules:

          The function must be declared as a far function at the root
          level. Refer to section Understanding Hooks in Chapter 3 for
          further information.

          The function must be declared with two passed parameters. The
          first parameter is a byte parameter indicating the ID of the
          field with focus. The second parameter is a variable of type
          byte which can be used to return a Refresh code, instructing
          Gold on how to proceed.

     The following procedure declaration follows these rules:

          {$F+}
          procedure MyHindHook(CurrentField:byte;var Refresh:byte);
          {}
          begin
             Refresh := RefreshNone;
             if CurrentField = 9 then {radio button}
             begin
                if (Cust.Radio = 4)
                and (FieldGetState(10) = FldOn) then
                begin
                   FieldSetState(10,FldOff);
                   Refresh := RefreshOthers;
                end
                else if (Cust.Radio <> 4)
                and not (FieldGetState(10) = FldOn) then
                begin
                   FieldSetState(10,FldOn);
                   Refresh := RefreshOthers;
                end;
             end;
          end; {MyHindHook}
          {$F-}

          The following procedure is then called to instruct Gold to
     call your procedure every time a character has been processed:

     AssignHindHook(Proc:HindHookProc);

          The demo program DEMIO6.PAS shows a hind hook being used to
     enable and disable fields.

Insert Hook

          The insert hook is called every time the user presses the
     insert key. By default, Gold sets the cursor to a full block in
     overtype mode, and an underscore in insert mode. The hook allows
     the programmer to introduce some other method of indicating the
     insert status, e.g. writing "insert" or "replace" somewhere on the
     screen.

          For a procedure to be eligible as a insert hook, it must
     adhere to the following rules:

          The function must be declared as a far function at the root
          level. Refer to section Understanding Hooks in Chapter 3 for
          further information.

          The function must be declared with one boolean parameter. This
          parameter indicates the new state of the insert key.

     The following procedure declaration adheres to these rules:

          {$F+}
          procedure MyInsHook(Insert:boolean);
          begin
             ...
          end;
          {$F-}

          The following procedure is then called to instruct Gold to
     call your procedure every time the insert key is pressed:

     AssignInsHook(Proc:InsProc);


Field Changing Hooks

          Gold provides 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 that 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 message, or
     move the user to another field if a specific condition is not met.

          You declare leave field and enter field hooks in the same way,
     i.e. they accept the same arguments. For a procedure to be eligible
     as a leave or enter field hook, it must adhere to the following
     rules:

          The procedure must be declared as a far function at the root
          level. Refer to section Understanding Hooks in Chapter 3 for
          further information.

          The procedure must be declared with two variable byte
          parameters. The first parameter indicates the ID of the field
          which the user is about to leave or enter. The second
          parameter is a refresh code which may be updated to instruct
          Gold to either terminate the edit session or refresh one or
          more fields. You can use one of the following constants to
          indicate the appropriate refresh action:

               RefreshNone    = 0;
               RefreshCurrent = 1;
               RefreshAll     = 2;
               RefreshOthers  = 3;
               EndInput       = 99;

          One of the following two procedures can then be used to
     instruct Gold to call your procedure every time the user tries to
     leave or enter a field, respectively:

     AssignLeaveFieldHook(Proc:MoveFieldProc);
     AssignEnterFieldHook(Proc:MoveFieldProc);

The Leave Field Hook

          As mentioned earlier, leave field hooks are often used to add
     some custom field's validation. For example, you might want to
     check that a filename field contains a valid filename. When you
     find that a field input is not valid, you can force the user to
     stay in the same field (i.e. stop changing focus) by setting the
     Field ID parameter to a value of zero.

          Listed below is an example of a leave field hook, which
     displays an error message and keeps the user in the same field when
     the field contains an odd number. (OK, it's a contrived example,
     but you get the idea!)

          {$F+}
          procedure MyLeaveHook(var CurrentField:byte;var Refresh:byte);
          {}
          begin
             if (CurrentField = 3)
             and odd(DressSize) then
             begin
                PromptOK(' Error ','The dress size must be an
                                    even number,e.g. 4,6,8...');
                CurrentField := 0;
             end;
          end;
          {$F-}

The Enter Field Hook

          An enter field hook is called every time the user changes
     focus to a new field. In fact, the hook is called just before the
     focus is changed, and so the hook can redirect the focus to a
     different field. If you want to direct the focus to a different
     field, just assign a new value to the Field ID parameter.

          Traditionally, enter field hooks were used to temporarily
     disable fields, i.e. to prevent a user from giving a field focus.
     Gold now offers the FieldSetState function as a quick and easy way
     to disable the field, so the primary need for an enter field hook
     has disappeared.

          Gold can display a context sensitive message when the user
     moves from field to field, but only a single line of text can be
     displayed. A good application for an enter field hook is to display
     custom multi-line help giving the user instructions relevant to the
     field with focus. The following code fragment shows how this could
     be done:

          {$F+}
          procedure MyEnterHook(var CurrentField:byte;var Refresh:byte);
          {}
          begin
             case CurrentField of
                1: begin
                   WriteAT(.....
                   WriteAT(.....
                   WriteAT(.....
                end;
                3: begin
                   WriteAT(.....
                   WriteAT(.....
                   WriteAT(.....
                end;
                ...
             end; {case}
          end;
          {$F-}

The Finished Hook

          The leave field hook provides a way to implement custom
     validation on a field by field basis. The finish hook, however,
     provides a way to implement complete validation when the user tries
     to end the edit session (other than by escaping).

          The finish hook is called after all Gold's internal validation
     routines have been performed. It provides the user with one last
     chance to check all the edits before the form is closed.

          For a function to be eligible as a finished hook, it must
     adhere to the following rules:

          The function must be declared as a far function at the root
          level. Refer to section Understanding Hooks in Chapter 3 for
          further information.

          The function must be declared no passed parameters, and must
          return a byte value.

          The hook function returns the ID of the field which failed
     validation. If the function returns a zero, Gold will close the
     form. If the function returns a non-zero value, the edit session
     will continue and focus will shift to the Field whose ID matches
     the value returned by the hook function.

          Having declared the hook function, instruct Gold to call your
     procedure every time the user tries to close the form by calling
     the following procedure:

     AssignFinishedHook(Proc:FinishedProc);

          The following example shows a finished hook which makes sure
     that the first field is not null if the second field (a check box
     field) is checked.

          {$F+}
          function CheckNullState:byte;
          {}
          begin
             if (PrinterHeader = true)
             and (Heading = '') then
             begin
                PromptOK(' Error ','Heading Field cannot be empty
                                    when print is checked');
                CheckNullState := 1
             end
             else
                CheckNullState := 0;
          end;
          {$F+}


Launching Forms on the Desktop

          All forms displayed on the desktop must be displayed in a
     window. However, in place of the procedure SetFormWindow (discussed
     earlier), a desktop form must be initialized with the
     LaunchFormInit function which is described below:

     LaunchFormInit(X1,Y1,X2,Y2,style:byte;
                                          CloseProc:FormCloseProc):byte;

          Initializes a form for use on the desktop. The first five
     parameters define the windows coordinates and style, respectively.
     The last parameter is the name of a function which will be called
     when the window containing the form is about to be removed from the
     desktop. LaunchFormInit returns the handle (or number) of the
     window which will contain the form.

          Once the form is initialized, the standard IO routines can be
     called in the normal manner, e.g. KwikAddField, DateField, etc.

          When the form has been fully defined, you launch the form
     (i.e. make it visible on the desktop) by calling the LaunchForm
     procedure which is described as follows:

     LaunchForm(StartField:byte);

          Displays the form window on the desktop. The single passed
     parameter identifies the FieldID of the field which will have focus
     when the form is first displayed.

Understanding the FormCloseProc

          By design, a form launched on the desktop is non-modal, i.e.
     the user can change focus from a form to any other window on the
     desktop. The user may close the form at any time using a variety of
     methods. The user might click on the OK button on the form, click
     on the close icon, or select Close Window from the desktop menu.
     Since the desktop is event driven, you have to "wait" for the form
     to be closed before using the data entered into the form, calling
     DisposeFields and performing all the other necessary house-keeping
     duties.

          When you prepare a form for use on the desktop (using
     LaunchFormInit), you must specify the name of a function declared
     to be of type FormCloseProc, which is declared as follows:

        FormCloseProc = function(FormID: byte):boolean;

          This function will be called by Gold when the user attempts to
     close the form. This is your chance to do the housekeeping duties
     which you would normally perform after a call to EditForm (in a
     non-desktop application). The function returns a boolean to
     indicate to Gold whether the form may be closed. If a FALSE value
     is returned, the form window will not be closed.

          For a function to be eligible as a form close function it must
     adhere to the following rules:

          The function must be declared as a far function at the root
          level. Refer to section Understanding Hooks in Chapter 3 for
          further information.

          The function must be declared with a single passed parameter
          of type byte and return a boolean indicating whether the form
          can be closed. The passed parameter indicates the window
          handle (or number) of the window that is being closed.

          The following function declaration (extracted from
     DEMDESK4.PAS) follows these rules:

          {$F+}
          function FormShutDown(Form:byte):boolean;
          {}
          begin
             BarSetActive(MainMenu,200,true);
             DeskRefreshBgnd;
             DisposeFields;
             DisposeForms;
             PrintInfo.TypeSize := Longvar;
             FormShutDown := true;
          end; { FormShutDown }
          {$F-}

          Review the demo program DEMDESK4.PAS for a complete example of
     using forms in a desktop application.

          var
             Name,Tel:string;
             Age:byte;
             Sex: string[1];
             Result: gAction;

          procedure SetScreen;
          {}
          begin
            ...
          end; { SetScreen }

          procedure SetVars;
          begin
             Name := '';
             Tel := '';
             Age  := 0;
             Sex := '';
          end; { SetVars }

          procedure SetFields;
          begin
             {create the 5 fields}
             KwikAddField(1, 25,9);   {Field 1, column 25, line 9}
             KwikAddField(2, 25,11);  {Field 2, column 25, line 11}
             KwikAddField(3, 25,13);  {Field 3, column 25, line 13}
             KwikAddField(4, 25,15);  {Field 4, column 25, line 15}
             KwikAddField(5, 26,18);  {Field 5, column 25, line 18}
             KwikAddLastField(6, 43,18); {Field 6, column 43, line 18}
             {Field 1}
             StringField(1, Name, '******************************');
             SetLabel(1, LabelLeft,LabelLeft,'Full name:');
             {Field 2}
             StringField(2, Tel, '(###) ###-####');
             SetLabel(2, LabelLeft,LabelLeft,'Tel:');
             {Field 3}
             ByteField(3, Age, '##', 13,65);
             SetLabel(3, LabelLeft,LabelLeft,'Patients age:');
             {Field 4}
             StringField(4, Sex, '!');
             SetLabel(4, LabelLeft,LabelLeft,'Sex:');
             {Fields 4 & 5: buttons}
             ButtonField(5,'   OK   ',finished);
             ButtonField(6,' Cancel ',escaped);
          end; { SetFields }

          begin
             SetScreen;
             SetVars;
             SetFields;
             SetValidation(ValidateAtEnd);
             MouseShow(true);
             Result := EditForm(1);
             GotoXY(1,25);
             DisposeFields;
             DisposeForms;
             ...
          end.

          Specify the field properties, and associate the field with a
     variable.

Add labels to the fields.

     Add the fields to the form and define their order and location

     Initialize the variables that are going to be used in the form

Run the form.

     Dispose of everything when the form is no longer needed.
