Module 8: Adding the icons

Progress so far

The purpose of this series is to enable anyone with little or no knowledge of Basic to learn the essentials of the language, and to be able to write a simple program to run in the desktop. Unfortunately, running a program in the desktop requires an extensive use of the Wimp that has little to do with Basic! We have therefore had to spend some time talking about the Wimp, and we need to do so a little more now before we can return to Basic itself.

All (well virtually all) the boxes and buttons that appear in a window are referred to as icons, and these are created using Wimp calls rather similar to those used to create the windows themselves. All such icons are created using the same SWI, and in Basic this is called in the form SYS"Wimp_CreateIcon", but like the windows, there are various parameters needed to specify the position, size, and nature of the icon required. For example, we must define the colour, whether they are flat or wedge-shaped, how they respond to clicks from the mouse, whether you can write in them, and so on.

Because this series is about Basic rather than the Wimp, we shall skate over some of the details, but hopefully provide enough to be useful in your own programming.

Revisiting the Run file

In Module 4 of this series we defined a Boot file and a Run file, but we kept them very simple at that stage. At this point we need to refine them a little.

In the Boot file we had the line

Set Secrets$Dir <Obey$Dir>

A problem might arise if we have more than one copy of Secrets because the program will soon make use of this definition for Secrets$Dir, and if you are using a different copy of the program then it will be looking at the wrong version. In general, you should make sure that lines like this are repeated in the Run file, thus overriding any declarations of this sort that were made at an earlier point in time.

Open your Run file, and add the line

Set Secrets$Dir <Obey$Dir>

immediately before the 'Wimpslot' line.

Creating icons

Before we go any further, there are a number of useful strings that will be used in various parts of our program that need to be defined. To be generally available, and to maintain a clear structure, these need to be defined together at the start of PROCinit. To save time later, we will set all these up even though we won't be using some them in this Module.

Open RunImage in Edit and find the four lines at the start of PROCinit which read:

DIM block% 255,message% 255
DIM R5% 2
DIM texta% 12,textb% 20,textc% 20,textd% 20
$R5%="R5"

Replace these four lines with the following:

DIM block% 255,mess% 255,path% 255
$path%="<Secrets$Dir>.Data0"
DIM texta% 12,textb% 20,textc% 20,textd% 20
DIM M2% 6,R2% 6,hideR2% 8,R3% 2,R5% 2,nul% 2,ok% 2
$M2%="R2;L32":$R2%="KAR;R2":$hideR2%="KR;D*;R2"
$R5%="R5":$R3%="R3":$nul%=" ":$ok%="OK"
n%=1

You will see that we have fallen to temptation here, and we have made a number of statements on one line that might have been written one line at a time. This is excusable where you are making a simple list of statements. Note that in Basic the colon is used as the separator between individual statements so, for example,

$M2%="R2;L32":$R2%="KAR;R2"

is saying in one line what would be written on two lines as

$M2%="R2;L32"
$R2%="KAR;R2"

Apart from this, there is nothing new in these lines, although you may wish to refer back to Module 5 to refresh your memory about the DIM statement, and how strings are inserted into memory locations.

Don't worry about the significance of all these definitions; all will be revealed in due course.

Now move down RunImage a few lines to where we define the main window and find the statements:

DIM title% 18
$title% = "Secrets"

These now need to be expanded to read as follows:

DIM title% 18,text0% 14,text1% 30,text2% 20
DIM keyleft% 2,keylast% 4,keynext% 4,keyright% 2
DIM loaddata% 9,add% 3,del% 6,savedata% 9
$title%="Secrets"
$text0%="Secret number"
$text1%="Description of item"
$text2%="Special number"
$keyleft%="|<":$keylast%="back":$keynext%="next":$keyright%=">|"
$loaddata%="load data":$add%="add":$del%="delete":$savedata%="save data"

We have now set up 12 different phrases that we are going to use in our icons. There are two ways in which icons can be created. Many programmers like to use templates, and there is software available such as Templed that helps to do the job without too much number work. I have always preferred to do mine the hard way, using Library functions to make the process easier. I suggest it is worth working at this nuts and bolts level to begin with because it gives a better understanding of what is going on, even if it only makes you appreciate the template methods all the more later!

The next line in RunImage should now be

main%=FNcreate_mainwindow(350,300,868,548,0,0,&87000012,1,12,&119,title%,-1,18,3,1)

to which we now add the following line, which creates the first of the 12 icons we need:

a%=FNcreate_icon(main%,0,-160,868,160,&1700013D,"",nul%,R5%,2)

This function called FNcreate_icon is in the Library and takes ten parameters as follows:

1. The memory location, often called the handle, that identifies the window in which the icon will appear - in this case, main%

2,3. The x and y coordinates of the bottom left corner of the icon relative to the window, the y-coordinate being measured downwards from the top edge of the window. Thus the y value is always negative.

4,5. The width and height of the rectangular shape of the icon.

6. The icon flags (described below).

7. A string to be written in the icon provided it does not exceed 12 characters, but left blank if it is longer or has to be more closely defined. In the latter case, this will be done in the remaining fields.

8. 0 if parameter 7 is used; otherwise it is said to be indirected by pointing to the string to be inserted. In our case, we point to a string " " located at null%, as defined above at the start of PROCinit.

9. 0 if parameter 7 is used; otherwise it points to a string that defines the type of icon to be used. In our case, R5% points to the string "R5" to make the icon into a 3-dimensional slab.

10. The last parameter tells the wimp how much space to allow for the string - in this case the string at null% is two spaces, so the parameter is '2'.

The icon flags making up the sixth parameter are similar to the window flags described in Module 7, and they are illustrated in the diagram.

We now add the remaining 11 icons to complete the main window:

a%=FNcreate_icon(main%,118,-144,640,128,&1A,"banasecret",0,0,0)

Note here that we are inserting a sprite, so the 7th parameter is the name of the sprite to be used.

[Insert Drawfile IconFlags here: caption 'Setting the flags for an icon'.]

a%=FNcreate_icon(main%,0,-408,868,248,&1000013D,"",nul%,R5%,2)
a%=FNcreate_icon(main%,16,-240,176,48,&7000211,"Description",0,0,0)
a%=FNcreate_icon(main%,240,-240,592,48,&700F13D,"",text1%,R2%,30)
a%=FNcreate_icon(main%,16,-312,220,48,&7000311,"",text0%,0,14)
a%=FNcreate_icon(main%,240,-312,592,48,&700F13D,"",text2%,R2%,20)
a%=FNcreate_icon(main%,240,-384,148,56,&1700313D,"",keyleft%,R5%,2)
a%=FNcreate_icon(main%,388,-384,148,56,&1700313D,"",keylast%,R5%,2)
a%=FNcreate_icon(main%,536,-384,148,56,&1700313D,"",keynext%,R5%,2)
a%=FNcreate_icon(main%,684,-384,148,56,&1700313D,"",keyright%,R5%,2)
a%=FNcreate_icon(main%,0,-548,868,140,&1000013D,"",nul%,R5%,2)
a%=FNcreate_icon(main%,16,-508,200,56,&1700313D,"",loaddata%,R5%,9)
a%=FNcreate_icon(main%,228,-508,200,56,&1700313D,"",add%,R5%,3)
a%=FNcreate_icon(main%,440,-508,200,56,&1700313D,"",del%,R5%,6)
a%=FNcreate_icon(main%,652,-508,200,56,&1700313D,"",savedata%,R5%,9)

This is a good point at which to run the program. You will need to ensure that this new version is saved as the !RunImage in the working copy of Secrets. The nice thing about this is that although typing in the above lines might have been a little tedious, the outcome is dramatic. We now have a complete window displaying all the main features of the Secrets program.

[Insert sprite Mod8/2. Caption: 'Something to look at, at last'.]

Exploring further

Before we move on to the next module, explore the code you have written above. In particular,

1. Try changing the 9th parameter to see what effect it has. As the program is set up, you can try varying this between any of the following: R5%, R3% and 0.

2. Identify each icon in the Secrets window with its corresponding definition in the code above, and determine how each parameter made it appear as it does. Try varying some of these, predicting what will happen when you do.

3. Explore the icon flags., by changing the colours, borders, and alignment.

Module 9: Arrays and Buttons

What is an array?

In our secrets program, we need to be able to hold several pieces of information. The freeware version allows you to store 50 different items of information, and for each of these there is a description as well as the secret data itself. We could choose 100 different variable names, and even make it easier to recognise them by using something like data1, data2, ... data50, but not only would this be tedious, but it would also be difficult to write a simple routine to step from one to another.

The answer is to use arrays. An array has the same name for all the items of data, but distinguishes between them by number. So we might set up a one-dimensional array to record the description as follows:

DIM description$(50)

This uses the same keyword DIM that we used before, and the purpose is the same if you stop to consider what is happening. The program is asking the OS to set aside space for 50 strings, and we can then pick out the 5th description as description$(5), etc. Actually this in not strictly correct because the OS will actually reserve space for 51 strings, the first one being description$(0), and we shall use this fact in our program in a few moments.

An array does not have to be one-dimensional. It is frequently useful to have 2 or 3-dimensional arrays, and a two dimensional array could be defined by the statement

DIM array2$(4,20)

This establishes a 5 x 21 array called array2$. Such an array might be established to store 5 different pieces of information about 21 different people. It could be that the five items are the name, sex, age, height and weight of each person, so that the age of the 5th person would be stored in array2$(2,4), and of the 12th person in array2$(2,11). Note here that array$(0,0) is the name of the first person.

[Insert illustration of arrays - Drawfile Mod9/1. Caption 'The first two records of array2$()'.]

The use of arrays is particularly useful when going through all the data. For example, to print all the data you might use a loop as follows:

FOR X%=0 TO 20
FOR Y%=0 TO 4
PRINT array2$(Y%,X%)+", ";
NEXT
PRINT
NEXT

Note the extra PRINT before repeating the X% loop. The semi-colon after the first PRINT statement makes the arrays print on one line. The second PRINT ensures a new line is begun for the next person. Note also how important it is to get the X% and Y% the right way round to ensure that one person's details are all printed together!

Adding the arrays to the program

There are a number of ways in which the arrays could be set up, but we are going to use two separate arrays for the description and the secret data. We shall use two-dimensional arrays in each case so that we can use the '0,0' element for work space when converting encrypted data. Find the line

app$="Secrets"

and insert immediately before this the following two lines:

DIM desc$(1,50), secret$(1,50)
desc$()="":secret$()=""

The second of the two lines uses a particularly useful feature of BASIC V which enables you to set all the elements in an array to a default value. In this case we have set all the elements in both desc$() and secret$() to be empty.

Note also that you can only DIM an array once, so it would be good practice to avoid using the DIM statement twice in the same program for the same array, even if you think the program will only meet one of them.

Responding to the buttons

You may have noticed in the last Module that having brought up the Secrets window on the screen, you could click on either of the writable icons and the caret would appear, just as you would expect from other programs. This is all controlled by the wimp as a result of our original descriptions of the icons. Furthermore, you could delete what was there using the delete key or Control-U, and you could type in your own statements. What you could not do, however, was save what you had typed, nor could you move on to another item. To do this, we must write code to describe what we want to happen when the buttons are pressed.

In general, there are two common actions we use to tell the program to do something: we click the mouse, or we press a key. This is picked up by the Wimp polling routine, which will then point to the appropriate procedure to be followed. In the remainder of this module we are going to implement the 'add', 'back' and 'next' buttons so that we can begin to make things happen.

Our code for the polling routine is already in place, and a click with the mouse calls the procedure PROCclick(win%). This already contains the code to handle clicks over the iconbar and the iconbar menu. It begins with a line 'CASE win% OF', the purpose of which is to determine which window the pointer is over when the mouse is clicked. So far, we only have the value 'WHEN -2' which means it is over the icon on the iconbar.

To indicate that the pointer is over the main window, go to the end of the 'WHEN -2' section (between the two consecutive occurrences of ENDCASE) and add a new section as follows

WHEN main%
CASE block%!8 OF
WHEN 1,4:
CASE block%!16 OF
WHEN 8:PROCback
WHEN 9:PROCnext
WHEN 13:PROCadd
ENDCASE
ENDCASE

Remember that the value in block%!8 tells us which button was clicked, 4 for Select and 1 for Adjust. We have therefore ensured that a click with the Menu button will have no effect in this procedure.

block%!16 contains the number of the icon that has been clicked over. The icons are numbered from 0 in the order in which they are defined for the particular window in PROCinit. If you count through the definitions you will see that number 8, the 9th icon, is the line

a%=FNcreate_icon(main%,388,-384,148,56,&1700313D,"",keylast%,R5%,2)

and keylast% contains the string "back". In other words, when the mouse is clicked on this icon, with the word 'back' on it, the procedure PROCback is called. Similarly, the button 'next' leads to PROCnext, and the button 'add' is the 14th button defined and leads to PROCadd.

We must now add these three procedures, and they should follow at the end of the listing after PROCmenuselect(). Enter as follows.

DEFPROCback
IFn%<=1 ENDPROC
n%-=1
PROCreset
ENDPROC

DEFPROCnext
IFn%>=50 ENDPROC
n%+=1
PROCreset
ENDPROC

DEFPROCadd
desc$(1,n%)=FNstring(text1%)
secret$(1,n%)=FNstring(text2%)
PROCreset
ENDPROC

PROCback and PROCnext are very similar, as you would expect. Note that the IF statement is very important because if you try to move back from the first entry, or beyond the last entry, not only will it not make sense, but you are also likely to step outside the range of the array causing an error.

PROCreset is used by all three of these procedures, and is the routine that rewrites the details in the icons and so updates the window on the screen. This procedure should now be added at the end:

DEFPROCreset
$text1%=desc$(1,n%)
$text2%=secret$(1,n%)
!block%=main%
block%!4=4:block%!8=0:block%!12=0
SYS"Wimp_SetIconState",,block%
!block%=main%
block%!4=6:block%!8=0:block%!12=0
SYS"Wimp_SetIconState",,block%
ENDPROC

Remember that we defined text1% and text2% to be the addresses of the data to be displayed in the writable icons in the window. n% is used to hold the number of the current record, and in PROCback for example it was reduced by 1, so PROCreset installs the new strings into these two icons. We then reset !block to point to main%, block%!4 to the number of the icon we are updating (and the next two words both to zero) and call SYS"Wimp_SetIconState" to update icon 4. We then repeat this for the second icon which is icon 6.

[Insert picture of buttons - Drawfile Mod9/2 - Caption 'Using buttons to move through the file'.]

DEFPROCadd is slightly different because writing a string in a writable icon does not automatically put it in to the corresponding array; we have to arrange for this to happen. We could write code so that it automatically happens when you move backwards or forwards through the data, but secret data could be too precious to risk mistakes. Therefore, we have chosen to only record the data by a specific click on the add button.

In the PROCadd procedure we use a Library function FNstring. This is invaluable, because it takes the string stored at the given address (e.g. text1%), converts it to a normal BASIC string, and puts it into the corresponding element of the array.

More to do

We have now activated three of the buttons, and you will not be able to enter data, move on and add more, go back to previous entries, etc. Unfortunately you cannot save any of this yet, nor can you encrypt it, but you might like to try to write the procedures, PROCbegin and PROClast, for the two buttons marked '|<' and '>|' which should display the first and last records respectively.

Module 10: Keeping the data (1)

The Theory

We are almost ready to save our data away secretly, but before we dash into the code we need to establish a clear procedure we expect to follow. In Secrets, it goes like this

On clicking the save button, open a password window
When the password is entered open a second password window to check the first entry
Now give the user a choice: Automatic save, or a 'manual' save
For the automatic save, nothing further is required on screen
For a 'manual' save a traditional save window is required
Finally the encrypted file is saved.

For this process we shall need three new windows, several extra elements in DEFPROCclick to respond to these steps, and the save process itself, so there is a sizeable amount of coding to be done.

The first password window

In the full program, a mouse click on the save data button opens the first password window. The code to create this window is as follows:

REM save data window

DIM code% 8,sctext% 30
$code%=STRING$(8,CHR$0):$sctext%="Enter code up to 8 characters"
savecode%=FNcreate_window(550,340,468,144,0,0,&87000012,1,12,&19,"Encode",3,0)
a%=FNcreate_icon(savecode%,0,-160,468,208,&1700013D,"",nul%,R5%,2)
a%=FNcreate_icon(savecode%,16,-72,436,48,&7000119,"",sctext%,0,30)
a%=FNcreate_icon(savecode%,144,-136,192,48,&700F13D,"",code%,hideR2%,9)

This code is very similar to the code already written to create the main window. The first line after the REM DIMensions two new locations, the first to hold the password of up to 8 letters, and the second to hold the on-screen instructions to the user, to enter code up to 8 characters.

The next line introduces a new Basic keyword STRING$. This enables us to create a string of specific length composed of all the same characters. In this case, STRING$(8,CHR$0) creates a string 8 characters long composed of the null element - CHR$0. This is a way of ensuring that the writable icon will be blank when it first appears on screen.

The last four lines create the window, identified as savecode%, and three icons. Note the use of hideR2% in the last definition. This is the writable icon, and this ensures that no one can read the code as it is entered. hideR2% is defined at the start of PROCinit as described in Module 8.

[Insert sprite Mod10/1 - Caption: 'The first password window'.]

Checking that it works

One of the great pluses of Basic is that, because it is not a compiled language, you can run your code straightaway without any delay to recompile, and thus ensure that it is still doing what you want before you move on. In this case, we need some more code, but it makes sense to write this so that we can bring this new window up on screen and confirm that all is well.

The window in question is opened when we click on the 'Save data' button in the main window. This button is icon number 15, and so we need to add

WHEN 15: REM Save data button
!block%=savecode%
SYS"Wimp_GetWindowState",,block%
block%!28=-1
SYS"Wimp_OpenWindow",,block%
PROCcaret(savecode%,2)

immediately after the line 'WHEN 13:PROCadd' in the main% loop of PROCclick. The four lines beginning '!block%= ... ' represent the standard method of opening a window, and can be used in most cases. In some programs I make this a procedure in itself, so that an alternative way of writing this would be

WHEN 15:REM Save data button
PROCopen(savecode%)

and the corresponding procedure would then be

DEFPROCopen(w%)
!block%=w%
SYS"Wimp_GetWindowState",,block%
block%!28=-1
SYS"Wimp_OpenWindow",,block%
ENDPROC

The final line of the original code calls PROCcaret which tells the Wimp to put the caret in icon 2, so the writable icon will already have the caret in place for you to type in your password without further ado. PROCcaret is contained within the Library.

Having typed in this code, you might like to test the program to make sure this all happens as predicted. Assuming all is well, compare the window flags with what actually appears, and try to identify the three icons, and why they appear as they do. Notice that icon 1 containing the instruction has no border and appears simply as text on the background of icon 0, which is itself just a grey slab giving a pleasant 3-dimensional appearance to the window. Building one icon on top of another is an easy way of creating more pleasing windows.

Responding to a key press

We now need to introduce another fundamental procedure that is used in virtually all programs -- a procedure to respond to a key press which we will call PROCkey. In the final version of Secrets, the only way to register that you have entered your code is to press Return. You may prefer to have another button, but most RISC users press Return automatically to enter data, and it is easier than returning to the mouse to click over a button.

So far our wimp poll routine does not recognise key presses, so we must add a line to change this. In the wimp poll loop at the beginning of the program, add the following line after the line that reads 'WHEN 6:PROCclick(block%!12)

WHEN 8: PROCkey(block%!24)

We now need to add the procedure just before DEFPROCmenuselect as follows.

DEFPROCkey(k%)
CASE k% OF
WHEN 13:
CASE !block% OF
WHEN savecode%
code$=FNstring(code%)
$code%=STRING$(8,CHR$0)
!block%=savecode%
SYS"Wimp_CloseWindow",,block%
!block%=savecheck%
SYS"Wimp_GetWindowState",,block%
block%!28=-1
SYS"Wimp_OpenWindow",,block%
PROCcaret(savecheck%,2)
ENDCASE
OTHERWISE
SYS"Wimp_ProcessKey",k%
ENDCASE
ENDPROC

The first point to make is that block%!24 identifies the key that has been pressed. For ordinary keys it will return the ASCII value, but for special keys such as F1 to F9 there are special values allocated so that they too can be recognised.

13 is the ASCII code for Return, so the one case so far corresponds to this key, but the second CASE statement makes sure that we only respond if the window is the correct one. The window handle is stored at block%, and needs to be savecode% for the password window.

The subsequent code uses FNstring to extract the password from the icon and replace it with a null string ready for next time. Just two lines are needed to close the window, and then the standard lines for opening the new window that checks the first entry.

Note the use of the keyword OTHERWISE in this procedure. In a CASE statement, if you want to take action when all other listed options have failed, then you can use the keyword OTHERWISE, so the structure is

CASE <variable> OF
WHEN ...
WHEN ...
...
OTHERWISE
...
ENDCASE

In PROCkey the OTHERWISE statement followed by the SYS call SYS"Wimp_ProcessKey",k% is essential. This ensures that the key press is made accessible to all other applications running at that time.

Checking the password

We cannot check this last piece of coding until we have defined the second window. The code for this is as follows, and should be inserted immediately after the code for the save data window entered at the start of this module.

REM check code window

DIM checktext% 30,auto% 9,manual% 15
$checktext%="Enter code again as a check"
$auto%="auto save":$manual%="enter filename"
savecheck%=FNcreate_window(550,340,468,216,0,0,&87000012,1,12,&19,"Check Code!",3,0)
a%=FNcreate_icon(savecheck%,0,-160,468,208,&1700013D,"",nul%,R5%,2)
a%=FNcreate_icon(savecheck%,16,-72,436,48,&7000119,"",checktext%,0,30)
a%=FNcreate_icon(savecheck%,144,-136,192,48,&700F13D,"",code%,hideR2%,9)
a%=FNcreate_icon(savecheck%,0,-216,234,56,&1700313D,"",auto%,R5%,10)
a%=FNcreate_icon(savecheck%,234,-216,234,56,&1700313D,"",manual%,R5%,16)

This code should cause no difficulty, and you can now run the program again to make sure all works so far. You should be able to get to this second window.

[Insert sprite Mod10/2 - Caption: 'Check the password before you continue'.]

Moving on

I always like to end each module with some kind of exercise, but this time we are in the middle of the save process which we hope to complete in Module 11. You might however like to explore the full program and see if you can create the code yourself to produce a standard looking save window opened by clicking on the last button of the check code window?

Module 11: Keeping the data (2)

Saving to a file

The process of saving data out to a file on disc can appear complex, especially if it involves using a standard save-type window and drag-and-drop, so we are going to postpone the more difficult elements and concentrate on making our application work at a simpler level.

The check code window offers two ways in which to save the data - auto save or enter filename. The autosave option will save the data file within the application folder under the name data0. It has the advantage that you don't have to enter a name for the file, nor do you need to do so when loading the data back in later. It has no real disadvantages, and has the advantage of simplicity.

The alternative is to enter a filename. This might be useful if you want to keep more than one file of secret data, of if you are sharing your computer with someone else in the family and you each want your own file. It is this process, however, that is more complex, and so we shall concentrate on the automatic method first.

Encrypting the data - using EOR

There is no point in saving the data if it can be read by simply loading into Edit! The whole point of this program is to protect your data from prying eyes, and as you may wish to record PIN numbers and other very sensitive information it must be robust. This does not mean that the encryption process itself has to be complicated.

The process used in Secrets takes advantage of the EOR function, itself a Basic keyword. It has the marvellous characteristic that if it is applied twice, then you get back to where you started. This means that you can encode something by operating with EOR once, and you then decode it by using EOR a second time in the same way. Those of you familiar with logic operators will have no difficulty here, but for those who are less confident, here is a brief explanation.

Take two binary numbers 101 and 011. the result of using the EOR function on these numbers is

101 EOR 011 = 110

If we now apply the reverse process, it will look like this

110 EOR 011 = 101 - the number we started with!

Think of EOR as meaning either, or, but not both. EOR operates on each digit in turn, so taking the first digit of both numbers we have 1 and 0, so that give a 1 - 1--.

Now compare the respective second digits 0 and 1 which give the same result, so we now have 11-.

Finally compare the last digits. These are both 1's and EOR does not allow both, so the result is zero and we have the encoded number 110.

Now imagine this process applied to every digit and letter of your secret data. The number we used, 011, is effectively the password. Applying the password to every character in turn ends up with pure gobbledygook - just what we want. Apply the same password again, and it magically unravels the jumbled code and restores your secret information. Another advantage of this method is that when you reload no harm is done to your precious information if you enter the wrong password! All that will happen is that you load further gobbledygook!

It follows that your data must be as secure as your password, so it is essential that (a) it is not too short or simple enabling it to be quickly found by trial and error, and (b) is not too obvious - you should never use your telephone number, your date of birth, or your partner's name!

For someone who loves puzzles and cracking codes, it is not impossible for someone to study the pattern of the encrypted file and deduce the password. A couple of extra devices are therefore included in the encryption routine to make this much more difficult, but since my personal data relies on this as well as possibly yours too, I will leave you to work these out for yourself if you really want to know!

The saving process

The first step is to add the routine to PROCclick that will respond to a click on the auto save button. This will close the check code window, and call the procedure that manages the saving process which we call PROCautosave. The code is as follows and should be added to PROCclick just before the last two lines of the procedure (ENDCASE, ENDPROC).

WHEN savecheck%
CASE block%!16 OF
WHEN 3:REM the auto save button
!block%=savecheck%
SYS"Wimp_CloseWindow",,block%
PROCautosave
ENDCASE

We now have to add a new procedure called autosave. To keep our program tidy and easy to follow, all the file handling procedures should be kept together, so add the following code at the end of the RunImage file.

REM Encryption, Saving and Loading routines

DEFPROCautosave
code2$=FNstring(code%)
$code%=STRING$(8,CHR$0)
IF code$<>code2$ PROCreport("Codes do not match",1):ENDPROC
PROCencode
path$="<Secrets$Dir>.Data0"
$path%=path$
PROCsave
FORX%=1TO50
secret$(0,X%)=""
desc$(0,X%)=""
NEXT
ENDPROC

Remember in the last Module we put the first password into the variable code$. This procedure now takes the second entry and puts it into code2$ so that we can make a comparison. If they are not the same then we branch out of the procedure before any harm is done, giving a message on the way to say that the Codes do not match. PROCreport is contained in the Library, and is invaluable for passing messages back about what is happening as well as reporting on any errors.

Having approved the password, the data is now encrypted by PROCencode. On return to PROCautosave the pathname of the file to be saved is put into path$ - notice the use of <Secrets$Dir> here to ensure it is saved in the application directory - and is put into memory at path% ready for the save process carried out by PROCsave. On returning again to PROCautosave, the arrays are cleared of redundant data before completing the process. Add PROCencode immediately after PROCautosave.

DEFPROCencode
cyc%=LEN(code$):point%=1
FORX%=1TO50
secret$(0,X%)=FNencrypt(secret$(1,X%))
desc$(0,X%)=FNencrypt(desc$(1,X%))
NEXT
ENDPROC

PROCencode does not do the encryption itself, but passes this task to a function FNencrypt which takes the content of secret$(1,X%) and returns the encrypted data in secret$(0,X%). You see at this point why we used a two-dimensional array for this data. It does the same for the desc$() data so that the description is not discernible in the encrypted file either. Now add the procedure encrypt.

DEFFNencrypt(a$)
c$=""
L%=LEN(a$):IF L%=0 THEN =c$
FORY%=1TOL%
b$=MID$(a$,Y%,1)
d$=MID$(code$,point%,1)
c%=ASC(b$) EOR ASC(d$)
b$=CHR$(c%)
point%+=1:IF point%>cyc% point%=1
c$+=b$
NEXT
=c$

Not a lot of code to keep your secrets safe! There are several Basic keyword used here, most of which we have already met, but as a reminder this is what each one does.

LEN(a$) returns the length (i.e. the number of characters) in the string a$

ASC(b$) returns the ASCII value of the first character in b$

CHR$(c%) is the reverse of ASC, returning the character with ASCII value c%

EOR either, or, but not both

and an important new keyword

MID$(a$,x%,y%), which is a way of extracting part of the string a$. It will take y% characters from the string starting at the x% th character. So, for example,

MID$("Butter",3,2) is "tt".

Other keywords like MID$ are LEFT$ and RIGHT$ which do the same thing but start from the left of the string or work back from the right of the string respectively. Examples are

LEFT$("Butter",3) is "But",

RIGHT$("Butter",2) is "er".

A nice feature added to BASIC V was to be able to abbreviate these functions further, so that if you left out the second parameter it would still work in a useful way. Thus

LEFT$("Butter") would be "Butte", just dropping the last character from the string, and

RIGHT$("Butter") would be "r", just the last character. Thus

"Butter" = LEFT$("Butter") + RIGHT$("Butter")

All this is useful information, and we could elaborate further, but returning to our function encrypt, it is also worth drawing attention to the penultimate line c$+=b$. Just as we can use this method of incrementing a numeric variable, so we can add to a string. What this line does is to take the string already in c$, add b$ onto the end, and leave the result in c$.

This section of code also demonstrates how a function works. Remember that a function is used to set a given variable. For example, we used it in the procedure encode in the line

secret$(0,X%) = FNencrypt(secret$(1,X%))

which means 'take the content of secret$(1,X%), encrypt it through the function encrypt, and place the result in secret$(0,X%). It is therefore necessary to have a means of passing the result back to this statement, and all function definitions do so by the '= a$' statement where a$ contains the result. Thus in FNencrypt it ends '=c$', meaning that the function ends by returning the content of c$.

The rest of the code, and why it works, you should be able to sort out for yourself.

PROCsave

The final step in the process is the procedure PROCsave, called towards the end of PROCautosave. The code for PROCsave should be added to the end of the RunImage file as follows:

DEFPROCsave
O=OPENOUT(path$)
FORX%=1TO50
PRINT#O,desc$(0,X%),secret$(0,X%)
NEXT
CLOSE#O
ENDPROC

As you can see, it is really short and simple, but it does use three more Basic keywords.

O = OPENOUT()

tells the computer to open a file defined by path$ to receive data. If it already exists, then it will be overwritten; if it does not exist, then it will be created as a new file. The channel used to write this data will be identified by the number it puts in O.

PRINT#O,a$

writes the content of a$ to the file identified by the channel number O. Note that if we have another file open at the same time with channel number T, then we would write to the second file using PRINT#T, thus ensuring the correct file is updated.

CLOSE#O

closes the file as soon as al data has been written. Remember that it is important not to leave files open when they are not being used. Because this procedure is so simple, we have not put any error trap in here. If any error does occur within this procedure then the file would be left open causing problems, but we will trust you to type this in with great care and avoid any further complications for the time being.

So your data is saved at last. If you open the data file Data0 within the application directory in Edit then you will see it is totally meaningless, but if you want to restore your data to see that it is really still there - well, you will just have to wait for the next Module, won't you, and don't forget your password!

Module 12: Getting it back again

A fully working program

In this module we shall fit in the last piece that makes our application into a fully functional program. It will record, save and restore up to 50 items of secret information. There will be further bells and whistles to add, but it will achieve its purpose for those who wish to start to actually use it. In Module 11 we saved our data automatically within the application directory. All we need do now is work out the routines for restoring that data.

To load existing data, we need to activate the load data button in the main window, and create a new window in which we can enter the password and select auto load or specify a file name. Again, autoload is easier, and we shall concentrate of that first. A procedure will be required to load the data, very similar to PROCautosave.

The decode window

[Insert sprite Mod12/1 - Caption: 'The decode window looks familiar'.]

The style of this window will be identical to the check code window, and you may wonder whether we can use the same window for both tasks. It would be possible, but the wording in the icons would have to be changed, and an essential feature of the Wimp is responding to clicks over a specific window that lead to specific forms of action. So, although it is similar, we will take the easier route and define a new window as follows.

REM load data window

DIM autoin% 9
$autoin%="auto load"
loadcode%=FNcreate_window(550,340,468,216,0,0,&87000012,1,12,&19,"Decode",3,0)
a%=FNcreate_icon(loadcode%,0,-160,468,208,&1700013D,"",nul%,R5%,2)
a%=FNcreate_icon(loadcode%,16,-72,436,48,&7000119,"",sctext%,0,30)
a%=FNcreate_icon(loadcode%,144,-136,192,48,&700F13D,"",code%,hideR2%,9)
a%=FNcreate_icon(loadcode%,0,-216,234,56,&1700313D,"",autoin%,R5%,10)
a%=FNcreate_icon(loadcode%,234,-216,234,56,&1700313D,"",manual%,R5%,16)

This code should be added at the end of PROCinit after the check code window definitions. Notice how similar these windows are, and how we actually use the same string in both of them for the last icon using the text already defined and found at manual%. We have also used the string "Enter code up to 8 characters" that was set up in the save data window definition and stored at sctext%. It is helpful to note that we can use the same variables in different icons and in different windows.

Now to make sure this window appears at the proper time we must add another option to the main% loop in PROCclick. The load window should be opened by a click on icon number 12, so after the line 'WHEN 9:PROCnext' we need to add

WHEN 12:REM Load
!block%=loadcode%
SYS"Wimp_GetWindowState",,block%
block%!28=-1
SYS"Wimp_OpenWindow",,block%
PROCcaret(loadcode%,2)

This section simply opens our new window, and puts the caret ready in the icon for you to enter the password. When you have done so, you will click on 'Auto load' to proceed. The next piece of coding must therefore respond to this further mouse click.

The load procedure

Still in PROCclick, but now at the end of the procedure after the loop beginning 'WHEN savecheck% ...' we add 'WHEN loadcode% ...' as follows.

WHEN loadcode%
CASE block%!16 OF
WHEN 3:
code$=FNstring(code%)
$code%=STRING$(8,CHR$0)
!block%=loadcode%
SYS"Wimp_CloseWindow",,block%
PROCautoload
ENDCASE

and this must be complemented by a new procedure PROCautoload. I suggest that this procedure is inserted between PROCautosave and PROCencode because they are two sides of the same process, but you will appreciate that it could be placed anywhere in the program. In order to keep coding tidily arranged, it should be kept within the 'Encryption, Saving and Loading routines' section that we have already established.

DEFPROCautoload
PROCencode
path$="<Secrets$Dir>.Data0"
$path%=path$
PROCload
FORX%=1TO50
secret$(0,X%)=""
desc$(0,X%)=""
NEXT
ENDPROC

PROCencode is an interesting one. It is here to set a couple of variables rather than to encode, and acts as a bit of a decoy! Having set up the path name of the data file, the procedure goes on to call PROCload, which is always relatively easy to write because it must mirror the way in which the data was saved, so PROCload will have a close similarity with PROCsave. Add PROCload after PROCsave:

DEFPROCload
I=OPENIN(path$)
FORX%=1TO50
INPUT#I,desc$(0,X%),secret$(0,X%)
secret$(1,X%)=FNencrypt(secret$(0,X%))
desc$(1,X%)=FNencrypt(desc$(0,X%))
NEXT
CLOSE#I
!block%=main%
SYS"Wimp_CloseWindow",,block%
n%=1
$text1%=desc$(1,n%)
$text2%=secret$(1,n%)
!block%=main%
SYS"Wimp_GetWindowState",,block%
block%!28=-1
SYS"Wimp_OpenWindow",,block%
ENDPROC

In terms of Basic, you will see the correspondence between O=OPENOUT in PROCsave, and I=OPENIN in the load routine. You will also notice the read function from a file is INPUT# as opposed to PRINT#. Note that the saved data is loaded into secret$(0, ...) etc, and is, of course, still encrypted, so the next two lines pass the loaded data through the function FNencrypt to use the reversing property of the EOR function, and puts the results where they belong in secret$(1, ...).

PROCload then has one more task to do compared to PROCsave, which is to update the main window to show the new data. To do this we close the window first, set the count n% back to the first record, updated the strings held at text1% and text2%, and then reopened the window. There are other ways of updating windows, but this method involves very little additional explanation and works well enough. Where it falls short is where frequent changes take place and the flashing off and on of the window becomes irritating to the eye, but the way to avoid that is another story.

Still to do

Our application is now up and running, so what remains to make it complete?

There are two buttons we have not yet activated in the main window. We suggested that you try to write the code for this in Module 9. If you have not done so yet, we will complete this in the next Module.

We have not provided the code to save a file other than automatically. This requires more knowledge of the Wimp than it does of Basic, but we will give an outline of this with the necessary code in Module 13.

A main window should have a menu as part of what is expected in a desktop program. The freeware version only has a couple of items in the menu, relying on the more popular button approach. All the buttons could be replicated in a menu, and we will take a look at this in Module 14.

Another useful function that is not included in the freeware version is a search facility. The argument is that it is not particularly arduous to flip through 50 records to fins the one you need, but if you prefer a search method, this could be added. Could is the operative word. This is more a project for those who want to enhance the program for themselves.

Module 13: Drag and Drop

Unfinished business

Before we discuss the way in which we save using a traditional save window, we must first complete the response to clicks on buttons in the main window. The '|<' and '>|' symbols are meant to represent 'go to the first record' and 'go to the last record', which provides a little extra help in moving through the records. Suggested as an exercise in module 9, we simply list the appropriate coding for you here.

Firstly, in PROCclick and the main% loop we need to add two more options:

WHEN 7: PROCbegin

and

WHEN 10: PROClast

The two procedures should now be inserted, the first before DEFPROCback, and the second after the ENDPROC of DEFPROCnext.

DEFPROCbegin
IFn%=1 ENDPROC
n%=1
PROCreset
ENDPROC

DEFPROClast
IFn%>=50 ENDPROC
n%=50
PROCreset
ENDPROC

[Insert sprite Mod13/1 - Caption: 'Completing the button functions'.]

Saving to a filer directory

This process is very much a Wimp-related one, and has little to add to our knowledge of how to use Basic. It is made complicated by the need to use the Wimp message system to communicate with the filer itself. We also have to define a new type of icon that can actually be dragged around the screen. In order to 'hear' the messages from the Wimp, we must include additional reason codes in the wimp poll loop. To do this, add the line

WHEN 17,18:PROCreceive

to the polling loop. Notice here that we can include more than one option in the WHEN statement. Both reason codes will be responded to in this case and handled by the same procedure, PROCreceive.

To keep matters tidy as we go along, now add DEFPROCreceive as a new procedure definition immediately following the ENDPROC of DEFPROCmenuselect.

DEFPROCreceive
CASE block%!16 OF
WHEN 0:quit%=TRUE
ENDCASE
ENDPROC

Strictly speaking, every program should contain this code, since otherwise it will always ignore messages sent to it by the Wimp. The code 0 picked up by this routine tells the program that it is being shut down, and it may be that you would want to take some action before it happens. quit% = TRUE makes the program leave the poll loop and end.

The order of events will be as follows.

1. A standard save-type window will be opened, and the user will type in a suitable name for the file, and then drag the icon to a filer directory.

2. When the icon in the save box is clicked on, PROCclick responds by sending the program to a procedure that drags the icon in response to movement of the mouse.

3. When the mouse button is released, a message is sent back from the Wimp to the program. This is picked up in the wimp poll loop as reason code 7 which directs the program to PROCstartsave. This procedure works out where the pointer is, and sends a message to the Wimp to ask for the details of the directory.

4. The poll routine picks up the reply through reason code 17 and goes to PROCreceive so that the information can be interpreted and acted upon. It carries with it an action code 2 enabling the procedure to pick out the necessary response by directing to PROCdatasave. This procedure acknowledges the information by sending back action code 3, and finally the wimp sends back a message to say all is complete.

5. The actual saving process now begins with PROCmanualsave.

We make no further attempt to explain the finer details of the required code, but some of it is complex and must be copied with care.

The save window

Insert the following at the end of PROCinit.

REM Save box window

DIM savespr% 8,savetext% 255,saveval% 5
$saveval%="R2A~ ":$savespr%="file_ffd":$savetext%="SecretData"
save%=FNcreate_window(604,336,280,180,0,0,&86000012,2,2,&19,"Save as:",3,0)
a%=FNcreate_icon(save%,100,-92,68,68,&6102,"",savespr%,1,9)
a%=FNcreate_icon(save%,8,-156,192,48,&700F12D,"",savetext%,saveval%,256)
a%=FNcreate_icon(save%,208,-156,64,48,&C701913D,"",ok%,R5%,3)

The code in saveval% will control what characters can be entered in the pathname of the file, thus preventing disallowed characters. Note the use of 'file_ffd' to define the icon type to appear in the window. This is the standard data icon. Note also the button type for this icon that makes it dragable.

[Insert sprite Mod13/2 - Caption: 'The save window is easily recognisable'.]

In PROCclick, we need to add yet another option in order to open this window, but remember that this window opens in response to 'Enter filename' in the check code window. The following lines must therefore be added in the savecheck section within PROCclick, between the lines 'PROCautosave' and 'ENDCASE'.

WHEN 4:
!block%=savecheck%
SYS"Wimp_CloseWindow",,block%
!block%=save%
SYS"Wimp_GetWindowState",,block%
block%!28=-1
SYS"Wimp_OpenWindow",,block%
PROCcaret(save%,1)

Dragging the icon

When we click on the icon to drag it, PROCclick needs to respond. Add the following code to the end of PROCclick.

WHEN save%
CASE block%!16 OF
WHEN 0:REM Click on dragable icon
IF block%!8 AND 64 PROCdragbox
WHEN 2:REM Click on OK
IF block%!8 AND 5 PROCquicksave
ENDCASE

[Insert Drawfile Mos13/3 - Caption: 'What is happening as we drag a file?'.]

We are ignoring the click on the OK button at the moment, but we must now write PROCdragbox. Copy it with care! Place it before DEFPROCautosave.

DEFPROCdragbox
!block%=save%
SYS "Wimp_GetWindowState",,block%
vx%=block%!4-block%!20
vy%=block%!16-block%!24
block%!4=0
SYS "Wimp_GetIconState",,block%
sprite$="file_ffd"
IF spritedrag% THEN
!block%=block%!8+vx%
block%!4=vy%+block%!12
block%!8=block%!16+vx%
block%!12=vy%+block%!20
SYS "DragASprite_Start",%11000101,1,sprite$,block%
ELSE
block%!4=5:block%!8=vx%+block%!8
block%!12=vy%+block%!12
block%!16=vx%+block%!16
block%!20=vy%+block%!20
block%!24=0:block%!28=0
block%!32=&7FFFFFFF
block%!36=&7FFFFFFF
SYS "Wimp_DragBox",,block%
ENDIF
ENDPROC

This procedure might have been better placed in the Library but it does contain two specific items to this program. Firstly it again specifies the data sprite in the eight line. Secondly it uses a variable called spritedrag% which we must now define at the start of PROCinit. The purpose of this is to enable solid drags if the OS in use is capable of doing so. The code to check this and set spritedrag% accordingly is as follows, and should be inserted immediately after the ON ERROR statement at the beginning of PROCinit.

SYS "OS_Byte",161,28 TO ,,val%
spritedrag%=((val% AND 2)=2)

spritedrag% is TRUE if solid drags are supported.

Exchanging messages

When the mouse button is released, the Wimp passes a reason code of 7, as so begins the exchange of messages. In the Poll loop, add the response

WHEN 7:PROCstartsave

Now add DEFPROCstartsave immediately following DEFPROCdragbox.

DEFPROCstartsave
IFspritedrag% SYS"DragASprite_Stop"
fsize%=2000:type%=&FFD
SYS "Wimp_GetPointerInfo",,block%
block%!20=block%!12:block%!24=block%!16
block%!28=!block%:block%!32=block%!4
block%!36=fsize%
!block%=64:block%!12=0
block%!16=1:block%!40=type%
$(block%+44)=FNgetleaf($savetext%)
SYS "Wimp_SendMessage",18,block%,block%!20,block%!24
saveref%=block%!8
ENDPROC

This process receives a reply through the line 'WHEN 17,18:PROCreceive', but to handle this we must now add a second option to the choices within PROCreceive:

WHEN 2:PROCdatasave

Add DEFPROCdatasave after the startsave procedure:

DEFPROCdatasave
$savetext%=FNstring(block%+44)
!block%=save%
block%!12=block%!8
block%!16=3:!block%=256
SYS "Wimp_SendMessage",18,block%,block%!20,block%!24
PROCmanualsave
altered%=FALSE
ENDPROC

and follow on with PROCmanualsave:

DEFPROCmanualsave
!block%=save%
SYS"Wimp_CloseWindow",,block%
code2$=FNstring(code%)
$code%=STRING$(8,CHR$0)
IF code$<>code2$ PROCreport("Codes do not match",1):ENDPROC
PROCencode
path$=FNstring(savetext%)
$path%=path$
PROCsave
FORX%=1TO50
secret$(0,X%)=""
desc$(0,X%)=""
NEXT
ENDPROC

Note that this is very similar to autosave. We are back on more familiar ground. Finally, for completeness, go on and add PROCquicksave which enables you to save by clicking on the OK icon.

DEFPROCquicksave
IF INSTR($savetext%,".") THEN
PROCmanualsave
altered%=FALSE
ELSE
PROCreport("To save, drag the icon to a directory viewer",1)
ENDIF
ENDPROC

Notice how we change the value of altered% in these routines to register that the current data has been saved. This is not exploited in the program yet, but could be with little further code.

You were warned that this would be complex. To know more about this you are referred to Lee Calcraft and Alan Wrigley's book 'Wimp Programming for All' or other similar texts. Meanwhile, you could of course have copied most of this code from the Freeware version, but would you have learnt as much?

Module 14: Bits and Pieces

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.

[Insert Sprite Mod14/1 - Caption: '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)

[Insert sprite Mod14/2 - Caption: '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.

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

DIM Allocating memory: eg DIM place% 16 4

Defining arrays: eg DIM array$(3,8) 9

END 1

EOR 11

FALSE 2

File commands: 11,12

OPENOUT()

PRINT# as in PRINT#O,a$

OPENIN()

INPUT# as in INPUT#I,a$

CLOSE#O

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