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 without having to modify each of the programs in which it is used.
You can build up your own set of such files, but BBC BASIC for Windows is supplied with the following libraries:
The library should be loaded from your program using the statement:
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.When executed this program will print:DIM N(3) N() = 1, 2, 3, 4 PROC_add(N(), 5) PRINT N(0),N(1),N(2),N(3)
6 7 8 9
When executed this program will print:DIM N(3) N() = 1, 2, 3, 4 PROC_mul(N(), 2) PRINT N(0),N(1),N(2),N(3)
2 4 6 8
When executed this program will print: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)
6 8 10 12
When executed this program will print: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)
40 46
When executed this program will print: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)
1 4 2 5 3 6
When executed this program will print: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)
0.09184 0.10204 -0.02041 -0.19048 0.04762 0.19048 0.13605 -0.03401 0.00680Note that PROC_invert is suitable only for 3x3 matrices or larger. To invert a 2x2 matrix M() use:
M() = M(1,1),-M(0,1), -M(1,0),M(0,0) M() /= M(0,0)*M(1,1) - M(0,1)*M(1,0)
When executed this program will print:DIM M(2,2) M() = 2,0,6,8,1,-4,0,5,7 PRINT FN_mod(M())
13.96424
When executed this program will print:DIM M(2,2) M() = 2,0,6,8,1,-4,0,5,7 PRINT FN_det(M())
294
The library should be loaded from your program using the statement:
INSTALL @lib$+"WINLIB"
The functions contained are:
The following program segment creates a status bar containing the string "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.hstat% = FN_createstatusbar("Press F1 for help")
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:
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.ON MOVE SYS "PostMessage",hstat%,@msg%,@wparam%,@lparam% : RETURN
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.
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.SB_SETTEXT = 1025 SB_SETPARTS = 1028 nparts% = 3 DIM edges%(nparts%-1) FOR N% = 0 TO nparts%-1 READ edges%(N%) NEXT N% SYS "SendMessage", hstat%, SB_SETPARTS, nparts%, ^edges%(0) FOR N% = 0 TO nparts%-1 READ text$ SYS "SendMessage", hstat%, SB_SETTEXT, N%, CHR$9+text$ NEXT N% DATA 440, 540, 640 DATA Part 1, Part 2, Part 3
The following program segment creates a toolbar containing three buttons: a cut button, a copy button and a paste button:
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; the corresponding entry in the button% array contains a value (normally zero) by which the width of the separator is increased, in pixels.nbutts% = 3 DIM button%(nbutts%-1), buttid%(nbutts%-1) button%() = 0, 1, 2 buttid%() = 100, 101, 102 htool% = FN_createtoolbar(nbutts%,button%(),buttid%())
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:
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.
Value Button 0 Cut 1 Copy 2 Paste 3 Undo 4 Redo 5 Delete 6 New 7 Open 8 Save 9 Find 10 Print Preview 11 Help 12 Find 13 Replace 14 15 no image
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:
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.ON MOVE SYS "PostMessage",htool%,@msg%,@wparam%,@lparam% : RETURN
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:
Some of the possible button states are:TB_SETSTATE = 1041 state% = 0 SYS "SendMessage", htool%, TB_SETSTATE, buttid%, state%
You can discover the current state of a button as follows:
0 Disabled 4 Enabled 6 Pressed 8 Hidden
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.TB_GETSTATE = 1042 SYS "SendMessage", htool%, TB_GETSTATE, buttid%, 0 TO state%
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:
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.ok% = FN_custombutton(htool%, "\pictures\bmp\owl.bmp", 102)
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:
The buttid% array may often be the same as that passed to FN_createtoolbar.nbutts% = 3 DIM buttip$(nbutts%-1), buttid%(nbutts%-1) buttip$() = "Cut", "Copy", "Paste" buttid%() = 100, 101, 102 PROC_addtooltips(htool%,nbutts%,buttip$(),buttid%())
The library should be loaded from your program using the statement:
The functions contained are:INSTALL @lib$+"WINLIB2"
The WINLIB2U library contains an identical set of functions to WINLIB2B except that those receiving a text string assume it to be in Unicode (UTF-8) format, allowing non-ANSI (e.g. foreign language) characters to be incorporated.
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.
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.dlg% = FN_newdialog(title$,x%,y%,cx%,cy%,font%,size%)
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 modifying the style after the call to FN_newdialog as follows:
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.dlg% = FN_newdialog("",x%,y%,cx%,cy%,font%,size%) WS_BORDER = &800000 dlg%!16 AND= NOT WS_BORDER
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.
All dialogue boxes should have at least one pushbutton, labelled OK, which enables the user to confirm that his input is complete:
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).PROC_pushbutton(dlg%,text$,id%,x%,y%,cx%,cy%,style%)
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. Alternatively you can set this parameter to a value returned from FN_setproc (in WINLIB5 or WINLIB5A) which will cause the specified procedure to be called when the button is clicked.
The value style% can be zero, but other values allow you to modify the appearance or behaviour of the pushbutton. Setting it to 1 (BS_DEFPUSHBUTTON) 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 (WS_GROUP) 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 (BS_BITMAP) 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.
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.PROC_checkbox(dlg%,text$,id%,x%,y%,cx%,cy%,style%)
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 (BS_LEFTTEXT) causes the associated text to appear to the left of the check box rather than to the right. Setting it to &20000 (WS_GROUP) 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.
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.PROC_radiobutton(dlg%,text$,id%,x%,y%,cx%,cy%,style%)
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 (BS_LEFTTEXT) causes the associated text to appear to the left of the radio button rather than to the right. Setting it to &20000 (WS_GROUP) 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.
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.PROC_groupbox(dlg%,text$,id%,x%,y%,cx%,cy%,style%)
The value style% may be zero, but will often have the value &20000 (WS_GROUP) signifying that the group box is the first item in a new group.
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.PROC_editbox(dlg%,text$,id%,x%,y%,cx%,cy%,style%)
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 (ES_AUTOHSCROLL) causes the contents of the edit box to scroll horizontally, if necessary. Setting it to &2000 (ES_NUMBER) causes the edit box to accept only numeric input. Setting it to &20000 (WS_GROUP) causes the edit box to be the first item in a new group. Setting it to &1004 (ES_WANTRETURN + ES_MULTILINE), along with an appropriate vertical size, creates a multi-line edit box. The values can be combined.
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.PROC_static(dlg%,text$,id%,x%,y%,cx%,cy%,style%)
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 (SS_CENTER) causes the text to be centred within the rectangle and setting it to 2 (SS_RIGHT) causes the text to be right-justified within the rectangle. Setting style% to &E (SS_BITMAP) 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.
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.PROC_listbox(dlg%,"",id%,x%,y%,cx%,cy%,style%)
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 (WS_GROUP) causes the list box to be the first item in a new group. To disable automatic sorting of the listbox contents subtract 2 (LBS_SORT) from the style% value you would otherwise have used.
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 vertical scroll bar is automatically generated. Adding &100200 (WS_HSCROLL + LBS_MULTICOLUMN) to style% instead results in the list box having a horizontal scroll bar, and the items are presented in columns.
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.PROC_combobox(dlg%,"",id%,x%,y%,cx%,cy%,style%)
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 (CBS_DROPDOWNLIST) 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 (CBS_SORT) causes the items in the list to be sorted into alphabetical order. Setting it to &20000 (WS_GROUP) 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.
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_dlgitem(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, trackbars and progress bars:PROC_dlgctrl(dlg%,text$,id%,x%,y%,cx%,cy%,style%,class$)
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).PROC_dlgctrl(dlg%,"",id%,0,0,0,0,&50000096,"msctls_updown32")
To set the range of the up-down control (which must be done after the PROC_showdialog):
UDM_SETRANGE = 1125 SYS "SendDlgItemMessage", !dlg%, id%, UDM_SETRANGE, 0, (min% << 16) + max%
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:PROC_dlgctrl(dlg%,"",id%,x%,y%,cx%,cy%,&50000000,"msctls_trackbar32")
To set the range of the trackbar (which must be done after the PROC_showdialog):
Style Name Effect 1 TBS_AUTOTICKS Show tick marks 2 TBS_VERT Vertical trackbar 4 TBS_LEFT Tick marks to top or left (default is bottom or right) 8 TBS_BOTH Tick marks on both sides
To set the current position of the trackbar:TBM_SETRANGE = 1030 SYS "SendDlgItemMessage", !dlg%, id%, TBM_SETRANGE, 1, (max% << 16) + min%
To read the current position of the trackbar:TBM_SETPOS = 1029 SYS "SendDlgItemMessage", !dlg%, id%, TBM_SETPOS, 1, position%
TBM_GETPOS = 1024 SYS "SendDlgItemMessage", !dlg%, id%, TBM_GETPOS, 0, 0 TO position%
The above example creates a horizontal progress bar. To alter the style of the progress bar add one or more of the following values to the &50000000:PROC_dlgctrl(dlg%,"",id%,x%,y%,cx%,cy%,&50000000,"msctls_progress32")
To set the range of the progress bar (which must be done after the PROC_showdialog):
Style Name Effect 1 PBS_SMOOTH Smooth (rather than segmented) progress bar 4 PBS_VERTICAL Vertical progress bar
To set the current position of the progress bar:PBM_SETRANGE = 1025 SYS "SendDlgItemMessage", !dlg%, id%, PBM_SETRANGE, 0, (max% << 16) + min%
To 'step' the progress bar:PBM_SETPOS = 1026 SYS "SendDlgItemMessage", !dlg%, id%, PBM_SETPOS, position%, 0
PBM_STEPIT = 1029 SYS "SendDlgItemMessage", !dlg%, id%, PBM_STEPIT, 0, 0
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.PROC_showdialog(dlg%)
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:
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.SYS "SetDlgItemText", !dlg%, id%, text$
If the item is an edit box used for numeric entry, the value displayed in the box can be set with SetDlgItemInt:
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).SYS "SetDlgItemInt", !dlg%, id%, value%, signed%
If the value is controlled by an up-down control you can set the allowed range as follows:
where id% is the identifier for the up-down control, min% is the lowest value permitted and max% is the highest value permitted.UDM_SETRANGE = 1125 SYS "SendDlgItemMessage", !dlg%, id%, UDM_SETRANGE, 0, (min% << 16) + max%
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 STM_SETIMAGE (370) to BM_SETIMAGE (247):
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:LR_LOADFROMFILE = 16 STM_SETIMAGE = 370 SYS "LoadImage", 0, bmpfile$, 0, cx%, cy%, LR_LOADFROMFILE TO hbitmap% SYS "SendDlgItemMessage", !dlg%, id%, STM_SETIMAGE, 0, hbitmap%
SYS "DeleteObject", hbitmap%
To enter the list of strings into a list box you should do the following:
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 (LBS_SORT) from the style% value you would otherwise have used (see PROC_listbox).LB_ADDSTRING = 384 SYS "SendDlgItemMessage", !dlg%, id%, LB_ADDSTRING, 0, "Listbox item 0" SYS "SendDlgItemMessage", !dlg%, id%, LB_ADDSTRING, 0, "Listbox item 1" SYS "SendDlgItemMessage", !dlg%, id%, LB_ADDSTRING, 0, "Listbox item 2" etc.
To empty a list box of its contents do:
LB_RESETCONTENT = 388 SYS "SendDlgItemMessage", !dlg%, id%, LB_RESETCONTENT, 0, 0
To enter the list of strings into a combo box you should do the following:
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:CB_ADDSTRING = 323 SYS "SendDlgItemMessage", !dlg%, id%, CB_ADDSTRING, 0, "Combobox item 0" SYS "SendDlgItemMessage", !dlg%, id%, CB_ADDSTRING, 0, "Combobox item 1" SYS "SendDlgItemMessage", !dlg%, id%, CB_ADDSTRING, 0, "Combobox item 2" etc.
The value index% determines which of the items is initially selected (starting at 0).CB_SETCURSEL = 334 SYS "SendDlgItemMessage", !dlg%, id%, CB_SETCURSEL, index%, 0
To empty a combo box of its contents do:
CB_RESETCONTENT = 331 SYS "SendDlgItemMessage", !dlg%, id%, CB_RESETCONTENT, 0, 0
To initialise the state of a set of radio buttons, you can use the CheckRadioButton API call:
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.SYS "CheckRadioButton", !dlg%, first%, last%, id%
To initialise the state of a check box, you can use the CheckDlgButton API call:
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.SYS "CheckDlgButton", !dlg%, id%, state%
To disable an item you can use the EnableWindow API call:
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%, 0
Note that, as with initialisation, these routines must be executed after the call to PROC_showdialog.SYS "GetDlgItem", !dlg%, id% TO h% SYS "EnableWindow", h%, 1
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:
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.DEF FNgetdlgtext(dlg%, id%) LOCAL text% DIM text% LOCAL 255 SYS "GetDlgItemText", !dlg%, id%, text%, 255 = $$text%
In the case of a multi-line edit box the maximum length should be set to an appropriate value. To save the returned data to a file you can do the following:
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: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
If the item is an edit box used for numeric entry, the current value can be read with GetDlgItemInt: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%)
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).SYS "GetDlgItemInt", !dlg%, id%, 0, signed% TO value%
To determine which item (if any) is selected in a list box you can do the following:
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:LB_GETCURSEL = 392 SYS "SendDlgItemMessage", !dlg%, id%, LB_GETCURSEL, 0, 0 TO sel%
To determine which item is selected in a combo box do the following:DEF FNgetlistboxtext(dlg%, id%, sel%) LOCAL text% DIM text% LOCAL 255 LB_GETTEXT = 393 SYS "SendDlgItemMessage", !dlg%, id%, LB_GETTEXT, sel%, text% = $$text%
To discover the current state of a check box or radio button, you can use the IsDlgButtonChecked API call:CB_GETCURSEL = 327 SYS "SendDlgItemMessage", !dlg%, id%, CB_GETCURSEL, 0, 0 TO sel%
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.SYS "IsDlgButtonChecked", !dlg%, id% TO state%
Alternatively you can use a polling technique to monitor button presses and other events handled by 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:REPEAT WAIT 1 UNTIL !dlg% = 0 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.Click% = 0 ON SYS Click% = @wparam% : RETURN REPEAT WAIT 1 click% = 0 SWAP click%,Click% 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%)
The dialogue box template remains in memory, so you can display it again at any time by calling PROC_showdialog.PROC_closedialog(dlg%)
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:
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.ON CLOSE PROC_closedialog(dlg%):QUIT ON ERROR PROC_closedialog(dlg%):PRINT'REPORT$:END
INSTALL @lib$+"WINLIB3"
The functions contained are:
Normally the floating toolbar has a title bar. This can be eliminated by modifying the style after the call to FN_createfloatingtoolbar as follows:
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.ftb% = FN_createfloatingtoolbar(nbutts%,button%(),buttid%(),50,50,"") WS_BORDER = &800000 ftb%!16 AND= NOT WS_BORDER
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):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%)
Note that you can only send messages to a floating toolbar after it has been displayed with PROC_showfloatingtoolbar.TB_SETSTATE = 1041 SYS "SendMessage", !ftb%, TB_SETSTATE, buttid%, 0
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.PROC_removefloatingtoolbar(ftb%)
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:
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.ok% = FN_custombuttons(!ftb%, "birds.bmp", nbutts%, buttid%())
The values can be combined.
Style Name Effect 1 TBS_AUTOTICKS Show tick marks 2 TBS_VERT Vertical trackbar 4 TBS_LEFT Tick marks to top or left (default is bottom or right) 8 TBS_BOTH Tick marks on both sides
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)
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:trackpos% = FN_trackbarpos(tb%)
(note the exclamation mark in !tb%).TBM_SETPOS = 1029 SYS "SendMessage", !tb%, TBM_SETPOS, 1, trackpos%
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.PROC_removetrackbar(tb%)
The values may be combined.
Style Effect 1 Progress 'blocks' are contiguous 4 The progress bar is vertical
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)
You can also set the progress bar to an absolute value as follows:PROC_stepprogressbar(pb%, 1)
PBM_SETPOS = 1026 SYS "SendMessage", !pb%, PBM_SETPOS, progress%, 0
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.PROC_removeprogressbar(pb%)
INSTALL @lib$+"WINLIB4"
The functions contained are:
The WINLIB4U library contains an identical set of functions except that those receiving a text string assume it to be in Unicode (UTF-8) format, allowing non-ANSI (e.g. foreign language) characters to be incorporated.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.
The following program segment creates a property sheet containing three dialogue box pages:
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: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%())
psh% = FN_newpropsheet("Property sheet",pages%,0,&20,page%())
The following program segment displays the property sheet or wizard created above:
DIM hdlg%(pages%-1) PROC_showpropsheet(psh%,hdlg%())
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:
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.SYS "SetDlgItemText", hdlg%(0), id%, text$
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% : RETURN REPEAT WAIT 1 click% = 0 SWAP click%,Click% 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%)
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:
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.PSM_SETWIZBUTTONS = 1136 PSM_GETCURRENTPAGEHWND = 1142 Click% = 0 ON SYS Click% = @wparam% : RETURN oldhdlg% = 0 REPEAT WAIT 1 click% = 0 SWAP click%,Click% SYS "SendMessage", !psh%, PSM_GETCURRENTPAGEHWND, 0, 0 TO hdlg% IF hdlg%<>oldhdlg% THEN oldhdlg% = hdlg% CASE hdlg% OF WHEN hdlg%(0): SYS "SendMessage", !psh%, PSM_SETWIZBUTTONS, 0, 2 : REM Next only WHEN hdlg%(1): SYS "SendMessage", !psh%, PSM_SETWIZBUTTONS, 0, 3 : REM Back and Next WHEN hdlg%(2): SYS "SendMessage", !psh%, PSM_SETWIZBUTTONS, 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 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 a property sheet or wizard:
DEF FNgetproptext(hdlg%(), page%, id%) LOCAL text% DIM text% LOCAL 255 SYS "GetDlgItemText", hdlg%(page%), id%, text%, 255 = $$text%
The property sheet templates remain in memory, so you can display it again at any time by calling PROC_showpropsheet.PROC_closepropsheet(psh%)
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:
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.ON CLOSE PROC_closepropsheet(psh%):QUIT ON ERROR PROC_closepropsheet(psh%):PRINT'REPORT$:END
The WINLIB5 and WINLIB5A libraries 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 statement:
INSTALL @lib$+"WINLIB5"orINSTALL @lib$+"WINLIB5A"
The functions contained are:
The WINLIB5U library contains an identical set of functions to WINLIB5A, except that those receiving a text string assume it to be in Unicode (UTF-8) format, allowing non-ANSI (e.g. foreign language) characters to be incorporated.
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 (BS_LEFT) causes the text to be left-justified rather than centered, &80 (BS_BITMAP) creates a bitmap button, 3 (BS_AUTOCHECKBOX) creates a checkbox and 9 (BS_AUTORADIOBUTTON) 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.
To load an image into a bitmap button do:
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 hbutton% is the value returned from FN_button. Once you have finished with the button (but not before) delete the bitmap handle:LR_LOADFROMFILE = 16 BM_SETIMAGE = 247 SYS "LoadImage", 0, bmpfile$, 0, cx%, cy%, LR_LOADFROMFILE TO hbitmap% SYS "SendMessage", hbutton%, BM_SETIMAGE, 0, hbitmap%
SYS "DeleteObject", hbitmap%
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 (CBS_DROPDOWNLIST) 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:
where hbox% is the value returned from FN_combobox. An initial (default) selection can be made as follows:CB_ADDSTRING = 323 SYS "SendMessage", hbox%, CB_ADDSTRING, 0, "Combobox item 0" SYS "SendMessage", hbox%, CB_ADDSTRING, 0, "Combobox item 1" etc.
To determine which item is selected in a combo box do:CB_SETCURSEL = 334 SYS "SendMessage", hbox%, CB_SETCURSEL, index%, 0
CB_GETCURSEL = 327 SYS "SendMessage", hbox%, CB_GETCURSEL, 0, 0 TO sel%
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 (ES_AUTOHSCROLL) 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:
where the parameter hbox% is the value returned from FN_editbox.DEF FNgettext(hbox%) LOCAL text% DIM text% LOCAL 65535 SYS "GetWindowText", hbox%, text%, 65535 = $$text%
To create a scrollable, multi-line edit box you can specify the style value as WS_VSCROLL OR ES_AUTOHSCROLL OR ES_MULTILINE. To create an edit control which behaves as an independent window, with its own title bar, system menu and close box you can additionally specify the style value as WS_POPUP OR WS_CAPTION OR WS_SYSMENU. Note that in this latter case you must set the id% value to zero (it has the meaning of menu handle in the case of a popup 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 (LBS_SORT) 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:
where hbox% is the value returned by FN_listbox. To determine which item is selected do:LB_ADDSTRING = 384 SYS "SendMessage", hbox%, LB_ADDSTRING, 0, "Listbox item 0" SYS "SendMessage", hbox%, LB_ADDSTRING, 0, "Listbox item 1" etc.
The value sel% gives the index (starting at 0) of the currently selected item or −1 if no item is selected.LB_GETCURSEL = 392 SYS "SendMessage", hbox%, LB_GETCURSEL, 0, 0 TO sel%
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 (SS_BITMAP) 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:
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:LR_LOADFROMFILE = 16 STM_SETIMAGE = 370 SYS "LoadImage", 0, bmpfile$, 0, cx%, cy%, LR_LOADFROMFILE TO hbitmap% SYS "SendMessage", hbox%, STM_SETIMAGE, 0, hbitmap%
SYS "DeleteObject", hbitmap%
Note that the style% parameter is exclusive-ORed with &50000000, which is the numeric equivalent of WS_CHILD + WS_VISIBLE. If you prefer the window not to be constrained within the bounds of its parent, add &C0000000 (WS_CHILD + WS_POPUP) to the supplied parameter.
To create a menu, in which selecting Open causes PROCopen to be executed and selecting Exit causes PROCexit to be executed:
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:SYS "CreatePopupMenu" TO hfile% SYS "AppendMenu", hfile%, 0, FN_setproc(PROCopen), "&Open" SYS "AppendMenu", hfile%, 0, FN_setproc(PROCexit), "E&xit"
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: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)
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: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)
You must then define your procedure to receive two parameters:hedit% = FN_editbox("",300,20,100,24,FN_setproc(PROCeditbox()),0)
where W% and L% receive the values of @wparam% and @lparam% respectively.DEF PROCeditbox(W%, L%)
The library should be loaded from your program using the statement:
INSTALL @lib$+"SPRITELIB"
The functions contained are:
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.IF FN_initsprites(2) = 0 STOP
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).
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.ok% = FN_createsprite(0, "bbcmicro.ico", 64, 64)
The sprite is not displayed until PROC_movesprite or PROC_updatesprite is executed.
The BMP file must contain a single bitmap which is twice the height of the sprite, the upper half of which contains the sprite's transparency mask and the lower half of which contains the sprite's image. Here is an example of a suitable bitmap:
The mask is white where the sprite is fully transparent and black where the sprite is fully opaque (intermediate transparency values are not supported). In areas where the sprite is transparent its image should be black.
In the case of an animated sprite (.ANI file) the show% parameter specifies the required frame number (+1), so 1 causes the first frame to be displayed, 2 causes the second frame to be displayed and so on.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.
In addition, by specifying negative values for wide% and/or high% it is possible to reflect the sprite about its horizontal or vertical axis (or both). Note that this feature works only for sprites having a 1-bit mask (not a linear alpha mask) and relies on an undocumented feature of Windows.
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.PROC_exitsprites
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 statement:
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).INSTALL @lib$+"FNUSING"
The functions contained are:
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.PRINT FNusing(fmt1$,val1) FNusing(fmt2$,val2) .....
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 number cannot be represented in the format supplied, question marks are printed:PRINT FNusing("Price ££#.## including VAT",29.99) Price £29.99 including VAT
PRINT FNusing("##.##",123) ?????
PRINT FNlower("The Quick Brown Fox") the quick brown fox
PRINT FNupper("The Quick Brown Fox") THE QUICK BROWN FOX
The library should be loaded from your program using the statement:
INSTALL @lib$+"MDILIB"
The functions contained are:
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).SYS "CreatePopupMenu" TO hwindow% SYS "CreateMenu" TO hmenu% SYS "AppendMenu", hmenu%, 16, hwindow%, "&Window" SYS "SetMenu", @hwnd%, hmenu% SYS "DrawMenuBar", @hwnd% PROC_initmdi(hwindow%)
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:
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:hwnd1% = FN_createmdichild("Hello world") hwnd2% = FN_createmdichild("Rectangles") hwnd3% = FN_createmdichild("Circles")
To write a text string to a particular window you would call this procedure as follows:DEF PROCwritetext(A$, @hwnd%, @memhdc%) PRINT A$ ENDPROC
(see below for a description of FN_hdc)PROCwritetext("Some text", hwnd1%, FN_hdc(hwnd1%))
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:
which you would call as follows:DEF PROCwritetext(A$, @hwnd%, @memhdc%, RETURN @vdu%!48, RETURN @vdu%!52) PRINT A$ ENDPROC
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.PROCwritetext("Text for window 1", hwnd1%, FN_hdc(hwnd1%), xpos1%, ypos1%) PROCwritetext("Text for window 2", hwnd2%, FN_hdc(hwnd2%), xpos2%, ypos2%)
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.
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
INSTALL @lib$+"DATELIB"
The functions contained are:
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.
For example:
d Day of month as digits with no leading zero. dd Day of month as digits with leading zero for single-digit days. ddd Day of week as a three-letter abbreviation. dddd Day of week as its full name. M Month as digits with no leading zero. MM Month as digits with leading zero for single-digit months. MMM Month as a three-letter abbreviation. MMMM Month as its full name. y Year as last two digits, but with no leading zero. yy Year as last two digits, but with leading zero for years less than 10. yyyy Year represented by full four digits.
will return a string of the form "Sun 22 Feb 2004".date$ = FN_date$(mjd%, "ddd dd MMM yyyy")
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.
INSTALL @lib$+"D3DLIB"
The functions contained are:
The libraries with an A suffix (D3DLIBA.BBC, D3D9LIBA.BBC) provide an additional parameter to the PROC_render function containing the camera's roll-angle.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.
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)
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.
Code Size Data Comments &002 12 XYZ position Always required &010 12 Surface normal When lighting used &040 4 Diffuse colour When neither texture nor material specified &080 4 Specular colour For shiny objects &100 8 UV texture coordinates When texture specified
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.
The value pobj% is the pointer returned from FN_initd3d, FN_load3d or FN_loadtexture.
F% = OPENOUT"TRIANGLE.FVF" 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
Notes:
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). roll (D3DLIBA and D3D9LIBA only) The camera's roll angle (in radians).
DIM light{Type%, Diffuse{r%,g%,b%,a%}, Specular{r%,g%,b%,a%}, \ \ Ambient{r%,g%,b%,a%}, Position{x%,y%,z%}, Direction{x%,y%,z%}, \ \ Range%, Falloff%, Attenuation0%, Attenuation1%, Attenuation2%, \ \ Theta%, Phi%} light%(0) = light{} light.Type% = 3 : REM directional light light.Diffuse.r% = FN_f4(1) : REM red component light.Diffuse.g% = FN_f4(1) : REM green component light.Diffuse.b% = FN_f4(0) : REM blue component light.Ambient.r% = FN_f4(0) : REM red ambient light light.Ambient.g% = FN_f4(0) : REM green ambient light light.Ambient.b% = FN_f4(0) : REM blue ambient light light.Direction.x% = FN_f4(0) : REM X component of direction light.Direction.y% = FN_f4(0) : REM Y component of direction light.Direction.z% = FN_f4(1) : REM Z component of direction
DIM material{Diffuse{r%,g%,b%,a%}, Ambient{r%,g%,b%,a%}, \ \ Specular{r%,g%,b%,a%}, Emissive{r%,g%,b%,a%}, Power%} mat%(0) = material{} material.Diffuse.r% = FN_f4(1) : REM red component of colour material.Diffuse.g% = FN_f4(1) : REM green component of colour material.Diffuse.b% = FN_f4(1) : REM blue component of colour material.Ambient.r% = FN_f4(0.25): REM red ambient material.Ambient.g% = FN_f4(0.25): REM green ambient material.Ambient.b% = FN_f4(0.25): REM blue ambient
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).INSTALL @lib$+"ELLIPSE"
The ellipse is drawn in the current graphics foreground colour and mode, as specified by GCOL.
It contains the single function FN_sortinit.INSTALL @lib$+"SORTLIB"
where dir% determines the sorting direction (0 = ascending, 1 = descending) and smode% determines how strings are sorted:sort% = FN_sortinit(dir%,smode%)
smode% string sort action 0 word sort, case sensitive 1 word sort, ignore case 2 ASCII (character code) sort &1000 string sort, case sensitive &1001 string sort, ignore case
In a word sort the hyphen and apostrophe are treated differently from the other nonalphanumeric symbols, in order to ensure that words such as "coop" and "co-op" stay together within a sorted list. In a string sort, the hyphen and apostrophe are treated like the other symbols. In an ASCII sort the order corresponds to the regular BASIC less-than (<) and greater-than (>) operators.
If you prefer, you can initialise it multiple times with the different options and then CALL the appropriate variable when needed:
To sort the contents of an entire array do the following: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 a '1-based' array (where the first element has the subscript 1) do the following:C% = DIM(array(),DIM(array()))+1 CALL sort%, array(0)
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% = DIM(array(),DIM(array())) CALL sort%, array(1)
To sort multiple arrays according to the contents of a key array do the following:C% = howmany% CALL sort%, array(first%)
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.C% = size% CALL sort%, keyarray(0), array2$(0), array3%(0)...
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% = DIM(array(),DIM(array()))+1 CALL sort%, array(0,0), array(1,0), array(2,0)
The library should be loaded from your program using the statement:
INSTALL @lib$+"SOCKLIB"
The functions contained are:
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.
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.
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_readsocket does not wait for data to be received. If no data has been received since the last call, it returns zero.
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 with Windows XP (or later). However it is available for earlier versions of Windows (98 onwards) in the form of the redistributable file GDIPLUS.DLL.
The library should be loaded from your program using the statement:
INSTALL @lib$+"GDIPLIB"
The functions contained are:
The colour should be supplied as an integer equivalent to the hexadecimal value &AARRGGBB where AA is the alpha channel or opacity (00 = transparent, FF = opaque), RR is the red component (00 = no red, FF = maximum red), GG is the green component and BB is the blue component.
The style can be a combination of one or more of the following values:
The width is the width of the pen in pixels. Note that this need not be an integer.LineEndFlat = 0 LineEndSquare = 1 LineEndRound = 2 LineEndTriangle = 3 LineEndSquareAnchor = &11 LineEndRoundAnchor = &12 LineEndDiamond = &13 LineEndArrow = &14 LineStartSquare = &100 LineStartRound = &200 LineStartTriangle = &300 LineStartSquareAnchor = &1100 LineStartRoundAnchor = &1200 LineStartDiamond = &1300 LineStartArrow = &1400 LineDash = &10000 LineDot = &20000 LineDashDot = &30000 LineDashDotDot = &40000
The fill mode should be 0 for alternate and 1 for winding. The images below illustrate the effect of the different modes on a five-pointed star:
Alternate | Winding |
---|
The COMLIB library contains a set of procedures and functions for controlling applications via the COM (Component Object Model) interface, otherwise known as COM Automation or ActiveX. This is a method for co-operation and data sharing between applications, standardised by Microsoft, which allows the manipulation of other programs through a defined interface.
The library should be loaded from your program using the statement:
The functions contained are:INSTALL @lib$+"COMLIB"
Some common Locale Identifier values are as follows:
LCID Language 1031 German 1032 Greek 1033 English 1034 Spanish 1036 French 1037 Hebrew 1040 Italian 1043 Dutch
Program Name | ProgID |
---|---|
Microsoft Access | Access.Application |
Microsoft Excel | Excel.Application |
Microsoft Outlook | Outlook.Application |
Microsoft PowerPoint | PowerPoint.Application |
Microsoft Word | Word.Application |
Internet Explorer | InternetExplorer.Application |
Text to Speech | Sapi.SpVoice |
For example you would create an instance of the Excel application (assuming it is installed on your PC) as follows:
You use the value returned in xlapp% for all subsequent references to that object. When you have finished with the object you should call PROC_releaseobject.xlapp% = FN_createobject("Excel.Application")
Note that you may not immediately be able to see Excel (etc.) running, but if you look at the "Processes" tab in Windows Task Manager you will see that it is there.
The value returned is a pointer or handle to the object and must be stored for future use. It may refer to a single object or a collection of objects. To specify a particular item in a collection you need to call it by name or its item number in the collection.mychild% = FN_getobject(parent%,"name_of_child(1)") mysheet% = FN_getobject(xlapp%,"Workbooks(1).Worksheet(1)") myrange% = FN_getobject(mysheet%,"Range(""D1"")")
The first parameter of FN_getobject may be the Application object or some other child object that you already have a handle to; this is the starting point to look up the named object that you give as the second parameter. The problem that you will most likely have is knowing what objects exist! For this you will need the application's documentation of its COM hierarchy, or an Object Viewer. See Object Browser for more details.
This must be called after the object is no longer required and before your program exits. The parameter is the object reference or handle returned by FN_createobject. Objects obtained using FN_getobject do not need to be explicitly released as they will be released when their parent object is released. However, you may prefer to release all objects to be on the safe side.PROC_releaseobject(xlapp%)
It is important that objects be released whenever your program exits, even unexpectly as the result of an error or the user clicking Close. You should therefore ensure that appropriate calls to PROC_releaseobject are made from your program's ON CLOSE and ON ERROR routines.
PROC_putvalue takes two parameters. Firstly a handle to the object whose property you wish to change, or a handle to a parent object. Secondly a reference to the property and value; this may inclue the path down the object tree:
The value being 'put', such as the Boolean BTRUE or the string "Some text" must be enclosed in parentheses. The type or format of the data has to be correct for the call to succeed. Although many applications will accept several data types it is up to the user to ensure that only sensibly typed data reach the application. Packaging the data to a format and syntax that the application will accept can present a real challenge! There are two main aspects:PROC_putvalue(xlapp%,"Visible(BTRUE)") PROC_putvalue(xlapp%,"ActiveBook.Activesheet.cell(1,1).value(""Some text"")")
Data type |
Prefix | Examples |
---|---|---|
Long Integer (4 bytes) | None | cell(1,1).value(3) value(mydata%) |
Short Integer (2 bytes) | S | value(S 525) |
Unsigned Integer (4 bytes) | U | value(U 123456) |
Double Floating Point (8 bytes) | F | value(F 3.66) cell(1,1).value(F myfloat#) |
Single Floating Point (4 bytes) | G | value(G myfloat) value(G 2.67777) |
Boolean (2 bytes) | B | Visible(B TRUE) Saved(B FALSE) |
Object reference (4 bytes) | O | ChartWizard(O oResizeRange%, Nul, , xlColumns%) |
Null (no data content) | N | BorderAround(Nul,4,7) |
String | " | value("""+a$+""") worksheet(""Sheet 1"") |
If the prefix is omitted COMLIB assumes a floating-point numeric value if a decimal point is present and an integer numeric value if not. This works well with constants but if a variable name is passed omitting the prefix will cause it to be sent as an integer, potentially resulting in truncation (conversely a structure member will be sent as a floating point value, because of the presence of the dots!).
It should be pointed out that all the prefixes present a restriction in that there could be confusion with variable names that start with capitals B, F, G, N, O, S and U. If for instance you had used variables North% and South% as parameters then this would fail:
The first variable North% would be ignored (a Null was assumed) and the second would look for a non-existent variable named outh%! This requires the user to choose variable names that do not conflict, in much the same way as you have to avoid variable names starting with BASIC keywords.PROC_callmethod(objref%,"child.some_method(North%,South%)")
Note that in any case the passing of named variables is not compatible with compiling the program with the Abbreviate names crunch option, unless you use the REM!Keep compiler directive. It may therefore be better to convert the variables' values to strings using STR$:
PROC_callmethod(objref%,"child.some_method("+STR$North%+","+STR$South%+")")
If the property value itself includes quotation marks they must each be represented in your program as CHR$34+CHR$34 or """". So for example to set an Excel formula to the value =A2 &" "& B2 you could do:PROC_putvalue(Xlbook%,"WorkSheets(1).Name(""My Sheet"")") PROC_putvalue(Xlbook%,"WorkSheets(1).Name("+CHR$34+"My Sheet"+CHR$34+")")
PROC_putvalue(oRng%,"Formula(""=A2 &"""" """"& B2"")") or quote$=CHR$34+CHR$34 PROC_putvalue(oRng%,"Formula(""=A2 &"+quote$+" "+quote$+"& B2"")")
For example Addshape is a method which takes one integer and four floating point values:
In this last example we have one parameter that was not defined and was optional. In such cases the application will use its default value. This can also be filled with anything beginning with the capital N such as Null or simply left empty. In each case a null variant is sent to the application.The commas are counted to determine that it was a parameter:PROC_callmethod(oWB%,"ActiveSheet.Shapes.AddShape(17, 466.5, 40.5, 95.5, 120.1)" PROC_callmethod(oChart%,"ChartWizard(O oResizeRange%, xl3DColumn%, , xlColumns%)")
You may have noticed that the earlier example used 'O oResizeRange%' as a parameter. The prefix O defines the value as an Object or Collections reference. In this case you have passed an object reference as a parameter just as you might pass an array in BBC BASIC.PROC_callmethod(myrange%,"BorderAround(Null,4,7)")
In the first instance the application's internal format may well have been floating point, in which case FN_getvalueint will round it to an integer. In the second instance the data type is Boolean but it is translated into integer with the standard values of 0 for FALSE and -1 for TRUE.R% = FN_getvalueint(oWB%,"ActiveSheet.cells(8,2).value") R% = FN_getvalueint(xlapp%,"Visible")
If the application's internal data format is not floating point it will be converted to a floating point value if possible.n = FN_getvaluefloat(oWB%,"ActiveSheet.cells(8,2).value")
The string a$ will contain the textual version of the data, be it a number, date, string or a formula.a$ = FN_getvaluestr(oWB%,"ActiveSheet.cells(8,2).value")
The member _ret.vartype% will contain the variant type. The low and high data words are returned in _ret.ldata% and _ret.hdata% respectively. A string describing the variant type is returned in _ret.text$ and the data expressed as a string is returned in _ret.data$. This function is useful if the data type to be returned is unknown. Examination of _ret.vartype% will allow correct interpretation of the resulting data, or allow your program to choose which FN_getvaluexxx to use.DIM _ret{vartype%,ldata%,hdata%,text$,data$}
When a string data type is returned you should use the 8-bit ANSI version at _ret.data$. The 16-bit Unicode string, which you might expect to find at the address returned in _ret.ldata% cannot be accessed because COMLIB frees the memory it occupied.
If the conversion succeeds FN_coerce returns zero, otherwise it returns an error code.
An object has two types of thing you can do to it. First it has Methods which are actions that do things with it, and secondly it has Properties which are the way it looks or what information it contains. Objects can encompass other objects. The overall object, as far as Office automation is concerned, is the Application object. This is a COM server, and your BBC BASIC program acts as the client. This Application object will contain other smaller objects such as workbooks or documents or task lists. These objects have Collections of other objects such as words or paragraphs or cells. Finally you get down to the smallest parts that only have methods and properties. Before you can manipulate these methods or change the properties you have to access them.
To access an individual property or method you have to spell out exactly which one you want. Starting from the application object we refer to the property by listing all the steps to get there, for example:
The Object that you create is called an instance of the class of objects. You can run three or five instances of Word.Application. Each one is a separate copy of the software stored in memory with its own workspace. These instances stay until you destroy them.Application.workbook(1).worksheet(2).cells(1,3).value(23.6)
What is a property and what is a method? Methods do things, they take actions and the order in which you activate methods is important. Common methods include Open, Add, Sort, Quit, Save. Methods can create objects, as in the case of Add: Add items, Add documents, Add worksheets. Properties change informational states, and it is not important what order you do this in. Properties include things like paragraph settings, font size, font colour, cell values. There are a lot more properties than methods and you will need good documentation or an Object Browser to find them all.
When we have many similar objects we refer to them as Collections. There is often a plural in the name. Shapes is a collection of Shape objects. Cells is a collection of range objects. You can refer to the individual Shape by number Shape(1) or it may have a name as in Shape("Triangle"). So how do you know how many there are and what names they have? Well collections always have a property called Count which is the number in the collection. Then you can use a FOR loop to go through all the Shapes asking for their name:
Collections are actually arrays of objects, and like any array you have to make sure you keep track of what is in what element and make sure that you don't go out of range. If you call a method and specify a collection as the object don't be surprised when every member of the collection gets acted upon.n% = FN_getvalueint(myobj%,"ActiveDocument.Shapes.Count") FOR i% = 1 TO n% a$ = FN_getvaluestr(myobj%,"ActiveDocument.Shape("+STR$i%+").name") PRINT a$ NEXT i%
Here var. t% AND &FFFF is the vartype, var.l% and var.h% are the data bytes. The member var.r% is reserved and not used. Integer type data and object references are placed in var.l%. Floating point (8 byte) format, currency and date use both var.l% and var.h%. When a string is contained then a pointer to the string is held in var.l%. The string is in Unicode (16-bit character) format and cannot be used directly by BBC BASIC.DIM var{t%,r%,l%,h%}
The functions contained are:INSTALL @lib$+"STRINGLIB"
PRINT FN_lower("The Quick Brown Fox") the quick brown fox
PRINT FN_upper("The Quick Brown Fox") THE QUICK BROWN FOX
PRINT FN_title("the quick brown fox") The Quick Brown Fox
PRINT FN_binary(22) 10110
PRINT FN_tobase(22, 8, 4) 0026
text$ = "The quick brown fox" num% = FN_findreplace(text$, "brown", "silver", 0) PRINT text$ The quick silver fox
text$ = "The quick brown fox" num% = FN_findreplacei(text$, "BROWN", "silver", 0) PRINT text$ The quick silver fox
PRINT FN_instrr("the quick brown fox", "o", 0) 18
PRINT FN_instri("the quick brown fox", "O", 0) 13
PRINT FN_instrri("the quick brown fox", "O", 0) 18
PRINT """" FN_trim(" The quick brown fox ") """" "The quick brown fox"
parts% = FN_split("The quick brown fox", " ", a$()) PRINT a$(1) quick
parts% = FN_split("The quick brown fox", " ", a$()) PRINT FN_join(a$(), "+", parts%) The+quick+brown+fox
The functions contained are: There are a few minor restrictions in its use:INSTALL @lib$+"WINLIB5A" INSTALL @lib$+"MULTIWIN"
Normally i% and e% can be set to zero, and s% can be set to &96C00000 which is the numeric equivalent of WS_POPUP + WS_VISIBLE + WS_CLIPSIBLINGS + WS_CLIPCHILDREN + WS_CAPTION. If you prefer the window to be constrained within the bounds of its parent, use WS_CHILD instead of WS_POPUP (&56C00000).
The function returns the handle of the created window.
After calling PROC_selectwin all text and graphics output goes to the selected window.
You cannot close the currently-selected output window; if necessary precede the statement with a PROC_selectwin(0).
The functions contained are: These replacements are particularly useful if your program uses the CALLBACK, SUBCLASS or TIMERLIB libraries, or for other reasons needs to guarantee a timely response to interrupts.INSTALL @lib$+"NOWAIT"
The functions contained are: The callback address which you need to pass to the API should be obtained as follows:INSTALL @lib$+"CALLBACK"
Here FNcbroutine is the name of the callback function in your program and npar% is the number of parameters it takes (this will be specified in the description of the API function).address% = FN_callback(FNcbroutine(), npar%)
The callback routine itself needs to be of the following form:
When using callbacks you must be careful to avoid the use of blocking operations in your program, that is functions which are time-consuming or stall interrupt (event) processing, such as INPUT, GET, SOUND and WAIT (with a non-zero parameter). If required the NOWAIT library provides non-blocking replacements for these.DEF FNcbroutine(par1%, par2%, ....) REM Do whatever is necessary here = return_value%
Not uncommonly the callback will be made from within an API function itself and in this case it is necessary to replace the conventional form of the SYS statement:
with the following non-blocking code:SYS "FunctionName", parameters TO result%
Note the exclamation mark! You must call FN_systo even if you don't need the value returned by the API function. In that case simply assign the result to a dummy variable. An alternative syntax, compatible with earlier versions of the CALLBACK library, is as follows:SYS FN_syscalls("FunctionName"), parameters TO !FN_systo(result%)
In the event that you need to call the API function by address rather than by name use the following code:SYS FN_syscalls("FunctionName"), parameters result% = FN_sysresult
In the event that one of the parameters needs to be a string, add an explicit NUL-termination:SYS FN_syscalln(FunctionAddr%), parameters TO !FN_systo(result%)
For some examples of the use of the CALLBACK library see this Wiki article.temp$ = parm$ + CHR$(0) SYS FN_syscalls("FunctionName"), temp$ TO !FN_systo(result%)
The functions contained are: When using the SUBCLASS library your program must avoid statements that are time-consuming or which stall interrupt (event) processing, in particular the GET/GET$ and INKEY/INKEY$ functions, and the WAIT, SOUND and INPUT statements. You should use the replacement functions in the NOWAIT library to achieve this. Some Windows messages, for example WM_NOTIFY, cannot be subclassed using this technique.INSTALL @lib$+"SUBCLASS"
You can subclass multiple messages by calling PROC_subclass for each one.DEF FNhandler(msg%, wparam%, lparam%) REM Do the necessary message processing here = return_value%
The functions contained are: Since the timer interrupts available using this library may occur at rates up to 1000 Hz, the problem of stalling them is particularly severe. To ensure a timely response to the interrupts, and reduce the likelihood of flooding the event queue, you should avoid using any time-consuming statements and functions such as INKEY, INPUT, GET, SOUND or WAIT. If necessary you can make use of the replacement routines in the NOWAIT library.INSTALL @lib$+"TIMERLIB"
Also, pay particular attention to the precautions described under Notes on the use of interrupts.
The function returns an identifier for use in a subsequent PROC_killtimer, or zero if the timer could not be created.
The functions contained are:INSTALL @lib$+"XMLLIB"
You can concurrently parse multiple XML files by using separate object structures for each file.
A token can be either a tag or the text contained between two tags; if it is a tag the first character of the returned string will be "<" and the last character will be ">".
At each level of nesting the value returned increases by one. If PROC_exitXML has been called the returned value will be zero.
If the third parameter is set to zero the function will scan the remainder of the file; if the third parameter is set equal to the current level (as returned by FN_getLevel) only the rest of the current 'block' will be searched.
Note that the returned string may contain Unicode characters encoded as UTF-8; see User-defined modes for how to display such characters.
The functions contained are: There are a number of limitations in the use of the ASMLIB library:INSTALL @lib$+"ASMLIB"
You should add a RESTORE ERROR statement after the closing bracket (]) of your assembler code, unless that is shortly followed by an ENDPROC or end-of-function (which automatically restore the ERROR status).ON ERROR LOCAL [OPT FN_asmext : ] [OPT pass% Assembler code starts here...
The returned structure contains the members A%, B%, C% and D% corresponding to the values returned in the eax, ebx, ecx and edx registers by the CPUID instruction. To test for the availability of the CMOV (conditional move) instructions use the following code:
To test for the availability of the SSE (Streaming SIMD Extension) instructions use the following code:IF FN_cpuid(1, cpuid{}) IF cpuid.D% AND &8000 THEN REM CMOV instructions available ELSE REM CMOV instructions not available ENDIF
To discover the Vendor ID string use code similar to the following:IF FN_cpuid(1, cpuid{}) IF cpuid.D% AND &2000000 THEN REM SSE instructions available ELSE REM SSE instructions not available ENDIF
IF FN_cpuid(0, cpuid{}) THEN PRINT "Vendor ID string: " LEFT$($$cpuid{},12) ELSE PRINT "Vendor ID string not available" ENDIF
ASMLIB2 is a superset of ASMLIB. It supports all the assembler extensions provided by ASMLIB and contains compatible functions. It is never necessary to INSTALL both ASMLIB and ASMLIB2.INSTALL @lib$+"ASMLIB2"
To test for the availability of the SSE2 instructions use the following code:
IF FN_cpuid(1, cpuid{}) IF cpuid.D% AND &4000000 THEN REM SSE2 instructions available ELSE REM SSE2 instructions not available ENDIF
If run from the BBC BASIC for Windows IDE the patch persists for the entire session and can only be removed by quitting and restarting. The functions contained are:INSTALL @lib$+"HQSOUND"
pstereo% is a pointer to eight consecutive 16-bit words in memory which control the stereo mix of the four sound channels. By default channels 0 and 1 are sent to the left output and channels 2 and 3 to the right output, but by storing values (in the range 0 to &8000) in these memory locations the stereo mix can be changed. To set all four sound channels to the centre of the stereo stage you would do:
pstereo%!0 = &40004000 : REM Channels 0 and 1 left pstereo%!4 = &40004000 : REM Channels 2 and 3 left pstereo%!8 = &40004000 : REM Channels 0 and 1 right pstereo%!12 = &40004000 : REM Channels 2 and 3 right
pvoices% is a pointer to four consecutive bytes in memory which control which of eight 'voices' (waveforms) each of the four sound channels uses. Voice 0 is an approximation to a square wave, voice 1 is a triangular wave, voice 4 is a sine wave and the other five voices are similar to other organ stops. By default all four channels use voice 0 (which is most similar to the 'unpatched' waveform). To set all four sound channels to use voice 1 you would do:
pvoices%?0 = 1 : REM Channel 0 pvoices%?1 = 1 : REM Channel 1 pvoices%?2 = 1 : REM Channel 2 pvoices%?3 = 1 : REM Channel 3
pwaves% is a pointer to eight consecutive waveforms in memory, each of which consists of 8,192 signed 16-bit samples (so the allocated memory is &20000 bytes in total). Any of the eight voices may be replaced with a custom waveform by modifying the associated block.
If the default waveforms are acceptable, and PROC_stereo and/or PROC_voice are used to make the selection, PROC_hqinit may be called without any parameters. If no control over the waveforms, stereo position or voice is required the library may be executed using the code below, in which case there is no need to call PROC_hqinit at all and the library will not remain resident in memory after it has patched the executable:
CALL @lib$+"HQSOUND"
The functions contained are:INSTALL @lib$+"UTF8LIB"
The library should be loaded from your program using the statement:
INSTALL @lib$+"EVENTLIB"
The functions contained are:
If you need to be informed that the event queue overflowed, you can register a handler for that by specifying an event ID of zero:DEF PROChandler(msg%, wparam%, lparam%)
PROC_eventregister(0, PROCoverflow())
REPEAT PROC_eventpoll WAIT 0 UNTIL FALSE
The library should be loaded from your program as follows:
The functions contained are:INSTALL @lib$+"IMGLIB"
The image$ parameter should contain the path and filename of the image to be loaded, which may be in BMP, GIF, JPG or PNG format. If the format supports an alpha channel (i.e. transparency) then this will be taken into account when the image is plotted. The value returned is used to refer to the image subsequently; if zero the file could not be opened, so this must always be tested.image% = FN_imgLoad(image$)
The anigif$ parameter should contain the path and filename of the animated GIF file; transparency, if any, will be taken into account when the image is plotted. The value returned is used to refer to the animation subsequently; if zero the file could not be opened, so this must always be tested.image% = FN_imgLoadAnimatedGIF(anigif$)
The image% parameter should contain the value returned by FN_imgLoadAnimatedGIF and the frame% parameter the frame number (starting at zero for the first frame). The function returns the period of time, in centiseconds (1/100 second) for which that frame should be displayed. If zero is returned the requested frame does not exist, i.e. the frame number is greater than or equal to the number of frames in the animation.delay% = FN_imgFrame(image%, frame%)
The image% parameter is the value returned from a previous call to FN_imgLoad or FN_imgLoadAnimatedGIF; it must not be zero. The red, green, blue and alpha parameters are multiplication factors, in the range 0.0 to 1.0, which determine by how much the different components should be attenuated. For example if you want to plot a shadow you might set the red, green and blue factors to zero to suppress the colour altogether, and the alpha factor to (say) 0.5 to set the degree to which what is 'behind' the image is darkened by the shadow.PROC_imgMult(image%, red, green, blue, alpha)
The image% parameter is the value returned from a previous call to FN_imgLoad or FN_imgLoadAnimatedGIF; it must not be zero. The xpos and ypos parameters specify the coordinates, in BBC BASIC graphics units, of the centre of the image; they need not be integers. The xscale and yscale parameters specify by how much to scale the image in the horizontal and vertical directions respectively; the image may be reduced or increased in size. To avoid image distortion you are recommended to set xscale and yscale to the same value. If xscale or yscale (or both) are negative the image is flipped (mirrored) in that direction. The angle parameter specifies by what angle, if any, the image should be rotated around its centre; the units are degrees in a clockwise direrction.PROC_imgPlot(image%, xpos, ypos, xscale, yscale, angle)
The image% parameter is the value returned from a previous call to FN_imgLoad or FN_imgLoadAnimatedGIF; it must not be zero.PROC_imgSize(image%, width%, height%)
This library has a dependency on the file BBCMODE7.FNT. If using it in a 'compiled' program ensure that you include the compiler directive:INSTALL @lib$+"MODE7LIB"
REM!Embed @lib$+"MODE7LIB", @lib$+"BBCMODE7.FNT"
The functions contained are:
The set% parameter should be set to 0 to select English as the primary character set and to 1 to select English as the secondary character set. To use the secondary character set you must enable it using VDU 23,18,3,1| after which VDU 155 toggles between primary and secondary sets (each row starts in the primary character set).PROC_saa5050(set%)
The set% parameter acts as it does in PROC_saa5050. The language% parameter selects the desired language: 0 for English, 1 for German, 2 for Swedish, 3 for Italian, 4 for Belgian, 5 for US ASCII, 6 for Hebrew, 7 for Russian or 9 for Greek.PROC_saa505x(set%, language%)
The address should be of a 40-byte block of memory into which the character dot pattern is read.PROC_osword139(address%, character&)
The address should be of a 40-byte block of memory from which the character dot pattern is updated.PROC_osword140(address%, character&)
CONTENTS |
CONTINUE |