Search the Catalog
Learning Carbon

Learning Carbon

By Apple Computer, Inc.
May 2001 (est.)
0-596-00161-4, Order Number: 1614
352 pages, $34.95

Chapter 6
Carbon Events

Contents:

Carbon Event Handling
MoonTravel Planner: Writing an Event Handler

Everything a typical Carbon application does, whether interacting with the user or communicating with the system, takes place in response to an event sent to the application by the operating system. Events include any activity that requires a response by the application--user actions, changes in processing status, hardware activity, and other occurrences. The core task of any Carbon application is to respond to events.

Carbon supports two event-handling models, although only one is recommended. The first is the legacy Mac OS Event Manager model, referred to as the classic Event Manager. The second is the Carbon Event Manager model. It's the one we'll cover in this chapter, and the event model your Carbon application should use. An application that uses this model tells the Carbon Event Manager what events are of interest. Then the application doesn't do anything until the Carbon Event Manager detects one of the events the application wants to handle. So, when someone using your application is asleep at the keyboard, your application is simply waiting. No processing cycles are wasted; it's a very efficient model.

Another big advantage to using the Carbon Event Manager is that it greatly reduces the amount of code needed to write a basic application. It provides standard handlers for most types of user interaction, so you can concentrate on writing code that's unique to your application.

In this chapter, you'll look at Carbon events and how to handle them. In particular, you'll:

Carbon Event Handling

Let's follow a typical user event--a mouse click-through the system. The user clicks a button in the interface. The action of the user pressing a mouse button sets off a low-level event from the device driver that controls the mouse. The I/O Kit, which forms the foundation of all device drivers on Mac OS X, creates the event, puts it in the window server's event queue, and notifies the window server. (The window server is a core service that manages windows. All user actions occur in a window or in a menu associated with a window.) The window server takes the event off the queue, consults a database of open windows, then sends the event to the process that owns the window in which the event occurred. The Carbon Event Manager gets the event from the window server, packages it in an appropriate form, and passes it to the event-handler mechanism specific to the application. This mechanism ensures that the event is handled by the function associated with the control the user clicked. Figure 6-1 depicts the subsystems that generate, repackage, and forward an event to its destination.

Figure 6.1. A mouse click handled by Mac OS X and the Carbon Event Manager

Carbon uses a callback mechanism to handle events. You define your application's response to various types of events by writing event handler functions and registering them with the Carbon Event Manager. Then, each time an event occurs, the Carbon Event Manager calls back the handler function you installed for that type of event.

Implementing handler-based event management in your application is fairly straightforward. It involves these steps:

  1. Identify the events your application must handle.

  2. Write handlers (functions) to respond to the events.

  3. Install the handlers. This informs the Carbon Event Manager of the events your application handles and which handlers respond to those events.

  4. Call the function RunApplicationEventLoop. The Carbon Event Manager does the rest. When the Carbon Event Manager detects an event that your application handles, the manager calls your handler.

The tricky part is to figure out, in Carbon Event Manager lingo, to what events your application responds. This involves learning some new terms and then looking up some predefined constants in the Carbon Event Manager documentation. Basically, you must:

Once you've defined what you're looking for, your application must be prepared to dig out event information from an event reference it gets from the Carbon Event Manager.

Event Targets

Carbon events happen, at a general level, in a window or in a menu. On a more specific level, events occur in a user pane, a control, a radio button, and so forth. The interface object (window, control, etc.) in which an event occurs is called the event target. When you install an event handler you must specify an event target.

An event target can have any number of events associated with it. For example, an event handler for an application's main window (the event target) could process command events, window events, and mouse events.

Event Types

An event type is a pair of values--an event class and an event kind--that define an event. As you identify the events your application handles, you need to find the pair of constants (event class and event kind) associated with each event.

Most Carbon events fall into one of nine event classes: application, command, control, keyboard, menu, mouse, tablet, text input, and window.

Table 6-1 lists the major event classes and the Carbon Event Manager constants you need to use when you refer to an event class in your application.

Table 6.1. Carbon Event Classes

Event Class Event Class Constant
Application kEventClassApplication
Command kEventClassCommand
Control kEventClassControl
Keyboard kEventClassKeyboard
Menu kEventClassMenu
Mouse kEventClassMouse
Tablet kEventClassTablet
Text Input kEventClassTextInput
Window kEventClassWindow

An event kind indicates a specific event within an event class. Some event classes have only a few event kinds (such as the command class); others (such as the window class) have many event kinds. To define an event type, in addition to the event class, you'll need to look up the constant associated with the event kind. You can find the event kind constants for each of the event classes listed in Appendix B, "Carbon Event Classesand Kinds".

When you specify an event in your application, you use a structure of type EventTypeSpec. Let's say your application is supposed to do something every time the user clicks a button in the interface. The event you want to handle is classified as a command (a command class event). The specific action you want to take is to process the command. You'd specify the event type as follows:

EventTypeSpec myEventSpecification = {kEventClassCommand,
    kEventCommandProcess};

Event References

When the Carbon Event Manager detects an event for your application, it returns an event reference to your application. The event reference is a pointer to an opaque structure that contains general information about the event's class, kind, and time of occurrence. An opaque structure is a data structure you can't "open up" to directly examine or manipulate its internal elements. You can, however, obtain information about the event's attributes by passing the event reference to various Carbon Event Manager accessor functions provided for this purpose. For instance, the functions Get EventClass and GetEventKind each accept an event reference as a parameter and return a 32-bit integer representing the event's class and kind, respectively:

EventRef theEvent;
UInt32    eventClass;
UInt32    eventKind;
eventClass = GetEventClass (theEvent);
eventKind  = GetEventKind (theEvent);

Similarly, the Carbon Event Manager function GetEventTime returns the time an event occurred, expressed as a double-length integer of type EventTime measured in seconds since the system was started up:

EventRef   theEvent;
EventTime  timeInSeconds;

timeInSeconds = GetEventTime (theEvent);

In addition to generic event attributes like class, kind, and time, an event can have additional event parameters whose number and nature vary depending on the event type. Each parameter has an event parameter name and an event parameter type, both of which are denoted by constants defined in the Carbon interface. For instance, a mouse-down event has these four event parameters:

Other event types have different parameters associated with them; see Appendix C, "Parameter Names and Typesfor Common Event Kinds". All such parameters are accessed with the same Carbon Event Manager function, GetEventParameter, which takes as arguments an event reference, the name and type of the requested parameter, the size of the expected value in bytes, and a pointer to a memory buffer of that size in which to return the value. (There is also a pair of arguments for returning the actual parameter type and size of the value returned; you can specify NULL for these arguments if you don't need this information or don't expect the actual type and size to differ from those requested.) Thus, for example, you might obtain the screen coordinates for a mouse-down event as follows:

EventRef  theEvent;
Point     mousePoint;

GetEventParameter (theEvent,
                kEventParamMouseLocation,
                typeQDPoint, NULL,
                sizeof(mousePoint), NULL,
                &mousePoint);

Default Event Handlers

Carbon provides a default event handler for each type of event target (window, menu, control, and application). The default handler defines a standard response to each type of event that a particular target may receive. The one for windows, for instance, implements all the standard behavior for manipulating a window with the mouse--dragging it by its title bar, closing it by clicking the Close button, resizing it by dragging the resize control, and so on. By installing the default handler when you create a window, you automatically inherit all of this standard behavior with no additional effort on your part. You can then proceed to install additional handlers of your own for those aspects of the window's behavior that are specific to your individual application, such as drawing the window contents or responding to the user's mouse actions inside it. Events of those specific types will be reported to your own installed handlers for processing; all others will instead be passed through to the default handler to deal with in the standard way. This frees you from having to provide your own handler for each of the hundred or so events that Carbon may throw at you. With the default event handler to back you up, you can focus your attention on those events whose behavior you need to modify or customize in some way and leave the rest to the default handler.

Sometimes the default event handler's response to a single event can trigger an elaborate cascade of other events. Consider, for example, what happens when the user presses the mouse button in a window's resize control. The mouse press generates an event of type kEventMouseDown, reporting such information as the time and location at which the button was pressed, what modifier keys were being held down at the time, and so forth. Responding to this event involves hit-testing the mouse location to determine that it lies in the window's resize control, tracking the mouse's movements for as long as the button is held down, providing appropriate visual feedback on the screen, and finally resizing the window when the button is released. Theoretically, you could provide a handler function for mouse-down events to do all this yourself, but it's generally more convenient to let the default event handler manage all these chores for you in the standard way. It does this by generating a sequence of further events representing various stages in the process of responding to the original mouse press:

  1. A hit-test event (kEventWindowHitTest) to analyze the mouse location and determine what object on the screen received the mouse press.

  2. A click-resize-region event (kEventWindowClickResizeRgn) indicating that the mouse button was pressed in the resize control of one of your windows.

  3. A get-minimum-size (kEventWindowGetMinimumSize) and a get-maximum-size (kEventWindowGetMaximumSize) event requesting the smallest and largest dimensions to which the user should be allowed to resize the window.

  4. The following cycle of events, repeated for as long as the mouse button is held down:

    • A mouse-dragged event (kEventMouseDragged) reporting the mouse's coordinates.

    • A window bounds-changing event (kEventWindowBoundsChanging) indicating that the window's size is about to change.

    • A get-grow-image-region event (kEventWindowGetGrowImageRegion) requesting the size and shape of the window outline to be drawn for visual feedback on the screen.

  5. A mouse-up event (kEventMouseUp) when the mouse button is released.

  6. A draw-frame event (kEventWindowDrawFrame) to redraw the window's structural elements (frame, title bar, and so forth) in the new size.

  7. A window bounds-changed event (kEventWindowBoundsChanged) indicating that the window's size has changed.

  8. A window update event (kEventWindowUpdate) indicating that the portion of the window's contents visible on the screen has changed and must be redrawn.

  9. A draw-content event (kEventDrawContent) to redraw the window's interior contents.

This proliferation of events may seem daunting, but most of them are really intended to be processed by the default event handler itself, with no active intervention on your part. The only reason for sending all these events is to give you the flexibility to step in at various points in the process and take control yourself if you choose to do so. Maybe you want to reimplement the draw-frame event to change the standard rectangular window frame to an octagonal viewing port for your starship simulation, or intercept mouse-dragged events to play a cool sound effect while the user is dragging the mouse around. Most of the time, you'll just leave these events for the default handler to manage in its own way.

Defining an Event Handler

When you define your own event handler, it must conform to this prototype:

pascal OSStatus HandlerName (EventHandlerCallRef  nextHandler,
                                 EventRef             theEvent
                                 void*                userData);

where HandlerName is the name you assigned to the function.

The parameter theEvent is an event reference describing the event to be handled. Your handler can use this value to obtain information about the event by passing it to one of the Carbon accessor functions, such as GetEventClass, GetEventKind, and GetEventParameter, as discussed above.

The handler returns a status code of type OSStatus as its function result: noErr to show that it has successfully handled the specified event or eventNotHandledErr to indicate that it hasn't. In the latter case, the Carbon Event Manager will relay the event to the next handler after this one in the chain of handlers for the given type of event, continuing up the chain until it finds a handler willing to accept and process the event. You can also explicitly propagate an event up the handler chain yourself by calling the Carbon Event Manager function CallNextEventHandler, passing it the value you received for the nextHandler parameter. This value is an event handler call reference, an opaque structure denoting the next handler after this one in the chain. This technique of event propagation is useful for incorporating the standard response to an event into your own handler while adding any desired pre- or postprocessing of your own.

The event handler's third parameter, userData, is a pointer to an arbitrary data value that your program can use for any purpose of its own. You specify the value of this item when you install your event handler; the Carbon Event Manager will then pass this same value back to the handler function each time it's called.

Installing an Event Handler

The basic Carbon function for installing an event handler is called InstallEventHandler:

OSStatus InstallEventHandler (EventTargetRef        target,
                             EventHandlerUPP        handlerProc,
                             UInt32                 numTypes,
                             const EventTypeSpec*   typeList,
                             void*                  userData,
                             EventHandlerRef*       handlerRef);

The second parameter, handlerProc, is a universal procedure pointer (UPP) to your handler function. The Carbon Event Manager function NewEventHandlerUPP returns a UPP of the required type; for instance:

EventHandlerUPP  handlerUPP;

handlerUPP = NewEventHandlerUPP(MyHandler);

where MyHandler is the name of your handler function.

NOTE:   A universal procedure pointer (UPP) is a generalized procedure pointer that lets code with different calling conventions call each other. Some Carbon functions require you to pass UPPs for callbacks because the calling routine doesn't know in advance if your code is Mach-O based or CFM-based.

The target parameter to InstallEventHandler identifies the event target on which the handler is to be installed. You can obtain a reference to the desired target by calling one of the Carbon functions GetApplicationEventTarget, GetWindowEventTarget, GetMenuEventTarget, or GetControlEventTarget. For convenience, the Carbon Event Manager also defines a set of specialized macros, InstallWindowEventHandler, InstallMenuEventHandler, and InstallControlEventHandler, which accept the targeted object as a parameter, obtain the corresponding target reference for you, and pass it to InstallEventHandler. The remaining parameters to these macros are the same as for the function InstallEventHandler. For example, the macro call:

InstallWindowEventHandler (theWindow, handlerUPP,
                              numTypes, typeList,
                              userData, &handlerRef);

is equivalent to:

theTarget = GetWindowEventTarget(theWindow);
InstallEventHandler (theTarget, handlerUPP,
                        numTypes, typeList,
                        userData, &handlerRef);

A similar macro, InstallApplicationEventHandler, needs no parameter to identify the application itself as the target; the call:

InstallApplicationEventHandler (handlerUPP,
                                 numTypes, typeList,
                                 userData, &handlerRef);

is equivalent to:

theTarget = GetApplicationEventTarget();
InstallEventHandler (theTarget, handlerUPP,
                                 numTypes, typeList,
                                 userData, &handlerRef);

In all of these cases, the typeList parameter specifies the event types for which the handler is to be installed. This parameter is nominally declared as a pointer to an event type specifier giving the class and kind of a single event type; but since the C language considers pointers and arrays to be equivalent, it may actually designate an array of such specifiers for more than one type. The numTypes parameter tells how many event types are being specified. For example, the following code installs a single handler for both key-down and key-repeat events:

EventTypeSpec    eventTypes[2];
EventHandlerUPP  handlerUPP;
eventTypes[0].eventClass = kEventClassKeyboard;
eventTypes[0].eventKind  = kEventRawKeyDown;

eventTypes[1].eventClass = kEventClassKeyboard;
eventTypes[1].eventKind  = kEventRawKeyRepeat;

handlerUPP = NewEventHandlerUPP(KeyboardHandler);

InstallApplicationEventHandler (handlerUPP,
                                2, eventTypes,
                                NULL, NULL);

The userData parameter to InstallEventHandler is a pointer to an arbitrary data value. As we've already seen, any value you supply for this parameter will later be passed back to your handler function each time it's called. You can use this parameter for any purpose that makes sense to your program.

Finally, handlerRef is an output parameter that returns an event handler reference, an opaque structure representing the new event handler. The handler reference is needed as a parameter to such Carbon Event Manager functions as AddEventTypesToHandler and RemoveEventTypesFromHandler, for dynamically changing the event types to which a handler applies, and RemoveEventHandler, for deinstalling it. If you're not going to be using any of these operations, you can simply pass NULL for the handlerRef parameter, indicating that no handler reference should be returned. In particular, the handler will be disposed of automatically when you dispose of the target object it's associated with, so there's no need to call RemoveEventHandler explicitly unless for some reason you want to deinstall the handler while the underlying target object still exists.

MoonTravel Planner: Writing an Event Handler

Now that you know what an event handler is supposed to look like, we're ready to add one to the Moon Travel Planner application. Here's what you'll do:

  1. Identify the events your handler processes.

  2. Write the main window event handler and the compute travel time function called by the handler.

  3. Install the main window event handler.

  4. Call the application event loop function.

  5. Make sure the code works.

Identify Events

As you recall from Chapter 2, "Specifying a Carbon Application: Moon Travel Planner" a user can select a mode of transportation, then click a button to compute the travel time to the moon. When you created the interface, you assigned a command to the Compute Travel Time button. Your application needs to handle this command when it's issued--a command class event kEventClassCommand.

Everything else that happens in the window--moving it, minimizing it, clicking a radio button--can be handled by the default handler for the window. (Recall you selected Standard Handler in Chapter 4, "Interface Builder: Nibs and Windows".)

Now that you've identified the event (a command class event) in which your application is interested, you need to declare an event type specifier to indicate to the Carbon Event Manager the event for which to call the handler.

Open the Moon Travel Planner project. In the main.c file file, copy the following to the main function, after the declaration OSStatus err:

 EventTypeSpec      mainSpec = {kEventClassCommand,
                                    kEventCommandProcess};

Write the Main Window Event Handler

In this section, you'll write an event handler, MTPMainWindowEventHandler, for the main window and a function, MTPComputeCommandHandler, called by the main window event handler. Note that we'll preface the functions and constants for the Moon Travel Planner application with MTP to distinguish them from functions and constants supplied by the Carbon programming interface.

Functions and constants from the Carbon programming interface contain either a prefix or keyword that indicates to which manager or technology they belong. For example, functions that take care of controls in the interface have the word Control somewhere in the function name, such as GetControlID. Functions from Core Foundation are prefaced by CF, such as CFStringCreateWithFormat.

We'll point out the manager or technology associated with each function. If you want complete, technical documentation on a function or related functions, you can consult the Carbon API (application programming interface) documentation by choosing Carbon Help in the Project Builder Help menu.

The main window event handler

You need to declare the event handler and then write the actual handler. The function declaration for the handler must follow the prototype discussed in Section 6.1.5, "Defining an Event Handler". Declare the handler by copying the following to the main.c file, at the top, just after the statement #include <Carbon/Carbon.h>:

// Function declarations for the window event handlers
pascal OSStatus MTPMainWindowEventHandler (EventHandlerCallRef myHandler, 
                                              EventRef event,
                                              void *userData);

You need to declare the constants and global variables used in the MTPComputeCommandHandler. Note that many of these constants represent the commands, IDs, and signatures you assigned to objects when you created the interface. Some represent constants you'll use in a switch statement to determine which formula to use to calculate travel time. Others represent constants you'll use to calculate travel time.

Copy the declarations shown in Example 6-1 to the main.c file, at the top, just after the statement #include <Carbon/Carbon.h>:

Example 6.1. Main Window Event Handler and Compute Command Function Declarations

// Define constants for the commands, IDs, and signature used 
// in the interface. Make sure the values match those you assigned when
// you set up the interface in Interface Builder.
#define kMTPApplicationSignature        'MTPP'
#define kMTPComputeCommand              'tRav'
#define kMTPTravelTimeFieldID               129
#define kMTPModeOfTransportButtonGroupID    130

// Define constants to use in the moon travel time computation.
#define kMTPHoursPerDay 24
#define kMTPDistanceToMoon 384467  // kilometers 

// Define constants to identify the mode of transportation.
#define kMTPFootMode               1
#define kMTPCarMode                2
#define kMTPCommercialJetMode      3
#define kMTPApolloSpacecraftMode   4

// Define a window reference to the main window
WindowRef           gMainWindow;

The main window event handler will handle the command issued by the user when the user clicks the Compute Travel Time button in the window. You'll need to use the function GetEventParameter to get the exact command. Once you know what the command is, you can call other functions in your application to carry out the command.

Next, copy the function in Example 6-2 into the main program (main.c), after the main function.

Example 6.2. The Main Window Event Handler

pascal OSStatus MTPMainWindowEventHandler (EventHandlerCallRef myHandler, 
                         EventRef event, void *userData)
{
    OSStatus        result  = eventNotHandledErr;                            // 1
    HICommand       command;

    GetEventParameter (event, kEventParamDirectObject, typeHICommand, NULL, 
                                    sizeof (HICommand), NULL, &command);    // 2
    switch (command.commandID)                       
              
      {
        case kMTPComputeCommand:                                             // 3
              MTPComputeCommandHandler ((WindowRef) userData);               // 4
              result = noErr;
              break;
      }
    return result;
}

Here's what the function does:

  1. Set the value of result to eventNotHandledErr to assure that if the event passed to the handler is not handled, the Carbon Event Manager gets passed back eventNotHandledErr. If your handler doesn't handle an event, the Carbon Event Manager will handle it if it can.

  2. The Carbon Event Manager function GetEventParameter gets the command ID associated with the command. The command IDs are the four-character codes you assigned to the buttons and commands in the interface and declared as constants in Section 6.2.1, "Identify Events".

  3. The k denotes a constant. The preface kMTP denotes a constant defined in the Moon Travel Planner application. The constant's value 'tRav' (defined in Example 6-1) corresponds to the command ('tRav') you entered in the Info window in Chapter 5, "Interface Builder: Toolsand Controls" for the Compute Travel Time button.

  4. MTPComputeCommandHandler computes travel time and displays the result. You'll write this function next.

The compute command handler

When the main window event handler gets the compute travel time command, it calls a function to compute travel time. In this section, you'll write a function that:

  1. Reads the value of the radio button group.

  2. Chooses a calculation based on the value of the radio button group.

  3. Does the calculation.

  4. Converts the numerical result to a string.

  5. Writes the string to the travel time field. You'll need to declare the command handler. Copy the following to the main.c file, just after the declaration for the main window event handler:

// Function declarations for command handlers
pascal void MTPComputeCommandHandler (WindowRef window);

The function is shown in Example 6-3. Copy it into the main program, after the function MTPMainWindowEventHandler.

Example 6.3. A Handler That Computes Travel Time Based on Mode of Transportation

pascal void MTPComputeCommandHandler (WindowRef window)
{
  ControlHandle     modeOfTransportButtonGroup,                           // 1
                    travelTimeField; 
  ControlID  modeOfTransportControlID = {kMTPApplicationSignature,        // 2
                        kMTPModeOfTransportButtonGroupID};
  ControlID  travelTimeControlID = { kMTPApplicationSignature, 
                        kMTPTravelTimeFieldID };
  CFStringRef       text;
  double            travelTime;
  SInt32            transportModeValue;
  OSErr           status;

  GetControlByID (window, &modeOfTransportControlID,                      // 3
                        &modeOfTransportButtonGroup);
  GetControlByID (window, &travelTimeControlID, &travelTimeField);
  transportModeValue = GetControl32BitValue (modeOfTransportButtonGroup); // 4
  switch (transportModeValue)                                             // 5
   {
     case kMTPFootMode:
        // Foot - good walking time is 4 miles per hour
        travelTime = (kMTPDistanceToMoon/(4.0/0.62))/kMTPHoursPerDay; 
        break;
     case kMTPCarMode:
        // Car - 70 miles per hour on the highway, no speed limit in space!
        travelTime = (kMTPDistanceToMoon/(70/0.62))/kMTPHoursPerDay; 
        break;
     case kMTPCommercialJetMode:
        // Commercial Jet - 600 miles per hour.
        travelTime = (kMTPDistanceToMoon/(600.0/0.62))/kMTPHoursPerDay;
        break;
     case kMTPApolloSpacecraftMode:
        // Apollo 11 took 4 days to get to the moon. 
        travelTime = 4; 
        break;
     default:
         travelTime = 0;
        break;
   }
  text = CFStringCreateWithFormat(NULL, NULL, CFSTR("%2.1f"),travelTime);  // 6
  status = SetControlData( travelTimeField, kControlEntireControl, 
             kControlEditTextCFStringTag,sizeof (CFStringRef), &text);     // 7
  CFRelease (text);                                                        // 8
  DrawOneControl (travelTimeField);                                        // 9
}

Here's what the function does:

  1. A ControlHandle is a reference to a structure that contains information about a control, such as its location in the interface, title, value, whether it's visible, and so forth. You need to pass this reference to a function that accesses the structure.

  2. You need to pass a control's ControlID when you get or set information associated with the control. The ControlID is a structure that contains the application's creator code (signature) and ID associated with a control. In Chapter 5, "Interface Builder: Toolsand Controls" you set signatures and IDs for controls. You use these values (or constants that represent these values) when you declare a ControlID in the MTPComputeCommandHandler function.

  3. You call the Control Manager function GetControlByID twice: once to get the control handle associated with the Mode of Transportation radio button group and a second time to get the control handle associated with the Travel Time field. GetControlByID takes three parameters: a reference to the windowin which the control resides; the ControlID for the control (whose value you set in item 2, above); and a control handle.

  4. The Control Manager function GetControl32BitValue returns a value that indicates which radio button in the radio button group is selected. You must pass the control handle to the radio button group.

  5. Calculations are based on the mode of transportation selected, but do not take into account the effects of gravity and inertia. Miles are converted to kilometers by dividing by 0.62.

  6. The Core Foundation String Services function CFStringCreateWithFormat converts the floating point number to a CFString. The "printf-style" format string %2.1f indicates how the string should be formatted.

  7. The Control Manager function SetControlData sets the Travel Time text field to the value of the string, but does not draw the field. It takes five parameters:

    • A handle to the control whose data you want to set.

    • The part code of the control part for which data is to be set. In this case our control (a text field) has no parts, so you need to pass kControlEntireControl. Control part constants are defined in the Control Manger reference documentation.

    • A constant representing the control-specific data you wish to set. In this case you need to put CFString data into the field, so you use the constant kControlEditTextCFStringTag. Editable text control data tag constants are defined in the Control Manager reference documentation.

    • The size (in bytes) of the data pointed to by the fifth parameter.

    • A pointer to the buffer containing the data that you are sending to the control.

  8. The Core Foundation Base Services function CFRelease releases the memory associated with the CFString.

  9. The Control Manager function DrawOneControl redraws the Travel Time text field with the new string.

Install the Main Window Event Handler

The event target is a window, so you can use one of the macros discussed in Section 6.1.6, "Installing an Event Handler" to install our handler InstallWindowEventHandler.

In the main function, after the line DisposeNibReference(nibRef), add the following code:

InstallWindowEventHandler (gMainWindow,
            NewEventHandlerUPP (MTPMainWindowEventHandler), 
            1, &mainSpec, (void *) gMainWindow, NULL);

InstallWindowEventHandler tells the Carbon Event Manager to call the window's event handler whenever a specified event type happens in the window. These are the parameters to the function InstallWindowEventHandler:

Modify the main function

When you created a skeletal application in Chapter 3, "Project Builder Projects" Project Builder provided several lines of code for you that you now need to modify. Notice in the main function the code to create and show the main window uses a variable called window. The Moon Travel Planner application will eventually have four windows, so you'll rename the generic window to gMainWindow. You declared this global (the g denotes global) variable in Section 6.2.2, "Write the Main Window Event Handler".

Modify these lines of code in the main function so they use gMainWindow:

err = CreateWindowFromNib(nibRef, CFSTR("MainWindow"), &window);
ShowWindow (window );

When you're done, they should look like this:

err = CreateWindowFromNib (nibRef, CFSTR ("MainWindow"), &gMainWindow);
ShowWindow (gMainWindow);

Finally, you can delete this line from the main function because the main window is now held in a global variable (gMainWindow):

WindowRef window;

Call the Application Event Loop Function

The function RunApplicationEventLoop is already in the main function. As you recall from Chapter 3, "Project Builder Projects" it's one of the lines of code provided "for free" by Project Builder. So let's build, run, and test the application to make sure the events are handled properly.

  1. Choose Save from the File menu.

  2. Click the Build button in the upper-left corner of the Moon Travel project window.

  3. Click the Run button in the upper-left corner of the project window.

  4. Select a mode of transportation and click the Compute Travel Time button. Does the value in the Travel Time field change? Is it the value you expect?

  5. Click the Quit button. Does the application quit? If so, you are ready to move on!

Recap

We've discussed one of the most important Carbon technologies in this chapter--the Carbon Event Manager. You followed a typical event through the operating system and saw how it's handled. Then we discussed the key concepts you need to set up event handlers for Carbon events. You had an opportunity to see how Carbon's default handlers work, and then to actually write an event handler for the Moon Travel Planner application.

Now that the Moon Travel Planner application is more than just a pretty face and actually does something--compute travel time--you can move on. There is still a lot to do to create the application described in Chapter 2, "Specifying a Carbon Application: Moon Travel Planner". Next you'll take care of menus.

Back to: Sample Chapter Index

Back to: Learning Carbon


O'Reilly Home | O'Reilly Bookstores | How to Order | O'Reilly Contacts
International | About O'Reilly | Affiliated Companies

© 2001, O'Reilly & Associates, Inc.
webmaster@oreilly.com