Previous Book Contents Book Index Next

Inside Macintosh: Programmer's Guide to MacApp / Part 2 - Working With MacApp
Chapter 25 - Working With Lists and Iteration

Recipes--Lists and Iteration

The recipes and sample code in this section demonstrate how to define a sorted list class with a Compare method, create a list based on the class, add items to a list, and iterate through the items in a list.

Recipe--Defining and Working With a Sorted List Class

The Calc sample application implements a spreadsheet view with rows and columns of cells. It uses the TRowList class for operations such as copying a selection, writing row data to disk, and writing row data to the desk scrap.

To define and work with a sorted list class, you perform these steps:

  1. Define a class based on one of MacApp's sorted list classes.
  2. Define a Compare method to order the items in your list.
  3. Create a list object in your application.
  4. Add items to the list.
  5. Optionally add accessor methods.

This recipe uses the TRowList class from the Calc application as an example of working with a sorted list class.

Define a Class Based on One of MacApp's Sorted List Classes

When you define a subclass of a sorted list class, you normally provide at least the following methods:

The constructor method has the same name as the class. It sets any fields you have added to the class to default or safe values. It should not perform any operation that might fail, such as allocating storage.
The TRowList class adds no fields, so it has no constructor method.
The destructor method has the same name as the class, prepended with a ~ (tilde) character. It frees any storage allocated by the object and does any other cleanup that should be performed when the object is deleted.
The TRowList class requires no special cleanup, so its destructor method is empty.
By convention, the initialization method is named by replacing the T in the class name with an I (for example, IRowList). The initialization method performs any initialization for the class that can't be safely performed in the constructor method, such as allocating storage.
The IRowList method just calls ISortedList, the initialization method of its parent class.
MacApp's TSortedList class calls the Compare method when it needs to insert an item into the list in order. You must supply a Compare method that can order the items stored in your sorted list class.
The Compare method of the TRowList class is shown in the next section. It compares two TRow objects based on their row numbers.
The following is the class definition for the TRowList class:

class TRowList : public TSortedList


   // Destructor.
   virtual ~TRowList();
   // Initialize the list procedurally.
   virtual void IRowList();

   // Compare two rows based on row number.
   virtual CompareResult Compare(TObject* item1,
                           TObject* item2); // Override.
   // Return the row that matches the passed row number.
   virtual TRow* GetRow(RowNumber r);

Define a Compare Method to Order the Items in Your List

The Compare method specifies an ordering between items in your sorted list. Passed two list items, it determines whether item 1 is greater than item 2, less than item 2, or equal to item 2. MacApp supplies constant definitions for these conditions: kItem1GreaterThanItem2, kItem1LessThanItem2, and kItem1EqualItem2.

The Compare method for TRowList compares TRow items based on row number:

CompareResult TRowList::Compare(TObject* item1,
                        TObject* item2) // Override.
   // The calling code makes sure never to insert anything but a
   // row into the list, so typecasting should always be safe.
   if (((TRow *)(item1))->fNumber > ((TRow *)(item2))->fNumber)
      return kItem1GreaterThanItem2;
   else if (((TRow *)(item1))->fNumber < ((TRow *)(item2))->fNumber)
      return kItem1LessThanItem2;
      return kItem1EqualItem2;

Create a List Object in Your Application

You can create an instance of your list class using the new operator. In the Calc application, the TCalcDocument class keeps a reference to a TRowList object in its fRows field. The following code from the ICalcDocument method creates and initializes a TRowList object and assigns it to the fRows field.

TRowList * aRowList;

aRowList = new TRowList;
fRows = aRowList;
The new operator causes a failure if the object can't be created, so this code doesn't have to check the returned TRowList object.

Add Items to the List

MacApp's list classes provide a number of methods for adding items to a list. Some of these methods, including the Insert method, are described in "Adding an Object to a List," beginning on page 574. For a sorted list, calling the Insert method inserts an item in sorted order, based on the ordering supplied by the Compare method.

In the Calc application, the TCalcDocument::AddRow method adds a row item to the fRows field by calling the Insert method:

As a result, rows in the list are stored in row order--not a surprising choice.

Recipe--Iterating Over the Items in a List

The general mechanism for iteration is described in "Iteration," beginning on page 576. To iterate through a list of items, you perform these steps:

  1. Choose an iterator for the list.
  2. Initialize the iterator.
  3. Use the iterator in a for loop to access the items in the list.
  4. Optionally add accessor methods.

The sample code shown in this recipe is from MacApp's TView class.

Choose an Iterator for the List

MacApp provides many specialized iterator classes. Included among them are iterators for processing lists of documents, views, windows, and generic objects. This recipe uses the CSubViewIterator class. If your list requires processing that is not provided by one of MacApp's iterator classes, you can define your own iterator subclass.

Initialize the Iterator

Iterators are commonly created as stack-based variables that are used within a method or routine. To initialize such a variable, you merely declare the variable and pass the required arguments to the constructor. Remember that many iterator classes have multiple constructor methods to choose from. For example, the CDocumentIterator class declares the following three constructors:

CDocumentIterator(TApplication* itsApplication,
               ArrayIndex itsLowBound, ArrayIndex itsHighBound,
               Boolean itsForward);
CDocumentIterator(TApplication* itsApplication, Boolean itsForward);
CDocumentIterator(TApplication* itsApplication);
As a result, the following three declarations are all valid initializations for a CDocumentIterator object. The first two declarations reference the global application object and could be placed in any routine. The third declaration must appear in an application method. The declarations use constants provided by MacApp to specify the direction of iteration (kIterateForward and kIterateBackward).

CDocumentIterator iter(gApplication, 1, 100, kIterateForward);

CDocumentIterator iter(gApplication, kIterateBackward);
CDocumentIterator iter(this); // From within an application method.
You normally initialize a document iterator by passing a reference to the application object as the single parameter. The two constructors with longer parameter lists are rarely used.

Use the Iterator in a For Loop to Access the Items in the List

To iterate through the items in a list, you initialize an iterator for the list and set up a for loop to examine the items in the list. The example shown here, from MacApp's TView class, iterates through all of the subviews of a view, opening each subview:

void TView::Open()
   CSubViewIterator iter(this);
   for (TView * theSubView = iter.FirstSubView();
         iter.More(); theSubView = iter.NextSubView())
This for loop starts with the first subview, continues as long as More returns TRUE, and on each pass through the loop calls NextSubView to get the next subview. It calls Open for each subview that is returned.

Optionally Add Accessor Methods

The TSortedList methods At, First, and Last all return a pointer to a TObject instance. As a result, you may frequently use code like the following:

theRow = (TRow *) theRowList->At(rowNumber);
To avoid using type coercion in every piece of code that calls one of these methods, and to apply additional testing, you can add accessor methods to take the place of At, First, and Last. For example, you could include the following lines in the definition of the TRowList class:

virtual TRow* RowAt(ArrayIndex index);
virtual TRow* RowFirst();
virtual TRow* RowLast();
Each of these methods calls the similarly named method of TSortedList, uses MA_DYNAMIC_CAST to make sure the returned item is indeed a TRow object, and provides error handling if it isn't. For example, the RowAt method could be implemented as follows:

TRow* RowAt(ArrayIndex index)
   TObject* theObject = this->At(index);
   TRow* theRow = MA_DYNAMIC_CAST(TRow, theObject);
   if (theRow != NULL)
      return theRow
      // Code to handle error condition.
You can now provide safe, built-in type coercion, using a line like the following:

theRow = theRowList->RowAt(rowNumber);
All type coercion is centralized in the three new methods rather than spread throughout your code. If, during the development process, the type ever changes, you can simply change three methods--there is no need to perform a global search and replace operation on your entire code base.

Previous Book Contents Book Index Next

© Apple Computer, Inc.
25 JUL 1996