Important: The information in this document is obsolete and should not be used for new development.
Recipes--Lists and Iteration
The recipes and sample code in this section demonstrate how to define a sorted list class with aCompare
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 theTRowList
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:
This recipe uses the
- Define a class based on one of MacApp's sorted list classes.
- Define a
Compare
method to order the items in your list.- Create a list object in your application.
- Add items to the list.
- Optionally add accessor methods.
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 following is the class definition for the TRowList class:
- constructor
- 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.
- destructor
- 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.
- initialization
- By convention, the initialization method is named by replacing the
T
in the class name with anI
(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.- comparison
- MacApp's
TSortedList
class calls theCompare
method when it needs to insert an item into the list in order. You must supply aCompare
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 twoTRow
objects based on their row numbers.
class TRowList : public TSortedList { MA_DECLARE_CLASS; public: // 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
, andkItem1EqualItem2
.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; else return kItem1EqualItem2; }Create a List Object in Your Application
You can create an instance of your list class using thenew
operator. In the Calc application, theTCalcDocument
class keeps a reference to a TRowList object in its fRows field. The following code from theICalcDocument
method creates and initializes a TRowList object and assigns it to the fRows field.
TRowList * aRowList; aRowList = new TRowList; aRowList->IRowList(); fRows = aRowList;Thenew
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 theInsert
method, are described in "Adding an Object to a List," beginning on page 574. For a sorted list, calling theInsert
method inserts an item in sorted order, based on the ordering supplied by theCompare
method.In the Calc application, the
TCalcDocument::AddRow
method adds a row item to thefRows
field by calling theInsert
method:
fRows->Insert(theRow);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:
The sample code shown in this recipe is from MacApp's
- Choose an iterator for the list.
- Initialize the iterator.
- Use the iterator in a
for
loop to access the items in the list.- Optionally add accessor methods.
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 theCSubViewIterator
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.
- Note
- 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 afor
loop to examine the items in the list. The example shown here, from MacApp'sTView
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()) theSubView->Open(); }Thisfor
loop starts with the first subview, continues as long asMore
returnsTRUE
, and on each pass through the loop callsNextSubView
to get the next subview. It callsOpen
for each subview that is returned.Optionally Add Accessor Methods
TheTSortedList
methodsAt
,First
, andLast
all return a pointer to aTObject
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 ofAt
,First
, andLast
. For example, you could include the following lines in the definition of theTRowList
class:
virtual TRow* RowAt(ArrayIndex index); virtual TRow* RowFirst(); virtual TRow* RowLast();Each of these methods calls the similarly named method ofTSortedList
, 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 } else { // 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.