Back to Contents


The Dialog class

Summary:

See also: Built-in classes, Interaction Model, Multiple Dialogs, Windows and Forms


The Dialog class is a built-in class that provides an interface to an interactive instruction such as INPUT.

Syntax:

ui.Dialog
 

Methods:

Class Methods
Name Description
setDefaultUnbuffered( v INTEGER ) Sets the default of the UNBUFFERED attribute for the next dialogs.
Object Methods
Name Description
accept( ) Validates all dialog fields.
appendRow( arrname STRING ) Append a row at the end of the list.
deleteRow( arrname STRING, pos INTEGER ) Deletes row at position pos.
getArrayLength( arrname STRING )
 
RETURNING INTEGER
Returns the total number of rows in a list.
getCurrentItem( ) RETURNING STRING Returns the current item having the focus. This can be a field, list or action.
getCurrentRow( arrname STRING )
 
RETURNING INTEGER
Returns the current row of a list.
getFieldBuffer( fieldname STRING )
 
RETURNING STRING
Returns the current input buffer of the field identified by fieldname.
getFieldTouched( fieldname STRING )
 
RETURNING STRING
Returns TRUE if the TOUCHED flag of the specified field is set.
getForm()
 
RETURNING ui.Form
Returns the current form used by this dialog.
insertRow( arrname STRING, pos INTEGER ) Inserts a row before position pos.
nextField( fieldname STRING ) Registers the name of the next field that must get the focus when control returns to the dialog.
setArrayLength( arrname STRING, v INTEGER ) Sets the total number of rows in a list for paged mode.
setActionHidden( actname STRING, v INTEGER ) Hides or shows the default action view identified by actname.
setActionActive( actname STRING, v INTEGER ) Enables or disables the action identified by actname.
setCurrentRow( arrname STRING, row INTEGER) Change the current row in a list.  
setFieldActive( fieldname STRING, v INTEGER ) Enables or disables the field identified by fieldname.
setFieldTouched( fieldname STRING, v INTEGER ) Sets the TOUCHED flag of the field identified by fieldname.
setCellAttributes( attarr ARRAY OF RECORD ) Defines decoration attributes for each cell (singular dialogs only).
setArrayAttributes( arrname STRING, attarr ARRAY OF RECORD ) Defines decoration attributes for each cell (multiple dialogs).

Usage:

The DIALOG object instance

This class provides an interface to the interactive instructions INPUT, INPUT ARRAY, DISPLAY ARRAY, CONSTRUCT, and MENU.

The DIALOG keyword is a pre-defined object variable. To get an instance of this class, use DIALOG inside the interactive instruction block:

01 INPUT BY NAME custid, custname
02   ON ACTION disable
03     CALL DIALOG.setFieldActive("custid",0)
04 END INPUT

Passing the dialog object to a function

The dialog object is only valid during the execution of the interactive instruction. Using the DIALOG keyword outside a dialog instruction block results in a compilation error. However, you can pass the object to a function, in order to write common dialog configuration code:

01 INPUT BY NAME custid, custname
02   BEFORE INPUT
03     CALL setupDialog(DIALOG)
04 END INPUT
05 
06 FUNCTION setupDialog(d)
07   DEFINE d ui.Dialog
08   IF user_group = "admin" THEN
09      CALL d.setActionActive("delete",1)
10      CALL d.setActionActive("convert",1)
11   ELSE
12      CALL d.setActionActive("delete",0)
13      CALL d.setActionActive("convert",0)
14   END IF
15 END FUNCTION

Terminating the dialog

You can use the accept() method to validate field input and terminate the dialog. This method is equivalent to the ACCEPT INPUT / ACCEPT DISPLAY / ACCEPT DIALOG instructions. The method is provided as a 3GL alternative to the ACCEPT control instructions, if you need for example to terminate the dialog in a function, outside the context of a dialog block, where control instructions cannot be used.

See ACCEPT DIALOG for more details.

Getting the total number of rows in a list

The getArrayLength() method can be used to retrieve the total number of rows of an INPUT ARRAY or DISPLAY ARRAY list. You must pass the name of the screen array to identify the list:

01  DIALOG
02       DISPLAY ARRAY custlist TO sa_custlist.*
03         BEFORE ROW
04          MESSAGE "Row count: " || DIALOG.getArrayLength("sa_custlist")
05          ...
06       END DISPLAY
07       INPUT ARRAY ordlist TO sa_ordlist.*
08         BEFORE ROW
09          MESSAGE "Row count: " || DIALOG.getArrayLength("sa_ordlist")
10          ...
11       END INPUT
12          ...

Setting the total number of rows in a list

The setArrayLength() method is used to specify the total number of rows of an INPUT ARRAY or DISPLAY ARRAY list when using the paged mode. You must pass the name of the screen array to identify the list, followed by an integer expression defining the number of rows. When using a dynamic array without ON FILL BUFFER you don't need to specify the total number of rows to the DIALOG instruction: It is defined by the number of elements in the array. However, when using the paged mode in a DISPLAY ARRAY, the total number of rows does not correspond to the elements in the program array, because the program array holds only a page of the whole list. In any other cases, a call to this method is just ignored.

Registering the next field to jump to

The nextField() method can be used register the name of the next field that must get the focus when control goes back to the dialog. This method is similar to the NEXT FIELD instruction, except that it does not implicitly break the program flow. If you want to get the same behavior as NEXT FIELD, the method call must be followed by a CONTINUE DIALOG / INPUT / CONSTRUCT instruction. 

Since this method takes an expression as parameter, you can write generic code, when the name of the target field is not known at compile time. In the next example, the check_value() function returns a field name where the value does not satisfy the validation rules:

01    DEFINE fn STRING
02    ...
03    ON ACTION save
04          IF ( fn := check_values() ) IS NOT NULL THEN
05              CALL DIALOG.nextField(fn)
06              CONTINUE DIALOG
07          END IF
08          CALL save_data()
09          ...

Getting the current row of a list

The getCurrentRow() method can be used to retrieve the current row of an INPUT ARRAY or DISPLAY ARRAY list. You must pass the name of the screen array to identify the list:

01  DIALOG
02       DISPLAY ARRAY custlist TO sa_custlist.*
03         BEFORE ROW
04          MESSAGE "Current row: " || DIALOG.getCurrentRow("sa_custlist")
05          ...
06       END DISPLAY
07       INPUT ARRAY ordlist TO sa_ordlist.*
08         BEFORE ROW
09          MESSAGE "Current row: " || DIALOG.getCurrentRow("sa_ordlist")
10          ...
11       END INPUT
12          ...

Setting the current row in a list

If you want to change the current row in an INPUT ARRAY or DISPLAY ARRAY list, you can use the setCurrentRow() method. You must pass the name of the screen array to identify the list, and the new row number:

01  DEFINE x INTEGER
02  DIALOG
03       DISPLAY ARRAY custlist TO sa_custlist.*
04          ...
05       END DISPLAY
06       ON ACTION goto_x
07          CALL DIALOG.setCurrentRow("sa_custlist", x)
08          ...

Note that moving to a different row with setCurrentRow() will not trigger control blocks such as AFTER ROW. This method will not set the focus either; You need to use NEXT FIELD to set the focus to a list (this works with DISPLAY ARRAY as well as with INPUT ARRAY).

Getting the current item having focus

The getCurrentItem() method returns the name of the current form item having the focus. This can be a simple field, a list or an action view.

Getting the input buffer of a field

The getFieldBuffer() method returns the current input buffer of the specified field. The input buffer is used by the dialog to synchronize form fields and program variables. In some situations, especially when using the BUFFERED mode or in a CONSTRUCT, you may want to access that input buffer.

The fieldname is a string containing the field qualifier, with an optional prefix ("[table.]column").

01    LET buff = DIALOG.getFieldBuffer("customer.cust_name")

The input buffer can be set with:

Getting the TOUCHED flag of a field

The getFieldTouched() method returns TRUE if the TOUCHED flag of the specified field is set.

The fieldname is a string containing the field qualifier, with an optional prefix ("[table.]column"), or a table prefix followed by a dot and a start ("table.*").

01    AFTER FIELD cust_name
02       IF DIALOG.getFieldTouched("customer.cust_address") THEN
03          ...

If the parameter is a screen record following by dot-star, the method checks the touched flags of all the fields that belong to the screen record:

01    ON ACTION quit
02       IF DIALOG.getFieldTouched("customer.*") THEN
03          ...

Setting the TOUCHED flag of a field

The setFieldTouched() method can be used to change the TOUCHED flag of the specified field(s).

The fieldname is a string containing the field qualifier, with an optional prefix ("[table.]column"), or a table prefix followed by a dot and a start ("table.*").

You typically use this method to set the touched flag when assigning a variable, to emulate a user input. Remember when using the UNBUFFERED mode, you don't need to DISPLAY the value to the fields. The setFieldTouched() method is provided as a 3GL replacement for the DISPLAY instructions to set the touched flags.

01    ON ACTION zoom_city
02       LET p_cust.cust_city = zoom_city()
02       CALL DIALOG.setFieldTouched("customer.cust_city", TRUE)
03          ...

If the parameter is a screen record following by dot-star, the method checks the touched flags of all the fields that belong to the screen record. You typically use this to reset the touched flags of a group of fields, after modifications have been saved to the database, to get back to the initial state of the dialog:

01    ON ACTION save
02       CALL save_cust_record()
03       CALL DIALOG.setFieldTouched("customer.*", FALSE)
04          ...

Note that the touched flags are reset to false when using an INPUT ARRAY list, every time you leave the modified row.

Current form used by the dialog

The getForm() method returns a ui.Form object as a handle to the current form used by the dialog. Use this form object to modify elements of the current form. For example, you can hide some parts of the form with the ui.Form.setElementHidden() method. The runtime system is able to detect hidden fields and exclude them from the input list. See Example 2.

Enabling/Disabling Actions

Dialog actions can be enabled and disabled with the setActionActive() dialog method:

01    BEFORE DIALOG
02       CALL DIALOG.setActionActive("zoom",FALSE)

In GUI applications, push-buttons triggering actions should normally be disabled if the current context / situation makes the corresponding action invalid or unusable. For example, a "print" button should be disabled if there is nothing to print. The setActionActive() method is typically used to enable/disable actions according to the context.

The action name must be passed in lowercase letters. Note that sub-dialog actions can be qualified with the sub-dialog prefix within a DIALOG instruction. When the setActionActive() method is called in the context of the sub-dialog, the prefix can be omitted. 

The second parameter of the method must be a boolean expression that evaluates to 0 (FALSE) or 1 (TRUE).

To simplify action activation, you can write a common "setup" function which centralizes all rules to enable/disable actions. This function can then be called from any place in the DIALOG instruction, passing the DIALOG object as parameter.  See Example 3.

In the DIALOG instruction, actions can be prefixed with the sub-dialog identifier. The setActionActive() method can be take a full-qualified action name as parameter.  

Enabling/Disabling Fields

Form fields used by the dialog can be enabled/disabled with the setFieldActive() method. The fields are identified by the field name used in the dialog, with an optional table prefix ("column", "table.column" or "formonly.field"). When a field is disabled, it is still visible, but the user cannot edit the value.

01    ON CHANGE cust_name
02       CALL DIALOG.setFieldActive( "customer.cust_addr", (rec.cust_name IS NOT NULL) )

See also Example 1.

Inserting a new row in a list

The insertRow() method is just a basic row creation function, to insert a row in the list at a given position. It does not set the current row or raise any BEFORE ROW / BEFORE INSERT / AFTER INSERT / AFTER ROW control blocks. It is quite similar to inserting a new element in the program array, except the internal registers are automatically updated (like the total number of rows returned by getArrayLength()). If the list was decorated with cell attributes, the program array defining the attributes will also be synchronized.

Warning: This method should not be called in control blocks such as BEFORE ROW, AFTER ROW, BEFORE INSERT, AFTER INSERT, BEFORE DELETE, AFTER DELETE, but it can safely be used in an ON ACTION block.

TO REMOVE: Default values defined in the form file with the DEFAULT attribute are applied when calling this method.

After the method is called, a new row is created in the program array, so you can assign values to the variables before the control goes back to the user. The getArrayLength() method will return the new row count.

Note that the method does not set the current row and does not give the focus to the list; you need to call setCurrentRow() and execute NEXT FIELD if you want to give the focus.

The method takes two parameters:

  1. The name of the screen array identifying the list.
  2. The index where the row has to be inserted (starts at 1).

If the index is greater as the number of rows, a new row will be appended at the end or the list. This will be equivalent to call the appendRow() method.

Warning: If the list is empty, getCurrentRow() will return zero. So you must test this and use 1 instead to reference the first row otherwise you can get -1326 errors when using the program array.

Following code example shows a user-define action to insert ten rows in the list at the current position:

01    ON ACTION insert_some_rows
02       LET r = DIALOG.getCurrentRow("sa")
03       IF r == 0 THEN LET r = 1 END IF
04       FOR i = 10 TO 1 STEP -1
05          CALL DIALOG.insertRow("sa", r)
06          LET p_items[r].item_quantity = 1.00
07       END FOR

Appending a new row in a list

The appendRow() method is just a basic row creation function to append a row to the end of the list. It does not set the current row or raise any BEFORE ROW / BEFORE INSERT / AFTER INSERT / AFTER ROW control blocks. It is quite similar to appending a new element to the program array, except the internal registers are automatically updated (like the total number of rows returned by getArrayLength()). If the list was decorated with cell attributes, the program array defining the attributes will also be synchronized.

Warning: This method should not be called in control blocks such as BEFORE ROW, AFTER ROW, BEFORE INSERT, AFTER INSERT, BEFORE DELETE, AFTER DELETE, but it can safely be used in an ON ACTION block.

TO REMOVE: Default values defined in the form file with the DEFAULT attribute are applied when calling this method.

After the method is called, a new row is created in the program array, so you can assign values to the variables before the control goes back to the user. The getArrayLength() method will return the new row count.

Note that the method does not set the current row and does not give the focus to the list; you need to call setCurrentRow() and execute NEXT FIELD if you want to give the focus.

The appendRow() method does not create a temporary row as the implicit append action of INPUT ARRAY; The row is considered as permanent once it is added.

The method takes only one parameter:

  1. The name of the screen array identifying the list.

Following code example implements a user-defined to append ten rows at the end of the list:

01    ON ACTION append_some_rows
02       FOR i = 1 TO 10
03          CALL DIALOG.appendRow("sa")
04          LET r = DIALOG.getArrayLength("sa")
05          LET p_items[r].item_quantity = 1.00
06       END FOR

Deleting a row from a list

The deleteRow() method is just a basic row deletion function. It does not reset the current row or raise any BEFORE DELETE / AFTER DELETE / AFTER ROW / BEFORE ROW control blocks (except in some particular situation as described below). It is quite similar to deleting an element to the program array, except that internal registers are automatically updated (like the total number of rows returned by getArrayLength()). If the list was decorated with cell attributes, the program array defining the attributes will also be synchronized.

Warning: This method should not be called in control blocks such as BEFORE ROW, AFTER ROW, BEFORE INSERT, AFTER INSERT, BEFORE DELETE, AFTER DELETE, but it can safely be used in an ON ACTION block.

After the method is called, the row does no more exist in the program array, and the getArrayLength() method will return the new row count.

If the deleteRow() method is called during an INPUT ARRAY, and after the call no more rows are in the list, the dialog will automatically append a new temporary row if the focus is in the list, to let the user enter new data. When using AUTO APPEND = FALSE attribute, no temporary row will be created and the current row register will be automatically changed to make sure that it will not be greater as the total number of rows.

If deleteRow() method is called during an INPUT ARRAY or DISPLAY ARRAY that has the focus, the BEFORE ROW control block will be executed if you delete the current row. This is required to reset the internal state of the dialog.

The method takes two parameters:

  1. The name of the screen array identifying the list.
  2. The index of the row to be deleted (starts at 1).

If you pass zero as row index, the method does nothing (if no rows are in the list, getCurrentRow() returns zero).

Following code example implements a user-defined action to remove the rows that have a specific property:

01    ON ACTION delete_invalid_rows
02       FOR r = 1 TO DIALOG.getArrayLength("sa")
03          IF NOT s_orders[t].is_valid THEN
04            CALL DIALOG.deleteRow("sa",r)
05            LET r = r - 1
06          END IF
07       END FOR

Deleting all rows of a list

The deleteAllRows() method removes all the rows of a list driven by a DISPLAY ARRAY or INPUT ARRAY. This is equivalent to a deleteRow() call, but instead of deleting one particular row, it removes all rows of the specified list.

Warning: This method should not be called in control blocks such as BEFORE ROW, AFTER ROW, BEFORE INSERT, AFTER INSERT, BEFORE DELETE, AFTER DELETE, but it can safely be used in an ON ACTION block.

After the method is called, all rows are deleted from the program array, and the getArrayLength() method will return zero.

The method takes the name of the screen-array as parameter.

If the deleteAllRows() method is called during an INPUT ARRAY, the dialog will automatically append a new temporary row if the focus is in the list, to let the user enter new data. When using AUTO APPEND = FALSE attribute, no temporary row will be created and the current row register will be automatically changed to make sure that it will not be greater as the total number of rows.

If deleteAllRows() method is called during an INPUT ARRAY or DISPLAY ARRAY that has the focus, the BEFORE ROW control block will be executed if you delete the current row. This is required to reset the internal state of the dialog.

Checking form level validation rules

In order to execute NOT NULL, REQUIRED and INCLUDE validation rules defined in the form specification files, you can call the validate() method by passing a list of fields or screen records as parameter. The method returns zero if success and the input error code of the first field which does not satisfy the validation rules.

Note that the current field is always checked, even if it is not part of the validation field list. This is mandatory, otherwise the current field may be left with invalid data.

If an error occurs, the validate() method automatically displays the corresponding error message, and registers the next field to jump to when the interactive instruction gets the control back.

Warning: The validate() method does not stop code execution if an error is detected. You must execute a CONTINUE DIALOG or CONTINUE INPUT instruction to cancel the code execution.

A typical usage is for a "save" action: 

01    ON ACTION save
02       IF DIALOG.validate("cust.*") < 0 THEN
03         CONTINUE DIALOG
04       END IF
05       CALL customer_save()

Handling default action view visibility

The Default View on an action can made visible or invisible with the setActionHidden() dialog method:

01    ON ACTION hide
02       CALL DIALOG.setActionHidden( "confirm", 1 )

Values of the second parameter can be 1 or 0.

Setting the UNBUFFERED mode

By default dialogs are not sensitive to variable changes. To make a dialog sensitive, use the UNBUFFERED attribute in the dialog instruction definition. However, you can define the default for all subsequent dialogs by using the setDefaultUnbuffered() class method:

01 CALL ui.Dialog.setDefaultUnbuffered(TRUE)

Setting TTY attributes for cells in lists

Multiple Dialogs

In an INPUT ARRAY or DISPLAY ARRAY instruction, the setArrayAttributes() method can be used to specify display attributes for each cell. You must define an array with the same number of record elements as the data array used by the INPUT ARRAY or DISPLAY ARRAY. Each element must have the same name as in the data array, and must be defined with a character data type (you typically use STRING).

Fill the display attributes array with color and video attributes. These must be specified in lowercase characters and separated by a blank (ex: "red reverse"). Then, pass the array to the dialog with the setArrayAttributes() method, in a  BEFORE INPUT or BEFORE DISPLAY block. Display attributes can be changed dynamically during the dialog:

01    ON ACTION set_attributes
02       CALL DIALOG.setArrayAttributes( "sr", attarr )

For a complete example, see Example 4.

Singular Dialogs

An equivalent method called setCellAttributes(), takes only the program array as argument. The setCellAttributes() method is designed for singular dialogs, where only one screen array is used.


Examples

Example 1: Disable fields dynamically.

01 FUNCTION input_customer()
02    DEFINE custid INTEGER
03    DEFINE custname CHAR(10)
04    INPUT BY NAME custid, custname
05      ON ACTION enable
06        CALL DIALOG.setFieldActive("custid",1)
07      ON ACTION disable
08        CALL DIALOG.setFieldActive("custid",0)
09    END INPUT
10 END FUNCTION

Example 2: Get the form and hide fields.

01 FUNCTION input_customer()
02    DEFINE f ui.Form
03    DEFINE custid INTEGER
04    DEFINE custname CHAR(10)
05    INPUT BY NAME custid, custname
06      BEFORE INPUT
08        LET f = DIALOG.getForm()
09        CALL f.setElementHidden("customer.custid",1)
10    END INPUT
11 END FUNCTION

Example 3: Pass a dialog object to a function.

01 FUNCTION input_customer()
02    DEFINE custid INTEGER
03    DEFINE custname CHAR(10)
04    INPUT BY NAME custid, custname
05      BEFORE INPUT
06        CALL setup_dialog(DIALOG)
07    END INPUT
08 END FUNCTION
09 
10 FUNCTION setup_dialog(d)
11    DEFINE d ui.Dialog
12    CALL d.setActionActive("print",user.can_print)
13    CALL d.setActionActive("query",user.can_query)
14 END FUNCTION

Example 4: Set display attributes for cells.

01 FUNCTION display_customer()
02    DEFINE i INTEGER
03    DEFINE arr DYNAMIC ARRAY OF RECORD
04                  key INTEGER,
05                  name CHAR(10)
06               END RECORD
07    DEFINE att DYNAMIC ARRAY OF RECORD
08                  key STRING,
09                  name STRING
10               END RECORD
11
12    FOR i=1 TO 10
13       CALL arr.appendElement()
14       LET arr[i].key = i
15       LET arr[i].name = "name " || i
16       CALL att.appendElement()
17       IF i MOD 2 = 0 THEN
18          LET att[i].key = "red"
19          LET att[i].name = "blue reverse"
20       ELSE
21          LET att[i].key = "green"
22          LET att[i].name = "magenta reverse"
23       END IF
24    END FOR
25
26    DIALOG ATTRIBUTES(UNBUFFERED)
27      DISPLAY ARRAY arr TO sr.*
28      ON ACTION att_set
29        CALL DIALOG.setArrayAttributes("sr", att)
30      ON ACTION att_clear
31        CALL DIALOG.setArrayAttributes("sr", NULL)
32    END DIALOG
33
34 END FUNCTION