Converting to MacApp Release 13:
Examples


Conversion to Release 13 is easy, once you know how to change things. This document walks you through several changes anticipated for MacApp developers who are migrating code, so that you can become more familiar with the process. All of the example code is based on the conversion of real-world application code.

This document illustrates how to migrate the following constructs:

If you would like to examine the example source code all at once, you can review to the Summary sections:


TObject removal

TObject was removed from MacApp in favor of using more lightweight mixin classes MStreamable_AC, MDependable_AC, and RTTI macros, which significantly reduces virtual table sizes within MacApp and in your own code. For your classes that don't use or override any methods now found in MStreamable or MDependable, you no longer need a base class. If you do override or use any methods of MStremable or MDependable, you should subclass from one or both of these classes. RTTI changes are covered in a later section.

Consider the example:

class TMyClass : public TObject
{
public:
	...
	virtual void ReadFrom(TStream* itsStream);
	...
};	

TMyClass must change its base class to MStreamable because it overrides stream methods, but it neither uses nor overrides MDependable methods, so it does not need to add that class as a base class. The class TStream now belongs in ACS, and so uses the name CStream_AC. Note: If the class previously did require the use of streams, dependency, or RTTI, the class could simply be declared without a superclass.

class TMyClass : public MStreamable_AC
{
public:
	...
	virtual void ReadFrom(CStream_AC* itsStream); // override
};


I methods

In most places in MacApp, polymorphic initialization was occuring in places other than I methods or was not required, and this allowed MacApp and ACS to integrate I methods with constructors. This enables the compiler to better optimize constructors, and generally allows for more compact code for creating and initializing objects. In general, MacApp and ACS preserve the same arguments from the I method in the constructor, where this was possible, but in a few cases, obsolete parameters and parameters requiring polymorphic initialization were removed in deference to alternatives.

Consider the example:

class TMyCommand : public TCommand
{
public:
	TMyCommand(MyDataHandle myDataHandle);
	void IMyCommand(TMyClass* itsMyClass);
	...
	MyDataHandle fMyDataHandle;
};
 
TMyCommand::TMyCommand(MyDataHandle myDataHandle)
{
	fMyDataHandle = myDataHandle;
}
 
TMyCommand::IMyCommand(TMyClass* itsMyClass)
{
	TDocument* itsDocument = NULL;
	itsMyClass->DoSomeCommandPreInitialization(itsDocument);
	ICommand(cMyCommand, itsDocument, TRUE, TRUE, itsDocument);
}

Here is some sample code that creates the command:

void TMyClass::DoSomethingThatMayFail()
{
	TMyCommand* cmd = new TMyCommand(myData);
	cmd->IMyCommand(this);
}

Because there are no more I methods, IMyCommand will be combined with the constructor. However, TMyCommand's constructor performs some command pre-initialization before calling ICommand, and we can't insert code before the constructor calls its base constructor. TCommand constructor arguments are exactly the same as ICommand, so the same parameters will be passed in.

class TMyCommand : public TCommand
{
public:
	TMyCommand(MyDataHandle myDataHandle, TDocument* itsDocument);
	...
	MyDataHandle fMyDataHandle;
};
 
TMyCommand::TMyCommand(MyDataHandle myDataHandle, TDocument* itsDocument) :
	TCommand(cMyCommand, itsDocument, true, true, itsDocument),
	fMyDataHandle(myDataHandle)
{
}

The function instantiating the command is now responsible for calling DoSomeCommandPreInitialization before constructing the command. Note: the call to new is replaced with TH_new, a macro that allows debug builds to record file locations when memory leaks are found.

void TMyClass::DoSomethingThatMayFail()
{
	TDocument* itsDocument = NULL;
	this->DoSomeCommandPreInitialization(itsDocument);
	TMyCommand* cmd = TH_new TMyCommand(myData, itsDocument);
}

Although subtle issues may arise from allocating memory within a constructor, it is generally safe to do so with MacApp and ACS classes which utilize CAutoPtr and TemplateTempHandle for data members, have well-formed constructors which use member initialization, and which support native C++ exceptions.

Well-formed TCommands

Here is example illustrating a more typical allocation of a TCommand class using the same TMyCommand subclass described above:

void TMyClass::PostSomeCommand(MyDataHandle myDataHandle, TDocument* itsDocument)
{
	TMyCommand* myCommand;
	myCommand = new TMyCommand(myData);
	myCommand->IMyCommand(itsDocument);
	PostCommand(myCommand);
}

This can be collapsed into a single line that doesn't require a temporary variable or multi-step initialization, as follows:

void TMyClass::PostSomeCommand(MyDataHandle myDataHandle, TDocument* itsDocument)
{
	PostCommand(TH_new TMyCommand(myData, itsDocument));
}


Freeing member data

Isn't freeing member data a chore? Don't you wish you could just say "delete this object when the object of which it is a member gets deleted"? By using auto-pointers such as ACS's CAutoPtr_AC, you can. ACS also has a handy templatized handle class which behaves similar to an auto-pointer.

Consider this example using the command object's member data from the previous section:

class TMyCommand : public TCommand
{
public:
	virtual Free();
	...
	MyDataHandle fMyDataHandle;
};
 
void TMyCommand::Free()
{
	fMyDatahandle = (MyDataHandle) FreeIfObject((Handle) fMyDataHandle);
	Inherited::Free();
}

By using a TemplateTempHandle, the member's destructor will automatically get called, so that the command doesn't have to manage the deletion manually or futz with coercions. The Free method is replaced with a destructor, and it doesn't have to do anything.

class TMyCommand : public TCommand
{
public:
	virtual ~TMyCommand();	
	...
	TemplateTempHandle<MyDataHandle> fMyDataHandle;
};
 
TMyCommand::~TMyCommand()
{
}


Failure/exception handling

MacApp's venerable failure handling has been replaced with native C++ exception handling, which in many cases reduces the amount of code, and also helps make exception handling code more robust. Two examples are given in this section, one where a Try(fi) block can be safely removed by using auto pointers, and another where it is replaced with a native C++ try block.

Bye bye try

Consider this example:

TCommand* TMyClass::DoSomethingThatMayFail()
{	
	MAVolatileInit(MyDataHandle, myData, (MyDataHandle) NewPermHandle(sizeof(MyData));	
	MAVolatile(TMyCommand*, cmd);
 
	FailInfo fi;
	Try(fi)
	{
		cmd = new TMyCommand(myData);
		myData = NULL;
		cmd->IMyCommand(this);
		fi.Success();
	}
	else
	{
		DisposeIfHandle((Handle) myData);
		FreeIfObject(cmd);
		fi.ReSignal();
	}
	return cmd;
}

This example has to manage a change in responsibility for a handle to data as well as cleaning up a command that's being constructed, and it has to worry about telling the compiler what local variables may get referenced when handling failure. Auto-pointers can be used to manage change in responsibility and cleaning up, and native C++ exceptions obviate the need to tell a compiler what is needed when handling an exception. Here is a rewritten version using auto-pointers without requiring a try block. In this case, the auto pointer method release() is used to tell the auto pointer it is no longer responsible for deleting the object when its destructor gets called which happens when it falls out of scope, or during an exception. The command pre-initialization code is a holdover from the earlier section on I method removal. Nice!

TCommand* TMyClass::DoSomethingThatMayFail()
{
	TemplateTempHandle<MyDataHandle> myData = (MyDataHandle) NewPermHandle(sizeof(MyData));	
	CAutoPtr_AC<TMyCommand> cmd;
 
	TDocument* itsDocument = NULL;
	this->DoSomeCommandPreInitialization(itsDocument);
	cmd = TH_new TMyCommand(myData, itsDocument);
	myData.release();
 
	return cmd.release();
}

Try and try again

Sometimes it's still necessary to use a try block. Consider this example:

void TMyClass::DoSomethingElseThatMayFail()
{
	extern TFile* gMyFile; // implementation detail
	
	FailOSErr(gMyFile->OpenFile());
	
	FailInfo fi;
	Try(fi)
	{
		ReadFromFile(gMyFile);
		fi.Success();
	}
	else
	{
		gMyFile->CloseFile();
		if (fi.error != noErr)
			PoseAlert(kMyAlert);
		fi.ReSignal();
	}
	FailOSErr(gMyFile->CloseFile());
}

In this example, the objective is to ensure that a system resource (an open file) is closed properly upon failure, so the try block is retained, but rewritten using C++ exception keywords and exception class member functions. FailInfo and Success are no longer needed, and accessors fi.GetError() and fi.GetMessage() can be used in place of fi.error and fi.message. The exception object is named 'fi' for clarity in this example; MacApp typically uses 'theException'.

To resignal an exception, use the 'throw' keyword. If you want debugging assistance code to be added to your catch block for your debug builds, add a call to the DoCatchMessage function.

void TMyClass::DoSomethingElseThatMayFail()
{
	extern TFile* gMyFile; // implementation detail
	
	ThrowIfOSErr_AC(gMyFile->OpenFile());
	try
	{
		ReadFromFile(gMyFile);
	}
	catch (CException_AC& fi)
	{
		DoCatchMessage_AC(fi);
		gMyFile->CloseFile();
		if (fi.GetError() != noErr)
			PoseAlert(kMyAlert);
		throw;
	}
	ThrowIfOSErr_AC(gMyFile->CloseFile());
}


Stack-based objects

Stack based objects have useful purposes other than just working magic during exceptions, they are also handy for tidying up temporary operations such as locking a handle.

For example, let's consider additional code added to the previous example (and turn the previous example into "etc."):

void TMyClass::DoSomeHandleWork()
{
	extern Handle gMyHandle; // implementation detail
 
	// do some non-handle work
	short savedState = LockHandleHigh(gHandle);
 
	// do stuff with gHandle locked
 
	HSetState(gHandle, savedState);
	// do some more non-handle work
}

Through the use of a temporary handle locking class whose destructor automatically unlocks the handle, we no longer have to remember to call cleanup code, and we can still decide when the handle should be unlocked again simply by carefully placing the braces:

void TMyClass::DoSomeHandleWork()
{
	extern Handle gMyHandle; // implementation detail
 
	// do some non-handle work
	{ // braces keep handle locked for shortest time
		CTempHandleLock_AC tempHandleLock(gHandle);
		// do stuff with gHandle locked
	}
	// do some more non handle work	
}


Iterators

Iterator syntax has become templatized and somewhat more compact, following some commonly adopted conventions. Here is an example using a common iterator class that has already been defined by MacApp.

void TMyClass::DoSomeCommandPreInitialization(TDocument* itsDocument)
{
	...
	CDocumentIterator iter(this);
	for (TDocument* document = iter.FirstDocument(); iter.More(); 
		document = iter.NextDocument())
	{
		if (document->IsEnabled())
			// do something with document
	}
}
 

Because iterators are now templatized, iter.Current() is typed to the class being iterated, and (*iter) or iter-> can be used to reference the currently iterated object.

void TMyClass::DoSomeCommandPreInitialization(TDocument* itsDocument)
{
	...
	for (CDocumentIterator iter(this); iter.Current(); ++iter)
	{
		if (iter->IsEnabled())
			// do something with iter.Current()
	}
}

CObjectIterator example

A TList is often used with a CObjectIterator without subclassing to iterate over its contents. Because the list doesn't know what type of object is being iterated, you may typically use type coercion in code where the object iterator is used. Here is a typical example.

void TMyClass::DoObjectIteration(TList* theList)
{
	CObjectIterator iter(theList);
	for (TBlahClass* blah = (TBlahClass*) iter.FirstObject(); iter.More(); 
		blah = (TBlahClass*) iter.NextObject())
	{
		blah->DoSomething();
	}
}

The CObjectIterator class has been replaced with a TemplateArrayIterator_AC class, which implements modern C++ style iteration and does not require type coercion because it is templatized on the class being iterated.

void TMyClass:DoObjectIteration(CList_AC* theList)
{
	for (TemplateArrayIterator_AC<TBlahClass*> iter(theList); iter; ++iter)
	{
		TBlahClass* blah = *iter; // no cast needed, iter is templated
		blah->DoSomething();
		// or just iter->DoSomething();
	}
}


Dynamic casting

No matter how hard we try, sometimes dynamic casting is more convenient than a more elaborately-designed class hierarchy, and sometimes it is downright necessary. Here is an example of how dynamic casting may have been done in MacApp 3.3, although there are a number of other ways this could be done (for example, the MA_MEMBER macro could be used place of the class desc gunk, and that macro could still be used in Release 13, but it would not be needed in this example since we subsequently want a typesafe cast anyway and we can kill two birds with one stone using a single native C++ dynamic cast).

void TMyClass::DoSomeCommandPreInitialization(TDocument* itsDocument)
{
	...
	if ((document->GetClassDescDynamic())->DescendsFrom(&TMyDocument::fgClassDesc)) 
	// can I safely cast this?
	{
		TMyDocument* myDoc = (TMyDocument*) document;
		...
	}
}

When the dynamic casting is performed using native C++ RTTI, the logic changes subtly from "can I safely cast this?" to "did the cast succeed?". Note that for places where you require that the cast succeed, you can call the dynamic_cast_AC macro, which adds a ThrowIfNULL_AC call after the cast.

void TMyClass::DoSomeCommandPreInitialization(TDocument* itsDocument)
{
	...
	TMyDocument* myDoc = dynamic_cast<TMyDocument*> (iter.Current());
	if (myDoc) // did cast succeed?
	{
		...
	}
}


GridView designators

GridViews now support 32-bit grid coordinates, and by association, 32-bit grid selections. Although GridViews provided abstractions for the day this would happen in GridCoordinate, GridPoint, and GridRect, selections were explicitly region-based. Not any more. TGridViewDesignator, which replaces the use of regions, can now manage much larger selections.

Consider this example:

void TMyGridView::MyUpdateSelection()
{
	// initialize things
	RgnHandle selectRgn = MakeNewRgn();
	RgnHandle tmpRgn = MakeNewRgn();
	
	// for each selected item, add it to the selection region
	for (short index = 1; index <= GetNumOfRows(); ++index)
		if (MyIsRowSelected(index))
		{
			SetRectRgn(tmpRgn, 2, index, 3, index + 1);
			UnionRgn(selectRgn, tmpRgn, selectRgn);
		}
 
	// set the selection to the selection region calcalated above and clean up
	DisposeRgn(tmpRgn);
	Inherited::SetSelection(selectRgn, kDontExtend, kHighlight, kSelect);
	DisposeRgn(selectRgn);
}

Note the mistaken assumption in using a short for row iteration. GridViews now use a long for GridCoordinate. By using GridCoordinate explicitly in your code, you are abstracted away from this implementation detail. Also watch for mistaken use of CRect when you mean to use GridRect. TGridViewDesignator handles memory allocation and deletion itself, so you get to concentrate on what is a selection. The methods map directly to the old region functions you may already be using.

void TMyGridView::MyUpdateSelection()
{
	TGridViewDesignator selectRgn;
	TGridViewDesignator tmpRgn;
	
	// for each selected item, add it to the selection region
	for (GridCoordinate index = 1; index <= GetNumOfRows(); ++index)
		if (MyIsRowSelected(index))
		{
			tmpRgn.Set(GridRect(2, index, 3, index+ 1));
			selectRgn.Append(&tmpRgn);
		}
 
	// set the selection to the selection region calculated above
	Inherited::SetSelection(&selectRgn, kDontExtend, kHighlight, kSelect);
}


Procedural view creation

Procedural view creation has changed a bit. Most view I method parameters have moved to the constructor, with the exception of two crucial parameters. This section explains how to cope with those two parameters. For information on handling the rest of the conversion from an I method to a constructor, see the command example in I Methods.

Consider this example:

void TMyGridView::MyEmbedSubView()
{
	TMyView* view = new TMyView;
	view->IMyView(this, this->fDocument);	
	// I-method embeds and associates with document
}

In the new MacApp, the superview and document parameters did not migrate into the view constructors. This is because both require polymorphic method calls in order to complete embedding and association with a document. Hence, AddSubView must be called directly to embed the view. If any other polymorphic initialization is needed by the view or if you intend to associate the view with a document, HandlePostCreate must be called as well. This design allows you to fully instantiate the view hierarchy by making repeated calls to AddSubView, followed by a single call to HandlePostCreate on the topmost view in the hierarchy, which will perform post-initialization on all the subviews at once through iteration.

void TMyGridView::MyEmbedSubView()
{
	TMyView* view = new TMyView;
	view->AddSubView(this);			// embed
	view->HandlePostCreate(this->fDocument); // associate with document
}


Undo/Redo

This example has not been completed yet.


Summary of MacApp 3.3 code

Interfaces

typedef struct MyData { /* etc. */ } MyData, *MyDataPtr, **MyDataHandle;
 
class TMyClass : public TObject
{
public:
	...
	virtual void ReadFrom(TStream* itsStream); // override
	
	void DoSomeCommandPreInitialization(TDocument* itsDocument);
	void DoObjectIteration(TList* theList);
	TCommand* DoSomethingThatMayFail();
	void PostSomeCommand(MyDataHandle myDataHandle, TDocument* itsDocument);
	void DoSomethingElseThatMayFail();
	void DoSomeHandleWork();
};
 
class TMyCommand : public TCommand
{
public:
	TMyCommand(MyDataHandle myDataHandle);
	virtual ~TMyCommand() {};
	
	void IMyCommand(TMyClass* itsMyClass);
	virtual void Free();
	...
	
	MyDataHandle fMyDataHandle;
};
 
class TMyGridView : public TGridView
{
public:
	...
	Boolean MyIsRowSelected() { /* etc. */ }
	void MyUpdateSelection();
	void MyEmbedSubView();
};


Implementation:

TMyCommand::TMyCommand(MyDataHandle myDataHandle)
{
	fMyDataHandle = myDataHandle;
}
 
void TMyCommand::Free()
{
	fMyDataHandle = (MyDataHandle) FreeIfObject((Handle) fMyDataHandle);
	Inherited::Free();
}
 
TMyCommand::IMyCommand(TMyClass* itsMyClass)
{
	TDocument* itsDocument = NULL;
	itsMyClass->DoSomeCommandPreInitialization(itsDocument);
	ICommand(cMyCommand, itsDocument, TRUE, TRUE, itsDocument);
}
 
void TMyClass::DoSomeCommandPreInitialization(TDocument* itsDocument)
{
	// this is a hypothetical example that does a couple of things
	// in its own peculiar way (e.g., it is highly representative!).
	itsDocument = NULL;
	CDocumentIterator iter(this);
	for (TDocument* document = iter.FirstDocument(); iter.More(); 
		document = iter.NextDocument())
	{
		if (document->IsEnabled())
			if ((document->GetClassDescDynamic())->DescendsFrom(&TMyDocument::fgClassDesc)) 
			// can I safely cast this?
			{
				TMyDocument* myDoc = (TMyDocument*) document;
				if (myDoc->IsMyClassDocument(this))
				// some imaginary test for contains-me-ness
				{
					itsDocument = myDoc;
					break;
				}
			}
	}
}
 
void TMyClass::DoObjectIteration(TList* theList)
{
	CObjectIterator iter(theList);
	for (TBlahClass* blah = (TBlahClass*) iter.FirstObject(); iter.More(); 
		blah = (TBlahClass*) iter.NextObject())
	{
		blah->DoSomething();
	}
}
 
TCommand* TMyClass::DoSomethingThatMayFail()
{	
	MAVolatileInit(MyDataHandle, myData, (MyDataHandle) NewPermHandle(sizeof(MyData)));	
	MAVolatile(TMyCommand*, cmd);
 
	FailInfo fi;
	Try(fi)
	{
		cmd = new TMyCommand(myData);
		myData = NULL;
		cmd->IMyCommand(this);
		fi.Success();
	}
	else
	{
		DisposeIfHandle((Handle) myData);
		FreeIfObject(cmd);
		fi.ReSignal();
	}
 
	return cmd;
}
 
void TMyClass::PostSomeCommand(MyDataHandle myDataHandle, TDocument* itsDocument)
{
	TMyCommand* myCommand;
	myCommand = new TMyCommand(myData);
	myCommand->IMyCommand(itsDocument);
	PostCommand(myCommand);
}
 
void TMyClass::DoSomethingElseThatMayFail()
{
	extern TFile* gMyFile; // implementation detail
	
	FailOSErr(gMyFile->OpenFile());
 
	FailInfo fi;
	Try(fi)
	{
		ReadFromFile(gMyFile);
		fi.Success();
	}
	else
	{
		gMyFile->CloseFile();
		if (fi.error != noErr)
			PoseAlert(kMyAlert);
		fi.ReSignal();
	}
	FailOSErr(gMyFile->CloseFile());
}
 
void TMyClass::DoSomeHandleWork()
{
	extern Handle gMyHandle; // implementation detail
 
	// do some non-handle work
	short savedState = LockHandleHigh(gHandle);
 
	// do stuff with gHandle locked
 
	HSetState(gHandle, savedState);
	// do some more non-handle work
}
 
void TMyGridView::MyUpdateSelection()
{
	// initialize things
	RgnHandle selectRgn = MakeNewRgn();
	RgnHandle tmpRgn = MakeNewRgn();
	
	// for each selected item, add it to the selection region
	for (short index = 1; index <= GetNumOfRows(); ++index)
		if (MyIsRowSelected(index))
		{
			SetRectRgn(tmpRgn, 2, index, 3, index + 1);
			UnionRgn(selectRgn, tmpRgn, selectRgn);
		}
 
	// set the selection to the selection region calcalated above and clean up
	DisposeRgn(tmpRgn);
 
	Inherited::SetSelection(selectRgn, kDontExtend, kHighlight, kSelect);
	DisposeRgn(selectRgn);
}
 
void TMyGridView::MyEmbedSubView()
{
	TMyView* view = new TMyView;
	view->IMyView(this, this->fDocument);	
	// I-method embeds and associates with document
}


Summary of Release 13 converted code

Summary notes:


Interface changes:

class TMyClass : public MStreamable_AC {
public:
	...
	virtual void ReadFrom(CStream_AC* itsStream); // override
	...
	void DoSomeCommandPreInitialization(TDocument* itsDocument);
	void DoObjectIteration(CList_AC* theList);
	TCommand* DoSomethingThatMayFail();
	void PostSomeCommand(MyDataHandle myDataHandle, TDocument* itsDocument);
	void DoSomethingElseThatMayFail();
	void DoSomeHandleWork();
};
 
class TMyCommand : public TCommand {
public:
	TMyCommand(MyDataHandle myDataHandle, TDocument* itsDocument);
	virtual ~TMyCommand();
	...
	TemplateTempHandle<MyDataHandle> fMyDataHandle;
};
 
class TMyGridView : public TGridView {
public:
	...
	bool MyIsRowSelected() const { /* etc. */ }
	void MyUpdateSelection();
};


Implementation changes:

TMyCommand::TMyCommand(MyDataHandle myDataHandle, TDocument* itsDocument) :
	TCommand(cMyCommand, itsDocument, true, true, itsDocument),
	fMyDataHandle(myDataHandle)
{
}
 
TMyCommand::~TMyCommand()
{
}
 
void TMyClass::DoSomeCommandPreInitialization(TDocument* itsDocument)
{
	itsDocument = NULL;
	for (CDocumentIterator iter(this); iter.Current(); ++iter)
	{
		if (iter->IsEnabled())
		{
			TMyDocument* myDoc = dynamic_cast<TMyDocument*> (iter.Current());
			if (myDoc) // did cast succeed?
				if (myDoc->IsMyClassDocument(this))
				// some imaginary test for contains-me-ness
				{
					itsDocument = myDoc;
					break;
				}
		}
	}
}
 
void TMyClass:DoObjectIteration(CList_AC* theList)
{
	for (TemplateArrayIterator_AC<TBlahClass*> iter(theList); iter; ++iter)
	{
		TBlahClass* blah = *iter; // no cast needed, iter is templated
		blah->DoSomething();
		// or just iter->DoSomething();
	}
}
 
TCommand* TMyClass::DoSomethingThatMayFail()
{
	TemplateTempHandle<MyDataHandle> myData = (MyDataHandle) NewPermHandle(sizeof(MyData));	
	CAutoPtr_AC<TMyCommand> cmd;
 
	TDocument* itsDocument = NULL;
	this->DoSomeCommandPreInitialization(itsDocument);
	cmd = TH_new TMyCommand(myData, itsDocument);
	myData.release();
 
	return cmd.release();
}
 
void TMyClass::PostSomeCommand(MyDataHandle myDataHandle, TDocument* itsDocument)
{
	PostCommand(TH_new TMyCommand(myData, itsDocument));
}
 
void TMyClass::DoSomethingElseThatMayFail()
{
	extern TFile* gMyFile; // implementation detail
	extern Handle gHandle; // ditto
 
	ThrowIfOSErr_AC(gMyFile->OpenFile());
	{ // scoping braces keeps handle locked as little as possible
		
		try
		{
			ReadFromFile(gMyFile);
			// & do something with gHandle locked
		}
		catch (CException_AC& fi)
		{
			DoCatchMessage_AC(fi);
			gMyFile->CloseFile();
			if (fi.GetError() != noErr)
				PoseAlert(kMyAlert);
			throw;
		}
	}
	ThrowIfOSErr_AC(gMyFile->CloseFile());
}
 
void TMyClass::DoSomeHandleWork()
{
	extern Handle gMyHandle; // implementation detail
 
	// do some non-handle work
	{
		CTempHandleLock_AC tempHandleLock(gHandle);
		// do stuff with gHandle locked
	}
	// do some more non handle work	
}
 
void TMyGridView::MyUpdateSelection()
{
	TGridViewDesignator selectRgn;
	TGridViewDesignator tmpRgn;
	
	// for each selected item, add it to the selection region
	for (GridCoordinate index = 1; index <= GetNumOfRows(); ++index)
		if (MyIsRowSelected(index))
		{
			tmpRgn.Set(GridRect(2, index, 3, index+ 1));
			selectRgn.Append(&tmpRgn);
		}
 
	// set the selection to the selection region calculated above
	Inherited::SetSelection(&selectRgn, kDontExtend, kHighlight, kSelect);
}
 
void TMyGridView::MyEmbedSubView()
{
	TMyView* view = new TMyView;
	view->AddSubView(this);			// embed
	view->HandlePostCreate(this->fDocument); // associate with document
}


Back to the Index



©Copyright 1997 by Apple Computer, Inc. -- Last Updated 7/31/97