Project

General

Profile

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.