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