DEFINITION MODULE FieldEditor;

        (********************************************************)
        (*                                                      *)
        (*              Screen editing utilities                *)
        (*                                                      *)
        (*  Programmer:         P. Moylan                       *)
        (*  Last edited:        13 May 1998                     *)
        (*  Status:             OK                              *)
        (*                                                      *)
        (********************************************************)

FROM SYSTEM IMPORT
    (* type *)  ADDRESS;

FROM Windows IMPORT
    (* type *)  Window;

(************************************************************************)
(*                                                                      *)
(*  The editor in this module is "generic" in a limited sense.  It      *)
(*  performs screen editing of variables of arbitrary types, provided   *)
(*  that those types have been declared by calls to DefineFieldType.    *)
(*                                                                      *)
(*  The caller is required, when calling DefineFieldType, to supply     *)
(*  procedures which write and edit variables of that type.  The        *)
(*  "write" procedures has three parameters: a window, a pointer to the *)
(*  variable to be written or edited, and the number of character       *)
(*  positions to use.  The "edit" procedure is similar but has an       *)
(*  extra parameter: the third parameter is for the size of the         *)
(*  variable itself, and the fourth parameter is for the number of      *)
(*  character positions to use on the screen.  For field types where    *)
(*  the number of character positions cannot be determined in advance,  *)
(*  the caller is expected to supply 0 as the value of the third        *)
(*  parameter, and the user-supplied procedures are expected to be able *)
(*  to work out the actual width required.  The user-supplied           *)
(*  procedures are expected, in all cases, to leave the screen cursor   *)
(*  at the character position just beyond the written form of the       *)
(*  field.  They must be prepared to deal with NIL addresses.  The      *)
(*  user-supplied editor must handle all keystrokes which belong to it, *)
(*  but leave intact (via Keyboard.Putback, for example) the keystroke  *)
(*  which causes it to return.  Note that it will be very common for    *)
(*  the editor to receive a cursor movement key implying that the user  *)
(*  does not want to modify this field but is simply skipping over it.  *)
(*  In such cases the editor still has the responsibility for showing   *)
(*  the user where the cursor is, by using blinking, reverse video, etc.*)
(*                                                                      *)
(*  Given all of these rules, and the fact that all the hard work is to *)
(*  be done by user-supplied procedures, you might by now be wondering  *)
(*  whether there is any point in having this module.  The main point   *)
(*  is that the rules impose some uniform standards which make it       *)
(*  easier to develop readable software for applications which need a   *)
(*  lot of screen editing.  They also help, in some applications, to    *)
(*  avoid duplication of effort.  These properties are used to          *)
(*  advantage in modules ListEditor and ScreenEditor.                   *)
(*                                                                      *)
(*  As an added bonus, this module exports some pre-defined field types *)
(*  for commonly encountered cases.  For those cases, the user does not *)
(*  need to call DefineFieldType, and therefore does not need to supply *)
(*  the procedures for writing and editing variables of those types.    *)
(*                                                                      *)
(************************************************************************)

TYPE
    FieldType;          (* is private *)
    WriteProc = PROCEDURE (Window, ADDRESS, CARDINAL);
    EditProc = PROCEDURE (Window, VAR (*INOUT*) ADDRESS, CARDINAL, CARDINAL);

(************************************************************************)
(*                      THE PREDEFINED TYPES                            *)
(************************************************************************)

VAR Byte, Cardinal, Real, String: FieldType;

(************************************************************************)
(*                      DEFINING A NEW TYPE                             *)
(************************************************************************)

PROCEDURE DefineFieldType (Writer: WriteProc;  Editor: EditProc): FieldType;

    (* Introduces a new field type into the system.  Writer is a        *)
    (* user-supplied procedure to write a variable of the new type.     *)
    (* Editor is the user-supplied procedure for editing a variable of  *)
    (* that type.                                                       *)

PROCEDURE DiscardFieldType (type: FieldType);

    (* A notification from the user that this type will not be used     *)
    (* again (unless it is redefined by another call to procedure       *)
    (* DefineFieldType).  Use of this procedure is optional, but is     *)
    (* recommended for the sake of "clean" memory management.           *)

(************************************************************************)
(*                         COMPARING TYPES                              *)
(************************************************************************)

PROCEDURE SameType (t1, t2: FieldType): BOOLEAN;

    (* Returns TRUE iff t1 = t2.        *)

(************************************************************************)
(*                          SCREEN OUTPUT                               *)
(************************************************************************)

PROCEDURE WriteField (w: Window;  address: ADDRESS;  type: FieldType;
                                                        width: CARDINAL);

    (* Writes address^ on the screen at the current cursor position in  *)
    (* window w.  The width parameter specifies how many character      *)
    (* positions to use.  Use width=0 for variable-width fields for     *)
    (* which the write procedure for that type must work out the width. *)

(************************************************************************)
(*                           THE EDITOR                                 *)
(************************************************************************)

PROCEDURE EditField (w: Window;  VAR (*INOUT*) address: ADDRESS;
                              type: FieldType;  varsize, width: CARDINAL);

    (* Edits the variable at the given address, and of the given type,  *)
    (* at the current cursor position in window w.  The width parameter *)
    (* specifies how many character positions are to be used on the     *)
    (* screen.  Set width=0 for variable-width fields where the editor  *)
    (* must determine the width.  We leave this procedure on seeing a   *)
    (* keyboard character which does not belong to us.  The cursor is   *)
    (* left just beyond the last character of the field as it is        *)
    (* displayed.  The terminating keystroke is returned to the         *)
    (* keyboard driver so that it can still be read by the caller.      *)
    (* Note that the address is an inout parameter because there are    *)
    (* cases where we allow the user to create and delete fields, i.e.  *)
    (* address could be NIL on entry but not on exit, or vice versa.    *)

END FieldEditor.
