Finally, we turn to the Index window. In HyperCard, the interface here is made up of four fields with "Lock Text", "Don't Wrap", and "Auto Select" turned on. In REALbasic, they're ListBox controls named
Recall that when the user clicks on a ListBox, we want the other ListBoxes populated appropriately. For example, if the user chooses from the
class ListBox, the
section ListBox should be filled with the different section headings for that class. We could implement this by searching the database every time the user clicks, but this would be too slow. We want the ListBoxes repopulated instantaneously. Because the structure of the database won't be changing, we can implement this by loading the heading information at startup into a data structure that can be queried immediately.
This is a situation where REALbasic is far stronger and more elegant than HyperCard. In HyperCard, structured data storage, aside from fields and cards, is primitive. Basically, we have to use strings (or fields, which amount to the same thing), and we can subdivide these only by the built-in notions of character, item, and line.
My solution in HyperCard was thus to use a large number of hidden fields whose names were the key to obtaining their contents. REALbasic on the other hand has true arrays, and lets us create custom classes that function both as data structures and as repositories of code.
Therefore we'll use an array where each element consists of text plus a pointer to a further array, creating a hierarchy identical to the hierarchical behavior of our ListBoxes. Here's a diagram of the resulting structure, which obviously has four levels: After starting with a pointer to embody the structure as a whole, we have the
class level (which is shown in its entirety), the
section level and
part level (of which a few entries are shown), and the
numword level (not shown).
So we create a
list class with two properties:
text, a string; and
list, an array of lists dimensioned initially to "-1" (meaning an empty array). We give the
application subclass a list property,
theList, which will hold our populated data structure. To build the data structure at startup, we run through the entire database, gathering the headings and handing them to our data structure. Here's the code from the
open event handler:
dim theList as list dim c as databasecursor dim anarray(-1) as string c = d.sqlselect("select * from data") theList = new list while not c.eof redim anarray(-1) anarray.append c.field("class").stringvalue anarray.append c.field("section").stringvalue anarray.append c.field("part").stringvalue anarray.append c.field("numword").stringvalue theList.accept anarray c.movenext wend self.theList = theList
The question is then, what the
accept method does. It turns out that we can formulate this very elegantly, because the rule is the same no matter what level we're at. If the text for this level is the same as the text of the last existing entry, there is nothing to do at this level. We just want to hand the rest of the information down to the next levels.
Otherwise, we need to create a new element at this level, and then hand the rest of the information down to the next levels. It should now be clear why we use an array as the
accept method's parameter: The first element in this array is the text intended for our level, so this is what we examine to decide what to do. When we're done, we strip it off and use the remaining array as the parameter for the call to
accept at the next level down. Here is the
dim u as integer u = ubound(self.list) if u = -1 or anarray(0) <> self.list(u).text then self.list.append new list u = u + 1 self.list(u).text = anarray(0) end anarray.remove 0 if ubound(anarray) > -1 then self.list(u).accept anarray end
theList is now in place, so it's easy to populate the ListBoxes. When the Index window opens for the first time, the
open event handler populates it just by cycling through the top level of
dim i,u as integer u = ubound(app.theList.list) for i = 0 to u me.addrow app.theList.list(i).text next me.listindex = 0
The last line
selects the first line of the
class ListBox -- just as
if the user had selected it -- and this triggers a chain reaction.
When a selection is made in a ListBox, the ListBox gets a
change event. The
change event handler populates the
dim i,k,u as integer dim aList as list k = me.listindex if k = -1 then return end aList = app.theList.list(k) u = ubound(aList.list) section.deleteAllRows for i = 0 to u section.addrow aList.list(i).text next section.listindex = 0
listindex value of "-1" means no selection; we start by checking for this because we don't want to do anything if the ListBox is being emptied in preparation for being repopulated.) Continuing the chain reaction, the
change event handler populates the
part ListBox -- the code is absolutely identical to that of the
class ListBox, except that the list being cycled through is one level down:
dim i,k,u as integer dim aList as list k = me.listindex if k = -1 then return end aList = app.theList.list(class.listindex).list(k) u = ubound(aList.list) part.deleteAllRows for i = 0 to u part.addrow aList.list(i).text next part.listindex = 0
part ListBox method's
change event handler is parallel; and so we come at last to the
numword ListBox method's
change event handler. Here the user's selection specifies an actual entry, and we want to respond by showing that entry in the Entry window. The key lines are very simple, because the Entry window is set up to allow us to do this:
entry.c = app.d.sqlselect( "select * from data where numword='" + me.text + "'") entry.dodisplay
REALbasic: The Definitive Guide, 2nd Edition
This completes the discussion. Not all the code of our application has been shown, but most of it has -- enough to show how the application works. And more important, how it works differently from the HyperCard model but achieves the same effect. (You can download the entire code, or the built application, from my web site.)
Basically, you can do just about anything with REALbasic that you were doing with HyperCard, but you won't necessarily be doing it the same way. Some things that were easy before will now take more work. On the other hand, some things that were hard or impossible before will now be easy.
If you're now thinking about migrating, you can download a free 30-day demo version of REALbasic from REAL's site. Along with my book, that should be enough time for you to become comfortable with the paradigm shifts involved, and to decide whether you'll be using REALbasic as the new HyperCard.
Return to the Mac DevCenter.