Project

General

Profile

Editing

Introduction

The Frames and Display chapters provide details of how programs can represent data on the screen for a user. To this point, all of the possible usage of frames has been for output purposes. This chapter provides details on the conversion of code that allows the user to provide input. In other words, this chapter documents the conversion of the interactive user-interface features that can be used to edit data.

For editing to work, the business logic must define a frame which includes widgets that are associated with variables or database fields. The Frames chapter documents how this is done. Optionally, the business logic can copy the current data in those associated variables (or database fields) into the screen-buffer for the frame. Displaying statements can make the current contents of the screen-buffer visible on the screen. If data hasn't been copied into the screen-buffer, then when displayed, the widgets will have default values. Details on how to copy data to the screen-buffer and how to display frames is in the Display chapter.

In each frame, widgets that can visually represent a data value can be placed into editing mode. More specifically, one or more widgets in a frame are placed into editing mode. This can be done explicitly using the ENABLE statement or by directly modifying the SENSITIVE attribute. At that point a WAIT-FOR statement must be issued such that user-interface event processing can occur. Shifting into editing mode can also be done implicitly as part of the CHOOSE, PROMPT-FOR, SET and UPDATE statements.

Once editing mode is active, one of the widgets that are editable will have the focus (i.e. will be in focus or will be the focused widget). The widget in focus is the target to which all user input (key and mouse events) will be directed. Depending on that input, the widget can respond by editing the data, ignoring the input or raising an error. If the input is ignored, the containing frame and window will have an opportunity to process the event. Finally, general event processing can occur, which may cause focus to change, validation expressions to be tested, triggers to be dispatched or the exit from editing mode. For details on the event processing and triggers, please see the Events chapter. For details on validation expressions, see the Validation chapter.

Any changes in the screen-buffer can then be copied back to the associated variables and database fields. This can be done explicitly with the ASSIGN statement or by using the SCREEN-VALUE attribute or INPUT built-in function. Implicitly, this is done in SET and UPDATE statements (there is a hidden ASSIGN statement in each one). See the Display chapter for details on the SCREEN-VALUE attribute and the INPUT built-in function.

The following features are addressed in this chapter:

Statement/Feature Description Java Supported
ASSIGN Moves data from the screen-buffer into a variable or database field. GenericFrame.assignScreenValue(FrameElement data) Yes
CHOOSE Allows user to choose one item from multiple ordered in a list. GenericFrame.choose(Choose ch) or
GenericFrame.choose(Choose ch, ColorSpec color)
Yes
DISABLE Ends editing mode for a widget (the widget can no longer get the input focus or can the contents be modified by key input). GenericFrame.disable() or
GenericFrame.disable(FrameElement[] list) or
GenericFrame.disable(GenericWidget[] list)
Yes
ENABLE Begins editing mode for a widget (the widget can get the input focus and the contents can be modified using key input). GenericFrame.enable() or
GenericFrame.enable(FrameElement[] list) or
GenericFrame.enable(GenericWidget[] list)
Yes
NEXT-PROMPT Defines the widget that will receive the input focus the next time editing mode is entered. GenericFrame.nextPrompt(GenericWidget field) Yes
PROMPT-FOR Waits for new data to be entered into the screen-buffer value of the variable. GenericFrame.promptFor(FrameElement[] widgets) or
GenericFrame.promptFor(FrameElement[] widgets, EventList wait) or
GenericFrame.promptFor(GenericWidget[] widgets) or
GenericFrame.promptFor(GenericWidget[] widgets, EventList wait) or
GenericFrame.promptFor(Stream in, FrameElement[] widgets)
Yes
SENSITIVE Attribute indicating whether the widget is able to receive the input focus or not. This is an alternate way to begin/end editing mode for a widget. GenericWidget.setSensitive(boolean s) or
GenericWidget.setSensitive(logical s)
Yes
SET Waits for new data to be entered into the screen-buffer value of the variable and stores the new value in variables or database fields. GenericFrame.set(FrameElement[] widgets) or
GenericFrame.set(FrameElement[] widgets, EventList wait) or
GenericFrame.set(Stream in, FrameElement[] data)
Yes
UPDATE Copies data to the screen-buffer, waits for the changes and stores the changed value back to variables or database fields. GenericFrame.update(FrameElement[] widgets) or
GenericFrame.update(FrameElement[] widgets, EventList wait) or
GenericFrame.update (Stream in, FrameElement[] data)
Yes
WAIT-FOR Start the event processing loop and applies generated events to the widget with focus. Exit the event processing loop when any one of a specific list of events is encountered. GenericFrame.waitFor() or
GenericFrame.waitFor(EventList el) or
GenericFrame.waitFor(EventList el, GenericWidget focusedWidget) or
GenericFrame.waitFor(EventList el, GenericWidget focusedWidget, int seconds) or
GenericFrame.waitFor(EventList el, int seconds)
Yes

The following sections will describe the editing functionality of the above listed statements and features. Non-editing related syntax such as the frame phrase or widget options are covered in other chapters as noted above and will not be repeated here.

ASSIGN statement

The core syntax and options of the ASSIGN statement is documented in the Data Types chapter. This section will focus exclusively on the use of the ASSIGN statement for user-interface processing. This is the implicit screen-buffer assignment mode, described in the Data Types chapter.

Each widget in a frame which can be used to edit data must be associated with a variable or database field. In the 4GL code it often appears that these things (e.g. the widget and the variable) are the same thing. In fact, there are two entities there. The 4GL keeps an implicit association between the two entities. This association is something that can be considered part of the containing frame.

Data in the widget is said to be in the screen-buffer. When the widget is rendered on the screen for the user, the value for the widget in the screen-buffer is what is displayed. The use of DISPLAY and/or the SCREEN-VALUE attribute will move a value into the screen-buffer for a specific widget.

Data in the associated variable is stored in that variable's memory location (which is separate from the screen-buffer's memory). Data in the associated database field is stored in the record buffer that is being referenced (likewise, it is in separate memory from the screen-buffer). Any use of variables or database fields in business logic, has nothing to do with the screen-buffer.

To explicitly move data from the screen-buffer back into a variable or database field, the following 4GL code can be used:

my-var = my-var:screen-value.

The ASSIGN statement's implicit screen-buffer assignment mode is a convenience syntax which does the same exact thing as the explicit assignment from SCREEN-VALUE, with less code. The core idea is that the frame's concept of the association between a widget and its backing variable or database field, can be leveraged to remember how to push the screen-buffer value(s) back implicitly. The same result could be achieved with this code:

ASSIGN my-var.

The ASSIGN statement does not move data into the variable or field if there is no data in the screen-buffer for that widget.

The Java implementation of the ASSIGN statement is defined in GenericFrame.assignScreenValue(FrameElement data). The FrameElement explicitly defines an association. This allows the runtime to copy the value for the specific widget into the associated variable or database field in the business logic.

If there is no value in the screen buffer or it is null the assignment does not occur.

Variable and Database Field Example

The following shows the common case of implicit copying from the screen-buffer to variables and database fields in business logic.

define temp-table tt field num as integer.
define variable   i            as integer.
define variable   txt          as character.

create tt.
prompt-for i tt.num txt.

message "Business logic value:" i tt.num txt.
message "Screen buffer value:" (input i) (input tt.num) (input txt).
pause.

assign i tt.num txt.

message "Business logic value:" i tt.num txt.
message "Screen buffer value:" (input i) (input tt.num) (input txt).

The converted result in Java:

...
RecordBuffer.openScope(tt);
RecordBuffer.create(tt);

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(i, frame0.widgeti()),
   new Element(new FieldReference(tt, "num"), frame0.widgetNum()),
   new Element(txt, frame0.widgetTxt())
};

frame0.promptFor(elementList0);
...
RecordBuffer.startBatch();
frame0.assignScreenValue(new Element(i, frame0.widgeti()));
frame0.assignScreenValue(new Element(new FieldReference(tt, "num"), frame0.widgetNum()));
frame0.assignScreenValue(new Element(txt, frame0.widgetTxt()));
RecordBuffer.endBatch();
...

Modify the contents of the 3 witches after the application starts. A sample screen:

Notice the difference between screen-buffer and variables or database fields in business logic. This is the stage after PROMPT-FOR but before ASSIGN. The screen-buffer was changed but the state of the business logic is unaltered. The next screen after ASSIGN will be:

Browse Qualifier Example

Widgets inside a BROWSE can be implicitly assigned using this syntax:

def browse brws query q no-lock no-wait display book with single 5 down.
display brws in frame F0.
assign browse brws price.

This will be implemented in Java as:

FrameElement[] elementList1 = new FrameElement[]
{
   new WidgetElement(frame0.widgetBrws())
};

frame0.display(elementList1);

RecordBuffer.startBatch();
frame0.assignScreenValue(new Element(new FieldReference(book, "price"), frame0.widgetPrice()));
RecordBuffer.endBatch();

This version should be used when a widget of the same name exists in more than one frame that is currently in scope. This means we resolve the names of the variables or database fields explicitly to avoid confusion. The same approach works for widgets that are not contained with a BROWSE by using a FRAME frame-name qualifier.

Record Example

The ASSIGN statement can implicitly copy an entire database record back from the screen-buffer to the record buffer. The reference to the table name will be expanded to a list of all fields in that table (minus any fields in the EXCEPT option).

An example in 4GL:

create book.

/* make edits here */

assign book except isbn.

This is implemented in Java as (ASSIGN portion only):

...
RecordBuffer.startBatch();
frame0.assignScreenValue(new Element(new FieldReference(book, "bookId"), frame0.widgetBookId()));
frame0.assignScreenValue(new Element(new FieldReference(book, "bookTitle"), frame0.widgetBookTitle()));
frame0.assignScreenValue(new Element(new FieldReference(book, "publisher"), frame0.widgetPublisher()));
frame0.assignScreenValue(new Element(new FieldReference(book, "onHandQty"), frame0.widgetOnHandQty()));
frame0.assignScreenValue(new Element(new FieldReference(book, "cost"), frame0.widgetCost()));
frame0.assignScreenValue(new Element(new FieldReference(book, "pubDate"), frame0.widgetPubDate()));
frame0.assignScreenValue(new Element(new FieldReference(book, "authorId"), frame0.widgetAuthorId()));
frame0.assignScreenValue(new Element(new FieldReference(book, "soldQty"), frame0.widgetSoldQty()));
frame0.assignScreenValue(new Element(new FieldReference(book, "price"), frame0.widgetPrice()));
RecordBuffer.endBatch();
...

All database fields in this table except for the isbn number will be copied from the screen-buffer to the record buffer for book.

Unsubscripted Array Reference Example

The ASSIGN statement can implicitly copy an entire array (extent variable) back from the screen-buffer to the array's storage in the business logic. The reference to the array name will be expanded to an explicit list of all elements in that array.

An example in 4GL:

define variable txt as character extent 3.

/* make edits here */

assign txt.

This is implemented in Java as:

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(subscript(txt, 1), frame0.widgetTxtArray0()),
   new Element(subscript(txt, 2), frame0.widgetTxtArray1()),
   new Element(subscript(txt, 3), frame0.widgetTxtArray2())
};
/* make edits here */
...
frame0.assignScreenValue(new Element(subscript(txt, 1), frame0.widgetTxtArray0()));
frame0.assignScreenValue(new Element(subscript(txt, 2), frame0.widgetTxtArray1()));
frame0.assignScreenValue(new Element(subscript(txt, 3), frame0.widgetTxtArray2()));

The assignScreenValue() has been expanded into the set of sequential calls with all of the array elements.

CHOOSE Statement

The CHOOSE statement is used to provide a set of mutually exclusive choices which the user can cursor through and select from. There are two modes: ROW and FIELD. In ROW mode, the user cursors up/down through one “column” of a down frame and selects one of the down frame “rows”. In FIELD mode, a list of the widgets of a non-down frame are selectable, and the user cursors through those widgets and selects one of them. This can be used or any purpose which requires selection from a list of options. The currently selected widget is drawn as a highlight (reverse video).

CHOOSE operates on a frame, which can have the programmer-controlled layout and options like any other frame. The frame can be used only for CHOOSE, or it can be used for multiple different language statements. During CHOOSE processing the frame enters a special editing mode that is custom to the CHOOSE statement.

The 4GL syntax:

CHOOSE { { ROW field [HELP char-constant] } | { FIELD { field [HELP char-constant] } ... } }
   [ AUTO-RETURN ]
   [ COLOR color-phrase ]
   [ GO-ON ( key-label ... ) ]
   [ KEYS char-variable ]
   [ PAUSE expression ]
   [ frame-phrase ]
   [ NO-ERROR ]

It is possible to specify the AUTO-RETURN, COLOR, GO-ON, KEYS, PAUSE, frame-phrase, and NO-ERROR options in any order.

The Java implementation of the statement is provided as instance methods in the GenericFrame class:

choose(Choose ch)
choose(Choose ch, ColorSpec color)

As with any other frame processing, the frame definition determines layout, options and the other visible characteristics of the frame. The frame phrase can be used in the CHOOSE statement, just as with other displaying statements. Please see the Frame Phrase section of the Frames chapter for details on how the frame phrase converts.

The specific configuration of widgets, row/field mode and other options are all configured in an instance of containing class com.goldencode.p2j.ui.chui.Choose. The class only has a default constructor:

Choose()

The business logic instantiates this class and then adds each option (except for the color specification) explicitly using setter methods. The class instance is passed to the GenericFrame.choose() for processing. The table below summarizes the option support.

4GL syntax Description Supported
ROW field Forces the CHOOSE to use ROW mode. The field specifies the widget (column) which is to be highlighted (and navigated) in the down frame when the frame becomes editable in the special CHOOSE editing mode. Yes
FIELD { field ... } Forces the CHOOSE to use FIELD mode. Specifies the list of widgets (could be array elements, scalar variables or database fields) which the CHOOSE will use for navigation and selection. These widgets must all be defined as FILL-IN widgets. Yes
HELP char-constant Provides help text for each widget in a CHOOSE FIELD statement or for the entire column in a CHOOSE ROW statement. For the CHOOSE ROW statement the help text is displayed throughout the CHOOSE operation. For the CHOOSE FIELD statement
the help text is widget-specific and changes when the focus is moved between widgets.
Yes
AUTO-RETURN When the user enters a string of initial characters which uniquely identifies a specific choice, that selection is made and the special CHOOSE editing operation automatically ends. The value of LASTKEY is set to KEYCODE as if the user ended the operation explicitly. Yes
COLOR color-phrase Specifies the color attribute value used for the highlight bar. Yes
GO-ON ( key-label ) ... Option to specify the label of the additional keys to be used to initiate GO-ON event. The default values for GO, RETURN, END-ERROR events respectively are the F1, RETURN, F4 keys. Yes
KEYS char-variable Use this option to highlight the particular choice when entering a CHOOSE statement or when it is required to know what keys the user can press to make the fast selection. It is required to provide the name of a character variable in which to place the keys typed by the user. When the selection bar is moved the system saves the keystrokes in char-variable. It is possible to examine these values after CHOOSE statement returns control to the calling procedure. Yes
NO-ERROR Overrides the default error handling for the CHOOSE statement. If using NO-ERROR option and the user presses an invalid key the CHOOSE statement ends silently. The LASTKEY function can be used to examine the key and take appropriate action. The default error behavior when not using NO-ERROR option is terminal beep. Yes
PAUSE expression Specifies the timeout period in seconds. If the user does not make any action and the specified number of seconds elapses the CHOOSE statement leaves editing mode and returns control to the business logic. Yes
frame-phrase Specifies the layout and processing properties of a frame the CHOOSE statement belongs to. Yes

In the next paragraphs we consider Java implementation of every option with specific examples.

ROW Mode Example

In ROW mode, the user navigates through and selects one of the “iterations” of a down frame. The field parameter is the column that will be highlighted. The SCROLL statement too is often used in conjunction with this mode.

4GL example:

define variable i   as integer.
define variable txt as character.

form i txt with 5 down frame fr.

/* loop to display the contents (5 iterations) of the down frame ... */

choose row i with frame fr.

message "Selection, business logic values:" i "(" txt ")".
message "Selection, screen-buffer values:" input i "(" input txt ")".

The result in Java:

...
Choose choose0 = new Choose();
choose0.addRow(frFrame.widgeti());

frFrame.choose(choose0);

message(new Object[]
{
   "Selection, business logic values:", i, "(", txt, ")" 
});
message(new Object[]
{
   "Selection, screen-buffer values:", frFrame.getI(), "(", frFrame.getTxt(), ")" 
});
...

The initial screen shot:

Move the cursor selecting some item and press RETURN. The CHOOSE exits editing mode:

The selection is saved inside the screen representation of the variable, not in the variable in the business logic. This is the way we can take the result of the CHOOSE statement. Note the screen-buffer values has been changed after selection while variable values not. The txt variable is changed synchronous with the i variable which is the subject of the CHOOSE statement.

FIELD Mode Example

FIELD mode allows the user to move the highlight bar through a set of widgets in a frame and select one of them. The field argument(s) specifies the list of widgets which participate in the navigation. Each of these widgets must be FILL-INs.

Consider the following 4GL example:

def var t-key   as char format "x(6)" extent 6 no-undo.
def var i       as int no-undo.
...
form t-key[1] space t-key[2] space t-key[3] skip
     t-key[4] space t-key[5] space t-key[6]
     with frame f-choose title " Menu Simulation ".

display t-key with overlay no-labels centered row 3 frame f-choose.

choose field t-key with frame f-choose.
message "Result: " + t-key[frame-index].

This will be implemented in Java as:

fChooseFrame.display(elementList0);

Choose choose0 = new Choose();
choose0.addField(fChooseFrame.widgetTKeyArray0());
choose0.addField(fChooseFrame.widgetTKeyArray1());
choose0.addField(fChooseFrame.widgetTKeyArray2());
choose0.addField(fChooseFrame.widgetTKeyArray3());
choose0.addField(fChooseFrame.widgetTKeyArray4());
choose0.addField(fChooseFrame.widgetTKeyArray5());

fChooseFrame.choose(choose0);

message(concat(new character("Result: "), subscript(tKey, getFrameIndex())));

The initial screenshot:

Move the highlight bar, select some item and press RETURN. The CHOOSE return control to the application and we can take the current selection to analyze:

A different approach is used to check the selected array element. The selected index can be obtained by checking FRAME-INDEX function value. This internally stores the current selection.

The next example demonstrates how to use the simple not array widget with CHOOSE statement. The 4GL code is:

def var one as integer initial 1.
def var two as integer initial 2.
def var three as integer initial 3.

form one two three with frame f-ch title " Make a Choice ".
display one two three with overlay no-labels centered frame f-ch.
choose field one two three with frame f-ch.
message "Result: " + frame-value.

This will be converted to Java as:

...
fChFrame.openScope();

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(one, fChFrame.widgetOne()),
   new Element(two, fChFrame.widgetTwo()),
   new Element(three, fChFrame.widgetThree())
};

fChFrame.display(elementList0);

Choose choose0 = new Choose();
choose0.addField(fChFrame.widgetOne());
choose0.addField(fChFrame.widgetTwo());
choose0.addField(fChFrame.widgetThree());

fChFrame.choose(choose0);

message(concat(new character("Result: "), getFrameValue()));
...

After application starts the screen is:

Move the cursor making another selection and press ENTER key.

Note the current selection for the frame can be taken by scanning the FRAME-VALUE internal variable.

HELP Option

The option is used to provide an alternative help string when the selection bar is moved to a CHOOSE statement item. This tool is usually used to provide the user with instructions or feedback about the selection.

If the help text is not specified in the CHOOSE statement any alternative help text already defined for the widget in the frame definition will be displayed for that widget. If there is no such text was defined for the widgets, the status default message is displayed while the CHOOSE statement is active.

Consider the following 4GL example:

choose row ivar help "Select item from the list" with frame fr.

This is implemented in Java as:

Choose choose0 = new Choose();
choose0.addRow(frFrame.widgetIvar(), "Select item from the list");

frFrame.choose(choose0);

The initial screenshot is:

The bottommost line of the terminal (the status line) displays the help text. In ROW mode, the text does not change while the user navigates.

This example shows how this option can be applied to FIELD mode. The 4GL code is:

choose field t-key[1] help "Select item 1 or press F4 to exit..." 
             t-key[2] help "Select item 2 or press F4 to exit..." 
             t-key[3] help "Select item 3 or press F4 to exit..." 
             t-key[4] help "Select item 4 or press F4 to exit..." 
             t-key[5] help "Select item 5 or press F4 to exit..." 
             t-key[6] help "Select item 6 or press F4 to exit..." 
             with frame f-choose.

This is implemented in Java as:

Choose choose0 = new Choose();
choose0.addField(fChooseFrame.widgetTKeyArray0(), "Select item 1 or press F4 to exit...");
choose0.addField(fChooseFrame.widgetTKeyArray1(), "Select item 2 or press F4 to exit...");
choose0.addField(fChooseFrame.widgetTKeyArray2(), "Select item 3 or press F4 to exit...");
choose0.addField(fChooseFrame.widgetTKeyArray3(), "Select item 4 or press F4 to exit...");
choose0.addField(fChooseFrame.widgetTKeyArray4(), "Select item 5 or press F4 to exit...");
choose0.addField(fChooseFrame.widgetTKeyArray5(), "Select item 6 or press F4 to exit...");

fChooseFrame.choose(choose0);

The initial screen shot:

Move the selection bar to see how the help string changes:

AUTO-RETURN Option

When each option can be unambiguously identified by typing a unique string of initial characters, then the AUTO-RETURN option will causes the CHOOSE to exit editing mode as soon as the user's selection can be unambiguously determined.

Consider the following 4GL code:

repeat i = 1 to extent(t-key):
  t-key[i]  = chr(asc("a") + i - 1).
end.
...
choose field t-key auto-return with frame f-choose.
message "Result: " + t-key[frame-index].

This will be implemented in Java as:

repeatTo("loopLabel0", toClause0, new Block()
{
   public void body()
   {
      assignSingle(tKey, i, chr(minus(plus(asc("a"), i), 1)));
   }
});
...
Choose choose0 = new Choose();
choose0.addField(fChooseFrame.widgetTKeyArray0());
choose0.addField(fChooseFrame.widgetTKeyArray1());
choose0.addField(fChooseFrame.widgetTKeyArray2());
choose0.addField(fChooseFrame.widgetTKeyArray3());
choose0.addField(fChooseFrame.widgetTKeyArray4());
choose0.addField(fChooseFrame.widgetTKeyArray5());
choose0.setAutoReturn();

fChooseFrame.choose(choose0);
message(concat(new character("Result: "), subscript(tKey, getFrameIndex())));

The initial screenshot is:

Press the 'c' key instead of the arrow navigation keys:

As you can see the CHOOSE terminates with appropriate selection.

The screen values of the items should begin with the different chars to allow the system to separate every item. In the case two or more item will begin with the same key the first one in the list will always be selected.

COLOR Option

This option controls the highlight bar color. The complete COLOR option syntax is:

COLOR color-phrase

For a detailed specification of the color-phrase, please see the COLOR@Statement@ section of the Displaying Data Using Frames chapter.

A 4GL example:

choose field t-key color input with frame f-choose.

This will be implemented in Java as:

Choose choose0 = new Choose();
choose0.addField(fChooseFrame.widgetTKeyArray0());
choose0.addField(fChooseFrame.widgetTKeyArray1());
choose0.addField(fChooseFrame.widgetTKeyArray2());
choose0.addField(fChooseFrame.widgetTKeyArray3());
choose0.addField(fChooseFrame.widgetTKeyArray4());
choose0.addField(fChooseFrame.widgetTKeyArray5());
choose0.setColor(new ColorSpec(ColorSpec.COLOR_INPUT));

fChooseFrame.choose(choose0);

The initial screenshot is:

This overrides the default reverse video in the highlight, instead it has the underline color attribute.

GO-ON Option

This option provides a list of keys that will cause a GO event to be generated which ends the CHOOSE editing mode. By default GO is generated using F1 or RETURN.

Consider the following 4GL code:

choose field t-key go-on(f2 ctrl-r) with frame f-choose.
message "Result: " + t-key[frame-index].

This is implemented in Java as:

Choose choose0 = new Choose();
choose0.addField(fChooseFrame.widgetTKeyArray0());
choose0.addField(fChooseFrame.widgetTKeyArray1());
choose0.addField(fChooseFrame.widgetTKeyArray2());
choose0.addField(fChooseFrame.widgetTKeyArray3());
choose0.addField(fChooseFrame.widgetTKeyArray4());
choose0.addField(fChooseFrame.widgetTKeyArray5());
choose0.addGoOn(new String[]
{
   "f2", "ctrl-r" 
});

fChooseFrame.choose(choose0);

message(concat(new character("Result: "), subscript(tKey, getFrameIndex())));

Move the selection to the some entry and press F2 or CTRL-R. The CHOOSE statement finishes with the current selection as if it receives the GO event:

This option sets additional events that cause GO to be raised, but the default sources of GO remain working.

KEYS Option

This option allows setting the initial selection when entering the CHOOSE statement and is modified on output with the list of keys pressed to make the selection. As a parameter the name of the character variable should be passed.

If the char-variable is initialized before entering the CHOOSE statement, a matching selection is highlighted before starting editing mode. While the selection is moved by pressing keys the system saves the keystrokes into the char-variable. After the CHOOSE is complete this variable can be examined to read the keys used.

Progress implements the KEYS option with a 40 character limit. This limit is not maintained in Java (there is no limit).

The 4GL sample for this case:

def var t-key   as char format "x(6)" extent 6 no-undo.
def var keylist as char init "b".
...
choose field t-key keys keylist with frame f-choose.

message "Key list: " + keylist.
message "Result: " + t-key[frame-index].

This will be implemented as:

character[] tKey = new character[6];
character keylist = new character("b");
...
   Choose choose0 = new Choose();
   choose0.addField(fChooseFrame.widgetTKeyArray0());
   choose0.addField(fChooseFrame.widgetTKeyArray1());
   choose0.addField(fChooseFrame.widgetTKeyArray2());
   choose0.addField(fChooseFrame.widgetTKeyArray3());
   choose0.addField(fChooseFrame.widgetTKeyArray4());
   choose0.addField(fChooseFrame.widgetTKeyArray5());
   choose0.setKeys(keylist);

   fChooseFrame.choose(choose0);

   message(concat(new character("Key list: "), keylist));
   message(concat(new character("Result: "), subscript(tKey, getFrameIndex())));

The initial screen:

Press the 'd' key and RETURN. The screen will change to this:

As you see the selection value and key variable are different. Both can be used to analyze selection.

NO-ERROR Option

Use this option to override the default error handling performing by the CHOOSE statement and to return control to the business logic. The default behavior is to sound a beep in the terminal when any invalid key is pressed. When using the NO-ERROR option, if the user presses an invalid key the CHOOSE statement silently ends. The LASTKEY value can be examined for the key that caused the return. This is a special case of the NO-ERROR because the option does not affect the ERROR-STATUS system handle value.

The simple 4GL example of usage:

choose field t-key no-error with frame f-choose.
message "Result: " + t-key[frame-index].
message "LASTKEY is: " + keyfunction(lastkey).

This will be implemented in Java as:

ErrorManager.silentErrorEnable();
Choose choose0 = new Choose();
choose0.addField(fChooseFrame.widgetTKeyArray0());
choose0.addField(fChooseFrame.widgetTKeyArray1());
choose0.addField(fChooseFrame.widgetTKeyArray2());
choose0.addField(fChooseFrame.widgetTKeyArray3());
choose0.addField(fChooseFrame.widgetTKeyArray4());
choose0.addField(fChooseFrame.widgetTKeyArray5());
choose0.setNoError();

fChooseFrame.choose(choose0);

ErrorManager.silentErrorDisable();
message(concat(new character("Result: "), subscript(tKey, getFrameIndex())));
message(concat(new character("LASTKEY is: "), keyFunction(lastKey())));

The initial screen is:

Select an item other than the default and press an invalid key, PAGE-DOWN for example:

As you can see the CHOOSE terminates and LASTKEY returns the value of the invalid key. The CHOOSE implementation is still enclosed in a silentErrorEnable()/silentErrorDisable() pair. However, the core of the editing logic is really modified by the use of the Choose.setNoError() method.

The following table summarizes the actions CHOOSE performs depending on the NO-ERROR option flag.

Key NO-ERROR Action
Valid cursor movement. On or Off Clears saved keys, selects another item from available ones.
Invalid cursor movement. Off Clears saved key, beep terminal.
Invalid cursor movement. On Returns control to the application. The LASTKEY contains the invalid key code.
String containing the letters that is neither included into the shortcut key set and can not be recognized as initial item's key. Off Clears saved key, try to find corresponding item value from available set. Beep terminal if the key is not found.
String containing the letters that is neither included into the shortcut key set and can not be recognized as initial item's key. On Returns control to the application. The LASTKEY contains the invalid key code.
Invalid string. Off Beep terminal.
Invalid string. On Return control to the procedure. If the KEYS option was used, save last printable key.
Other keys. Off Beep terminal.
Other keys. On Return control to the procedure.

The validity of a key used for cursor movement depends on the CHOOSE layout. For example, in the screen above the RIGHT key is valid when the current selection is “Item b” and becomes invalid when the current selection is “Item c”.

PAUSE Option

Use this option to specify the timeout in seconds. The syntax is:

PAUSE expression

expression can be a constant or any expression which evaluates to a numeric data type. If the time elapses and there is no input from the user, the CHOOSE statement exits editing mode and returns to the business logic. The time-out counter begins before the first keystroke and resets after each key stroke. The LASTKEY value becomes -1 if CHOOSE returns because of a timeout.

A 4GL example:

choose field t-key pause 10 with frame f-choose.
message "Result: " + t-key[frame-index].
message "LASTKEY is: " + lastkey.

This will be implemented in Java as:

Choose choose0 = new Choose();
choose0.addField(fChooseFrame.widgetTKeyArray0());
choose0.addField(fChooseFrame.widgetTKeyArray1());
choose0.addField(fChooseFrame.widgetTKeyArray2());
choose0.addField(fChooseFrame.widgetTKeyArray3());
choose0.addField(fChooseFrame.widgetTKeyArray4());
choose0.addField(fChooseFrame.widgetTKeyArray5());
choose0.setPause(10);

fChooseFrame.choose(choose0);

message(concat(new character("Result: "), subscript(tKey, getFrameIndex())));
message(concat(new character("LASTKEY is: "), lastKey()));

Select some item and wait. After 10 seconds of inactivity beginning from the most recent key press the screen will change to this:

The option is useful to prevent the application locking in a case of inactivity.

ENABLE Statement

The ENABLE statement marks one or more widgets within a frame as being able to receive user input. As part of the processing, if any widget is not already visible it is made visible. There is the equivalent of having an embedded VIEW statement hidden inside the ENABLE processing. Full details of the visible representation of data using ENABLE are described in the ENABLE Statement section of the Displaying Data Using Frames chapter.

Syntax:

ENABLE [ UNLESS-HIDDEN ]
{
     ALL [ EXCEPT field ... ]
   | {   widget-phrase [ WHEN expression ]
       | TEXT ( { widget-phrase [ WHEN expression ] } ... )
       | constant [ AT n | TO n ] [ presentation-element ] [ VIEW-AS TEXT ]
       | SPACE [ ( n ) ]
       | SKIP [ ( n ) ]
     } ...
}
[ IN WINDOW window ] [ frame-phrase ]

There are essentially two different modes for this statement.

1. In ALL mode, all edit-capable widgets in the frame are enabled for editing. An optional EXCEPT clause will drop specific widgets from the list (those widgets will not be enabled).

2. In “explicit widget list” mode, a set of widget-phrase and/or TEXT groups are provided. This forms an explicit list of widgets to be enabled for editing. This mode includes an option to create a TEXT group. TEXT groups temporarily “bind” multiple FILL-IN widgets together into a weak form of multi-line editing. This is an old technique that is still supported, but Progress subsequently implemented the EDITOR widget, which is a better way to do the same thing.

In the above syntax diagram, please note that the constant, SPACE, SKIP, IN WINDOW and frame-phrase options only have visual implications. Essentially these options configure and layout the frame but do not affect the edit processing in any way. For this reason, this chapter does not provide any further detail on these options. Please see the Frames or Displaying Data Using Frames chapters for more details.

ALL Mode

This mode enables all edit capable widgets in a frame. Consider the simple 4GL example:

def var ia as int.
def var ib as int.
def var ic as int.
def var id as int.
def var ie as int.

display ia ib ic id ie with frame f1.
enable all with frame f1.

This will be converted to Java as:

...
integer ia = new integer(0);
integer ib = new integer(0);
integer ic = new integer(0);
integer id = new integer(0);
integer ie = new integer(0);
...
   FrameElement[] elementList0 = new FrameElement[]
   {
      new Element(ia, f1Frame.widgetIa()),
      new Element(ib, f1Frame.widgetIb()),
      new Element(ic, f1Frame.widgetIc()),
      new Element(id, f1Frame.widgetId()),
      new Element(ie, f1Frame.widgetIe())
   };

   f1Frame.display(elementList0);
   f1Frame.enable();
...

The screen after the application starts:

Use TAB button to verify all widgets in a frame are enabled for input. For example 3 times:

Press F4 inside any of the fields to close application.

In the next example we exclude one widget from the list of enabled ones. The 4GL code modification will be in this case:

enable all except id with frame f1.

converting to Java as(widgetId is the name of the widget to be disabled):

...
   f1Frame.enableExcept(f1Frame.widgetId());
...

And the initial screen will look like this:

Again press the TAB key 3 times. The screen become:

As you can see the widget id is drawn disabled and is cannot be used for input. The input focus skips that widget and is moved to the next available widget.

Explicit Widget Mode

This mode enables explicitly specified widgets in a frame to be editable. Consider the simple 4GL example:

...
enable ia ic id ie with frame f1.
...

This will be converted to Java as:

...
FrameElement[] elementList1 = new FrameElement[]
{
   new WidgetElement(f1Frame.widgetIa()),
   new WidgetElement(f1Frame.widgetIc()),
   new WidgetElement(f1Frame.widgetId()),
   new WidgetElement(f1Frame.widgetIe())
};

f1Frame.enable(elementList1);
...

After the application starts the screen will be:

Note the field ib is shown as not enabled. It is not possible to move focus to it and enter any data. Press TAB key and type any digits:

TEXT Group Option

The TEXT group option for the ENABLE statement is not yet supported.

UNLESS-HIDDEN Option

This option is not currently supported.

DISABLE Statement

The DISABLE statement marks one or more widgets within a frame as no longer able to accept user input. This is the opposite of enabling a widget for receiving input by the ENABLE statement. The statement does not hide the widget from the screen. Just makes the widget read-only.

Syntax:

DISABLE [ UNLESS-HIDDEN ]
{
     ALL [ EXCEPT field ... ]
   | {   widget-phrase [ WHEN expression ] } ...
}
[ frame-phrase ]

Notes:

1. In ALL mode, every widget in the referenced frame will be disabled. An exception can be made for those fields that are listed in the EXCEPT field option.

2. It is possible to provide a widget list explicitly to make selective disable instead of disabling all widgets of a frame. It is possible to include a condition for disabling on a per widget basis, using the WHEN clause. The usage of a WHEN clause in a DISABLE statement is the same as in the ENABLE statement.

Please see the Frames or Displaying Data Using Frames chapters for more details.

ALL Mode

This mode disables all widgets in a frame. Since this is such a common use of DISABLE, the GenericFrame.disable() method (with no parameters) is the equivalent of DISABLE ALL. Consider the following 4GL example:

def var ia as int.
def var ib as int.
def var ic as int.
def var id as int.
def var ie as int.

display ia ib ic id ie with frame f1.
disable all with frame f1.

This is converted to Java as:

integer ia = new integer(0);
integer ib = new integer(0);
integer ic = new integer(0);
integer id = new integer(0);
integer ie = new integer(0);
...
   FrameElement[] elementList0 = new FrameElement[]
   {
      new Element(ia, f1Frame.widgetIa()),
      new Element(ib, f1Frame.widgetIb()),
      new Element(ic, f1Frame.widgetIc()),
      new Element(id, f1Frame.widgetId()),
      new Element(ie, f1Frame.widgetIe())
   };

   f1Frame.display(elementList0);
   f1Frame.disable();

After application starts the screen is:

It is not possible to enter any data inside the field or to move the input focus inside. Press F4 to close the application.

Using the EXCEPT option, this mode disables all widgets in a frame except those explicitly specified. It is converted to GenericFrame.disableExcept(FrameElement[]) if there is more than one widget in the list or GenericFrame.disableExcept(GenericWidget) in the case that there is only 1 widget to leave enabled. The following example demonstrates this usage:

...
enable all with frame f1.
disable all except ib with frame f1.
...

This converts to Java as:

...
f1Frame.enable();
f1Frame.disableExcept(f1Frame.widgetIb());
...

After starting application screen now is:

We see one enabled field and can type the data inside.

Explicit Widget Mode

This mode disables a specific list of widgets in a frame. It is converted to GenericFrame.disable(FrameElement[]). Consider the simple 4GL example:

...
disable ib id with frame f1.
...

Converting to Java as:

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(ia, f1Frame.widgetIa()),
   new Element(ib, f1Frame.widgetIb()),
   new Element(ic, f1Frame.widgetIc()),
   new Element(id, f1Frame.widgetId()),
   new Element(ie, f1Frame.widgetIe())
};
...
FrameElement[] elementList1 = new FrameElement[]
{
   new WidgetElement(f1Frame.widgetIb()),
   new WidgetElement(f1Frame.widgetId())
};

f1Frame.disable(elementList1);
...

After application starts the screen is:

Only some fields are disabled in the result. Type TAB key 2 times. The screen becomes:

NEXT-PROMPT Statement

This statement specifies the widget that will receive the input focus when the frame the widget belongs to next receives the input focus.

The complete 4GL syntax for this statement is:

NEXT-PROMPT widget [ frame-phrase ]

widget is the name of the widget in which to place the cursor the next time the frame receives the input focus. If the named widget is not a valid widget in the frame, the conversion process will drop the NEXT-PROMPT statement and no Java code will emit.

For details on the frame-phrase, please see the Frame Phrase section of the Frames chapter.

In Java, the statement has been implemented in class GenericFrame method nextPrompt(GenericWidget widget).

This statement can be used to change the initially focused widget or to dynamically reposition the focus inside an EDITING block.

Consider the following 4GL code:

def var ia as int.
def var ib as int.
def var ic as int.
def var id as int.
def var ie as int.

display ia ib ic id ie.
next-prompt ic.
update ia ib ic id ie.

This will be implemented in Java as:

integer ia = new integer(0);
integer ib = new integer(0);
integer ic = new integer(0);
integer id = new integer(0);
integer ie = new integer(0);
...
   FrameElement[] elementList0 = new FrameElement[]
   {
      new Element(ia, frame0.widgetIa()),
      new Element(ib, frame0.widgetIb()),
      new Element(ic, frame0.widgetIc()),
      new Element(id, frame0.widgetId()),
      new Element(ie, frame0.widgetIe())
   };
...
   frame0.display(elementList0);
   frame0.nextPrompt(frame0.widgetIc());
...
   frame0.update(elementList1);

After the application starts the screen will look like this:

Consider the initial cursor and focus location. It differs from the default one (normally the first widget listed in an UPDATE statement would be in focus).

PROMPT-FOR, SET and UPDATE Statements

Each of these statements is a logical aggregation of multiple other language statements to implement an editing session with the user. The core idea is to specify a list of widgets that should be edited by the user.

Runtime Differences

These statements are virtually identical in 4GL source code (and thus in converted Java code as well). The behavior at runtime is the primary difference. The following table documents the runtime differences:

PROMPT-FOR SET UPDATE
Copy from business logic (variables and database fields) into the screen-buffer.    
ENABLE statement (which has an embedded VIEW).    
WAIT-FOR statement    
DISABLE statement    
ASSIGN statement (copy back from screen-buffer to the associated variables and/or database fields in the business logic).    

As can be seen from the above table, SET is a superset of the functionality of PROMPT-FOR and UPDATE is a superset of the functionality of SET.

It should be noted that UPDATE in particular is more than just the sequential execution of the logical statements. It has the equivalent of a hidden 4GL block embedded in the runtime which changes how it responds to error processing, retry, infinite loop protection and other block-related features of the 4GL.

Further discussion of the runtime behavior is beyond the scope of this book. Please see the book entitled FWD Internals.

Syntax

As noted above, these statements have virtually identical syntax. The Progress documentation does in fact show more differences, but in reality there is only a single real difference (PROMPT-FOR does not support the embedded-assignment cause, SET and UPDATE do support it).

Core syntax:

{ PROMPT-FOR | SET | UPDATE }
   [ STREAM stream ]
   [ UNLESS-HIDDEN ]
   {
        widget [ format-phrase ] [ WHEN expression ]
      | TEXT ( { widget [ format-phrase ] [ WHEN expression ] } ... )
      | { variable | database-field } = expression
      | constant
           [ { AT | TO | COLON } n ]
           [ VIEW-AS TEXT ]
           [ FGCOLOR expression ]
           [ BGCOLOR expression ]
           [ FONT expression ]
      | SPACE [ ( n ) ]
      | SKIP [ ( n ) ]
      | ^
   } ...
   [ GO-ON ( key-label ... ) ]
   [ IN WINDOW window ]
   [ frame-phrase ]
   [ editing-phrase ]

Alternate syntax:

{ PROMPT-FOR | SET | UPDATE }
    [ STREAM stream ]
    [ UNLESS-HIDDEN ]
    record [ EXCEPT field ... ]
    [ IN WINDOW window ]
    [ frame-phrase ]

All options described above are independent (use of each one has no dependencies upon usage of other options) and can be specified in any order.

In the above syntax diagrams, the embedded assignment code in green is only available for SET and UPDATE. PROMPT-FOR does not implement the ASSIGN statement inside and thus cannot support an embedded-assignment.

The following parts of the syntax are related to the configuration, layout and visual aspects of the frame or widgets. For this reason, they are documented elsewhere:

4GL syntax Description Supported
STREAM stream Redirects the input read by these statements. This changes the statement from an interactive editing statement, into a field-by-field parsed reading of a line from the input stream. For more details, see the Redirected Terminal chapter. Yes
format-phrase Specifies the configuration, position and other features of a specific widget. These widget-specific values are converted into static portions of the frame definition or dynamic callbacks registered from the business logic. Please see the Frames chapter for details. Yes
= expression This is the syntax for an embedded-assignment, which can only be used in SET and UPDATE statements. For details, please see the Embedded Assignments section of the Frames chapter. Yes
constant [ { AT | TO | COLON } n ]
[ VIEW-AS TEXT ]
[ FGCOLOR expression ]
[ BGCOLOR expression ]
[ FONT expression ]
Specifies a literal value to be displayed in the frame and its configuration. The COLON option is undocumented but is supported by Progress. The VIEW-AS, color and FONT choices are only documented for PROMPT-FOR but it is believed that these work for SET and UPDATE as well. The conversion supports AT, TO and COLON, but at this time the other options are not supported. See the Format Phrase and Widget Properties sections of the Frames chapter for details. Partial
SPACE [ ( n ) ] Adds horizontal space into the layout of the frame. See the Frames chapter for details. Yes
SKIP [ ( n ) ] Adds 1 or more line breaks after (the preceding widget) into the layout of the frame. See the Frames chapter for details. Yes
^ Specifies that the widget normally in that relative position in the frame is to be skipped when reading from a stream. The data in the stream that would have been parsed by that widget is bypassed instead. See the Redirected Terminal chapter for details. Yes
[ IN WINDOW window ] Specifies the window in which the prompt is happening. The expression must be a handle to a window. No
frame-phrase Provides frame-specific configuration details. See the Frames chapter for details. Yes

Since the 4GL syntax of the 3 statements is virtually identical (except for the statement keyword and embedded assignments), the converted code that results follows this same pattern.

Each statement has a set of 3 public methods which can be generated in the business logic. These methods are implemented in the GenericFrame class, which is the parent class to each frame. The following signatures will be seen:

Statement Common Form Common + GO-ON Form Redirected Input Form
PROMPT-FOR promptFor(FrameElement[]) promptFor(FrameElement[], EventList) promptFor(Stream,
FrameElement[])
SET set(FrameElement[]) set(FrameElement[], EventList) set(Stream,
FrameElement[])
UPDATE update(FrameElement[]) update(FrameElement[], EventList) update(Stream,
FrameElement[])

Each method call requires an array of frame elements. These frame elements define the widgets to be edited, the variables/database fields that are associated with those widgets, any embedded assignments and text groups. The converted code will build the frame element array before calling the promptFor(), set() or update() method.

In the case of the UPDATE statement, data in the associated variables/database fields will be copied into the screen-buffer (from which the widgets get their value). All widgets in the array are enabled for editing, the latest value for the widgets from the screen-buffer is made visible on the screen and the frame moves to the top of the Z-order (this is the result of the hidden ENABLE statement). null entries in the array are silently ignored. The internal WAIT-FOR is entered and the user will edit until a GO or END/ ERROR @condition is raised. On exit, the widgets are disabled and in the case of SET and UPDATE , changes are copied back to the associated variables/database fields by a hidden ASSIGN . During this ASSIGN any embedded-assignments will be processed in the same order as they were specified in the frame element array.

The EventList is the list of keyboard events to be considered as GO. That is the mechanism to replace the GO-ON clause.

The version with the Stream parameter is for redirecting input. See the Redirected Terminal chapter for more details.

PROMPT-FOR Example

This simple example of PROMPT-FOR usage uses a single variable to show the conversion details. The 4GL code:

define variable i as integer initial 3.
prompt-for i.
message "Business logic value:" i.
message "Screen buffer value:" input i.

This is implemented in Java as:

integer i = new integer(3);
...
   FrameElement[] elementList0 = new FrameElement[]
   {
      new Element(i, frame0.widgeti())
   };
   frame0.promptFor(elementList0);
   message(new Object[]
   {
      "Business logic value:", i
   });
   message(new Object[]
   {
      "Screen buffer value:", frame0.getI()
   });
...

This is the screen on the start of the program:

Since PROMPT-FOR does not copy the value of the variable i into the screen-buffer, the initial value shown on the screen is not the number 3.

Type a 9 into the field and then press F1. The screen will look like this:

From this example we can see the differences between this statement and implementation and the SET or UPDATE statements.

The statement has been implemented as GenericFrame.promptFor() method. The statement does not have the built in ASSIGN implementation. That is why there are different values in the variable and the screen-buffer.

To synchronize the content of the screen-buffer with the business logic, the ASSIGN statement is often used, as in this example:

define variable i as integer initial 3.
prompt-for i.
assign i.
message "Business logic value:" i.
message "Screen buffer value:" input i.

This will be implemented in Java as:

integer i = new integer(3);
...
   FrameElement[] elementList0 = new FrameElement[]
   {
      new Element(i, frame0.widgeti())
   };

   frame0.promptFor(elementList0);

   frame0.assignScreenValue(new Element(i, frame0.widgeti()));
   message(new Object[]
   {
      "Business logic value:", i
   });
   message(new Object[]
   {
      "Screen buffer value:", frame0.getI()
   });
...

Type a 10 into the field and then press F1. The screen will look like this:

As you can see the both values are the same after ASSIGN statement.

SET Example

This simple example of SET usage uses a single variable to show the conversion details. The 4GL code:

define variable i as integer initial 3.
set i.
message "Business logic value:" i.
message "Screen buffer value:" input i.

This is implemented in Java as:

integer i = new integer(3);
...
   FrameElement[] elementList0 = new FrameElement[]
   {
      new Element(i, frame0.widgeti())
   };

   frame0.set(elementList0);

   message(new Object[]
   {
      "Business logic value:", i
   });
   message(new Object[]
   {
      "Screen buffer value:", frame0.getI()
   });
...

This is the screen on the start of the program:

Since SET does not copy the value of the variable i into the screen-buffer, the initial value shown on the screen is not the number 3.

Type a 7 into the entry field and then press F1. The screen will look like this:

The value in the screen-buffer matches the value in business logic in this case (as opposed to the PROMPT-FOR case) since there is a hidden ASSIGN.

UPDATE Example

This simple example of UPDATE usage uses a single variable to show the conversion details. The 4GL code:

define variable i as integer initial 3.
update i.
message "Business logic value:" i.
message "Screen buffer value:" input i.

This is implemented in Java as:

integer i = new integer(3);
...
   FrameElement[] elementList0 = new FrameElement[]
   {
      new Element(i, frame0.widgeti())
   };
   frame0.update(elementList0);
   message(new Object[]
   {
      "Business logic value:", i
   });
   message(new Object[]
   {
      "Screen buffer value:", frame0.getI()
   });
...

The initial screenshot will be:

Note the difference between the SET or PROMPT-FOR statement for the similar example. The UPDATE statement copies data from the business logic into the screen-buffer before it displays the widget on the screen.

Type the value 10 into the entry field and then press F1. The screen will look like this:

The value in the screen-buffer matches the value in business logic in this case (as opposed to the PROMPT-FOR case) since there is a hidden ASSIGN.

Widget Specifications

The core of each of the statements is a list of the widgets to process. This list of widgets is provided as an array of FrameElement instances. These are the widgets to be edited and more specifically, these specifications may establish linkages between variables and database fields in the business logic and the widget that is associated with that variable or field. For more details, please see the Displaying Data Using Frames chapter.

Use of variables as a widget specification is already shown in the examples above. The following sections show variations of widget specifications that are not otherwise documented elsewhere.

Essentially, all of these different use cases are handled by including different sub-classes of FrameElement in the array passed to the runtime. These different subclasses can be mixed and matched within the array just as different types of widget (variable, database-field, text group, embedded-assignment...) can be used in the 4GL.

Database Field Reference

Just as a widget can be defined from a variable, so too can a widget be defined from a database field. All 3 statements work the same way.

The 4GL code:

SET book.price.

This converts to Java as:

...
FrameElement[] elementList0 = new FrameElement[]
{
   new Element(new FieldReference(book, "price"), frame0.widgetPrice())
};

frame0.set(elementList0);
...

In the code above we can see FrameElement subclass Element is created using a FieldReference instance which presents the database field as if it was a variable. This “trick” is accomplished by encapsulating the record buffer instance and the name of the field, allowing a delegated get/set of the value of that field without having the user-interface runtime code have any awareness of the database.

To clearly present the difference in conversion between a variable reference and the use of a database field consider this:

define variable txt as character.
create book.
update txt book.price.

The converted code:

The only important difference is in the first parameter to the Element constructor.

TEXT Grouping

The TEXT option introduces a special text processing mode where a set of character widgets are considered as a single multi-line editor. This means the variables or database fields used with this option can only be of the character type. When new data is inserted into the middle of the existing characters the text is wraps the data into the following widget. Likewise, when data is deleted in the middle of a field the following data will be wrapped backwards into the empty area from the following widget.

If entering data exceeds the format defined for the fields the extra data will be discarded.

To illustrate consider 4GL code:

def var ich as character format "x(40)" extent 5.
PROMPT-FOR TEXT(ich COLUMN-LABEL "Editor Simulation" COLON 20 FORMAT "x(20)").

This will be implemented in Java as:

FrameElement[] elementList0 = new FrameElement[]
{
   new WordWrapElement(subscript(ich, 1), frame0.widgetIchArray0()),
   new WordWrapElement(subscript(ich, 2), frame0.widgetIchArray1()),
   new WordWrapElement(subscript(ich, 3), frame0.widgetIchArray2()),
   new WordWrapElement(subscript(ich, 4), frame0.widgetIchArray3()),
   new WordWrapElement(subscript(ich, 5), frame0.widgetIchArray4())
};

frame0.promptFor(elementList0);

The WordWrapElement is used in this case. The initial screen will be:

Type the text in the field and see how the word wrapping works. Use BACKSPACE or DELETE keys to verify text moving from one line to another.

The following table lists the keys available to use inside the TEXT option fields and their actions.

Key Action
APPEND-LINE Joins two adjacent lines to make a single long line.
BACK-TAB Shift the cursor (input focus) to the previous field in a TEXT group.
BREAK-LINE Splits one line in two separate lines at the place where the cursor is currently located.
BACKSPACE Deletes the character before the current cursor position and shifts the cursor to the left. If the cursor is at the beginning of a current line, the previous position is the last position of the previous line.
CLEAR Clears the current widget and all widgets in the TEXT group that are located after the cursor.
DELETE-LINE Clears the line which contents the current location of the cursor.
NEW-LINE Insert a new empty line at the position of the current cursor.
RECALL Causes an undo operation for the TEXT group returning the data for the group to its initial state.
RETURN Moves the cursor (input focus) to the next field in the TEXT group in overwrite mode. Breaks the line at the cursor position in insert mode, leaving the cursor at the beginning of the new line.
TAB Moves the cursor (input focus) to the next widget outside the ones from current TEXT group. If there is no other widgets, the cursor returns to the start of the current TEXT group.
Record Syntax

As a convenience, the 4GL allows the programmer to specify “all fields in a table except these” instead of explicitly listing all the fields. All 3 statements provide this support.

The 4GL code:

create book.
update book except price.

This will be implemented in Java as:

RecordBuffer.create(book);
FrameElement[] elementList0 = new FrameElement[]
{
   new Element(new FieldReference(book, "bookId"), frame0.widgetBookId()),
   new Element(new FieldReference(book, "bookTitle"), frame0.widgetBookTitle()),
   new Element(new FieldReference(book, "publisher"), frame0.widgetPublisher()),
   new Element(new FieldReference(book, "isbn"), frame0.widgetIsbn()),
   new Element(new FieldReference(book, "onHandQty"), frame0.widgetOnHandQty()),
   new Element(new FieldReference(book, "cost"), frame0.widgetCost()),
   new Element(new FieldReference(book, "pubDate"), frame0.widgetPubDate()),
   new Element(new FieldReference(book, "authorId"), frame0.widgetAuthorId()),
   new Element(new FieldReference(book, "soldQty"), frame0.widgetSoldQty())
};

frame0.update(elementList0);

All fields except price are included in the list of the elements passed to the GenericFrame.update() method.

The conversion process expands the record reference to be the full list of fields in the given table excluding any fields listed in the EXCEPT clause. The order of the fields is the same as in Progress: the order they are defined in the database schema. Once these are expanded, the rest of the conversion runs exactly like the fields were explicitly defined as part of that frame.

WHEN Clause

The WHEN clause allows the conditional inclusion/exclusion of a widget from the editing statement based on runtime evaluation of an expression. The following example is for PROMPT-FOR, but substituting SET or UPDATE would work exactly the same. There is no difference in support.

Sample 4GL code:

def var ich as character initial "The char value".
def var bVar as logical initial true.
PROMPT-FOR ich WHEN bVar.
bVar = false.
PROMPT-FOR ich WHEN bVar.

This will have the Java equivalent:

boolean whenCondition0 = bVar.booleanValue();

FrameElement[] elementList0 = new FrameElement[]
{
   whenCondition0 ? new Element(ich, frame0.widgetIch()) : null
};

frame0.promptFor(elementList0);

bVar.assign(false);
boolean whenCondition1 = bVar.booleanValue();

FrameElement[] elementList1 = new FrameElement[]
{
   whenCondition1 ? new Element(ich, frame0.widgetIch()) : null
};

frame0.promptFor(elementList1);

The Java result evaluates the expression and saves the result as a boolean local variable. Inside the frame array initialization, the ternary operator is used to test the boolean variable and if true, to pass the frame element and if false, then pass null. null elements are silently ignored during runtime processing of these statements.

GO-ON Option

Sometimes it is convenient to redefine the default GO key behavior. For this purpose the GO-ON option must be used. For example if we want to use F3 and F5 to produce the GO event, the 4GL code will be:

def var ich as character format "x(20)" initial "The constant value".
PROMPT-FOR ich go-on(f3 f5).

This is implemented in Java as:

FrameElement[] elementList0 = new FrameElement[]
{
   new Element(ich, frame0.widgetIch())
};
EventList list0 = new EventList();
list0.addGoOn(new String[]
{
   "f3", "f5" 
});

frame0.promptFor(elementList0, list0);

The new set of key events will be added to the existing default (the F1 key).

EDITING Block

The EDITING block defines one or more lines of 4GL code that are executed after every key that is entered by the user and before the widget does any default processing of the key. The code in an editing block can use READKEY to determine what key was entered and can execute APPLY LASTKEY to force the widget to execute its default processing. If APPLY LASTKEY is not executed, then the default processing of that key is bypassed.

Progress documents that editing blocks can be used for SET or UPDATE statements. Although it is undocumented, editing blocks also do work with PROMPT-FOR as well (both in Progress and in Java).

Use of editing blocks is to be avoided as it has poor performance compared with the alternative of triggers.

The syntax of the phrase in 4GL is:

[label:] EDITING: statement ... END

statement ... is the one or more statements will be executed for every keystroke entered. In most cases the first statement used in this block is READKEY.

The Java implementation of this phrase has been defined in com.goldencode.p2j.util.BlockManager class:

promptForEditing(CommonFrame frame, FrameElement[] widgets, String label, Block block)
promptForEditing(CommonFrame frame, FrameElement[] widgets, String[] enclosing,String label,
                 Block block)
promptForEditing(CommonFrame frame, FrameElement[] widgets, EventList goOn, String label,
                 Block block)
promptForEditing(CommonFrame frame, FrameElement[] widgets, EventList goOn, String[] enclosing,
                 String label, Block block)
setEditing(CommonFrame frame, FrameElement[] widgets, String label, Block block)
setEditing(CommonFrame frame, FrameElement[] widgets, String[] enclosing, String label, Block block)
setEditing(CommonFrame frame, FrameElement[] widgets, EventList goOn, String label, Block block)
setEditing(CommonFrame frame, FrameElement[] widgets, EventList goOn, String[] enclosing,
           String label, Block block)
updateEditing(CommonFrame frame, FrameElement[] widgets, String label, Block block)
updateEditing(CommonFrame frame, FrameElement[] widgets, String[] enclosing,String label,
              Block block)
updateEditing(CommonFrame frame, FrameElement[] widgets, EventList goOn, String label, Block block)
updateEditing(CommonFrame frame, FrameElement[] widgets, EventList goOn, String[] enclosing,
              String label, Block block)

frame - the frame for which the editing block is associated.

widgets - the widgets in the frame to be enabled for editing.

goOn - the exit conditions for this loop. See the GO-ON section above for details on how the EventList is created.

label - the label of the block for use in control flow statements such as LEAVE or NEXT.

block - the code for the editing block body.

enclosing - the list of enclosing blocks of which this class is unaware. Any LEAVE or NEXT that references one of those enclosing blocks must not unwind the stack further using an exception, but rather it must return and let the caller check to see if a deferred LEAVE or NEXT must be honored.

The basic idea is that these helper methods hide the complexity of the editing block execution and call the necessary processing in GenericFrame instance that is passed (it implements the CommonFrame interface).

The simplest example:

update ivar editing:
   readkey.
   apply lastkey.
end.

This will be implemented in Java as:

updateEditing(frame0, elementList0, "editingLabel0", new Block()
{
   public void body()
   {
      KeyReader.readKey();
      apply(lastKey());
   }
});

Please note that code which just uses READKEY and APPLY LASTKEY is the equivalent of the the normal widget processing that occurs in PROMPT-FOR , SET or UPDATE statements. Essentially, the example above is the same as this code:

update ivar.

The READKEY statement does not have to be the first statement in the editing block, but it must be somewhere inside the block because Progress does not automatically read keystrokes in this case.

Because the code in an editing block is processing while inside of the PROMPT-FOR, SET or UPDATE statements, any changes to widget data values cannot be accessed directly from the associated variables or database fields. Instead, the INPUT function or SCREEN-VALUE attribute must be used to read from the screen-buffer.

The NEXT statement in an editing block cancels any pending GO event and waits for the next key to be entered.

The LEAVE statement in an editing block exits the editing mode for the statement (which would execute the assignment part of the SET or UPDATE statement if not in a PROMPT-FOR).

In next example the code reads the key entered and then inserts that key and a preceding hyphen character into the text of the FILL-IN widget (using the APPLY statement).

The 4GL code:

def var ich as character format "x(30)" initial "The constant value".

prompt-for ich
editing:
   readkey.
   apply keycode("-").
   apply lastkey.
end.

This is implemented in Java as:

promptForEditing(frame0, elementList0, "editingLabel0", new Block()
{
   public void body()
   {
      KeyReader.readKey();
      apply(keyCode("-"));
      apply(lastKey());
   }
});

Type the word “hello”. In the resulting screen shot below the typed string is interleaved with the artificial characters inserted during the handling of every key pressed.

For more details on editing blocks, please see the Blocks chapter.

SENSITIVE Attribute

This attribute shows whether the widget is able to receive the input focus or not. Practically the ENABLE statement sets this logical attribute to TRUE while DISABLE statement - sets it to FALSE. The syntax is:

widget:sensitive = true|false.

Consider the following 4GL example:

enable aCh bCh with frame f1.
message "aCH:sensitive - "+aCh:sensitive.
message "bCH:sensitive - "+bCh:sensitive.
pause.
aCh:sensitive = false.
message "aCH:sensitive - "+aCh:sensitive.
message "bCH:sensitive - "+bCh:sensitive.
pause.
disable aCh bCh with frame f1.
message "aCH:sensitive - "+aCh:sensitive.
message "bCH:sensitive - "+bCh:sensitive.
pause.
aCh:sensitive = true.
message "aCH:sensitive - "+aCh:sensitive.
message "bCH:sensitive - "+bCh:sensitive.
pause.

Will be converted to Java as:

f1Frame.enable(elementList0);

message(concat(new character("aCH:sensitive - "), f1Frame.widgetACh().isSensitive()));
message(concat(new character("bCH:sensitive - "), f1Frame.widgetBCh().isSensitive()));
pause();
f1Frame.widgetACh().setSensitive(new logical(false));
message(concat(new character("aCH:sensitive - "), f1Frame.widgetACh().isSensitive()));
message(concat(new character("bCH:sensitive - "), f1Frame.widgetBCh().isSensitive()));
pause();
FrameElement[] elementList1 = new FrameElement[]
{
   new WidgetElement(f1Frame.widgetACh()),
   new WidgetElement(f1Frame.widgetBCh())
};
f1Frame.disable(elementList1);
message(concat(new character("aCH:sensitive - "), f1Frame.widgetACh().isSensitive()));
message(concat(new character("bCH:sensitive - "), f1Frame.widgetBCh().isSensitive()));
pause();

f1Frame.widgetACh().setSensitive(new logical(true));
message(concat(new character("aCH:sensitive - "), f1Frame.widgetACh().isSensitive()));
message(concat(new character("bCH:sensitive - "), f1Frame.widgetBCh().isSensitive()));

After start the screen is:

The SENSITIVE attribute is TRUE for both fields meaning the widgets are available for input. Press spacebar to continue:

The code manually turns the SENSITIVE attribute of aCH to FALSE. And the respective fields become unable to be focused or accept input. Press spacebar to continue. Now the code disables both fields. The screen becomes:

Both fields are disabled and SENSITIVE attributes are set to FALSE. Finally the code explicitly sets the SENSITIVE attribute to TRUE. Press spacebar to continue:

From the functional point of view, setting the SENSITIVE attribute to TRUE/*FALSE* is the equivalent of using ENABLE/*DISABLE* statements.

WAIT-FOR Statement

This statement drives the interactive user interface event processing loop. That loop is necessary to read keys and mouse events from the user and apply them to the widget with the focus. This event processing loop enables the core of interactive editing mode.

While in this loop, the execution of the business logic is paused. The event processing loop is exited when any one of a list of specific events occurs. That list is configurable as part of the statement.

During the event processing loop, events that match trigger definitions will dispatch those triggers for processing. Likewise, validation processing can also be dispatched during this loop. See the Events and Validation chapters respectively, for more details.

Only widgets that have been enabled for editing (through the ENABLE statement or the SENSITIVE attribute) can be targets of events. That means that non-enabled widgets cannot receive focus and their values cannot be modified by the user directly (although triggers could be written to modify non-enabled widgets).

Progress recommends against nesting WAIT-FOR statements and also recommends against nesting WAIT-FOR in PROMPT-FOR/ SET /*UPDATE* (or other statements that have a hidden WAIT-FOR inside). This nesting can be achieved using triggers or a@ HELP application. In either case, if the code invoked during a trigger or HELP application executes an implicit or explicit WAIT-FOR, then nesting will occur. The reason to avoid nesting is that the resulting behavior is complex, often non-intuitive and can easily lead to infinite loops or dead locks. Although Progress makes this recommendation, many applications nevertheless do implement nested WAIT-FOR statements. The Java implementation fully supports this.

Syntax

The syntax of the WAIT-FOR statement is:

WAIT-FOR event-list OF widget-list [ OR event-list OF widget-list ] ...
   [ FOCUS widget ]
   [ PAUSE n ]

The Java implementation has been defined in the LogicalTerminal class. There are several implementations for different cases:

waitFor(CommonFrame frame)
waitFor(CommonFrame frame, EventList el)
waitFor(CommonFrame frame, EventList el, GenericWidget focusedWidget)
waitFor(CommonFrame frame, EventList el, int seconds)
waitFor(CommonFrame frame, EventList el, NumberType seconds)
waitFor(CommonFrame frame, EventList el, GenericWidget focusedWidget, int seconds)
waitFor(CommonFrame frame, EventList el, GenericWidget focusedWidget, NumberType seconds)
waitFor(CommonFrame frame, EventList el, CommonFrame focusedFrame)
waitFor(CommonFrame frame, EventList el, CommonFrame focusedFrame, int seconds)
waitFor(CommonFrame frame, EventList el, int focusedWidgetId, int seconds)

All these calls are working with either directly specified list of the events or with the default one. Consider the possible options:

4GL syntax Description Supported
event-list A list of the space or comma-delimited events which cause the event processing loop to exit. Yes
widget-list The list widgets (space or comma-delimited) to which the events must be targeted for the event processing loop to exit. Yes
FOCUS widget The widget that initially receives the focus when the WAIT-FOR statement is executed. Yes
PAUSE n A timeout in seconds. If this elapses without any events the event processing loop will exit. Yes

Simple Example

The following is an example of very basic usage:

def var chx as character label "chx label".
enable chx with frame frame1.
display chx with frame frame1.
wait-for window-close,f2 of current-window.
message "left wait-for".

The code is implemented in Java as:

FrameElement[] elementList0 = new FrameElement[]
{
   new WidgetElement(frame1Frame.widgetChx())
};

frame1Frame.enable(elementList0);

FrameElement[] elementList1 = new FrameElement[]
{
   new Element(chx, frame1Frame.widgetChx())
};

frame1Frame.display(elementList1);

EventList list0 = new EventList();
list0.addEvent(new String[]
{
   "window-close", "f2" 
}, currentWindow());
waitFor(null, list0);
message("left wait-for");

The initial screen is:

To sucessfully exit the event processing loop (exit editing mode), the user must press F2 or initiate the WINDOW-CLOSE event. If the user executes END-ERROR, then the event processing loop is also exited, but abnormally.

FOCUS Option

Use this option to specify the widget which will get the focus when the event processing loop starts. In the example below there are several widgets to display. Normally the first widget in the ENABLE statement will have the focus (chx in this case). In this example, the initial focus will be on the Exit button.

The 4GL code:

def var chx as character label "Label".
def button one-btn label "One".
def button two-btn label "Two".
def button exit-btn label "Exit".
enable chx one-btn two-btn exit-btn with frame frame1.
display chx one-btn two-btn exit-btn with frame frame1.
wait-for window-close,f2 of current-window focus exit-btn.
message "left wait-for".

The Java implementation:

FrameElement[] elementList0 = new FrameElement[]
{
   new WidgetElement(frame1Frame.widgetChx()),
   new WidgetElement(frame1Frame.widgetOneBtn()),
   new WidgetElement(frame1Frame.widgetTwoBtn()),
   new WidgetElement(frame1Frame.widgetExitBtn())
};

frame1Frame.enable(elementList0);

FrameElement[] elementList1 = new FrameElement[]
{
   new Element(chx, frame1Frame.widgetChx()),
   new WidgetElement(frame1Frame.widgetOneBtn()),
   new WidgetElement(frame1Frame.widgetTwoBtn()),
   new WidgetElement(frame1Frame.widgetExitBtn())
};

frame1Frame.display(elementList1);

EventList list0 = new EventList();
list0.addEvent(new String[]
{
   "window-close", "f2" 
}, currentWindow());
waitFor(null, list0, frame1Frame.widgetExitBtn());
message("left wait-for");

The initial screen will be:

The reverse video color on the Exit button shows that the input focus is being honored as specified.

PAUSE Option

This specifies a timeout in seconds for the event processing loop. If there will be no user action and time is elapsed the event processing loop ends and the business logic resumes.

The 4GL code:

def var chx as character label "Label".
def button exit-btn label "Exit".
enable chx exit-btn with frame frame1.
display chx exit-btn with frame frame1.
wait-for window-close,f2 of current-window pause 10.
message "left wait-for".

This will be implemented in Java as:

FrameElement[] elementList0 = new FrameElement[]
{
   new WidgetElement(frame1Frame.widgetChx()),
   new WidgetElement(frame1Frame.widgetExitBtn())
};

frame1Frame.enable(elementList0);

FrameElement[] elementList1 = new FrameElement[]
{
   new Element(chx, frame1Frame.widgetChx()),
   new WidgetElement(frame1Frame.widgetExitBtn())
};

frame1Frame.display(elementList1);

EventList list0 = new EventList();
list0.addEvent(new String[]
{
   "window-close", "f2" 
}, currentWindow());
waitFor(null, list0, 10);
message("left wait-for");

The initial screen:

The initial focus is on the first available widget because in this example there is no use of the FOCUS option. After 10 seconds and no actions the screen becomes:

Complex Example

The next example use all options defined for WAIT-FOR statement. Initially the frame is displayed with one FILL-IN widget and three buttons. The input focus in set to button One. The user can leave the application by either pressing F2 inside the FILL-IN, by pressing the Exit button or by not pressing any keys for 20 seconds.

The 4GL code:

def var chx as character label "Label".
def button one-btn label "One".
def button two-btn label "Two".
def button exit-btn label "Exit".
enable chx one-btn two-btn exit-btn with frame frame1.
display chx one-btn two-btn exit-btn with frame frame1.
wait-for window-close,f2 of chx or choose of exit-btn focus one-btn pause 20.
message "left wait-for".

This is implemented in Java as:

FrameElement[] elementList0 = new FrameElement[]
{
   new WidgetElement(frame1Frame.widgetChx()),
   new WidgetElement(frame1Frame.widgetOneBtn()),
   new WidgetElement(frame1Frame.widgetTwoBtn()),
   new WidgetElement(frame1Frame.widgetExitBtn())
};

frame1Frame.enable(elementList0);

FrameElement[] elementList1 = new FrameElement[]
{
   new Element(chx, frame1Frame.widgetChx()),
   new WidgetElement(frame1Frame.widgetOneBtn()),
   new WidgetElement(frame1Frame.widgetTwoBtn()),
   new WidgetElement(frame1Frame.widgetExitBtn())
};

frame1Frame.display(elementList1);

EventList list0 = new EventList();
list0.addEvent("choose", frame1Frame.widgetExitBtn());
list0.addEvent(new String[]
{
   "window-close", "f2" 
}, frame1Frame.widgetChx());
waitFor(frame1Frame, list0, frame1Frame.widgetOneBtn(), 20);
message("left wait-for");

The initial screenshot is:

h3(#focus-management).
Focus Management

Focus is the concept that there is a specific widget which will be the target for user-generated events such as keystrokes. Focus only occurs during editing mode. When not in editing mode, keys are not dispatched to widgets. Instead in non-editing mode, if keys are pressed, they queue up in the keyword input buffer and must be read directly using READKEY. The rest of this discussion relates only to editing mode.

The widget that is in focus will be drawn differently (usually with an underlined “color” of the name “INPUT”). This provides feedback to the user to show which widget is focused. This focused widget can be either a control widget like a BUTTON or a data editing widget like a FILL-IN.

If there is at least one enabled widget (and sometimes even if there are no enabled widgets), some widget will receive the focus when the event processing loop (WAIT-FOR) starts. The default processing for certain events (usually TAB and BACK-TAB) will cause the focus to shift from the currently focused widget to the next widget (TAB) or previous widget (BACK-TAB) in the “tab order”. The tab order is the list of enabled widgets, which can receive focus. This list has a specific ordering, which determines how the user can navigate the frame during editing.

By default, the tab order will match the same order as the widgets listed in the ENABLE statement, with the first widget in the list being the widget that by default receives focus when the event processing loop starts. This can be explicitly modified using the NEXT-PROMPT statement or the FOCUS option of WAIT-FOR. Sadly, there are many complications and exceptions to how the tab order is defined and how default focus is assigned. To go into the full details is beyond the scope of this book. Please see FWD Internals for more details.

The tab order is usually processed as a kind of loop. If the currently focused widget is the last widget in the tab order and the user generates an event that causes focus to move forward (e.g. TAB), then the next focused widget will be the one at the beginning of the list. Likewise moving the focus backward (e.g. BACK-TAB) from the start of the list will cause the last widget in the list to be focused.

When focus changes, the currently focused widget usually (more complications that are out of scope here) will receive a LEAVE event. Triggers can be defined to process on that event. If the LEAVE trigger executes RETURN NO-APPLY, then generally, the focus change will be canceled. If the focus change is not canceled, then the widget which is going to be receiving focus will usually (more out of scope complications here) receive an ENTRY event. Triggers on ENTRY can likewise cancel the focus transfer using RETURN NO-APPLY (see below for a more specific description).

Focus is managed in scopes. A focus scope has the lifetime of a single WAIT-FOR event processing loop, either coded directly as an explicit WAIT-FOR statement, or through other high level user-interface statements (e.g. PROMPT-FOR, SET or UPDATE). Scopes get nested when a trigger or a HELP application is invoked that performs another WAIT-FOR operation.

Creation of a new focus scope preserves the existing scope's focus information. The new scope then establishes its own focus. This change is not accompanied with the usual LEAVE event, however. The rules for this initial focus setting are out of scope for this book.

While in the event processing loop, the focus is checked after the application of every event. The purpose of this check is to detect a situation where the focus needs to be adjusted forcibly. There is only one such situation: the current focus is either undefined or invalid before applying the event, and the processing of the event has made available at least one focusable widget. In this case the first focusable widget in the tab order for the active frame gets the focus without receiving the ENTRY event so no triggers are involved. However, if the available widgets belong to another frame or frames, the active frame is switched and the focus is set on the first widget in the tab order of the new active frame. The first displayed frame becomes the new active frame. This focus adjustment won't be active if the initial focus on entry to WAIT-FOR was rejected by triggers for all enabled widgets.

When WAIT-FOR exits the loop, it destroys the focus scope and restores the focus for the previous scope. However, no ENTRY event is applied to the focused widget. In some cases, the resulting focus differs from the original. The rules for restoring the focus are out of scope for this book.

When trying to adjust focus (with or without ENTRY) to a widget, the widget must be ENABLED and both parent frame and widget must be VISIBLE.

If an ENTRY trigger caused by a navigation key executes RETURN NO-APPLY, then focus must be searched (sending ENTRY) within widgets in the tab-order after the current focus, without wrapping back to the beginning of the list. If no widget accepts ENTRY, then focus remains on the original widget.

Unlike in most event-driven operating environments, Progress (and the converted Java application) allows a disabled widget to be in focus. As the result, the current focus can be in one of the following states: valid (an enabled widget is in focus); invalid (a disabled widget is in focus); or undefined (no widget is in focus at all).

For more complete details on focus management, please see the FWD Internals book.


© 2004-2017 Golden Code Development Corporation. ALL RIGHTS RESERVED.