Washington Apple Pi

A Community of Apple iPad, iPhone and Mac Users

Building a Check Register in REALbasic, Part 8

By Brent Malcolm

Washington Apple Pi Journal, reprint information

Over a year ago, I wrote a brief article for the May/June 2007 issue of the Washington Apple Pi Journal that was an introduction to programming in REALbasic (RB). I followed that up with another article demonstrating the use of RB to build a simple checkbook register.  Since then I have written several more articles, each adding more features to the original program to expand the original simple checkbook register into a full-featured application. This is the final article in my series.

In this installment, I will show you how to add functions that will let you run the application as your own check register. No data files will accompany the application; you will generate them.

The application through Part 7 can be downloaded here:

http://www.wap.org/journal/realbasic/

Up to now, I have stored the application and the data files in the same folder so the application knew where the files were and thus, opened them automatically. This was convenient for a tutorial such as this. However, Macintosh applications are normally stored in the Applications folder and data files are normally stored in the Documents folder. Therefore I must add the capability to open the application and its files while they reside in two separate locations and to generate these files when the application is opened for the first time.

To support the new code additions I will add five new globalFinancial properties: the boolean newAccount, shutdown and fileIsOpen and two folder items, dataFileFolder and dataFile. I will also add a new method, readFilePointer, so the program can locate the checkbook data files.

First, I need to modify the transWindow initialize method to support the case where the application is opened for the first time.

today = new date
readFilePointer
readPrefs
if newAccount Then
  NewAcct.ShowModal  // Call dialog
  if shutdown then Quit // UserCancelled
Else
  dataFile = dataFileFolder.child("Checkbook Data")
  readDataFile
  readPayeeList
  readDepositorList
End
setSystemYear
findBankBalance // Compute bank's balance

Read the FilePointer

When the application opens there are two cases to consider so the logic goes like this. First, it looks for a file containing a pointer to the location of the folder that contains the check register files. If no pointer file is found, it follows that the application has never been run and new data files must be generated. However, if a pointer is found but it does not point to valid files, the application has been previously run and a correct pointer must simply be restored.

The following readFilePointer method (added to globalFinancial) manages the first case and is called by the transWindow initialize method above.

dim f As FolderItem
dim s As String
 
f = preferencesFolder.child("Check Register pointer")
if f <> nil and f.exists then
  inStream = f.openAsTextFile
  s = instream.ReadAll
  dataFilefolder = DocumentsFolder.GetRelative(s)
inStream.Close
Else
  // file pointer file does not exist
  newAccount = True
End

This method opens a file in the Preferences Folder to get the location of the data file folder. If the file doesn’t exist it must be a first opening and newAccount is set true. The new term “GetRelative” produces a function that points to the data folder location relative to the Documents Folder. The case where the pointer is invalid will be addressed later.

The second file to be opened is the Preference file. Previously, if the Preference file was not present, a message box alerted the user. Instead, I’ll set the change flag:

prefsHaveChanged = True  // force a writePrefs

This will force the application to write a new Preference file upon closing.

Next, in the initialize method where newAccount is true, I will call the NewAcct window. The term “ShowModal” halts the program until either the OK or Cancel button in that window is pressed. If the NewAcct Cancel button is clicked, the variable shutdown will be set True. This will cause the application to quit under the assumption that the user doesn’t want to set up the new account at this time.

The two pieces of information needed to open a new account are the account balance and the next check number. The new dialog, NewAcct, shown in Figure 1, briefly describes the files that will be generated and provides edit boxes to collect the required information. In addition the check boxes allow the user to prepare an initial listing of Payees and Depositors.

Opening checkbook for first time

Figure 1: When you open your checkbook application for the first time, this window appears and prompts you to enter the information you need to get started.

Posting the New Account Data

Once the user has provided the requested data in NewAcct, the OK button action handler executes the following:

dim d as new date
dim s, eoyDate As string
 
if not testEntries then return
 
d.year = d.year-1
d.month = 12
d.day = 31
eoyDate = d.shortdate

// Build record
s = eoyDate + chr(9)
s = s + chr(9) + chr(9)
s = s + "Brought Forward" + chr(9) + chr(9)
s = s + newAmount.text
TransWindow.addTransRow(s, 0)
nTransactions = nTransactions + 1
nextcknr = val(newCkNr.text)

if addPayees.value = true then
  newNames.title = "Payees Names"
  newNames.showmodal
End

if addDepositors.value = true then
  newNames.title = "Depositors Names"
  newNames.showmodal
End

newAccount = True
ListHasChanged = true
self.close

This code begins with the method testEntries that insures all entries are valid. Then, the present year is used to develop the date for 31 December of last year to accompany the Brought Forward amount in the first record of the register that is built and posted in the following lines. Next, if the user has checked either of the “Add” check boxes  the newNames dialog box appears with the appropriate title (see Figure 2) to collect the Payee and/or Depositor names and build new files for them (see below).

Add payee

Figure 2: Add Payee & Depositor names with this window.

Lastly, two flags are set. The newAccount flag blocks the reading of the Data, Payee and Depositor files which have yet to be written. The ListHasChanged flag will prompt the writing of the new data file when the application is closed.

New Payees & Depositors

The newNames dialog merely collects names and adds them to the variable arrays depList() or payList() as appropriate. These variables were discussed in Part 3 when I added the Payee and Depositor lists. The Add button code:

If self.title = "Payee Names" then
  npayees = npayees +1
  PayList.append nameEditField.text
  refreshPayees // update the list
  nameEditField.text = ""
  payeesChanged = true
End

If self.title = "Depositor Names" then
  ndepositors = ndepositors + 1
  DepList.append nameEditField.text
  refreshDepositors // update the list
  nameEditField.text = ""
  depositorsChanged = true
End

Here the window title identifies which list is involved. Then the list count is incremented, the appropriate list updated, the dialog’s list box and edit field are reset and the change flag is set. The refreshPayees and refreshDepositors are the same but use different variables:

dim i as integer

newNameList.deleteAllRows
for I = 1 to npayees
  newNameList.addrow PayList(i)
next
nameEditField.setfocus

This method erases the window’s list box and rebuilds it from PayList. When done the Quit button merely closes the window, since the variable array is already updated.

Where to Save the Files?

The final step is to prompt the user to indicate where the application’s files should be saved. The user will specify where the dataFileFolder will be saved within the Documents Folder. The new defineNewFileFolder method will be called from the existing transWindow saveAll method, to which I will add a few lines:

// If it's a new operation, define data file folder
dim ok As boolean
if newAccount = true then
  ok = defineNewFileFolder
  if not ok then return
End

Notice that by calling the defineNewFileFolder method as a boolean, if the user clicks Cancel, the save operation aborts.

Here is the new transWindow method called defineNewFileFolder. The prompt is made with a special form of the Open dialog called a SelectFolderDialog:

Dim dlg as New SelectFolderDialog
Dim f as FolderItem
dlg.ActionButtonCaption = "Select Folder"
dlg.InitialDirectory = DocumentsFolder
dlg.Title = "Indicate where Checkbook Folder should be located"
f = dlg.ShowModal()

dataFileFolder = f.Child("Checkbook Folder")
dataFileFolder.CreateAsFolder

if dataFileFolder <> Nil then
  //File folder defined - define other folder items
  dataFile = dataFileFolder.child("Checkbook Data")
  payeeFile = dataFileFolder.child("Payee Names")
  depositorFile = dataFileFolder.child("Depositor Names")
  writeFilePointer  // location of dataFileFolder
  newAccount = false
  return true
Else
    return false  //user canceled

End

When this method is run, the dialog will prompt the user to specify where the Checkbook Folder is to be placed (see Figure 3). Note the dialog’s title and the button, “Select Folder.” Once this selection is made, the dataFile, the payeeFile and the depositorFile are placed within the Checkbook Folder in that location. Finally, the data pointer file is written to the Preferences Folder.

Where to save files

Figure 3: Where to save the Checkbook files?

The writeFilePointer method:

dim s As String
dim f As FolderItem
 
s = dataFileFolder.GetSaveInfo(DocumentsFolder) // loc of folder
 
f = preferencesFolder.child("Bank Checking pointer")
outstream = f.createTextFile
outstream.writeLine s
outstream.Close

Opening an Old Archive

Opening files is normally done in applications using the File > Open menu so I must add that menu item here. The menu handler reads as follows:

If promptSave Then return(False)

//create a new openDialog
dim dlg As  new OpenDialog
dlg.filter = "text"
dlg.initialDirectory = DocumentsFolder
dlg.promptText = "Select a Checkbook Data file"
dataFile = dlg.showModalwithin(self)

If dataFile.exists and dataFile.name <> "" Then
Else
  MsgBox "Invalid Checkbook File!"
  return(False)
End

// is a file already open?
if fileIsOpen Then
  Transwindow.TransList.deleteAllRows
  ntransactions = 0
End

//open file
readDataFile
Return True

This handler must consider the situation where you have a checking data file already open–for instance your current file–and want to open an archive file, so a test for saving the current file is required. Then a new open dialog is constructed so the user can select the desired file. If a file is already open, the check register is cleared and then the data file is read.

The Missing File

If the dataFilePointer is in error and points to a missing data file folder, the condition would be detected by these lines in the readDataFile method:

if not dataFile.Exists Then
  resetFilePointer
Else

Then the correct folder must be located by this resetFilePointer method:

Dim dlg as new SelectFolderDialog
Dim f as FolderItem
 
dlg.InitialDirectory = DocumentsFolder
dlg.Title = "The Checkbook data folder cannot be found."
dlg.PromptText = "Please select the Checkbook data folder."
dlg.ActionButtonCaption = "Select"
f = dlg.ShowModal()
If f <> Nil then
  dataFileFolder = f
  writeFileFolder  // update saved file pointer
Else
  Quit  //User Cancelled
End

Finis

This completes the construction of a working check register. The application, along with the source code, can be downloaded from the WAP Web site:

http://www.wap.org/journal/realbasic/

I hope these articles have convinced you how simple it is to write an application using REALbasic. I encourage you to try it yourself; a trial version of RB can be downloaded here:

http://www.realbasic.com/

Good luck, and happy programming.