Release 3.3 Copyright ©1995 by NeXT Computer, Inc.  All Rights Reserved.





7 Program Dynamics





Changes made for the current release of NeXTstep affect the information presented in this chapter.  For details see:

/NextLibrary/Documentation/NextDev/ReleaseNotes/AppKit.rtf
/NextLibrary/Documentation/NextDev/ReleaseNotes/AllocInitAndNew.rtf





The previous chapter, "Program Structure," described how a program based on the Application Kit is constructed from a small number of interconnected objects.  This chapter shows how applications use that structure to carry out essential interactive activities:

Responding to events
Drawing on the screen (and on the printed page)
Sending and receiving remote messages

Because events motivate almost all program activities, the chapter begins with a discussion of event handling in the Application Kit.




Event Handling

NeXT applications are driven by the user's actions on the keyboard and mouse--that is, by events.  The application receives an event from the Window Server, responds to it, then looks for the next event.  If there is no next event, the application waits until the user does something and an event is received.

The Application object, NXApp, initiates this event loop--the application's main event loop--when it receives a run message:

[NXApp run];

On each cycle of the loop, NXApp gets an event, analyzes it, and sends an appropriate message to initiate the application's response.  It passes keyboard events to the key window and mouse events to the Window associated with the event in the event record.  The Window, in turn, dispatches the event to one of its Views.  Most of your program's activity will be in reaction to the messages NXApp and the application's Windows send out after receiving an event.

Some of their event messages will reach objects that can respond directly.
Some event messages will reach Control objects--Buttons, Sliders, Scrollers, TextFields, and the like--and will be translated into more specific action messages for other objects.

NXApp continues to get events out of the event queue and dispatch them until the event loop is broken.  Typically, it's broken only when the application terminates.  If the response to an event includes a terminate: message,

[NXApp terminate:self];

NXApp closes all the application's windows, frees its objects, and exits the program.

Note:  The terminate: method takes an argument only so that it can respond to an action message, usually an action message coming from the Quit command.  It doesn't actually looks at its argument, so it doesn't matter what value is passed.  See "Action Messages," later in this chapter, for more on the structure of these messages.

Sometimes an application's response to an event is to set up a modal event loop that will get all subsequent events for a short period of time.  For example, the response to a mouse-down event may be to set up a modal loop that will collect events until the user releases the mouse button and a mouse-up event is received.  Modal loops are set up within the main event loop in response to an event or action message.

Most of the unique behavior of your application will be encoded in class definitions for objects that can respond to event and action messages.

This section of the chapter looks at event handling in a typical application, beginning where the application itself begins, with the code that sets up its event-handling objects.




Setting Up Event-Handling Objects

An Objective-C program begins just as a C program does, by calling its main() function.  In a program based on the Application Kit, main() is usually very short.  Its job is to set up the Application object and other core objects your program needs at startup, then to turn over control of the program to them.  It can be as short as just three or four lines of code:

main()
{
[Application new];
setUp();
[NXApp run];
[NXApp free];
}

In this version of main(), the new method creates an Application object, NXApp, which receives a run message to begin getting events from the Window Server.  When the run method quits, free cleans up after the application.

Most of the application's time is spent in the run method, getting and responding to events.  But before a run message can be sent, the application must prepare itself for the events it's about to receive.  It must:

Create the Windows, Views, and other objects it needs to handle events at startup.  It's possible to create new objects at any time while the program is running, but an initial set of core objects must be in place before the first event is processed.
Initialize the objects so they're connected into a program framework.
Present the application to the user by placing the initial display on-screen and designating a Window to serve as the initial key window.  If the application allows users to edit text or graphics within the window, it should also designate a View to show the initial selection, and then set the selection.

In the version of main() shown above, all this code has been segregated into the setUp() function.  For some applications, it might be appropriate to define a subclass of the Application class and include setup code in a redefined version of the new method:

@implementation MyApplication : Application
{
}
+ new

{
self = [super new];
/* setup code goes here */
}

The main() function could then consist of just a single line of nested messages:

main()
{
[[[MyApplication new] run] free];
}

When you design your application using Interface Builder, you can do most of the work of setting up your application by selecting objects from palettes and editing them on-screen.  Interface Builder lets you graphically lay out the Windows, Views, and other interface objects your application needs, initialize them, display them, and archive them in a file for later use.  The file can then be inserted into the __NIB segment of the application executable:

cc ...  -segcreate __NIB myProject.nib myProject.nib

Here the -segcreate linker option copies the myProject.nib file into a section of the __NIB segment and assigns the section the same name as the file.

A message to NXApp to open this section takes the place of the setUp() function:

main()
{
[Application new];
[NXApp loadNibSection:"myProject.nib" owner:NXApp];
[NXApp run];
[NXApp free];
}

In the example above, the objects that were archived in myProject.nib are loaded into memory and connected to the object that "owns" them, NXApp.

Archived objects can have owners other than NXApp:

main()
{
id  theHub;

[Application new];
theHub = [MyCoordinator new];
[NXApp setDelegate:theHub];
[NXApp loadNibSection:"newProject.nib" owner:theHub];
[NXApp run];
[NXApp free];
}

For this example, the programmer defined a class, MyCoordinator, to contain the basic algorithms of the application--its inner workings as opposed to its interface.  After creating an Application object, main() creates an instance of the MyCoordinator class, makes it the delegate of the Application object, and connects it with the user-interface objects archived in newProject.nib.  Whenever a Window object is loaded from the archive, it's automatically added to NXApp's list of windows; NXApp doesn't have to be named as the Window's owner.

With Interface Builder, you can create any number of archive files (or __NIB sections), each with a different set of objects and, if desired, a different owner.  For example, every panel the application uses could be archived separately. The archive file (or section) would contain the Panel object and all the Control objects it displays; its owner would be an object you'd design to receive action messages from the panel and coordinate its activities.  The owner could be made the Panel's delegate; if needed, NXApp could be provided with an instance variable to keep track of the Panel's owner.

In this way, your application can build its own network of objects, all relying on the basic network of core objects described under "Program Framework" in the previous chapter.

Interface Builder is described in the next chapter.  This chapter concentrates on the program structure defined in the Application Kit, and so returns to a simple version of a setUp() function that can be bracketed (as can loadNibSection:owner:) by new and run messages.



setUp() Example

Interface Builder is the preferred way to program an application.  But to show the steps required by the Application Kit for setting up an application, the code for a simple program is listed below.

There are three windows in this example program--a main menu, an information panel, and a small window where the user can enter and edit text.  All three are illustrated in Figure 7-1.



Figure 7-1.  Little



This program has the basic elements of a real application, but is too simplified to be very useful, hence its name, "Little."  But despite its simplicity, it behaves like a full-fledged application.  It can be hidden, its principal window can be miniaturized, the panel and menu disappear when it's deactivated and reappear again when it's activated, the keyboard alternatives work, the text the user types can be selected and edited, and so on.

So that you can compile this little program and try it out for yourself, the source code and a makefile for it are on-line in /NextLibrary/Documentation/NextDev/Examples/Little.

The entire application is written in two functions, main() and setUp(); it includes no class definitions of its own.  The main() function has just four lines of code, as illustrated above and repeated below; the setUp() function is just 58 lines, so the whole program can be printed in a little over a page.

#import <appkit/appkit.h>

void setUp(void)
{
id      myWindow, myPanel, myMenu, windowText;
NXRect  aRect;

/*** Step 1:  Set up a Window ***/
NXSetRect(&aRect, 100.0, 350.0, 300.0, 300.0);
myWindow = [Window newContent:&aRect
style:NX_TITLEDSTYLE
backing:NX_BUFFERED
buttonMask:NX_MINIATURIZEBUTTONMASK
defer:NO];
[myWindow setTitle:"A Little Demonstration"];

NXSetRect(&aRect, 0.0, 0.0, 300.0, 300.0);
windowText = [Text newFrame:&aRect
text:""
alignment:NX_LEFTALIGNED];
[windowText setOpaque:YES];
[[myWindow contentView] addSubview:windowText];

/*** Step 2:  Set up a Panel ***/
NXSetRect(&aRect, 100.0, 700.0, 300.0, 40.0);
myPanel = [Panel newContent:&aRect
style:NX_TITLEDSTYLE
backing:NX_BUFFERED
buttonMask:NX_CLOSEBUTTONMASK
defer:YES];
[myPanel setTitle:"About Little"];
[myPanel removeFromEventMask:(NX_KEYDOWNMASK | NX_KEYUPMASK)];

/*** Step 3:  Set up a Menu ***/
myMenu = [Menu newTitle:"Little"];
[[myMenu addItem:"Info..."
action:@selector(orderFront:)
keyEquivalent:'\0']
setTarget:myPanel];
[myMenu addItem:"Hide"
action:@selector(hide:)
keyEquivalent:'h'];
[myMenu addItem:"Quit"
action:@selector(terminate:)
keyEquivalent:'\0'];
[myMenu sizeToFit];
[NXApp setMainMenu:myMenu];

/*** Step 4:  Display all windows that aren't deferred ***/
[myWindow display];

/*** Step 5:  Move myWindow on-screen ***/
[myWindow orderFront:nil];

/*** Step 6:  Make it the key window ***/
[myWindow makeKeyWindow];

/*** Step 7:  Show a selection in the key window ***/
[windowText selectAll:nil];
}

main()
{
[Application new];
setUp();
[NXApp run];
[NXApp free];
}

All the methods used in this example are defined in the Application Kit.  Some were discussed in the previous chapter under "Managing Windows"; others are described in Chapter 9, "User-Interface Objects."  The NXSetRect() function, which assigns values to an NXRect structure, is discussed in NeXTstep Reference, Volume 2.

As its first act (step 1), the setUp() function creates a new instance of the Window class and titles it.  It then creates a Text object the same size as the Window's content area and makes it a subview of the Window's content view.  (It could equally as well have made the Text object the content view and freed the default content view provided by the Window object.)

Next (step 2), setUp() creates a Panel to serve as Little's information panel.  The Panel is assigned a title, and keyboard events are removed from its event mask so that it can't become the key window.  This panel will behave just like any other information panel, except for the glaring fact that it doesn't contain any information.  Additional code would be required to draw the application's icon in the panel and provide text giving version and copyright information. (Little is too little to have any such information to impart.)  Usually, information panels are designed in Interface Builder.

The setUp() function then (step 3) creates a menu with the three minimal commands every application should have:

The Info command sends an orderFront: message to its target, the information panel, placing the panel on-screen at the front of its tier.
The Hide command sends a hide: message that NXApp will respond to by hiding all the application's windows. The command is assigned Command-h as its keyboard alternative.
The Quit command sends a terminate: message that NXApp will respond to by shutting down the application. Although it's not required by the user-interface guidelines, the command is assigned the common keyboard alternative Command-q.

The size of the Menu is altered so that it exactly fits the three commands, and it's made the main menu of the application.

At this point, all the objects in the application have been created and initialized.  It remains only to present the application to the user by displaying the contents of the one window that hasn't been deferred (step 4), moving the principal Window of the application on-screen (step 5), making it the key window on launch (step 6), and setting the Text object to display an initial selection (step 7).  Steps 5 and 6 could have been combined by using a single method, makeKeyAndOrderFront:.

The display message that myWindow receives in step 4 calls upon every View within its view hierarchy to draw itself.  The images are displayed into the window's backup buffer, but nothing appears on-screen.  A window must be reordered into the screen list for its display to be visible.  That's the function of step 5.

It's unnecessary to supply code in step 4 to display myMenu and myPanel along with myWindow.  Menus are created as deferred windows and so are displayed automatically just before they're placed on-screen.  The Panel was also created as a deferred window in step 2, and will be displayed just before the user brings it to the screen with the Info command.

Similarly, no code is needed to put the Menu and Panel on-screen.  When the application is activated, the Application Kit places the main menu at the location specified by the NXMenuX and NXMenuY parameters.  By default, it's in the upper left corner of the screen.  The Info command will put the information panel on-screen and the panel's close button will remove it again.



Opening Files

If the Little program had the ability to open and display files, it would be organized a bit differently.  Instead of creating a main window (myWindow) at the outset, it would have waited for an instruction indicating which file to open.  Only then would it create a Window for the file and, in actions paralleling steps 4, 5, 6, and 7 of the example program, display the file in the Window, move the Window on-screen, make it the key window, and designate an initial selection.

The instruction to open a file can come from three different sources:

If the user launches an application by double-clicking an icon for one of its files, the Workspace Manager passes the name of the file to the application.  The Application object receives an openFile:ok: message just before getting its first event.
If the user double-clicks a file in a directory window after the application is launched, the Workspace Manager sends the application a message with the name of the file.  Application's openFile:ok: method is again entrusted with the message.
If the user selects a file from the application's Open panel, the application must get the name (and directory) of the file from the OpenPanel object and open the file.  The Open panel is brought to the screen by the Open menu command, in much the same way that an information panel is.

In all three cases the application must supply the code that creates the Window, displays the file, places the Window on-screen, makes it the key window, and sets the initial selection within the window.  But instead of doing this as part of a setUp() function, it's done in response to the user's selection of a file.

If the selection of a file generates an openFile:ok: message to the Application object, you should put this code in an appOpenFile:type: method defined in either an Application subclass or as part of the Application object's delegate. openFile:ok: does some preprocessing of the message, and sends an appOpenFile:type: message for the delegate or NXApp to actually open the file.



Final Initialization

Just before the run method gets its first event, it does some final initialization to make sure that the application is ready:

It activates the application, which will cause it to receive an application-activated subevent (of the kit-defined event).
It provides the application with default Listener and Speaker objects, if a Listener and Speaker weren't created in the application's setup code.  These objects give the application the ability to communicate with the Workspace Manager and other applications.
It checks in the application with the network name server so that it will have a public port where it can receive messages from the Workspace Manager and other applications.  The application is checked in under the name returned by the Application object's appListenerPortName method, which is usually the name assigned in the header to the __ICON segment of the application executable, or possibly the name passed in the first string of the NXArgv array.  You can override appListenerPortName so that it returns a different name.  If it returns NULL,, the application is assigned a private port so that it can communicate with the Workspace Manager, but it won't have a public port.
It sends an openFile:ok: message to NXApp, if the user launched the application by double-clicking a file icon.

The run method also gives the application one last chance to do any final initialization of its own.  It sends the Application object's delegate an appDidInit: message, if the delegate has a method that can respond.

The delegate's appDidInit: method can make any adjustments necessary for the events that are about to arrive.  For example, if an application normally opens and displays a file, but the user launched it by double-clicking an application icon rather than a file icon, appDidInit: could open an empty window for the user to work in.

The argument passed in an appDidInit: message is the Application object's id.




Event Masks

Each window's event mask, maintained by the Window Server, determines which events the Server can send to the application process for that window.  See Chapter 5, "Events," for information on the structure of event masks and the methods used to associate events with particular windows.

Each Window object keeps track of its own event mask through its winEventMask instance variable.  The eventMask method returns the current mask:

int  myMask;
myMask = [myWindow eventMask];

A new menu or list has these event types in its default event mask:

mouse-down (both left and right)
mouse-up (both left and right)
mouse-dragged (both left and right)
kit-defined

The default mask for panels and standard windows includes this full range of events:

key-down
key-up
mouse-down (both left and right)
mouse-up (both left and right)
mouse-entered
mouse-exited
kit-defined
system-defined
application-defined

Miniwindows and icons, whether in or out of the dock, have a similar event mask, but exclude key-down and key-up events.

To ensure that they work properly, you should refrain from changing the event masks of menus, lists, icons, and miniwindows.  However, the event masks of standard windows and panels can be altered to suit the needs of your application.

If you want a window to receive an event type that's not included in the default mask, or to avoid receiving an event type that is included, you must change the Window object's event mask:

int  oldMask;
oldMask = [myWindow setEventMask:myNewMask];

If myNewMask is different from the mask maintained by the Window Server for myWindow, setEventMask: changes both the Window Server's mask and myWindow's winEventMask instance variable.  The following code resets a default mask to include flags-changed events:

oldMask = [myWindow setEventMask:([myWindow eventMask] |
NX_FLAGSCHANGED)];

This operation could be accomplished more simply with the addToEventMask: method:

oldMask = [myWindow addToEventMask:NX_FLAGSCHANGED];

There's also a method for removing event types from the current mask:

oldMask = [myWindow removeFromEventMask:NX_APPDEFINEDMASK)];

Each of these methods returns the former event mask so that you can cache it and restore it later if needed.



Asking for Particular Events

An application should limit the events it receives from the Window Server to just those it's interested in.  This saves processing time, reduces the amount of code you must write, and limits potential errors from handling unwanted events.

But, for most types of events, it's best to set the window's event mask at the outset so that it will receive all the events you ever want for it.  It's generally not a good idea to change the mask in response to events, since the user might act between the time you send the message and the time the Window Server gets around to resetting the mask.  The delay could cause your application to miss events it expected to receive.

The principal exception to this rule arises when you want to receive mouse-dragged or mouse-moved events:

Since these events are sent continuously, as long as the mouse is in motion, it doesn't matter that the Window Server won't begin sending them until it resets the event mask.  Missing the first in a series of mouse-dragged or mouse-moved events is usually of little consequence.
Since dispatching a continuous stream of events demands a lot of processing time, you should keep the mask for these two events set only for a limited period.  Have your application set the Window's event mask to include mouse-dragged or mouse-moved events just before it's ready to respond to them and have it reset the mask when it's finished.  Your application shouldn't ask for these events and then attend to other things.

A menu includes mouse-dragged events in its event mask because it must always be ready to respond to them.  When using a menu, users typically drag through the list of menu commands.



Receiving Keyboard Events

The Application Kit guarantees that keyboard events are sent to the active application, regardless of whether its on-screen windows have event masks that accept keyboard events.  This enables the application to respond to keyboard alternatives when it's active, even if it has no windows for the user to type in.

Although the event mask doesn't determine which application gets keyboard events, it does help determine which Window within the application will receive them:

The Application Kit associates keyboard events with the current key window.
For a Window to be the key window, it must have an event mask that accepts key-down events.  The initial event mask for all Windows (except menus, miniwindows, and icons) includes key-down events, so, by default, they all are potential key windows.

Since the NeXT user interface requires every standard window to be the key window whenever it's the main window the user is working in, standard windows should keep key-down events in their event masks.  If the window doesn't display typing, it will beep whenever it receives a key-down event.

However, if you have a panel that won't respond to keyboard events and shouldn't be marked as the key window, you must reset its mask to exclude key-down events.  Any Window that excludes key-down events should also exclude key-up events, so this example removes both event types from the event mask:

[myPanel removeFromEventMask:(NX_KEYDOWNMASK | NX_KEYUPMASK)];

A Panel should remove keyboard events from its event mask if it meets all three of these tests:

It doesn't display typing.
It's not an attention panel.
It doesn't have a button that the user can operate from the keyboard by pressing Return.  Normally, such a button is only permitted in an attention panel or in a panel that displays a text field.


Receiving Timer and Cursor-Update Events

A Window's event mask never needs to include either timer events or cursor-update events.

Timer events are synthetic events, generated by the application when it needs them.  Because they aren't sent across the connection from the Window Server, the event mask doesn't determine whether they can be received.  See "Using Timer Events" under "Modal Event Loops" later in this chapter for information on how to generate these events.

Cursor-update events signal that it's time to change the cursor image.  Applications don't respond to these events directly; the change is made by the Application Kit.

A Window receives cursor-update events if a cursor has been associated with a particular area (a cursor rectangle) located within the window and registered with the Window object.  The Kit makes sure the application gets these events when they're needed; the event doesn't have to be included in the Window's event mask.  However, because cursor-update events are based on mouse-entered and mouse-exited events, it's best to keep those two events in the event mask if you want to receive cursor-update events.

See "Changing the Cursor" later in this chapter for more information on setting cursor rectangles.




Selecting an Application, Window, and View

After the application's core objects have been set up, the event masks of its windows have been adjusted, and a run message sent, the Application object begins getting events from the Window Server.  Most events are dispatched in messages to other objects, as described in the next section, "Event Messages."

Left mouse-down events also serve to select the application, the window, and even the View that will be the focus of future events.  The selected object's event-handling status is designated by terms that are mostly familiar from the user interface:

active application The application that's been selected to receive keyboard events, and to have visible menus and panels.
key window The Window that's been selected to handle keyboard events for the application, and to be the primary recipient of action messages from menus and panels.
main window The Window that's the principal focus of user actions.  It's usually identical to the key window.
first responder The View that's been selected to have the first chance at responding to keyboard events and action messages sent to a Window.

The active application, key window, and main window are concepts important to the user interface and are defined in Chapter 2, "The NeXT User Interface."  The first responder is no less important, but, like all Views, is a part of the implementation of the user interface, rather than part of its definition.



The First Responder

When the user clicks in a Text object, such as the one in the Little example program listed earlier under "Setting Up Event-Handling Objects," it's selected to receive subsequent events, especially keyboard events.  The click also selects the insertion point where future typing will appear.

That simple demonstration program had just one View--a Text object--displayed within its Window.  But imagine a Window, such as the one illustrated in Figure 7-2, with four Text objects sharing its content area.



Figure 7-2.  Four Text Objects in a Window



By clicking in one quadrant or another, the user determines where typing will appear (which Text object will receive keyboard events).  Only one of the four objects will show a selection or insertion point at a time.

The object that's selected to be the focus of future events for a Window is the first responder.  Each Window has its own first responder, which it returns when asked:

id  handler;
handler = [myWindow firstResponder];

The first responder is typically a View object in the Window's view hierarchy, but it can be any Responder.  At the outset, each Window is its own first responder.  Because Windows generally can't respond to keyboard and mouse events, this is usually the same as having a nil first responder.

The first responder is a central actor in the handling of event and action messages.  It receives:

Keyboard event messages (keyDown:, keyUp:, and flagsChanged:).
Action messages from Controls that don't have explicit targets of their own.  This includes messages from menu commands that affect the current selection, such as the Cut, Copy, Paste, Bold, and Italic commands.  See "Action Messages," later in this chapter.
Messages that notify the first responder when the Window becomes the key window and when it stops being the key window (becomeKeyWindow and resignKeyWindow).  See "The Key Window and Main Window" below.
Mouse-moved event messages (mouseMoved:).

If the first responder can't respond to any of these messages, its next responder is given a chance to respond.  See "Event Messages in the Responder Chain" and "Action Messages" later in this chapter for details.



Changing the First Responder

As shown by the example illustrated in Figure 7-2 above, the Application Kit lets the user pick the first responder; the Window object alters its firstResponder instance variable on the basis of the left mouse-down events it receives (see "Left Mouse Events," under "Event Messages" below).

Before making the View selected by a mouse-down event the first responder, the Window sends it an acceptsFirstResponder message to ask whether it accepts this role.  By default, all Views--in fact, all Responders--answer NO, leaving the current first responder in place.  An object can agree to be made the first responder simply by implementing an acceptsFirstResponder method that answers YES:

- (BOOL)acceptsFirstResponder
{
return YES;
}

Some objects may return YES under certain circumstances and NO under others.  If a Text object displays editable or selectable text, it answers YES.  If the text is neither editable nor selectable, it answers NO.

If the selected View returns YES, the Window attempts to make it the first responder through its makeFirstResponder: method:

[self makeFirstResponder:selectedView];

As used by the Application Kit, the acceptsFirstResponder and makeFirstResponder: methods permit users to alter the first responder in a carefully regulated manner.  You can also set the first responder from within your application using the same methods.  This is most appropriate when registering an object as the Window's initial first responder on launch.

To function as the initial first responder of a Window, a Text object needs not only to be made the first responder, it must also be assigned a selection.  The Text class defines three methods that do both; they register a new selection and send the Window a makeFirstResponder: message.  The selectAll: method used in the Little example earlier in this chapter selects all the object's text; the selectText: method does the same.  The setSel:: method defines a range of text to select.  In the example below, it selects the characters at positions 52 through 190 (up to position 191):

[windowText setSel:52 :191];

If the range of text selected is 0--if both arguments to setSel:: are the same, or there's no text for selectAll: and selectText: to select--the selection is an insertion point.  The selectAll: message in the Little example will make the receiving Text object show a blinking caret for the insertion point when the program is launched and the Window becomes the key window; it won't take an initial click to select the Text object as the first responder.



Notification

The makeFirstResponder: method first sends the current first responder a resignFirstResponder message to notify it that a change is about to be made; the new first responder is then notified with a becomeFirstResponder message. The default implementation for both these methods is simply to return self.  An object can override the default to keep track of whether it's the current first responder, or to prevent the change from being made:

If an object returns nil to a resignFirstResponder message, it refuses to be deactivated and remains the first responder.  No becomeFirstResponder message is sent and makeFirstResponder: returns NULL.
If an object returns nil to a becomeFirstResponder message, it refuses to be the new first responder.  Since the current first responder has already resigned, the Window is made the first responder instead.  This returns the Window to its state before any events or makeFirstResponder: messages were received.

An object could refuse to become the first responder if its internal state temporarily prevents it from responding to events and action messages.  An object might refuse to give up being the first responder if it needs to receive additional events.  For example, an object that asks the user to type a directory name might remain the first responder until it receives the name of a valid directory.



Accepting First Responder Status

An object should agree to be the first responder (by returning YES to an acceptsFirstResponder message) only if it needs to receive some of the event and action messages that are directed to a first responder.  These messages were listed above and include, most prominently:

Keyboard event messages
Action messages that aren't hard-wired to a specific target

Action messages from the Controls within the Font panel and from menu commands such as Cut, Copy, and Paste fit this description.  They affect the current selection, which can change from Window to Window and from View to View.  More precisely, they affect the current selection of the first responder in the key window or main window.

Any View that displays material that the user can select or edit must be able to respond to untargeted action messages like these, and therefore must accept first responder status.  In the Application Kit, for example, Text objects, TextFields, and Forms agree to be the first responder when they display editable text, whereas Buttons, Sliders, and Scrollers always refuse.

Most objects that refuse to be the first responder fit into one of two categories:

If an object responds only to mouse events, it can set up its own modal loop to get all the events it needs, except the mouse-down events that initiate the loop.  It doesn't have to be the first responder to receive mouse-down event messages.  See "Modal Event Loops," later in this chapter, for a description of this type of object.
If an object inherits from View simply so that it can draw on the screen, not so that it can respond to events, it won't want any events at all.  The event messages it receives will be passed on to its next responder.


The Key Window and Main Window

The Application Kit changes the key window (and main window) in response to left mouse-down events received from the Window Server.  If the window associated with the event isn't already the key window, it's made the new key window provided that it has an event mask that accepts key-down events.  Unless the window is a panel, it's also made the main window.  The main window changes only when the key window does.

Each Window object keeps track of whether it's the key window or main window:

BOOL  keyStatus, mainStatus;

keyStatus = [myWindow isKeyWindow];
mainStatus = [myWindow isMainWindow];

The Application object can identify which of its Windows is the key window and main window:

id  key, main;

key = [NXApp keyWindow];
main = [NXApp mainWindow];

These two methods return nil if the application isn't active or if there is no key window or main window.



Changing the Key Window

You can alter the key window (and main window) programmatically with the makeKeyWindow method:

[myWindow makeKeyWindow];

This method can also be performed indirectly, through the makeKeyAndOrderFront: method. makeKeyAndOrderFront: combines a makeKeyWindow message with an orderFront: message.

[myWindow makeKeyAndOrderFront:self];

For the Window that receives a (direct or indirect) makeKeyWindow message to actually be made the key window, it must be on-screen and must accept key-down events.  For it also to be made the main window, it must be on-screen, accept key-down events, and not be a Panel.  The canBecomeMainWindow method returns whether these three conditions are true:

BOOL  potentialMain;
potentialMain = [myWindow canBecomeMainWindow];

Since the key window and main window belong only to the active application, the receiving Window's application must also be the active application when a makeKeyWindow message is sent.  If it's not, the message serves only to register the intended key window (and main window) for the next time the application is activated.  The user can override this intention simply by clicking another window.

Programmatically making a Window the key window is appropriate only in a limited number of situations:

After the user acts in a panel that's the key window, the panel should return key-window status to the main window. For example, when the Font panel sets the font of the current selection, it makes the main window (where the selection is located) the key window.
To respond to a remote message, the application might need to designate an appropriate key window.  After receiving a message to open a file, for example, the application should make the window that displays the file the key window.
An initial key window is designated in the application's setup code.  For example, the Little demonstration program listed under "Setting Up Event-Handling Objects" above sent a makeKeyWindow message to its principal Window as part of its setUp() function.  Since the application wasn't active at the time the message was sent, it served only to register the Window as the desired key window (and main window) once the application was activated.


Notification

When the key window changes, the new key window is notified with a becomeKeyWindow message.  If the Window also becomes the main window, it's notified with a becomeMainWindow message.  The former key window and main window are notified of their lost status with resignKeyWindow and resignMainWindow messages:

[thatWindow resignKeyWindow];
[thisWindow becomeKeyWindow];

[thatWindow resignMainWindow];
[thisWindow becomeMainWindow];

These messages are sent whenever the key window or main window changes, no matter what the reason.  The change could be caused by:

A left mouse-down event deactivating one application and activating another
A left mouse-down event within another window of the same application
The user hiding an application, and thus deactivating it
The user unhiding an application, and thus activating it
The user closing the key window or main window
The user miniaturizing the key window or main window
A makeKeyWindow message in the active application

In other words, the messages exactly parallel the changes in window status that the user sees.  See Chapter 2 for more on how user actions affect a window's status.

When any of the four messages are generated by a left mouse-down event, they're sent before any becomeFirstResponder and resignFirstResponder messages that might be generated by the same event.

The Window class defines methods that can respond to all four messages shown above.  Each method records the Window's change in status and notifies the Window's delegate of the change, provided the delegate has a method that can respond to the message:

[[self delegate] windowDidBecomeKey:self];
[[self delegate] windowDidResignKey:self];

[[self delegate] windowDidBecomeMain:self];
[[self delegate] windowDidResignMain:self];

The Window also passes becomeKeyWindow and resignKeyWindow messages on to its first responder, if the first responder can respond.  In the Application Kit, the Text object uses these messages to learn when to begin blinking the caret marking the insertion point and when to stop.  The caret should blink only in the Text object that will display the user's typing--that is, only in the first responder of the key window.

Your application can use these notification messages to keep itself current with the user's actions.  For example, you may want to make sure that the PrintInfo object cached by the Application object reflects the document in the key window.  You might also want to update panels (such as an "inspector" panel) so that they display information appropriate for the main window.

To take advantage of the notification, you must implement methods that can respond either to the messages sent to the Window or to the ones sent to its delegate.  The methods you define in a Window subclass should perform the default versions defined in the Window class:

- becomeKeyWindow
{
[super becomeKeyWindow];
. . .
}


The Active Application

When a left mouse-down event selects a new active application, it generates an application-activated subevent (of the kit-defined event).  The subevent is sent to the application even before the mouse-down event.

When one application is activated, another might be deactivated, so a mouse-down event may also cause an application-deactivated subevent (also of the kit-defined event) to be sent to the current active application.  The active application is always deactivated before another application is activated, so the application-deactivated subevent is sent before the application-activated subevent.  But since they're sent to different processes, it's not determined which one will be acted on first.

When the user hides an application, an application-deactivated subevent is generated, but no application-activated subevent.

The isActive method returns the application's current status:

BOOL  activeStatus;
activeStatus = [NXApp isActive];

The activeApp method returns a user object (an integer) identifying the PostScript execution context of the active application:

int   activeOne;
activeOne = [NXApp activeApp];


Changing the Active Application

Usually, applications are activated by left mouse-down events that spawn application-activated and application-deactivated subevents.  But the Application Kit is sometimes called upon to activate an application in the absence of an event.  An application is activated when:

It's launched.
It receives a message to open a file.
It's returned to the screen after being hidden.

In the first two cases, the activation is conditional:  The application will become active only if there's no current active application.  This condition is usually met because the Workspace Manager deactivates the current active application both when it launches a new application and when it sends an inactive application an openFile:ok: message.  The condition won't be met only if the user chooses another application to work in before the target application has a chance to activate.

If your application accepts messages (other than openFile:ok: messages) from other applications, it may need to activate itself in response:

[NXApp activateSelf:NO];

The NO flag indicates that the activation is conditional.  This method may generate application-activated subevents. With YES as an argument, the activation is unconditional; it will force the current active application to be deactivated and so may also generate application-deactivated subevents for another application.

In general, protocols between cooperating applications require the application receiving a message to activate itself conditionally, if it needs user interaction to do its work.  The sending application should deactivate itself so the receiving application can become active:

[NXApp deactivateSelf];

If, in response to a message, an application activates itself unconditionally, it should restore the previous active application (the one sending the message) when it's finished.  The activateSelf: method returns the PostScript execution context of the previous active application:

int  lastActive
lastActive = [NXApp activateSelf:YES];

The number can be used to reactivate the application later:

[NXApp activate:lastActive];

Instead of using activateSelf: for unconditional activation, applications can also use the unhide: method:

[NXApp unhide:anId];

unhide: includes an activateSelf: message (with YES as its argument) and also ensures that the windows of the newly activated application aren't hidden.



Notification

When the Application object receives an application-activated subevent from the Window Server, it first does what's necessary to activate the application.  It then sends itself a becomeActiveApp message.  An application-deactivated subevent generates a resignActiveApp message.

The methods that respond to these two messages simply notify the Application object's delegate of the change in status, if the delegate has a method that can handle the message:

[[self delegate] appDidBecomeActive:self];
[[self delegate] appDidResignActive:self];

The sole purpose of becomeActiveApp and resignActiveApp messages is to give your application a way of coordinating its activities with changes in its status.  You can take advantage of this opportunity either by implementing appDidBecomeActive: and appDidResignActive: methods for the delegate or by overriding Application's becomeActiveApp and resignActiveApp methods in a subclass definition.




Event Messages

As the Application object gets events from the Window Server, it dispatches them as Objective-C messages to other objects.  With few exceptions, NXApp sends every event to a Window object:

Keyboard events are sent to the key window.  (However, Command key-down events, potential keyboard alternatives, can be sent to any Window.)
Mouse events are sent to the Window associated with the event--that is, to the Window whose window number is recorded in the window component of the event record.
The window-moved, window-resized, and window-exposed subevents of the kit-defined event are also sent to the Window associated with the event.  (The window-resized subevent isn't currently used by the Application Kit. However, should window-resized subevents be posted or placed in the event queue, the Kit would dispatch them to the window in the event record.)

If the event is a keyboard or mouse event, the Window usually sends it on to one of the objects in its view hierarchy.  It handles kit-defined subevents itself.

The events that NXApp doesn't dispatch to a Window are:

Application-defined events, which NXApp handles itself with the aid of its delegate.
The application-activate and application-deactivate subevents of the kit-defined event.  These two subevents are handled internally by the Application Kit; your objects never need to respond to them directly, though they can be notified when the application is activated or deactivated.  See "Selecting an Application, Window, and View" above.
Subevents of the system-defined event.  All but the power-off subevent are used internally by objects defined in the Kit; your application never has to deal with them.  The power-off subevent is handled by the Workspace Manager, which notifies all the applications it launched that the power is about to go off.  Applications generally respond to the Workspace Manager's message rather than to the event itself.
Timer events.  The Application object doesn't dispatch timer events; you should ask for them only when you're prepared to get them out of the event queue yourself.  See "Using Timer Events" under "Modal Event Loops" later in this chapter.
Cursor-update events, which are used internally by the Application Kit.  See "Changing the Cursor" later in this chapter.

The object that receives an event from the Application object, or from a Window, gets it in the form of an event message--a message to apply a method named after the event type or subtype it reports.

Event Category Method
Mouse events mouseDown: (for the left mouse button)
mouseUp: (for the left mouse button)
mouseDragged: (for the left mouse button)
rightMouseDown: (for the right mouse button)
rightMouseUp: (for the right mouse button)
rightMouseDragged: (for the right mouse button)
mouseMoved:
mouseEntered:
mouseExited:

Keyboard events keyDown:
keyUp:
flagsChanged:

Kit-defined subevents windowExposed:
windowMoved:
windowResized:
System-defined subevents powerOff:
Application-defined events applicationDefined:

In each case, the method takes a single argument, a pointer to the event record.  By its very name the event message identifies the event type or subtype; its argument passes along all the other available information about the event.

Note:  The Application Kit and this documentation take the point of view of a right-handed user.  The primary mouse button, whether left or right, generates "left mouse events" and the other mouse button, if it functions differently than the primary button, generates "right mouse events."  If a user enables the left mouse button to bring the main menu to the cursor, it will generate right mouse events and the right mouse button will generate left mouse events.  If neither button is enabled to bring the main menu to the cursor, both buttons generate left mouse events.

The Application Kit implements default versions of methods that respond to event messages.  Some have default behavior that your application can inherit and rely on.  Others--especially those for keyboard and mouse events--do little or nothing.  You must either implement your own methods so that your application can respond to the events in its own way, or make use of the objects provided in the Kit with defined responses to event messages:

The Text object responds to keyboard events by formatting and displaying the user's typing.  It responds to mouse events by altering the selection and insertion point.
Control objects (such as Buttons, Sliders, and TextFields) capture keyboard and mouse events and turn them into action messages for other objects.  By implementing a method that can respond to an action message, you can add specific behavior to your application without directly responding to the event message.  (Action messages are described in a later section.)
ScrollViews capture the mouse events that scroll one (larger) View within another (smaller) View, and do the scrolling for you.

The following sections discuss how event messages are dispensed and the Kit's default response to each one.



Keyboard Events

The Application object sends keyboard events to the key window, which passes them on as event messages to its first responder.

There's just one exception to this rule.  When the Application object gets a key-down event, it checks whether the Command key was down at the time of the event.  If it was, NXApp first tries to pass the event as a potential keyboard alternative--a keystroke that can activate a menu item or a button.  Only after determining that no object will respond to the keyboard alternative does NXApp distribute it as an ordinary key-down event.  See "Keyboard Alternatives," later in this section, for more information.



Left Mouse Events

Users can select an object on the screen (a View) by moving the cursor so that it points to the object and pressing the left mouse button.  The mouse-down event is sent to the object the user selects as a mouseDown: event message. Mouse-dragged and mouse-up events that follow the mouse-down event are sent to the same object.

However, mouse-dragged and mouse-up events generally aren't distributed through event messages.  Once an object receives a mouse-down event, its mouseDown: method can set up its own event loop to get these events until the user releases the mouse button.  See "Modal Event Loops," later, for details.



Hit Testing

When it receives a mouse-down event, the Window object uses View's hitTest: method to look for the View in which the cursor was located when the mouse button was pressed--the View that contains the coordinates of the mouse-down event's location component.  If location is within more than one View, hitTest: picks the View that's lowest in the view hierarchy.

hitTest: searches through a subviews list by starting at the end and working its way back toward the beginning.  This gives the last subview to draw the first opportunity to accept the event.  If the cursor is located in an area shared by two overlapping subviews of the same superview, as illustrated in Figure 7-3, the subview on top gets the event.



Figure 7-3.  Overlapping Subviews



Trapping the First Event

A left mouse-down event selects more than just the object that's to receive subsequent mouse events.  It can also select:

The active application, if the mouse-down event spawns application-activated and -deactivated subevents.
The key window (and main window), if the Window that receives the event isn't already the key window.
The first responder, if the View associated with the event accepts first-responder status.

When a left mouse-down event is used to select a new key window, and possibly a new active application, you may not want it to do anything more.

Suppose, for example, that the user has two of the Quadrant windows illustrated above in Figure 7-2, "Four Text Objects in a Window," on-screen at the same time; each of the windows has Text objects displaying editable text.  By clicking first in one window, then in another, the user can repeatedly alter the key window.  If the click were passed through to the Text object each time, it would also select a new first responder and alter the current text selection or insertion point.  This would make it nearly impossible to select a window without also selecting text.

To prevent this from happening, you can trap mouse-down events that choose the key window before they change the first responder and before they're sent through as event messages to Views within the Window's view hierarchy.

Whenever a Window receives a left mouse-down event that makes it the key window, it sends the View that was selected by the event an acceptsFirstMouse message before passing it the event.  If the View returns YES, the Window will send it a mouseDown: message for the event.  If the View returns NO, the mouse-down event won't be sent to the View, and neither will the mouse-dragged and mouse-up events that follow the mouse-down.

In general, Views that display material that the user can select or edit should return NO to an acceptsFirstMouse message; other Views should return YES.  In the Application Kit, Text objects, TextFields, and Forms refuse the first series of left mouse events, but Sliders, Buttons, and Scrollers accept them.

The default response of the acceptsFirstMouse method defined in the View class is NO.  To change the default in your View subclass, simply implement your own version of the method:

- (BOOL)acceptsFirstMouse
{
return YES;
}


Selecting the First Responder

The Application Kit tries to make the View selected by a left mouse-down event the first responder.  If it answers YES to an acceptsFirstResponder message, its Window is sent a makeFirstResponder: message as discussed above under "The First Responder."

If the View selected by the mouse-down event is one that accepts being the first responder, but the current first responder refuses to give up that status (returns nil to a resignFirstResponder message), the mouse-down event and the following mouse-dragged and mouse-up events are sent to the current first responder, rather than to the View selected by the mouse-down event.



Right Mouse Events

Right mouse events within the key window are distributed very much like left mouse events.  When the user presses the right mouse button while the cursor is in the key window, hit testing finds the View where the cursor is located.  The right mouse-down event and subsequent right mouse-dragged and right mouse-up events are sent to that View.

However, Views rarely respond to right mouse events.  In the user interface, these events are reserved for bringing a copy of the main menu to the cursor.  Therefore, rightMouseDown: event messages generally are passed up the responder chain from next responder to next responder until they reach the Window, which then makes sure that the main menu gets the event.  The main menu sets up a modal event loop that collects all subsequent right mouse events until the mouse button is released.  (Event loops are discussed under "Modal Event Loops" later in this chapter, and passing event messages up the responder chain is discussed under "Event Messages in the Responder Chain" below.)

This pattern of distribution permits a View that needs to distinguish between left and right mouse events to get both.  At the same time, it ensures that right mouse events that aren't intercepted by a View do the job that they're meant to do in the user interface.

Unlike left mouse events, right mouse events don't change the active application, key window, or first responder, and they aren't trapped if the View returns NO to an acceptsFirstMouse message.

Only Views in the key window are given a chance to respond to right mouse events.  If the Window that receives a right mouse event isn't the key window, it turns the event over to the main menu.  This restriction makes the user interface for right mouse events match that of left mouse events more exactly.  When the user begins working within a window using the left mouse button, the window becomes the key window.  To ensure that the user's work with the right mouse button also takes place in the key window, the window has to be the key window before any right mouse events can be distributed to its Views.



Mouse-Exited and Mouse-Entered Events

An application can receive mouse-exited and mouse-entered events, provided:

At least one of its Windows has an event mask that accepts the events, and
The application has set a tracking rectangle within the Window.

Mouse-exited events are generated when the cursor leaves the tracking rectangle; mouse-entered events occur when the cursor enters the rectangle.

A Window can have any number of tracking rectangles.  So that particular events can be matched to particular rectangles, you can assign each rectangle an identifying tag that will be reported back in the event records of the mouse-exited and mouse-entered events that it generates.

Window's setTrackingRect:inside:owner:tag:left:right: method sets a tracking rectangle:

NXRect  rect;

[aView getFrame:&rect];
[[aView superview] convertRect:&rect toView:nil];
[myWindow setTrackingRect:&rect
inside:YES
owner:aView
tag:3
left:YES
right:NO];

In this example, aView's frame rectangle is made the tracking rectangle, and aView itself is made the tracking rectangle's owner, the object responsible for handling the events the rectangle generates.  The owner need not be a View, but it should be a Responder.  When the Window receives mouse-exited and mouse-entered events, it dispatches mouseExited: and mouseEntered: event messages directly to the owner.

The convertRect:toView: message above transforms aView's frame rectangle to the correct coordinate system for making it a tracking rectangle.  A tracking rectangle is specified in the base coordinate system for the window--here in myWindow's base coordinate system.  Since a View records its frame instance variable in its superview's coordinate system, the message is sent to aView's superview.  See "Converting Coordinates" later in the chapter for more on convertRect:toView: and similar methods.

The rectangle's tag distinguishes it from other tracking rectangles within the same window.  The inside flag indicates whether the cursor starts out inside the rectangle (YES) or outside it (NO).  If it's YES, the first event received for the rectangle will be a mouse-exited event, regardless of where the cursor is actually located.  If NO, the first event will be be a mouse-entered event.

The last two arguments to setTrackingRect:inside:owner:tag:left:right: specify whether events are to be generated for the rectangle only if one or both of the mouse buttons is being held down.  In this example, mouse-exited and mouse-entered events will be generated only while the left mouse button is down.  This makes these events somewhat akin to left mouse-dragged events.

A tracking rectangle remains in effect until another rectangle with the same tag is set for the Window, or until it's removed by the discardTrackingRect: method:

[myWindow discardTrackingRect:3];

Note:  Don't assign negative tags to tracking rectangles.  The Application Kit uses negative numbers to identify the tracking rectangles that generate cursor-update events.



Kit-Defined Events

The application-activated and application-deactivated subevents of the kit-defined event don't generate event messages.  However, they do initiate becomeActiveApp and resignActiveApp messages to the Application object, and appDidBecomeActive: and appDidResignActive: messages to the Application object's delegate, as discussed under "The Active Application" above.  You should respond to these subevents only by writing methods that can respond to these messages.

The subevents of the kit-defined event that concern the state of a window generate event messages to the Window object whose window number is listed in the event record.  The Window class defines methods to respond to these messages.



Window-Moved

The windowMoved: method updates the Window's frame instance variable to record the new location of the window. It then informs the Window's delegate of the move by sending it a windowDidMove: message, if the delegate has a method that can respond.  The delegate can use Window's getFrame: method to get the window's new location in screen coordinates:

- windowDidMove:sender
{
NXRect  rect;
[sender getFrame:&rect];
. . .
}

The location of a window is important to the Application Kit as it responds to user actions that manipulate windows, but it's generally of little use to an application.  Most applications shouldn't care where the user places windows and won't need to do anything special when a window moves.



Window-Exposed

The windowExposed: method redisplays part (sometimes all) of the Window's contents.  The area that's redisplayed is a rectangle calculated from the event record.  The location of the rectangle is taken from the event record's location component and its size is taken from the data.compound.misc.L component.

After sending a display message, windowExposed: informs the Window's delegate with a windowDidExpose: message, if the delegate has a method that can respond.



Window-Resized

The windowResized: method redisplays the Views within a window in response to a window-resized subevent. However, this method isn't currently used.  When the user resizes a window by dragging its resize bar, no window-resized subevents are generated.  Instead, the Window's frame view, which contains the resize bar, resizes the Window and redisplays its Views.  Applications are informed of the resizing, not by an event, but by notification messages sent to the Window's delegate (or, in the absence of a delegate, to the Window itself):

As the user drags an outline of the window, repeated windowWillResize:toSize: messages are sent to the delegate, if the delegate implements a windowWillResize:toSize: method.  These messages give the delegate a chance to determine the new size of the window.  The first argument to windowWillResize:toSize: is the id of the Window object.  The second argument passes a pointer to an NXSize structure containing the proposed new width and height of the window.  The delegate can alter these values to constrain the size of the window.  It can be kept within maximum and minimum size limits, or be made to grow and shrink by defined amounts (as is the Workspace Manager's Directory Browser).  The on-screen outline will reflect the altered values the delegate places in the NXSize structure.
After the user releases the mouse button and just before the window is redisplayed in its new size, the delegate is informed with a windowDidResize: message, if it has a method that can respond.  The argument passed in a windowDidResize: message is the id of the Window object, which can provide the new dimensions of the window in response to a getFrame: message.

If a Window doesn't have a delegate, or its delegate doesn't respond to windowWillResize:toSize: and windowDidResize: messages, these messages will be sent to the Window instead--but only if the Window can respond.  This means that you can implement windowWillResize:toSize: and windowDidResize: methods either in a Window delegate or in a Window subclass.



Application-Defined Events

If your application makes use of application-defined events, you must write the code to respond to them.

When it receives the event, the Application object sends itself an applicationDefined: event message.  But its applicationDefined: method does nothing more than pass the same message on to its delegate, if the delegate can respond.

You can implement an applicationDefined: method either in a subclass of the Application class, or in a class definition for the Application object's delegate.



System-Defined Events

Most subevents of the system-defined event are handled internally by the Application Kit.  Only one, the power-off subevent, results in event messages.

Power-off subevents are generated when the user presses the Power key on the keyboard.  The Window Server broadcasts the event to every application with on-screen windows.

One of the applications that receives the event is the Workspace Manager.  It puts up a panel that requires users to confirm the power-off instruction or cancel it.  If the user doesn't rescind the instruction, the Workspace Manager sends each application it launched a powerOffIn:andSave: message.  This is the same message it sends to its applications when the user wants to log out.

The powerOffIn:andSave: message is received by the Application object, which terminates the main event loop and sends its delegate an appPowerOffIn:andSave: message, if the delegate has a method that can respond.  The first argument to both methods is the number of milliseconds before the power goes off.  The second argument is currently meaningless and should be ignored.

The Application object's delegate can ask for more time by sending the Workspace Manager an extendPowerOffBy:actual: message.  It should then save files and take whatever other steps are necessary to prepare for the shutdown.

Applications that are launched in the workspace ignore the power-off subevent and attend only to the messages received from the Workspace Manager.  Applications that are launched from the command line won't get these messages and must respond to powerOff: event messages instead.

NXApp is the object that receives the powerOff: message.  If the application was launched by the Workspace Manager, its powerOff: method does nothing.  Otherwise it sends its delegate an appPowerOff: message, if the delegate can respond.



Cursor Coordinates

Methods that respond to mouse events may want to note the exact location of the cursor on-screen.  The coordinates in the location component of the event record are given in the window's base coordinate system; they can be translated to the receiving View's local coordinates by the convertPoint:fromView: method.

NXPoint  where;

where = eventPtr->location;
[self convertPoint:&where fromView:nil];

This method transforms a point expressed in the coordinate system of its second argument to the receiving View's reference coordinate system.  When the second argument is nil, as it is here, it's assumed that the point is expressed in the base coordinate system.  convertPoint:fromView: alters the NXPoint structure referred to by its first argument and returns a pointer to the same structure.

Note that once the location component of the event record has been transformed from the base coordinate system, other objects in the view hierarchy will be unable to rely on it.  For this reason, it's first assigned to the local variable where before being passed to convertPoint:fromView: in the example above.



Querying the Cursor

If the process of responding to an event takes some time and you need a more recent indication of the cursor's location, Window's getMouseLocation: method provides one:

NXPoint  where;
[myWindow getMouseLocation:&where];

This method places the current cursor location in the NXPoint structure referred to by its argument; it returns self.  The point is specified in the receiving Window's base coordinate system and can be altered by the convertPoint:fromView: method illustrated above.



Testing the Cursor's Location

Methods that respond to events often must test whether the cursor is located in a particular View or in a particular region of a View, usually expressed as a rectangle.  The function that makes the test is NXMouseInRect().

BOOL    inside;
NXRect  rect;

[myView getBounds:&rect];
inside = NXMouseInRect(&where, &rect, NO);

This function takes a pointer to the cursor location and a pointer to a rectangle, here myView's bounds rectangle, and returns whether the point is inside the rectangle.  The third argument, NO in the example, is best explained by examining what's meant by the "location of the cursor."

The cursor's location, as reported in the event record or by getMouseLocation:, is, in fact, the location of just one point on the cursor, its hot spot.  Since the cursor is an image, displayed with pixels, the hot spot corresponds visually to a particular pixel--in effect a "hot pixel."  The pixel chosen by the hot spot is the one cursor pixel that users can't drag off-screen.

When testing whether the cursor is inside a rectangle, what we really want to know is whether the pixel corresponding to the hot spot is inside the rectangle.

The hot spot has no fractional coordinates, so it always lies on a corner where four pixels meet.  On the MegaPixel Display, the pixel chosen by the hot spot is, in accord with the rules described under "Imaging Conventions" in Chapter 4, "Drawing," the one that lies below it and to its right.  In Figure 7-4, small arrows point to the potential hot spots that would chose the "hot pixels" labeled "A" and "B".



Figure 7-4.  Testing the Cursor's Location



In this diagram, pixel A lies inside the shaded rectangle and pixel B lies outside it, but both hot spots lie on the edge of the rectangle.  To correctly determine which hot spot chooses a pixel inside the rectangle and which does not, NXMouseInRect() must know the polarity of the y-axis.  When passed NO as the third argument, it assumes that y coordinate values increase from bottom to top, so hot spots with y coordinate values that match the minimum rectangle values are excluded from the rectangle.  When passed YES, it assumes that the y-axis has been flipped, with coordinate values increasing from top to bottom, so hot spots with y coordinates equal to the maximum rectangle values are the ones excluded.

Flipped coordinate systems are described under "View Coordinate Systems" later in this chapter.



Event Messages in the Responder Chain

Every Window has its own responder chain of Views.  When a View receives a keyboard or mouse event message that it can't handle, the message is passed to its next responder.

The chain is established as each View object has its nextResponder instance variable initialized to another object.  As a default, and with the sole exception of the content view, a View's next responder is its superview.  The content view's next responder is the Window.  The Window ends the responder chain; its next responder is nil.

You can add other Responders into this default chain:

[anotherResponder setNextResponder:[self nextResponder]];
[self setNextResponder:anotherResponder];

The mechanism for passing events along the chain is inherited from the Responder class.  Responder has a default implementation for the methods that respond to keyboard and mouse event messages.  Its methods don't take any action in response to the event; they simply send the same message on to the next responder:

- keyDown:(NXEvent *)theEvent
{
[nextResponder keyDown:theEvent];