Programming for non programmers
Part 10 - Improving the VAT program
David Holden continues his series.
In the last session we finally produced a fully working Wimp program. To briefly remind you about this, it was a simple VAT calculator which opened the window shown below and either added VAT to the value entered or returned the ex-VAT amount from a VAT inclusive price.
The VAT Calculator
Although the program works, there are a number of things which could be improved. In this issue I'll deal with some of these, which are:
Adding a Clear button
At the moment the 'old' data remains until the user deletes it from the 'Enter amount' icon and clicks on either the Add or Subtract button. It would be nice if we added a button to clear the icons. Luckily (!) there's a gap between the Quit and Add buttons which is just the right size.
The new version of the program on the current CD already has this button included, but I've also included the original templates file so that I can describe how this is done using !TemplEd (also included again). As usual these can all be found in the archive BEGINPRG in the SOFTWARE directory on the CD. There is a copy of the final program from the last session in the directory Prog_9.
Double-click on !TemplEd so it installs on the icon bar. Now double click on the file 'Templates' from the !VAT_Calc program in the Prog_9 directory and this will make TemplEd load the template file. At the top left of the screen a filer-like window will open with just one icon labelled 'main'. Double-click on this and TemplEd will open the window ready for you to edit. Click MENU over the Quit button in this window and a menu will appear. Follow the second item (which should be labelled 'Icon #4' if you've made sure that the mouse pointer was over the 'Quit' button when you clicked MENU) to open the sub-menu shown. Now follow 'Copy' on this menu to open the Copy sub-menu. At this point you should be seeing something like this.
On the final (Copy) sub-menu click on 'Right' and a new button should appear to the right of the Quit button. As you might expect this will be a copy of the Quit button and it will be given the next available icon number. If you place the mouse pointer over this new button and look at the little 'Icon info' window at the top right corner of the screen you will see that this icon is number 7.
Because this new icon is a copy of the Quit icon it will, of course, have the same label. We want to change this label to 'Clear'. Click MENU over the new icon and follow the second item, this time labelled 'Icon #7', to open the sub-menu and this time click on the fourth item. Edit. This will open the 'Edit icon' window shown below. An easier way to open this window is to double-click on an icon.
The icon at the top of this window shows the text that appears on the button. Delete the word 'Quit' and enter the word 'Clear'. Click on 'Update & Exit '. The Edit Icon window will close and you should find that the window now has a new button labelled 'Clear', and, as we have seen, this button is number 7 so we can use it in our program.
The part of the program that deals with mouse clicks in the window is PROCmouse_click.
DEFPROCmouse_click(win%,icon%) IF win%<>main_handle% THEN ENDPROC CASE icon% OF WHEN 4:PROCdie WHEN 5:PROCadd_vat WHEN 6:PROCsubtract_vat ENDCASE ENDPROC
We need to add a new line to this to deal with a click on the new icon, number 7.
Now we obviously have to add this procedure to the program. Luckily this procedure is very simple as to 'clear' a text icon all we need to do is place empty or null string in the icon. We already have a routine to write text to an icon, PROCset_icon_text, so we can use this. The new PROCclear_icons therefore is:
DEFPROCclear_icons PROCset_icon_text(main_handle%,1,"") PROCset_icon_text(main_handle%,2,"") ENDPROC
You shouldn't have any trouble understanding exactly how this works. This modified program, with the altered Templates file, can be found in the directory Prog_10_1. You will find that when you click on the 'Clear' button any text in the two text icons is cleared.
Restricting user input
Up until now there has been no limit to what characters the user could type into the icon. As the program deals only with numbers that is all we want the user to be able to enter. With this program nothing terrible will happen if you type 'Rhubarb' into the icon and click on 'Add', but it would be better to ensure that only valid characters could be entered.
If you run the version of the program in the Prog_10_1 directory you will find that you can only enter the numbers 0 to 9 or a full stop (to represent the decimal point).
We don't need to add anything to the program itself to do this, the Wimp does it all for us. One of the items that can be set when an icon is defined is a validation string. The main purpose of this is to restrict or 'validate' the characters the user can type into an icon. In fact, as additions have been made to the features that icons can have the validation string has assumed a much more extensive purpose and is used to define a variety of features. However, for the present we will just consider its use in restricting the characters that can be typed into a writable icon.
Load the Template file from the program in the Prog_10_1 directory into TempEd and open the Edit Icon window for the icon where the number is entered. The top part of this window should look like this.
The first character in the Validation icon is 'A'. This (or 'a') is an abbreviation for Allow and means that the characters that follow the 'A' are the only ones that are allowed to be entered. After the 'A' will come the list. '0-9' means the character 0 to 9. Note the use of '-' (the minus sign). This means that you can't put the minus sign itself in the validation string (there is a way of doing this but we'll deal with that later). After '0-9' is a full stop. We need this to represent the decimal point. You should also note that there must not be any spaces in the validation string. If there are then the space will be taken as part of the string and so will be allowed. For our purpose we don't want the user to be able to type a space so we must ensure that we don't accidentally include one in the validation string.
Responding to the RETURN key
It would make the program easier to use it, after entering the amount, the user merely had to press the RETURN key to have the calculation carried out.
Wimp_Poll returns an event code type 8 to a task if a key is pressed while that task has the input focus in any of its windows. As usual various information is placed in the returned data block to enable the task to decide what to do. The data block contains:
As our program has only one window and only one writable icon in that window we don't need to bother with the first two items. However, we would have to check them if we had more than one of either. The next three items are really only for use where text is being entered into an editor-like window. It's the last, at block + 24, which interests us. This will be the ASCII code of the key pressed. Certain keys, such as function keys, return special codes, but we are only interested in te RETURN key which will return the ASCII code 13.
You may recall that the section of the program that deals with Wimp_Poll is in a REPEAT - UNTIL loop. This is shown below with the addition of an extra line in the CASE structure to intercept type 8 events and direct them to a new procedure, PROCkey.
REPEAT SYS "Wimp_Poll",0,blk% TO event% CASE event% OF WHEN 2:SYS "Wimp_OpenWindow",,blk% WHEN 3:SYS "Wimp_CloseWindow",,blk% WHEN 6:PROCmouse_click(blk%!12,blk%!16) WHEN 8:PROCkey(blk%!24) WHEN 17,18:PROCmessage_received(blk%!16) ENDCASE UNTIL FALSE
We need to add the procedure PROCkey to deal with the keypress.
DEFPROCkey(key%) IF key%=13 THEN PROCadd_vat ENDPROC
This takes the parameter at blk%+24, which is the ASCII value of the key pressed. If this is 13, the RETURN key, it will go to the add VAT procedure.
You might think that is all we need to do, but there's a problem. First this only allows us to add VAT, but, more importantly, because we've introduced a routine to deal with keypresses this will intercept keypresses which might otherwise be handled by other applications.
Try adding the modifications shown above to the program in Prog_10_1. It will seem to work OK. You can enter a number in the top icon, click on the Add or Subtract buttons, and if you press RETURN after entering a number the program will add VAT to it. But try pressing F12 to get to the command line and nothing will happen. The keys specified in the validation string are automatically handled by the Wimp, as are the standard keys used when entering data into writable icons, the left and right arrows, CTRL-U etc. If you press RETURN then the program will intercept the keypress and add VAT as expected.
If you introduce a routine which intercepts the keypress event, number 8, returned by Wimp_Poll then you must ensure that any keypresses not used by your program are 'passed on' so that any other applications which are interested in them will receive them. This is done by the SWI Wimp_ProcessKey. This takes just one parameter in R0, and this parameter is the same value as the key code returned by Wimp_Poll event 8 for the key pressed. You could, in fact, use this SWI to initiate a keypress, but its main use is to ensure that we pass on anything we are not interested in to other applications.
DEFPROCkey(key%) IF key%=13 THEN PROCadd_vat ELSE SYS "Wimp_ProcessKey,key% ENDIF ENDPROC
Try changing PROCkey as shown and you should now find that when you press F12 with the caret in the top icon the command line appears.
We have now produced the equivalent of clicking on the Add button by pressing the RETURN key. What we still need is a similar equivalent to clicking the Subtract button. We could choose almost any key, but as we've used RETURN for Add something similar should be chosen for Subtract. An obvious choice would be SHIFT+RETURN, but although most keys return different ASCII values when used with SHIFT the RETURN key doesn't, it still returns 13.
Fortunately there's a way to find out if the SHIFT key is actually depressed. There is a Basic function INKEY. This takes a single parameter and can be used in two ways.
If the parameter is a positive number then it will wait the specified number of hundredths of a second for a key to be pressed and then return the ASCII value of any key pressed or -1 if no key was pressed within the specified time limit. This was intended to be used in single-tasking programs and should never be used in a multi-tasking environment like the Wimp as it 'freezes' the computer while waiting for a keypress.
If the parameter is a negative number then it is taken as the Internal Key number of a key and returns TRUE if that key is pressed or FALSE if it is not. The internal key numbers are not related to the ASCII values of the keys and for a full list you will need to refer to the BBC Basic Reference Manual, but the internal key number for either SHIFT key is -1, so if we use - variable = INKEY(-1) then 'variable' will be TRUE if SHIFT is pressed at that moment or FALSE if it is not. Using INKEY we can therefor check if the ASCII code 13 returned is the result of SHIFT+RETURN or just RETURN alone.
DEFPROCkey(key%) IF key%=13 THEN IF INKEY(-1)=TRUE THEN PROCsubtract_vat ELSE PROCadd_vat ELSE SYS "Wimp_ProcessKey,key% ENDIF ENDPROC
You should be able to see how this works, but I'll describe it to make sure you understand it fully. The first part of the main IF - THEN - ELSE structure is enacted if the key is RETURN, ASCII 13. If it isn't 13 then the second part, which passes the key code on via Wimp_ProcessKey is enacted. The difference between this procedure and the previous one is that the first part of the structure also contains an IF - THEN - ELSE structure, this time on a single line. This line first calls the INKEY function with -1 as a parameter to see if the SHIFT key is pressed. If it is then INKEY will returen TRUE, so the program goes to PROCsubtract_vat, if it is not it goes to PROCadd_vat.
The modified program appears in the directory Prog_10_2, and if you Run it you will see that it behaves as expected. In the next session we will make our program install itself on the iconbar in the 'proper' RISC OS manner.