Important: The information in this document is obsolete and should not be used for new development.
Recipes--Events and Commands
The recipes and sample code in this section describe how to create command objects to respond to events, how to implement an undoable command, how to change the current target object, how to ensure idle time for an event-handler object, and how to use a behavior object to modify the operation of an event-handler object.Creating a Command Object to Perform an Action--A General Overview
When the application object receives an event that specifies an action, it dispatches the event by calling a method of an event-handler object. The application dispatches mouse-down events by calling theDoMouseCommand
method, menu commands by callingDoMenuCommand
, Apple events by callingDoScriptCommand
, and so on. In any of those methods, the event-handler object can create and post a command object to perform the required action. Command objects are capable of doing, undoing, and redoing the action, as well as sending an Apple event to record the action or to allow an attached script to handle the operation.For information on handling menu commands, see "Working With Menus--A General Outline," beginning on page 313.
To create a command object to perform an action, you follow these general steps:
- Define a command class to perform the action:
- Define a subclass of
TCommand
or one of its subclasses.- Implement constructor, destructor, and initialization methods for the command class.
- Override the
DoIt
method to perform the command action. If the command is undoable, override theUndoIt
andRedoIt
methods.- If the command needs to perform an action only if it is completed in the done state, override the
Commit
method.- If the command allocates storage, free it in the destructor. If the storage must be freed before the destructor is called, override the
Free
method.- If the command is recordable or can be handled by an attached script, set the
fUseAppleEvent
field toTRUE
(usually in the routine that creates the command) and override theMakeAppleEvent
method to create an Apple event that describes the command action.
- Decide which of your event-handling classes will create the command object.
- If a view class will create the command object and the view is created as part of a view hierarchy defined in a view resource, you may need to specify which view in the hierarchy is the target view.
- To create and initialize the command object and post it to the MacApp command queue, you override the appropriate method in your event-handling class:
DoMenuCommand
to respond to a menu choiceDoMouseCommand
to respond to a mouse eventDoKeyEvent
orDoCommandKeyEvent
to respond to a key eventDoScriptCommand
to respond to an Apple event
Recipe--Implementing an Undoable Zoom In Menu Command
This recipe is based on the IconEdit application, which displays an icon and allows the user to zoom in to any desired magnification. In IconEdit, the Zoom In menu command appears on the Icon menu and is handled by theTIconDocument
class.To add a Zoom In menu command to a menu and respond when a user chooses that menu command, you perform these steps:
- Define a command-number constant for the Zoom In menu item.
- Add the Zoom In menu item to a
'CMNU'
resource in your resource file.- Implement a command class that handles zoom in.
- Override the
DoIt
,UndoIt
, andRedoIt
methods.
- Override the
DoSetupMenus
method in the view class to enable the Zoom In menu item when appropriate.- Override the
DoMenuCommand
method in the document class to create and post a command object to perform the zoom operation.
The sample code shown in this recipe is from the IconEdit application. For information on making the Zoom In command recordable, see "Recipe--Defining a Recordable Command Class," beginning on page 353.
- Note
- The steps for adding a Zoom Out menu command are nearly identical.
Define a Command-Number Constant for the Zoom In Menu Item
The IconEdit application defines a constant for the Zoom In item in both its resource file and its implementation file. The resource file,IconEdit.r
, contains the following line:
#define cZoomIn1000The implementation file,UIconEdit.cp
, contains the following line:
const CommandNumber cZoomIn = 1000; // Zoom In menu command.The constants defined in the resource file and implementation file must match or your application will not be able to respond to the menu choice correctly.Add the Zoom In Menu Item to a 'CMNU' Resource
The'CMNU'
resource for the Icon menu is located in the fileIconEdit.r
. It contains the following line:
/* [1] */"Zoom In", noIcon, "M", noMark, plain, cZoomIn;This line specifies that the Zoom In item is the first item in the menu, it has no icon associated with it, it has the Command-key equivalent of Command-M, it is not marked with a checkmark, it is displayed in plain text, and it has the command number cZoomIn.Implement a Command Class That Handles Zoom In
The IconEdit application defines theTZoomInCommand
to implement zooming in. The class is defined as follows:
class TZoomInCommand : public TCommand { MA_DECLARE_CLASS; // Generate RTTI information. public: TIconDocument * fIconDocument;// The document that is zoomed. TZoomInCommand(); // Constructor. virtual ~TZoomInCommand();// Destructor. virtual void IZoomInCommand(TIconDocument* itsIconDocument); virtual void DoIt(); // Override. virtual void RedoIt(); // Override. virtual void UndoIt(); // Override. virtual TAppleEvent* MakeAppleEvent();// Override. };The constructor, TZoomInCommand, sets the fIconDocument field toNULL
. The destructor, ~TZoomInCommand, does nothing. The MakeAppleEvent method creates an Apple event that specifies the Zoom In command. MakeAppleEvent is not shown in this recipe but is described in "Recipe--Defining a Recordable Command Class," beginning on page 353. All the other methods of the TZoomInCommand class are shown below.The IZoomInCommand Method
The initialization method for the Zoom In command class sets a reference to the icon document and calls the initialization method of its parent class,TCommand
:
void TZoomInCommand::IZoomInCommand(TIconDocument* itsIconDocument) { // Save a reference to the icon document. fIconDocument = itsIconDocument; // Initialize the inherited command. this->ICommand(cZoomIn, itsIconDocument, kCanUndo, kCausesChange, itsIconDocument); // No view or scroller to track. }The kCanUndo constant specifies that the command is undoable, while the kCausesChange constant specifies that the command changes its object to notify. A command'sfObjectToNotify
field may point to an object, such as a document, to notify when the command operation is completed. For example, a document might update its change count to show it has been modified.The DoIt Method
TheDoIt
method for the TZoomInCommand command gets the icon view from the icon document and increases the magnification value by 2.
void TZoomInCommand::DoIt() { TIconView * theIconView = fIconDocument->GetIconView(); if (theIconView != NULL) { short currentMagnification = theIconView->GetMagnification(); theIconView->SetMagnification(currentMagnification + 2); } }The RedoIt Method
TheRedoIt
method for the TZoomInCommand command is identical to the DoIt method--it gets the icon view from the icon document and increases the magnification value by 2. In fact, theRedoIt
method could just call the DoIt method, as many command classes do in similar circumstances.The UndoIt Method
TheUndoIt
method for the TZoomInCommand command is similar to the DoIt method--it gets the icon view from the icon document and decreases the magnification value by 2:
void TZoomInCommand::UndoIt() { TIconView * theIconView = fIconDocument->GetIconView(); if (theIconView != NULL) { short currentMagnification = theIconView->GetMagnification(); theIconView->SetMagnification(currentMagnification - 2); } }Override DoSetupMenus in the View Class
The following is the DoSetupMenus method for theTIconEditView
class:
void TIconEditView::DoSetupMenus() { // Set up inherited menus. Inherited::DoSetupMenus(); // Can always zoom in. Enable(cZoomIn, TRUE); // Can zoom out if not at smallest size. Enable(cZoomOut, fMagnification > 1); // Can always cut, copy, and clear, or set the color. Enable(cSetColor, TRUE); Enable(cCut, TRUE); Enable(cCopy, TRUE); Enable(cClear, TRUE); // Can paste if 'ICON' data is available. gClipboardMgr->CanPaste(kIconClipType); } // TIconEditView::DoSetupMenusThe IconEdit application uses the icon view class to enable Zoom In because whenever the icon view is present, the user can zoom in on the icon. The DoSetupMenus method forTIconEditView
callsInherited
to allow other classes in the view hierarchy to enable command items. It then calls MacApp'sEnable
routine to enable various menu items, including Zoom In. TheEnable
routine calls the Toolbox routineEnableItem
.
- Note
- A
DoSetupMenus
method doesn't normally disable any menu items, because MacApp disables all menu items before callingDoSetupMenus
.Override DoMenuCommand in the Document Class
The IconEdit application handles the Zoom In menu command in the document class because the document owns the view where zooming takes place. When the DoMenuCommand method is called with the command numbercZoomIn
, it creates, initializes, and posts a TZoomInCommand object:
void TIconDocument::DoMenuCommand (CommandNumber aCommandNumber) { switch (aCommandNumber) { // Handling for some command numbers not shown. case cZoomIn: TZoomInCommand * theZoomInCommand = new TZoomInCommand(); theZoomInCommand->IZoomInCommand(this); theZoomInCommand->fUseAppleEvent = TRUE; this->PostCommand(theZoomInCommand); break; default: Inherited::DoMenuCommand(aCommandNumber); break; } }The DoMenuCommand method sets the fUseAppleEvent field toTRUE
, indicating that when MacApp executes the Zoom In command it should send an Apple event describing the operation. This makes the operation recordable and gives any attached script a chance to handle the command. For more information, see "Recipe--Defining a Recordable Command Class," beginning on page 353.Recipe--Changing the Current Target Object and
MacApp's target chain mechanism is described in "Target Chain Dispatching," beginning on page 107. The current target object is the object that gets the first chance to handle user actions such as mouse clicks and menu command choices. A typical target chain consists of the application object, an open document, the document's window, and one or more nested subviews in the window, with a view designated as the current target object.
Performing ValidationMacApp automatically changes the target object for standard operations such as switching between application windows. This recipe describes how you can set the target object directly in your code and how you can validate view data when a target change is attempted.
Changing the Current Target Object
To change the current target object, you call theBecomeTarget
method of the window or view object that you want to set as the current target object.The DemoDialogs sample application uses the
TMonthlyDialog
view class to display a window containing 12 number-entry views labeled with the months of the year. To demonstrate setting the target object, theDoKeyEvent
method of theTMonthlyDialog
class sets the target to the view's window whenever the Option-Tab combination is pressed. Although DemoDialogs does this merely for show, your application can use similar code to set the target view.
void TMonthlyDialog::DoKeyEvent(TToolboxEvent* event) // Override. { TWindow * theWindow = NULL; Boolean dummy; // If Option-Tab is pressed, make the view's window the current // target. This is for test purposes only. if ((event->fCharacter == chTab) && (event->IsOptionKeyPressed())) { // Get the view's window and make it the current target. // The return value indicates whether the window actually // became the target. Since we're just testing, ignore the // return value. theWindow = this->GetWindow(); if (theWindow != NULL) dummy = theWindow->BecomeTarget(); } else Inherited::DoKeyEvent(event); }TheBecomeTarget
method belongs to the view'sTEventHandler
ancestor. It does nothing if the view is already the current target object. If the view is not already the current target,BecomeTarget
calls theResignTarget
method of the current target.If the current target object is willing to resign,
ResignTarget
returnsTRUE
andBecomeTarget
sets the event handler (in this case the view) to be the current target. If the current target is not willing to resign,ResignTarget
returnsFALSE
and the view does not become the target.Performing Validation When a Target Change Is Attempted
TheTEventHandler::ResignTarget
method calls theWillingToResignTarget
method. MacApp'sTDialogTEView::WillingToResignTarget
method gives a text-editing view an opportunity to validate its data and refuse to resign as target if it contains invalid data:
long TDialogTEView::WillingToResignTarget() // Override. { long result = 0; // Give the current edit text view a chance to validate its data. if (fEditText) { // Some code not shown. result = fEditText->GetValidationError(); } return result; }This code shows that when the target view changes from aTDialogTEView
view to another view, aTEditText
orTNumberText
view within theTDialogTEView
view gets a chance to validate its data. TheTEditText::
GetValidationError method checks for too many characters. TheTNumberText::GetValidationError
method checks for a valid numeric value. Your subclass of either of these views can provide additional checking.Recipe--Ensuring Idle Time for an Event-Handler Object
When a MacApp application isn't busy, it distributes idle time as described in "MacApp's Idling Mechanism," beginning on page 134. MacApp automatically gives idle time to the event-handler objects (objects based on classes that descend from theTEventhandler
class) in the target chain.An event-handler object performs idle operations in its
DoIdle
method. To specify how often theDoIdle
method should be called, you set thefIdleFreq
field. The smaller the value offIdleFreq
, the more frequently theDoIdle
method is called. A value of 0 indicates that theDoIdle
method should be called as often as possible, while the default value of LONG_MAX (231 - 1, or 2,147,483,647) essentially disables idling.MacApp also distributes idle time to event-handler objects in the cohandler chain (a list of event-handler objects pointed to by the application object's
fHeadCohandler
field). To ensure that an event-handler object gets idle time, even if it isn't always (or ever) part of the target chain, you install the object in the cohandler chain. An event-handler object used in the cohandler chain is referred to as a cohandler.To install a cohandler, you call the
TApplication::InstallCohandler
method; you use the same method to remove the cohandler when it has finished its task. You specify how often a cohandler'sDoIdle
method should be called by setting thefIdleFreq
field, as described above.To guarantee that an event-handler object will get idle time, you perform these steps:
The sample code in this recipe is for a hypothetical application.
- Override the
DoIdle
method.- Install the object in the cohandler chain.
- Set the
fIdleFreq
field to specify how often theDoIdle
method should be called.- When the object has completed its task, remove it from the cohandler chain.
Override the DoIdle Method
Your cohandler class that performs a background or polling task during idle time must override theDoIdle
method. You add the following line to the class definition:
virtual Boolean DoIdle (IdlePhase aPhase);TheDoIdle
method is a Boolean routine. If it doesn't complete its task, it returnsFALSE
. If it does complete its task, it removes itself from the cohandler chain and returnsTRUE
. TheDoIdle
method for your cohandler class should look something like the following:
Boolean TYourCohandler::DoIdle ( IdlePhase aPhase ) { Boolean taskComplete = FALSE; // Your cohandler code--the code that you want executed // periodically--goes here. // If the task is completed, set taskComplete to TRUE. // If the cohandler has completed its task, remove it. if (taskComplete) gApplication->InstallCohandler(this, FALSE); return taskComplete; }Install the Object in the Cohandler Chain
A cohandler object can install itself in the cohandler chain with a line like the following:
gApplication->InstallCohandler (this, TRUE);Set the fIdleFreq Field to Specify How Often DoIdle Should Be Called
A cohandler object that needs to be called as often as possible sets its fIdleFreq field with code like the following:
this->SetIdleFreq(0);If your cohandler's DoIdle method should be called less often, pass a larger value to SetIdleFreq.Remove the Object From the Cohandler Chain
You call the same application method to remove a cohandler that you call to install one, InstallCohandler. The following code, from theDoIdle
method shown above, demonstrates how to remove a cohandler from the cohandler chain:
// If the cohandler has completed its task, remove it. if (taskComplete) gApplication->InstallCohandler(this, FALSE);Recipe--Using a Behavior Object to Implement Alphabetic Type-Ahead
The Dialogs menu of the DemoDialogs application has a Format Dialog menu choice that displays a format dialog box. The dialog box includes a font list implemented with aTFontListView
object. When the font list view is active, you can use alphabetic type-ahead to specify a font in the list. TheTFontListView
class provides this service by adding aTKeySelectionBehavior
behavior object.To use a behavior object to add alphabetic type-ahead to a view, you perform these steps:
The sample code shown in this recipe is from the DemoDialogs application.
- Define a view class that descends from MacApp's
TTextListView
class.- Add a
TKeySelectionBehavior
behavior object to the view object.
For another recipe that uses behavior objects, see "Recipe--Using Dependencies and Behaviors to Synchronize Control Views," beginning on page 588.
Define a View Class That Descends From TTextListView
The class definition for the TFontListView class in the DemoDialogs application includes the following code:
class TFontListView : public TTextListView { MA_DECLARE_CLASS; public: FontListPtr fFontList;// Font resource IDs. . . . // Build an array of font resource IDs whose names are // in alphabetical order. virtual void InitFontList(); . . . }The TFontListView class descends from MacApp's TTextListView class, a class designed to display text items in a one-dimensional list. TFontListView defines the InitFontList method to initialize the font list and put the items in the list into alphabetical order.Add a TKeySelectionBehavior Behavior to the View Object
Your view class adds aTKeySelectionBehavior
behavior object in its initialization method. The TFontListView class does so with the following code, taken from theInitFontList
method:
TKeySelectionBehavior *behavior; behavior = new TKeySelectionBehavior; behavior->IKeySelectionBehavior('KYSL'); this->AddBehavior(behavior);The value passed to the IKeySelectionBehavior method,'KYSL'
, is an arbitrary identifier for the behavior object. Once the behavior is added to the view by the call toAddBehavior
, it works together with the view to supply alphabetic type-ahead.