Module 14: Bits and Pieces
Copyright: C Wragg 2000

Another menu

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.

submenu
The main menu includes a submenu to Save or Load

Guarding against filing errors

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.

A final twist

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)

Load window
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.

A summary of BASIC keywords used

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

Return to the Module Index