CanDo: An Interactive Authoring Tool, Part 2 - Documents, User-Defined Variables, and Databases

by Randy Finch

Welcome to the second part of a series of articles about CanDo. I hope you enjoyed the first part comparing CanDo 2.0 with Visual Basic 2.0 for the Windows environment. I have just received Visual Basic 3.0 and am anxious to install it and try it out. I also understand that Inovatronics will soon release CanDo 2.5. I do not know any of the new features it will offer except the support of AmigaDOS 3.0 and AGA graphics. Well, onward and upward! As I promised, this installment discusses using CanDo documents in conjunction with user-defined variables as well as getting you started with database programming.

First off, let me say that it is not my intent to present a detailed tutorial of the CanDo programming language in this article. Rather, I will be showing you how to combine various features of CanDo to perform interesting and useful tasks. If you have any questions about the syntax of any statements in my programs, I refer you to the CanDo manual or the on-line help system available in the CanDo software.

I will be presenting two programs. The first shows you how documents and user-defined variables can be combined in a useful way. The technique presented can be used in many different applications. The second program shows how easy it is to develop database applications with CanDo.

Excerpts from a Simple Budget

The first program I will present is an excerpt from an application I am writing entitled SimpleBudget. As you may have guessed, this is a home budgeting application. While writing SimpleBudget, I had to use documents and user-defined variables extensively. The program excerpt I will be discussing is centered around one of several cards and its support routines. The purpose of this card is to allow the user to enter the names of the financial accounts he or she has and the names of the different categories each account is divided into. For instance, the user may have one account named Checking which is divided into categories such as Car Payment, Mortgage, Food, Clothes, etc. and another account named Savings with categories of Johnny's Education and Sue's Summer Trip. Each of these accounts represents an actual account at a bank, credit union, or other financial institution. The categories are just artificial divisions of the accounts created by the user for budgeting purposes.

The card shown in Figure 1 is named Create_Accounts. On the card are several CanDo objects. There are two TextField objects named Field_Account and Field_Category. They are the two beveled rectangles near the top of the screen and are used for entering new account and category names. Directly under these objects are two large beveled boxes which are List objects named Document_Accounts and Document_Categories. They are used to display lists of account and category names. Finally, there are six TextButton objects. The two to the left of the Document_Accounts list object are named DeleteAccount and SortAccount. The first allows the user to delete a single selected account while the latter causes the program to sort the list of account names. The two TextButtons to the left of the Document_Categories are named DeleteCategory and SortCategory. They perform the deleting and sorting of the category names. The final two TextButtons are located in the lower right corner of the screen and are named OK and Cancel. The first allows the user to accept the changes made to the account and category names while the latter allows the changes to be discarded.

The Create_Accounts card also contains four text strings: "Enter Accounts:", "Enter Categories:", and two "000" strings. The latter two strings will be continually updated by the program to keep the user informed about the current number of accounts and categories in the two List objects. Figure 2 shows how the card may look after adding some accounts and categories. Notice that the account named Checking has been selected by the user. This account has five categories associated with it: Car Payment, Church, Clothes, Food, and House Payment.

The Paper Work

Now that the user interface has been laid out and we know how it should function, it's time to do a little paper work. We must decide how to manage the user's data. This is where CanDo's flexible user-defined variables become extremely useful. First, a primary variable name must be chosen. I use the controversial, yet thought provoking name Budget. Several levels of secondary names will hang off of this primary variable name. The variable Budget looks like this:

	Budget.NumAccounts
	Budget.Account[].Name
	Budget.Account[].Numcategories
	Budget.Account[].Category[].Name
	Budget.Account[].Category[].BeginBalance.Dollars
	Budget.Account[].Category[].BeginBalance.Cents
	Budget.Account[].Category[].EndBalance.Dollars
	Budget.Account[].Category[].EndBalance.Cents
	Budget.Transactions[].Type
	Budget.Transactions[].DollarsTotal
	Budget.Transactions[].CentsTotal
	.
	.
	.

Notice that Budget handles a dynamic array (represented by the brackets) of account names and a dynamic array of category names for each account along with other types of information. The entire data structure for Budget is quite lengthy and is not presented in its entirety. The Create_Accounts card only makes use of the first eight items in the structure. The other items in the Budget data structure are used by other cards in SimpleBudget and are not discussed in this article.

The nice thing about storing all of the data in a user-defined structure as presented above is that it makes it very simple to save and retrieve the data from a disk file. For example, if you want to save the Budget data structure, you can issue the following command within the CanDo code:

	SaveVariable Budget, Filename

You can just as easily retrieve this data back into the Budget variable with this command:

	Budget=LoadVariable(Filename)

This feature makes CanDo extremely useful for application development. It beats the heck out of having loops within loops within loops around a PRINT or WRITE command like you would need in BASIC.

More on the User Interface

Listing 1 shows all the information about the Create_Accounts card. The listing was generated by a utility that is packaged with CanDo, and the line numbers were added using the AmigaDOS CLI command "TYPE filename OPT N". When you are creating the user interface discussed earlier, you can use the information in this listing to help you lay out the interface exactly the way it appears in Figure 1. The window information for the card is shown in lines 64-77. All the information you need for laying out the various objects are shown in the Definition sections of the listing. Notice in lines 270-299 that there are two TextMenu objects attached to the card. These are drop-down menus. You do not need to add these objects if you do not want to. They simply allow an alternative to the OK and Cancel text buttons.

Adding Functionality

Once the interface is defined within CanDo, it is time to add functionality to it by adding code. The code can be added as event routines or as global routines. The event routines are executed when some event happens to an object or card. Global routines are subroutines that can be called from event routines or other global routines and are usually used when certain tasks need to be accomplished from within more than one routine. If the programmer does not add any code to an event routine, then nothing happens when the event occurs.

Let's start with the Create_Accounts card. It has two event routines: BeforeAttachment and AfterAttachment (lines 34-52 and 53-63). The former is executed just before a card is displayed on the screen and is normally used for assigning values to variables that are used by the card. The latter executes after the card is displayed and is normally used for printing text and graphics on the card.

The BeforeAttachment routine is extremely important for the Create_Accounts card. It transfers information stored in the Budget variable to several documents. Documents in CanDo are like variable length strings except that sophisticated operations can be performed on documents. Also, a document can be attached to a List object so that when the document changes, the List object is updated automatically. The Document_Accounts list object has the Accounts document attached to it (line 86); the Document_Categories list object has the Categories document attached to it (line 202). These attachment assignments are made while designing the List objects.

Since there is an array of category names for each account, a mechanism is needed to create a multitude of documents with different names, one for each account. Once these documents are created, the text from these documents can be moved into the Categories document as the user selects different accounts. This is necessary since the name of the document attached to a List object cannot change; however, the text of the attached document can be changed. Therefore, I decided to, in essence, create an array of documents. Documents cannot actually be part of an array; thus, a little ingenuity is needed. Since documents are referenced by string variables, it is possible to use the string concatenation operator to create the document array. Each document in the array will have a name of the form Categories.AccountName where AccountName can be any of the user account names. Thus, if the user has three accounts named Checking, Savings, and MutualFund, three documents will be created named Categories.Checking, Categories.Savings, and Categories.MutualFund to store the three lists of category names for the accounts.

The BeforeAttachment routine for the Create_Accounts card is used to move the current names of accounts and categories in the Budget variable to appropriately named documents. The user can then manipulate the documents to his or her heart's content. Once the user is finished modifying the accounts and categories, he or she can select the OK TextButton or the Okay TextMenu, in which case the information in the modified documents will be put back into the Budget variable for later saving via the SaveVariable command. However, if the user selects the Cancel TextButton or the Cancel TextMenu, the document contents are not transferred back to the Budget variable, thus leaving the original values intact. Therefore, the documents act as temporary storage areas for new input.

The code for BeforeAttachment is quite simple. It sets the working document to Accounts, clears it, determines the number of accounts in the Budget variable, and then uses an embedded loop to build up the Accounts document and each of the Categories.AccountName documents. Each name is inserted into the appropriate document using the Type command (lines 40 and 46). Notice the NEWLINE parameter at the end of this command. This causes the pointer for the document to advance to the next line. Without it, all the names would appear on the same line.

Global Routines

Before looking at the code in the rest of the event routines for the Create_Accounts card, let's take a look at the seven global support routines in Listings 2-8. This will help you better understand what the event routines are doing when they call one of these global routines.

Listing 2 is the Cancel Accounts routine. It is only called when the Cancel menu item is selected. (The Cancel button has the same code as part of the event routine itself. This is just to show you that there are different ways of accomplishing the same task.) This global routine simply directs the program to a card called Intro. This is another card in the SimpleBudget application that is not discussed in this article. Take a look at line 3. CanDo does not allow a comment to reside on a line alone; it must follow a command. Therefore, the Nop (meaning "No Operation") command is used to precede the comment. This is something I want to see fixed in a future release of CanDo.

Listing 3 is the Display Categories routine. It is used to move the appropriate Categories.AccountName document into the Categories document so it will be displayed in the Document_Categories list object. Typically this routine will be called whenever the user selects an account name in the Document_Accounts object with the left mouse button. Since there can only be one working document at a time, this routine first assigns the name of the current working document, stored in the system variable DocumentName, to the variable CurrentDocument. Next, the working document is changed to Accounts and the name that is stored in the currently selected line of Document_Accounts is assigned to the variable ChosenAccount. TheLine is a system variable used to store the text of the currently selected line of the current working document. If the value of ChosenAccount is not NULL, then the working document is changed to the appropriate Categories.AccountName using the WorkWithDocument command. This command will create the document if it does not already exist. Next, the text from this document, if any, is assigned to the string variable NewCategoryDocument. The working document is changed to Categories (the one attached to the Document_Categories object), cleared of its current text, and then assigned the text from NewCategoryDocument. At this point, the categories for the selected account will be displayed. Since the documents Accounts and Categories will always have a blank line as the last line in the document (used as the insertion point for a newly entered name), it is possible for ChosenAccount to be NULL. If this is so, the Categories document is cleared because there are no categories for this blank account name. Finally, the original working document is reset.

Listing 4 is the OK Accounts routine. It is executed when the OK button or Okay menu item is selected by the user. It is very similar to the BeforeAttachment routine discussed earlier except it moves all of the information in the documents to the Budget variable rather than the other way around. The number of accounts and categories in each document is determined by taking the number of lines in the document and subtracting one (lines 5 and 13). The one is subtracted because there is always a blank line at the end of the document. The system variable LinesInDocument always holds the number of lines in the current working document. The account and category names are extracted from the document by moving the cursor to the start of the appropriate document (lines 4 and 12) and using the system variable TheLine as described earlier. The document cursor is then moved through the document one line at a time (lines 23 and 27) extracting the names as it goes.

Listing 5 is the Print Num Accounts routine. It updates the display near the top of the Create_Accounts card showing the number of accounts in the Document_Accounts object. It does this by clearing the current number on the card and then displaying the new number.

Listing 6 is the Print Num Cats routine. It is identical to Print Num Accounts except it updates the number of categories displayed in the Document_Categories object.

Listing 7 is the Sort the Document routine. It sorts the current working document; therefore, the working document needs to be set before calling this routine. The main body of the sort routine (lines 5-9) is only executed if there is at least one non-blank line in the document. The SortDocument command is used to accomplish the sort and the NOCASE option is used to make the sort non-case-sensitive. After the sort is complete, the first line will always be the blank insertion line that is normally at the end of the document. Thus, the document cursor has to be moved to the first line in the document and that line deleted. The cursor is then moved to the end of the document and the NewLine command is issued in order to leave the document with a blank line at the end.

Listing 8 is the Update Categories routine. It moves the contents of the Categories document into the appropriate Categories.AccountName document. This is normally done whenever the user selects a different account name to make sure that any changes made to the categories for the previously selected account are stored properly. The routine is very similar to the Display Categories routine.

Event Routines

Now it is time to look at the other event routines for the Create_Accounts card shown in Listing 1. I have already discussed the BeforeAttachment routine; let's now look at the AfterAttachment routine (lines 53-63). This routine puts some text on the screen and prints the number of accounts and categories via global routines. Finally, it activates the Field_Account object, anticipating that the first thing the user will want to do is add an account name. At this point the application will simply wait until the user does something. The code that is executed is dependent upon the user's action. Let's take a look at these possibilities.

The Field_Account object has two events associated with it: OnClick (lines 113-119) and OnRelease (lines 106-112). The OnClick routine executes when the user moves the mouse pointer to the field and clicks the left mouse button. This routine sets the working document to Accounts, moves the cursor to the last line in that document, and then updates the categories. The OnRelease routine executes when the user presses the ENTER key while in the field. It obtains the text from the field, inserts it into the working document, which was set to Accounts in the OnClick routine, clears the field, reactivates the field, and then updates the number of accounts. At this point, the new account name entered by the user will be shown in the list of accounts in the Document_Accounts list object.

The Field_Category object also has an OnClick event routine (lines 265-268) and an OnRelease event routine (lines 256-264). These routines perform functions similar to the OnClick and OnRelease routines for the Field_Account object.

The Document_Accounts object also has OnClick (lines 88-92) and OnRelease (lines 93-95) routines associated with it. When the user clicks on an account name in this object, the current categories are updated, the categories for the chosen account are displayed, and the number of categories is updated. The OnRelease routine simply activates the Field_Category object anticipating that the user will want to add new categories to the chosen account.

The Document_Categories object only has an OnClick routine (lines 204-206). It sets the working document to Categories.

The DeleteAccount button has only an OnRelease routine (lines 163-174) associated with it. When the user presses and releases the left mouse button while the pointer is over this object, this routine will execute. Making it an OnRelease routine rather than an OnClick routine allows the user to change his or her mind about the selection after clicking the mouse button by moving the pointer off the object before releasing the mouse button. The routine sets the appropriate working document, assigns the variable DeletedAccount to the name of the chosen account, deletes the account name, and then updates the number of accounts. Since the categories associated with the deleted account are no longer valid, the routine clears the Categories.AccountName document associated with the deleted account. Finally, the category information is updated. The DeleteCategory button also has an OnRelease routine (lines 219-226). It deletes the chosen category and updates the category information, leaving Field_Category activated.

The SortAccount button has an OnRelease routine (lines 187-192) that updates the current categories list, makes Accounts the working document, and then calls the Sort the Document global routine. Remember that the working document has to be set before calling the sort routine. Finally, the Field_Account object is activated.

The SortCategory button has an OnRelease routine (lines 239-245) that works similarly to the SortAccount OnRelease routine.

Finally, the OnRelease routine (lines 132-134) for the OK button and the Occurred routine (lines 281-283) for the Okay menu item simply call the OK Accounts global routine described earlier. The equivalent routines for the Cancel button (lines 147-150) and the Cancel menu item (lines 296-298) go to another card named Intro (not discussed in this article).

I hope that this CanDo program excerpt has helped you see how useful user-defined variables in conjunction with pseudo-arrays of documents can be for application programming. Many different types of applications could benefit from the techniques discussed. Let's now turn our attention to some of the nice features of CanDo that make for easy database programming.

Database Programming

Figure 3 shows the user interface to a simple database application. The name of the card on which the interface was created is DataEntry; the program is shown in Listing 9. The interface consists of five TextField objects and six TextButton objects. The TextField objects are used for entering names and addresses. Every name/address combination is called a record. The TextButton objects are used to add a new record, delete an existing record, display the previous record, display the next record, load the database from disk, and save the database to disk.

Notice in Listing 9 that the names of all of the TextField objects have something in common; they all begin with a period. They are named .Name, .Address, .City, .State, and .ZipCode. When the names of these fields begin with a period, some special commands are available for moving the data in the fields to user-defined variables and vice versa. The primary variable name used in the program is Address. Its structure is as follows:

	Address[].Name
	Address[].Address
	Address[].City
	Address[].State
	Address[].ZipCode

This is a dynamic array of addresses. Notice that all the extension names match the names of the TextField objects on the DataEntry card. This is necessary for the special database commands to work properly. (However, this is essentially a moot point since user-defined variables do not have to be pre-defined.)

Each TextField object has an OnRelease routine that simply activates the next field on the screen. Thus, if the user types a name in the .Name field and presses ENTER, the .Address field will be activated and so on. This allows the user to easily enter data for a record without having to activate each field with a mouse click. When ENTER is pressed in the .ZipCode field, the .Name field is reactivated. The six TextButton objects also have OnRelease routines associated with them. Let's take a look at them in order.

The Add button is used to add another record to the Address variable. The OnRelease routine (lines 129-134) uses the GetDBObjects system variable. This variable, when assigned to the current record of the Address variable, which is numbered CurIndex, moves the values of all the specially named fields on the DataEntry card into the Address variable. (Nice, huh?) This, in essence, stores the currently displayed record. Next, CurIndex is incremented, a new blank record is inserted into the Address array using the InsertArrayEntry command, and the global routine Show Record is called to display this newly inserted, blank record on the screen ready for user input.

Listing 10 shows the global routine Show Record. It uses the SetDBObjects command. This command extracts the current record from the Address variable and displays the components of the record in the appropriate fields on the card. (Also nice, huh?) Next, the window title for the card is changed to reflect the new record number, and the .Name field is activated, awaiting additional user input.

Now we return to Listing 9 and the Delete button. The OnRelease routine (lines 147-153) for this button first deletes the current record using the DeleteArrayIndex command. If the record being deleted is the last one in the database, then the variable type (obtained with the VarType function) of Address[CurIndex] will now be equal to "Nothing" since it does not exist. If this is the case, the LastArrayIndex function is used to determine the actual index of the last record in the Address variable. This value is assigned to CurIndex. Finally, this record is displayed.

The OnRelease routine for the Prev button (lines 166-173) stores the current record, determines the previous record number using the PreviousArrayIndex function, and shows this record. If there is no previous record, meaning the one currently shown is the first in the array, then the system variable SearchFound will be set to FALSE when the PreviousArrayIndex function is executed. In this case, the current record is set to the last record in the database.

The OnRelease routine for the Next button (lines 186-193) works the same as the Prev button routine except it searches forward in the array using the NextArrayIndex function. If there is no next record, then the first record is displayed. This, in combination with the first to last looping of the Prev button makes the database circular rather than linear. This is like allowing a King-Ace-Two spread in rummy.

The Load button's OnRelease routine (lines 206-214) deletes all the data in the Address variable using the Dispose command. It then uses the AskForFilename function to display a file requester on screen. Once the user selects a filename and closes the requester, the filename is returned to the variable FN. If this file exists, it is loaded into the Address variable using the LoadVariable function, and the first record is displayed.

Finally, the OnRelease routine for the Save button (lines 227-231) stores the currently displayed record, obtains a filename from the user, and then uses the SaveVariable command to save the data in the Address variable to the file.

Of course, this database program is very simple and more code needs to be added to make it a usable program. However, my intent is to help you become familiar with how the special database commands and functions are used.

Closing Comments

Well, that's about enough, don't you think? You are probably tired of reading, and I know I am tired of banging these blasted keys on my computer keyboard. I hope this article has helped you to appreciate the invaluable tools available in CanDo for writing application software for the Amiga computer. If you need to ask me anything about the article, you can write me care of this magazine.

In the next part of this series, I will discuss how you can combine built-in CanDo objects with some graphics and code to create a custom object that can be used in many different applications.


Back to list of articles
Back to Randy Finch's Home Page
Last modified on April 1, 1996
Randy Finch at webmaster@rcfinch.com