VOICE Home Page: http://www.os2voice.org
|By Thomas Klein © October 2002|
Well - summer holiday's are gone and elections around here in Germany are done also. Time to get back to more important thing! ;) First I'll start by checking your homework (did you forget that!?) I have to take this occasion to express my thanks for all those great mails that have reached me since last month's issue was published. Never did I expect my series on Rexx would be that "interesting" to a greater public.The amount and quality of feedback proves the OS/2 community is more alive and kicking than some others want to make us (or themselves) believe. At least this is true for you people out there who are reading the VOICE newsletter.
Fine. Okay - now for the homework:
The question was how to get our textbox to be "empty" at program startup. There are two ways of doing this:
call output.text("Hello world!")
We simply change this into:
Okay, that was pretty clear, but the more complicated part of it is knowing "where to put" this command. And that was where I left you a little puzzled last month - again, sorry for not being very fair. Again, you can choose among two ways of doing it: Either the command is executed at dialog load time (when the appropriate EVENT is taking place - we'll see details later) or you make use of the "global main routine". This doesn't make things more easy, right? It's not easy to find a good start into the whole matter - let's try to put it this way...
With "event-oriented" programming this is different: There still is only one starting point for your program of course, but as soon as your dialog is displayed, you're programmatically left somehow "nowhere": There's no idea of what happens first and what might happen next. If your dialog for example is made of multiple text entry fields, list boxes and command buttons, you'll never know if the user is scrolling the list box first or typing some text or closing your window or clicking "ok" or "cancel"... This is, why small parts of source code exist for each possible event and why they are kind of "lying around in your source code" without any special link between them. Actually, each of these is nothing but a procedural sequence of commands, but the difference is that these parts are not called by the programmer but by operating system (or the window management sub-system) itself. I ask the technically more sophisticated readers among you to forgive me for not being accurate in details, but I think that this series is not intended to explain window messaging and all those techniques behind it, as this is not mandatory for our audience (at least for the moment).
The whole sequence of a program in DrDialog is based upon events. There are events which take place upon user actions only - like clicking a pushbutton or dragging a slider. However, there are some events that are independent from user actions, like the initial display a dialog window. These events are provided by DrDialog to enable us to control certain stages in program execution. In our example, we could make use of the "OPEN DIALOG" event to clear the textbox contents.
By now, we know that we're dealing with an EVENT concerning a DIALOG window: So let's check out the EVENTS that are provided for the DIALOG window. Click mouse button two on an empty part of the dialog window to bring up the context menu - then, select "Events". This will pop up the submenu holding a list of all events that are provided by DrDialog for use with dialog windows. Hmmm. There's one called "OPEN"... sounds good, right? This is the event that is "triggered" when the program opens the dialog window - exactly what we need. Go ahead and select this menu entry.
The code editor window is back - showing us the part of program source that will be executed when the dialog window is opened. I think we should type our command here:
Selecting the open-event of a dialog window can be done in a more comfortable way by simply double-clicking on an empty part of the window - but I wanted you to go through the context menu to see how to "reach" the events overview. You might want to check the result of the command we just entered. Do you remember how to use the "running man"...?
1) If there is no subroutine called "INIT" in the global procedures area go on by internally providing it (without any contents)
2) All dialogs specified in the "INIT" subroutine are loaded and displayed subsequently
3) What? Still no dialog windows displayed? Okay, go on by loading and displaying the dialog having the smallest ID number of all dialogs in the program. (Do you remember DrDialog identifying all controls by a unique ID number? Well - dialogs have such unique IDs too.)
4) Oh c'mon! Still no dialog displayed? That's enough:
Display error message "No dialog to load".
As we didn't provide a subroutine (or "procedure") called "INIT" anywhere in our sample program, rule #3 above is taking effect: DrDialog starts by loading the dialog window having the smallest ID number. This method is both simple and rock solid: At design time startup, DrDialog automatically provides an empty dialog template. As soon as you're not specifying an "INIT"-routine, DrDialog will make use of rule #3 to load the first dialog window and enter it's "normal" state of operation: Wait for events There's another feature built into DrDialog to ensure it's flow of process, which does not seem to make sense at first glance: If you remove the last (or the only) dialog from your application, DrDialog will immediately create an empty template. This way, DrDialog makes sure it can always enter its normal state of operation.
Q1: What are "routines"?
A: Actually, from our point of view, this is everything that's contained in the code editor. If you want to react upon the clicking of a pushbutton for example, all the commands you want to execute are written into the appropriate notebook page of the code editor. Such "page" is called an event-handler; it's a routine called by the system each time that it's according event takes place. To sum it up: Each notebook page actually represents a procedure (or subroutine).
Q2: What does "global main" stand for in this context?
A: As said above, there's an events handler (routine) associated with each events. In addition, the programmer can provide his own functions or routines. Usually, this is done to avoid having identical command sequences in multiple places of your program: The parts being used are packaged into a single subroutine that can then be called from where it's needed. If the programmer discovers an error or needs to change/expand the routine, he only needs to do it once, except keeping track of the changes in all occurrences of the code. Sometimes, such procedures are used to simplify the reading of more complex processing sequences. Anyway - such routines can be defined on dialog level, that's to say, that the routine can be called from anywhere *within the dialog* code. Quite useful, eh? But what if I have a program with multiple dialogs that requires my routine to be available to ALL dialogs - e.g. routines doing error handling or retrieving settings from an INI-file? In this case, the routine must not be defined on the dialog level, but GLOBALLY. Global in this context means, that the routine can be called from 'anywhere within the program' code.
In DrDialog, there's a special page in the code editor notebook that is used to deal with global stuff - represented by a globe icon.
Now, the "global main" routine is a special routine used to tell DrDialog what to do at program startup. The presence of such routine (must be called "INIT") tells DrDialog to avoid loading dialogs automatically. This routine might seem to be another good place to put our clear textbox contents command. It is, yes, but the global INIT routine requires deeper knowledge of DrDialog's execution model concepts: The way that DrDialog triggers and reacts to events is certainly worth a special detailed discussion (in a separate part), but first, we should get to know what all those controls are used for and very first of all: How the heck do we stop the program once it is up and running. ;) We'll talk about the "INIT"-routine later.
Once you made use of either method 1 or 2 to clear the textbox (design-time or
"open"-event at run time), we should figure out what to do to stop the
program. Once again, there are multiple ways to do so: We could provide a Quit-button,
a "file/quit"-menu entry or by using the system's functions
(system menu of window, close button (Warp4 and above),
advantages of the system menu or close-button (or
If selected, the window contains a title bar. The contents of the title bar can be provided by the programmer.
Specifies whether the dialog window will show a button to minimize it. This equally takes effect on the contents of the window's system menu: If this option is selected, you'll find "minimize" being selectable in the system menu. Otherwise, it will be grayed (not selectable).
Specifies whether or not the dialog window will contain a button to maximize it. As with the minimize button described above, this setting also effects the contents of the windows system menu. The according "maximize" entry will either be selectable or not (thus grayed). In addition, the maximize button is used to toggle between the maximize and "restore" function. If the window is maximized, the button will show the "restore" symbol, used to reset the window to it's size before maximize was used. Again, this takes effect on the system menu contents accordingly.
Specifies that the window will show it's system menu icon in the upper left corner of the window frame. The system menu provides several window functions as described above, as well as an entry to bring up the window list.
Vertical/Horizontal scroll bar
This will add according scroll bars to the window, which is of no importance to us at the moment. ;)
Starting with Warp4 (or prior by using several WPS extension/enhancement tools),
there is another button added to the window frames: The "close" button.To
make our dialog window show such a button, we simply need to include both system
menu as well as at least the maximize or minimize button. This combination will
tell the system to show the close button too. As you might remember from last months
So let's create another pushbutton:
Either we make use of the controls collection by dragging and dropping a push button onto our dialog window or we can take advantage of the dialog window's context menu: Select the controls submenu and choose the push button symbol from there.
Okay, this is the push button. We might want to provide a "useful" name for it - by invoking the buttons context menu and selecting name. Now enter something like "btn_quit". There is no need to follow any rules for the names, but I strongly recommend to do so. My preferred scheme for naming controls is "cmd_..." for push buttons ("command buttons"), "lst_..." for list boxes and so on. This will make all controls of the same type being grouped in the controls list of your application. In addition, it provides a simple means of self-contained documentation. Believe me: You'll appreciate any naming convention once you get back to work on a program after - let's say - two months. ;)
Well, we now have the button and we gave it a new name. But "push" being the text on the button? Hm. It might be quite clear that one must PUSH the button to use it and it appears to be preferable to give a clue on what happens once we did. Again: Context menu of the push button - select Text and enter "Exit" or "Quit" or something equivalent - you can even use "bye-bye" if you want.
Now only the actual command is missing. To tell the program what to do, we must bring up the code editor and select the correct place - or simply double-click on the push button.;) Now all that's left is one extremely complicated statement we need to type in:
That's it. ;)
Of course this is not the "politically correct" way of terminating a program, as it will immediately stop execution regardless of opened files or dialogs. In our case it's okay, but usually one would prefer to bring up "save changes yes/no" or save some settings in INI-files. All this needs to be done before using the "exit" command.
By now, you might be wondering why
Besides the "exit" command, there's another way of termination built into DrDialog: Closing the last opened dialog will terminate the program. And this is why
This statement actually means, that we want to call the CLOSE method of the control DLG_TEST. Check to see if it works by running the program - it works, does it? Now let's face the question of advantages and differences of both ways to "exit" a program:
Using the "exit" command has the advantage of immediate termination
- regardless of open dialogs. Closing the window of course has the advantage of
not closing all other dialogs - in addition, using the "close" method
will trigger the dialog's "exit" event. Make sure not to mess up the "exit"
statement of Rexx and the "exit" event of the dialog. They have nothing
in common, except their name of course. By using the close method, we're making
use of the same internal processes as
call beep 500,100
This will sound a beep tone of 500 hertz frequency for 100 milliseconds. Okay,
that's not a really sophisticated close method, but it will help us to find out
all ways of closing a program which make use of the "close" event. Try
it out: Run the program multiple times and use different ways of closing it (
That reminds me - I complete forgot that one - of the fact, that DrDialog's code editor window provides a nice mean of showing WHAT events were already reacted upon by the programmer: As soon as you start writing anything in an event handler routine which was previously empty, its notebook tab will be changed by adding a "*" (asterisk) to its name. (Example: "EXIT" -> "EXIT*").
That's it for this months issue.
If you're still not fed up or like to play around a little, I would suggest you try to use different settings in the Style-Options for the dialog to see how they effect both appearance and behaviour of the dialog window. If you prefer a melody instead of simple beeps in the exit event, check the "beep" commands documentation within the rexx manual of OS/2 (usually installed during installation of OS/2). As each and every location has changed on my system since I installed OS/2, I'm afraid that I can't tell you the exact folder names to look for simply search for a file called REXX.INF. Usually, it should be placed in the x:\OS2\BOOK path; "x" being the drive letter of your OS/2 installation.
I'm sorry that we did not proceed a lot with the sample program in this issue, but I think that some of the basic concepts of DrDialog require more attention at this stage. Once you understand how things work, you won't run into problems later, even when dealing with more complex applications that might use multiple dialogs.
Next month, we'll extend our sample application by a text entry field, a list box and a second dialog to see what we're able to do with them and what to take care of. Later on, we'll look at the other basic components of GUI dialogs and explore their behaviour by some simple example programs. After doing a small tour on "pure rexx", we'll start with a "large scale" sample application. If you have any idea(s) on what this application could be about, let me know! Up to now, there are two suggestions I prefer: A PIM application (personal information manager; address/date/to-do list) or a GUI frontend application for handling ZIP files (and/or other types of archives). Personally, I prefer the ZIP-frontend, as it does not require special knowledge and it represents a "typical" purpose of rexx or DrDialog.
So stay tuned!
[Previous Page] [Newsletter Index] [Next Page]
VOICE Home Page: http://www.os2voice.org