| Search Locate Previous Next | Contents |
Virtually all Windows applications now protect their users against accidental replacement of existing data with a new file. Naturally, they also warn users against exiting with unsaved data. When you start to work through the logic needed to implement this in your APL system it all gets surprisingly complicated for example if you try to clear from an (untitled) file the application must check if you changed anything, prompt for a file name, check that the name you supply does not already exist, and only then write to disk! Here is part of the decision tree:
No -- Clear
/
New -- Dirty? No -- Clear
\ /
Yes -- Wanted? Yes -- Save & Clear
\ /
Yes -- Named? No -- see above
\ /
No -- Get name -- Exists? Yes
^ \ /
| Yes -- Overwrite?
| \
+---------<--------<--------<-No
... and of course there is the option to Cancel at any stage!
This is obviously one of those problems you want to solve once, and never have to think about again! One implementation of the solution is encapsulated in the Causeway NOSE (New-Open-Save-Exit) class if you want to follow through the logic have a look at the FilePath function with )ed #.Class.NOSE.FilePath. The changes needed in the application code are (fortunately) nil, as we wrote it with Gui-independence in mind. All we need to do is to add a NOSE (under the Special submenu) to the main dialogue box and make some minor changes to the menu calls and tool button actions:
Here is the property browser for the nose it will update the file name for us (file is set as its File property), but it needs to know how to prompt for it in the Windows filebox. This is why you should fill in something like:
'*.con' 'Contacts file'
... in the Specification property. It appears in the form (in the designer only) as a small square which is conventionally stuck in the top left corner, but of course you can put it wherever you like. Naturally, it has no visible caption, so it uses the caption you give it as a hint!
Now to change the main menu, but first a useful trick that you will come across frequently when building complex forms with interacting objects. We are going to want to run several built-in functions (methods) on the nose, and to do this we need its true (Dyalog) name as the left argument for CPro.Do. The easy way is to catch this name on creation, and set it as a local variable on the main form.
Here you can see that I have done the same trick for the status bar, which we will need shortly.
Now set up the event table for the nose itself to look like this:
#.CPro.Root.FM12.NOSE23) as local var. nose.
SAVE to zero at the above checkpoints, and set it to one whenever you touch some data. Dangerous, in that you need to remember all the places where the user can touch something. Just set the action to ¾SAVE.
Now all we need to do is to set the actions on the menu to call the nose rather than running the file functions directly:
[file] &New=Ctrl+N;Clear all data : nose Do 'New' &Open=Ctrl+O;Open an existing file : nose Do 'Open' &Save=Ctrl+S;Save your work : nose Do 'Save' Save&As;Save with a new name : nose Do 'SaveAs' ------------- E&xit:Post 'SC'
... and the same for the action table on the toolbar:
Disp dbx.tbar[2 3 4;1 2 5]ÚÎÎÂÎÎÎÎÎÂÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÌ
ÛTLÛ*New ÛÚÎÎÎÎÎÎÎÎÎÂÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÌ Û
Û Û ÛÛBehaviourÛÚÎÎÂÂÎÎÎÎÎÎÎÎÎÎÎÎÎÎÂÎÎÌÛ Û
Û Û ÛÛ ÛÛSLÛÛnose Do NewÛ{}ÛÛ Û
Û Û ÛÛ ÛÀÎÎÁÁÎÎÎÎÎÎÎÎÎÎÎÎÎÎÁÎÎÙÛ Û
Û Û ÛÀÎÎÎÎÎÎÎÎÎÁÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÙ Û
ÃÎÎÏÎÎÎÎÎÏÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÝ
ÛTLÛ*OpenÛÚÎÎÎÎÎÎÎÎÎÂÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÌÛ
Û Û ÛÛBehaviourÛÚÎÎÂÂÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÂÎÎÌÛÛ
Û Û ÛÛ ÛÛSLÛÛnose Do OpenÛ{}ÛÛÛ
Û Û ÛÛ ÛÀÎÎÁÁÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÁÎÎÙÛÛ
Û Û ÛÀÎÎÎÎÎÎÎÎÎÁÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÙÛ
ÃÎÎÏÎÎÎÎÎÏÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÝ
ÛTLÛ*SaveÛÚÎÎÎÎÎÎÎÎÎÂÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÌÛ
Û Û ÛÛBehaviourÛÚÎÎÂÂÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÂÎÎÌÛÛ
Û Û ÛÛ ÛÛSLÛÛnose Do SaveÛ{}ÛÛÛ
Û Û ÛÛ ÛÀÎÎÁÁÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÁÎÎÙÛÛ
Û Û ÛÀÎÎÎÎÎÎÎÎÎÁÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÙÛ
ÀÎÎÁÎÎÎÎÎÁÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÙ
Finally, we must allow the nose to intervene when the user hits File,Exit, or (in Windows 95) accidentally catches the X button at the top right of the form. Causeway always signals a Close Request (CL) event just before the main form exits if you run an action which rejects this event, then the form will not be closed.
This is the first time you have seen the Condition column used in the event-action table. The nose will check if it is safe to exit, and return 1 if all the conditions are met (nothing to save, or the user hit No).
This leads to the rather odd double negative if the condition is not true we must reject the event, so preventing the form from closing.
To explain the condition/action columns more formally:
... In this example, the condition runs the Exit function from the nose object, if this returns 1 it is safe for the user to leave there is no unsaved data or he/she declined to save it. If it returns 0, the condition column is TRUE, so Causeway executes the action CPro.Reject. This stops any further processing of the table, and causes Dyalog to reject the event so the form will not be closed.
That completes the introduction to the Causeway nose class. It is not simple, and you might well come back to these notes several times in the future I often find myself checking existing applications as a reminder of how I coded it. Probably what you should now do is to spend some time testing your main dialogue, and exploring all possible branches of the decision-tree to prove that it really is impossible for your users to (accidentally) destroy their data.