The most significant gap now left in our application is the absence of a menu for the main window. We will make a menu of three items, one of which will lead to a submenu. The three items will be 'Info', 'File' and 'Quit'. Info will open the Info window, just as on the iconbar menu. File will give a submenu, with choices to Save or Load data. Quit will quit.
The following code added at the end of PROCinit will define the elements required for the new menu. It begins by defining space to store the details in mainmenu% and submenu%. The size of memory required allows 28 bytes for the header, and 24 bytes for each item in the menu, so a menu with three items requires 28 + 3 x 24 = 100 bytes.
REM menu for window main
DIM mainmenu% 100,submenu% 76
PROCmake_menu(mainmenu%)
DATA "Main menu",3,0,info%,Info,0,submenu%,File,&80,-1,Quit
PROCmake_menu(submenu%)
DATA File,2,0,-1,Save,&80,-1,Load
Now we must catch the click of the menu button over the main window in PROCclick. In the option starting WHEN main%, we take the CASE of block%!8 which contains the button code. 1 and 4 are the adjust and select buttons; we need to catch 2 for the menu button, so add to this loop:
WHEN 2:REM menu button
openmenu%=2:PROCshowmenu(mainmenu%,!block%-64,block%!4)
The value 2 put in openmenu% enables us to identify which menu we want in PROCmenuselect. Moving down to PROCmenuselect, you will find a line 'CASE openmenu% OF'. It just contains the value 1 which we gave to the iconbar menu, so we now add an option for openmenu%=2 as follows.
WHEN 2:REM main menu
CASE sel1% OF
WHEN 1:REM File
CASE sel2% OF
WHEN 0:REM Save
!block%=savecode%
SYS"Wimp_GetWindowState",,block%
block%!28=-1
SYS"Wimp_OpenWindow",,block%
PROCcaret(savecode%,2)
WHEN 1:REM Load
!block%=loadcode%
SYS"Wimp_GetWindowState",,block%
block%!28=-1
SYS"Wimp_OpenWindow",,block%
PROCcaret(loadcode%,2)
ENDCASE
WHEN 2:quit%=TRUE
ENDCASE
Note that the load and save routines are identical to those in PROCclick which arise when the save and load buttons are clicked. Buttons and menus are often just two versions of the same thing, leading to the same routines.

The main menu includes a submenu to Save or Load
It is good practice to make sure that if an error occurs during a filing operation, nothing crashes, and any open files belonging to our application are closed. The best way to do this when only one file is likely to be open at a given time is to use the Basic command ON ERROR LOCAL. All we need to do is include this statement, with instructions on what to do in such an event, in our loading or saving procedures. For example, consider PROCsave which is
DEFPROCsave
O=OPENOUT(path$)
FORX%=1TO50
PRINT#O,desc$(0,X%),secret$(0,X%)
NEXT
CLOSE#O
ENDPROC
Just before we open the file, we need to insert a line
ON ERROR LOCAL CLOSE#O:PROCreport("Error in save procedure",1):ENDPROC
Now if an error occurs while saving, control will jump to this line, close the file, send up an error message so you know something went wrong, and exit the procedure. ON ERROR LOCAL is automatically 'forgotten' as soon as the program exits the procedure by whatever route. A similar line should be added to PROCload which is actually more susceptible to errors such as a file being misnamed.
This brings us to the last piece of code before we end this series, and it is to enable us to load data that was not saved by autosave. This is a little bit like the last Module, in that it relies on PROCreceive to know what it is to load, and we use a technique common in many install programs in which we drag the file we want to load onto the window.
To begin, we need to define a load window that will respond to the enter filename button in the load window. Here is the definition to be entered into PROCinit in the usual manner.
REM load box window
DIM loadtext% 255,howtoload% 28
$loadtext%=STRING$(255,CHR$0):$howtoload%="Drag file onto this window"
load%=FNcreate_window(604,336,360,224,0,0,&86000012,2,2,&19,"Load:",3,0)
a%=FNcreate_icon(load%,8,-156,192,144,&2701313D,"",howtoload%,M2%,28)
a%=FNcreate_icon(load%,8,-216,344,48,&700F12D,"",loadtext%,saveval%,256)
a%=FNcreate_icon(load%,248,-128,64,48,&C701913D,"",ok%,R5%,3)

The load window enables you to drag the file in to it
Now find the code for loadcode% in PROCclick which contains the choice WHEN 3, and add the further option for icon 4:
WHEN 4:
code$=FNstring(code%)
$code%=STRING$(8,CHR$0)
!block%=loadcode%
SYS"Wimp_CloseWindow",,block%
!block%=load%
SYS"Wimp_GetWindowState",,block%
block%!28=-1
SYS"Wimp_OpenWindow",,block%
This loads our new window - you might like to try it at this point to make sure. Its a bit like a save box in reverse! Fortunately the coding required is either already written or not so tortuous.
What happens is that the Filer sends a message via the Wimp to say that a sprite has landed on our window. This is picked up by PROCreceive with an action code of 3, so we need the following code to be added in PROCreceive after the line 'WHEN 2:PROCdatasave'.
WHEN 3:
CASE block%!20 OF
WHEN load%
infile$=FNstring(block%+44):$loadtext%=infile$
!block%=load%
SYS"Wimp_CloseWindow",,block%
SYS"Wimp_GetWindowState",,block%
block%!28=-1
SYS"Wimp_OpenWindow",,block%
OTHERWISE
PROCreport("Transfer not possible",17)
ENDCASE
All this does is transfer the pathname of the file into the writable icon in the load window. All we now have to do is respond to a click on the OK button to initiate the normal load routine. Note that the OTHERWISE condition will report an error if you try to load onto a wrong window.
Going back to PROCclick for the last time, add the following case at the end of the procedure:
WHEN load%:
CASE block%!16 OF
WHEN 2:
!block%=load%
SYS"Wimp_CloseWindow",,block%
path$=infile$:point%=1:cyc%=LEN(code$)
PROCload
ENDCASE
This completes our application and we can now sit back and enjoy knowing our secret data can be kept safe and secure. This was, however, primarily a series on Basic. You will have gathered how much this is complicated by using the Wimp, and we have deliberately avoided explaining anything about the Wimp that was not essential to our program, so if this has whetted you appetite, I do recommend further reading about the Wimp, and would remind you that the Basic handbook and PRM are available on CD ROM from RISCOS Ltd . So - enjoy your programming.
In conclusion, here is a list of all the Basic keywords and the main concepts we have covered, and the Module number in which it was first introduced.
| Keywords and Concepts | Module |
|---|---|
| Variable types: string A$; integer B%; floating point C | 1 |
| arrays | 9 |
| AND | 13 |
| ASC(A$) returns the ASCII value of A$ | 11 |
| CASE <variable> OF ... WHEN ... OTHERWISE ... ENDCASE | 4 |
| CHR$(G) returns the character with ASCII value G | 3 |
| Comparing values with >, <, >=, =, <= | 2 |
| DATA | 4 |
| DIMAllocating memory: eg DIM place% 16 | 4 |
| Defining arrays: eg DIM array$(3,8) | 9 |
| END | 1 |
| EOR | 11 |
| FALSE | 2 |
| File commands: OPENOUT() PRINT# as in PRINT#O,a$ OPENIN() INPUT# as in INPUT#I,a$ CLOSE#O | 11,12 |
| FOR ... NEXT loop - e.g. FOR X%=3 TO 15 STEP 3 ... NEXT | 3 |
| FN and DEFFN. The DEFFN returns a value with the = statement | 11 |
| GET as in A = GET (and also GET$ for strings as in A$ = GET$) | 2 |
| IF <expression> THEN <statements> ELSE <statements> | 2 |
| Incrementing values using += (or decrementing with -=) | 1 |
| Joining strings using + | |
| Eg 1 If A$="Gun" and B$="boat" then A$+B$="Gunboat" | |
| Eg 2: A$ = B$ + " is number 1." | 3 |
| LEFT$ | 11 |
| LEN | 11 |
| LIBRARY | 4 |
| MID$ | 11 |
| NOT | 3 |
| ON ERROR | 4 |
| ON ERROR LOCAL | 14 |
| Operators: * to multiply and / to divide | 2 |
| PRINT and use of punctuation to tabulate or produce new lines, etc | 3 |
| PROC, DEFPROC ... ENDPROC | 4 |
| READ | 6 |
| REM to insert comments | 2 |
| REPEAT ... UNTIL loop | 1 |
| RIGHT$ | 11 |
| RND as in A%=RND(n) | 2 |
| Statement separator : | 8 |
| STEP - see FOR | |
| STR$ converts a number to a string | 4 |
| STRING$() | 10 |
| SYS | 4 |
| TRUE | 2 |
| WHILE ... ENDWHILE | 3 |