| APL+Win Programming Basics
In order to write Windows programs with APL+Win, you first need to
get some basic understanding of how Windows is working.
Events Programming vs Procedural Programming
In Windows you are using an "Events Programming" scheme,
while in DOS programmers were using "Procedural Programming".
Procedural means that your program is the Master who decides what
happens: it executes its instructions in order and does not listen to your input while it
is doing its processings. Sometimes it stops for requesting input from you and when you
press Enter or click the mouse, it resumes execution, stopping listening to you again
until the next input request.
Windows is very different: if you have used Windows at all, you may
have noticed that Windows is always listening to you and reacting, sometimes with some
delay, to everything you have done with your keyboard or mouse. This is because of the
Events handling nature of Windows.
Under Windows the Master is no longer the program: it is you!
In order to achieve this, Windows programs have to be constantly
listening to "events", even though they might sometimes be busy processing data
at the same time.
An event is most often generated by the user, by pressing a key on
his keyboard or moving or clicking his mouse. But, as will see later, there are many other
possible sources of events.
When you develop a Windows application, the language you are using
must be able to achieve this "events" listening and must be able to inform you
when an event has occured. It must also provide you with all the necessary information
concerning this event.
Your task as a Windows programmer is simply to react to each occuring
event by writing a program (often a small program) which handles this event, i.e. which
does the processing you have analyzed it should do when this event occurs.
APL+Win high and low level Windows tools
APL+Win is a pure Windows version of APL (the successor of APL*PLUS
II and APL*PLUS III with which it is 100% compatible) and is an "events
programming" language. It is a Windows language.
APL+Win offers you 2 ways of programming with Windows:
| High level tools |
This is the simplest way, which we will describe in detail in this
chapter. It implements high level Objects which you use in an Object Oriented manner,
working with their properties, events and methods, pretty much as you would in such
programming languages as Visual Basic and Delphi.
|
| Low level tools |
APL+Win allows you to access the whole 16-bit and 32-bit Windows API
(Application Programming Interface), i.e. the hundreds of
functions and messages which are part of the Windows SDK (Software Development
Kit). You can develop programs this way much as a C++ programmer would: using
low level tools is much more tedious and difficult than using high level tools, but gives
you access to everything Windows can do. |
What is more, APL+Win lets you easily mix its high
level and low level programming tools.
At the beginning, you will certainly mostly use high level tools and,
as you gain more experience with Windows and develop more and more complex Windows
applications with APL+Win, you might find that you also more and more often use the
Windows API.
That's why this book will first concentrate on the high level Object
Oriented tools built in APL+Win and then talk about the Windows API.
Objects, Properties, Events and Methods
In Windows, almost everything you see on the screen is an object.
A window is an object, a button is an object, a scroll bar is an
object: list boxes, combo boxes, menu and menu options, toolbars, status bars, radio
buttons, check boxes, etc. are objects.
All these things are called objects because just like real life
objects, they have properties, events that can happen on them and actions that can be
performed on them.
Let's take a pencil object: it has a size, a color, a brand written
on it which are some of its Properties. Events can happen on it: it can for
example be moved on the table surface or dropped on the floor. You can decide to put its
cap on it and this is an action (in OOP terms: a Method which is applied to the
pencil).
Similarly, a window has a size, a color and a caption (some title
written on it) which are some of its Properties. When you drag it with the mouse, a
Move event occurs on this window. When you resize it with the mouse, a Resize event
occurs. If you decide to close the window by program, you would apply its Close method
to it.
There are a limited number of objects in APL+Win, which almost all
refer to standard Windows objects.
Each of these objects has built in properties, events and methods
which you can use in your programs.
APL+Win Windows Functions
APL+Win contains the following system functions:
| WI |
Creates, controls and runs Graphics User Interfaces (GUI) |
| WCALL |
Makes calls to Windows DLL |
| WGIVE |
Allows you to yield the CPU to Windows during execution of a long
runnning callback |
and the following system variables:
| WSELF |
Contains the fully qualified name of the current Windows object |
| WARG |
Contains the argument associated with an event or method, during a
callback |
| WEVENT |
Contains the name of the current event during a callback |
| WRES |
Contains the explicit result, if any, that a Windows method or
callback returns |
for developing Windows applications.
The WI system function
To work with high level Object Oriented tools, APL+Win provides you
with the WI system
function.
This function does most of the work you need to write a Windows
application.
Its syntax is:
result „ {object} ŒWI action {arguments}
Here are examples of all what it can do:
| WSELF'object'WI'New'
'obj_class' |
Create a new form or control
Example:
WSELF'fmTest'WI'New'
'Form'
WSELF'fmTest.bnOK'WI'New' 'Button'
|
| result 'object'WI'Method' |
Invoke a method
Method names always start with an uppercase character.
Example:
'fmTest'WI'Close'
The result is most often 0 0½0 except for some methods like New, Close or Wait. |
| value 'object'WI'property' |
Reference a property
Get the current value of one of the object's properties.
Example:
'fmTest' wi 'caption'
Property names start with a lowercase character.
|
| 'object'WI'property'
'value' |
Set a property
Change one of the object's properties to a new value.
Example:
'fmTest'wi'caption' 'Test Window'
|
| 'object'WI'event'
'callback' |
Associate a callback with an event
Example:
'fmTest'wi'onMouseMove' 'WARG'
This means that every time the user will move the mouse over the fmTest window, the
'WARG' expression will be executed.
|
| result'fmTest'wi'Wait' |
Wait on a form for user input
Example:
'fmTest'wi'Wait'
Every callback that is associated with an event is automatically executed when the event
occurs.
|
Things will become much clearer through an example.
Let's write a Windows game with APL+Win! Yes, APL+Win is so simple
that you already know enough to write a simple Windows game, with just a few APL
instructions.
The Catch Me! game
The game consists of trying to click a button on a form with the
mouse: however, every time the user moves the mouse cursor over the button, the button
escapes. The user wins if he succeeds in clicking the button.
This game is an excellent way of explaining how to write a Windows
application, what is an event and a callback, etc. Once we finish this example, you should
be able to start writing simple Windows applications with APL.
First, we need a form. Every Windows application starts with a form.
Let's create one: we will call the form object: fmCatch.
'fmCatch'Œwi'New' 'Form'
By default the form you just created is centered on the screen and is
half the size of the screen in height and width.
The New method returns as a result, the name of the object which has
been created, here fmCatch.
You generally want to capture this result in the WSELF system
variable. When you do so, the current object becomes the one assigned to WSELF
and
you may omit the WI
left argument when working on this particular object, until WSELF
is changed to some other object. Let's try it:
ŒWSELF„'fmCatch'Œwi'New' 'Form'
ŒWI OBJECT ERROR: Object already exists
ŒWSELF„'fmCatch'Œwi'New' 'Form'
^
We get an error because the APL+Win system has a security
which prevents you from creating an object having the name of an existing object. We must
first destroy our fmCatch window. This is done by invoking its Delete method.
'fmCatch'ŒWI'Delete'
ŒWSELF„'fmCatch'ŒWI'New' 'Form'
Let us change a few properties of our form. First we need to reduce
its size:
ŒWI'size' 12 32
An APL+Win form's size is by default expressed in screen number of
lines and columns (which the standard Windows System font would use).
Let us change our window's title to "Catch Me!'. To do
so, we need to assign a new value to the form's caption property:
ŒWI'caption' 'Catch Me!'
Our form immediately reflects the change.
If we wanted to check what is the current window's title, we would
just need to do:
ŒWI'caption'
Catch Me!
Up to now, it's pretty easy.
Note that we could have set one or more of our windows properties at
creation time:
'fmCatch'ŒWI'Delete'
ŒWSELF„'fmCatch'ŒWI'New' 'Form'('size' 12 32)('caption' 'Catch Me!')
or, we could benefit from having assigned the result of the New
method to WSELF and written maybe more clearly:
'fmCatch'ŒWI'Delete'
ŒWSELF„'fmCatch'ŒWI'New' 'Form'
ŒWI'size' 12 32
ŒWI'caption' 'Catch Me!'
So, for now, our application contains at most 4 APL+Win instructions.
We need a button on the form. This button will be a "child"
of the form and this is denoted by using a dot notation. Applying what we just learnt, we
could write the following additional instruction:
ŒWSELF„'fmCatch.bnCatch'ŒWI'New' 'Button'('caption' 'Catch Me!')
We have just define the entire user interface of our Windows game. We
now need to react to a few events.
When the mouse cursor comes over the Catch Me! Button, we want to
change its position in the form. In order to do so, we need to define a callback function
for the MouseMove event on the Catch Me! button.
'fmCatch.bnCatch'ŒWI'onMouseMove' 'fmCatch_bnCatch_MouseMove'
This instructs the APL+Win system that WHENEVER a MouseMove event
occurs on the button (i.e. whenever the user moves the mouse cursor over the button), it
must execute the fmCatch_bnCatch_MouseMove callback function.
You may ask yourself why I chose a so complex name for the callback
function. You must understand now that a standard Windows application will contain a
number of different forms, each of them containing a number of children objects (we call
them "controls"), and that on each of these controls a number of events
may occur for which you have decided to associate a callback function.
If your Windows application has 10 forms, each having 10 controls on
each of which you want to handle 10 events, this means that your application will have
10x10x10 = 1000 callback functions!
The only way to not get lost in this "jungle" of callback
functions is to adopt a strict naming convention for callback functions. The recommended
naming convention consists of using the name of the object on which the event occurs,
followed by the event name separated by underscore characters, hence here:
fmCatch_bnCatch_MouseMove
I will however show you a better way of coping with callback
functions at the end of this example.
We are left with writing our first callback function: fmCatch_bnCatch_MouseMove. The only thing we need to do in this function is to move the
button to another location on the form.
By now, you might guess that moving the button is pretty easy: we
only need to change its where property. However, we must ensure that the Catch Me! button
stays inside the form: we must not set its where property to a location outside the form
otherwise it would not be fair to the user!
The where property determines the top left corner position of the
button, in terms of its y and x coordinates relative to the top left corner of the client
area of the form. The button's where property must be larger than 0 and smaller or
equal to the form size minus the button size.
So, here is our callback function:
’ fmCatch_bnCatch_MouseMove;A;B
[1] A„'fmCatch'Œwi'size' © get the form size
[2] B„'fmCatch.bnCatch'Œwi'size' © get the button size
[3] 'fmCatch.bnCatch'Œwi'where'(?˜A-B) © randomly change button pos
’
However, when an event is being handled by a callback function, the system has set the
WSELF system variable to the object on which
the event occurs. This means that you may omit this object name in the left argument of
WI, whereever it appears in the function.
The callback function therefore gets simplified to:
’ fmCatch_bnCatch_MouseMove;A;B
[1] A„'fmCatch'Œwi'size' © get the form size
[2] B„Œwi'size' © get the button size
[3] Œwi'where' (?˜A-B) © random change button position
’
You could even write this callback function in one instruction, as:
’ fmCatch_bnCatch_MouseMove
[1] Œwi'where'(?˜('fmCatch'Œwi'size')-Œwi'size')
’
Callback functions are often very short in APL+Win, thanks to its
high level object oriented tools, but remember that on the other hand a real Windows
application may have hundreds or thousands of such callback functions.
Well, we are now ready to start our game!
In order to start a Windows application, we should invoke the Wait
method on its main form (here fmCatch):
'fmCatch'ŒWI'Wait'
The Wait method returns a value (0 by default), so it would have been
better to use the following expression which absorbs the result since we do not need it
for now:
0 0½'fmCatch'ŒWI'Wait'
Just before doing so, let us summarize our programming efforts so
far:
'fmCatch'ŒWI'Delete'
ŒWSELF„'fmCatch'ŒWI'New' 'Form'('size' 12 32)('caption' 'Catch Me!')
ŒWSELF„'fmCatch.bnCatch'ŒWI'New' 'Button'('caption' 'Catch Me!')
'fmCatch.bnCatch'ŒWI'onMouseMove' 'fmCatch_bnCatch_MouseMove'
0 0½'fmCatch'ŒWI'Wait'
which we can express it in a more readable manner in a simple
function:
’ BookCatchMe1;Œwself
[1] ©’ BookCatchMe1 -- Catch Me! game version 1.
[2]
[3] 'fmCatch'Œwi'Delete'
[4] Œwself„'fmCatch'Œwi'New' 'Form'
[5] Œwi'caption' 'Catch Me!'
[6] Œwi'size' 12 32
[7] Œwself„'fmCatch.bnCatch'Œwi'New' 'Button'
[8] Œwi'caption' 'Catch Me!'
[9] Œwi'onMouseMove' 'fmCatch_bnCatch_MouseMove'
[10] 0 0½'fmCatch' Œwi 'Wait'
’
and the following callback function:
’ fmCatch_bnCatch_MouseMove
[1] Œwi'where'(?˜('fmCatch'Œwi'size')-Œwi'size')
’
Note the indentation in the BookCatchMe1 function which makes it more
readable: APL+Win retains all blanks in source code, so do not hesitate to use blanks to
indent lines, align comments, etc.
It's time to try our game:
BookCatchMe1
Try to click on the Catch Me! button: every time you approach it, it
escapes from you and it is quite difficult to catch it, isn't it?
In fact, our game is not complete. We need a way to terminate it.
I suggest that, if the user happens to succeed in clicking the Catch
Me! button, we change the button caption to something like 'Game Over!'.
Is that enough?
No, we also need to inhibit the onMouseMove event and callback
function, otherwise the button would indicate Game Over! but would continue to escape from
the mouse when the user moves the mouse on it.
Does all this seem difficult to you?
You might be surprised to see how simple it is with APL+Win.
Again, we need to react to an event, this time to a Click event on
the Catch Me! button, so we need to associate a callback function to the button
onClick
event:
'fmCatch.bnCatch'ŒWI'onClick' 'fmCatch_bnCatch_Click'
The callback function may be defined as:
’ fmCatch_bnCatch_Click
[1] Œwi'onMouseMove' ''
[2] Œwi'caption' 'Game Over!'
’
Remember that we do not need to specify the WI left argument to
fmCatch.bnCatch, since we are currently in a callback for this exact button (and therefore
the system has set WSELF
to
'fmCatch.bnCatch').
The first instruction simply redefines the button MouseMove event as
no longer having any callback function associated with it (the fmCatch_bnCatch_MouseMove
stays in the workspace, but is no longer automatically called by APL+Win when a
MouseMove
event occurs on the button).
The second instruction sets the button caption to Game Over!.
Isn't it easy and fun?
Let's summarize what our programming efforts have been so far:
’ BookCatchMe2;Œwself
[1] ©’ BookCatchMe2 -- Catch Me! game version 2.
[2]
[3] 'fmCatch'Œwi'Delete'
[4] Œwself„'fmCatch'Œwi'New' 'Form'
[5] Œwi'caption' 'Catch Me!'
[6] Œwi'size' 12 32
[7] Œwself„'fmCatch.bnCatch'Œwi'New' 'Button'
[8] Œwi'caption' 'Catch Me!'
[9] Œwi'onMouseMove' 'fmCatch_bnCatch_MouseMove'
[10] Œwi'onClick' 'fmCatch_bnCatch_Click'
[11] 0 0½'fmCatch' Œwi 'Wait'
’
’ fmCatch_bnCatch_MouseMove
[1] Œwi'where'(?˜('fmCatch'Œwi'size')Œwi'size')
’
and:
’ fmCatch_bnCatch_Click
[1] Œwi'onMouseMove' ''
[2] Œwi'caption' 'Game Over!'
’
Let's play our game:
BookCatchMe2
It is not so easy to catch the button: in fact it is even pretty
difficult and this shows you how fast is APL+Win to react to events. You might even think
that it is impossible!
However, here is a hint: wait until the button moves in the top left
part of the form, then resize the form with the mouse to a much smaller form, where the
button is still at least partly visible, now try again. |