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 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.)

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. 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.

  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 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 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 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:

  • The target of the event handler—in this case, the window to which the handler should be registered, which is the main window.

  • A pointer to the window event handler—the function NewEventHandlerUPP returns a pointer to the function you just wrote, MTPMainWindowEventHandler.

  • The number of events for which the handler is registered—in this case, 1, a command.

  • A pointer to the event types handled by the event handler—you defined these as mainSpec.

  • A value that’s passed to the window event handler when the Carbon Event Manager calls the handler—in this case, a pointer to the window to which the handler is registered, that is, the main window.

  • An event handler reference—in this case, NULL.

Modify the main function

When you created a skeletal application in Chapter 3 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.

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 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. Next you’ll take care of menus.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.12.156