macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

REALbasic for HyperCard Users
Pages: 1, 2, 3

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 class, section, part, and numword.



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

Diagram.
Schematic diagram of the index data structure.

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 application subclass's 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 list class's 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 list class's accept method:

  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 class ListBox's open event handler populates it just by cycling through the top level of theList:

  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 class ListBox's change event handler populates the section ListBox:

  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

(A 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 section ListBox's 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

The 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

Final thoughts

REALbasic: The Definitive Guide, 2nd EditionREALbasic: The Definitive Guide, 2nd Edition
By Matt Neuburg
2nd Edition September 2001
0-596-00177-0, Order Number: 1770
752 pages, $39.95

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.

Matt Neuburg is the author of O'Reilly's "AppleScript: The Definitive Guide," "REALbasic: The Definitive Guide," and "Frontier: The Definitive Guide," and is a former editor of MacTech magazine.


Return to the Mac DevCenter.