RTR logo

BBC BASIC for Windows

Library Routines



Introduction to library modules

The INSTALL statement allows you to load a library module containing functions and procedures which can be called from within your program, without them appearing in the program listing. If you, or somebody else, has written a number of useful functions which you might want to call from several different programs, this provides a convenient way of packaging and distributing them.

Because the procedures and functions do not form part of your program the size of its file is reduced, and if a bug is found in one of the functions it can be corrected by updating the library file without having to modify each of the programs in which it is used.

You can build up your own library of such files, but BBC BASIC for Windows is supplied with the following modules:

These can be found in the LIB sub-directory (folder) in the main BBC BASIC directory.

Array and matrix functions

The ARRAYLIB library contains a set of procedures and functions for performing arithmetic and matrix operations on 1- and 2-dimensional arrays. These include adding two matrices together, multiplying two matrices, transposing a matrix and inverting a matrix.

The library should be loaded from your program using the command:

INSTALL @lib$+"ARRAYLIB"

The functions contained are:

In BBC BASIC for Windows version 3.00a and later several of these operations are incorporated within the interpreter. Using the built-in operations will be considerably faster than using the library routines; see the Array arithmetic section for details. The library routines which are not supported as built-in operations are PROC_transpose, PROC_invert and FN_det.

PROC_add(A(), B)

PROC_add adds a scalar value B to all the elements of 1D or 2D (numeric) array A(), and returns the result in A():
DIM N(3)
N() = 1, 2, 3, 4
PROC_add(N(), 5)
PRINT N(0),N(1),N(2),N(3)
When executed this program will print:
         6         7         8         9

PROC_mul(A(), B)

PROC_mul multiplies all the elements of 1D or 2D (numeric) array A() by the scalar value B, and returns the result in A():
DIM N(3)
N() = 1, 2, 3, 4
PROC_mul(N(), 2)
PRINT N(0),N(1),N(2),N(3)
When executed this program will print:
         2         4         6         8

PROC_sum(A(), B())

PROC_sum adds 1D or 2D (numeric) arrays A() and B() together, and returns the result in A(). A() and B() must have the same dimensions.
DIM N(3), S(3)
N() = 1, 2, 3, 4
S() = 5, 6, 7, 8
PROC_sum(N(), S())
PRINT N(0),N(1),N(2),N(3)
When executed this program will print:
         6         8        10        12

PROC_dot(A(), B(), C())

PROC_dot multiplies 2D matrices A() and B() together and returns the result in C(). The number of columns of A() must equal the number of rows of B(), the number of columns of C() must equal the number of columns of B() and the number of rows of C() must equal the number of rows of A().
DIM N(0,2), S(2,1), D(0,1)
N() = 1, 2, 3
S() = 4, 5, 6, 7, 8, 9
PROC_dot(N(), S(), D())
PRINT D(0,0) D(0,1)
When executed this program will print:
        40        46

PROC_transpose(A(), B())

PROC_transpose transposes 2D matrix A() and returns the result in B(). The number of columns of A() must equal the number of rows of B() and the number of rows of A() must equal the number of columns of B().
DIM N(1,2), T(2,1)
N() = 1, 2, 3, 4, 5, 6
PROC_transpose(N(), T())
PRINT T(0,0) T(0,1)
PRINT T(1,0) T(1,1)
PRINT T(2,0) T(2,1)
When executed this program will print:
         1         4
         2         5
         3         6

PROC_invert(A())

PROC_invert inverts square matrix A() and returns the result in A().
DIM M(2,2)
M() = 2,0,6,8,1,-4,0,5,7
PROC_invert(M())
PRINT M(0,0) M(0,1) M(0,2)
PRINT M(1,0) M(1,1) M(1,2)
PRINT M(2,0) M(2,1) M(2,2)
When executed this program will print:
   0.09184   0.10204  -0.02041
  -0.19048   0.04762   0.19048
   0.13605  -0.03401   0.00680

FN_mod(A())

FN_mod returns the modulus (the square-root of the sum of the squares of all the elements) of a 1D or 2D array.
DIM M(2,2)
M() = 2,0,6,8,1,-4,0,5,7
PRINT FN_mod(M())
When executed this program will print:
13.96424

FN_det(A())

FN_det returns the determinant of a square array.
DIM M(2,2)
M() = 2,0,6,8,1,-4,0,5,7
PRINT FN_det(M())
When executed this program will print:
294

Toolbars and status bars

The WINLIB library contains a set of procedures and functions for creating and controlling toolbars and status bars. These allow you to make your BBC BASIC for Windows programs look more like genuine Windows™ programs, with a proper Graphical User Interface.

The library should be loaded from your program using the command:

INSTALL @lib$+"WINLIB"

The functions contained are:

FN_createstatusbar(text$)

This function creates a status bar, and returns its window handle. A single string parameter must be supplied, which contains the text (if any) to be displayed by default in the status bar. The returned value can be used for manipulating the status bar using Windows™ API functions (see below).

The following program segment creates a status bar containing the string "Press F1 for Help":

hstat% = FN_createstatusbar("Press F1 for help")
Note that BASIC's output window is not automatically made smaller to accommodate the status bar. You should use the VDU 24 and/or VDU 28 commands to reduce the size of the graphics and/or text windows to suit.

If you want the status bar to resize itself automatically when the size of the main window changes (e.g. as the result of the user dragging a corner) you should forward the size-change message as follows:

ON MOVE SYS "PostMessage",hstat%,@msg%,@wparam%,@lparam% : RETURN
You should remove the status bar by calling PROC_removestatusbar before your program exits or returns to immediate mode. It is good practice to trap errors (using ON ERROR ) and to remove the status bar if an unexpected error occurs.

Once you have created a basic status bar, you can use Windows™ API functions to divide it into a number of parts and write different text in each part. The following program segment illustrates how to split the status bar into three parts; hstat% is the value returned from the FN_createstatusbar function.

nparts% = 3
DIM edges%(nparts%-1)
FOR N% = 0 TO nparts%-1
  READ edges%(N%)
NEXT N%
SYS "SendMessage", hstat%, 1028, nparts%, ^edges%(0)
FOR N% = 0 TO nparts%-1
  READ text$
  SYS "SendMessage", hstat%, 1025, N%, CHR$9+text$
NEXT N%
DATA 440, 540, 640
DATA Part 1, Part 2, Part 3
The first DATA statement determines the width of each part, by specifying the position of its right-hand edge (in pixels). The second DATA statement determines the text which will appear in each part; the CHR$9 character causes the text to be displayed centrally.

PROC_removestatusbar

This procedure removes a status bar previously created with FN_createstatusbar. You should always remove the status bar before your program exits or returns to immediate mode.

FN_createtoolbar(nbutts%,button%(),buttid%())

This function creates a toolbar, and returns its window handle. Three parameters must be supplied: the number of buttons in the toolbar, an integer array of button types and an integer array of button identifiers. The returned value can be used for manipulating the toolbar using Windows™ API functions (see below). To create a floating toolbar see the FN_createfloatingtoolbar function in the WINLIB3 library.

The following program segment creates a toolbar containing three buttons: a cut button, a copy button and a paste button:

nbutts% = 3
DIM button%(nbutts%-1), buttid%(nbutts%-1)
button%() = 0, 1, 2
buttid%() = 100, 101, 102
htool% = FN_createtoolbar(nbutts%,button%(),buttid%())
The button% array contains the button types and the buttid% array contains the button identifiers (which are used to identify the buttons, and to determine which button has been clicked). If the button identifier is zero this signifies a separator rather than a button.

In version 1.7 and later of the WINLIB library you can specify a negative identifier value. This creates an auto toggle button which alternates between the 'pressed' and 'unpressed' states when you click on it. In this case the actual identifier value allocated to the button is minus the value specified in the buttid% array.

The available button types are as follows:

Value   Button
0Cut
1Copy
2Paste
3Undo
4Redo
5Delete
6New
7Open
8Save
9Find
10Print Preview
11Help
12Find
13Replace
14Print
15no image
Note that BASIC's output window is not automatically made smaller to accommodate the toolbar. You should use the VDU 24 and/or VDU 28 commands to reduce the size of the graphics and/or text windows to suit.

If you want the toolbar to resize itself automatically when the size of the main window changes (e.g. as the result of the user dragging a corner) you should forward the size-change message as follows:

ON MOVE SYS "PostMessage",htool%,@msg%,@wparam%,@lparam% : RETURN
You should remove the toolbar by calling PROC_removetoolbar before your program exits or returns to immediate mode. It is good practice to trap errors (using ON ERROR ) and to remove the toolbar if an unexpected error occurs.

Once you have created a toolbar, clicking on the buttons will cause messages to be sent to your program which can be detected with ON SYS, in exactly the same way as a selection from a menu bar. The @wparam% value will be the button identifier you chose. Alternatively you can automate the process and avoid the need to use ON SYS in your code by allocating the identifier using FN_setproc in WINLIB5.

You can use Windows™ API functions to determine the appearance of the buttons. The following program segment illustrates how to cause a button to be disabled (greyed out); htool% is the value returned from the FN_createtoolbar function:

state% = 0
SYS "SendMessage", htool%, 1041, buttid%, state%
Some of the possible button states are:
0   Disabled
4Enabled
6Pressed
8Hidden
You can discover the current state of a button as follows:
SYS "SendMessage", htool%, 1042, buttid%, 0 TO state%
In the case of an auto toggle button which has been toggled to the 'pressed' state, the returned value is one greater than those shown. So 5 signifies that it has been toggled to the 'pressed' state and 7 that it is also being pressed by the user.

PROC_removetoolbar

This procedure removes a toolbar previously created with FN_createtoolbar. You should always remove the toolbar before your program exits or returns to immediate mode.

FN_custombutton(htool%,bmpfile$,buttid%)

This function allows you to customise a button image to suit your particular application. Three parameters must be supplied: the handle of the toolbar (as returned from FN_createtoolbar), the name of a Windows Bitmap file containing the required button image (it is automatically scaled to fit the button) and the identifier of the button you wish to change.

The function returns TRUE if the button image was successfully changed, and FALSE otherwise (most likely because the specified file could not be read or has an invalid format). You can customise as many buttons as you like by calling this function multiple times with different values of buttid%; alternatively you can customise multiple buttons from a single bitmap file using the FN_custombuttons function to be found in the WINLIB3 library.

The following program segment illustrates how to customise a button image:

ok% = FN_custombutton(htool%, "\pictures\bmp\owl.bmp", 102)
When initially creating the toolbar set the button 'type' of any custom buttons to 15 (no image). Also ensure that the background colour of your bitmap file is R=192, G=192, B=192 (&C0C0C0). These measures will ensure that your custom images are displayed correctly.

PROC_addtooltips(htool%,nbutts%,buttip$(),buttid%())

This procedure allows you to add tool tips to your toolbar. A tool tip is a text string which appears in a small yellow box when you 'hover' the mouse over the toolbar button for a short while. Adding tool tips to your toolbar gives your program an even more authentic Windows™ interface.

Four parameters must be supplied: the handle of the toolbar (as returned from FN_createtoolbar), the number of buttons for which you want to provide tips, an array of strings containing the tips to be displayed and an integer array of identifier values for the appropriate buttons.

The following program segment illustrates how to add tooltips to the toolbar created in the earlier example:

nbutts% = 3
DIM buttip$(nbutts%-1), buttid%(nbutts%-1)
buttip$() = "Cut", "Copy", "Paste"
buttid%() = 100, 101, 102
PROC_addtooltips(htool%,nbutts%,buttip$(),buttid%())
The buttid% array may often be the same as that passed to FN_createtoolbar.

Dialogue boxes

The WINLIB2 library contains a set of procedures and functions for creating and controlling custom dialogue boxes. These allow you to adopt the preferred Windows™ method of requesting input from the user.

The library should be loaded from your program using the command:

INSTALL @lib$+"WINLIB2"
The functions contained are: The WINLIB2B library contains an identical set of functions but additionally forwards to your program any WM_HELP and WM_NOTIFY messages generated by the dialogue box controls. These messages can be intercepted using *SYS 1 to allow your program to provide, for example, context-sensitive help for your dialogue box. WINLIB2B also suppresses the automatic closing of the dialogue box when you click on its close button, or press Escape.

The supplied example program DLGEDIT.BBC can simplify the creation of dialogue boxes by providing a 'visual' editor and generating some of the required BBC BASIC code automatically.

FN_newdialog

Before you can use a custom dialogue box, you must first define its position, size and title. This should be done only once, typically in an initialisation routine, for each dialogue box your program contains (the dialogue box can subsequently be displayed as many times as you like):
dlg% = FN_newdialog(title$,x%,y%,cx%,cy%,font%,size%)
The string title$ specifies the title of the dialogue box, which is displayed in its title bar. The values x% and y% specify the initial position of the dialogue box, with respect to the top left corner of your program's window. The values cx% and cy% specify the width and height, respectively, of the dialogue box.

The value font% specifies the size, in points, of all the text strings in the dialogue box, and it also determines the size of the units used to specify the position and size of the box. Therefore, if you change the size of the text all the other dimensions of the dialogue box are scaled to suit. A common value to use is 8.

The value size% specifies the number of bytes of memory to allocate for the dialogue box template. As a rule of thumb this can be set to approximately the number of individual items within the dialogue box multiplied by 50; if the value is too small you will receive the error message No room for dialogue template when the items are created. There is no harm, apart from wasted memory, in making it too big.

The returned value (dlg% in this case) identifies the dialogue box, and must be stored for use in the subsequent procedures.

Normally a dialogue box has a title bar. This can be eliminated by adding an extra line after the call to FN_newdialog as follows:

dlg% = FN_newdialog("",x%,y%,cx%,cy%,font%,size%)
dlg%!16 AND= &FF7FFFFF
Note, however, that if you do this the user won't be able to move the dialogue box, so you may need to set its position more carefully.

Make sure you only call FN_newdialog once (for each dialogue box); if you need to re-open a dialogue box that has already been opened, simply call PROC_showdialog. Preferably place all your calls to FN_newdialog in an initialisation routine that is only executed once.

PROC_pushbutton

Once you have created the basic dialogue box, you must define its contents. The common items (pushbuttons, edit boxes etc.) each have their own procedure. Again, the contents of the dialogue box must be defined just once, during your program's initialisation phase.

All dialogue boxes should have at least one pushbutton, labelled OK, which enables the user to confirm that his input is complete:

PROC_pushbutton(dlg%,text$,id%,x%,y%,cx%,cy%,style%)
The value dlg% identifies the dialogue box, and is the value returned from FN_newdialog. The string text$ specifies the text to appear inside the button (e.g. "OK"), the values x% and y% specify the position of the button within the dialogue box and the values cx% and cy% specify the size of the button (in dialogue box units).

The value id% is a unique identifier of the pushbutton; all the items within a particular dialogue box must have different values. You can choose any value (within reason) but the values 1 and 2 are reserved for the OK and Cancel buttons respectively so should only be used for that purpose.

The value style% can be zero, but other values allow you to modify the appearance or behaviour of the pushbutton. Setting it to 1 causes the pushbutton to be the default button: it is displayed with a thicker border and pressing the Enter key has the same effect as pressing that button. There should only be one button in each dialogue box with this style; typically you would use it for the OK button. Setting style% to &20000 causes the button to be the first item in a new group; this affects navigation of the dialogue box using the cursor keys. Setting style% to &80 indicates that the button will display a bitmap image (see Initialising the contents of a dialogue box); in this case text$ should be empty. The values can be combined.

PROC_checkbox

A check box is a small square which can either be checked (contain a tick mark) or not. When the user clicks on the box, it (usually) toggles between being checked and being unchecked. A check box is used to select one of two states (e.g. off or on).
PROC_checkbox(dlg%,text$,id%,x%,y%,cx%,cy%,style%)
The value dlg% identifies the dialogue box, and is the value returned from FN_newdialog. The string text$ specifies the text to appear alongside the check box, the values x% and y% specify the position of the check box within the dialogue box and the values cx% and cy% specify the size of the check box and its associated text (in dialogue box units). The value id% is a unique identifier for the check box.

The value style% can be zero, but other values allow you to modify the appearance or behaviour of the check box. Setting it to &20 causes the associated text to appear to the left of the check box rather than to the right. Setting it to &20000 causes the check box to be the first item in a new group; this affects navigation of the dialogue box using the cursor keys. The values can be combined.

PROC_radiobutton

A radio button is a small circle which can either be checked (contain a central spot) or not. Radio buttons are used in groups of two or more, where only one of the buttons is checked at any one time. When the user clicks one of the buttons it becomes checked, and all the other buttons in the group become unchecked. A radio button is used to select one of two or more states.
PROC_radiobutton(dlg%,text$,id%,x%,y%,cx%,cy%,style%)
The value dlg% identifies the dialogue box, and is the value returned from FN_newdialog. The string text$ specifies the text to appear alongside the radio button, the values x% and y% specify the position of the radio button within the dialogue box and the values cx% and cy% specify the size of the radio button and its associated text (in dialogue box units). The value id% is a unique identifier for the radio button.

The value style% can be zero, but other values allow you to modify the appearance or behaviour of the radio button. Setting it to &20 causes the associated text to appear to the left of the radio button rather than to the right. Setting it to &20000 causes the radio button to be the first item in a new group; this has special significance for radio buttons, since only one radio button in a group can be checked at any one time. The values can be combined.

PROC_groupbox

A group box is a rectangle which is used to enclose a number of items within the dialogue box, thus emphasising that they are grouped together.
PROC_groupbox(dlg%,text$,id%,x%,y%,cx%,cy%,style%)
The value dlg% identifies the dialogue box, and is the value returned from FN_newdialog. The string text$ specifies the text to appear in the top edge of the group box, the values x% and y% specify the position of the group box within the dialogue box and the values cx% and cy% specify the size of the group box (in dialogue box units). The value id% is a unique identifier for the group box.

The value style% may be zero, but will often have the value &20000 signifying that the group box is the first item in a new group.

PROC_editbox

An edit box is a rectangular field into which the user can type textual or numeric input.
PROC_editbox(dlg%,text$,id%,x%,y%,cx%,cy%,style%)
The value dlg% identifies the dialogue box, and is the value returned from FN_newdialog. The string text$ specifies the initial text (if any) to appear in the edit box, the values x% and y% specify the position of the edit box within the dialogue box and the values cx% and cy% specify the size of the edit box (in dialogue box units). The value id% is a unique identifier for the edit box.

The value style% can be zero, but other values allow you to modify the appearance or behaviour of the edit box. Setting it to &80 causes the contents of the edit box to scroll horizontally, if necessary. Setting it to &2000 causes the edit box to accept only numeric input. Setting it to &20000 causes the edit box to be the first item in a new group. Setting it to &1004 (along with an appropriate vertical size) creates a multi-line edit box. The values can be combined.

PROC_static

A static item is a rectangular area containing (usually) a text string or an image. This may be used to label another item or be simply informative.
PROC_static(dlg%,text$,id%,x%,y%,cx%,cy%,style%)
The value dlg% identifies the dialogue box, and is the value returned from FN_newdialog. The string text$ specifies the required text (if any), the values x% and y% specify the position of the rectangle within the dialogue box and the values cx% and cy% specify the size of the rectangle (in dialogue box units). The value id% is a unique identifier for the static item.

The value style% may be zero, but other values allow you to modify the appearance of the static item. By default text is left-justified within the rectangle but setting style% to 1 causes the text to be centred within the rectangle and setting it to 2 causes the text to be right-justified within the rectangle. Setting style% to &E indicates that the item will contain a bitmap image to be loaded later (see Initialising the contents of a dialogue box); in this case text$ should be empty.

PROC_listbox

A list box displays a list of two or more items from which the user can select one.
PROC_listbox(dlg%,"",id%,x%,y%,cx%,cy%,style%)
The value dlg% identifies the dialogue box, and is the value returned from FN_newdialog. The text string is unused and should be set to an empty string, the values x% and y% specify the position of the list box within the dialogue box and the values cx% and cy% specify the size of the list box (in dialogue box units). The value id% is a unique identifier for the list box.

The value style% can be zero, but other values allow you to modify the appearance or behaviour of the list box. For example setting it to &20000 causes the list box to be the first item in a new group. To disable automatic sorting of the listbox contents subtract 2 from the style% value you would otherwise have used (e.g. use –2 instead of zero or &1FFFE instead of &20000). Adding &100000 to style% results in the list box having a horizontal scroll bar if there are too many items to fit in the box at once.

The items to be displayed in the list box must be written as a separate exercise once the dialogue box has been displayed. See Initialising the contents of a dialogue box for details. If the height of the list box is insufficient for the number of items to be displayed, a scroll bar is automatically generated.

PROC_combobox

A combo box consists of a list and a selection field. The list presents the options a user can select and the selection field displays the current selection.
PROC_combobox(dlg%,"",id%,x%,y%,cx%,cy%,style%)
The value dlg% identifies the dialogue box, and is the value returned from FN_newdialog. The text string is unused and should be set to an empty string, the values x% and y% specify the position of the combo box within the dialogue box and the values cx% and cy% specify the size of the combo box (in dialogue box units). The value id% is a unique identifier for the combo box.

The value style% can be zero, but other values allow you to modify the appearance or behaviour of the combo box. Setting it to 3 creates a drop down list, where the list of items from which the selection can be made is only displayed when the user clicks on the arrow button (note particularly that in this case cy% is the dropped down height of the box). Setting it to &100 causes the items in the list to be sorted into alphabetical order. Setting it to &20000 causes the combo box to be the first item in a new group. The values can be combined.

The items to be displayed in the combo box list must be written as a separate exercise once the dialogue box has been displayed. See Initialising the contents of a dialogue box for details. In the case of a drop-down list the height of the combo box should be made sufficient for the list when it is displayed.

PROC_dlgitem

PROC_dlgitem can be used to create items other than those for which dedicated procedures are provided.
PROC_dlgitem(dlg%,text$,id%,x%,y%,cx%,cy%,style%,class%)
The value dlg% identifies the dialogue box, and is the value returned from FN_newdialog. The values x% and y% specify the position of the item within the dialogue box and the values cx% and cy% specify the size of the item (in dialogue box units). The values of text$, style% and class% depend on the type of item. The value id% is a unique identifier for the item.

PROC_dlgctrl

PROC_dlgctrl is similar to PROC_dlgitem except that a class name is specified rather than a numeric value:
PROC_dlgctrl(dlg%,text$,id%,x%,y%,cx%,cy%,style%,class$)
This allows you to embed in your dialogue box standard Windows™ controls for which dedicated procedures are not provided. Examples of such controls are up-down controls and trackbars:

Adding an up-down control

You can add an up-down control (a pair of arrows used for incrementing and decrementing a numeric value in an edit box) using the following program segment:
PROC_dlgctrl(dlg%,"",id%,0,0,cx%,cy%,&50000096,"msctls_updown32")
This item must immediately follow a numeric edit box in which the value controlled by the up and down arrows appears. The up-down control automatically positions itself at the right-hand end of the edit box (the position values are unused and are set to 0,0).

To set the range of the up-down control (which must be done after the PROC_showdialog):

SYS "SendDlgItemMessage", !dlg%, id%, &465, 0, (min% << 16) + max%

Adding a trackbar

You can add a trackbar to a dialogue box using the following program segment:
PROC_dlgctrl(dlg%,"",id%,x%,y%,cx%,cy%,&50000000,"msctls_trackbar32")
The above example creates a horizontal trackbar with no tick-marks. To alter the style of the trackbar add one or more of the following values to the &50000000:
StyleEffect
0Horizontal trackbar with no tick marks
1Show tick marks
2Vertical trackbar
4Tick marks to top or left (default is bottom or right)
To set the range of the trackbar (which must be done after the PROC_showdialog):
SYS "SendDlgItemMessage", !dlg%, id%, 1030, 1, (max% << 16) + min%
To set the current position of the trackbar:
SYS "SendDlgItemMessage", !dlg%, id%, 1029, 1, position%
To read the current position of the trackbar:
SYS "SendDlgItemMessage", !dlg%, id%, 1024, 0, 0 TO position%

PROC_showdialog

Once the position, size and contents of the dialogue box have been defined, it may be displayed on the screen. Whilst the creation of the dialogue box and the definition of its contents should be done just once, the box may subsequently be displayed as many times as you like:
PROC_showdialog(dlg%)
The value dlg% identifies the dialogue box, and is the value returned from FN_newdialog. Once the dialogue box has been displayed, you can send messages to it to affect its contents, and request information about its current contents.

Initialising the contents of a dialogue box

In many cases the initial contents of the dialogue box are defined when the items within it are created. For example, the initial contents of an edit box can be specified in the PROC_editbox procedure call. However you may want to change the contents at other times, and in the case of list boxes and combo boxes, which contain multiple items, you must use other methods to initialise the contents. Note that this initialisation must take place after the call to PROC_showdialog.

To change the text associated with a dialogue box item (which may for example be the contents of an edit box or a label for a check box) you can use the SetDlgItemText API call:

SYS "SetDlgItemText", !dlg%, id%, text$
The value !dlg% (note the exclamation mark) is the handle of the dialogue box, which is contained in memory at the address returned from FN_newdialog. The value id% is the identifier for the item in question and text$ is the new text string to be associated with the item.

If the item is an edit box used for numeric entry, the value displayed in the box can be set with SetDlgItemInt:

SYS "SetDlgItemInt", !dlg%, id%, value%, signed%
The value !dlg% is the handle of the dialogue box, id% is the identifier for the item in question, value% is the new value to display and signed% determines whether the value should be interpreted as signed (1) or unsigned (0).

If the value is controlled by an up-down control you can set the allowed range as follows:

SYS "SendDlgItemMessage", !dlg%, id%, &465, 0, (min% << 16) + max%
where id% is the identifier for the up-down control, min% is the lowest value permitted and max% is the highest value permitted.

To load a bitmap image into a static item or a pushbutton you should use the following program segment. In the case of a pushbutton change &172 to &F7 in the second line:

SYS "LoadImage", 0, bmpfile$, 0, cx%, cy%, 16 TO hbitmap%
SYS "SendDlgItemMessage", !dlg%, id%, &172, 0, hbitmap%
The value bmpfile$ is the name of a Windows™ bitmap file containing the image, cx% and cy% are the dimensions of the image in pixels, !dlg% is the handle of the dialogue box and id% is the identifier for the static item or pushbutton in question. Once you have finished with the dialogue box (but not before) delete the bitmap handle as follows:
SYS "DeleteObject", hbitmap%

To enter the list of strings into a list box you should do the following:

SYS "SendDlgItemMessage", !dlg%, id%, &180, 0, "Listbox item 0"
SYS "SendDlgItemMessage", !dlg%, id%, &180, 0, "Listbox item 1"
SYS "SendDlgItemMessage", !dlg%, id%, &180, 0, "Listbox item 2"
etc.
where the value id% is the identifier for the list box. The list box will (by default) sort the strings into alphabetical order, so the order in which they are sent is not important. To disable sorting subtract 2 from the style% value you would otherwise have used (see PROC_listbox).

To empty a list box of its contents do:

SYS "SendDlgItemMessage", !dlg%, id%, &184, 0, 0

To enter the list of strings into a combo box you should do the following:

SYS "SendDlgItemMessage", !dlg%, id%, &143, 0, "Combobox item 0"
SYS "SendDlgItemMessage", !dlg%, id%, &143, 0, "Combobox item 1"
SYS "SendDlgItemMessage", !dlg%, id%, &143, 0, "Combobox item 2"
etc.
where the value id% is the identifier for the combo box. In this case the items are not sorted (by default), so they must be sent in the order in which they should appear. The initial selection from the list should be made as follows:
SYS "SendDlgItemMessage", !dlg%, id%, &14E, index%, 0
The value index% determines which of the items is initially selected (starting at 0).

To empty a combo box of its contents do:

SYS "SendDlgItemMessage", !dlg%, id%, &14B, 0, 0

To initialise the state of a set of radio buttons, you can use the CheckRadioButton API call:

SYS "CheckRadioButton", !dlg%, first%, last%, id%
Here first% is the identifier of the first radio button in the group, last% is the identifier of the last radio button in the group and id% is the identifier of the button you want to be checked.

To initialise the state of a check box, you can use the CheckDlgButton API call:

SYS "CheckDlgButton", !dlg%, id%, state%
Here id% is the identifier of the button you want to affect and state% is the state you wish it to have: 0 signifies unchecked and 1 signifies checked.

Disabling and enabling dialogue box items

You may wish to disable one or more items in a dialogue box, for example you may want to disable a button if it is not appropriate to click it in the current circumstances. An item which is disabled is shown in grey.

To disable an item you can use the EnableWindow API call:

SYS "GetDlgItem", !dlg%, id% TO h%
SYS "EnableWindow", h%, 0
Here id% is the identifier of the button you want to affect. To re-enable the item change the zero to a one:
SYS "GetDlgItem", !dlg%, id% TO h%
SYS "EnableWindow", h%, 1
Note that, as with initialisation, these routines must be executed after the call to PROC_showdialog.

Reading the contents of a dialogue box

Since the whole purpose of a dialogue box is to receive user input, it is vital that the current contents can be determined, particularly when the OK button is pressed.

To read the text associated with a dialogue box item (which may for example be the contents of an edit box or the current selection of a combo box) you can use the GetDlgItemText API call:

DEF FNgetdlgtext(dlg%, id%)
LOCAL text%
DIM text% LOCAL 255
SYS "GetDlgItemText", !dlg%, id%, text%, 255
= $$text%
The parameter dlg% is the value returned from FN_newdialog and the parameter id% is the identifier for the item in question. The maximum length of the string is 255 characters.

In the case of a multi-line edit box the maximum length should be set to an appropriate value (up to a maximum of 65535 bytes). To save the returned data to a file you can do the following:

DEF PROCsavetofile(dlg%,id%,filename$)
LOCAL text%, Len%
DIM text% LOCAL 65535
SYS "GetDlgItemText", !dlg%, id%, text%, 65535 TO Len%
OSCLI "SAVE """+filename$+""" "+STR$~text%+"+"+STR$~Len%
ENDPROC
The data returned from a multi-line edit box consists of lines of text separated by CRLF (CHR$13+CHR$10) sequences. If you want to process the data you can split it into individual lines as follows:
P% = text%
REPEAT
  A$ = $P%        : REM. get line of text from memory
  PRINT A$        : REM. print the text (for example)
  P% += LEN(A$)+2 : REM. advance pointer to next line
UNTIL P% >= (text%+Len%)
If the item is an edit box used for numeric entry, the current value can be read with GetDlgItemInt:
SYS "GetDlgItemInt", !dlg%, id%, 0, signed% TO value%
The value !dlg% is the handle of the dialogue box, id% is the identifier for the item in question and signed% determines whether a negative value should be accepted (1) or not (0).

To determine which item (if any) is selected in a list box you can do the following:

SYS "SendDlgItemMessage", !dlg%, id%, &188, 0, 0 TO sel%
where the value id% is the identifier for the list box. The returned value sel% gives the index (starting at 0) of the currently selected item. If no item is selected, –1 is returned. Note that if the listbox contents have been sorted, which is the default behaviour, knowing the index alone is of little value. However you can use it to discover the selected text as follows:
DEF FNgetlistboxtext(dlg%, id%, sel%)
LOCAL text%
DIM text% LOCAL 255
SYS "SendDlgItemMessage", !dlg%, id%, &189, sel%, text%
= $$text%
To determine which item is selected in a combo box do the following:
SYS "SendDlgItemMessage", !dlg%, id%, &147, 0, 0 TO sel%
To discover the current state of a check box or radio button, you can use the IsDlgButtonChecked API call:
SYS "IsDlgButtonChecked", !dlg%, id% TO state%
where the value id% is the identifier for the check box or radio button. The value state% is set to 0 if the button is not checked and to 1 if it is checked.

Determining when a button is clicked

You must be able to determine when the user has clicked on a button, so that (for example) when the OK button is clicked the contents of the dialogue box can be read and the box removed from the screen. When any item in a dialogue box is clicked, a message is sent (similar to those from a menu or a toolbar) which can be detected with ON SYS.

The following code segment can be used to wait for the OK or Cancel button to be clicked, and then take appropriate action:

click% = 0
ON SYS click% = @wparam% AND &FFFF : RETURN
REPEAT pause% = INKEY(1) : UNTIL click% = 1 OR click% = 2 OR !dlg% = 0
ON SYS OFF
IF click% = 1 THEN
  PRINT "OK pressed"
  REM. process contents of dialogue box here
ELSE
  PRINT "Cancel pressed"
ENDIF
PROC_closedialog(dlg%)
Since clicking the close button of a dialogue box (or floating toolbar) always produces the same ID code (2) you cannot directly tell from ON SYS which dialogue box or toolbar was closed. If your program has more than one open at the same time this could be a problem. You can determine which are still open (and therefore, by a process of elimination, which was closed) by examining the window handles (!dlg% in the above example). If the handle is non-zero the window is still open, and if it is zero it has been closed.

PROC_closedialog

When the user has clicked on OK, and the contents of the dialogue box have been processed, the box should (generally) be removed from the screen:
PROC_closedialog(dlg%)
The dialogue box template remains in memory, so you can display it again at any time by calling PROC_showdialog.

The dialogue box should also be removed whenever your program returns to immediate mode (for example if an error occurs or the END statement is executed) or when your program's window is closed by the user. This can be achieved by executing the following statements immediately after the call to PROC_showdialog:

ON CLOSE PROC_closedialog(dlg%):QUIT
ON ERROR PROC_closedialog(dlg%):PRINT'REPORT$:END
Because the dialogue box uses space on the heap, it is essential that you remove it before executing a CLEAR, CHAIN or RUN statement. Failure to do so is very likely to crash BBC BASIC for Windows.

Trackbars and progress bars

The WINLIB3 library contains a set of procedures and functions for creating and controlling floating toolbars, trackbars and progress bars. The library should be loaded from your program using the command:
INSTALL @lib$+"WINLIB3"

The functions contained are:

FN_createfloatingtoolbar(nbutts%,button%(),buttid%(),xpos%,ypos%,title$)

This function creates a floating toolbar, and returns a pointer to its window handle. It works in a similar way to FN_createtoolbar except that it needs three extra parameters: the initial horizontal and vertical coordinates of the toolbar (measured, in dialogue box units, from the top-left corner of BASIC's output window) and a string to display in the toolbar's title bar. Unlike FN_createtoolbar, the toolbar is not actually displayed until you call PROC_showfloatingtoolbar; this allows you to remove and restore the toolbar without having to re-create it each time (avoiding the memory wastage that would involve).

Normally the floating toolbar has a title bar. This can be eliminated by adding an extra line after the call to FN_createfloatingtoolbar as follows:

ftb% = FN_createfloatingtoolbar(nbutts%,button%(),buttid%(),50,50,"")
ftb%!16 AND= &FF7FFFFF
Note, however, that if you do this the user won't be able to move the toolbar, so you may need to set its position more carefully.

PROC_showfloatingtoolbar(ftb%)

This procedure displays a previously created floating toolbar. It requires one parameter, being the value returned from FN_createfloatingtoolbar. The following program segment creates and displays a floating toolbar containing three buttons: a cut button, a copy button and a paste button:
nbutts% = 3
DIM button%(nbutts%-1), buttid%(nbutts%-1)
button%() = 0, 1, 2
buttid%() = 100, 101, 102
ftb% = FN_createfloatingtoolbar(nbutts%,button%(),buttid%(),50,50,"Floating")
PROC_showfloatingtoolbar(ftb%)
See FN_createtoolbar for more details. If you want to send messages to the toolbar (for example to control the appearance of the buttons) you can do so using the means described under FN_createtoolbar, but note that the value returned from FN_createfloatingtoolbar is a pointer to the toolbar handle not the handle itself. For example, to cause a button to be disabled (greyed out):
SYS "SendMessage", !ftb%, 1041, buttid%, 0
Note that you can only send messages to a floating toolbar after it has been displayed with PROC_showfloatingtoolbar.

PROC_removefloatingtoolbar

This procedure removes a floating toolbar previously created with FN_createfloatingtoolbar. You should always remove the toolbar before your program exits or returns to immediate mode:
PROC_removefloatingtoolbar(ftb%)
Because FN_createfloatingtoolbar reserves space on the heap, it is essential that you remove the toolbar before executing a CLEAR, CHAIN or RUN statement. Failure to do so is very likely to crash BBC BASIC for Windows.

FN_custombuttons(htool%,bmpfile$,nbutt%,buttid%())

This function is very similar to FN_custombutton except that it allows you to customise the images of multiple buttons from a single bitmap file. Four parameters must be supplied: the handle of the toolbar (e.g. as returned from FN_createtoolbar), the name of a Windows Bitmap file containing the required button images (left to right), the number of button images in the file and an array of identifiers of the buttons you wish to change.

The function returns TRUE if the button images were successfully changed, and FALSE otherwise (most likely because the specified file could not be read or has an invalid format). You can customise as many buttons as you like by providing a bitmap with suitable dimensions.

Note that when used with a floating tool bar the value returned from FN_createfloatingtoolbar is a pointer to the toolbar handle not the handle itself. So to customise all the buttons in a floating toolbar:

ok% = FN_custombuttons(!ftb%, "birds.bmp", nbutts%, buttid%())
When initially creating the toolbar set the button 'type' of any custom buttons to 15 (no image). Also ensure that the background colour of your bitmaps is R=192, G=192, B=192 (&C0C0C0). These measures will ensure that your custom images are displayed correctly.

FN_createtrackbar(hwnd%,xpos%,ypos%,width%,height%,style%)

This function creates a trackbar (sometimes called a slider). A trackbar allows the user to control a parameter by dragging the slider to the wanted position. Six parameters must be supplied: the window handle of the trackbar's parent window (usually @hwnd% unless you need to place the trackbar in a dialogue box), the horizontal and vertical coordinates of the trackbar (in pixels, measured from the top-left corner of the bar's parent window), the width and height of the trackbar (in pixels) and the trackbar style. Possible values for the style are as follows:
StyleEffect
0Horizontal trackbar with no tick marks
1Show tick marks
2Vertical trackbar
4Tick marks to top or left (default is bottom or right)
The values can be combined.

PROC_showtrackbar(tb%,max%)

This function displays a trackbar created with FN_createtrackbar. It requires two parameters: the value returned by FN_createtrackbar and the maximum value to which the slider can be moved. If tick marks are shown, the total number (including both ends) will be max%+1.

To create and display a horizontal trackbar with tick marks, suitable for selecting a value between zero and ten:

tb% = FN_createtrackbar(@hwnd%, 100, 200, 20, 300, 1)
PROC_showtrackbar(tb%, 10)

FN_trackbarpos(tb%)

This function returns the current position of the trackbar (between zero and the value of max% supplied to PROC_showtrackbar):
trackpos% = FN_trackbarpos(tb%)
Normally the trackbar is moved by the user, but if you want your program to move it to a specific position you can do that as follows:
SYS "SendMessage", !tb%, 1029, 1, trackpos%
(note the exclamation mark in !tb%).

PROC_removetrackbar(tb%)

This procedure removes a trackbar previously created with FN_createtrackbar. You should always remove the trackbar before your program exits or returns to immediate mode:
PROC_removetrackbar(tb%)
Because FN_createtrackbar reserves space on the heap, it is essential that you remove the trackbar before executing a CLEAR, CHAIN or RUN statement. Failure to do so is very likely to crash BBC BASIC for Windows.

FN_createprogressbar(hwnd%,xpos%,ypos%,width%,height%,style%)

This function creates a progress bar which is typically used to inform the user of the progress of a time-consuming operation. Six parameters must be supplied: the window handle of the progress bar's parent window (usually @hwnd% unless you need to place the progress bar in a dialogue box or status bar), the horizontal and vertical coordinates of the progress bar (in pixels, measured from the top-left corner of the bar's parent window), the width and height of the progress bar (in pixels) and the progress bar style. A style value of zero selects a horizontal progress bar with separate 'blocks'. Other values are as follows:
StyleEffect
1Progress 'blocks' are contiguous
4The progress bar is vertical
The values may be combined.

PROC_showprogressbar(pb%,max%)

This function displays a progress bar created with FN_createprogressbar. It requires two parameters: the value returned by FN_createprogressbar and the maximum value which can be indicated. For example if the progress is shown in percent, max% should be 100.

To create and display a progress bar, suitable for showing a value between zero and ten:

pb% = FN_createprogressbar(@hwnd%, 100, 200, 20, 300, 0)
PROC_showprogressbar(pb%, 10)

PROC_stepprogressbar(pb%, step%)

This procedure advances the progress bar by the number of steps indicated by step% (which can be negative). If the end result exceeds the value of max% (supplied to PROC_showprogressbar), or is negative, it wraps around. To advance a progress bar by one step:
PROC_stepprogressbar(pb%, 1)
You can also set the progress bar to an absolute value as follows:
SYS "SendMessage", !pb%, 1026, progress%, 0

PROC_removeprogressbar(pb%)

This procedure removes a progress bar previously created with FN_createprogressbar. You should always remove the progress bar before your program exits or returns to immediate mode:
PROC_removeprogressbar(pb%)
Because FN_createprogressbar reserves space on the heap, it is essential that you remove the progress bar before executing a CLEAR, CHAIN or RUN statement. Failure to do so is very likely to crash BBC BASIC for Windows.

Property sheets and wizards

The WINLIB4 library contains a set of procedures and functions for creating and controlling property sheets and wizards. The library should be loaded from your program using the command:
INSTALL @lib$+"WINLIB4"

The functions contained are:

Property sheets and wizards are very similar, both effectively consisting of multiple dialogue box pages within a single window (only one page being displayed at a given time). They differ principally in the way the individual pages are selected: in a property sheet the pages can be selected in any order (each has a tab which can be clicked by the user), whereas in a wizard the pages must be selected sequentially by means of Next and Back buttons.

Because property sheets and wizards are like multiple dialogue boxes, this is exactly how they are created in BBC BASIC for Windows. Each page must be created using the FN_newdialog function in the WINLIB2 library. However, unlike conventional dialogue boxes, you should not include OK, Apply or Cancel buttons in the individual pages.

Instead of displaying the individual dialogue boxes with PROC_showdialog they are grouped together as a property sheet or wizard using FN_newpropsheet and displayed using PROC_showpropsheet.

FN_newpropsheet(caption$,npages%,initial%,style%,dlg%())

This function groups together multiple dialogue boxes to form a single property sheet or wizard. Five parameters must be supplied: a caption for the title bar, the number of dialogue box pages, the page which should be initially displayed (where the first page is numbered zero), a window style (usually 0 for a property sheet or &20 for a wizard) and an array of dialogue box pointers (each being the value returned from FN_newdialog).

The following program segment creates a property sheet containing three dialogue box pages:

pages% = 3
DIM page%(pages%-1)
page%(0) = FN_newdialog("First page", 32, 32, 288, 128, 8, 650)
REM create the contents of the first page here
page%(1) = FN_newdialog("Second page", 32, 32, 288, 128, 8, 1100)
REM create the contents of the second page here
page%(2) = FN_newdialog("Third page", 32, 32, 288, 128, 8, 1100)
REM create the contents of the third page here
psh% = FN_newpropsheet("Property sheet",pages%,0,0,page%())
Following each FN_newdialog call the contents of the relevant page are created using the procedures provided in WINLIB2, e.g. PROC_static, PROC_editbox etc. If a wizard rather than a property sheet is wanted, the last line would be:
psh% = FN_newpropsheet("Property sheet",pages%,0,&20,page%())

PROC_showpropsheet(psh%,hdlg%())

This procedure displays the property sheet or wizard. Two parameters must be supplied: the pointer to the property sheet returned by FN_newpropsheet and an empty array to receive the dialogue box handles. This array should be DIMensioned to have at least as many elements as there are pages in the property sheet or wizard, and on return from PROC_showpropsheet will contain the handles of each dialogue box page. These will be needed in order to initialise and interrogate the contents of the individual pages.

The following program segment displays the property sheet or wizard created above:

DIM hdlg%(pages%-1)
PROC_showpropsheet(psh%,hdlg%())

Initialising the contents of a property sheet

As with a dialogue box, the initial contents of the property sheet pages may be defined when the items within them are created. For example, the initial contents of an edit box can be specified in the PROC_editbox procedure call. However you may want to change the contents at other times, and in the case of list boxes and combo boxes, which contain multiple items, you must use other methods to initialise the contents.

The methods for doing this are identical to those listed in Initialising the contents of a dialogue box except that whenever the dialogue box handle is needed you must supply an element from the hdlg%() array returned from PROC_showpropsheet rather than the value pointed to by FN_newdialog. For example, to change the text associated with an item in the first page of the property sheet or wizard:

SYS "SetDlgItemText", hdlg%(0), id%, text$
You should normally ensure that the item identifier id% is unique rather than being used in two or more different pages. If the specified item is not in the page corresponding to the specified dialogue box handle, the call will fail.

Determining when a property sheet button is pressed

You must be able to determine when the user has clicked on the OK or Finish button, so that the contents of the property sheet or wizard can be read and the window removed from the screen. When any item in a property sheet is clicked, a message is sent (similar to those from a menu or a toolbar) which can be detected with ON SYS.

The following code segment can be used to wait for the OK, Finish or Cancel button to be clicked, and then take appropriate action:

click% = 0
ON SYS click% = @wparam% AND &FFFF : RETURN
REPEAT pause% = INKEY(1) : UNTIL click% = 1 OR click% = 2
ON SYS OFF
IF click% = 1 THEN
  PRINT "OK or Finish pressed"
  REM process contents of property sheet here
ELSE
  PRINT "Cancel pressed"
ENDIF
PROC_closepropsheet(psh%)

Monitoring property sheet page changes

Under some circumstances you may need to know when the user has changed the property sheet or wizard page. This is particularly true of a wizard, since it is the responsibility of the user's program to replace the Next button by a Finish button when the last page of the wizard is displayed (and also to inhibit the Back button when the first page is displayed).

You can do this by monitoring the current page handle, which can be determined by means of an API call. For example, the loop for monitoring which buttons are pressed can be modified to check also for page changes as follows:

ON SYS click% = @wparam% AND &FFFF : RETURN
oldhdlg% = 0
REPEAT
  click% = 0
  REPEAT pause% = INKEY(1)
    SYS "SendMessage", !psh%, &476, 0, 0 TO hdlg%
  UNTIL click%<>0 OR hdlg%<>oldhdlg%
  IF hdlg%<>oldhdlg% THEN
    oldhdlg% = hdlg%
    CASE hdlg% OF
      WHEN hdlg%(0): SYS "SendMessage", !psh%, &470, 0, 2 : REM Next only
      WHEN hdlg%(1): SYS "SendMessage", !psh%, &470, 0, 3 : REM Back and Next
      WHEN hdlg%(2): SYS "SendMessage", !psh%, &470, 0, 5 : REM Back and Finish
    ENDCASE
  ENDIF
  CASE click% OF
    WHEN 1: PRINT "Finish pressed"
    REM Process contents of wizard here
    WHEN 2: PRINT "Cancel pressed"
  ENDCASE
UNTIL hdlg% = 0
PROC_closepropsheet(psh%)
The above example is for a wizard with three pages. According to which page is currently displayed the Back, Next and Finish buttons are displayed appropriately.

Reading the contents of a property sheet

Since the whole purpose of a property sheet or wizard is to receive user input, it is vital that the current contents can be determined, particularly when the OK or Finish button is pressed.

The methods for doing this are identical to those listed in Reading the contents of a dialogue box except that whenever the dialogue box handle is needed you must supply an element from the hdlg%() array returned from PROC_showpropsheet rather than the value pointed to by FN_newdialog. For example, to read the text associated with an item in the second page of the property sheet or wizard:

DIM text% 255
SYS "GetDlgItemText", hdlg%(1), id%, text%, 255
content$ = $$text%

PROC_closepropsheet(psh%)

When the contents of the property sheet or wizard have been processed, it should be removed from the screen:
PROC_closepropsheet(psh%)
The property sheet templates remain in memory, so you can display it again at any time by calling PROC_showpropsheet.

The property sheet or wizard should also be removed whenever your program returns to immediate mode (for example if an error occurs or the END statement is executed) or when your program's window is closed by the user. This can be achieved by executing the following statements immediately after the call to PROC_showpropsheet:

ON CLOSE PROC_closepropsheet(psh%):QUIT
ON ERROR PROC_closepropsheet(psh%):PRINT'REPORT$:END
Because the property sheet uses space on the heap, it is essential that you remove it before executing a CLEAR, CHAIN or RUN statement. Failure to do so is very likely to crash BBC BASIC for Windows.

Boxes and buttons

Windows™ programs containing push buttons, edit boxes and so on generally incorporate them within dialogue boxes. BBC BASIC for Windows supports this use by means of the WINLIB2 library. However it is perfectly possible to incorporate such items directly within your program's output window.

The WINLIB5 library contains a set of procedures and functions for incorporating push buttons, edit boxes etc. in your program without the need to create a dialogue box to contain them. The library should be loaded from your program using the command:

INSTALL @lib$+"WINLIB5"

The functions contained are:

The WINLIB5A library contains an identical set of functions but each takes an additional first parameter: the parent window handle. Use this library instead of WINLIB5 when you need to place the buttons, boxes or controls on a window other than your main output window. For example you might wish to place a combo box on a toolbar or rebar.

FN_button(text$,x%,y%,cx%,cy%,id%,style%)

This function creates a rectangular pushbutton. The string text$ specifies the text to appear inside the button, the values x% and y% specify the position of the button and the values cx% and cy% specify the size of the button (in pixels, where 0,0 is the top-left corner of the window).

The value id% is a unique identifier of the pushbutton, and can be any constant you choose (within reason) or a value returned from FN_setproc. The value style% can be zero, but other values allow you to modify the appearance or behaviour of the button, for example the value &100 causes the text to be left-justified rather than centered, 3 creates a checkbox and 9 creates a radiobutton.

Clicking on the button causes an ON SYS event in the same way as a menu selection, with @wparam% equal to the value of id%.

The function returns the window handle of the button, which is needed when removing the button with PROC_closewindow.

FN_combobox("",x%,y%,cx%,cy%,id%,style%)

This function creates a combo box. The text string is unused and should be set to an empty string, the values x% and y% specify the position of the box and the values cx% and cy% specify the size of the box (in pixels, where 0,0 is the top-left corner of the window).

The value id% is a unique identifier of the combo box, and can be any constant you choose (within reason) or a value returned from FN_setproc. The value style% can be zero, but other values allow you to modify the appearance or behaviour of the box, for example the value 3 creates a drop down list combo box. The function returns the window handle of the box.

To enter a list of strings into a combo box do:

SYS "SendMessage", hbox%, &143, 0, "Combo box item 0"
SYS "SendMessage", hbox%, &143, 0, "Combo box item 1"
etc.
where hbox% is the value returned from FN_combobox. An initial (default) selection can be made as follows:
SYS "SendMessage", hbox%, &14E, index%, 0
To determine which item is selected in a combo box do:
SYS "SendMessage", hbox%, &147, 0, 0 TO sel%

FN_editbox(text$,x%,y%,cx%,cy%,id%,style%)

This function creates an edit box. The string text$ specifies the initial contents of the box, the values x% and y% specify the position of the box and the values cx% and cy% specify the size of the box (in pixels, where 0,0 is the top-left corner of the window).

The value id% is a unique identifier of the edit box, and can be any constant you choose (within reason) or a value returned from FN_setproc. The value style% can be zero, but other values allow you to modify the appearance or behaviour of the box, for example the value &80 allows the contents of the box to scroll horizontally. The function returns the window handle of the box.

To read the contents of an edit box do:

DEF FNgettext(hbox%)
LOCAL text%
DIM text% LOCAL 65535
SYS "GetWindowText", hbox%, text%, 65535
= $$text%
where the parameter hbox% is the value returned from FN_editbox.

FN_listbox("",x%,y%,cx%,cy%,id%,style%)

This function creates a list box. The text string is unused and should be set to an empty string, the values x% and y% specify the position of the box and the values cx% and cy% specify the size of the box (in pixels, where 0,0 is the top-left corner of the window).

The value id% is a unique identifier of the list box, and can be any constant you choose (within reason) or a value returned from FN_setproc. The value style% can be zero, but other values allow you to modify the appearance or behaviour of the box, for example the value 2 causes the contents of the box to be sorted. The function returns the window handle of the box.

To enter a list of strings into a list box do:

SYS "SendMessage", hbox%, &180, 0, "List box item 0"
SYS "SendMessage", hbox%, &180, 0, "List box item 1"
etc.
where hbox% is the value returned by FN_listbox. To determine which item is selected do:
SYS "SendMessage", hbox%, &188, 0, 0 TO sel%
The value sel% gives the index (starting at 0) of the currently selected item or –1 if no item is selected.

FN_staticbox(text$,x%,y%,cx%,cy%,id%,style%)

This function creates a static box. The string text$ specifies the contents (if a text box), the values x% and y% specify the position of the box and the values cx% and cy% specify the size of the box (in pixels, where 0,0 is the top-left corner of the window).

The value id% is a unique identifier of the static box, and can be any constant you choose (within reason) or a value returned from FN_setproc. The value style% can be zero, but other values allow you to modify the appearance or behaviour of the box, for example the value &E indicates that the box will contain a bitmap image. The function returns the window handle of the box.

To load a bitmap image into a static box do:

SYS "LoadImage", 0, bmpfile$, 0, cx%, cy%, 16 TO hbitmap%
SYS "SendMessage", hbox%, &172, 0, hbitmap%
The value bmpfile$ is the name of a Windows™ bitmap file containing the image, cx% and cy% are the dimensions of the image in pixels and hbox% is the value returned from FN_staticbox. Once you have finished with the box (but not before) delete the bitmap handle:
SYS "DeleteObject", hbitmap%

FN_createwindow(class$,text$,x%,y%,cx%,cy%,id%,style%,exst%)

This function can be used to create child window types other than those described above. The parameters have the same meaning as for the other types except that class$ is the window class name and exst% is an extended window style. The function returns the window handle.

PROC_closewindow(hwnd%)

This procedure can be used to close (i.e. remove) any window created with the above functions. The value hwnd% is the window handle returned from the appropriate function.

PROC_setfocus(hwnd%)

This procedure can be used to control which window has the input focus (i.e. receives keyboard input). The value hwnd% is the handle of the window to receive the focus, which can be the value returned from one of the above functions or @hwnd% in order for your BASIC program to receive keyboard input again.

FN_setproc(PROCname)

This function (available in version 1.4 or later of WINLIB5) takes as a parameter the name of a procedure and returns an ID number which you can use when creating a control or a menu item. Using this function automates the process of executing a procedure when (for example) a button or menu item is clicked, without needing to use ON SYS. Here are some examples of its use:

To create a menu, in which selecting Open causes PROCopen to be executed and selecting Exit causes PROCexit to be executed:

SYS "CreatePopupMenu" TO hfile%
SYS "AppendMenu", hfile%, 0, FN_setproc(PROCopen), "&Open"
SYS "AppendMenu", hfile%, 0, FN_setproc(PROCexit), "E&xit"
To create buttons in a dialogue box, where clicking on Button 1 causes PROCbutton1 to be executed and clicking on Button 2 causes PROCbutton2 to be executed:
INSTALL @lib$+"WINLIB2"
dlg% = FN_newdialog("Button test", 200, 100, 100, 100, 8, 1000)
PROC_pushbutton(dlg%,"Button 1",FN_setproc(PROCbutton1),20,10,64,16,0)
PROC_pushbutton(dlg%,"Button 2",FN_setproc(PROCbutton2),20,32,64,16,0)
To create buttons on your main window, where clicking on Button 3 causes PROCbutton3 to be executed and clicking on Button 4 causes PROCbutton4 to be executed:
hbutt3% = FN_button("Button 3",300,20,100,24,FN_setproc(PROCbutton3),0)
hbutt4% = FN_button("Button 4",300,90,100,24,FN_setproc(PROCbutton4),0)
If you need to know the values of @wparam% and/or @lparam% (unlikely in the case of menu selections or button presses, but possible with other controls) you can arrange for them to be passed to your procedure by adding a pair of parentheses when you call FN_setproc, as follows:
hedit% = FN_editbox("",300,20,100,24,FN_setproc(PROCeditbox()),0)
You must then define your procedure to receive two parameters:
DEF PROCeditbox(W%, L%)
where W% and L% receive the values of @wparam% and @lparam% respectively.

Sprites

The SPRITELIB library contains a set of procedures and functions for creating and controlling sprites. A sprite is a graphical object which is displayed in front of, and independent of, whatever else is plotted in the BBC BASIC window. A sprite can have any shape and pattern: where it is opaque it will completely obscure what is behind and where it is transparent the normal window contents will show through. You can move a sprite around without having to worry about redrawing the background which is revealed.

The library should be loaded from your program using the command:

INSTALL @lib$+"SPRITELIB"

The functions contained are:

FN_initsprites(nsprites%)

This function initialises the sprite system. It requires one parameter: the maximum number of sprites which will need to be displayed. This parameter should be set as small as possible, since the larger the number of sprites the more the memory used, and the greater the slowing effect on the display.
IF FN_initsprites(2) = 0 STOP
The returned value is TRUE if the sprite system was initialised successfully and FALSE if not. The function will fail if the supplied parameter is zero, or if there is insufficient memory.

FN_createsprite(sprite%,file$,width%,height%)

This function creates a sprite of a given size and with a specified shape and appearance. It requires four parameters: the number of the sprite (which must be between zero and one less than the parameter supplied to FN_initsprites), the name of the file containing the sprite image (and transparency mask), the width of the sprite and the height of the sprite (both in pixels).

The file must be an Icon-format file (usually having the extension .ICO) generated using a suitable icon editor. BBC BASIC for Windows is supplied with a simple icon editor (ICONEDIT.BBC) which will suffice if no other editor is available. Under normal circumstances you should specify the same dimensions in the FN_createsprite call as were used when the icon was created, however if you do not do so the sprite will be scaled to the specified size (with some attendant loss of quality).

ok% = FN_createsprite(0, "bbcmicro.ico", 64, 64)
The returned value is non-zero if the sprite was created successfully, and zero otherwise. The most likely reason for the function to fail is if the file does not exist or is not a suitable icon-format file.

The sprite is not displayed until PROC_movesprite is executed.

PROC_movesprite(sprite%,xpos%,ypos%,show%)

This function displays (or hides) a sprite, and moves it to a specified position on the screen. It requires four parameters: the number of the sprite, the horizontal and vertical coordinates of the sprite (in BBC BASIC graphics units) and a value which determines whether the sprite should be displayed (1) or not (0). The coordinates refer to the top left-hand corner of the sprite (which is consistent with text characters plotted in the VDU 5 mode).
PROC_movesprite(0, 200, 200, 1)
Sprites are unaffected by the current graphics window (if any) and always display in front of any other graphics or text. Sprites have a predefined priority order such that, if they overlap, a higher-numbered sprite always appears in front of a lower-numbered sprite.

PROC_exitsprites

When you have finished with the sprites, you should disable them:
PROC_exitsprites
Because the sprite routines use space on the heap, it is essential that you call PROC_exitsprites before executing a CLEAR, CHAIN or RUN statement. Failure to do so is very likely to crash BBC BASIC for Windows.

Formatting and conversion

BBC BASIC does not include the PRINT USING statement which, in many dialects of BASIC, provides a convenient means of formatting numeric output. Although equivalent effects may be obtained by using the format-control variable @%, if necessary in conjunction with string-manipulation functions (e.g. MID$), they are less straightforward to achieve.

BBC BASIC also lacks the UPPER$ (or UCASE$) and LOWER$ (or LCASE$) functions provided in some dialects of BASIC to convert strings to uppercase (capitals) or lowercase characters respectively.

The FNUSING library provides replacements for these operations. It should be loaded from your program using the command:

INSTALL @lib$+"FNUSING"
Alternatively, since the functions are quite short, you might prefer to incorporate them in your own program (use the Insert command from the File menu).

The functions contained are:

FNusing(format$,value)

The FNusing function takes two parameters, a format string and a numeric value. It will normally be used within a PRINT statement in the following context:
PRINT FNusing(fmt1$,val1) FNusing(fmt2$,val2) .....
A significant difference from the conventional PRINT USING statement is that each format string can only refer to one numeric value, so you must call FNusing for each value you want to output.

The format string is a string literal or variable containing special formatting characters, as follows:

# The hash character is used to represent a digit position. Digit positions are always filled: if the number has fewer digits than positions specified it is right-justified (preceded by spaces) in the field. A decimal point may be inserted at any position in the field and numbers are rounded as necessary. For example:
PRINT FNusing("##.##",.78)
 0.78
PRINT FNusing("###.##",987.654)
987.65
+ A plus sign at the beginning or end of the format field causes the sign of the number (plus or minus) to be printed before or after the number. For example:
PRINT FNusing("+###.##",2.4)
  +2.40
PRINT FNusing("##.##+",55.678)
55.68+
PRINT FNusing("##.##+",-3)
 3.00-
A minus sign at the end of the format field causes negative numbers to be printed with a trailing minus sign. For example:
PRINT FNusing("##.##-",-68.95)
68.95-
PRINT FNusing("###.##-",-7)
  7.00-
** A double asterisk at the beginning of the format field causes leading spaces in the field to be filled with asterisks. The ** also specifies two more digit positions. For example:
PRINT FNusing("**#.#",12.39)
*12.4
PRINT FNusing("**##.##",-0.9)
**-0.90
$$ A double dollar (or pound) sign at the beginning of the format field causes a dollar (or pound) sign to be printed to the immediate left of the formatted number. The $$ also specifies two more digit positions, one of which is the currency symbol. For example:
PRINT FNusing("$$###.##",45.67)
  $45.67
PRINT FNusing("££###.##",123.45)
 £123.45
**$ A **$ (or **£) at the beginning of the format field combines the effects of the previous two formats. Leading spaces are filled with asterisks, and a dollar (or pound) sign is printed before the number. **$ specifies three more digit positions, one of which is the currency symbol. For example:
PRINT FNusing("**$##.##",2.34)
***$2.34
, A comma to the left of the decimal point in the format string causes a comma to be printed between every third digit before the decimal point. For example:
PRINT FNusing("#,###.##",1234.5)
1,234.50
PRINT FNusing("##,###,###",1E6)
 1,000,000
^^^^ Four carets may be placed after the digit characters to specify exponential format. The four carets allow space for "E-xx" to be printed. For example:
PRINT FNusing("##.##^^^^",234.56)
 2.35E2
PRINT FNusing("##.##^^^^",1E-30)
 1.00E-30
If the format string includes any characters other than those listed above, they are incorporated verbatim in the output string. For example:

PRINT FNusing("Price ££#.## including VAT",29.99)
Price £29.99 including VAT
If the number cannot be represented in the format supplied, question marks are printed:
PRINT FNusing("##.##",123)
?????

FNlower(string$)

The FNlower function takes a string parameter and returns a string in which capital letters (A to Z inclusive), if any, have been converted to lowercase (a to z).
PRINT FNlower("The Quick Brown Fox")
the quick brown fox

FNupper(string$)

The FNupper function takes a string parameter and returns a string in which lowercase letters (a to z inclusive), if any, have been converted to capitals (A to Z).
PRINT FNupper("The Quick Brown Fox")
THE QUICK BROWN FOX

Multiple Document Interface

The MDILIB library contains a set of procedures and functions for using the Windows™ Multiple Document Interface (MDI). This allows a program to open multiple output windows, typically so the user can work with more than one document at a time or with more than one type of view. For example a spreadsheet program might use one window for a tabular view of the data and another window for a graphical view.

The library should be loaded from your program using the command:

INSTALL @lib$+"MDILIB"

The functions contained are:

PROC_initmdi(menu%)

This procedure initialises the Multiple Document Interface. It requires one parameter: the handle of the Window top-level menu. Every MDI program must have a Window menu, because the contents of that menu are automatically modified by the operating system as child windows are added and removed. Therefore as a minimum your program should include code similar to the following:
SYS "CreatePopupMenu" TO hwindow%
SYS "CreateMenu" TO hmenu%
SYS "AppendMenu", hmenu%, 16, hwindow%, "&Window"
SYS "SetMenu", @hwnd%, hmenu%
SYS "DrawMenuBar", @hwnd%
PROC_initmdi(hwindow%)
See the section on adding popup and sub-menus for more details. PROC_initmdi must be called just once at the start of your program; once it has been called no output to the screen is possible until you have created one or more child windows (if necessary you can still display information using a message box).

PROC_exitmdi

This procedure shuts down the Multiple Document Interface. It must be called before your program exits and before executing a CLEAR, CHAIN or RUN statement; failure to do so is very likely to crash BBC BASIC for Windows. You are advised to include ON CLOSE and ON ERROR statements in your program so that you can ensure PROC_exitmdi is called as necessary.

FN_createmdichild(name$)

This function creates a child window and gives it the specified name; the name is displayed in the title bar of the child window and in the Window menu. If the child window is maximised it no longer has a title bar, but the name is then automatically appended to the main program's title.

The child window is created with a default size and position, but your program can change that subsequently if necessary. It can be minimised, maximised, re-sized and moved by the user in the usual way, except that the child window is constrained to remain within the confines of your program's main window. You can create as many child windows as you like (within reason!).

FN_createmdichild returns the window handle of the child window it has created:

hwnd1% = FN_createmdichild("Hello world")
hwnd2% = FN_createmdichild("Rectangles")
hwnd3% = FN_createmdichild("Circles")
Once you have created a child window you can send output to it in the same way as you would to BASIC's normal output window, however you must first select the appropriate window by changing the values of @hwnd% and @memhdc%. The easiest way of doing that (and of ensuring they are restored to their original values afterwards) is to pass them as parameters of a procedure. So for example a procedure for writing text to a child window might be:
DEF PROCwritetext(A$, @hwnd%, @memhdc%)
PRINT A$
ENDPROC
To write a text string to a particular window you would call this procedure as follows:
PROCwritetext("Some text", hwnd1%, FN_hdc(hwnd1%))
(see below for a description of FN_hdc)

As it stands this will work fine when writing text to just one window, but if you write text to two or more windows concurrently you will find that all the windows share the same text output position, so sending a 'newline' to one window will affect the subsequent position of text written to another window. One way to provide each child window with its own 'private' text position is to pass the X and Y coordinates to the procedure:

DEF PROCwritetext(A$, @hwnd%, @memhdc%, RETURN @vdu%!48, RETURN @vdu%!52)
PRINT A$
ENDPROC
which you would call as follows:
PROCwritetext("Text for window 1", hwnd1%, FN_hdc(hwnd1%), xpos1%, ypos1%)
PROCwritetext("Text for window 2", hwnd2%, FN_hdc(hwnd2%), xpos2%, ypos2%)
Since they are passed by reference the variables containing the text coordinates for each window are automatically updated. All the other text and graphics parameters (colour, plotting mode, window positions etc.) are similarly shared between the child windows, so you may need to set the parameters appropriate to that window before performing any output.

Note that (in BBC BASIC for Windows) MDI child windows do not display the text cursor (caret). User input, when required, should normally be done via a dialogue box.

FN_hdc(hwnd%)

This function returns the memory hdc belonging to the specified window handle. This is needed when selecting a particular child window for output (it must be copied into @memhdc%).

PROC_closemdichild(hwnd%)

This procedure closes the specified child window. It has the same effect as the user clicking on the window's close button or using the keyboard shortcut Ctrl+f4.

You cannot prevent the user closing a child window (there is no direct equivalent to ON CLOSE) but you can detect that he has closed it using the IsWindow API call:

SYS "IsWindow", hwnd1% TO res%
IF res% = 0 THEN
  REM child window has been closed
  REM take appropriate action if necessary
ENDIF

PROC_tile

This procedure causes Windows™ to tile all the currently-open child windows within your program's output window. It would typically be called in response to the user selecting the Tile item in the Window menu.

PROC_cascade

This procedure causes Windows™ to cascade all the currently-open child windows within your program's output window. It would typically be called in response to the user selecting the Cascade item in the Window menu.

Calendar functions

The DATELIB library contains a set of procedures and functions for performing operations on dates. The library should be loaded from your program using the command:
INSTALL @lib$+"DATELIB"

The functions contained are:

FN_mjd(day%, month%, year%)

This function takes a date (consisting of a day-of-month, a month and a year) and converts it to the corresponding Modified Julian Day number. The Modified Julian Day is a count of days starting from Wednesday 17th November 1858 (which is MJD 0). Days prior to that date have negative MJD numbers. You can easily calculate the number of days between two different dates by subtracting their Modified Julian Day numbers.

The parameters supplied are the day of the month (1-31), the month number (1-12) and the year number (1-9999). Note that the functions in the DATELIB library will behave consistently for any date in that range (for example, converting from DMY to MJD and back will return the original values) but should not normally be used for dates prior to the introduction of the Gregorian calendar (in the UK on Thursday 14th September 1752, MJD –38779). For earlier dates the day, month and year values may not be correct, and since use of the old Julian calendar persisted in some countries until as late as 1927 care should be taken when using this function.

FN_day(mjd%)

This function takes a Modified Julian Day number and returns the day-of-month (1-31) to which it corresponds.

FN_month(mjd%)

This function takes a Modified Julian Day number and returns the month (1-12) to which it corresponds.

FN_year(mjd%)

This function takes a Modified Julian Day number and returns the year (1-9999) to which it corresponds.

FN_dow(mjd%)

This function takes a Modified Julian Day number and returns the day-of-week (0-6, where 0 is Sunday) to which it corresponds.

FN_dim(month%, year%)

This function takes a month (1-12) and a year (1-9999) and returns the number of days in the month (in the range 28 to 31). By setting month% to 2 (February) you can use this function to determine whether the year is a Leap Year.

FN_today

This function returns the Modified Julian Day number corresponding to today's date (assuming the PC's clock is correctly set).

FN_date$(mjd%, format$)

This function takes a Modified Julian Day number and a format string, and returns a formatted string containing the date. The format string can contain any of the following codes:
dDay of month as digits with no leading zero.
ddDay of month as digits with leading zero for single-digit days.
dddDay of week as a three-letter abbreviation.
ddddDay of week as its full name.
MMonth as digits with no leading zero.
MMMonth as digits with leading zero for single-digit months.
MMMMonth as a three-letter abbreviation.
MMMMMonth as its full name.
yYear as last two digits, but with no leading zero.
yyYear as last two digits, but with leading zero for years less than 10.
yyyyYear represented by full four digits.
For example:
date$ = FN_date$(mjd%, "ddd dd MMM yyyy")
will return a string of the form "Sun 22 Feb 2004".

FN_readdate(date$, code$, minyear%)

This function parses a string containing a date, and returns the corresponding Modified Julian Day number. In addition to the date you must supply a string containing one of the following codes: "dmy", "mdy", "ymd", "ydm", "dym" or "myd"; this informs the function of the order in which the various elements of the date (day, month, year) are present in the string. If the year is specified as a four digit number the third parameter is not used; if only the last two digits of the year are specified the third parameter is the minimum year number to return. For example if the third parameter is 1950, two digit year numbers correspond to the years 1950 to 2049 inclusive.

The FN_readdate function attempts to make sense of the date string however it is formatted, so long as the elements are in the specified order. For example it will accept "22/2/2004", "22 Feb 04", "22-02-04" etc. If it cannot make sense of the string it will return the value &80000000.

Direct3D graphics

The D3DLIB library contains a set of procedures and functions for displaying and animating three-dimensional graphics. It provides an interface to Microsoft's Direct3D™ and requires DirectX version 8.0 or later to be installed. The library should be loaded from your program using the command:
INSTALL @lib$+"D3DLIB"

The functions contained are:

FN_initd3d(hw%,cull%,light%)

This function initialises the Direct3D system and returns a pointer to a Direct3D device. If the returned value is zero it probably indicates that DirectX 8.0 or later is not installed.

The value hw% is the handle of the window which is to contain the 3D graphics. It can be set to @hwnd% if the graphics are to be displayed in BBC BASIC's main output window or to the handle of a child window (for example as returned from FN_staticbox) if you want them to appear in a separate frame. Note that you cannot mix Direct3D graphics and normal BBC BASIC output (text or graphics) in the same window.

The value cull% specifies the culling mode, which determines whether surfaces behave as single sided or double sided. Possible values are 1 (none), 2 (clockwise) or 3 (counterclockwise). If in doubt, set to 1.

The value light% determines whether Direct3D's lighting engine is enabled. Set to 1 to enable lighting or to 0 to disable lighting. When lighting is disabled all objects appear normally as if uniformly illuminated. When lighting is enabled it is necessary for all objects to include surface normals in the vertex description.

FN_load3d(pdev%,file$,num%,fmt%,size%)

This function loads an object or scene (comprising a set of triangles, each consisting of three vertices) from a file. It returns a pointer to a vertex buffer; if zero is returned the file could not be opened.

The value pdev% is the pointer returned from FN_initd3d. The value file$ is the name of a file containing vertex data.

The values num%, fmt% and size% are outputs from FN_load3d and are set to the number of vertices, the vertex format and the size in bytes of each vertex respectively.

The file format is as follows:

Number of vertices (4 bytes, LSB first)
Vertex format (2 bytes, LSB first)
Vertex size in bytes (2 bytes, LSB first)
Data for each vertex (see below)

Vertex description

The vertex data must conform to the requirements of Direct3D's flexible vertex format. Each vertex consists of one or more of the following items, in the specified order:
CodeSizeDataComments
&00212XYZ position Always required
&01012Surface normal When lighting used
&0404Diffuse colour When neither texture nor material specified
&0804Specular colour For shiny objects
&1008UV texture coordinates When texture specified
To obtain the vertex format code add together the codes for the items included. To obtain the vertex size add together the sizes of the items included. The XYZ position and surface normal items each consist of three 4-byte floating point numbers (see FN_f4). The diffuse colour and specular colour items each consist of 4-byte colour values (&FFrrggbb). The texture coordinates consist of a pair of 4-byte floating point numbers. The simplest vertex description consists of an XYZ position and a diffuse colour (format &042; size 16 bytes). See FN_f4 for an example of creating a file in this format. Refer to Microsoft documentation for more details.

FN_loadtexture(pdev%,file$)

This function loads a texture map from an image file (BMP, ICO, GIF, JPEG, EMF or WMF format) and returns a pointer to the texture; if zero is returned the file could not be opened or recognised. The filename must begin with a drive letter to distinguish it from a web URL.

The value pdev% is the pointer returned from FN_initd3d. The value file$ is the name of the image file.

The image will be padded to a size of 2^n pixels in both horizontal and vertical directions.

PROC_release(pobj%)

This function releases an object (Direct3D device, vertex buffer or texture) when it is no longer required. It should be used to free the resources used by the object(s), otherwise you may eventually run out of memory.

The value pobj% is the pointer returned from FN_initd3d, FN_load3d or FN_loadtexture.

FN_f4(num)

This function converts a number to a 4-byte (single precision) floating point value. All floating point values used by Direct3D (e.g. within vertex descriptions) are in this format. As an illustration of its use the following code segment creates a file containing a single triangle consisting of three vertices, suitable for being loaded by FN_load3d:
F% = OPENOUT"TRIANGLE.B3D"
PROC4(3):REM 3 vertices
PROC4(&100042):REM vertex size &10 and format &42
PROC4(FN_f4(-1.0)):PROC4(FN_f4(-1.0)):PROC4(FN_f4(1.0)):PROC4(&FF0000FF)
PROC4(FN_f4(1.0)):PROC4(FN_f4(-1.0)):PROC4(FN_f4(1.0)):PROC4(&FF00FF00)
PROC4(FN_f4(0.0)):PROC4(FN_f4(1.0)):PROC4(FN_f4(0.0)):PROC4(&FFFF0000)
CLOSE #F%
DEF PROC4(A%):BPUT#F%,A%:BPUT#F%,A%>>8:BPUT#F%,A%>>16:BPUT#F%,A%>>24:ENDPROC

PROCrender(pdev%,....)

This procedure draws a 2D view of the 3D world to the screen. It takes 23 parameters as follows:
pdev% The value returned from FN_initd3d.
bcol% The background colour (&FFrrggbb).
nlight% The number of lights. Set to zero if lighting is not used.
light%() An array of pointers to D3DLIGHT8 structures (see note 1).
nobj% The number of objects (i.e. vertex buffers).
mat%() An array of pointers to D3DMATERIAL8 structures (see note 2).
tex%() An array of texture pointers (e.g. returned from FN_loadtexture).
vbuf%() An array of vertex buffer pointers (e.g. returned from FN_load3d).
vnum%() An array of vertex counts (e.g. returned from FN_load3d).
vfmt%() An array of vertex format codes (e.g. returned from FN_load3d).
vsize%() An array of vertex sizes (e.g. returned from FN_load3d).
yaw() An array of yaw angles (rotations about the Y-axis).
pitch() An array of pitch angles (rotations about the X-axis).
roll() An array of roll angles (rotations about the Z-axis).
X() An array of translations along the X-axis.
Y() An array of translations along the Y-axis.
Z() An array of translations along the Z-axis.
eye() An array eye(0), eye(1), eye(2) holding the XYZ coordinates of the eye or camera.
look() An array look(0), look(1), look(2) holding the XYZ coordinates of a point on the eyeline.
fov The vertical field-of-view in radians (equivalent to the camera's zoom).
ar The aspect ratio of the 3D graphics window (width/height).
zn The distance from the camera to the near plane (objects nearer than this are invisible).
zf The distance from the camera to the far plane (objects further away than this are invisible).
Notes:
  1. A Direct3D directional light can be created as follows. Refer to Microsoft documentation for other light types.
    DIM light%(0) 103
    light%(0)!0 = 3 : REM directional light
    light%(0)!4 = FN_f4(1)  : REM red component
    light%(0)!8 = FN_f4(1)  : REM green component
    light%(0)!12 = FN_f4(0) : REM blue component
    light%(0)!64 = FN_f4(0) : REM. X component of direction
    light%(0)!68 = FN_f4(0) : REM. Y component of direction
    light%(0)!72 = FN_f4(1) : REM. Z component of direction
    
  2. A matt Direct3D material can be created as follows. Refer to Microsoft documentation for other material types.
    DIM mat%(0) 67
    mat%(0)!0 = FN_f4(1) : REM red component of colour
    mat%(0)!4 = FN_f4(1) : REM green component of colour
    mat%(0)!8 = FN_f4(1) : REM blue component of colour
    
  3. The arrays of object parameters should contain at least as many elements as the value of nobj%. Unused arrays, for example mat%() or tex%(), should contain zeros (this is the initial state following DIM).

  4. Rotations take place around world axes in the order roll then pitch then yaw.

Plotting angled ellipses

The BBC BASIC for Windows ELLIPSE statement plots only axis-aligned ellipses. The ELLIPSE library contains the procedures PROCellipse and PROCellipsefill which provide the facility to plot an outline or filled ellipse rotated by a specified angle. The library should be loaded from your program using the command:
INSTALL @lib$+"ELLIPSE"
Alternatively, since the procedures are quite short, you might prefer to incorporate them in your own program (use the Insert command from the File menu).

PROCellipse(x,y,a,b,angle)

This procedure plots an outline ellipse centred at graphics coordinates x,y and with radii of length a and b. The fifth parameter specifies that the ellipse should be rotated anticlockwise by angle radians (if the angle is zero the ellipse is plotted with the a axis horizontal, as with the normal ELLIPSE statement).

The ellipse is drawn in the current graphics foreground colour and mode, as specified by GCOL.

PROCellipsefill(x,y,a,b,angle)

This procedure works in an identical fashion to PROCellipse, except that a filled (solid) ellipse is plotted. Again, the ellipse is drawn in the current graphics foreground colour and mode, as specified by GCOL.

Sorting data arrays

The SORTLIB library provides a fast, highly optimised and flexible means of sorting data contained in arrays. SORTLIB will sort byte arrays, integer arrays, floating point arrays (both *FLOAT40 and *FLOAT64) and string arrays. The library should be loaded from your program using the command:
INSTALL @lib$+"SORTLIB"
It contains the single function FN_sortinit.

FN_sortinit(dir%,smode%)

Before any sorting operations can be carried out the library must be initialised as follows:
sort% = FN_sortinit(dir%,smode%)
where dir% determines the sorting direction (0 = ascending, 1 = descending) and smode% determines how strings are sorted (0 = normal, 1 = ignore case).

If you prefer, you can initialise it four times with the different options and then CALL the appropriate variable when needed:

sortascendingnormal% = FN_sortinit(0,0)
sortdescendingnormal% = FN_sortinit(1,0)
sortascendingignorecase% = FN_sortinit(0,1)
sortdescendingignorecase% = FN_sortinit(1,1)
To sort the contents of an entire array do the following:
C% = DIM(array(),1)+1 : CALL sort%, array(0)
To sort the contents of a '1-based' array (where the first element has the subscript 1) do the following:
C% = DIM(array(),1) : CALL sort%, array(1)
To sort only part of an array, set C% to the number of elements you want to sort and specify the first element to be sorted:
C% = howmany% : CALL sort%, array(first%)
To sort multiple arrays according to the contents of a key array do the following:
C% = size% : CALL sort%, keyarray(0), array2$(0), array3%(0)...
There can be any number of dependent arrays of any type. If the primary key array contains two or more identical elements, the remaining array(s) will be used as secondary keys, in the order specified.

To sort a two-dimensional array, where the contents of the first row are used as a key for the other rows, do the following:

DIM array(2,999)
C% = 1000 : CALL sort%, array(0,0), array(1,0), array(2,0)

Socket (network) connections

The SOCKLIB library contains a set of procedures and functions for making socket connections to a remote computer via a local area network or the internet, and transferring data in both directions.

The library should be loaded from your program using the command:

INSTALL @lib$+"SOCKLIB"

The functions contained are:

PROC_initsockets

PROC_initsockets must be called (once) before any other functions in SOCKLIB are accessed.

PROC_exitsockets

PROC_exitsockets must be called after all socket operations have been completed. You should normally incorporate ON ERROR and ON CLOSE statements in your program to ensure that PROC_exitsockets is called even if the program is terminated unexpectedly. If you don't do so your program might not work correctly if executed a second time.

FN_gethostname

FN_gethostname returns, as a string, the network name of your local computer (the one on which BBC BASIC for Windows is running).

FN_tcplisten(host$,port$)

FN_tcplisten creates a listening socket to listen for incoming TCP/IP connections. If your program needs to respond to connections made from a remote computer (a web server would be an example) it must call FN_tcplisten. The function takes two string parameters: the name of the local computer (typically as returned from FN_gethostname) and the port name or number on which you want to listen. For example if you are listening for incoming HTTP (Hyper Text Transport Protocol) connections you would normally set port$ to "80".

If the listening socket is created successfully the socket number is returned. If the call fails a negative number is returned (see the code of SOCKLIB.BBC for details). You can call FN_socketerror to discover more about the error.

FN_tcpconnect(host$,port$)

FN_tcpconnect makes a TCP/IP connection to a remote machine. You would call this function if your program needs to initiate a connection (a program to send email would be an example). The function takes two string parameters: the name or IP address of the remote computer and the port name or number on which to make the connection. For example if you were connecting to an SMTP mail server would would normally set port$ to "25" or "mail". If an IP address rather than a machine name is specified, it must be supplied as a string of the form "xx.xx.xx.xx".

If the connection is made successfully the socket number is returned. If the call fails a negative number is returned (see the code of SOCKLIB.BBC for details). You can call FN_socketerror to discover more about the error.

FN_check_connection(socket%)

FN_check_connection checks to see if an incoming connection has been received by a listening socket; it takes as a parameter the socket number (as returned from FN_tcplisten). If no connection has been received zero is returned, and the listening socket continues to listen for a connection. If an incoming connection has been received, FN_check_connection returns the socket number on which the connection has been opened. Note that if you want to listen for further connections, another listening socket must be opened.

FN_writesocket(socket%,buffer%,size%)

FN_writesocket writes data to a connected socket. It takes three parameters: the socket number (as returned from FN_tcpconnect or FN_check_connection), the address of a buffer containing the data, and the length of the data in bytes. If the function succeeds it returns the number of bytes sent. If the socket has been disconnected (e.g. by the remote computer) –1 is returned.

Note that it is possible for FN_writesocket to return a value less than the total length of the data. This indicates that only some of the data has been sent, and you should make further calls (adjusting the values of buffer% and size% accordingly) until all the data has been sent.

FN_writelinesocket(socket%,string$)

FN_writelinesocket writes a string, followed by the characters carriage return (CHR$13) and line feed (CHR$10), to a connected socket. It takes two parameters: the socket number (as returned from FN_tcpconnect or FN_check_connection) and the string to be sent. If the function succeeds it returns zero. If the socket has been disconnected (e.g. by the remote computer) –1 is returned.

FN_readsocket(socket%,buffer%,size%)

FN_readsocket reads data from a connected socket. It takes three parameters: the socket number (as returned from FN_tcpconnect or FN_check_connection), the address of a buffer to receive the data, and the maximum length of the data in bytes (the size of the buffer). If the function succeeds it returns the number of data bytes received. If the socket has been disconnected (e.g. by the remote computer) –1 is returned.

FN_readsocket does not wait for data to be received. If no data has been received since the last call, it returns zero.

FN_readlinesocket(socket%,maxtime%,string$)

FN_readlinesocket reads a line from a connected socket (a line is defined as a string of characters terminated by CRLF, LFCR or LF, where CR signifies carriage return and LF signifies line feed); the terminator is not returned as part of the string. It takes three parameters: the socket number (as returned from FN_tcpconnect or FN_check_connection), a maximum time to wait in centiseconds (e.g. 100 signifies one second) and the name of the string variable in which the line will be returned. If the function succeeds it returns the length of the string in characters. If the socket has been disconnected (e.g. by the remote computer) –1 is returned.

FN_getpeername(socket%)

FN_getpeername returns the IP address of the remote machine to which a socket is connected, as a string of the form "xx.xx.xx.xx". It takes as a parameter the socket number (as returned from FN_tcpconnect or FN_check_connection). If the function fails an empty string is returned.

PROC_closesocket(socket%)

PROC_closesocket closes a socket connection. It takes as a parameter the socket number (as returned from FN_tcpconnect or FN_check_connection).

FN_socketerror

FN_socketerror returns the number of the most recent socket error to have occurred (if any). If FN_tcpconnect or FN_tcplisten returns a negative number, you can call FN_socketerror to discover more details of the error.

The error code numbers can be found in the Microsoft™ file WINERROR.H.

Antialiased graphics

The built-in BBC BASIC graphics statements, and graphics drawn using the Windows™ Graphics Device Interface (GDI), are not antialiased. As a result things like curves and gently-sloping lines can have aliases (or jaggies) causing the appearance to be impaired.

The GDIPLIB library contains a set of procedures and functions for drawing antialiased graphics. It relies upon the presence of the Microsoft GDI+ graphics subsystem, which is installed as standard on