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 theDoMouseCommandmethod, 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
TCommandor one of its subclasses.- Implement constructor, destructor, and initialization methods for the command class.
- Override the
DoItmethod to perform the command action. If the command is undoable, override theUndoItandRedoItmethods.- If the command needs to perform an action only if it is completed in the done state, override the
Commitmethod.- If the command allocates storage, free it in the destructor. If the storage must be freed before the destructor is called, override the
Freemethod.- If the command is recordable or can be handled by an attached script, set the
fUseAppleEventfield toTRUE(usually in the routine that creates the command) and override theMakeAppleEventmethod 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:
DoMenuCommandto respond to a menu choiceDoMouseCommandto respond to a mouse eventDoKeyEventorDoCommandKeyEventto respond to a key eventDoScriptCommandto 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 theTIconDocumentclass.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, andRedoItmethods.
- Override the
DoSetupMenusmethod in the view class to enable the Zoom In menu item when appropriate.- Override the
DoMenuCommandmethod 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 theTZoomInCommandto 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'sfObjectToNotifyfield 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
TheDoItmethod 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
TheRedoItmethod 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, theRedoItmethod could just call the DoIt method, as many command classes do in similar circumstances.The UndoIt Method
TheUndoItmethod 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 theTIconEditViewclass:
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 forTIconEditViewcallsInheritedto allow other classes in the view hierarchy to enable command items. It then calls MacApp'sEnableroutine to enable various menu items, including Zoom In. TheEnableroutine calls the Toolbox routineEnableItem.
- Note
- A
DoSetupMenusmethod 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 theBecomeTargetmethod of the window or view object that you want to set as the current target object.The DemoDialogs sample application uses the
TMonthlyDialogview class to display a window containing 12 number-entry views labeled with the months of the year. To demonstrate setting the target object, theDoKeyEventmethod of theTMonthlyDialogclass 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); }TheBecomeTargetmethod belongs to the view'sTEventHandlerancestor. It does nothing if the view is already the current target object. If the view is not already the current target,BecomeTargetcalls theResignTargetmethod of the current target.If the current target object is willing to resign,
ResignTargetreturnsTRUEandBecomeTargetsets the event handler (in this case the view) to be the current target. If the current target is not willing to resign,ResignTargetreturnsFALSEand the view does not become the target.Performing Validation When a Target Change Is Attempted
TheTEventHandler::ResignTargetmethod calls theWillingToResignTargetmethod. MacApp'sTDialogTEView::WillingToResignTargetmethod 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 aTDialogTEViewview to another view, aTEditTextorTNumberTextview within theTDialogTEViewview gets a chance to validate its data. TheTEditText::GetValidationError method checks for too many characters. TheTNumberText::GetValidationErrormethod 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 theTEventhandlerclass) in the target chain.An event-handler object performs idle operations in its
DoIdlemethod. To specify how often theDoIdlemethod should be called, you set thefIdleFreqfield. The smaller the value offIdleFreq, the more frequently theDoIdlemethod is called. A value of 0 indicates that theDoIdlemethod 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
fHeadCohandlerfield). 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::InstallCohandlermethod; you use the same method to remove the cohandler when it has finished its task. You specify how often a cohandler'sDoIdlemethod should be called by setting thefIdleFreqfield, 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
DoIdlemethod.- Install the object in the cohandler chain.
- Set the
fIdleFreqfield to specify how often theDoIdlemethod 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 theDoIdlemethod. You add the following line to the class definition:
virtual Boolean DoIdle (IdlePhase aPhase);TheDoIdlemethod 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. TheDoIdlemethod 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 theDoIdlemethod 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 aTFontListViewobject. When the font list view is active, you can use alphabetic type-ahead to specify a font in the list. TheTFontListViewclass provides this service by adding aTKeySelectionBehaviorbehavior 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
TTextListViewclass.- Add a
TKeySelectionBehaviorbehavior 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 aTKeySelectionBehaviorbehavior object in its initialization method. The TFontListView class does so with the following code, taken from theInitFontListmethod:
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.