OPL DIALOG ROUTINES FOR PSION S3A
Introduction
This code was originally written as part of my "Address Book" shareware application. I found that the Dialog facilities provided by OPL were too limited for what I wanted to do. In particular I wanted the ability to: 
•	perform dialogs asynchronously (i.e. handle key presses and other events outside the dialogs),;
•	to have more than eight dialog entries;
•	to have multi-line entries.
Structure
The file DIALG1V3.OPL contains 'interface' procedures, which are called from user's code. They all have the prefix DG, and their use is described below.
The file DIALG2V3.OPL contains other procedures which support the interface procedures.
In most cases the interface procedures return pointers to data structures, which are commonly used as parameters in subsequent procedure calls. The procedures usually return a 'null' pointer (i.e. 0) if an error prevents the function from being completed. The most likely error is insufficient memory.
A typical sequence for using the routines is:
•	Define the dialog;
•	Display the dialog
•	Input key presses;
•	Call dialog routines to handle key presses;
•	Read back values entered in dialog fields;
•	Clear the dialog.
Using the Routines
Defining a Dialog
First you must initialise a dialog with the following call:
dg%=DGinit%:(title$)
where:
title$	is the title for the dialog;
dg%	is the dialog handle (used in subsequent calls).
This is very similar to the OPL DGINIT function.
Next you define each field in the dialog. Currently only text fields are supported, which are added to the dialog by these calls:
field%=DGtext%:(dg%,label$,flags%)
dg%	is the dialog handle;
label%	is the label for the field;
flags%	is a bit mask defining the characteristics of the field, formed by ORing together the following values:
1	the field is 'selectable';
2	the field is modifiable;
4	the field is 'replaceable' (this is explained later under 'default text');
field%	is the field handle (used in subsequent calls).
This is very similar in function to DEDIT and DTEXT, except that no initial contents are defined at this stage. Use this call to specify the initial contents:
ret%=DGaddtx%:(field%,text$,def%)
where:
field%	is the field handle;
text$	is the text string;
def%	is a flag indicating this is 'default' text (see later, set to 0 for 'simple' operation);
ret%	is 0 for success, otherwise a negative error code.
You can call this repeatedly for the same field, to append further text. If a string is terminated by a CHR$(21) then subsequent text will always be displayed starting on a fresh line.
Displaying a Dialog
Once you have defined your dialog you can display it with the following call:
field%=DGshow%:(dg%,font%)
where:
dg%	is the dialog handle;
font%	is the font to be used (specified as for gFONT);
field%	is the handle of the first selectable field (or -1 if there are no selectable fields).
This is similar to DIALOG except that control is returned immediately to the calling program (rather than waiting for the dialog to be completed). You can DGshow% any number of times at any point in a dialog's existence; e.g. the dialog can be zoomed by supplying different values for font%.
Handling Key Presses
Input from the keyboard is handled by the calling program, e.g. by calling KEY, GETEVENT, etc. The program must then call an appropriate dialog routine to handle the key press.
For 'movement' keys (e.g. up, down, home, page up) call:
field%=DGmove%:(dg%,key%,mod%)
where:
dg%	is the dialog handle;
key%	is the key code;
mod%	is the modifier (i.e. as returned by KMOD);
field%	is the handle of the new 'current' field.
To insert a character at the current position call:
field%=DGinsrt%:(dg%,text$)
where:
dg%	is the dialog handle;
text$	is the character to be inserted;
field%	is the handle of the 'current' field.
If CHR$(21) is supplied as text$ then a new line is opened up in the dialog.
To perform a delete operation call:
field%=DGdelet%:(dg%,mod%,paste%)
where:
dg%	is the dialog handle;
mod%	is the modifier (i.e. as returned by KMOD);
paste%	is a pointer to a paste buffer;
field%	is the handle of the 'current' field.
If any text is highlighted then this is removed, and if paste% is not zero copied to the designated paste buffer (see 'Cutting and Pasting' later). Otherwise the character to the left of the current position is removed (or to the right if mod% indicates shift down, or characters to the end of line if mod% indicates psion-shift down).
Reading Back Values
When a dialog has been completed it is necessary to read back the values that have been entered in each field. First find out how many lines of text have been entered in a field by calling:
lines%=DGflns%:(field%)
where:
field%	is the field handle;
lines%	is the number of lines.
Then call the following routine to read back the first line:
ptext%=DGfstln%:(field%)
where:
field%	is the field handle;
ptext%	is a pointer to the text in the first line.
The actual text string can be obtained by calling:
PEEK$(ptext%)
Then call the following repeatedly to read back subsequent lines:
ptext%=DGnxtln%:(field%)
where:
field%	is the field handle;
ptext%	is a pointer to the text in the next line.
The following routine indicates if the field has been modified (by user input):
mod%=DGmodfd%:(field%)
where:
field%	is the field handle;
mod%	is non-zero if the field has been modified
The following routine indicates if any field in the dialog has been modified (by user input):
mod%=DGmoddg%:(dg%)
where:
dg%	is the dialog handle;
mod%	is non-zero if any field has been modified.
You can use the following call to find out the currently selected field:
field%=DGselct%:(dg%)
where:
dg%	is the dialog handle;
field%	is the handle of the 'current' field.

Clearing a Dialog
Once a dialog has been completed, call the following to clear the dialog from the screen, and free all associated memory allocations:
dg%=DGclear%:(dg%)
where:
dg%	is the dialog handle (always returned as zero);
Cutting and Pasting
Call the following routine to copy the highlighted text into a paste buffer:
lines%=DGcopy%:(dg%,paste%)
where:
dg%	is the dialog handle;
paste%	is the address of a paste buffer (an array of 3 integers);
lines%	is the number of lines of text copied (0 if no highlighted text to copy, -1 if the copy can't be completed, due to lack of memory etc.).
This text can be pasted back into the dialog at the current position with the following call:
field%=DGpaste%:(dg%,paste%)
where:
dg%	is the dialog handle;
paste%	is the address of the paste buffer;
field%	is the handle of the 'current' field.
A paste buffer which has been set up by a call to DGdelet% can be used in the same way.
Buttons
You can add buttons to a dialog by calling the following procedure during definition of the dialog, to define each button required:
button%=DGbuttn%:(dg%,key%,text$)
where:
dg%	is the dialog handle;
key%	is the key code to activate the button;
text$	is the text to display above the button;
button%	is the button handle.
You can use the following procedure to check if a key press matches the code specified for any button in the dialog:
button%=DGpshbt%:(dg%,key%)
where:
dg%	is the dialog handle;
key%	is the entered key code;
button%	is the handle of the matching button, or 0 if there is no matching button.
The selected button is shown 'depressed'. It can be returned to the 'released' state by calling:
button%=DGrelbt%:(dg%,key%)
where:
dg%	is the dialog handle;
key%	is the entered key code;
button%	is the handle of the matching button, or 0 if there is no matching button.
Default Text
An advanced feature is the ability to associate 'default' text with a field. This text is displayed in grey and cannot be modified, but it can be replaced with fresh text input by the user. If a field with default text is completely cleared by a call to DGdelet%, then the default text is displayed.
If a field has default text which is to be shown initially:
•	Call DGtext% with flags%=5 (replaceable but not modifiable);
•	Call DGaddtx%: for the default text with def%=-1;
If a field has default text, and also some different initial text:
•	Call DGtext% with flags%=7 (replaceable and modifiable);
•	Call DGaddtx%: for the initial text with def%=0;
•	Call DGaddtx%: for the default text with def%=-1;
On completion of a dialog you can find out if a field contains the default text, or text which has been entered by the user, by calling:
mod%=DGismod%:(field%)
where:
field%	is the field handle;
mod%	is non-zero if the field contains modifiable text (i.e. NOT the default text).
Other Functions
You can find out the current cursor position and bounds of highlighted text within a modifiable field by calling:
DGgetxy:(field%,pxy%)
where:
field%	is the field handle;
pxy%	is a pointer to an array of 4 integers.
After the call the array contains the following values (in order):
•	X position of cursor;
•	Y position of cursor;
•	X position of other bound of highlighted text (-1 if nothing highlighted);
•	Y position of other bound of highlighted text.
You can use the following procedure to modify the text in a field during dialog entry:
ret%=DGmodtx%:(field%,text$)
where:
field%	is the field handle;
text$	is the new text;
ret%	is non-zero if the call is successful.
Currently this can only be used on non-modifiable fields containing a single line of text.
Example
The file DIALOGEX.OPL contains a simple example program using some of the procedures. Note that no error handling is included; a real program should check the return values from each call. Also it is preferable to use GETEVENT to read key presses rather than KEY; this then enables the program to respond to system events as well.
Future Developments
This initial version was implemented to meet the requirements of my 'Address Book' program. However it has been structured so that it should be easy to add extra functionality. In particular:
•	Support for other field types (numeric, choice, date, filenames, etc.);
•	Support for more flexible dynamic changing of field characteristics and contents, e.g. making fields selectable/modifiable in response to user input in another other field.
The code as written is only suitable for the Psion Series 3A. However I believe it can be adapted for other platforms, e.g. Series 3. The changes required include:
•	Replacing UADD/USUB with +/-;
•	Replacing gXBORDER with gBORDER;
•	Eliminating use of grey plane.
I am currently in the process of replacing all the PEEK and POKE statements with OPP Macros. This should make the code more readable and intelligible!
Distribution and Use
This code may be freely distributed provided that:
•	No charge is made, other than to cover reasonable costs;
•	These notes are distributed with  it.
You are free to use this code for personal purposes. However if you find it useful I would ask you to make a donation to a charity I am closely involved with. Reading Steiner School Project (Registered Charity ???) provides education in the Reading area based on the principles of the Austrian philosopher and educationalist Dr Rudolf Steiner. Please send cheques (payable to RSSP) to:
Department ADBK
Alder Bridge School
Mill Lane,
Padworth,
Reading
England
RG7 4JU
If you incorporate this code into software which is distributed as shareware then I would ask you to donate a proportion of any proceeds to the above charity.
This code may not be used in any software which is sold commercially.
While I have taken reasonable care in the preparation of this code, I cannot take responsibility for any damage, loss, or injury resulting from its use. You are welcome to modify and adapt this code for your own purposes, but at your own risk!
I will be very pleased to receive comments queries and suggestions relating to this code. I can be contacted via:
CIX:	jdawson
Compuserve:	100415,370
E-mail:	john.w.dawson@baesema.co.uk
'Phone:	+44 1734 666185
Snail mail:	19, St Edward's Rd.
Reading
Berkshire
RG6 1NL
England
Note that my company is currently having problems with its mail system, so if you do not receive any response from messages sent to the above e-mail address, please try my CIX address as an alternative. Internet address is:
	jdawson@cix.compulink.co.uk
Please 'phone outside working hours if possible, and not after 11pm UK time!
JOHN W DAWSON
October 1995