Return to the Index

Friendlier interaction

Everyday OPL programs can use the same graphical interface seen throughout the Series 3a:

Menu keywords begin with an "M", and dialog keywords with a "D". In this manual a lower case is used for these letters for example, "mINIT" and "dEDIT" but you can type them using upper or lower case letters.


Menus provide a simple way for any reasonably complex OPL program to let you choose from its various options.

To display menus in OPL takes three steps:

You use the displayed menus like any others on the Series 3a. Use the arrow keys to move around the menus. Press Enter (or an option's hot-key) to select an option, or press Esc to cancel the menus without making a choice. In either case, the menus are removed, the screen redrawn as it was, and MENU returns a value to indicate the selection made.

Defining the menus

The first argument to mCARD is the name of the menu. This will appear at the top of the menu; the names of all of the menus form a bar across the top of the screen.

From one to eight options on the menu may be defined, each specified by two arguments. The first is the option name, and the second the keycode for a hot-key. This specifies a key which, when pressed together with the Psion key, should select the option. (Your program must still handle hot-keys which are pressed without using the menu.) It is easiest to specify the hot-key with "%" eg "%a" gives the value for "a".

If an upper case character is used for the hot-key keycode, the Shift key must be pressed as well to select the option. If you supply a keycode for a lower case character, the option is selected only without the Shift key pressed. Both upper and lower case keycodes for the same character can be used in the same menu (or set of menus). This feature may be used to increase the total number of hot-keys available, and is also commonly used for related menu options eg. "%z" might be used for zooming to a larger font and "%Z" for zooming to a smaller font (as in the built-in applications).

For example, "mCARD "Comms","Setup",%s,"Transfer",%t"
defines a menu with the title "Comms". When you move to this menu using " LEFT RIGHT ", you'll see it has the two options "Setup" and "Transfer", with hot-keys Psion-S and Psion-T respectively (and no Shift key required). On the other hand,
"mCARD "Comms","Setup",%S,"Transfer",%T"
would give these options the hot-keys Shift-Psion-S and Shift-Psion-T.

The options on a large menu may be divided into logical groups (as seen in many of the menus for the built-in applications) by displaying a grey line under the final option in a group. To do this, you must pass the negative value corresponding to the hot-key keycode for the final option in the group. For example, "-%A" specifies hot-key Shift-Psion-A and displays a grey line under the associated option in the menu.

Each subsequent mCARD defines the next menu to the right. A large OPL application might use mCARD like this:

mCARD "File","New",%n,"Open",%o,"Save",%s
mCARD "Edit","Copy",%c,"Insert",-%i,"Eval",%e
mCARD "Search","First",%f,"Next",%g,"Previous",%p

Displaying the menus

The MENU function displays the menus defined by mINIT and mCARD, and waits for you to select an option. It returns the hot-key keycode of the option selected, in the case supplied by you, whether you used Enter or the hot-key itself to select it. If you supplied a negative hot-key keycode for an underlined option, it is converted to its positive equivalent.

If you cancel the menus by pressing Esc, MENU returns 0.

When a set of menus is displayed, the cursor is positioned to the menu and option that the user selected previously (or, if no menus have previously been displayed, to the first option in the first menu).

This works only if your program has only one set of menus. If you have another set of menus, the cursor is still set to the position of the menu and option selected in the first set of menus (if that position exists in the new menus). To get around this, use "m%=menu(init%)" and set "init%" to zero the first time a set of menus is displayed. The cursor will in this case be positioned to the first option in the first menu. "init%" is set to a value which specifies the menu and option selected, and should be passed to MENU the next time that same set of menus is called If your program has more than one set of menus, you should have a different "init%" variable for each set of menus.

Problems with menus

When choosing hot-keys, do not use those such as the number keys which produce different characters when used with the Psion key. Unless you have a good reason not to, stick with "a" to "z" and "A" to "Z".

You must ensure that you do not use the same hot-key twice when defining the menus, as OPL does not check for this.

Each menu definition uses some memory, so `No system memory' errors are possible.

Don't forget to use mINIT before you begin defining the menus.

If the menu titles defined by mCARD are too wide in total to fit on the screen, MENU will raise an error.

A menu example

This procedure allows you to press the Menu key and see a menu. You might instead be typing a number or some text into the program, or moving around in some way with the arrow keys, and this procedure returns any such keypresses. You could use this procedure instead of a simple GET whenever you want to allow a menu to be shown, and its hot-keys to work.

Each option in the menus has a corresponding procedure named "proc" plus the hot-key letter so for example, the option with hot-key Psion-N is handled by the procedure "procn".

This procedure uses the technique of calling procedures by strings, as described in the `Advanced Topics' chapter.


Note: this procedure allows you to press a hot-key with or without the Shift key. So Shift-Psion-N would be treated the same as Psion-N.

Neither LOC nor the "@" operator (for calling procedures by strings) differentiate between upper and lower case. If you have Shifted hot-keys you will need to compare against two sets of hot-key lists. For example, with hot-keys "%A", "%C", "%a" and "%d", you would have upper/lowercase hot-key lists like "hu$="AC"" and "hl$="ad"", and the "MENU CHECK" section becomes:

IF k%<=%Z     REM if upper case hot-key
  IF LOC(hu$,CHR$(k%))
    "@"(a$): REM procua:, procuc:, ...
ELSE          REM else lower case hot-key
  IF LOC(hl$,CHR$(k%))
    "@"(a$): REM procla:, procld:, ...
(This calls procedures "procua:", "procuc:", "procla:" and "procld:"). If a hot-key was pressed directly you cannot tell from "k%" whether Shift was used; so make the same change to the "DIRECT HOT-KEY CHECK" section, but use "IF KMOD AND 2" instead of "IF k%<=%Z".


In OPL, dialogs are constructed in a similar way to menus:

Use " UP DOWN " to move from line to line, and enter the relevant information, as in any other Series 3a dialog. You can even press Tab to produce vertical lists of options.

Each of the commands like dEDIT and dDATE specifies a variable to take the information you type in. If you press Enter to complete the dialog, the information is saved in those variables. The dialog is then removed, and the screen redrawn as it was.

You can press Esc to abandon the dialog without making any changes to the variables.

If you enter information which is not valid for the particular line of the dialog, you will be asked to re-enter different information.

Here is simple example. It assumes a global variable "name$" exists:

This procedure displays a dialog with "Who are you?" as its top-line title, and an edit box for typing in your name. If you end by pressing Enter, the name you have typed will be saved in "name$"; if you press Esc, "name$" is not changed.

When the dialog is first displayed, the existing contents of "name$" are used as the string to edit.

Note that the dialog is automatically created with a width suitable for the item(s) you defined, and is centred in the screen.

Lines you can use in dialogs

This section describes the various commands that can define a line of a dialog. In all cases:

Where appropriate, this variable provides the initial value shown in the dialog.

Although examples are given using each group of commands, you can mix commands of any type to make up your dialog.

More details of the commands may be found in the alphabetic listing.

Strings, secret strings and filenames

dEDIT var str$,prompt$,len%
defines a string edit box.

len% is an optional argument. If supplied, it gives the width of the edit box (allowing for the widest possible character in the font). The string will scroll inside the edit box, if necessary. If "len%" is not supplied, the edit box is made wide enough for the maximum width "str$" could be. (You may wish to set a suitably small "len%" to stop some dialogs being drawn all the way across the screen)

dXINPUT var str$,prompt$
defines a secret string edit box, such as for a password. A special symbol will be displayed for each character you type, to preserve the secrecy of the string.

dFILE var str$,prompt$,f%
defines a filename edit box.

Here is an example dialog using these three commands:

This returns `True' if Enter was used, indicating that the GLOBAL variables "n$", "pw$" and "f$" have been updated.

dFILE automatically has a `Disk' selector on the line below it. The third argument to dFILE controls the type of file editor you see, and the kind of input allowed. See the `Alphabetic listing' for more details of dFILE.

Choosing one of a list

dCHOICE var choice%,prompt$,list$
defines a choice list. "list$" should contain the possible choices, separated by commas for example, ""Yes,No"". The "choice%" variable specifies which choice should initially be shown 1 for the first choice, 2 for the second, and so on.

For example, here is a simple "choice" dialog:

Numbers, dates and times

dLONG var long&,prompt$,min&,max&
dFLOAT var fp,prompt$,min,max
define edit boxes for long integers and floating-point numbers respectively. Use dFLOAT to allow fractions, and dLONG to disallow them. "min(&)" and "max(&)" give the minimum and maximum values which are to be allowed. There is no separate command for ordinary integers use dLONG with suitable "min&" and "max&" values.

dDATE var long&,prompt$,min&,max&
dTIME var long&,prompt$,type%,min&,max&
define edit boxes for dates and times. "min&" and "max&" give the minimum and maximum values which are to be allowed.

For dDATE, "long&", "min&" and "max&" are specified in "days since 1/1/1900". The DAYS function is useful for converting to "days since 1/1/1900".

For dTIME, "long&", "min&" and "max&" are in "seconds since 00:00". The DATETOSECS and SECSTODATE functions are useful for converting to and from "seconds since midnight" (they actually use "seconds since 00:00 on 1/1/1970").

dTIME also has a "type%" argument. This specifies the type of display required:

"type%"		time display
0		absolute time without seconds
1		absolute time with seconds
2		duration without seconds
3		duration with seconds
For example, "03:45" is an absolute time while 3 hours 45 minutes is a duration.

This procedure creates a dialog, using these commands:

The secs&: procedure uses the HOUR and MINUTE functions, which return the time as kept by the Series 3a. It is called twice to guard against an incorrect result, in the (albeit rare) case where the time ticks past the hour between calling HOUR and calling MINUTE.

The INT function is used in secs&: to force OPL to use long integer arithmetic, avoiding the danger of an `Integer overflow' error.

d& and t& are set up to give the current date and time when the dialog is first displayed. The value in "d&" is also used as the minimum value for dDATE, so that in this example you cannot set a date before the current date.

DATETOSECS is used to give the number of seconds representing the time 23:59. The first three arguments, "1970", "1" and "1", represent the first day from which DATETOSECS begins calculating.

Results from dDATE

dDATE returns a value as a number of days. To convert this to a date:

If you do use this procedure, be careful to type it exactly as shown here.

Displaying text

dTEXT prompt$,body$,type%
defines "prompt$" to be displayed on the left side of the line, and "body$" on the right. There is no variable associated with dTEXT. If you use a null string ("""") for "prompt$", "body$" is displayed across the whole width of the dialog.

"type%" is an optional argument. If specified, it controls the alignment of "body$":

"type%"		effect
0		left align "body$"
1		right align "body$"
2		centre "body$"
In addition, you can add any or all of the following three values to "type%", for these effects:
"type%"		effect
$100		use bold text for "body$".
$200		draw a line below this item.
$400		make this line selectable. (It will also be
		bulleted if "prompt$" is not """".)
dTEXT is not just for displaying information. Since DIALOG returns a number indicating the line you were on when you pressed Enter (or 0 if you pressed Esc), you can use dTEXT to offer a choice of options, rather like a menu:

In each case "type%" is $402 ($400+2). The "$400" makes each text string selectable, allowing you to move the cursor onto it, while "2" makes each string centred.

Displaying exit keys

Most dialogs are completed by pressing Enter to confirm the information typed, or Esc to cancel the dialog. These keys are not usually displayed as part of the dialog.

However, some Series 3a dialogs offer you a simple choice, by showing pictures of the keys you can press. A simple "Are you sure?" dialog might, for example, show the two keys `Y' and `N', and indicate the one you press.

If you want to display a message and offer Enter, Esc and/or Space as the exit keys, you can display the entire dialog with the ALERT function.

If you want to use other keys, such as "Y" and "N", or display the keys below other dialog items such as dEDIT, create the dialog as normal and use the dBUTTONS command to define the keys.

ALERT and dBUTTONS are explained in detail in the alphabetic listing.

Other dialog information

Positioning dialogs

If a dialog overwrites important information on the screen, you can position it with the dPOSITION command. Use dPOSITION at any time between dINIT and DIALOG.

dPOSITION uses two integer values. The first specifies the horizontal position, and the second, the vertical. "dPOSITION -1,-1" positions to the top left of the screen; "dPOSITION 1,1" to the bottom right; "dPOSITION 0,0" to the centre, the usual position for dialogs.

"dPOSITION 1,0", for example, positions to the right-hand edge of the screen, and centres the dialog half way up the screen.

Restrictions on dialogs

The following general restrictions apply to all dialogs:

Giving information

Status window temporary and permanent

Pressing Psion-Menu when an OPL program is running will always display a temporary status window. This status window is in front of all the OPL windows, so your program can't write over it.

Use "STATUSWIN ON" or "STATUSWIN ON,type%" to display a permanent status window. It will be displayed until you use "STATUSWIN OFF". "type%" specifies the status window type. The small status window is displayed for "type%=1" and the large status window either when "type%" is not supplied or when "type%=2".

You might use "STATUSWIN ON" when Control-Menu is pressed, for consistency with the rest of the Series 3a.

The status window is displayed on the right-hand side of the screen.

The rank of the status window

Important: The permanent status window is behind all other OPL windows. In order to see it, you must use either FONT or both SCREEN and gSETWIN, to reduce the size of the text window and the default graphics window. You should ensure that your program does not create windows over the top of it.

FONT automatically resizes these windows to the maximum size excluding any status window. It should be called after creating the status window because the new size of the text and graphics windows depends on the size of the status window. Note that "FONT -$3fff,0" leaves the current font and style it just changes the window sizes and clears them.

If you use SCREEN and gSETWIN instead of FONT, you should use the STATWININFO keyword (described next) to find out the size of the status window.

Finding the position and size of a status window

sets the four element array extent%() as follows:
extent%(1) = pixels from left of screen to status window
extent%(2) = pixels from top of screen to status window
extent%(3) = status window width in pixels
extent%(4) = status window height in pixels for status window type%

"type%=3" specifies the compatibility mode status window and "type%=-1" specifies whichever type of status window is currently shown. Otherwise, use the same values of "type%" as for STATUSWIN.

STATWININFO returns the type of the current status window. The values are as for "type%", or zero if there is no current status window.

Note : If type%=-1 for the current status window and there is none, STATWININFO returns consistent information in "extent%()" corresponding to a status window of width zero and full screen height positioned one pixel to the right of the physical screen.
So to set a graphics window to have height "h%" and to use the full screen width up to the current status window (if any), but leaving a one pixel gap between the graphics window and the status window, you could use:
"STATWININFO(-1,extent%()) :gSETWIN 0,0,extent%(1),h%"

Alternatively you could simply use "FONT -$3fff,0" as described under STATUSWIN above, which also sets the height to full screen height and sets the text window size to fit inside it.

What the status window does

The status window always displays the OPL program name, a clock and, by default, an icon. This will be the default OPL icon, unless your program is an OPA with its own icon. (OPAs are described in the `Advanced topics' chapter.) In addition, the settings selected in the `Status window' menu option of the System screen are automatically used in OPL status windows. The status window will therefore also display all the indicators required, and a digital or analog clock as selected there.

The status window is inaccessible to, and does not affect, the OPL keywords gORDER and gRANK.

You can set or change the name displayed in the status window with SETNAME for example, "SETNAME "ABCD"" or "SETNAME a$".

Using a DIAMOND list in the status window

Your program may have several distinct modes/views/screens between which you would like the DIAMOND key to switch. The built-in applications use the DIAMOND key extensively Agenda uses it it to switch to the different views, while Word switches between `Normal' and `Outine' view.

The DIAMOND list the list of modes/views/screens which the DIAMOND key goes between is displayed in the status window.

OPL programs can set up a DIAMOND list. Use
"DIAMINIT pos%,str1$,str2$,..."
to initialise the list (discarding any existing list). The list can be initialised before or after a status window is displayed.

"str1$", "str2$" etc. contain the text to be displayed in the status window for each item in the list.

"pos%" is the initial item on to which the DIAMOND indicator should be positioned, with "pos%=1" specifying the first item. (Any value greater than the number of strings specifies the final item.)

If "pos%=0", or if DIAMINIT is used on its own with no arguments, no bar is defined.

If "pos%=-1" the list is replaced by the icon instead in the large status window.

If "pos%>=1" you must supply at least this many strings.

Defining a list uses some memory, so `No system memory' errors are possible.

"DIAMPOS pos%" positions the DIAMOND indicator in a list. You might move the indicator to the next item when the DIAMOND key is pressed and to the previous item when Shift-DIAMOND is pressed. The DIAMOND key has keycode value 292 and KMOD returns 2 when the Shift key is pressed.

Positioning outside the range of the items wraps around in the appropriate way if there are three items in the list, "DIAMPOS 4" positions to the first.

"DIAMPOS 0" causes the DIAMOND symbol to disappear.

Use "chr$(4)" to display a DIAMOND key in a menu. If you use it as a hot-key, a Shift will be added automatically.

Information messages

GIPRINT displays an information message for 2 seconds, in the bottom right corner of the screen. For example, "GIPRINT "Not Found"" displays "Not Found". The string you specify can be up to 63 characters. If a string is too long for the screen, it will be clipped.

You can add an integer argument to control the corner in which the message appears:

value		corner
0		top left
1		bottom left
2		top right
3		bottom right
For example, "GIPRINT "Who?",0" prints "Who?" in the top left corner.

Only one message can be shown at a time. You can make the message go away for example, if a key has been pressed with "GIPRINT """.

`Busy' messages

Messages which say a program is temporarily busy, or cannot respond for some reason, are by convention shown in the bottom left corner. The BUSY command lets you display your own messages of this sort. Use "BUSY OFF" to remove it.

"BUSY "Paused..."", for example, displays ""Paused..."" in the bottom left corner. This remains shown until "BUSY OFF" is used.

You can control the corner used in the same way as for GIPRINT. You can also add a third argument, to specify a delay time (in half seconds) before the message should be shown. Use this to prevent `busy' messages from continually appearing very briefly on the screen.

For example, "BUSY "Wait:",1,4" will display "Wait:" in the bottom left corner after a delay of 2 seconds. As soon as your program becomes responsive to the keyboard, it should use "BUSY OFF". If this occurs within two seconds of the original BUSY, no message is seen.

Only one message can be shown at a time. The string to display can be up to 19 characters long.

<== Return to the Index