Extending
                                                                           Input
                                                                           Field
                                                                           Types




         "Stay humble. Always answer the phone - no matter who else is in the
         car."

                                                                     Jack Lemmon


         One of the most used elements of the Toolkit is the full screen input
         facility. If the Toolkit field types do not meet your exact needs, you
         can create your own custom field types. This chapter explains how.


The Input Object Hierarchy

         The objects FormOBJ and WinFormOBJ are used to manage and control full
         screen input. You may recall that the method AddItem is used to add
         individual input fields to the form. AddItem accepts any of the input
         fields shown in the TOTIO Object Hierarchy on page 11.5. As the diagram
         illustrates, all IO objects are descended from the root object Base-
         IOOBJ. If you want to create new input field objects which can be
         managed by the form objects, the new objects must be descended from
         BaseIOOBJ, or any object descended from BaseIOOBJ.

         The BaseIOOBJ object includes the following data and methods, which are
         inherited by all descendant objects:

         ItemIOOBJ = object
            vBoundary: tCoords;
            vHotKey: word;
            vID: word;
            vActive: boolean;
            {methods ...}
            constructor Init;
            procedure   SetActiveStatus(Selectable:boolean);
            function    Active:boolean;
            function    GetHotKey: word;
            procedure   SetHotkey(HK:word);
            function    GetID: word;
            procedure   SetID(ID:word);
            function    Visible: boolean;                        VIRTUAL;
            procedure   Display(Status:tStatus);                 VIRTUAL;
            function    Select(K:word; X,Y:byte):tAction;        VIRTUAL;
            function    ProcessKey(InKey:word;X,Y:byte):tAction; VIRTUAL;
            function    Suspend:boolean;                         VIRTUAL;
            destructor  Done;                                    VIRTUAL;
         end; {ItemIOOBJ}

         Note: the BaseIOOBJ also includes signal-related methods. These are
         discussed in a later section.

20-2                                                       Extending the Toolkit

--------------------------------------------------------------------------------

         The vBoundary variable identifies the (X1,Y1) and (X2,Y2) coordinates
         of the field. When the user clicks the left mouse button during full-
         form input, the form object scans the list of active input objects and
         moves the user to the input object with coordinates corresponding to
         the mouse cursor position. Any descendant field should therefore update
         the vBoundary variable to indicate the physical location of the field.
         The other three BaseIOOBJ variables identify the field's hotkey, ID and
         whether the field is active or selectable. These variables are managed
         by the BaseIOOBJ methods SetActiveStatus, Active, GetHotkey, SetHotkey,
         SetID and GetID. All these methods are suitable for any field type, and
         should not need modification in descendant objects. Just inherit them
         and use them!

         The virtual methods, highlighted in bold, are specific to each descen-
         dant object. As a minimum, any descendant objects should redefine these
         bold methods -- they are the main methods called by the form object
         during full-screen input.

         Apart from special hotkeys and navigation control keys, all the user
         input fields are visible. That is, the user can see them. As the TOTIO
         Hierarchy Diagram illustrates, all visible fields are descended from
         VisibleIOOBJ, which is, in turn, descended from BaseIOOBJ. In addition
         to the BaseIOOBJ objects just discussed, the VisibleIOOBJ objects
         inherit the following methods:

           procedure   SetLabel(Lbl:string);
           procedure   SetMessage(X,Y:byte; Msg:string);
           procedure   WriteMessage;
           procedure   WriteLabel(Status:tStatus);               VIRTUAL;

         As their names suggest, these methods are used to set and display
         labels and messages. Labels are displayed to the immediate left of a
         field and act as a field title. A message is the field's descriptive
         text which is displayed when the user moves to the field. Under normal
         circumstances you will not need to modify these methods. They are
         appropriate to any field type.



Creating New Field Types

         When you want to create a new field object, you must decide which
         existing field object has the properties most closely resembling the
         new field type you want to create. For example, if the field includes
         data input, then you would probably create a descendant of CharIOOBJ.
         However, if the field has multiple lines (like a radio button or list),
         then the new object would best be a descendant of MultiLineIOOBJ. If
         none of the existing fields come anywhere close, create a descendant
         from VisibleIOOBJ.



Extending Input Field Types                                                 20-3

--------------------------------------------------------------------------------

         To illustrate the principles, a new boolean object will be created.
         This object will display two different options, e.g. Yes or No, True or
         False, Live or Die, etc. The field will display one of the options, and
         when the user presses the space bar or clicks the mouse, the field will
         flip to the other option.

         Since the boolean object does not process individual character input,
         and does not occupy multiple lines, the best object to descend from is
         VisibleIOOBJ. The following methods are inherited from VisibleIOOBJ and
         do not need to be modified:

            procedure   SetActiveStatus(Selectable:boolean);
            function    Active:boolean;
            function    GetHotKey: word;
            procedure   SetHotkey(HK:word);
            function    GetID: word;
            procedure   SetID(ID:word);
            procedure   SetLabel(Lbl:string);
            procedure   SetMessage(X,Y:byte; Msg:string);
            procedure   WriteMessage;
            procedure   WriteLabel(Status:tStatus);              VIRTUAL;
            function    Visible: boolean;                        VIRTUAL;

         As well as replacing Init and Done, the primary inherited methods which
         need to be over-written are Display, Select, Processkey and Suspend.
         These four methods are called by the form object during full screen
         input. Additionally, if you want the new boolean object to function
         stand-alone, i.e. without being part of a form, an Activate method
         should be added. Activate will display the field and process user input
         until [KEYCAP] or [KEYCAP] is pressed.

         In keeping with the Toolkit style, SetValue and GetValue methods should
         also be added. These methods are used to set the object's default
         value, i.e. which option to display when the field is activated, and to
         get the user-selected value after input is complete.

         The new boolean object will need to include three data variables: the
         two strings that represent the true and false settings, and a boolean
         to record the object's actual value.

         After all the methods and data have been included, the definition of
         the new BooleanIOOBJ is as follows:

         BooleanIOOBJ = object (VisibleIOOBJ)
            OnString: StringBut;
            OffString: StringBut;
            vInput: boolean;
            {methods...}
            Constructor Init(X,Y:byte; Yes,No:stringbut);
            function    GetValue: boolean;
            procedure   SetValue(On:boolean);
            procedure   Activate;



20-4                                                       Extending the Toolkit

--------------------------------------------------------------------------------

            procedure   Display(Status:tStatus);                  VIRTUAL;
            function    Select(K:word; X,Y:byte):tAction;         VIRTUAL;
            function    ProcessKey(InKey:word;X,Y:byte):tAction;  VIRTUAL;
            function    Suspend:boolean;                          VIRTUAL;
            destructor  Done;                                     VIRTUAL;
         end; {BooleanIOOBJ}


         In the following sections, each method is individually discussed:



Extending Input Field Types                                                 20-5

--------------------------------------------------------------------------------

Init

         The primary responsibilities of the Init method are to set the values
         of the true and false strings, and to update the vBoundary variable
         with the location of the field. In keeping with the other input fields,
         the Init method is passed the (X,Y) coordinate of the leftmost charac-
         ter. By finding the length of the longest string, the method can com-
         pute the rightmost (X,Y) coordinate. The method detail is, therefore,
         as follows:

         constructor BooleanIOOBJ.Init(X,Y:byte; Yes,No:stringbut);
         {}
         var L:byte;
         begin
            VisibleIOOBJ.Init;
            OnString := Yes;
            OffString := No;
            L := length(OnString);
            if L < length(OffString) then
               L := length(OffString);
            with vBoundary do
            begin
               X1 := X;
               X2 := X + pred(L);
               Y1 := Y;
               Y2 := Y;
            end;
            vInput := true;
         end; {BooleanIOOBJ.Init}



SetValue and GetValue

         These methods are short and to the point. All they do is set or return
         the value of the field, and are defined as follows:

         function BooleanIOOBJ.GetValue: boolean;
         {}
         begin
            GetValue := vInput;
         end; {BooleanIOOBJ.GetValue;

         procedure BooleanIOOBJ.SetValue(On:boolean);
         {}
         begin
            vInput := On;
         end; {BooleanIOOBJ.SetValue}




20-6                                                       Extending the Toolkit

--------------------------------------------------------------------------------

Display

         Display is a virtual method which must be declared with a single passed
         parameter of type tStatus. tStatus is an enumerated type with the mem-
         bers HiStatus, Norm and Off, and the value is used to indicate whether
         the field is highlighted (the field the user is currently editing),
         normal (one of the other fields in a form), or inactive (cannot be
         selected).

         The primary responsibility of Display is to display the field contents
         in the appropriate color. The first task is to decide the display
         attribute. To be consistent with the other input objects, the field
         should ascertain the attribute by calling a TOTIO^ function method.
         TOTIO controls the colors for field labels and messages, button fields,
         group fields, list fields, and single line fields. In this case, the
         single line field colors are appropriate. Refer to page 11-40 for a
         full discussion of IOTOT.

         The BooleanIOOBJ method Display is implemented as follows:

         procedure BooleanIOOBJ.Display(Status:tStatus);
         {}
         var Att: byte;
         begin
            case Status of
               HiStatus: Att := IOTOT^.FieldCol(2);
               Norm:     Att := IOTOT^.FieldCol(1);
               Off:      Att := IOTOT^.FieldCol(4);
            end; {case}
            with vBoundary do
               if vInput then
                  Screen.WriteAT(X1,Y1,Att,padleft(OnString,succ(X2-X1),' '))
               else
                  Screen.WriteAT(X1,Y1,Att,padleft(OffString,succ(X2-X1),' '));
         end; {BooleanIOOBJ.Display}



Select

         The Select method is called by the form object whenever the user tries
         to enter the field. The method is responsible for displaying the field
         contents as well as the field's label and message. Select is also
         responsible for moving the cursor to the field.

         Select is actually a function method which returns a member of the
         enumerated type tAction. tAction is used to instruct the form object on
         how to proceed, and includes the members None, NextField, PrevField,
         Finished, Escaped, Refresh, Signal, Enter, Help, Stop1..Stop9. Under
         normal circumstances, Select should return a value of None. This




Extending Input Field Types                                                 20-7

--------------------------------------------------------------------------------

         instructs the Toolkit to proceed as normal. The majority of the other
         members are used by "buttons", which the user selects to finish the
         input session or to ask for help.

         The BooleanIOOBJ method Select is implemented as follows:

         function BooleanIOOBJ.Select(K:word; X,Y:byte):tAction;
         {}
         begin
            Display(HiStatus);
            WriteLabel(HiStatus);
            WriteMessage;
            Screen.GotoXY(vBoundary.X1,vBoundary.Y1);
            Select := none;
         end; {BooleanIOOBJ.Select}



ProcessKey

         When a field is active, the form object repeatedly passes the user
         input to the highlighted object. This continues until the user moves to
         another field, or presses a key which indicates the user wants to fin-
         ish the input session.

         The form object calls the field's method ProcessKey and passes the user
         input to it. The ProcessKey method then updates the value of the field
         based on the user's input. In the case of the BooleanIOOBJ field, the
         field will flip to the other string whenever the keys [KEYCAP] [KEYCAP]
         or [KEYCAP] are pressed. The field will also flip if the mouse is
         clicked on the field. The Toolkit responds extremely quickly to a mouse
         press, and it is a good idea to delay for a tenth of a second when the
         mouse has been clicked. This overcomes the problem of the field flip-
         ping a dozen or more times each time the user clicks the mouse.

         ProcessKey is a function method which returns a member of the enumer-
         ated type tAction. Under normal circumstances, the function should
         return a value of None, which indicates that the form object should
         continue passing keys to the field.



           Note: if you were creating a different input field type, you might
           want to return a value of NextField when the current field became
           full. This instructs the form object to suspend the current field
           and select the next field.




         The BooleanIOOBJ method ProcessKey is implemented as follows:




20-8                                                       Extending the Toolkit

--------------------------------------------------------------------------------

         function BooleanIOOBJ.ProcessKey(InKey:word;X,Y:byte):tAction;
         {}
         begin
            if (InKey = 513)
            or (InKey = 32)
            or (inKey = 328)
            or (InKey = 336) then
            begin
               vInput := not vInput;
               Display(HiStatus);
            end;
            if InKey = 513 then {absorb mouse}
               delay(100);
            ProcessKey := None;
         end; {BooleanIOOBJ.ProcessKey}



Suspend

         The Suspend method is called by the form object when the user wants to
         terminate input or move to another field. Suspend is responsible for
         displaying the field and label in the normal attribute, and for remov-
         ing the field message. All these tasks are performed by the inherited
         VisibleIOOBJ method Suspend.

         Suspend is actually a function method which returns a boolean value.
         This provides a mechanism for not allowing the user to leave the field.
         If Suspend returns False, the form object stays in the field. This
         facility should only be used when the user has entered some invalid
         input, and it is good practice to display a message stating how the
         user can correct the problem.

         A user cannot enter an invalid value in a BooleanIOOBJ, and so Suspend
         will always return True. The method Suspend is implemented as follows:

         function BooleanIOOBJ.Suspend:boolean;
         {}
         begin
            Suspend := VisibleIOOBJ.Suspend;
         end; {BooleanIOOBJ.Suspend}


Activate

         The Activate method provides a way to use the object as a stand-alone
         field, i.e. not as part of a form. Activate is responsible for select-
         ing the field, getting input, and passing the input to ProcessKey.
         Activate should repeatedly pass input to ProcessKey until the user
         presses [KEYCAP] or [KEYCAP] to indicate the end of input. The method
         Suspend should then be called to deselect the field.



Extending Input Field Types                                                 20-9

--------------------------------------------------------------------------------

         The BooleanIOOBJ method Activate is implemented as follows:

         procedure BooleanIOOBJ.Activate;
         {}
         var
            Action: tAction;
         begin
            Action := Select(0,0,0);
            with Key do
            begin
               repeat
                  GetInput;
                  Action := ProcessKey(LastKey,LastX,LastY);
               until ((LastKey = 324) or (LastKey = 13)) and Suspend;
            end;
         end; {BooleanIOOBJ.Activate}



Done

         Since BooleanIOOBJ has no dynamic data of its own, all Done needs to do
         is call VisibleIOOBJ's method Done, as follows:

         destructor BooleanIOOBJ.Done;
         {}
         begin
            VisibleIOOBJ.Done;
         end; {BooleanIOOBJ.Done}




         That's the new BooleanIOOBJ defined. The full source code is contained
         in the file EXTIO.PAS.


Using BooleanIOOBJ

         Since BooleanIOOBJ is inherited from BaseIOOBJ, it can be used in full
         form input just like any other input object. Listed below is the demo
         program EXTDEM7.PAS which shows the new object in action. This demo
         program is actually an adaptation of DEMIO2.PAS discussed in chapter
         11. Figure 20.1 illustrates the generated display.

         Program ExtendedDemoSeven;

         Uses DOS,CRT,
              totFAST, totIO1, totIO2, extIO;




20-10                                                      Extending the Toolkit

--------------------------------------------------------------------------------

         Var
            Name: LateralIOOBJ;
            Phone: PictureIOOBJ;
            Price: FixedRealIOOBJ;
            Status: BooleanIOOBJ;
            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');
               SetRules(JumpIfFull);
            end;
            with Price do
            begin
               Init(35,9,8,2);
               SetLabel('Unit Price');
               SetValue(250.0);
               SetMinMax(0.1,12250.0);
               SetRules(EraseDefault);
            end;
            with Status do
            begin
               Init(35,11,' Nice Guy ',' Jerk ');
               SetLabel('Category');
            end;
            Keys.Init;
         end; {InitVars}

         begin
            ClrScr;
            Screen.TitledBox(15,3,65,13,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);




Extending Input Field Types                                                20-11

--------------------------------------------------------------------------------

               AddItem(Name);
               AddItem(Phone);
               AddItem(Price);
               AddItem(Status);
               Result := Go;
               if Result = Finished then
                  {update the database..}
               else
                  {call Esc routine};
            end;
         end.




Figure 20.1                                                             [SCREEN]
Using
BooleanIOOBJ



Understanding Signals

         In sophisticated input forms, the data input by a user in one field may
         affect the data of some related fields on the form. Signals provide
         this capability in the Toolkit.



Signal Theory

         The BaseIOOBJ object includes the following three signal-related meth-
         ods:

         procedure   RaiseSignal(var TheSig:tSignal);                       VIR-
         TUAL;
         procedure   ShutdownSignal(var BaseSig:tSignal);
         VIRTUAL;
         procedure   HandleSignal(var BaseSig:tSignal; var NewSig:tSignal); VIR-
         TUAL;

         The totIO1 unit includes the record tSignal, which is defined as fol-
         lows:

         tSignal = record
            ID: word;
            MsgType: word;
            case word of           {variant record}
            0: (MsgPtr: pointer);
            1: (MsgLong: longint);
            2: (MsgWord: word);
            3: (MsgInt: integer);




20-12                                                      Extending the Toolkit

--------------------------------------------------------------------------------

            4: (MsgByte: byte);
            5: (MsgChar: char);
         end;

         tSignal is a variant record which can be used to store any data which
         needs to be communicated between input fields.

         An input field's object methods Select, ProcessKey, and ProcessEnter
         are function methods which return a value of type tAction. If any of
         these methods return a value of SIGNAL, the form object will immedi-
         ately call that input object's RaiseSignal method. This method is
         passed a variable parameter of type tSignal. The variable is updated
         with the information which needs to be passed to other fields. Each
         signal has an ID, and the ID should be assigned a non-zero value. In a
         situation where more than one signal can be raised, this ID will indi-
         cate to the other fields which signal is being raised. The signal's
         MsgType field can be used to provide further information about the
         signal, and usually indicates the format of the data being passed with
         the signal, e.g. a 1 might indicate a longint, a 2 might indicate a
         word, etc.



           Note: Input objects which are descended from CharIOOBJ inherit the
           virtual function method ProcessEnter. This method is passed no
           parameters, and returns a value of type tAction. The method is
           called whenever the user presses [KEYCAP]. It is typically used to
           raise a signal or move the user to the next field.




         When a field raises a signal, the form manager passes the signal to the
         next field in the form. This is achieved by calling the next field's
         method HandleSignal. This method is passed the tSignal variable raised
         by the originating field. The HandleSignal method can inspect the sig-
         nal ID and, if appropriate, respond to the signal. If the field han-
         dling the signal knows that the signal is not intended for any other
         field, it can update the signal ID with a value of 0. This tells the
         form object that the signal has been handled, and the signal is dis-
         carded. Otherwise, the signal is passed to each input field in turn
         until one of the fields sets the ID to 0, or until all the fields have
         been passed the signal.

         When the signal has been handled or passed to every other field, the
         originating field's ShutdownSignal method is called. This method can be
         used to dispose of any data that was created specifically for the sig-
         nal, and for any other housekeeping.




Extending Input Field Types                                                20-13

--------------------------------------------------------------------------------

         Any object which handles a signal can optionally raise a signal of its
         own. The HandleSignal method is passed two parameters of type tSignal.
         The first parameter is the original signal raised by another field. The
         second parameter is an empty signal which the handling field can update
         with its own signal. The form object inspects the second signal
         returned by the field's HandleSignal method, and if the ID is set to a
         non-zero value, a new signal is raised and passed to the other fields.
         Only when this new signal has been handled will the form manager con-
         tinue with the processing of the original signal.



A Signal Example

         The way to use signals is best illustrated by example. In this section
         a demo program will be developed which prompts the user to input some
         directories, as a precursor to installing some software. The user is to
         be prompted to input five different directories, one for the programs,
         one for the doc files, etc. Like Turbo Pascal's own Install program,
         each of the input fields needs to be updated if the user enters a new
         default directory into the first field.

         To solve this problem, two new field objects must be created, and both
         of them will be descended from StringIOOBJ. One object will be used to
         prompt the user to input the default directory, and will raise a signal
         when the user changes the field value. The other object will be used
         for the input of the other directories, and will include a method to
         handle the signal raised by the first object.

         In this example, the first object is called MasterStringIOOBJ, and it
         will raise a signal whenever the user enters a new directory. The new
         object is declared as follows:

         TYPE
         MasterStringIOOBJ = object (StringIOOBJ)
            vLastInput: string;
            {methods}
            constructor Init(X,Y,FieldLen: byte);
            function    ProcessEnter: tAction;                    VIRTUAL;
            function    Select(K:word; X,Y:byte): tAction;        VIRTUAL;
            procedure   RaiseSignal(var TheSig:tSignal);          VIRTUAL;
            procedure   ShutdownSignal(var BaseSig:tSignal);      VIRTUAL;
            function    Suspend:boolean;                          VIRTUAL;
            destructor  Done;                                     VIRTUAL;
         end; {MasterStringIOOBJ}


         The new object should only raise a signal when the user has changed the
         value of the field. The new string variable vLastInput is used to
         record the value of the string when the field is selected. The value of




20-14                                                      Extending the Toolkit

--------------------------------------------------------------------------------

         vLastInput can then be compared to vInputStr (the edited field value)
         when the user tries to leave the field or presses [KEYCAP]. The method
         Select is therefore declared as follows:

         function MasterStringIOOBJ.Select(K:word; X,Y:byte): tAction;
         {}
         begin
            vLastInput := vInputStr;
            Select := StringIOOBJ.Select(K,X,Y);
         end; {MasterStringIOOBJ.Select}


         The object needs to raise a signal when the user presses [KEYCAP]. The
         method ProcessEnter is implemented as follows:

         function MasterStringIOOBJ.ProcessEnter: tAction;
         {}
         begin
            if vLastInput <> vInputStr then {need to signal}
               ProcessEnter := Signal
            else
               ProcessEnter := none;
         end; {MasterStringIOOBJ.ProcessEnter}

         If the value of the string has changed, SIGNAL is returned, otherwise
         NONE is returned.

         The object also needs to raise a signal when the method Suspend is
         called, and the value of the field has changed. Now we are faced with a
         problem, because Suspend cannot directly raise a signal. Suspend
         returns a boolean value to indicate whether the field can be suspended,
         not a tAction value. The trick is to return a boolean value of False,
         indicating that the user may not leave the field, and then stuff the
         keyboard with the keystrokes [KEYCAP] [KEYCAP]. The Toolkit will not
         allow the user to leave the field, the [KEYCAP] key will then be pro-
         cessed, thereby raising a signal via the ProcessEnter method, and
         finally, the [KEYCAP] key will be processed to move the user to the
         next field. The Suspend method is implemented as follows:

         function MasterStringIOOBJ.Suspend:boolean;
         {}
         begin
            if vLastInput <> vInputStr then {need to signal}
            begin
               Suspend := false;
               Key.StuffBuffer(13); {Enter}
               Key.StuffBuffer(9);   {Tab}
            end
            else
               Suspend := StringIOOBJ.Suspend;
         end; {MasterStringIOOBJ.Suspend}




Extending Input Field Types                                                20-15

--------------------------------------------------------------------------------

         The RaiseSignal method must update the signal variable with the infor-
         mation required by the other fields, i.e. the string representing the
         new directory entered by the user. The RaiseSignal method is
         implemented as follows:

         procedure MasterStringIOOBJ.RaiseSignal(var TheSig:tSignal);
         {}
         begin
            with TheSig do
            begin
               ID := SignalNewDirectory;
               MsgType := length(vInputStr);
               MsgPtr := @vInputStr;
            end;
            vLastInput := vInputStr;
         end; {MasterStringIOOBJ.RaiseSignal}

         The signal ID is set to SignalNewDirectory --  a constant assigned the
         value of 1. The MsgType field is set to indicate the length of the
         string, and the variant record MsgPtr is updated to point to the user
         input string. This signal, therefore, provides sufficient data for the
         dependent fields to ascertain the new directory.

         In this example, no dynamic data is created for the signal, and so the
         ShutdownSignal method doesn't need to do anything.


         Now let's turn our attention to the object which needs to respond to
         the signal raised by MasterStringIOOBJ. In this example, we will name
         the new object SlaveStringIOOBJ, and it will inherit all the properties
         of StringIOOBJ. The only method (in addition to Init and Done) which
         needs to be updated is HandleSignal. This method needs to check the
         value of the Signal and update the value of the field with the new
         directory string. The signal field MsgType stores the length of the new
         string, and the field MsgPtr points to the new string.

         The HandleSignal method is implemented as follows:

         procedure SlaveStringIOOBJ.HandleSignal(var BaseSig:tSignal; var NewS-
         ig:tSignal);
         {}
         var temp:string;
         begin
            with BaseSig do
            begin
               if (ID = SignalNewDirectory) then
               begin
                  move(MsgPtr^,Temp,succ(MsgType));
                  if Temp <> vInputStr then
                  begin
                     vInputStr := Temp;




20-16                                                      Extending the Toolkit

--------------------------------------------------------------------------------

                     Display(Norm);
                  end;
               end;
            end;
         end; {SlaveStringIOOBJ.HandleSignal}




         To recap, two new field objects have been created. A MasterStringIOOBJ
         field raises a signal when its value is changed, and SlaveStringIOOBJ
         fields change their value accordingly. The on-disk demo file EXT-
         DEM8.PAS includes the full solution to the problem. Figure 20.2 illus-
         trates the output generated by this program.



Figure 20.2                                                             [SCREEN]
Raising
Signals



         Review the source code of the DirWinOBJ object in totDIR for another
         example of how fields can communicate with signals.