Unlike other properties which only have a single value, this property has a value corresponding to each of the different types of event that may be generated by a particular object. Consequently the syntax for setting the Event property differs from the general syntax that applies to other properties.
Two syntactic forms are allowed:
'Event' 'MouseUp' 'foo' 88 'Event' ('MouseUp' 'MouseDown') 'foo' 88 'Event' ('MouseUp' 'foo' 88)('MouseDown' 'goo')
Like any other property, the Event property can be set using assignment. However, certain special considerations apply which are discussed later.
When you specify the Event property using ⎕WC or ⎕WS, the action to be taken for an event type or types is specified by a 2 or 3-element vector containing:
Element | Item | Description | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
[1] | Type(s) | see below | ||||||||||||
[2] | Action | numeric scalar or character vector
|
||||||||||||
[3] | Arg | any array (optional) |
The first element, Type(s) may be one of the following:
The onEvent syntax causes all objects reported in the event message (see below) to be identified by a ref. Otherwise, objects reported in the event message are identified by name.
If Action is set to ¯1, the event is inhibited (if possible) by APL. If, for example, you set the action on a KeyPress event to ¯1, all keystrokes for the object in question will be ignored. Similarly, if you set the action on a Close event for a Form to ¯1, the user will be unable to close the Form. This is possible because APL intercepts most events before Windows itself takes any action. However, certain events (e.g. focus change events) are not notified to APL until after the event has occurred and after Windows has itself responded in some way. In these circumstances it is not always practical for APL to undo what Windows has already done, and an action code of ¯1 is treated as if it were 0. For further details, see the individual entries for each event type in this chapter.
If Action is set to 0 (the default), the event is processed by APL and Windows in the normal way (this is referred to herein as the default processing) but your program is not notified in any way that the event has occurred. For example, the default processing for a keystroke is to action it and either echo a character in the object or perform some other appropriate function.
If Action is set to 1, the event is first processed by APL (and Windows) in the normal way, then ⎕DQ terminates, returning an event message as its result. The format of the event message is given under the description of each event type.
If Action is set to a character vector that specifies the name of a function, this function (termed a callback) will be executed automatically by ⎕DQ every time the event occurs. The function may be a traditional defined function or a dfn.
A traditional defined function may be monadic, dyadic, or niladic. If dyadic, the left argument may be optional. A niladic callback may be appropriate if the function can perform its task without needing to interrogate the event message.
Unless the callback function is niladic, it will be supplied a right argument (⍵ for a dfn) containing the event message and a left argument (⍺ for a dfn) of the value of the array Arg (if specified).
The function may be defined to return no result, a result, or a shy result. The result determines how the event is handled.
The default processing of the event is deferred until after the callback has been run, and may be inhibited or modified by its result. If the callback function returns no result, or returns a scalar 1, normal processing of the event is allowed to continue as soon as the callback completes. If the callback returns a scalar 0, normal processing of the event is inhibited and the effect is identical to setting Action to ¯1. A callback function may also return an event message as its result. If so, ⎕DQ will action this event rather than the original one that fired the callback.
If a callback function does not exist at the instant it is invoked, ⎕DQ terminates with a VALUE ERROR. However, the name of the missing function is reported in the Status Window.
If Action is set to a character vector that specifies the name of a callback function, followed by the character &, the callback function will be executed asynchronously in a new thread when the event occurs.
For example, the event specification:
'Event' 'onSelect' 'DoIt&'
tells ⎕DQ to execute the callback function DoIt asynchronously as a thread when a Select event occurs on the object. Note that a callback function executed in this way should not return a result (because ⎕DQ does not wait for it) and any result will be displayed in the Session window.
If action code is set to a character vector whose first element is the execute symbol (⍎) the remaining string will be executed automatically whenever the event occurs. The default processing for the event is performed first and may not be changed or inhibited in any way.
Notice that when you specify the action to be taken on the occurrence of an event there is a great difference between 'FOO' and '⍎FOO'. The former causes APL to invoke the function FOO as a callback function. If the function takes an argument, APL will supply it with the event message. Secondly, the result (if any) of the function FOO will be used by APL and may cause the event to be disabled or changed in some way. In the second case, APL will perform the default processing for the event and then execute FOO without supplying an argument. If the function returns a result, it will be displayed in the Session.
If specified, Arg is an array whose value will be passed as the left argument to a callback function when that particular event (or events) is generated. Note that this is a constant defined when the value is assigned to the Event property.
If the callback function is defined to take an explicit left argument and Arg was not specified, the call will fail with the error message:
SYNTAX ERROR: The function requires a left argument
If the callback function is defined to take an optional left argument and Arg was not specified, a reference to the left argument (⍺ for a dfn) will generate VALUE ERROR.
When a callback function is invoked by ⎕DQ, the corresponding event message is supplied as its right argument. The event message is a vector whose first 2 elements identify the object that generated the event and the type of the event. Additional elements may be provided, depending upon the type of the event.
The same event message is returned as a (shy) result by ⎕DQ when it is terminated by an event whose Action is set to 1.
The first element of the event message always identifies the object that generated the event. Other elements may identify other objects associated with the event. For example, a DragDrop event reports both the object being dropped, and the object on which it is being dropped.
Objects are identified by names or refs. If the Event property was set using the onEvent syntax (whereby the event name or number is prefixed by the string 'on'), for example, 'onSelect' or 'on99', objects are identified by refs. This is also true if the object which generated the event has no name (i.e. was created by ⎕NEW). Otherwise, objects are identified by their names.
If, when the event type was specified it was identified by its name, the second element of the event message will be a character vector containing that name. If it was identified by its number, the second element of the event message will be an integer containing that number. If the event type was identified using the onEvent syntax, the second element of the event message will be a character vector containing the prefix 'on' followed by the event name, even if it had been specified by number. The exception is that if the event is a user-defined event, the second element of the event message will be a character vector containing the prefix 'on' followed by the character representation of the user-defined event number.
There are two ways to specify the Event property using assignment; you can specify the entire set of events, or you can set events one by one (see below).
To specify the entire set of events, you assign an array to the Event property. The array must contain one or more nested vectors, each containing 2 or 3 elements (Type, Action and optionally Arg) as described above.
F1.Event ← 'onMouseDown' 'FOO'
Means: invoke callback function FOO on MouseDown, the first element of the right argument to FOO will contain a namespace reference to F1. All other events perform their default actions.
F1.Event ← 'MouseDown' 'FOO'
Means: invoke callback function FOO on MouseDown, the first element of the right argument to FOO will contain the character vector'F1'. All other events perform their default actions.
F1.Event ← ('onMouseDown' 'FOO')('onMouseUp' 'FOO')
Means: invoke callback function FOO on MouseDown and MouseUp. All other events perform their default actions.
F1.Event, ← ⊂ 'onMouseMove' 'FOO' ('THIS' 1)
Means: add a callback function FOO on the MouseMove event. The function will receive the array('THIS' 1) as its left argument. All other events perform their default actions.
To define the action to be taken for individual events, one by one, you use the onEvent syntax and make the assignment to the event name prefixed by the string 'on'.
F1.onMouseDown ← 'FOO'
Means: invoke callback function FOO on MouseDown.
F1.onMouseUp ← 'FOO'
Means: add the same callback for MouseUp.
F1.onMouseMove ← 'FOO' ('THIS' 1)
Means: add the same callback function FOO for the MouseMove event. The function will receive the array ('THIS' 1) as its left-argument.
Notice that you must use the 'on' prefix; you cannot assign to the Event name itself. This would cause an error:
F1.MouseUp←'foo' SYNTAX ERROR: Invalid modified assignment, or an attempt was made to change nameclass on assignment F1.MouseUp←'foo' ∧
When you set the Event property using ⎕WC and ⎕WS you define the actions for the event types that you specify in the argument, leaving the actions for all other event types unchanged. When you create an object with ⎕WC, all unspecified event types will be unhandled; i.e. those events will perform the default processing. However, when you specify the action for a new event type using ⎕WS, any actions previously defined for other event types will remain as they were.
Ignore MouseDown (1) event (APL will perform the default processing for you)
'F1' ⎕WS 'Event' 'MouseDown' 0
Terminate ⎕DQ on MouseDown
'F1' ⎕WS 'Event' 'MouseDown' 1
Invoke callback function FOO on MouseDown, the first element of the right argument to FOO will contain a namespace reference to F1
'F1' ⎕WS 'Event' 'onMouseDown' 'FOO'
Invoke callback function FOO on MouseDown, the first element of the right argument to FOO will contain the character vector 'F1'
'F1' ⎕WS 'Event' 'MouseDown' 'FOO'
Invoke callback function FOO on MouseDown and MouseUp
'F1' ⎕WS 'Event' ('onMouseDown' 'onMouseUp') 'FOO'
Invoke callback function FOO with ('THIS' 1) as its left-argument on MouseDown
'F1' ⎕WS 'Event' 'onMouseDown' 'FOO' ('THIS' 1)
Invoke callback function FOO with ('THIS' 1) as its left-argument on MouseDown, MouseUp and MouseMove
EV ← 'onMouseDown' 'onMouseUp' 'onMouseMove' 'F1' ⎕WS 'Event' EV 'FOO' ('THIS' 1)
Execute the expression COUNT+←1 on MouseDown
'F1' ⎕WS 'Event' 'MouseDown' '⍎COUNT+←1'
Execute the expression COUNT+←1 on MouseDown, MouseUp and MouseMove
EV ← 'MouseDown' 'MouseUp' 'MouseMove' 'F1' ⎕WS 'Event' EV '⍎COUNT+←1'
Ignore MouseDown (1) event (APL will perform the default processing for you)
'F1' ⎕WS 'Event' (1 0) 'F1' ⎕WS 'Event' 1 0 ⍝ Ditto
Terminate ⎕DQ on MouseDown
'F1' ⎕WS 'Event' (1 1) 'F1' ⎕WS 'Event' 1 1 ⍝ Ditto
Call function FOO on MouseDown
'F1' ⎕WS 'Event' (1 'FOO') 'F1' ⎕WS 'Event' 1 'FOO' ⍝ Ditto
Call function FOO on MouseDown and MouseUp
'F1' ⎕WS 'Event' ((1 2) 'FOO') 'F1' ⎕WS 'Event' (1 2) 'FOO' ⍝ Ditto 'F1' ⎕WS 'Event' 1 2 'FOO' ⍝ Ditto 'F1' ⎕WS 'Event' (1 'FOO')(2 'FOO') ⍝ Ditto
Call function FOO with ('THIS' 1) as its left-argument on MouseDown
'F1' ⎕WS 'Event' (1 'FOO' ('THIS' 1)) 'F1' ⎕WS 'Event' 1 'FOO' ('THIS' 1) ⍝ Ditto
Call function FOO with ('THIS' 1) as its left-argument on MouseDown and MouseUp
'F1' ⎕WS 'Event' ((1 2) 'FOO' ('THIS' 1)) 'F1' ⎕WS 'Event' (1 2) 'FOO' ('THIS' 1) ⍝ Ditto 'F1' ⎕WS 'Event' 1 2 'FOO' ('THIS' 1) ⍝ Ditto 'F1' ⎕WS 'Event' 1 2 'FOO' ('THIS' 1) ⍝ Ditto
Execute the expression COUNT+←1 on MouseDown
'F1' ⎕WS 'Event' 1 '⍎COUNT+←1'
Execute the expression COUNT+←1 on MouseDown, MouseUp and MouseMove
'F1' ⎕WS 'Event' (1 2 3) '⍎COUNT+←1' 'F1' ⎕WS 'Event' 1 2 3 '⍎COUNT+←1' ⍝ Ditto
In addition to the standard events supported directly by Dyalog APL, you may specify your own events. For these, you must use event numbers; user-defined event names are not allowed.
You may use any numbers not already defined, but it is strongly recommended that you choose numbers greater than 1000 to avoid potential conflict with future releases of Dyalog APL.
You can only generate user-defined events under program control with ⎕NQ.
∇ foo m [1] ⎕SE.UCMD'display m' ∇ 'f'⎕WC'Form' ('Event' 1001 'foo') f.Event 1001 #.foo ⎕NQ 'f' 1001 ┌→─────────┐ │ ┌→┐ │ │ │f│ 1001 │ │ └─┘ │ └∊─────────┘ 'f' ⎕WS 'Event' 1002 'foo' f.Event 1001 #.foo 1002 #.foo ⎕NQ 'f' 1002 ┌→─────────┐ │ ┌→┐ │ │ │f│ 1002 │ │ └─┘ │ └∊─────────┘
Notice that if you use the onEvent syntax, the event property reports the event type as you specified, but the callback function receives just the number as before.
f.on1003←'foo' f.Event 1001 #.foo 1002 #.foo on1003 #.foo ⎕NQ 'f' 1003 ┌→─────────┐ │ #.f 1003│ └+─────────┘
If no events are set, the result obtained by ⎕WG and the result obtained by referencing Event directly are different:
'F'⎕WC'Form' DISPLAY 'F'⎕WG'Event' ┌→──┐ │0 0│ └~──┘ DISPLAY F.Event ┌⊖────────────┐ │ ┌→────────┐ │ │ │ ┌⊖┐ ┌⊖┐ │ │ │ │ │ │ │ │ │ │ │ │ └─┘ └─┘ │ │ │ └∊────────┘ │ └∊────────────
To reset the Event property, the same (different) values must be used accordingly:
f.Event←0⍴⊂'' ''
or
'f'⎕ws'Event' 0 0
If you use the onEvent syntax with built-in event numbers, the effect is the same as if you had used the event name. This does not apply to user-defined events.
'f'⎕WC'Form' f.on2←'foo' f.Event onMouseUp #.foo ∇foo∇ ∇ foo m [1] ⎕SE.UCMD'display m' ∇ ┌→──────────────────────────────────────────┐ │ ┌→──────┐ │ │ #.f │MouseUp│ 24.81481552 73.33333588 1 0 │ │ └───────┘ │ └∊──────────────────────────────────────────┘
This differs from the behaviour when you use event number normally:
'f'⎕WC'Form' 'f' ⎕ws 'Event' 2 'foo' f.Event 2 #.foo ┌→──────────────────────────────────┐ │ ┌→┐ │ │ │f│ 2 52.77777863 13.22916698 1 0 │ │ └─┘ │ └∊──────────────────────────────────┘
When you query the Event property using ⎕WG, names of callbacks associated with events are reported exactly as they were set. When you reference the Event property, the names are reported as absolute pathnames.
)ns x #.x )cs x #.x 'f'⎕WC'form' f.onMouseUp←'foo' f.Event ┌───────────────────┐ │┌─────────┬───────┐│ ││onMouseUp│#.x.foo││ │└─────────┴───────┘│ └───────────────────┘ 'f'⎕wg'event' ┌───────────────┐ │┌─────────┬───┐│ ││onMouseUp│foo││ │└─────────┴───┘│ └───────────────┘ )cs # #.x.f.Event ┌───────────────────┐ │┌─────────┬───────┐│ ││onMouseUp│#.x.foo││ │└─────────┴───────┘│ └───────────────────┘ '#.x.f'⎕wg'Event' ┌─────────────────┐ │┌─────────┬─────┐│ ││onMouseUp│x.foo││ │└─────────┴─────┘│ └─────────────────┘
When using regular event names, case is unimportant. For example, the system will accept 'MouseUp', 'MOUSEUP' or even 'mOuSeUp'. When using the onEvent syntax, case is critical. The 'on' must be in lower-case and the case of the event name must be spelled exactly as documented. In all cases, the event name will be reported using the documented spelling.
The event number 0 and the event name 'All', are convenient shorthands to associate a particular action (such as a callback) with all the built-in events supported by an object.
'f'⎕WC'Form' ('Event' 'All' 1) f.Event All 1 ⎕←⎕DQ 'f' f Create 1