Events¶
- Events
- Overview of Event Processing
- Trigger Conversion
- ON Statement Syntax
- Example Conversion
- APPLY Statement Syntax
- Trigger Scoping
- Trigger Nesting
- PROCESS EVENTS Event Processing
- WAIT-FOR Event Processing
- WAIT-FOR Event List Conversion
- PROMPT-FOR Event Processing
- PROMPT-FOR Event List Conversion
- CHOOSE Event Processing
- CHOOSE Event List Conversion
- INSERT Statement Event Processing And Conversion
- SET and UPDATE Statements Event Processing And Conversion
- Remapping Key Functions with the ON Statement
- Supported Events
Progress 4GL provides editing statements which allow programs to solicit user input in a standard manner. In cases where it is necessary to change or customize standard UI behavior, the 4GL provides an event processing model and features such as triggers which can be used to hook into that event processing to meet custom requirements which the editing statements cannot provide. For more details on the standard statements, please see the Editing chapter.
FWD provides support for conversion and execution of event processing and triggers. This chapter describes the conversion of these event processing features.
Overview of Event Processing¶
Inside the core of every editing statement in 4GL (PROMPT-FOR
, UPDATE
, SET
, WAIT-FOR
, etc.) is an event processing loop which processes events generated by the user (when the user interacts with application by pressing keys or clicking the mouse), by widgets (for example, ENTRY
and LEAVE
events which are generated when focus changes) and events generated by the application (with the APPLY
statement). The event processing loop is most explicitly exposed to the 4GL programmer via the WAIT-FOR
statement. But all editing statements embed a WAIT-FOR
somewhere inside their processing.
Each event is defined by 2 pieces of information: a name and a target.
The event name answers the question: what event has occurred? Event names include key labels like F1
, key functions like GO
, low level mouse events like LEFT-MOUSE-CLICK
, direct manipulation events (drag-and-drop which are manufactured mouse events) like SELECTION
, manufactured user-interface events like CHOOSE
(these are called “high level widget events” in Progress), manufactured non-user-interface events (created by sockets, ProDataSets or other 4GL language processing) and user-defined events (U1
through U10
and CLOSE
).
Every event has a target to which the event will be routed. This means that an event, regardless of its source, is sent to a specific entity (in case of user-interface events this is a widget). The trigger definition contains list of event names and event targets (explicit or implicit) which actually form full event “fingerprint” which should match exactly to make trigger recognize event.
A trigger is a user-defined block of code that is registered with the user-interface at runtime at a specific point in the code. The point of definition determines the scope within which the trigger is active. Along with the code block (which can contain arbitrary logic), the trigger must include the event definition (as described above) or a list of event definitions. These event definitions identify the exact events which cause the trigger's code block to be executed when the described events occur within the scope that the trigger is active.
The runtime code essentially “filters” events during the event-processing loop, looking for ones matching those specified in any trigger declaration (which is currently registered or in scope). When a match is found, the trigger is executed. When the trigger returns, it can allow or disallow further “regular” (default) processing of the event (see more details in ON Statement Syntax section below).
During execution of the trigger body other editing statements can be used and they, in turn, can have their own set of triggers. This means that triggers can (effectively) be nested (using nested WAIT-FOR
loops). Such processing is fully supported, though the behavior can be hard to predict or understand when looking at the code. But that behavior is in-fact deterministic, and FWD faithfully duplicates this behavior.
Trigger Conversion¶
A general purpose trigger is defined using an ON
statement in the form ON event-definition trigger-body
.
Each trigger is converted into two parts in the business logic java class. The first part is responsible for the trigger registration. The second part is the actual trigger code block. The small example provided below provides a general look of the Progress 4GL trigger code before and after conversion. Details of the conversion are provided in the following sections.
Progress 4GL source:
def var i as int. display i. enable i. on return, r, R of i do: message "return i". end. wait-for "5" of current-window.
Business logic class code after conversion (excerpt showing only the ON
statement conversion):
public class Trig1 ... public void body() { ... EventList list0 = new EventList(); list0.addEvent(new String[] { "return", "r", "R" }, frame0.widgeti()); registerTrigger(list0, TriggerBlock0.class, Trig1.this); ... } ... public class TriggerBlock0 extends Trigger { public void body() { message("return i"); } } ...
ON Statement Syntax¶
Trigger is declared using ON
statement. There are four forms of ON
statement:
Form | Description | Supported | Notes |
---|---|---|---|
ON database-event-definition trigger-body |
Defines and registers a database trigger that is scoped to the current procedure. | No | Scoped (in Progress terms “session”) database triggers can process on CREATE (table), WRITE (table), DELETE (table), FIND (table) and ASSIGN (field). Some of these have experimental support in the FWD runtime, but no conversion support exists for any database triggers at this time. These scoped triggers are different from the TRIGGER-PROCEDURE statement which defines a “schema” level trigger. |
ON “WEB-NOTIFY” ANYWHERE trigger-body |
Defines and registers a WebSpeed trigger that processes the WEB-NOTIFY event. | No | |
ON event-definition trigger-body |
Defines and registers a general purpose trigger that is scoped to the current procedure. | Yes | |
ON key-name key-function |
Remaps key processing to generate the specified key function instead of the current mapping (which if not already modified is the default mapping). | Yes | See Defining Key Functions with the ON Statement below. |
The remainder of this section provides details of the general purpose trigger conversion (form 3 above).
The event-definition
has following syntax:
event-declaration [OR event-declaration ...]
Where event-declaration
has following syntax:
event-name[, event-name …] { ANYWHERE | [OF widget-ref [, widget-ref ...]] }
The event-name
is an unquoted string literal which represents the event. There are two kinds of literals. The first one represents single letters as they are entered by the user. The case of the letter is significant for these events, for example a
and A
represent different events. The second kind of literals represent predefined names of events or special keys (for example, F10
or ENTRY
). The case in which these literals is entered is irrelevant, so entry and ENTRY
represent the same event name. The full list of recognized event names is provided in the section Supported Events below.
The ANYWHERE
keyword denotes event which may be targeted to any enabled widget in the frame with active input focus.
The widget-ref
defines widget which is a target of specified event. Full syntax of widget-ref
is following:
widget-name [IN FRAME frame]
The widget-name
represents name of the widget. Optional IN FRAME
clause explicitly refers frame where widget is defined.
Lists of event names and widget names are multiplicative. A trigger will match any listed event when the event target is any of the listed widgets. For example, following statement:
on a, b, c, d of s, t, u, v
This defines four events and four widgets. The event a
targeted to widgets s
, t
, u
or v
will match the event specification and the trigger will be executed. The same will happen with events b
, c
and d
. Overall this specification will match 16 different pairs of event-name
+ event-target
.
The trigger-body
can be one of the following forms:
Form | Description | Supported | Notes |
---|---|---|---|
trigger-block |
This defines a 4GL statement or a block of 4GL code which is registered as the code to be executed when the event-definition is matched. | Yes | The trigger is registered in the current scope and all more deeply nested scopes. |
REVERT |
Removes the registration and activates any previously registered trigger (that was hidden in this scope by the more recent registration) or restores the default processing if there were no triggers defined for the specified events. | Yes | Only affects the current procedure. All triggers defined in calling procedures (enclosing scopes) remain registered. This does not affect persistent triggers (see next item). |
PERSISTENT RUN procedure-name [(parameters)] |
Defines and registers a procedure file as a persistent procedure (scoped globally instead of scoped to the current block). This means the trigger remains registered until the end of the session. | No | Only available for user-interface events. The optional parameters are evaluated at registration time and then are passed to the procedure as input parameters. No output parameters are allowed. |
In first form trigger-body
is a single Progress 4GL statement or a block of statements bracketed in regular DO/END
block braces. The trigger body may end naturally or include one or more RETURN
and/or RETURN NO-APPLY
statements. The RETURN
statement continues processing of the event and passes it to default handler (usually it just passes the event to target widget which performs necessary actions). The RETURN NO-APPLY
statement stops event processing and finishes execution of trigger-body without further actions in regard to event for which trigger was invoked.
Example Conversion¶
As mentioned above, a trigger is converted in two main parts: the registration and the trigger body. The event definition is part of the trigger registration code and is converted as a declaration of an instance of EventList
class followed by call to the addEvent()
instance method. The addEvent()
method has several implementations with different signatures and depending on the event declaration different methods can be used by conversion. In this example, the simplest case of a single event and single widget is used. This results in the usage of the simplest version of addEvent()
.
Progress 4GL source code:
def var txt as char. enable txt with frame f0. on 'a' of txt do: return no-apply. end. wait-for 'b' of txt.
Converted business logic class (excerpt):
... public void body() { ... EventList list0 = new EventList(); list0.addEvent("a", f0Frame.widgetTxt()); registerTrigger(list0, TriggerBlock0.class, Trigger4a.this); ... } ... public class TriggerBlock0 extends Trigger { public void body() { returnConsume(); } } ...
The trigger-block
portion is emitted as a named inner class which is a sub-class of com.goldencode.p2j.util.Trigger
. By implementing this as an inner class, the code block has access to the state of the containing business logic class. The registration code is passed the class name and the containing business logic class instance (TriggerBlock0.class
and Trigger4a.this
in the example above) as parameters, along with the EventList
instance. When the event-definition has been matched, a new instance of this trigger will be instantiated and then invoked (necessary to duplicate the 4GL behavior).
In more complex event-definition cases (when trigger definition contains more events and/or more widgets), other versions of addEvent()
are used, as in the following.
Progress 4GL source code:
def var txt1 as char. def var txt2 as char. enable txt1 txt2 with frame f0. on 'a', 'A', b, B of txt1 do: return no-apply. end. on c of txt1, txt2 do: return no-apply. end. on d, 'D', 'e', E of txt1, txt2 do: return no-apply. end. wait-for 'b' of txt1.
Converted business logic class (excerpt):
... public void body() { ... EventList list0 = new EventList(); list0.addEvent(new String[] { "a", "A", "b", "B" }, f0Frame.widgetTxt1()); registerTrigger(list0, TriggerBlock0.class, Trigger4b.this); // many events, single widget EventList list1 = new EventList(); list1.addEvent("c", new GenericWidget[] { f0Frame.widgetTxt1(), f0Frame.widgetTxt2() }); registerTrigger(list1, TriggerBlock1.class, Trigger4b.this); // many widgets, single event EventList list2 = new EventList(); list2.addEvent(new String[] { "d", "D", "e", "E" }, new GenericWidget[] { f0Frame.widgetTxt1(), f0Frame.widgetTxt2() }); registerTrigger(list2, TriggerBlock2.class, Trigger4b.this); // many widgets and events ... } ...
The Progress 4GL strips any quotes from the event name, so quoted and unquoted names can be safely used in any mixture. After conversion the event name obviously is always a quoted String
literal (according to Java language specification).
APPLY Statement Syntax¶
The APPLY
statement creates the specified event and dispatches it to the (optionally) specified target. Here is te syntax of this statement:
APPLY event-specification [event-target]
The event-specification has following syntax:
event-expression | LASTKEY
Where event-expression is an expression which produces either predefined event name or key code value. The LASTKEY
is a name of Progress 4GL built-in function which returns the last key entered by the user or -1 if a key can't be determined (for example, last interactive statement was PAUSE
which ended by timeout). A more detailed description of LASTKEY can be found in the Terminal Management chapter.
Trigger Scoping¶
Non-persistent triggers are scoped to the top-level block (external procedure, internal procedure, user-defined function or another trigger) in which they are defined and they remain active until that top-level block ends or the trigger is explicitly reverted (by using the ON … REVERT
statement). Nevertheless, nested blocks may define triggers which will intercept (override within the same scope) some or all events declared for the triggers in outer blocks.
There is no difference in scoping behaviors between any of the top-level blocks. The following example illustrates trigger scoping and redefinition of trigger in nested blocks.
Progress 4GL source code:
def var txt as char. enable txt with frame f0. on 'a' of txt do: apply 'd' to txt. return no-apply. end. wait-for 'b' of txt. run procedure1. wait-for 'b' of txt. procedure procedure1. on 'a' of txt do: apply 'e' to txt. return no-apply. end. wait-for 'b' of txt. return.
Converted business logic class (excerpt):
... public void body() { f0Frame.openScope(); FrameElement[] elementList0 = new FrameElement[] { new WidgetElement(f0Frame.widgetTxt()) }; f0Frame.enable(elementList0); EventList list0 = new EventList(); list0.addEvent("a", f0Frame.widgetTxt()); registerTrigger(list0, TriggerBlock0.class, TriggerScoping01.this); // first trigger registered EventList list1 = new EventList(); list1.addEvent("b", f0Frame.widgetTxt()); waitFor(f0Frame, list1); // first trigger active procedure1(); EventList list2 = new EventList(); list2.addEvent("b", f0Frame.widgetTxt()); waitFor(f0Frame, list2); // first trigger active again } ... public void procedure1() { internalProcedure(new Block() { public void body() { EventList list3 = new EventList(); list3.addEvent("a", f0Frame.widgetTxt()); registerTrigger(list3, TriggerBlock1.class, TriggerScoping01.this); // second trigger EventList list4 = new EventList(); list4.addEvent("b", f0Frame.widgetTxt()); waitFor(f0Frame, list4); // second trigger active returnNormal(); // second trigger is deregistered } }); } public class TriggerBlock0 extends Trigger { public void body() { f0Frame.widgetTxt().apply("d"); returnConsume(); } } ...
Execution of converted application produces following screen shots:
Right after start:
┌────────┐ │txt │ │────────│ │ │ └────────┘
After pressing 'a':
┌────────┐ │txt │ │────────│ │d │ └────────┘
After pressing 'b' (in order to leave active WAIT-FOR
loop) and then 'a':
┌────────┐ │txt │ │────────│ │e │ └────────┘
After pressing 'b' (in order to leave active WAIT-FOR
loop) and then 'a' again:
┌────────┐ │txt │ │────────│ │d │ └────────┘
The first time the main trigger is invoked, then the nested trigger is invoked and finally the main trigger is invoked again. In other words, the end of the internal procedure block cleans up the registered nested trigger and restored it to the state in which the triggers were before the internal procedure call.
Triggers can be defined for the same block more than once and in this case the last definition is active. However, previous definitions are not completely forgotten by the runtime. Reverting the current trigger definition activates the last one and so on. Inside the same block a trigger behaves as if it was redefined inside inner block.
The following example illustrates this behavior.
Progress 4GL source code:
def var txt as char. enable txt with frame f0. on 'a' of txt do: apply 'd' to txt. return no-apply. end. wait-for 'b' of txt. on 'a' of txt do: apply 'e' to txt. return no-apply. end. wait-for 'b' of txt. on 'a' of txt revert. wait-for 'b' of txt. on 'a' of txt revert. wait-for 'b' of txt.
Converted business logic class (excerpt):
... public void body() { … // default handler is active EventList list0 = new EventList(); list0.addEvent("a", f0Frame.widgetTxt()); registerTrigger(list0, TriggerBlock0.class, TriggerRecursion6.this); // first trigger EventList list1 = new EventList(); list1.addEvent("b", f0Frame.widgetTxt()); waitFor(f0Frame, list1); // first trigger active EventList list2 = new EventList(); list2.addEvent("a", f0Frame.widgetTxt()); registerTrigger(list2, TriggerBlock1.class, TriggerRecursion6.this); // second trigger EventList list3 = new EventList(); list3.addEvent("b", f0Frame.widgetTxt()); waitFor(f0Frame, list3); // second trigger active EventList list4 = new EventList(); list4.addEvent("a", f0Frame.widgetTxt()); deregisterTrigger(list4); // revert second trigger EventList list5 = new EventList(); list5.addEvent("b", f0Frame.widgetTxt()); waitFor(f0Frame, list5); // first trigger active again EventList list6 = new EventList(); list6.addEvent("a", f0Frame.widgetTxt()); deregisterTrigger(list6); // revert first trigger EventList list7 = new EventList(); list7.addEvent("b", f0Frame.widgetTxt()); waitFor(f0Frame, list7); // default handler active again } ... public class TriggerBlock0 extends Trigger { public void body() { f0Frame.widgetTxt().apply("d"); returnConsume(); } } public class TriggerBlock1 extends Trigger { public void body() { f0Frame.widgetTxt().apply("e"); returnConsume(); } } ...
Execution of converted application produces the following screen shots:
Right after start:
┌────────┐ │txt │ │────────│ │ │ └────────┘
After pressing 'a':
┌────────┐ │txt │ │────────│ │d │ ← first trigger is active and replaces 'a' with 'd' └────────┘
After pressing 'b' (to leave active WAIT-FOR
) followed by 'a':
┌────────┐ │txt │ │────────│ │e │ ← second trigger is active and replaces 'a' with 'e' └────────┘
After pressing 'b' (to leave active WAIT-FOR
) followed by 'a':
┌────────┐ │txt │ │────────│ │d │ ← note that first trigger is active again └────────┘
Finally after pressing 'b' and 'a':
┌────────┐ │txt │ │────────│ │a │ ← default handler is active └────────┘
All triggers with the same event specification are registered in a stack. Reverting the active trigger re-activates the previous one. This happens until the last trigger in the stack is reverted and the default handler is activated.
Trigger Nesting¶
There are two kinds of trigger nesting: trigger body may contain the definition of other triggers and a trigger may produce events (by using APPLY
statement or by executing some editing statements) which, in turn invoke other triggers. These cases are handled at runtime as if the procedure was invoked from another procedure. The exception is that the currently active trigger (i.e. trigger which intercepted the event and executes its trigger block) is excluded from recursively invoking itself (directly or indirectly). Following example illustrates this behavior:
Progress 4GL source code:
def var txt as char. enable txt with frame f0. on 'a' of txt do: apply 'c' to txt. end. on 'c' of txt do: apply 'a' to txt. end. wait-for 'b' of txt.
Execution of converted application when user presses key 'a' produces following screen shots:
Right after start:
┌────────┐ │txt │ │────────│ │ │ └────────┘
After pressing 'a':
┌────────┐ │txt │ │────────│ │aca │ └────────┘
Although the second screen looks as if trigger 'a' invoked twice, this is not so. Actually processing happens as follows: trigger 'a' sends event 'c' and and trigger 'c' is executed. Trigger 'c' sends event 'a' to first trigger, but this event skips executing trigger 'a' (because it is excluded from event processing) and passes it to the default handler. The default handler passes the event to widget txt
and the first 'a' appears on the screen. Then trigger 'c' finishes processing and since it uses the default return (i.e. event is passed for further processing), the event 'c' also processed by the default handler and 'c' appears on the screen. Finally, when trigger 'a' exits, it passes control to default handler and the second 'a' appears on the screen. If appropriate triggers would not be excluded from the event processing on activation, the program provided above would cause infinite trigger recursion.
PROCESS EVENTS Event Processing¶
The PROCESS EVENTS statement , unlike other statements which perform event processing, does not wait for user input. It just processes all events which are already waiting in the internal event queue and immediately exits. Since there is no need to specify any events, the syntax of the statement is very simple:
PROCESS EVENTS
Also, since no events can be specified by user, no EventList
instances are generated during conversion:
4GL code:
process events.
Converted code:
... public void body() { processEvents(); } ...
Note that this statement provides chance to active triggers to run.
WAIT-FOR Event Processing¶
The WAIT-FOR
statement is most basic event processing loop available to 4GL users. In fact most other statements which perform event processing (PROMPT-FOR
, SET
and UPDATE
) can be expressed as a number of lower level statements and WAIT-FOR
among them. Other similar statements (CHOOSE
) can't be redefined as set of simple statements, but event processing performed by these statements behaves as if WAIT-FOR
is a part of them.
Basically WAIT-FOR
is a generic event processing loop which processes events until it meets some event listed among those which it should wait. Once such an event occurs, event processing stops and the WAIT-FOR
statement ends. While WAIT-FOR
handles events (during the core event processing loop) it compares each event + target widget to the list of in-scope trigger definitions. When a trigger definition matches an event, the trigger is dispatched (executed).
Since the single purpose of the WAIT-FOR
is to process events, its syntax is pretty simple:
WAIT-FOR wf-event-def [FOCUS widget] [PAUSE n]
The wf-event-def
has following syntax:
wf-event-declaration [OR wf-event-def]
The wf-event-declaration
has following syntax:
wf-event-list OF wf-widget-list
The wf-event-list
is the list of event names separated by space or comma and wf-widget-list
is the list of widgets also separated by space or comma. The event definition syntax, although very similar, differs from the ON
statement event definition.
The FOCUS widget
clause defines widget which will receive input focus before WAIT-FOR
start processing events. The PAUSE n
clause defines time during which events will be processed in event loop. If this clause is specified then event processing stops once specified time elapses between events generated by user (or event specified in WAIT-FOR
event definition is observed). More details regarding the syntax and conversion of WAIT-FOR
can be found in the Editing chapter.
WAIT-FOR Event List Conversion¶
During conversion of WAITF-FOR
statement its event declaration part is converted into the appropriate version of the EventList
constructor calls and the resulting event list is passed as a parameter to the LogicalTerminal.waitFor()
method. There are many versions of LogicalTerminal.waitFor()
and the particular one is chosen from the event declaration and other options passed to WAIT-FOR
. Consider following 4GL example:
def var txt as char label "Some Text" format "x(20)". form txt with frame f-frame. enable all with frame f-frame. wait-for window-close of current-window focus txt pause 60.
It is converted into following code:
public static final String FMT_STR_1 = "x(20)"; WaitForWidgetListClosureFFrame fFrameFrame = GenericFrame.createFrame(WaitForWidgetListClosureFFrame.class, "f-frame"); /** * External procedure (converted to Java from the 4GL source code * in wait_for_widget_list_closure.p). */ public void execute() { externalProcedure(new Block() { character txt = new character(""); public void init() { TransactionManager.registerUndo(txt); } public void body() { fFrameFrame.openScope(); fFrameFrame.enable(); EventList list0 = new EventList(); list0.addEvent("window-close", currentWindow()); // only one event waitFor(null, list0, fFrameFrame.widgetTxt(), 60); } }); }
Note the waitFor()
call parameters: the first parameter denotes the frame for which events should be processed. In this case it is set to null
and this means that the currently focused frame is the target. The second parameter contains the list of events for which to wait. The third parameter refers to the widget which should receive the focus when event loop processing is stated. Finally the last parameter defines the timeout in seconds between events. If the last parameter is specified, then WAIT-FOR
waits at maximum for the specified amount of time for a matching event. If no input events are generated during the specified interval of time, the WAIT-FOR
exits anyway. If a new event is generated before the timeout, then the event is processed and timeout is reset to initial value. If the timeout is not specified, then the interval is undefined and WAIT-FOR
waits for events indefinitely.
PROMPT-FOR Event Processing¶
The PROMPT-FOR
statement is an convenient wrapper for ENABLE → WAIT-FOR → DISABLE
sequence. In other words, PROMPT-FOR
contains a WAIT-FOR
internally and therefore inherits its behavior. In case of PROMPT-FOR
the list of the events which is actually passed by the internal WAIT-FOR
call is predefined (it contains only GO
event) although can be extended with GO-ON
clause (see full PROMPT-FOR
syntax description in PROMPT-FOR Statement section of Editing chapter). Predefined events and events defined in GO-ON
clause are used during event processing stage - once any of the defined events is observed, the WAIT-FOR
part of the PROMPT-FOR
statement is stopped and the next stage is executed.
PROMPT-FOR Event List Conversion¶
Since event target can be is already encoded in the PROMPT-FOR
statement parameters (the widgets listed for PROMPT-FOR
input processing are also the event targets), during conversion only events specified by the user in the GO-ON
clause are emitted in the EventList
instance. The predefined event and events listed in GO-ON
clause are combined with the list of enabled widgets so finally PROMPT-FOR
statement uses all listed events for all listed widgets. In other words, PROMPT-FOR
lacks the event definition flexibility of WAIT-FOR
. The following example illustrates conversion of the event list for the PROMPT-FOR
statement when the PROMPT-FOR
contains a GO-ON
clause:
4GL code:
def var i as int. def var j as char. form i label "Record #" skip j label "Text" with frame fr. prompt-for i j go-on(F5, F6) with frame fr.
Converted code (excerpt):
... frFrame.openScope(); FrameElement[] elementList0 = new FrameElement[] { new Element(i, frFrame.widgeti()), new Element(j, frFrame.widgetj()) }; EventList list0 = new EventList(); list0.addGoOn(new String[] { "F5", "F6" }); frFrame.promptFor(elementList0, list0); ...
The EventList
instance is filled with events differently than the usage in trigger definitions, the dedicated method EventList.addGoOn()
is invoked. The list of widgets for which events will be watched is taken from the FrameElement
array passed as the first parameter. In this case PROMPT-FOR
is converted as a call to CommonFrame.promptFor()
method instead of a call to static LogicalTerminal.promptFor()
. This happens because the PROMPT-FOR
statement has an explicitly specified WITH FRAME
clause.
In cases when PROMPT-FOR
contains no GO-ON
clause, no EventList
instance creation code is generated during conversion and version of CommonFrame.promptFor()
method with a different signature is used:
4GL code:
def var i as int. def var j as char. form i label "Record #" skip j label "Text" with frame fr. prompt-for i j with frame fr.
Converted code (excerpt):
... integer i = new integer(0); character j = new character(""); public void init() { TransactionManager.register(i, j); } public void body() { frFrame.openScope(); FrameElement[] elementList0 = new FrameElement[] { new Element(i, frFrame.widgeti()), new Element(j, frFrame.widgetj()) }; frFrame.promptFor(elementList0); // no EventList instances passed } ...
In this case, no EventList
instances are created or passed to the promptFor()
method.
CHOOSE Event Processing¶
Event processing for the CHOOSE
statement is very similar to one performed for PROMPT-FOR
statement. Just like PROMPT-FOR
it handles by default only GO
event and like PROMPT-FOR
this set can be extended by listing events in GO-ON
clause.
CHOOSE Event List Conversion¶
The conversion of CHOOSE
event list differs from one performed for PROMPT-FOR
. Although like PROMPT-FOR
, the list of event targets (widgets) is explicitly specified in the statement itself and only events specified by the user in the GO-ON
clause can extend the list of default events, the EventList
instances are not created. Instead an instance of the Choose
class is created and Choose.addGoOn()
method is used to add events specified by user in GO-ON
clause. The following examples illustrate conversion of the CHOOSE
statement with and without the GO-ON
clause:
4GL code:
define variable my-list as integer extent 3 init [1, 2, 3] no-undo. display my-list with frame fr. choose field my-list go-on (ctrl-@ ctrl-t) with frame fr.
Converted code (excerpt):
... integer[] myList = new integer[] { new integer(1), new integer(2), new integer(3) }; public void body() { frFrame.openScope(); FrameElement[] elementList0 = new FrameElement[] { new Element(subscript(myList, 1), frFrame.widgetMyListArray0()), new Element(subscript(myList, 2), frFrame.widgetMyListArray1()), new Element(subscript(myList, 3), frFrame.widgetMyListArray2()) }; frFrame.display(elementList0); Choose choose0 = new Choose(); choose0.addField(frFrame.widgetMyListArray0()); // add widgets involved in CHOOSE processing choose0.addField(frFrame.widgetMyListArray1()); choose0.addField(frFrame.widgetMyListArray2()); choose0.addGoOn(new String[] { "ctrl-@", "ctrl-t" }); // add events specified by user frFrame.choose(choose0); } ...
In the above example, Choose.addGoOn()
method is used to add events specified by user. There would be no call to this method if the GO-ON
clause is not present in CHOOSE
statement:
4GL code:
define variable my-list as integer extent 3 init [1, 2, 3] no-undo. display my-list with frame fr. choose field my-list with frame fr.
Converted code (excerpt):
... integer[] myList = new integer[] { new integer(1), new integer(2), new integer(3) }; public void body() { frFrame.openScope(); FrameElement[] elementList0 = new FrameElement[] { new Element(subscript(myList, 1), frFrame.widgetMyListArray0()), new Element(subscript(myList, 2), frFrame.widgetMyListArray1()), new Element(subscript(myList, 3), frFrame.widgetMyListArray2()) }; frFrame.display(elementList0); Choose choose0 = new Choose(); choose0.addField(frFrame.widgetMyListArray0()); // add widgets involved in CHOOSE processing choose0.addField(frFrame.widgetMyListArray1()); choose0.addField(frFrame.widgetMyListArray2()); frFrame.choose(choose0); } ...
In the above example, there is no call to Choose.addGoOn()
.
INSERT Statement Event Processing And Conversion¶
Basically INSERT
statement is a convenient way to execute following sequence of statements:
CREATE
DISPLAY
PROMPT-FOR
ASSIGN
From the list above only PROMPT-FOR
is involved in event processing. The INSERT
statement syntax does not provide way to extend or modify default list of events for its built-in PROMPT-FOR
, so during conversion no EventList
instances are generated and event processing performed exactly as for plain PROMPT-FOR
statement without GO-ON
clause.
SET and UPDATE Statements Event Processing And Conversion¶
These statements are very similar in syntax and identical from the point of view of event processing and event list conversion so it is convenient to describe them together.
Basically UPDATE
statement consist of following built-in statements executed sequentially:
DISPLAY
PROMPT-FOR
ASSIGN
The SET
statement contains only last two statements from the list provided above. Unlike the INSERT
statement, which also contains built-in PROMPT-FOR
, the SET
and UPDATE
statement syntax contains a GO-ON
clause which allows the user to extend the list of events statement waits for (by default it contains only GO
event). Event processing and the conversion of the event list is identical to the PROMPT-FOR
statement. The following example illustrates conversion of UPDATE
statement when user specified events are provided in GO-ON
clause:
4GL code:
def var txt1 as char format "x(10)". update txt1 go-on(F7,F8) with frame f0.
Converted code (excerpt):
... character txt1 = new character(""); public void init() { TransactionManager.registerUndo(txt1); } public void body() { f0Frame.openScope(); FrameElement[] elementList0 = new FrameElement[] { new Element(txt1, f0Frame.widgetTxt1()) }; EventList list0 = new EventList(); list0.addGoOn(new String[] { "F7", "F8" }); f0Frame.update(elementList0, list0); } ...
The events are converted in same way as for PROMPT-FOR
.
Remapping Key Functions with the ON Statement¶
As mentioned above in the section ON Statement Syntax one of the forms of this statement allows the programmer to map a specific key to the specified key function:
ON key-name key-function
The key-name
may represent the key itself in case of regular characters or it may have some of the predefined event names (see Supported Events table below). The key-function
is one of the predefined functions (they are also events). In other words, this statement maps events to some other events. The event which is marked as key-name
can be any recognized event (see the table below) but events marked as key-function
can only be from a limited set of events (the table below explicitly marks them as “_Key Function_” or “_Both_” in the “Type” column).
Supported Events¶
The table provided below contains list of all known events except printable ASCII characters and function keys. The ACSII printable characters can be used as key labels or event names generated in response on pressing the corresponding key (i.e. these keys represent themselves as events). Function keys from F0 to F99 and from PF0 and PF99 are recognized as key labels or event names. Events generated internally or events which can't be used as key functions are marked as “_Event_” in Type column. Note that some events can be both - key labels and key functions (they are labeled as “_Both_” in Type column). Column Support describes level of support for each event. The “_FULL_” in this column means complete support by both, conversion and runtime. The “PART” means that event is supported by conversion but runtime support is not implemented or either not provided or not implemented. Note that even if an event is not supported by the runtime it has appropriate key mapping (where applicable) so such an event can be generated during interaction with the user. The “_NONE_” means that event is not supported at all.
Event Name | Type | Support | Notes |
---|---|---|---|
ABORT | Key Function | YES | |
APPEND-LINE | Key Function | YES | Undocumented as key function, but supported by 4GL and FWD |
BACK-TAB | Both | YES | |
BACKSPACE | Both | YES | |
BELL | Key Function | YES | |
BOTTOM-COLUMN | Event | PART | |
BREAK-LINE | Event | YES | |
CANCEL-PICK | Event | PART | |
CHOICES | Event | PART | |
CHOOSE | Event | YES | |
CLEAR | Key Function | YES | |
CLOSE | Event | PART | |
COMPILE | Event | PART | Compilation at runtime is not supported |
COPY | Event | PART | |
CTRL-^ | Key Label | YES | |
CTRL-_ | Key Label | YES | |
CTRL-] | Key Label | YES | |
CTRL-@ | Key Label | YES | |
CTRL-\\ | Key Label | YES | |
CTRL-A | Key Label | YES | |
CTRL-B | Key Label | YES | |
CTRL-BREAK | Key Label | YES | |
CTRL-C | Key Label | YES | |
CTRL-D | Key Label | YES | |
CTRL-E | Key Label | YES | |
CTRL-F | Key Label | YES | |
CTRL-G | Key Label | YES | |
CTRL-J | Key Label | YES | |
CTRL-K | Key Label | YES | |
CTRL-L | Key Label | YES | |
CTRL-N | Key Label | YES | |
CTRL-O | Key Label | YES | |
CTRL-P | Key Label | YES | |
CTRL-Q | Key Label | YES | |
CTRL-R | Key Label | YES | |
CTRL-S | Key Label | YES | |
CTRL-T | Key Label | YES | |
CTRL-U | Key Label | YES | |
CTRL-V | Key Label | YES | |
CTRL-W | Key Label | YES | |
CTRL-X | Key Label | YES | |
CTRL-Y | Key Label | YES | |
CTRL-Z | Key Label | YES | |
CURSOR-DOWN | Both | YES | |
CURSOR-LEFT | Both | YES | |
CURSOR-RIGHT | Both | YES | |
CURSOR-UP | Both | YES | |
CUT | Event | PART | |
DEFAULT-ACTION | Event | YES | |
DEFAULT-POP-UP | Event | PART | |
DEL | Key Label | YES | |
DEL-CHAR | Key Label | YES | |
DEL-LINE | Key Label | YES | |
DELETE-CHARACTER | Key Function | YES | |
DELETE-COLUMN | Event | PART | |
DELETE-END-LINE | Event | YES | |
DELETE-FIELD | Event | PART | |
DELETE-LINE | Key Function | YES | Undocumented as key function, but supported by 4GL and FWD |
DELETE-WORD | Event | YES | |
EDITOR-BACKTAB | Event | YES | |
EDITOR-TAB | Key Function | YES | Undocumented as key function, but supported by 4GL and FWD |
END | Both | YES | |
END-ERROR | Key Function | YES | |
ENDKEY | Key Function | YES | |
ENTER-MENUBAR | Key Function | PART | GUI-specific event |
ENTRY | Event | YES | |
ERASE | Key Label | PART | |
ERROR | Event | YES | |
ESC | Key Label | YES | |
EXECUTE | Key Label | NONE | |
EXIT | Key Label | PART | |
F0-F99 | Key Label | YES | Function keys |
FIND | Key Label | YES | |
FIND-NEXT | Event | PART | |
FIND-PREVIOUS | Event | PART | |
GET | Key Function | YES | |
GO | Key Function | YES | |
GOTO | Event | YES | |
HELP | Key Function | YES | |
HELP-KEY | Key Label | YES | |
HOME | Both | YES | |
INS | Key Label | YES | |
INSERT-COLUMN | Event | PART | |
INSERT-FIELD | Event | PART | |
INSERT-FIELD-DATA | Event | PART | |
INSERT-FIELD-LABEL | Event | PART | |
INSERT-HERE | Key Label | NONE | |
INSERT-MODE | Key Function | YES | |
LEAVE | Event | YES | |
LEFT-END | Key Function | YES | |
LINE-ERASE | Key Label | YES | |
MAIN-MENU | Event | PART | |
MOUSE-EXTEND-CLICK | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-EXTEND-DBLCLICK | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-EXTEND-DOWN | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-EXTEND-DRAG | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-EXTEND-UP | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-MENU-CLICK | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-MENU-DBLCLICK | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-MENU-DOWN | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-MENU-DRAG | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-MENU-UP | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-MOVE | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-MOVE-CLICK | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-MOVE-DBLCLICK | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-MOVE-DOWN | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-MOVE-DRAG | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-MOVE-UP | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-SELECT-CLICK | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-SELECT-DBLCLICK | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-SELECT-DOWN | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-SELECT-DRAG | Key Label | PART | Mouse “keys” are not supported by runtime |
MOUSE-SELECT-UP | Key Label | PART | Mouse “keys” are not supported by runtime |
MOVE | Event | PART | |
NEW | Event | PART | |
NEW-LINE | Key Function | YES | Undocumented as key function, but supported by 4GL and FWD |
NEXT-ERROR | Event | PART | |
NEXT-FRAME | Key Function | YES | |
NEXT-WORD | Event | YES | |
OPEN-LINE-ABOVE | Event | YES | |
OPTIONS | Event | PART | |
PAGE | Key Label | NONE | |
PAGE-DOWN | Key Label | YES | |
PAGE-ERASE | Key Label | NONE | |
PAGE-LEFT | Event | YES | |
PAGE-RIGHT | Event | YES | |
PAGE-UP | Key Label | YES | |
PASTE | Event | PART | GUI-specific event |
PICK | Event | PART | |
PICK-AREA | Event | PART | |
PICK-BOTH | Event | PART | |
PREV-FRAME | Key Function | YES | |
PREV-WORD | Event | YES | |
PUT | Event | PART | |
RECALL | Key Function | YES | |
REMOVE | Key Label | NONE | |
REPLACE | Event | YES | |
REPORTS | Event | PART | |
RESUME-DISPLAY | Event | PART | |
RETURN | Both | YES | |
RIGHT-END | Key Function | YES | |
ROW-DISPLAY | Event | YES | |
SAVE-AS | Event | PART | |
SCROLL-LEFT | Event | PART | |
SCROLL-MODE | Key Function | PART | |
SCROLL-RIGHT | Event | PART | |
SELECT | Key Label | YES | |
SETTINGS | Event | NONE | |
START-RESIZE | Event | NONE | GUI-specific event |
STOP | Key Function | YES | |
STOP-DISPLAY | Event | PART | |
TAB | Both | YES | |
TOP-COLUMN | Event | YES | |
U1 | Key Label | NONE | |
U2 | Key Label | NONE | |
U3 | Key Label | NONE | |
U4 | Key Label | NONE | |
U5 | Key Label | NONE | |
U6 | Key Label | NONE | |
U7 | Key Label | NONE | |
U8 | Key Label | NONE | |
U9 | Key Label | NONE | |
U10 | Key Label | NONE | |
UNIX-END | Event | PART | Non-interactive event (i.e. not triggered by UI) |
VALUE-CHANGED | Event | YES | |
WINDOW-CLOSE | Event | YES | |
© 2004-2017 Golden Code Development Corporation. ALL RIGHTS RESERVED.