This is the most important function of the program. It implements the multiple dialog
        instruction to control order and items input simultaneously.
The function uses the opflag variable to determine the state of the operations
            for items:
- N - no current operation
 
- T - temporary row was created
 
- I - row insertion was done in the list
 
- M - row in the list was modified
 
Function orditems_dialog (orders.4gl)
001 FUNCTION orditems_dialog()
002   DEFINE query_ok SMALLINT,
003          id INTEGER,
004          name LIKE customer.store_name,
005          opflag CHAR(1),
006          curr_pa INTEGER
007 
008   DIALOG ATTRIBUTES(UNBUFFERED)
009 
010    INPUT BY NAME order_rec.*, order_total 
011      ATTRIBUTES(WITHOUT DEFAULTS, NAME="order")
012 
013     ON ACTION find 
014        IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
015        CALL order_query()
016 
017     ON ACTION new 
018        IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
019       IF NOT order_new() THEN
020          EXIT PROGRAM
021       END IF
022 
023     ON ACTION save 
024        IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
025 
026     ON CHANGE store_num 
027        IF NOT order_check_store_num() THEN NEXT FIELD CURRENT END IF
028 
029     ON ACTION zoom1
030        CALL display_custlist() RETURNING id, name 
031        IF id > 0 THEN
032           LET order_rec.store_num = id 
033           LET order_rec.store_name = name 
034           CALL DIALOG.setFieldTouched("store_num", TRUE)
035        END IF
036 
037     AFTER INPUT
038        IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
039 
040     ON ACTION first 
041        IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
042        CALL order_move(move_first)
043     ON ACTION previous 
044        IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
045        CALL order_move(move_prev)
046     ON ACTION next 
047        IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
048        CALL order_move(move_next)
049     ON ACTION last 
050        IF NOT order_update(DIALOG) THEN NEXT FIELD CURRENT END IF
051        CALL order_move(move_last)
052 
053   END INPUT
054 
055   INPUT ARRAY arr_items FROM sa_items.*
056     ATTRIBUTES (WITHOUT DEFAULTS, INSERT ROW =FALSE)
057 
058     BEFORE INPUT
059       MESSAGE msg19
060 
061     BEFORE ROW
062       LET opflag = "N"
063       LET curr_pa = DIALOG.getCurrentRow("sa_items")
064       CALL DIALOG.setFieldActive("stock_num", FALSE)
065 
066     BEFORE INSERT
067       LET opflag = "T"
068       LET arr_items[curr_pa].quantity = 1
069       CALL DIALOG.setFieldActive("stock_num", TRUE)
070 
071     AFTER INSERT
072       LET opflag = "I"
073 
074     BEFORE DELETE
075       IF opflag="N" THEN
076          IF NOT item_delete(curr_pa) THEN
077             CANCEL DELETE
078          END IF
079       END IF
080 
081     AFTER DELETE
082       LET opflag="N"
083 
084     ON ROW CHANGE 
085       IF opflag != "I" THEN LET opflag = "M" END IF
086 
087     AFTER ROW
088       IF opflag == "I" THEN
089          IF NOT item_insert(curr_pa) THEN
090             NEXT FIELD CURRENT
091          END IF
092          CALL items_line_total(curr_pa)
093       END IF
094       IF opflag == "M" THEN
095          IF NOT item_update(curr_pa) THEN
096             NEXT FIELD CURRENT
097          END IF
098          CALL items_line_total(curr_pa)
099       END IF
100 
101     ON ACTION zoom2
102        LET id = display_stocklist()
103        IF id > 0 THEN
104           IF NOT get_stock_info(curr_pa,id) THEN
105              LET arr_items[curr_pa].stock_num = NULL
106           ELSE
107              LET arr_items[curr_pa].stock_num = id 
108           END IF
109           CALL DIALOG.setFieldTouched("stock_num", TRUE)
110        END IF
111 
112     ON CHANGE stock_num 
113        IF NOT get_stock_info(curr_pa,
114                   arr_items[curr_pa].stock_num) THEN
115           LET arr_items[curr_pa].stock_num = NULL
116           CALL __mbox_ok(title2,msg07,"stop")
117           NEXT FIELD stock_num 
118        ELSE
119           CALL items_line_total(curr_pa)
120        END IF
121 
122     ON CHANGE quantity 
123        IF arr_items[curr_pa].quantity <= 0 THEN
124           CALL __mbox_ok(title2,msg13,"stop")
125           NEXT FIELD quantity 
126        ELSE
127           CALL items_line_total(curr_pa)
128        END IF
129 
130   END INPUT
131 
132   BEFORE DIALOG
133      IF NOT order_select("1=1") THEN
134        CALL order_query()
135      END IF
136 
137   ON ACTION about 
138      CALL __mbox_ok(title1,msg18,"information")
139 
140   ON ACTION quit 
141      EXIT DIALOG
142 
143   END DIALOG
144 
145 END FUNCTION
Note: 
- Lines 002 thru 006 define the variables used by this function.
 
- Lines 008 thru 143 define a DIALOG instruction implementing the
                    controller of the form.
                        - Lines 010 thru 053 implement the INPUT BY
                                NAME sub-dialog, controlling the order_rec
                            record input. All actions triggers declared inside the INPUT BY
                                NAME sub-dialog will only be activated if the focus is in
                            this sub-dialog. Data validation will occur when focus is lost by this
                            sub-dialog, or when the user presses the Save button.
                                - Lines 013 thru 015 implement the
                                        find
                                    ON ACTION trigger, to execute a Query By
                                    Example with the order_query() function. Before
                                    calling the query function, we must validate and save current
                                    modifications in the order record with the
                                        order_update() function. If the
                                    validation/save fails, the cursor remains in the current field
                                    (when the user clicks an action view, such as a Toolbar icon,
                                    the focus does not change.)
 
                                - Lines 017 thru 021 implement the
                                        new
                                    ON ACTION trigger, to create a new order
                                    record. Before calling the new function, we must validate and
                                    save current modifications in the order record with the
                                        order_update() function.
 
                                - Lines 023 thru 024 implement the
                                        save ON ACTION trigger, to
                                    validate and save current modifications in the order record with
                                    the order_update() function.
 
                                - Lines 026 thru 027 declare the ON
                                        CHANGE trigger for the store_num
                                    field, to check if the number is a valid store identifier with
                                    the order_check_store_num() function. If the
                                    function returns FALSE, we execute a
                                        NEXT FIELD to stay in the field.
 
                                - Lines 029 thru 035 implement the
                                        zoom1
                                    ON ACTION trigger for the f01
                                    field, to open a typical "zoom" window with the
                                        display_custlist() function. If the user
                                    selects a customer from the list, we mark the field as touched
                                    with the DIALOG.setFieldTouched() method. This
                                    simulates a real user input.
 
                                - Lines 037 thru 038 implement the
                                        AFTER INPUT trigger, to validate and save
                                    current modifications with the order_update()
                                    function when the focus is lost by the order header
                                    sub-dialog.
 
                                - Lines 040 thru 051 implement the
                                        ON ACTION triggers for the four navigation
                                    actions to move in the order list with the
                                        order_move() function. Before calling the
                                    query function, we must validate and save current modifications
                                    with the order_update() function.
 
                            
 
                        - Lines 055 thru 130 implement the INPUT
                                ARRAY sub-dialog, controlling the
                                arr_items array input. All actions triggers
                            declared inside the INPUT ARRAY sub-dialog will only be
                            activated if the focus is in this sub-dialog. The sub-dialog uses the
                                opflag technique to implement SQL instructions
                            inside the dialog code and update the database on the fly.
                                - Lines 058 thru 059 implement the
                                        BEFORE INPUT trigger, to display
                                    information message to the user, indicating that item row data
                                    will be validated and saved in the database when the user moves
                                    to another row or when the focus is lost by the item list.
 
                                - Lines 061 thru 064 implement the
                                        BEFORE ROW trigger, initialize the
                                        opflag operation flag to "N" (no current
                                    operation), save the current row index in
                                        curr_pa variable and disable the
                                        stock_num field (only editable when
                                    creating a new line).
 
                                - Lines 066 thru 069 implement the
                                        BEFORE INSERT trigger, to set the
                                        opflag to "T" (meaning a temporary row was
                                    created). A row will be fully validated and ready for SQL
                                        INSERT when we reach the AFTER
                                        INSERT trigger, there we will set
                                        opflag to "I". The code initializes the
                                    quantity to 1 and enables the stock_num field
                                    for user input.
 
                                - Lines 071 thru 072 implement the
                                        AFTER INSERT trigger, to set the
                                        opflag to "I" (row insertion done in list).
                                    Data is now ready to be inserted in the database. This is done
                                    in the AFTER ROW trigger, according to
                                        opflag.
 
                                - Lines 074 thru 079 implement the
                                        BEFORE DELETE trigger. We execute the SQL
                                        DELETE only if opflag
                                    equals "N", indicating that we are in a normal browse
                                    mode (and not inserting a new temporary row, which can be
                                    deleted from the list without any associated SQL
                                    instruction).
 
                                - Lines 081 thru 082 implement the
                                        AFTER DELETE trigger, to reset the
                                        opflag to "N" (no current operation). This
                                    is done to clean the flag after deleting a new inserted row,
                                    when data validation or SQL insert failed in AFTER
                                        ROW. In that case,  opflag equals
                                    "I" in the next AFTER DELETE / AFTER
                                        ROW sequence and would invoke validation rules
                                    again.
 
                                - Lines 084 thru 085 implement the
                                        ON ROW CHANGE trigger, to set the
                                        opflag to "M" (row was modified), but only
                                    if we are not currently doing a row insertion: Row insertion can
                                    have failed in AFTER ROW  and AFTER
                                        INSERT would not be executed again, but ON
                                        ROW CHANGE would. The real SQL
                                        UPDATE will be done later in AFTER
                                        ROW.
 
                                - Lines 087 thru 099 implement the
                                        AFTER ROW trigger, executing
                                        INSERT or UPDATE SQL
                                    instructions according to the opflag flag. If
                                    the SQL statement fails (for example, because a constraint is
                                    violated), we set the focus back to the current field with
                                        NEXT FIELD CURRENT and keep the
                                        opflag value as is. If the SQL instruction
                                    succeeds, opflag will be reset to "N" in the
                                    next BEFORE ROW.
 
                                - Lines 101 thru 103 implement the
                                        zoom2
                                    ON ACTION trigger for the f08
                                    field, to open a typical "zoom" window with the
                                        display_stocklist() function. If the user
                                    selects a stock from the list, we mark the field as touched with
                                    the DIALOG.setFieldTouched() method. This
                                    simulates a real user input.
 
                                - Lines 112 thru 120 declare the ON
                                        CHANGE trigger for the stock_num
                                    field, to check if the number is a valid stock identifier with
                                    the get_stock_info() lookup function. If the
                                    function returns FALSE, we execute a
                                        NEXT FIELD to stay in the field, otherwise
                                    we recalculate the line total with
                                        items_line_total().
 
                                
                                - Lines 122 thru 128 declare the ON
                                        CHANGE trigger for the quantity
                                    field, to check if the value is greater than zero. If the value
                                    is invalid, we execute a NEXT FIELD to stay in
                                    the field, otherwise we recalculate the line total with
                                        items_line_total().
 
                            
 
                        - Lines 132 thru 134 implement the BEFORE
                                DIALOG trigger, to fill the list of orders with an initial
                            result set.
 
                        - Lines 137 thru 138 implement the
                                about
                            ON ACTION trigger, to display a message box with the
                            version of the program.
 
                        - Lines 140 thru 141 implement the
                                quit
                            ON ACTION trigger, to leave the dialog (and quit the
                            program).