VOICE Home Page: http://www.os2voice.org
|By Thomas Klein© December 2002|
A warm 'Welcome back' from a guy who's still completely overwhelmed by the impressions from this year's Warpstock Europe! This month, DrDialog will show us some of the basics in rexx GUI programming, as we'll take a look on the standard components of any graphical user interface, regardless of platform.
But let's just check last month's 'homework' first... using rexx's
function to - well - strip leading and trailing spaces off a string.
As stated in the REXX.INF file, its syntax is
Remember that square brackets equal optional parameters:
[char] specifies the character that is to be removes from String1 and defaults to SPACE - thus, if you don't specify an explicit character here, strip() will remove spaces.
[option] is an optional command string that specifies, which characters to remove from String1 : Leading, Trailing or Both. You only need to specify the first letter of the option, like 'l' if you want to remove the leading spaces. This parameter defaults to 'Both' - and strip() will remove both leading and trailing spaces if nothing was specified. To sum up things:
If you just want to remove both leading and trailing SPACE characters from String1, you only need to write
is a function and thus returns something. To be exact, it'll return
the string that was stripped off spaces. Either you need to provide a variable to
hold the return value
or use it in conjunction with another operation like
that simply displays the result like in
And to show how this was meant to be used in last months example, here we go
with version 1 that makes use of 'intermediate' variables:
And finally (if you prefer this one) the second version which makes use of the
'spectacular' one-line statement...:
Okay for that. Once you'll start increasing you rexx programming skills you'll deal with STRIP() on a more frequently basis. But for now, let's concentrate on what we were about to discuss today
As I mentioned last month, their appearance might vary depending on the platform. But in most cases, you'll always recognise them due to the way appearance and behaviour are implemented into them... they are:
Of course, there are quite a lot more, but those mentioned above are the most basic "common ground" when taking into consideration OS/2 Warp, Windows (3.x to 2K) and Mac. I can't tell for the Linux GUI or BeOS as I never dared to look close enough <g>, but if it has a GUI, you'll find them too, I'm sure. Even when looking at Java or your webbrowser's 'form' components: Here they are again! And back in the DOS days, when character-oriented user interfaces were starting to use kind-of GUI-like graphical components they were already present in programs like Norton Utilities or PCTools
Well, most of us are almost used to interact with these components without thinking a lot about it, so you might already have an idea about what kind of user guidance task can be accomplished with specific components. Let me tell you that the world is full of funny guys who prefer to do funny things with GUI components, not taking care of 'GUI ethics'. A standard example that comes to mind is 'Optionbutton vs. Checkbox'.
Actually, option buttons are used to specify options that
are mutually exclusive - that's to say you can always use only ONE of them. This
is why they sometimes are called RADIO BUTTONS too; thinking of those frequency
or station selector keys on old radios: Once you push one of them in a lowered position,
the one that was previously selected pops up back. A typical example for radio buttons
would be the image on the right.
Checkboxes on the other hand
are used to refer to options that can be combined together, each enabling or disabling
a single option like in this image.
What makes some guys funny is the fact that they absolutely don't care about self-contained logic in behaviour of controls and user guidance. Of course you are free to use all the means that you are provided with by such components. You can make a radio button group out of pushbuttons or checkboxes if you want to make life a little more complicated, but some things are better left undone like...
left: Obviously the programmer prefers to do strict input validation instead of simply using the means of the appropriate GUI to control the input process. Using two radio buttons with one being enabled by default would have saved 'code work'...
Right: This is one of my favorite 'idiot' GUIs. To fully understand how ridiculous it is, I even took the time to create a little animated sequence...
They are quite uncomplicated to work with - the Default option in the Style dialog of a pushbutton control will make the button 'capture' the ENTER key on a dialog, even if the push button is not the currently active control. No pointer focus will disable drawing of the 'focus' square around the buttons text when it is clicked upon with the mouse. However tabbing onto the pushbutton will still show the focus square:
No border will make the pushbutton appear as almost a textbox. The 'shifting' effect of the text upon clicking the button however will remain.
Well believe it or not: There's a click event for a push button! ;) Besides, there's the INIT event we already know from our textbox sample and a DROP event which occurs, when another DrDialog object was dropped onto the pushbutton at run time. Drag-and-Drop require additional things and although DnD is very useful and quite easy to do with DrDialog, we'll take care of it later.
They're quite easy to use with DrDialog - actually a checkbox can have 3 states ( unchecked, checked and 'undetermined' which will show some gray shade) but in most cases, the 2 states of checked/unchecked will do. If you want to make use of the third state in your program, invoke the Style dialog of the checkbox and watch for the 3state option. Anyway, you don't need to take a lot care of the processing behind it, if you're using its default Auto option enabled in the Style dialog.
This will automatically make the box state shift from one to the other (or next) each time the checkbox is clicked. If you deselect Auto , you'll have to provide the logic on your own by using the click event that is triggered each time the checkbox is clicked. Notice that you can't manually set the undetermined state for a box, if you disabled 3state - instead the box will be simply checked. The text that is displayed on the right of the checkbox is specified by the TEXT entry of its context menu or by calling the TEXT() function/statement in the same way we did for our textbox last month.
Like all types of controls, the CHECKBOXES generate a DROP
event, if they were enabled to do so and if some other control was dragged to and
dropped on them. We'll discuss Drag-and-Drop stuff later in this series, so there's
two events left: INIT (we know that, don't we?) and CLICK. The
CLICK event might be useful to visually clarify dependencies of options
in somewhat more complex situations - I'll try to show that with an animated GIF:
For both getting and setting the state of a checkbox, you have to use the
method/property. For reading the state of the checkbox you simply use
in the 'function' notation:
and it will return 0 (zero) if the box is unchecked,
1 (one) if it is checked or 2 if it is in 'undetermined' state (only available if
was enabled in the box Style dialog). If you want to immediately process
the state - e.g. by doing something special if it is checked - you might do this
by specifying an IF statement like:
If you need to set the checkbox state according to
e.g. saved options in an .INI file, you simply use
with its CALL-type notation:
...which checks the box. According to the states returned by the function call, you use 0 (zero) instead of 1 (one) to uncheck the box or 2 if you want to make it appear in undetermined state (again, this is only available if you enabled '3state' in the checkbox Style dialog). And that's it about checkboxes. Of course, there's a lot of stuff left like hiding the control through the VISIBLE function or enabling/disabling it by using ENABLE/DISABLE or the ENABLED function, but this applies to all types of window controls and thus will be discussed later.
If you happen to have two independent groups of radio buttons in sequence, DrDialog will recognise them as one group, as long as you don't tell him to treat them otherwise. This can be accomplished by either:
It's the same as with the checkboxes.
. Period. The 'states' are even more simple than with checkboxes, as
radio buttons don't come with an 'undetermined' state. Fortunately - I wonder what
funny guys might be capable of creating with that... So to determine the state of
an option button, you need to specify
Setting a specific radio button to
will automatically uncheck all other radio buttons within the same group.
Appears to be logical. But setting a specific button to
checked will NOT automatically check any other button in that group - this
might seem to be logical as well, because how is DrDialog supposed to know which
one to check instead... but even if the group is only made of two radio buttons,
this will not automatically check the other one... okay: To set the sate of a radio
like with the checkbox stuff (the same for the states: 0 = unchecked,
1 = checked):
This would check myRadio1 and automatically uncheck all other radio buttons that are grouped together with myRadio1. Actually, most of the time you don't care about the state of a radio button but you preferably want to know WHICH ONE of a group is checked, right? Well, there is no built-in property or function in DrDialog for this, but you can accomplish it by reacting to the click events of the group radio buttons and memorize the current button into a variable. The variables contents always reflects the radio button that was just checked. Another smart way is making use of the focus upon click events, but we'll talk about that at some later time...
Well, we already know something about the textbox,
don't we? By looking at the textbox
dialog window, one might not think of any special clues regarding text
alignment, but... there is something to mention about the
option in conjunction with the text alignment. If you happen to create
a textbox with multiple lines of text, you MUST use a vertical alignment set to
or it won't work, because the text will be put on a single line. Take a look
at the screenshots to figure out what I'm talking about:
This is okay - text appears entirely in textbox /
Text is written on a single line due to 'Vertical alignment' setting...
The horizontal alignment option will not interfere with word break and works just as expected.
To make a control appear in 'disabled' state, you actually only need to activate its Style option Disabled , which will make the control not respond to user interaction anymore and make it appear in a shaded/faded manner. However you're free to simply ignore all concepts of GUI ;) and select the Halftone option, which will make the textboxes text ALWAYS display in a 'disabled' style. This might be useful if you want to show a 'disabled' state but still want the control to be enabled to react upon user interaction. For a textbox however, there is only one event it reacts to: Dropping an object. I'm not very fond of creating such program behaviour (dropping an object on a disabled control to do something), but you might not share my ethical point of view in GUI basics. Or maybe you'll get along a programming task where you just want exactly this to be feasible, so it's good to know that this effects are available at least.
Contrary to similar controls in other development tools, the text box in DrDialog does not trigger a click event... anyway: Why should it? What should be the sense in clicking on a written text? Okay, one might simulate an URL link by having an underlined text that will fire up the webbrowser if it's clicked onto, but this can surely be accomplished in other ways. So there's just the INIT event left (we already know) and the DROP event we'll discuss later.
Margin simply specifies whether a border is drawn around the entry field. It has nothing to do with text margins one might know from text processing - this is why I think it was a bad idea to name it 'margin' instead of 'border', but now you know what it's about, right?
Unreadable will make asterisks appear instead of the characters you typed in your entry field. This is the 'password' effect, if you want to put it that way. But that's not all it does: Did you ever try to cut a 'password' entry fields content and paste it into - let's say - an editor? It won't work. Fortunately. I know of some 'hand-written' dialogs that didn't take care of this possibility, thus providing only a ridiculous kind of security effect... fortunately DrDialog (as all other development tools I know) takes care of this effect and disables text cut or copy from within an entry field that was set 'unreadable'. On the other hand, it still enables you to paste text into the field.
do interact in a certain way:
specifies the maximum length of an input. If you set it to 5 for example,
users can't type anything longer than 5 characters (space characters included).
is specified too, it'll make the next control become active automatically,
once the maximum amount of input was reached. Example:
You're having an automatic order form. There's a 5-digit part number, a quantity and a unit price to be entered. Setting the auto tab option for the part number entry field will result in the user not being forced to press the TAB key or click the mouse to change from the part number entry field to the quantity entry field. Typing the fifth digit in the part number entry field will make the cursor move automatically into the next field (determined by the controls ID). A very useful function for data input forms!
DBCS means 'double byte character set' and refers to codepages that require two bytes to store a single character, like Chinese for example. SBCS (single byte character set) is what we're used to; it requires only 1 Byte to hold a character. The SBCS/DBCS options will be discussed later - if ever I find someone that explains me what these options are about - sorry.
Besides the INIT and DROP event, there are some interesting events that can be used with entry fields: OVERFLOW for example is triggered at each time that the user pressed a key and made the entry field contents being longer than allowed by the 'Max length' option we've just discussed above.
Changed is triggered on every change of the entry field's TEXT content. It won't be triggered if you simply select a range of characters for example.
Scrolling is quite funny: This event is triggered each time that the contents of the entry field needed to be scrolled to enable the user see what he's typing once the text becomes larger than the entry fields display size. What makes it funny is the fact, that scrolling is not by one character each time continuously but rather in chunks of three characters at once. I have no idea at the moment, why this could be useful to know. Hm.
GetFocus and LoseFocus require a little explanation first: The focus is a kind of system internal identifier that tells what CONTROL is currently the active one and thus 'attracts' the user interaction. If you ever noticed a small dotted line around a Pushbuttons text... that's indicating that the Pushbutton is the active control ("has the focus"). Now, the GetFocus event occurs if your entry field has just become the active control, that's to say the user either pressed the tab key to move into the entry field or pointed the mouse on it and clicked. This event is often used to entirely select the entry fields contents (a single keystroke will then delete the contents before new text is typed). The LoseFocus event on the other hand occurs as soon as your entry field is no longer the active control because the user clicked somewhere else or because he tabbed to the next field. Usage of this event mostly consists of data input validation (e.g. format check for dates).
Using the entry field by code is nothing extremely complicated, as you'll only
use the text()function most of the time. The
function can be used to control the
setting at run time, because changing the according bits of the Style
by using the
function at run time would be a little tedious. Setting
to 12 at run time for example is done by:
There is however one more function to be mentioned:
Select. It deals with the selected part of the entry fields content. Users can use
the shift key in combination with either mouse or cursor movements to select all
or parts of the text in an entry field. And this is where
comes in - it enables the programmer to detect what parts are selected
by returning two values: The index of the first selected character (1 equals first
character in string) and the total number of selected characters.
If for example the user selected 'ell' from the text 'Hello' the SELECT function would return '2 3' (without the quotes).
Again, you can use
in its CALL type notation to SET the selected area. All you need to
supply is starting character number and length. If you supply a zero for the length
parameter, it'll unselect the entire text. To select the entire contents of an entry
field named 'myEntryField', just issue
Notice that a comma is needed to separate both parameters
while NO comma is contained in the return value of
(when used to GET the selected area). The '0,9999' parameter
is taken from DrDialog's online help. Yep, it works, but ACTUALLY one should use
another call to select the entire contents, making use of rexx
This uses the actual length of the text contained in myEntryField as the length parameter for the SELECT function and will always start at the first position and select the entire contents based on their actual current length.
MLE's are actually entry fields that contain special additions to handle line breaks and multiple lines of text.
Notice that some combinations don't quite make sense: Activating word wrap will make sure that text is wrapped to fit to the MLEs width - no need for a horizontal scroll bar in this case. ;) Anyway - there's more important things to mention on MLE's: The option ignore tab . The MLE is capable of inserting tab characters if pressed by the user for text formatting purposes. Activation of this option will make the MLE ignore the 'formatting' function and pass the tab key on to the window system which then uses it to enable the next control (change the 'focus').
As you don't have a max length option for MLEs, there's no OVERFLOW event as well. And because MLEs are made to take up multiple lines of text and usually bring their scroll bars with them, there's no need for a SCROLL event either. All that's left is the usual INIT, CHANGED, GetFocus, LoseFocus and DROP . Regarding functions, it's the same as with entry fields, except for RANGE and SELECT : They do not exist for MLEs.
On of the most important aspects of list boxes to
be taken into consideration at design time is the
: By default, you can only select one entry of the list. Specifying
will enable the user to select more than one entry by either mouse-clicking
on the entries or pressing the space bar to select / unselect them. The
selection enables the selection of a whole range of entries by using the
shift key (or single entries without losing the previous selection via the ctrl
key). Note that both options can be used either on their own or altogether.
Owner draw will turn off DrDialog's standard display mode for adding entries, leaving an empty list even if entries were added. I think this is intended to be used for special purposes available with other development tools or programming languages, as DrDialog can also be used to 'only' generate Dialog definitions which then can be used from other development tools. Let's not take care of this option... ;)
A horizontal scroll bar is quite useful in case that the free space on your dialog still doesn't allow you to size your listbox sufficiently to display the full length of its entries. Vertical scroll bars are always provided by the system and will be enabled as soon as there are more entries in your list than can be displayed at once.
, there are three events that can be used with list boxes:
- SCROLL is triggered when the list box was scrolled horizontally (!) not, if it was scrolled vertically.
- SELECT takes place each time the user (un)selected an entry that was in a different state before and
- ENTER occurs if the user double-clicked an entry or pressed the ENTER key.
On the function side, there is ADD and DELETE that can be used to control the listbox contents. Note that there is no automatic sorting of the entries when they are added to the box. You have to specifically use the appropriate parameters if you want a sorted list. If no sort parameter was specified, the Items are stuffed into the listbox in the same order as they were added.
|No sort:||call myList.ADD newitem|
|Ascending:||call myList.ADD newitem, 'A'|
|or:||call myList.ADD newitem, 'Ascending'|
|Descending:||call myList.ADD newitem, 'D'|
|or:||call myList.ADD newitem, 'Descending'|
In addition, items can be insert into the list in
a particular position, (e.g. 17th place) regardless of sort order if any by specifying
the desired index:
Or, to make sure the newly added entry is the last in the list:
|call myList.ADD newitem, 'L'|
|or||call myList.ADD newitem, 'LAST'|
function/call can be used to SET or GET the selection state of a single
item in the list. This looks quite difficult at first sight when taking into consideration
a list that uses multiple selection, but it's quite simple. In a single selection
type listbox, there is only one item that can be selected so we just need to issue
and that's it. Either
holds the list index of the selected item (1 being the first entry and
so on) or it holds 0 (zero) if there ain't. In multiple selection list boxes, this
function will return the index of the FIRST selected entry (if any). To get to know
the other selected entries, you actually loop through all entries of the list starting
with the one that follows the FIRST selected.
which will again search for the first selected entry
(if any) but STARTING with the entry following the last returned one. The advantage
of this way is that it speeds up processing compared to do a manual check of each
entry 1 by 1 and that it works regardless whether you know the total amount of entries.
To select a certain entry, you simply
to select the
th entry of the list. Okay, but talking about the amount of entries... how
do we get to know it? By using the listbox
It can be used to either query information about a single item or - if no parameters are passed - to return the number of items in the list:
If you need to know the actual value of the first
selected entry of a list, you just combine
in the following way:
or in a single line:
A very useful addition to list boxes is the VALUE that can be stored together with the actual list entry. Just imagine this to work like a second, invisible column that holds additional data. When adding an item, you can do a subsequent call that will provide the additional data to be stored along with the entry. This is useful for doing cross-references of indexes for example. We'll check this feature in a later series, let me just tell you that the VALUE is retrieved with the ITEM function as well - by specifying an additional parameter.
Well, those combo boxes are nothing more than a combination of an entry field along with a list box, but... that's not really all of course. an open list or to contain a droppable list - at run time you'll simply notice an entry field with a 'drop down button' attached to it. While sizing the 'open list' type at design time will actually make it look the same at run time, this is different for the 'droppable' list types: At run time, they initially appear as an entry field, but the amount of list that will be dropped by clicking the button is determined from the size you gave the drop down list at design time. Okay? Well, just take a look here:
If you take a look at the combo box Style dialog, you'll notice three different types: Simple, Dropdown and Drop down list . But what is actually the difference between the types? Behaviour of course. Simple Combobox is an entry field with a list attached to it. You can either type whatever you want or use the text to preposition the list index for further scrolling by arrow keys. This type is mostly used in cases where a user is given the choice between selecting from a list or typing his own choice. This is the same with the second type Drop down , except that it appears as an entry field that can be 'expanded' upon user request which initially saves space on your dialog screen. Still the user is allowed to type in or select.
In some cases however you might want to make sure that the user is ONLY allowed to chose from a preconfigured selection. This is where the the third type comes in: Drop down list . Although it appears just to be an entry field initially, you're not allowed to type in anything. Instead, the list is searched for the first entry whose starting character matches the one you just typed and it will be copied into the entry field part. Okay, now don't ask me what to do if you want a 'select only' combo that contains an open list part... because the answer will be: That's a listbox, man! ;)
We won't talk about the horizontal scroll bar again - it's just the same as with list boxes.
While ENTER and SELECT are the same as with the listbox and INIT and DROP are the same as with all others, there's the CHANGED event we already know from the entry field. Like the entry fields SCROLL event, the combo box provides us with a SCROLLENTRY event which equally occurs when the entry field was scrolled horizontally. The reason for the different naming is, that there is another scroll event that occurs for a combo box: SCROLLLIST takes place whenever the list part of the combo box was scrolled. Finally, there's the SHOWLIST event, that is triggered upon the users request for dropping the list (when the button was clicked). Notice that the SHOWLIST event only takes place when the list is about to be shown and not, when the button might have been clicked again to make the list disappear.
Nothing exciting can be said about the FUNCTIONs available for a combo box, as
they are all the same as with list boxes and entry fields, except for the fact that
there is no
function for the entry field part. There indeed is a
function, but it deals with the list part entries, just like in the
control. On the other hand here is something that works although it's not
mentioned in the online help: As I said, a combo box is made of an entry field and
a list box. But although the
function is missing from the combo box manual, it works. ;) That's good to
know: You might setup a 'selection only' list, then issue
and you're done with a fine GUI effect: Referencing the selected item of your list will return zero as long as the user did not select an entry. and once he did, he won't be able to reselect the headline entry '- select your...' because it has vanished since. Now, that's smart programming, isn't it? ;)
Nothing much to say except for what we were talking about in the radio button part: It can be used to group controls. At design time, dragging a group frame will move all contained controls accordingly. Setting position and size of the same group frame at run time will only apply to the frame, not its contents. Halftone seems to not work - at least on my system, but selecting DISABLED of course will do... except for the fact that it only disables the group frame, not the controls contained in it - I just mentioned it for you to be aware, okay? ;)
Well, INIT and DROP again of course, but ShowMenu is quite new to us: This is triggered if the user requested a context-sensitive menu (by clicking mouse button 2). Most of the time this event is used in conjunction with the MenuPopup command of DrDialog which can be used to display self-defined context menus at run time. Again, I'll disappoint you by telling that we'll deal with menus later in the series...
Nothing magic: Used to enable or disable controls at run time like in
No to be messed up with ENABLE , because this one is able to also GET only the current state of a control, whereas the above ones are only capable of SETting the state. To know the state of a control, use
The return codes are 1 (enabled) or 0 (zero; disabled).
They are used to set the state in the same way - to disable a control using
makes a control being shown or hidden at run time and can be equally used to only check if a control is visible or not. Quite useful if you're limited in space at run time - just hide the parts you don't need and show the ones you require. But don't abuse this too much ;) The visibility states are quite simple to memorize: 0=invisible, 1=visible To hide a listbox at run time for example, use
And to query if a listbox is visible use
When dealing with fancy effects like having buttons embedded into a picture or having multiple pictures arranged together with other controls, they sometimes are not having the right "z-order", making one control being shown 'above' another in the wrong order. To correct the "z-order" (derived from the three- dimensional scale model x-axis, y-axis and (3rd dimension:) z-axis) you can tell each control to become either the topmost or the bottommost one in that order. This is done by calling
Yeah, those ones are cute: One might simply think that they're just used to show or hide controls at run time, just like VISIBLE does, but although this is true, there's a lot more under the hood! HIDE can be used in a special way to 'turn off' the update process of a window. This is extremely useful when you want to fill a list with a lot of entries that require sorting as well. Each ADD will result in an update process of the list window, making it flash and bump its entries. If you turn off the list update, add the entries, then turn back the update on, you'll notice significant increase in speed.
Note, that the control itself will not be hidden, but only the display updates are suppressed. To make control being invisible, just like the VISIBLE function does, use
In order to simply make it suppress screen updates
instead (or use
to shorten it). For reactivation of the screen updates or to
the control again, use
Actually this months part was meant to be rather short, but there's so much
to discover, to tell, explain and take care of... and I even just mentioned the
most common ones. Well, time to leave again, folks.
See you next month!
[Previous Page] [Newsletter Index] [Next Page]
VOICE Home Page: http://www.os2voice.org