Mac Developer Library Developer
Search

OS X Developer Release Notes:
Cocoa Application Framework (10.8 and Earlier)

This document contains the release notes for OS X 10.8, its updates, and earlier. Please refer to the current release notes for AppKit before referring to this document, as the more recent changes in the current release could have obsoleted some of the items discussed here.

The notes below are split into the following sections:




Notes specific to OS X 10.8

Some of the major topics covered in this document:




NSDocument Support for iCloud

In 10.8, NSDocument-based applications with a ubiquity-container-identifiers entitlement gain new functionality and UI to facilitate iCloud document management.

When iCloud is enabled and an application is first launched or re-activated and no windows are visible or being restored, instead of creating a new Untitled document, NSDocumentController will display a non-modal open panel showing the user's iCloud library. From there an application can either open an existing document from iCloud or the local file system, or create a new document. To support this new non-modal open panel, NSDocumentController has added -beginOpenPanelWithCompletionHandler: and -beginOpenPanel:forTypes:completionHandler:. Note that currently the former method will only run the open panel non-modally when iCloud is enabled.

If your application overrides -[NSDocumentController openDocument:] and invokes -URLsFromRunningOpenPanel, you should switch to calling -beginOpenPanelWithCompletionHandler: instead.

If your application overrides -[NSDocumentController runModalOpenPanel:forTypes:] to customize the open panel, including adding an accessory view, you should also override -beginOpenPanel:forTypes:completionHandler: and do the same customization there. For compatibility NSDocumentController will continue to invoke the former if it is overridden when the latter isn't. As a result, the panel will be run modally instead of non-modally, giving your users a suboptimal experience.

iCloud-enabled applications also gain UI to facilitate moving documents into the cloud via the move and save panels. There is also a shortcut in the document titlebar menu to quickly move the document to iCloud. This menu item uses a new IBAction method on NSDocument: -moveDocumentToUbiquityContainer:. Also, as mentioned in the section on Drafts, new documents can get autosaved directly to the application's iCloud container, depending on the user's preference.

NSDocument also provides a new conflict resolution UI that allows users to inspect the contents of each conflict and save each conflicting version as a separate file, if they wish.

Applications that do not wish to use these features for any or all of their NSDocument subclasses can override +[NSDocument usesUbiquitousStorage] and return NO. If all of the application's declared NSDocument subclasses return NO from this method, then NSDocumentController will never show the new non-modal open panel.

NSDocument Versions Preservation Optimization

In Mac OS 10.7, when NSDocument decided that its file deserved to be preserved as a version prior to saving it would synchronously copy the file to perform the preservation. For significantly large files this could cause long hangs that interrupt the user's workflow. In applications linked on or after 10.8, NSDocument can avoid this hang by leveraging the 'backup' file that gets created during safe-saving.

When the current file's contents need to be turned into a version prior to saving, the file will get moved out of the way prior to the newly written file overwriting it. After the new file is successfully saved in the old file's place, a version can be created from the old file without requiring an expensive copy.

NSDocument uses -[NSDocument backupFileURL], a new method in 10.8, to control this behavior. This method only returns a URL when the current file needs to be preserved. When this method returns non-nil, -writeSafelyToURL:ofType:forSaveOperation:error: must ensure that the original contents of the file exist at that location prior to returning. Failing to do so is tantamount to data loss, since the user will be unable to revert to their previous versions. If your NSDocument subclass overrides -writeSafelyToURL:ofType:forSaveOperation:error: and doesn't call super, when you link against 10.8 you must ensure that your implementation obeys this rule. Alternatively, you can override -backupFileURL and return nil to disable this optimization. By doing so, -saveToURL:ofType:forSaveOperation:completionHandler: will fall back to the old synchronous copy technique. Applications that do saving in-place and don't use the safe-saving technique will almost always want to disable this optimization unless the cost of creating the 'backup' file is significantly less than a copy.

You should not override -backupFileURL to return a custom URL. Your override should either call super or return nil.

Sometimes versions are preserved after the new file is written, such as when the user chooses File > Save. In this scenario, the backup file optimization is not possible, so a copy is still necessary. However, for applications linked on 10.8, NSDocument will perform this copy on a background thread, leaving the main thread unblocked. To avoid corrupting these versions by modifying the file during this asynchronous copy, you should make sure that any direct access to your document's file is done with -performSynchronousFileAccessUsingBlock: or -performAsynchronousFileAccessUsingBlock:.


Debugging -performActivityWithSynchronousWaiting:usingBlock:, -performSynchronousFileAccessUsingBlock:, and -performAsynchronousFileAccessUsingBlock:

In Mac OS 10.7, NSDocument added these three APIs to ensure proper serialization of file access and user activity when using asynchronous saving. When an asynchronous activity or file access fails to properly invoke its completion handler, subsequent uses of these APIs wait forever. Unfortunately, due to the asynchronous nature of these APIs, the backtrace of these hangs usually doesn't provide enough information to debug the problem and identify the unterminated operation.

In 10.8, NSDocument provides a tool to help you more easily debug these problems. If your application is hanging in -performActivityWithSynchronousWaiting:usingBlock: or -performSynchronousFileAccessUsingBlock:, break in the debugger and execute this command:
po _NSDocumentSerializationInfo()
The output of this command will contain a listing of all unterminated invocations to these serialization APIs, including the document instance in question, the backtrace where the API was invoked, the type of serialization (file access vs. activity), and the current status (waiting for exclusive access vs. exclusive access is granted but block hasn't been executed yet vs. block is executing).

This function is intended for debug purposes only. It is not declared in any headers, and its name and output are subject to change at any time.


New and Updated NSDocument Operations

In 10.8, NSDocument has added some new document management operations and has enhanced others.

Users can now move and rename documents from within the application by using the Move To… and Rename… commands from the File menu or the document titlebar menu. The Move To… command shows the user a simple panel that allows the user to move the document to a new directory. The Rename… command causes the title in the window titlebar to become editable, allowing the user to change the title of the document. The new APIs controlling these operations are -renameDocument:, -moveDocument:, -moveDocumentWithCompletionHandler:, and -moveToURL:completionHandler:.

The Duplicate command has also been enhanced. Immediately after the duplicate document is created, the window's title will become editable, allowing the user to customize the title. If the user specifies a custom title, then the document will be saved into the same directory as the original document, if possible.

Certain documents that the system decides should not be edited, such as attachments from the Mail application, will be automatically duplicated when the user edits them to avoid modifying the original. This works by immediately causing the document to be autosaved to the ~/Library/Autosave Information directory with NSAutosaveElsewhereOperation. The autosavedContentsFileURL is set to that location and the fileURL is set to nil, so the document starts behaving like an unnamed duplicate document.

There is new API to query and control the "locked" state of the document. -isLocked returns YES when NSDocument thinks its file cannot be edited for reasons including, but not limited to, the "user immutable" flag being set, insufficient permissions, and -checkAutosavingSafetyAndReturnError: returning NO. You can attempt to unlock the document with the -unlockDocumentWithCompletionHandler: and -unlockWithCompletionHandler: APIs. NSDocument will attempt to unlock the file by clearing the "user immutable" flag and restoring write permissions. If that is not sufficient to unlock the file, or the file cannot be unlocked, then the operation will fail. You can attempt to lock the document with the -lockDocumentWithCompletionHandler: and -lockWithCompletionHandler: APIs. NSDocument will attempt to lock the file by setting the "user immutable" flag. If the file cannot be locked, then the operation will fail. There are two separate IBAction methods for unlocking and locking: -unlockDocument: and -lockDocument:. If you provide custom UI for these menu items, at validation time you should use -isLocked to decide which action should be used.

For applications adopting +autosavesInPlace, the Revert command has been broken up into multiple menu items that allow reverting to key versions without requiring the user to enter the Versions browser. These menu items continue to use the -revertDocumentToSaved: action and use the menu item's tag to identify which item the user selects. You should not attempt to modify these menu items in any way.

Finally, -browseDocumentVersions: is a new IBAction method on NSDocument that triggers its entry into the Versions browser.


NSDocument Drafts

In applications linked on or after 10.8, when a user edits an Untitled document in an iCloud-enabled application, NSDocument will automatically save it to the default ubiquity container as defined by [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil], unless the user has specifically disabled this. In this case, the document will be saved to ~/Library/Autosave Information as normal.

Once saved to iCloud, these documents behave very much like regular saved documents—they have a proxy icon in the title bar, they can accumulate versions, and so forth. However, when the user chooses to Save or Close the document, a save panel will be presented to allow the user to confirm the automatically chosen name and location, assign a new one, or even delete the file. Documents in this state are called "drafts". If the user does something prior to Save or Close to indicate that they want the document to be kept around, the document will no longer be considered a draft and hence will not show the save panel on Save or Close. Moving, renaming, or locking the file or editing it in another application are some examples of what can cause a document to stop being a draft. You can get and set the draft status of a document with the new -[NSDocument isDraft] and -[NSDocument setDraft:] APIs.

When a newly created document is saved as a draft, NSDocument invokes -setFileURL: without affecting the "Edited" status of the document, as shown in the titlebar. None of the NSSaveOperations in Mac OS 10.7 allow for this, so NSDocument in 10.8 defines a new save operation: NSAutosaveAsOperation. This new NSSaveOperation will be used by -autosaveWithImplicitCancellability:completionHandler: under specific circumstances. For example, a new unsaved document that has been changed with NSChangeDone will use NSAutosaveAsOperation instead of NSAutosaveElsewhereOperation. The URL that is used for the NSAutosaveAsOperation is determined internally by NSDocument and attempts to avoid name conflicts by appending a number to the document's display name that will be incremented until no conflict is found.

Some applications use -updateChangeCount: to cause NSDocument to autosave changes that don't originate directly from the user. For example, when importing a non-native document type, some applications create a new document with the imported contents and call -updateChangeCount: to ensure that the document gets autosaved with those contents. Many applications use NSChangeDone for this purpose. However, since the user did not explicitly cause this change, it is undesirable to turn this document into a draft. Applications should be careful to use the correct NSDocumentChangeType—in this case, NSChangeReadOtherContents—to prevent the conversion into a draft. Using NSChangeDiscardable will also prevent the creation of a draft.

When a document is scheduled to become a draft, the effective autosaving delay is greatly reduced to ensure that an autosave happens very soon after the first change. Once NSDocument has completed the NSAutosaveAsOperation, or if the operation has failed without any expectation of succeeding in the future, that NSDocument instance will no longer use NSAutosaveAsOperation on future autosaves. A document that has failed an NSAutosaveAsOperation will revert to behaving like a traditional Untitled document and autosaving will use NSAutosaveElsewhereOperation until the user gives the document a location via the save panel.

If one or more your application's NSDocument subclasses should not save untitled documents as drafts, you should override +[NSDocument autosavesDrafts] and return NO.


NSDocument Customizing Default Draft Names

In Mac OS 10.7, all newly created documents were called "Untitled" or "Untitled <number>" until the user gave them a title by saving. In 10.8, NSDocument will allow your application to replace "Untitled" with a custom string that is more descriptive for your application. For example, a word processing app may choose to automatically name documents "Résumé" when they’re created from a certain template, or a spreadsheet application may prefer new documents to be called "Spreadsheet" instead of "Untitled". You can do this in your application by overriding -[NSDocument defaultDraftName] and returning your custom localized string. If necessary, NSDocument will append a number to make the name unique.



CoreAnimation updates for NSView (i.e.: CALayer backed NSView)

For applications linked on 10.8 and higher, the following changes are in effect:

• NSTextField has been updated to allow LCD font smoothing to work when the view is layer-backed. Prior to 10.8, NSTextField would directly draw the text into the layer's contents; this would cause text to render incorrectly due to the LCD font smoothing algorithm not having adjacent pixels to smooth fonts with. Layer-backed applications that manually draw text should move to using NSTextField to get proper LCD font smoothing. One caveat is that NSTextField does require an ancestor which is layer-backed and opaque; it does not have to be the direct parent view, but some view in the ancestor chain must be opaque for this to be turned on and work correctly.

• For applications that link on 10.8, all layer-backed views may turn off LCD font smoothing to have text render better. Specifically, if a view returns NO from -isOpaque, then AppKit will turn off LCD-font smoothing before calling the view's drawRect: implementation. Otherwise, the drawing of text into a transparent layer will look incorrect (generally, it looks bolder and not smooth). However, there may be some specific cases where a layer-backed view knows it is manually drawing text into an opaque area. For these views, it is recommended that font smoothing be explicitly turned back on before drawing text. An example:
CGContextRef ctx = NSGraphicsContext.currentContext.graphicsPort;
CGContextSetShouldSmoothFonts(ctx, true);
[@"Tandem Unicycle" drawInRect: ... ];
• Prior to 10.8, calling setLayer: on NSView may not have properly updated the layer's position until a resize or reposition of the view happened; this has been fixed.

• Prior to 10.8, an AppKit managed layer would be destroyed (and removed) when a view was removed from a window or moved to another parent view. This would have the undesired effect of losing any custom properties set on the layer. For any view that explicitly has setWantsLayer:YES, the underlying AppKit managed layer will not be destroyed, and custom set layer properties will not get lost.

• Prior to 10.8, when AppKit removed the layer it managed, it would leave the layer's delegate set to the NSView that had the layer. For applications linked on 10.8 and higher, the delegate will be set to nil when it is removed (only if it is "owned" by AppKit). An application must re-assign the delegate to the view, if they save off and restore an AppKit owned layer (in general, this isn't recommended, and to get a layer to stick around one should call setWantsLayer:YES).
• Prior to 10.8, an AppKit managed layer would always have the layerContentsRedrawPolicy set to NSViewLayerContentsRedrawDuringViewResize when the layer was (lazily) created by AppKit. This prevented the value from being set at an earlier time before the layer was created. In 10.8, this is now done at view initialization time (either in -init or -initWithCoder). For views that host custom layers (via a call to setLayer:), the layerContentsRedrawPolicy will still be set to NSViewLayerContentsRedrawNever when setLayer: is called; it is assumed that the client wants to own the layer and control when it redraws. For all other regular NSView backing layers, it is required to properly set the layerContentsRedrawPolicy (usually in initWithFrame: or initWithCoder:). Prior to 10.8, the layerContentsRedrawPolicy was encoded in the NIB along with other properties of the view, despite there being no way to set it in Interface Builder. On 10.8, the layerContentsRedrawPolicy is no longer encoded in the NIB and should be explicitly set in code.

• Prior to 10.8, a flipped view would explicitly control a layer's position by updating it every call to setFrameSize:. However, this conflicts with allowing CoreAnimation to do frame size animations on a background thread without input from AppKit. To solve this, AppKit now controls the -geometryFlipped on the backing CALayer. Prior to 10.8, the anchorPoint was also set to be either (0,0) or (0,1) -- it varied depending on the superview being isFlipped or not. On 10.8, the layer's frame is now equal to the view's frame. The anchorPoint is also always set to be (0,0), since the positions are now equal. In order to properly control the flipped state of a layer, override -isFlipped on NSView.

• There are now two ways to provide a layer's contents when a view is layer-backed. The new preferred way is to implement -wantsUpdateLayer and return YES. When this returns YES, a new method called -updateLayer will be called. At this point, it is appropriate to directly set the view.layer.contents to an image (NSImage or CGImageRef) which can properly stretch (i.e.: a 3 part or 9 part image). To indicate how it should stretch use the CALayer contentsCenter property. If additional UI is needed to represent a particular control, add subviews (or sublayers) in -layout. -layout will be called for views that respond YES to wantsUpdateLayer, even if auto layout is not used. To get a new call to -updateLayer, invalidate the view with a setNeedsDisplay:YES. To get a new call to -layout, call setNeedsLayout:YES. Utilizing wantsUpdateLayer can lead to better performance when a view is layer-backed. Most of the standard AppKit controls will implement this to return YES if drawing methods are not subclassed and overridden. If a view returns NO from -wantsUpdateLayer, then the layer's contents will be filled in with whatever is drawn in drawRect:.

• As of 10.7, the way layer-backed views animate frame changes varies depending on the value of layerContentsRedrawPolicy. Animations are typically initiated using the animator proxy (i.e.: [[view animator] setFrame:]). If the layerContentsRedrawPolicy is NSViewLayerContentsRedrawDuringViewResize, then AppKit assumes the view needs to redraw on each step of the animation, and it will drive the animation on the main thread by calling setFrame: multiple times. If the layerContentsRedrawPolicy is NSViewLayerContentsRedrawOnSetNeedsDisplay, then AppKit will utilize Core Animation to drive the animation, which is done implicitly on a background thread, and the view will not get a setFrame: call for each step of the animation. Instead, Core Animation will stretch the layer's contents based on how properties are set on the CALayer. Note that using NSViewLayerContentsRedrawOnSetNeedsDisplay makes the view work just like a CALayer would for the animation; this means any property changes (which animate) will instantly appear at their final state as soon as the animation call is made. In other words the following happens:
view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicyOnSetNeedsDisplay; // This should be set in init
NSRect frame = view.frame; // Starts out as 100, 100
frame.size = NSMakeSize(300, 300);
[view.animator setFrame:frame]; // Animates the view frame from 100,100 to 300,300
// However, reading back in the view.frame at this point will indicate it is already at 300,300

• For 10.8 and higher, the default value for the layerContentsRedrawPolicy varies on a control-by-control basis inside of AppKit. For a plain NSView, the value will be NSViewLayerContentsRedrawOnSetNeedsDisplay. For a custom NSView that overrides drawRect:, the default value will be NSViewLayerContentsRedrawDuringViewResize. When using -wantsUpdateLayer, it is important to always set the layerContentsRedrawPolicy (ideally to NSViewLayerContentsRedrawOnSetNeedsDisplay).This will allow the view to animate frame changes with Core Animation, as opposed to AppKit driving the animation on the main thread.

• On 10.8, AppKit will control the following properties on a CALayer (both when "layer-hosted" or "layer-backed"): geometryFlipped, bounds, frame (implied), position, anchorPoint, transform, shadow*, hidden, filters, and compositingFilter. geometryFlipped is only changed for apps that link on 10.8 and higher. Use the appropriate NSView cover methods to change these properties.

• When a layer-backed view is invalidated with setNeedsDisplay: (or setNeedsDisplayInRect:) the window is no longer invalidated. This allows just the view that needs to redraw to be redrawn, and no others. However, applications need to take care that they didn't accidentally depend on side effect drawing that previously happened due to a parent view invalidating its children views.

• When a view had the layerContentsRedrawPolicy set to NSViewLayerContentsRedrawOnSetNeedsDisplay, the view (and layer) may still have accidentally been invalidated on frame changes. This has been fixed for applications linked against 10.8, and when NSViewLayerContentsRedrawOnSetNeedsDisplay is set, the application must explicitly call setNeedsDisplay: on frame changes (if required).

• NSAnimationContext now has a new property, allowsImplicitAnimation, for layer-backed views. The default value is NO, and it is automatically set to YES when the -animator proxy is used. When YES, it allows any CoreAnimation animatable property to be animated. This allows for nested animations. In other words, doing a [[view animator] setFrame:frame] will animate the frame on 'view', but also any subviews also. Typically this is desired, as it allows the autoresizing of subviews to be done animated, and all the animations to be tied together in the same animation block with the same duration. If a subview has some particular property they don't want to animate, a new animation group can easily be created to disable this behavior. The converse is also true; if you want a particular property to always have a CoreAnimation animation, then -allowsImplicitAnimation can explicitly be set to YES. An example use:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
     context.allowsImplicitAnimations = YES; // Enable implicit CoreAnimation animations
     // Any operations done in this block will be animated
} completionHandler:nil];
The use of allowsImplicitAnimation is very similar to the existing properties in UIKit on iOS: [UIView areAnimationsEnabled] and [UIView setAnimationsEnabled:]

•NSView's -cacheDisplayInRect:toBitmapImageRep: will now work properly with layer-backed views. For applications linked against 10.8, the layer will be drawn using CALayer's renderInContext:, allowing it to render all sublayers, including ones that may not have an associated view. This allows the use of -wantsUpdateLayer and -updateLayer: to work with -cacheDisplayInRect:toBitmapImageRep:.

• When using layer-backed views, Core Animation will animate a layer from its presentation state to whatever state you set on the layer (or view). However, sometimes animating a layer-backed view may not animate from the desired location or state. This can happen if your view (and implicitly the layer) never had a chance to display. For instance, consider this example:
NSRect frame = view.frame; // Starts at 0,0 -- the view was already drawn on screen at this location
frame.origin = NSMakePoint(100, 100);
view.frame = frame; // The view should move to 100,100 -- however, it has not drawn yet
frame.origin = NSMakePoint(300, 300);
[[view animator] setFrame:frame]; // The view should animate from point 100,100 to point 300,300
The desired animation is from point 100,100 to 300,300 - however, what may happen is the view will animate from point 0,0 to 300,300! This is because changes to view and layer trees are coalesced inside of NSAnimationContexts/CATransactions. Because it is impossible to know the outermost scoping of an animation context / transaction, changes may be coalesced even when not expressly desired. To handle these cases, each set of changes/animations you want to be visible should be isolated in their own animation grouping. This will cause the view/layer to render with the requested property change. For instance, this will work to enforce the first rendering to happen before the animation starts by chaining the actual animation to the end of the first animation group:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
context.duration = 0;
NSRect frame = view.frame; // Starts at 0,0 -- the view was already drawn on screen at this location
frame.origin = NSMakePoint(100, 100);
view.frame = frame; // The view should move to 100,100 -- however, it has not drawn yet
} completionHandler:^ {
NSRect frame = view.frame; // Now it has drawn at 100,100
frame.origin = NSMakePoint(300, 300);
[[view animator] setFrame:frame]; // This will correctly animate from 100,100 to 300, 300 using the default animation context duration
}];

NSTableView/NSOutlineView

For a View Based NSTableView (or NSOutlineView), it has always been incorrect to acquire a view from the table inside the delegate method tableView:heightOfRow: (or outlineView:heightOfItem:). Now, the table will detect if either of these methods is called from inside the delegate method implementation and throw an exception (or log an error): viewAtColumn:row:makeIfNecessary: and rowViewAtRow:makeIfNecessary:.

NSControl now has new API to show "expansion tooltips". This is primarily available for expansion tooltips to be shown when using a View Based NSTableView. Call setAllowsExpansionToolTips:YES to allow them to be shown.

NSTableView now has a method for registering (or associating) additional NIBs for a View Based NSTableView called -registerNib:forIdentifier:. When one calls -makeViewWithIdentifier:owner: the identifier, first the table will look in its reuse queue for a view to reuse with that same identifier. If one is not found, it will then look for registered NIBs with that identifier. If a NIB is found, it is instantiated with the 'owner' passed to the method. All top-level views in the instantiated NIB are then placed in the reuse queue with their current identifier. For this reason, it is important to always set the identifier for all top-level views in a NIB that is registered with the table. The particular view with the requested 'identifier' is then returned from -makeViewWithIdentifier:owner:. For a particular identifier, if there are no available views for reuse, or no associated/registered NIBs (or none created at design time in IB), then the method -makeViewWithIdentifier:owner: will return nil. A single NIB can be registered multiple times with different identifiers; this should be done for each top-level view that is in the NIB. A corresponding method, -registeredNibsByIdentifier, will return an NSDictionary with all registered NIBs. The key in the dictionary will be the identifier, and the value will be the NIB that has been registered. To unregister a NIB, pass 'nil' in for the NIB in -registerNib:forIdentifier:.

NSOutlineView

The following methods now support being animated via the -animator proxy: -expandItem:, -expandItem:expandChildren:, -collapseItem:, and -collapseItem:collapseChildren:. As an example, to animate the expansion of a particular item: [[outlineView animator] expandItem:item];

Prior to 10.8, calling -reloadItem: (or -reloadItem:reloadChildren:) on a NSView Based NSOutlineView may not have reloaded the disclosure triangle button if the row changed from being expandable to not being expandable (or vice-versa).


NSButton / NSButtonCell

For applications linked on 10.8, NSButton and NSButtonCell now use the Lion API drawFocusRingMask and focusRingMaskBounds to do its focus ring drawing. Previously, calling setShowsFirstResponder:YES on NSButtonCell and manually drawing the cell would include the focus ring. On 10.8 this is no longer the case, the cell no longer explicitly draws the focus ring. To customize the focus ring, override NSButtonCell's implementation of drawFocusRingMaskWithFrame:inView:.

An NSButton configured as a radio button (with the -buttonType set to NSRadioButton), will now operate in a radio button group for applications linked on 10.8 and later. To have the button work in a radio group, use the same -action for each NSButton instance, and have the same superview for each button. When these conditions are met, checking one button (by changing the -state to 1), will uncheck all other buttons (by setting their -state to 0).

NSInterfaceStyle

NSInterfaceStyle is now deprecated. The methods have not been used in AppKit in a while.

All the "mnemonic" set of methods are now deprecated. IE, in NSButtonCell: setTitleWithMnemonic:, setAlternateTitleWithMnemonic:, setAlternateMnemonicLocation:, alternateMnemonicLocation, and alternateMnemonic. The methods that take a title will still call setTitle: with the ampersand stripped from the parameter. Under MacOS, these methods have historically not been used and did nothing.



Zooming ScrollView

NSScrollView now has built-in magnification support. You must opt-in by setting the allowsMagnification property to YES. When the user performs a pinch gesture, NSScrollView will magnify, centering around the mouse location. When the user performs a smart zoom gesture (2-finger double tap on trackpads), NSScrollView will attempt to intelligently magnify the content under the cursor. However, NSScrollView needs help from your document view to determine your content layout. See the Smart Zoom section below for more details.
/* Allow the user to magnify the scrollview. */
@property BOOL allowsMagnification;
/* This value determines how much the content is currently scaled. To animate the magnification, use the object's animator.
The default value is 1.0.
*/
@property CGFloat magnification;
/* This value determines how large the content can be magnified. It must be greater than or equal to the minimum magnification.
The default value is 4.0.
*/
@property CGFloat maxMagnification;
/* This value determines how small the content can be magnified. The default value is 0.25.
*/
@property CGFloat minMagnification;
/* Magnify content view proportionally such that the entire rect (in content view space) fits centered in the scroll view.
The resulting magnification value is clipped to the minMagnification and maxMagnification values. To animate the magnification,
use the object's animator.
*/
- (void)magnifyToFitRect:(NSRect)rect;
/* Scale the content view such that the passed in point (in content view space) remains at the same screen location once the scaling
is completed. The resulting magnification value is clipped to the minMagnification and maxMagnification values. To animate the
magnification, use the object's animator.
*/
- (void)setMagnification:(CGFloat)magnification centeredAtPoint:(NSPoint)point;
The magnification property is observable. There are also notifications that will inform you when the magnification property is being changed due to user action. This may be due to the user performing a pinch gesture or a smart zoom gesture. When animating the magnification value yourself via the object's animator, these notifications are not sent.
/* This notification is sent at the beginning of a magnify gesture. The notification object is the scroll view performing the magnification.
*/
NSString *NSScrollViewWillStartLiveMagnifyNotification;
/* This notification is sent at the end of magnify gesture. The notification object is the scroll view view performing the magnification.
*/
NSString *NSScrollViewDidEndLiveMagnifyNotification;

Smart Magnification

There is a new NSEvent type for the smart zoom gesture (2-finger double tap on trackpads) along with a corresponding NSResponder method. In response to this event, you should intelligently magnify the content.
NSEventTypeSmartMagnify
- (void)smartMagnifyWithEvent:(NSEvent *)event;
NSEventTypeSmartMagnify is limited to 64 bit only.

Better yet, let NSScrollView perform the intelligence for you. In your NSScrollView document view, implement the following method to provide NSScrollView with semantic layout information of your content.
/* Return the complete rect of the most appropriate content grouping at the specified location. For example, if your content
is divided into three columns, return the entire rect of the column that contains the location. NSScrollView will attempt
to magnify such that the smaller dimension fits inside the scroll view while remaining within the
minMagnification, maxMagnification range.
   If your content layout is sub-divided further than one level deep (for example, two boxes that each contain multiple text
boxes), then use the visibleRect parameter to determine when to provide the rect of a sub-grouping. Always return a rect
for the appropriate grouping. If there is no deeper content grouping, return the rect for the deepest grouping. NSScrollView
will determine when to pan, magnify in, and magnify out.
   Return NSZeroRect for the default behavior.
*/
- (NSRect)rectForSmartMagnificationAtPoint:(NSPoint)location inRect:(NSRect)visibleRect;


Quick Look Gesture

There is now API to receive the Quick Look gesture (also known as the dictionary look up gesture). The user can perform this gesture with a three finger single tap, or the appropriate keyboard shortcut (Cmd-Ctl-D by default). There are two new NSResponder methods: an event method and an action method.
/* Perform a Quick Look on the content at location in the event. If there are no Quick Look items at the location, call super.
Also, see quickLookPreviewItems: further below.
*/
- (void)quickLookWithEvent:(NSEvent *)event;
/* Perform a Quick Look on the text cursor position, selection, or whatever is appropriate for your view. If there are no Quick Look
items, then call [[self nextResponder] tryToPerform:_cmd with:sender]; to pass the request up the responder chain. Eventually
AppKit will attempt to perform a dictionary look up. Also see quickLookWithEvent: above.
*/
- (void)quickLookPreviewItems:(id)sender;
To support the new event responder method, there is also a new NSEventType, NSEventTypeQuickLook. The only valid properties of and NSEventTypeQuickLook event are: -locationInWindow and -modifiers. A quick look event does not come in through the normal event mechanism, therefore there is no corresponding event mask for it, nor should you attempt to look for it in sendEvent: or with nextEventMatchingMask:… methods.

NOTE: NSApplication's default implementation of quickLookPreviewItems:, will perform a dictionary lookup. NOTE: There is a bug that causes the quickLoop responder methods to jump from NSWindow directly to NSApp bypassing NSWindowController, NSDocument, and NSAppDelegate.


NSPageController

NSPageController is a new class in 10.8 used for controlling swipe navigation and animations between views or view content.  It is useful for user interfaces which control multiple pages as in a book or a web browser history. NSPageController has a delegate that can be used to manage a predefined array of content via NSViewControllers, or used to manage a history of content snapshots built up due to user navigation.

NSPageController inherits from NSViewController. You must assign the view property to a view in your view hierarchy. NSPageController does not vend a view. NSPageController does insert itself into the responder chain.

Conceptually, NSPageController manages swiping between an array of pages (arrangedObjects). Using the selectedIndex, you can determine how many pages forward or backward the user may navigate.

There are two modes that NSPageController may operate in, History and Custom. The main difference between the two modes is that History mode expects pageController.view to be the content and Custom mode expects pageController.view to be be a container for the content that you will supply by returning viewControllers in your delegate methods.

NSPageController History Mode aka Non View Controller Mode

History mode is designed to be the easiest way to create a history UI. NSPageController will manage the history (arrangedObjects), snapshots and user navigation between pages in the history.

As the user navigates to new content, add to the history by calling navigateForwardToObject:. NSPageController will remove any arrangedObjects after the selectedIndex and then add object to the end of the arrangedObjects (and update the selectedIndex). Just like navigating in a new direction in a web browser, all forward history is lost once the user starts navigating a new path. After returning from navigateForwardToObject: you are free to update the contents of pageController.view.

During swiping, the following optional delegate methods are called in the following order:
- (void)pageControllerWillStartLiveTransition:(NSPageController *)pageController;
This is a good time to stash information that you need to restore, such a scroll positions. After returning from the above delegate method, pageController.view is hidden. In its place NSPageController shows a private view hierarchy to animate previously taken snapshots of the page history. This allows NSPageController to remain responsive to the user without any work on your part.
- (void)pageController:(NSPageController *)pageController didTransitionToObject:(id)object;
Called after a physically successful swipe, but before the animation has completed. The supplied object is the page the user navigated to -- the new selectedIndex object in arrangedObjects. If you need to start some background loading tasks, now is the time to do it, however, do not block the main thread or the animation will stutter or pause.
- (void)pageControllerDidEndLiveTransition:(NSPageController *)pageController;
The swipe and all animations are completed. Restore any scrolls setting or anything else you need to do. The pageController.view is still hidden at this point and you must call -completeTransition on NSPageController to tell NSPageController to hide the private transition view and show pageController.view. Often you do this immediately, however, if your content is not ready you can call this at a later date. See "Completing the Transition" below.

NSPageController Custom Mode aka View Controller Mode

Custom mode is designed to give you more control over the swiping process and to facilitate more UI designs than just history. You can even use custom mode to create a history UI. In this mode, pageController.view is a container view and the content views are vended by viewControllers supplied by your delegate. (Tip: Draw your background in this view.) To enable custom mode, you must implement the following two methods in your delegate:
- (NSString *)pageController:(NSPageController *)pageController identifierForObject:(id)object;
- (NSViewController *)pageController:(NSPageController *)pageController viewControllerForIdentifier:(NSString *)identifier;
NSPageController caches the view controllers supplied for each identifier and only asks its delegate to create more if one does not already exists in its cache (this is very similar to how view based NSTableViews work.) If you have different types of views you want to swipe in, then supply a different identifier for each type.

When needed, you will be asked to prepare a viewController with a page via the following optional delegate method. If you do not implement this method, then the representedObject of viewController is set to object.
- (void)pageController:(NSPageController *)pageController prepareViewController:(NSViewController *)viewController withObject:(id)object;
You will be asked to prepare a viewController with a nil object for each unique identifier it encounters. NSPageController will use this to generate a default snapshot for that identifier.

Generally, when using custom mode, the set of pages are known and it is your responsibility to set the arrangedObjects and initial selectedIndex.

During swiping, the following optional delegate methods are called int the following order:
- (void)pageControllerWillStartLiveTransition:(NSPageController *)pageController;
This is a good time to stash information that you need to restore, such a scroll positions. After returning from the above delegate method, NSPageController takes a snapshot of the selectedViewController.view and then removes it from pageController.view. NSPageController replaces it with a private view hierarchy to animate previously taken snapshots. Unlike when building up a history, snapshots may not yet exist for the page being navigated to. In this case, a previously gathered default snapshot is used for that page's identifier. Regardless if using a default snapshot, or a previously gathered snapshot of actual contents, a viewController is prepared for the page being navigated to. This viewController.view is then asked to draw on a background thread while swiping continues (note, the view is currently windowless). Once the background threaded drawing completes, the initial snapshot is replaced with the newly generated snapshot.
 - (void)pageController:(NSPageController *)pageController didTransitionToObject:(id)object;
Called after a physically successful swipe, but before the animation has completed. The supplied object is the page the user navigated to -- the new selectedIndex object in arrangedObjects. Important, the pageController.selectedViewController has not been updated yet. In custom mode, this delegate method isn't all that interesting, however, If you need to start some background loading tasks, now is the time to do it. Do not block the main thread or the animation will stutter or pause.
- (void)pageControllerDidEndLiveTransition:(NSPageController *)pageController;
The swipe and all swipe animations are completed. The selectedViewController.view is still detached at this point and you must call -completeTransition on NSPageController to tell NSPageController to hide the private transition view and update the selectedViewController. Often you do this immediately, however, if your content is not ready you can call this at a later date. See "Completing the Transition" below.

NSPageController Completing the Transition

As discussed above, NSPageController uses a private view hierarchy during swiping. To create a seamless transition to the new content, it is your responsibility to inform NSPageController when you are ready to draw the new content. Ideally, the new content should match the snapshot and the user is none the wiser. You tell NSPageController to complete the transition by calling -completeTransition. If needed, a viewController is prepared and then the contentView is shown (or added) to the view hierarchy and the private transition view is hidden.

During NSPageController initiated animations, -pageControllerWillStartLiveTransition and -pageControllerDidEndLiveTransition are called on the delegate. Generally during pageControllerDidEndLiveTransition you will call -completeTransition. Programatic animations via the animator proxy do not call the delegate methods and you are responsible for calling -completeTransition when the animation completes.This is easily done via a completion handler on an NSAnimationContext grouping. (see below)

To instantly change the selectedIndex:
  pageController.selectedIndex = newIndex;
To animate a selectedIndex change:
  [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[[pageController animator] setSelectedIndex:newIndex];
} completionHandler:^{
[pageController completeTransition];
}];

NSPageController Transition Styles

There are three transition animation styles. These transition styles are independent of the delegate mode. It is perfectly reasonable to create a history style UI using the viewController (custom mode) delegate methods. Simply set the transition style appropriately.

NSPageControllerTransitionStyleStackHistory - Pages are stacked on top of each other. Pages animate out to the right to reveal the previous page. Next pages animate in from the right. (See Safari as an example)

NSPageControllerTransitionStyleStackBook - Pages are stacked on top of each other. Pages animate out to the left to reveal the next page. Previous pages animate in from the left. (See Preview as an example)

NSPageControllerTransitionStyleHorizontalStrip - Pages are laid out next to each other in one long horizontal strip

The best way to think about the two stack transition styles is to think about the way the pages are stacked on top of each other. In a Book, page 1 is first. Page 2 and subsequent pages are stacked underneath page one. When navigating forward, the user swipes the current page out to reveal the next page. When navigating backward, the user swipes the previous page in and on top of the current page. In History, the pages are stacked over time in reverse order. That is, page 2 is stacked on top of page 1 and page 3 is stacked on top of page 2, etc… When navigating backward, the user swipes the current page out to reveal the previous page. When navigating forward, the user swipes the next page in and on top of the current page.

NSPageController Layer Backed Mode

When using the Custom mode, if pageController.view is layer backed, live layers are used during transition instead of snapshots.

NSPageController Notes

NSTableView is not thread safe! Do not tell NSTableView to reload its data while it is drawing on a second thread or it will crash. When using history mode, do not wire pageController.view to an NSSplitView subview. This will actually cause a 3rd split when the private transition view is put in the view hierarchy. You can work around this by using an empty NSView as the NSSplitView subview and placing the real contents in a subview of the empty view.


Smooth Scrolling

Smooth scrolling has been removed from user preferences. A consequence of this is that the default name has changed. Any use of @"AppleScrollAnimationEnabled" should be replaced with @"NSScrollAnimationEnabled". If you need to determine the default state of smooth scrolling, the following code will return the correct BOOL value
  [[NSUserDefaults standardUserDefaults] boolForKey:@"NSScrollAnimationEnabled"];

Accelerated Scrolling

Similar to iOS, on the third consecutive fast scroll gesture, the scrollingDelta of the scrollWheel event is automatically accelerated until either the user takes too long between physical scrollWheel gestures or the user starts to scroll slowly during another accelerated scrollWheel gesture.



Changes in Loading Nib Files

To support new features in localization, some nib loading paths in AppKit no longer use the deprecated loadNibFile:externalNameTable:withZone: method. If your application uses certain alternative localization techniques that depend on this method being called, and is linked with the Mountain Lion SDK or later, then localized resources may not be loaded correctly. You should switch to the new localization features that are part of Mountain Lion.



Auto Layout View Priorities

In 10.8, the NSView methods setContentHuggingPriority:forOrientation: and setContentCompressionResistancePriority:forOrientation: now raise an exception if the priority is zero or less, or more than NSLayoutPriorityRequired. In 10.7 and earlier, these values were silently accepted but could’ve later led to a crash.

Auto Layout NSSplitView improvements

In 10.8, NSSplitView properly respects constraints applied to its subviews, such as their minimum view widths. There are also new APIs for controlling the holding priorities, which determine both the NSLayoutPriority at which a split view holds its sizes and also which views change size if the split view itself grows or shrinks.
- (NSLayoutPriority)holdingPriorityForSubviewAtIndex:(NSInteger)subviewIndex;
- (void)setHoldingPriority:(NSLayoutPriority)priority forSubviewAtIndex:(NSInteger)subviewIndex;
In order to take advantage of these improvements, you must NOT implement any of the following NSSplitViewDelegate methods:
splitView:constrainMinCoordinate:ofSubviewAt:
splitView:constrainMaxCoordinate:ofSubviewAt:
splitView:resizeSubviewsWithOldSize:
splitView:shouldAdjustSizeOfSubview:
These methods are incompatible with auto layout. You can typically achieve their effects and more with auto layout.

Auto Layout NSTextField wrapping improvements

In 10.8, NSTextField has some new APIs to allow better support for wrapping text fields under auto layout.
- (void)setPreferredMaxLayoutWidth:(CGFloat)width;
- (CGFloat)preferredMaxLayoutWidth;
When you ask a wrapping text field for its size, by default it lays out as if it has infinite width available, i.e. in one long line (assuming it has no line break characters). Its intrinsicContentSize is the same: wide and short. However, if you set a preferred maximum layout width, the text field then measures itself as if it were confined to a rect of that width (and infinite height). Under auto layout, assuming no other constraints determine its size, its width will not exceed its preferred max, and its height will be sufficient to show all the text contents.

A common pattern is to have a wrapping NSTextField whose width is some constant determined by other constraints, and whose height should be sufficient to show the full text at that width. That can be achieved with the following pattern:
[[textField window] layoutIfNeeded];
[textField setPreferredMaxLayoutWidth:[textField alignmentRectForFrame:[textField frame]].width];
This works as long as the text field view width is not dynamic.

Auto Layout NSControl alignment rects

In Lion, the alignment rects, baselines, and intrinsic content sizes reported by many controls were slightly off. In 10.8, these have been corrected. In particular, NSPopUpButton has been modified to report an intrinsicContentSize with significantly less extra padding.

AppKit has historically resisted changing a control's reported cellSize for binary compatibility reasons, even if the underlying artwork changes. The intrinsicContentSize is intended to always be correct, and can be used as a "better sizeToFit" even when not using auto layout, like so:
  NSRect alignmentFrame = (NSRect){NSZeroPoint, [control intrinsicContentSize]};
[control setFrame:[control frameForAlignmentRect:alignmentFrame]];

Be aware that this is only sensible for controls that have both a natural width and height, like NSButtons. Controls that are fully flexible (such as NSSlider) will report NSViewNoIntrinsicMetric for their width and/or height, and you should set those dimensions explicitly.

Auto Layout and Non-Finite Frames

Under auto layout in 10.7, if a view returns YES from translatesAutoresizingMaskToConstraints and that view has a non-finite frame size or origin (such as infinity or NaN), an exception would be raised when creating the corresponding constraints. When not using auto layout, the frame has historically been silently tolerated (though the view would not appear). In 10.8, auto layout now logs a warning and flushes all non-finite frames to zero, for better compatibility with the non-auto layout behavior.

Of course, all non-finite view frames are bugs. To assist in tracking these down, a new user default NSViewRaiseOnInvalidFrames is provided. If this is set to YES, then both setFrameSize: and setFrameOrigin: (and by extension, setFrameSize:) will raise an exception if their parameters are non-finite.



NSSharingService

NSSharingService is a new class in 10.8 that can be used to share items to different kinds of local and remote services. Items are objects which respond to the NSPasteboardWriting protocol, like NSURL, NSImage or NSString. Please refer to the interface in <AppKit/NSSharingService.h> for the API.

If an NSURL is a file URL (pointing to a video for example), then the content of the file will be shared. If the URL is remote, then the URL itself will be shared.


Sharing service integration for NSTextView

The default NSTextView contextual menu contains sharing service items relevant for the current selection.

In addition to the contextual menu support, -[NSTextView orderFrontSharingServicePicker:] action method provides support for the sharing service picker facility provided by NSSharingService. A new NSTextViewDelegate method, -textView:willShowSharingServicePicker:forItems:, can be implemented by the delegate to further customize the picker behavior using NSSharingServicePicker.


ARC Weak References

Starting in 10.8, instances of NSWindow, NSWindowController, and NSViewController can be pointed to by ARC weak references.


NSApplication

If an application is launched because a user selected a notification in the Notification Center, NSApplicationLaunchUserNotificationKey will be present in the userInfo dictionary of NSApplicationDidFinishLaunchingNotification. Its value is an NSUserNotification object.
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
NSUserNotification *launchNotification = [[notification userInfo]
objectForKey:NSApplicationLaunchUserNotificationKey];
if (launchNotification) {
// application was launched by a user selection from Notification Center
}
}
NSApplicationLaunchUserNotificationKey has replaced NSApplicationRemoteNotificationKey, which was introduced in Lion but is deprecated in Mountain Lion.


Full Screen

We now allow a fullscreen window to occupy an external display. This allows users to specify which display should contain the fullscreen window by first moving the non-fullscreen window to the desired display. When a fullscreen window is on an external display, other displays are covered in linen shield windows, as before. The menu bar can be shown by mousing up to the top of the external display. The Dock is autohidden, and remains on whatever display edge is specified in user preferences. If your application implements a custom fullscreen transition, using window:startCustomAnimationToEnterFullScreenWithDuration:, you should choose the destination screen for the fullscreen window by finding the screen containing the majority of the window prior to the transition.

Much of the underlying Spaces architecture has changed in 10.8. This should have no effect on your application if you are using the NSWorkspace and NSWindow API, but you may notice changes if you have assumptions about timing of when the current space changes during a fullscreen transition, for example.



Using NSSavePanel / NSOpenPanel API in an App Sandbox

When you enable app sandboxing for your application (by checking "Enable App Sandboxing" for a target in Xcode), any calls to NSSavePanel and NSOpenPanel objects are redirected to a separate process which displays the Save/Open panel. This process is pboxd (aka "Powerbox").

Sandboxed NSSavePanel and NSOpenPanels were introduced in Lion. There are no new API changes in Mountain Lion, however the internals were worked on extensively to add support for iCloud and to resolve various usage and performance issues. The notes here are tips for developer usage.

A Note on Deprecated APIs:

Subtle aspects of deprecated methods in NSSavePanel and NSOpenPanel will not work when your app is sandboxed. Replacement methods have been available for a while, Changing your code to use the supported APIs will not prevent you from disabling the app sandboxing in the future. Now would be a good time to heed the warnings the compiler has been giving you.

Class Hierarchy:

As described in : The App Sandbox Design Guide

The declared (compile-time) class hierarchy for NSOpenPanel and NSSavePanel appears the same for sandboxed and non-sandboxed applications, but the actual (run-time) class hierarchy changes. Early versions of the App Sandbox Design Guide documentation were incorrect. Here is a (correct) recap:

The declared hierarchy looks like this:
NSOpenPanel : NSSavePanel : NSPanel : NSWindow : NSResponder : NSObject
The actual (run-time) hierarchy looks like this:
NSOpenPanel : NSSavePanel : NSObject
if you or anyone to whom you pass an instance of an NSOpenPanel or NSSavePanel assumes that the methods of NSPanel, NSWindow, or NSResponder are available through that instance — your app may encounter a method-not-found exception.

Availability of NSWindow APIs on an instance of NSOpenPanel and NSSavePanel:

In Mountain Lion, in a sandboxed app, you can treat an instance of NSSavePanel or NSOpenPanel as if it were subclassed from NSPanel during three well-defined spans of time: Delegate method calls, panel completion blocks, and accessory view action method calls.

Restricted File Access affects Delegate Method support:

As described in the "Powerbox and File System Access Outside of Your Container" section of the The App Sandbox Design Guide, a sandboxed app has restricted access to the file system unless and until the user specifies otherwise on a file-by-file basis, or your application has extra entitlements to files. The NSOpenPanel and NSSavePanel delegate methods behave the same under App Sandboxing, however what you can do with the URLs supplied by those methods has changed. The URLs passed to these delegate methods cannot be used to obtain information about the objects to which the URLs refer. This means not only that file contents are inaccessible but also object meta-data is inaccessible. In other words, about all you can do with these URLs is analyze them as (standardized, structured) strings.

NSOpenPanel and NSSavePanel are not intended for use within a Sandboxed Application's container directory. The Application container directory is an abstraction which an end user is unlikely to be able to find with Finder. Therefore an Application container directory should not be presented to an end user in the open and save panels either. If your application attempts to set the initial directory of an open or save panel to the Application container directory (or a sub-directory therein), the code in pboxd detects this and redirects the panel to the user's home directory.

Titlebar Renaming

There is no public API for Titlebar Renaming outside of NSDocument. You may note that even if your application is not app sandboxed, an instance of pboxd (aka "Powerbox") is running while a Titlebar Renaming session is in progress. Titlebar Renaming uses pboxd even in non-sandboxed applications.



Changes to Resume for Applications

In order to improve system startup performance, applications which were hidden at time of logout will by default only be partially launched when resuming after a reboot. Such partially relaunched applications use much less system resources. When the user activates an application launched this way, it will be fully restored and behave normally.

If this behavior is undesirable for your application (for example, a mail application may be checking for mail and displaying a count of unread messages in its dock icon even if the user isn't interacting with it), you can use the Automatic Termination APIs to provide information to the system about what your app is doing, allowing it to make more appropriate choices. If an automatic-termination-aware application disables automatic termination via the -[NSProcessInfo disableAutomaticTermination:] API, the OS will assume that it's in the middle of something that should be resumed, and will fully relaunch it on restart. Once automatic termination is reenabled via -[NSProcessInfo enableAutomaticTermination:], the OS will resume deciding between full, partial, or no relaunch to minimize startup time without violating the user's expectations.

Adopt Automatic Termination

Automatic Termination is a facility introduced in OS X Lion. It allows underlying processes to be terminated by the system automatically, independent of the running state of the application itself. We intend to rely on Automatic Termination even more in the future, and encourage you to adopt it in your applications. You can do this by opt'ing into Automatic Termination via the Info.plist entry NSSupportsAutomaticTermination (or calling -[NSProcessInfo setAutomaticTerminationSupportEnabled:YES] early on), and then calling -[NSProcessInfo disableAutomaticTermination:] and -[NSProcessInfo enableAutomaticTermination:] as needed.



NSTextAlternatives

NSTextAlternatives is a new immutable value class that stores a list of alternatives for a piece of text and communicates selection of an alternative via a notification. NSTextAlternatives instances are attached to attributed strings as the value of a new attribute, NSTextAlternativesAttributeName.

In 10.8 the use case for NSTextAlternatives is dictation, but this class is not strictly tied to dictation and may be used for other purposes in future releases.

The class has two attributes:  primaryString (representing the text that was actually chosen and used in the input string), and alternativeStrings (representing the alternative possible interpretations that the user might select instead).  These are separate because the text system needs to deal with them separately, using the primaryString to make sure that the text is still in the same state as when it was entered, and presenting the alternativeStrings via UI similar to that currently used for correction alternatives.

When the user picks one of the alternative strings, the text view calls -noteSelectedAlternativeString: on the NSTextAlternatives object.  Subclasses of NSTextAlternatives may do what they like in this method, but the base class implementation sends a notification NSTextAlternativesSelectedAlternativeStringNotification, with the selected alternative string in the user info under the key @"NSAlternativeString". This way arbitrary objects can listen for user selections of alternative strings.


Additional API for Spellchecking

There is an additional method on NSSpellChecker:
- (NSString *)languageForWordRange:(NSRange)range
inString:(NSString *)string
orthography:(NSOrthography *)orthography;
Clients who have an NSOrthography from NSTextCheckingTypeOrthography checking and wish to determine a specific language from it for a particular word, for example to pass in to -guessesForWordRange:inString:language:inSpellDocumentWithTag:, -correctionForWordRange:inString:language:inSpellDocumentWithTag:, or -completionsForPartialWordRange:inString:language:inSpellDocumentWithTag:, should use this method to obtain it. This method is publicly documented in 10.8 but is available back to 10.7.



NSImage

In 10.8 we've added API to NSImage and NSCustomImageRep allowing clients to delegate drawing to a block:
+[NSImage imageWithSize:flipped:drawingHandler:]
-[NSCustomImageRep initWithSize:flipped:drawingHandler:]
-[NSCustomImageRep drawingHandler]
These APIs make it easy to create an image (or custom image rep) that delegates its drawing to a block. This block is invoked at draw time, allowing drawing to be tweaked as appropriate for the destination context pixel density, color space, and other properties. This API can replace many (but not all) existing uses of lockFocus/unlockFocus. The existing lockFocus implementation performs all drawing against a single bitmap representation. This causes problems when images are moved between displays of different pixel densities (and to a lesser extent, different colorspaces).

Like other non-bitmap image rep types, drawing is cached as appropriate for the destination context. Practically speaking, the drawingHandler block will be invoked the first time the image is drawn to a particular type of destination (1x or 2x screen, for example). Subsequent drawing operations to the same type of destination will reuse the previously generated bitmap.

When drawing, NSImage has always tried to use a representation with at least as many pixels as the destination rectangle. Many apps try to implement banners and 3 part / 9 part images by stretching an NSImage over a much larger area (usually only on a single axis). With the addition of 2x assets these apps are finding this policy displays the 2x image rep when they would prefer the 1x rep. This behavior can be changed by using the new matchesOnlyOnBestFittingAxis property on NSImage. It is still preferable to use NSDrawThreePartImage and NSDrawNinePartImage, when possible.

Since SnowLeopard it has been possible to use NSImage as the contents of a CALayer. With the recent proliferation of 2x assets, this technique is increasingly being used to simplify HIDPI adoption.  This technique does however face certain limitations:

• The contentsGravity of the layer must be kCAGravityResize, kCAGravityResizeAspect, or kCAGravityResizeAspectFill. For all other contentsGravities behavior is undefined.
• When rasterizing a resolution independent representation, or selecting from multiple representations, only the backingScaleFactor of the view hosting the layer is used. This means that any scales introduced by modifying the bounds or transform of the layer or its ancestors are ignored.

In 10.8, NSImage now offers two new methods to more explicitly deal with a layer's contentsScale. These methods are -recommendedLayerContentsScale: and layerContentsForContentsScale:. These methods can be used to account for scales present in the layer tree (for example due to the layer bounds, or a layer transform). They also work for all contentsGravities.
static void updateLayerWithImageInWindow(NSImage *image, CALayer *layer, NSWindow *window)
{
    CGFloat baseScaleFactor = [window backingScaleFactor]; // This is the scale factor of the screen we are displayed on.
    NSSize imageSize = [image size];
    CGRect layerSize = [layer bounds].size;
    CGFloat additionalLayerScaleFactor = fmax(layerSize.width / imageSize.width, layerSize.height / imageSize.height);
    CGFloat desiredScaleFactor = baseScaleFactor * additionalLayerScaleFactor; // this scale factor is appropriate
     // for the actual pixel bounds of the layer.
    CGFloat actualScaleFactor = [image recommendedLayerContentsScale: desiredScaleFactor]; // Don't use a higher scale
     // factor if the image can't provide it. If the image is resolution independent
     // the return value will be the same as the input.
    id layerContents = [image layerContentsForContentsScale:actualScaleFactor]; // Now we have an appropriately
     // sized object to use as the contents of a layer.
    [layer setContents:layerContents];
    [layer setContentsScale:actualScaleFactor];
}

NSWindow

As of 10.7.3, AppKit sends NSWindowDidChangeBackingPropertiesNotification when a window's backingScaleFactor and/or colorSpace changes. When running on a system version where this new notification is available, applications should use it instead of NSWindowDidChangeScreenProfileNotification to watch for changes to either of these backing store properties. There is also a corresponding delegate method.
NSString *NSWindowDidChangeBackingPropertiesNotification;
@protocol NSWindowDelegate <NSObject>
@optional
...
- (void)windowDidChangeBackingProperties:(NSNotification *)notification;
...
@end
The userInfo dictionary contains two keys: NSBackingPropertyOldScaleFactorKey has an NSNumber value that specifies the window's previous backingScaleFactor, and NSBackingPropertyOldColorSpaceKey has an NSColorSpace value that specifies the window's previous colorSpace . You can compare these with the backingScaleFactor and colorSpace at the time of the notification, to determine which of these two properties changed. Note it is possible for both to have changed.
NSString *NSBackingPropertyOldScaleFactorKey;
NSString *NSBackingPropertyOldColorSpaceKey;

NSView

NSView has a new override point: -viewDidChangeBackingProperties. This method provides a simple override point from which a client may apply changes to their layer hierarchy. It will be invoked when the view is added to a window, or when that window changes colorspace or backing scale factor. This method may be overridden to apply the current contentsScale to their layer tree.

Additionally, we have introduced an optional method for CALayer delegates:
- (BOOL)layer:(CALayer *)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow *)window;
When implemented, NSView will invoke this method on a layer's delegate when the view is added to the window, or the window's resolution changes. NSView does this by enumerating the layer tree attached to it and inspecting each layer delegate in turn. Note that when a layer is added to an already visible layer tree this method will *not* be invoked. If you return YES from this method, NSView will set the layer's contentScale to match the backingScaleFactor of the window it is hosted in; and mark the layer dirty using -setNeedsDisplay.


Managing NSImages with multiple representations

Taking advantage of NSImage's multiple representation capability is important for high resolution support in your apps. NSImage has facility to choose and load the appropriate representation at runtime, depending on the context in which it is being drawn. It will also reload additional representations as needed when asked to draw into different contexts.

Although NSImages with multiple representations can be created at runtime, by calling addRepresentation:, it's often much easier to load such images from files. A call such as [NSImage imageNamed:@"foo"] will create an NSImage backed with all image files named "foo" in your app bundle, for instance foo.png, foo.tiff, foo.pdf, etc. In addition, the tiff file format can contain multiple representations within a single file, eliminating the need to have multiple files. This facility has been in place since OS X 10.0.

When multiple representations are providing for a single image, it's very important that the representations all have the same intrinsic size (as returned by -size). For bitmap image files with different pixel counts this is achieved by setting the density of the image properly. Since the default density of images is 72dpi, setting the density of 2x files to 144dpi achieves this constraint. When creating artwork this is often done by specifying a resolution value in an image sizing panel; programmatically this is done by setting the size of the NSBitmapImageRep explicitly to something other than {pixelsWide, pixelsHigh}.

Starting in 10.7, NSImage added support for the iOS naming convention for high resolution images, which means [NSImage imageNamed:@"foo"] will also pick up foo@2x.png. In such cases the pixel dimensions of the 2x image need to be 2x, and NSImage will assume that the density is twice that of the 1x file.

For artwork coming from bundles other than the app bundle, the 10.7 method -[NSBundle imageForResource:] can be used to do the same combination of individual files.

Despite this multi-file support in NSImage, we don't really expect you to use it much. If you have resources foo.png and foo@2x.png in your project resources, Xcode by default is going to roll them together into a multi-representation foo.tiff that contains both during build. We recommend this approach as a way to reduce the number of files in your shipping app bundles.

Command line tool tiffutil can be used to manually combine individual bitmap image files into single tiff files.

Managing application and document icons

Xcode 4.4 provides support for combining application and document icons, which use the .icns image file format. In your project resources you provide "iconset" folders that contain the individual files which will be combined at build time into .icns files. The format for the iconset folder is described in the document "High Resolution Guidelines for OS X."

Command line tool iconutil can be used to manually combine individual bitmap files into single icns files.

Note that support for icns files with high resolution representations extends back to OS X 10.6, but not 10.5.

NSOpenGL

The NSOpenGLContext methods -setFullScreen and -setOffScreen:width:height:rowbytes: no longer do anything. This is because the functions on which they are are based (CGLSetFullscreen and CGLSetOffScreen) no longer work. Check the documentation for CGLSetFullscreen and CGLSetOffScreen for replacement functionality.



NSColor

There's a new NSColor class method to return the lighter linen color that should be used for backgrounds of pages and revealed view areas:
+ (NSColor *)underPageBackgroundColor;
There are two new NSColor methods to convert back and forth from CGColorRefs:
+ (NSColor *)colorWithCGColor:(CGColorRef)cgColor;
- (CGColorRef)CGColor;
These methods do not guarantee round-trip fidelity. -colorWithCGColor: may return nil in some cases (for instance, it's not implemented for indexed color spaces). -CGColor will never return nil, but the result may be an approximation.

Conversion of colors with -[NSColor colorUsingColorSpace:] using arbitrary spaces has been speeded up greatly to match the speed of conversion using the built-in standard color spaces.



NSProgressIndicator

The bar-style progress indicator now supports progress animation in right-to-left configuration when -[NSApplication userInterfaceLayoutDirection] returns NSUserInterfaceLayoutDirectionRightToLeft.


NSRulerView

The icon for right and left tab markers are correctly rendered in the vertical text orientation.


New gestures for NSTextView

NSTextView supports the Quick Look gesture by implementing new NSResponder APIs, -quickLookPreviewItems: and -quickLookWithEvent:. Also, the smart magnify gesture is supported through -rectForSmartMagnificationAtPoint:inRect:.


NSFont

The default fixed pitch font in Mountain Lion is Menlo 11pt. +[NSFont userFixedPitchFontOfSize:] with 0.0 point size now returns Menlo 11 instead of Monaco 10.

Starting from this release, the default use of screen fonts is discontinued. The text rendering and measurement APIs for user interface elements such as NSStringDrawing and NSCell methods no longer substitute font attributes with their screen font counterparts automatically. The default setting for -[NSLayoutManager usesScreenFonts] is now NO for applications linked on this and later releases. You can keep your applications running with Lion-behavior by setting a new NSUserDefaults key, NSFontDefaultScreenFontSubstitutionEnabled. NSFontDefaultScreenFontSubstitutionEnabled controls the overall default screen font substitution behavior. When set to YES, the screen font substitution is performed by all text APIs just as on previous releases. The default value is NO.

Applications can store the screen font setting into documents via NSUsesScreenFontsDocumentAttribute.




Notes specific to Mac OS X 10.7

Some of the major topics covered in this section include:



Automatic Reference Counting

Automatic Reference Counting (ARC) is a new feature which enables the compiler to generate calls for retain, release, and autorelease. It is available with Xcode 4.2, for Mac OS X and iOS, back to Mac OS X 10.6 and iOS 4. However, on these earlier systems the zeroing weak reference feature of ARC is not available.

On 10.7, some Cocoa classes including NSWindow, NSTextView, NSFont, and NSImage do not support zeroing weak references. Many other classes do.

Unlike garbage collection, ARC does not provide any cycle-detection or breaking features. So traditionally weak references such as delegates, outlets, or targets should remain weak by default under ARC. In most cases this can mean zeroing-weak, although in cases where the object in question is one of those that does not support zeroing weak reference (or you want to deploy on 10.6), you may have to use non-zeroing weak, which is indicated with the variable qualifier __unsafe_unretained or property attribute assign.

Note that Xcode 4.2 defaults to ARC when creating new projects, and in the WWDC seed release generates outlet declarations that are strong (in new projects as well as when adding new outlet declarations from Interface Builder). In most cases these should be changed to weak (zeroing or not) to avoid cycles that may cause leaks.


iCloud

10.7 includes APIs to enable applications to store configuration information and documents in the iCloud. The main APIs here are in a number of Foundation classes; please refer to the Foundation release notes.


Lion's Two Scroller Styles

Lion introduces new, iOS-inspired "Overlay scrollers", as well as restyled "Legacy" scrollers that provide for application compatibility, accessibility, and accommodation of user preferences. Applications should be prepared to work with either style of scroller, and for the system's preferred scroller style to change over time as the user connects/disconnects pointing devices or makes changes to Appearance, Trackpad, or Mouse scrolling preferences.

"Overlay" scrollers are composited atop the margins of an NSScrollView's contentView, instead of subtracting reserved space from the NSScrollView's potential content area. Scroll thumbs appear automatically during gesture-scrolling (as well as programmatically induced scrolling), and disappear after scrolling stops, giving users an uncluttered, content-focused UI. This automatic show/hide behavior and coordination of a horizontal and vertical scroller pair requires a managing NSScrollView, which provides the necessary logic and relationship to the scrollable content area. An NSScroller instance that's used on its own, without being managed by a parent NSScrollView, can be configured to use the Overlay scroller appearance, but will not have the associated animated show/hide behavior.

To avoid interfering with user-content interaction, simply mousing into the content area edges does not cause the Overlay scrollers to change from hidden to shown, since it's possible the user may intend mouse actions in those margins for the underlying content. Gesture-scrolling, or any keyboard equivalent or application-defined action that results in scrolling, is necessary to cause the scrollers to be shown.

Once the Overlay scrollers are shown, any mouse motion within the scrollable content area is sufficient to delay their hiding, which allows time to mouse into either scroller if desired. When the mouse enters an Overlay scroller, the scroller shows its track to aid the user in hitting the desired scroller part, and both scrollers remain pinned to visible until the mouse exits the scroller.

Overlay scrollers offer the familiar mouse-driven capabilities: By mousing into an Overlay scroller, users can drag the scroller knob (including [Option]+drag to achieve fine scrolling) and can click in the scroller track to page or jump (according to the user's "Click in the scroll bar to:" Appearance preference setting).

Application code can programmatically request showing of the Overlay scrollers using the NSScrollView's new -flashScrollers method. This may be desirable when changing a document view's size or swapping new content into the view, or to give the user a sense of the current position within the scrollable range at each step of an incremental search or similar operation. (Without the explicit -flashScrollers message at each find step, the scrollers would be pulsed to visible only when advancing to the next find result happens to cause programmatic scrolling.) -flashScrollers may be sent any number of times; AppKit coalesces the requests into smooth scroller show/hide behavior. An NSScrollView's vertical and horizontal Overlay scrollers always show and hide together. -flashScrollers is OK to send to an NSScrollView that's using the Legacy scroller style, and simply has no effect in that case.

Overlay scrollers are designed for use with gesture-scroll-capable devices. To provide compatibility for various circumstances, including users with Accessibility needs and systems with non-gesture-scroll pointing devices, AppKit also provides a "Legacy" scroller style. Legacy scrollers have been restyled to provide an appearance similar to Overlay scrollers, and no longer have scroll arrow buttons, but are otherwise functionally identical to 10.6 Aqua scrollers: They have the same metrics, have layout space reserved for them instead of being overlaid atop the contentView, and are always visible (except when the "autohidesScrollers" NSScrollView feature is used). NSScroller has a new "scrollerStyle" property that can be consulted to determine the style in use by a particular NSScroller instance. A matching setter method is tentatively provided, but should not in general be used except by NSScrollView's implementation.

The "Appearance" preference panel contains a new "Show scroll bars:" preference that enables users to choose between Overlay and Legacy scrollers. By default, the setting is "Automatically, based on input device", which leaves the decision up to AppKit based on the connected pointing device hardware, but users can also choose "When scrolling" to prefer Overlay scrollers, or "Always" to request Legacy scrollers.

When the setting is "Automatically…" and more than one pointing device is connected, the decision is based on the most capable pointing device that is found, disregarding a built-in trackpad if other pointing devices are present. The idea is that a user who connects a pointing device probably means to use it, but we also don't want to force fallback to the least common denominator when a touch-scroll capable external device is also present. A touch-scroll device that has its touch-scroll capability disabled in the user's Mouse/Trackpad preferences is treated the same as a non-capable device, but, again, the internal trackpad's configuration is disregarded when one or more external pointing devices is present. So, for example, if a user has a MacBook Pro with a Magic Mouse connected, the trackpad's touch-scroll capability can be disabled without causing fallback to Legacy scrollers, as long as the Magic Mouse has its touch-scroll capability enabled.

AppKit updates scrollers at runtime to reflect changes to the user's "Show scroll bars:" Appearance preference, and as pointing devices (including wireless devices) are connected and disconnected.

There are two cases where a particular set of scrollers are forced to the Legacy style, even if the user's preference is set to "Show scroll bars: When scrolling".

An NSScrollView's scrollers may be forced to the Legacy scroller style if AppKit detects the presence of an "accessory view" in the track areas of the NSScrollView. The zoom popup that appears in TextEdit's wrap-to-page mode is an example of such an accessory view. This technique of nudging scrollers aside to make room for additional small informational views or controls was useful when scrollers had space reserved for them separate from the content area, but isn't compatible with scrollers that sit atop the content area and disappear when not in use. Applications that want to make use of Overlay scrollers should seek alternatives to accessory views for presenting the desired functionality. Once the accessory views are removed or moved out of the scroller margins, the ScrollView will again be eligible to use Overlay scrollers.

Instances of an NSScroller subclass are, for compatibility, defaulted to the Legacy scroller style and behavior. NSScroller subclasses can declare themselves to be Overlay-scroller-compatible using the new +isCompatibleWithOverlayScrollers API, allowing customization relative to the Overlay scroller appearance and behavior. See the comments accompanying this method in NSScroller.h for usage and the complete set of compatibility requirements.



NSAnimationContext API Additions

NSAnimationContext now has a “timingFunction” property that’s analogous to the CATransaction API’s “animationTimingFunction”. Animations initiated through the “animator” proxy syntax, that do not have an explicitly specified timingFunction, will inherit the enclosing NSAnimationContext’s timingFunction if it is not nil (which is the NSAnimationContext default).

As with the existing “duration” property, changing an NSAnimationContext’s timingFunction causes the same change in the underlying CATransaction’s animationTimingFunction. Also as with the “duration” property, you may change the timingFunction any number of times within a given NSAnimationContext -beginGrouping/-endGrouping block. Changes to the timingFunction will apply to any animations that are subsequently initiated in that NSAnimationContext grouping (until the timingFunction is possibly changed again).

NSAnimationContext has also been given a new “completionHandler” block. Once set to a non-nil value, a context’s completionHandler is guaranteed to be called (on the main thread) as soon as all animations subsequently added to the current NSAnimationContext grouping have completed (or been cancelled). This API drives the underlying CATransaction "completionBlock" property (though AppKit may assign a different, intermediary completionBlock to the current CATransaction). NSAnimationContext's completionHandler firing waits for all animations to which the handler applies, independent of whether they are evaluated by AppKit or delegated to Core Animation for evaluation in the render tree. If no animations are added before the current NSAnimationContext grouping is ended (or the completionHandler is set to a different value), the handler will be invoked immediately.

Lastly, NSAnimationContext has an accompanying new +runAnimationGroup:completionHandler: class method. Its use provides for more natural syntax, letting you specify a completion block body after the set of animation actions whose completion will trigger the completion block.

The following:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
    // Start some animations.
[[myView animator] setFrameSize:newViewSize];
[[myWindow animator] setFrame:newWindowFrame display:YES];
} completionHandler:^{
    // This block will be invoked when all of the animations started above have completed or been cancelled.
NSLog(@"All done!");
}];
is semantically equivalent to:
[NSAnimationContext beginGrouping];
    [NSAnimationContext setCompletionHandler:^{
// This block will be invoked when all of the animations started below have completed or been cancelled.
NSLog(@"All done!");
    }];
    // Start some animations.
[[myView animator] setFrameSize:newViewSize];
[[myWindow animator] setFrame:newWindowFrame display:YES];
[NSAnimationContext endGrouping];
The “NSAnimationContext *context” parameter passes the thread’s current NSAnimationContext to the block as a convenience, so that code within the block that wants to change or query properties of the current context does not have to query (possibly repeatedly) for [NSAnimationContext currentContext].


NSWindow “animationBehavior” property

In 10.7, we added automatic animation of NSWindow -orderFront:/-orderOut: operations, which can be controlled using the new “animationBehavior” NSWindow property. By default, an NSWindow’s animationBehavior is set to NSWindowAnimationBehaviorDefault, which causes AppKit to determine the style of animation to use automatically based on its inference of a window’s “type” from various window properties. A window’s animationBehavior can be set to NSWindowAnimationBehaviorNone to disable Appkit's automatic animations for the window. (This may be useful if AppKit’s animation interferes with an animation that your application implements.) Or, it can be set to one of the other non-Default NSWindowAnimationBehavior values to override AppKit's automatic inference of appropriate animation behavior based on the window's apparent type.


Custom View Unarchiving and “wantsLayer”

Prior to 10.7, a Custom View being unarchived from a .nib or .xib file would always receive a -setWantsLayer: message during loading, regardless of whether the unarchived wantsLayer setting was YES or NO. Receiving this message with a parameter of NO could interfere needlessly with a wantsLayer==YES setting the view might attempt to make in its -initWithFrame: method. On 10.7, AppKit will only send -setWantsLayer: to a Custom View during unarchiving if the parameter is YES, eliminating this pitfall.



Easier Focus Ring Management

A "focus ring" is an Aqua-tinted halo that indicates a control is the active first responder that will receive key events. New NSView API in 10.7 makes it much easier to specify a focus ring shape to be shown for a custom view, while providing AppKit with the information it needs to automatically ensure consistent focus ring erasure and redraw. This new automatic focus ring model also yields correct results in layer-backed mode, which previously was constrained by the inability of focus ring drawing to extend out, reduces the need for view content redraw in many cases, and even provides a useful hint to Accessibility regarding where the user's attention is likely to be focused.
- (void)drawFocusRingMask NS_AVAILABLE_MAC(10_7);
- (NSRect)focusRingMaskBounds NS_AVAILABLE_MAC(10_7);
- (void)noteFocusRingMaskChanged NS_AVAILABLE_MAC(10_7);
A custom view that wants a focus ring to be shown when it is the active first responder, and that doesn't inherit that behavior from a superclass, needs only specify a focus ring mask shape. AppKit will automatically determine when the focus ring needs to be shown and erased (as the view becomes, and ceases to be, the active first responder -- that is, the firstResponder in the application's keyWindow), and will invoke the -focusRingMaskBounds and -drawFocusRingMask methods as needed.

NSView's implementation of -drawFocusRingMask draws nothing, and its -focusRingMaskBounds method returns an empty rectangle. To opt into the new 10.7 focus ring drawing model, a view simply overrides the -drawFocusRingMask method to draw the shape whose outline should serve as the template for the focus ring, and -focusRingMaskBounds to return the bounding box of that shape (expressed in the view's interior ("bounds") coordinate space). For example, a simple rectangular focus ring surrounding a view's frame would be requested by simply doing:
- (void)drawFocusRingMask {
NSRectFill([self bounds]);
}
- (NSRect)focusRingMaskBounds {
return [self bounds];
}
AppKit will automatically invoke these methods when appropriate, to render the view's focus ring. (Make sure the view is eligible to become its window's firstResponder, by overriding -acceptsFirstResponder to return YES.) The focus ring is rendered using the style specified by the view's focusRingType (which must not be NSFocusRingTypeNone unless you want to suppress drawing of the focus ring). An implementation of -drawFocusRingMask can assume that it is drawing in the view's interior (bounds) coordinate space, and that the fill and stroke colors have been set to an arbitrary fully opaque color. It needs only draw the desired focus ring shape (or an image or other object whose outline defines the focus ring mask).

This model provides an alternative to the previous focus ring technique, wherein a view was responsible for determining when to draw and erase its own focus ring, was required to perform its focus ring drawing explicitly as part of its -drawRect: implementation, and had to use the special -setKeyboardFocusRingNeedsDisplayInRect: method to ensure correct focus ring erasure and redraw. A view that adopts this new 10.7 focus ring API should no longer call NSSetFocusRingStyle() and perform its own focus ring drawing in -drawRect:, unless running on 10.6 or earlier, since the use of NSSetFocusRingStyle() would cause AppKit to fall back to the old focus ring drawing model for the view. Invoking -setKeyboardFocusRingNeedsDisplayInRect: is OK, but will cause more redraw than necessary on 10.7, where AppKit is able to determine the exact coverage of focus ring rendering based on the focus ring mask, and is thus able to show, hide, and move the focus ring with minimal redraw of affected window areas. In layer-backed mode, AppKit draws a focus ring that's implemented using this new 10.7 API into its own layer, which permits the focus ring to extend outside the associated view's backing layer bounds and allows the focus ring to be efficiently shown, moved, and hidden without necessitating redraw of the associated view content.

The third new method, -noteFocusRingMaskChanged, is provided for you to invoke when some state that AppKit doesn't know about (such as which of a set of non-view components of your content you consider "focused"), that affects the focus ring shape or where you'd like it to be drawn, changes. AppKit automatically invokes -noteFocusRingMaskChanged when your view receives a -setNeedsDisplayInRect: message (as happens when the view is resized or moved), and when the view's focusRingType is changed, which covers most cases. It is only necessary to invoke -noteFocusRingMaskChanged in cases where some internal state change of your own, that AppKit can't know about, affects the desired shape or position of your focus ring mask.

For the benefit of cell-based NSControls, there is a parallel set of overridable NSCell API:
- (void)drawFocusRingMaskWithFrame:(NSRect)cellFrame inView:(NSView *)controlView NS_AVAILABLE_MAC(10_7);
- (NSRect)focusRingMaskBoundsForFrame:(NSRect)cellFrame inView:(NSView *)controlView NS_AVAILABLE_MAC(10_7);
There is no NSCell equivalent to -noteFocusRingMaskChanged. When a cell determines that its focus ring mask needs updating, in a situation where the cell’s content is not otherwise being redrawn, it can invoke [[self controlView] noteFocusRingMaskChanged].

New Tiling Model for Layer-Backed Views

Handling of large views poses a fundamental challenge for a layer-backed rendering model, as it implies the need to allocate commensurately large layer backing stores that consume memory and may exceed the maximum allowable texture size of the host system's graphics hardware. Some form of tiling is necessary, to enable the needed, visible portions of a view to be buffered, while potentially large regions that aren't currently visible (e.g. parts of a document view that are scrolled out of view) are free to be unbuffered until they are made visible.
In 10.5 and 10.6, AppKit's tiling model was based on Core Animation's CATiledLayer, and assumed that the documentView of an NSScrollView, and only the documentView of an NSScrollView, should receive a tiled backing layer. This approach permitted common case of large layer-backed documentViews, but didn't address the potential for other views to exceed the maximum texture size, and came with asynchronous drawing behavior (fade-in of tiles as they were drawn) that in some cases didn't yield the right user experience.
On 10.7, AppKit uses an entirely new, synchronous tiling implementation that eliminates the visual tile-fade-in artifacts associated with the previous model, and that automatically tiles and untiles view backing layers as necessary based on the view's size, rather than being tied to the assumption that only an NSScrollView's documentView is likely to be large. A layer-backed view that is tiled will be called to draw via the usual -drawRect: mechanism to draw portions of its content to be cached into tiles. AppKit automatically manages the creation and population of tile layers, which reside under a "tile container layer" parent that AppKit adds as a sublayer of the view's normal backing layer.
The new tiling model is automatic and does not require the use of any new API, but responds to a set of user defaults that can be used to experimentally tune its parameters on a per-process basis. These user defaults should be considered private and subject to change in future releases, but the ability to adjust them might be of use if the standard values prove unsuitable for a particular application.
NSViewBackingLayerTileSize determines the tile size, and defaults to 512 (pixels on a side). Power-of-two values are recommended. (512 * 512 * 4bytes/pixel) = 1MB per tile. Larger values produce fewer tiles and reduce tile management overhead, but will tend to waste more space for views whose frames don't map to an integral number of tiles.

NSViewMaxNonTiledBackingLayerSize specifies the maximum size (in pixels) that a view's backing layer can be on either side before the view is forced to be tiled. The default is 2000 pixels.

NSViewMaxResidentTiles determines the maximum number of tile backing stores that can be allocated for the process. When AppKit reaches this limit, it begins discarding the contents of non-visible tiles to make room for new visible tiles. The default is 64.

NSAppKitBackingLayerTiling controls the new tiling model, and defaults to YES. It can be set to NO to revert to the 10.6 tiling behavior (use of CATiledLayer backing layers for NSScrollViews' documentViews).

NSDebugBackingLayerTiling can be set to YES to enable informative logging regarding tiling activity. The output could be useful in assessing and tuning performance, or when filing a bug report.


NSOpenGL and Resolution Independence

As part of 10.7's updated Resolution Independence architecture, individual views have a new means to declare their compatibility with high-resolution framebuffers. Since OpenGL is innately a pixel-oriented API and much existing OpenGL code does not function correctly when "surprised" with a high-resolution backing store, OpenGL views are run in scaled mode by default, and need to specifically opt into high-resolution backing.

The accessors for this new property are declared in a category on NSView in NSOpenGLView.h, to emphasize that their relevance is confined to OpenGL rendering, but the property is applicable to any view that renders using an associated NSOpenGLContext.
@interface NSView (NSOpenGLSurfaceResolution)
- (BOOL)wantsBestResolutionOpenGLSurface NS_AVAILABLE_MAC(10_7);
- (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag NS_AVAILABLE_MAC(10_7);
@end
The "wantsBestResolutionOpenGLSurface" property specifies whether a given view instance wants, and is capable of correctly handling, an OpenGL backing surface (framebuffer) with resolution greater than 1 pixel per point. This property is relevant only for views to which an NSOpenGLContext is bound (including, but not limited to, NSOpenGLViews); its value does not affect the behavior of other views, including CALayer-backed views (which may choose to render at a higher surface resolution independent of this property's value. For compatibility, wantsBestResolutionOpenGLSurface defaults to NO, providing a 1 pixel per point framebuffer regardless of the backing scale factor for the display the view occupies. (When the backing scale factor is > 1.0, the rendered surface contents are scaled up to the appropriate apparent size.) Setting this property to YES for a given view gives AppKit permission to allocate a higher-resolution framebuffer when appropriate for the backing scale factor and target display. AppKit may vary the surface resolution when the display mode is changed or the view is moved to a different display, but with this property set to YES it is capable of allocating a surface of greater than 1 pixel per point for the view.

To function correctly with wantsBestResolutionOpenGLSurface set to YES, a view must take care to perform correct conversions between view units and pixel units when needed. For example: The common practice of passing the width and height of [self bounds] to glViewport() will yield incorrect results (partial instead of complete coverage of the render surface) at backing scale factors other than 1.0, since the parameters to glViewport() must be expressed in pixels. Instead, use the dimensions of [self convertRectToBacking:[self bounds]], which are in appropriate (pixel) units.

The "wantsBestResolutionOpenGLSurface" property is archived (keyed archiving required).

For testing purposes only, the effect of this property can be overridden globally for all views in a process, using the "NSSurfaceResolution" user default. If NSSurfaceResolution is set to "Device", all views that have surfaces (including not only OpenGL surfaces, but layer tree render surfaces as well) will be opted into using the best resolution surface for the primary display the view is presented on. This can be used to quickly assess whether an apps view's are ready for non-1x surfaces. If NSSurfaceResolution is set to "1x", all views that have surfaces will be opted into using 1x (1 pixel per point) surfaces, independent of the display or backing scale factor. If NSSurfaceResolution is set to any other value, or no value is present for it, then wantsBestResolutionOpenGLSurface will be consulted as described above for views that perform NSOpenGL rendering, and AppKit will separately determine the appropriate resolution for other surfaces, as also described above.



NSView now passes unhandled -rightMouseDown: events up the responder chain

Prior to 10.7, NSView did not pass unhandled -rightMouseDown: events up the responder chain. On 10.7, NSView passes -rightMouseDown: up the responder chain, if AppKit doesn’t find an associated context menu to display for the view. To avoid binary compatibility issues, this new behavior is enabled only for applications linked on 10.7 or later.




Deprecation of Pixel Buffers (PBuffers)

Pixel Buffers (PBuffers) have been deprecated in 10.7. Modern clients should use Frame Buffer Objects (FBOs) instead of NSOpenGLPixelBuffer and associated methods. See GL_EXT_framebuffer_object.



Deprecation of NSOpenGLContext -setOffScreen:width:height:rowbytes:

NSOpenGLContext’s -setOffScreen:width:height:rowbytes: API should be considered deprecated as of 10.7. As noted in NSOpenGL.h, this API forces use of the software rasterizer, which is much slower than GPU rendering. It is generally much better nowadays to use a normal pixel format with either an off-screen window or an FBO (Frame Buffer Object), and then call glReadPixels() to read the rendered result back to CPU memory (if that's where it is needed).



Changes for Resolution Independence

Significant changes have been made to the underlying graphics model for resolution independence, and the behavior of Cocoa applications running under high resolution operation has been updated. Scaling of user interface content is now limited to an integral 2x scale factor, and is keyed off of per display settings rather than a global scale factor. Pixel-accurate high resolution bitmap artwork now only needs to be provided at a 2x size multiple, similar to the operation of iOS on a Retina display.

The underlying device pixel geometry of both window backing stores and screen frame buffers has been virtualized and abstracted behind new point-based coordinate spaces for NSWindow and NSScreen. The relationship between pixels and points is now driven by attributes of the display mode any given display is operating in. When a display is configured to run in one of the new "HiDPI" modes, the corresponding NSScreen object and any windows created on that display are automatically scaled with a 2:1 pixel per point backing ratio.

The implication of this change is that under high resolution operation, rectangles, sizes and positions are all unchanged from low resolution operation, and are now universally assumed to be in points. Previously, event coordinates and both the window base coordinate system and the screen global coordinate system were presumed to be in device pixels, and were in an "unscaled" space compared to view coordinates. This is no longer the case: window frame coordinates, event coordinates and view coordinates converted to window space are all expressed in points and do not change between low and high resolution operation. The relevant scaling and mapping to the display is performed automatically by the Quartz Window Manager and by the NSGraphicsContext attached to any given window.

Coordinate Space Conversion

The consequence of window and screen device virtualization is significantly enhanced application compatibility under high resolution operation, and under most circumstances, apps will not need to be aware of the underlying pixel geometry of their windows. However, circumstances can arise where applications may wish to perform pixel-accurate drawing operations to optimize or align their content under high resolution operation. To address these needs, a complete set of new coordinate space conversion methods have been introduced to go back and forth between virtual point geometry and a pixel aligned coordinate system matching the characteristics of the backing store for that object.

All of the methods below are defined on NSView, NSWindow and NSScreen.
/* New methods for converting to and from backing store pixels */
- (NSPoint)convertPointToBacking:(NSPoint)aPoint
- (NSPoint)convertPointFromBacking:(NSPoint)aPoint
- (NSSize)convertSizeToBacking:(NSSize)aSize
- (NSSize)convertSizeFromBacking:(NSSize)aSize
- (NSRect)convertRectToBacking:(NSRect)aRect
- (NSRect)convertRectFromBacking:(NSRect)aRect
In order to provide a conceptually consistent mapping between window and screen coordinates in the face of screen device virtualization, two new methods have been introduced on NSWindow. Under high resolution operation, these methods now simply offset the given rectangle based on the window's position in screen space. No scaling transform is necessary since both coordinate systems are virtual and aligned in point space.
- (NSRect)convertRectToScreen:(NSRect)aRect
- (NSRect)convertRectFromScreen:(NSRect)aRect

Deprecated Coordinate Conversion Methods

As a result of the virtualization of window and screen coordinates, the concept of the 'base' coordinate system has been deprecated, along with methods referring to that coordinate space. The following methods have now been superseded by the convertToBacking methods detailed above.
/* NSView deprecated methods */
- (NSPoint)convertPointToBase:(NSPoint)aPoint
- (NSPoint)convertPointFromBase:(NSPoint)aPoint
- (NSSize)convertSizeToBase:(NSSize)aSize
- (NSSize)convertSizeFromBase:(NSSize)aSize
- (NSRect)convertRectToBase:(NSRect)aRect
- (NSRect)convertRectFromBase:(NSRect)aRect
/* NSWindow deprecated methods */
- (NSPoint)convertBaseToScreen:(NSPoint)aPoint
- (NSPoint)convertScreenToBase:(NSPoint)aPoint

Compatibility Notes for High Resolution Operation

It is important to note that in order to maximize backwards compatibility, the deprecated NSView and NSWindow methods will retain their historic compatible and documented behaviors.

When running at @2x, the behavior of these  methods will still remain consistent with their previously documented semantics, but there will be an unavoidable break in compatibility for code which feeds pixel-based positions (e.g. resulting from -[NSView convertPointToBase:]) into -[NSWindow convertScreenFromBase:].

Application software calling -convertBaseTo/FromScreen: incorrectly will need to adopt the new routines and/or ensure that they refrain from using NSView's convertTo/FromBase routines when providing input positions, and instead use -[NSView convertPoint:To/FromView:nil] instead.  (Note: This is the way everyone did things anyway prior to 10.5 when -[NSView convertTo/FromBase:] was introduced)

Backing Aligned Drawing under High Resolution Operation

One of the most common reasons for making drawing code aware of its underlying backing pixel geometry is to perform rounding to ensure pixel aligned drawing or layout. Although NSView's -centerScanRect: has been updated to work correctly under high resolution operation, some scenarios call for tighter control over the rounding behavior of the alignment operation on each edge of a rectangle. We have introduced a new convenience method to satisfy this need, built on top of the services provided by Foundation's NSIntegralRectWithOptions() API.

This method is available on NSView, NSWindow and NSScreen.
- (NSRect)backingAlignedRect:(NSRect)aRect options:(NSAlignmentOptions)options
This method accepts rectangles in local (virtual) coordinates and ensure that the result is aligned on backing store pixel boundaries, subject to specific rounding hints given in the options argument. Foundation already provides NSIntegralRect(), which takes a rectangle and pushes each edge outward to the nearest integral boundary.  The new method is a slightly more flexible version that gives you control over how the rect is massaged into being integral, after the input rectangle is converted to the appropriate backing space.

Determining Scale Factor

The move to virtualization of screen space and event coordinates fortunately means that developers remain isolated from the hardware details of the displays they are presenting their content on.

There are some scenarios, however, where an app that is resolution-aware may want to reason on its own about the display environment it is running in, and make decisions or computations that are not effectively covered by existing API. These circumstances require an efficient way to determine the effective scale factor of the current display environment.

A new public method will be introduced that returns the effective scale relationship between virtual and physical coordinates for a given screen in it's current mode.  This factor can be thought of as the extra multiple of device pixels which the system has determined need to be used when representing user content on the given display.
/* NSScreen and NSWindow methods */
- (CGFloat)backingScaleFactor
This method is expected to return 2.0 for high resolution scaled display modes, and 1.0 for all other cases.

An identical API is needed on NSWindow to report the scale factor relationship of its backing store pixels to virtual window coordinates, since that relationship is independent of its containing screen. This method is critically useful for contexts where a window exists, but the graphics context may not be setup yet or may be ambiguous.  As an example, consider an off screen window.  The AppKit will ensure that this method will return a 'reasonable' answer depending on the available display environment.

It is important to note that this number returned by this method does not represent anything concrete, such as pixel density or physical size, since it can vary based on the configured display mode. For example, the display may be in a mirrored configuration that is still high resolution scaled, resulting in pixel geometry that may not match the native resolution of the display device.

For almost all common cases, developers should avoid keying off of or using the backingScaleFactor as an input to layout or drawing calculations. Developers should avail themselves of the backing coordinate space conversion methods instead, as the resulting code will more likely work consistently and correctly under both low and high resolution operation.


Window edge resizing

In 10.7, windows may be resized from all edges and corners, and the resize indicator has been removed. This affects all Cocoa apps, and no new API is associated with this behavior. The existing live resize support functions continue to function for all resize directions, and the best practices for maximum live resize performance are unchanged. The setShowsResizeIndicator: method is ignored and the NSWindow method showsResizeIndicator always returns NO.

The cursor also changes to indicate when a click will result in a resize operation, and in which direction. The available resize directions are determined from the minSize and maxSize of a window, and of course whether NSResizableWindowMask is set. Previously it was possible to limit resizing with the NSWindow delegate method windowWillResize:toSize:, but this approach will produce misleading cursors, so prefer to set a minSize and maxSize in addition to (or instead of) this delegate method.

NSWorkspace's NotificationCenter and Blocks

In SnowLeopard, the blocks-based API was not reliable for adding observers to the special notifications vended by NSWorkspace's notification center (such as NSWorkspaceDidTerminateApplicationNotification). Specifically, at least one observer would have to use the target - selector based API for the notification to be successfully received by the blocks-based API.

In 10.7, this has been resolved, so that NSWorkspace's notification center works with the blocks-based API in all cases.

NSWorkspace openURLs: return value

In 10.7, the NSWorkspace method - (BOOL)openURLs: withAppBundleIdentifier: options: additionalEventParamDescriptor: launchIdentifiers: will now return NO and not open anything, if the bundle identifier does not refer to an existing app. In SnowLeopard and earlier, it would return YES, ignore the bundle ID, and open the URLs with the default apps. If the bundle identifier is nil or empty, it will use the default application for the URLs (which is the same behavior on SnowLeopard and earlier).

To determine if a bundle identifier is valid, you can use the absolutePathForAppBundleWithIdentifier: method.

NSWorkspace setDesktopImageURL: now works with folders

In 10.7, you may pass a URL to a folder to the NSWorkspace desktop image API, and the desktop will cycle through the images in the folder (non-recursively). Similarly, the desktopImageURLForScreen: API may return a URL to a folder.

Self Service

Before 10.7, if an application invoked its own Service and the Service has return types, the app would deadlock until the timeout (or the user hits escape). In 10.7, an application can invoke its own Service without deadlock.


Restorable State

In 10.7, AppKit supports a way for applications to restore their state when relaunched. See the methods in NSWindowRestoration.h for more information.

Ignoring Existing Restorable State

When the user default ApplePersistenceIgnoreState is defined, existing restorable state and Untitled documents are ignored. New restorable state and Untitled document autosaves are redirected to a temporary directory, whose path will be logged to the console. This user default is intended for automated tests that want to start with a clean environment, and for debugging.

Toolbar Versioning

In 10.7, when apps add items to their default toolbar (perhaps in new versions), users with customized toolbars will have the new items automatically added.Prior to 10.7, if an application added a new item to a toolbar's default item list, that new item would not appear for users with customized toolbars. This is because the NSToolbar simply stored the list of items in the customized toolbar, so there was no way to distinguish between an item that was removed by the user, and an item that was not available when the app was last run.

In 10.7, NSToolbar records both the customized list of items and the default items. When run on a system with one or more new items in the default array, it will insert the new item into the customized toolbar. NSToolbar makes a best-effort attempt to add it at a location that reflects its position in the default array, but it will always be added somewhere.

Furthermore, if the customized toolbar is identical to the default toolbar, the customizations are no longer recorded at all, so that all changes to the default array are reflected.

These changes are automatically applied when the app is run on 10.7 (so the user must have launched the previous version of the app at least once on 10.7). No app adoption is necessary to take advantage of these enhancements.

NSWorkspace recycleURLs Changes for Volumes Without Trash

In SnowLeopard, if you use use recycleURLs: on a volume that does not support trash, such as an AFP mount, the files would not be moved and the result would be a "file not found" error. In 10.7, this method has changed to prompt the user to either delete the files or to cancel. Canceling results in NSUserCanceledError, while deleting results in the error obtained from attempting to delete the file, or no error if the file is successfully deleted.

Text and Search Fields in Toolbars

In 10.7, search fields and text fields draw slightly differently when they are within toolbars, compared to within the window's content view. The toolbar variant requires one additional point, so text and search fields in toolbars will have their height automatically increased to at least 23 points. Furthermore, if an NSToolbarItem has a search field as its view, that search field automatically has its minimum and maximum size adjusted to the system-specified standard values (currently 140 and 240 points).

Toolbar Contextual Menu Changes

In 10.7, NSToolbar no longer allows users to control the visibility priority of toolbar items, although users can still add and remove items. Furthermore, the Remove Item menu item has itself been removed, except when running with Voice Over.

Toolbar Separator and Customize items removed

In 10.7, the Customize Toolbar item and the Separator item (with the vertical dots) have been removed from toolbars and customization palettes, and their item identifiers are ignored.

Right to Left in -[NSMenu popUpMenuPositioningItem:atLocation:inView:]

In 10.7, the NSMenu method popUpMenuPositioningItem:atLocation:inView: will position the menu to the right of the passed-in point, when running in a right to left context. See the description of the method in the NSMenu.h header for more information.

NSResizableWindowMask and window title bars

Prior to 10.7, passing NSResizableWindowMask to NSWindow init methods would create the window with a titlebar, as if you had passed NSTitledWindowMask. In 10.7, this is no longer true: passing NSResizableWindowMask without NSTitledWindowMask will create a resizable window with no titlebar. For compatibility, this change only affects applications compiled on 10.7 or later, and only affects windows loaded from nibs that were saved on 10.7.

For maximum compatibility, you should of course specify NSTitledWindowMask whenever you want a window to have a titlebar.

Untitled windows at launch

As part of the restorable windows feature, the application delegate may not be asked to create an Untitled window at launch in some circumstances. This was found to cause crashes in certain apps, so these apps will maintain 10.6 behavior of more often opening Untitled windows. When these apps are recompiled on 10.7, they will acquire the 10.7 behavior. For maximum compatibility, do not depend on being asked to create an Untitled window at launch.

Rounded Popup Button Widths

In 10.7, the blue indicator region of rounded popup buttons has been removed, and so the button's content (such as its title) is allowed to consume more space. Be aware when designing on 10.7 that a rounded popup button that does not clip on 10.7 may clip when run on earlier versions.



Modernized Document Model

Mac OS 10.7 introduces a modernized document model in which:
• The user does not have to manually manage multiple copies of their document files to be able to retrieve old versions that they deemed interesting at some point in the past.
• The user is not bothered by alerts asking what to do with unsaved document changes when closing documents or terminating applications. (See other sections of this release note for changes to when applications are terminated.)
• The user does not have to know and take care to save document changes before causing the document’s file to be read by another application. For example, if the user drags a document’s icon from the Finder and drops it in a Mail message editing window to make an attachment then the attachment will always include all of the changes the user had made to the document up to that point. In other words, from the user's point of view a document as presented in an application window is simply the same thing as the document file presented by the Finder, and vice versa.

The next several sections describe changes to NSDocument that you have to know about to make your application conform to the modernized document model.

NSDocument Autosaving in Place

Mac OS 10.4 introduced to NSDocument the “autosaving” of documents to protect against the user losing work due to application crashes, kernel panics, and power failures. You can easily create an NSDocument-based application that periodically saves the current contents of a document to a file next to the actual document file in the file system and having a name like “My Great Document (Autosaved).” Autosaved content files for documents that have never been saved and so don’t yet have document files go in ~/Library/Autosave Information. NSDocument takes care of triggering periodic autosaving, choosing where autosaved document contents file go, invoking your NSDocument subclass’ code for document file writing, cleaning up autosaved document contents files when they are redundant, and reopening autosaved documents when the application is relaunched after an apparent malfunction.

Autosaving still serves the purpose of protecting against malfunction but is now also an important part of NSDocument’s implementation of Mac OS 10.7’s modernized document model. A new kind of autosaving, autosaving in place, has been added. Autosaving in place is different from the old kind of autosaving in that it overwrites the actual document file instead of writing a separate document contents file next to it in the file system. The old kind of autosaving, now called autosaving elsewhere, is still supported for backward binary compatibility and also for when the document has never been saved, so there is no document file, and autosaving requires writing to some other file. You enable autosaving in place, and a wide variety of new behaviors that follow from it, for NSDocument subclasses in your application by overriding a new class method and returning YES from your override:
+ (BOOL)autosavesInPlace;
So that your NSDocument subclass' overrides of -save… and write… methods can distinguish between the two kinds of autosaving the NSAutosaveOperation enumerator that was published in Mac OS 10.4 has been renamed and a new one has been added:
    NSAutosaveOperation = 3,
NSAutosaveElsewhereOperation = 3,
    NSAutosaveInPlaceOperation = 4
In Mac OS 10.4 through Mac OS 10.6 “autosaving” was a misnomer because it was not synonymous with “automatic saving,” which would be the writing of a document’s current documents to the document’s file and the updating of NSDocument attributes that has to be done when that happens. In Mac OS 10.7 NSDocument's new autosaving in place really is automatic saving.

Autosaving in place can sometimes result in the user changing document files that they did not really mean to change. To protect against that, NSDocument does autosaving safety checking when the user changes the document. Examples are checking for documents that are old (does the user really want to edit last year's tax return?) and checking for documents that are in folders where the user typically does not edit documents (does the user really want to edit something they just downloaded?). You can override a new method to customize this checking:
- (BOOL)checkAutosavingSafetyAndReturnError:(NSError **)outError;
See the comments in <AppKit/NSDocument.h> for details.

NSDocument Autosaving Changes

In Mac OS 10.7 the manner in which NSDocument triggers periodic autosaving for crash protection is more complicated than simply setting a timer when the user changes the document. There is no way to customize this behavior other than using the existing -setAutosavingDelay: and -autosavingDelay methods in NSDocumentController but there is a new NSDocument method you can override to implement your own behavior:
- (void)scheduleAutosaving;
This method is invoked by -updateChangeCount: and -updateChangeCountWithToken:forSaveOperation:.

Turning on autosaving in place in your NSDocument-based application changes what NSDocument does when the user closes the document and there are unsaved changes. Instead of presenting an alert asking the user what to do with the changes, NSDocument simply autosaves the document before it is closed. That autosaving is not done by invoking the existing -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo: method. Instead it is done by invoking a new method:
- (void)autosaveWithImplicitCancellability:(BOOL)autosavingIsImplicitlyCancellable
completionHandler:(void (^)(NSError *errorOrNil))completionHandler;
See the comments in <AppKit/NSDocument.h> for details.

Things to Watch For When Enabling NSDocument Autosaving in Place

Enabling autosaving in place in your application may require other changes to your NSDocument subclass. Here are some things to watch for:

• Turning on asynchronous saving

Because autosaving happens periodically, at unpredictable times while the user is editing the document, user interface delays during saving are less acceptable than they were. When you turn on autosaving in place you should strongly consider also turning on asynchronous saving. See the next section.

• Invoking and overriding deprecated methods

Over the years many NSDocument methods have been deprecated. Notably, in Mac OS 10.4 new methods that take NSURLs and return NSErrors replaced methods that do not. You should update your application to stop invoking and overriding the replaced methods. They are listed at the bottom of <AppKit/NSDocument.h>.

• Overriding -revertToContentsOfURL:ofType:error:

When you enable autosaving in place you also enable version preserving. (Unless you also override +preservesVersions to turn version preserving off.) With autosaving in place enabled it's more important than ever to invoke super when you override -revertToContentsOfURL:ofType:error:, because it's NSDocument's default implementation of that method that updates the document's state to reflect what happens during reverting to an old version. If you don't, NSDocument might present the user with alerts about the document having been changed by another application when that is not the case.

Your override of -revertToContentsOfURL:ofType:error: may now be passed a URL that is not the same as the document's current file URL, and a file type that is not the same as the document's current file type.

• Overriding the various -write… methods

During duplicating and reverting NSDocument may cause the current version of the document, the one the user is looking at at that moment, to be preserved. Doing this sometimes requires that the document's current contents be written to disk. NSDocument does not invoke any of the -save… methods in that case. Instead, it invokes -writeSafelyToURL:ofType:forSaveOperation:error:, passing it a URL to a file that will become the preserved version, the file type that [self autosavingFileType] returns, and NSAutosaveElsewhereOperation. Your NSDocument subclass must write the entire contents of the document to that file. You will probably not notice this if you are not overriding -writeSafelyToURL:ofType:forSaveOperation:error:, you are correctly overriding one of the simple writing methods, which are -writeToURL:ofType:error:, -fileWrapperOfType:error:, and -dataOfType:error:, and if -autosavingFileType always returns an appropriate value.

Simple duplicating may cause the same kind of writing to be done. In that case the file that is written will be used as the already-autosaved contents of the new document.

• Overriding -keepBackupFile

You should not override -keepBackupFile in such a way that it returns YES when +autosavesInPlace returns YES.

• Overriding -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo:

When autosaving in place is enabled not all autosaving is done by an invocation of -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo:, so it is not a good method to override to customize all kinds of autosaving. Consider overriding -saveToURL:ofType:forSaveOperation:completionHandler: and checking the passed-in save operation type instead.

• Overriding -validateUserInterfaceItem: and -validateMenuItem:

Enabling autosaving in place adds a popup menu to the title bar of document windows. The popup menu has actions that must be validated by the NSDocument instance. If you override -validateUserInterfaceItem: or -validateMenuItem: you should make sure your override invokes super when the action is not one that your subclass recognizes.

• Updating of the File menu

In applications that have enabled autosaving in place, the Save As… and Save All items in the File menu are hidden, and a Duplicate menu item is added.

• The meaning of NSDocumentController's -setAutosavingDelay: and -autosavingDelay methods

In applications that have enabled autosaving in place, as opposed to the old style of autosaving that was introduced in Mac OS 10.4, passing a value of -[NSDocumentController setAutosavingDelay:] to 0.0 does not turn off autosaving.


NSDocument Asynchronous Saving

Mac OS 10.7 introduces to NSDocument the notion of asynchronous saving, in which the writing to a file being saved is done on a background thread so the application's user interface remains responsive even if that writing is slow. Saving is now done by a new method that does not have to signal success or failure before it returns:
- (void)saveToURL:(NSURL *)url
ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation
completionHandler:(void (^)(NSError *errorOrNil))completionHandler;
-saveToURL:ofType:forSaveOperation:error:, the old, synchronous, variant of this method, is deprecated. If your application is overriding that method to customize saving you should override this new method instead. If your application must continue to run on versions of Mac OS X older than 10.7 you can leave your override of the old method there. If you do that then NSDocument will invoke the old method on Mac OS 10.6 and older and the new one on Mac OS 10.7 and newer.

Whether or not saving is asynchronous is controlled by a new method that you can override:
- (BOOL)canAsynchronouslyWriteToURL:(NSURL *)url
ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation;
This method is invoked during saving. If your override of it returns YES then NSDocument will create a separate writing thread and invoke -writeSafelyToURL:ofType:forSaveOperation:error: on it, but the main thread will remain blocked until something on the writing thread invokes another new method:
- (void)unblockUserInteraction;
When this method is invoked the main thread will become unblocked, the application will resume dequeueing user interface events, and the user will be able to continue viewing and editing their document, even if the actual writing of the file takes some time. The right moment to invoke this method is when an immutable "snapshot" of the document's contents has been taken so that the writing of the snapshot's contents can continue safely on the writing thread even when the user is editing the document on the main thread. In many applications taking that snapshot will be as simple as creating an NSFileWrapper, so the default implementation of -writeToURL:ofType:error: invokes - unblockUserInteraction after it has invoked -fileWrapperOfType:error: but before writing that NSFileWrapper to a file. If your NSDocument subclass simply overrides -fileWrapperOfType:error: or -dataOfType:error: to implement writing and your override never takes too long to return then you don't have to worry about invoking -unblockUserInteraction.

There is an error in the comment for this method in <AppKit/NSDocument.h>. The default implementation of -fileWrapperOfType:error: does not invoke -unblockUserInteraction. The default implementation of -writeToURL:ofType:error:, on the other hand, does.

In some applications taking a snapshot quickly enough won't be possible all of the time regardless of whether it's an NSFileWrapper or something else. One way to unblock user interaction quickly in that case is to not take a snapshot at all and instead trigger copy-on-write behavior ("write" in this case refers to the mutation of the document's contents in memory, not the writing to a file). In a copy-on-write model objects that are to be mutated to reflect the user's edits are first copied if writing to a file is happening right then so that the writing thread can continue its work without risk of writing a file whose contents are internally inconsistent. While writing continues the values that are displayed in the user interface must be gotten from the copies if there are any, and when the writing is done the copies must be folded back into the document model's regular object graph.

In a substantial application multithreaded copy-on-write is difficult to implement. In an effort to strike a more realistic balance between responsiveness and ease of implementation NSDocument's asynchronous saving machinery also supports a "stop-copying-on-write" model, in which your NSDocument subclass can attempt to take the snapshot mentioned above but often cancel that snapshotting, and actually the entire saving operation, if it takes so long that the user's continued editing of the document requires the mutation of the objects being snapshotted. Implementing this correctly requires implementing a thread-safe way to cancel snapshotting and also a way to sense when the snapshotting must be cancelled. Hopefully this will still be easier for application developers than copy-on-write. To find out when that sort of snapshotting cancellation is appropriate for a particular save operation you can invoke a new method:
- (BOOL)autosavingIsImplicitlyCancellable;
This method returns YES, for example, when periodic autosaving is being done for crash protection and therefore nothing bad would happen if that autosaving were to be cancelled by your stop-copying-on-write mechanism. It returns NO when autosaving should be allowed to continue even it is going to take a noticeable amount of time, when the document is being closed for example.

When saving is asynchronous and the user is allowed to edit a document while it is being saved NSDocument's old technique of updating the changedness of the document after successful saving by invoking -updateChangeCount and passing it NSChangeCleared or NSChangeAutosaved is not sufficient. Information must be recorded about the state of the document at the beginning of the save operation and used at the end. NSDocument now does that, using two new methods:
- (id)changeCountTokenForSaveOperation:(NSSaveOperationType)saveOperation;
- (void)updateChangeCountWithToken:(id)changeCount
forSaveOperation:(NSSaveOperationType)saveOperation;
See the comments in <AppKit/NSDocument.h> for details about all of the methods published in this section.

NSDocument UI and File Access Serialization

With asynchronous saving it is possible for the user to instigate a user interface action that might present modal UI, a sheet for example, when asynchronous saving is about to fail and present an error alert sheet of its own, which would not work. This problem is prevented by the use of a set of new methods:
- (void)performActivityWithSynchronousWaiting:(BOOL)waitSynchronously
usingBlock:(void (^)(void (^activityCompletionHandler)(void)))block
- (void)continueActivityUsingBlock:(void (^)(void))block;
As a general rule you should use these methods, if your NSDocument subclass supports asynchronous saving, around the performance of user interface actions that might present modal UI, like sheets, including error alerts.

For any instance of NSDocument there are values in memory that only make sense in the context of the document file's current state. Problems arise when one thread gets or sets these values while a different thread is changing the file's current state. These problems are prevented by the use of a different set of new methods:
- (void)performSynchronousFileAccessUsingBlock:(void (^)(void))block;
- (void)performAsynchronousFileAccessUsingBlock:(void (^)(void (^completionHandler)(void)))block;
You should use these methods, if your NSDocument subclass supports asynchronous saving, in code that makes decisions based on values that are changed by the act of saving a document, and in code that changes those values.

-performActivityWithSynchronousWaiting:usingBlock: and -performSynchronousFileAccessUsingBlock: can block the main thread. Sometimes that blocking must be interrupted. You can do that, using another new method:
- (void)continueAsynchronousWorkOnMainThreadUsingBlock:(void (^)(void))block;
See the comments in <AppKit/NSDocument.h> for details about all of the methods published in this section.

NSDocument Duplicating

Part of Mac OS 10.7's new document model is a Duplicate item in the File menu that replaces the existing Save As… item, so turning on autosaving in place in any NSDocument subclass in your application does two more things:
• Makes NSDocumentController add a Duplicate menu item to your application's File menu.
• Makes instances of that NSDocument subclass in the responder chain hide the Save As… item in the File menu.

The handling of the user's choosing the Duplicate item is done by three new NSDocument methods:
- (IBAction)duplicateDocument:(id)sender;
- (void)duplicateDocumentWithDelegate:(id)delegate
didDuplicateSelector:(SEL)didDuplicateSelector
contextInfo:(void *)contextInfo;
- (NSDocument *)duplicateAndReturnError:(NSError **)outError;
-duplicateAndReturnError: invokes a new NSDocumentController method to actually create the new document:
- (NSDocument *)duplicateDocumentWithContentsOfURL:(NSURL *)url
copying:(BOOL)duplicateByCopying
displayName:(NSString *)displayNameOrNil
error:(NSError **)outError;
Both of these methods work reliably only when only sent to instances of NSDocument in which autosaving in place is enabled.

When NSDocumentController creates a new duplicate document it sets its name by invoking another new NSDocument method:
- (void)setDisplayName:(NSString *)displayNameOrNil;
If your NSDocument subclass previously implemented this method, you may need to accommodate for NSDocument's implementation and use of this method when you eventually link on Mac OS 10.7. See the comments in <AppKit/NSDocument.h> and <AppKit/NSDocumentController.h> for details.

NSDocument Error Handling Change

The default implementations of some NSDocument methods return recoverable NSErrors. Recovery from some kinds of errors uses resources, like temporary files, that cannot be cleaned up before the NSError is returned. If such an NSError is presented to the user the resource will be cleaned up automatically, regardless of what recovery option the user chooses, but in some rare cases it is not appropriate to present an NSError to the user. In those cases you can trigger the necessary resource cleanup by invoking a new method:
- (void)willNotPresentError:(NSError *)error;
See the comment in <AppKit/NSDocument.h> for details.

File Type Fixing in -[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:]

The new -saveToURL:ofType:forSaveOperation:completionHandler: method has a feature that the old -saveToURL:ofType:forSaveOperation:error: method does not. When the passed-in URL has a file name extension that is not valid for the passed-in type, -saveToURL:ofType:forSaveOperation:completionHandler: internally creates a new URL whose file name extension is valid, and uses that instead for the save operation. It passes that "fixed" URL when it invokes -writeSafelyToURL:ofType:forSaveOperation:error:. If the writing succeeds, it deletes the file located by the original URL. It uses -[NSFileCoordinator itemAtURL:didMoveToURL:] properly when it does this.

This is useful in applications whose documents' types can be changed by user action. For example, in Mac OS 10.7, TextEdit takes advantage of this to more easily convert .rtf files to .rtfd when the user adds attachments to documents.

-saveToURL:ofType:forSaveOperation:error: does not do this. Also, -saveToURL:ofType:forSaveOperation:completionHandler: does not do this when it's invoking -saveToURL:ofType:forSaveOperation:error: for backward binary compatibility.

NSDocumentController Asynchronous Opening and Reopening

Mac OS 10.6 introduced to NSDocumentController the notion of concurrent document opening but no corresponding method was published. In Mac OS 10.7 opening is now done by a new method that does not have to signal success or failure before it returns:
- (void)openDocumentWithContentsOfURL:(NSURL *)url
display:(BOOL)displayDocument
completionHandler:(void (^)(NSDocument *document, BOOL documentWasAlreadyOpen, NSError *error))completionHandler;
Reopening, which is what happens during application launching when AppKit's new Restorable State machinery restores a document that was open when the application was last terminated, is now also done by a new method that does not have to signal success or failure before it returns:
- (void)reopenDocumentForURL:(NSURL *)urlOrNil
withContentsOfURL:(NSURL *)contentsURL
display:(BOOL)displayDocument
completionHandler:(void (^)(NSDocument *document, BOOL documentWasAlreadyOpen, NSError *error))completionHandler;
-openDocumentWithContentsOfURL:display:error: and -reopenDocumentForURL:withContentsOfURL:error:, the old, synchronous, variants of these methods, are deprecated. If your application is overriding one of those methods to customize open or reopening you should override the new method instead. If your application must continue to run on versions of Mac OS X older than 10.7 you can leave your override of the old method there. If you do that then NSDocument will invoke the old method on Mac OS 10.6 and older and the new one on Mac OS 10.7 and newer.

See the comments in <AppKit/NSDocumentController.h> for details.

Move of NSFileWrapper from the AppKit Framework to the Foundation Framework

In Mac OS 10.7 the NSFileWrapper class has been moved from the AppKit framework to the Foundation framework. An NSFileWrapper(NSExtensions) category, declared in <AppKit/NSFileWrapperExtensions.h> remains behind. It includes the -setIcon: and -icon methods. The NSFileWrapper class, including all other methods, is now declared in <Foundation/NSFileWrapper.h>.




NSResponder

- (id)supplementalTargetForAction:(SEL)action sender:(id)sender;
This method is used in the process of finding a target for an action method for both nil target action routing and user interface validation (menus, toolbar items). It allows you to add non NSResponder objects to the action responder chain. Some example uses of this API are: allowing a control's delegate or a plugin to respond to actions.

If this NSResponder instance does not itself respondsToSelector:action, then supplementalTargetForAction:sender: is called. This method should return an object which responds to the action; if this responder does not have a supplemental object that does that, the implementation of this method should call super's supplementalTargetForAction:sender:. NSResponder's implementation returns nil. A basic implementation example:
- (id)supplementalTargetForAction:(SEL)action sender:(id)sender {
    return [_foo respondsToSelector:action] ? _foo : [super supplementalTargetForAction:action sender:sender];
}
Returning an object that does not respond to the action is an error. Generally, this method is called by NSApplication and NSApplication raises an exception in this case.

NSPrintOperation

If the print sheet is unresponsive or sluggish due to the time is takes you to fully render a page, you can check this method in drawRect and other printing methods such as beginDocument and knowsPageRage: to determine if the print operation prefers speed over fidelity. Most applications render each page fast enough and do not need to call this method. Only use this API after establishing that best quality rendering does indeed make the user interface unresponsive.
enum {
// Render at the best quality you can regardless of how slow that may be
NSPrintRenderingQualityBest,
    // Sacrifice the least possible amount of rendering quality for speed
// to maintain a responsive user interface
NSPrintRenderingQualityResponsive
};
typedef NSInteger NSPrintRenderingQuality;
@implementation NSPrintOperation ...
- (NSPrintRenderingQuality)preferredRenderingQuality;
This is an example use of this API:
- (void)drawRect:(NSRect)rect {
NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
if (![currentContext isDrawingToScreen]) {
NSPrintOperation *printOperation = [NSPrintOperation currentOperation]
if ([printOperation preferredRenderingQuality] == NSPrintRenderingQualityResponsive) {
// Render with the best possible quality such that the user interface remains responsive
} else {
// Printing, do a full render
}
}
}

ScrollWheel Events

There are new NSEvent methods to access the scroll wheel event delta that allow for more precise delta values from Magic Mice and trackpads. -deltaX and -deltaY should no longer be used when the event is type NSScrollWheel.

A generic scroll wheel issues rather coarse (line) scroll deltas. That is, the amount that you actually scroll is the line height multiplied by the scroll delta. Some Apple mice and trackpads provide much more precise delta. Instead of multiplying by the line height, you use the scroll delta value as is. The new -scrollingDeltaX/Y methods always return the most precise delta provided by the input device. You can determine precision of the -scrollingDelta with the new -hasPreciseScrollingDeltas method. When -hasPreciseScrollingDeltas returns NO, multiply the returned value by line or row height. When -hasPreciseScrollingDeltas returns YES, scroll by the returned value (in points).
- (BOOL)hasPreciseScrollingDeltas;
- (CGFloat)scrollingDeltaX;
- (CGFloat)scrollingDeltaY;
Touch capable Apple mice and trackpads know when the user physically starts and stops scrolling. AppKit publishes this state using the new method below.
enum {
NSEventPhaseNone = 0, // event not associated with a phase.
NSEventPhaseBegan = 0x1 << 0,
NSEventPhaseStationary = 0x1 << 1,
NSEventPhaseChanged = 0x1 << 2,
NSEventPhaseEnded = 0x1 << 3,
NSEventPhaseCancelled = 0x1 << 4,
};
typedef NSUInteger NSEventPhase;
- (NSEventPhase)phase;
Scroll wheel events issued from non-touch scrolling devices that cannot distinguish when the user starts and ends a scroll gesture have a phase of NSEventPhaseNone. For touch scrolling devices, the first scroll wheel event in the gesture sequence has a phase of NSEventPhaseBegan. Subsequent scroll in the sequence have a phase of NSEventPhaseChanged. When the user physically ends the gesture, the final scroll wheel event has a phase of NSEventPhaseEnded and a scrollingDelta of 0,0.

Some Apple mice and trackpads also issue momentum scroll wheel events. That is, the hardware continues to issue scroll wheel events even though the user is no longer physically scrolling. AppKit routes these scroll wheel events to the view the cursor was over when they started. For custom cells, this may not be enough. Using the new methods below, you can now determine when momentum scrolling starts, continues and stops and further route the events appropriately.
- (NSEventPhase)momentumPhase;
For normal (non momentum) scroll wheel events, the momentumPhase is NSEventPhaseNone. When the hardware switches from user performed scrolls events to momentum scroll wheel events, the momentumPhase is set to NSEventPhaseBegan. Subsequent scroll wheel events from that device have a momentumPhase of NSEventPhaseChanged until the momentum decays to 0, or the user stops the momentum scrolling. The final momentum scroll wheel event has a momentumPhase of NSEventPhaseEnded. All momentum scroll wheel events have a phase of NSEventPhaseNone.


Scroll and Gesture Direction

There is a new user preference to "Move content in the direction of finger movement." To accomplish this, deltaX/Y and scrollingDeltaX/Y are automatically inverted for NSEventScrollWheel events according to the user's preferences. The direction of swipes match the direction of scrolling and, as such, swipe deltas are inverted. However, for some uses of NSEventScrollWheel and NSEventTypeSwipe events, the behavior should not respect the user preference. This method allows you to determine when the event has been inverted and compensate by multiplying -1 if needed.
- (BOOL)isDirectionInvertedFromDevice;

Multi-image dragging

There are new classes and methods to handle dragging. Instead of dragging a single image, you now drag individual images for each item that is in the drag. You can even specify multiple images for each item. These images are then animated into various formations as the user drags. Additionally, the destination of a drop may participate in the drag presentation by setting the images for each item when the user is over the destination.

NSDraggingSource and NSDraggingDestinations are now formal protocols. The following new classes have been introduced. NSDraggingSession, NSDraggingItem, and NSDraggingImageComponent. When a drag is started, an NSDraggingSession is created. The current image based dragging methods are deprecated in favor of new NSDraggingSession based methods (See NSView.h and NSDragging.h). The NSDraggingInfo protocol is expanded with new methods that allow modifications to the drag items. The new NSView method to begin a drag takes an array of NSDraggingItems (pasteboardItems, frames and image providers) and creates the appropriate pasteboard for the drag. The current drag methods on NSWindow and NSView are informally deprecated in favor of this new method.

A basic drag starts by calling -beginDraggingSessionWithItems:event:source: on a view. This new method returns right away and the actual drag occurs later. The caller can take the returned NSDraggingSession and continue to modify its properties such as -animatesToStartingPositionsOnCancelOrFail. When the drag actually starts, the source is sent a -draggingSession:willBeginAtPoint: message followed by multiple -draggingSession:movedToPoint: messages as the user drags. Once the drag is ended or cancelled, the source receives a -draggingSession:endedAtPoint:operation: and the drag is complete.

The destination of a drag works much the same way as before. The draggingEntered:, draggingUpdated:, etc... methods are called at the same times. The sender for these messages still conforms to <NSDraggingInfo>. NSDraggingInfo has some new methods that allow the destination to enumerate the dragging items changing their properties such as frame and image.

When the DragManager determines it is a good time for the drag to change (either formation change and / or image change), the destination is sent an -updateDraggingItemsForDrag: message, if implemented. Waiting for a call to -updateDraggingItemsForDrag: is the preferred way to change the drag image at the right time.


Fluid Swipe Tracking - API

The following API allows for tracking gesture scroll wheel events as a fluid swipe gesture. Similar to iOS, NSScrollView will bounce once if needed, then optionally pass on the gesture scroll wheel events so your controller can use this API to track the scroll gesture as a fluid swipe gesture.

ScrollWheel NSEvents now respond to the -phase message. There are 3 types of scrolls: 1) Gesture scrolls - these begin with NSEventPhaseBegan, have a number of NSEventPhaseChanged events and terminate when the user lifts their fingers with an NSEventPhaseEnded. 2) Momentum scrolls - these have a phase of NSEventPhase none, but they have a momentumPhase of NSEventPhaseBegan/NSEventPhaseChanged/NSEventPhaseEnded. 3) Legacy scrolls - these scroll wheel events have a phase of NSEventPhaseNone and a momentumPhase of NSEventPhaseNone. There is no way to determine when the user starts, nor stops, performing legacy scrolls.

NSScrollView processes all gesture scroll wheel events and does not pass them up the responder chain. Often, tracking a swipe is done higher in the responder chain, for example at the WindowController level. To achieve an iOS style "bounce when not at the edge, otherwise swipe" behavior, you need to inform NSScrollView that it should forward scroll wheel messages when appropriate. Instead of manually setting a property on NSScrollView, your NSResponder can implement the following method and return YES.
- (BOOL)wantsScrollEventsForSwipeTrackingOnAxis:(NSEventGestureAxis)axis;
When the appropriate controller receives a -scrollWheel: message with a non NSEventNone phase, you can call the following message on that event instance to track the swipe or scroll to both user completion of the event, and animation completion.
enum {
NSEventSwipeTrackingLockDirection = 0x1 << 0,
NSEventSwipeTrackingClampGestureAmount = 0x1 << 1
};
typedef NSUInteger NSEventSwipeTrackingOptions;
@interface NSEvent ...
- (void)trackSwipeEventWithOptions:(NSEventSwipeTrackingOptions)options
dampenAmountThresholdMin:(CGFloat)minDampenThreshold
max:(CGFloat)maxDampenThreshold
usingHandler:(void (^)(CGFloat gestureAmount, NSEventPhase phase, BOOL isComplete, BOOL *stop))handler;
...
Below is a pseudo code example of swiping a collection of pictures like the iOS Photo app.
- (BOOL)wantsScrollEventsForSwipeTrackingOnAxis:(NSEventGestureAxis)axis {
return (axis == NSEventGestureAxisHorizontal) ? YES : NO;
}
- (void)scrollWheel:(NSEvent *)event {
// NSScrollView is instructed to only forward horizontal scroll gesture events (see code above). However, depending
// on where your controller is in the responder chain, it may receive other scrollWheel events that we don't want
// to track as a fluid swipe because the event wasn't routed though an NSScrollView first.
if ([event phase] == NSEventPhaseNone) return; // Not a gesture scroll event.
if (fabsf([event scrollingDeltaX]) <= fabsf([event scrollingDeltaY])) return; // Not horizontal
    // If the user has disabled tracking scrolls as fluid swipes in system preferences, we should respect that.
// NSScrollView will do this check for us, however, depending on where your controller is in the responder chain,
// it may scrollWheel events that are not filtered by an NSScrollView.
if (![NSEvent isSwipeTrackingFromScrollEventsEnabled]) return;
    if (_swipeAnimationCanceled && *_swipeAnimationCanceled == NO) {
// A swipe animation is still in gestureAmount. Just kill it.
*_swipeAnimationCanceled = YES;
_swipeAnimationCanceled = NULL;
}
    CGFloat numPhotosToLeft = // calc num of photos we can move to the left and negate
CGFloat numPhotosToRight = // calc num of photos we can move to the right
    __block BOOL animationCancelled = NO;
[event trackSwipeEventWithOptions:0 dampenAmountThresholdMin:numPhotosToLeft max:numPhotosToRight
usingHandler:^(CGFloat gestureAmount, NSEventPhase phase, BOOL isComplete, BOOL *stop) {
if (animationCancelled) {
*stop = YES;
// Tear down animation overlay
return;
}
        if (phase == NSEventPhaseBegan) {
// Setup animation overlay layers
}
        // Update animation overlay to match gestureAmount
        if (phase == NSEventPhaseEnded) {
// The user has completed the gesture successfully.
// This handler will continue to get called with updated gestureAmount
// to animate to completion, but just in case we need
// to cancel the animation (due to user swiping again) setup the
// controller / view to point to the new content / index / whatever
} else if (phase == NSEventPhaseCancelled) {
// The user has completed the gesture un-successfully.
// This handler will continue to get called with updated gestureAmount
// But we don't need to change the underlying controller / view settings.
}
        if (isComplete) {
// Animation has completed and gestureAmount is either -1, 0, or 1.
// This handler block will be released upon return from this iteration.
            // Note: we already updated our view to use the new (or same) content
// above. So no need to do that here. Just...
            // Tear down animation overlay here
self->_swipeAnimationCanceled = NULL;
}
}];
    // We keep a pointer to a BOOL __block variable so that we can cancel our
// block handler at any time. Note: You must assign the BOOL pointer to your
// ivar after block creation and copy!
self->_swipeAnimationCanceled = &animationCancelled;
}



Full Screen

In Lion, we have added support for a fullscreen experience that allows users to run windows in fullscreen mode, where a new space is dynamically created for each window in fullscreen mode. This means for example that you can have a Safari window on the desktop, and another Safari window in a fullscreen space.

NSWindow Full Screen API

An application must specify if a window can enter fullscreen.  Note that existing space-related window collection behaviors continue to have meaning in fullscreen spaces as well. For example, a window with NSWindowCollectionBehaviorTransient will be automatically shown on the fullscreen space if it was visible on the desktop space when you entered fullscreen mode. A window with NSWindowCollectionBehaviorMoveToActiveSpace will be shown in the fullscreen space if the user performs an action to show it, even if it was previously shown on the desktop space. A window with NSWindowCollectionBehaviorManaged stays attached to the space on which it was first shown, as long as it is not closed and reopened.

Windows belonging to other applications will not be allowed on a fullscreen space, except as follows: Windows belonging to a UIElement application are allowed on a fullscreen space. Non-activating panels owned by other applications are also allowed on a fullscreen space, but only if their window collection behavior includes NSWindowCollectionBehaviorFullScreenAuxiliary. Depending on the nature of your window, you may want to include NSWindowCollectionBehaviorCanJoinAllSpaces in the window collection behavior in order to have qualifying windows appear on fullscreen spaces as well as desktop spaces.

enum {
// a window with this collection behavior will have a fullscreen button in the upper right of its titlebar
    NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
// windows with this collection behavior can be shown on the same space as the fullscreen window
    NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8
};

A window can be taken into or out of fullscreen using -toggleFullScreen:.  If an application supports fullscreen, it should add an "Enter Full Screen" menu item to the View menu.  The menu item is now available through Xcode 4. You can also add the item programmatically, with toggleFullScreen: as the action, nil as the target, and cmd-ctrl-f as the key equivalent. AppKit will automatically update the menu item title as part of its menu item validation.
- (void)toggleFullScreen:(id)sender;
The fullscreen window button is exposed as a standard window button.
enum {
NSWindowCloseButton,
NSWindowMiniaturizeButton,
NSWindowZoomButton,
NSWindowToolbarButton,
NSWindowDocumentIconButton,
NSWindowFullScreenButton
};
typedef NSUInteger NSWindowButton;
A fullscreen window does not draw its titlebar, and may have special handling for its toolbar.  A styleMask is set to indicate that a window has fullscreen appearance.
enum {
    NSFullScreenWindowMask = 1 << 14
};
Notifications are sent for fullscreen changes:
NSString *NSWindowWillEnterFullScreenNotification;
NSString *NSWindowDidEnterFullScreenNotification;
NSString *NSWindowWillExitFullScreenNotification;
NSString *NSWindowDidExitFullScreenNotification;
The window also invokes new delegate methods as appropriate.

NSWindow Delegate Full Screen API

NSWindow invokes the following delegate methods as a window enters and exits fullscreen mode; these give you an opportunity to reconfigure the window layout and perform other operations if necessary.
- (void)windowWillEnterFullScreen:(NSNotification *)notification;
- (void)windowDidEnterFullScreen:(NSNotification *)notification;
- (void)windowWillExitFullScreen:(NSNotification *)notification;
- (void)windowDidExitFullScreen:(NSNotification *)notification;
The default frame of a fullscreen window fills the entire screen (including menuBar and dock areas).  The proposedSize will be a calculated contentSize, leaving space for the toolbar if present.  The window delegate can implement this method to provide a different content size for the window.  The window positioning will be implementation defined (probably centered) if it does not fill the entire screen.
- (NSSize)window:(NSWindow *)window willUseFullScreenContentSize:(NSSize)proposedSize;
The default presentationMode for fullscreen (NSApplicationPresentationFullScreen| NSApplicationPresentationAutoHideDock|NSApplicationPresentationAutoHideMenuBar). A delegate can provide a different set of options to take effect in the space containing this fullscreen window.
- (NSApplicationPresentationOptions)window:(NSWindow *)window
willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions;
The default animation between a window and its fullscreen representation is a crossfade.  With knowledge of the layout of a window before and after it enters fullscreen, an application can do a much better job on the animation.  The following API allows a window delegate to customize the animation by providing a custom window or windows containing layers or other effects.  In order to manage windows on spaces, we need the window delegate to provide a list of windows involved in the animation, in desired z-order, and including the receiver itself if the receiver should stay visible on the fullscreen space. If an application does not do a custom animation, this method can be unimplemented or can return nil.  window:startCustomAnimationToEnterFullScreenWithDuration: will be called only if customWindowsToEnterFullScreen: returns non-nil.
- (NSArray *)customWindowsToEnterFullScreen:(NSWindow *)window;

The system then starts its animation into fullscreen, including transitioning to a new space.  The method below is called to start the window fullscreen animation immediately, and perform the animation with the given duration to  be in sync with the system animation.  This method is called only if customWindowsToEnterFullScreen: returned non-nil.
- (void)window:(NSWindow *)window startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration;
In some cases, the transition to enter fullscreen will fail, due to being in the midst of handling some other animation or user gesture.  We will attempt to minimize these cases, but believe there is a need for failure handling.  This method indicates that there was an error, and the application should clean up any work it may have done to prepare to enter fullscreen.  This message will be sent whether or not the delegate indicated a custom animation by returning non-nil from  customWindowsToEnterFullScreen:. Note though that the window will be taken out of the fullscreen space and returned to the desktop even in the failure case, so it is especially important to return any custom animation to the non-fullscreen state in case of error. This is not the case when a failure is encountered while entering fullscreen. In the case of failure to enter, the window is left on the desktop space.
- (void)windowDidFailToEnterFullScreen:(NSWindow *)window;
The following API allows a window delegate to customize the animation when the window is about to exit fullscreen.  In order to manage windows on spaces, we need the window delegate to provide a list of windows involved in the animation, in desired z-order and including the receiver if the receiver should be visible during the animation.  If an application does not do a custom animation, this method can be unimplemented or can return nil.  window:startCustomAnimationToExitFullScreenWithDuration: will be called only if customWindowsToExitFullScreen: returns non-nil.
- (NSArray *)customWindowsToExitFullScreen:(NSWindow *)window;

The system then starts its animation out of fullscreen,  including transitioning back to the desktop space.  The method below is called to start the window animation immediately, and perform the animation with the given duration to  be in sync with the system animation.  This method is called only if customWindowsToExitFullScreen: returned non-nil.
- (void)window:(NSWindow *)window startCustomAnimationToExitFullScreenWithDuration:(NSTimeInterval)duration;
In some cases, the transition to exit fullscreen will fail, due to being in the midst of handling some other animation or user gesture.  We will attempt to minimize these cases, but believe there is a need for failure handling.  This method indicates that there was an error, and the application should clean up any work it may have done to prepare to exit fullscreen.  This message will be sent whether or not the delegate indicated a custom animation by returning non-nil from  customWindowsToExitFullScreen:.
- (void)windowDidFailToExitFullScreen:(NSWindow *)window;

NSApplication Full Screen API

There is a fullscreen presentation option, NSApplicationPresentationFullScreen.   This presentation option is set when in a fullscreen space.

A window delegate may also specify that the window toolbar be removed from the window in fullscreen and auto-shown with the menubar by including NSApplicationPresentationAutoHideToolbar in the presentationOptions returned from -window:willUseFullScreenPresentationOptions:.
enum {
    NSApplicationPresentationFullScreen = (1 << 10),
    NSApplicationPresentationAutoHideToolbar = (1 << 11),
};
typedef NSUInteger NSApplicationPresentationOptions;

Multiple Monitors and Full Screen

We have made some simplifying assumptions for Lion Full Screen behavior on multiple monitors. Multiple monitors are treated as a single unit by Spaces, and therefore are also treated as a single unit in Full Screen. This means that all monitors will be dedicated to windows belonging to the full screen application, and there can be only one primary full screen window visible at a time. A secondary monitor is useful for inspector windows.

Secondly, because the menu bar is located on the main monitor, the primary full screen window will be located on the main monitor as well. This allows the menu bar, floating toolbar, and full screen window to maintain their interrelationship on the same monitor.




Find Bar Support with NSTextFinder

Mac OS 10.7 includes a new class called NSTextFinder that provides a standard Safari-like find bar interface. NSTextFinder also provides incremental search functionality.

NSTextFinder serves as a controller class for the standard Cocoa find bar. NSTextFinder interacts heavily with its 'client', which is an object that provides access to the content being searched and provides visual feedback for a search operation by conforming to the NSTextFinderClient protocol, which will be explained later.


All menu items related to finding (Find…, Find Next, Find Previous, Use Selection for Find, etc.) will have the same action, performTextFinderAction:, which gets sent down the responder chain in the usual way. (Note: Before Mac OS 10.7, the default action for these menu items was performFindPanelAction: which you should update to this new action.) A responder of performTextFinderAction: is responsible for creating and owning an instance of NSTextFinder. Before any other messages are sent to the NSTextFinder, you should set its 'client' property to an object which implements the NSTextFinderClient protocol.


Each user interface item used for finding has a different tag, which corresponds to the appropriate value in the NSTextFinderAction enum (see below). Upon receipt of the performTextFinderAction: message, the responder should call the following on its NSTextFinder instance:
- (void)performAction:(NSTextFinderAction)action;
This method will determine the desired action from the tag and make various callbacks to its client to perform that action. These callbacks are defined in the NSTextFinderClient protocol.

Validation occurs by a similar pattern. The responder should implement validateUserInterfaceItem: and, when the item's action is performTextFinderAction:, it should pass the item's tag to the following method and return the result:
- (BOOL)validateAction:(NSTextFinderAction)action;
This method will also make various callbacks to its clients to determine what the appropriate response should be. These callbacks are also defined in the NSTextFinderClient protocol.


The NSTextFinderAction enum used in the above methods is listed below. The values from this enum are duplicates from the old NSFindPanelAction enum, with the exception of the NSTextFinderActionHideFindInterface value. The NSFindPanelAction values will be mapped to NSTextFinderAction values. Their existing numeric values will be preserved by this mapping.
enum {
NSTextFinderActionShowFindInterface = 1,
NSTextFinderActionNextMatch = 2,
NSTextFinderActionPreviousMatch = 3,
NSTextFinderActionReplaceAll = 4,
NSTextFinderActionReplace = 5,
NSTextFinderActionReplaceAndFind = 6,
NSTextFinderActionSetSearchString = 7,
NSTextFinderActionReplaceAllInSelection = 8,
NSTextFinderActionSelectAll = 9,
NSTextFinderActionSelectAllInSelection = 10,
NSTextFinderActionHideFindInterface = 11,
NSTextFinderActionShowReplaceInterface = 12,
NSTextFinderActionHideReplaceInterface = 13
};
typedef NSInteger NSTextFinderAction;

When an action is performed, NSTextFinder will ask its client for the string it is supposed to search. There are two ways the client can provide this string. It can either implement the -string method, and simply return an NSString, or it can implement the following methods:
- (NSString *)stringAtIndex:(NSUInteger)characterIndex effectiveRange:(NSRangePointer)outRange endsWithSearchBoundary:(BOOL *)outFlag;
- (NSUInteger)stringLength;
These methods make it easier to use NSTextFinder with data that cannot be easily or efficiently flattened into a single NSString. In the first method, the client should return the string which contains the character at the given index in a conceptual concatenation of all strings that are to be searched. For example, if a client had the strings "The quick", " brown fox" , "jumped over the lazy", and "dog", and NSTextFinder requests the string at index 18, then the client should return " brown fox", because the 18th character of all the strings were concatenated together would be the 'x' in "fox". Additionally, the client should return, by reference through the outRange parameter, the effectiveRange of the string that is returned. In the above example, the range of " brown fox" is {9, 10}, because, in the imagined concatenation, the substring starts at index 9 and is 10 characters long.

In some cases, it is not desirable for a match to be found which overlaps multiple strings returned by the above method. For example, suppose a client has a list of names like "John Doe", and "Jane Doe", etc. Normally, if the string is concatenated together like so: "John DoeJane Doe", then a search for "DoeJane" would succeed. To prevent this typically undesirable behavior from happening, the client should return YES, by reference through the outFlag parameter, when returning each individual string. This indicates that there is a boundary between the current string and the next string that should not be crossed when searching for a match.


One of the two approaches described above must be implemented by the client, or else the NSTextFinder will not be able to function. If both approaches (so all three methods) are implemented, stringAtIndex:effectiveRange:endsWithSearchBoundary: will be used by default.


Below are some properties reported by the client for use in NSTextFinder's -validateAction: method. If any of these properties is not implemented, a value of YES will be assumed. Returning NO from any of these methods will disable the actions that require them. For more information about which actions require these properties, consult the NSTextFinder.h header.
@property (getter=isSelectable, readonly) BOOL selectable;
@property (readonly) BOOL allowsMultipleSelection;
@property (getter=isEditable, readonly) BOOL editable;

When an action is sent via -performAction:, NSTextFinder may need additional information from the client to perform that action, or it may require the client to perform some parts of the action on its behalf. The following methods and properties provide the hooks NSTextFinder needs for each of the actions it supports. If the client does not implement one of these methods or properties, then the action that requires it will be disabled.
@property (readonly) NSRange firstSelectedRange;
This property is required for the NextMatch, PreviousMatch, Replace, ReplaceAndFind, and SetSearchString actions. The client just needs to return its first selected range, or {index, 0} to indicate the location of the insertion point if there is no selection.
@property (retain) NSArray *selectedRanges;
This property is required for the ReplaceAllInSelection, SelectAll, and SelectAllInSelection actions. The array should contain NSRanges wrapped in NSValues.
- (void)scrollRangeToVisible:(NSRange)range;
This method is used by many actions, but is not strictly required by any. This method instructs the client to scroll its view to make the given range visible.
- (BOOL)shouldReplaceCharactersInRanges:(NSArray *)ranges withStrings:(NSArray *)strings;
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string;
- (void)didReplaceCharacters;
These methods are used by the ShowReplaceInterface, Replace, ReplaceAll(InSelection) and ReplaceAndFind actions. NSTextFinder does not have the ability to directly make changes to the client’s content, so the client is responsible for performing replace operations in these methods. Before a replace operation is performed, NSTextFinder calls the first method to determine if a replacement should take place. If it returns NO, then the given range will not be replaced. If the method returns YES, or is not implemented, then NSTextFinder will call the second method, instructing the client to carry out the replacement. Finally, the third method will be called, if implemented, to indicate that the replacement was completed. For ReplaceAll actions, these methods may be called multiple times, starting from the end of the string and moving toward the beginning, in order to preserve the indexes of the matches which precede the current one.

In order to display the find bar, a "container" for the find bar must be specified. A "container" is an object that conforms to the NSTextFinderBarContainer protocol, described below. You can specify a find bar container using the following property on NSTextFinder:
@property (assign) IBOutlet id <NSTextFinderBarContainer> findBarContainer;
The NSTextFinderBarContainer contains the following methods for displaying and updating the find bar:
@protocol NSTextFinderBarContainer <NSObject>
@required
@property (retain) NSView *findBarView;
@property (getter=isFindBarVisible) BOOL findBarVisible;
- (void)findBarViewDidChangeHeight;
[...]
@end
When a new NSTextFinder is created and instructed to display the find bar, it will create an NSView for the find bar and assign that to the container via the findBarView property. From that point on, the container owns that view and should release it when the container is deallocated. The container should make the find bar visible when the findBarVisible property is set to YES. The container should implement the findBarViewDidChangeHeight method so it can reposition the find bar when its height changes, usually in response to user action.

Important: The container is free to modify the width of the find bar view, but it should never modify its height directly.


Find bar support in AppKit

In Mac OS 10.7, NSScrollView now conforms to NSTextFinderBarContainer in order to support the find bar for any document view searched by NSTextFinder. The find bar can be positioned either above or below the document view by assigning one of the values of the NSScrollViewFindBarPosition enum to the findBarPosition property.

NSTextView also now supports the find bar. The find bar can be enabled or disabled on a text view with the following methods:
- (BOOL)usesFindBar;
- (void)setUsesFindBar:(BOOL)flag;
Only one of usesFindBar and usesFindPanel can be YES at a time. If one value is set to YES, the other will be set to NO.


NSTextFinder Search Feedback

Since Mac OS 10.5, NSTextView has used an animated find indicator to give feedback to the user about a successful find action. The find indicator could be activated manually on an NSTextView via the -showFindIndicatorForRange: method.

To provide this functionality for NSTextFinder clients in Mac OS 10.7, NSTextFinder will show the find indicator at the appropriate time automatically when performing a find. However, NSTextFinder needs to know where to show the indicator and it needs something to draw the text contents of the find indicator. During incremental search, NSTextFinder needs also to know the locations of all the visible matches so it can highlight them as well. The following additions to the NSTextFinderClient protocol provide these capabilities.
@protocol NSTextFinderClient
[...]
@optional
/* This method is used when displaying feedback about find operations to the user.
The client should return the view the contains the character at the given index.
It should also return by reference the entire range of the string displayed by
the view.
*/
- (NSView *)contentViewAtIndex:(NSUInteger)index effectiveCharacterRange:(NSRangePointer)outRange;
/* NSTextFinder uses this method to determine the location to display the find
indicator. The client should convert the given content range to an array of
rectangles in the contentView's coordinate system and return that array. The
given range is guaranteed not to overlap multiple views.
*/
- (NSArray *)rectsForCharacterRange:(NSRange)range;
@end
The first method is called to determine what view the content is displayed in, over which the find indicator will be displayed. Since content may potentially be spread over multiple views (like NSLayoutManager, which supports multiple NSTextViews), the method asks for the view at a given index, and the full range of the string that is contained by that view.

In the second method, the client should determine and return the rectangles in which the content with the given range is displayed in its contentView. The given range is guaranteed not to span multiple content views. The returned rectangles tell NSTextFinder where the matched range is found, so it can show the find indicator there.

NSTextFinder and NSView will handle the find indicator correctly when the contentView is resized, moved, or removed from the view hierarchy. If your content view's scrolling is done by an NSScrollView, the find indicator will also be handled for you during scrolling. Beyond these cases, there may be some circumstances where the find indicator should be immediately cancelled and hidden, such as when the view's content or selection is changed without the knowledge of the NSTextFinder. The find indicator can be cancelled with the cancelFindIndicator method on NSTextFinder listed below. If your document is not scrolled by NSScrollView, then you should set the findIndicatorNeedsUpdate property to YES.

NSTextFinder is responsible for drawing the yellow find indicator background bezel, but the view must provide the contents. NSTextFinder will setup a drawing context and cause the contentView to draw into it. There are two ways this can happen.

NSTextFinder will invoke drawCharactersInRange:forContentView: on its client, if the method is implemented. The client should then draw the requested characters (or ask the containing view the characters, which is provided as a convenience) at the origin of the current context. If the requested character range partially intersects a glyph range, the client may draw the entire glyph, if necessary for performance.

If drawCharactersInRange:ForContentView: is not implemented, then NSTextFinder will use the normal NSView drawing mechanisms. The contentView does not need take any additional action to make this happen.

You should draw the contents of a find indicator slightly differently than normal to make sure it is readable on the background of the find indicator. For example, instead of drawing the text in its original color, it should be black. You can find out when find indicator drawing is occurring by checking the drawingFindIndicator property.


Incremental Search Support with NSTextFinder

A common feature used in conjunction with the find bar is incremental search. This feature allows users to find matches immediately as they are typing. In Mac OS 10.7, NSTextFinder provides this feature for its clients with minimal additional API.

Incremental searching can be enabled by setting the incrementalSearchingEnabled property on NSTextFinder to YES. This property alone is sufficient to cause the NSTextFinder to start searching incrementally.

For improved responsiveness, when a document is sufficiently long, NSTextFinder will search the document in the background. To ensure thread-safety, a client using incremental search must call noteClientStringWillChange on its NSTextFinder before any changes are made to the string provided to the NSTextFinder.

During incremental search, all visible matches are highlighted. NSTextFinder provides two alternatives for highlighting. If your client implements the read-only visibleCharacterRanges property, then by default a gray overlay will appear over your find bar container’s contentView with transparent "holes" cut out for each match. This contentView should be a superview of all contentViews reported by the NSTextFinderClient. NSScrollView already implements this property, so you only need to implement this property when using a different container class. Finally, this mode also requires the same two NSTextFinderClient methods that are used to display the find indicator: contentViewAtIndex:effectiveCharacterRange: and rectsForCharacterRange:. However, the implementation of these methods is identical for both purposes, so no additional work is required to support incremental search.

To disable the overlay, you should set the incrementalSearchingShouldDimContentView property to NO. If you do, you can still show matches from incremental search by using the NSTextFinder’s incrementalMatchRanges property. This property is an array of ranges specifying all incremental matches found so far. The array is updated on the main thread while the incremental search is performed on a background thread. You may use Key Value Observing to receive notifications when the contents of this array changes. If you use NSKeyValueObservingOptionNew and NSKeyValueObservingOptionOld, the dictionary passed to the KVO notification method will provide the indexes and ranges that are added or removed.

You may use the incrementalMatchRanges array to display the matches however you choose, but if you choose to highlight them in your view, it is recommended that you use the drawIncrementalMatchHighlightInRect: method to draw the standard highlight.


Incremental Search Support in NSTextView

In Mac OS 10.7, NSTextView provides incremental search support. It is disabled by default, but it can be enabled by setting the incrementalSearchingEnabled property to YES. Also, since incremental searching requires the find bar, usesFindBar must be set to YES for incremental searching to be occur.


NSCollectionView Drag and Drop Click-and-hold Delay

When NSCollectionView gained drag and drop support in Mac OS 10.6, a click-and-hold delay was required to differentiate drag and drop from drag selection. This is still the case in Mac OS 10.7 for collection views that support multiple selection. However, since drag selection is not important for single-selection collection views a click-and-hold delay is no longer required to start a drag and drop operation in applications linked on or after Mac OS 10.7.


NSCollectionView Multi-image dragging

NSCollectionView now supports multi-image dragging.

To support multi-image dragging for a dragging source, all you need to do is implement -collectionView:pasteboardWriterForItemAtIndex:. NSCollectionViewItem is responsible for creating dragging image components. You can customize its default behavior by overriding -draggingImageComponents. You can customize dragging session settings like draggingFormation or animatesToDraggingFormationOnBeginDrag by implementing -collectionView:draggingSession:willBeginAtPoint:forItemsAtIndexes:. You can also be informed when dragging ends by implementing -collectionView:draggingSession:endedAtPoint:dragOperation:.

More work is required to support multi-image dragging for dragging destinations. First of all, you need to make sure your acceptDrop: method uses the new item-based NSPasteboard APIs. Some time after a multi-image drag enters your collection view, -collectionView:updateDraggingItemsForDrag: is called on the delegate. Within that method you should enumerate the dragging info's dragging items and do the following:

1. Map the NSDraggingItem's item to a representedObject for your collection.
2. Create a new NSCollectionViewItem with the represented object. (The collection view item is typically created by copying the itemPrototype. Also, you may create a single collection view item and reuse that for each NSDraggingItem.)
3. Determine the target frame size for the item and set the NSDraggingItem's frame size.
4. Update the NSDraggingItem's image components provider using the frame from (3) and NSCollectionViewItem's -draggingImageComponents (or other custom code for generating the image components).

NSCollectionView's subview layout is dependent on the number of items contained in the collection. The method -frameForItemAtIndex: is based on the number of items currently contained in the collection. Since drag and drop operations typically change the number of items in the collection, you will likely need to use the new -frameForItemAtIndex:withNumberOfItems: method to compute the proper frame sizes.

To make the dragged images animate to their final destinations, you can set the draggingInfo's animatesToDestination property in your delegate's implementation of validateDrop:. If you do, you should enumerate the dragging info's dragging items in acceptDrop: and update them with their proper frames. Be sure to update your collection view's or array controller's content synchronously in acceptDrop: to get the proper animations.



NSDocument Autosaving In Place – Unintentional Edits and Versions Browser

NSDocument's "Autosaving In Place" eliminates the largely unnecessary user task of manually saving documents. However, when saving happens without user knowledge, it becomes easier for unintentional edits to get saved to disk. To help avoid this problem, when an NSDocument subclass adopts autosaving in place by returning YES from +autosavesInPlace, it will use various heuristics to determine when a user may have opened a document with the intention to read, but not edit it. When such a file is opened, NSDocument will warn the user when an edit is made to the document, offering them the choice of canceling the change, creating a new document with the change, or allowing editing, if possible. A document that is preventing edits will display "Locked" in the right-hand corner of the title bar. If the user intends to edit the document, he can avoid the alert by clicking on the "Locked" text and allowing editing by choosing "Unlock" in the pop-up menu. A document that has been changed since it was last opened and is therefore being actively autosaved in place will show "Edited" in the titlebar instead of "Locked".

Sometimes a user may choose to edit a file, then decide to revert those changes later. While a user is working on an autosaved in place document, NSDocument will, at various times, preserve the current contents of the document to disk. The user can access these preserved versions by selecting "Revert to Saved" from the File menu, or by selecting "Browse All Versions…" from the menu available in the right-hand corner of the document window's title bar. This brings the user into an interface that displays both the current document and a "stack" of previous versions. If the user chooses to restore a previous version, the current document contents are preserved on disk, if necessary, and the file's contents are replaced with those of the selected version. By holding the Option key, the user can also choose to restore a copy of a previous version, which will not affect the current document contents. Since old document versions are live windows, the user can also select and copy contents from them and paste them into the current document. Since the version browser interface takes control of the entire screen, you can set the NSDocumentRevisionsDebugMode user default to YES for easier debugging of your application in this interface.

Mac OS 10.7 includes several APIs to allow your application to customize its behavior in the version browser. First of all, the window that is used in the version browser is determined by the -windowForSheet API. You may also wish to simplify application's interface when the user is viewing old document versions. You can detect when to do this by checking the -isInViewingMode method on NSDocument. This value is set to YES before your the interface is loaded for an old document version. If you wish to change the interface of the current document when it enters or exits the version browser you can use the NSWindowDelegate methods, -windowWillEnterVersionBrowser: and -windowDidExitVersionBrowser:, or their NSNotification equivalents.

In order to fit two document windows side-by-side, the version browser may attempt to resize and/or scale the windows. When a window is scaled down the user can click on it to bring it back to full size. You can control the size of your document window in the version browser by implementing the -window:willResizeForVersionBrowserWithMaxPreferredSize:maxAllowedSize: method. The returned size will be used to determine the resulting size of the document windows in the version browser. You may return any size from this method, but that size is constrained to the "maximum allowed size" to ensure the version browsing controls are still accessible. If the window is larger than this size and cannot be resized, an exception will be thrown. The "maximum preferred size" parameter specifies the largest size a window can be without requiring scaling. If this method is not implemented, the version browser will use -window:willUseStandardFrame: to determine the resulting window frame size.


NSDocument File Dependence

When an NSDocument's file disappears from the file system the document can usually continue to function properly and even allow the user to re-save it if the entire file has been loaded into memory. When this happens with a file that has not been completely loaded, then unexpected errors may occur. NSDocument has a new method in Mac OS 10.7 called -isEntireFileLoaded, which is intended to provide explicit knowledge about a document's dependence on its file. NSDocument may use this information to do things like prevent volume ejection or warn the user when a partially loaded file disappears from the file system.

NSDocument's base implementation of this method returns YES, providing the same default behavior as before Mac OS 10.7. If you override -readFromURL:ofType:error: or -readFromFileWrapper:ofType:error: and read only a portion of the file or file wrapper, you should override -isEntireFileLoaded and return NO. If at some point the entire file is completely loaded, then you can start returning YES. Similarly, if portions of the file are removed from memory, you should start returning NO again.


NSSplitView Restorable State

In Mac OS 10.7, NSSplitView will participate in automatic state restoration by persisting its subview's frames, and restoring them when the app launches. If necessary, this process will invoke the same NSSplitViewDelegate methods used for constraining divider positions to ensure proper layout. For compatibility reasons, an NSSplitView will not participate in Mac OS 10.7's automatic state restoration unless it is assigned an autosave name. You can set the autosave name in Interface Builder, or in code using the -setAutosaveName: method.




Changes for @2x images

10.7 contains some changes to accommodate the iOS naming convention for high resolution images. If your bundle contains files named foo.png, foo@2x.png, and foo.pdf then [NSImage imageNamed:@"foo"] will return an NSImage backed by all three files, provided your app is linked against the 10.7 SDK.

This accomplishes something similar to how iOS handles hi-res images, but the mechanics are a little bit different. On iOS it's known at device startup whether @2x images are appropriate. On Mac OS X, the machine might go to sleep and wake up with a different screen attached. On iOS [UIImage imageNamed:@"foo"] loads foo.png if you are running on an iPhone 3Gs, and it loads foo@2x.png if you are running on an iPhone 4. On the mac, [NSImage imageNamed:@"foo"] unconditionally returns an NSImage object that contains three NSImageRep objects, one for each of foo.png, foo@2x.png, and foo.pdf. Each time the NSImage is drawn, it selects the representation best for the drawing context.

That takes care of +[NSImage imageNamed:], but +imageNamed: only finds resources in the main app bundle, not in frameworks or plugins. Framework authors typically do something like this:
   [[NSImage alloc] initByReferencingURL:[pluginBundle URLForImageResource:@"foo"]]
…which can only pick up one of foo.png, foo@2x.png, and foo.pdf since the URL is explicit. To provide a way forward, we add a one step method to get an image:
    @interface NSBundle(NSBundleImageExtension)
    - (NSImage *)imageForResource:(NSString *)name NS_AVAILABLE_MAC(10_7);
    @end
This can return an NSImage backed by multiple files, and it can also automatically call setTemplate: when appropriate according to the template image naming convention used in +imageNamed:.

Despite NSImage having this multifile support, we don't really expect you to use it much. If you have resources foo.png and foo@2x.png in your project resources, Xcode is going to roll them together into a multi-representation foo.tiff that contains both during build. NSImage has always had support for reading TIFF files with multiple image representations.

Performance note: NSImage has laziness in instantiating images. [NSImage imageNamed:] does no file IO at all, provided bundle contents are already cached. When you draw an image, we read the header of each backing file in order to see how large each is and select the best one given the drawing context. However, we avoid decoding the image data for a representation that is never selected. If you let Xcode assemble your images into a single TIFF, there's only one file whose header we have to read.

Bug in deprecated -[NSImage composite…] methods fixed conditional on linking with 10.7 SDK

We don't mention every bug fix in the release notes, but we do try to mention every time we do something that has behavior conditional on the SDK you link with. I say this to explain why this rather complicated section is here. If you don't use the deprecated -composite… methods, you can skip to the next section.

For 10.6, we rewrote the internals of NSImage, and fixed a lot of bugs and strangenesses. As part of that, we deprecated all the NSImage methods that start with composite, because they have surprising semantics as compared to the methods that start with draw. Since the -composite… methods were being deprecated, we didn't try to change their behavior and correct bugs in places where the fixes might mess up apps depending on the bugs.

However, the -composite… methods are used in really a lot of extant code, despite being deprecated. And one bug that we were aware of messes up a lot of applications when running at HiDPI, even if they're okay at 1.0x.

So, we fixed the bug, conditional on the app either being run at HiDPI or on it being linked against the 10.7 SDK or greater.

The bug is that -[NSImage compositeToPoint:fromRect:operation:fraction:] interpreted the fromRect in the coordinates of the NSImageRep selected to draw with instead of in the coordinates of the NSImage.

The symptom is that instead of a whole image at the correct size, you see just a corner, blown up, or you see the image too small. If you hit this, switch to one of the NSImage methods that start with -draw… (and also fix the fromRect, since it's probably wrong).


Cocoa Autolayout

10.7 introduces a new system for layout, reworking the springs and struts model. So big it needs its own release note!




NSApplication and Apple Push Notifications

NSApplication can register to receive push notifications similar to UIApplication on iOS. Currently, only badge notifications are supported on Mac OS X for non-running applications. Running applications can interpret data from the push server as desired.

To register to receive push notifications, use:
- (void)registerForRemoteNotificationTypes:(NSRemoteNotificationType)types;
If successful, the application’s delegate will receive a callback message with an opaque data blob representing the token used to communicate with the Apple Push Notification server (read the documentation on Push Notifications for more details on that interaction). If the application is not open when a push notification is received, the notification information is delivered in the userInfo dictionary for the NSApplicationDidFinishLaunchingNotification notification using the key NSApplicationLaunchRemoteNotificationKey. If the application is open when a push notification is received, the delegate gets a application:didReceiveRemoteNotification: message.


Button Appearance Changes

As part of an ongoing refresh of Aqua in Mac OS X Lion, some buttons look different and may not look the same in every context in your application. Specifically, the “Round Textured” button is not appropriate in any context other than directly on the background of a textured window. If you are using this kind of button in a table view or other context, please consider changing it to a “Round Rect” button.



NSPasteboard data-reading methods

The documented behavior of NSPasteboard has historically required that -types or -availableTypeFromArray: be sent to a pasteboard before invoking the data-reading methods -dataForType:, -propertyListForType:, and -stringForType:. Beginning in the 10.6 (the previous release of Mac OS X), calling these data-reading methods perform an implicit check for the requested type before attempting to read the data, making it no longer strictly required to call -types or -availableTypeFromArray: before calling the data-reading methods.

NSPasteboard UTI and Scrap Manager/Drag Manager Interoperability

As applications move towards using UTIs with NSPasteboard, as opposed to pboard types, an application may still need to interact with existing applications which use four character OS types with the legacy Scrap Manager and Drag Manager.

To interoperate with these legacy types, use the functions declared in UTTypes.h to generate the appropriate UTI string for a given OSType. The code below generates the appropriate UTI from a fictional OS type 'test':
CFStringRef flavorString = UTCreateStringForOSType('test');
CFStringRef conformsToType =  (floor(NSAppKitVersionNumber) <=  NSAppKitVersionNumber10_5) ? NULL : kUTTypeData;
CFStringRef utiForConvertedScrapFlavor = UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, flavorString, conformsToType);
// Use the utiForConvertedScrapFlavor
CFRelease(flavorString);
CFRelease(utiForConvertedScrapFlavor);
Using the generated UTI to read and write pasteboard data allows your application to interoperate with legacy Scrap Manager and Drag Manager clients.

Two things to note about the code sample:

1. The AppKit version check is required because of a change in behavior between 10.5 and 10.6. All UTIs used on the pasteboard should conform to kUTTypeData, but in 10.5 UTIs generated internally by NSPasteboard did not. This was addressed in 10.6. The version check ensures that the UTI string generated in your code will match the UTI that will appear on the pasteboard for that release. If your application will ship only on 10.6 and later, you can remove the version check and use kUTTypeData as the last argument to UTTypeCreatePreferredIdentifierForTag().

2. Always generate the UTI dynamically from the OSType. Do not store or cache the UTI string between application launches. This will ensure your pasteboard code continues to work, even if the developer of the legacy application releases a new version at a later date that formally maps the legacy OSType to a declared UTI string, or if the algorithm for generating dynamic UTIs changes in a future OS release.



Additional NSPasteboard Exceptions Added

NSPasteboard and NSPasteboardItem have methods to set NSData, NSString, and property list values. Classes that implement the NSPasteboardWriting protocol also provide property list values. Prior to 10.7, setting or providing an object of the wrong class, or invalid property lists, would fail silently. For applications linked on or after 10.7, this programming error now causes an exception to be raised.



Accessibility

When an accessibility client sets the AXSelectedRows and AXSelectedColumns of NSTableView, the behavior now correctly mirrors user selection of rows and columns, including invoking appropriate delegate methods such as -tableView:tableViewselectionIndexesForProposedSelection:.

NSScroller no longer reports accessibility children when its subelements are not visible.

NSSecureTextField now reports its caps lock and num lock indicators as AXImage accessibility children, if they are visible.

NSRulerView now posts NSAccessibilityUnitsChangedNotification when its measurement units change.

Accessibility of View-Based NSTableView and NSOutlineView

Mac OS X 10.7 adds support for using NSViews instead of NSCells to define the contents of table and outline views. View based table and outline views automatically provide a richer and more complete representation to accessibility. This is because complex table interfaces can now be represented as a composition of standard AppKit views and controls, such as text fields and image views, instead of custom cell subclasses. This allows a view-based table or outline to take advantage of the built-in accessibility of AppKit views and controls, removing the need for custom accessibility code to support complex cell subclasses.

View-based table views are reported to accessibility as using AXCells as the children of its rows. (Despite the similar terminology, an AXCell has no relationship to an NSCell). This means that changing an existing table view or outline view to be view-based will require AppleScript-based or AX-based user interface automation scripts to be updated to handle the new structure.

Accessibility and table cell views

The NSTableCellView class has a built-in 'textField' outlet and property. You should connect this outlet to the main text field in the cell. This text field will be automatically reported as the title UI element of the cell. Connecting this outlet greatly enhances the usability of the table for VoiceOver users, especially when the cell contains multiple pieces of information, such as additional subordinate pieces of information.

If you use a table cell view which is not a subclass of NSTableCellView, adding a 'textField' outlet and property is strongly recommended. AppKit treats 'textField' as a special key for any table cell view, and reports the value of that property as the title UI element of the table cell.

Changes to behavior of NSAccessibilityPostNotification()

In 10.7 NSAccessibilityPostNotification() checks for accessibility client observers differently depending on the notification. See NSAccessibility.h for a detailed description of the behavior changes.

NSPopover Accessibility

Popovers displayed using the new NSPopover class report themselves to accessibility clients using the new NSAccessibilityPopoverRole constant in NSAccessibility.h.

The accessibility parent of a popover is the user interface element that the popover popped from. Because an NSView subclass can have accessibility subelements, AppKit uses a heuristic to determine the correct accessibility parent for a popover, treating that element as the popover's parent, and temporarily making the popover a child of that element while the popover is visible.

If you need to override AppKit's default assignment of the popover's accessibility parent, override -accessibilityAttributeValue: in an NSPopover subclass to return a different object that implements NSAccessibility.

Accessibility and Automatic Termination

For All AppKit Applications:
After an accessibility client, such as VoiceOver, has made an accessibility request to an application, automatic termination will be disabled for that application to ensure that accessibility clients can continue to access the same application process.  Because accessibility clients such as GUI Scripting are also used for automated testing during application development, setting the user default NSDisableAutomaticTerminationAfterAccessibilityRequest to NO will override the default behavior so that these automated tests can be run without automatically disabling automatic termination.

For Assistive Applications Only:
If your application is an accessibility client (i.e. those that use the AX APIs found in the HIServices framework to interact with other applications), you should consider how your application uses the AX APIs when planning to enable Automatic Termination in your application.  For example, if your application needs to respond to accessibility notifications, even when it is hidden, you should disable automatic termination.  On the other hand, if your application displays information about the UI element under the mouse, but does not make accessibility requests when hidden, the usage of AX APIs would not prevent you from enabling automatic termination. See the documentation for more details on Automatic Termination itself.

Accessibility and Overlay Scrollers

An overlay scroller reports the AXHidden attribute to indicate if it is currently hidden or visible. This attribute is settable. An assistive application can set this value to 'NO' to make the scroller visible temporarily. Note that 'NO' is the only valid value, visible scrollers cannot be forced to be hidden with a value of 'YES'.



Press-and-Hold for Certain Keys

A new system-wide behavior has been implemented for cases in which the user presses and holds down certain keys while editing text. Instead of repeating the key, this new behavior provides a mechanism for entering alternative (usually accented) forms. UI appears near the text showing these alternative forms, which can be selected by clicking, by arrowing, or by pressing a digit corresponding to the index of the desired form. Keys other than letter and digit keys continue to repeat when pressed and held.

This new behavior generally should be active only in cases in which text is actually being edited, but there are a few cases in which it may need to be turned off explicitly. There is a default provided for this, ApplePressAndHoldEnabled, which may be set to NO to turn off the new press-and-hold behavior and re-enable key repeat for all keys. This may be set for a specific application, to control the behavior for that application only, or globally, to control the behavior for all applications.


Additional API for Autocorrection

There is new API on NSSpellChecker for supporting autocorrection. First, there is now a convenience method that allows separate access to correction results, which otherwise had been available only via the unified text checking methods checkString:range:types:options:inSpellDocumentWithTag:orthography:wordCount: and requestCheckingOfString:range:types:options:inSpellDocumentWithTag:completionHandler:.
- (NSString *)correctionForWordRange:(NSRange)range
inString:(NSString *)string
language:(NSString *)language
inSpellDocumentWithTag:(NSInteger)tag;
Second, there is now API to allow clients to report the user's response to a proposed correction, so that the spell checker can learn from this and adjust future correction behavior accordingly. This is used by views such as NSTextView and WebView, and third parties implementing their own text editing views that support autocorrection can use it as well. The tag, language, word, and correction should match those from the original correction result, so that the spellchecker can match them. This implies that in order to record responses properly, clients must store the original word and original correction at least from the point at which the user accepts it until the user edits or reverts it.
enum {
NSCorrectionResponseNone, // No response was received from the user
NSCorrectionResponseAccepted, // The user accepted the correction
NSCorrectionResponseRejected, // The user rejected the correction
NSCorrectionResponseIgnored, // The user continued in such a way as to ignore the correction
NSCorrectionResponseEdited, // After the correction was accepted, the user edited the
// corrected word (to something other than its original form)
NSCorrectionResponseReverted // After the correction was accepted, the user reverted the
// correction back to the original word
};
typedef NSInteger NSCorrectionResponse;
- (void)recordResponse:(NSCorrectionResponse)response
toCorrection:(NSString *)correction
forWord:(NSString *)word
language:(NSString *)language
inSpellDocumentWithTag:(NSInteger)tag;
Third, there is now API to allow clients to bring up the new UI for autocorrection. This is also used by views such as NSTextView and WebView, and third parties implementing their own text editing views that support autocorrection can use it as well. This user interface is used to indicate a correction intended to be made, allowing the user to accept or reject it; or once a correction has been made, to indicate the original form, allowing the user to revert back to it; or to display multiple alternatives from which the user may choose one if desired. The primaryString is the first string displayed, a correction or reversion according to the type of indicator; the alternativeStrings should be additional alternatives, if available. Only one indicator at a time may be displayed for a given view, and the only thing a client may do with the indicator after displaying it is to dismiss it. When an indicator is dismissed, whether by user action or by the view, the completion block will be called, with the acceptedString argument being either the replacement string accepted by the user, or nil if the user has not accepted a replacement.
enum {
NSCorrectionIndicatorTypeDefault = 0, // The default indicator shows a proposed correction
NSCorrectionIndicatorTypeReversion, // Used to offer to revert to the original form after a correction has been made
NSCorrectionIndicatorTypeGuesses // Shows multiple alternatives from which the user may choose
};
typedef NSInteger NSCorrectionIndicatorType;
- (void)showCorrectionIndicatorOfType:(NSCorrectionIndicatorType)type
primaryString:(NSString *)primaryString
alternativeStrings:(NSArray *)alternativeStrings
forStringInRect:(NSRect)rectOfTypedString
view:(NSView *)view
completionHandler:(void (^)(NSString *acceptedString))completionBlock;
- (void)dismissCorrectionIndicatorForView:(NSView *)view;
Finally, there is additional API to support the new global user preference settings for automatic text replacement and spelling correction. NSTextView now by default will keep track of and follow these settings automatically, but applications using NSTextView can override that by programmatically using existing NSTextView methods such as -setAutomaticTextReplacementEnabled: and -setAutomaticSpellingCorrectionEnabled: to control an individual text view's settings. The new API is primarily for non-text view clients who wish to keep track of the settings for themselves, using the NSSpellChecker class methods to determine their values, and optionally also notifications to determine when the settings have changed.
+ (BOOL)isAutomaticTextReplacementEnabled;
+ (BOOL)isAutomaticSpellingCorrectionEnabled;
NSString * const NSSpellCheckerDidChangeAutomaticSpellingCorrectionNotification;
NSString * const NSSpellCheckerDidChangeAutomaticTextReplacementNotification;

Regular Expression Text Checking

There is an additional key, NSTextCheckingRegularExpressionsKey, to be used in the options dictionary with the unified text checking methods checkString:range:types:options:inSpellDocumentWithTag:orthography:wordCount: and requestCheckingOfString:range:types:options:inSpellDocumentWithTag:completionHandler:. This key allows clients to specify a set of regular expressions to be searched for during text checking. Results will be reported using NSTextCheckingResults of type NSTextCheckingTypeRegularExpression. There are no default actions provided in response to these results, but clients may in this way detect the occurrence of particular regular expressions in text, in somewhat the same way that Data Detectors currently detects addresses, phone numbers, etc., and take any desired action in response.



NSTableView / NSOutlineView General Updates

Tables/Outlines setup as a source list (identified by the selectionHighlightStyle of NSTableViewSelectionHighlightStyleSourceList) on 10.7 and higher will now only allow a drag to begin from "content" in the cells. This is determined by using the NSCell methods hitTestForEvent:inRect:ofView: and returning NSCellHitContentArea.

Since Leopard, NSTableView has supported full width cells (which span all columns) by returning a cell from tableView:dataCellForTableColumn:row: when a 'nil' tableColumn is passed in. Up until Lion, NSTableView would incorrectly pass a non-nil value to the tableColumn parameter when the user right clicked on a row. This has been fixed for Lion linked applications, but prior applications will need to work around this by returning the proper cell even though a column was passed to the tableView:dataCellForTableColumn:row: method.

In 10.7 and higher tables that support variable row heights now have the row height values returned from -tableView:heightOfRow: cached by the NSTableView/NSOutlineView until noteHeightOfRowsWithIndexesChanged: (or reloadData) is called. Applications targeting 10.6 or lower need to ensure the implementation always returns the same value for the same row (as it may be called multiple times for the same row) until noteHeightOfRowsWithIndexesChanged: / reloadData is called.

Allowing column selection ( [tableView setAllowsColumnSelection:YES]) combined with the "none" selection highlighting style (NSTableViewSelectionHighlightStyleNone) would previously cause a crash when selecting a row after selecting a column. This has been fixed for applications that link on 10.7 or higher. Applications targeting 10.6 or lower should turn off column selection with [tableView setAllowsColumnSelection:NO].

Calling [tableView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList] will have the side effect of changing the backgroundColor to the standard "source list" color, and setting the draggingDestinationStyle to be NSTableViewDraggingDestinationFeedbackStyleSourceList. This allows one to easily setup a "source list" style NSTableView or NSOutlineView. For applications linked less on an OS lower than 10.7, calling setSelectionHighlightStyle to anything else would change the backgroundColor to [NSColor controlBackgroundColor] and draggingDestinationStyle to NSTableViewDraggingDestinationFeedbackStyleRegular. 10.7 now correctly only does this change if the previous style was a source list.

NSTableView now has a new rowSizeStyle property. This is mainly for sidebars/source lists to have a consistent layout defined by the Human Interface Guidelines. Setting the rowSizeStyle to NSTableViewRowSizeStyleCustom causes the table to behave as it always has. For any other value, the table will automatically change the -rowHeight based on various options; for instance, group rows may have a smaller row height than non-group rows. When the rowSizeStyle is not NSTableViewRowSizeStyleCustom, the -rowHeight will return the standard row height for non-group rows. Variable row heights can still be used; it is recommended to return the -rowHeight when a default height is needed, but group rows will require a custom specific row height to be returned (i.e.: 20). View Based TableViews should use NSTableCellView -- it will automatically layout the textField and imageView based on the current rowSizeStyle, and apply appropriate "sidebar / source list" attributes to the textField's stringValue and imageView's image.

NSTableView / NSOutlineView - View Based TableView and Animations

NSTableView and NSOutlineView in Mac OS 10.7 now support the use of NSViews instead of NSCells. Search for "View Based" in NSTableView.h, NSOutlineView.h for more information, and see: NSTableRowView.h and NSTableCellView.h.

NSTableView and NSOutlineView now have methods to insert/remove/move rows and items, optionally with an animation. These methods work for both the "Cell Based" and "View Based" NSTableView/NSOutlineView, however, the "Cell Based" version must always call -beginUpdates before doing any insert/remove/move calls, and then end with an -endUpdates. This allows the NSTableView to capture the current state to perform animations. You must not make changes to your model until after -beginUpdates has called, and you must update your model before -endUpdates is called.

NSTextFields inside a View Based TableView may have the -backgroundColor automatically changed when editing. Specifically, if an NSTextField is not bordered (isBordered==NO && isBezeled==NO), and does not draw a background (drawsBackground==NO), then NSTableView will automatically make the -backgroundColor be white, and set to be drawn, when it is editing (ie: when it is the firstResponder). This helps provide a complete Table View experience for the end user.

The NSTableRowView is responsible for drawing the bottom line of a horizontal grid if the gridStyleMask has NSTableViewSolidHorizontalGridLineMask set. This will happen automatically. However, the TableView draws any grid lines below the last row with drawGridInClipRect:. Subclassing drawGridInClipRect: is still acceptable, but horizontal grid lines should only be drawn past the last row. Vertical grid lines are currently not customizable.

Note that the View Based TableView does not support column selection.

The View Based NSTableView supports animating selection changes. This can be done using the animator proxy and calling -selectRowIndexes:byExtendingSelection:, such as: [[tableView animator] selectRowIndexes:indexes byExtendingSelection:NO];

NSTableView now implements -cancelOperation: to allow escape to automatically cancel text editing. This is accomplished by calling -abortEditing on the NSControl that is showing the current field editor. This action can be customized by overriding -cancelOperation: on NSTableView or overriding -abortEditing on the control that exists in the View Based TableView.

To disable animations, create an animation group around the code you want them to be disabled for and set the duration to 0. IE:
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0];
...
[NSAnimationContext endGrouping];

Syncing Animations with NSTableView

The following NSTableView methods can animate:
- (void)insertRowsAtIndexes:(NSIndexSet *)indexes withAnimation:(NSTableViewAnimationOptions)animationOptions;
- (void)removeRowsAtIndexes:(NSIndexSet *)indexes withAnimation:(NSTableViewAnimationOptions)animationOptions;
- (void)moveRowAtIndex:(NSInteger)oldIndex toIndex:(NSInteger)newIndex;
- (void)noteHeightOfRowsWithIndexesChanged:(NSIndexSet *)indexSet; // View-based tableview only
There may be a need to sync animations that happen inside the view, or with other things outside of the Table. This can be done by surrounding the calls with a specific NSAnimationContext grouping that already has a specific duration set. See the TableViewPlayground demo app for an example. In addition, if the duration is 0, then no animation will happen.

NSTableView / NSOutlineView Multi-image Dragging

NSTableView and NSOutlineView now support multi-image dragging. NSOutlineView methods mirror the NSTableView ones, but use 'item' instead or 'row. There are two components to dragging; being a dragging source and a dragging destination.

For a dragging source, the dataSource should implement tableView:pasteboardWriterForRow: and return an object that implements NSPasteboardWriting. That is all that is required to be a source. However, dataSource hooks are also provided for when the dragging session begins (tableView:draggingSession:willBeginAtPoint:forRowIndexes:) and ends (tableView:draggingSession:endedAtPoint:operation:). To control the drag image, the NSCell method draggingImageComponentsWithFrame:inView: is called on the clicked cell. For View Based TableViews, NSTableCellView's draggingImageComponents should be used, and can be overridden to customize the drag image.

The default implementation of NSCell's draggingImageComponentsWithFrame:inView: will generate an image from the cell and return two components: one for NSDraggingImageComponentLabelKey and another for NSDraggingImageComponentIconKey. This is done by capturing the portion from titleRectForBounds: and imageRectForBounds: respectively.
NOTE: NSCell currently has an issue where it will return an empty array from draggingImageComponentsWithFrame:inView: if the cell does not have an image portion. To work around this, subclass NSCell and override draggingImageComponentsWithFrame:inView: and generate your own NSDraggingImageComponents in the returned array.

To be a dragging destination, more work is required than to be a source. Implement tableView:updateDraggingItemsForDrag: to provide a drag image representation for the source content that was dragged over the tableView. Generally, this involves using a template NSCell and calling the new NSCell method draggingImageComponentsWithFrame:inView:. For View Based TableViews, NSTableCellView's draggingImageComponents should be used. tableView:validateDrop:proposedRow:proposedDropOperation: should be implemented as normal. In tableView:acceptDrop:row:dropOperation one should enumerate the NSDraggingInfo's classes and create new model objects. The table should be told of the changes by doing an insertRowsAtIndexes:withAnimation: using the animation option NSTableViewAnimationEffectGap to create a gap for the dragged image. The NSDraggingItem's draggingFrame should be updated to the appropriate -frameOfCellAtColumn:row:.

See the DragNDropOutlineView (cell based) and TableViewPlayGround (view based) examples for more information.

Implementing Sidebar Source Lists with NSOutlineView

The proper way to create a source list sidebar is to use NSOutlineView. The rowSizeStyle should be set to NSTableViewRowSizeStyleDefault, and all three sizes should be tested. Items which should not be expandable or collapsable by the user can have the outline cell hidden by using the delegate method outlineView:shouldShowOutlineCellForItem:. These items should be programmatically expanded. Group header items are created by using the delegate method outlineView:isGroupItem: and returning YES. To find out what rowSizeStyle the system is using, you can call [tableView setRowSizeStyle:NSTableViewRowSizeStyleDefault] and then read the current value with: [tableView effectiveRowSizeStyle]. This will return NSTableViewRowSizeStyleSmall, NSTableViewRowSizeStyleMedium or NSTableViewRowSizeStyleLarge as based on what the user has set in System Preferences. There is no direct way to know the image size for the icons. If you use NSTableCellView in a View Based NSOutlineView, then the cell will automatically be setup with the correct sizes when the rowSizeStyle is set. If this is not sufficient, then you can make the assumption for the following sizes: NSTableViewRowSizeStyleSmall = 16x16, NSTableViewRowSizeStyleMedium = 18x18, and NSTableViewRowSizeStyleLarge=32x32.

For Cell Based TableViews, a default set of attributes will be applied to the cell's stringValue creating a custom attributedStringValue based on the Human Interface Guidelines. This is done before calling -willDisplayCell, so in general, it is recommended to not change the text color or font of the cell in -willDisplayCell:. In Leopard and SnowLeopard, attributes were only applied to the group header cells. For applications linked on Lion and later, custom attributes will also be applied to all other cells (specifically, a 1-pixel drop shadow). For applications that want this feature pre-Lion, they can use -willDisplayCell to customize the -attributedStringValue as desired.

For View Based TableViews, a plain NSTextField (as setup in Xcode's Interface Builder) should be returned for the header items. Or, alternatively an NSTableCellView can be used with the -textField property properly setup to a basic NSTextField. The text should be set to an uppercase string, but no attributes need to be applied; they will automatically be applied by NSOutlineView. All other rows should use NSTableCellView, which will have proper layout done based on the -rowSizeStyle set to it. In addition, the -textField property will automatically have attributes applied to it as necessary. In general, source lists should not float the group rows, and [outline setFloatsGroupRows:NO] should be called.

To add an accessory view to an NSTableCellView that resides in a sidebar source list, you will need to subclass NSTableCellView and perform layout of the additional views in -viewWillDraw. First call [super viewWillDraw] to have the standard layout of the text and image performed.

To create an unread indicator or accessory button, use an NSButton (or NSButtonCell) with a bezelStyle set to NSInlineBezelStyle. This bezel style creates a special inline look for tables. When used inside a source list, it has appropriate color attributes applied to it to appear as an unread indicator (or colored button). It will behave as a button only if the target/action is set; otherwise, it can be used as a static unread indicator. -cellSizeForBounds: properly implements the minimum size required for an unread indicator if the cell has text. If the cell has an image, it will default to a circular size.

Prior to Lion, the source list background color was a special color that varied depending on the window key state. On Lion, it is now a gradient.

Debugging NSTableView

To help debug general issues, one can have the NSTableRowView show row numbers by running the application with “-NSTableRowViewShowRows YES” as a parameter. In addition, this also works in the "Cell Based" TableView.

Debugging View Based NSOutlineView Changes

To help debug errors in calls to insert/deleting/moving inside NSOutlineView, run the application with “-NSOutlineViewValidateChanges YES”. This performs some validation to ensure the inserts/deletes/moves match what the dataSource will return. Note that this should only be turned on for testing/debugging, and will cause a slowdown for all inserts/deletes/moves inside NSOutlineView. Each validation will happen after each call to insert/delete or move (it will NOT happen at the end of batched updates called inside of a beginUpdates/endUpdates block). Errors will be reported by throwing an exception.

NSApplication

AppKit now has the ability to report uncaught exceptions. It is controlled by a user default: NSApplicationShowExceptions (YES/NO). The default shipping value is NO. In general, it is recommend that developers set it to YES during development to catch programming errors. Individual applications can automatically turn this on by using [[NSUserDefaults standardUserDefaults] registerDefaults: ...] to register the option on. It can be set with defaults via: 'defaults write com.yourdomain.app NSApplicationShowExceptions YES'. It can also globally be turned on by writing to the global domain.

NSOpenPanel / NSSavePanel

Using the new method -beginWithCompletionHandler: in 10.6 requires you to first retain the save panel (or reference it in the completion handler block, which implicitly retains it). Otherwise, the panel will briefly show and then correctly be autoreleased. For applications linked on 10.7 and later, this problem has been fixed and applications will no longer need to explicitly retain panel.

For applications linked on 10.7 and higher, URLs vended from the NSSavePanel may now contain non-file schemes (i.e.: something other than NSURLFileScheme). This will happen if they don't have a physical backing file, such as things in the SHARED section. For compatibility, certain delegate methods (such as panel:shouldEnableURL:) will only be called with non-file URLs, and all other URLs will automatically be enabled. It is recommended that the delegate always enable non-file URLs unless some other behavior is desired.

NSSavePanel and NSOpenPanel have an updated look to match Finder. This look contains four view options (includes cover flow and group options), while the classic look has only three view options and no cover flow. Carbon based applications will always use the classic option. If you have an application which crashes in the open or save panel, you can opt it out of using the new look with a user default: NSSavePanelUseFinderKit NO. Please log bugs reporting any issues encountered.

In Auto Save applications, closing a yet-unsaved document window brings up a combined alert/save panel. Since this panel includes both a "Don't Save" button and a file browser, an ambiguity is created for the keyboard shortcut cmd-d, which can stand both for "Don't Save" and "Desktop". In order to make sure users do not unintentionally lose their unsaved documents, we have chosen to go with "Desktop" for cmd-d, and have switched to using cmd-delete for "Don't Save." This new shortcut should also work in most non Auto Save applications.


NSPopover

AppKit now has a new class for presenting popovers. The header contains lots of self documentation.

NSPopover Delegate Methods

When a popover is closed and the delegate should be consulted, the following sequence of events occurs. First, the -popoverShouldClose: method will be invoked on the delegate if implemented. If the delegate returns NO, the popover will not be closed. Otherwise, if the popover instance implements -popoverShouldClose:, that method will be invoked on the popover instance and if the method returns NO, the popover will not be closed.

Due to a bug in NSPopover, however, implementing -popoverShouldClose: on the NSPopover instance will not behave as expected. It is recommended that for now -popoverShouldClose: should not be implemented on the popover instance.

Additionally, -popoverShouldClose: is currently incorrectly invoked on the popover delegate and instance if implemented when the popover detaches to a window. You should never return NO in this case.

NSPopover Bindings

A bug in AppKit prevents the NSPopover bindings from working in the initial release of 10.7.


NSButtonCell - New Bezel Style

NSButtonCell has a new bezel style: NSInlineBezelStyle. This bezel style creates a special inline look for tables and lists, but can be used in any appropriate location. When used inside a source list table view, it will have automatic color attributes applied to it to appear as an unread indicator (or colored button). To make the button appear as a label (ie: not respond to clicks), call [buttonCell setHighlightsBy:0]. -cellSizeForBounds: properly implements the minimum size required for an unread indicator if the cell has text. If the cell has an image, it will default to a circular size.

NSWindow - Child Windows Ordering Out

For applications linked on 10.7 and later, ordering out a child window will now first remove itself from its parent window. Previously, ordering out a child window would implicitly order out the parent window too.



NSColor

NSColor now has two class methods to enable creating colors with sRGB or its gray counterpart color spaces. These create colors with colorSpaceName = NSCustomColorSpace and the appropriate color space (+[NSColorSpace sRGBColorSpace] or +[NSColorSpace genericGamma22GrayColorSpace]).

These days the sRGB color space is a better match to the default Mac display environment than the calibrated color space. For this reason in general we recommend using sRGB color space for your images and in your colors—this can lead to better fidelity and performance.

In NSColor there is a change in the treatment of individual component accessor methods such as redComponent, etc. In 10.6 and earlier, these are defined to work only on colors in the named colorspaces NSCalibratedRGB and NSDeviceRGB. (Same for component accessors in the gray and CMYK color spaces.)

In 10.7, these component accessors work on any custom colorspace color which has the appropriate underlying colorspace model. So for example this means it's legal to send -redComponent to a color created with the new sRGB creating method.

It's important to note that this does not mean these methods will start doing any conversion; as before, they will work only in cases where no color space conversion is needed.


NSColorSpace

The initialization method initWithColorSyncProfile: now accepts ColorSyncProfileRef as well as CMProfileRef as before. Whatever was passed into this initializer will also be returned when the accessor method colorSyncProfile is invoked.

If colorSyncProfile is invoked on instances not created with initWithColorSyncProfile:, then colorSyncProfile will return an instance of ColorSyncProfileRef for Lion-linked apps, and an instance of CMProfileRef for earlier apps.

Note that ColorSync APIs that take ColorSyncProfileRef also take CMProfileRefs as arguments, so the intermixing of the two types should work fine.


NSTextStorage

The text system will now use UTF-8 rather than the default C-string encoding for any documents that otherwise have no encoding tags or hints, and which look like they are valid UTF-8.



Vertical Text

Mac OS X 10.7 introduces the complete vertical text writing support through out the Cocoa Text System. There are several API additions for application developers to take advantage of this typographical enhancement.

NSVerticalGlyphFormAttributeName

This NSAttributedString attribute controls the glyph appearance based on the layout orientation value specified. When non-nil, the value must be an NSNumber containing an integer value. The value 1 indicates that the run of text is to be rendered with the vertical font variant if available. The layout and rendering engine in the Cocoa Text System substitutes the NSFontAttributeName value with its vertical font returned from -verticalFont method.
0 indicates forces horizontal font regardless of higher-level layout orientation settings.

NSFont

NSFont now can manage vertical variant instances if the underlying font typeface can handle the text layout orientation. With vertical font, the text matrix is rotated 90 degrees counter-clock wise. Also, vertical glyph substitution typographic features are enabled by default. There are two API for managing vertical font instances introduced.
- (NSFont *)verticalFont;
- (BOOL)isVertical;

Text Layout Orientation

The orientation of lines rendered by NSTextView is determined by the text layout orientation property. NSLayoutManager accesses the orientation value for each view via NSTextContainer. It is allowed to have different orientation settings for views sharing a single layout manager. A new protocol, NSTextLayoutOrientationProvider, and its value enum type, NSTextLayoutOrientation, are declared in NSLayoutManager.h. NSTextContainer and NSTextView conform to NSTextLayoutOrientationProvider protocol.
@protocol NSTextLayoutOrientationProvider
- (NSTextLayoutOrientation)layoutOrientation;
@end
In addition to -layoutOrientation method, NSTextView implements -setLayoutOrientation: and -changeLayoutOrientation: methods.
- (void)setLayoutOrientation:(NSTextLayoutOrientation)theOrientation;
- (void)changeLayoutOrientation:(id)sender;

Reading and writing text layout orientation

A new text document attribute key, NSTextLayoutSectionsAttribute, is introduced for reading and writing the text layout orientation setting for each text view. The value is an array of dictionaries. Each dictionary specifies the text layout orientation for a range of string. Two keys, NSTextLayoutSectionOrientation and NSTextLayoutSectionRange, are provided for the dictionary content.

I-Beam cursor for vertical text layout orientation

When the mouse pointer enters a text area with the vertical text layout orientation, the shape should be changed to an I-beam cursor that is 90 degrees rotated. NSCursor has a new factory method for the pointer type.
+ (NSCursor *)IBeamCursorForVerticalLayout;

NSRulerView value translation

NSRulerView has a built-in facility for translating its client's coordinate system location to the ruler value. With the introduction of the vertical text layout orientation that rotates the bounds of NSTextView, the current logic is ambiguous for determining the intended axis for representing the ruler value. For example, with a vertical text view, a vertical ruler wants to translate the x coordinate value. In order to disambiguate the client view intention, NSRulerView now has an explicit value translation delegate methods.
- (CGFloat)rulerView:(NSRulerView *)ruler locationForPoint:(NSPoint)aPoint;
- (NSPoint)rulerView:(NSRulerView *)ruler pointForLocation:(CGFloat)aLocation;

Integrated Quick Look support for NSTextView

NSTextView in Mac OS X 10.7 has built-in Quick Look support for attachments. There are three new NSTextView APIs for working with the Quick Look preview panel.
- (IBAction)toggleQuickLookPreviewPanel:(id)sender;
- (NSArray *)quickLookPreviewableItemsInRanges:(NSArray *)ranges;
- (void)updateQuickLookPreviewPanel;
In the meantime, NSTextViewDelegate has a new optional delegate method, -textView:URLForContentsOfTextAttachment:atIndex:, for managing the content URL for NSTextAttachment. This delegate method can enable both Quick Look and double-click action.
- (NSURL *)textView:(NSTextView *)view URLForContentsOfTextAttachment:(NSTextAttachment *)attachment atIndex:(NSUInteger)charIndex;

NSLayoutManager glyph rendering

NSLayoutManager in Mac OS X 10.7 deprecated the glyph rendering primitive, -showPackedGlyphs:length:glyphRange:atPoint:font:color:printingAdjustment:, and introduced the following new method taking a CGGlyph array.
- (void)showCGGlyphs:(const CGGlyph *)glyphs
positions:(const NSPoint *)positions
count:(NSUInteger)glyphCount
font:(NSFont *)font
matrix:(NSAffineTransform *)textMatrix
attributes:(NSDictionary *)attributes
inContext:(NSGraphicsContext *)graphicsContext;
-[NSLayoutManager drawGlyphsForGlyphRange:atPoint:] dynamically determines which primitive to invoke based on the instance method implementation. The new primitive method is invoked regardless of the framework linkage version unless:

- the instance has an overridden implementation of the old primitive and does not have an overridden implementation of the new primitive
- the NSLayoutManagerForcesShowPackedGlyphs preference value is true


Attributed Editing with NSTokenFieldCell

NSTokenFieldCell no longer forces -allowsEditingTextAttributes to return YES.


Marked underline color rendering with NSTextView

When rendering NSMarkedClauseSegmentAttributeName, NSTextView no longer forces black NSUnderlineColorAttributeName. Since NSLayoutManager uses NSForegroundColorAttributeName in absence of NSUnderlineColorAttributeName, the NSMarkedClauseSegmentAttributeName matches the base color.


Placeholder string in focused NSTextField

The placeholder string is now displayed even with focused NSTextField.


NSSearchField

The search menu now can be popped up with the Command-Down Arrow key combination while the field has focus.


NSSecureTextField

The caps lock and num lock indicators are now rendered with a color derived from the field's text color.




Enhanced Text System Formatting controls in inspector bar

In place of the current formatting controls in the ruler accessory view, we're introducing an inspector bar containing text formatting controls much like iWork applications. This new option is available through -[NSTextView usesInspectorBar].


NSRulerView mouse handling

NSRulerView now routes mouse events occurring in the marker area in addition to the ruler area to its clients via -rulerView:handleMouseDown:. It now sends both NSLeftMouseDown and NSRightMouseDown. It is the client view's responsibility to determine the right behavior.


NSTextTab markers in ruler view

The right mouse down events reveal a context menu for choosing text tab type in Mac OS X Lion.


NSFontCollection

We introduced a dedicated class for representing font collections, NSFontCollection, previously accessed partly through NSFontManager. NSFontCollection is a bag of NSFontDescriptor. You can publicize the font collection as a named collection and presented through UI such as the font panel & Font Book.




Notes specific to Mac OS X 10.6

NSApplication presentationOptions API

New “presentationOptions” API on NSApplication provides a Cocoa replacement for the existing Carbon “SystemUIMode” API (whose usage was outlined in TN2062: "Guide to Creating Kiosks on Mac OS X"). Using this API, an application can request certain behaviors for system user interface elements (the Dock, menu bar, task switcher, etc.) that will apply when the application is the active application, and can also observe changes to these UI element behaviors that are made by other applications when they are active.

The supported set of options is expressed using a new NSApplicationPresentationOptions bitmask type declared in NSApplication.h:
/* Flags that comprise an application's presentationOptions */
enum {
NSApplicationPresentationDefault = 0,
NSApplicationPresentationAutoHideDock = (1 << 0), // Dock appears when moused to
NSApplicationPresentationHideDock = (1 << 1), // Dock is entirely unavailable
    NSApplicationPresentationAutoHideMenuBar            = (1 <<  2),    // Menu Bar appears when moused to
NSApplicationPresentationHideMenuBar = (1 << 3), // Menu Bar is entirely unavailable
    NSApplicationPresentationDisableAppleMenu           = (1 <<  4),    // all Apple menu items are disabled
NSApplicationPresentationDisableProcessSwitching = (1 << 5), // Cmd+Tab UI is disabled
NSApplicationPresentationDisableForceQuit = (1 << 6), // Cmd+Opt+Esc panel is disabled
NSApplicationPresentationDisableSessionTermination = (1 << 7), // PowerKey panel and Restart/Shut Down/Log Out disabled
NSApplicationPresentationDisableHideApplication = (1 << 8), // Application "Hide" menu item is disabled
NSApplicationPresentationDisableMenuBarTransparency = (1 << 9) // Menu Bar's transparent appearance is disabled
};
typedef NSUInteger NSApplicationPresentationOptions;
This type is used by the following new NSApplication API methods.

The following gets or sets the presentationOptions that should be in effect for the system when this application is the active application. Only certain combinations of NSApplicationPresentationOptions flags are allowed, as detailed in the AppKit Release Notes and the reference documentation for -setPresentationOptions:. When given an invalid combination of option flags, -setPresentationOptions: raises an exception.
- (NSApplicationPresentationOptions)presentationOptions;
- (void)setPresentationOptions:(NSApplicationPresentationOptions)newOptions;
Returns the set of application presentation options that are currently in effect for the system. These are the presentation options that have been put into effect by the currently active application.
- (NSApplicationPresentationOptions)currentSystemPresentationOptions;
For most applications, the initial value of presentationOptions is NSApplicationPresentationDefault. If an application’s Info.plist specifies a value for the LSUIElement key as described in TN2062, the application’s presentationOptions is initialized to an equivalent combination of NSApplicationPresentationOptions flags instead.

Both presentationOptions and currentSystemPresentationOptions are KVO-observable. A client that observes currentSystemPresentationOptions will receive notifications when (1) the client is the active application, and makes a change itself using either -setPresentationOptions: or SetSystemUIMode(), (2) another application is active, and makes such changes of its own, and (3) making a different application active causes the active set of presentation options to change. Notifications are not sent for non-changes (e.g. when a different application becomes active, but has the same set of presentation options as the previously active application).

The presentationOptions / currentSystemPresentationOptions API interoperates with the SystemUIMode API (both reflect the same underlying state). The combination of option flags that you pass to -setPresentationOptions: must satisfy the same set of requirements that apply to the SystemUI “Mode” and “Options” combinations as described in TN2062. Specifically:

NSApplicationPresentationAutoHideDock and NSApplicationPresentationHideDock are mutually exclusive: You may specify one or the other, but not both.

Likewise, NSApplicationPresentationAutoHideMenuBar and NSApplicationPresentationHideMenuBar are mutually exclusive: You may specify one or the other, but not both.

If you specify NSApplicationPresentationHideMenuBar, it must be accompanied by NSApplicationPresentationHideDock. If you specify NSApplicationPresentationAutoHideMenuBar, it must be accompanied by either NSApplicationPresentationHideDock or NSApplicationPresentationAutoHideDock.

If you specify any of NSApplicationPresentationDisableProcessSwitching, NSApplicationPresentationDisableForceQuit, NSApplicationPresentationDisableSessionTermination, or NSApplicationPresentationDisableMenuBarTransparency, it must be accompanied by either NSApplicationPresentationHideDock or NSApplicationPresentationAutoHideDock.

When -setPresentationOptions: receives a newOptions parameter that does not conform to these requirements, it raises an NSInvalidArgumentException.


NSApplicationPresentationOptions and FullScreen Mode

On 10.6, NSView's -enterFullScreenMode:withOptions: API accepts a new option, whose value specifies the NSApplicationPresentationOptions to use while the view is operating in full-screen mode:
NSString *NSFullScreenModeApplicationPresentationOptions;  // NSNumber numberWithUnsignedInteger:(NSApplicationPresentationOptions flags)
Note that the presence or absence of this option affects whether entering full-screen mode for a view captures displays, and whether the NSFullScreenModeSetting option is permitted, as follows:

When the options dictionary you pass to -enterFullScreenMode:withOptions: does not contain a value for NSFullScreenModeApplicationPresentationOptions, AppKit does not alter the presentation options that were previously in effect when taking the view full-screen. AppKit also captures either the destination screen, or (if you specify YES for NSFullScreenModeAllScreens) all attached screens, consistent with the behavior on 10.5. ("Capturing" a screen takes exclusive control of it, in the same sense that is provided for by CGDisplayCapture() and related Core Graphics APIs.)

When the options dictionary you pass to -enterFullScreenMode:withOptions: does contain a value for NSFullScreenModeApplicationPresentationOptions, AppKit does not capture any displays, since doing so would prevent showing of presentationOptions-controlled UI elements such as the menu bar and Dock. (Because displays are not captured in this case, and the app therefore doesn’t hold exclusive ownership of them, the NSFullScreenModeSetting option is disallowed; specifying it when NSFullScreenModeApplicationPresentationOptions is also specified will cause -enterFullScreenMode:withOptions: to raise an exception.) AppKit puts the requested NSApplicationPresentationOptions value into effect when switching the view into full-screen mode, and will restore the previously active NSApplication presentationOptions setting when the view exits from full-screen mode via -exitFullScreenModeWithOptions:. Even if you don’t wish to change the NSApplication presentationOptions setting when entering full-screen mode, you can pass [NSApp presentationOptions] for the NSFullScreenModeApplicationPresentationOptions key as a means of preventing screen capture, if desired.


NSView -enterFullScreenMode:withOptions: API Changes

On 10.5, NSView’s -enterFullScreenMode:withOptions: method would throw an exception if the receiving view wasn’t in a window, as might be the case for an offscreen view that’s created exclusively for the purpose of being presented fullscreen. This was an unintentional limitation. On 10.6, -enterFullScreenMode:withOptions: can now be sent to a view for which [view window] == nil. For applications that must also run on 10.5, a simple workaround is to place the view in an offscreen dummy window.


Implications of NSWindow and NSScreen Color Space support for NSViews

Most views draw into their window’s backing store, and are therefore drawn in their window’s assigned NSColorSpace. A few kinds of views are instead drawn into a separate backing store called a “surface” that’s composited atop the window, to facilitate hardware (GPU) accelerated rendering. This includes OpenGL views, as well as other kinds of views that render via OpenGL as a detail of their implementation (currently, views that display a Core Animation CALayer tree, QuickTime QTMovieViews, Quartz Composer QCViews, and ImageKit IKImageBrowserViews, among others).

For surface-based views, AppKit sets the surface’s color space to match, and track changes to, the window’s color space. Applications should therefore look to NSWindow’s -setColorSpace: and -setDisplaysWhenScreenProfileChanges: APIs to determine the color space used for surface-based rendering in a given window.

Animation Support for Integer Values

On 10.6, integer-valued properties can now be animated using the CAAnimation-based “animator” proxy API. All integer types are supported: BOOL values as well as signed and unsigned variants of char, short, int, long, and long long may be animated. As with animating any custom property, be sure to associate a suitable CAAnimation with the property name key so that messaging through the target’s animator to set a new value will actually animate. For example, an NSImageView’s “imageFrameStyle” property can be configured to animate when changed through the ImageView’s animator like so:
    [imageView setAnimations:[NSDictionary dictionaryWithObject:[CABasicAnimation animation] forKey:@"imageFrameStyle"]];
Any new value set through the animator will then trigger animation:
    [imageView setImageFrameStyle:NSImageFramePhoto];
[[imageView animator] setImageFrameStyle:NSImageFrameButton];
As when animating integer-typed properties of Core Animation CALayers, the animation proceeds in discrete steps.


Animation Support for NSColor Values

The 10.5 AppKit Release Notes incorrectly stated that NSColor-valued properties could be animated. While applications that happened to link against the Quartz framework had the benefit of this capability, it was not provided as an intrinsic AppKit feature.

Support for animating NSColor-valued properties has been added to AppKit for 10.6, so the code example that was presented in the 10.5 AppKit Release Notes (under the heading “NSAnimatablePropertyContainer protocol”), will now work as intended on 10.6, irrespective of whether the application links against the Quartz framework.
@implementation MyView
+ (id)defaultAnimationForKey:(NSString *)key {
if ([key isEqualToString:@"borderColor"]) {
// By default, animate border color changes with simple linear interpolation to the new color value.
return [CABasicAnimation animation];
} else {
// Defer to super's implementation for any keys we don't specifically handle.
return [super defaultAnimationForKeyKey:key];
}
}
@end


Animation Evaluation Scheduling Changes

On 10.5, NSView and NSWindow property animations that were scheduled through the view’s/window’s “animator”, and that were evaluated by AppKit (rather than being delegated to Core Animation for threaded asynchronous evaluation), were scheduled for updating in the NSDefaultRunLoopMode.

On 10.6, such animations are now evaluated in NSRunLoopCommonModes.

“animator”-initiated animations that are evaluated by AppKit include:

- all NSWindow property animations
- all NSView property animations, for views that aren’t layer-backed
- a view’s frameSize, for a layer-backed view whose layerContentsRedrawPolicy (new in 10.6) equals the default value of NSViewLayerContentsRedrawDuringViewResize
- animation of any custom property that’s added by an NSView or NSWindow subclass, or of any existing NSView property that doesn’t readily map to a corresponding CALayer property (and therefore cannot be delegated to Core Animation for evaluation)


Changes to AppKit’s CATransaction Usage

AppKit’s NSAnimationContext “groupings” serve a very similar purpose to Core Animation’s CATransactions, and in fact invoking [NSAnimationContext beginGrouping] performs a [CATransaction begin] (in addition to doing other work), and invoking [NSAnimationContext endGrouping] performs a [CATransaction commit].

On 10.5, AppKit also began and committed CATransactions in the course of its own layer-tree management activities, notably during certain modal event tracking operations, and to suppress implicit animations during non-animated view backing layer property updates.

On 10.6, we’ve removed this automatic CATransaction usage to avoid interfering with a client’s own CATransaction management -- in particular, so that changes a client application makes expecting an implicit CATransaction will not be unexpectedly delayed. AppKit instead makes very sparing use of [CATransaction flush], flushing only before it initiates an explicit layer tree draw, and on each cycle through a modal event tracking loop. This change makes client application usage of [CATransaction flush] more reliable as a means to force layer tree changes to be committed to the render tree. The general philosophy now is to leave creation of explicit CATransactions for the exclusive use of client applications.


Concurrent View Drawing

To help applications with especially high drawing loads to more effectively leverage multi-core hardware, while seeking to minimize adoption requirements for view code that conforms to AppKit’s established threading conventions, 10.6 introduces a simple model for concurrent view drawing, and accompanying control API. Our findings to date have indicated that concurrent view drawing is of benefit only in some circumstances, and applications may experience a net performance decrease in cases where parallelism gains do not outweigh the additional overhead involved, so use of concurrent view drawing should be accompanied by performance investigations to measure results. Forward-looking applications may want to consider the implications of the thread safety model, so as to make design decisions that are compatible with potentially leveraging this feature in the future.

While the new model’s requirements for safely concurrent -- meaning threaded -- view drawing are comparatively simple (see “Concurrent View Drawing - Thread Safety Implications” below), existing UIs cannot be assured of 100% thread safety out of the box, so we provide an API mechanism to enable clients to opt in to concurrent view drawing when desired.

1. Concurrent view drawing is enabled by default for NSWindow instances, but disabled by default for all NSView instances within those windows. (We might in a future release decide to enable canDrawConcurrently by default for instances of particular generally thread-safe classes, such as simple controls.)  From this initial state, clients can enable concurrent drawing for individual views, or use disabling at the NSWindow level as a master switch that can be thrown to suppress concurrent drawing in case problems precluding its use are encountered.

The view setting is not recursive in its effect; it specifies the threadability of drawing for a given view, not for the view’s entire subtree. The view’s descendants may have their threadability configured independently.

Our recommendation is that potential clients start by seeking out a handful of the more costly custom views in their UIs (typically those that draw complex content and/or cover large areas) and try enabling concurrent drawing for those.  Additional views can be marked as safe for concurrent drawing to give the view drawing system further scheduling flexibility (this may be particularly worthwhile for descendants of the primary threadable views, due to implicit back-to-front drawing order dependencies), but switching this on for even a few of a UI’s “heavier” leaf views is often sufficient to reap the bulk of the potential performance benefit, where such benefit exists.

To the NSWindow class, we’ve added a Boolean “allowsConcurrentViewDrawing” property with the following accessor methods:
/* Reports whether threading of view drawing is enabled for this window.  Defaults to YES.
*/
- (BOOL)allowsConcurrentViewDrawing;
/* Sets whether threading of view drawing should be enabled for this window.  Defaults to YES.  When this is set to YES,
AppKit's view system is allowed to perform -drawRect: activity for the window's views on threads other than the main thread,
for views that have canDrawConcurrently == YES. When this is set to NO, the window's views will be drawn serially as on 10.5
and earlier, even though some of the views may have canDrawConcurrently == YES.
*/
- (void)setAllowsConcurrentViewDrawing:(BOOL)flag;
To the NSView class, we’ve added a “canDrawConcurrently” property with the following accessor methods:
/* Reports whether AppKit may invoke the view's -drawRect: method on a background thread, where it would otherwise be invoked
on the main thread. Defaults to NO.
*/
- (BOOL)canDrawConcurrently;
/* Sets whether AppKit may invoke the view's -drawRect: method on a background thread, where it would otherwise be invoked
on the main thread. Defaults to NO for most kinds of views. May be set to YES to enable threaded drawing for a particular
view instance. The view's window must also have its "allowsConcurrentViewDrawing" property set to YES (the default) for
threading of view drawing to actually take place.
*/
- (void)setCanDrawConcurrently:(BOOL)flag;
2. The -viewWillDraw recursion that happens at the beginning of a “-display...” pass will still be executed on the main thread, and will complete serially as before.  This allows the potentially view-hierarchy-modifying on-demand layout activity for which -viewWillDraw is designed to complete in a safe and sane single-threaded environment, and is not expected to pose a significant limitation because -viewWillDraw activity is meant to be kept lightweight compared to drawing.

3. Any view instance that has canDrawConcurrently == YES, in a window that allowsConcurrentViewDrawing, is eligible to have its drawing automatically delegated to a background thread/operation at AppKit’s discretion.  AppKit sees to it that the concurrent view drawing operations it spawns have their completion serialized as needed, such that overlapping views (both siblings and ancestor/descendant chains) are rendered with correct results.

4. Servicing of normal view tree drawing (“-displayIfNeeded” activity) will still be initiated and managed by the main thread, which will seek to load-balance view drawing work by spawning background operations for certain eligible views.   Decisions about which views to spin off to background operations may be based in part on view draw measurements, among other potential factors.  The presence or absence of overlapping sibling views will influence the partitioning as well.  The main thread will complete remaining view tree drawing, and then block on completion of any background view drawing operations it spawned before flushing the window and returning control to the runloop.

Concurrent View Drawing - Thread Safety Implications

The simplifying implications of this last point are important: AppKit must force a synchronization point before the window flush anyway, to allow for all contributed drawing into the window to complete before the flush, but this has the added benefit that the runloop on the main thread is guaranteed to be blocked during a drawing pass, including during any background-thread drawing of views in the window.  So excepting side effects wherein the act of drawing a view may modify data it shares in common with another view that may be attempting to draw simultaneously on another thread (though hopefully relatively rare, this is the reason why both view- and window-level disabling API are provided), views that reference data outside themselves largely needn’t be any more concerned about the data changing out from under them than they have been with today’s primarily main-thread-based view drawing.  If view drawing is happening, then model data that the views reference can be relied on not to be changed by event-handling and other main thread activity while that’s happening. Background-thread changes to the model will need to be coordinated with views on the main thread as before, using available mechanisms such as locks, notification queues, -performSelectorOnMainThread:..., or the like.

Concurrent View Drawing - Testing Features and Additional Notes

To facilitate experimenting with this feature, some AppKit user defaults have been added. These are subject to changing or going away entirely, so as with other debug user defaults AppKit provides, do not rely on them in production code.

“NSEnableConcurrentViewDrawing” can be set to NO to disable concurrent view drawing entirely for a process, reverting to Leopard-and-earlier view drawing behavior regardless of the allowsConcurrentViewDrawing and canDrawConcurrently settings on individual windows and views. It defaults to YES otherwise.

“NSConcurrentViewClasses” can be set to a comma-delimited list of view class names to force canDrawConcurrently to be set to YES for all instances of those classes, allowing for preliminary testing of this feature with existing built app executables. Note that the class test used here is intentionally an exact equality test, not an isKindOfClass: test -- so including “NSButton” in the list would not cause instances of subclasses of NSButton to be enabled for concurrent drawing. To force enabling of concurrent drawing for both NSButton and NSPopUpButton instances, one would need to include both those class names in the list.

“NSShowConcurrentViewDrawing” can be set to YES, which frames every concurrently drawn view in green for visual confirmation that concurrent view drawing is operating.

“NSDebugConcurrentViewDrawing” can be set to YES to enable some basic diagnostic console log output related to concurrent view drawing.

“NSConcurrentViewDrawingMinCores” specifies the minimum number of CPU cores that must be available on the host system for AppKit to actually attempt concurrent view drawing. AppKit compares this value to [[NSProcessInfo processInfo] activeProcessorCount]. The default value is 2.

An example of applying some of these defaults in launching TextEdit.app:
/Applications/TextEdit.app/Contents/MacOS/TextEdit -NSShowConcurrentViewDrawing YES -NSConcurrentViewClasses "NSScroller,NSPopUpButton,NSButton"
To assist developers in assessing typical drawing costs of various views, AppKit accumulates basic timing stats for views’ -drawRect: invocations, which are now shown as part of the -_subtreeDescription debug info. Printing a window’s view subtree description in gdb as below shows these stats appended to the line for each view, reporting a minimum, mean (average), and maximum draw time in milliseconds. Draw times of several milliseconds, and especially more than 10ms or so, typically indicate a good potential candidate view for concurrent drawing. Draw times of less than a hundredth of a millisecond show up as 0.00ms. This particular example (TextEdit with an empty document window) does not show any particularly strong candidates for concurrent view drawing.
(gdb) po [[[[[NSApplication sharedApplication] windows] objectAtIndex:0] _borderView] _subtreeDescription]
[ A O P ] h=--- v=--- NSThemeFrame 0x185650 "Untitled" f=(0,0,475,442) b=(-) TIME drawRect: min/mean/max 0.62/0.62/0.62 ms
[ AF ] h=--- v=--- _NSThemeCloseWidget 0x15e840 "Button" f=(8,422,14,16) b=(-) TIME drawRect: min/mean/max 0.04/0.05/0.06 ms
[ AF ] h=--- v=--- _NSThemeWidget 0x15fd00 "Button" f=(50,422,14,16) b=(-) TIME drawRect: min/mean/max 0.06/0.07/0.08 ms
[ AF ] h=--- v=--- _NSThemeWidget 0x15fe50 "Button" f=(29,422,14,16) b=(-) TIME drawRect: min/mean/max 0.04/0.04/0.04 ms
[ A ] h=--- v=--- NSView 0x187840 f=(0,0,475,420) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ AF O ] h=-&- v=-&- ScalingScrollView 0x13c680 f=(0,0,475,420) b=(-) TIME drawRect: min/mean/max 0.03/1.39/2.75 ms
[ AF OGP ] h=--- v=--- NSClipView 0x17c6b0 f=(0,55,460,365) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ AF OG ] h=-&- v=--- NSTextView 0x19db90 f=(0,0,460,365) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ AF O ] h=--- v=--- NSScroller 0x1a2aa0 f=(460,55,15,350) b=(-) TIME drawRect: min/mean/max 0.06/0.07/0.07 ms
[ AF O ] h=--- v=--- NSScroller 0x14c770 f=(-100,-100,479,15) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ AF O ] h=--- v=--- NSRulerView 0x15cf00 f=(0,0,475,55) b=(-) TIME drawRect: min/mean/max 1.51/1.51/1.51 ms
[ P ] h=--- v=--- NSStopTouchingMeBox 0x15a680 "Title" f=(0,0,475,24) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ ] h=--- v=--- NSView 0x1aaa70 f=(0,0,475,24) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ P ] h=--- v=--- NSBox 0x17bfc0 "Title" f=(0,-1,388,24) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ A ] h=--- v=--- NSView 0x14c700 f=(0,0,388,24) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ AF ] h=--- v=--- NSPopUpButton 0x1a9360 "Spacing" f=(194,-1,85,22) b=(-) TIME drawRect: min/mean/max 0.18/0.19/0.21 ms
[ AF ] h=--- v=--- NSPopUpButton 0x158b80 "Styles" f=(2,-1,85,22) b=(-) TIME drawRect: min/mean/max 0.08/0.10/0.11 ms
[ AF ] h=--- v=--- NSPopUpButton 0x162520 "Lists" f=(279,-1,85,22) b=(-) TIME drawRect: min/mean/max 0.08/0.09/0.10 ms
[ AF ] h=--- v=--- NSSegmentedControl 0x1807f0 f=(88,-3,105,25) b=(-) TIME drawRect: min/mean/max 0.40/0.46/0.52 ms
[ P ] h=--- v=--- NSBox 0x164830 "Title" f=(388,-1,88,24) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ ] h=--- v=--- NSView 0x1595a0 f=(0,0,88,24) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ P ] h=--- v=--- NSBox 0x188980 "Title" f=(0,0,79,20) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ A ] h=--- v=--- NSView 0x169af0 f=(0,0,79,20) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ P ] h=&-- v=--- NSBox 0x1b6370 "Title" f=(0,0,79,20) b=(-) TIME drawRect: min/mean/max 0.23/0.23/0.23 ms
[ ] h=--- v=--- NSView 0x1673c0 f=(3,3,73,14) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ A ] h=--- v=--- NSTabWell 0x180330 f=(4,1,15,13) b=(-) TIME drawRect: min/mean/max 0.02/0.02/0.02 ms
[ A ] h=--- v=--- NSTabWell 0x1847b0 f=(20,1,15,13) b=(-) TIME drawRect: min/mean/max 0.02/0.02/0.02 ms
[ A ] h=--- v=--- NSTabWell 0x1b8bf0 f=(37,1,15,13) b=(-) TIME drawRect: min/mean/max 0.02/0.02/0.02 ms
[ A ] h=--- v=--- NSTabWell 0x1a7a70 f=(54,1,15,13) b=(-) TIME drawRect: min/mean/max 0.01/0.01/0.01 ms
A=autoresizesSubviews, C=canDrawConcurrently, D=needsDisplay, F=flipped, G=gstate, H=hidden (h=by ancestor),
O=opaque, P=preservesContentDuringLiveResize, S=scaled/rotated, W=wantsLayer (w=ancestor wantsLayer), #=has surface
To provide more meaningful data, only full redraws of a view will contribute to these timing stats. Resizing a view’s window is a good way to prompt full redraws so as
to gather draw time stats, for views that are not specifically optimized preserve content using the preservesContentDuringLiveResize mechanism.

Simple standard AppKit NSControls that don’t use dataSources, and don’t otherwise rely on data outside their own at draw time, are likely to be safe for canDrawConcurrently enabling, but have not all been formally vetted for concurrent drawing safety yet. For now, it is recommended that applications restrict enabling of canDrawConcurrently to their own custom view classes.

Layer-backed view drawing does not yet take advantage of the ability to draw views concurrently. This is a possible future direction.



NSView -setNeedsDisplay: and -setNeedsDisplayInRect: Activity During Drawing

One Mac OS X 10.5, sending -setNeedsDisplay: or -setNeedsDisplayInRect: to a view might not produce the requested redraw if the view’s window is in the middle of a recursive view display operation when the request arrives. On 10.6, rectangles invalidated during display are accumulated in a side region that will be serviced in a subsequent window display cycle. This new behavior is on by default for applications linked on or after 10.6. For debugging purposes, it can be disabled by running with the “NSWindowsUseDeferredNeedsDisplayRegion” user default set to NO, or forced on by running with this user default set to YES. If you see a drawing glitch or drawing performance issue that goes away when running with “-NSWindowsUseDeferredNeedsDisplayRegion NO”, please file a Radar.



NSView -viewWillDraw and Offscreen Drawing

On Mac OS X 10.5, the newly introduced -viewWillDraw method was only invoked when views were drawn into their window. On 10.6, -viewWillDraw is also invoked when either -cacheDisplayInRect:toBitmapImageRep: or -displayRectIgnoringOpacity:inContext: is used to draw views to an alternate destination.

10.6 also fixes an issue that sometimes prevented -viewWillDraw messages from being sent to views that were being printed.


Layer-Backed NSOpenGLViews and Pixel Formats

On 10.6, the layer-backed operating mode of an NSOpenGLView now inherits pixel format attributes from the view’s existing OpenGL context. If the view does not have an existing OpenGL context (e.g. because the view begins life in layer-backed mode, without having first operated in conventional view compositing mode), AppKit checks whether the view provides a -pixelFormat method. If so, AppKit invokes the view’s -pixelFormat method and examines the result to construct a compatible OpenGL pixel format and context for use in layer-backed mode.

NSOpenGLLayer

Mac OS X 10.6 introduces a new “NSOpenGLLayer” class, that supports fully generalized OpenGL usage in layer-backed mode.

On Mac OS X 10.5, AppKit supported OpenGL rendering in layer-backed mode only through the use of NSOpenGLView. When an app set wantsLayer to YES for an NSOpenGLView or one of its ancestor views, AppKit would automatically set up a suitable backing layer and OpenGL context for rendering, using the view’s existing, surface-backed OpenGL context (if any) as a “share” context (so that the same texture IDs and other OpenGL object references would carry over to layer-backed mode).

On Mac OS X 10.6, the addition of NSOpenGLLayer makes it possible for any arbitrary NSView (not necessarily derived from NSOpenGLView) to render via OpenGL in layer-backed mode -- much as an NSOpenGLContext can be bound to any arbitrary NSView in non-layer-backed mode. The essential requirements for enabling an arbitrary view to render via OpenGL in layer-backed mode are as follows:

1. Subclass NSOpenGLLayer.

2. In your NSOpenGLLayer subclass, override -openGLPixelFormatForDisplayMask: to return an autoreleased NSOpenGLPixelFormat suitable for the specified set of displays and the content you want to render.

3. If you need your context to share OpenGL objects with another, existing context, you may also want to override -openGLContextForPixelFormat: so that you can specify the desired “share” context when creating the new context. (You should return an autoreleased NSOpenGLContext.) Note that you can ask an NSOpenGLLayer for its associated view, which may help you to obtain a pointer to the desired “share” context.

4. In your view class, override -makeBackingLayer to return an autoreleased instance of your NSOpenGLLayer subclass. For example:
- (CALayer *)makeBackingLayer {
return [[[CubeViewOpenGLLayer alloc] init] autorelease];
}
5. In your view class, override -setFrameSize: to make your layer’s openGLContext current and -update it, and to re-specify the OpenGL viewport and GL_MODELVIEW / GL_PROJECTION transforms as you would normally do when an OpenGL view is resized. You can use this same technique to retrieve the layer’s openGLContext anytime you need to make it current or message it for any other reason:
- (void)setFrameSize:(NSSize)newSize {
[super setFrameSize:newSize];
    NSOpenGLContext *openGLContext = [(NSOpenGLLayer *)[self layer] openGLContext];
    [openGLContext makeCurrentContext];
[openGLContext update];
[self reshape]; // Assume we've defined a -reshape method that calls glViewport()
// and updates the GL_PROJECTION and GL_MODELVIEW matrices.
}
There are a number of additional issues involved in implementing a fully general OpenGL view -- one that can be switched into and out of layer-backed mode at will. The necessary techniques are likely to be illustrated by a future update to the LayerBackedOpenGLView code sample.



Layer Contents Placement and Redraw Policy API

Mac OS X 10.6 introduces new API on NSView, that clients can use to dramatically improve the performance of layer-backed view resize animations. The new NSView methods are accessors for two new properties: layerContentsRedrawPolicy and layerContentsPlacement.
- (NSViewLayerContentsRedrawPolicy)layerContentsRedrawPolicy;
- (void)setLayerContentsRedrawPolicy:(NSViewLayerContentsRedrawPolicy)newPolicy;
- (NSViewLayerContentsPlacement)layerContentsPlacement;
- (void)setLayerContentsPlacement:(NSViewLayerContentsPlacement)newPlacement;
Prior to 10.6, AppKit did not provide any way for implementers or users of views to express how a view’s drawn content would be affected by resizing the view, and whether redrawing of the view’s content would be required. Without such information, AppKit has had to assume that a view’s content could change in arbitrary ways when the view was resized, and must be redrawn at each intermediate frame of an animated resize. In layer-backed mode, this forces AppKit to divert view resizing animations off of Core Animation’s asynchronous render+evaluation thread, and implement the resizing using timer-driven animation on AppKit’s main runloop -- yielding both reduced performance and sometimes visually unpleasant synchronization issues when AppKit-driven resize animations are combined with asynchronous, Core Animation-evaluated animations.

Using this new API, the implementation of a view class, or code that uses instances of a particular view type, can control how resizing is handled when the view is layer-backed. The primary enabler for this is the new layerContentsRedrawPolicy property, which has the following declaration and allowed values:
enum {
NSViewLayerContentsRedrawNever = 0,
NSViewLayerContentsRedrawOnSetNeedsDisplay = 1,
NSViewLayerContentsRedrawDuringViewResize = 2,
NSViewLayerContentsRedrawBeforeViewResize = 3
};
typedef NSInteger NSViewLayerContentsRedrawPolicy;
The semantics of these modes are:
NSViewLayerContentsRedrawNever—Leave the layer's contents alone. Never mark the layer as needing display, or draw the view's contents to the layer. This is the way AppKit treats developer-provided layers on both 10.5 and 10.6.
NSViewLayerContentsRedrawOnSetNeedsDisplay—Map view -setNeedsDisplay...: activity to the layer, and redraw affected layer parts by invoking the view's -drawRect:, but don't mark the view or layer as needing display when the view's size changes.
NSViewLayerContentsRedrawDuringViewResize—Resize the layer and redraw the view to the layer when the view's size changes. If the resize is animated, AppKit will drive the resize animation itself and will do this resize+redraw at each step of the animation. Affected parts of the layer will also be redrawn when the view is marked as needing display. (This mode is a superset of NSViewLayerContentsRedrawOnSetNeedsDisplay.) This is the way we treat AppKit-generated view backing layers today.
NSViewLayerContentsRedrawBeforeViewResize—Resize the layer and redraw the view to the layer when the view's size changes. This will be done just once at the beginning of a resize animation, not at each frame of the animation. Affected parts of the layer will also be redrawn when the view is marked as needing display. (This mode is a superset of NSViewLayerContentsRedrawOnSetNeedsDisplay.)


For a view that has no associated layer, or that has been assigned a developer-provided layer using NSView’s -setLayer: API, the default layerContentsRedrawPolicy is NSViewLayerContentsRedrawNever, with an accompanying layerContentsPlacement of NSViewLayerContentsPlacementScaleAxesIndependently. This instructs AppKit that it is not allowed to replace the layer’s content, and provides the same content placement as CALayer’s default contentsGravity setting of kCAGravityResize.

For a view that has acquired an AppKit-generated backing layer, AppKit sets the view’s layerContentsRedrawPolicy to a default of NSViewLayerContentsRedrawDuringViewResize, forcing the view’s content to be continually redrawn into the view’s backing layer during animated resizing of the view, which produces strictly correct but not optimally performant results.

If you know that redrawing at each animation frame is not necessary to produce correctly rendered results for a particular view, or are willing to accept an approximation of the view’s intermediate appearance during potentially brief animations in exchange for an animation performance and smoothness benefit, you can change the view’s layerContentsRedrawPolicy to one of the other supported values. When doing this, you should also specify the desired layerContentsPlacement for the view. The layerContentsPlacement determines how the backing layer’s existing cached content image will be mapped into the layer as the layer is resized. (It is analogous to, and underpinned by, CALayer’s contentsGravity property, as indicated by the comments below.)

The provided layerContentsPlacement values are as follows:
enum {
NSViewLayerContentsPlacementScaleAxesIndependently = 0, // equivalent to kCAGravityResize
NSViewLayerContentsPlacementScaleProportionallyToFit = 1, // equivalent to kCAGravityResizeAspect
NSViewLayerContentsPlacementScaleProportionallyToFill = 2, // equivalent to kCAGravityResizeAspectFill
NSViewLayerContentsPlacementCenter = 3,
NSViewLayerContentsPlacementTop = 4,
NSViewLayerContentsPlacementTopRight = 5,
NSViewLayerContentsPlacementRight = 6,
NSViewLayerContentsPlacementBottomRight = 7,
NSViewLayerContentsPlacementBottom = 8,
NSViewLayerContentsPlacementBottomLeft = 9,
NSViewLayerContentsPlacementLeft = 10,
NSViewLayerContentsPlacementTopLeft = 11
};
typedef NSInteger NSViewLayerContentsPlacement;
The first three values specify that the layer’s existing content should be displayed scaled in one of three supported ways when the layer is resized, while being kept centered within the layer’s bounds. The remaining nine values specify that instead of being scaled, the layer’s contents should be displayed at their existing size, and pinned to one of nine canonical positions relative to the layer’s new frame.


NSOpenGLView Minor Method Behavior Changes

On Mac OS X 10.5 and earlier, invoking NSOpenGLView’s -setPixelFormat: method with the view’s current pixelFormat object as its parameter could cause the pixelFormat to be deallocated, if nothing else had retained it. This has been fixed on 10.6.

The same applied to NSOpenGLView’s -setOpenGLContext: method and its context parameter. This has also been fixed on 10.6. Additionally, on 10.5 and earlier -setOpenGLContext: would invoke -clearGLContext, even when the given context was the same as the view’s existing openGLContext. To reduce the risk of compatibility breakage, this behavior has been preserved on 10.6, for application binaries that were linked on 10.5 and earlier. Applications linked on 10.6 and later will only see -setOpenGLContext: invoke -clearGLContext when the view is actually being switched to a different context.


New NSOpenGL-CGL Bridging Methods

To provide improved interoperability between the NSOpenGL and CGL APIs, Mac OS X 10.6 completes the set of CGL-typed initializer and getter methods provided by the NSOpenGLContext, NSOpenGLPixelBuffer, and NSOpenGLPixelFormat classes. Using these methods, one can initialize an NSOpenGL object to wrap an object of the corresponding CGL type, and/or get the CGL object that’s wrapped by a corresponding NSOpenGL object.

Continuing the convention we've followed of avoiding explicit use of CGL argument types in AppKit API, the arguments and return values are typed as void *, and must be cast by clients to the appropriate underlying type (CGLContextObj, CGLPBufferObj, or CGLPixelFormatObj) when necessary.  Also following our existing convention, the -init... and getter methods listed here include the names of the underlying CGL types: CGLContextObj, CGLPixelFormatObj, and CGLPBufferObj.  The CGL objects are retained by their owning NSOpenGL wrapper for the NSOpenGL wrapper's lifetime.
@interface NSOpenGLContext
...
- (id)initWithCGLContextObj:(void *)context;
- (void *)CGLContextObj; // 10.3 and later
...
@end
@interface NSOpenGLPixelBuffer
...
- (id)initWithCGLPBufferObj:(void *)pbuffer;
- (void *)CGLPBufferObj;
...
@end
@interface NSOpenGLPixelFormat
...
- (id)initWithCGLPixelFormatObj:(void *)format;
- (void *)CGLPixelFormatObj; // 10.3 and later
...
@end

Layer Tree Rendering and Modal Windows

An issue that could cause flicker when using Core Animation layer tree rendering in a modal window has been fixed.


Printing Layer-Backed Views

On Mac OS X 10.5 through 10.5.2, attempting to print layer-backed views would produce empty output. This has been fixed for 10.5.3 and on 10.6, such that printing now proceeds in the same way as if the views were not layer-backed. (The views are drawn into the printing context.)

AppKit does not currently provide automatic printing support for standalone, developer provided layer trees. To generate printed output in such cases, it is necessary to use Core Animation’s CARenderer API together with OpenGL to render to a bitmapped image, and then print the resultant image.


Live toolbar layout during customization

Prior to SnowLeopard, toolbars would not show the actual layout produced while rearranging, adding, or removing toolbar items. In SnowLeopard, toolbars should show their correct layout even during customization.

Toolbars no longer validate for some eventsAs an optimization, NSToolbar no longer validates for the following events: NSLeftMouseDragged, NSRightMouseDragged, NSOtherMouseDragged, NSMouseEntered, NSMouseExited, NSScrollWheel, NSCursorUpdate, NSKeyDown. In addition, validation for NSKeyUp and NSFlagsChanged events is deferred for up to .85 seconds in SnowLeopard, with the timer restarting for every new deferrable event. So a sequence of key events will not trigger any validation at all, until either a pause of .85 seconds, or an event other than NSKeyUp or NSFlagsChanged is processed.

To trigger validation for a single toolbar manually, call -[NSToolbar validateVisibleItems]. To trigger validation for all toolbars, call [NSApp setWindowsNeedUpdate:YES]

Flipped images in toolbars

A common but incorrect practice is to call setFlipped:YES on an image that you plan to draw into a flipped graphics context. This is incorrect because if the image is later drawn into a normal unflipped context, the image will appear upside down. However, Leopard and earlier contained a bug in which images set on NSToolbarItem (via setImage:) would ignore their isFlipped property. SnowLeopard fixes this bug for apps compiled on SnowLeopard with the 10.6 SDK; as a result, some images which should have drawn upside down in Leopard, will begin doing so when your app is recompiled.

If you recompile your app on SnowLeopard and discover that your toolbar item images are drawing upside down, then it indicates that you are calling setFlipped:YES somewhere within your code. You should remove those calls and replace the image drawing with methods that correctly handle flipped contexts. See the discussion of setFlipped: in these Release Notes for more discussion.

Spell checkers must be in a Services directory

In SnowLeopard, spell checkers will only be recognized if they are in one of the standard Services directory (/Library/Services, ~/Library/Services, or /System/Library/Services). This is to prevent compatibility problems with spell checkers from previous versions of the OS.

setuid/setgid apps disallowed

As a security measure, SnowLeopard takes steps to prevent applications that use AppKit from running setuid or setgid. If AppKit detects that it is running issetugid(), the following will happen:

Under 64 bit, it will log a message and then exit(EXIT_FAILURE).

Under 32 bit, it will give the user a chance to authenticate as an administrator. If the attempt succeeds, the app will run as normal; if the user fails to authenticate, or cancels, it will exit(EXIT_FAILURE). If the attempt fails because the authentication dialog could not be shown, then it will perform a linked on or after check. Apps linked before SnowLeopard will be allowed to run; applications linked on or after SnowLeopard will be exited.

This only affects applications that have the setuid or setgid Unix permission bit set, or apps that inherit this bit from a fork() of a setugid app. This does not affect applications run via sudo, su, or normally as root.

Tool tips on NSTabViewItem

NSTabViewItem now supports custom tool tips on the tabs themselves. See the NSTabViewItem.h header for more information.

Underscore treated like minus key

Starting with Leopard, AppKit began interpreting ⌘= as ⌘+ if no menu item claims the key equivalent ⌘+ and the pressed = key also has a + on it. In SnowLeopard, this support has been extended to interpret ⌘_ as ⌘-, if the pressed key has both _ and -.

New NSWorkspace APIs

NSWorkspace now contains APIs for gathering information about file labels, and for changing the desktop background. NSWorkspace also contains new APIs for duplicating files and moving them to the trash, which are more powerful and convenient than the older performFileOperation:. There are additional APIs not mentioned in these notes. See the NSWorkspace class header for more information.

NSWorkspace now posts notifications when apps are hidden, unhidden, activated, or deactivated. It also posts notifications when the name or mount location of a volume is changed. Lastly, there is a notification for when the available file colors or labels changes. The new app notifications fire for all apps, compared to the existing notifications which fire for only foreground apps.

NSRunningApplication

SnowLeopard includes a new class, NSRunningApplication, for inspecting and manipulating running applications on the system. See the NSRunningApplication header in AppKit for more information.

NSApplication setHelpMenu:

The new setHelpMenu: API on NSApplication allows apps to determine which menu gets the Spotlight for Help search field. The default Main Menu nib, when created on SnowLeopard, will automatically set the Help menu in the same manner as the Window menu.

NSMenu validation

The NSMenu method -performActionForItemAtIndex: no longer triggers menu validation. This is because validation is typically done during menu tracking or key equivalent matching, so the subsequent performActionForItemAtIndex: validation was redundant. To trigger validation explicitly, use the -[NSMenu update] method.

NSMenu Function Key Equivalents

NSF16FunctionKey through NSF19FunctionKey are now available as key equivalents in menu items.

NSMenu performActionForItemAtIndex: highlighting and accessibility

The NSMenu method performActionForItemAtIndex:, when called, now triggers highlighting in the menu bar. It also sends out appropriate accessibility notifications indicating the item was selected.

NSMenu popUpMenuPositioningItem: atLocation: inView:

NSMenu has a new method for popping up a menu as if it were a popup button. This is meant to be an replacement for the Carbon function PopUpMenuSelect(). See the NSMenu.h header for more information.

NSMenu confinementRectForMenu:

NSMenu has a new delegate method confinementRectForMenu: onScreen: to allow some control over where a menu may pop up. See the NSMenu.h header for more information.

NSPopUpButtonCell supports NSImageOnly

Popup buttons now support the NSImageOnly image position, which prevents the title from showing in the control.

NSMenu key equivalent matching is one per event

In Leopard, the main menu may be queried for key equivalent matches multiple times within a particular event's lifecycle, if no menu item is found to match. This was a performance problem because a menus would be populated multiple times for a single event. In SnowLeopard, we only populate and search the menu once per key event.

It is possible, though unlikely, that apps depended on the old behavior. For example, if an override of -[NSApplication sendEvent:] changed key equivalents so that they match the event, then these key equivalents would no longer work in SnowLeopard. For this reason, the user default "NSAllowMultipleKeyEquivalentSearchesPerEvent" is available to restore the Leopard behavior on SnowLeopard.

Nibless apps and the application menu

In Leopard and earlier, apps that tried to construct a menu bar without a nib would get an undesirable stubby application menu that could not be removed. To work around this problem on Leopard, you can call the undocumented setAppleMenu: method and pass it the application menu, like so:
[NSApp setAppleMenu:[[[NSApp mainMenu] itemAtIndex:0] submenu]];
In SnowLeopard, this workaround is unnecessary and should not be used. Under SnowLeopard, the first menu is always identified as the application menu.

NSApplication application menu item title

In Leopard, the first item in the main menu (the application menu) would have its title modified to the empty string. In SnowLeopard, this is only done for apps linked on Leopard or earlier: new apps will not see this title modified. The title of the this menu item is ignored for display purposes - the application menu always reflects the application name.

Main menu items no longer specially retained during tracking

In Leopard, every menu item contained directly or indirectly within the main menu would be retained during menu tracking. This was an inefficiency, but some apps would message menu items after removing them within menuNeedsUpdate:. In SnowLeopard, menu items within the main menu are retained across calls to menuNeedsUpdate:, and then only for 32 bit apps compiled on Leopard or earlier. You should not depend on menu items being retained after they are removed from the main menu.


Services Menu visual changes

The Services menu now categories Services based on their send and receive types, and shows icons for Services. The Services menu is also flattened - no more submenus. When updating your Service for SnowLeopard, make sure your Service has an icon and a descriptive title that makes it easy to identify your Service in the flattened menu. Also consider how your Service can be made contextual, as discussed under Services Contextuality. See the SysServices guide for more information.

Services Preferences

SnowLeopard allows the users to enable or disable individual Services. The preference pane is accessible from the Services Preferences menu item within the Services menu, and also via System Preferences.

Services Contextuality

In SnowLeopard, Services can appear only in certain contexts. For example, the Open URL Service appears only if the selected text contains a URL, the Reveal in Finder Service appears only if a file path is selected, and the Set Desktop Picture Service appears only if images are selected in Finder. The contextuality of a Service is specified within its Info.plist. For the full list of ways a Service can be made contextual, see the System Services guide in the developer documentation.

Non-contextual Services disabled by default

In SnowLeopard, Services that are not contextual are disabled by default. They can be reenabled within the Services Preferences. Because the contextual Services feature is new in SnowLeoaprd, all existing non-Apple Services are disabled by default.

Services that are updated for SnowLeopard will appear enabled by default again. A Service indicates it is updated for SnowLeopard by adding the NSRequiredContext key to its Info.plist, as documented in the System Services guide. Services that cannot limit themselves to certain contexts, but still want to be enabled for SnowLeopard, can add an empty NSRequiredContext and will appear by default.

Services in context menus

In SnowLeopard, Services may appear in context menus. Context menus will generally show a subset of those Services available in the Services menu.

The following Services will not show in context menus, even if they appear in the Services menu:

- Services that already have equivalent normal menu items in the context menu, such as Open URL or Look Up in Dictionary.
- Services that have neither a send or receive type.
- Services that will not operate on the selection (have no send type) when a selection is present. For example, Capture Selection from Screen does not appear in context menus if there is selected text.
- A fixed list of Services that cannot be made contextual and would appear everywhere, such as Make New Sticky Note or New Email With Attachment.

All other Services that appear in the Services menu should also appear in context menus.

Services can be Automator workflows

In SnowLeopard, you can create a Service from an Automator workflow. To create an Automator workflow Service, start Automator and choose Service from the list of starting points. Workflow Services are first class citizens: they can accept and/or return data, and be made contextual via the same mechanism as regular Services.

Workflow Services must be in one of the Library/Services directories to be recognized. These Service directories are live: adding or removing a Workflow Service from ~/Library/Services/ or /Library/Services/ will cause the Services menu to be updated immediately.


Fetching the cursor set by any application

SnowLeopard adds an NSCursor method that returns a cursor whose image and hot spot match those of the currently-displayed cursor on the system, regardless of which application set that cursor, and regardless of whether Cocoa or Carbon APIs were used to set it: +[NSCursor currentSystemCursor]

This replaces the deprecated QDGetCursorData API.

(The existing +[NSCursor currentCursor] method only returns the cursor set by your application via NSCursor methods, but not cursors set by other applications or cursors set by your application using Carbon API.)

Displaying a Finder search results window

SnowLeopard adds an NSWorkspace method that displays a search results window in Finder for a specific query string: -[NSWorkspace showSearchResultsForQueryString:]

This is the programmatic equivalent of the user switching to Finder, creating a new window, and typing the search string into the search field.

This effectively replaces Carbon's HISearchWindowShow API.

NSEvent additions for event monitoring

There are new NSEvent class methods for monitoring events just in your own application, or events in all applications:
+ (id)addGlobalMonitorForEventsMatchingMask:(NSEventMask)mask handler:(void (^)(NSEvent *))block;
+ (id)addLocalMonitorForEventsMatchingMask:(NSEventMask)mask handler:(NSEvent *(^)(NSEvent *))block;
+ (void)removeMonitor:(id)eventMonitor;
+addLocalMonitorForEventsMatchingMask allows monitoring and modification of all events that are dispatched via -[NSApp sendEvent:]. +addGlobalMonitorForEventsMatchingMask allows monitoring (but not modification) of user input events dispatched to other applications, such as mouse events and keyboard events.

Customizing Spotlight for Help

We introduced an API in AppKit to let developers implement searching their own custom Help Data.  In general, users find the Help search functionality very useful. However, many large applications don't use Apple Help API because of cross platform requirements. Hence, important Help topics are not presented as part of the Help menu. The new API will allow developers to incorporate their own Help topics and take full advantage of the Help feature.

In your application you implement the NSUserInterfaceItemSearching protocol and then register your object with -[NSApplication registerUserInterfaceItemSearchHandler:]. See NSUserInterfaceItemSearching.h for API.

NSObjects implements awakeFromNib

On Mac OS X 10.6 and later, NSObject provides an implementation of awakeFromNib. This means that you can safely call through to [super awakeFromNib] in an overridden implementation when running on Mac OS X 10.6 and later.


Advice for People who Are Looking for -viewWillLoad and -viewDidLoad Methods in NSViewController

Even though NSWindowController has -windowWillLoad and -windowDidLoad methods for you to override the NSViewController class introduced in Mac OS 10.5 does not have corresponding -viewWillLoad and -viewDidLoad methods. You can override -[NSViewController loadView] to customize what happens immediately before or immediately after nib loading done by a view controller.

New Support for Concurrent Document Opening in NSDocument

NSDocument now has the ability to read documents concurrently, using background threads. A new class method has been added to NSDocument to give you control over this:
+ (BOOL)canConcurrentlyReadDocumentsOfType:(NSString *)typeName;
Return whether instances of the receiving class can concurrently read documents of the specified type. The default implementation of this method returns NO. You can override it to return YES to enable concurrent opening of documents but you must make sure your document reading code can be safely executed concurrently, in non-main threads.

Note that NSUndoManager's mechanism for automatically creating one group per UI event doesn't coexist very well with NSUndoManager usage on non-main threads. You should disable undo registration during document reading, which is a good idea even in the absence of concurrency. See for example SKTDocument.m in /Developer/Examples/Sketch.

Note that code executed during the opening of a document triggered by an Apple event will not be able to get the current Apple event, because the event is suspended until all documents are read, to enable correct reporting of success or failure to the Apple event sender. If, for example, you are checking the current Apple event for a search string, you should not enable concurrent document opening unless you have another solution for getting the search string.

Advice for Overriders of -[NSDocumentController openDocumentWithContentsOfURL:display:error:]

Since the introduction of -[NSDocumentController openDocumentWithContentsOfURL:display:error:] in Mac OS 10.4 it has been easy to mask bugs, in either your application's code or its Info.plist file, that cause -[NSDocumentController typeForContentsOfURL:error:], also introduced in Mac OS 10.4, to return nil and an error inappropriately. The default implementation of -openDocumentWithContentsOfURL:display:error: was the only place in AppKit from which -typeForContentsOfURL:error: was frequently invoked so if you overrode the former method and did not invoke super or the latter method in your override you would not notice such bugs. With the introduction of support for concurrent document opening in Mac OS 10.6 there are now other places in AppKit from which -typeForContentsOfURL:error: is invoked. For backward binary compatibility with Mac OS 10.5 and earlier this is done only in applications linked against Mac OS 10.6. If you notice new document opening problems in your application when you start linking it against the Mac OS 10.6 SDK you might want to start debugging by seeing what -typeForContentsOfURL:error: is returning.

Advice for Overriders of -[NSDocument close]

Merely overriding -[NSDocument close] is usually not sufficient for many purposes because it's often not invoked at application termination time. This is for performance and there are no plans to change this in the future. It's best to avoid requiring the sort of resource cleanup that must be done at document closing time but, if it's unavoidable in your application, try initiating such cleanup for documents that are still open in your application delegate's -applicationWillTerminate: method in addition to an override of -[NSDocument close]. Both -[NSDocument close] overrides and -applicationWillTerminate: application delegate methods are by the way at odds with the new "Fast Killing of Applications" mechanism that is described in the Foundation release notes and which we would like you to adopt.

New Opportunity for Customization of Save Panels Presented by NSDocument

In earlier versions of Mac OS X there was no way for a subclass of NSDocument to customize the initial value of the file name field in presented save panels, not even by overriding -[NSDocument prepareSavePanel]. In Mac OS 10.6 NSDocument now uses the NSSavePanel methods added in Mac OS 10.6 so it can allow this kind of customization. If you override -prepareSavePanel and send a -setNameFieldStringValue: message to the save panel NSDocument will not overwrite the value that's set. It also doesn't overwrite any value set by sending -setDirectoryURL: to the save panel in your override of -[NSDocument prepareSavePanel].

New Support for "Save As PDF…" in NSDocument Printing

-[NSDocument printDocumentWithSettings:showPrintPanel:delegate:didPrintSelector:contextInfo:] has a new behavior in Mac OS 10.6: if the passed-in print settings dictionary has an NSPrintJobDisposition entry whose value is NSPrintSaveJob, indicating that the printed pages should be written to a PDF file, but no NSPrintJobSavingURL (new) or NSPrintSavePath (old, deprecated) entry indicating where the PDF file should be written, then NSDocument will present a save panel asking the user where the PDF file should be saved. See TextEdit's -[Document saveDocumentAsPDFTo:] for an example of using this.

Bug Fixes in NSDocumentController

In earlier versions of Mac OS X -[NSDocumentController documents] simply returned a pointer to its internal array of documents, which could change as documents were opened or closed, requiring invokers to make a copy to enumerate while closing documents. This was a bug and has been fixed in Mac OS 10.6. A copy of the documents array (autoreleased of course) is now returned.

In Mac OS 10.5 -[NSDocument displayNameForType:], when passed a UTI for a type that conforms to another type for which the application also has a CFBundleDocumentTypes entry, could return the display name for that other type. This was a bug and has been fixed in Mac OS 10.6.

In Mac OS 10.5 -[NSDocument displayNameForType:], when passed a UTI for which the application has a CFBundleDocumentTypes entry that includes a CFBundleTypeName subentry, would return nil if there was no corresponding InfoPlist.strings entry. This was a bug and has been fixed in Mac OS 10.6. Now the unlocalized value of the CFBundleTypeName entry is returned if there is no corresponding InfoPlist.strings entry.

In Mac OS 10.5 -[NSDocument typeFromFileExtension:] would return unpredictable values when passed nil. This meant that -[NSDocumentController typeForContentsOfURL:error:] could also return unpredictable values, in apps in which not all of the Info.plist CFBundleDocumentTypes entries have LSItemContentTypes entries. (See the description of how -typeForContentsOfURL:error: works nowadays in the "Support for UTIs in NSDocumentController" section of the AppKit release notes for Mac OS 10.5.) This was a bug and has been fixed in Mac OS 10.6.

New Support for URLs and Good Error Reporting in NSFileWrapper

NSFileWrapper has been modernized in Mac OS 10.6 and now deals in URLs instead of paths, and reports errors using NSError. These old methods have been deprecated:
- (id)initWithPath:(NSString *)path;
- (id)initSymbolicLinkWithDestination:(NSString *)path;
- (NSString *)symbolicLinkDestination;
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)atomicFlag updateFilenames:(BOOL)updateFilenamesFlag;
- (BOOL)needsToBeUpdatedFromPath:(NSString *)path;
- (BOOL)updateFromPath:(NSString *)path;
- (NSString *)addFileWithPath:(NSString *)path;
- (NSString *)addSymbolicLinkWithDestination:(NSString *)path preferredFilename:(NSString *)filename;
These new methods have been published:
- (id)initWithURL:(NSURL *)url options:(NSFileWrapperReadingOptions)options error:(NSError **)outError;
- (id)initSymbolicLinkWithDestinationURL:(NSURL *)url;
- (NSURL *)symbolicLinkDestinationURL;
- (BOOL)writeToURL:(NSURL *)url options:(NSFileWrapperWritingOptions)options originalContentsURL:(NSURL *)originalContentsURL error:(NSError **)outError;
- (BOOL)matchesContentsOfURL:(NSURL *)url;
- (BOOL)readFromURL:(NSURL *)url options:(NSFileWrapperReadingOptions)options error:(NSError **)outError;
(We're not publishing new NSURL/NSError-using replacements for -addFileWithPath: and -addSymbolicLinkWithDestination: because the new methods would not actually be necessary and the old methods were so unpopular.)

New NSFileWrapperWritingAtomic and NSFileWrapperWritingWithNameUpdating options have been published for use with -writeToURL:options:originalContentsURL:error:. They correspond to the atomically: and updateFilenames: parameters, respectively, of -writeToFile:atomically:updateFilenames:, with the exception that NSFileWrapperWritingWithNameUpdating, unlike updateFilenames:YES, only causes -setFilename: to be sent to child file wrappers, not the receiver itself.

See the comments in <AppKit/NSFileWrapper.h> for more information.

New Support for Incremental Writing in NSFileWrapper

The new -writeToURL:options:originalContentsURL:error: method mentioned above differs from the now-deprecated -writeToFile:atomically:updateFilenames: method in that in has an additional parameter in which you can pass a URL that locates the previous revision of a file package being saved. When the value of that parameter is not nil NSFileWrapper attempts to avoid unnecessary I/O by merely writing hard links to files instead of actually writing out their contents.

New Reading Options in NSFileWrapper

The new -initWithURL:options:error: and -readFromURL:options:error: messages mentioned above have options: parameters. The possible options are:

NSFileWrapperReadingImmediate - This causes descendent file wrappers to be instantiated and their contents and attributes read immediately. It's good to use this when your application uses NSFileWrapper to represent new attachments.

NSFileWrapperReadingWithoutMapping - This causes NSFileWrapper to never use file mapping for regular file contents. NSFileWrapper does a good job of deciding when to use file mapping instead of nonlazily reading the entire contents of files into memory but you can use this if necessary to make sure your application is particularly robust against the user doing things like unplugging external hard disks without ejecting them first.

New Support for Preserving Metadata in NSFileWrapper

In earlier versions of Mac OS X the only file attributes that NSFileWrapper ever attempted to preserve when writing were the file modification date and the POSIX permissions. In Mac OS 10.6 it now reads and writes these attributes:
• Creation and modification dates
• File name extension hiding
• HFS creator and file type codes
• POSIX permissions
• Extended attributes
• The resource fork

Because NSFileWrapper deals in the same kind of attribute dictionaries as NSFileManager, you can specify the values of the first four kinds of attribute by invoking -setFileAttributes:, passing a dictionary with values for these keys:
• NSFileCreationDate and NSFileModificationDate
• NSFileExtensionHidden
• NSFileHFSCreatorCode and NSFileHFSTypeCode
• NSFilePosixPermissions
Be careful when using -setFileAttributes:. It doesn't add to the receiver's set of file attributes, it completely replaces it.

Bug Fixes in NSFileWrapper

In earlier versions of Mac OS X NSFileWrapper did not take into account the fact that two items whose names differ only by case can't both be written to the same directory in most file systems without one overwriting the other. This bug has been fixed in Mac OS 10.6. -[NSFileWrapper addFileWrapper:] for example will now create a new unique file name for a child whose preferred file name is @"TestyMCTest.test" if there is already a child whose unique file name is @"TestyMcTest.test".

In earlier versions of Mac OS X sending -writeToFile:atomically:updateFilenames: to an NSFileWrapper would do three things for updateFilenames:YES:
• Send -setFilename: to the receiver and its child file wrappers.
• Reread the file attributes of the receiver and its child wrappers from the file system items just written.
• Remap the contents of any regular file wrappers to the corresponding files just written.
The last two things were not right and in Mac OS 10.6 they are no longer done. The remapping of regular file contents in particular caused trouble with applications holding files open at surprising times (memory mapping a file effectively opens it). updateFilenames:YES now just causes -setFilename: to be sent to the receiver and its child file wrappers.

New Cursor Invalidation in -[NSSplitView adjustSubviews]

In earlier versions of Mac OS X -[NSSplitView adjustSubviews] did not invalidate the split view's cursor. This made it difficult to correctly animate divider positioning by simply sending -setFrameSize: to the results of sending -animator to the two subviews on either side of the divider and letting -adjustSubviews get invoked repeatedly during the animation. In Mac OS 10.6 -[NSSplitView adjustSubviews] now invokes [[self window] invalidateCursorRectsForView:self] so the cursor over the divider is always correct after such an animation.

Bug Fix in -[NSSplitView minPossiblePositionOfDividerAtIndex:]

Mac OS 10.5 added a new -minPossiblePositionOfDividerAtIndex: method to NSSplitView as well as the ability to hide dividers. -minPossiblePositionOfDividerAtIndex: when passed a divider index of 0 is supposed to return zero minus the divider thickness when that divider is hidable. (Conceptually the user can drag the topmost or leftmost divider right off the top or left edge of a split view if it is hidable, and the position of a divider is its top or left edge.) In Mac OS 10.5 it instead returned zero when the subview above or to the left of the divider was collapsed. This was a bug and has been fixed in Mac OS 10.6.

Change In Invocation of NSSplitView Delegate Constraint Methods For Hidden Dividers

In Mac OS 10.5 an NSSplitView would pass the exact same value returned by -minPossiblePositionOfDividerAtIndex: or -maxPossiblePositionOfDividerAtIndex: when sending -splitView:constrainMinCoordinate:ofSubviewAt: or -splitView:constrainMaxCoordinate:ofSubviewAt:, respectively, to the split view's delegate. Because that value takes into account the fact that a hidden divider is effectively draggable off the edge of the split view it would be surprisingly small or large, respectively, for hidable dividers. This made it difficult to specify the minimum sizes of subviews using the relatively simple technique described in <AppKit/NSSplitView.h>. This has been changed in Mac OS 10.6. The value passed to -splitView:constrainMinCoordinate:ofSubviewAt: or -splitView:constrainMaxCoordinate:ofSubviewAt: is now the same regardless of whether the relevant divider is hidable. For backward binary compatibility this is only done in applications linked against Mac OS 10.6 or later.

NSPrintInfo Improvements

To let you easily take advantage of NSPrintInfo's KVO-compliance (without having to observe the NSPrintInfo's attributes dictionary), accessor methods have been published for the print scaling attribute:
- (void)setScalingFactor:(CGFloat)scalingFactor;
- (CGFloat)scalingFactor;
NSPrintInfo is now as KVO-compliant for "scalingFactor" as it is for "paperSize," "orientation," etc.

Because we're standardizing on NSURL as the representation of locations of files in Mac OS 10.6, a new key into the attributes dictionary has been published:
NSString *NSPrintJobSavingURL;
The value must be an NSURL containing the location to which the job file will be saved, for use when the job disposition is NSPrintSaveJob.

The NSPrintSavePath key has been deprecated.

In earlier versions of Mac OS X there was no way for you to specify that the name extension of the file being written for an NSPrintSaveJob job disposition should be hidden. A new key for that has been published:
NSString *NSPrintJobSavingFileNameExtensionHidden;
The value must be a boolean NSNumber. The default is NO.

New Job Style Hints in NSPrintPanel

In Mac OS 10.6 two new job style hints have been published for use with -[NSPrintPanel setJobStyleHint:]. They correspond to Core Printing's new kPMPresetGraphicsTypeAll and kPMPresetGraphicsTypeNone graphics types, respectively. There's no new job style hint corresponding to Core Printing's kPMPresetGraphicsTypeGeneral. A nil job style hint still means the same thing as that.
NSString *const NSPrintAllPresetsJobStyleHint;
NSString *const NSPrintNoPresetsJobStyleHint;


Better Print Preview

The Print Preview on the print panel now draws respecting Border, N-Up, Mirror, Reverse Page Orientation, and Page Range.

Also, the page range text/controls underneath the preview shows the physical pages to be printed after taking into consideration the user's settings for N-Up, Page Range, etc...


Printing just the selected content of a document

The NSPrintPanel can now optionally add a "Selection" radio option to the list of page range options (All, From:To:, Selection). To enable "Selection" as a user selectable page range in the print panel, Add NSPrintPanelShowsPrintSelection to the NSPrintPanel options when setting up the print operation.
- (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)printSettings error:(NSError **)outError {
    ...
    NSPrintOperation *op = [NSPrintOperation printOperationWithView:...
    NSPrintPanel *printPanel = [op printPanel];
    [printPanel setOptions:[printPanel options] | NSPrintPanelShowsPrintSelection];
    ...
}
To print only the selection, the view supplied to the NSPrintOperation must check the isSelectionOnly property of the printInfo and adjust appropriately when determining the page range (knowsPageRange:) and drawing (drawRect:).
- (BOOL)knowsPageRange:(NSRangePointer)aRange {
    NSPrintOperation *curPrintOperation = [NSPrintOperation currentOperation];
    BOOL printSelectionOnly = [[curPrintOperation printInfo] isSelectionOnly];
    ...
}
- (void)drawRect:(NSRect)rect {
    BOOL printSelectionOnly = NO;
    if(![NSGraphicsContext currentContextDrawingToScreen]) {
        NSPrintOperation *curPrintOperation = [NSPrintOperation currentOperation];
        BOOL printSelectionOnly = [[curPrintOperation printInfo] isSelectionOnly];
    }
    ...
}

NSBrowser - Item Based only features

NSBrowser no longer caches the contents of each column. Instead it depends on the data source to provide speedy results. One ability this enables is that the same object can now appear in multiple columns. A consequence of this is that the following new API have been removed:
- (NSIndexPath *)indexPathForItem:(id)item;
- (id)parentForItem:(id)item;
- (void)reloadItem:(id)item reloadChildren:(BOOL)flag;
The reloadItem:reloadChildren: method has been replaced with:
- (void)reloadDataForRowIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column;
The following method:
- (id)itemAtRow:(NSInteger)row column:(NSInteger)column;
has been replaced with the following to provide better consistency with method names:
- (id)itemAtRow:(NSInteger)row inColumn:(NSInteger)column;
You can now specify the row height:
- (void)setRowHeight:(CGFloat)height;
- (CGFloat)rowHeight;
Also the delegate can specify variable row heights by implementing the following method:
- (CGFloat)browser:(NSBrowser *)browser heightOfRow:(NSInteger)row inColumn:(NSInteger)columnIndex;
The delegate should inform the NSBrowser of row height changes via this method:
- (void)noteHeightOfRowsWithIndexesChanged:(NSIndexSet *)indexSet inColumn:(NSInteger)columnIndex;

NSBrowser - General

Updated the header comment for -browser:sizeToFitWidthOfColumn: to inform developers that they can opt out of supplying a column width for any particular column by returning -1. When NSBrowser gets a -1 return value, it will make its own column width determination as if the delegate did not implement this method.

New API to get the column/row at a point and one to get the frame.

Finds the row and column located at 'point', returning YES if both can be found. If a row does not exist at 'point', then -1 is set for the row. If a column does not exist at 'point', then -1 is set for the column. 'point' is expected to be in the NSBrowser's coordinate system. *row and *column may be NULL. The correct BOOL value is returned and the non NULL *row or *column (if any) are filled in.
- (BOOL)getRow:(NSInteger *)row column:(NSInteger *)column forPoint:(NSPoint)point;
Returns the frame of the row at 'row' / 'column' including the area for the expandable arrow. The returned NSRect is in the NSBrowser coordinate space.
- (NSRect)frameOfRow:(NSInteger)row inColumn:(NSInteger)column;

MultiTouch

In addition to gestures, developers can now get individual touches on a multitouch trackpad. You must explicitly opt-in in order to receive the new touch responder methods. Touches may or may not move the cursor via separate mouse events. The mouse event may occur slightly before or after its associated touch event.

The same event that contains the touches may also contain a gesture. The gesture NSResponder methods are called in addition to the new touch NSResponder methods.


MultiTouch - NSView

New methods on NSView allow an application to opt in to receive touches. By default, views do not accept touch events:
- (void)setAcceptsTouchEvents:(BOOL)flag;
- (BOOL)acceptsTouchEvents;
In some cases, the user may rest a thumb or other touch on the device. If the device can detect this (MacBooks with glass trackpad) then it will mark the touch as resting. By default, these touches are not delivered and are not included in the event's set of touches. Touches may transition in and out of resting at any time. Unless the view wants restingTouches, began / ended events are simulated as touches transition from resting to active and vice versa. In general resting touches should be ignored.
- (void)setWantsRestingTouches:(BOOL)flag;
- (BOOL)wantsRestingTouches;

MultiTouch - NSEvent

There is a new method on NSEvent to extract the touches from the event.

This is only valid for gesture events (Gesture, Magnify, Swipe, Rotate, etc.). A view can get all of the touches associated with a gesture without overriding the touch responder methods. Touches that target view, or any of view's descendants, are returned. Pass nil as the view to get touches regardless of their targeted view.
- (NSSet *)touchesMatchingPhase:(NSTouchPhase)phase inView:(NSView *)view;

MultiTouch - NSTouch

An NSTouch is a snapshot of a particular touch at some instance in time. That is quite different from an iPhone UITouch which persists for the life of the touch and is modified by the system during its life. The identity property is used to follow a specific touch across its lifetime.

Touches do not have a corresponding screen location. The first touch of a touch collection is latched to the view underlying the cursor using the same hit detection as mouse events. Additional touches on the same device are also latched to the same view as any other touching touches. A touch remains latched to its view until the touch has either ended or is cancelled.

NSTouchPhaseBegan:
A finger touched the device. Or, a resting touch transitioned to an active touch and resting touches are not wanted by the view hierarchy.

NSTouchPhaseMoved:
A finger moved on the device.

NSTouchPhaseStationary:
A finger is touching the device, but hasn't moved since the previous event.

NSTouchPhaseEnded:
A finger was lifted from the screen. Or, an active touch transitioned to a resting touch and resting touches are not wanted by the view hierarchy.

NSTouchPhaseCancelled:
The system cancelled tracking for the touch, as when (for example) the window associated with the touch resigns key or is deactivated.
enum {
    NSTouchPhaseBegan           = 1 << 0,
    NSTouchPhaseMoved           = 1 << 1,
    NSTouchPhaseStationary      = 1 << 2,
    NSTouchPhaseEnded           = 1 << 3,
    NSTouchPhaseCancelled       = 1 << 4,
    NSTouchPhaseTouching        = NSTouchPhaseBegan | NSTouchPhaseMoved | NSTouchPhaseStationary,
    NSTouchPhaseAny             = NSUIntegerMax
};
typedef NSUInteger NSTouchPhase;
Unlike the iPhone, NSTouch objects do not persist for the life of the touch.
@interface NSTouch : NSObject <NSCopying>
Use the identity property to track changes to a particular touch during the touch's life. While touch identities may be re-used, they are unique during the life of the touch, even when multiple devices are present. Note: identity objects implement the NSCopying protocol so that they may be used as keys in an NSDictionary. Use isEqual: to compare two touch identities.
@property(readonly) id<NSObject, NSCopying> identity;
@property(readonly) NSTouchPhase phase;
Normalized, absolute position [0,1] of the touch on the device where (0, 0) is the lower left of the device surface.
@property(readonly) NSPoint normalizedPosition;
@property(readonly) BOOL isResting;
The following are properties of an NSTouch, but they really describe properties of the underlying touch device.

The digitizer that generated the touch. Useful to distinguish touches emanating from multiple-device scenario:
@property(readonly) id device;
The range of the touch device in points (72ppi). Note: 0,0 is the lower left of the surface:
@property(readonly) NSSize deviceSize;

MultiTouch - NSResponder

Once a view opts in to receive touches, the following new responder methods are called. Each touch event may have more than one touch in various phases. Each responder method corresponding to that phase is called. (Note: the order these methods are called is undefined.)

Note: It is possible for more than one of the following methods to be called for the same event. The order the methods are sent is not guaranteed.
/* A new set of touches has been recognized. To get the set of touches that began for this view (or descendants
of this view): [event touchesMatchingPhase:NSTouchPhaseBegan inView:self]; Note: this is not always the point
of contact with the touch device. A touch that transitions from resting to active may be part of a Began set.
*/
- (void)touchesBeganWithEvent:(NSEvent *)event;
/* One or more touches have moved. To get the set of touches that moved for this view (or descendants
of this view): [event touchesMatchingPhase:NSTouchPhaseMoved inView:self];
*/
- (void)touchesMovedWithEvent:(NSEvent *)event;
/* A set of touches have been removed. To get the set of touches that ended for this view (or descendants
of this view): [event touchesMatchingPhase:NSTouchPhaseEnded inView:self]; Note: this is not always the point
of removal with the touch device. A touch that transitions from active to resting may be part of an Ended set.
*/
- (void)touchesEndedWithEvent:(NSEvent *)event;
/* The System has cancelled the tracking of touches for any reason.
*/
- (void)touchesCancelledWithEvent:(NSEvent *)event;

NSDatePicker

User editing of a text date picker has been vastly improved. Editing a date/time field now replaces the contents of the field with the new digits. After each digit change, there is a short amount of time where the user can append more or delete digits (reseting the time out for each change). Once the timeout has occurred, future edits to the field replace the current value of the field. The new value in progress is committed if the timer runs out or, if adding any more digits would result in a invalid value for that field. Note, if the new value is committed before the time out expires, new edits cannot occur until the timeout expires. Return commits current changes and cancels the time out. Esc reverts any non committed edit. Delete will remove a digit but effectively sets an infinite timer until another change or UI focus movement occurs.

MultiTouch - NSEvent

The subType of touch generated mouse events is now NSTouchEventSubtype. This is useful in when determining when to ignore mouse events in favor of touch events when they are both getting generated. This is not applicable to scrollWheel events.


Gesture support

We have added event types and responder methods for gestures as generated by the trackpad on the MacBook Air and the newest generation MacBook Pro.

NSEvent additions for gesture support

    NSEventTypeGesture = 29
    NSEventTypeMagnify = 30,
    NSEventTypeSwipe = 31,
    NSEventTypeRotate = 18,
    NSEventTypeBeginGesture = 19,
    NSEventTypeEndGesture = 20
An event of type NSEventTypeGesture is used for a gesture event that is not promoted to its own event type. An event of this type has additional information available in the CGEventRef.
An event of type NSEventTypeBeginGesture and an event of type NSEventTypeEndGesture will be generated by the parser at the start and end of a stream of gestures, somewhat analogous to NSMouseDown and NSMouseUp. Applications can use the NSEventTypeEndGesture to commit a change, for example to do more detailed drawing or to register an undo operation.

An event of type NSEventTypeMagnify, NSEventTypeRotate, NSEventTypeSwipe, NSEventTypeBeginGesture, or NSEventTypeEndGesture will be delivered to the view under the mouse in the key window via the responder methods described below.

You can ask for a particular event type using -nextEventMatchingMask: or -nextEventMatchingMask:untilDate:inMode:dequeue:.


These accessors are valid for all events. When a gesture event is delivered to -[NSApplication sendEvent:] or dequeued via -[NSApplication nextEventMatchingMask:], it does not yet have a window target. At this point, -window is nil and -windowNumber is 0, and -locationInWindow is in screen coordinates. Before dispatching to -[NSWindow sendEvent:], NSApplication sets the window target in the event.
- (NSEventType)type;
- (NSUInteger)modifierFlags;
- (NSTimeInterval)timestamp;
- (NSWindow *)window;
- (NSInteger)windowNumber;
- (NSGraphicsContext*)context;
- (NSPoint)locationInWindow;

There is a new accessor, -magnification, that will return the desired magnification for an NSEventTypeMagnify event as a fraction. A magnification of 1 should be interpreted as increasing the scale factor by 100%, eg. from 100% to 200%, and a negative magnification should decrease the scale factor, with some minimum scale enforced so that you can never get to 0, for example.
- (CGFloat)magnification;
The existing -rotation accessor is valid for NSEventTypeRotate events. The rotation is relative, and is measured in degrees, counterclockwise.

For NSEventTypeSwipe, the -deltaX and -deltaY accessors will return the swipe direction. A non-0 deltaX will represent a horizontal swipe, -1 for swipe right and 1 for swipe left. A non-0 deltaY will represent a vertical swipe, -1 for swipe down and 1 for swipe up.

Key equivalents

The fix to prevent NSKeyUp events from being sent to performKeyEquivalent: has been extended to include all applications, not just those built on Leopard or later.

NSTrackingArea

There has been a change to the return value of -[NSTrackingArea userInfo]. When the NSTrackingArea is installed by the implementation of -[NSView addTrackingRect:owner:userData:assumeInside:], -[NSTrackingArea userInfo] will return nil rather than the given userData, which is declared to be a pointer and not guaranteed to be an object. The use of NSTrackingArea for this method is an implementation detail, and applications typically get the userData from the mouseEntered: or mouseExited: event. Returning nil from -[NSTrackingArea userInfo] for this case allows implementations of mouseEntered: and mouseExited: to send messages to the userInfo to determine whether the event is meant for a particular object in a class hierarchy. Prior to this change, there was no guarantee that the return of userInfo was a valid object.

When setting the cursor for the current mouse location, for example after scrolling or when making a window key, NSWindow now sends cursorUpdate: through the responder chain. If no responder reponds to cursorUpdate:, a cursorRect added with addCursorRect:cursor: will be used if under the mouse. This change allows cursorUpdate: to coexist with addCursorRect:cursor:, but has binary compatibility implications. In particular, if a view calls invalidateCursorRectForView: from hitTest:, this can lead to a repeating pattern of invalidating cursor rects and hitTesting to set the current cursor. For this reason, the change to use cursorUpdate: is limited to applications linked on SnowLeopard or later.

NSResponder additions for gesture support

The following NSResponder methods have been added. The message will be sent to the view under the mouse in the key window.
- (void)magnifyWithEvent:(NSEvent *)event;
- (void)rotateWithEvent:(NSEvent *)event;
- (void)swipeWithEvent:(NSEvent *)event;
- (void)beginGestureWithEvent:(NSEvent *)event;
- (void)endGestureWithEvent:(NSEvent *)event;

NSEvent

There are now NSEvent class methods providing access to some system settings. Because these are accessors for system settings, overrides will have no effect.
/* the time in which a second click must occur in order to be considered a doubleClick */
+ (NSTimeInterval)doubleClickInterval;
/* the time for which a key must be held down in order to generate the first key repeat event */
+ (NSTimeInterval)keyRepeatDelay;
/* the time between subsequent key repeat events */
+ (NSTimeInterval)keyRepeatInterval;
There are also NSEvent class methods for getting the modifier flags and mouse button state outside of the event stream.
+ (NSUInteger)modifierFlags;
+ (NSUInteger)pressedMouseButtons;

NSWorkspace

We added NSWorkspace notifications for display wake and sleep. Few applications are likely to be interested in these notifications, but they may be useful for certain hardware-based drawing decisions, for example in OpenGL.
NSString * const NSWorkspaceScreensDidSleepNotification;
NSString * const NSWorkspaceScreensDidWakeNotification;
We also added an NSWorkspace notification for space switches. This notification is sent after a space switch has occurred.
NSString * const NSWorkspaceActiveSpaceDidChangeNotification;

NSWindow

You can now ask if a window is on the active space. If the window is offscreen, due to being minimized or hidden for example, -isOnActiveSpace will return YES if ordering the window onscreen will order it on to the active space. - (BOOL)isOnActiveSpace;

There is an NSWindow class method, +windowNumbersWithOptions:, to retrieve window numbers for all onscreen windows. By default, +windowNumbersWithOptions: returns the window numbers for the visible windows belonging to the calling process on the active space. You can also request the window numbers for all visible windows, regardless of owner, and for windows on all spaces.
enum {
    NSWindowNumberListAllApplications = 1 << 0,
    NSWindowNumberListAllSpaces = 1 << 4
};
+ (NSArray *)windowNumbersWithOptions:(NSWindowNumberListOptions)options;
There is also a class method to get the window that would be hit by a mouseDown at a given screenPoint. Pass a non-0 windowNumber to start the search below that window in z-order. Because this method uses the same rules as mouseDown hitTesting, windows with transparency at the given point, and windows that ignore mouseEvents, will not be returned.
+(NSInteger)windowNumberAtPoint:(NSPoint)point belowWindowWithWindowNumber:(NSInteger)windowNumber;
There is new NSWindow API to override the default behavior where a sheet or modal panel disables application termination. - (BOOL)preventsApplicationTerminationWhenModal;
- (void)setPreventsApplicationTerminationWhenModal:(BOOL)flag;
We added a notification, delegate methods, and window accessor for live resize. The notification and corresponding delegate methods are sent when window live resize starts do to a mouseDown in the resize corner, and when it ends due to a mouseUp.
NSString * const NSWindowWillStartLiveResizeNotification;
NSString * const NSWindowDidEndLiveResizeNotification;
The window delegate can implement the desired methods below to get automatically registered for the corresponding notification on the window.
- (void)windowWillStartLiveResize:(NSNotification *)notification;
- (void)windowDidEndLiveResize:(NSNotification *)notification;
While the mouse is down in the resize corner, -inLiveResize will return YES.
- (BOOL)inLiveResize;
You can now call setFrame:display:animate: on a sheet to cause the sheet to resize while remaining correctly positioned on the document window. The document window will move if necessary to show the sheet completely onscreen. The animate flag must be YES to trigger this behavior. This is useful for expanding/collapsing a sheet, as we do for the save and print sheets, for example.
There is now API to disable windowServer moving of a window:
- (void)setMovable:(BOOL)flag;
- (BOOL)isMovable;
By default, windows can be dragged by their titlebar, toolbar, or bottom border, and by content if -isMovableByWindowBackground returns YES. The mouseDown/mouseDragged/mouseUp events that cause window moving are interpreted by the windowServer, and the window is moved in the windowServer process rather than in the application process. So, up until now, applications have not had a good way to disable or customize window moving. Calling setMovable:NO disables windowServer moving. If desired, you can implement application-controlled window moving by handling mouseDown/mouseDragged/mouseUp events in a window subclass. If a window is not movable, its relative screen position is also preserved in spaces F8 mode, but you can still drag the window to a different space in F8 mode.

We've also added a heuristic to trigger a space switch for a client side move of a window. The window must return NO for isMovable, and must have its origin changed while the mouse is down. One thing this heuristic does not address is the ability to pull a non-movable window to another space by clicking in its draggable area and switching spaces. Lastly, there is an attempt to preserve the origin of a non-movable window on display configuration change, but it will be moved if necessary to keep the window onscreen.

NSWindow's firstResponder is KVO-compliant. Note we don't generally guarantee KVO-compliance for NSWindow and NSView properties; you should not assume KVO-compliance unless specifically documented.

NSWindow autodisplay behavior

NSWindow autodisplay is now throttled to occur no more frequently than the beam sync interval (about 1/60 second). On Leopard and previous, marking a view as dirty in an autodisplay window would cause window display to occur at the end of the event loop. On SnowLeopard, window display may be deferred until the beam sync time interval has elapsed. This is important for performance, since it avoids blocking the main thread in drawing operations.

NSWindow collection behavior

In Leopard, we introduced the concept of window collection behavior.  We added an enumeration to specify that a window could be visible on all spaces or that it would become visible on the current space when ordered front, or that it would get default behavior.  The default behavior is currently dependent on window level.

When a window has NSNormalWindowLevel, it gets these default behaviors:
- the window is visible and selectable in Exposé F9 and F10 modes
- the window is visible during Spaces F8 mode, and is associated with one space at a time.  Dragging the window to a space boundary triggers a space switch
- the window participates in window cycling (cmd-` and ctrl-F4)

When a window has any other window level, it gets these default behaviors:
- the window is hidden in Exposé F9 and F10 modes
- the window is hidden in Spaces F8 mode, and floats across space switches if offscreen during a switch.  Dragging the window to a space boundary does not trigger a space switch
- the window does not participate in window cycling

In order to provide additional application control, we've additional window collection behaviors to be used with -[NSWindow setCollectionBehavior:]
enum {
// participates in spaces, exposé.  Default behavior if windowLevel == NSNormalWindowLevel
 NSWindowCollectionBehaviorManaged = 1 << 2,
// floats in spaces, hidden by exposé.  Default behavior if windowLevel != NSNormalWindowLevel
NSWindowCollectionBehaviorTransient = 1 << 3,
// unaffected by exposé.  Stays visible and stationary, like desktop window
 NSWindowCollectionBehaviorStationary = 1 << 4,
};
enum {
  // default behavior if windowLevel == NSNormalWindowLevel
NSWindowCollectionBehaviorParticipatesInCycle = 1 << 5,
  // default behavior if windowLevel != NSNormalWindowLevel
NSWindowCollectionBehaviorIgnoresCycle = 1 << 6
};

NSWindowCollectionBehaviorManaged
- the window is visible and selectable in Exposé F9 and F10 modes
- the window is visible during Spaces F8 mode, and is associated with one space at a time.  Dragging the window to a space boundary triggers a space switch

NSWindowCollectionBehaviorTransient
- the window is hidden in Exposé F9 and F10 modes
- the window is hidden in Spaces F8 mode, and floats across space switches if offscreen during a switch.  Dragging the window to a space boundary does not trigger a space switch

NSWindowCollectionBehaviorStationary
- the window is unaffected by Exposé.  It stays in its current position and will most likely be covered by the Exposé shield window, just as the desktop is.

NSWindowCollectionBehaviorParticipatesInCycle
- the window participates in window cycling (cmd-` and ctrl-F4)

NSWindowCollectionBehaviorIgnoresCycle
- the window does not participate in window cycling

As in Leopard, -collectionBehavior will return only the behavior explicitly set via -setCollectionBehavior, or NSWindowCollectionBehaviorDefault if no behavior has been set explicitly.

The following examples illustrate how the new constants interact with the window-level-based default behavior.
[window setCollectionBehavior:NSWindowCollectionBehaviorDefault]
    - window will get behavior based on its window level, as described above under "Window collection behavior based on window level"
[window setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces]
    - window will be visible on all spaces, and will have behavior based on its window level for exposé and window cycling
[window setCollectionBehavior:NSWindowCollectionBehaviorMoveToActiveSpace]
    - window will become visible on active space when ordered front, and will have behavior based on its window level for exposé and window cycling
[window setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorManaged|NSWindowCollectionBehaviorIgnoresCycle]
    - window will be visible on all spaces, will be visible and selectable in exposé, and will not participate in window cycling
[window setCollectionBehavior:NSWindowCollectionBehaviorTransient|NSWindowCollectionBehaviorParticipatesInCycle]
    - will be hidden in spaces and exposé, and will participate in window cycling

NSWindow modal window level in LSUIElement applications

Modal windows in LSUIElement applications get special window level treatment, where the window level is NSModalPanelWindowLevel whether or not the owning application is active. Modal windows belonging to regular applications are promoted to NSModalPanelWindowLevel when the application is active, but restored to NSNormalWindowLevel when the owning application is inactive, in order to prevent modal windows from floating above other applications. Since LSUIElement applications follow different activation rules, active status is not required in order to keep the modal window in front of other windows.

NSWindow and NSScreen Color Space support (Modified since WWDC 2009)

NSScreen has an accessor to get the colorSpace of a screen.
@interface NSScreen : NSObject
...
- (NSColorSpace *)colorSpace;
...
@end
There is also a notification sent when the colorSpace of a screen changes, NSScreenColorSpaceDidChangeNotification. The notification object is the screen whose color space has changed.

In applications built on Leopard and previous, a window will inherit the colorSpace of the main screen (that is, the screen containing the menu bar). If a window returns YES for displaysWhenScreenProfileChanges, it will instead inherit that colorSpace of the screen which contains the majority of the window. displaysWhenScreenProfileChanges:YES is the default for windows in applications built on SnowLeopard or later. Either of these inheritance behaviors can be overridden by explicitly setting the color space of the window, which can be desirable to display content with better accuracy.
@interface NSWindow : NSResponder
...
/* -setColorSpace: allows you to specify a colorSpace that matches custom content.   Calling -setColorSpace: with an unsupported colorSpace will raise an exception.
Typically, this would be called immediately after creating the window; changing the colorSpace of a window with backingStore will trigger redisplay
*/
- (void)setColorSpace:(NSColorSpace *)colorSpace;
- (NSColorSpace *)colorSpace;
...
@end

NSWindow Carbon/Cocoa Interaction

Cocoa has a notion of a main window distinct from the key window. Historically, we have not supported Cocoa main windows in Carbon apps; a Cocoa window in a Carbon app has been either key or inactive. In SnowLeopard, we added support for Cocoa main windows in Carbon apps. This support is limited to apps linked on SnowLeopard or later since it has binary compatibility implications.

NSWindowDepth constants

NSWindow has existing API to get/set a window depth limit:
- (void)setDepthLimit:(NSWindowDepth)limit;
- (NSWindowDepth)depthLimit;
We haven't previously published the possible values for NSWindowDepth. The way it's worked historically is that one would call NSBestDepth with parameters specifying characteristics of the window depth, and NSBestDepth would return a value suitable for passing to setDepthLimit:.
NSWindowDepth NSBestDepth (NSString *colorSpace, NSInteger bps, NSInteger bpp, BOOL planar, BOOL *exactMatch);
NSWindow now publishes the supported values for NSWindowDepth.  Note that this is a depth limit, which means that the window may use a lower depth depending on graphics capabilities.enum {
    NSWindowDepth32BitRGB = 0x208,
    NSWindowDepth64BitRGB = 0x210,
    NSWindowDepth128BitRGB = 0x220
};

Displaying dictionary contents

We added a facility to display a definition or other dictionary contents for a selection, similar to HIDictionaryWindowShow from Carbon.

Both -showDefinitionForAttributedString:atPoint: and -showDefinitionForAttributedString:range:options:baselineOriginProvider: show a window that displays the definition (or other subject depending on available dictionaries) of the specified attributed string.

showDefinitionForAttributedString:atPoint: takes an attributedString "attrString", and a baseline origin of that string in the receiver view coordinate system "textBaselineOrigin". This method can be used for implementing the same functionality as NSTextView's 'Look Up in Dictionary' contextual menu on a custom view.

-showDefinitionForAttributedString:range:options:baselineOriginProvider: takes an attributedString "attrString", a range in that string (normally the selected range) "targetRange", an options dictionary "options", and an origin provider "originProvider". If there is no selection, the "targetRange" should be zero-length to cause the appropriate range to be auto-detected around the given offset. "options" may be nil, or may contain a dictionary with presentation options. If a small overlay window is selected as default presentation (see NSDefinitionPresentationTypeKey option for details), the overlay text is rendered starting from the origin specified by the originProvider block. The originProvider block should return the baseline origin of the first character at proposed adjustedRange in the receiver view coordinate system. If the receiver is an NSTextView, both attrString and originProvider may be nil, in which case the text view will automatically supply values suitable for displaying definitions for the specified range within its text content.  This method does not cause scrolling, so clients should perform any necessary scrolling before calling this method.
@interface NSView(NSDefinition)
- (void)showDefinitionForAttributedString:(NSAttributedString *)attrString
atPoint:(NSPoint)textBaselineOrigin;
- (void)showDefinitionForAttributedString:(NSAttributedString *)attrString
range:(NSRange)targetRange
options:(NSDictionary *)options
baselineOriginProvider:(NSPoint (^)(NSRange adjustedRange))originProvider;
@end
NSDefinitionPresentationTypeKey is an optional key in '"options" dictionary that specifies the presentation type of the definition display.  The possible values are NSDefinitionPresentationTypeOverlay, which produces a small overlay window at the string location, or NSDefinitionPresentationTypeDictionaryApplication, which invokes the Dictionary application to display the definition.  Without this option, the definition will be shown in either of those presentation forms depending on 'Contextual Menu:' setting in Dictionary application preferences by default.
NSString * const NSDefinitionPresentationTypeKey;
NSString * const NSDefinitionPresentationTypeOverlay;
NSString * const NSDefinitionPresentationTypeDictionaryApplication;

NSApplication termination

NSApplication now disables the terminate: action while waiting for the delegate to call replyToApplicationShouldTerminate: after returning NSTerminateLater from applicationShouldTerminate:. The purpose of this change is to avoid multiple calls to applicationShouldTerminate: while the application is doing termination-time cleanup. It has a side effect of disabling the Quit menu while waiting for the delegate. This should be desirable behavior, but is limited to applications linked on SnowLeopard or later to avoid preventing termination of applications whose delegates may not be calling replyToApplicationShouldTerminate: properly.

NSHelpManager

In Leopard and previous releases, NSHelpManager has done automatic registration of help books in the main bundle in its implementations of showHelp:, openHelpAnchor:inBook:, and findString:inBook:.  In SnowLeopard,we have added API so a plugin can register its help book for later use in help lookup.

The following method registers one or more help books in the given bundle.  The main bundle is automatically registered by -openHelpAnchor:inBook: and -findString:inBook:.  You can use -registerBooksInBundle: to register help books in a plugin bundle, for example.  The Info.plist in the bundle should contain a help book directory path, which specifies one or more folders containing help books.  Returns NO if the bundle doesn't contain any help books or if registration fails.  Returns YES on successful registration.
- (BOOL)registerBooksInBundle:(NSBundle *)bundle;

NSDockTile plugin (Modified since WWDC 2009)

An application may customize its dock tile when not running via a plugin whose principal class implements the NSDockTilePlugIn protocol.  The name of the plugin is indicated by a NSDockTilePlugIn key in the application's Info.plist file, and the plugin should have an extension of .docktileplugin.  The plugin is loaded in a system process at login time or when the application tile is added to the Dock.  When the plugin is loaded, the principal class' implementation of -setDockTile: is invoked.  If the principal class implements -dockMenu, -dockMenu is invoked whenever the user causes the application's dock menu to be shown.  When the dock tile is no longer valid (eg. the application has been removed from the dock, -setDockTile: is invoked with a nil NSDockTile.

Note that the plugin needs to have its own copy of the application icon, unless it is completely overriding drawing by setting its own contentView in the dockTile.
@protocol NSDockTilePlugIn <NSObject>
@required
- (void)setDockTile:(NSDockTile*)dockTile;
@optional
- (NSMenu*)dockMenu;
@end

NSViewController-based NSCollectionViewItem

In Mac OS 10.6, the superclass of NSCollectionViewItem was changed from NSObject to NSViewController. This was done to improve how the prototype view is replicated. The view replication method used previously in Mac OS 10.5 works for many simple views, but some connections and bindings may not be copied correctly.

With the new NSViewController-based NSCollectionViewItem, this limitation can be overcome by initializing the prototype NSCollectionViewItem with a nib name and bundle, from which the view can reliably replicated.

Converting your NSCollectionViewItem prototypes to using nibs is usually very simple. First, create a new empty nib in Interface Builder. Next, copy your prototype view into the new nib. Change the class of the File's Owner of the new nib to NSCollectionViewItem and connect the view to its 'view' outlet. In the new nib, reconnect any bindings from your view to the NSCollectionViewItem. Save the new nib. In your original nib, delete the prototype view and enter the name of your new nib (and the bundle identifier, if required—leaving it blank will search the main bundle) in the prototype NSCollectionViewItem's inspector and save the nib.

The new NSCollectionViewItem is completely compatible with existing ones. Using a nib-based prototype view is optional and NSCollectionViewItems archived in 10.5 will still be readable in 10.6.


NSCollectionView Drag and Drop

In Mac OS 10.6, NSCollectionView has a new drag and drop API similar to the existing APIs for NSTableView, NSBrowser, etc. For an NSCollectionView to be a dragging source, it must have a delegate that implements and returns YES from collectionView:canDragItemsAtIndexes:withEvent: and collectionView:writeItemsAtIndexes:toPasteboard:. For an NSCollectionView to be a dragging destination, it must have a delegate that implements collectionView:validateDrop:proposedIndex:dropOperation: and collectionView:acceptDrop:index:dropOperation:. For more detail, see the comments in NSCollectionView.h.

Two methods have been added to NSCollectionView, which may be necessary within your drag and drop delegate method implementations. The first is itemAtIndex:, which returns the NSCollectionViewItem associated with the represented object at the given index. This is especially useful if you need to get a reference to specific subviews of the NSCollectionView. The second is frameForItemAtIndex:, which returns the frame that the receiver has calculated for the subview at the given index. You should always use this method instead of calling [[[collectionView itemAtIndex:index] view] frame]. Doing this would force the view to be created prematurely, if the collection view is trying to lazily load its items. This method is especially useful in the collectionView:draggingImageForItemsAtIndexes:withEvent:offset: delegate method to determine which views are in the visible area of the collection view.


NSCollectionView bug fixes

Signficant bugs in NSCollectionView were fixed in Mac OS 10.6

When an NSCollectionView is resized, it will no longer cause its enclosing scroll view to scroll to the top.

Strange glitches in NSCollectionView's animations are fixed. Subviews will no longer jump unexpectedly, and will not stutter when their positions are animated. NSCollectionView will also work better together with NSScrollViews that autohide scrollers.

For NSCollectionViews that do not use the new nib-based prototype views, the view copying mechanism was made more reliable. In Mac OS 10.5, the views could potentially be copied before all their bindings or other connections had been loaded from the nib.

NSCollectionView will now correctly trigger KVO notifications for selectionIndexes when the selection is changed by mouse events.

NSCollectionView now ensures that its subviews are correctly positioned on pixel boundaries to avoid blurring or gaps between subviews.


NSSplitView appearance changes

In Mac OS 10.5, the default value of -[NSSplitView dividerColor] for thin dividers was a gray color, regardless of the window's style. This created a usually undesirable appearance when drawn on textured windows. In applications linked on or after Mac OS 10.6, -dividerColor will now return [NSColor clearColor] by default for split views with thin dividers on textured windows.

Also, NSSplitView has stopped drawing different versions of the divider dimple. From now on, it will only draw the dimmed version, regardless of the containing window's key state.


Formal Protocol Adoption

In Mac OS 10.6, AppKit switched to using formal protocols for all delegates and data sources to provide better compile-time type checking. Required protocol methods are marked with @required, where possible. The rest are marked with @optional.

The affected classes are NSAlert, NSAnimation, NSApplication, NSBrowser, NSComboBox, NSComboBoxCell, NSControl, NSDatePicker, NSDatePickerCell, NSDrawer, NSImage, NSLayoutManager, NSMatrix, NSMenu, NSOutlineView, NSPathCell, NSPathControl, NSRuleEditor, NSSavePanel, NSSound, NSSpeechRecognizer, NSSpeechSynthesizer, NSSplitView, NSTableView, NSTabView, NSText, NSTextField, NSTextStorage, NSTextView, NSTokenField, NSTokenFieldCell, NSToolbar, and NSWindow

The changes will introduce new warnings in code using these classes' delegates. Fortunately, the changes needed to correct these warnings are fairly simple.

• Your delegate classes need to declare conformance to the new protocols. For example:
@interface MyDelegate : NSObject <NSApplicationDelegate> ... @end
• Sending messages to [foo delegate] or [foo dataSource] that are not in the delegate's protocol will generate a warning. This is not generally recommended since there are no guarantees about the delegate's class. However, you may work around this by casting the result of [foo delegate] or [foo dataSource] to id. Also, you should always perform a respondsToSelector: check before invoking a method on [foo delegate] or [foo dataSource] that is not an @required method in the protocol.

• If you have a subclass of one of these classes that adds additional delegate methods you must also create a subprotocol and override -delegate and -setDelegate:. For example:
@protocol MySplitViewDelegate;
@interface MySplitView : NSSplitView
- (id <MySplitViewDelegate)delegate;
- (void)setDelegate:(id <MySplitViewDelegate>)delegate;
@end
@protocol MySplitViewDelegate <NSStreamDelegate>
- (void)additionalDelegateMethod;
@end
@implementation MySplitView
- (id <MySplitViewDelegate)delegate {
    return (id <MySplitViewDelegate>)[super delegate];
}
- (void)setDelegate:(id <MySplitViewDelegate>)delegate {
    [super setDelegate:delegate];
}
@end
• If you need to target Leopard or Tiger with the same sources, you should conditionally declare empty protocols, or else the compiler will complain about missing protocols declarations. For example:
#if MAC_OS_X_VERSION_10_6 > MAC_OS_X_VERSION_MAX_ALLOWED
@protocol NSTableViewDelegate <NSObject> @end
#endif

New NSSplitView delegate method

To make it easier for your application to maintain the size of given subviews of a split view while the split view is being resized, NSSplitView in Mac OS 10.6 has a new -splitView:shouldAdjustSizeOfSubview: delegate method you can implement. It controls how -adjustSubviews behaves so you don't have to rewrite so much of its default behavior in a -splitView:resizeSubviewsWithOldSize: delegate method.

By returning NO from this method, you can lock the size of a split view subview while the split view is resized. However, if the split view shrinks beyond the point that all of the resizable subviews are completely hidden, the subviews you returned NO for will start resizing as well to prevent -adjustSubviews from creating an invalid subview layout.

New NSSplitView pane splitter divider style

In Mac OS 10.6, NSSplitView has a new divider style called NSSplitViewPaneSplitterDividerStyle. This new style is significantly different than the "thick" style because it draws an opaque gradient along the entire length of the dividers and its width is slightly smaller. This style matches the appearance of the split view dividers in many of Apple-created applications.

Snow Leopard's Human Interface Guidelines strongly encourage you to migrate away from the "thick" style to this new style. Because of this, when you create a new NSSplitView instance in Interface Builder you will only be able to choose between the "thin" and "pane splitter" divider styles.

NSSplitView's paneSplitter and setPaneSplitter: methods are deprecated in Mac OS 10.6 in favor of the new divider style.



NSCell controlView property changes

The declarations for controlView and setControlView: are on NSCell, but until SnowLeopard the storage for the controlView property was at the NSActionCell level. -[NSCell controlView] always returned nil and -[NSCell setControlView:] did nothing. This caused a variety of unexpected behaviors.

For applications linked on or after SnowLeopard, the storage for this property is at the NSCell level, and the NSCell accessors behave as you might expect. The fix is limited to apps linked on or after SnowLeopard. This is because NSCell does not retain its controlView, so there are new opportunities for dangling pointers. If your app keeps a static NSCell instance that you use for drawing in many different contexts, for example, you may need to be careful to clear out the controlView between uses. This requirement was already in place for NSActionCell, so this is only an issue if you use something that doesn't descend from that class.

Also, prior to SnowLeopard, NSActionCell propagated its controlView to its copy in copyWithZone:. This is not correct and causes problems. The controlView is a transient property set just before a cell is drawn. As methods are invoked on the cell in the course of copying it, the original cell's control typically ends up dirtied and redrawn. Since a cell does not retain its controlView, this causes crashes if the original control view is deallocated.

For applications linked against the SnowLeopard SDK or later, the controlView is not propagated.

NSImageCell disabled images

In Leopard and previous, there is no out of the box view that displays an image that dims out when it is disabled. One can try to fake this with a button, but then the view accepts key focus when full keyboard access is enabled, has accessibility problems, darkens when pressed, etc.

One might expect NSImageView to do this, but NSImageView instead shows that it is disabled by drawing its frame differently. There are cases where you want this: When the image view is set up as an image well, the image may be user data that you are just presenting. It's odd to dim it out, especially when it suffices to dim the image well.

However, it seems unambiguous that if an image view has no frame, it is correct to show disabled by dimming the image.

This is the behavior for applications linked on or after SnowLeopard. The linkage check is here because some applications do disable image views without intending grayed out drawing. This could easily arise from experimenting with checkboxes in Interface Builder.

NSButton

The disclosure triangle style of NSButton is now able to invert to white on dark backgrounds and such, by interpreting the backgroundStyle property of the cell.

A caveat is that unfortunately our art for the raised background style is not as widely suitable as would be nice. It's really drawn for a source list style table, and doesn't look good very on the window surface. This control isn't suitable directly on the window surface for now. We're sorry. To avoid making existing apps look worse in this case, we only interpret the background styles for apps that have linked on or after 10.6, though the 10.5-ish non-engraved art doesn't look very good here either.

NSImage, CGImage, and CoreGraphics impedance matching

NSImage has changed substantially internally for SnowLeopard in ways that developers might want to be aware of. There is also some new API. AppKit imaging is now more closely aligned with CoreGraphics.

The basic change is to move to consistent use of CGImage internally. For example, in Leopard and previous, -[NSImage lockFocus] drew into a view in an offscreen window. Now it draws into a buffer, and a CGImage is extracted from the buffer in +unlockFocus. NSImage no longer uses windows at all in its implementation. We've entirely deprecated NSCachedImageRep, which is the NSImageRep subclass that represents drawing backed by a window. NSCachedImageRep does still work, but AppKit never instantiates it internally.

This has the potential to break clients. On Leopard and previous, +[NSView focusView] returned a view from within +[NSImage lockFocus]. Hopefully you were unaware of this! Now it returns nil. If you were looking at [[NSView focusView] isFlipped], you may wish to look at [[NSGraphicsContext currentContext] isFlipped] instead. The two coincide whenever focus is actually locked on a view.

Another issue we've seen is with clients calling -lockFocus on an image, then drawing the image elsewhere before calling -unlockFocus. This no longer works. The NSImage itself is not modified until -unlockFocus is called, as described above.

NSImage in Leopard and previous rounded destination rectangles to integral values in user-space, which was not so good for resolution independence, and not good for people who genuinely wanted images placed where they were put, with antialiasing. This rounding is out, but may mean that some images look blurry.

We've added a method of extracting CGImages from an NSImage, -[NSImage CGImageForProposedRect:context:hints:]. Why all the parameters? Well, an NSImage is potentially resolution independent, and may have multiple representations suited for different contexts.  For example, an NSImage may have a hand drawn 16x16 bitmap as well as a 512x512 bitmap. This allows the image to display well at large and very small sizes. A CGImage is more like a single pixel-based representation.   Think of -CGImageForProposedRect:context:hints: as producing a snapshot of how the NSImage would draw if it was asked to draw in *proposedDestRect in the passed context.

A snapshot is a good way to think of it, but may lead you to think the method is less efficient than it really is. There's nothing guaranteed about the characteristics of the returned CGImage (e.g., pixel size, colorspace) except that drawing it in the passed context produces the same results as drawing the original NSImage in the context. In particular, if an NSImage is backed by a single NSBitmapImageRep, then it will (probably) always return the one CGImage that backs the bitmap. That's a valid snapshot for the image for any destination rect, because in this case the CGImage _does_ capture all the drawing the NSImage is capable of.

The rect, context and hints are first used to select the best representation for the described destination using new API -[NSImage bestRepresentationForRect:context:hints:], then the image rep is asked for a CGImage using -[NSImageRep CGImageRepForProposedRect:context:hints:]. NSImage may cache the CGImage, same as it may cache for normal drawing and controlled in the same way, and the CGImage is returned.

The NSImageRep level method is intended as an override point for image rep subclasses that naturally have a CGImage available. For example, NSBitmapImageRep overrides it to return the CGImage that naturally backs the rep. You don't need to override the method except possibly for performance, though. The NSImageRep-level implementation will produce a CGImage by making a buffer and calling -[self draw]. That's likely to be the best possible implementation for reps that aren't naturally CGImage backed. -draw remains the only method of NSImageRep that a subclasser really needs to override.

One subtlety: The 'proposedRect' parameter passes a pointer to an NSRect. This is because a CGImage is necessarily pixel-integral, while an NSImage is not. In order to produce a CGImage for rect (0.5, 0.5, 4.0, 4.0) without distortion or double-antialiasing, we may have to produce a 5x5 CGImage, and also inflate the proposedRect. Drawing the CGImage in the out-value in proposed rect is the same as drawing the NSImage in the in-value of proposed rect.

For example, this block of code
- (void)drawRect {
NSRect dstRect = NSMakeRect(0.5, 0.5, 4.0, 4.0);
CGImageRef cgImage = [pdfBackedImage CGImageForProposedRect:&dstRect context:[NSGraphicsContext currentContext] hints:nil];
// dstRect might be something like {{0.0, 0.0}, {5.0, 5.0}} on return
CGContextDraw([[NSGraphicsContext currentContext] graphicsPort], NSRectToCGRect(dstRect), cgImage);
}
should produce drawing equivalent to (or at least very close to) this code:
- (void)drawRect {
NSRect dstRect = NSMakeRect(0.5, 0.5, 4.0, 4.0);
[pdfBackedImage drawInRect:dstRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
The latter code is better, though. Producing a snapshot CGImage may be more expensive than just drawing the NSImage (though we'll use an existing CGImage whenever possible), so prefer to just draw with -[NSImage drawInRect:fromRect:operation:fraction:] or related methods unless you truly require a CGImage.

There is also a method for conveniently and efficiently producing an NSImage from a CGImage, -[NSImage initWithCGImage:size:].

NSImage Behavior: Simplifying caching and related interfaces

In Leopard and previous, NSImage has several methods that influence caching and related behavior, sometimes with important effects on how the image draws.  These important effects have sometimes been a source of confusion for clients.  For SnowLeopard, the intent is that caching should be handled more transparently, and that caching should affect only performance, not the basic meaning of how an image draws.

To support this, the following methods are deprecated without replacement:
    - (void)setCachedSeparately:(BOOL)flag;
    - (BOOL)isCachedSeparately;
    - (void)setCacheDepthMatchesImageDepth:(BOOL)flag;
    - (BOOL)cacheDepthMatchesImageDepth;
    - (void)setScalesWhenResized:(BOOL)flag;
    - (BOOL)scalesWhenResized;
    - (void)setDataRetained:(BOOL)flag;
    - (BOOL)isDataRetained;
Any call to set one of these properties can be removed.  Briefly:
isCachedSeparately:
    This controlled whether an image shared a cache window with other instances of NSImage.  NSImage no longer caches to windows.
cacheDepthMatchesImageDepth:
    This controlled the bit depth used in making cache windows.  NSImage no longer caches to windows.  A cache is now generated appropriate for the destination where an image is drawn, and it doesn't apply if the image is drawn into a context that doesn't match in some way that would have led to a different cache being created.
scalesWhenResized:
    One might think -scalesWhenResized would be a semantic property rather than a caching flag, but it really had to do with whether caches were invalidated when -[NSImage setSize:] was called.  The size of an image is no longer relevant for caching purposes - only the size of the rect where the image is drawn is important.  In SnowLeopard, NSImage caching should never have any obviously observable user effect like an image being drawn into a different size region of the screen.  Caching is about performance.

    The complete behavior when the property was set to NO was complex, but it did not reliably achieve keeping the image from scaling.  It did make the scaled image draw blocky, sometimes.
isDataRetained:
    When this was NO, which it was by default, an image was permitted to throw out its backing data when drawn and keep something only appropriate for the context it which it was first used.  For example, a PDF-backed image would typically be rasterized when first drawn, and the PDF data would be irrecoverably discarded.  In SnowLeopard, NSImage no longer throws out data in such a way that the original can no longer be reconstructed.

Please note that -[NSImage setCacheMode:] is not deprecated.  In particular, you can use NSImageCacheNever to disable all use of caches in NSImage.  Disabling caching may be an optimization if the image is known to be temporary, or if there is caching taking place elsewhere that would make a cache at this level wasted memory.

NSImage: deprecating -[NSImage setFlipped:], adding a drawing method that respects context flippedness

The flipped attribute of NSImage is widely misunderstood.  We are deprecating it for SnowLeopard, and replacing its typical uses with less error-prone API.

The property describes the orientation of the internal coordinate system of the NSImage.  Just as a superview never cares about the flippedness of its subviews, a user of an NSImage should not care about its flippedness.

The typical (flawed) use case is to try to call [image setFlipped:[[NSGraphicsContext currentContext] isFlipped]] just prior to drawing, but this does not accomplish the intended goal.  If called before caching, then representations end up caching upside down, and the flip is absorbed into the cache.  If called after caching, it has no effect-the cached representation is already supposed to incorporate any necessary flipping.  In the former case, if the NSImage is drawn anywhere else later, it ends up upside down in _that_ place, which is also confusing because the bug and the expression of the bug are far apart.  Lack of understanding regarding flippedness is also frequently the source of poorly performing code, in which people make unnecessary intermediate buffers to work around perceived framework bugs.  The framework behaves according to design, but contrary to expectation, and the semantics are not all that useful. It's also difficult to change the semantics of -[NSImage isFlipped], because a lot of code is very closely dependent on the current behavior. Rather than attempt this, we have deprecated the property.

We are providing a simple and correct way to draw images in a flipped or unflipped context, which is a draw method that can account for context flippedness.  We are also adding a hints parameter matching the hints in -bestRepresentationForRect:context:hints:.
- (void)drawInRect:(NSRect)dstRect
fromRect:(NSRect)srcRect
operation:(NSCompositingOperation)op
fraction:(CGFloat)alpha
respectFlipped:(BOOL)respectContextIsFlipped
hints:(NSDictionary *)hints;
Pass YES for respectFlipped to get the fancy new behavior. One note for those that understand the CTM and worry that this method has an odd interaction, where modifying the CTM could fail to have any effect on image drawing: This is not the case. This method branches behavior based on [[NSGraphicsContext currentContext] isFlipped]. Modifying the CTM might turn your axes upside down, but it will not alter the result of -[NSGraphicsContext isFlipped]. They're completely orthogonal.

A second valid use of -[NSImage setFlipped:] was to specify the flippedness of the context obtained via -[NSImage lockFocus].  There are cases, for example drawing directly via NSLayoutManager, that require a flipped context.  To cover this case, we add
- (void)lockFocusFlipped:(BOOL)flipped;
This doesn't alter the state of the image itself, only the context on which focus is locked. It means that (0,0) is at the top left and positive along the Y-axis is down in the locked context.

NSImage: Breaking change to drawAtPoint:fromRect:operation:fraction:

-[NSImage drawAtPoint:fromRect:operation:fraction:] has different behavior for applications linked on or after SnowLeopard than it did previously. We corrected a bug that has substantial impact on behavior, but it's weird enough, and easy enough to work around, that we think we can get away with fixing it. The natural thing to do if you ever encountered this bug was to switch to doing something else, not to depend on it.

Prior to SnowLeopard, -drawAtPoint:fromRect:... called -drawInRect:fromRect:... passing [image size] as the size of the destination rect. If 'image' was 100x10, then the snippet
    [image drawAtPoint:NSZeroPoint fromRect:NSMakeRect(0,0,10,10) operation:NSCompositeSourceOver fraction:1.0];
was equivalent to
    [image drawInRect:NSMakeRect(0,0,10,100) fromRect:NSMakeRect(0,0,10,10) operation:NSCompositeSourceOver fraction:1.0];
which makes little sense. This doesn't even preserve the aspect ratio of the source rect. In applications linked on SnowLeopard, the snippet is instead equivalent to
    [image drawInRect:NSMakeRect(0,0,10,10) fromRect:NSMakeRect(0,0,10,10) operation:NSCompositeSourceOver fraction:1.0];
This obviously may require changes when you recompile your code, so search your source for drawAtPoint. The change has no effect on applications that pass {{0,0}, [image size]} for the source rect, which is what most applications do. As with the other drawing methods that take source rects, NSZeroRect is treated as sentinel meaning 'the whole image'.

If you were passing a partial source rect on Leopard and earlier, and you need compatibility on Leopard and SnowLeopard, then replace your usage of -drawAtPoint:fromRect:operation:fraction: with -drawInRect:fromRect:operation:fraction:.

NSImage: Deprecating old drawing methods

NSImage has a suite of drawing methods that start with "draw" and a suite of drawing methods that start with "dissolve" or "composite".
- (void)dissolveToPoint:(NSPoint)point fraction:(CGFloat)aFloat;
- (void)dissolveToPoint:(NSPoint)point fromRect:(NSRect)rect fraction:(CGFloat)aFloat;
- (void)compositeToPoint:(NSPoint)point operation:(NSCompositingOperation)op;
- (void)compositeToPoint:(NSPoint)point fromRect:(NSRect)rect operation:(NSCompositingOperation)op;
- (void)compositeToPoint:(NSPoint)point operation:(NSCompositingOperation)op fraction:(CGFloat)alpha;
- (void)compositeToPoint:(NSPoint)point fromRect:(NSRect)rect operation:(NSCompositingOperation)op fraction:(CGFloat)alpha;
The "draw" methods are newer, and are generally what you want to be using.

The "composite" methods have surprising semantics. They set the current transformation matrix of the graphics context to be the identity matrix (or more accurately, the identity scaled up by the default user space scale factor – think default window scaling with resolution independence) before drawing. That is, in a scaled or rotated view, composite methods draw as if the view is not scaled or rotated.

This isn't usually what one is really after. Often people use these methods because they seem to produce right-side-up drawing in flipped views. If that's what you're after, use the new method -[NSImage drawInRect:fromRect:operation:fraction:respectContextFlipped:hints:] passing YES for respectContextFlipped.

We are not currently providing a direct replacement, but if you do have a case that uses the composite methods for their intended purpose, you can reproduce the result by manipulating the graphics context before drawing.

NSImage: Deprecating -lockFocusOnRepresentation:

The NSImage method:
- (void)lockFocusOnRepresentation:(NSImageRep *)imageRepresentation;
is deprecated because it is commonly misunderstood and easy to replicate. It does not set up the rep as a drawing destination, it sets the image up as a drawing destination, then draws the rep into it. You can replace usage with
[image lockFocus];
[rep drawInRect:NSMakeRect(0,0,[image size].width, [image size].height)];

NSImageRep: More flexible drawing

This addition to NSImageRep provides drawing flexibility equivalent to NSImage's. Prior to SnowLeopard, one needed to wrap a rep up with an NSImage to draw with any operation other than NSCompositeCopy, for example. That's a pain since it's far more common to draw with NSCompositeSourceOver.
- (BOOL)drawInRect:(NSRect)dstRect
fromRect:(NSRect)srcRect
operation:(NSCompositingOperation)op
fraction:(CGFloat)alpha
respectFlipped:(BOOL)respectContextIsFlipped
hints:(NSDictionary *)hints;

NSImage: Hit testing for non-transparent regions

NSImage has gained a method that tests whether clicks and drags hit an opaque part of an image.
- (BOOL)hitTestRect:(NSRect)testRectDestSpace
withImageDestinationRect:(NSRect)imageRectDestSpace
context:(NSGraphicsContext *)context
hints:(NSDictionary *)hints
flipped:(BOOL)flipped;
More precisely, this method answers the question, "If you were to draw the image in the passed destination rect in the passed context respecting the passed flippedness with the passed hints, would the test rect in the context intersect a non-transparent portion of the image?". The information about the drawing destination is necessary because an NSImage draws differently depending on where it's going. Consider a typical Mac OS X icon that has a 16x16, 32x32 and 256x256 pixel representations. The choice of representation to draw depends on the destination, so accurate hit testing also needs destination information.

However, the context and hints parameters can be omitted, and we'll assume the context is set up like a window context with default scaling. This is the same as in -[NSImage bestRepresentationForRect:context:hints:] and the other NSImage methods that take similar parameters. If you don't have an NSGraphicsContext handy but you know that when the image draws it is into a context that differs from the window's context in some respects, you can use the hints to describe that context.

The flipped parameter is called out separately rather than supplied as a hint because it's a critical parameter. It determines the orientation with which the image draws in the destination rect, and therefore which portion of the image you are hit testing. If you are passing a non-nil context, you probably want to pass [context isFlipped]. If you don't have a context handy, but you're are calling this from within a view and hit testing an image that you draw in drawRect:, then you should probably pass [view isFlipped] and the rect where you draw the image for imageRectDestSpace.

There is one special case behavior. If the test rect has {0,0} size, then this method instead tests whether the origin point hits something non-transparent in the image. You could think of this as testing a rect with infinitesimal size.

NSImage: New standard images

We added a passel of new names for use with +[NSImage imageNamed:]. Search for AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER near the bottom of NSImage.h, or look at the "Media" tab in Interface Builder's library panel.

NSImage: Orientation metadata (e.g. exif) now respected by default

Some image file formats, such as JPEG, contain metadata that describes the 'orientation' of the data backing the image.  A camera may tag its image data as rotated by 90 degrees counterclockwise relative to what the user should see.

NSImage has never interpreted this metadata.  For apps linked on or after SnowLeopard, we do.  Preview and ImageKit interpreted this information on 10.5.  WebKit currently does not.  ImageIO, the fundamental image reading framework, reports the information but does not account for it.

We interpret the data in a way that is transparent to clients.  Bitmap data, pixel size, and all other properties are reported with respect to the image after orientation correction.

This is a major behavior change in some ways, but we do not expect it to impact very much code in practice.  Still, there are some developers who will need to conditionally opt-out for compatibility.  We introduce a new piece of API on NSImage to support them.  This is a backward-looking method for opting out of the new behavior.
    - (id)initWithDataIgnoringOrientation:(NSData *)data;
Who needs this?  Consider a document format that references images.  If a document is saved on Leopard, it's usually correct for it to look as it did on Leopard when opened on SnowLeopard.  In a drawing program that includes images, the user may have manually rotated an image to compensate for ignored orientation information. The image would end up double rotated if the app began to interpret the embedded orientation.  See the end of this section for an example of how to handle this kind of compatibility constraint.

It's not all that common to need to change code, though. In Mac OS X, only AppKit needed to react to the change.  The difference is relevant to image loading, not to working with images, and is only relevant for images which are user data as opposed to embedded in a framework.  Images written out by, say, TIFFRepresentation or by archiving an NSImage are always in a normalized orientation, so no changes necessary when reading them.  Most apps _want_ to interpret image orientation data.  A big exception is existing documents that directly reference image files that came from the user untouched.

-[NSImage initWithDataIgnoringOrientation:] is really a convenience method.  Another way to ignore image orientation is to use ImageIO's CGImageSource to make a CGImage, and then -[NSImage initWithCGImage:size:] to represent it as an NSImage.  ImageIO reports orientation metadata, but does not automatically interpret it.

We also provide a global opt-out. If you set the user default NSBitmapImageRepRespectOrientation to NO, NSImage will behave the old way all of the time.  This is an option of last resort for developers who cannot update affected code used by their app for some reason.

Here's an example of the work that might be required when moving an app up to the 10.6 SDK:

AppKit itself reads and writes the RTFD format.  Here's what we had to do:  First, each image attachment in memory now has an internal 'noOrient' bit, and we added a /noorient tag in the on-disk file format.  If noOrient is set, then when the corresponding image is loaded from disk, it's loaded with -[NSImage initWithDataIgnoringOrientation:].  Each RTFD document on disk also has a global version tag.  For old versions, every image attachment is implicitly noOrient.  When a new image is added to a document in memory, the bit is not set.  Finally, when we save an RTFD in our current file format version, we write out the /noorient tag for any image attachment on which it is set.

NSImage: CALayer accepts NSImage as contents

You may now call -[CALayer setContents:] with an NSImage as the contents object.  Previously you needed a CGImage.

There is one subtlety. CoreAnimation is built to work with a raster object, a bitmap.

If your image comes from a bitmap file like a TIFF then there is no issue, but a PDF, for example, will be rasterized.  The layer will not scale like a PDF. It will scale like a bitmap.   If the default way we produce the raster form doesn't meet your needs, you can exert more control using -[NSImage CGImageForRect:context:hints:].  This will produce a CGImage that you can set as the contents of the layer.

CALayer will also perform caching.  You should not modify your NSImage after setting it as the contents of a layer.  When or if your changes show up would be arbitrary.  This issue not specific to CALayer;  it is never a good idea to modify an NSImage unless you just created it and the modifications are part of the initial setup of the image.  If an NSImage has escaped to code you don't own, or an NSImage has been handed to you by code you don't own, you should not modify it.

Note: Even before this change, you could not call -[CALayer contents] and assume that the result was a CGImage.  The method is typed id, and the object returned could be one of several different private types in 10.5.

NSImage: Clarifying the contract for threading

NSImage, NSImageRep, and all of its subclasses have the same threading contract as NSMutableDictionary, NSMutableArray, NSMutableString and company.

If you aren't calling mutating methods, an image can be safely used from arbitrary threads.

If you _are_ mutating an image, you need to guarantee that the image is not being accessed on any other threads while the mutation is occurring, or risk crashes. You need to have full knowledge of what references the image and be able to prevent, say, drawing on any other thread.

In practice, this often means that you may only modify an image as part of its initial setup, before it has escaped to the app at large. Copying an image and modifying the copy is valid and occasionally necessary. We try hard to make copies cheap. The backing data is shared whenever possible (without impairing thread safety).

By "mutating method" we mean those methods that do explicit mutation, like -setSize: or -addRepresentation:. We do _not_ mean methods like -drawInRect:fromRect:operation:fraction: that may modify the image's ivars as a side effect of caching and lazy faulting and such.  If an image has internal caching or laziness, it will be handled in a way that does not impair thread safety. Locking focus on an image and drawing in it is a mutation, however, as is modifying the bitmapData of an NSBitmapImageRep.

There's one notable exception to the above rule: Even though it's a mutation, you may call -[NSBitmapImageRep incrementalLoadFromData:complete:] while other threads are accessing the image.

Subclassers of NSImageRep take note: You need to adhere to this contract as well. Expect non-mutating methods to be called on arbitrary threads.

NSBitmapImageRep: CoreGraphics impedance matching and performance notes

Release notes above detail core changes at the NSImage level for SnowLeopard. There are also substantial changes at the NSBitmapImageRep level, also for performance and to improve impedance matching with CoreGraphics.

NSImage is a fairly abstract representation of an image. It's pretty much just a thing-that-can-draw, though it's less abstract than NSView in that it should not behave differently based aspects of the context it's drawn into except for quality decisions. That's kind of an opaque statement, but it can be illustrated with an example: If you draw a button into a 100x22 region vs a 22x22 region, you can expect the button to stretch its middle but not its end caps. An image should not behave that way (and if you try it, you'll probably break!). An image should always linearly and uniformly scale to fill the rect in which its drawn, though it may choose representations and such to optimize quality for that region. Similarly, all the image representations in an NSImage should represent the same drawing. Don't pack some totally different image in as a rep.

That digression past us, an NSBitmapImageRep is a much more concrete object. An NSImage does not have pixels, an NSBitmapImageRep does. An NSBitmapImageRep is a chunk of data together with pixel format information and colorspace information that allows us to interpret the data as a rectangular array of color values.

That's the same, pretty much, as a CGImage. In SnowLeopard an NSBitmapImageRep is natively backed by a CGImageRef, as opposed to directly a chunk of data. The CGImageRef really has the chunk of data. While in Leopard an NSBitmapImageRep instantiated from a CGImage would unpack and possibly process the data (which happens when reading from a bitmap file format), in SnowLeopard we try hard to just hang onto the original CGImage.

This has some performance consequences. Most are good! You should see less encoding and decoding of bitmap data as CGImages. If you initialize a NSImage from a JPEG file, then draw it in a PDF, you should get a PDF of the same file size as the original JPEG. In Leopard you'd see a PDF the size of the decompressed image. To take another example, CoreGraphics caches, including uploads to the graphics card, are tied to CGImage instances, so the more the same instance can be used the better.

However: To some extent, the operations that are fast with NSBitmapImageRep have changed. CGImages are not mutable, NSBitmapImageRep is. If you modify an NSBitmapImageRep, internally it will likely have to copy the data out of a CGImage, incorporate your changes, and repack it as a new CGImage. So, basically, drawing NSBitmapImageRep is fast, looking at or modifying its pixel data is not. This was true in Leopard, but it's more true now.

The above steps do happen lazily: If you do something that causes NSBitmapImageRep to copy data out of its backing CGImageRef (like call bitmapData), the bitmap will not repack the data as a CGImageRef until it is drawn or until it needs a CGImage for some other reason. So, certainly accessing the data is not the end of the world, and is the right thing to do in some circumstances, but in general you should be thinking about drawing instead. If you think you want to work with pixels, take a look at CoreImage instead - that's the API in our system that is truly intended for pixel processing.

This coincides with safety. A problem we've seen with our SnowLeopard changes is that apps are rather fond of hardcoding bitmap formats. An NSBitmapImageRep could be 8, 32, or 128 bits per pixel, it could be floating point or not, it could be premultiplied or not, it might or might not have an alpha channel, etc. These aspects are specified with bitmap properties, like -bitmapFormat. Unfortunately, if someone wants to extract the bitmapData from an NSBitmapImageRep instance, they typically just call bitmapData, treat the data as (say) premultiplied 32 bit per pixel RGBA, and if it seems to work, call it a day.

Now that NSBitmapImageRep is not processing data as much as it used to, random bitmap image reps you may get ahold of may have different formats than they used to. Some of those hardcoded formats might be wrong.

The solution is _not_ to try to handle the complete range of formats that NSBitmapImageRep's data might be in, that's way too hard. Instead, draw the bitmap into something whose format you _know_, then look at that.

That looks like this:
NSBItmapImageRep *bitmapIGotFromAPIThatDidNotSpecifyFormat;
NSBitmapImageRep *bitmapWhoseFormatIKnow = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:width pixelsHigh:height
bitsPerSample:bps samplesPerPixel:spp hasAlpha:alpha isPlanar:isPlanar
colorSpaceName:colorSpaceName bitmapFormat:bitmapFormat bytesPerRow:rowBytes
bitsPerPixel:pixelBits];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmapWhoseFormatIKnow]];
[bitmapIGotFromAPIThatDidNotSpecifyFormat draw];
[NSGraphicsContext restoreGraphicsState];
unsigned char *bitmapDataIUnderstand = [bitmapWhoseFormatIKnow bitmapData];
This produces no more copies of the data than just accessing bitmapData of bitmapIGotFromAPIThatDidNotSpecifyFormat, since that data would need to be copied out of a backing CGImage anyway. Also note that this doesn't depend on the source drawing being a bitmap. This is a way to get pixels in a known format for any drawing, or just to get a bitmap. This is a much better way to get a bitmap than calling -TIFFRepresentation, for example. It's also better than locking focus on an NSImage and using -[NSBitmapImageRep initWithFocusedViewRect:].

So, to sum up:
(1) Drawing is fast. Playing with pixels is not.
(2) If you think you need to play with pixels, (a) consider if there's a way to do it with drawing or (b) look into CoreImage.
(3) If you still want to get at the pixels, draw into a bitmap whose format you know and look at those pixels.

NSImage performance debugging: NSNewBitmapBackingStore

The most fundamental rule to image performance on Mac OS X is this: If you see something drawing repeatedly on the screen, it should be drawn with the same identical image object each time.

In 10.6 there's a private function to help you notice if you aren't managing this. NSNewBitmapBackingStore is the funnel point in AppKit for making a new buffer of image data. New buffers of image data mean new images.

Thus, you don't want to see this function trip on every frame during repeated drawing situations like live resize. DTrace via Instruments is a nice way to see if this is happening. In Instruments, select "Build New Instrument…" from the Instrument menu. Fill in "AppKit" for the Library and NSNewBitmapBackingStore for the function. Now start recording in Instruments, and use your app as normal. You'll see a blip whenever the function is called. A smattering of calls is normal, but if you see it called furiously then you likely have a performance problem.

A few things to note:

(1) This is a private function. That means it won't necessarily be there in the future. You can use it to debug, but don't compile knowledge of it into your app.
(2) The goal is not to avoid seeing this function called, the goal is to make your app perform better. If you drop to Quartz level to make images, for example, you can bypass calling this function without making your app better. Rather, if you see this function being called a bunch, try to understand why new images are being created and address the cause. The other material in these release notes may help you understand.

NSBitmapImageRep notable bug fix: -[NSBitmapImageRep CGImage] safer now

In Leopard, -[NSBitmapImageRep CGImage] would under most circumstances return a CGImage that was not retained by the bitmap rep, but which would cause a crash if its data was accessed (e.g., by drawing it) after the bitmap rep had been destroyed.

This is fixed in SnowLeopard.

NSBitmapImageRep: Expanded colorspace support

Prior to SnowLeopard, NSBitmapImageRep supported specific colorspaces through a colorspace name property, and ICC profile data through -[NSBitmapImageRep valueForProperty:] with NSImageColorSyncProfileData property.

For SnowLeopard, we add support for working directly with colorspaces.
- (NSColorSpace *)colorSpace;
- (NSBitmapImageRep *)bitmapImageRepByConvertingToColorSpace:(NSColorSpace *)targetSpace renderingIntent:(NSColorRenderingIntent)renderingIntent;
- (NSBitmapImageRep *)bitmapImageRepByRetaggingWithColorSpace:(NSColorSpace *)newSpace;
Both -[NSBitmapImageRep bitmapImageRepByConvertingToColorSpace:renderingIntent:] and -[NSBitmapImageRep bitmapImageRepByRetaggingWithColorSpace:] may fail, in which case they will return nil. The latter definitely _will_ fail if you pass a colorspace that has a different color space model than the receiver. That is, if your original image is sRGB, you can only retag with some other RGB colorspace.

Both methods will return the original bitmap if it already has the requested colorspace.

NSComboBox

For apps linked on or after SnowLeopard, if a combo box is configured to send action on enter only, then it will not send its action when a selection is made from the drop down, either with the mouse or the keyboard. Only enter in the text field part of the combo box will send the action.

NSDrawThreePartImage accepts nil caps

As of 10.6, NSDrawThreePartImage can handle a nil for either the start cap or the end cap in NSDrawThreePartImage. If you pass nil for both, you'll be tiling the fill image either horizontally or vertically across the specified rect.



Support for EPS images

EPS images can now be read without crashing, though images with the bounding box specified at the end of the file will probably still load with an incorrect bounding box.


Rotated PDFs in patterns

PDFs with a /Rotate entry are now correctly displayed rotated when used in patterns (e.g. +[NSColor colorWithPatternImage:]).


NSPasteboard

Multiple Pasteboard Items

Historically, NSPasteboard has been able to store multiple data representations of a single logical item. Each representation is data of a particular type, keyed by the type name. In SnowLeopard, NSPasteboard adds support for multiple pasteboard items. Each item represents a single logical item which can hold multiple data representations. Prior to multiple pasteboard items, plural pboard types were necessary to put multiple pieces of data onto the single logical item of the pasteboard. For example, the NSFilenamesPboardType consists of an array of file paths all combined into one serialized property list. Using multiple pasteboard items, the analogous operation would result in multiple pasteboard items, with each file URL represented by a single pasteboard item.

Interacting with NSPasteboard has always been data type-centric, centered around getting and setting data for particular types. The new NSPasteboard API also allows for higher-level class-centric operations, while still allowing type-centric interaction when necessary.

For instance, the following code writes the appropriate data from an NSAttributedString onto the general pasteboard:
    NSAttributedString *attString =...;
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
[pboard clearContents];
[pboard writeObjects:[NSArray arrayWithObject:attString]];
The following code reads data from each pasteboard item and creates an NSAttributedString instance from each item that contains data an attributed string can be initialized with:
    NSPasteboard *pboard = [NSPasteboard generalPasteboard];
NSArray *desiredClasses = [NSArray arrayWithObject:[NSAttributedString class];
NSArray *attributedStrings = [pboard readObjectsForClasses:desiredClasses] options:nil];
And, for things such as menu item validation, the following code checks if at least one NSAttributedString instance can be created from the contents of the pasteboard:
    NSPasteboard *pboard = [NSPasteboard generalPasteboard];
NSArray *desiredClasses = [NSArray arrayWithObject:[NSAttributedString class];
BOOL canRead = [pboard canReadObjectForClasses:desiredClasses] options:nil];
This functionality is supported by two new protocols, NSPasteboardWriting and NSPasteboardReading. These protocols respectively allow a class to specify which data types its instances can provide to the pasteboard, and which data types its instances can be created from. The Cocoa classes NSAttributedString, NSImage, NSColor, NSURL, NSString, and NSSound implement this protocol so that common pasteboard types can be easily written and read. Other classes, such as an application's model classes, can also implement these protocols and be used in the same fashion.

See NSPasteboard.h for more information and examples of using multiple pasteboard items.

NSPasteboardItem

Although the class-centric APIs are a convenient abstraction from the details of data types, there are times when it is necessary to interact with the contents of the pasteboard by data type. The NSPasteboardItem class, introduced in SnowLeopard, represents a single pasteboard item and can hold multiple data representations. The NSPasteboardItem API is very similar to the original NSPasteboard API, with facilities for getting and setting data by type, as well as adding a data provider to provide data lazily.

NSPasteboardItem implements the NSPasteboardWriting and NSPasteboardReading protocols. As such, you add pasteboard items to the pasteboard by calling -writeObjects: and you can read pasteboard items from the pasteboard by calling -readObjectsForClasses:options: and specifying the NSPasteboardItem class. As a convenience, the NSPasteboard method -pasteboardItems method will return the array of all pasteboard items. The NSPasteboard validation method -canReadItemWithDataConformingToTypes: will check if any pasteboard item contains data conforming to the specified types. This supports writing validation code without asking for and iterating through all pasteboard items.

Some cases where you may need to use a pasteboard item directly:
1. When the set of types you wish to write to the pasteboard does not map well to a particular class. For instance, you may have a custom pasteboard type which is a property list that does not map directly to a class in your application. In this case, you might create a pasteboard item, set the property list for your custom type, and then write the pasteboard item to the pasteboard.

2. When you implement a delegate method or subclass a method that receives a pasteboard. In this case the superclass or class you are the delegate of has already written to the pasteboard. In your delegate or overridden method, you might get the pasteboard items, see what data has already been provided, and then call methods on the NSPasteboardItem instances to set additional or replacement data for particular types.

3. When reading the pasteboard and the destination of the data is not an object. For instance, if your application commonly used CGImages, you might get the array of pasteboard items, and iterate through it looking for image data, getting the data for an image type, and creating the CGImages.

An NSPasteboardItem instance can be associated with a single pasteboard. When you first create a pasteboard item, it can be written to any pasteboard. When you pass in a pasteboard item to -writeObjects:, that pasteboard item becomes bound to the pasteboard it was written to. When you retrieve pasteboard items using -pasteboardItems or -readObjectsForClasses:options:, the returned pasteboard items are associated with the messaged pasteboard. Passing a pasteboard item that is already associated with a pasteboard into -writeObjects: causes an exception to be raised. Note that as long as a pasteboard item is associated with a pasteboard, all methods to set data take affect on the pasteboard.

Pasteboard items are intended to be transient objects, used during a single pasteboard interaction, not held onto and used repeatedly. A pasteboard item is only valid until the owner of the pasteboard changes. If a pasteboard item is stale because the pasteboard owner has changed, it will return nil or NO values from its methods.

NSPasteboardItem instances are created only when necessary. In the code example above, an attributed string is both written to and read from the pasteboard without an NSPasteboardItem instance being created. The class-centric approach should be used when possible, and the type-centric approach of NSPasteboardItem should be used when necessary.

See NSPasteboardItem.h for more information.

Additional NSPasteboard Notes

- By historical convention, absolute URLs have always been expected on the pasteboard. The NSURL implementation of the NSPasteboardWriting protocol formalizes this by not providing types or data to be written to the pasteboard unless the URL is an absolute URL.

- In SnowLeopard, filtering pasteboards only operate and are valid on the first item on the pasteboard.

- In SnowLeopard, none of the Cocoa classes that implement NSPasteboardWriting and NSPasteboardReading write or read the types used on the NSFontPboard and NSRulerPboard pasteboards.

UTIs on the Pasteboard

All NSPasteboard APIs introduced in Snow Leopard accept UTIs only and do not accept NSPboardType strings. The new APIs will accept an informally defined UTI string. This is any string which follows the naming convention for UTIs, but is not formally declared in an Info.plist. These informal UTI strings are useful for declaring pasteboard types which are for internal use within an application, since it may not be desirable to formally declare those types. If you do formally declare UTIs for use on the pasteboard, the UTIs should conform to the public.data type.

Migrating from pboard types to UTIs

Use of pboard types should be replaced with the use of UTIs. Pboard types will be deprecated in a future release. For all pboard types declared in AppKit, see NSPasteboard.h for the migration path for each pboard type. In almost all cases, migration consists of substituting the pboard type constant for an analogous UTI-based constant. The main exception is the NSFilenamesPboard type, where the migration path is to use the new multiple pasteboard item API to read and write file URLs on the pasteboard. For custom pboard types, the migration path is either to create a constant that follows UTI naming conventions and use it as an undeclared informal UTI, or to formally declare that constant. See below for additional information if other applications rely on your existing custom pboard types.

Moving public pboard types to UTIs

For applications that currently define a public pboard type that other applications rely on, the following is necessary to ensure that existing applications will continue to see your pboard type even after your application has moved from pboard types to UTIs:

1. Formally declare the new pasteboard type UTI as an exported type declaration.
2. Ensure the declared UTI conforms to the public.data type.
3. Add a tag specification for the 'com.apple.nspboard-type' tag to the type declaration. Note that the value you provide should be the literal string value of the pboard type, not the name of the constant. An example tag specification to associate a UTI with a pboard type:
    <key>UTTypeTagSpecification</key>
    <dict>
        <key>com.apple.nspboard-type</key>
        <string>MyCustomPboardType</string>
    </dict>

Deprecating PICTPboardType

NSPICTPboardType is deprecated in SnowLeopard. The PICT format was formally deprecated in Tiger along with QuickDraw. Applications should not be explicitly providing or looking for PICT data on the pasteboard.

To aid in this deprecation, if PICT is the only image type on the pasteboard, as is sometimes the case when copying images from 32-bit Carbon applications, a translated image type will be automatically reported and provided by NSPasteboard. The translated type is added to the types array ahead of PICT so that the deprecated PICT format is not the preferred format.

Although NSPICTPboardType and its UTI equivalent kUTTypePICT will appear in a pasteboard's type array retrieved from the existing NSPasteboard API, it may cease to be reported in future releases.

Reading image types from the pasteboard

Historically, TIFF has been the single commonly used pasteboard type for image data. With the introduction of NSPasteboard support of UTIs, it is possible to put image data of any format onto the pasteboard.

This means that when retrieving image data from the pasteboard that you should be certain to use the array of types returned by [NSImage imageTypes], not just the NSTIFFPboardType.

For backwards compatibility for applications linked before SnowLeopard, if we find image data UTIs on the pasteboard, but no TIFF data present, NSPasteboard will automatically promise and translate to TIFF. For applications linked on SnowLeopard, you should update your pasteboard code to look for and accept any image type, rather than just TIFF. Using the new writeObjects: / readObjectsForClasses:options: methods with NSImage will handle this automatically.

Promised data and Sudden Termination

When an application promises to provide data lazily to a pasteboard, sudden termination is disabled to prevent the application from terminating without having the opportunity to provide the promised data at application quit. Sudden termination is re-enabled when either all of promised data is provided or the ownership of the pasteboard changes.

Pasteboard types that predate Mac OS X

NSPasteboard no longer promises deprecated pasteboard types that predate Mac OS X, and have never been public in Mac OS X: "NeXT plain ascii pasteboard type", "NXColor pasteboard type", "NeXT filename pasteboard type", "NXTypedFilenamePboardType".

Using pbcopy and pbpaste with different encodings

The command line tools pbcopy and pbpaste now use standard locale environment variables to determine the character encoding used for reading and writing string data. See man pages for details.

Accessibility

NSImage Accessibility Description

-accessibilityDescription Property

In SnowLeopard, NSImage adds an -accessibilityDescription property. The string should be a localized accessibility description. If an NSImage has an accessibility description, Cocoa interface elements that display images will report the description to accessibility. This functionality is currently supported by NSImageCell, NSButtonCell, and NSSegmentedCell.

This makes it easier for a single user interface element to report different AXDescriptions depending on which image is shown. For instance, in a chat application, different images might be used to report status such as "online", "offline", "away". Now, you can set the accessibility description for each image. As you change which image is displayed, the image cell will report the description of the currently set image.

Default accessibility description strings for named system images

In Leopard, constants were introduced in NSImage.h for named system images. In SnowLeopard, these images now have a default accessibility description, providing an additional amount of default accessibility for commonly used images.

AccessibilityImageDescriptions.strings file

In applications, most images used in the interface are named resources loaded using NSImage's +imageNamed: method. If you provide a .strings file called 'AccessibilityImageDescriptions.strings', it will be checked for an accessibility description. The keys in the .strings file are the names of the images as provided to +imageNamed:. The values are the accessibility description.

Image Description Search Path

When an image is asked for its accessibility description, if an accessibility description string has been set programmatically using -setAccessibilityDescription: that string will be returned. If no string has been set programatically, the string value for that image in the application's AccessibilityImageDescriptions.strings file, if present, will be returned. Finally, if AppKit provides a default value for the image, that value will be returned.

Additional Accessibility Description Notes

Note that calling +imageNamed: repeatedly with the same name returns the same instance of NSImage. If you change the accessibility description on a named image, the description will change everywhere that named image instance is being used. If you need different descriptions for the same image, when used in different places in the UI, use an accessibility overridden attribute to provide the description on the UI element itself.

Note also that setting an overridden AXDescription attribute on an element will override the value provided by the element itself, including the accessibility description of the displayed image.


NSMenu and NSMenuItem Accessibility


In SnowLeopard, both NSMenu and NSMenuItem support the NSAccessibilityAdditions method accessibilitySetOverrideValue:forAttribute:. The support is only for values of type NSString, and is intended to allow adding an AXDescription to a menu or menu item that contains an image instead of text.

NSLevelIndicator role changes

In SnowLeopard, NSLevelIndicator reports new accessibility roles and subroles.
The discrete and continuous capacity styles report the new NSAccessibilityLevelIndicatorRole as their role. They also report the new NSAccessibilityWarningValueAttribute and NSAccessibilityCriticalValueAttribute attributes.
The relevancy indicator style now reports the NSAccessibilityRelevanceIndicatorRole as its role.
The rating indicator style continues to report its role as a slider, but with the NSAccessibilityRatingIndicatorSubrole.

Changes in setting AXValue of a text view or text field

Setting the AXValue of a text view or focused text field now behaves like the user entering text, including triggering bindings. For text fields it is recommended to set the AXFocus of the text field to true before setting its AXValue. In the future, text fields may require focus before having their value set through accessibility, to more closely mirror direct user interaction.

New outline view accessibility notifications

NSOutlineView supports the new NSAccessibilityRowExpandedNotification and NSAccessibilityRowCollapsedNotification notifications. Assistive applications register to receive the notification from the outline element. The element reported with the notification is the outline row that expanded or collapsed.

Optional array attribute methods

Accessibility clients such as VoiceOver have long had the ability to get a count or a subarray for any accessibility attribute whose value is an array. In addition, various internal accessibility operations rely on getting the index of an element's child. The default framework implementation for each of these operations calls -accessibilityAttributeValue: to retrieve the entire array of values, and then performs the appropriate operation. If these new methods are implemented, they will be used instead. For accessibility objects with many children, or many values in any array attribute, the results to these methods can sometimes be calculated without generating the entire array of values, which can improve performance.
/* Given an accessibility child of an object, return the index of that child in the parent.
*/
- (NSUInteger)accessibilityIndexOfChild:(id)child;
/* Return the count of an accessibility array attribute.
*/
- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute;
/* Return a subarray of values of an accessibility array attribute.  Note this method does not take a range.
The max count is the maximum desired number of items requested by an accessibility client.
This number may be beyond the bounds of your array.
*/
- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount;

Miscellaneous Accessibility Changes

- NSTableView and subclasses now correctly return the selected columns to accessibility, not selected column indexes.

- For a table view with hidden columns, AXColumn now reports the correct AXIndex.

- NSProgressIndicator no longer erroneously reports an AXChildren attribute to accessibility.

- NSScroller no longer reports its AXChildren attribute twice.

- Sort buttons now correctly report their role as AXButton with AXSortButton as a subrole.

- The NSAccessibilitySortButtonRole constant has been deprecated and the NSAccessibilitySortButtonSubrole constant added.

- NSAccessibilityUnknownOrientationValue constant added to match AX API.

- Circular sliders now report their AXOrientation as AXUnknownOrientation.

- AXBusyIndicators and AXProgressIndicators now have an AXOrientation attribute. The bar style reports AXHorizontalOrientation, the spinning style reports AXUnknownOrientation.

- NSAccessibilityPlaceHolderValueAttribute added. Interface elements with placeholder strings report the value of the placeholder using this attribute.

Miscellaneous Accessibility Changes

- Setting AXFrontmost to true on a Cocoa app now brings all windows to the front as happens in Carbon apps.



Text Checking

Snow Leopard includes a new facility known as text checking, which provides a unified interface for a variety of types of checking, including spell checking, grammar checking, URL detection, smart quotes, date and address detection via Data Detectors, and others. Among other things, this facility allows for the automatic identification of the languages used in a piece of text, so that spellchecking can proceed without the user having to label text as to language.

The basic interface for this facility is available in NSSpellChecker, in the form of the methods
- (NSArray *)checkString:(NSString *)stringToCheck
range:(NSRange)range
types:(NSTextCheckingTypes)checkingTypes
options:(NSDictionary *)options
inSpellDocumentWithTag:(NSInteger)tag
orthography:(NSOrthography **)orthography
wordCount:(NSInteger *)wordCount;
- (NSInteger)requestCheckingOfString:(NSString *)stringToCheck
range:(NSRange)range
types:(NSTextCheckingTypes)checkingTypes
options:(NSDictionary *)options
inSpellDocumentWithTag:(NSInteger)tag
completionHandler:(void (^)(NSInteger seqNumber, NSArray *results, NSOrthography *orthography, NSInteger wordCount))block;
which request unified text checking for the given range of the given string. The checkingTypes should be a bitmask of checking types from the Foundation header NSTextCheckingResult.h, describing which types of checking are desired. The options dictionary allows clients to pass in options for certain types of checking. The orthography and wordCount arguments return these two attributes of the range as a whole, while the return value is an array of NSTextCheckingResult objects describing particular items found during checking and their individual ranges. The first method here invokes checking synchronously and returns results immediately; the second requests checking in the background, and returns results at some later point to a completion handler block. The completion handler block will be invoked in an arbitrary context; if completion work needs to be performed on the main thread, the client will need to arrange for that via a performSelector method or the equivalent. The return value of the second method is a monotonically increasing sequence number, which can be used to keep track of requests in flight, and which will be supplied as an argument to the completion block.

NSTextCheckingResult and NSOrthography are two new classes defined in Foundation for use with text checking. An instance of NSTextCheckingResult represents something that has been found during checking--a misspelled word, a sentence with grammatical issues, a detected URL, a straight quote to be replaced with curly quotes, and so forth. NSOrthography is a class used to describe the linguistic content of a piece of text, especially for the purposes of spelling and grammar checking. It describes (a) which scripts the text contains, (b) a dominant language and possibly other languages for each of these scripts, and (c) a dominant script and language for the text as a whole. The results of automatic language identification are described using NSOrthography. See the Foundation release notes for more details on these two classes.

NSSpellChecker has the additional methods
- (NSArray *)userPreferredLanguages;
- (BOOL)automaticallyIdentifiesLanguages;
- (void)setAutomaticallyIdentifiesLanguages:(BOOL)flag;
- (NSArray *)guessesForWordRange:(NSRange)range inString:(NSString *)string language:(NSString *)language inSpellDocumentWithTag:(NSInteger)tag;
- (NSArray *)userQuotesArrayForLanguage:(NSString *)language;
- (NSDictionary *)userReplacementsDictionary;
to facilitate the use of text checking and automatic language identification. Entries in the availableLanguages list are all available spellchecking languages in user preference order, as described in the spellchecker's info dictionary, usually language abbreviations such as en_US. The userPreferredLanguages will be a subset of the availableLanguages, as selected by the user for use with spellchecking, in preference order. If automaticallyIdentifiesLanguages is YES, then text checking will automatically use these as appropriate; otherwise, it will use the language set by setLanguage:. The older checkSpellingOfString:... and checkGrammarOfString:... methods will use the language set by setLanguage:, if they are called with a nil language argument.

There are also the following methods to support the user interface associated with text checking:
- (NSPanel *)substitutionsPanel;
- (NSViewController *)substitutionsPanelAccessoryViewController;
- (void)setSubstitutionsPanelAccessoryViewController:(NSViewController *)accessoryController;
- (void)updatePanels;
- (NSMenu *)menuForResult:(NSTextCheckingResult *)result
string:(NSString *)string
options:(NSDictionary *)options
atLocation:(NSPoint)location
inView:(NSView *)view;
along with the constants
NSString *NSTextCheckingOrthographyKey;
NSString *NSTextCheckingQuotesKey;
NSString *NSTextCheckingReplacementsKey;
NSString *NSTextCheckingReferenceDateKey;
NSString *NSTextCheckingReferenceTimeZoneKey;
NSString *NSTextCheckingDocumentURLKey;
NSString *NSTextCheckingDocumentTitleKey;
NSString *NSTextCheckingDocumentAuthorKey;
to be used as keys in the options dictionaries for the checkString:..., requestCheckingOfString:..., and menuForResult:... methods described above. All of these keys are optional.

The substitutions panel provides an interface for various auxiliary functionality associated with text checking. It may have an accessory view if an accessory view controller is set for it. The updatePanels method is one that clients should call when any of their state changes with respect to spelling, grammar, or text checking, that might affect the spelling or substitutions panel; for example, if a client changes whether one of the text checking features is turned on, it should call this method on the shared spellchecker. The menuForResult:string:options:atLocation:inView: method returns a menu containing contextual menu items suitable for certain kinds of detected results (notably date/time/address results). The string argument is optional; if supplied, it should be the string contents of the range detected by the result. The location in the view will be used if an action eventually calls for putting up a temporary window.

NSTextCheckingOrthographyKey may be used to supply an NSOrthography indicating an orthography to be used as a starting point for orthography checking, or as the orthography if orthography checking is not enabled. The value for NSTextCheckingQuotesKey should be an NSArray containing four strings to be used with NSTextCheckingTypeQuotes (opening double quote, closing double quote, opening single quote, and closing single quote in that order). The default value used if this key is not present is taken from user preferences, and may be determined via the -userQuotesArrayForLanguage: method. The value for NSTextCheckingReplacementsKey should be an NSDictionary containing replacements to be used with NSTextCheckingTypeReplacement; if this is not specified, values will be taken from user's preferences, and these default values may be obtained via the -userReplacementsDictionary method.

The NSTextCheckingReferenceDateKey, NSTextCheckingReferenceTimeZoneKey, NSTextCheckingDocumentURLKey, NSTextCheckingDocumentTitleKey, and NSTextCheckingDocumentAuthorKey keys may be used to specify metadata associated with the current document for use with NSTextCheckingTypeDate, NSTextCheckingTypeAddress, and NSTextCheckingTypeLink. NSTextCheckingReferenceDateKey can be used to supply an NSDate to be used as a referent for relative dates, and NSTextCheckingReferenceTimeZoneKey can be used to supply an NSTimeZone to be used as a referent for dates without time zones; the current date and time zone will be used if these are not specified. These keys may be used if specific information is available about the document--for example, the date and time zone of its composition, for something like an email document. NSTextCheckingDocumentURLKey can be used to specify an NSURL associated with the document, NSTextCheckingDocumentTitleKey an NSString representing a title associated with the document, and NSTextCheckingDocumentAuthorKey an NSString representing a name of an author associated with the document. These are especially useful with the -menuForResult:string:options:atLocation:inView: method, which supplies menu items appropriate for a contextual menu to be displayed for a detected item at the given location in a view.


Text Checking in NSTextView

In addition, there are methods on NSTextView to support its use of text checking.
- (void)setAutomaticDataDetectionEnabled:(BOOL)flag;
- (BOOL)isAutomaticDataDetectionEnabled;
- (void)toggleAutomaticDataDetection:(id)sender;
- (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag;
- (BOOL)isAutomaticDashSubstitutionEnabled;
- (void)toggleAutomaticDashSubstitution:(id)sender;
- (void)setAutomaticTextReplacementEnabled:(BOOL)flag;
- (BOOL)isAutomaticTextReplacementEnabled;
- (void)toggleAutomaticTextReplacement:(id)sender;
- (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag;
- (BOOL)isAutomaticSpellingCorrectionEnabled;
- (void)toggleAutomaticSpellingCorrection:(id)sender;
- (NSTextCheckingTypes)enabledTextCheckingTypes;
- (void)setEnabledTextCheckingTypes:(NSTextCheckingTypes)checkingTypes;
Turning on automatic data detection enables detection of both date and address/phone number items; turning on automatic dash substitution enables automatic conversion of sequences of ASCII '-' to typographic dashes; turning on automatic text replacement enables automatic substitution of a variety of static text items based on user preferences; and turning on automatic spelling correction enables autocorrection of certain misspellings. In addition, text checking is now used for continuous spelling and grammar checking, automatic link detection, and automatic quote substitution. The -enabledTextCheckingTypes and -setEnabledTextCheckingTypes: methods allow the state of all of these forms of checking to be retrieved or set at once.
- (void)orderFrontSubstitutionsPanel:(id)sender;
- (void)checkTextInSelection:(id)sender;
- (void)checkTextInDocument:(id)sender;
The -orderFrontSubstitutionsPanel: method brings forward a panel provided by NSSpellChecker that allows control over the various enabled substitution types. For example, it provides the user the ability to use the -checkTextInSelection: and -checkTextInDocument: action methods. Ordinarily text checking occurs in the background, and results that replace text are applied only to text that has been typed by the user, but these two methods cause the currently enabled text checking types to be applied immediately to the selection or to the document, respectively, and results that replace text are applied regardless of the origin of the text.
- (void)checkTextInRange:(NSRange)range types:(NSTextCheckingTypes)checkingTypes options:(NSDictionary *)opts;
- (void)handleTextCheckingResults:(NSArray *)results
forRange:(NSRange)range
types:(NSTextCheckingTypes)checkingTypes
options:(NSDictionary *)options
orthography:(NSOrthography *)orthography
wordCount:(NSInteger)wordCount;
The checkTextInRange:types:options: and handleTextCheckingResults:forRange:orthography:wordCount: methods will be called by NSTextView as appropriate when text checking is started and when results are received, respectively. They are not intended to be called by clients, but can be overridden to observe or modify text checking behavior. The same sort of observation or modification will also be available to the delegate, using the two new NSTextView delegate methods
- (NSDictionary *)textView:(NSTextView *)view
willCheckTextInRange:(NSRange)range
options:(NSDictionary *)options
types:(NSTextCheckingTypes *)checkingTypes;
- (NSArray *)textView:(NSTextView *)view
didCheckTextInRange:(NSRange)range
types:(NSTextCheckingTypes)checkingTypes
options:(NSDictionary *)options
results:(NSArray *)results
orthography:(NSOrthography *)orthography
wordCount:(NSInteger)wordCount;
that will be called by checkTextInRange:types:options: and handleTextCheckingResults:forRange:orthography:wordCount: respectively.


Bidirectional Text

Snow Leopard includes a number of new facilities to support the entry and editing of bidirectional text, such as Hebrew or Arabic. First, there is a new attributed string attribute, NSWritingDirectionAttributeName, intended to provide a higher-level alternative to the inclusion of explicit bidirectional control characters such as LRE, RLE, LRO, RLO, and PDF in text. This gives an attributed string equivalent to what in HTML would be expressed by such constructs as <span dir="ltr"></span>, <span dir="rtl"></span>, <bdo dir="ltr"></span>, and <bdo dir="rtl"></span>..

The value of this attribute should be an array of NSNumbers, each of which should have a value of 0, 1, 2, or 3, considered as consisting of either NSWritingDirectionLeftToRight (= 0) or NSWritingDirectionRightToLeft (= 1) plus one of the new constants NSTextWritingDirectionEmbedding (= 0) or NSTextWritingDirectionOverride (= 2). This array represents a sequence of nested bidirectional embeddings or overrides, in order from outermost to innermost, with 0 (NSWritingDirectionLeftToRight | NSTextWritingDirectionEmbedding) corresponding to a LRE/PDF pair in plain text or <span dir="ltr"></span> in HTML, 1 (NSWritingDirectionRightToLeft | NSTextWritingDirectionEmbedding) corresponding to a RLE/PDF pair in plain text or a <span dir="rtl"></span>, 2 (NSWritingDirectionLeftToRight | NSTextWritingDirectionOverride) corresponding to a LRO/PDF pair in plain text or <bdo dir="ltr"></span>, and 3 (NSWritingDirectionRightToLeft | NSTextWritingDirectionOverride) corresponding to a RLO/PDF pair in plain text or <bdo dir="rtl"></span> in HTML.

In general, there should be very few situations that require the use of more than one level of bidirectional embeddings or overrides, but where necessary NSWritingDirectionAttributeName will support nesting, up to the maximum nesting depth provided for by the Unicode bidirectional algorithm. To determine the value for this attribute for a given character, note all of the embeddings or overrides that should apply to that character, in the order of their nesting from outermost to innermost, and for each one add an NSNumber to the array whose value specifies the type of the embedding or override as described above.

The intent is that the new attribute should be used in preference to inserting explicit bidirectional control characters in text, whenever text is being edited, even if rich text is not being used; that is, these attributes are considered to be useful even with plain text, because of the difficulties of editing text containing explicit control characters. When the contents of a text storage are actually converted into a plain-text file format for storage or interchange, then the attribute would need to be replaced by explicit bidirectional control characters, and likewise when loading plain text into a text storage for editing, explicit bidirectional control characters would need to be replaced by suitable values of the attribute. NSTextView will do this automatically on copy and paste, and NSAttributedString on conversion to and from external formats. In some cases, NSTextView may automatically apply this attribute to text that is typed in bidirectional contexts, where this is necessary to resolve ambiguities in directionality. Users of purely left-to-right text should never encounter this attribute.

In addition, there are new action methods defined on NSResponder and implemented in NSTextView for setting the values of both NSWritingDirectionAttributeName and the existing paragraph-level base writing direction defined on NSParagraphStyle.
- (void)makeBaseWritingDirectionNatural:(id)sender;
- (void)makeBaseWritingDirectionLeftToRight:(id)sender;
- (void)makeBaseWritingDirectionRightToLeft:(id)sender;
- (void)makeTextWritingDirectionNatural:(id)sender;
- (void)makeTextWritingDirectionLeftToRight:(id)sender;
- (void)makeTextWritingDirectionRightToLeft:(id)sender;
The first three of these set the paragraph-level base writing direction on NSParagraphStyle to NSWritingDirectionNatural, NSWritingDirectionLeftToRight, or NSWritingDirectionRightToLeft respectively. The latter three of these set the new character-level NSWritingDirectionAttributeName. Specifically, makeTextWritingDirectionNatural: removes NSWritingDirectionAttributeName, and makeTextWritingDirectionLeftToRight: and makeTextWritingDirectionRightToLeft: set it to a single left-to-right or right-to-left embedding respectively. (No action methods are provided to set bidirectional overrides, or to nest embeddings or overrides.)

These action methods are intended to be used both as the targets of menu items and for key bindings. The intent is that the base writing direction methods should be the target of three menu items, and the text writing directions should be the target of three similar menu items, within the Writing Direction menu. This menu will be provided in the context menu when appropriate, and applications that are concerned with editing text should also supply it in their main menu. TextEdit has been updated to follow these guidelines. Default key bindings are also provided for these methods, but they are enabled only for users of Hebrew or Arabic, or those who have otherwise enabled a suitable preference in the Text tab of the Language & Text preferences.

The previous public action method for writing direction, -toggleBaseWritingDirection:, is no longer recommended for use, and will be formally deprecated in the future.


Additional Key Binding Methods

There are four new additional key binding methods, defined on NSResponder and implemented in NSTextView,
- (void)moveToLeftEndOfLine:(id)sender;
- (void)moveToRightEndOfLine:(id)sender;
- (void)moveToLeftEndOfLineAndModifySelection:(id)sender;
- (void)moveToRightEndOfLineAndModifySelection:(id)sender;
intended for binding to cmd- and cmd-shift-left/right arrow, in order to disambiguate the former similar methods expressed in terms of the beginning and end of the line in cases of bidirectional text.

There are also a number of other key binding methods, also defined on NSResponder and implemented in NSTextView, that have been present and bound to keys for some time, but not explicitly public:
- (void)moveToBeginningOfLineAndModifySelection:(id)sender;
- (void)moveToEndOfLineAndModifySelection:(id)sender;
- (void)moveToBeginningOfParagraphAndModifySelection:(id)sender;
- (void)moveToEndOfParagraphAndModifySelection:(id)sender;
- (void)moveToEndOfDocumentAndModifySelection:(id)sender;
- (void)moveToBeginningOfDocumentAndModifySelection:(id)sender;
- (void)pageDownAndModifySelection:(id)sender;
- (void)pageUpAndModifySelection:(id)sender;
- (void)moveParagraphForwardAndModifySelection:(id)sender;
- (void)moveParagraphBackwardAndModifySelection:(id)sender;
- (void)scrollToBeginningOfDocument:(id)sender;
- (void)scrollToEndOfDocument:(id)sender;
- (void)insertSingleQuoteIgnoringSubstitution:(id)sender;
- (void)insertDoubleQuoteIgnoringSubstitution:(id)sender;
These methods have now been made public.


NSLayoutManager Methods

NSLayoutManager now has a method
- (NSUInteger)characterIndexForPoint:(NSPoint)point
inTextContainer:(NSTextContainer *)container
fractionOfDistanceBetweenInsertionPoints:(CGFloat *)partialFraction;
analogous to glyphIndexForPoint:inTextContainer:fractionOfDistanceThroughGlyph:, but expressed in character index terms. The method returns the index of the character falling under the given point, expressed in the given container's coordinate system; if no character is under the point, the nearest character is returned, where nearest is defined according to the requirements of selection by mouse. However, this is not simply equivalent to taking the result of the corresponding glyph index method and converting it to a character index, because in some cases a single glyph represents more than one selectable character, for example an fi ligature glyph. In that case, there will be an insertion point within the glyph, and this method will return one character or the other, depending on whether the specified point lies to the left or the right of that insertion point. In general, this method will return only character indexes for which there is an insertion point. The partial fraction is a fraction of the distance from the insertion point logically before the given character to the next one, which may be either to the right or to the left depending on directionality.

In addition, there is a new method
- (void)fillBackgroundRectArray:(NSRectArray)rectArray count:(NSUInteger)rectCount forCharacterRange:(NSRange)charRange color:(NSColor *)color;
used as a primitive for rect drawing by -drawBackgroundForGlyphRange:atPoint: for actually filling rects with a particular background color, whether due to a background color attribute, a selected or marked range highlight, a block decoration, or any other rect fill needed by that method. As with -showPackedGlyphs:..., the character range and color are merely for informational purposes; the color will already be set in the graphics state. If for any reason you modify it, you must restore it before returning from this method. You should never call this method, but you might override it. The default implementation will simply fill the specified rect array. The graphics operation used for this fill is controlled by a link check; for compatibility reasons, it will be NSCompositeCopy for applications linked prior to Snow Leopard, and NSCompositeSourceOver for applications linked on Snow Leopard or later. Applications can control the operation used, or modify the drawing, by overriding this method in an NSLayoutManager subclass.


NSTextView Undo Coalescing

NSTextView now has a method
- (BOOL)isCoalescingUndo;
to go along with the previous -breakUndoCoalescing. The new method permits clients to determine when undo coalescing is in progress.


NSTextList Starting Item Number

NSTextList now has two methods
- (void)setStartingItemNumber:(NSInteger)itemNum;
- (NSInteger)startingItemNumber;
for supplying a starting item number for a given list. The default value is 1. This value will be used only for ordered lists, and ignored in other cases.


NSAttributedString Constants

The existing NSDocumentTypeDocumentAttribute and NSDocumentTypeDocumentOption express document types in terms of a set of constants particular to NSAttributedString. In Snow Leopard, there are new constants, NSFileTypeDocumentAttribute and NSFileTypeDocumentOption, that express the same information in terms of system-wide UTIs. Where these are used for output, in expressing the type of a document that has been read, both file type and document type will be provided; as input, however, when indicating the type to be written or the type to be forced on loading, the two are mutually exclusive.

NSAttributed string also has two new metadata attributes, NSManagerDocumentAttribute and NSCategoryDocumentAttribute.



NSTableView / NSOutlineView - General updates

NSTableView and NSOutlineView now support autosizing of table columns when you double click on the resize divider that is directly to the right of a given column. By default, NSTableView iterates every row in the table, accesses a cell via preparedCellAtRow:column:, and requests the -cellSize to find the appropriate largest width to use. For performance, it is recommended that the programmer implement a new delegate method (-tableView:sizeToFitWidthOfColumn: or -outlineView:sizeToFitWidthOfColumn:) to quickly determine the size.

The indentationPerLevel property on NSOutlineView now works better for applications linked on SnowLeopard or higher. Previously, it would indent every level by indentationPerLevel -- including the items at level 0. This has the undesired side effect of indenting too much when the value was large, and not enough to show a disclosure triangle when the value was small. In SnowLeopard-linked applications, if the indentationPerLevel is greater than 0, level-0 will have a specific width that contains enough room for the disclosure triangle based on the current selectionHighlightStyle. All other levels will properly be indented by 'indentationPerLevel'.

NSTableView now has a method to correctly invalidate cells for redrawing: -reloadDataForRowIndexes:columnIndexes:. The method will effeciently redisplay only the row/columns that are actually visible.

NSTableView now supports restricting the reordering of particular columns with a new delegate method: -tableView:shouldReorderColumn:toColumn:.

For applications linked on SnowLeopard and later, -cellSizeForBounds: (and subsequently -cellSize) will return the correct width for NSTableHeaderCells. Previously, it would not return a size large enough for the sort indicator, if the associated NSTableColumn had a sortDescriptorPrototype set.

Mac OS 10.5 Leopard introduced sub-cell focusing. SnowLeopard now allows developer control over the sub-cell focusing with several methods. -shouldFocusCell:atColumn:row: can be overridden to control whether or not a cell can be focused. -focusedColumn is the particular column that has focus. Note that the focus is only shown for the -selectedRow (if available). Finally, focused cells allow clicking via the space bar, and a method is provided for developers to call or override: -performClickOnCellAtColumn:row:.

Leopard introduced custom menu highlighting when a menu is assigned to an NSTableView instance. Subclassers that override -menuForEvent: and return a new menu instance would cause NSTableView to incorrectly highlight. Previously, it would watch NSMenuDidEndTrackingNotification for just one menu, and now it watches for all menus to correct this problem. For Leopard compatibility, it is recommended that you don't create a new menu instance, and instead use the current one as shown in the DragNDropOutlineView demo application.

On Leopard, NSOutlineView's expandItem: method started allowing 'nil' as an option to expand all items from the root. Before Leopard, this would cause an assertion. If you are deploying your application on a release before Leopard, you should not use expandItem:nil, as it will assert.

NSTableViewSelectionHighlightStyle updates: There is now a highlight style that draws no highlighting at all, but still allows rows to be selected: NSTableViewSelectionHighlightStyleNone. The NSTableViewSelectionHighlightStyleSourceList has also been updated to no longer de-select all rows when clicking on empty space in the table view.

For applications linked on SnowLeopard or later, the NSTableHeaderCell will always draw the sort indicator on the right side, even if the alignment for the cell is set to the right side. Previously, it would draw it on the left when the alignment was set to the right.

For applications linked on SnowLeopard or later, -rowAtPoint: will correctly return -1 if the point passed is outside of the horizontal bounds of the view. Previously, only the vertical bounds was checked.

NSTableView now has a new draggingDestinationFeedbackStyle property. One of the NSTableViewDraggingDestinationFeedbackStyle values is acceptable for it. The NSTableViewDraggingDestinationFeedbackStyleSourceList option is appropriate for source lists, or things that appear as source lists but don't use the source list highlighting style (such as AddressBook). NSTableViewDraggingDestinationFeedbackStyleRegular is now the standard default look, which differs from Leopard by drawing a solid round-rect blue background around drop target rows. For compatibility, this style will be the default for applications that link on SnowLeopard or later. Other applications will use NSTableViewDraggingDestinationFeedbackStyleSourceList, which was the default look for Leopard. Applications that target Leopard can still get the SnowLeopard look by first doing a [tableView respondsToSelector:@selector(setDraggingDestinationFeedbackStyle:)] check before calling the method. There is also a NSTableViewDraggingDestinationFeedbackStyleNone option to show "no drop feedback". This style is appropriate for subclasses that which to control the drawing themselves and disable the default NSTableView drawing.

Prior to SnowLeopard, NSTableView and NSOutlineView would always attempt to enable live-resize content caching, even if a subclass overrode preservesContentDuringLiveResize and returned NO. On SnowLeopard, it now works to return NO from preservesContentDuringLiveResize to avoid live-resize image caching. Prior to SnowLeopard, one could disable it by overriding -drawRect: and doing nothing but calling [super drawRect:rect]. It is recommended to do both if your application is targeting prior releases.

For applications that link on SnowLeopard and higher, NSOutlineView will correctly set the "isItemExpanded:item" bit for item to YES only AFTER sending the outlineViewItemWillExpand: notification. Similarly, NSOutlineView will correctly set the "isItemExpanded:item" bit for item to NO only AFTER sending the outlineViewItemWillCollapse: notification. For applications that link against an OS prior to SnowLeopard, the bit will be incorrectly set before sending the notification.

It has always been incorrect to return a negative row count from NSTableView and NSOutlineView delegate methods. For applications that link on SnowLeopard and higher, returning a negative count will now assert to assist the developer in debugging such problems.

For applications linked on SnowLeopard and later, the disclosure triangle (outline cell) in an NSOutlineView will correctly be white when the row is selected. It is possible to override this behavior by implementing -willDisplayOutlineCell: to explicitly set the backgroundStyle of the outline cell to NSBackgroundStyleLight. IE:
- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell
forTableColumn:(NSTableColumn *)tableColumn item:(id)item {
// This explicitly makes the disclosure triangles always be dark
[cell setBackgroundStyle:NSBackgroundStyleLight];
}
NSTableView and NSOutlineView will automatically add attributes to plain text for the source list (NSTableViewSelectionHighlightStyleSourceList) highlight style (selectionHighlightStyle). On Leopard, these attributes were added before the backgroundStyle was set on the cell, sometimes producing incorrectly colored cells on rows that immediately followed a group-row cell. This has been fixed on SnowLeopard, and can be worked around on Leopard with the following delegate method:
- (NSCell *)outlineView:(NSOutlineView *)outlineView
dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
NSCell *result = tableColumn != nil ? [tableColumn dataCell] : nil;
if (result != nil) {
[result setBackgroundStyle:NSBackgroundStyleLight];
}
return result;
}
On Leopard when you right click on a row in NSTableView that is a "group row" (ie: the column is -1, indicating it spans across all the rows), it will incorrectly call preparedCellAtColumn:row: with a column value that is not -1. This has been fixed on SnowLeopard to properly call it with -1, but applications that need to target Leoaprd will need to take this change into consideration and be prepared for the incorrect call.

NSSavePanel / NSOpenPanel - NSURL and Blocks

The NSSavePanel and NSOpenPanel now have NSURL properties, delegate methods. In addition, new block-based methods are available for displaying the panels. These versions should be used instead of the non-NSURL versions, which will be deprecated in a future release. The old versions and their replacements:
    NSSavePanel:
        filename -> URL
        directory -> directoryURL
        requiredFileType -> Always the first item in allowedFileTypes

        panel:isValidFilename: -> panel:validateURL:error:
        panel:directoryDidChange: -> panel:didChangeToDirectoryURL
        panel:compareFilename:width:caseSensitive: -> Deprecated and should not be used for performance reasons
        panel:shouldShowFilename: -> panel:shouldEnableURL:

        beginSheetForDirectory:file:modalForWindow:modalDelegate:didEndSelector:contextInfo: -> beginSheetModalForWindow:completionHandler:
        runModalForDirectory:file: -> runModal

    NSOpenPanel:
        filenames -> URLS
        beginSheetForDirectory:file:types:modalForWindow:modalDelegate:didEndSelector:contextInfo: -> beginSheetModalForWindow:completionHandler:
        beginForDirectory:file:types::modelessDelegate:didEndSelector:contextInfo: -> beginSheetModalForWindow:completionHandler:
        runModalForDirectory:file:types: -> runModal
        runModalForTypes: -> runModal

You'll notice that there are now three acceptable ways to show the open or save panel:
  - (NSInteger)runModal;
- (void)beginSheetModalForWindow:(NSWindow *)window completionHandler:(void (^)(NSInteger result))handler;
- (void)beginWithCompletionHandler:(void (^)(NSInteger result))handler;
The methods no longer take a directory or initial file type. Instead, the properties can be set prior invoking the above methods. Prior to SnowLeopard, there was no way to set the initial filename; this can now be done with:
  - (NSString *)nameFieldStringValue;
- (void)setNameFieldStringValue:(NSString *)value;
Calling setNameFieldStringValue: will cause the 'value' to be processed slightly to be an acceptable file name, possibly including hiding the extension based if -isExtensionHidden is set to YES.

You'll notice that the NSOpenPanel version has no way to show the enabled "fileTypes". Applications that link on SnowLeopard can now use allowedFileTypes/setAllowedFileTypes:, which was previously not used for the NSOpenPanel. Please refer to the header comments for more information.

Example use of the new block based methods:
    // Run a modeless open panel for pdf file types:
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
openPanel.allowedFileTypes = [NSArray arrayWithObject:@"com.adobe.pdf"];
[openPanel beginWithCompletionHandler:^(NSInteger result) {
if (result) {
NSLog(@"Picked: %@", openPanel.URLs);
}
}];
    // Run a sheet modal for 'window' that allows saving as a jpeg
NSSavePanel *savePanel = [NSSavePanel savePanel];
savePanel.allowedFileTypes = [NSArray arrayWithObject:@"jpg"];
savePanel.nameFieldStringValue = @"foo.jpg";
[savePanel beginSheetModalForWindow:window completionHandler:^(NSInteger result) {
if (result) {
NSLog(@"Save as: %@", savePanel.URL);
}
}];
Compatibility notes: If a delegate implements both the old and new delegate methods, the new delegate methods will be used on SnowLeopard, while the older delegate methods will be used on older versions of the OS.

NSSavePanel / NSOpenPanel - General Updates

The NSSavePanel and NSOpenPanel now have a keyboard shortcut to show hidden files: cmd-shift-period. Hidden files will be shown only while that instance is open.

Using only UTIs to enable application file types in the open panel was not working on Leopard (com.apple.application-bundle, com.apple.application-bundle, com.apple.application-file). The work around was to include the "app" extension in the enabled file types. On SnowLeopard, all the UTIs work correctly, but for applications targeting Leopard as the minimum operating system, the "app" extension should also be included.

The NSOpenPanel now supports Quick Look. Select any files and press the space bar to invoke Quick Look.

The list view mode for the open and save panels now allow multiple columns to be viewed. Right click on the column header to show or hide them.

The open and save panel now allows connecting to shared servers. The sidebar also now contains a SEARCH FOR section.

The save panel now respects the Finder option for "Show all file extensions". If it is set, then the "Hide Extension" checkbox will be hidden and the extension will always be shown by always returning YES from -isExtensionHidden.

For applications that link on SnowLeopard and higher: NSSavePanel now tracks the accessoryView's frame. The programmer can dynamically make changes to the frame and the panel will properly layout. In addition, animated changes can be done using the animator proxy, such as: [[accessoryView animator] setFrame:frame].

NSPathControl / NSPathCell

The NSPathCell would incorrectly call [openPanel setCanChooseDirectories:YES] when about to show the NSOpenPanel. On 10.6, it will correctly set the value to YES only if the allowedTypes contains 'public.folder' or is nil.

For SnowLeopard linked applications, NSPathCell will correctly return an appropriate height from - (NSSize)cellSizeForBounds:(NSRect)bounds for NSPathStyleStandard and NSPathStyleNavigationBar.

NSPathComponentCell (and subsequently NSPathCell and NSPathControl) no longer encodes the image property when the URL property is a file URL. Instead, it grabs the appropriate image from NSWorkspace. Previously, if the file didn't exist, the previously set images would show up. Now, the appropriate current images will be used from the file system, and if the file at URL no longer exists, it will show a generic image. You must re-save nibs on SnowLeopard for the new encoding to take effect.

NSPathCell will now properly highlight the NSPathComponentCell that is clicked upon when a context menu is shown. Correctly setting a menu can be done in one of two ways: set the -menu property for the NSPathCell (or the NSPathControl - it forwards the setMenu: to the cell). In the menu's delegate, one can access the clickedPathComponentCell and update the menu based on the cell that was clicked. In the menu's action, one can access the clickedPathComponentCell and process the action based on the item that was initially clicked. Alternatively, each NSPathComponentCell can have the -menu property set, and the appropriate menu will be returned from NSPathCell via -menuForEvent:inRect:ofView:. Subclasses of NSPathControl, NSPathCell, and NSPathComponentCell should not override -menuForEvent: / -menuForEvent:inRect:ofView: if they wish to opt into this behavior. Note that this behavior should only be used on applications that target SnowLeopard or higher, as it will not work properly on Leopard.

NSBrowser

NSBrowser now goes through the public methods -moveLeft: and -moveRight: when changing columns in response to a left/right keyboard arrow.

On SnowLeopard linked applications, -scrollColumnToVisible: will now always attempt to scroll the start of the column to the visible rect. Previously, it would do nothing if part of the column was already visible.

On SnowLeopard linked applications, NSBrowser will return YES from -acceptsFirstResponder, unless the browser has been set to refuse first responder status with: -[browser setRefusesFirstResponder:YES]. Previously, it would only accept first responder status if it had at least one column that accepted first responder status. This made it impossible to set an NSBrowser as the first responder in IB.

On SnowLeopard linked applications, NSBrowser will correctly accept an empty index set to selectRowIndexes:inColumn: to allow deselection of all indexes in a given column. Previously an empty index set would be ignored.


NSBrowser - Context Menu

NSBrowser now properly supports the menu property. -[NSBrowser setMenu:] is propagated to subviews of NSBrowser matrices where appropriate to ensure the menu is available at all locations. Customizing -[NSBrowser menuForEvent:] will have no effect, as the individual subviews handle the menu. If you need to customize the menu, consider using a NSMenu delegate instead.

While the contextual menu is displayed, you may call -[NSBrowser clickedRow] and -[NSBrowser clickedColumn] to determine which cell was underneath the mouse when the context menu was displayed. The return value of both these functions will be -1 if no cell was clicked.

NSBrowser - Convenience methods

In Leopard and earlier releases, the default column width of an NSBrowser was set by passing the column index -1 to -[NSBrowser setWidth:ofColumn:], and retrieved by passing -1 to -[NSBrowser widthOfColumn:]. There are now first-class methods for accessing this property: -[NSBrowser setDefaultColumnWidth:] and -[NSBrowser defaultColumnWidth].

NSBrowser - Appearance

Browsers no longer leave a white gap at the bottom right side of the scroll bar unless it is necessary to do so, such as when displaying a window resize corner.

When targeting a drop at the entire browser by setting the drop column to -1, the a highlight rectangle will be drawn around the entire control.

NSApplication - Full keyboard access

When implementing compound controls such as NSTableView, it can be useful to get the current state of full keyboard access to determine what your control’s Tab behavior should be. NSApplication now has a -isFullKeyboardAccessEnabled method which will return the current state of the setting from the Keyboard system preference pane.



Bindings and Processing KVO Notifications

NSBindingDebugLogLevel

The Binding debug log level now recognizes values greater than 1. With the user default set to 2, Cocoa Bindings will log whenever a KVO notification gets processed. The result looks like:

    "Cocoa Bindings: <YourBoundView: 0x101ed6930> binding 'isIndeterminate' processing observer notification from object YourObservedObject 0x101ebed90 for keyPath your.observed.keyPath."


Cocoa Java

The Java runtime for running Cocoa applications has been removed in SnowLeopard.


NSColorSpace

This new method returns the list of color spaces available on the system that are displayed by the color panel, in the order they are displayed in the color panel. Doesn't return arbitrary color spaces which may have been created on the fly, or spaces without user displayable names. Pass model==NSUnknownColorSpaceModel to get all color spaces. Empty array is returned if no color spaces are available for the specified model.
+ (NSArray *)availableColorSpacesWithModel;

NSColorSpace

This new method returns a gray color space with 2.2 gamma. It mirrors the kCGColorSpaceGenericGrayGamma2_2 added in Quartz:
+ (NSColorSpace *)genericGamma22GrayColorSpace;

NSColor

NSColors will now always properly compare isEqual: (and return the same hash) after archiving/unarchiving. Previously in some cases (and more often in 64-bit), a color would not compare isEqual: with its unarchived counterpart.

Note that the individual component values will not all compare exactly the same after unarchiving, but they should compare the same if cast to float.


64-bit Conversion

There is now an improved tool for converting Cocoa applications to 64-bit:

    /Developer/Extras/64BitConversion/ConvertCocoa64

The underlying functionality is very similar to the tops script that shipped with Leopard; however, this is a Ruby script that will run tops for you on the specified source files, and also clean up some of the unnecessary warnings that tops inserts in your code.

Use of this (or some customized variant appropriate for your needs, or just some other automated tool of your choice) is highly recommended for the first-pass bulk conversion of Cocoa sources to 64-bit. In our experience, one-by-one manual conversion attempts can lead to errors stemming from copy/paste or oversight, and automated conversions avoid these sorts of problems.


NSApplication

-userInterfaceLayoutDirection is a new NSApplication interface that returns the default layout directionality of general user interface flow for the running application. The method returns NSUserInterfaceLayoutDirectionRightToLeft when running with localizations such as Arabic or Hebrew that should have the user interface layout origin on the right edge of the coordinate system.


NSBezierPath

-cachesBezierPath and -setCachesBezierPath: methods are now officially declared to be deprecated.


NSCell

NSCell now has -userInterfaceLayoutDirection and -setUserInterfaceLayoutDirection:. This new property describes the in-cell layout directionality. For NSCell subclasses that have multiple visual components in a single cell instance, this property should specify the directionality or flow of components.

A new NSCell method, -fieldEditorForView:, is now invoked by -[NSWindow fieldEditor:forObject:] method allowing NSCell subclasses to easily provide a custom field editor object. If -fieldEditorFoView: returns a non-nil value, it's used for the cell object editing. The default implmentation returns nil.

A new NSCell property, -usesSingleLineMode, determines the layout behavior for text cells. When -usesSingleLineMode == YES, the Cocoa Text System forces to the text in a single line by ignoring line/paragraph separators and treating wrapping line breaking modes as the clipping mode. The field editor object is expected to filter line/paragraph separator characters entering into the cell value from user actions. Also, the baseline position for the text becomes fixed.


NSFontManager

The -changeAttributes: action message is now targeted to [NSFontManager target].


NSGraphicsContext

-focusStack and -setFocusStack: methods are deprecated.
There is a new NSImageInterpolation type, NSImageInterpolationMedium, added.


NSProgressIndicator

-animationDelay, -setAnimationDelay:, and -animate: methods are deprecated.


NSSearchFiedlCell

All new NSSearchFieldCell instances use single-line mode. NSSearchFieldCell instances unarchived from nib files created on pre-10.6 systems with the clipping line break mode are interpreted to use single-line mode.


NSSecureTextField

NSSecureTextField accepts input from various non-keyboard input sources such as Character Palette.


NSRulerView

NSRulerView now layout markers from the right when attached to a right-to-left wirting direction paragraph style.


NSTextFieldCell

NSTextFieldCell now fills its background with NSCompositeSourceOver instead of NSCompositeCopy.


NSTextInputClient

There is a new optional method, -drawsVerticallyForCharacterAtIndex:, that can inform the Text Input system whether the protocol conforming client renders the character at index vertically.


NSTextInputContext

The new NSTextInputContext class represents the interface to the Cocoa Text Input system. It represents a state or context unique to its client object such as the key binding state, input method communication session, etc. Each NSTextInputClient compliant object (typically an NSView subclass) carries its own NSTextInputContext instance. The default implementation of -[NSView inputContext] manages an instance automatically if the view subclass conforms to the NSTextInputClient protocol.

The client object needs to interact with its NSTextInputContext in order to handle text inputs. The clients are expected to send -handleEvent: message for all key/mouse events received. The -[NSView interpretKeyEvents:] method sends the message for key events. Also, -invalidateCharacterCoordinates should be send whenever the visual location of the client changes (i.e. window frame change, view scrolling, etc).


The original Text Input system classes and protocol

The original Cocoa Text Input system introduced in Mac OS X 10.0 is now deprecated. NSInputServer is replaced by the Input Method Kit framework. NSInputManager functionalities are integrated into the NSTextInputContext class. The NSTextInput protocol is replaced by the NSTextInputClient protocol introduced in Mac OS X 10.5 Leopard.

Mappings for deprecated NSInputManager methods:
+cycleToNextInputLanguage: and +cycleToNextInputServerInLanguage: are covered by more direct input source APIs such as the selectedKeyboardInputSource property.
-markedTextAbandoned: is replaced by -discardMarkedText
-handleMouseEvent: is replaced by -handleEvent:.

-markedTextSelectionChanged:client:, -wantsToInterpretAllKeystrokes, -wantsToHandleMouseEvents, and -wantsToDelayTextChangeNotifications are no longer necessary.


NSTextTab

NSDecimalTabStopType tab stops returns NSNaturalTextAlignment from -alignment for applications linked against Mac OS X 10.6 SnowLeopard libraries. This allows correct decimal tab layout in right-to-left paragraph style. Applications can specify NSDecimalTabUsesNaturalAlignment == YES using NSUserDefaults to force the new behavior.


NSTokenField

The behavior for handling whitespace characters such as space, tab, etc between tokens and tokenizing characters have changed in Mac OS X 10.5 Leopard. In 10.4, those characters were silently trimmed before tokenizing. With 10.5, those characters are left untouched and included into tokens. If the whitespace trimming behavior is desirable, the delegate could implement -tokenField:representedObjectForEditingString: and perform the trimming in the method.
In order to preserve binary compatibility with Tiger, the first post-Leopard AppKit now performs the trimming unless -tokenField:representedObjectForEditingString: is implemented by its delegate or the binary is linked against pre-Leopard releases.



Notes specific to Mac OS X 10.5

New features and significant changes in AppKit





64-Bit

Leopard contains 64-bit versions of many system frameworks, enabling building and running many Cocoa apps as 64-bit. There are some API changes in Cocoa to accomodate this. Most are due to the introduction of two new types, NSInteger and NSUInteger, as a way to represent "address-sized" integers on both 32 and 64-bit. NSInteger is defined as "int" on 32-bit and "long" on 64-bit, and NSUInteger is its unsigned counterpart. Almost all Cocoa-based APIs have been upgraded to use NSInteger or NSUInteger in place of int or unsigned int. NSInteger is analogous to CoreFoundation's CFIndex type.

(Note that early in Leopard, NSInteger and NSUInteger were named NSInt and NSUInt, respectively. These old names have been removed before final release of Leopard.)

Moving forward, applications should be using these new types (and CGFloat - see below) instead of int, unsigned int, and float, since this will make an eventual move to 64-bit much easier. We recommend this even for apps that need to run on Tiger; they can accomplish this with their own, Tiger-only definitions of these types.

We have a conversion script in /Developer/Extras/64BitConversion to help convert Cocoa applications to 64-bit. Information about this script and the Cocoa 64-bit effort in general can be found in the 64-Bit Transition Guide for Cocoa.

In general it should be possible to use the same source base for both the 32 and 64-bit versions of an application or framework. Running this script on your source base to convert your sources to 64-bit should still enable them to build and run correctly under 32-bit. If needed, you can do:
#if __LP64__
...
#endif
as a way to do 64-bit specific code.


In APIs where the term "int" appeared as a part of the method name (for instance, "intValue"), the term "integer" is used to represent this new NSInteger type, while "int" continues to refer to the native int type (which is 32-bit under both 32 and 64). Thus methods like intValue, numberWithInt:, scanInt:, etc continue to take or return ints, while methods such as integerForKey: in NSUserDefaults have been changed to take NSInteger. We are adding a number of counterpart methods in Foundation and AppKit that take or return NSInteger or NSUInteger arguments.

The new methods in Foundation are:

NSCoder:
 - (void)encodeInteger:(NSInteger)intv forKey:(NSString *)key;
- (NSInteger)decodeIntegerForKey:(NSString *)key;
NSString:
 - (NSInteger)integerValue;
NSScanner:
 - (BOOL)scanInteger:(NSInteger *)ptr;
NSNumber:
 - (NSInteger)integerValue;
- (NSUInteger)unsignedIntegerValue;
- (id)initWithInteger:(NSInteger)value;
- (id)initWithUnsignedInteger:(NSUInteger)value;
+ (NSNumber *)numberWithInteger:(NSInteger)value;
+ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value;
For the AppKit, this means the following new methods in both NSCell and NSControl:
- (NSInteger)integerValue;
- (void)setIntegerValue:(NSInteger)val;
- (void)takeIntegerValueFrom:(id)sender;
We also have the following new constants in NSObjCRuntime.h:
 #define NSIntegerMax   LONG_MAX
#define NSIntegerMin LONG_MIN
#define NSUIntegerMax ULONG_MAX

Note that by design, keyed archiving's handling of integral types is not strict. An integer quantity written with any of encodeInteger:forKey:, encodeInt32:forKey:, or encodeInt64:forKey: can be read back using any of the integer decode methods. If the value is too large to read using the specified decode method, an exception is raised.

For most integral values, we recommend the use of encodeInteger:forKey: and decodeIntegerForKey:. For values whose ranges are larger than what 32-bit signed integers can hold, the Int64: variants continue to be the more appropriate choice, even on 32-bit.

There are additional archiving and other considerations in the presence of 64-bit changes in our APIs. The 64-bit Transition Guide referred to above has more information on this topic and more.


CGFloat

Another major change in Cocoa APIs is the introduction of the CGFloat type in Quartz. This replaces the use of float, and is defined as double for 64-bit. Note that this is not a change dictated by the 64-bit move; however, we are taking advantage of the move to introduce this new type. The purpose of CGFloat is to provide higher precision and range in graphical values, for 64-bit applications. This type replaces the use of all graphical float types in Cocoa APIs, including those in Foundation's NSGeometry.h.

Another change in NSGeometry.h is the redeclaration of NSRect, NSPoint, and NSSize using the Quartz counterparts, CGRect, CGPoint, and CGSize. Unfortunately, due to binary compatibility considerations, this change is done for 64-bit only. Note that the Objective C type signatures of these types thus differs on 64-bit from that on 32-bits.


Enum name removal

As a part of 64-bit clean-up, we added explicitly sized types where we were previously using enums. For instance, we went from:
typedef enum NSAlertStyle {
NSWarningAlertStyle = 0,
NSInformationalAlertStyle = 1,
NSCriticalAlertStyle = 2
} NSAlertStyle;
to
enum {
NSWarningAlertStyle = 0,
NSInformationalAlertStyle = 1,
NSCriticalAlertStyle = 2
};
typedef NSUInteger NSAlertStyle;
The latter makes sure that NSAlertStyle remains a fixed size and signedness no matter how the enum contents change.

In doing this, we removed the enum names from the enum declarations, so any attempt to use "enum NSAlertStyle" will now fail. Please switch to using the typedef instead, which is fixed size.

NSOpenGL and 64-Bit

The AppKit API declarations in NSOpenGL.h have been changed to use standard OpenGL types (GLint, GLsizei, GLenum, and GLbitfield) in place of explicit C "int" and "long" types, to help facilitate the transition to 64-bit. The effective sizes and signedness of the parameters and return values remain the same as before for 32-bit development.

APIs removed in 64-Bit AppKit

A number of classes have been removed in 64-bit version of AppKit:

- NSMenuView - You can use NSView customizations on NSMenuItem
- NSMovieView - Use QTKit
- NSMovie - Available with very limited functionality (see here); use QTKit
- NSQuickDrawView - QuickDraw is not supported in 64-bit
- NSSimpleHorizontalTypesetter - Deprecated for a while now, use the new NSTypesetter facilities

In addition, a number of previously deprecated individual APIs have also been removed from the 64-bit AppKit, including the old Display PostScript related symbols exported solely for binary compatibility under 32-bit.


Objective-C 2.0

Leopard includes Objective-C 2.0, which brings a number of new features to the language and runtime: Fast enumeration, garbage collection, class extensions, properties, method and class attributes, and optional methods on protocols.

There are a number of additional runtime features available in 64-bit only: Stricter access control on private instance variables and classes, "non-fragile" instance variables, and C++ compatible, "zero-cost" exceptions (which are very cheap to set up, but expensive to throw).

It is important to note that all of these features are Leopard-only. Please refer to the Objective-C 2.0 documentation for full details on these features.

Note that many Objective-C APIs in Leopard have not yet adopted these features. But in future releases, you are likely to see:

- @property used to declare properties
- Delegate declarations switched from informal protocols (categories on NSObject) to formal protocols, with optional methods
- Increased restricted visibility of private symbols in the runtime

The last point is especially important, since it would prevent applications using these symbols from launching in future releases. As always: Please do not access undeclared APIs in your applications. This includes undeclared classes, methods, and instance variables. If you have a strong need for a private API and have no workaround, please let us know.


Interface Builder 3.0

Leopard developer tools includes a new version of Interface Builder (IB). IB 3.0 has a redesigned interface, a much improved integration with Xcode, and ability to access many more AppKit features in your nib files than before.

IB 3.0 supports three file formats. The 2.x file format is same file format that's been in use in Interface Builder previously. It can be edited by IB 2.0, and is deployable on earlier versions of Mac OS X. The 2.x file format does not support some new features of IB, such as the ability to edit custom cell subclasses and toolbars. The 3.x file format supports all the new features, is also deployable on earlier versions of Mac OS X, but can only be edited with IB 3.0. IB 3.0 also supports a textual, human-readable format called the "xib" format. This format is equivalent to the 3.x format, but it's more SCM-friendly. It is compiled down to a 3.x file at build time.

For editing and building your project on Leopard only, we recommend the xib format, since it provides the best development time experience. If you need to build your project on Tiger, but edit your nibs on Leopard only, then you can use the 3.x format. Finally, if you wish to be able to continue to edit your nib files on Tiger, you can stick to the 2.x format. In all these cases, the files can be deployed on Tiger, but some features supported by IB 3.0 may not work on the earlier systems. IB has facilities to warn you in those cases.


Core Animation Layer Tree Rendering

Leopard AppKit provides the means to render a Core Animation layer tree inside the bounds of any given NSView. Clients need only provide the root CALayer of the layer tree to be displayed, then enable layer tree rendering for the same view:
    [view setLayer:rootLayer];
    [view setWantsLayer:YES];
AppKit responds by setting up a Core Animation renderer that animates and composites the layer tree on a background thread.

The Core Animation renderer continues its asynchronous operation until layer tree rendering is disabled for the view (via a setWantsLayer: message with a parameter of NO), or the view is hidden or removed from the window. Un-hiding a view, or adding a view that has wantsLayer enabled to a window, resumes layer tree rendering.

To conserve system resources, AppKit also suspends layer tree rendering for the views in a given window when the window is ordered out, and for all the views in a process when the user's login session is switched out via the "Fast User Switching" feature. In such cases, layer tree rendering automatically resumes when the window is ordered back in, or when the user's login session is switched back in, as appropriate.

See the documentation for the QuartzCore framework's new Core Animation API for more information about layer tree creation, capabilities, and use.

New View Animation Facilities, and Layer-Backed View Drawing

In addition to supporting rendering of user-defined Core Animation layer trees, Leopard AppKit adopts API paradigms similar to Core Animation's to allow clients to request animation of view and window properties, and also adds a new "layer-backed" mode for rendering and animation of view trees. Use of layer-backed mode is not required in order to use AppKit's new CAAnimation-based API, but does enable the use of additional new visual treatments for views (per-view overall alpha, shadows, and the ability to apply Core Image filters to rendered content), and accelerates animation of nonresizing views for the price of caching their rendered content into per-view CALayers.

In conventional view rendering, a window's view tree is drawn back-to-front into the window's backing store. The drawn view content is thus pre-composited into a single backing store, in such a way that updating any given part of the window requires re-drawing the affected parts of all the views that contribute to the result. In the new "layer-backed" view drawing mode that is now supported in Leopard, each of the views in a layer-backed view subtree has its content drawn to its own layer in an AppKit-created-and-managed CALayer tree. This carries an additional memory cost, of order (4 * pixels wide * pixels high) bytes per view, that should be weighed when considering whether and where to employ layer-backed mode in an application's user interface, but in return it enables already-rendered view content to be moved around more efficiently during animations, since the content only has to be re-compositied instead of being re-rendered from scratch.

Most of the standard views and controls that AppKit and Mac OS X's other Cocoa frameworks provide are able to function in layer-backed mode in Leopard, with the exception of certain specialized views such as WebKit WebViews and Quartz Composer QCViews, whose use in layer-backed mode is not presently supported.

Layer Size Limits and Tiled Layers

One inherent limitation of rendering view content into a Core Animation layer is that the size of ordinary CALayers is constrained by the maximum OpenGL texture size supported by the host system's graphics hardware. On most current graphics hardware the effective limit is 2046x2046 pixels, beyond which size layer creation will fail. Care should therefore be taken to insure that layer-backed views do not exceed this size limit.

To get around this limitation for potentially larger document views, AppKit employs CATiledLayers to serve as the backing layers for the document views of NSScrollViews. This specialized layer type caches its content in a grid of "tiles" (of default size 256x256 pixels) that are drawn as they become visible, and can be garbage-collected when they go out of view. From the user's perspective, the tiles are added asynchronously as they are revealed during scrolling. The visual appearance of the tile addition can be minimized by enabling the "drawsBackground" property for the enclosing NSScrollView (or, equivalently, its NSClipView), and choosing a background color that most closely matches the document view content being drawn.

Note that when using layer-backed mode for an NSScrollView's document view, it's necessary for the enclosing NSScrollView, or at least its NSClipView ("content view"), to also be layer-backed in order for scrolling to function correctly.

Using Layer-Backed Views

A quick-start guide for beginning to experiment with this functionality:

1. Choose a view that will serve as the root view for Core Animation-based view rendering, and switch on Core Animation-based rendering for that view (and implicitly its descendants) by doing [theRootView setWantsLayer:YES];

2. Properties of views and windows for which auto-animation is supported can be animated by messaging through the target object's (view's or window's) "animator" proxy:
    [[someDescendantOfTheRootView animator] setFrame:newFrame];
To specify a duration in place of the global default of 0.25 seconds, enclose such messages in an NSAnimationContext that specifies the duration for animation:
    [NSAnimationContext beginGrouping];
    [[NSAnimationContext currentContext] setDuration:0.25];
    [[someDescendantOfTheRootView animator] setFrame:newFrame];
    [NSAnimationContext endGrouping];
Basic default animation parameters are provided for the following NSView and NSWindow properties, such that they will animate automatically when assigned a new target value via the view or window's animator:

for NSView: alphaValue, frame, frameOrigin, frameSize, frameRotation, frameCenterRotation, bounds, boundsOrigin, boundsSize, backgroundFilters, contentFilters, compositingFilter, shadow

for NSWindow: alphaValue, frame

Some further notes regarding the new animation and visual property additions to NSView follow, in the "NSAnimationContext", "new NSView properties", and "NSAnimation additions" sections.

NSAnimationContext

NSAnimationContext is a new class in Leopard. NSAnimationContexts are analogous to Core Animation's CATransactions, and are also somewhat similar in overall concept to NSGraphicsContexts. Each thread maintains its own stack of nestable NSAnimationContext instances, with each new instance initialized as a copy of the instance below it (so, inheriting its current properties). Currently, an NSAnimationContext exists for the sole purpose of holding the default duration to be used for "animator"-proxy-initiated animations.

Each thread starts with a current NSAnimationContext whose default duration is 0.25 seconds (the same default value used by Core Animation), meaning that value-set operations for animatable object properties that go through "animator" proxies will animate with that duration by default. To animate with a different duration, a section of code would begin a new NSAnimationContext with the desired duration, perform the desired value-set operations through "animator" proxy or proxies, then close the context when done:
    [NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:1.0]; // Animate enclosed operations with a duration of 1 sec
[[aView animator] setFrame:newFrame];
[NSAnimationContext endGrouping];
NSAnimationContexts can be nested, allowing a given block of code to initiate animations using its own specified duration without affecting animations initiated by surrounding code.
    [NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:1.0]; // Animate enclosed operations with a duration of 1 sec
[[aView animator] setFrame:newFrame];
...
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.5]; // Animate alpha fades with half-second duration
[[aView animator] setAlphaValue:0.75];
[[bView animator] setAlphaValue:0.75];
[NSAnimationContext endGrouping];
...
[[bView animator] setFrame:secondFrame]; // Will animate with a duration of 1 sec
[NSAnimationContext endGrouping];
Since an "animator" proxy can be handed off to code that expects an ordinary object of the kind the proxy targets (presently, an NSView or NSWindow), it might in rare circumstances be necessary to suppress animation for code that does not explicitly go through "animator" proxy objects. This can be accomplished using an animation context with a duration of zero:
    [NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.0]; // Makes value-set operations take effect immediately
[aViewOrMaybeAnAnimator setFrame:newFrame];
[NSAnimationContext endGrouping];

New NSView properties

Leopard AppKit adds some new properties to NSViews, which are described below with their corresponding accessor methods.
- (void)setWantsLayer:(BOOL)flag;
- (BOOL)wantsLayer;
The "wantsLayer" property determines whether a view and its descendants should be composited and animated using a Core Animation layer tree, enabling the use of advanced animation and compositing effects. Defaults to NO. Setting this property to YES for the rootmost view for which Core Animation-based compositing is desired is all that's needed to activate Core Animation-based view buffering, compositing, and animation for a given view subtree. The view subtree is then said to be "layer-backed", since each view is given a corresponding Core Animation layer that serves as its backing store.
- (CALayer *)layer;
The -layer method returns the view's corresponding AppKit-created-and-managed CALayer, if the view is layer-backed. Callers may use the returned pointer to message the layer directly, as a means of accessing features that aren't re-exported as NSView properties. May return nil for a view that's currently marked as layer-hosted, if AppKit hasn't yet displayed the view for the first time and thus created the view's layer. For most ordinary usage of animating views' frames and content and applying effects, awareness of and direct access to views' underlying layers is unlikely to be needed, as AppKit will be able to manage them automatically.
- (void)setLayer:(CALayer *)newLayer;
The -setLayer: method sets a given CALayer to be a view's backing layer. This causes the view to dissociate from its previously assigned layer (if any), removing that layer from its surrounding layer tree and releasing the view's reference to the layer. The new layer takes on the old layer's position in the layer tree (or is simply added to the layer tree in the appropriate place, if it isn't replacing an existing layer). A view retains its layer, but AppKit maintains only a weak reference from the layer back to the view. This method manages both associations.

-setLayer: is published to allow for the usage where a developer has an arbitrary layer tree that's not tied to a view subtree and isn't automatically managed by AppKit, and wants to render that in a view (see "Core Animation Layer Tree Rendering").
- (void)setAlphaValue:(CGFloat)viewAlpha;
- (CGFloat)alphaValue;
Sets the overall opacity value with which the view and its descendants are composited into their superview (analogous to a window's alphaValue). Defaults to 1.0. This setting may be varied independently of the class' return value for -isOpaque, and the implementation of the latter needn't take the view's alphaValue into account, since AppKit consults both values when necessary. A view's alphaValue will affect both Core Animation-based and conventional view compositing.
- (NSShadow *)shadow;
- (void)setShadow:(NSShadow *)shadow;
Sets an optional shadow to be drawn behind the view subtree. Defaults to nil. This setting only has an effect for Core Animation-based view compositing. Note that, although Core Animation's shadow model uses the same parameters as a Quartz shadow, the rendered results may differ from those achieved using Quartz shadow rendering. NSShadow is used here merely as an appropriate Cocoa encapsulation for the identical set of shadow parameters.


The following three pairs of accessor methods can be used to apply arbitrary Core Image filter effects for views that are backed by CALayers. As specified by Core Animation, the conceptual model used to apply filter operations and combine their results is:
   maskop(mask, compositeop(layerop(layer), backgroundop(background)), background)
- (CIFilter *)compositingFilter;
- (void)setCompositingFilter:(CIFilter *)filter;
Sets a CIFilter that will be used to composite the view subtree over its (possibly filtered) background. Defaults to nil, which implies that source-over compositing should be used. This setting only has an effect for Core Animation-based view compositing.
- (NSArray *)contentFilters;
- (void)setContentFilters:(NSArray *)filters;
Allows the view's content to be filtered through an optional chain of CIFilters before being composited into the render destination. The supplied array of filters needn't be connected to one another, as they will be connected in series automatically by Core Animation. Defaults to nil. This setting only has an effect for Core Animation-based view compositing.
- (NSArray *)backgroundFilters;
- (void)setBackgroundFilters:(NSArray *)filters;
Allows the background behind the view's subtree to be filtered through an optional chain of CIFilters before the view subtree is composited into it. The supplied array of filters needn't be connected to one another, as they will be connected in series automatically by Core Animation. Defaults to nil. This setting only has an effect for Core Animation-based view compositing.

Layer-Backed Views and Out-of-Band Drawing

For the ordinary case of views drawn into a shared window backing store, it has historically been possible to draw into the window area that a view occupies at will, by locking focus on the view, drawing, and unlocking focus:
    [view lockFocus];
/* Perform some drawing. */
[view unlockFocus];
This was sometimes used to replace some animated content in response to a timer callback, for example.

Due to the inherently different buffering semantics for layer tree based rendering, this technique cannot be used by layer-backed views. Instead, you should perform all needed drawing in -drawRect:, and use -setNeedsDisplay: and/or -setNeedsDisplayInRect: to prompt updates as needed. This is generally a preferred technique anyway, since it avoids potential inconsistencies with -drawRect:-based drawing, allows other views to contribute potentially necessary content to the affected window area, and enables AppKit to coalesce updates for greater efficiency.

Avoiding Synchronization Issues with Layer-Backed View Animations

When a view is resized, the content that it draws can respond in potentially arbitrary ways.  For this reason, AppKit's "animator" proxy based animation API asks resizing views to redraw their content at each step along the way, to insure correct results.

When used with layer-backed views, this can lead to synchronization problems where a single view can appear to "jitter" when its frameOrigin and frameSize are simultaneously animated, or where gaps between adjacent animating views fluctuate, because each view's frameOrigin move is being animated on a background thread by Core Animation.  Such synchronization problems can be remedied by insuring that the view frame animations are initiated using a -setFrame: message to each view's animator, rather than going through -setFrameOrigin: and/or -setFrameSize: separately.  This cues AppKit to animate both the frameOrigin and frameSize changes itself, so that the results will stay in sync throughout the animation.

Note that AppKit-driven animations are timer-based, and thus require normal periodic runloop servicing to occur in order for them to advance.  For best results, try to avoid having lengthy operations block the runloop while AppKit-driven animations are in flight.

Layer-backed NSImageView optimization

For complex views, animations that cause the frame size to change often result in sub-optimal animation performance. When the view's size changes, it must redrawn for each frame of the animation, which often lowers framerates. NSImageView is a good example of this, since rendering images can be expensive. NSImageView has been optimized in certain cases for resizing animations. The following is a set of conditions that must be met in order for the optimization to take effect:

• The image view must be uneditable and have a frame style of NSImageFrameNone.
• The image view must contain AppKit's standard NSImageCell and the view's drawRect: method must not be overridden.
• The view must be layer-backed and must be using the layer that AppKit creates for it (meaning you may not provide your own layer with -setLayer:).
• The "best" image rep (according to -bestRepresentationForDevice:) for the view's image must be an NSBitmapImageRep, NSCachedImageRep, NSPICTImageRep, or NSCGImageRep.
• The view's imageScaling must be NSImageScaleProportionallyUpOrDown with centered imageAlignment or NSImageScaleAxesIndependently or NSNSImageScaleNone with any imageAlignment. Note that the default imageScaling is NSImageScaleProportionallyDown which does not allow the optimization to take effect.

Shifting "needsDisplay" Rectangles in a View

The following method enables shifting of the receiving view's dirty rects in a single operation. The effect of this method is to get all of the receiving view's dirty rectangles, clear all dirty rectangles in the intresection of the specified clipRect and the view's bounds, shift the retrieved rectangles by the given "delta" offsets, clip the result to the intersection of clipRect and the view's bounds, and add the resultant rectangles back to the view.
- (void)translateRectsNeedingDisplayInRect:(NSRect)clipRect by:(NSSize)delta;
This method should rarely be needed, but may be useful to clients that implement their own copy-on-scroll logic.

Pixel Alignment and Transforming View Coordinates To and From "Base" Space

In Leopard, NSView provides a new set of methods that should be used when performing pixel-alignment of view content. They provide the means to transform geometry to and from a "base" coordinate space that is pixel-aligned with the backing store into which the view is being drawn:
- (NSRect)convertRectToBase:(NSRect)aRect;
- (NSPoint)convertPointToBase:(NSPoint)aPoint;
- (NSSize)convertSizeToBase:(NSSize)aSize;
- (NSRect)convertRectFromBase:(NSRect)aRect;
- (NSPoint)convertPointFromBase:(NSPoint)aPoint;
- (NSSize)convertSizeFromBase:(NSSize)aSize;
For conventional view rendering, in which a view hierarchy is drawn flattened into a window backing store, this "base" space is the same as the coordinate system of the window, and the results of using these new methods are the same as converting geometry to and from view "nil" using the existing -covert[Rect/Point/Size]:[to/from]View: methods.

Views that are rendered into backing layers in a Core Animation layer tree, however, have their own individual backing stores, which may be aligned such that window space is not necessarily the appropriate coordinate system in which to perform pixel alignment calculations.

These new coordinate transform methods provide a way to abstract view content drawing code from the details of particular backing store configurations, and always achieve correct pixel alignment without having to special-case for layer-backed vs. conventional view rendering mode. Regardless of the underlying details of how view content is being buffered, converting to base space puts one in the native device coordinate system, in which integralizing coordinates produces pixel alignment of geometry.

When using layer-backed views at a user interface scale factor other than 1.0, note that the dimensions of a view and the dimensions of its corresponding backing layer will vary according to the scale factor, since CALayer bounds are always expressed in pixels, while NSView dimensions remain expressed in points. Most clients of layer-backed views will not have a need to perform operations directly in layer space, but for those that do it's important to use the preceding methods to convert geometric quantities between view space and layer ("base") space when appropriate.

Responding to View Hiding/Unhiding

Hiding or un-hiding a given view using the -setHidden: API affects the effective visibility of all of its descendant views. Leopard adds two new NSView methods that clients can override, if desired, to have their custom view classes respond to becoming effectively hidden or unhidden vis-à-vis the -setHidden: API:
- (void)viewDidHide;
- (void)viewDidUnhide;
A view will receive a "viewDidHide" message when its "isHiddenOrHasHiddenAncestor" state goes from NO to YES. This can happen when the view or an ancestor is marked as hidden, or when the view or an ancestor is spliced into a new view hierarchy.)

A view will receive a "viewDidUnhide" message when its "isHiddenOrHasHiddenAncestor" state goes from YES to NO. (This can happen when the view or an ancestor is marked as not hidden, or when the view or an ancestor is removed from its containing view hierarchy.)

Performing Just-Before-Drawing View Activity

NSView has a new "-viewWillDraw" API method in 10.5 that can be overridden to perform any last-minute activity that might be desired at the outset of a view hierarchy "-display..." operation.
- (void)viewWillDraw;
Most often, the activity to be performed at this time consists of some combination of view layout (assigning new frame sizes and/or positions to views) and marking additional view areas as needing display (typically as the result of performing layout of non-view content, such as text glyphs, graphics, or web content). The desired effect is to perform such computations on demand, deferred until their results are about to actually be needed, allowing for the same kind of update coalescing performance benefits that we get with the deferred display mechanism itself, rather than forcing content layout to be performed immediately when the content is established or deferred until a subsequent drawing pass.

At the outset of recursive display of part or all of a view hierarchy, which is always initiated by one of NSView's eight public "-display..." methods, AppKit recurses down the view hierarchy, sending a -viewWillDraw message to each of the views that may be involved in the display operation.

NSView's default implementation of this method is itself the mechanism for the recursion, and allows overriders the flexibility to head-recurse or tail-recurse as best suits the needs of the operations they have to perform. Conceptually, NSView's implementation looks like:
@implementation NSView
- (void)viewWillDraw {
    if (any descendant of self overrides "-viewWillDraw") {
        for (each subview that intersects the window area being drawn in back-to-front order) {
            [subview viewWillDraw];
        }
    }
}
@end
So an override of this method could do:
- (void)viewWillDraw {
    /* Perform some operations before recursing for descendants. */
    /* Now recurse to handle all our descendants. Overrides must call up to super like this. */
    [super viewWillDraw];
    /* Perform some operations that might depend on descendants already having had a chance to update. */
}
During the -viewWillDraw recursion, sending of -setNeedsDisplay: and -setNeedsDisplayInRect: messages to views in the hierarchy that's about to be drawn is valid and supported, and will affect AppKit's assessment of the total area to be rendered in that drawing pass.

If desired, an implementation of -viewWillDraw can use NSView's existing -getRectsBeingDrawn:count: method to obtain a list of rectangles that bound the affected area, enabling it to restrict its efforts to that area.

Setting a View's Subviews

NSView has a new -setSubviews: API method in 10.5 that can be used to reorder a view's subviews, remove existing subviews, and/or add new subviews all via a single API entry point:
- (void)setSubviews:(NSArray *)newSubviews;
With this single method, one can:
- reorder the receiver's existing subviews
- add or remove subviews from the receiver
- potentially replace all of the receiver's previous subviews with a whole new set of views
- potentially remove all of the receiver's previous subviews, leaving it with none (think "[aView setSubviews:[NSArray array]]")

The views in the "newSubviews" array can by any combination of existing subviews of the receiver, subviews of other views, or views that currently have no superview. If "newSubviews" is nil, or contains any duplicate entries, -setSubviews: throws an invalid argument exception. (Prior to the WWDC 2007 Leopard seed, -setSubviews: would also raise an exception if any of the new subviews was already a subview of some view. This is now allowed, and simply results in the views being removed from their previous superviews before being added as subviews of the receiver.)

Given a valid "newSubviews" parameter, -setSubviews: performs whatever sorting of the subviews array, subview addition, and subview removal activity is necessary to leave the receiver with the requested new array of subviews. Thus, any member of "newSubviews" that isn't already a subview of the receiver is added. Any member of the view's existing "subviews" array that isn't in "newSubviews" is removed. And any views that are in both "subviews" and "newSubviews" is simply moved in the subviews array as needed, without being removed and re-added. The -setSubviews: method also marks affected view/window areas as needing display to reflect the new z-ordering.

Views, Focus Rings, and Drawing Performance

To help guarantee correct redraw of focus rings, AppKit may automatically draw additional parts of a window beyond those that application code marked as needing display. It may do this for the first responder view in the application's key window, if that first responder view's focusRingType property is set to a value other than NSFocusRingTypeNone. Any view that can become first responder, but doesn't draw a focus ring, should have its focusRingType set to NSFocusRingTypeNone to avoid unnecessary additional redraw.

NSAnimatablePropertyContainer protocol

NSAnimatablePropertyContainer is a new protocol in Leopard, currently adopted by NSView and NSWindow. The methods in NSAnimatablePropertyContainer are as follows:
- (id)animator;
The -animator method returns a proxy object for the receiver that can be used to initiate implied animation of property changes. An object's "animator" should be treated as if it was the object itself, and may be passed to any code that accepts the object as a parameter. Sending of KVC-compliant "set" messages to the proxy will trigger animation for automatically animated properties of its target object, if the active NSAnimationContext in the current thread has a duration value greater than zero, and an animation to use for the property key is found by the -animationForKey: search mechanism defined below. An object's automatically animated properties are those for which [theObject animationForKey:] finds and returns an CAAnimation instead of nil, often because [[theObject class] defaultAnimationForKey:] specifies a default animation for the key.

It's perfectly valid to set a new value for a property for which an animation is currently in progress; this simply sets a new target value for that property, with animation to the new target proceeding from whatever current value the property has reached. An in-flight property animation can be stopped by setting a new value for the property with 0.0 as the surrounding NSAnimationContext's duration value.

For the common specific case of animating views:
Initiating animation via an "animator" proxy object works under both Core Animation-based and conventional view compositing. The primary difference under Core Animation-based compositing is that for intrinsic geometric properties such as the view's "frame," all animation is handled at the Core Animation level, meaning that the view's property will be immediately set to the desired target value, and the view won't see the successive intermediate values. The animation effect in such cases is purely visual and exists only in Core Animation's "render tree" backend. In contrast, under conventional (non-layer-backed) view compositing, view property animations are executed at the AppKit level, and during the course of those animations views will receive value-set operations for successive intermediate values. This is also true for animation of all developer-defined properties, under both layer-backed and conventional view compositing and animation.
- (NSDictionary *)animations;
- (void)setAnimations:(NSDictionary *)dict;
An animatable property container's optional "animations" dictionary maps NSString keys to CAAnimation values. When an occurrence matching the key fires for the view, -animationForKey: first looks in this dictionary for an animation to execute in response.
- (id)animationForKey:(NSString *)key;
When the occurrence specified by "key" fires for an object, -animationForKey: is consulted to find the animation, if any, that should be performed in response. Like its Core Animation counterpart, -[CALayer actionForKeyPath:], this method is a funnel point that defines the standard order in which the search for an animation proceeds, and is not one that clients would typically need to override. This method first checks the receiver's "animations" dictionary, then falls back to +defaultAnimationForKey: for the receiver's class.
+ (id)defaultAnimationForKey:(NSString *)key;
As described above, -animationForKey: next consults the class method +defaultAnimationForKey: when its search of an instance's "animations" dictionary doesn't turn up an animation to use for a given property change.

NSView's +defaultAnimationForKey: method returns a default animation that should be performed when the occurrence specified by "key" fires for a view, where "key" typically names a property whose value is being changed. For each of NSView's own animatable properties, NSView's implementation returns a suitable default CAAnimation to be used. For all other keys this method returns nil.

A developer implementing a custom view subclass can enable automatic animation of the subclass' added properties by overriding this method, and having it return the desired default CAAnimation to use for each of the property keys of interest. The override should defer to super for any keys it doesn't specifically handle, facilitating inheritance of default animation specifications.
@implementation MyView
+ (id)defaultAnimationForKey:(NSString *)key {
if ([key isEqualToString:@"borderColor"]) {
// By default, animate border color changes with simple linear interpolation to the new color value.
return [CABasicAnimation animation];
} else {
// Defer to super's implementation for any keys we don't specifically handle.
return [super defaultAnimationForKeyKey:key];
}
}
@end

NSCollectionView

A new view class has been added to facilitate interesting animations: NSCollectionView. You can set or bind a collection view's content to an array of objects. For each object, the collection view will create an NSCollectionViewItem, which in turn manages a view that is used to display the values of its "represented object." All views automatically create a layout to fit all items into its content and animates them if the content changes (for example, if the content is reordered, it will slide the items into the new positions).

Usually collection view items are created from a "prototype" which is set as the "itemPrototype" outlet in Interface Builder. For the view of the collection view item, you can use standard controls/views to form a "compound" view. For example, you can group an NSImageView and an NSTextField in an NSBox to form a unit that displays images and names for it. To set the view used by a collection view item, you typically use the "view" outlet.

To populate a collection view item's view with values from the represented object, you will typically create bindings from the view (or any of the subviews) to the "representedObject" of the collection view item (example: you could bind the value binding of text field to the key "representedObject.name" of the collection view item). Alternatively, you could subclass NSCollectionViewItem and make it the data source or delegate of one of the views.

Note that in early Leopard seeds (including WWDC 2006) NSCollectionView was known as NSGridView, and NSCollectionViewItem was NSLayoutItem. As foreshadowed in the release notes, these classes have changed; however, the changes are limited strictly to the two class and several method name changes:

- layoutView in NSLayoutItem has become collectionView
- minGridSize, maxGridSize, and corresponding setter methods have become minItemSize, maxItemSize, setMinItemSize:, and setMaxItemSize:
- newLayoutItemForRepresentedObject: is now newItemForRepresentedObject:
- layoutItemPrototype and setLayoutItemPrototype: are now itemPrototype and setItemPrototype:

The old names have been removed in the final version of Leopard.


Resolution Independence

On Leopard, resolution independence (aka "HiDPI") is a developer feature. You can use QuartzDebug in /Developer/Applications/Performance Tools to set the user space scale factor to 1.25, 1.5, 2.0, or 3.0, then launch your application. The user interface of your application will be scaled by the user space scale factor.

Most standard controls are now drawn in high resolution, which allows them to appear crisp when running with user space scaling enabled.

In addition a number of standard images are now available in high-resolution, enabling graphics to appear crisp when scaled. Names of these images are declared in NSImage.h. Note that it's important to use these images only for the purpose indicated by the name, since the actual graphic may change in future releases. See the NSImage section for more info.

There are some known issues with the non-integral scale factors of 1.25 and 1.5.

- Most drawing should occur on integral pixel boundaries, but views are not automatically constrained to fall on pixel boundaries
- Image tiling also looks most correct if the tiled image is integral-pixel sized and adjusted as needed to avoid pixel cracks
- Tracking rects on non-integral boundaries may generate a mouseEntered: event while the mouse is still slightly outside the fractional bounds, or a mouseExited: while the mouse is still slightly inside the fractional bounds.
- Some controls on non-pixel boundaries may have jagged edges if the end caps do not vertically or horizontally align with the fill
- Text may jitter when scrolling if the scrollView is on a non-pixel boundary

You can use -[NSView centerScanRect:] to position a view or a rect within a view on pixel boundaries.  On Tiger, this method used NSIntegralRect, which expanded the given rectangle outward as needed to land on integral pixel values.  On Leopard, this method is size preserving, which results in better layout behavior when applied to adjacent rectangles.  This change applies only to applications linked on Leopard or later for compatibility reasons. Alternatively, you can convert to window coordinates using -[NSView convertRectToBase:], round the result to integral values with rounding rules that suit your needs, then convert back to view coordinates using -[NSView convertRectFromBase:].

Because the scaling from points to pixels is non-integral when the user space scale factor is non-integral, you need to be sure not to use floor, ceil, or round on coordinates expressed in points. This rounding would be likely to result in non-integral pixel values, which would lead to the problems listed above.

NSOpenGL and resolution independence

Applications and frameworks that are striving to become resolution-independent can encounter problems with OpenGL usage, due to the fact that many OpenGL API functions, such as glViewport(), expect their parameters in pixel units. Code that is accustomed to running under a user interface scale factor of 1.0 may contain latent errors where view dimensions were incorrectly treated as if they were in pixel units. For scale factors other than 1.0, the distinction between view space units and device (pixel) units becomes much more important to properly observe.

A common usage pattern in Cocoa-based OpenGL applications, for example, has been to pass a view's bounds dimensions directly to glViewport(), which expects to receive its parameters in pixel units:
- (void)reshape {
NSSize bounds = [self bounds];
    // This is technically INCORRECT, because bounds is not expressed in pixel units.
glViewport(0, 0, bounds.size.width, bounds.size.height);
}
To help ease the transition to resolution independence for applications that use this common code pattern, Leopard AppKit automatically configures the bounds of any view that has an associated NSOpenGLContext (thus, NSOpenGLViews, as well as ordinary NSViews that are drawn into using an NSOpenGLContext) so that the bounds are expressed in pixel units, according to the current user interface scale factor. So for example, if an application has an NSOpenGLView whose frame size is 100x100 points, and that application is run at a user interface scale factor of 1.25, the NSOpenGLView's frame will remain 100x100 points, but its bounds will be reported as 125x125. That enables commonly used code constructs such as the above -reshape method to function correctly without code changes.

While this automatic workaround may suffice to provide compatibility for many applications, the ideal solution is for OpenGL client code to perform correct unit conversions where needed. For example, the above -reshape method would be more correctly written as:
- (void)reshape {
// Convert up to window space, which is in pixel units.
NSSize boundsInPixelUnits = [self convertRect:[self bounds] toView:nil];
    // Now the result is glViewport()-compatible.
glViewport(0, 0, boundsInPixelUnits.size.width, boundsInPixelUnits.size.height);
}
Code that targets Mac OS 10.5 and later can use the -convertRectToBase: method instead of converting to view nil, which has the advantage of correctly producing a result in pixel units regardless of whether the view is layer-backed. (If the view is layer-backed, -convertRectToBase: converts to the coordinate space of the layer's backing store, instead of to the window's coordinate space.)

Text rendering and resolution independence

When rendering with non-integral scale factor, we recommend you use the antialiased text rendering mode. Since the glyph origin might not be pixel-aligned at layout time, rendering non-antialiased text doesn't produce optimal quality.


NSRuleEditor

NSRuleEditor is a new AppKit control introduced in Leopard. NSRuleEditor allows the user to configure "rules," similar to the rules in Mail or in the Finder search window, by selecting from popups, manipulating custom views, and adding, deleting, or rearranging rows.

NSRuleEditor:
- Supports nesting and non-nesting modes
- Gets the available popups/views from its delegate
- Is bindings-compatible
- Supports automatic generation of NSPredicates
- Supports flexible localization through a strings file or dictionary

Please refer to NSRuleEditor.h and documentation for more info on this new class.


NSPredicateEditor

NSPredicateEditor is a subclass of NSRuleEditor specialized for working with predicates. Unlike NSRuleEditor, NSPredicateEditor does not depend on its delegate to populate its rows (and does not call the populating delegate methods). Instead, its rows are populated from its objectValue, which is an NSPredicate.

NSPredicateEditor relies on instances of a new class, NSPredicateEditorRowTemplate, which is responsible for mapping back and forth between the displayed view values and various predicates.

NSPredicateEditor exposes one property, rowTemplates, which is an array of NSPredicateEditorRowTemplates.
- (void)setRowTemplates:(NSArray *)rowTemplates;
- (NSArray *)rowTemplates;
Developers will typically configure NSPredicateEditor with some NSPredicateEditorRowTemplates, either programmatically or in Interface Builder, and then set and get NSPredicates on the NSPredicateEditor. Changes to the predicate are announced with the usual target/action mechanism.


NSMenu

New menu customization APIs

Leopard now allows applications to set a custom view for a menu item, via the following new NSMenuItem methods:
- (void)setView:(NSView *)view;
- (NSView *)view;
The custom view takes over all aspects of the menu item's drawing. Mouse event processing is handled normally for the view, including mouse down, mouse up, mouse moved, mouse entered, mouse exited, mouse dragged, and scroll wheel events. In non-sticky tracking mode (manipulating menus with the mouse button held down), the view will receive mouseDragged: events. See the header file NSMenuItem.h for more information about custom menu item views.

Animation in menu item views

Views in menu items can be animated with the usual mechanisms, such as calling - [NSView setNeedsDisplay:] or - [NSView display] in a repeating timer. But be aware that menu tracking occurs with the run loop running in NSEventTrackingRunLoopMode, so any timers you add must be set to fire while in that mode.

New delegate methods and notifications

Menus will now notify their delegates when they are opened or closed, and when they highlight or unhighlight items during menu tracking. See NSMenu.h for more information about these new delegate methods.

Removal of NSMenuItem protocol

All uses of id <NSMenuItem> have been removed from AppKit in Leopard. Applications should switch to the NSMenuItem class.

NSMenuItem attributed title truncation

In versions of Mac OS X prior to Leopard, NSMenuItems with excessively long attributed titles would be clipped against the edge of the menu. In Leopard, NSMenuItem attributed titles will be truncated by default using the "NSLineBreakByTruncatingMiddle" truncation style. You may override this by specifying a different line break mode for the NSParagraphStyle attached to the attributed title string.

validateItem: no longer called

Historically, AppKit has called the validateItem: method to validate menu items, if validateMenuItem: and validateUserInterfaceItem: were not implemented. This behavior was never documented and it is now disabled for applications built on Leopard. Applications that rely on on validateItem: should switch to validateMenuItem: or validateUserInterfaceItem:.

validateMenuItem: only called on matching key equivalents

Before Leopard, typing a key equivalent would cause AppKit to attempt to validate every menu item in the menu (via validateMenuItem:). As an optimization, AppKit will now only validate the item with the matching key equivalent. Applications should use NSMenu's delegate methods to lazily populate a menu.

Command key = treated like +

As a special case, if no menu item claims ⌘= as a key equivalent, then typing ⌘= will trigger any ⌘+ key equivalent, if the pressed = key is shared with the + key. For example, on a US keyboard layout, pressing the "=" key adjacent to the "-" key on the number row will trigger any ⌘+ key equivalent, but the "=" key above the keypad will not. If the keyboard layout does not have any "+" and "=" characters on the same key, this does not apply.

Key equivalent uniqueing

AppKit tries to ensure that multiple menu items do not have the same key equivalent, because otherwise the user might accidentally invoke one while expecting to invoke the other. This is called key equivalent uniqueing. In Tiger, popup button menus were uniqued with the main menu (and in Leopard, this has been extended to also cover segmented controls). However, menu items that are thought to be aliases of one another can now share the same key equivalent, in Leopard. (Menu items are considered to be an aliases if they have the same action). Thus, in Leopard, you can have "Bold" in a popup button and also in the main menu, and both will show ⌘B as the key equivalent.

Disabled key equivalents passed through

Prior to Leopard, key equivalents corresponding to disabled menu items would be ignored. In Leopard, your application now has a chance to handle these. For example, a key equivalent for control-K on a disabled menu item will no longer block the emacs shortcut in an NSTextView, in Leopard.

ß support in key equivalents

The German sharfes s character (ß) is now supported as a key equivalent.

Menu Item Key Equivalents in non-Roman Scripts

In Tiger and earlier, key equivalents were incorrectly interpreted as follows: Characters were extracted as MacRoman, and the resulting values were interpreted as if they were in the application encoding.  This was not a problem in apps running with the MacRoman character encoding, but would cause problems in other cases. (The default character encoding of apps often depends on the user's localization choice.)

For example, specifying a key equivalent of the backslash @"\" (U+005C) would result in a yen sign key equivalent (U+00A5) when running under a Japanese localization, because the backslash value in MacRoman matches the yen value in MacJapanese: both are 0x5C in the respective character sets.  This was the only way to obtain a yen key equivalent in Tiger.

In Leopard, key equivalents and character sets are handled correctly.  To specify a yen key equivalent, specify the Unicode string containing yen (U+00A5).

For binary compatibility, Leopard will revert to Tiger's behavior for applications linked before Leopard.  When updating your application for Leopard, you should ensure that, when running on Leopard, your key equivalent string contains the characters you want, instead of the MacRoman characters that match your character's code points in the application encoding.

State images implemented

The NSMenuItem methods setOnStateImage:, setOffStateImage:, and setMixedStateImage: now work as documented.

No CMM plugins in 64-bit

NSMenu does not attempt to load Contextual Menu Manager plugins in 64-bit apps. There is no public interface for creating 64-bit CMM plugins in Leopard.

Pre-existing overrides of -[NSMenuItem isHidden]

During testing, some apps were found to have pre-existing implementations of -[NSMenuItem isHidden] that interfered with the new Leopard API of the same name. For compatibility reasons, AppKit will not call -[NSMenuItem isHidden] on apps built on Tiger or earlier. When you build your app on Leopard, AppKit will call your method and you may see strange behavior, such as menus disappearing. We recommend that you do not override -[NSMenuItem isHidden], and remove any existing overrides at the point that you build on Leopard.

Titles of Menus in the Menubar

Consider the application's main menu, an instance of NSMenu. That menu contains NSMenuItems, such as the File menu item, and the File menu item has a submenu of type NSMenu containing items such as New, Open, etc.

In Tiger, the initial label appearing in the menu bar was taken from the submenu's title. But due to a bug, changes to this menu's title did not affect the menu bar; instead, the parent item's title was used instead. So for example, the title of the File submenu determines the initial label, but to change the the label, you must modify the title of the File item itself.

For compatibility, Leopard preserves this behavior for apps built on Tiger. However, apps built on Leopard will find that the menubar consistently reflects the titles of the submenus. The titles of the items themselves in the main menu are ignored. For maximum compatibility with both Tiger and Leopard, you should set both the title of a main menu item and the title of its submenu to the same thing.

This note only applies to items that are within the NSApplication's mainMenu, and their immediate submenus. The titles of other menus have no effect.

-[NSApplication currentEvent] set when menu tracking ends

For applications linked on or after Leopard, -[NSApplication currentEvent] will reflect the event that caused menu tracking to end during the action of the selected menu item.

representedObject in NSPopUpButtonCell and NSMenuItemCell

In Leopard, NSMenuItemCell and NSPopUpButtonCell forward both setRepresentedObject: and representedObject to their current NSMenuItem. (Prior to Leopard, NSMenuItemCell and NSPopUpButtonCell would forward representedObject, but not setRepresentedObject:)

NSPopUpButtonCell setImagePosition:

NSPopUpButtonCell now respects the following image positions: NSImageOnly, NSImageLeft, NSImageRight, NSImageOverlaps. Prior to Leopard, NSPopUpButtonCell always drew with the NSImageLeft position.

Unbordered NSPopUpButtonCells

The layout of the text in unbordered popup button cells has been modified to more closely match that of NSButtonCell. For applications linked before Leopard, the layout will be preserved in some cases.

NSPopupButton Content Placement Tag binding option

The content bindings for NSPopupButton have a new option, Content Placement Tag. This provides a way of mixing static menu items with dynamically generated menu items in a popup. When this option is set to a non-zero integer value, the content bindings will only put dynamic content in place of the one menu item in the popup whose tag value matches the option value.

The typical use for this is to place some dynamically generated menu items in one part of the popup, and the follow or precede them with a menu separator and a static "action" menu item.

NSStatusItem

For consistency with NSCell and NSControl, -[NSStatusItem sendActionOn:] now returns the old mask.


NSToolbar

NSToolbar - Interface Builder support

Interface Builder 3 supports creating toolbars and toolbar items. However, you may wish to provide additional toolbar items programatically. To accomplish this, connect the delegate outlet of the NSToolbar to your desired delegate, and implement the NSToolbar delegate methods normally. The default and allowed item identifiers will be the union of those created in Interface Builder and those returned by your delegate.

NSToolbar - Show/Hide menu items

In versions of Mac OS X prior to Leopard, interface items, including menu items, that had toggleToolbarShown: set as their action would have their titles changed to "Show Toolbar" or "Hide Toolbar." For applications linked on or after Leopard, AppKit will only flip "Show Toolbar" to "Hide Toolbar" (or their localized variations) and vice versa, as appropriate. The empty title will also be modified. Nonempty titles other than the localized variants of these strings must be managed by your application.

NSToolbar - Key equivalents can match in toolbars

Before Leopard, only NSPopUpButtons in toolbars could respond to key equivalents. In Leopard, AppKit now respects the key equivalents of all controls in (visible) toolbars, not just popup buttons.

NSToolbar - NSToolbarItem default sizes

As of Leopard, if you call setView: on an NSToolbarItem without having called setMinSize: or setMaxSize:, the toolbar item will set its minimum and maximum size equal to the view's frame size.

NSToolbar - View archival

In Tiger, NSToolbar would always "copy" your toolbar item's view when displaying it in the customization palette with an NSKeyedArchiver. In Leopard, NSToolbar will only copy your view if you set the view on two different toolbar items at once. For example, say you allocate exactly one view (perhaps in a nib) and always pass it to -[NSToolbarItem setView:] in your toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar: method. If that view is in an active toolbar item, and you display the customization palette, NSToolbar will copy that view once. If that view is not in an active toolbar item and then you display the customization palette, NSToolbar will not copy the view for the customization palette, but will copy that view at the point the user drags the corresponding toolbar item from the palette into the toolbar.

If you always ensure that you pass fresh views into setView:, then NSToolbar will never copy the view.

NSToolbar - mouseDownCanMoveWindow

Previous to Leopard, only textured and unified windows were draggable by their toolbar. In Leopard, all windows are draggable by their toolbars. Apps that placed custom views in toolbars and did not override isOpaque or mouseDownCanMoveWindow may find that their views unexpectedly become draggable in addition to responding to clicks. To prevent this, the mouseDownCanMoveWindow is considered to return NO if all of the following are true:

- The app was linked on Tiger or earlier.
- The view is in a toolbar item.
- The view does not override mouseDownCanMoveWindow.
- The window is not textured or unified title/toolbar.


NSSound

NSSound has additional features on Leopard and later. These include the ability to get the duration of a sound, change the volume of a sound, set and get the playback location within the sound, and cause a sound to loop. See the documentation or comments within the NSSound.h header for more about these new APIs.

In Tiger, NSSound would willingly create a "sound" for any valid Quicktime file, including images. In Leopard, NSSound's init methods will now return nil for files that do not have a sound track.


NSSpeechSynthesizer

NSSpeechSynthesizer now provides complete access to the full power of Apple's synthesizer, including full access to third-party synthesizers compatible with Apple's Speech Synthesis framework.  Developers can now get and set individual synthesizer properties, pause and continue speech, stop speech at specific boundaries, conveniently set rate and volume properties, specify external dictionaries and receive additional callbacks for errors and synchronization events. Cocoa developers that formerly needed to use the Carbon Speech Synthesis API to gain access to certain synthesizer features should now be able to use the NSSpeechSynthesizer class instead.

Also new for Leopard is Alex, an English voice that leverages advanced Apple technology to deliver natural intonation even at very fast speaking rates.


NSViewController

A new class, NSViewController, has been added to the AppKit in Mac OS 10.5. It serves roughly the same purpose as NSWindowController, but for views instead of windows. It:
• Does the same sort of memory management of top-level objects that NSWindowController does, taking the same care to prevent reference cycles when controls are bound to the nib file's owner that NSWindowController began taking in Mac OS 10.4.
• Has a generic "represented object" property, to make it easy for you to establish bindings in the nib to an object that isn't yet known at nib-loading time or readily available to the code that's doing the nib loading.
• Has an implementation of the key-value binding NSEditor informal protocol, so that objects that own view controllers can easily make bound controls in the views commit or discard the changes the user is making.
• Has a "title" property, because we expect this to be used in many situations where the user is given the opportunity to select from several named views.

NSViewController is meant to be very reusable. For example, as described below, NSPageLayout's and NSPrintPanel's -addAccessoryController: methods each take a view controller, and set the represented object to the NSPrintInfo that is to be shown to the user. When the user dismisses a printing panel, NSPageLayout and NSPrintPanel each send NSEditor messages to each accessory view controller to ensure that the user's changes have been committed or discarded properly. The titles of the accessories are gotten from the view controllers and shown to the user in pulldown menus that the user can choose from. If your application needs to dynamically associate relatively complex views with the objects that they present to the user, you might be able to reuse NSViewController in similar ways.

NSViewController is a subclass of NSResponder, but view controllers never make themselves the next responders of the controlled views. It's the responsibility of the object that puts the controlled view in a window to insert the view controller in the responder chain if that would be worthwhile. For example, after getting the view from a view controller and adding it to a superview with -[NSView addSubview:], you can set the subview's next responder to the view controller, and then set view controller's next responder to the superview.


NSPageLayout and NSPrintPanel Accessory View Enhancements

NSPageLayout and NSPrintPanel have always had -setAccessoryView: methods that allow you to specify custom panes to be shown in page setup and print panels, respectively. However, there has been no way to specify the corresponding title that should appear in the panel's pop-up menu of panes. There has also been no way to associate summary text that should be shown for the pane's custom print settings in the print panel, or add more than one such pane to a panel. Lastly, with the introduction of built-in preview to NSPrintPanel in Mac OS 10.5 (described in the next section), we must provide a way for you to trigger the redrawing of the preview when the user changes printing parameters that you present to the user in accessory views.

New methods have been added to the NSPageLayout class to take advantage of the new NSViewController class:
- (void)addAccessoryController:(NSViewController *)accessoryController;
- (void)removeAccessoryController:(NSViewController *)accessoryController;
- (NSArray *)accessoryControllers;
When the page setup panel is presented to the user each accessory controller is automatically sent a -setRepresentedObject: message with this object's NSPrintInfo. Each controller is also automatically sent a -title message. If that returns nil the application's short name is used in the popup menu that lets the user choose an accessory view.

Slightly different new methods have also been added to the NSPrintPanel class:
- (void)addAccessoryController:(NSViewController<NSPrintPanelAccessorizing> *)accessoryController;
- (void)removeAccessoryController:(NSViewController<NSPrintPanelAccessorizing> *)accessoryController;
- (NSArray *)accessoryControllers;
These are very similar to their NSPageLayout equivalents, except the accessory controller must also conform to the new NSPrintPanelAccessorizing protocol, which has just two methods:
- (NSArray *)localizedSummaryItems;
Return the text that summarizes the settings that the user has chosen using this print panel accessory view and that should appear in the summary pane of the print panel. It must be an array of dictionaries (not nil), each of which has an NSPrintPanelAccessorySummaryItemNameKey entry and an NSPrintPanelAccessorySummaryItemDescriptionKey entry whose values are strings. A print panel acccessory view must be KVO-compliant for "localizedSummaryItems" because NSPrintPanel observes it to keep what it displays in its Summary view up to date. (In Mac OS 10.5 there is no way for the user to see your accessory view and the Summary view at the same time, but that might not always be true in the future.)
- (NSSet *)keyPathsForValuesAffectingPreview;
Return the key paths for properties whose values affect what is drawn in the print panel's built-in preview. NSPrintPanel observes these key paths and redraws the preview when the values for any of them change. For example, if you write an accessory view that lets the user turn printing of page numbers on and off in the print panel you might provide an implementation of this method that returns a set that includes a string like @"pageNumbering", as in TextEdit's PrintPanelAccessoryController class. This protocol method is optional because it's not necessary if you're not using NSPrintPanel's built-in preview, but if you use preview you almost certainly have to implement this method properly too.

Because of the requirements of this protocol, which are dictated by the realities of providing a good user interface when customizing the print panel, you must always subclass NSViewController when adding a new-style accessory view to a print panel.

The new NSPrintPanelAccessorySummaryItemNameKey and NSPrintPanelAccessorySummaryItemDescriptionKey strings are for use by implementations of NSPrintPanelAccessorizing's -localizedSummaryItems method.

The -setAccessoryView: and -accessoryView methods are now deprecated in both NSPageLayout and NSPrintPanel.

Also deprecated are NSPageLayout's -readPrintInfo and -writePrintInfo methods, and NSPrintPanel's -updateFromPrintInfo and -finalWritePrintInfo methods. They were never very useful. NSViewController provides the functionality those methods were supposed to provide; you can implement an override of the -view method to make your accessory view's UI consistent with the print info to be presented, and overrides of the KVB -commitEditing and -commitEditingWithDelegate:didCommitSelector:contextInfo: methods to make the presented print info consistent with the values showing in your accessory view.

New NSPrintPanel Methods to Control Preview and Which Standard Controls Appear

in Mac OS 10.5 a new preview feature has been added to the AppKit's printing system. The print panel now has a view that shows the user an image of the pages to be printed. It is on by default, but you can turn it off in your application. See the backward binary compatibility note at the end of this section for more information.

In many applications it is not appropriate to present page setup panels to the user, or even include a Page Setup... item in the File menu, but there has been no other easy way to let the user specify page setup parameters to be used when printing. (New UI advice: if your application doesn't persistently store page setup parameters on a per-document basis, or have some mechanism to associate them with whatever other kind of large-scale objects your application may deal with, it probably shouldn't have a page setup panel at all.) Also, some applications need to present print panels that don't include the standard fields for letting the user specify the number of copies or the range of pages to be printed.

So, In Mac OS 10.5, a new enumeration and new methods have been added to the NSPrintPanel class so that you can control previewing and what standard controls appear in the print panel. Please see the header file and documentation for info on these options.
enum {
NSPrintPanelShowsCopies = 0x01,
NSPrintPanelShowsPageRange = 0x02,
NSPrintPanelShowsPaperSize = 0x04,
NSPrintPanelShowsOrientation = 0x08,
NSPrintPanelShowsScaling = 0x10,
    NSPrintPanelShowsPageSetupAccessory = 0x100,
    NSPrintPanelShowsPreview = 0x20000
};
typedef NSInteger NSPrintPanelOptions;
- (void)setOptions:(NSPrintPanelOptions)options;
- (NSPrintPanelOptions)options;
In Mac OS 10.5 an -options message sent to a freshly-created NSPrintPanel will return (NSPrintPanelShowsCopies | NSPrintPanelShowsPageRange) unless it was created by an NSPrintOperation, in which case it will also return NSPrintPanelShowsPreview. To allow your application to take advantage of controls that may be added by default in future versions of Mac OS X, get the options from the print panel you've just created, turn on and off the flags you care about, and then set the options.

Backward binary compatibility note: The behavior described above applies to applications that are linked against Mac OS 10.5 or later. In any application linked against Mac OS 10.4 or earlier, the NSPrintPanelShowsPreview option is ignored if the application has set a print panel accessory view. Until Mac OS 10.5 there hasn't been any API that would allow you to cause the print preview to be redrawn when the user changes print settings using an accessory view, and if the print preview doesn't redraw then it's showing inaccurate information, and that's worse than no preview at all.

Other NSPrintPanel Enhancements

So that you can change the title of the default button in a print panel from "Print" to something else, two new methods have been added to NSPrintPanel:
- (void)setDefaultButtonTitle:(NSString *)defaultButtonTitle;
- (NSString *)defaultButtonTitle;
The title of the default button in the print panel. You can override the standard button title, "Print," when you're using an NSPrintPanel in such a way that printing isn't actually going to happen when the user presses that button.

So that you can change the help anchor for the question mark button in a print panel to point to something customized for your application, two other new methods have been added to NSPrintPanel:
- (void)setHelpAnchor:(NSString *)helpAnchor;
- (NSString *)helpAnchor;
The HTML help anchor for the print panel. You can override the standard anchor of the print panel's help button.

NSPrintPanel's -runModal method always uses the current NSPrintOperation's NSPrintInfo, so there has been no way to present an application-modal panel that uses any other NSPrintInfo. In Mac OS 10.5 a new method has been added to fix that:
- (NSInteger)runModalWithPrintInfo:(NSPrintInfo *)printInfo;
The default implementation of -runModal now simply invokes [self runModalWithPrintInfo:[[NSPrintOperation currentOperation] printInfo]].

An accessor for the NSPrintInfo that's being presented to the user for editing has also been added:
- (NSPrintInfo *)printInfo;
A simple accessor. Your -beginSheetWithPrintInfo:... delegate can use this so it doesn't have to keep a pointer to the NSPrintInfo elsewhere while waiting for the user to dismiss the print panel.

Access to Underlying Core Printing Objects from NSPrintInfo

In previous versions of Mac OS X there has been no public way to get the Core Printing (previously known as "Printing Manager") objects that an NSPrintInfo is actually built on top of. To make it easy for you do to exactly the same things that the PMPrintSettingsGetValue()/PMPrintSettingsSetValue() functions added in Mac OS 10.4 let you do, a new method has been added to NSPrintInfo in Mac OS 10.5. The most immediate problem that this method solves is that there has been no way for your accessory views to put settings in a place where they can be saved and restored by the printing systems presets mechanism:
- (NSMutableDictionary *)printSettings;
The print info's print settings. You can put values in this dictionary to store them in any preset that the user creates while editing this print info with a print panel. Such values must be property list objects. You can also use this dictionary to get values that have been set by other parts of the printing system, like a printer driver's print dialog extension (the same sort of values that are returned by the Carbon Printing Manager's PMPrintSettingsGetValue() function). Other parts of the printing system often use key strings like "com.apple.print.PrintSettings.PMColorSyncProfileID" but dots like those in key strings wouldn't work well with KVC, so those dots are replaced with underscores in keys that appear in this dictionary, as in "com_apple_print_PrintSettings_PMColorSyncProfileID". You should use the same convention when adding entries to this dictionary.

Because there is a substantial amount of functionality exposed by Core Printing API that is not generally applicable enough to be exposed by AppKit API, new methods have been added to NSPrintInfo in Mac OS 10.5 to allow access to the underlying Core Printing objects:
- (void * /* PMPrintSession */)PMPrintSession;
- (void * /* PMPageFormat */)PMPageFormat;
- (void * /* PMPrintSettings */)PMPrintSettings;
Return a Core Printing PMPrintSession, PMPageFormat, or PMPrintSettings object, respectively. The returned object is always consistent with the state of the NSPrintInfo at the moment the method is invoked, but isn't necessarily updated immediately if other NSPrintInfo methods like -setPaperSize: and -setPaperOrientation: are invoked. The returned object will always be valid (in the Core Printing sense). If you set any values in the returned PMPageFormat or PMPrintSettings you should afterward invoke -updateFromPMPageFormat or -updateFromPMPrintSettings, respectively. You don't also have to call PMSessionValidatePageFormat() or PMSessionValidatePrintSettings() if you do that. You should not call PMRelease() for the returned object, except of course to balance any calls of PMRetain() you do.
- (void)updateFromPMPageFormat;
- (void)updateFromPMPrintSettings;
Given that the NSPrintInfo's PMPageFormat or PMPrintSettings has been changed by something other than the NSPrintInfo itself, updates the NSPrintInfo to be consistent.

NSPrintInfo's Key-Value Coding and Key-Value Observing Compliance

Despite what the comment for -[NSPrintInfo printSettings] in <AppKit/NSPrintInfo.h> says, NSPrintInfo's KVC/KVO-compliance is not really complete enough to be useful in Mac OS 10.5. You can add a key-value observer for a key path to an NSPrintInfo but, for example, the observer won't be consistently notified of changes made by the user when the NSPrintInfo is being presented by an NSPrintPanel.

NSPrintOperation Enhancements

In previous versions of Mac OS X the print job title that the printing system uses has been gotten by invoking -[NSView(NSPrinting) printJobTitle:]. Because it is often not programmatically convenient to make such information available to printed views, new methods have been added to NSPrintOperation in Mac OS 10.5, so that the code that creates the print operation can simply set the print job title right away, and not have to leave it somewhere the printed view can find it:
- (void)setJobTitle:(NSString *)jobTitle;
- (NSString *)jobTitle;
If a job title is set it overrides anything that might be gotten by sending the printed view an [NSView(NSPrinting) printJobTitle] message.

In previous versions of Mac OS X the -currentPage method has allowed you to find out exactly which page is being printed during the running of a print operation, but there has been no easy way to determine how many pages the document being printed has. In Mac OS 10.5 a new method has been added to let you do that:
- (NSRange)pageRange;
The first page number might not be 1, depending on what the printed view returned when sent an -[NSView(NSPrinting) knowsPageRange:] message.

Support for Multiple Page Orientations per Print Operation

In Mac OS 10.5 you can now change the orientation of pages while a job is being printed. To do so your printed view must do its own pagination, and indicate so by returning YES when sent -knowsPageRange: messages. Then, when sent -rectForPage: messages, it may change the orientation of the current print operation's print info, and Cocoa will heed the change. For example: [[[NSPrintOperation currentOperation] printInfo] setOrientation:theNewOrientation].

Deprecated NSPrintOperation Methods, and a Bug Fix in -[NSPrintOperation printPanel]

In Mac OS 10.5 the -setAccessoryView: and -accessoryView methods are now also deprecated in NSPrintOperation.

Also deprecated are the -setJobStyleHint: and -jobStyleHint: methods. Instead of sending one of those messages to an NSPrintOperation, just invoke -[NSPrintOperation printPanel] and send them to the returned NSPrintPanel instead. -[NSPrintOperation printPanel] has been updated to create and return a new print panel if none has been set yet. (It would usually just return nil before.)

Bug Fixes in -[NSPrintInfo paperName], -[NSPrintInfo setPaperName:], and -[NSPrinter pageSizeForPaper:]

In Mac OS 10.2 through Mac OS 10.4 there was a bug in -[NSPrintInfo paperName] that would cause it to return incorrect paper names. For example, it might return "na-letter" or "Letter" when it should return "LetterSmall" (remember, the paper "names" returned by -paperName are not for showing to users; use -localizedPaperName to get those). Likewise, -[NSPrintInfo setPaperName:] could set a paper that had the same size as the named paper but a different name. These problems were particularly noticeable when a printer supports multiple "papers" that have the same sizes but different imageable areas. The bug has been fixed in Mac OS 10.5.

In earlier versions of Mac OS X -[NSPrinter pageSizeForPaper:] only worked when passed strings that are paper names, not paper IDs, in the senses established by the Core Printing API. In Mac OS 10.5 this method now always works when passed paper IDs (the sort of string now always returned by -[NSPrintInfo paperName]). For backward binary compatibility it returns the same values that it would have in Mac OS 10.4 if passed a paper name.

Bug Fixes in NSPrintOperation/NSPrintPanel Cancellation

In earlier versions of Mac OS X there was a bug in which a print job would be sent to the printer even though the user had hit the Cancel button in the print progress panel, if rendering of the last page of the job had already begun. This bug has been fixed in Mac OS 10.5.

There was also a bug in which invoking [[[NSPrintOperation currentOperation] printInfo] setJobDisposition:NSPrintCancelJob] from within overrides of NSView(NSPrinting) methods did not prevent the part of the print job that had already been rendered from being sent to the printer. This bug has also been fixed in Mac OS 10.5. You can now programmatically cancel the current print operation in your overrides of any of the NSView(NSPrinting) methods.

In earlier versions of Mac OS X there was no way to distinguish between user cancellation and failure when invoking -[NSPrintOperation runOperationModalForWindow:delegate:didRunSelector:contextInfo:] or -[NSPrintPanel beginSheetWithPrintInfo:modalForWindow:delegate:didEndSelector:contextInfo:]. In Mac OS 10.5 these methods always set the job disposition of the presented print info to NSPrintCancelJob when the user has cancelled the print panel, just as -[NSPrintOperation runOperation] and -[NSPrintPanel runModal] always have. For backward binary compatibility this is only done if the application is linked against Mac OS 10.4 or earlier. In the case of NSPrintOperation the "presented print info" is the one that can be gotten with -[NSPrintOperation printInfo], not the one that was passed in when the NSPrintOperation was created.

There was a bug in which an NSPrintOperation created with a print info whose job disposition was NSPrintCancelJob would never result in output, regardless of what button the user used to dismiss the print panel. This bug has been fixed.

Advice for Developers Working with Printing Margins

The "Printer Margins" in the Custom Page Sizes panel are confusingly named. They are not margins in the NSPrintInfo sense, or in the sense of Microsoft Word, AppleWorks, or any app that lets the user set a document's page margins. They are the widths and heights of the areas of the page that lie outside of the imageable area for the paper size. In other words, the Custom Page Sizes panel is giving you the opportunity to simulate hardware limitations when defining a new paper size. You can use the -[NSPrintInfo imageablePageBounds] method to get the area of page that is surrounded by the printer margins.


Support for Uniform Type Identifiers (UTIs) in NSDocument-Based Applications

If your application requires Mac OS 10.5, it may stop using these legacy Info.plist keys in its CFBundleDocumentTypes entries:
CFBundleTypeExtensions
CFBundleTypeMIMETypes
CFBUndleTypeOSTypes
NSExportableAs

It may instead use the LSItemContentTypes and (new for Mac OS 10.5) NSExportableTypes keys, and declare the corresponding UTIs as appropriate in its Info.plist. Declaration of UTIs in Info.plist files is described by <http://developer.apple.com/macosx/uniformtypeidentifiers.html>. For each CFBundleDocumentTypes entry that has an LSItemContentTypes subentry, sibling subentries with the keys listed above are ignored. (Actually Cocoa's Info.plist parsing has always ignored CFBundleTypeMIMETypes subentries. In Mac OS 10.5 it continues to. LaunchServices pays attention to them though, except when there's an LSItemContentTypes sibling. So, in Mac OS 10.5 Cocoa and LaunchServices follow the same rules about ignoring LSItemContentTypes siblings.)

If your application uses the LSItemContentTypes key it must also use the NSExportableTypes key where applicable. NSExportableTypes entries serve the same purpose as NSExportableAs entries, and are likewise subentries of CFBundleDocumentTypes entries, but their values are arrays of UTIs instead of arrays of old-style free-form type names.

Use of these keys becomes optional:
CFBundleTypeName
CFBundleTypeIconFile
For each CFBundleDocumentTypes entry that has an LSItemContentTypes subentry you can provide subentries using these keys to override the default icon and kind string that would otherwise result from UTI declarations. As has always been the case, any use of CFBundleTypeName should be accompanied by an entry in the application's InfoPlist.strings files.

The meanings of CFBundleTypeRole and NSDocumentClass subentries have not changed in Mac OS 10.5.

For each CFBundleDocumentTypes entry that has an LSItemContentTypes subentry, the UTIs are used as programmatic type names by NSDocument and NSDocumentController, as described below, instead of the value of any CFBundleTypeName subentry that might also be present.

For backward binary compatibility, none of this new behavior takes affect if the application is linked against Mac OS 10.4 or earlier. A CFBundleDocumentTypes entry that has no LSItemContentTypes subentry at all has the same meaning to Cocoa as in Mac OS 10.4.

Debugging tip: In Mac OS 10.5 you can find out what NSDocumentController thinks it's found in your application's Info.plist by executing 'print-object [[NSClassFromString(@"NSDocumentController") sharedDocumentController] _typeDescriptions]' in gdb (using the Xcode Debugger Console, for instance). Do not attempt to invoke or override -_typeDescriptions in your application. It is there just for this debugging purpose, and may disappear at any time.

Shipping tip: If you want to take advantage of UTIs in your application but have it still run on both Mac OS 10.4 and Mac OS 10.5, you can merely add LSItemContentTypes (and NSExportableTypes, where appropriate) subentries to your Info.plist, and then update your overrides of NSDocumentController and NSDocument methods so that they recognize UTIs as type names in addition to the sort of document types names that were always used in Mac OS 10.4. Be careful to add LSItemContentTypes subentries to all of the app's CFBundleDocumentTypes Info.plist entries though, or things could get more complicated. See the description of -typeForContentsOfURL:error:'s new behavior down below for instance.

Support for UTIs in NSDocumentController

In Mac OS 10.5 NSDocumentController supports UTIs. In the following paragraphs an "app-declared" UTI is one that is declared in a well-formed LSItemContentTypes subentry of a well-formed CFBundleDocumentTypes Info.plist entry, in an application that is linked against Mac OS 10.5 or later. A "recognized" UTI is app-declared or conforms to one of the app-declared UTIs.

None of the NSDocumentController methods that were deprecated in Mac OS 10.4 have been updated to handle UTIs properly. You have to stop invoking and overriding deprecated methods to take advantage of UTIs in your application.

-makeUntitledDocumentOfType:error:, -makeDocumentWithContentsOfURL:ofType:error:, and -makeDocumentForURL:withContentsOfURL:ofType:error: all now accept recognized UTIs as type strings, in addition to the sort of document type names that were accepted in Mac OS 10.4. The latter two methods have also been updated to properly handle unrecognized types too. (Because -typeForContentsOfURL:error: might now return an urecognized type.)

-URLsFromRunningOpenPanel now, for each CFBundleDocumentTypes entry that declares an openable document type (as in, has a CFBundleTypeRole of Editor or Viewer, as in Mac OS 10.4) with app-declared UTIs, passes those UTIs into -runModalOpenPanel:forTypes:. For each CFBundleDocumentTypes that declares an openable document without recognized UTIs it still passes file name extensions and encoded HFS file types as in Mac OS 10.4.

-runModalOpenPanel:forTypes: nows accepts all valid UTIs in the array of type strings, in addition to the file name extensions and encoded HFS file types that were accepted in Mac OS 10.4.

-defaultType chooses a CFBundleDocumentTypes entry and considers that document type to be the default type, as in Mac OS 10.4. Once it has made that choice it now returns the first app-declared UTI for that type, if there are any, or a document type name, of the sort returned in Mac OS 10.4, if not. (So the order of elements in the LSItemContentTypes array does matter, just as the order of elements in the CFBundleDocumentTypes array always has.)

-typeForContentsOfURL:error: now uses -[NSWorkspace typeOfFile:error:] to find out the UTI of the file located by the URL. (So it might return a UTI that is not recognized. AppKit's own invocations of it take that fact into account.) For backward binary compatibility it however first tries the same thing that it did in Mac OS 10.4 (invoke -typeFromFileExtension:, possibly twice, passing an HFS file type string for the second invocation) if there are any CFBundleDocumentTypes Info.plist entries that don't have LSItemContentTypes subentries.

-documentClassForType: now accepts any recognized UTI as the type string. As in Mac OS 10.4 it sends a +readableTypes message to each class named in the results of invoking -documentClassNames, and returns the first class whose readable types include one that matches the passed-in type. "Matches" for a UTI means "conforms to," instead of merely "is equal to." (So this method will work even when passed a UTI that is not explicitly declared in the application's Info.plist, but conforms to one that is.)

-displayNameForType: now accepts all valid UTIs as the type string, in addition to the sort of document type names that were accepted in Mac OS 10.4. It returns nil when passed an invalid UTI. For backward binary compatibility it continues to not return nil when passed a non-UTI.

-fileExtensionsFromType: and -typeFromFileExtension: have not changed, but are being deprecated. -fileExtensionsFromType: does not work when passed a UTI. -typeFromFileExtension: only works when passed a file name extension used in a CFBundleDocumentTypes entry that does not have an LSItemContentTypes subentry. In general, if each of the application's CFBundleDocumentTypes Info.plist entries has a valid LSItemContentTypes subentry, and the application doesn't invoke deprecated methods like -fileNamesFromRunningOpenPanel, then these methods will never be invoked from within Cocoa.

Support for UTIs in NSDocument

In Mac OS 10.5 NSDocument's handling of types is consistent with NSDocumentController. Every non-deprecated NSDocument method that takes a type: or ofType: argument now accepts recognized UTIs as type strings, in addition to the sort of document type names that were accepted in Mac OS 10.4. -setFileType: and -fileType: don't actually interpret the file type name in any way, so they work fine with UTIs.

None of the NSDocument methods that were deprecated in Mac OS 10.4 have been updated to handle UTIs properly. You have to stop invoking and overriding deprecated methods to take advantage of UTIs in your application.

+readableTypes, +writableTypes, and -writableTypesForSaveOperation: all now return UTIs for CFBundleDocumentTypes entries with app-declared UTIs, and document types of the sort that were returned in Mac OS 10.4 for those that don't.

+isNativeType: now accepts all valid UTIs as the type string, in addition to the sort of document type names that were accepted in Mac OS 10.4. It will return YES if a passed-in UTI conforms to a recognized UTI for the document class in question, NO otherwise.

A new method has been added to NSDocument, because -[NSDocumentController fileExtensionsFromType:] is being deprecated:
- (NSString *)fileNameExtensionForType:(NSString *)typeName saveOperation:(NSSaveOperationType)saveOperation;
For a specified type, and a particular kind of save operation, return a file name extension that can be appended to a base file name. The default implementation of this method invokes [[NSWorkspace sharedWorkspace] preferredFilenameExtensionForType:typeName] if the type is a UTI or, for backward binary compatibility with Mac OS 10.4 and earlier, invokes [[NSDocumentController sharedDocumentController] fileExtensionsFromType:typeName] and chooses the first file name extension in the returned array if not.

You can override this method to customize the appending of extensions to file names by NSDocument. In Mac OS 10.5 it's only invoked from two places within Cocoa: 1) -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo: uses this method when creating a new file name for the autosaved contents. 2) -[NSDocument(NSScripting) handleSaveScriptCommand:] uses this method when adding an extension to the file name specified by a script. In all other cases the name of any file being saved will have been fully specified by the user, with the save panel (whether they know it or not).

Support for UTIs in NSPersistentDocument

In Mac OS 10.5 NSPersistentDocument's handling of types is consistent with NSDocumentController's and NSDocument's. Every NSPersistentDocument method that takes a type string argument now accepts recognized UTIs, in addition to the sort of document type names that were accepted in Mac OS 10.4.

Support for UTIs in NSOpenPanel

In Mac OS 10.5 NSOpenPanel supports UTIs. The following methods all now accept all valid UTIs as type strings, in addition to the file name extensions and encoded HFS file types that were accepted in Mac OS 10.4:
-beginSheetForDirectory:file:types:modalForWindow:modalDelegate:didEndSelector:contextInfo:
-beginForDirectory:file:types:modelessDelegate:didEndSelector:contextInfo:
-runModalForDirectory:file:types:
-runModalForTypes:
NSOpenPanel will let the user choose files whose types conform to those identified by the passed-in UTIs. So, you can let the user select any image file by passing in a UTI like public.image. Be aware however that the set of types conforming to another can be extended by any application installed on the computer, so this might not be a good idea if your application actually has to open the files the user chooses with the open panel. Typically you'll pass in UTIs for more concrete types, like public.tiff, com.adobe.pdf, or com.apple.sketch2.

Support for UTIs in NSSavePanel

In Mac OS 10.5 NSSavePanel supports UTIs. The following methods now accept or return all valid UTIs as type strings, in addition to the file name extensions that were accepted and returned in Mac OS 10.4 (encoded HFS file types have never been valid values for these methods):
-setAllowedFileTypes:
-setRequiredFileType:
-allowedFileTypes:
-requiredFileType:

Support for UTIs in NSWorkspace

In Mac OS 10.5 NSWorkspace supports UTIs. -iconForFileType: now accepts all valid UTIs as type string, in addition to the file name extensions and encoded HFS file types that were accepted in Mac OS 10.4.

Several methods have been added to NSWorkspace:
- (NSString *)typeOfFile:(NSString *)absoluteFilePath error:(NSError **)outError;
Given an absolute file path, return the uniform type identifier (UTI) of the file, if one can be determined. Otherwise, return nil after setting *outError to an NSError that encapsulates the reason why the file's type could not be determined. If the file at the end of the path is a symbolic link the type of the symbolic link itself will be returned, not the type of the linked file. You can invoke this method to get the UTI of an existing file.
- (NSString *)localizedDescriptionForType:(NSString *)typeName;
Given a UTI, return a string that describes the document type and is fit to present to the user, or nil for failure. You can invoke this method to get the name of a type that must be shown to the user, in an alert about your application's inability to handle the type, for instance.
- (NSString *)preferredFilenameExtensionForType:(NSString *)typeName;
Given a UTI, return the best file name extension to use when creating a file of that type, or nil for failure. You can invoke this method when your application has only the base name of a file that's being written and it has to append a file name extension so that the file's type can be reliably identified later on.
- (BOOL)filenameExtension:(NSString *)filenameExtension isValidForType:(NSString *)typeName;
Given a file name extension and a UTI, return YES if the file name extension is a valid tag for the identified type, NO otherwise. You can invoke this method when your application needs to check if a file name extension can be used to reliably identify the type later on. For example, NSSavePanel uses this method to validate any extension that the user types in the panel's file name field.
- (BOOL)type:(NSString *)firstTypeName conformsToType:(NSString *)secondTypeName;
Given two UTIs, return YES if the first "conforms to" to the second in the uniform type identifier hierarchy, NO otherwise. This method will always return YES if the two strings are equal, so you can also use it with other kinds of type name, including those declared in CFBundleTypeName Info.plist entries in apps that don't take advantage of the support for UTIs that was added to Cocoa in Mac OS 10.5. You can invoke this method when your application must determine whether it can handle a file of a known type, returned by -typeOfFile:error: for instance. Use this method instead of merely comparing UTIs for equality.

Support for UTIs in NSPasteboard

In Mac OS 10.5 NSPasteboard supports UTIs. Every NSPasteboard method that takes a type string or type string array argument now accepts UTIs as type strings, in addition to the sort of pasteboard type names that were accepted in Mac OS 10.4.

-types now returns an array that contains UTIs, as well as the pasteboard type names that would be returned in Mac OS 10.4.

When one of your application's pasteboard owners' -pasteboard:provideDataForType: methods is invoked it will still always be passed the same string that was specified in the promising invocation of -declareTypes:owner: or -addTypes:owner.

When -availableTypeFromArray: encounters a UTI in the type array provided to it, it will return that UTI if the exact UTI exists anywhere in the pasteboard's array of types. If no pasteboard type matches the UTI exactly, the first type on the pasteboard that conforms to the UTI will be returned.

Support for UTIs in NSView and NSWindow

Likewise, in Mac OS 10.5 NSView and NSWindow support UTIs. NSView and NSWindow's -registerForDraggedTypes: methods now accept UTIs as type strings, in addition to the sort of pasteboard type names that were accepted in Mac OS 10.4. NSView's -dragPromisedFilesOfTypes:fromRect:source:slideBack:event: method now accepts UTIs as type strings, in addition to the sort of file name extensions that were accepted in Mac OS 10.4.

For UTIs registered as dragged types, UTI conformance is checked instead of equality to determine if a dragging destination should be given a chance to handle a drag. For example, a view with the UTI kUTTypeImage registered as a dragged type will have its dragging destination methods called to handle a drag in its bounds when the dragging pasteboard contains any type that conforms to kUTTypeImage.

Support for UTIs in Services

You can now specify declared UTIs instead of pasteboard types as the elements of the NSSendTypes or NSReturnTypes arrays in the Services declaration part of an application's Info.plist.

Support for UTIs in Miscellaneous AppKit Classes

In earlier versions of Mac OS X, these four classes:
NSImage
NSImageRep
NSSound
NSAttributedString (in the NSAttributedStringKitAdditions category)

have all had pairs of methods that return either arrays of file type strings (file name extensions and encoded HFS file types) or pasteboard type strings. In Mac OS 10.5, these methods are joined by single new methods that just return arrays of UTIs. Also, NSImageRep's methods that take file type or pasteboard type strings are joined by a new method that just takes UTIs.

It's important to keep in mind when working with UTIs that mere string equality checking is not the correct way to check if the type identified by one UTI "conforms" to the type identified by another. See the description of the -[NSWorkspace type:conformsToType:] in the "Support for UTIs in NSWorkspace" section.

The follow sections contain the details of this change for each class.

Support for UTIs in NSImage

In NSImage, these new methods:
+ (NSArray *)imageTypes;
+ (NSArray *)imageUnfilteredTypes;
join these methods, which might be deprecated in a future release of Mac OS X, but are not yet:
+ (NSArray *)imageFileTypes;
+ (NSArray *)imagePasteboardTypes;
+ (NSArray *)imageUnfilteredFileTypes;
+ (NSArray *)imageUnfilteredPasteboardTypes;
(The old methods are not yet deprecated because you might still have a reason to override them, because the -initWithContentsOfFile:, -initWithContentsOfURL:, -initByReferencingFile:, -initByReferencingURL:, -initWithPasteboard:, and +canInitWithPasteboard: methods have not yet been updated to use UTIs when deciding which subclass of NSImageRep should be instantiated. The same is true of -[NSBundle(NSBundleImageExtension) pathForImageResource:].)

Support for UTIs in NSImageRep

In NSImageRep, these new methods:
+ (Class)imageRepClassForType:(NSString *)type;
+ (NSArray *)imageTypes;
+ (NSArray *)imageUnfilteredTypes;
join these methods, which might be deprecated in a future release of Mac OS X, but are not yet:
+ (Class)imageRepClassForFileType:(NSString *)type;
+ (Class)imageRepClassForPasteboardType:(NSString *)type;
+ (NSArray *)imageFileTypes;
+ (NSArray *)imagePasteboardTypes;
+ (NSArray *)imageUnfilteredFileTypes;
+ (NSArray *)imageUnfilteredPasteboardTypes;
(The old methods are not yet deprecated because you might still have a reason to override them, because the +imageRepsWithContentsOfFile:, +imageRepWithContentsOfFile:, +imageRepsWithContentsOfURL:, +imageRepWithContentsOfURL:, +imageRepsWithPasteboard:, +imageRepWithPasteboard:, and +canInitWithPasteboard: methods have not yet been updated to use UTIs when deciding which subclass of NSImageRep should be instantiated, or whether a subclass can be instantiated, in the case of the last method.)

Support for UTIs in NSSound

In NSSound, this new method:
+ (NSArray*)soundUnfilteredTypes;
replaces these deprecated methods:
+ (NSArray *)soundUnfilteredFileTypes;
+ (NSArray *)soundUnfilteredPasteboardTypes;

Support for UTIs in AppKit's NSAttributedStringKitAdditions Category on NSAttributedString

In NSAttributedString(NSAttributedStringKitAdditions), these new methods:
+ (NSArray *)textTypes;
+ (NSArray *)textUnfilteredTypes;
replace these deprecated methods:
+ (NSArray *)textFileTypes;
+ (NSArray *)textPasteboardTypes;
+ (NSArray *)textUnfilteredFileTypes;
+ (NSArray *)textUnfilteredPasteboardTypes;
The -initWithURL:options:documentAttributes:error:, -initWithPath:documentAttributes:, and -initWithURL:documentAttributes: methods have all been updated to use UTIs when appropriate. So have NSMutableAttributedString(NSMutableAttributedStringKitAdditions)'s -readFromURL:options:documentAttributes:error: and -readFromURL:options:documentAttributes: methods.


Rewritten NSDocument Safe Saving, and a Bug Fixing for Saving Documents That Change From Plain Files to File Packages

-[NSDocument writeSafelyToURL:ofType:forSaveOperation:error:] has been rewritten to use CarbonCore's new FSPathReplaceObject() function. Some kinds of metadata, like extended attributes and access control lists, will now more often be properly preserved during document saving, especially of file packages. Also, safe document saving is now a little safer, particularly when the disk being written to is full. For example, your users will no longer see their documents get renamed with a "~" on the end, and left that way, when document saving fails because there is not enough space on disk to save a new document revision.

In Mac OS 10.4 and earlier there was a bug in which NSDocument would malfunction when a document that was a plain file on disk was overwritten with a file package of the same name during a save operation. (Some applications use the same file name extension for both the flat-file and the directory-based variants of what is conceptually, as far as the user is concerned, the same file format.) This bug has been fixed in Mac OS 10.5.

NSDocument Checking for Modified Files At Saving Time

In Mac OS 10.5 -[NSDocument saveDocumentWithDelegate:didSaveSelector:contextInfo:] now checks to see if the document's file has been modified since the document was opened or most recently saved or reverted, in addition to the checking for file moving, renaming, and trashing that it has done since Mac OS 10.1. When it senses file modification it presents an alert telling the user "This document’s file has been changed by another application since you opened or saved it," giving them the choice of saving or not saving. For backward binary compatibility this is only done in applications linked against Mac OS 10.5 or later.

When updating your application to link against Mac OS 10.5, keep in mind that it's usually more appropriate to invoke one of NSDocument's -save… methods in your application code than one of the -write… methods. The -write… methods are there primarily for you to override. -saveToURL:ofType:forSaveOperation:error:, which is the method that's meant to always be invoked during document saving, invokes -setFileModificationDate: with the file's new modification date after it's been written (for NSSaveOperation and NSSaveAsOperation only).

Likewise, it's usually more appropriate to invoke one of NSDocument's -revert… methods in your application code code than one of the -read… methods. The -read… methods are there primarily for you to override. -revertToContentsOfURL:ofType:error:, which is the method that's meant to always be invoked during rereading of an open document, invokes -setFileModificationDate: with the file's modification date after it's been read.

Bug Fix in -[NSDocument isDocumentEdited], and New Constant Used with -[NSDocument updateChangeCount:]

In previous versions of Mac OS X there has been a bug in which saving a document, undoing changes, and then making an equal number of new changes would cause the document to appear unmodified. If the user closed the document it would simply be closed, with no warning about unsaved changes, which would be lost. This happened in any application in which the document did not send [self updateChangeCount:NSChangeCleared] during document saving (which you're not supposed to have to do). This bug has been fixed in Mac OS 10.5, by virtue of NSDocument now drawing a distinction between doing and redoing of changes. It no longer invokes [self updateChangeCount:NSChangeDone] when it receives an NSUndoManagerDidRedoChangeNotification. Now it invokes [self updateChangeCount:NSChangeRedone] instead. (NSChangeDone is still used for NSUndoManagerWillCloseUndoGroupNotification.) NSChangeRedone is new, and declared in <AppKit/NSDocument.h>.

For backward binary compatibility NSDocument only uses NSChangeRedone instead of NSChangeDone in applications linked against Mac OS 10.5 or later, or if -updateChangeCount: is not overridden.

Bug Fix in NSDocument for Nested Undo Manager Groups

In previous versions of Mac OS X there was a bug in which NSDocument's undo support did not take into account nested undo manager groups. Each NSDocument would merely send itself a [self updateChangeCount:NSChangeDone] message whenever it received an NSUndoManagerWillCloseUndoGroupNotification from its undo manager. This would cause it to improperly count how many changes the user had made so that, for example, making one undoable change represented by multiple actions in nested undo groups, and then undoing that change, would still show the document as modified. This problem was particularly noticeable in document-based Core Data applications, because NSManagedObjectContext uses nested undo manager groups. This has been fixed in Mac OS 10.5. NSDocument now checks the undo manager's grouping level when it receives NSUndoManagerWillCloseUndoGroupNotification, and only invokes -updateChangeCount: if the nesting level is less than two.

Bug Fix in -[NSDocument writeToURL:ofType:error:]'s Use of NSFileWrapper

In Mac OS 10.4, -[NSDocument writeToURL:ofType:error:] passed NO as the last argument when invoking -[NSFileWrapper writeToFile:atomically:updateFilenames:]. In Mac OS 10.5 it now passes YES so that the file wrapper and any file wrappers it contains have their file names updated during saving. -[NSDocument writeToFile:ofType:], which was deprecated in Mac OS 10.4, has not been updated in a similar way.

Bug Fix in NSDocument's Use of -[NSDocument autosavingFileType]

In Mac OS 10.4, -[NSDocument autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo:] would invoke [self autosavingFileType] the first time a document was autosaved, create an autosaved contents file URL using the implied file name extension, and then keep using that URL for all subsequent autosaves. If -autosavingFileType was overridden to return different values at different times, this could result in inconsistencies. For example, the new version of TextEdit in Leopard that uses autosaving could end up autosaving an RTFD file package with an ".rtf" file name extension. (Because it overrides -autosavingFileType to account for attachments being added to the document.) This was a bug, and has been fixed in Mac OS 10.5. NSDocument now consistently uses the result of invoking -autosavingFileType when determining where to autosave.

Advice for Overriders of -[NSDocument autosavingFileType]

Even with the bug fixed mentioned above, overriding -autosavingFileType can result in incorrect behavior during reopening of autosaved documents if you're not careful. -[NSDocument initForURL:withContentsOfURL:ofType:error:], which is invoked during reopening of autosaved documents after a crash, takes two URLs, but only the type name of the autosaved contents file. The default implementation invokes [self setFileType:] with that type name, but that is often not the right thing to do, if -autosavingFileType had returned something other than -fileType during document autosaving. If you override -autosavingFile, you probably have to override -initForURL:withContentsOfURL:ofType:error: too, and make the override invoke -setFileType: with the type of the actual document file, after invoking super. See TextEdit's Document class for an example of how to do this.

Advice for Overriders of NSDocument Reading and Writing Methods

If you subclass NSDocument and override any of these methods for reading:

-readFromData:ofType:error:
-readFromFileWrapper:ofType:error:
-readFromURL:ofType:error:

Or any of these for writing:

-dataOfType:error:
-fileWrapperOfType:error:
-writeToURL:ofType:error:
-writeToURL:ofType:forSaveOperation:originalContentsURL:error:
-fileAttributesToWriteToURL:ofType:forSaveOperation:originalContentsURL:error:
-writeSafelyToURL:ofType:forSaveOperation:error:

Or any of the methods that were deprecated in favor of them, in Mac OS 10.4:

-dataRepresentationOfType:
-fileAttributesToWriteToFile:ofType:saveOperation:
-fileWrapperRepresentationOfType:
-initWithContentsOfFile:ofType:
-initWithContentsOfURL:ofType:
-loadDataRepresentation:ofType:
-loadFileWrapperRepresentation:ofType:
-readFromFile:ofType:
-readFromURL:ofType:
-writeToFile:ofType:
-writeToFile:ofType:originalFile:saveOperation:
-writeToURL:ofType:
-writeWithBackupToFile:ofType:saveOperation:

Don't invoke -fileURL (or -fileName, the method that was deprecated in favor if it, in Mac OS 10.4), -fileType, or -fileModificationDate from within your overrides. During reading, which typically happens during object initialization, there is no guarantee that NSDocument properties like the file's location or type have been set yet. Your overridden method should be able to determine everything it needs to do the reading from the passed-in parameters. During writing, your document may be being asked to write its contents to a different location, or using a different file type. Again, your overridden method should be able to determine everything it needs to do the writing from the passed-in parameters.

If your override cannot determine all of the information it needs from the passed-in parameters, consider overriding another method. For example, if you see the need to invoke -fileURL from within an override of -readFromData:ofType:error:, perhaps you should instead override -readFromURL:ofType:error:. For another example, if you see the need to invoke -fileURL from within an override of -writeToURL:ofType:error:, perhaps you should instead override -writeToURL:ofType:forSaveOperation:originalContentsURL:error:.

Advice for Overriders of -[NSDocument displayName]

Some applications have subclasses of NSDocument that override -displayName to customize the the titles of windows associated with the document. That is usually not the right thing to do. Use a subclass of NSWindowController, and override -[NSWindowController windowTitleForDocumentDisplayName:] instead. If even deeper customization is required override -[NSWindowController synchronizeWindowTitleWithDocumentName]. A document's display name is used in several other places where the custom value that an application might want to use as a window title is typically not appropriate:
- In error alerts that may be presented during reverting, saving, or printing of the document.
- In alerts that will be presented during document saving if the document has been moved, renamed, or put in the trash.
- In the alert that will be presented when the user attempts to close the document with unsaved changes.
- As the default value shown in the "Save As:" field of save panels.
It may be used in even more places in future releases of Mac OS X.

Advice for Overriders of -[NSDocument init]

If you subclass NSDocument and override -init, make sure your override never returns nil. When running on Mac OS 10.4 this will likely cause a crash in AppKit. In Mac OS 10.5 it will cause AppKit to present an error alert that is not very useful to the user (for example, "No document could be created."). If, for example, you want to prevent the creation or opening of documents under circumstances unique to your application, override a specific NSDocumentController method instead. Read the next section before doing so.

Advice for Overriders of Methods That Return NSErrors by Reference

In Mac OS 10.4 we published many new methods that return NSErrors. For example, in NSDocumentController:
- (id)openUntitledDocumentAndDisplay:(BOOL)displayDocument error:(NSError **)outError;
- (id)openDocumentWithContentsOfURL:(NSURL *)absoluteURL display:(BOOL)displayDocument error:(NSError **)outError;
etc. When overriding such methods take care to follow this rule: a method that takes an error:(NSError **)outError argument must, if it returns a value that signals failure (typically nil or NO), and if outError!=NULL, set the value of *outError to point to an NSError. It is not the responsibility of code that invokes such methods to nil-initialize the variable whose address is taken and passed as the error parameter, just so it can safely check to see if the variable's value is no longer nil after the invocation.

If you're overriding such a method to prevent some action, but you don't want an error alert to be presented to the user, return an error whose domain is NSCocoaErrorDomain and whose code is NSUserCancelledError. AppKit itself consistently presents NSErrors to the user with the machinery described at <http://developer.apple.com/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/index.html>. Unless your application overrides AppKit's error presentation methods in novel ways, use of this machinery consistently results in invocations of -[NSApplication presentError:] or -[NSApplication presentError:modalForWindow:delegate:didPresentSelector:contextInfo:]. Both of these methods silently ignore NSCocoaErrorDomain/NSUserCancelledError errors. So, for example:
- (id)openDocumentWithContentsOfURL:(NSURL *)absoluteURL display:(BOOL)displayDocument error:(NSError **)outError {
    /* The user double-clicked on a document in the Finder or something, but we don't want to
open it yet if our application's custom licensing panel (for example) is being shown
as an application-modal dialog right now.
*/
id openedDocument = nil;
if (_licensingPanelIsShown) {
/* Defer the opening of the document until the user has dismissed the licensing panel.
*/
    ... Left as an exercise to the reader ...
        /* We're about to return nil, so we _must_ set *outError to something, unless of course outError is NULL.
Return an error that won't result in the presentation of an error alert. Regular Cocoa memory
management rules dictate that the invoker of this method is not responsible
for releasing the NSError, but of course +[NSError error:code:userInfo:] returns an autoreleased
object, so this is all correct.
*/
if (outError) {
*outError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil];
}
} else {
/* Just do the regular Cocoa thing. We don't have to touch outError here.
NSDocumentController's implementation of this method has
to follow the rules too, so it sets *outError if it returns nil and outError!=NULL.
*/
openedDocument = [super openDocumentWithContentsOfURL:absoluteURL display:displayDocument error:outError];
}
return openedDocument;
}

Advice for Overriders of Methods that Follow the delegate:didSomethingSelector:contextInfo: Pattern

There are methods in the AppKit, especially in the NSDocument and DocumentController classes, that all have pretty much the same three parameters:
- A delegate object to be notified when the method's operation has been completed.
- The selector of a method to invoke to do the notifying.
- A "context info," which is just a value to pass back to the delegate so it can continue with a larger overall operation, free memory, etc.

Each method is this way because a sheet may be shown during the operation performed by the method. Such methods have to return before the user has dismissed the sheet (because of the way user event dispatching is done in the AppKit), when the result of the operation is still unknown. The delegate:didSomethingSelector:contextInfo: pattern is used so that the result can be passed to the object that requested the operation when the result is finally known.

You may discover a need to override one of these methods in your application. This is easy when the override has to add some custom behavior before invoking the superclass' implementation, but it's really not obvious how to write the override when the custom behavior goes after invoking the superclass' method. Here's an example of how to do that, in a subclass of NSDocument:
- (void)canCloseDocumentWithDelegate:(id)delegate
shouldCloseSelector:(SEL)shouldCloseSelector
contextInfo:(void *)contextInfo {
    /* No matter what happens, the original delegate must be messaged (to prevent memory leaks, at the
very least). Because we're not going to pass the passed-in parameters to super, we have
to record them somewhere. The easy place to record them is in the NSInvocation we're going
to create anyway to message the original delegate. The method selected by shouldCloseSelector
must have the same signature as...
           - (void)document:(NSDocument *)document shouldClose:(BOOL)shouldClose contextInfo:(void *)contextInfo;
        ...and that dictates how we build our invocation. We don't set a value for the shouldClose:
argument (atIndex:3) because we don't know the value yet.
*/
NSInvocation *originalDelegateInvocation = [NSInvocation invocationWithMethodSignature:
[delegate methodSignatureForSelector:shouldCloseSelector]];
[originalDelegateInvocation setTarget:delegate];
[originalDelegateInvocation setSelector:shouldCloseSelector];
[originalDelegateInvocation setArgument:&self atIndex:2]; // document:
[originalDelegateInvocation setArgument:&contextInfo atIndex:4]; // contextInfo:
    /* Do the regular NSDocument thing, arranging to take back control afterward. We must retain
the invocation object here because contextInfo: arguments are not automatically retained.
*/
[super canCloseDocumentWithDelegate:self
shouldCloseSelector:@selector(thisDocument:shouldClose:contextInfo:)
contextInfo:[originalDelegateInvocation retain]];
}
- (void)thisDocument:(NSDocument *)document shouldClose:(BOOL)shouldClose contextInfo:(void *)contextInfo {
NSInvocation *originalDelegateInvocation = (NSInvocation *)contextInfo;
    // Is the document about to be closed?
if (shouldClose) {
        // Here we can do all sorts of things with this document that's about to be closed.
    }
    /* A little bit of UI advice: changing the value of shouldClose here might result in confusing
behavior. For example, if the user hit the Save button in a "Do you want to save the changes..."
panel, and the save succeeded, and shouldClose is YES, canceling closing by changing
shouldClose to NO before messaging the delegate would be an odd thing to do.
*/
    // Tell the original delegate that the decision to close this document or not has been made.
[originalDelegateInvocation setArgument:&shouldClose atIndex:3];
[originalDelegateInvocation invoke];
    // Balance the retain we did up above.
[originalDelegateInvocation release];
}

Bug Fix for NSDocument/NSPersistentDocument Quitting-Time Hang When Using Bindings

In Mac OS 10.4 there was a bug in NSDocument that would cause an application to hang if:
- A document window has a control with a binding to a property of the document or, in a CoreData app, a managed object in the persistent document's managed object context.
- The user edits using the control but does not cause the editing to be committed (as in, types in an edit field but does not hit tab).
- The user tries to the quit the application.
- The user hits the Save button in the "Do you want to save the changes...?" alert that's presented.
This bug has been fixed in Mac OS 10.5.

New Behavior in NSWindowController at Window Closing Time

In previous versions of Mac OS X, each NSWindowController registered for NSWindowWillCloseNotification from its window and then did a number of things when it got the notification if it had a document (like removing itself from the document's window controllers and trying to close document). This way of doing things caused several problems, including:
• Other recipients of NSWindowWillCloseNotification would find that the window controller already had no document when they received their notification.
• The NSWindowController would not get the NSWindowWillCloseNotification, and the document would not be closed, if the window controller had been the window's delegate and then the window's delegate was reset (because of the way -[NSWindow setDelegate:] deregisters the previous delegate as an observer of notifications).

In Mac OS 10.5 an NSWindow that has a window controller now sends it a private message when the window did (not will) close, fixing those and other problems. NSWindowControllers no longer register for or depend on receiving NSWindowWillCloseNotification.

Also, a change that's similar to one in -[NSWindowController dealloc] has been made, and the window controller itself is now released instead of autoreleased (to balance a [self retain] that the window controller does before sending -removeWindowController:self to its document). For backward binary compatibility the new behavior is only done in applications linked against Mac OS 10.5 or later.

Bug Fixes in -[NSWindowController dealloc]

-[NSWindowController setWindow:] makes the window control the next responder of the window, which is as designed. However, this could result in the NSWindow messaging the NSWindowController's zombie, depending on which object was deallocated first. In Mac OS 10.5, -[NSWindowController dealloc] fixes this problem by sending the window -setNextResponder:nil if the window controller has a window and is its next responder.

In previous versions of Mac OS X, -[NSWindowController dealloc] autoreleased the window and the top level objects from the nib it loaded. This made it needlessly difficult to debug programming mistakes in applications. In Mac OS 10.5 -[NSWindowController dealloc] now releases the window and top level objects instead. For backward binary compatibility the new behavior is only done in applications linked against Mac OS 10.5 or later.

Advice for People Setting up Nibs that Have NSWindowController File's Owners

NSWindowController has never depended on being the controlled window's delegate to do its job. NSWindowController doesn't even implement any NSWindow delegate methods. A subclass of NSWindowController is a fine place to put implementations of NSWindow delegate methods, and if you do so you'll probably need to connect the delegate outlet of a window in a nib to the nib file's owner, an instance of your NSWindowController subclass, but you do not have to do so on NSWindowController's account.

New Behavior for NSWindowController Frame Autosave Names

In previous versions of Mac OS X, -[NSWindowController setWindow:], which is typically invoked at nib-loading time, always set the frame autosave name of the window to the window controller's own frame autosave name. This was a significant inconvenience because it meant that the name you set in the window using Interface Builder was always effectively ignored. In Mac OS 10.5 -[NSWindowController setWindow:] now only sets the window's frame autosave name if its own is something other than nil or the empty string. This means that a window's frame autosave name is now useful even when the window is loaded by a window controller. You can still explicitly invoke -[NSWindowController setWindowFrameAutosaveName:] if for some reason you need to override the window's frame autosave name at runtime. For backward binary compatibility the new behavior is only done in applications linked against Mac OS 10.5 or later.


NSSplitView Enhancements

Several enhancements have been made to NSSplitView in Mac OS 10.5. For detailed information on all of the methods mentioned here, see the comments in <AppKit/NSSplitView.h>.

So that you can programmatically set the position of a divider to an arbitrary position, a new -setPosition:ofDividerAtIndex: method has been added.

So that you can programmatically query the range of useful values that can be passed to -setPosition:ofDividerAtIndex:, or implement relatively complex behaviors in split view delegate methods, new -minPossiblePositionOfDividerAtIndex: and -maxPossiblePositionOfDividerAtIndex: methods have been added.

You can programmatically collapse a subview by invoking -minPossiblePositionOfDividerAtIndex: or -maxPossiblePositionOfDividerAtIndex: and passing the result to -setPosition:ofDividerAtIndex:.

To make it easier for you to let the user collapse subviews by double-clicking on dividers, a new -splitView:shouldCollapseSubview:forDoubleClickOnDividerAtIndex: delegate method has been added.

Autosaving of divider positions and subview collapsing has been added to NSSplitView. It's controlled by the new -setAutosaveName: and -autosaveName: methods.

NSSplitView now puts an NSSplitViewDividerIndex entry in the user info dictionaries of NSSplitViewWillResizeSubviewsNotification and NSSplitViewDidResizeSubviewsNotification notifications it sends, and passes to the delegate's -splitViewWillResizeSubviews: and -splitViewDidResizeSubviews: methods, when the user is dragging a divider, or has double-clicked on a divider to collapse a subview, or when -setPosition:ofDividerAtIndex: is being invoked.

So that you can easily configure split views with thin dividers, NSSplitView now has -setDividerStyle: and -dividerStyle methods. The two possible styles are NSSplitViewDividerStyleThick and NSSplitViewDividerStyleThin. The default is thick.

In case NSSplitView's default divider color does not look good in in the context of your application's UI, NSSplitView now has a -dividerColor method. It's invoked by -[NSSplitView drawDividerInRect:], and returns a value based on the split view's style. You can override it to customize.

Note: -[NSSplitView dividerColor] has changed since the WWDC 2007 seed. It now returns [NSColor clearColor] instead of [[self window] backgroundColor] for the thick divider style. Also, for backward binary compatibility it behaves differently in apps linked against Mac OS 10.4 or earlier. In those older apps it returns [[self window] backgroundColor] if [self isOpaque] returns YES, nil otherwise. -[NSSplitView isOpaque] has also changed since the WWDC 2007 seed. In apps linked against Mac OS 10.5 or newer it returns YES if the divider color is opaque and (because NSSplitViews don't draw backgrounds, but do adjust their subviews to cover everything but the dividers) all of the subviews are opaque. In apps linked against Mac OS 10.4 or older it returns YES if all of the subviews are opaque and [self dividerThickness] returns the same value NSSplitView's default implementation of -dividerThickness would return and [self isPaneSplitter] returns YES.

Thin dividers would be hard to drag if the user had to precisely click on them them. To give the user a bigger area to click on, the "effective" area of thin dividers is larger than the drawn area, by two points in either direction, in Mac OS 10.5. So that you can customize this behavior, when stealing mouse clicks away from adjacent controls is not appropriate for example, there is a new -splitView:effectiveRect:forDrawnRect:ofDividerAtIndex: delegate method you can implement.

Also, you can put a divider "handle" in your UI to give the user another way to drag the divider. So that you can point a split view to the handle's area and let it manage divider dragging (and the mouse cursor too), there is a new -splitView:additionalEffectiveRectOfDividerAtIndex: delegate method you can implement.

A common UI pattern now is to provide a button to show and hide one subview or another of a split view, and completely hide the divider when the subview between it and the edge of the window is hidden. To make it easy for you to do this there is a new -splitView:shouldHideDividerAtIndex: delegate method you can implement.

Bug Fixes in NSSplitView

In Mac OS 10.5 some longstanding bugs have been fixed in NSSplitView.

-[NSSplitView adjustSubviews] now does a much better job of not letting rounding errors accumulate when it is invoked repeatedly during window resizing. This fixes a wide variety of problems involving drifting, disappearing, and reappearing subviews.

NSSplitView now uses -[NSView setHidden:] when collapsing and uncollapsing a subview instead of setting the origin of the subview's frame somewhere far, far way. One result is that controls inside collapsed subviews can no longer hold onto the key focus, and can therefore no longer interfere with a window's tabbing behavior.

Accessibility: because of NSSplitView's use of -[NSView setHidden:], collapsed subviews are automatically no longer present in the accessibility hierarchy.

-[NSSplitView adjustSubviews] now reliably apportions space to uncollapsed subviews even when their total width (in vertical split views) or height (horizontal) is zero when it is invoked.

During divider dragging NSSplitView now more reliably heeds the value returned by the delegate's -splitView:constrainMaxCoordinate:ofSubviewAt: method to prevent the user from making the collapsible subview below (in a horizontal split view) or to the right of (vertical) the divider smaller than a minimum size before it is collapsed. NSSplitView also now collapses and uncollapses adjacent subviews simultaneously as the user drags the divider, when there isn't room to show both of them uncollapsed, and both are collapsible.

NSSplitView now uses NSCursor's +resizeLeftCursor and +resizeRightCursor when appropriate, instead of just using +resizeLeftRightCursor all of the time. The same goes for +resizeUpCursor and +resizeDownCursor instead of +resizeUpDownCursor.

Advice for Programming with NSSplitView

If your application needs to know when a subview is collapsed or expanded, register for the NSSplitViewDidResizeSubviewsNotification, or implement the -splitViewDidResizeSubviews: delegate method, and use -[NSSplitView isSubviewCollapsed:]. Don't, for example, assume that a view will be collapsed soon after the delegate returned YES when sent -splitView:canCollapseSubview:.


NSWindow

We added API to specify that a window can become visible before login. Windows that need to be shown as part of login UI should have this property set. The default setting is NO.
- (BOOL)canBecomeVisibleWithoutLogin;
- (void)setCanBecomeVisibleWithoutLogin:(BOOL)flag;
We have added API to control sharing of the window content. -setSharingType: specifies whether the window content can be read and/or written from another process. The default sharing type is NSWindowSharingReadOnly, which means other processes can read the window content (eg. for window capture) but cannot modify it. If you set your window sharing type to NSWindowSharingNone, so that the content cannot be captured, your window will also not be able to participate in a number of system services, so this setting should be used with caution. If you set your window sharing type to NSWindowSharingReadWrite, other processes can both read and modify the window content.
enum {
    NSWindowSharingNone = 0, // Window contents may not be read by another process
    NSWindowSharingReadOnly = 1, // Window contents may be read but not modified by another process
    NSWindowSharingReadWrite = 2 // Window contents may be read or modified by another process
};
typedef NSUInteger NSWindowSharingType;
- (void)setSharingType:(NSWindowSharingType)type;
- (NSWindowSharingType)sharingType;
We have also added -setPreferredBackingLocation: to set the preferred location for the window backing store. In general, you should not use this API unless indicated by performance measurement. The default preferred location is NSWindowBackingLocationDefault, which means that the system determines whether window backing store is kept in VRAM or main memory. You can use this API to set a preferred location for your window backing store, but the system may choose a different location. You can use -backingLocation to find the current location of your window backing store.
enum {
    NSWindowBackingLocationDefault = 0,        // System determines if window backing store is in VRAM or main memory
    NSWindowBackingLocationVideoMemory = 1,        // Window backing store is in VRAM
    NSWindowBackingLocationMainMemory = 2        // Window backing store is in main memory
};
typedef NSUInteger NSWindowBackingLocation;
- (void)setPreferredBackingLocation:(NSWindowBackingLocation)backingLocation;
- (NSWindowBackingLocation)preferredBackingLocation;
- (NSWindowBackingLocation)backingLocation;
We have also added API to make it easier to customize the behavior of the document icon in a window title bar. Prior to Leopard, you could call -[NSWindow setTitleWithRepresentedFilename:] or -[NSWindow setRepresentedFilename:] to indicate that a window represents a file at the given path. This causes two behaviors: The window shows a document icon appropriate for the given file path. This icon is draggable, and creates an alias, copy, or generic representation of the file when dropped. Secondly, the document icon and the title form a cmd-clickable region. A cmd-click in this region shows a popup menu. Selecting an item from this menu causes it to be shown in Finder.

In Leopard, we have added API to allow a document icon for any window, given a URL. If you call setRepresentedURL: with a valid non-nil URL, the window will show a document icon in the titlebar. If the url represents a filename or other resource with a known icon, that icon will be used as the document icon. Otherwise the default document icon will be used. The icon can be customized using [[NSWindow standardWindowButton:NSWindowDocumentIconButton] setImage:customImage]. If the URL is not nil and its path is not empty, the window will have a pop-up menu which can be shown via command-click on the area containing the document icon and title. By default, this menu will display the path components of the URL. The presence and contents of this menu can be controlled by the delegate method window:shouldPopUpDocumentPathMenu:. If the URL is nil or has an empty path, the window will not show a document icon and will not have a pop-up menu available via command-click.
- (void)setRepresentedURL:(NSURL *)url;
- (NSURL *)representedURL;
If a window has a representedURL, the window will by default show a path popup menu for a command-click on a rectangle containing the window document icon button and the window title. The window delegate may implement -window:shouldPopupDocumentPathMenu: to override NSWindow's default behavior for path popup menu. A return of NO will prevent the menu from being shown. A return of YES will cause the window to show the menu passed to this method, which by default will contain a menuItem for each path component of the representedURL. If the representedURL has no path components, the menu will have no menu items. Before returning YES, the window delegate may customize the menu by changing the menuItems. menuItems may be added or deleted, and each menuItem title, action, or target may be modified.
- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu;
The window delegate may implement -window:shouldDragDocumentWithEvent:from:withPasteboard: to override NSWindow document icon's default drag behavior. The delegate can prohibit the drag by returning NO. Before returning NO, the delegate may implement its own dragging behavior using -[NSWindow dragImage:at:offset:event:pasteboard:source:slideBack:]. Alternatively, the delegate can enable a drag by returning YES, for example to override NSWindow's default behavior of prohibiting the drag of an edited document. Lastly, the delegate can customize the pasteboard contents before returning YES.
- (BOOL)window:(NSWindow *)window
shouldDragDocumentWithEvent:(NSEvent *)event
from:(NSPoint)dragImageLocation
withPasteboard:(NSPasteboard *)pasteboard;
NSWindow now has a method to get a dock tile instance, which allows you to control some aspects of the dock tile corresponding to the miniaturized window. For further discussion, see the NSDockTile section.
- (NSDockTile *)dockTile;
-[NSWindow center] now uses a different algorithm to position windows. The effect of this change is that a window whose height is close to or greater than 2/3 of the visible screen height will now be positioned vertically in a way that is consistent with smaller windows.

The action of the standard toolbar button now goes through the public toggleToolbarShown: method, passing the toolbar button as the sender.

Support has been added for a Heads Up Display (HUD) window style. A HUD window can be created using NSHUDWindowMask. The window must be an NSPanel or subclass. NSHUDWindowMask may be combined with other window styles to create a borderless or titled window with a certain appearance and behavior. Both the titled and borderless window float above other windows and are partially transparent. They hide-on-deactivate, which means a HUD window will only be visible when its owning app is active. The following combinations are valid:
NSHUDWindowMask
    | NSBorderlessWindowMask - borderless window with HUD transparency and window level
or
    | NSTitledWindowMask | NSUtilityWindowMask - titled window with HUD transparency and window level
        and any of the following:
        | NSClosableWindowMask - titled window with HUD close box, transparency, and window level
        | NSResizableWindowMask - titled window with HUD resize corner, transparency, and window level
        | NSNonactivatingPanelMask - no effect on appearance, but owning app will not necessarily be active when this window is the key window

the following are not valid

    NSMiniaturizableWindowMask - not supported
    NSTexturedBackgroundWindowMask - not supported
    NSDocModalWindowMask - not supported
    NSUnifiedTitleAndToolbarWindowMask - not supported

A secondary click in the title bar of a window now shows the context menu of the toolbar, if any. Likewise, a double-click in the toolbar background of such a window will minimize the window if minimize on double-click is enabled. Because there is no visual separation between the title bar and the toolbar, they are now treated more as if they were one area. A secondary click in the document icon or title bar of a window with a represented URL will now show the document pop up, just as a command click does.

We have modified the window appearance. The window titlebar and toolbar background are drawn with a dark gradient when key or main, and a lighter gradient when inactive. The NSUnifiedTitleAndToolbarWindowMask styleMask no longer has any effect, since all windows with toolbars now have a unified look. Windows whose styleMask includes NSTexturedBackgroundWindowMask have a window background which also darkens when key or main and lightens when inactive, and may have a second gradient in the section below the window content. Windows whose styleMask does not include NSTexturedBackgroundWindowMask have a window background which is a solid fill and does not change when key, main, or inactive.

We have modified the look of main and utility windows. A window that is main and not key now has the same title bar color as a key window, but inactive window buttons. A utility window that is not key now has an inactive title bar buttons. With this change, active title bar buttons now have a more consistent correlation with the key window state.

In Leopard, textured windows have a gradient on the top and bottom section of the window where metal was previously visible.

In many cases, we can detect the appropriate area for a textured window gradient using an algorithm based on opaque views, but in some cases we cannot, since a view may declare itself opaque then draw the window background, for example. For application windows where the window background detection doesn't work, we have provided API. We hope that most windows will look correct without invoking this API.

If the automatic calculation of window gradient does not yield the correct results, the automatic calculation can be disabled with -setAutorecalculatesContentBorderThickness:NO. If this method is called without also setting a content border thickness for a given edge, the content border thickness on that edge will be 0. A content border thickness can be set by calling -setContentBorderThickness:forEdge:. Alternatively, a window subclass can override -contentBorderThicknessForEdge:. If -setContentBorderThickness:forEdge: is called other than by NSWindow's automatic calculation, and autorecalculatesContentBorderThicknessForEdge: returns YES for the given edge, the behavior is undefined. (That is, NSWindow is likely to overwrite the custom value).

Non-textured windows can also now be told to draw the window background gradient in a border on the bottom of the window. By default, non-textured windows have no bottom border.

-setContentBorderThickness:forEdge: and contentBorderThicknessForEdge: use the coordinate system of the window content, so they are in points rather than pixels. Note that the contentBorder does not include the titlebar or toolbar, so a window that just wants the gradient in the titlebar and toolbar will have a contentBorderThickness of 0 for NSMaxYEdge.

Calling -setContentBorderThickness:forEdge:NSMinXEdge/NSMaxXEdge will raise an exception. Likewise calling -setAutorecalculatesContentBorderThickness:NO forEdge:NSMinXEdge/NSMaxXEdge will raise an exception. In a non-textured window only, calling -setContentBorderThickness:forEdge:NSMaxYEdge will raise an exception, as will calling -setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge. It is only valid to set the content border thickness of the top edge in a textured window.

The behavior of -setContentBorderThickness:forEdge:NSMinYEdge and -setAutorecalculatesContentBorderThickness:NO forEdge:NSMinYEdge for non-textured windows will do the following: The top gradient will be repeated in the bottom border, separator lines will be drawn between the content and the bottom border, and the bottom corner will be rounded. Other methods on non-textured windows or unused edges will return 0.0 or YES.

- (void)setContentBorderThickness:(CGFloat)thickness forEdge:(NSRectEdge)edge;
- (CGFloat)contentBorderThicknessForEdge:(NSRectEdge)edge;

- (void)setAutorecalculatesContentBorderThickness:(BOOL)flag forEdge:(NSRectEdge)edge;
- (BOOL)autorecalculatesContentBorderThicknessForEdge:(NSRectEdge)edge;

We deprecated an API added earlier in Leopard for Spaces and replaced it with a more general form. Windows have different behavior under Spaces based on this API.

NSWindowCollectionBehaviorDefault windows can be either document or floating. Document windows are associated with one space at a time. If you order a normal window onscreen, it becomes associated with the current space. If you then switch spaces, the window does not show up in the new space. If you switch focus back to the window which is onscreen in another space, you get switched to the space containing the window. A TextEdit document window is an example of a document window. Floating windows are also almost space independent, except that they are associated with their owning app. If you order a floating window onscreen, it orders onscreen in the current space. If you switch spaces, the floating window shows up in the new space if and only if the owning app is active. The floating window itself is not counted as a window that can cause the app to be chosen as active on a space switch. Note that floating windows are usually, but not required to be, hide on deactivate. AppKit automatically sets windows whose window-level is non-0 to floating. The TextEdit font panel is an example of a floating window.

NSWindowCollectionBehaviorCanJoinAllSpaces windows can be thought of as space independent. If you order an all-space window onscreen, it orders onscreen in the current space. If you then switch spaces, the all-space window shows up in the new space. If you switch focus to the all-space window, you stay in the current space. The menu bar is an example of this kind of window.

NSWindowCollectionBehaviorMoveToActiveSpace windows are visible on only one space at a time, but move to the active space when needed. If you order a MoveToActiveSpace window onscreen, it becomes associated with the active space (which is the current space). If you then switch spaces, the window does not show up in the new space. If you switch focus back to the MoveToActiveSpace window, it becomes visible in the active space, rather than causing a space switch like a normal window would. The AppKit find panel is an example of a this kind of window.
enum {
NSWindowCollectionBehaviorDefault = 0,
NSWindowCollectionBehaviorCanJoinAllSpaces = 1 << 0,
NSWindowCollectionBehaviorMoveToActiveSpace = 1 << 1
};
typedef NSUInteger NSWindowCollectionBehavior;
- (void)setCollectionBehavior:(NSWindowCollectionBehavior)behavior;
- (NSWindowCollectionBehavior)collectionBehavior;
The setCanBeVisibleOnAllSpaces/canBeVisibleOnAllSpaces API, introduced earlier in Leopard, is deprecated in favor of setCollectionBehavior:/collectionBehavior
-(void)setCanBeVisibleOnAllSpaces:(BOOL)flag    AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER_BUT_DEPRECATED;
-(BOOL)canBeVisibleOnAllSpaces AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER_BUT_DEPRECATED;
NSBackingStoreRetained is no longer supported as a backing store type. Windows created with a backing store type of NSBackingStoreRetained will be silently promoted to NSBackingStoreBuffered. In the MacOSX implementation, NSBackingStoreRetained doesn't have any advantages in behavior or performance over NSBackingStoreBuffered, and on some hardware may behave significantly worse due to performance of display frame buffer access.

Sheets

Since the September 2007 seed of 10.5 we have introduced a new sheet effect for standard windows.  Sheets are now attached under the content border, which includes the titlebar, toolbar, and any additional content border thickness on the top edge.  There is no longer any "slot" at the attachment point between the sheet and the window.  Instead, there is a shadow on the top edge of the sheet where it connects to the window.  If there is no toolbar or content border aside from the titlebar, the sheet is positioned below the titlebar.

Due to compatibility concerns, the additional content border thickness is only taken into account for positioning the sheet if it has been set explicitly.  That is, if the window returns YES from [self autorecalculatesContentBorderThicknessForEdge:NSMaxYEdge], the auto-calculated content border thickness is not included when positioning the sheet.

NSScreen

In order to return the correct screen frame information after a programmatic display configuration change, NSScreen no longer caches screen frame information. Instead, it retrieves the requested information from Quartz. This has implications for applications which may have worked around the previous NSScreen behavior where a stale screen frame could be returned after a display configuration change.


NSAlert

We have added support to NSAlert for a suppression checkbox and an accessory view. setShowsSuppressionButton: indicates whether or not the alert should contain a suppression checkbox. The default is NO. This checkbox is typically used to give the user an option to not show this alert again. If shown, the suppression button will have a default localized title similar to @"Do not show this message again." You can customize this title using [[alert suppressionButton] setTitle:]. When the alert is dismissed, you can get the state of the suppression button, using [[alert suppressionButton] state] and store the result in user defaults, for example. This setting can then be checked before showing the alert again. By default, the suppression button is positioned below the informative text, and above the accessory view (if any) and the alert buttons, and left-aligned with the informative text. However do not count on the placement of this button, since it might be moved if the alert panel user interface is changed in the future. If you need a checkbox for purposes other than suppression text, it is recommended you create your own using an accessory view.
- (void)setShowsSuppressionButton:(BOOL)flag;
- (BOOL)showsSuppressionButton;
suppressionButton returns a suppression button which may be customized, including the title and the initial state. You can also use this method to get the state of the button after the alert is dismissed, which may be stored in user defaults and checked before showing the alert again. In order to show the suppression button in the alert panel, you must call -setShowsSuppressionButton:YES.
- (NSButton *)suppressionButton;

setAccessoryView: sets the accessory view displayed in the alert panel. By default, the accessory view is positioned below the informative text and the suppression button (if any) and above the alert buttons, left-aligned with the informative text. If you want to customize the location of the accessory view, you must first call -layout. See the discussion of -layout for more information.
- (void)setAccessoryView:(NSView *)view;
- (NSView *)accessoryView;
The following method can be used to indicate that the alert panel should do immediate layout, overriding the default behavior of laying out lazily just before showing the panel. You should only call this method if you want to do your own custom layout after it returns. You should call this method only after you have finished with NSAlert customization, including setting message and informative text, and adding buttons and an accessory view if needed. You can make layout changes after this method returns, in particular to adjust the frame of an accessory view. Note that the standard layout of the alert may change in the future, so layout customization should be done with caution.
- (void)layout;

[alert setIcon:nil] now restores the application icon, as documented. Prior to this change, [alert setIcon:nil] would actually remove the icon from the alert.


If alertWithError: is called with a nil NSError, it will now return an alert that displays a generic error message rather than an empty and confusing panel. Under normal circumstances a nil argument to such a method should be considered a programming error and should result in an exception. However, since runtime errors that this method is supposed to help with are not always encountered during testing, and since the problem is already occurring in the context of a user level error that the user should know about, displaying the alert remains a reasonable approach. Clearly the problem should be fixed by finding and fixing the subsystem that is failing to properly create an NSError.


NSDockTile

We have added a new class, NSDockTile, which can be used to customize the dock tile behavior for both NSApplication and NSWindow.

An NSDockTile has a size, which corresponds to the size of the backing store in the dock, which may be bigger than the current tile size, and is defined by the current user space scale factor. Currently, it is 128x128 on a system with user space scale factor 1.0, but you should not make any assumptions about this size remaining the same in the future.

You can add a contentView to the dock tile. If you want to draw into the dock tile, you have to tell the dock tile to display, and you have to draw the whole tile. Certain system activity will also cause the dock tile to resize and/or display. Any badging will be applied after the dock tile displays.

You can badge the dock tile with a localized string representing a count. You can also specify whether or not the tile should show the application badge.

Existing API for dock tile management (setting app icon, dock tile menu, miniwindow icon, and miniwindow title) will not be replaced with dock tile API at this point. A future direction could be to move all dock tile management to NSDockTile.
@interface NSDockTile : NSObject
- (NSSize)size;
- (void)setContentView:(NSView *)view;
- (NSView *)contentView;
- (void)display;
- (void)setShowsApplicationBadge:(BOOL)flag;
- (BOOL)showsApplicationBadge;
- (void)setBadgeLabel:(NSString *)string;
- (NSString *)badgeLabel;
- (id)owner;
@end
Please refer to <AppKit/NSDockTile.h> and the documentation for further details on NSDockTile.


NSApplication

Like NSWindow, NSApplication now has a method to get a dock tile instance. It allows you to control some aspects of the dock tile corresponding to the application. For further discussion, see the NSDockTile section right above.
- (NSDockTile *)dockTile;
For applications built on Leopard or later, the dock tile icon is now restored to its default state when the application terminates, meaning badge labels and such are removed automatically.  Some applications previously accomplished this by calling RestoreApplicationDockTileImage.  This is incompatible with NSDockTile on Leopard, so you should modify your application if you are doing this.  If you need to restore the dock tile icon on Tiger in a Leopard compatible way, you can do so by calling -[NSApp setApplicationIconImage:nil].


Tracking areas

We created a new model for mouse tracking and cursor updates. An NSTrackingArea will encapsulate the information used to create trackingRects today, including a rect (relative to view bounds), an owner (which will receive events generated on behalf of the trackingArea), a userInfo dictionary, and options as described below. Note that NSTrackingArea conforms to NSCoding and NSCopying.

An NSTrackingArea can be used like a traditional trackingRect, which will generate mouseEntered and mouseExited events as the mouse moves in and out of the area. It can also be used for cursorUpdates, which will be sent to the view under the mouse when the mouse moves in or out of the area. Lastly, it can be used to register for mouseMoved events for all mouse movement inside of the area. These options can be combined with bit-wise or to install a single NSTrackingArea that provides all three options.

You can get the NSTrackingArea that generated a mouseEntered or mouseExited event using -[event trackingArea].

An NSTrackingArea can be active only when its view is the firstResponder, or when the view is in the key window, or when the view is in any window in the active app, or always. Some of these options do not work with some of the types. For example, you cannot request cursorUpdates in an inactive app.

As with traditional trackingRects, you can specify whether you want to assume that the mouse is inside or outside of the trackingArea. Additionally, a trackingArea can be set up to stay in sync with the visibleRect of the view, which then makes one of the most common situations trivial. Lastly, you can request mouseEntered and mouseExited events to be generated while the mouse button is down (that is, during mouse dragging).
@interface NSTrackingArea : NSObject <NSCopying, NSCoding>
- (NSTrackingArea *)initWithRect:(NSRect)rect
options:(NSTrackingAreaOptions)options
owner:(id)owner
userInfo:(NSDictionary *)userInfo;
- (NSRect)rect;
- (NSTrackingAreaOptions)options;
- (id)owner;
- (NSDictionary *)userInfo;
@end
Please refer to <AppKit/NSTrackingArea.h> and the documentation for further details on NSTrackingArea.

The following API has been added to NSView:
- (void)addTrackingArea:(NSTrackingArea *)trackingArea;
A view or other object creates an NSTrackingArea, then adds it to a view using this API. Not meant to be overridden.
- (void)removeTrackingArea:(NSTrackingArea *)trackingArea;
This API is used to remove a trackingArea from a view. Not meant to be overridden.
- (NSArray *)trackingAreas;
Get the list of trackingAreas that have been added to the view. Not meant to be overridden.
- (void)updateTrackingAreas;
This will be sent to a view when something has changed which is likely to require recomputation of trackingAreas, for example a change in the size of the visibleRect. Moving a view into or out of a window will not cause this message to be sent, except that it will be sent once when the view is first created and added to a window. Should be overridden by a view to remove and add its tracking areas, and should call super.

The third part of the API improves cursor arbitration by adding a responder method, -cursorUpdate:. When an NSCursorUpdate event is received for a window, it gets routed to the view under the mouse, using normal hitTesting. The view under the mouse receives the cursorUpdate: message. This behaves like other event responder methods - if the view doesn't implement cursorUpdate:, the NSResponder implementation will send it to the nextResponder. If the view implements cursorUpdate: but decides not to handle the particular event, it should invoke super.

The following API has been added to NSResponder:
- (void)cursorUpdate:(NSEvent *)event;
Override to set cursor. Default implementation uses cursorRect if cursorRects are valid. If no cursorRect calls super, to send up responder chain.

The following API has been added to NSEvent, and is valid for NSMouseEntered and NSMouseExited events. Note that it is not valid for NSMouseMoved events.
- (NSTrackingArea *)trackingArea;
-trackingArea can be sent to an NSMouseEntered, NSMouseExited, or NSCursorUpdate event. It is not valid for an NSMouseMoved event. If the event was generated by an old-style trackingRect, -trackingArea will return nil.


We fixed a bug in the interpretation of the assumeInside flag for tracking rects added via -[NSView addTrackingRect:owener:userData:assumeInside:]. On Tiger and previous, passing YES for the assumeInside flag yielded inconsistent results. If the mouse was initially outside the tracking rect when the tracking rect was added, no event was generated, and a mouseEntered event was sometimes generated when the mouse entered the tracking rect through a subsequent mouseMoved. On Leopard, passing YES for the assumeInside flag will cause a mouseExited event to be generated if the mouse is initially outside the tracking rect. A mouseEntered will be generated when the mouse enters the tracking rect if it was initially outside. If this new behavior causes a problem for your application, you can get the Tiger behavior by setting the NSTigerBehaviorForTrackingRects user default to YES.


NSEvent

We added methods to convert between an NSEvent and a Carbon EventRef. -eventRef is valid for all events and returns an EventRef corresponding to the NSEvent. The EventRef is retained by the NSEvent, so will be valid as long as the NSEvent is valid, and will be released when the NSEvent is freed. You can use RetainEvent to extend the lifetime of the EventRef, with a corresponding ReleaseEvent when you are done with it. If there is no EventRef corresponding to the NSEvent, -eventRef will return NULL. +eventWithEventRef: returns an autoreleased NSEvent corresponding to the EventRef. The EventRef is retained by the NSEvent and will be released when the NSEvent is freed. If there is no NSEvent corresponding to the EventRef, +eventWithEventRef: will return nil.
- (const void * /* EventRef */)eventRef;
+ (NSEvent *)eventWithEventRef:(const void * /* EventRef */)eventRef;
We also added methods to convert between an NSEvent and a CGEventRef. -CGEvent is valid for all events and returns an autoreleased CGEventRef corresponding to the NSEvent. If you want to control the lifetime of the CGEventRef, you should retain it. If there is no CGEventRef corresponding to the NSEvent, -CGEvent will return NULL. + eventWithCGEvent: returns an autoreleased NSEvent corresponding to the CGEventRef. If there is no NSEvent corresponding to the EventRef, +eventWithCGEvent: will return nil. Converting from an NSEvent to a CGEventRef can be lossy, and you should not attempt to use the key event handling facilities provided by CGEventRef.
- (CGEventRef)CGEvent;
+ (NSEvent *)eventWithCGEvent:(CGEventRef)cgEvent;
There is now a method to enable or disable mouse coalescing, and a method to query the current state. Mouse coalescing is on by default.
+ (void)setMouseCoalescingEnabled:(BOOL)flag;
+ (BOOL)isMouseCoalescingEnabled;
If you build your application on Leopard, and your application installs an event handler on the event monitor target using GetEventMonitorTarget, the monitored event will be sent to the event handler you installed rather than to -[NSApplication sendEvent:]. For applications built on Tiger or previous, the monitored event will be sent to sendEvent:. You can override this default behavior by setting NSDispatchMonitoredEvents. If NSDispatchMonitoredEvents is YES, the event will be sent to sendEvent:; if NO, it will be sent to the installed event handler.

NSScrollWheel events will now be sent to the window under the mouse, whether or not the window is active. In previous version of Mac OS X, NSScrollWheel events were only sent to the window under the mouse if the window had key focus, with the exception of utility windows which received NSScrollWheel events even when inactive.


Key equivalents

We fixed a problem where NSKeyUp events were sent to performKeyEquivalent: if the command-key was not down. This problem could cause a key equivalent to be performed twice if the receiver didn't check the event type for NSKeyDown. The fix applies only to applications built on Leopard or later, to preserve binary compatibility for any application which may have been dependent on the old behavior.

The modifier flags are now preserved in NSKeyDown <esc> events. Prior to this change, NSWindow would strip modifier flags from the <esc> key event before invoking -performKeyEquivalent:. We don't expect applications to be dependent on this behavior, but if you need to ignore modifier flags on <esc> keyDown events, you can override -performKeyEquivalent: to do so.

In -[NSWindow sendEvent:] we now send keyDown: to the first responder even if the command-key modifier is set. An NSKeyDown event will only reach this point if it was not recognized as a key equivalent. One effect of this change is to enable custom key binding entries with command-key modifiers. The change applies only to applications built on Leopard or later, to preserve binary compatibility for any application which may have been dependent on the old behavior.

NSApplication now sends a ctrl-key event to performKeyEquivalent: before sending the keyDown: event through the responder chain. This allows ctrl-key events to be used as menu key equivalents more reliably. Prior to this change, a ctrl-key event which had an emacs key binding would not be sent to performKeyEquivalent: if focus was in an NSTextView.


Dragging

-[NSDraggingInfo slideDraggedImageTo:] is now implemented to behave as documented. This change is enabled only for applications built on Leopard or later to avoid changing behavior of older binaries in ways that may be incorrect.

-draggingEnded is now implemented for applications linked on Leopard or later.


NSDatePicker

NSDatePicker - Range mode

NSDatePicker now implements the existing date range selection API for the mini-calendar style date picker. This is enabled by calling setDatePickerMode: and passing in NSRangeDateMode. The range is represented by a start date (NSDate; accessible through dateValue) and a time interval (NSTimeInterval; accessible through timeInterval). If the date picker is a textfield style date picker, setDatePickerMode: is ineffectual and timeInterval returns 0.0 as before. If the date picker is in NSSingleDateMode, then timeInterval always returns 0.0.

Date ranges can be selected in the mini-calendar interface by clicking and dragging across calendar days or by shift-clicking. The calendar's months will also advance or retreat as the mouse is dragged below or above the area where calendar days are displayed.

NSDatePicker - Small and mini sizes

The text field styles of NSDatePicker now support small and mini control sizes through the -setControlSize: method. However, this only affects the size of the internal stepper cell. The size of the text field is determined by the size of its font and number of displayed date components.

NSDatePicker - Text field only style

NSDatePicker now supports a third style: NSTextFieldDatePickerStyle. This is identical to NSTextFieldAndStepperDatePickerStyle in every way, except it does not display a stepper.

NSDatePickerCell - Creation performance

The NSDatePickerCell creation speed has been significantly improved by drastically reducing the number of NSDateFormatters created internally.

NSDatePicker - Date Arithmetic Improvements

NSDatePickerCell's date arithmetic implementation has changed substantially in Leopard, abandoning use of the obsolete NSCalendarDate class (which only supports the Gregorian calendar, and yields imprecise results for dates in the distant past -- e.g. for years circa 1500) in favor of a fully modern NSCalendar-based implementation underpinned by ICU library routines. This fixes significant editing issues for Gregorian calendar dates, while providing substantial localization improvements for non-Gregorian calendars.


Text

New Text Document Formats

The text system now has support for reading and writing both the OASIS Open Document text document format and the ECMA Office Open XML text document format. AppKit/NSAttributedString.h has two new constants for this, NSOfficeOpenXMLTextDocumentType and NSOpenDocumentTextDocumentType. The /usr/bin/textutil command has been updated to support both formats.

NSAttributedString HTML Import

Since Tiger, NSAttributedString has used WebKit for all import of HTML documents (but not for export). Since WebKit document loading is not threadsafe, this has not been safe to use on background threads. For applications linked on Leopard and later, if NSAttributedString is used to import HTML documents on a secondary thread, the usage of WebKit will be transferred to the main thread via performSelectorOnMainThread:withObject:waitUntilDone:. This makes such usage threadsafe, but it requires that the main thread be running the run loop in one of the common modes. This behavior can be overridden by setting the default NSRunWebKitOnAppKitThread to either YES (to obtain the new behavior regardless of linkage) or NO (to obtain the old behavior regardless of linkage).

In versions prior to Leopard, NSAttributedString HTML import would set the NSBackgroundColorDocumentAttribute to [NSColor whiteColor] in cases in which the HTML did not specify a background color. This will continue to be true for applications linked on system versions prior to Leopard, but on Leopard and later, for applications linked on Leopard and later, no NSBackgroundColorDocumentAttribute will be set in these cases.

NSMutableAttributedString Note

The method -[NSMutableAttributedString readFromURL:options:documentAttributes:error:] is expected to replace the text of the receiver with the contents of the desired file. (The documentation states: "Sets the contents of receiver from the file at url.")

It works as expected for plaintext formatted files. However, for RTF formatted files, the contents of the file are appended to the previous string instead of replacing the previous string. This inconsistency is a bug; when using this method with existing content it's best to clear the content away explicitly.

NSTextStorage Scripting

For applications linked on Leopard or later, we now check for nil font in scripting calls and treat it as Helvetica 12. This impacts the calls font, fontName, fontSize, setFontName:, and setFontSize:.


Non-Contiguous Layout

NSLayoutManager now has a new option, referred to as non-contiguous layout. Previously, both glyph generation and layout have always been performed in order from the beginning to the end of the document. When non-contiguous layout is turned on, however, the layout manager gains the option of performing glyph generation or layout for one portion of the document without having done so for previous sections. This can provide significant performance improvements for large documents.

Non-contiguous layout is not turned on automatically because direct clients of NSLayoutManager typically have relied on the previous behavior--for example, by forcing layout for a given glyph range, and then assuming that previous glyphs would therefore be laid out. Clients who use NSLayoutManager only indirectly--for example, those who use NSTextView without directly calling the underlying layout manager--can usually turn on non-contiguous layout without difficulty. Clients using NSLayoutManager directly will need to examine their usage before turning on non-contiguous layout.

The methods directly concerned with non-contiguous layout are as follows:
- (void)setAllowsNonContiguousLayout:(BOOL)flag;
- (BOOL)allowsNonContiguousLayout;
- (BOOL)hasNonContiguousLayout;
The first allows non-contiguous layout to be turned on and off, and the second examines the state of that setting. Note that turning the flag on allows but does not require the layout manager to use non-contiguous layout, and it may in fact choose not to do so depending on the configuration of the layout manager. In addition, there may be times at which there is no non-contiguous layout, such as when layout is complete; the third method allows the layout manager to report that to clients.

In addition, there are a number of new methods that are especially useful when working with non-contiguous layout. Previously, glyph generation and layout have been implicit side effects of calls that require that information. That is still the case, but with the possibility of non-contiguous layout it is not always obvious what portion of the document will be affected. The new methods allow this to be specified explicitly, although the layout manager reserves the right to generate glyphs or perform layout for a larger portion of the document as appropriate. In particular, if non-contiguous layout is not in use, then the range affected will always be extended back to the beginning of the document.
- (void)ensureGlyphsForCharacterRange:(NSRange)charRange;
- (void)ensureGlyphsForGlyphRange:(NSRange)glyphRange;
- (void)ensureLayoutForCharacterRange:(NSRange)charRange;
- (void)ensureLayoutForGlyphRange:(NSRange)glyphRange;
- (void)ensureLayoutForTextContainer:(NSTextContainer *)container;
- (void)ensureLayoutForBoundingRect:(NSRect)bounds inTextContainer:(NSTextContainer *)container;

New NSLayoutManager Implementation

Large portions of NSLayoutManager have been updated to use a new implementation that supports the new non-contiguous layout feature. However, independent of whether non-contiguous layout is in use, the new implementation also rationalizes and clarifies a number of other aspects of the layout manager API.

For example, setCharacterIndex:forGlyphAtIndex: nominally allows for an arbitrary mapping between glyph and character indexes. This has never actually been the case; there have always been implicit restrictions, in the sense that other NSLayoutManager functionality would fail in the presence of a nonsensical glyph-to-character mapping. Now, however, it is possible to state the restrictions and, eventually, enforce them by raising an exception when they are violated. The basic restriction is that the glyph stream may never be out of order with respect to the character stream. Multiple glyphs may map to a single character, or multiple characters to a single glyph, but all of the glyphs for a given character must follow all glyphs for preceding characters and precede all glyphs for following characters. Glyphs that would not otherwise have a character (such as hyphens) are assigned to the character for the nearest previous glyph that has characters, and characters that would not otherwise have glyphs are assigned to the glyph for the nearest previous character that has glyphs. The stock glyph generator and typesetter attempt to preserve a one-to-one character-to-glyph mapping, to the extent possible, by including padding NSNullGlyph entries; for example, if the characters 'f' and 'i' are represented by an 'fi' ligature, then the glyph stream would include an 'fi' ligature glyph followed by a null glyph so that there are two glyphs to match the two characters. However, this is neither guaranteed nor required in general.

The basic methods for examining the character-glyph mapping are characterIndexForGlyphAtIndex: and the new method
- (NSUInteger)glyphIndexForCharacterAtIndex:(NSUInteger)charIndex;
that now plays a symmetric role. Thus characterIndexForGlyphAtIndex: returns the index for the first character associated with the specified glyph, and glyphIndexForCharacterAtIndex: returns the index for the first glyph associated with the specified character. In neither case is there any special treatment for null glyphs. In the 'fi' ligature case, for example, if the null padding is used, then these methods would report an identity mapping between glyph and character indexes. Both methods also accept indexes beyond the last character or glyph; they return an index extrapolated from the last actual character or glyph index. Thus if there is an identity mapping between glyph and character indexes, then both characterIndexForGlyphAtIndex: and glyphIndexForCharacterAtIndex: will always return results numerically equal to their arguments.

The more complex methods glyphRangeForCharacterRange:actualCharacterRange: and characterRangeForGlyphRange:actualGlyphRange:, on the other hand, do take null glyphs into account. For example, to consider the 'fi' ligature case again, if glyphRangeForCharacterRange:actualCharacterRange: were to be called with a character range of length 1 covering either the 'f' or the 'i', the resulting glyph range would include both the 'fi' glyph and the null glyph, and the actual character range would include both the 'f' and the 'i' character. Likewise, if characterRangeForGlyphRange:actualGlyphRange: were to be called with a glyph range of length 1 covering either the 'fi' glyph or the null glyph, the resulting character range would include both the 'f' character and the 'i' character, and the actual glyph range would include both the 'fi' glyph and the null glyph.

Both methods also have special treatment for ranges of zero length. For example, in the 'fi' ligature case, if glyphRangeForCharacterRange:actualCharacterRange: were to be called with a character range of length 0 before the 'f', the result would be a zero-length glyph range before the 'fi' glyph. However, if if glyphRangeForCharacterRange:actualCharacterRange: were to be called with a character range of length 0 between the 'f' and the 'i', the result would be a zero-length glyph range after the null glyph, and the actual character range would be a zero-length range after the 'i' character. In general, a zero-length character range within a multi-character sequence maps to a zero-length glyph range after the last glyph associated with the sequence, with a zero-length actual character range after the last character of the sequence. The same description applies to characterRangeForGlyphRange:actualGlyphRange:, interchanging the role of character and glyph indexes.

In general, character and glyph indexes now play a symmetric role in the character-glyph mapping, and glyphRangeForCharacterRange:actualCharacterRange: and characterRangeForGlyphRange:actualGlyphRange: behave symmetrically to each other. Both glyphRangeForCharacterRange:actualCharacterRange: and characterRangeForGlyphRange:actualGlyphRange: also accept indexes beyond the the last character or glyph, but the results they return are truncated after the last character or glyph rather than being extrapolated. This is in line with their role in, for example, calculating ranges of selected characters or glyphs.

Another aspect of NSLayoutManager that has been clarified is invalidation. Previously there was a distinction between hard and soft layout invalidation; it was expected that any change to the text would cause hard invalidation of the region that actually changed, followed by soft invalidation of all subsequent portions of the document, since they might move due to the change. Usually this would happen automatically as a result of change messages sent by the text storage to the layout manager, but anyone needing to invoke layout invalidation manually would have needed to respect the distinction. In Leopard, however, that distinction is no longer required; hard layout invalidation is the only sort necessary, and the equivalent of soft invalidation is arranged automatically. As a result, a new method
- (void)invalidateLayoutForCharacterRange:(NSRange)charRange actualCharacterRange:(NSRangePointer)actualCharRange;
has been provided to supersede the existing invalidateLayoutForCharacterRange:isSoft:actualCharacterRange:, as the equivalent of the latter with the soft flag set to NO. For code intended to run on Leopard only, the new method can be used. For code intended to run on both Leopard and Tiger, the old method should be used as before, in two calls, first with the soft flag set to NO, for the range actually being changed, and subsequently with the soft flag set to YES, for the range following the portion changed, to the end of the document.

In addition, layout manager/typesetter communication has been clarified by adding a new NSLayoutManager method for the use of the typesetter, to allow it to specify explicitly when portions of the glyph stream depend on layout--for example, because they have had hyphens inserted. The typesetter calls
- (void)invalidateGlyphsOnLayoutInvalidationForGlyphRange:(NSRange)glyphRange;
to specify that a certain range of glyphs is layout-dependent, and therefore the glyphs should be invalidated the next time their layout is invalidated, so that they will be regenerated before being laid out again.

Also, a new bulk NSLayout Manager method has been added to allow the typesetter to set locations for many glyph ranges at once:
- (void)setLocations:(NSPointArray)locations
startingGlyphIndexes:(NSUInteger *)glyphIndexes
count:(NSUInteger)count
forGlyphRange:(NSRange)glyphRange;
All of the glyph indexes should lie within the specified glyph range, the first of them should be equal to glyphRange.location, and the remainder should increase monotonically. Each location will be set as the location for the range beginning at the corresponding glyph index, and continuing until the subsequent glyph index, or until the end of the glyph range for the last location. Thus this method is equivalent to calling setLocation:forStartOfGlyphRange: for a set of ranges covering all of the glyphs in glyphRange.

NSLayoutManager setShowsInvisibleCharacters:

-setShowsInvisibleCharacters: method is now functional and substitute whitespace characters with either LOZENGE U+25CA or FULL STOP U+002E depending on glyph availability of the rendering font.

Thread safety of NSLayoutManager

Generally speaking, a given layout manager (and associated objects) should not be used on more than one thread at a time. Most layout managers will be used on the main thread, since it is the main thread on which their text views are being displayed, and since background layout occurs on the main thread. If it is intended that a layout manager should be used on a background thread, first make sure that text views associated with that layout manager (if any) will not be displayed while the layout manager is being used on the background thread, and second, turn off background layout for that layout manager while it is being used on the background thread.

Miscellaneous New NSLayoutManager Methods

To go along with the existing defaultLineHeightForFont:, NSLayoutManager has publicized
- (CGFloat)defaultBaselineOffsetForFont:(NSFont *)theFont;
to allow clients to obtain the baseline offset appropriate for a particular font within a particular layout manager, given its typesetter behavior and other settings.

In addition, it has also publicized the methods
- (BOOL)usesFontLeading;
- (void)setUsesFontLeading:(BOOL)flag;
that control whether the layout manager will use leading as specified by the font. The default is YES, since in most cases this is appropriate, but there are some cases where it is not; for example, for UI text a fixed leading is often specified by UI layout guidelines. All three of these methods are available going back to Mac OS X 10.2.

Some additions have been made to the NSLayoutManager temporary attribute methods to parallel more of the NSAttributedString attribute methods.
- (id)temporaryAttribute:(NSString *)attrName atCharacterIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range;
- (id)temporaryAttribute:(NSString *)attrName atCharacterIndex:(NSUInteger)location
longestEffectiveRange:(NSRangePointer)range inRange:(NSRange)rangeLimit;
- (NSDictionary *)temporaryAttributesAtCharacterIndex:(NSUInteger)location
longestEffectiveRange:(NSRangePointer)range inRange:(NSRange)rangeLimit;
- (void)addTemporaryAttribute:(NSString *)attrName value:(id)value forCharacterRange:(NSRange)charRange;
There is a new NSLayoutManager method to obtain insertion points in bulk for a given line fragment. Previously the rects used for the insertion point indicator were obtained by calling rectArrayForCharacterRange:withinSelectedCharacterRange:inTextContainer:rectCount: or the glyph-based equivalent with a zero-length range; that is still available, but it has the limitation that only one insertion point can be obtained at a time. There are many cases in which one wishes to retrieve multiple insertion points at once--for example, when one is trying to move from one to another. The method
- (NSUInteger)getLineFragmentInsertionPointsForCharacterAtIndex:(NSUInteger)charIndex
alternatePositions:(BOOL)aFlag
inDisplayOrder:(BOOL)dFlag
positions:(CGFloat *)positions
characterIndexes:(NSUInteger *)charIndexes;
allows clients to obtain all insertion points for a line fragment in one call. The caller specifies the line fragment by supplying one character index within it, and can choose whether to obtain primary or alternate insertion points, and whether they should be in logical or in display order. The return value is the number of insertion points returned. Each pointer passed in should either be NULL, or else point to sufficient memory to hold as many elements as there are insertion points in the line fragment (which cannot be more than the number of characters + 1). The positions buffer passed in will be filled in with the positions of the insertion points, in the order specified, and the charIndexes buffer passed in will be filled in with the corresponding character indexes. Positions indicate a transverse offset relative to the line fragment rect's origin. Internal caching is used to ensure that repeated calls to this method for the same line fragment (possibly with differing values for other arguments) will not be significantly more expensive than a single call.

Finally, there is a new NSLayoutManager delegate method,
- (NSDictionary *)layoutManager:(NSLayoutManager *)layoutManager
shouldUseTemporaryAttributes:(NSDictionary *)attrs
forDrawingToScreen:(BOOL)toScreen
atCharacterIndex:(NSUInteger)charIndex
effectiveRange:(NSRangePointer)effectiveCharRange;
This is sent when the layout manager is drawing and needs to decide whether to use temporary attributes or not. The delegate returns a dictionary of temporary attributes to be used, or nil to suppress the use of temporary attributes altogether. The effectiveCharRange argument is both an in and out by-reference effective range for those attributes. The default behavior if this method is not implemented is to use temporary attributes only when drawing to the screen, so an implementation to match that behavior would return attrs if toScreen is YES and nil otherwise, without changing effectiveCharRange.

NSTextView

A new property, allowedInputSourceLocales, controls the text input sources enabled for a NSTextView instance. The property can be accessed by the following accessor methods. There is a meta locale identifier, NSAllRomanInputSourcesLocaleIdentifier, available for specifying input sources that are limited for Roman script editing.
- (NSArray *)allowedInputSourceLocales;
- (void)setAllowedInputSourceLocales:(NSArray *)localeIdentifiers;
Command-delete is now bound to -deleteToBeginningOfLine:.


NSTextView Find Panel

The standard find panel for NSTextView now keeps track of the most recently used find and replace strings and displays them to the user in an NSComboBox.

In addition to communicating search strings via the find pasteboard, the standard find panel for NSTextView now also communicates search option metadata, including case sensitivity and substring matching options. This metadata is stored in a plist as the NSFindPanelSearchOptionsPboardType value on the global find pasteboard. As such, third party applications may store additional keys in this plist to communicate additional metadata as desired to support the various search options common to many third-party applications' find panels. NSTextView.h contains the AppKit-provided keys and values.

NSTextView Find Indicator

NSTextView now supports the new lozenge-style indication of find results, with a new method
- (void)showFindIndicatorForRange:(NSRange)charRange;
that causes a temporary indicator or indicators to appear around the visible portion(s) of the specified range. The indicators will automatically disappear after a certain period of time, or when the method is called again, or when any of a number of changes occur to the view (such as changes to text, to view size, or to view position). Note that this method does not itself scroll the specified range to be visible; any desired scrolling should be done before this method is called, first because the method acts only on the visible portion of the specified range, and second because scrolling will cause the indicators to disappear. Calling this method with a zero-length range will always remove any existing indicators.

Miscellaneous New NSTextView Methods

NSTextView includes a number of new flags for controlling its behavior.
- (void)setDisplaysLinkToolTips:(BOOL)flag;
- (BOOL)displaysLinkToolTips;
- (void)setAllowsImageEditing:(BOOL)flag;
- (BOOL)allowsImageEditing;
- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag;
- (BOOL)isAutomaticQuoteSubstitutionEnabled;
- (void)toggleAutomaticQuoteSubstitution:(id)sender;
- (void)setAutomaticLinkDetectionEnabled:(BOOL)flag;
- (BOOL)isAutomaticLinkDetectionEnabled;
- (void)toggleAutomaticLinkDetection:(id)sender;
The flag for displaying link tooltips controls whether the text view will automatically supply the destination of a link as a tooltip for text with a link attribute. The default value for this is YES; clients who do not wish this must explicitly disable it. In a related change, NSTextView will no longer automatically open file: URLs in links; by default, file: URLs will be revealed in the Finder. Clients wishing to override this should implement textView:clickedOnLink:atIndex: (as a delegate) or clickedOnLink:atIndex: (in a subclass).

The image editing flag controls whether text attachments representing images should allow their contents to be edited inline in the text, provided they support this and the text view is editable. Stock text attachments currently do not have support for this feature, but may do so in the future.

Automatic quote substitution, when it is turned on, causes ASCII quotation marks and apostrophes to be automatically replaced on a context- and language-dependent basis with more typographically accurate symbols. Automatic link detection, when it is turned on, causes strings representing URLs typed in the view to be automatically made into links to those URLs. In addition, the existing smart copy/paste functionality has a newly publicized action method,
- (void)toggleSmartInsertDelete:(id)sender;
In support of automatic link detection, there is a new method on NSAttributedString,
- (NSURL *)URLAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)effectiveRange;
that returns a URL from the contents of text at the given location, if the text at the location appears to be a string representing a URL. The effective range is the range of the URL string, or of non-URL text if no apparent URL is found.

To go along with the new NSLayoutManager insertion point functionality, there is a new NSTextView method
- (NSUInteger)characterIndexForInsertionAtPoint:(NSPoint)point;
which takes a point in view coordinates and returns a character index appropriate for placing a zero-length selection for an insertion point when the mouse is over the given point. NSTextInput protocol methods generally are not suitable for uses other than those associated with text input methods, and the NSTextInput protocol method characterIndexForPoint: is no exception; it is intended only for usages related to text input methods. The new characterIndexForInsertionAtPoint: should be used for insertion points associated with mouse clicks, drag events, and so forth. For other purposes, it is better to use NSLayoutManager methods, as demonstrated in a variety of code examples.

There is a new NSTextView delegate method
- (NSMenu *)textView:(NSTextView *)view menu:(NSMenu *)menu forEvent:(NSEvent *)event atIndex:(NSUInteger)charIndex;
which allows clients to control the context menu via the delegate, instead of having to subclass and override menuForEvent:. The menu parameter is the context menu that NSTextView would otherwise provide, and the charIndex argument is the index of the character that was right-clicked.

Finally, there is a new pasteboard type used by NSTextView when copying and pasting multiple selections.
NSString *NSMultipleTextSelectionPboardType;
This type is used only when the pasteboard is representing a multiple selection. The contents for this type should be an array of NSNumbers, one for each subrange of the selection, indicating the number of paragraphs contained in each subrange. The plain or rich text contents of the pasteboard will be a string representing the contents of each subrange concatenated with paragraph breaks in between them (where they do not already end in paragraph breaks); that combined with the paragraph counts in the NSMultipleTextSelectionPboardType is sufficient to determine which portions of the contents are associated with which subrange. This mechanism has been chosen because it is consistent across plain and rich text, and across different representations of rich text. The counts may be checked for consistency by comparing the total number of paragraphs in the plain or rich text contents of the pasteboard with the total of the numbers in the NSMultipleTextSelectionPboardType array; if the two do not match, then the NSMultipleTextSelectionPboardType contents should be ignored.

Additional List Styles

In Tiger, NSTextList marker formats supported the following numbering specifier keywords: decimal, lower-roman, upper-roman, lower-alpha, upper-alpha, lower-hexadecimal, upper-hexadecimal, and octal. In addition, it also supported the following constant specifier keywords: box, check, circle, diamond, disc, hyphen, square. These keywords are aligned with the corresponding draft CSS3 list-style-type values. In Leopard, NSTextList marker formats also support the following additional numbering specifier keywords: decimal-leading-zero, lower-greek, upper-greek, lower-russian, upper-russian, cjk-ideographic, hiragana, hiragana-iroha, katakana, katakana-iroha, cjk-earthly-branch, and cjk-heavenly-stem.

NSTypesetter

There is a new NSLayoutManager-interface API superseding -layoutGlyphsInLayoutManager:startingAtGlyphIndex:maxNumberOfLineFragments:nextGlyphIndex:. NSLayoutManager sends this message to the typesetter when its -allowsNonContiguousLayout setting is YES.
- (NSRange)layoutCharactersInRange:(NSRange)characterRange
forLayoutManager:(NSLayoutManager *)layoutManager
maximumNumberOfLineFragments:(NSUInteger)maxNumLines;

The last visible line truncation

The Cocoa Text System now allows the last visible line to have an ellipsis character appended if the entire content cannot fit into the specified bounding box. The behavior can be controlled with -truncatesLastVisibleLine for text cells. The -lineBreakMode must be either NSLineBreakByWordWrapping or NSLineBreakByCharWrapping for this option to take effect.. Also, the NSStringDrawingTruncatesLastVisibleLine flag can be specified to NSStringDrawing APIs that take NSStringDrawingOptions. The NSStringDrawingUsesLineFragmentOrigin flag must also be specified for the truncation flag to take effect.
- (BOOL)truncatesLastVisibleLine;
- (void)setTruncatesLastVisibleLine:(BOOL)flag;

NSFont

The AppKit framework no longer retains NSFont instances, and they are subject to the standard retain/release scheme. For debugging purpose, you can use the NSDisableFontDeallocation key. When the value is YES, font instances are not deallocated. Also, by setting NSFontDebugLevel to non-0 value, memory space previously occupied by NSFont is more aggressively reclaimed to allow finding over-released instances easily.

NSFontDescriptor

There is a new font descriptor matching API -matchingFontDescriptorWithMandatoryKeys: that returns the most suitable normalized instance.

We're allowing the following attribute and dictionary keys. They allow applications to create font descriptors that have non-default font feature settings.
NSString *NSFontFeatureSettingsAttribute;
NSString *NSFontFeatureTypeIdentifierKey;
NSString *NSFontFeatureSelectorIdentifierKey;

NSFontManager

NSFontManager now has the following 4 new public methods. You can use -currentFontAction and -convertFontTraits: to determine the action -convertFont: would perform. With the new accessor methods for the target property, you can now redirect the target for the action invoked by -sendAction method.
- (NSFontAction)currentFontAction;
- (NSFontTraitMask)convertFontTraits:(NSFontTraitMask)traits;
- (void)setTarget:(id)aTarget;
- (id)target;

RTF Handling

The RTF writer now prefers Microsoft font encodings over Mac scripts. The generated RTF data has much better compatibility with RTF reader implementations on Windows.


NSInputManager

The automatic loading of bundles located in InputManagers folders is now officially unsupported. The conditions for valid input manager bundle is further tightened. This functionality is likely to be disabled in a future release.

1. The valid installation is now restricted to the /Library/InputManagers folder only. Bundles in other locations are silently ignored.

2. All the files in the bundle and /Library/InputManagers folder itself must be owned by the root user and admin group. No files inside the bundle can have group or other write permissions.

3. Processes running with the root privilege (getuid() == 0 or geteuid() == 0) cannot load any bundle input manager.

4. Processes running with the wheel group privilege cannot load any bundle input manager.

5. The process must be in the active workspace session at the time of loading the bundles.

6. The process must not be tainted by changing user or group id (checked by issetugid()).

7. No 64-bit processes can load any bundle input managers.

NSTextInputClient protocol

There is a new protocol, NSTextInputClient, for objects willing to participate in the Cocoa Text Input system. The protocol supersedes the old protocol, NSTextInput, and allows conforming objects to take advantage of various system supplied functionalities. The Cocoa framework uses the new protocol whenever it is adopted by the responder object. For NSTextView subclasses, the new protocol is preferred unless any of the NSTextInput methods is overridden and none of the new methods is overridden.

There is now a public attributed string property, NSMarkedClauseSegmentAttributeName, to communicate clause segments inside marked text.


Spelling and Grammar Checking

Grammar checking is a new feature associated with the existing spellchecking functionality. Any spellchecking server has the option of also providing grammar checking, by implementing the NSSpellServer delegate method
- (NSRange)spellServer:(NSSpellServer *)sender checkGrammarInString:(NSString *)stringToCheck
language:(NSString *)language details:(NSArray **)details;
The return value is intended to be the range of the next sentence or other grammatical unit that contains sections to be flagged for grammar, since grammar checking generally must be performed sentence by sentence. The details argument optionally returns by reference an array of dictionaries, each one describing a grammatical issue detected in the sentence (since a single sentence may contain more than one problem). In these dictionaries the following keys will be recognized:
NSString *NSGrammarRange;
NSString *NSGrammarUserDescription;
NSString *NSGrammarCorrections;
The value for the NSGrammarRange key should be an NSValue containing an NSRange, a subrange of the sentence range used as the return value, whose location should be an offset from the beginning of the sentence--so, for example, an NSGrammarRange for the first four characters of the overall sentence range should be {0, 4}. The value for the NSGrammarUserDescription key should be an NSString containing descriptive text about that range, to be presented directly to the user; it is intended that the user description should provide enough information to allow the user to correct the problem. A value may also be provided for the NSGrammarCorrections key, consisting of an NSArray of NSStrings representing potential substitutions to correct the problem, but it is expected that this may not be available in all cases. It is recommended that NSGrammarUserDescription be supplied in all cases; in any event, either NSGrammarUserDescription or NSGrammarCorrections must be supplied in order for something to be presented to the user. If NSGrammarRange is not present, it will be assumed to be equal to the overall sentence range. Additional keys may be added in future releases.

The corresponding client method on NSSpellChecker is
- (NSRange)checkGrammarOfString:(NSString *)stringToCheck
startingAt:(NSInteger)startingOffset
language:(NSString *)language
wrap:(BOOL)wrapFlag
inSpellDocumentWithTag:(NSInteger)tag
details:(NSArray **)details;
similar to the existing spellchecking methods. NSSpellChecker also has a new method,
- (NSArray *)availableLanguages;
suitable for use with the existing -language and -setLanguage:. This returns the list of available languages for spellchecking, in the forms specified by the spelling servers; usually these will be language abbreviations such as "fr" or "en_GB", of the sort used by NSBundle for identifying localizations. The -setLanguage: method preferentially accepts one of these, but will attempt to find an inexact match for any value it is given. Also new are the NSSpellChecker methods
- (void)learnWord:(NSString *)word;
- (BOOL)hasLearnedWord:(NSString *)word;
- (void)unlearnWord:(NSString *)word;
that allow clients access to the learning and unlearning features of the spellchecker. The learnWord: method is not actually new, but is newly public; it has always been present on NSSpellChecker. The other methods are new, but there was a previous equivalent of unlearnWord:, named forgetWord:.

NSTextView has methods to control its use of grammar checking,
- (void)setGrammarCheckingEnabled:(BOOL)flag;
- (BOOL)isGrammarCheckingEnabled;
- (void)toggleGrammarChecking:(id)sender;
If grammar checking is enabled, then it will be performed alongside spellchecking, whenever the text view checks spelling, whether continuously or manually.

Means are also now provided to control the display of the spelling and grammar indicators on text, used to highlight portions of the text that are flagged for spelling or grammar issues. These regions are denoted by a temporary attribute on the layout manager, using the key
NSString *NSSpellingStateAttributeName;
This key is available going back to Mac OS X 10.2, but its interpretation has changed. Previously, any non-zero value would cause the spelling indicator to be displayed. For Mac OS X 10.5, the (integer) value will be treated as being composed of the following flags:
enum {
NSSpellingStateSpellingFlag = (1 << 0),
NSSpellingStateGrammarFlag = (1 << 1)
};
There is a new method on NSTextView for setting the spelling state, which may be called by clients or overridden by subclassers.
- (void)setSpellingState:(NSInteger)value range:(NSRange)charRange;
This method in turn calls a new NSTextView delegate method,
- (NSInteger)textView:(NSTextView *)textView shouldSetSpellingState:(NSInteger)value range:(NSRange)affectedCharRange;
which allows the delegate to control any change in the value of the spelling state.


Text and image effects: -[NSCell backgroundStyle]

Prior to Leopard, you may have noticed that text turns white in selected table view rows. The mechanism that controlled this behavior, unfortunately, was arcane. The table would set the highlight color of the cell to the alternateSelectedControlColor and set the cell to be highlighted. The cell would then introspect its highlight color, and use white text if the color was the alternateSelectedControlColor.

Another text effect occurred in applications such as 10.4's Address Book. Text looked engraved when drawn on the metal window surface. This was done with custom drawing.

For Leopard, this kind of behavior is formalized and extended with an API, -[NSCell setBackgroundStyle:]. This provides a cell with metadata that can be used to make good decisions about drawing. There are only four possible background styles: light, dark, raised and lowered. Most backgrounds are light. Selected table rows are, for 10.5, dark, so text will draw white. A metal surface is raised, so text that draws directly on it should look engraved. Some surfaces are lower than the plane of cell drawing, in which case text should look embossed.

There are a few methods involved. -[NSCell backgroundStyle] describes the background drawn on in the method -[NSCell drawWithFrame:inView:]. -[NSCell interiorBackgroundStyle] describes the background drawn on in the method -[NSCell drawInteriorWithFrame:inView:]. What's the difference? -[NSCell drawWithFrame:inView:] is the primary top level cell drawing method - it draws the whole cell. One of the methods it calls is -[NSCell drawInteriorWithFrame:inView:], which draws interior content like text and images. If a button draws a bezel, then -[NSCell backgroundStyle] will describe the surface the button is drawing on, while -[NSCell interiorBackgroundStyle] will describe the surface of the bezel itself.

Consider the bookmarks buttons in Safari 3.0. This is a standard style of button available in Interface Builder. The buttons are placed on a metal-ish surface that is raised relative to the button, so the -backgroundStyle is NSBackgroundStyleRaised. When the button is rolled over or clicked, it draws a bezel, and the bezel appears to be lower than the interior content of the button. So, when the button is not rolled over, -interiorBackgroundStyle returns NSBackgroundStyleRaised and the text looks engraved. When the button is rolled over, -interiorBackgroundStyle returns NSBackgroundStyleLowered and the text looks embossed.

This also makes it clear why a method to directly ask a cell to draw white, or engraved, or embossed would be inappropriate. The only cells that should get white text in a selected table row are those that do not interpose any drawing between the surface of the table and the text. Only a cell itself knows whether that is the case.

The complete list of backgroundStyle-related methods for Leopard is -[NSCell backgroundStyle], -[NSCell setBackgroundStyle:], -[NSCell interiorBackgroundStyle], and -[NSSegmentedCell interiorBackgroundStyleForSegment:].

Text and image effects: -[NSCell backgroundStyle] developer responsibilities

The main idea with background styles is this: "He who knows what art looks like is responsible for describing it to whatever draws on top." Often, it's AppKit that draws the art. All buttons that draw bezels will describe them with the return from -[NSButtonCell interiorBackgroundStyle]. If you pay attention to this value, you will get some independence from changes in framework art when doing custom drawing on a button surface. This might include choosing an image to draw, or even processing an image with CoreImage filters chosen based on backgroundStyle and other state.

Similarly, if you have a custom button bezel, you should override -interiorBackgroundStyle to describe the artwork you draw. This will ensure that AppKit renders appropriate effects on top of the bezel. You shouldn't rely on your art having the same general look as drawing that you've overridden - the framework art could change in a release, leaving you out of sync.

There is no setter for -[NSButtonCell interiorBackgroundStyle] because the return value could only be incorrect if you override cell drawing. If you override cell drawing, you can override -interiorBackgroundStyle as well.

Generally, it's the control's responsibility to call -[NSCell setBackgroundStyle:] on a cell right before drawing it. For example, NSTableView will set a backgroundStyle on its cells before it draws them. This is typically light or dark, but may also be raised or lowered in the case of group rows in source lists, and potentially elsewhere. If you have a custom control subclass, you should call -setBackgroundStyle on your cells before drawing them.

A caveat: Most AppKit controls do no drawing of their own. The cell does all the drawing. In these cases, you'd want a backgroundStyle of the superview to be inherited on the subview in some fashion, but there is no support for this in Leopard. So, in some cases you'll have to help, and sometimes hardwire a background style that it'd be nicer to introspect. For example, if you place an borderless NSTextField directly on the window surface you will likely want to have a controller call setBackgroundStyle:NSBackgroundStyleRaised on the text cell to get engraved text. Button cells and segmented cells that are usually used in a particular context have an initial backgroundStyle that matches that use, but feel free to change it if it isn't correct. For example, buttons with NSRecessedBezelStyle start out with NSBackgroundStyleRaised.


Text and image effects: -[NSImage isTemplate]

So far we've mostly talked about text. Images drawing in cells also have visual effects applied. For example, images are automatically dimmed out in disabled cells and darkened when pressed.

This is extended in Leopard with a new metadata property, -[NSImage isTemplate]. An image is a 'template' if it is a black and clear mask that should not be presented to the user directly, but always processed into a final form. It's similar to a glyph in a font. If a cell is given a template image, it can perform much more sophisticated effects than usual, similar to those done with text. The image can be dark most of the time, white in a selected table view row, and engraved or embossed the same way text is.

The template property does not affect the way an image draws. It's metadata that a sophisticated client can look at. The only sophisticated client in AppKit for 10.5 is NSCell. If you need to produce an NSImage with effects pre-rendered, draw with a cell into an image.

A good example of template image processing occurs in the button that shows the bookmarks collections in Safari 3.0. This is very much a stock AppKit button. There's a single template image set on it, which is an icon of a book. That single image appears engraved or embossed depending on whether button is rolled over (really, on the -interiorBackgroundStyle of the cell).

To mark an image as a template, call -[NSImage setTemplate:]. As a convenience, in applications linked on or after 10.5, any image read off of disk via -[NSImage imageNamed:] whose name ends in @"Template" will be marked as a template as it is created. This makes it easy to use template images in Interface Builder. Just make sure your image filenames end in "Template".

One particular effect is worth calling out, because you'll see it and likely wonder exactly what triggers it: If a template image is drawing on a raised background and is also supposed to look on, it will draw with a blue glow. Think of the image as a translucent gray piece of plastic cut into the surface of the cell, with a blue backlight behind it when the cell is on. This will happen, for example, in selected segments of an NSSegmentedControl with NSSegmentStyleTexturedRounded and NSSegmentSwitchTrackingSelectAny (which means that segments behave like check boxes). You cannot rely on this glow occurring in any particular place because framework art can change, but AppKit promises to continue to do something appropriate.

For an NSButtonCell, a cell's -image will be processed to look 'on' if the cell's state is NSOnState, its showsStateBy mask contains NSContentsCellMask (which means 'use alternate image and title when on'), and it doesn't have an alternateImage. In Interface Builder, make a 'Toggle' button and give the button an image. This also sets up the highlightsBy mask, but it's likely to be what you want.

You may note that it isn't possible to both use a distinct piece of art as an alternateImage on a button or segmented control and also get the blue glow. This may be somewhat limiting, but should tend to make sure this effect is only used when appropriate. This effect helps the user to distinguish between a button that shows an action and a button that shows the state of something. Most buttons in the OS are actions. However, it has always been hard to distinguish those buttons that give state. The blue glow communicates state very effectively. Look at the calendar button in the lower left corner of iCal in 10.5. When the mini calendar pane isn't shown, the button shows an engraved mini-calendar. The user might think of this as either an action 'show mini-calendar' or as a state, 'mini-calendar is off'. Luckily, both interpretations agree on the effect of clicking the button - it should show the calendar. Once the user clicks the button, the correct interpretation becomes very clear: The calendar icon glows blue when the pane is shown. This is obviously showing state, and the user will realize that if he clicks again then the button will turn off and the calendar pane will be hidden.

Text and image effects: NSStatusItem

It's worth mentioning separately that NSStatusItem is template image savvy. If you use template images for your status items, you will get rendering appropriate for the look of the menu bar in Leopard and beyond. Unfortunately, if you use a custom view in a status item, we do not have API to propagate a background style from the status item to the view. You will have to hardwire the background styles.

NSButton metrics

-[NSButton sizeToFit] applied to buttons with bezel style NSRecessedBezelStyle or NSRoundRectBezelStyle now produces buttons which are two pixels narrower than the buttons produced on Tiger. The new buttons have the same dimensions as those in Safari's bookmarks bar.

sizeToFit is substantially different for buttons with NSCircularBezelStyle for applications linked on and after Leopard. The Tiger 'regular' size for this button was very large, really too large to be used anywhere. What was called the 'small' size was something that would generally be called 'regular', and looked correct next to other regular sized controls.

In Leopard, buttons with NSCircularBezelStyle are available in regular, small and mini sizes where the art for the small and mini sizes is new, and the regular size is what used to be called small. For compatibility, the art chosen at draw time is no longer based on the marked controlSize of the button cell, it's based on the size of the frame in which the cell is drawn. The largest art that fits in the provided frame will be used, including the very large art that was available in Tiger. The upshot is that (1) existing buttons should continue to draw they way they did, (2) new buttons will be smaller. It is a bad idea, however, to call sizeToFit at runtime on circular buttons in an app linked on Leopard but that has to run on Tiger as well. The sizeToFit call will produce visually different buttons when running on Tiger vs Leopard. If you need to do this, write your own sizing routine and call it instead of sizeToFit.


Standard images

Leopard includes a number of standard images that developers may find useful. These images are available as names for use with -[NSImage imageNamed:]. For example, NSImageNameBookmarkTemplate is the image used in Safari for the button that shows the bookmark collections view. See NSImage.h for all of the names.

The string values of the constants are also documented so that the images can be used in Interface Builder. The string corresponding to NSImageNameBookmarkTemplate is @"NSBookmarkTemplate". Note that this image is a template and conforms to the template naming convention, as discussed in the release notes section on -[NSImage isTemplate].

Most images are named by a specific function or situation where they are intended to be used. In some cases, the artwork may be more generic than the name. For example, the image for NSImageNameInvalidDataFreestandingTemplate is an arrow in 10.5. Please do not use an image outside of the function for which it is intended - the artwork can change between releases. The invalid data image could change to a yellow exclamation-point-in-triangle icon. If there is no image available for the situation you're interested in, please file a bug and use your own custom art in the meantime.

There is an informal naming convention used with some images. If an image has "Freestanding" in the name, it's usable as a small inline image without any additional bezel. For example, Safari uses NSImageNameStopProgressTemplate (an X for 10.5) as the stop button in a bezeled button in the toolbar, while it uses NSImageNameStopProgressFreestandingTemplate (X in a circle for 10.5) in the downloads window where it appears inline with a progress indicator.

The standard images are mostly provided as a convenience. Use them if they fit your need, but if they don't then custom art is okay. For example, you may find that you want a different stroke weight for a larger or smaller image with an "add" or "remove" button. It'd be great if AppKit could accommodate that, but use your own art if it's better.

Standard Images: -[NSButtonCell setImageScaling:] and -[NSSegmentedCell setImageScaling:forSegment:]

One major caveat with the standard images is that their size is not guaranteed to stay the same between releases. Also, since they're supposed to be used in multiple contexts, you may find that the original size of an image is inappropriate for your use.

To help with this, NSButton and NSButtonCell have gained an imageScaling property, and NSSegmentedControl and NSSegmentedCell have gained -imageScalingForSegment: and -setImageScaling:forSegment: methods.

These behave similarly to the imageScaling property of NSImageCell. A button can now be configured to always draw its image scaled such that it fits in the available space. This will ensure that even if an image gets larger, it will still fit in your button. This is something of a backup fail safe, since many images have narrow lines that will not look good scaled, but it does provide a level of safety that would otherwise be a pain. All new buttons and segmented controls created in interface builder will have NSImageScaleProportionallyDown set by default.

Image scaling has no bearing on -sizeToFit behavior.

We also changed the values available in the NSImageScaling enumeration, because we needed one value that wasn't available, and two of the existing values were not well named.
enum {
NSImageScaleProportionallyDown = 0, // Scale image down if it is too large for destination. Preserve aspect ratio.
NSImageScaleAxesIndependently, // Scale each dimension to exactly fit destination. Do not preserve aspect ratio.
NSImageScaleNone, // Do not scale.
NSImageScaleProportionallyUpOrDown // Scale image to maximum possible dimensions while (1) staying within destination area
// and (2) preserving aspect ratio
};
typedef NSUInteger NSImageScaling;

NSImage layout metadata

NSImage now supports an alignmentRect property, which provides metadata that a client may use to help determine layout. The bottom of the rect gives the baseline of the image. The other edges give similar information in the other directions.
- (NSRect)alignmentRect;
- (void)setAlignmentRect:(NSRect)rect;
The alignmentRect of an image has no effect on methods such as drawInRect:fromRect:operation:Fraction: or drawAtPoint:fromRect:operation:fraction:. Rather, an interested client may use the alignmentRect to improve layout where applicable.

A 20x20 image of a phone icon with a glow might specify an alignmentRect of {{2,2},{16,16}} that excludes the glow. NSButtonCell will take advantage of the alignmentRect to place the image in the same visual location as an 16x16 phone icon without the glow. A 5x5 star that should render high when aligned with text might specify a rect of {{0,-7},{5,12}}.

The default alignmentRect of an image is {{0,0},imageSize}. The rect is adjusted when setSize: is called.

Multipart image drawing

NSCell.h contains two new functions for use with custom cell drawing, though they may be useful elsewhere as well.

NSDrawThreePartImage is used to draw a button of fixed height and variable width, or vice versa. It takes two end caps and a center image to be tiled.

NSDrawNinePartImage is used to draw something like a box that can be drawn at any height or width. It takes four corner images, four images to tile along the edges, and an image to tile in both directions for a center fill.

It's a good idea to use these functions for your custom drawing. AppKit can take care to get resolution independence issues right, and there are some subtle issues.

NSCell Automatic Expansion ToolTip Frame

NSCell has some new API to support expansion tool tips in certain controls:
- (NSRect)expansionFrameWithFrame:(NSRect)cellFrame inView:(NSView *)view;
- (void)drawWithExpansionFrame:(NSRect)cellFrame inView:(NSView *)view;
An expansion tool tip allows one to see truncated text in a special floating window that is similar but different from a normal ToolTip. Currently, NSTableView, NSOutlineView and NSBrowser display expansion tool tips when the text is truncated. If you have a cell that will be displayed in one of these controls, it is recommended that you implement the above methods to properly support the expansion tool tip feature. By default, the methods are properly implemented in NSTextFieldCell and NSBrowserCell. NSCell will always return NSZeroRect, and prevent the expansion from happening. For an example of how to implement it, see the SimpleBrowser demo app.

Clients of NSTableView, NSOutlineView and NSBrowser can prevent the cell expansion from happening on a particular row/column by using the new delegate methods declared in the appropriate header files.

NSCell Marked Text Support

Changing the content of NSCell subclasses no longer forces confirming the marked text (also known as the inline hole) unless the new content is actually different from the original.

There is a new NSCell API telling its field editor to post text change notifications. NSTextFieldCell also provides -setWantsNotificationForMarkedText:
- (BOOL)wantsNotificationForMarkedText;
The bug introduced in Tiger that NSCell's various sizing methods such as -cellSizeForBounds: ignoring bounds sizes smaller than 0.0 is fixed.

NSTextFieldCell

-[NSTextFieldCell accessibilityPerformAction:] now tries to perform NSAccessibilityConfirmAction for cells with no associated action. It uses -currentEditor and -selectedCell to determine if the current field editor is bound to self.

A new property, allowedInputSourceLocales, controls the text input sources enabled for the NSTextView instance. The property can be accessed by the following accessor methods. There is a meta locale identifier, NSAllRomanInputSourcesLocaleIdentifier, available for specifying input sources that are limited for Roman script editing.
- (NSArray *)allowedInputSourceLocales;
- (void)setAllowedInputSourceLocales:(NSArray *)localeIdentifiers;
NSTextFieldCell no longer disables itself temporarily in -trackMouse:inRect:ofView:untilMouseUp:. It is the application's responsibility now to properly set up the action invocation configuration via -sendActionOn: method.

In order to prevent inconsistent rendering, background color rendering is disabled for rounded-bezel NSTextFieldCell instances.

Autorelease pool in -[NSCell trackMouse:inRect:ofView:untilMouseUp:]

For applications linked on or after Leopard, NSCell uses an internal autorelease pool while looping over events in -trackMouse:inRect:ofView:untilMouseUp:. This should have no effect on an app that already practices correct memory management, except that memory usage will not temporarily climb while a user, say, scrubs an NSSlider back and forth. However, an application that isn't always careful about memory management may have newly expressed bugs. If pressing a button causes an NSTableView delegate to receive a final autorelease, and the programmer is not careful to call [tableView setDelegate:nil] to clear the non-retained back reference, the app could crash if the tableview tries to message its datasource before the end of the runloop cycle.

Archive reading for very old cells

Prior to Leopard, NSCell had residual support for reading very old un-keyed archive data saved prior to Mac OS X 10.0. This support has been removed. An attempt to read such an NSCell will now throw an exception. By the way, in case you missed it, you should really be using keyed archiving. The unkeyed archiver does not save out any object properties added in the last several OS releases. If we did, those archives would not be readable on earlier versions of the OS.


NSBitmapImageRep - Creating with a Custom Color Profile

It is now possible to create an NSBitmapImageRep with an arbitrary ICC color profile. The specified profile will be used when the image is drawn, and will also be saved with the image when it is encoded to a bitmapped image data format such as TIFF.

To do this for an NSBitmapImageRep that is created from scratch using one of the "-initWithBitmapDataPlanes:..." initializer methods, client code should pass NSCalibratedRGBColorSpace to the initializer, and then provide the ICC profile data as an NSData object using the -setProperty:withValue: method and the NSImageColorSyncProfileData property key:
NSBitmapImageRep *bitmapImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:planes
/* ...more parameters... */
colorSpaceName:NSCalibratedRGBColorSpace
/* ...more parameters... */
];
[bitmapImageRep setProperty:NSImageColorSyncProfileData withValue:iccProfileData];
One can also set a new replacement color profile for an NSBitmapImageRep that is initialized from an existing TIFF, PNG, etc. file using the same "-setProperty:withValue:" technique.

An ICC profile can be obtained as NSData from an NSColorSpace instance using the "-ICCProfileData" accessor method:
NSColorSpace *colorSpace = [NSColorSpace sRGBColorSpace];
NSData *iccProfileData = [colorSpace ICCProfileData];
It can also be obtained by initializing an NSData instance with the contents of an ".icc" profile file:
NSData *iccProfileData = [NSData dataWithContentsOfFile:@"/System/Library/ColorSync/Profiles/sRGB Profile.icc"];

NSBitmapImageRep - Creating from a CIImage

AppKit now includes an initializer method for creating an NSBitmapImageRep from a Core Image CIImage object:
- (id)initWithCIImage:(CIImage *)ciImage;
The CIImage is required to be of finite extent (if not, -initWithCIImage: raises an exception). The -initWithCIImage: method produces an NSBitmapImageRep whose pixel dimensions equal the incoming CIImage's extent.

Note that if the given CIImage is the result of a Core Image filter operation whose evaluation has been deferred, invoking -initWithCIImage: will cause the image recipe to be evaluated (rendered) so that the resultant image pixels can be obtained, so this method may be expected to take some time to execute and return, depending on the complexity of the filter chain and the availability of Core Image-accelerating graphics hardware on the host system.

NSBitmapImageRep - Creating from a CGImage, Getting a CGImage

AppKit also includes a new initializer method for creating an NSBitmapImageRep from a Quartz CGImage:
- (id)initWithCGImage:(CGImageRef)cgImage;
An NSBitmapImageRep that is created in this way retains the given CGImage as its primary underlying representation, reflecting the CGImage's properties as its own and using the CGImage to draw when asked to draw.

Since the CGImageRef is simply retained by the NSBitmapImageRep, its resident image data is referenced instead of being copied. If the NSBitmapImageRep is asked for its pixel data (via a -bitmapData or -getBitmapDataPlanes: message), it will oblige by unpacking a copy of the CGImage's content to an internal buffer. The resultant pixel data should be considered read-only. Changing it through the returned pointer(s) will not cause the CGImage to be re-created.

Regardless of how it was created, an NSBitmapImageRep can now be asked to return an autoreleased CGImage representation of itself:
- (CGImageRef)CGImage;
Using this method may cause the CGImage to be created, if the NSBitmapImageRep does not already have a CGImage representation of itself. Once created, the CGImage is owned by the NSBitmapImageRep, which is responsible for releasing it when the NSBitmapImageRep itself is deallocated.

NSBitmapImageRep - Fallback Background Color for JPEG output

NSBitmapImageRep.h declares an additional NSBitmapImageRep attribute, available on Leopard, that can be used to specify a fallback background color to use when encoding an image to a data/file format that doesn't support alpha channels:
NSString* NSImageFallbackBackgroundColor  AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // JPEG output (NSColor)
This allows clients to specify the background color to use for the image data that's written out, in the event that the NSBitmapImageRep being encoded has an alpha channel. It corresponds to the kCGImageDestinationBackgroundColor property defined by the ImageIO framework.


New NSBox properties

NSBox now supports generic colored box drawing via four new properties:
- (CGFloat)borderWidth;
- (void)setBorderWidth:(CGFloat)borderWidth;
- (CGFloat)cornerRadius;
- (void)setCornerRadius:(CGFloat)cornerRadius;
- (NSColor *)borderColor;
- (void)setBorderColor:(NSColor *)borderColor;
- (NSColor *)fillColor;
- (void)setFillColor:(NSColor *)fillColor;
These properties only apply to boxes of type NSBoxCustom. The word 'custom' refers to these boxes having nothing to do with the human interface guidelines for Mac OS X. As such, the style should be used sparingly. It may be useful for simple custom drawing, or as a placard of some sort, but should not be used as a general control grouping box.

This box style may be useful in conjunction with the new Leopard class NSCollectionView. NSCollectionView supports single and multiple selection, but doesn't draw anything to indicate selection itself. That responsibility is delegated to the views it contains. A custom box can be used to draw a simple selection highlight.

One problem with a using NSBox to draw the selection highlight in NSCollectionView is that one needs to be able to toggle drawing on and off corresponding to the selected property of NSCollectionViewItem. To support this case, we've added one more property to NSBox:
- (BOOL)isTransparent;
- (void)setTransparent:(BOOL)flag;
Bind the transparent property to the selected property of NSCollectionViewItem. The semantics of NSBox's transparent property of are the same as the semantics of NSButton's transparent property.


NSGradient

Instances of the new NSGradient class support the creation of a color gradient with multiple color stops, and drawing itself as a linear or or radial gradient. A gradient defines a transition between colors at locations from 0.0 to 1.0. The starting color is considered to be at location 0.0, the ending color at 1.0. Additional color stops can be located at positions between 0.0 and 1.0. The color gradient is able to draw itself in a variety of ways, as a linear gradient and as a radial gradient.

The designated initializer takes an NSArray of colors for the gradient and a C array containing CGFloats with values between 0.0 and 1.0 to indicate the locations of the colors within the gradient. In addition, the color space of the gradient is provided. All provided colors will converted to this color space. If no color stop is located at 0.0 or 1.0, the color of the nearest color stop will be drawn to the appropriate end of the gradient.
- (id)initWithColors:(NSArray *)colorArray atLocations:(CGFloat *)locations colorSpace:(NSColorSpace *)colorSpace;
Three additional initializers handle common cases. The first takes a start and end color to create a two-color gradient. The second takes an array of colors, and will space the provided colors at equal intervals from 0.0 to 1.0. The third takes a nil-terminated variable length list of color/location pairs. The generic RGB color space will be used for gradients created with these init methods.
- (id)initWithStartingColor:(NSColor *)color endingColor:(NSColor *)endingColor;
- (id)initWithColors:(NSArray *)colorArray;
- (id)initWithColorsAndLocations:(NSColor *)firstColor, ...;
Once a color gradient is defined, it can be used to draw both linear and radial gradients.

Drawing methods allow for the drawing of linear and radial gradients within a rectangle and path. Given a rectangle or path, the convenience method will calculate appropriate locations and do the appropriate clipping.

For linear gradients, the angle of the gradient within the rect or path can be specified.
- (void)drawInRect:(NSRect)rect angle:(CGFloat)angle;
- (void)drawInBezierPath:(NSBezierPath *)path angle:(CGFloat)angle;
For radial gradients, a relative center position can be specified, using NSZeroPoint specifies a radial gradient centered within the rectangle or path bounding rect. The radial gradient drawn by these methods always draws from an inner point to an outer circle, with the inner point at the center of the outer circle.

The relative center position allows a developer to proportionally adjust the center of the radial gradient with respect to the rectangle or path bounding rect. The relative center position maps the point -(1.0, -1.0) to the origin of the rectangle and the point (1.0, 1.0) to the maximum x and y values of the rectangle. The center of the bounding rect maps to a relative center position of (0, 0).
- (void)drawInRect:(NSRect)rect relativeCenterPosition:(NSPoint)relativeCenterPosition;
- (void)drawInBezierPath:(NSBezierPath *)path relativeCenterPosition:(NSPoint)relativeCenterPosition;
These drawing methods provide an easy way to draw gradient fills. Developers who wish other drawing behavior can use the primitive drawing methods described below, and create additional convenience methods as categories which calculate where the primitive methods should draw.

Two primitive drawing methods map closely to the drawing of Quartz shadings. Note that, just as with Quartz shadings, these primitive drawing methods perform no clipping before drawing.

Each primitive drawing method takes an options: argument. Two options are defined:
    NSGradientDrawsBeforeStartingLocation
    NSGradientDrawsAfterEndingLocation
These options control whether drawing extends before and after the gradient start and end locations. These options are only present for the primitive drawing methods, since the rect and path-centric drawing methods ensure that the entire rect or path is filled by the gradient. These drawing primitive methods draw a linear gradient and a radial gradient, respectively.
- (void)drawFromPoint:(NSPoint)startingPoint toPoint:(NSPoint)endingPoint options:(NSGradientDrawingOptions)options;
- (void)drawFromCenter:(NSPoint)startCenter radius:(CGFloat)startRadius
toCenter:(NSPoint)endCenter radius:(CGFloat)endRadius options:(NSGradientDrawingOptions)options;

A number of methods are provided primarily to allow for the creation of controls that edit/create gradients. These methods return the number of color stops, access the color and location of each stop, return the color space of the gradient, and determine the interpolated color at a particular location between 0.0 and 1.0 in the color gradient.
- (NSInteger)numberOfColorStops;
- (void)getColor:(NSColor **)color location:(CGFloat *)location atIndex:(NSInteger)index;
- (NSColorSpace *)colorSpace;
Overriding this method to provide different color values will not affect the underlying calculation of the color gradient, and will not affect how the color gradient is drawn.
- (NSColor *)interpolatedColorAtLocation:(CGFloat)location;

NSTableView/NSOutlineView

NSTableView and NSOutlineView now properly handle alpha backgroundColors. In addition, you can set the color to [NSColor clearColor] to see through to the area behind you (ie: make it transparent), but note that you must also set drawsBackground to NO on the containing NSScrollView, such as: [[tableView enclosingScrollView] setDrawsBackground:NO].

The documentation correctly states that one can programatically select multiple rows even if allowsMultipleSelection was set to NO, but this was actually not allowed. On Leopard linked applications, this is fixed, and one can call [table selectRowIndexes:indexes byExtendingSelection:YES] or [table selectRow:row byExtendingSelection:YES] and correctly select multiple rows, even if allowsMultpleSelection is set to NO. The documentation correctly states that one can progmatically deselect all rows regardless of whether an empty selection is allowed, but this was actually not allowed. This is also fixed for Leopard linked applications.

Tables now support inter-cell navigation as follows:

- Tabbing forward to a table focuses the entire table.
- Hitting Space will attempt to 'performClick:' on a NSButtonCell in the selected row, if there is only one instance in that row.
- Tabbing again focuses the first "focusable" (1) cell, if there is one.
- If the newly focused cell can be edited, editing will begin.
- Hitting Space calls 'performClick:' on the cell and sets the datasource value afterwards, if changed. (2)
- If a text cell is editing, hitting Enter will commit editing and focus will be returned to the tableview, and Tab/Shift-tab will commit the editing and then perform the new tab-loop behavior.
- Tabbing will only tab through a single row
- Once the last cell in a row is reached, tab will take the focus to the next focusable control.
- Back tabbing into a table will select the last focusable cell.

(1) A focusable cell is generally defined as [cell isEnabled] && [cell isSelectable] in a table column that is editable. However, NSTextFieldCells also check if the row is selectable, and tableView:shouldEditTableColumn:row: returns YES (if implemented by the delegate). NSImageCells cannot be focused. NSButtonCells only check if the [cell isEnabled].
(2) To make this work with NSPopUpButtonCell, performClickWithFrame:inView: is called.

NSTableView/NSOutlineView - Delegate Changes

The following two new Leopard delegate methods introduced at WWDC have been renamed from:
- (NSIndexSet *)tableView:(NSTableView *)tableView
selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes
byExtendingSelection:(BOOL)extend;
- (NSIndexSet *)outlineView:(NSOutlineView *)outlineView
selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes
byExtendingSelection:(BOOL)extend;
to:
- (NSIndexSet *)tableView:(NSTableView *)tableView
selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes;
- (NSIndexSet *)outlineView:(NSOutlineView *)outlineView
selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes;
In releases prior to Mac OS 10.5, for the following delegate method:
- (void)tableView:(NSTableView*)tableView didDragTableColumn:(NSTableColumn *)column;
the tableColumn parameter would incorrectly be the NSTableColumn that existed at the dragged column's original index. For applications linked on or after Leopard, the tableColumn will correctly be the column that was dragged.

NSOutlineView requires all of its items to be pointer unique, however, between a reload items that have the same hash value and are considered [NSObject isEqual], will preserve the expanded state for those items, even if they are not pointer equal.

Prior to Leopard, NSTableView and NSOutlineView would not clear out the drag pasteboard before calling:
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard;
or
- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard;
In Leopard, it will now clear out the pasteboard. You should declare the types you support for drag and drop with:
    NSMutableArray *types = [[pboard types] mutableCopy];
// Add our custom type and leave the ones NSOutlineView supports in the list.
[types addObject:MyType];
// Make ourselves the owner. For any types we don't handle we can forward to NSOutlineView/NSTableView.
[pboard declareTypes:types owner:self];
The new delegate method:
- (BOOL)tableView:(NSTableView *)tableView
shouldTrackCell:(NSCell *)cell
forTableColumn:(NSTableColumn *)column
row:(NSInteger)row;
allows a cell to be tracked, even if the row isn't selectable or selected. This allows you make a table view that will not allow any rows to be selected, but still allow the user to interact with the cells, as seen below (using outline view as an example):
- (BOOL)outlineView:(NSOutlineView *)ov shouldSelectItem:(id)item {
return NO;
}
- (BOOL)outlineView:(NSOutlineView *)ov shouldTrackCell:(NSCell *)cell forTableColumn:(NSTableColumn *)column item:(id)item {
return YES;
}
Another example is to not allow check box button cells to change the selection, but still allow them to be clicked on and tracked. [NSApp currentEvent] will always be correct when this method is called, and you may use it to perform additional hit testing of the current mouse location. See the DragNDropOutlineView demo application for an example of how to do this.

In outlineView:shouldSelectItem: and tableView:shouldSelectRow: you can now access clickedRow and clickedColumn to see what row and column was clicked on, if the method is being sent in response to an initial click event. At that point, [NSApp currentEvent] will also be valid.

For Leopard (and higher) linked applications, the following drag and drop delegate method will get called only when the dragOperation or drop location changes:
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView
validateDrop:(id <NSDraggingInfo>)info
proposedItem:(id)item
proposedChildIndex:(NSInteger)index;
Prior to Leopard, it would get called constantly, which is not needed.

NSTableView/NSOutlineView - Type to Select

NSTableView and NSOutlineView now support type to select (also known as incremental searching). See the header for the new delegate methods.

NSTableView/NSOutlineView - Disabled state

For applications linked on Leopard, NSTableView and NSOutlineView now properly supports [NSControl isEnabled] and [NSControl setEnabled:(BOOL)flag].

A disabled NSTableView will:
- Refuse first responder status
- Not do any tracking in mouseDown
- Not allow editing of any columns
- Disable selection of rows and columns
- Disable draggging of rows and columns
- Not allow reordering of rows or columns
- Call [NSCell setEnabled:NO] for each cell that is being drawn
- Additionally, NSTextFieldCell's will be drawn with a disabled text color
- Draw the header view with a disabled text color

Setting an NSTableView/NSOutlineView to be enabled or disabled will walk all of the NSTableColumns enable or disable the dataCell and headerCell of that NSTableColumn. You can override the enabled/disabled state of cells in the willDisplayCell delegate method.

NSTableView/NSOutlineView - Cell Hit Testing, Drag and Drop, and Cell Editing

NSTableView now uses the new NSCell hit testing API to perform certain actions. Custom NSCell subclasses in applications that link on or after Leopard should properly implement -hitTestForEvent:inRect:ofView:; see NSCell.h for more information.

NSTableView performs hit testing in the cells to do the following actions:
- Drag and Drop: NSTableView calls hitTestForEvent:inRect:ofView in canDragRowsWithIndexes:atPoint. If the hit cell returns NSCellHitTrackableArea, the particular row will be tracked instead of dragged.
- Cell Editing: When NSTableView recieves a mouse down, single-click editing of text (like Finder) will happen if there is only one row selected, and the cell returns NSCellHitEditableTextArea.

See the DragNDropOutlineView demo application for an example of how to properly implement the NSCell methods.

NSTableView/NSOutlineView - Single click to edit

NSTableView and NSOutlineView now behave like Finder and allow a single click to put it into edit mode. This is done by calling -hitTestForEvent:inRect:ofView: and checking if the cell returns NSCellHitEditableTextArea. This allows you to set a doubleAction and perform some different task when the doubleAction is invoked (for instance, Finder opens files on a double click, and edits via a single click). If the doubleAction is not set, editing is still allowed via a double click.

NSTableView/NSOutlineView - Contextual menu support

NSTableView and NSOutlineView now have better contextual menu support. Please see the DragNDropOutlineView demo application for an example of how to properly do contextual menus with a TableView.

The key thing to note is that clickedRow and clickedColumn will now both be valid when a contextual menu is popped up. In addition, one can dynamically set the popup menu for a particular cell/column in the delegate method willDisplayCell:. NSTableView handles this by properly overriding menuForEvent:, setting the clickedRow/clickedColumn, and calling [NSCell menuForEvent:inRect:ofView] to find the correct menu. If no menu was returned, the menu for the NSTableView will be used. If one right clicks on a selection of items, all the items are highlighted. One should check to see if clickedRow is one of the selected rows, and if it is then do the menu operation on all the selected rows. The clickedRow and clickedColumn properties will still be valid when the action is sent from the NSMenuItem.

NSTableView/NSOutlineView - rowHeight

For applications linked on or after Leopard, the default row height for NSTableView's created with [[NSTableView alloc] init] will be set correctly for the initial font used by the default cell inside an NSTableColumn. NSTableView's created in Interface Builder on Tiger and greater already have the rowHeight correctly set for the particular font selected.

NSTableView/NSOutlineView - Full Width cells and group row look

NSTableView and NSOutlineView now have new delegate API to create the "group row" look. See NSTableView.h/NSOutlineView.h and the DragNDropOutlineView demonstration app for an example of how to use the new delegate methods.

tableView:dataCellForTableColumn:row: allows you to easily return a custom cell for any particular row, without having to subclass NSTableColumn. In addition, you can return a cell that will span the entire width of a table view (a "full width" cell).

If you return YES from tableView:isGroupRow: then the "group row" style will be drawn for that row. The "group row" style is dependent on the selectionHighlightStyle, and will draw differently for different styles.

NSTableView/NSOutlineView - Column rects

Since table columns now can be hidden (see below), the columnsInRect: method is being deprecated in favor of one that can return a set of column indexes rather than just a range:
- (NSIndexSet *)columnIndexesInRect:(NSRect)rect;

NSTableView /NSOutlineView - selectionHighlightStyle:

There is a new property called selectionHighlightStyle with the main purpose of allowing the "source list" highlighting style to be automatically adopted with little or no work.

If the style is set to NSTableViewSelectionHighlightStyleSourceList, then it will draw selected items with a "source list" highlighting style. Additionally, setting NSTableViewSelectionHighlightStyleSourceList on an NSOutlineView will also change the following properties to match the HI guidelines: The backgroundColor, indentationPerLevel, rowHeight and intercellSpacing. If you would like to change any of these properties, you must do so after calling [outlineView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList]. Additionally, combining "group rows" with the "source list" highlighting style will produce the standard section header look by implementing -outlineView:isGroupItem: and returning YES for the section headers. To not show a disclosure triangle, override -frameOfOutlineCellAtRow: and return an empty rect. You will also want to not allow it to be collapsed by returning NO from the delegate method -outlineView:shouldCollapseItem:. When the disclosure triangle is not shown for a section header, the item will automatically be unindented. When using the "source list" highlighting style, it is recommended that you draw your cells with 16x16px icons.

One additional note: selected items in "source lists" should be bold, and NSTableView will automatically convert an NSString value inside an NSTextFieldCell to be bold; however, this may conflict with formatters with the cell that expect an NSString instead of an NSAttributedString. If editing a cell makes the title draw blank, be sure to examine any formatters attached to the cell.


NSTableColumn

NSTableColumns now have:
- (void)setHidden:(BOOL)hidden;
- (BOOL)isHidden;
Columns which are hidden still exist in the the -[NSTableView tableColumns] array and -[NSTableView numberOfColumns] includes columns which are hidden. Hidden columns are not included in the indexes returned by the new columnIndexesInRect: method.

The state is saved out with the other autosave information if autosaveTableColumns is set to YES.

You can also now set the tooltip for the column header:
- (void)setHeaderToolTip:(NSString *)string;
- (NSString *)headerToolTip;

NSSavePanel/NSOpenPanel

NSSavePanel and NSOpenPanel now support drag and drop reordering and insertion of items in the sidebar. In addition, you can drag and drop any of the files into the sidebar or Finder.

NSSavePanel and NSOpenPanel now support an "icon view" mode. The icon selection allows the user to change icon size and label location.

For a selection of files, cmd-i will show the Finder info panel and cmd-r will reveal the location in Finder.

NSSavePanel and NSOpenPanel can once again show hidden files by setting a user default. For instance you can use 'defaults write -g AppleShowAllFiles 1' to display hidden files in the panels.

In NSSavePanel, Ctrl-Tab will now quickly tab you to the file list from the name editing text. Ctrl-Shift-Tab will take you back to where you were.

NSSavePanel and NSOpenPanel now have advanced search options. Click the plus button located to the right of the search location buttons to customize the search. Searches can now be saved to the sidebar for use in all applications, or just your application. You can also edit existing searches and resave them.

NSOpenPanel now allows you to browse your iLife and related media. The appropriate media items in the sidebar are displayed based on the types passed to the open panel.


NSPathControl, NSPathCell, NSPathComponentCell

AppKit now has a new control that allows you to easily represent a path, be it a file system path, or a virtual path.

NSPathControl supports three styles:
- NSPathStyleStandard, the same style seen in the NSOpenPanel/NSSavePanel/Finder when a search is done and a file is selected.
- NSPathStyleNavigationBar (aka "breadcrumb"), which is similar to what iTunes has in the iTunes Music Store.
- NSPathStylePopUp, a style that only shows the last path component and uses a popup to display the full path and allow the user to choose a new path.

The primary interaction with the control is done via [NSPathControl setURL:url], which is a wrapper around [NSPathCell setURL:url]. The control automatically supports drag and drop, which can be further customized via delegate methods. The cell's properties can easily be modfied via accessor methods. The NSPathCell also works well in an NSTableView/NSOutlineView, however, the automatic animation will not work when the style is set to NSPathStyleStandard or NSPathStyleNavigationBar. Please refer to the corresponding header files for more information.

NSPathCell

When NSPathCell has the style set to NSPathStylePopUp, if it contains 0 pathComponentCells, it will use the placeholderString for the pop up button. Previously, it would not use a pop up button, and would simply draw the string.

When NSPathCell has the style set to NSPathStylePopUp, if the cell has a border (ie: [cell setBordered:YES]), it will draw the "round bezel" border around the pop up button. Previously, it would not ever draw a border.


NSDictionaryController

There is a new controller class: NSDictionaryController. It can be used to edit dictionary contents as key/value pairs in table views, for example. The controller allows to configure "included keys" (keys always visible in the displayed array of key-value pairs, whether present in the content dictionary or not) and "excluded keys" (keys always hidden in the displayed array of key-value pairs). To support localization, dictionary controllers allow to set a localized key dictionary, which can specify a table of display labels for each key (and will typically be read from a string table).

NSDictionaryController offers bindings for the content dictionary as well as the included keys, excluded keys, and localized key dictionary (which means that the object that provides the content dictionary can also provide the included, excluded, and localized keys at the same time, potentially dynamically computed based on the content dictionary).


NSTreeController

NSTreeController now provides better access to the displayed content objects in the form of NSTreeNode objects. NSTreeNode is designed to represent an individual node in a tree data structure. Each NSTreeNode instance wraps a single representedObject and maintains a reference to its parent and child nodes. NSTreeNodes also know where they are relative to the tree's root node. This location information represented by an NSIndexPath and is returned by NSTreeNode's indexPath method.

In Tiger, NSTreeController's arrangedObjects method returned an opaque proxy that developers weren't supposed to use directly. In Leopard, the arrangedObjects method returns a proxy that responds to the NSTreeNode methods
- (NSArray *)childNodes;
and
- (NSTreeNode *)descendantNodeAtIndexPath:(NSIndexPath *)indexPath;
With these two methods, it is now possible to navigate the tree controller's arranged tree.

By using NSTreeNodes to wrap represented objects, an NSOutlineView bound to an NSTreeController can display the same represented object in multiple rows.

When bound to an NSTreeController, NSOutlineView's row items will be NSTreeNodes. This applies to your outline view delegate or datasource - the items passed to your delegate/datasource methods are NSTreeNode instances. The same is true for NSBrowsers bound to an NSTreeController and the browser delegate methods. You can use the node's location (indexPath) in the arranged tree being displayed, or the underlying model object for the node (representedObject) in order to make decisions in delegate code.


Leveraging NSTreeNode, NSTreeController also exposes two new methods for moving nodes from one place to another.
- (void)moveNode:(NSTreeNode *)node toIndexPath:(NSIndexPath *)indexPath;
- (void)moveNodes:(NSArray *)nodes toIndexPath:(NSIndexPath *)startingIndexPath;
When a node is moved from one location to another, its represented objects in the new and old parent will be updated using Key Value Coding and the tree controller's childrenKeyPath. You are encouraged to use these methods to modify relationships in the tree, rather than modify the represented objects directly. This will ensure better selection handling and improve outline view's ability to maintain item "expanded" state.

NSTreeNodes can be used outside of an NSTreeController to maintain an ad hoc tree of objects. When created outside of a tree controller, the tree nodes do not have a concept of a childrenKeyPath and so don't automatically update relationships of their model objects.

NSTreeController selectedObjects accessor not accurate when not deleting objects through NSTreeController remove:

There is a known bug in NSTreeController's selectedObjects accessor that means that under certain circumstances, the contents of selectedObjects does not accurately reflect the controller selection. This can happen when an object is deleted by mutating the relationship of one of the treeController's model objects instead of using the deleteObjectAtArrangedIndexPath: method of the remove: IBAction method. If this bug affects you, a potential workaround is to replace use of code like
[treeController selectedObjects]
with code like
[[treeController selectedNodes] valueForKey:@"representedObject"]
This will produce an array that accurately represents the treeController's selected objects, even if objects are deleted via manipulating model objects' relationship containers.


NSArrayController

NSArrayController now has API to automatically re-sort/filter content arrays when the objects in it change.
- (void)setAutomaticallyRearrangesObjects:(BOOL)flag;    // default: NO
- (BOOL)automaticallyRearrangesObjects;
- (NSArray *)automaticRearragementKeyPaths;
This method returns the array of key paths that trigger automatic rearranging from the sort descriptors and filter predicates; subclasses may override this method to customize the default behavior (for example if additional arrangement criteria are used in custom implementations of -rearrangeObjects).
- (void)didChangeArrangementCriteria;
This method is invoked by the controller itself when any criteria for arranging objects change (sort descriptors or filter predicates) to reset the key paths for automatic rearranging; subclasses should invoke this method if additional arrangement criteria are used in custom implementations of -rearrangeObjects and those criteria change.


NSObjectController setUsesLazyFetching:

NSObjectController and its subclasses, when in entity mode, can now fetch "lazily." With the behaviour enabled, the controller will try to fetch only a small amount of data from available persistent stores. This can provide a huge improvement in memory use when dealing with a lot of data on disk but just a subset of that data needs to be in memory.

When set to use lazy fetching, a controller will fetch objects in batches - default batch size is 96. You can change the default batch size for your application by setting a value for the the user default "com.apple.CocoaBindings.LazyFetchBatchSize". If you have table views bound to an array controller set to use lazy fetching, the size of the controller's batch size will grow as the table views' visible row count grows.

Add, Insert, and Remove operations on a controller that uses lazy fetching behave similarly to the same operations on a regular controller. The difference is that it is faster to sort an arraycontroller using lazy fetching if
- all of the keys in the sortDescriptors array are modeled, non transient properties
- all of the selectors in the sortDescriptors array are compare: or caseInsensitiveCompare:
- there are no changes in the controller's managed object context


KeyValueObserving notifications for IBOutlets during Nib loading

Prior to 10.5, when an outlet instance variable was set on an object during Nib loading, no corresponding KVO notification would be sent. This meant that registered KVO observers and bindings wouldn't be notified that the observed ivar changed. This could lead to bad behaviour or displaying stale data.

For applications linked on or after 10.5, observers will get KVO notifications when the IBOutlet instance variable is set during Nib loading. Observers should expect to get KVO notifications for an observed object before the observed object's awakeFromNib method is called.


NSAccessibility

Cocoa/Carbon Integration

In Leopard, Cocoa accessibility information is now correctly reported for Cocoa windows used in a Carbon application, as well as Cocoa views embedded into Carbon windows via the new Carbon HICocoaView.

Toolbar Accessibility

Image-based toolbar items have always appeared in the accessibility hierarchy as a single AXButton, with the label of the toolbar item serving as the AXTitle of the button. In Leopard, certain configurations of view-based toolbar items will also be represented automatically in the accessibility hierarchy in this manner:
- The item view is an NSButton that reports itself to accessibility as an AXButton.
- The item view is an NSSegmentedControl with a single segment.
- A toolbar item group has an NSSegmentedControl as its view, and has one labeled subitem per segment.

For view-based toolbar items that do not fit into these special cases, the label of the toolbar item will be automatically attached to the item's view as an AXTitleUIElement in Icon & Text display mode, and as the view's AXDescription in Icon Only mode.

New Accessibility Constants

NSAccessibilityGridRole is a role that should be used for user interfaces like thumbnails and media browsers that present a grid of items. The children of a grid are ordered. A grid is like an accessibility list with the addition of three new attributes: NSAccessibilityRowCountAttribute and NSAccessibilityColumnCountAttribute which return the number of rows and columns in the grid, respectively and NSAccessibilityOrderedByRowAttribute which returns a boolean number value indicating whether the children are ordered row major, or column major.

NSAccessibilityTimelineSubrole is a subrole of NSAccessibilitySliderRole and should be used for media timelines, such as QuickTime movie or audio and video playback and editing controls. A timeline can have multiple value indicators as children to represent different markers on the timeline - for instance the current time, an in point, and an out point for video editing. If multiple value indicators are present, each should have an accessibility description.

The optional attribute NSAccessibilityValueDescriptionAttribute should be used when the value of an element does not provide enough information for assistive applications. For instance, the sliders in the Energy Saver preference pane represent values that include "1 minute", "1 hour", "2 hours 20 minutes", and "never." The numeric value of the slider does not convey this information, so the optional value description attribute should be used. The string values of the attribute should be localized, lower case, and as brief as possible to convey the information.

The optional attribute NSAccessibilitySelectedTextRangesAttribute returns an array of ranges of selected text. NSTextView supports selection of multiple ranges of text as well as this new optional attribute. The existing NSAccessibilitySelectedTextRangeAttribute still returns the first selected text range.

NSAccessibilityDisclosureTriangleRole is a new accessibility role for disclosure triangles.


NSColor

Asking for colorWithAlphaComponent: on NSDeviceRGBColorspace and NSDeviceWhiteColorSpace colors would return results in their calibrated counterparts. This has now been fixed.


NSColorSpace

NSColorSpace now has methods to create from and return a CGColorSpace. Note that the method to return a CGColorSpace might return NULL if the NSColorSpace cannot be represented as a CGColorSpace. In addition, creating an NSColorSpace from a CGColorSpace does not guarantee future pointer identity of the CGColorSpace.

NSColorSpace also has methods to return sRGB and Adobe1998 color spaces.

A CMProfileRef leak was fixed in -[NSColorSpace initWithICCProfileData:].


NSColorPanel

NSColorPickingCustom protocol now enables specifying a tooltip for the toolbar button:
- (NSString *)buttonToolTip;
and setting the minimum size for your picker:
- (NSSize)minContentSize;
NSColorPanel will not allow resizing smaller than this size. By default, you will not have to do anything if you properly setup the autosizing attributes in IB for your view.


Services

We now have a number of NSErrors for reporting services errors. Like other NSErrors in NSCocoaErrorDomain, these errors come ready with user-presentable error messages, and for the most part they will automatically be bubbled up to the user via the Cocoa error presentation machinery, so no developer action is needed. The individual error codes can be found in AppKitErrors.h.

The NSUpdateDynamicServices() function is functional in Leopard. When NSUpdateDynamicServices() is called, applications should see updated services immediately, without needing to be quit and relaunched. Services are also updated at login.


NSScroller

To help improve API uniformity, NSScroller's -setFloatValue:knobProportion: method is being deprecated in favor of using the separate -setDoubleValue: and -setKnobProportion: accessors that have been added in Leopard. To maintain binary compatibility, AppKit will continue to invoke overrides of -setFloatValue:knobProportion:. Code that targets Mac OS 10.5 and later should use -setDoubleValue: and -setKnobProportion: instead, and eliminate any overrides of -setFloatValue:knobProportion:. Code that needs to remain compatible with Mac OS 10.4 and earlier should continue to use -setFloatValue:knobProportion:.


NSUserInterfaceValidations

The interface declarations for NSApplication, NSButton, NSDocument, NSDocumentController, NSMatrix, NSMovieView, NSTableView, NSTextField, and NSWindow now correctly show their conformance to the NSUserInterfaceValidations protocol.


NSTabView

Prior to Leopard, an NSTabViewItem added to multiple tab views would produce inconsistencies. For applications built on or after Leopard, tab view items are automatically removed from the old tab view when added to a new one.


NSLevelIndicator behavior changes

Prior to Mac OS X 10.5, NSLevelIndicatorCell ignored its editable flag; all cells were effectively editable. In 10.5 we pay attention to the flag for apps linked on Leopard and later. The default value of -[NSLevelIndicator isEditable] is and has been NO, so it is necessary to explicitly set the flag for cells that should be editable.


NSRulerView

In the past, the method -moveRulerlineFromLocation:toLocation: immediately handled drawing for temporary lines on the ruler which could cause performance degradation when temporary lines were drawn in conjunction with automatic beam sync. Now, the temporary lines are drawn during -drawRect:.

Usage of -moveRulerlineFromLocation:toLocation: remains unchanged with a few caveats. Each new line location is drawn no more than once. There is however now no guarantee that every new line location will be drawn - if a particular location is set to be drawn and then set to be erased before -drawRect: occurs, that location is never drawn. In addition, any subclass that uses the default -moveRulerlineFromLocation:toLocation: but implements -drawRect: will have to override -moveRulerlineFromLocation:toLocation: to handle ruler line drawing in their own -drawRect: method.


NSBrowser

NSBrowser now supports drag and drop. Please see the AppKit example application "SimpleBrowser" for an example of how to use the API and read the comments in NSBrowser.h. The API is very similar to the drag and drop API for NSTableView.


NSTokenField

NSTokenField has been rewritten and numerous bugs were fixed. Tokens now show the pulldown menu icon regardless of selection state. Applications can set the NSTokenAttachmentUsesDynamicPulldownIcon preference setting to preserve the pre-Leopard behavior.


NSGraphicsContext

NSGraphicsContext now has color rendering intent API:
- (NSColorRenderingIntent)colorRenderingIntent;
- (void)setColorRenderingIntent:(NSColorRenderingIntent)renderingIntent;

NSBezierPath

There are now convenience methods for creating rounded rectangular paths:
+ (NSBezierPath *)bezierPathWithRoundedRect:(NSRect)rect xRadius:(CGFloat)xRadius yRadius:(CGFloat)yRadius;
- (void)appendBezierPathWithRoundedRect:(NSRect)rect xRadius:(CGFloat)xRadius yRadius:(CGFloat)yRadius;

-containsPoint: method now follows the winding rule returned from -windingRule.


NSProgressIndicator

NSProgressIndicatorSpinningStyle and determinate progress indicators now render pie-style determinate indicator found in Mail and Xcode. Spinning-style progress indicators now properly use larger images for NSRegularControlSize rendering. Applications dynamically creating progress indicators for 16x16 image now need to explicitly set the control size to NSSmallControlSize.

The threaded animation setting for newly created indicators is on by default now regardless of the style.


NSWorkspace

NSWorkspace method selectFile: inFileViewerRootedAtPath: no longer follows symlinks. It will now show the symlink in the Finder instead of what it points at. If you wish to show the linked file, use -[NSString stringByResolvingSymlinksInPath] to resolve any symlinks before calling the NSWorkspace method.

The setIcon: forFile: options: method on NSWorkspace will set 512x512 icons, unless NSExclude10_4ElementsIconCreationOption is set in options.

The NSWorkspace method getFileSystemForPath: isRemovable: isWritable: isUnmountable: description: type: now returns meaningful values in description: and type:. (Prior to Leopard, these values were always nil.)


NSMovie/NSMovieView in 64-bit

For 64-bit, NSMovie and NSMovieView have been deprecated in favor of QTKit's QTMovie and QTMovieView. However, because NSMovies may exist in archives that may need to be read or written by 64-bit applications, limited functionality has been provided for NSMovie in 64-bit to enable this.

NSMovies can be unarchived in 64-bit as expected, but because of the absence of NSMovieView, they are not useful on their own. To facilitate the transition to QTMovie and QTMovieView, the existing -QTMovie method on NSMovie has been modified to return a reference to a QTMovie object in 64-bit. This QTMovie instance contains the same movie data that the original NSMovie contained.

For backwards compatibility purposes, an NSMovie can be created in 64-bit using the -initWithMovie: method, which has been modified for 64-bit to accept a QTMovie instance. Archives containing these NSMovie objects are backwards compatible with NSMovies in 32-bit.

Please note that NSMovie in 64-bit does not support movies referenced by URLs. -initWithCoder: will properly read archives containing these movies, but will return nil instead of a valid NSMovie object.


TextEdit

The TextEdit application in 10.5 has been changed to use NSDocument. It thus provides many features provided or enabled by NSDocument, such as correct file tracking and autosaving. In addition it continues to be a showcase for the Cocoa text system, and highlights many of its new features. You can find more info in the README file, in /Developer/Examples/AppKit/TextEdit.


"open" tidbit

Thank you for reading this far! If you are a Terminal or command line user, be sure to check out some of the new features in "open". For instance "open -h" will now search and open header files in Xcode (or your default editor for header files).



Notes specific to Mac OS X 10.4

New AppKit features in Tiger

The following are some of the new features in Tiger.


Foundation:

Some of the other new Mac OS X APIs to be aware of:



NSDatePicker and NSDatePickerCell

AppKit has a new date/time control, whose API is declared in NSDatePicker.h and NSDatePickerCell.h. Currently this class pair provides two styles of date/time control: a "text field and stepper" style that is similar to Carbon's familiar "ClockDate" control, and a graphical "clock and calendar" variant like those that appear in the "Date & Time" System Preferences panel.

A date picker's "objectValue" is an NSDate. The "-dateValue/-setDateValue:" accessor pair provides a type-specific equivalent to the inherited "-objectValue/-setObjectValue:" methods.

A date picker additionally has a "mode" attribute and a "timeInterval" attribute that are not currently used. These attributes exist to support the possibility of a "date range" control mode in the future. The time interval is not applicable, and always zero, when the control is in NSSingleDateMode (the only mode supported by the currently supplied control styles). In NSRangeDateMode, it will specify the duration of the range, which extends forward in time from the cell's dateValue.

An instance's "datePickerElements" attribute determines which components of its value it considers to be specified/specifiable. This setting is composed by bitwise-ORing together one or more of the "DatePickerElementFlag" values declared in NSDatePickerCell.h. It influences both display and editing behavior, as appropriate for the style of date control in use.
typedef unsigned int NSDatePickerElementFlags;
enum {
/* Time Elements */
NSHourMinuteDatePickerElementFlag = 0x000c,
NSHourMinuteSecondDatePickerElementFlag = 0x000e,
NSTimeZoneDatePickerElementFlag     = 0x0010,
    /* Date Elements */
NSYearMonthDatePickerElementFlag     = 0x00c0,
NSYearMonthDayDatePickerElementFlag     = 0x00e0,
NSEraDatePickerElementFlag         = 0x0100,
};
For a case where the "time" portion of the date value is not of interest, for example, this could be set to NSYearMonthDayDatePickerElementFlag. NSTimeZoneDatePickerElementFlag and NSEraDatePickerElementFlag have been declared for possible future use, and do not yet have any effect.

A date picker also has a "calendar", "locale", and "timeZone" that influence date display and editing. In Tiger, date picker functionality supports Gregorian format date display and editing only, but the calendar and locale settings will be used to support other calendars and date formats in the future.

A date picker has a "minDate" and a "maxDate" that can be used to impose a simple range constraint on the possible values the date picker can take. (Both default to nil, meaning that the date value is unconstrained.) Clients can impose more sophisticated constraints in addition to this basic range constraint by providing a delegate object that validates proposed changes to the cell's value. A date picker never allows itself to get into a state where its current value does not satisfy the constraints imposed by minDate, maxDate, and the delegate's validation method (if provided). Its -setObjectValue:/-setDateValue: and -setTimeInterval: accessors will likewise constrain their received parameters to valid values.

The signature of the optional delegate method is:
- (void)dateCell:(NSDatePickerCell *)aDatePickerCell
validateProposedDateValue:(NSDate **)proposedDateValue
timeInterval:(NSTimeInterval *)proposedTimeInterval;
If an date picker has a delegate assigned to it, and the delegate responds to this selector, this method will be invoked each time the user attempts to make a change to the date picker's value, giving the delegate the opportunity to approve, modify, or reject the change.

"proposedDateValue" points to the proposed new dateValue. "proposedTimeInterval" points to the proposed new timeInterval, which will always be zero if the cell is not in NSDateRangeMode. Implementors may find it helpful to consult the NSDatePickerCell's current dateValue and timeInterval for comparison, to determine which of these values (potentially one or both) the cell is proposing to change.

On entry to this delegate method, the start and end points of the proposed range are guaranteed to lie between the NSDatePickerCell's minDate and maxDate, and *proposedTimeInterval is guaranteed to be nonnegative. The delegate can leave *proposedDateValue untouched to accept it, or replace it with a pointer to another NSDate instance. (The replacement value should be autoreleased or otherwise not require a subsequent release.) Likewise, this method can leave the proposedTimeInterval untouched to accept it, or replace its value through the provided pointer.


NSTokenField and NSTokenFieldCell (Section updated since WWDC)

There is a new token field control that behaves like the address field in Mail.app. The new widget supports tokenizing based on a character set (comma by default) and on end editing. In addition, we join tokens for multiple selection using a comma if comma is in the tokenizing character set; Otherwise, we join with space. We invoke text completion after a specified delay.

The tokens are draggable. By default, we put the tokens on the pasteboard using the NSStringPboardType (joined in the same way as the multiple token selection).

Use NSControl/NSCell's setObjectValue: method to set the token field's array of represented objects. If the array contains objects other than NSStrings, you must implement the tokenField:displayStringForRepresentedObject: delegate method.

Known NSTokenField Issues:
- NSTokenFieldCell are not fully functional inside of an NSTableView/Matrix.
- NSTokenField's cell must be a subclass of NSTokenField.

Please refer to NSTokenField.h and NSTokenFieldCell.h for the complete API.


NSLevelIndicator/NSLevelIndicatorCell

There is now a level indicator control and corresponding cell class. This control can display one of 4 styles of capacity, ranking, or relevancy. The capacity style can either be continuous or discrete. The ranking appearance is similar to the iTunes 'stars' ranking and the relevancy ranking can be used to display search ranking in a table.

Depending on the style, there is different behaviour. For relevancy and continuous capacity, the bar is drawn to fill the cell frame and the min/max/value can be any value. For the discrete styles for capacity and rating level, all values are rounded to the nearest integer when displaying. For the rating level, the images are not stretched or shrunk. For the the discrete capacity, the cell will stretch each segment the same amount to fill the cell frame as much as possible. -cellSizeForBounds: will reflect this.

If the cell is editable, the value can be changed by having the user track using the mouse or using the up/down or left/right arrows though only the rating one should be used in that manner. Any of the styles can be marked editable or have tick marks though in most cases, it only the ranking style should be editable and the continuous capacity indictor needs tick marks.

By default, the cell image is nil but if -setImage:, it replaces the default stars with the custom image for the NSRatingLevelIndicatorStyle style. Setting the image has no effect on the other styles. The image is lightened for highlighted selection and the dots for empty spots are still drawn. The image is not stretched no space is added between images.

See the cell for descriptions of the control methods:
@interface NSLevelIndicator : NSControl
- (double)minValue;
- (void)setMinValue:(double)minValue;
- (double)maxValue;
- (void)setMaxValue:(double)maxValue;
- (double)warningValue;
- (void)setWarningValue:(double)warningValue;
- (double)criticalValue;
- (void)setCriticalValue:(double)criticalValue;
- (NSTickMarkPosition)tickMarkPosition;
- (void)setTickMarkPosition:(NSTickMarkPosition)position;
- (int)numberOfTickMarks;
- (void)setNumberOfTickMarks:(int)count;
- (int)numberOfMajorTickMarks;
- (void)setNumberOfMajorTickMarks:(int)count;
- (double)tickMarkValueAtIndex:(int)index;
- (NSRect)rectOfTickMarkAtIndex:(int)index;
@end
Here is the cell declaration:
enum {
NSRelevancyLevelIndicatorStyle,
NSContinuousCapacityLevelIndicatorStyle,
NSDiscreteCapacityLevelIndicatorStyle,
NSRatingLevelIndicatorStyle
} NSLevelIndicatorStyle;
@interface NSLevelIndicatorCell : NSActionCell
- (id)initWithLevelIndicatorStyle:(NSLevelIndicatorStyle)levelIndicatorStyle;
Create new cell with indicator style. Default for -init is NSRelevancyLevelIndicatorStyle. Default value and minimum value are 0, default maximum value depends on the style. For continuous styles, the maximum is 100.0. For discrete ones, it's 5.0
- (void)setLevelIndicatorStyle:(NSLevelIndicatorStyle)levelIndicatorStyle;
- (NSLevelIndicatorStyle)levelIndicatorStyle;
Get/set display style. Will not affect values. Setting will notify enclosing control to update.
- (double)minValue;
- (void)setMinValue:(double)minValue;
- (double)maxValue;
- (void)setMaxValue:(double)maxValue;
These are the same method names as NSSlider and set min/max values for ranking. Setting will notify enclosing control to update.
- (double)warningValue;
- (void)setWarningValue:(double)warningValue;
- (double)criticalValue;
- (void)setCriticalValue:(double)criticalValue;
These set and get the 'warning' and 'critical' values where the indicator goes from green to yellow to red. The order of the values determines which side is green and which side is red. If the critical value is greater than the warning value, then the indicator is green below the warning, yellow above that but below the critical, and red above. If the critical value is less than the warning value, the indicator is red when the value is below the critical value, yellow up to the critical value, and then green to the maximum value. If the values are the same, the indicator is always green.
- (void)setTickMarkPosition:(NSTickMarkPosition)position;
- (NSTickMarkPosition)tickMarkPosition;
- (void)setNumberOfTickMarks:(int)count;
- (int)numberOfTickMarks;
- (NSRect)rectOfTickMarkAtIndex:(int)index;
- (double)tickMarkValueAtIndex:(int)index;
These methods for tick marks are identical to the NSSliderCell APIs with the same behaviour. Set the number of ticks to 0 to not have any. Default is 0. An exception is raised if index is out of range. Setting will notify enclosing control to update if necessary.
- (void)setNumberOfMajorTickMarks:(int)count;
- (int)numberOfMajorTickMarks;
We also allow larger or 'major' tick marks. The count must be less than or equal to the number of tick marks. Setting will notify enclosing control to update if necessary. The major tick marks will be drawn in place of the minor ones.


Resolution Independent UI

As an ongoing effort, we're adding "resolution independent UI." This enables users to choose between more detail or larger user interface without actually having to change physical screen resolution.

In the AppKit we intend to concentrate on the "framework scaling" model (as described in the "Resolution Independent UI" release note), which by default causes windows to be scaled. One fundamental change in this mode is that when drawing to the screen, 1 point in the base window coordinate system is no longer necessarily the same as 1 pixel in the backing store of the window. Although we intend much of the support for this mode to be provided by the AppKit, there are some things applications will need to do themselves or watch out for, as outlined below.

For testing purposes developers can change the display resolution using the Quartz Debug application (located in the folder /Developer/Applications/Performance Tools). Note that because the work for supporting resolution independence in both Cocoa and Carbon is ongoing and not yet complete, there are various drawing problems when running with non-integral scale factors in Tiger. This is especially true when Quartz 2D Extreme acceleration is enabled.

Applying the scale factor

Windows are scaled using a transformation on the coordinate system of the top level view (the frameView). The dimensions of the frame of the frameView are equal to the dimensions of the window frame, as in the non-scaled case, but the dimensions of the bounds of the frameView are scaled by dividing the dimensions of the frame by the scaleFactor. For non-integral scaleFactors, the frame is kept integral but the bounds are allowed to have a fractional component. So, for example, a 100x100 window will have a frameView whose bounds is 80x80 for a userSpaceScaleFactor of 1.25. We consider the window frame to be in pixels, and the frameView bounds to be in points. Drawing within the window content is then done in points. Note that this implies that all views within the window are scaled, but we have decided that views whose only scaling is this base scaling for resolution independence purposes will return NO from -isRotatedOrScaledFromBase, so that scrolling, etc. will continue to go through the fast path.

Implications for view positioning and window sizing

Applications must not assume that the window frame and contained view frames use the same coordinate systems. For example, applications that use the window frame to position views will not get correct results. Likewise, applications that compute a change in window frame based on view size (eg. when adding an auxiliary view) will be incorrect. One mechanism for converting between coordinate systems correctly is -[NSView convertRect/Size/Point to/fromView:nil]. Another is -[NSWindow frameRectForContentRect:] and its inverse -[NSWindow contentRectForFrameRect:].

Images

Each imageRep that contains bitmap data indicates its own DPI, since it has both size in points and pixel width and height. An imageRep with 72 DPI has a 1-1 correspondence between points and pixels. An imageRep with 144 DPI has two times more pixels than points in both dimensions. We now create cachedImage reps with the scale factor of the destination window. For a window with a userSpaceScaleFactor of 1.25, a cachedImageRep of 100x100 points would report a size of 100x100, and a pixelWidth and pixelHeight of 125.

Compositing

Historically, compositing has been done in the base coordinate system, regardless of the destination coordinate system. To allow composite: to continue to work in a resolution independent environment, we define the base coordinate system of scaled windows to include the current scale factor.

Example 1 - compositing 72dpi 100x100 source image to view in 1.25x scaled window

A 72dpi 100x100 source image will contain 100x100 pixels. When composited into a 100x100 rect in a view in a scaled window, this image will be scaled to fill 125x125 pixels in the window using the appropriate interpolation algorithm. Any coordinate transforms on the destination view aside from window scaling will be ignored.

Example 2 - compositing 90dpi 100x100 source image to view in 1.25x scaled window

A 90dpi 100x100 source image will contain 125x125 pixels. When composited into a 100x100 rect in a view in a scaled window, this image will exactly fit 125x125 pixels in the window, so no interpolation will be needed. Any coordinate transforms on the destination view aside from window scaling will be ignored.

Example 3 - creating cached image rep from 72dpi 100x100 source image

A 72dpi 100x100 source image will contain 100x100 pixels. The cached image rep will be created with size 100x100, but will hold 125x125 pixels. The source image will be scaled to fit the pixel size of the cached image rep, using the appropriate interpolation. When the cached image rep is later drawn into a scaled window, this will be a 1 to 1 copy from cached image rep pixels to destination window pixels.

Dealing with non-integral view coordinates

In the past, view coordinates have been modified to land on integral boundaries in order to use an exact number of pixels. In a scaled window, any computation to put a view on integral boundaries should be done in the window coordinate system (pixels) then converted to the view coordinate system.

API for "application scaling" mode

The following API allow applications to use their own techniques to achieve resolution independence themselves. One use of this API might be to generate an inverse scaling on the bounds of a view that wants to draw in pixels rather than points.

This is the default scaling from user space to device space on the given screen:
@interface NSScreen : NSObject
...
- (float)userSpaceScaleFactor
...
@end
Since the scale factor gets applied to individual windows, we also provide a method to ask a window for its scaling. By default, this scale factor will be equal to the scale factor of the NSScreen on which the window was created, or the highest scale factor of the available NSScreens if no screen was specified at creation time. (Note that for the foreseeable future the scale factor of all NSScreens will be equal at any given time):
@interface NSWindow : NSResponder
...
- (float)userSpaceScaleFactor
...
@end
It might also be necessary to allow creation of windows without a scale factor, especially for custom windows. You can create an unscaled window by specifying a styleMask of NSUnscaledWindowMask at creation time.

An unscaled window would then return 1.0 for -userSpaceScaleFactor.

Impact on existing API

Both NSWindow and NSScreen define a -deviceDescription method, This method returns an NSDictionary containing a NSDeviceResolution key. NSDeviceResolution has historically contained an NSSize of (72.0, 72.0). On a scaled system, NSDeviceResolution will contain an NSSize of (72.0*userSpaceScaleFactor, 72.0*userSpaceScaleFactor).


NSAnimation

This base class implements timing for animation in Cocoa. There is one subclass available for view animation. The animation can run in the main event thread in blocking mode (i.e. not returning until done), in non-blocking mode so that events are still accepted and in an separate private thread.
typedef enum {
NSAnimationEaseInOut, /* s-curve, default */
NSAnimationEaseIn,
NSAnimationEaseOut,
NSAnimationLinear
} NSAnimationCurve;
typedef enum {
NSAnimationBlocking,
NSAnimationNonblocking,
NSAnimationNonblockingThreaded
} NSAnimationBlockingMode;
typedef float NSAnimationProgress;                      // value in range 0..1
extern NSString *NSAnimationProgressMarkNotification;   // has single entry in user info dictionary
extern NSString *NSAnimationProgressMark; // NSNumber(float) with NSAnimationProgress
@interface NSAnimation
- (id)initWithDuration:(NSTimeInterval)duration animationCurve:(NSAnimationCurve)animationCurve;
- (void)startAnimation;
- (void)stopAnimation;
- (BOOL)isAnimating;
Starts and stops the animation. Doesn't reset the progress when stopped. If at a progress of 1.0, calling -startAnimation starts again at progress 0.0. You can play an animation with no view, target or action. If the mode is set to NSAnimationBlocking, then -startAnimation only returns after the animation has run. The delegate can still stop the animation while running if necessary. When -startAnimation is called, the animation retains itself and then is autoreleased on -stopAnimation.
- (NSAnimationProgress)currentProgress;
- (void)setCurrentProgress:(NSAnimationProgress)progress;
Set/get the current progress (values 0.0...1.0). Can change while running. Out of range values are pinned to 0.0 or 1.0. The -setCurrentProgress method is called while playing to change the progress for the next frame. Subclasses should override to get the value and do their action. This action may be in a secondary thread if requested.
- (void)setDuration:(NSTimeInterval)duration;
- (NSTimeInterval)duration;
Set/get the duration of the effect. Duration is in seconds. Can change while running. Negative values raise an exception. If the duration set is past the current time and the animation is playing, the animation is ended.
- (NSAnimationBlockingMode)animationBlockingMode;
- (void)setAnimationBlockingMode:(NSAnimationBlockingMode)animationBlockingMode;
Set/get mode for running animation. Will take effect the next time the animation is started. Has no effect if animation already running. Default is NSAnimationBlocking. If set to NSAnimationBlocking, animation is run in main thread in custom run loop mode blocking UI. If animation is run NSAnimationNonblocking then animation is run in main thread in the common run loop modes or the ones specified in -runLoopModesForAnimating. NSAnimationNonblockingThreaded spawns a new thread that runs the animation.
- (void)setFrameRate:(float)framesPerSecond;
- (float)frameRate;
Set/get the frame rate (updates/second) of the effect. The frame rate is not guaranteed. Can be changed while running and will be used at the next frame. Value must be positive. A value of 0.0 means as fast as possible (currently limited to 30 fps). Negative values raise an exception.
- (void)setAnimationCurve:(NSAnimationCurve)curve;
- (NSAnimationCurve)animationCurve;
Set/get the animation curve. Predefined curves are linear, ease in (slow down as we reach end), ease out (slowly speed up start), and ease in/outS-curve. This setting is ignored if the delegate implements -animation:valueForProgress:. Invalid values raise an exception.
- (float)currentValue;
This is the current value of the effect based on the current progress. It is derived from the animation curve or from the delegate. This is a read-only setting. A subclass can override this method to provide a custom curve. The current value can be less than 0.0 or larger than 1.0. For example, by allowing the size to be greater then 1.0, one could do a 'rubber effect' where temporarily, the size of the view is larger than the final.
- (void)setDelegate:(id)delegate;
- (id)delegate;
Set/get the delegate. This is a weak reference - the delegate is not retained.
- (NSArray *)progressMarks;
- (void)setProgressMarks:(NSArray *)progressMarks;
These set/get all the progress marks at once. Array contains a list of NSNumbers containing NSAnimationProgress (floats). If there are no progress marks set, -progressMarks returns an empty array. Passing in nil to -setProgressMarks: will clear all progress marks..
- (void)addProgressMark:(NSAnimationProgress)progress;
- (void)removeProgressMark:(NSAnimationProgress)progress;
These set and clear 'progress marks'. These are used to notify the delegate or post a notification that the particular progress point has been reached. They can be used to synchronize animations (e.g. starting a new animation when the first one has reached the half-way point.) The notifications are only sent if the animation is playing. They can be called during playing of the animation. The notification is sent as soon as the progress point is passed so the actual currentProgress may be different from the requsted mark point. Valid intervals are from 0.0 to 1.0. Both 0.0 and 1.0 marks will always be send. Multiple marks may be sent during a single frame if the times are close enough together..
- (void)startWhenAnimation:(NSAnimation *)animation reachesProgress:(NSAnimationProgress)startProgress;
- (void)stopWhenAnimation:(NSAnimation *)animation reachesProgress:(NSAnimationProgress)stopProgress;
- (void)clearStartAnimation;
- (void)clearStopAnimation;
This links another animation to this one. When the linked animation reaches a certain progress point, the animation starts and/or stops. You can only have one animation set as a start animation and one set as a stop animation. Setting a new will will clear out the old one. You can also clear out the old one using -clearStartAnimation or -clearStopAnimation.
- (NSArray *)runLoopModesForAnimating;
By default, it returns nil. Custom subclass can override to return specific list run loop modes to run animation timer in. If it returns nil, the animation is run in any of default, modal, and event tracking modes. Ignored if animation mode isn't set to NSAnimationNonblocking.

Delegate Methods
- (BOOL)animationShouldStart:(NSAnimation *)animation;
- (void)animationDidStop:(NSAnimation *)animation;
- (void)animationDidEnd:(NSAnimation *)animation;
Called on start/stop animation and when animation reaches a progress vallue of 1.0. -animationShouldStart: can return NO to cancel the start. -animationDidStop: is called when the animation is explicitly stopped. -animationDidEnd: is called when it ends by reaching a progress value of 1.0. Only called if actual change occurs (i.e. won't call -animationShouldStart: if already playing)
- (float)animation:(NSAnimation *)animation valueForProgress:(NSAnimationProgress)progress;
Delegate can provide custom curve values. progress will always be from 0.0 to 1.0.
- (void)animation:(NSAnimation *)animation didReachProgressMark:(NSAnimationProgress)progress;
Called when the animation reaches a previously marked progress value. The actual current progress may be past the one passed in. Can also use the NSAnimationProgressMarkNotification notification.


NSViewAnimation

This is the only public subclass of NSAnimation. It takes an array of dictionaries that are copied and parses the dictionary. The dictionary contains a target which is required and which can be a window or view. It takes an optional start and/or end frame which if not defined uses the current frame when the animation starts. It can optionally take an effect which will, fade in or out the view or window. If the target is a view and the effect is to fade out or the end frame is empty, the view is hidden at the end. If the effect is to fade in and the end frame is non-empty and the view starts hidden, it is unhidden at the end. If there is no effect, the view frame is changed while animating. If the target is a window, the window is similarly ordered in or out. The animation is non-blocking by default, a duration of 0.5 seconds and the ease in-out curve.
APPKIT_EXTERN NSString *NSViewAnimationTargetKey;       // NSWindow* or NSView* (required)
APPKIT_EXTERN NSString *NSViewAnimationStartFrameKey; // NSValue*(NSRect) (optional)
APPKIT_EXTERN NSString *NSViewAnimationEndFrameKey; // NSValue*(NSRect) (optional)
APPKIT_EXTERN NSString *NSViewAnimationEffectKey; // NSString*(effect strings)(optional)
APPKIT_EXTERN NSString *NSViewAnimationFadeInEffect;
APPKIT_EXTERN NSString *NSViewAnimationFadeOutEffect;
@interface NSViewAnimation
- (id)initWithViewAnimations:(NSArray *)viewAnimations;
- (NSArray *)viewAnimations;
- (void)setViewAnimations:(NSArray *)viewAnimations;
@end

AppKit Extensions to support use of CoreImage API (Section added since WWDC)

The following API has been added to facilitate more convenient use of CoreImage functionality by Cocoa applications.

There is a new NSImageRep subclass called NSCIImageRep, which makes it possible to construct an NSImage that references a CIImage, as in the following code sample:
CIImage ciImage = [aCIFilter valueForKey:@"outputImage"];
CGRect extent = [ciImage extent];
/* Be careful here. A CIImage can have infinite extent. The following is OK only if you know your CIImage is of finite extent. */
NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(extent.size.width, extent.size.height)];
NSCIImageRep *ciImageRep = [NSCIImageRep imageRepWithCIImage:outputImage];
[image addRepresentation:ciImageRep];
The resultant NSImage should be usable in any context where an NSImage is called for. CoreImage will automatically render the result on demand. Note that CIImage instances are immutable, so when a change is made to a CIFilter parameter that affects the filter's output image, a new "outputImage" must be requested from the filter, and a new NSCIImageRep constructed from it.

NSCIImageRep.h also adds three new methods to CIImage via a category. The first enables clients to create a CIImage from an NSBitmapImageRep:
@interface CIImage (NSAppKitAdditions)
- (id)initWithBitmapImageRep:(NSBitmapImageRep *)bitmapImageRep;
The remaining two provide a convenient means to render all or part of a CIImage into the current NSGraphicsContext. They behave identically to the like methods in NSImage:
- (void)drawInRect:(NSRect)rect fromRect:(NSRect)fromRect operation:(NSCompositingOperation)op fraction:(float)delta;
- (void)drawAtPoint:(NSPoint)point fromRect:(NSRect)fromRect operation:(NSCompositingOperation)op fraction:(float)delta;
@end
NSGraphicsContext has a new method, -CIContext, that returns an associated CIContext that can be used to render into the NSGraphicsContext. The CIContext is created on demand, and remains in existence for the lifetime of its owning NSGraphicsContext. If desired, a CIContext can be asked to free the resources it holds by sending it a -reclaimResources or -clearCaches message.

New methods have been added to facilitate conversion between NSColor and CIColor types (the declarations are in NSColor.h):
@interface NSColor (NSQuartzCoreAdditions)
+ (NSColor *)colorWithCIColor:(CIColor *)color;
@end
@interface CIColor (NSAppKitAdditions)
- (id)initWithColor:(NSColor *)color;
@end
An NSColor can be converted to a CIColor as long as it isn't a pattern color. A CIColor can always be converted to an NSColor.

See the Core Image documentation for additional information on the use of Core Image functionality.


New NSResponder-Based Error Presentation (Section added since WWDC)

A new mechanism has been added to Cocoa to enable user-friendly error alerts that are informative, take proper advantage of sheets, and are easily customizable. Cocoa affords customization by publishing an overridable NSResponder method and an NSApplication delegate method that can be implemented. Such a method will typically examine the passed-in NSError object and, using the NSError's domain and code to determine what kind of error is to be presented, return a different NSError object if appropriate. NSError's existing underlyingError attribute makes it feasible to replace one NSError with another that is more presentable without destroying any information about the original detected cause of the problem.

Sometimes it is appropriate to present the user with error recovery options, and act accordingly after the user has chosen one of the options. For example, NSDocument can, when a document being saved is found to be locked, offer to override the lock and save anyway (it doesn't though, in Tiger). Cocoa supports this sort of functionality with the localizedRecoverySuggestion, localizedRecoveryOptions and recoveryAttempter attributes that have been added to Foundation's NSError class and that are honored by various AppKit classes that deal in NSErrors. See the "NSError" section of the Foundation release notes.

Three new methods have been added to the NSResponder class:
- (void)presentError:(NSError *)error modalForWindow:(NSWindow *)window
delegate:(id)delegate didPresentSelector:(SEL)didPresentSelector contextInfo:(void *)contextInfo;
Present an error alert to the user, as a document-modal panel. When the user has dismissed the alert and any recovery possible for the error and chosen by the user has been attempted, send the selected message to the specified delegate. The method selected by didPresentSelector must have the same signature as:
- (void)didPresentErrorWithRecovery:(BOOL)didRecover contextInfo:(void *)contextInfo;
The default implementation of this method always invokes [self willPresentError:error] to give subclassers an opportunity to customize error presentation. It then forwards the message, passing the customized error, to the next responder or, if there is no next responder, NSApp. NSApplication's override of this method invokes [[NSAlert alertWithError:theErrorToPresent] beginSheetModalForWindow:window modalDelegate:self didEndSelector:selectorForAPrivateMethod contextInfo:privateContextInfo]. When the user has dismissed the alert, the error's recovery attempter is sent an -attemptRecoveryFromError:optionIndex:delegate:didRecoverSelector:contextInfo: message, if the error had recovery options and a recovery delegate.

Errors for which ([[error domain] isEqualToString:NSCocoaErrorDomain] && [error code]==NSUserCancelledError) are a special case, because they do not actually represent errors and should not be presented as such to the user. NSApplication's override of this method does not present an alert to the user for these kinds of errors. Instead it merely invokes the delegate specifying didRecover==NO.

Between the responder chain in a typical application and various overrides of this method in AppKit classes, objects are given the opportunity to present errors in orders like these:

For windows owned by documents:
view -> superviews -> window -> window controller -> document -> document controller -> application

For windows that have window controllers but aren't associated with documents:
view -> superviews -> window -> window controller -> application

For windows that have no window controller at all:
view -> superviews -> window -> application

You can invoke this method to present error alert sheets. For example, Cocoa's own -[NSDocument saveToURL:ofType:forSaveOperation:delegate:didSaveSelector:contextInfo:] invokes this method when it's just invoked -saveToURL:ofType:forSaveOperation:error: and that method has returned NO.

You probably shouldn't override this method, because you have no way of reliably predicting whether this method vs. -presentError will be invoked for any particular error. You should instead override the -willPresentError: method described below.
- (BOOL)presentError:(NSError *)error;
Present an error alert to the user, as an application-modal panel, and return YES if error recovery was done, NO otherwise. This method behaves much like the previous one except it does not return until the user has dismissed the alert and, if the error had recovery options and a recovery delegate, the error's recovery delegate has been sent an -attemptRecoveryFromError:optionIndex: message.

You can invoke this method to present error alert dialog boxes. For example, Cocoa's own [NSDocumentController openDocument:] invokes this method when it's just invoked -openDocumentWithContentsOfURL:display:error: and that method has returned nil.

You probably shouldn't override this method, because you have no way of reliably predicting whether this method vs. -presentError:modalForWindow:delegate:didPresentSelector:contextInfo: will be invoked for any particular error. You should instead override the -willPresentError: method described below.
- (NSError *)willPresentError:(NSError *)error;
Given that the receiver is about to present an error (perhaps by just forwarding it to the next responder), return the error that should actually be presented. The default implementation of this method merely returns the passed-in error.

You can override this method to customize the presentation of errors by examining the passed-in error and if, for example, its localized description or recovery information is unsuitably generic, returning a more specific one. When you override this method always check the NSError's domain and code to discriminate between errors whose presentation you want to customize and those you don't. For those you don't just return [super willPresentError:error]. Don't make decisions based on the NSError's localized description, recovery suggestion, or recovery options because it's usually not a good idea to try to parse localized text.

NSDocument and NSDocumentController are not subclasses of NSResponder, and documents and the shared document controller are not in the responder chain, for historical reasons. NSDocument and NSDocumentController nonetheless implement the three new methods described above, and error presentation messages are by default forwarded as if documents and the shared document controller are in the responder chain. (NSWindowController also overrides the error presentation methods to help make this happen.)

In many applications it will be appropriate to override -willPresentError: in a subclass of NSWindowController, NSDocument, or NSDocumentController, but in some applications it will be easiest to customize some error presentation on a per-application basis. So that you don't have to subclass NSApplication to do so, a new application delegate method has been added:
- (NSError *)application:(NSApplication *)application willPresentError:(NSError *)error;
Given that the application object is about to present an error, return the error that should actually be presented.

You can implement this delegate method to customize the presentation of any error presented by your application, as long as no code in your application overrides -presentError:modalForWindow:delegate:didPresentSelector:contextInfo: or -presentError: in a way that prevent errors from being passed down to the application object. Your implementation of this delegate method should follow the advice given for overriding of -[NSResponder willPresentError:], except that it should just return the passed-in error instead of [super willPresentError:error].


New Error Presentation Method in NSAlert (Section added since WWDC)

We may in the future add to NSError still more attributes that are meant to contribute to the presentation of the error to the user. In that case it would be ideal if NSErrors carrying such attributes were presented properly, so a new method has been added to NSAlert to reduce the need for NSErrors to be picked apart by code that is naive of future NSError additions:
+ (NSAlert *)alertWithError:(NSError *)error;
Given an NSError, create an NSAlert that can be used to present the error to the user. The error's localized description, recovery suggestion, and recovery options will be used to set the alert's message text, informative text, and button titles, respectively.


NSDocument/NSDocumentController Error Handling (Section updated since WWDC)

In previous releases of Mac OS X Cocoa's NSDocument and NSDocumentController classes presented alerts that weren't informative, and it was very difficult to customize them. Both classes have been updated to take advantage of the NSError class that was added to Cocoa in Mac OS 10.3. Methods have been added to both classes, and methods have been deprecated. The new methods all consistently deal in NSURLs, replacing the melange of paths and URLs that previously existed.


New NSDocument Methods for Error Handling (Section updated since WWDC)

- (id)initWithContentsOfURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError;
Initialize a document located by a URL, of a specified type, and return it if successful. If not successful, return nil after setting *outError to an NSError that encapsulates the reason why the document could not be initialized. The default implementation of this method invokes [self init], [self readFromURL:absoluteURL ofType:typeName error:outError], [self setFileURL:absoluteURL], [self setFileType:typeName], and [self setFileModificationDate:theModificationDate].

This method replaces -initWithContentsOfFile:ofType: and -initWithContentsOfURL:ofType:, which are now deprecated. For backward binary compatibility -initWithContentsOfFile:ofType: is still invoked when appropriate if it is overridden. -initWithContentsOfURL:ofType: is never invoked from anywhere within Cocoa, as in Mac OS 10.3 and earlier.
- (BOOL)revertToContentsOfURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError;
Discard all unsaved document modifications and replace the document's contents by reading a file or file package located by a URL, of a specified type, and return YES if successful. If not successful, return NO after setting *outError to an NSError that encapsulates the reason why the document could not be reverted. The default implementation of this method invokes [self readFromURL:absoluteURL ofType:typeName error:outError], [self setFileModificationDate:theModificationDate], [self updateChangeCount:NSChangeCleared], and, if the document has an undo manager, [[self undoManager] removeAllActions]. It also deletes autosaved contents files when they have become obsolete.

This method replaces -revertToSavedFromFile:ofType: and -revertToSavedFromURL:ofType: which are now deprecated. For backward binary compatibility, -revertToSavedFromFile:ofType is still invoked when appropriate if overridden. -revertToSavedFromURL:ofType: is never invoked from anywhere within Cocoa, as in Mac OS 10.3 and earlier.
- (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError;
- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper ofType:(NSString *)typeName error:(NSError **)outError;
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError;
- (BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError;
- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName error:(NSError **)outError;
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError;
Methods that are meant to be optionally overridden in the same manner as the non-NSError-returning methods that they replace. See the comments in <AppKit/NSDocument.h> for details.

The replaced methods are -readFromFile:ofType:, -loadFileWrapperRepresentation:ofType:, -loadDataRepresentation:ofType:, -writeToFile:ofType:, -fileWrapperRepresentationOfType:, and -dataRepresentationOfType:. They are all now deprecated. For backward binary compatibility, the old methods are still invoked when appropriate if overridden. Also deprecated are -readFromURL:ofType: and -writeToURL:ofType:, which are never invoked from anywhere within Cocoa, as in Mac OS 10.3 and earlier.
- (BOOL)writeSafelyToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation
error:(NSError **)outError;
- (BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation
originalContentsURL:(NSURL *)absoluteOriginalContentsURL
error:(NSError **)outError;
- (NSDictionary *)fileAttributesToWriteToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation
originalContentsURL:(NSURL *)absoluteOriginalContentsURL
error:(NSError **)outError;
More methods that are meant to be optionally overridden in the same manner as the non-NSError-returning methods that they replace. See the comments in <AppKit/NSDocument.h> for details.

The replaced methods are -writeWithBackupToFile:ofType:saveOperation:, -writeToFile:ofType:originalFile:saveOperation:, and -fileAttributesToWriteToFile:ofType:saveOperation:. They are all now deprecated. For backward binary compatibility, the old methods are still invoked when appropriate if overridden.
- (void)setFileURL:(NSURL *)absoluteURL;
- (NSURL *)fileURL;
Accessors for the location of the document's on-disk representation. The set method doesn't actually rename the document, it's just for recording the document's location during initial opening or saving. The default implementation of -setFileURL: just records the URL so that the default implementation of -fileURL can return it. The default implementation of -fileURL returns whatever was stored by a previous invocation of the default implementation of -setFileURL:.

As part of the parallel effort to use NSURLs consistently, these methods replace -setFileName: and -fileName, which are now deprecated. For backward binary compatibility the old methods are still invoked when appropriate if overridden.
- (void)saveToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation
delegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(void *)contextInfo;
Save the contents of the document to a file or file package located by a URL, formatted to a specified type, for a particular kind of save operation. When saving is completed, regardless of success or failure, send the message selected by didSaveSelector to the delegate, with the contextInfo as the last argument. The method selected by didSaveSelector must have the same signature as:
- (void)document:(NSDocument *)document didSave:(BOOL)didSaveSuccessfully contextInfo:(void *)contextInfo;
The default implementation of this method first makes sure that any editor registered using Cocoa Bindings' NSEditorRegistration informal protocol has committed its changes (except for autosave operations), then invokes [self saveToURL:absoluteURL ofType:typeName forSaveOperation:saveOperation error:&anError] and, if NO is returned, presents the error to the user in a document-modal panel before messaging the delegate.

As part of the parallel effort to use NSURLs consistently, this method replaces -saveToFile:saveOperation:delegate:didSaveSelector:contextInfo:, which is now deprecated. For backward binary compatibility the old method is still invoked when appropriate if overridden.


New NSDocumentController Methods for Error Handling (Section updated since WWDC)

- (id)openUntitledDocumentAndDisplay:(BOOL)displayDocument error:(NSError **)outError;
- (id)makeUntitledDocumentOfType:(NSString *)typeName error:(NSError **)outError;
- (id)openDocumentWithContentsOfURL:(NSURL *)absoluteURL display:(BOOL)displayDocument error:(NSError **)outError;
- (id)makeDocumentWithContentsOfURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError;
Methods that are meant to be optionally overridden in the same manner as the non-NSError-returning methods that they replace. See the comments in <AppKit/NSDocumentController.h> for details.

The replaced methods are -openUntitledDocumentOfType:display:, -makeUntitledDocumentOfType:, -openDocumentWithContentsOfFile:display:, and makeDocumentWithContentsOfFile:ofType:. They are all now deprecated. For backward binary compatibility, the old methods are still invoked when appropriate if overridden. Also deprecated are -openDocumentWithContentsOfURL:display: and -makeDocumentWithContentsOfURL:ofType:, which are never invoked from anywhere within Cocoa, as in Mac OS 10.3 and earlier.
- (id)documentForURL:(NSURL *)absoluteURL;
Given a URL, return the open document whose file or file package is located by the URL, or nil if there is no such open document. The default implementation of this method queries each open document to find one whose URL matches, and returns the first one whose URL does match.

As part of the parallel effort to use NSURLs consistently, this method replaces -documentForFileName:, which is now deprecated. For backward binary compatibility the old method is still invoked when appropriate if overridden.

-fileNamesFromRunningOpenPanel: is also deprecated. It is no longer invoked by -openDocument: unless it is overridden. The existing -URLsFromRunningOpenPanel: method is now used instead.

Finally, -setShouldCreateUI: and -shouldCreateUI are deprecated, because as used by NSDocumentController in the past they are not useful. -shouldCreateUI is still invoked by the also-deprecated -openUntitledDocumentOfType:display:, -openDocumentWithContentsOfFile:display:, and -openDocumentWithContentsOfURL:display: methods, and also the new -openUntitledDocumentAndDisplay:error: and -openDocumentWithContentsOfURL: display:error: methods, but only for backward compatibility. If your NSDocument subclass is sending itself a -makeWindowControllers message during initialization, and you're using -setShouldCreateUI: to keep NSDocumentController from sending another -makeWindowControllers message, you should considering fixing your NSDocument subclass, because NSDocuments should never need to invoke -makeWindowControllers during initialization.


Use of NSSaveAsOperation Instead of NSSaveOperation in NSDocument

-saveDocumentWithDelegate:didSaveSelector:contextInfo: now invokes -runModalSavePanelForSaveOperation:delegate:didSaveSelector:contextInfo: with NSSaveAsOperation instead of NSSaveOperation when a save panel is going to be presented. Checking saveOperation==NSSaveOperation without also checking [self fileName] or [self fileURL] is now valid, for apps that require Tiger.


Change to NSDocument's Standard Save Panel Accessory View

The File Format popup in the standard save panel accessory view installed by NSDocument no longer includes export-only file type items for NSSaveOperation and NSSaveAsOperation. Those items were always disabled, but because the user could not possibly cause them to become enabled by doing something in the save panel it was not appropriate to include them.


Change to NSDocumentController's Presentation of Document Opening Errors

In previous releases of Mac OS X NSDocumentController would present an error alert if a document selected by the user with the open panel could not be opened, but it would not present the same alert when the document opening was attempted as a result of the user double-clicking on the document's icon in the Finder, or dragging of the document's icon onto the application's icon. This has been fixed. NSDocumentController now consistently presents an error alert whenever document opening fails. For backward binary compatibility, NSDocumentController does not do this in applications linked against Mac OS 10.3 or earlier, because several applications have worked around the problem by presenting error alerts of their own (and two error alerts are not better than one).


New NSDocument Initializer Just For New Documents (Section updated since WWDC)

In previous releases of Mac OS X there was no NSDocument initializer that would be invoked when a new document was created but not when a document was being opened. Such an initializer has been added:
- (id)initWithType:(NSString *)typeName error:(NSError **)outError;
Initialize a new empty document of a specified type, and return it if successful. If not successful, return nil after setting *outError to an NSError that encapsulates the reason why the document could not be initialized. The default implementation of this method just invokes [self init] and [self setFileType:typeName].

You can override this method to perform initialization that must be done when creating new documents but should not be done when opening existing documents. Your override should typically invoke super, or at least it must invoke -init, NSDocument's designated initializer, to initialize NSDocument's private instance variables.


New NSDocument Methods for File Modification Dates (Section updated since WWDC)

New NSDocument methods have been added to track the modification date of the document's file on disk:
- (void)setFileModificationDate:(NSDate *)modificationDate;
- (NSDate *)fileModificationDate;
The method -fileModificationDate returns the last known modification date of the document's on-disk representation. -setFileModificationDate: is invoked by the default implementations of -initWithContentsOfURL:ofType:error:, -initForURL:withContentsOfURL:ofType:error:, -revertToContentsOfURL:ofType:error:, and -saveToURL:ofType:forSaveOperation:error:. In a future release -saveDocumentWithDelegate:didSaveSelector:contextInfo: may test the file modification date and warn the user when they may be about to overwrite modifications by something other than the current application.


New NSDocument Method for All Document Saving

A new method has been added to NSDocument so that you can override it to do things before and after any save operation:
- (BOOL)saveToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation error:(NSError **)outError;
Save the contents of the document to a file or file package located by a URL, formatted to a specified type, for a particular kind of save operation, and return YES if successful. If not successful, return NO after setting *outError to an NSError that encapsulates the reason why the document could not be saved.

The default implementation of this method invokes [self writeSafelyToURL:absoluteURL ofType:typeName forSaveOperation:saveOperation error:outError]. If that returns YES, it also invokes some combination of -setFileModificationDate:, -setFileType:, -setFileURL:, -updateChangeCount:, and -setAutosavedContentsFileURL:, as appropriate for the kind of save operation. It also updates information that -saveDocumentWithDelegate:didSaveSelector:contextInfo: uses to check for modification, renaming, moving, deleting, and trashing of open documents, and deletes autosaved contents files when they have become obsolete. Because this method does several diffferent things, and because the things are likely to change in future releases of Mac OS X, your override of this method should virtually always invoke super, merely adding new behavior before or after the saving.


Document Autosaving

Support for autosaving of documents has been added to Cocoa. The support takes the form of optional periodic autosaving behavior, new autosaving behavior at application-quitting time, and new NSDocument and NSDocumentController methods that you can invoke and override to customize the behavior.


New NSDocumentController Methods for Autosaving

Two new methods have been added to NSDocumentController so that you can enable periodic autosaving and control how often documents are periodically autosaved:
- (void)setAutosavingDelay:(NSTimeInterval)autosavingDelay;
- (NSTimeInterval)autosavingDelay;
The time interval in seconds for periodic autosaving. A value of 0 indicates that periodic autosaving should not be done at all. NSDocumentController will use this number as the amount of time to wait between detecting that a document has unautosaved changes and sending the document an -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo: message. The default value is 0. You can change it to enable periodic autosaving.

Two new methods that are invoked when reopening autosaved documents have been added to NSDocumentController:
- (BOOL)reopenDocumentForURL:(NSURL *)absoluteDocumentURL
withContentsOfURL:(NSURL *)absoluteDocumentContentsURL error:(NSError **)outError;
Reopen a document located by a URL by reading the contents for the document from another URL, present its user interface, and return YES if successful. If not successful, return NO after setting *outError to an NSError that encapsulates the reason why the document could not be reopened. The default implementation of this method determines the type of document being reopened, sends a -makeDocumentForURL:withContentsOfURL:ofType:error: to instantiate it, then invokes -addDocument: to record its opening. It then sends the document -makeWindowControllers and -showWindows messages.
- (id)makeDocumentForURL:(NSURL *)absoluteDocumentURL withContentsOfURL:(NSURL *)absoluteDocumentContentsURL
ofType:(NSString *)typeName error:(NSError **)outError;
Instantiate a document located by a URL, of a specified type, but by reading the contents for the document from another URL, and return it if successful. If not successful, return nil after setting *outError to an NSError that encapsulates the reason why the document could not be instantiated. The default implementation of this method invokes -documentClassForType: to find out the class of document to instantiate, allocates a document object, and initializes it by sending it an -initForURL:withContentsOfURL:ofType:error: message.


New NSDocument Methods and Enumerators for Autosaving

New methods have been added to NSDocument. New NSDocumentChangeTypes and a new NSSaveOperationType have been added too:
- (BOOL)hasUnautosavedChanges;
Return YES if the document has changes that have not been autosaved, NO otherwise, as determined by the history of previous invocations of -updateChangeCount:. The default implementation of this method returns NO immediately after invocation of -updateChangeCount:NSChangeCleared or -updateChangeCount:NSChangeAutosaved. It will then return YES if a different number of -updateChangeCount:NSChangeDone and -updateChangeCount:NSChangeUndone invocations have been done since. (-updateChangeCount:NSChangeReadOtherContents has no effect on what the default implementation of this method returns.)
- (void)autosaveDocumentWithDelegate:(id)delegate didAutosaveSelector:(SEL)didAutosaveSelector contextInfo:(void *)contextInfo;
Autosave the document's contents at an appropriate location, and then send the message selected by didAutosaveSelector to the delegate, with the contextInfo as the last argument. The method selected by didAutosaveSelector must have the same signature as:
- (void)document:(NSDocument *)document didAutosave:(BOOL)didAutosaveSuccessfully contextInfo:(void *)contextInfo;
If any error occurs while autosaving, it must be reported to the user, typically in a document-modal alert panel, before the delegate is messaged with succeeded:NO.

The default implementation of this method figures out where the autosaved document contents should go and invokes [self saveToURL:autosavedDocumentContentsURL ofType:[self autosavingFileType] forSaveOperation:NSAutosaveOperation delegate:inDelegate didSaveSelector:inDidAutosaveSelector contextInfo:inContextInfo].
- (NSString *)autosavingFileType;
Return the document type that should be used for an autosave operation. The default implementation just returns [self fileType], so for never-been-saved documents this method will return the first type in the array returned by [[self class] writableTypes], or nil if the array is empty. You can override this method and return nil in your override to completely disable autosaving of individual documents (NSDocumentController will not send -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo: to a document that has no autosaving file type.) You can also override it if your application defines a document type that is specifically designed for autosaving. For example, one that efficiently represents document contents _changes_ instead of complete document contents.
- (void)setAutosavedContentsFileURL:(NSURL *)absoluteURL;
- (NSURL *)autosavedContentsFileURL;
The location of the most recently autosaved document contents. The default implementation of -setAutosavedContentsFileURL: records the URL and notifies the shared document controller that this document should be autoreopened if the application is quit by a fast logout (a feature not in Tiger), or crashes before the document is saved. The default implementation of -autosavedContentsFileURL just returns whatever was stored by a previous invocation of the default implementation of -setAutosavedContentsFileURL:.

For example, -saveToURL:ofType:forSaveOperation:error: invokes -setAutosavedContentsFileURL: to record when autosaving has been done, and both methods as part of deleting autosaved document contents when a regular Save or Save As operation has been done successfully. -revertToContentsOfURL:ofType:error: also invokes these as part of deleting autosaved document contents.

During autosaving a new NSSaveOperationType is used:
NSAutosaveOperation
The writing of a document's current contents to a file or file package that is separate from the document's current one, without changing the document's current one. Backward binary compatibility code in NSDocument ensures that this value is never passed to overrides of methods that take NSSaveOperationTypes. NSSaveToOperation is always used in those cases instead.

After successful autosaving -updateChangeCount: with a new NSDocumentChangeType must be invoked:
NSChangeAutosaved
The value to pass to indicate that the document's contents have been autosaved. For example, -saveToURL:ofType:forSaveOperation:error: uses this for a successful NSAutosaveOperation.

A new method that you can override to customize the reopening of autosaved documents has been added:
- (id)initForURL:(NSURL *)absoluteDocumentURL withContentsOfURL:(NSURL *)absoluteDocumentContentsURL
ofType:(NSString *)typeName error:(NSError **)outError;
Initialize a document located by a URL, of a specified type, but by reading the contents for the document from another URL, and return it if successful. If not successful, return nil after setting *outError to an NSError that encapsulates the reason why the document could not be initialized. The default implementation of this method invokes [self readFromURL:absoluteDocumentContentsURL ofType:typeName error:outError], [self setFileURL:absoluteURL], [self setAutosavedContentsFileURL:absoluteDocumentContentsURL], [self setFileType:typeName], and [self setFileModificationDate:theModificationDate]. It also invokes [self updateChangeCount:NSChangeReadOtherContents] if the two URLs aren't identical, so that -isDocumentEdited will always return YES until the user saves or reverts the document.

To ease the adoption of the autosaving feature introduced in Mac OS 10.4, the default implementation of this method invokes [self initWithContentsOfFile:[absoluteDocumentContentsURL path] ofType:typeName] if -initWithContentsOfFile:ofType: is overridden and the URL uses the "file:" scheme. It still invokes [self setFileModificationDate:theModificationDate] and [self updateChangeCount:NSChangeReadOtherContents] in this situation. It still also invokes [self setFileURL:absoluteURL], to overwrite the incorrect invocation of -setFileName: that the override of -initWithContentsOfFile:ofType: likely did.

absoluteDocumentURL will be nil if the initializing is being done as part of the reopening of an autosaved document when the autosaved document had never been saved in the first place.

During autosaved document reopening -initForURL:withContentsOfURL:ofType:error: uses a new NSDocumentChangeType:
NSChangeReadOtherContents
The value to pass to -updateChangeCount: to indicate that the document has been initialized with the contents of a file or file package other than the one whose location would be returned by -fileURL, and therefore can't possibly be synchronized with its persistent representation. For example, -initForURL:withContentsOfURL:ofType:error: uses this to indicate that an autosaved document is being reopened.


NSDocument/NSDocumentController Support for Dynamically-Enabled Document Types

Several interesting Cocoa applications have implemented dynamically-enabled document types, in which support for additional document types is provided by plugins. Because NSDocumentController and NSDocument access document type declarations in Info.plist in a non-public way, developers have resorted to accessing private NSDocumentController and NSDocument instance variables directly, which is discouraged. To obviate such discouraged behavior, new methods that you can override, in addition to overriding existing methods, have been added so you can safely implement your own dynamically-enabled document typing scheme.


New NSDocumentController Methods for Dynamically-Enabled Document Types

- (NSString *)defaultType;
Return the name of the document type that should be used when creating new documents. The default implementation of this method returns the first Editor type declared in the application's Info.plist, or returns nil if no Editor type is declared. You can override it to customize the type of document that is created when, for instance, the user chooses New in the File menu.

[NSDocumentController openUntitledDocumentAndDisplay:error:] for instance always invokes this method. -[NSDocumentController validateUserInterfaceItem:] also invokes this when deciding whether or not any UI item whose action is newDocument: (the New item in the File menu, typically) should be enabled.
- (NSString *)typeForContentsOfURL:(NSURL *)inAbsoluteURL error:(NSError **)outError;
Given a URL, return the name of the document type that should be used when opening the document at that location, if successful. If not successful, return nil after setting *outError to an NSError that encapsulates the reason why the document's type could not be determined, or the fact that the document's type is just unrecognized. The default implementation of this method invokes -typeFromFileExtension:, possibly twice, passing an HFS file type string for the second invocation. The default implementation is of course subject to change however. You can override this to customize type determination for documents being opened.

[NSDocumentController openDocumentWithContentsOfURL:display:error:] and -[NSDocumentController reopenDocumentForURL:withContentsOfURL:error:] for instance always invoke this method.
- (NSArray *)documentClassNames;
Return the names of NSDocument subclasses supported by this application. The default implementation of this method returns information derived from the application's Info.plist. You can override it to return the names of document classes that are dynamically loaded from plugins.


New NSDocument Method for Dynamically-Enabled Document Types

- (NSArray *)writableTypesForSaveOperation:(NSSaveOperationType)saveOperation;
Return the names of the types to which this document can be saved for a particular kind of save operation. For every kind of save operation except NSSaveToOperation the returned array must only include types for which the the application can play the Editor role. For NSSaveToOperation the returned array may also include types for which the application can only play the Viewer role, and still more types that the application can merely export. The default implementation of this method returns [[self class] writableTypes] with, except during NSSaveToOperations, types for which +isNativeType returns NO filtered out.

You can override this method to limit the set of writable types when the documently currently contains data that is not representable in all writable types. For example, you can disallow saving to .rtf files when the document contains an attachment and can only be saved properly to .rtfd files. NSDocument currently uses this this method during save operations that present save panels, but it may be called at other times in future releases of Mac OS X.

You can invoke this method when creating a custom save panel accessory view to easily present the same set of types that NSDocument would present in its standard file format popup menu.


Proper Invocation of Existing NSDocument Methods for Dynamically-Enabled Document Types

The implementations of the existing NSDocument class methods +readableTypes, +writableTypes, and +isNativeType: have not changed significantly, but they are now invoked when you would expect them to be invoked, so that it is useful to override them. For example, each NSDocument subclass whose name is returned by -[NSDocumentController documentClassNames] is now sent a +readableTypes messages to determine the list of types than can be opened. For another example, the class of a document that is being saved is sent a +writableTypes message to determine the list of of types than can be saved. In the case of an NSSaveOperation or NSSaveAsOperation +isNativeType: is sent for each type returned by +readableTypes, to filter out export-only types.


New NSDocumentController Method to Control the Open Recents Menu

A new method has been added to NSDocumentController so that you can override it to control NSDocumentController's presentation of the standard Open Recents menu:
- (unsigned int)maximumRecentDocumentCount;
Return the maximum number of items that may be presented in the standard Open Recent menu. A value of 0 indicates that NSDocumentController will not attempt to add an Open Recent menu to your application's File menu, though NSDocumentController will not attempt to remove any Open Recent menu item if there is one already there. The default implementation returns a value that is subject to change and may or may not be derived from a setting made by the user in a System Preferences panel.


Changes in NSDocument's Handling of the 'Save' Scripting Command (Section added since WWDC)

The behavior of -[NSDocument(NSScripting) handleSaveScriptCommand:], the method that is typically invoked when a script sends a 'save' command to a document, is changed in Mac OS 10.4. It now implements the behavior described in <http://developer.apple.com/technotes/tn2002/tn2106.html>. Here's a comparison of the old and new behavior:

When an 'in' argument is provided for an already-saved document
• 10.3: Acted like 'Save As...' unless the document class returned NO for +isNativeType:typeDerivedFromFileExtension, in which case acted like 'Save a Copy...' (also known as 'Save To...'). Invoked -writeWithBackupToFile:ofType:saveOperation: directly.
• 10.4: Acts like 'Save a Copy...' Invokes -saveToURL:ofType:forSaveOperation:delegate:didSaveSelector:contextInfo:.

When an 'in' argument is provided for a never-been-saved document
• 10.3: Acted like 'Save As...' unless the document class returned NO for + isNativeType:typeDerivedFromFileExtension, in which case acted like 'Save a Copy...' Invoked -writeWithBackupToFile:ofType:saveOperation: directly.
• 10.4: Acts like 'Save As...' Invokes -saveToURL:ofType:forSaveOperation:delegate:didSaveSelector:contextInfo:.

When no 'in' argument is provided for an already-saved document
• 10.3: Acted like 'Save.' Invoked -saveDocument:.
• 10.4: Acts like 'Save.' Invokes -saveDocumentWithDelegate:didSaveSelector:contextInfo:.

When no 'in' argument is provided for a never-been-saved document
• 10.3: Acted like 'Save As...' Invoked -saveDocumentAs:.
• 10.4: Acts like 'Save,' which in this case is indistinguishable from 'Save As...,' because a save dialog must be presented to the user. Invokes -saveDocumentWithDelegate:didSaveSelector:contextInfo:.

TextEdit, which is not NSDocument-based, has been updated to implement the same behavior.


Changes in NSDocument's Handling of Editors Registered via Cocoa Bindings' NSEditorRegistration Informal Protocol (Section added since WWDC)

In Mac OS 10.3 NSDocument implemented Cocoa Bindings' NSEditorRegistration informal protocol and would tell key-value binding editors to commit and discard their changes at certain times. In Mac OS 10.4 NSDocument's handling of key-value binding editors has been refined. These methods no long tell registered editors to commit their changes:
-saveDocument:, -saveDocumentAs:, and -saveDocumentTo:, and -printDocument:.
-canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo:.
-[NSDocumentController saveAll:].
Instead, these NSDocument methods now tell registered editors to commit their changes:
-saveDocumentWithDelegate:didSaveSelector:contextInfo:
-runModalSavePanelForSaveOperation:delegate:didSaveSelector:contextInfo:
-saveToURL:ofType:forSaveOperation:delegate:didSaveSelector:contextInfo: (except for NSAutosaveOperations)
-printDocumentWithSettings:showPrintPanel:delegate:didPrintSelector:contextInfo:
The end result is that any alert sheets that editors may present when asked to commit are now always presented at the right time.

-revertDocumentToSaved: now tells registered editors to discard after the user has seen the "do you want to revert?" panel and chosen "Revert," instead of before the panel is presented.

-close now tells editors to discard their changes.

-isDocumentEdited now returns YES whenever there are registered key-value binding editors.

For backward binary compatibility, NSDocument will send -commitEditingWithDelegate:didCommitSelector:contextInfo: messages to registered editors that implement the this new method, but will fall back to using the old (but not deprecated) -commitEditing message to those that don't.


Bug Fix for Instantation of NSDocumentController Subclasses (Section added since WWDC)

According to the documentation titled "Creating a Subclass of NSDocumentController:"

"The application will not ask for its shared document controller until after the applicationWillFinishLaunching: message is sent to its delegate. Therefore, you can create an instance of your sub-class of NSDocumentController in your application delegate's applicationWillFinishLaunching: method and that instance will be set as the shared document controller."

In Mac OS 10.0-10.3 this was not always true. Code within AppKit would invoke +[NSDocumentController sharedDocumentController] during the loading of your application's main nib, if it contained an Open Recent menu item, before the sending of -applicationWillFinishLaunching: to the application delegate, preventing the NSDocumentController instantiated by the application delegate from being returned by subsequent invocations of +sharedDocumentController. This was a bug and has been fixed in Mac OS 10.4. (The bug did not affect applications that have no Open Recent menu item in their main nib and let NSDocumentController add one under the first item that had a nil target and an openDocument: action, like Sketch and Property List Editor.)


Bug Fix in -[NSDocument displayName]

In Mac OS 10.3, NSDocuments began sending themselves -displayName messages for the purpose of determining whether or not the document was untitled. NSDocument's implementation of -displayName would return nil in this situation. This interfered with overrides of -displayName that invoked [super displayName] and assumed that a non-nil value would be returned (a valid assumption). This bug has been fixed. -[NSDocument displayName] no longer ever returns nil.


Removal of Obsolete Method Declarations from NSDocument.h and NSDocumentController.h

Four NSDocument methods were marked as obsolete of as Mac OS 10.0, having been superseded by methods whose behavior in the presence of multiple sheets is markedly better:

-canCloseDocument: was replaced by -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo:
-fileNameFromRunningSavePanelForSaveOperation: was replaced by -saveDocumentWithDelegate:didSaveSelector:contextInfo:
-runModalSavePanel:withAccessoryView: was replaced by -runModalSavePanelForSaveOperation:delegate:didSaveSelector:contextInfo:
-shouldCloseWindowController: was replaced by -shouldCloseWindowController:delegate:shouldCloseSelector:contextInfo:

Two NSDocumentController methods were likewise marked as obsolete at the same time:

-closeAllDocuments: was replaced by -closeAllDocumentsWithDelegate:didCloseAllSelector:contextInfo:
-reviewUnsavedDocumentsWithAlertTitle:cancellable: was replaced by -reviewUnsavedDocumentsWithAlertTitle:cancellable:delegate:didReviewAllSelector:contextInfo:

The declarations for these six methods have been removed from NSDocument.h and NSDocumentController.h. You should stop using or overriding them in your applications and start using or overriding their replacements. However, for backward binary compatibility their implementations remain in Cocoa, in categories named NSDocument(NSCompatibility) and NSDocumentController(Compatibility). If you are not able to migrate to the newer methods, you can do away with any compiler warnings caused by the changes in NSDocument.h and NSDocumentController.h by adding the following declarations to the relevant source code files in your project:
@interface NSDocument(NSDeprecatedLongAgo)
- (BOOL)canCloseDocument;
- (NSString *)fileNameFromRunningSavePanelForSaveOperation:(NSSaveOperationType)saveOperation;
- (int)runModalSavePanel:(NSSavePanel *)savePanel withAccessoryView:(NSView *)accessoryView;
- (BOOL)shouldCloseWindowController:(NSWindowController *)windowController;
@end
@interface NSDocumentController(NSDeprecatedLongAgo)
- (BOOL)closeAllDocuments;
- (BOOL)reviewUnsavedDocumentsWithAlertTitle:(NSString *)title cancellable:(BOOL)cancellable;
@end

Summary of Deprecated NSDocument Methods

Search in the above release notes for information about why each has been deprecated, and when it is still invoked.
- (NSData *)dataRepresentationOfType:(NSString *)type;
- (NSDictionary *)fileAttributesToWriteToFile:(NSString *)fullDocumentPath
ofType:(NSString *)documentTypeName saveOperation:(NSSaveOperationType)saveOperationType;
- (NSString *)fileName;
- (NSFileWrapper *)fileWrapperRepresentationOfType:(NSString *)type;
- (id)initWithContentsOfFile:(NSString *)absolutePath ofType:(NSString *)typeName;
- (id)initWithContentsOfURL:(NSURL *)absoluteURL ofType:(NSString *)typeName;
- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)type;
- (BOOL)loadFileWrapperRepresentation:(NSFileWrapper *)wrapper ofType:(NSString *)type;
- (void)printShowingPrintPanel:(BOOL)flag;
- (BOOL)readFromFile:(NSString *)fileName ofType:(NSString *)type;
- (BOOL)readFromURL:(NSURL *)url ofType:(NSString *)type;
- (BOOL)revertToSavedFromFile:(NSString *)fileName ofType:(NSString *)type;
- (BOOL)revertToSavedFromURL:(NSURL *)url ofType:(NSString *)type;
- (int)runModalPageLayoutWithPrintInfo:(NSPrintInfo *)printInfo;
- (void)saveToFile:(NSString *)fileName saveOperation:(NSSaveOperationType)saveOperation
delegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(void *)contextInfo;
- (void)setFileName:(NSString *)fileName;
- (BOOL)writeToFile:(NSString *)fileName ofType:(NSString *)type;
- (BOOL)writeToFile:(NSString *)fullDocumentPath ofType:(NSString *)documentTypeName
originalFile:(NSString *)fullOriginalDocumentPath saveOperation:(NSSaveOperationType)saveOperationType;
- (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)type;
- (BOOL)writeWithBackupToFile:(NSString *)fullDocumentPath ofType:(NSString *)documentTypeName
saveOperation:(NSSaveOperationType)saveOperationType;

Summary of Deprecated NSDocumentController Methods

Search in the above release notes for information about why each has been deprecated, and when it is still invoked.
- (id)documentForFileName:(NSString *)fileName;
- (NSArray *)fileNamesFromRunningOpenPanel;
- (id)makeDocumentWithContentsOfFile:(NSString *)fileName ofType:(NSString *)type;
- (id)makeDocumentWithContentsOfURL:(NSURL *)url ofType:(NSString *)type;
- (id)makeUntitledDocumentOfType:(NSString *)type;
- (id)openDocumentWithContentsOfFile:(NSString *)fileName display:(BOOL)display;
- (id)openDocumentWithContentsOfURL:(NSURL *)url display:(BOOL)display;
- (id)openUntitledDocumentOfType:(NSString*)type display:(BOOL)display;
- (void)setShouldCreateUI:(BOOL)flag;
- (BOOL)shouldCreateUI;

NSPersistentDocument

NSPersistentDocument is a subclass of NSDocument that enables easily creating document types which are made persistent by the CoreData framework. NSPersistentDocument instances have an NSManagedObjectContext (and thus a NSPersistentStoreCoordinator - the default implementation creates a separate stack of context, coordinator and persistent store for each document, but that can be re-configured by overriding). The coordinator is created with [self model].

NSPersistentDocument by default stores data in an XML file (an XML persistent store), but that can be reconfigured to SQL or binary formats by overriding the configurePersistentStoreCoordinatorForURL:ofType:error: method.

Summary of how various document operations work:
- Open: Invokes the configure... method with the file URL, adds a store of the default type (XML). Objects will be loaded from the persistent store on demand through the document's context.
- Save: On a new document, will add a store of the default type with the user's chosen URL, then invokes save: on the context. For an already open document, just invokes save: on the context.
- Save As: For a new document, falls back to Save:. For an already open document, migrates the persistent store to the new URL and invokes save: on the context.
- Save To: Not supported in Tiger.
- Revert: Reverts or resets the context. Objects will be loaded again from the persistent store just like the Open: case.

File package support (where potentially multiple persistent stores are inside a file wrapper) is left to subclasses where managing different stores can be done by hand through the coordinator.


NSApplication Support for the Enhanced Print Apple Event

A new Enhanced Print Apple event was defined by Mac OS 10.3 (http://developer.apple.com/technotes/tn2002/tn2082.html) but Cocoa did not support it properly. In Tiger mechanisms have been added to retrofit print event support to existing applications, but they do not work in all cases. To allow you to easily create non-NSDocument-based applications that handle Print Apple events reliably, a new NSApplication delegate has been added, and one deprecated:
- (NSApplicationPrintReply)application:(NSApplication *)application printFiles:(NSArray *)fileNames
withSettings:(NSDictionary *)printSettings showPrintPanels:(BOOL)showPrintPanels;
Given that the application has been asked to print a group of files, print them. printSettings is a dictionary containing NSPrintInfo-compatible print job attributes. Your application should add them to any NSPrintInfo used to print the files, typically by invoking [[thePrintInfo dictionary] addEntriesFromDictionary:printSettings]. showPrintPanels is a flag indicating whether or not a print panel should be presented for each file being printed. If it is NO, no print panels should be presented (but print progress panels should still be presented). See -[NSPrintOperation setShowsPrintPanel:] below. Implementations of this method must return one of the following:
typedef enum NSApplicationPrintReply {
NSPrintingCancelled = 0,
NSPrintingSuccess = 1,
NSPrintingFailure = 3,
NSPrintingReplyLater = 2
} NSApplicationPrintReply;
Return NSPrintingReplyLater if the result of printing cannot be returned immediately, if printing will cause the presentation of a sheet for instance. If your method returns NSPrintingReplyLater it must always invoke -[NSApplication replyToOpenOrPrint:] when the entire print operation has been completed, successfully or not.

This delegate method replaces -application:printFiles:, which is now deprecated. If your application delegate only implements -application:printFiles: it will still be invoked, and NSApplication will use private functionality to arrange for the print settings to take effect despite the fact that an implementation of -application:printFiles: can't possibly know about them.


NSPrintInfo Support for the Enhanced Print Apple Event

Support for attributes corresponding to fields in the Enhanced Print Apple event's "with properties" parameter has been added to NSPrintInfo:
NSString *NSPrintPagesAcross; // an NSNumber containing the number of logical pages to be placed across a physical sheet
NSString *NSPrintPagesDown; // an NSNumber containing the number of logical pages to be placed down a physical sheet
NSString *NSPrintTime; // an NSDate containing the time at which printing should begin
NSString *NSPrintDetailedErrorReporting; // an NSNumber containing a boolean value
NSString *NSPrintFaxNumber; // an NSString containing a fax number
NSString *NSPrintPrinterName; // an NSString containing the name of a printer
Bug: NSPrintTime has no effect in Mac OS 10.4.


NSPrintOperation Support for the Enhanced Print Apple Event

Because presentation of the print panel during handling of the Print event is optional, but the print progress panel should always be presented, new methods have been added to NSPrintOperation to give you individual control over the two kinds of panels:
- (void)setShowsPrintPanel:(BOOL)flag;
- (BOOL)showsPrintPanel;
- (void)setShowsProgressPanel:(BOOL)flag;
- (BOOL)showsProgressPanel;
These methods replace -setShowPanels: and -showPanels, which are now deprecated. The old methods function as they did in Mac OS 10.3, but the return value of -showPanels: is ambiguous (implementation-wise, it returns the same value as -showsPrintPanel).


New NSDocument Methods for the Enhanced Print Apple Event

A new Enhanced Print Apple event was defined by Mac OS 10.3 (<http://developer.apple.com/technotes/tn2002/tn2082.html>) but Cocoa did not support it properly. In Tiger mechanisms have been added to retrofit print event support to existing applications, but they do not work in all cases. To allow you to easily create document-based applications that handle Print Apple events properly, two new methods have been added to NSDocument, and methods have been deprecated:
- (void)printDocumentWithSettings:(NSDictionary *)printSettings showPrintPanel:(BOOL)showPrintPanel
delegate:(id)delegate didPrintSelector:(SEL)didPrintSelector contextInfo:(void *)contextInfo;
Print the document. If showing of the print panel is specified, present it first, and print only if the user OKs the panel. The NSPrintInfo attributes in the passed-in printSettings dictionary should be added to a copy of the document's print info, and the resulting print info should be used for the operation. When printing is completed or has been canceled, send the message selected by didPrintSelector to the delegate, with the contextInfo as the last argument. The method selected by didPrintSelector must have the same signature as:
- (void)document:(NSDocument *)document didPrint:(BOOL)didPrintSuccessfully contextInfo:(void *)contextInfo;
The default implementation of this method first makes sure that any editor registered using Cocoa Bindings' NSEditorRegistration informal protocol has committed its changes, then invokes [self printOperationWithSettings:printSettings error:&anError]. If nil is returned it presents the error to the user in a document-modal panel before messaging the delegate. Otherwise it invokes [thePrintOperation setShowsPrintPanel:showPrintPanel] then [self runModalPrintOperation:thePrintOperation delegate:delegate didRunSelector:didPrintSelector contextInfo:contextInfo]. Your are unlikely to need to override this method.

This method replaces -printShowingPrintPanel:, which is now deprecated. For backward binary compatibility the old method is still invoked when appropriate if it is overridden. When this is done Cocoa uses private functionality to arrange for 1) the print settings to take effect despite the fact that the override of -printShowingPrintPanel: can't possibly know about them, and 2) getting notified when the print operation has been completed, so it can message the delegate at the correct time. Correct messaging of the delegate is necessary for correct handling of the Print Apple event.
- (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)printSettings error:(NSError **)outError;
Create a print operation that can be run to print the document's current contents, and return it if successful. If not successful, return nil after setting *outError to an NSError that encapsulates the reason why the print operation could not be created. The NSPrintInfo attributes in the passed-in printSettings dictionary should be added to a copy of the document's print info, and the resulting print info should be used for the operation. The default implementation of this method does nothing. You must override it to enable printing in your application.

-runModalPageLayoutWithPrintInfo: is now deprecated, because it can only present an application-modal panel, which is a poor user interface in this case. Use -runModalPageLayoutWithPrintInfo:delegate:didRunSelector:contextInfo: instead. -runModalPageLayoutWithPrintInfo: is never invoked from anywhere within Cocoa, as in Mac OS 10.3 and earlier.


NSView Page Header/Footer Printing API (Section added since WWDC)

Tiger adds API that makes it easier for a Cocoa application to include page headers and footers in its printed output. This feature is off by default, but can be enabled using NSPrintInfo's new NSPrintHeaderAndFooter attribute:
APPKIT_EXTERN NSString *NSPrintHeaderAndFooter;
AppKit also recognizes an identically-named user default, so this feature can be easily tested with an existing application executable by launching the application with the default set to YES.

For example:
MyApp.app/Contents/MacOS/MyApp -NSPrintHeaderAndFooter YES

When this feature is enabled, NSView's -drawPageBorderWithSize: method, which previously did nothing, prints a header and footer on each page. (Thus any override of -drawPageBorderWithSize: will need to invoke super's implementation if it wants to inherit this behavior.) The header and footer content are provided by two new NSView methods:
- (NSAttributedString *)pageHeader;
- (NSAttributedString *)pageFooter;
NSView's implementations of these methods provide a default header that includes the print job title (which typically is the same as the document or window title) and the date, and a default footer that includes the page number and page count. A printable view class can override one or both of these methods to substitute its own content in place of these defaults.

Each of these methods is invoked once per page during printing, offering implementors the chance to provide potentially page-specific header/footer content (including an appropriate page number, and perhaps offering different content or alignment for even vs. odd pages, for example). Implementors will typically consult NSPrintOperation's printInfo object to obtain data of interest to include in the header/footer text.

Note that the use of NSAttributedString as the return type for these methods provides for a great deal of power and flexibility. Content can appear both horizontally centered and at the page corners through use of left/center/right aligned tab stops, and images can be included as text attachments.

It is left up to the client application to expose this feature in its user interface, if desired. A simple checkbox in a print sheet accessory view, or the application's Preferences panel, would suffice. An application that provided for user customization the header/footer content could opt to go further.


NSView Drawing Redirection API (Section added since WWDC)

Tiger adds three new methods to NSView that enable an application to ask a view subtree to draw into a specified NSBitmapImageRep or NSGraphicsContext. This feature can be used to take a snapshot of the drawing done by a view and its descendants, either as a cache for computationally expensive view drawing, or for use in some subsequent graphics operation. It does not currently support capture of views that render to hardware-accelerated surfaces (i.e. NSOpenGLViews and NS/QTMovieViews), but otherwise works with all Quartz-based view drawing. The views to be drawn can, but need not, reside in a window. A client can request drawing of a view's entire content, or some rectangular subregion.

Clients of this API should begin by obtaining an NSBitmapImageRep of appropriate dimensions and color depth for capturing the desired view content. Such a bitmap can be obtained using the new NSView method:
- (NSBitmapImageRep *)bitmapImageRepForCachingDisplayInRect:(NSRect)rect;
A client wishing to capture only the parts of the receiver that are not clipped by ancestor views should pass the receiver's "visibleRect" as the NSRect parameter. It is also possible to snapshot parts of a view that are currently clipped (due to an enclosing NSScrollView, for example), by specifying a rectangle that is outside the receiver's visibleRect, but still within its bounds. The -bitmapImageRepForCachingDisplayInRect: method returns a new, autoreleased NSBitmapImageRep instance that is suitable for caching a copy of the specified view area.

Once a suitable target bitmap has been obtained, the client should initialize it as desired before asking the view subtree to draw into it. To initialize the bitmap to fully transparent, for example, one could use the following code:
    NSGraphicsContext *bitmapGraphicsContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:cacheBitmapImageRep];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:bitmapGraphicsContext];
[[NSColor clearColor] set];
NSRectFill(NSMakeRect(0, 0, [cacheBitmapImageRep size].width, [cacheBitmapImageRep size].height));
[NSGraphicsContext restoreGraphicsState];
The desired view subtree area can then be drawn into the bitmap using the new method:
- (void)cacheDisplayInRect:(NSRect)rect toBitmapImageRep:(NSBitmapImageRep *)bitmapImageRep;
The "rect" parameter should be of the same size as that passed to bitmapImageRepForCachingDisplayInRect:. This method creates an NSGraphicsContext for drawing into the NSBitmapImageRep, translates its coordinate system to place the requested rectangle at the origin, and then asks the view subtree to draw into the NSGraphicsContext using another new NSView method:
- (void)displayRectIgnoringOpacity:(NSRect)aRect inContext:(NSGraphicsContext *)context;
Clients that want to set up a different transform or other graphics context parameters may also send this message directly instead of cacheDisplayInRect:toBitmapImageRep:, but will otherwise usually find the latter approach more convenient.

NSView.h contains a declaration for an additional new method:
- (BOOL)lockFocusIfCanDrawInContext:(NSGraphicsContext *)context;
This is not yet used in Tiger, but may be made functional in the future.


NSView -registeredDraggedTypes API

Tiger adds a -registeredDraggedTypes accessor to NSView. This method can be used to query a view for the set of drag types for which the view has been registered via -registerForDraggedTypes. The elements in the returned array are in no particular order, but the array is guaranteed not to contain duplicate entries.


NSView -drawPageBorderWithSize: fix (Section added since WWDC)

On previous versions of Mac OS X, locking focus to draw in an override of NSView's -drawPageBorderWithSize: method applies a transform and clip that are unsuitable for drawing in the page border area. A workaround for this issue is discussed in Cocoa : Printing : Providing a Custom Pagination Scheme. On Tiger, the workaround is no longer necessary, but will cause no harm in applications that employ it.


NSView +new Behavior Change

For applications linked on or after Tiger, NSView's +new is equivalent to [[ViewClass alloc] init], which is more consistent with the usual meaning of +new than the previous behavior of invoking [[ViewClass alloc] initWithFrame:...].

Thus, if an NSView subclass overrides -init, +new will invoke the subclass's -init implementation, whereas it would have been bypassed in favor of -initWithFrame: before.


NSView Transform Fixes

NSView's -convertRect:fromView: and -convertRect:toView: methods have been fixed in Tiger. Previously, these methods could return a rect of insufficient width or height under some transformations.


NSApplication

In Tiger, we changed the behavior of NSApplication for some modal panels. It is now possible to hide an application while a modal panel is open. It is also possible to quit an application while the open panel is up.

We now show the menuBar earlier in application launch - right after sending the applicationWillFinishLaunching notification. If your application wants to hide the menuBar on launch, you should call +[NSMenu setMenuBarVisible:NO] during applicationWillFinishLaunching.

Applications will no longer be told to open an untitled document if launched as a login item or if launched as a service provider.

The following problems were fixed:

- an application delegate that implemented only application:openFiles: (and not application:openFile:) would not be able to open recent documents.

- NSWorkspaceWillPowerOffNotification was sent whenever an application received a 'quit' AppleEvent, whether from loginwindow, the dock, or an AppleScript. This notification is now only sent for logout, restart, or shutdown.

- drawers and NSStatusItems could prevent an application whose delegate returned YES from -applicationShouldTerminateAfterLastWindowClosed: from terminating when the last window was closed.


Dragging (Section updated since WWDC)

In Tiger, we fixed a problem where the offset passed to -[NSView dragImage:at:offset:event:pasteboard:source:slideBack:] was ignored when positioning the dragged image onscreen, but not ignored when passing the drag location to the source in draggedImage:endedAt:operation:. This offset is now universally ignored, because it no longer makes sense with the underlying implementation. This fix applies only to applications built on Tiger or later for compatibility reasons.

We added a method to the NSDraggingDestination informal protocol to allow dragging destinations to specify that they are not interested in periodic -draggingUpdated messages. On Panther and previous, the dragging code sends periodic -draggingUpdated messages to a destination even if there is no change to the drag (the mouse hasn't moved and modifier flags haven't changed), as documented. We have added a new method to allow dragging destinations to override this behavior.
@interface NSObject(NSDraggingDestination)
...
- (BOOL)wantsPeriodicDraggingUpdates;
...
@end
-wantsPeriodicDraggingUpdates will be sent to the dragging destination view or window if it responds. If the destination responds and returns NO, -draggingUpdated messages will be sent only when the mouse moves or a modifier flag changes. Otherwise, the default behavior will be to continue to send periodic dragging updated messages even if nothing is changing.

We added the ability to specify an HFS promise drag without using dragPromisedFilesOfTypes:fromRect:source:slideBack:event:. A dragging source may now add the NSFilesPromisePboardType to the pasteboard directly, and should call setPropertyList:forType: to write the array of fileTypes as the data for this type. The object passed in the source argument of dragImage:at:offset:event:pasteboard:source:slideBack: must implement namesOfPromisedFilesDroppedAtDestination: to provide the file names when the drag is dropped.

We fixed a bug where hidden views could be the target of a drag.


NSEvent (Section updated since WWDC)

In Tiger, we added a constant, NSDeviceIndependentModifierFlagsMask, to be used in a bitwise-and with -[NSEvent modifierFlags] to retrieve only the device independent modifer flags. This allows applications to mask off the device dependent modifierFlags, include event coalescing information. Eg. an application could detect if any device independent modifier flags are present in theEvent using:
    if (([theEvent modifierFlags] & NSDeviceIndependentMofidierFlagsMask) != 0) ...
We fixed a long standing bug where the event passed to NSApplication and NSWindow -discardEventsMatchingMask:beforeEvent: was ignored, and all events in the queue matching the mask were discarded. For applications built on Tiger or later, we now check event times against the event passed in, and discard only those events whose event time is earlier. For applications built on Panther or earlier, we continue to discard all events in the queue matching the mask.


NSScrollWheel events (Section added since WWDC)

In order to support finer-grained scrolling required by new devices, the NSScrollWheel event and the handling of NSScrollWheel events within NSScrollView has changed. For scrollWheel mice, the delta fields may now contain fractional components. These fractional values are made available to applications built on Tiger or later. Applications built on Tiger or later which do their own handling of the NSScrollWheel event need to be prepared to deal with fractional values, for example by rounding the scroll amount computed from the event to an integral value before scrolling. Applications built on Panther or earlier will receive integral values compatible with the values received on previous releases.

In addition, new devices may send the NSScrollWheel event much more frequently, in some cases with both deltaX and deltaY of 0. These scroll events contain device dependent scroll information which allow NSScrollView to do continuous scrolling, so should be passed to super if your subclass allows NSScrollView to do the scrolling. If your subclass is doing custom scrolling, you should be prepared to ignore NSScrollWheel events whose deltaX and deltaY are both 0. This change affects applications built on Tiger and on previous releases.


Tablet event support

In Panther, tablet drivers started generating native tablet events in addition to mouse events with embedded tablet data. In Tiger, we have added API to define the tablet event types, to provide accessors for tablet event data from native tablet events and mouse events with tablet data, and to allow tablet events to be sent through the responder chain.

Tablet drivers generate tablet events in two ways. In some cases (dual-puck tablet point events, "modern" proximity events), the driver generates pure tablet events. These event have been added to NSEvent.h:
typedef enum _NSEventType {        /* various types of events */
...
NSTabletPoint = 23,
NSTabletProximity = 24,
...
} NSEventType;
Along with the corresponding masks:
enum {                    /* masks for the types of events */
...
NSTabletPointMask = 1 << NSTabletPoint,
NSTabletProximityMask = 1 << NSTabletProximity,
...
};
In other cases (single-puck tablet point events, compatibility proximity events), the driver generates mouseDown/Up/Dragged/Moved events with additional tablet data. These events can be identified by their subtype. We have published the mouse event subtypes so applications can tell when an event has tablet information. These subtypes are declared in terms of constants defined in IOLLEvent.h:
enum {        /* event subtypes for mouse events */
NSMouseEventSubtype = NX_SUBTYPE_DEFAULT,
NSTabletPointEventSubtype = NX_SUBTYPE_TABLET_POINT,
NSTabletProximityEventSubtype = NX_SUBTYPE_TABLET_PROXIMITY
};
The NSPointerType enumeration declares the possible values returned by -pointerType. These values are declared in terms of constants that will be defined in IOLLEvent.h:
/* pointer types for NSTabletProximity events or mouse events with subtype NSTabletProximityEventSubtype*/
typedef enum {
NSUnknownPointingDevice = NX_TABLET_POINTER_UNKNOWN,
NSPenPointingDevice = NX_TABLET_POINTER_PEN,
NSCursorPointingDevice = NX_TABLET_POINTER_CURSOR,
    NSEraserPointingDevice = NX_TABLET_POINTER_ERASER
} NSPointingDeviceType;
The following enumeration declares possible values to be included in the bitwise-or returned by -buttonMask. These values are declared in terms of constants defined in IOLLEvent.h:
/* button masks for NSTabletPoint events or mouse events with subtype NSTabletPointEventSubtype */
enum {
    NSPenTipMask = NX_TABLET_BUTTON_PENTIPMASK,
    NSPenLowerSideMask = NX_TABLET_BUTTON_PENLOWERSIDEMASK,
    NSPenUpperSideMask = NX_TABLET_BUTTON_PENUPPERSIDEMASK
};
The -pressure and -subtype messages are now valid for new event types as described in the comments:
/* This message is valid for all mouse down/up/drag events */
/* This message is also valid for mouse events with subtype NSTabletPointEventSubtype and
for NSTabletPoint events on 10.4 or later */
- (float)pressure;
/* this message is valid for kit, system, and app-defined events */
/* this message is also valid for mouse down/up/drag/move events on 10.4 or later */
- (short)subtype;
In addition, we have added accessors for the fields of the tablet events:
/* this message is valid for mouse events with subtype NSTabletPointEventSubtype or
NSTabletProximityEventSubtype, and for NSTabletPoint and NSTabletProximity events */
- (unsigned int)deviceID; // system-assigned unique device ID
/* these messages are valid for mouse events with subtype NSTabletPointEventSubtype, and for NSTabletPoint events */
- (int)absoluteX; // absolute x coordinate in tablet space at full tablet resolution
- (int)absoluteY; // absolute y coordinate in tablet space at full tablet resolution
- (int)absoluteZ; // absolute z coordinate in tablet space at full tablet resolution
- (unsigned int)buttonMask; // mask indicating which buttons are pressed.
- (NSPoint)tilt; // range is -1 to 1 for both axes
- (float)rotation; // device rotation in degrees
- (float)tangentialPressure; // tangential pressure on the device; range is -1 to 1
- (id)vendorDefined;         // NSArray of 3 vendor defined shorts
/* these messages are valid for mouse events with subtype NSTabletProximityEventSubtype, and  for NSTabletProximity events */
- (unsigned int)vendorID; // vendor defined, typically USB vendor ID
- (unsigned int)tabletID; // vendor defined tablet ID
- (unsigned int)pointingDeviceID; // vendor defined ID of pointing device
- (unsigned int)systemTabletID; // system assigned unique tablet ID
- (unsigned int)vendorPointingDeviceType; // vendor defined pointing device type
- (unsigned int)pointingDeviceSerialNumber; // vendor defined serial number of pointing device
- (unsigned long long)uniqueID; // vendor defined unique ID
- (unsigned int)capabilityMask; // mask representing capabilities of device
- (NSPointingDeviceType)pointingDeviceType; // type of pointing device
- (BOOL)isEnteringProximity; // YES - entering; NO - leaving
Lastly, we have added NSResponder methods for the pure tablet event types:
@interface NSResponder : NSObject <NSCoding>
...
- (void)tabletPoint:(NSEvent *)theEvent;
- (void)tabletProximity:(NSEvent *)theEvent;
...
@end

NSWindow (Section updated since WWDC)

In Tiger, ColorSync has added the ability to update the ColorSync profile attached to a window context. On Panther and previous, the ColorSync profile was defined by the profile of the main display at window server initialization time (=login time) and was cached and used for all new window contexts. In other words, windows could not pick up an updated ColorSync profile until the user logged out and back in. Even new windows created after a ColorSync profile change were not affected by the change. In Tiger, we will maintain this behavior for windows that do not adopt the new API. The profile of the main display will continue to be cached and used for any new window context that does not indicate otherwise. Maintaining this compatibility is important for correctness in apps that are doing cached drawing, and desirable for performance reasons in less sophisticated apps.

We have exposed the new ColorSync support to windows that want to adopt it. Exposing the functionality implies updating the display profile attached to the window CGContextRef, and notifying the window when the display profile has changed.
@interface NSWindow : NSResponder
...
- (void)setDisplaysWhenScreenProfileChanges:(BOOL)flag;
- (BOOL)displaysWhenScreenProfileChanges;
...
@end
If a window returns YES for -displaysWhenScreenProfileChanges, AppKit will call CGWindowContextUpdateDisplayInfo for the window context then tell the window to display, under the following conditions:
- the majority of the window has moved to a different screen and the display profile for the current screen is different than that of the previous screen
- we receive a notification from color sync that the display profile has changed for the screen containing the majority of the window
- the window context is first created (although in this case the window would not need to be told to redisplay)

The default is NO.

We have also added a notification of when the display profile has changed, to be sent after AppKit has called CGWindowContextUpdateDisplayInfo and before the window is told to display, so that the window can update any offscreen caches:
APPKIT_EXTERN NSString *NSWindowDidChangeScreenProfileNotification;
The window delegate can automatically register for this notification by implementing a new delegate method:
@interface NSObject(NSWindowNotifications)
...
- (void)windowDidChangeScreenProfile:(NSNotification *)notification;
...
@end
Adding a child window using -addChildWindow:ordered: now causes the child window to be ordered onscreen in the correct relative location (above or below) if the receiver is onscreen.

We added support for a new window appearance where the background from the titlebar continues into the toolbar, making them appear unified. There is a new styleMask NSUnifiedTitleAndToolbarWindowMask to specify this window appearance.

The toolbar button is now optional:
- (void)setShowsToolbarButton:(BOOL)show;
- (BOOL)showsToolbarButton;

NSWindow -disableScreenUpdatesUntilFlush API (Section added since WWDC)

When a view that renders to a hardware surface (such as an OpenGL view) is placed in an NSScrollView or NSSplitView, there can be a noticeable flicker or lag when the scroll position or splitter position is moved. This happens because each move of the hardware surface takes effect immediately, before the remaining window content has had the chance to draw and flush.

To enable applications to eliminate this visual artifact, Tiger AppKit provides a new NSWindow message, -disableScreenUpdatesUntilFlush. This message asks a window to suspend screen updates until the window's updated content is subsequently flushed to the screen. This message can be sent from a view that is about to move its hardware surface, to insure that the hardware surface move and window redisplay will be visually synchronized. The window responds by immediately disabling screen updates via a call to NSDisableScreenUpdates(), and setting a flag that will cause it to call NSEnableScreenUpdates() later, when the window flushes. It is permissible to send this message to a given window more than once during a given window update cycle; the window will only suspend and re-enable updates once during that cycle.

A view class that renders to a hardware surface can send this message from an override of -renewGState (a method that is is invoked immediately before the view's surface is moved) to effectively defer compositing of the moved surface until the window has finished drawing and flushing its remaining content.
- (void)renewGState {
NSWindow *window = [self window];
if ([window respondsToSelector:@selector(disableScreenUpdatesUntilFlush)]) {
[window disableScreenUpdatesUntilFlush];
}
[super renewGState];
}
In the above example, a -respondsToSelector: check has been used to provide compatibility with previous system releases. On pre-Tiger systems, where NSWindow has no -disableScreenUpdatesUntilFlush method, the -renewGState override will have no effect.


Coalesced Updates (Section added since WWDC)

In Tiger the "coalesced updates" feature of the graphics subsystem improves drawing performance and eliminates visual tearing. However, it also causes applications which flush to the screen faster than 60 fps to stall while drawing. In order to maintain backward compatibility, there are compatibility checks in place where pre-Tiger applications which stall due to over-flushing are reverted back to Panther flushing behavior.

In order to guarantee best performance, applications should refrain from animating at rates above 60 fps. And as before, it's a good idea to use NSView's setNeedsDisplay: and related methods rather than the immediate display method whenever possible, since setNeedsDisplay: enables coalescing drawing updates from different parts of the window and reduces unnecessary flushing to the screen.

The Quartz Debug application provides debugging facilities that can help determine if an application is over-flushing.

We expect to publish a tech note or a Q&A on this topic.


NSWindow live resize optimization

In Tiger, we added content preservation for windows and views during live resize. Prior to this change, the top level view in a window marked itself as needing display in its entire bounds when the window resized. When the top level view was drawn, all subviews were drawn as well, so all views completely redrew during live resize. This isn't always necessary, since in some cases the content of the view is only changing in the newly exposed area. For example in a simple window with left to right orientation, the left side window titlebar buttons and other portions of the window titlebar do not need to redraw.

When this optimization is enabled, any view whose upper left corner moves relative to the upper left of the window is assumed to need complete redraw, because in general view content is position dependent (eg. when you move the splitView bar in the Mail viewer window, the content in the MessageTextView changes relative to the upper left corner of the window). However, any view whose upper left corner remains stationary with respect to the upper left of the window has the opportunity to preserve its content.

On Tiger, content preservation during live resize is enabled by default. However, we provide windows with a way to opt-out of this optimization. If there are no views that can take advantage of it, the window would pay an unneeded expense for preserving the old content.
@interface NSWindow : NSResponder
...
-(void)setPreservesContentDuringLiveResize:(BOOL)flag;
flag - YES to indicate that window should preserve content on live resize, NO otherwise.  Default is YES.
- (BOOL)preservesContentDuringLiveResize;
...
@end
Content preservation at the view level is disabled by default for compatibility with views that should redraw completely when resized. A view can adopt content preservation by implementing the following to return YES:
@interface NSView : NSResponder
...
- (BOOL)preservesContentDuringLiveResize;
...
@end
A view that returns YES for -preservesContentDuringLiveResize is responsible for invalidating its own dirty rects during live resize. Some views may find it convenient to implement setFrameSize: and do their own computation. Other views will want a way to get information about what has changed. We are adding a method that returns the rect representing the previously drawn content, and another method that returns rects representing the minimal area to dirty in the new bounds. These methods can be called during live resize, any time after setFrameSize: has been called on the view for the live resize operation (that is, any time after NSView's implementation of setFrameSize: has been called for that view) and before recursive display of the view hierarchy begins.
@interface NSView : NSResponder
...
/* the rect returned from -rectPreservedDuringLiveResize indicates the rect the view previously occupied,
in the coordinate system of the current bounds. This may be smaller or larger than the current bounds,
depending on whether the view grew or shrunk. The result of this method is an empty rect for a view that
returns NO from -preservesContentDuringLiveResize or a view in a window that returns NO from
-preservesContentDuringLiveResize
*/
-(NSRect)rectPreservedDuringLiveResize;
/* exposedRects - on return from this method, exposedRects contains a list of rects (at most 4)
indicating the parts of the view that are newly exposed. If the view decreased in both height and width,
this list will be empty. If the view increased in both height and width and stayed anchored in the
upper left corner (currently always true but may change in the future), this list will include a
vertical component and a horizontal component indicating the exposed "L".
count - on return, the number of rectangles in the exposedRects list. Can be 0.
The result of this method is the entire view bounds for a view that returns NO from
-preservesContentDuringLiveResize or a view in a window that returns NO from -preservesContentDuringLiveResize
*/
-(void)getRectsExposedDuringLiveResize:(NSRect[4])exposedRects count:(int *)count;
...
@end

NSWindow graphicsContext

A new API graphicsContext is added. It returns an NSGraphicsContext instance associated with the receiver for the calling thread.


New Behavior in -[NSWindow print:]

In Mac OS 10.0-10.3.x -[NSWindow print:] made a copy of [NSPrintInfo sharedPrintInfo], set some print parameters in the copy, and used the copy when creating a print operation. One of the print parameters that was set was orientation, to match the window's orientation. This was inappropriate, because it overrode whatever orientation the user had already set in the shared print info using a page setup panel. This has been fixed. -[NSWindow print:] no longer sets the printing orientation except, for backward binary compatibility, in applications linked against Mac OS 10.3 or earlier.


Window menu (Section added since WWDC)

We have added an "Arrange in Front" alternate menu item for the "Bring All to Front" menu item in the Window menu. Applications should not hard code the indices of items in the Window menu, as the addition of an alternate menu item, or any other Window menu changes, will change the indices of other menu items. We have limited this change to applications built on Tiger or later to preserve binary compatibility.


Metal window background

If you use the background color from a metal window in another window, it will not archive and unarchive correctly, and the unarchived color will lose its dynamic scaling capability. You can avoid this in a metal window by using -[NSWindow backgroundColor] or -[NSColor controlColor] to draw the metal background in custom controls.


NSWindow key view loop

Methods have been added to allow a window to automatically recalculate the key view loop after views have been added.
- (void)setAutorecalculatesKeyViewLoop:(BOOL)flag;
- (BOOL)autorecalculatesKeyViewLoop;
- (void)recalculateKeyViewLoop;
If the autorecalculatesKeyViewLoop is YES, then whenever a view is added to the window, the key view loop is dirtied and recalculated automatically when -[NSWindow selectKeyViewFollowingView] or -[NSWindow selectKeyViewPrecedingView] are called. If autorecalculatesKeyViewLoop is NO, then adding views will not dirty the key view loop and the loop will not be recalculated. You can always call explicitly recalculateKeyViewLoop to recalculate the loop and clear the dirty key view loop flag. The method is also called if the key view loop needs to be calculated when the window is first ordered in. The loop computed is the same as the default one if initialFirstResponder is not set. It is based on the geometric order of the views in the window.


Keyboard Navigation

A window's toolbar and drawers are now reachable using the keyboards <tab> navigating mechanism. The keyboard navigation system expects separate keyboard loops in the toolbar, window content, and each drawer content. For example, the keyboard loop within a window's contentView should be a complete loop (following nextKeyView/previousKeyView) which does not leave the confines of the -contentView. Keyboard navigation is able to reach other loops (for example, travel from the window content to the toolbar), via -nextValidKeyView/-previousValidKeyView. When appropriate, next/previousValidKeyView will return key views in other key loops.


NSAlert

In Tiger, we changed the instance variable names of NSAlert to be prefixed by underscores to be consistent with the AppKit naming conventions. This should not have any binary compatibility implications, but may require source changes in any NSAlert subclass that references instance variables by name. (Of course, since unless otherwise specified instance variables of Cocoa classes are private, applications should not be accessing instance variables.)


NSScreen (Section added since WWDC)

+[NSScreen screens] will no longer return multiple screens when mirroring is enabled.


Text

Text system's backing store string (as obtained by the string methods on NSTextView or NSTextStorage) now do better argument checking for applications linked on Tiger.

Turns out initWithRTF:documentAttributes:, initWithDocFormat:documentAttributes:, and initWithRTFD:documentAttributes: could cause a crash if the document failed to open and the message was sent to NSTextStorage. This has been fixed in Tiger and in 10.3.3, but in earlier 10.3.x releases the only reasonable workaround is to avoid these messages if the data being provided might fail to open. You can use the read method, or call these on NSAttributedString.


NSTypesetter

The set of API introduced as part of NSATSTypesetter in Mac OS X 10.3 Panther is moved to NSTypesetter. Applications can now implement a concrete subclass with a custom layout engine by overriding the layoutParagraphAtPoint: method.

NSSimpleHorizontalTypesetter is deprecated and its interface declaration is moved to a new header NSSimpleHorizontalTypesetter.h.

In addition to the interface migrated from NSATSTypesetter, NSTypesetter has additional new API:

paragraphCharacterRange and paragraphSeparatorCharacterRange return ranges in the character space corresponding to their glyph version counterparts. attributesForExtraLineFragment is for accessing style information that should be used for the extra line fragment. actionForControlCharacterAtIndex: is used for determining actions triggerd from a control character at index.

The new API -getLineFragmentRect:usedRect:emainingRect:forStartingGlyphAtIndex:proposedRect:lineSpacing:paragraphSpacingBefore:paragraphSpacingAfter: supercedes -lineFragmentRectForProposedRect:remainingRect: in NSATSTypesetter.


NSATSTypesetter

lineFragmentRectForProposedRect:remainingRect: is deprecated. Refer to a new API provided by the base NSTypesetter class getLineFragmentRect:usedRect:emainingRect:forStartingGlyphAtIndex:proposedRect:lineSpacing:paragraphSpacingBefore:paragraphSpacingAfter:.


NSTextView Multiple Selection (Section updated since WWDC)

The concept of selection in NSTextView has been extended in order to allow the selection of multiple discontiguous ranges at once. The two new primitives for selection are
- (void)setSelectedRanges:(NSArray *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)flag;
- (NSArray *)selectedRanges;
parallel to the existing setSelectedRange:affinity:stillSelecting and selectedRange methods. The existing single-range methods setSelectedRange:, setSelectedRange:affinity:stillSelecting, and selectedRange are now defined in terms of these new methods. The affinity and stillSelectingFlag arguments retain their current meanings unaltered. The ranges argument is required to be a non-nil, non-empty array of objects responding to - (NSRange)rangeValue. The selectedRanges method is guaranteed to return an array satisfying the same criteria, and in addition its elements will be sorted ascending by location, and discontiguous (i.e. if r1 appears before r2 then NSMaxRange(r1) < r2.location), and furthermore if there is more than one element then no element will have length zero.

There are several additional new NSTextView methods:
- (void)setSelectedRanges:(NSArray *)ranges;
- (NSArray *)rangesForUserTextChange;
- (NSArray *)rangesForUserCharacterAttributeChange;
- (NSArray *)rangesForUserParagraphAttributeChange;
- (BOOL)shouldChangeTextInRanges:(NSArray *)affectedRanges replacementStrings:(NSArray *)replacementStrings;
The first is a convenience method parallel to the existing setSelectedRange:. The next three are parallel to the existing rangeForUserTextChange, rangeForUserCharacterAttributeChange, and rangeForUserParagraphAttributeChange methods, obeying the same restrictions described above for selectedRanges, except that they will return nil in case the appropriate change is not permitted, in the situations in which the existing single-range methods now return (NSNotFound, 0). The last is similar to the existing shouldChangeTextInRange:replacementString:; the replacementStrings array should either be nil, or else contain one element for each range in affectedRanges.

The existing methods returning a single range will now return the first subrange where there is a multiple-range selection. This means that code that uses the existing single-range APIs will continue to operate, acting on the first selected subrange when there are multiple selections. This is in fact the appropriate behavior for many purposes, and is expected to remain a reasonable default. Most AppKit methods have been updated where appropriate to act on all of the subranges of a multiple selection, particularly methods that modify attributes.

There are two new NSTextView delegate methods:
- (NSArray *)textView:(NSTextView *)textView
willChangeSelectionFromCharacterRanges:(NSArray *)oldSelectedCharRanges
toCharacterRanges:(NSArray *)newSelectedCharRanges;
- (BOOL)textView:(NSTextView *)textView
shouldChangeTextInRanges:(NSArray *)affectedRanges
replacementStrings:(NSArray *)replacementStrings;
in which the arguments satisfy criteria like those of the return value of selectedRanges. In the first, the return value must satisfy criteria like those for the ranges argument to setSelectedRanges:affinity:stillSelecting:.

If a delegate implements the old delegate method
- (NSRange)textView:(NSTextView *)textView
willChangeSelectionFromCharacterRange:(NSRange)oldSelectedCharRange
toCharacterRange:(NSRange)newSelectedCharRange;
and not the new one, then multiple selection will effectively be disallowed; attempts to set the selected ranges will call the old delegate method with the first subrange, and afterwards only a single selected range will be set. If a delegate implements the new method, then the old one will be ignored.

If the delegate implements the old delegate method
- (BOOL)textView:(NSTextView *)textView
shouldChangeTextInRange:(NSRange)affectedCharRange
replacementString:(NSString *)replacementString;
then it will be called with an appropriate range and string. If a delegate implements the new method, then the old one will be ignored.

There are several means provided for users to make a multiple selection. First, command-selection can be used in a text view with the usual meaning--it selects new portions of the text, or unselects already selected portion. Second, option-selection can be used in a text view to make a rectangular selection; there is a distinctive cursor provided for this mode. A default is provided, NSProhibitMultipleTextSelectionByMouse, to control this behavior; if the default is set to YES, then command- and option-selection in text will not have their new special meanings, but will behave as they did in Panther. This mechanism allows the new means of selection to be turned off for applications in which they may not be desired.

In addition, two new NSFindPanelActions are provided, NSFindPanelSelectAll and NSFindPanelSelectAllInSelection, to allow selection of all instances of a particular find string; the standard find panel uses these if the control key is held down. Finally, the styles panel supports selection of all text having a particular style--this could be used, for example to select all text in a given font, then subsequently convert it to a different font, or to select all underlined text and subsequently italicize it.


Removal of Override of -attributedSubstringFromRange: in NSTextStorage Category

In Mac OS 10.0-10.3.x there was an override of -[NSAttributedString attributedSubstringFromRange:] in a private category of NSTextStorage. The override returned an instance of NSSubTextStorage, a private subclass of NSTextStorage (and therefore NSAttributedString) used by Cocoa's scripting subsystem. This NSSubTextStorage object responded to all NSAttributedString messages, but its value would change with the value of the "parent" NSAttributedString, which was not correct and caused several difficult-to-diagnose bugs. This override has been removed. NSTextStorages now return nonmutating NSAttributedStrings when sent the -attributedSubstringFromRange: message.


NSParagraphStyle

NSParagraphStyle features new methods hyphenationFactor and tighteningFactorForTruncation. Corresponding writer methods, setHyphenationFactor: and setTighteningFactorForTruncation: are added to NSMutableParagraphStyle.


NSParagraphStyle Header Level

NSParagraphStyle now defines a new attribute, the header level, an integer which should either be 0 or else in the range 1-6. This is currently used only for HTML import/export, defining whether a particular paragraph is a header or not and if so of what level. The relevant methods are
- (int)headerLevel;
on NSParagraphStyle and
- (void)setHeaderLevel:(int)level;
on NSMutableParagraphStyle.


Base writing direction

Accessor methods for the base writing direction, baseWritingDirection and setBaseWritingDirection:, have been added to NSCell, NSControl, and NSText classes.

Also, NSTextView and NSMutableAttributedString now have setBaseWritingDirection:range: API.


NSStringDrawing

New methods have been added to NSString and NSAttributedString categories defined in NSStringDrawing.h. drawWithRect:options: and boundingRectWithSize:options: offer more precise string rendering/sizing functionality.


NSFont and NSFontDescriptor

The NSFont and NSFontDescriptor classes have been restructured. NSFontDescriptor is now promoted to be the primary font reference in AppKit replacing Postscript names. Also, new functionalities such as font matching, font substitution, and font cascading are built into the NSFontDescriptor class.


NSGlyphInfo (Section added since WWDC)

The NSGlyphInfo class now conforms to the NSCopying protocol.


NSTextView Typing Attributes Delegate Method

In addition to the existing notification, NSTextView now has a new delegate method allowing the delegate to intervene to allow, prevent, or modify changes to typing attributes:
- (NSDictionary *)textView:(NSTextView *)textView
shouldChangeTypingAttributes:(NSDictionary *)oldTypingAttributes
toAttributes:(NSDictionary *)newTypingAttributes;

NSTextView undo coalescing (Section added since WWDC)

NSTextView now exposes a new method to control undo coalescing:
- (void)breakUndoCoalescing;
This is useful for instance as a way to get an app to prevent successive typing operations before and after a save from being coalesced into a single undoable item by the text view.


New Responder Methods

NSResponder now defines two new action methods that are implemented in NSTextView. The first is intended to insert a line break (as distinguished from a paragraph break). The NSTextView implementation inserts an NSLineSeparatorCharacter. The second is intended to insert a container break (typically a page break). The NSTextView implementation inserts an NSFormFeedCharacter.
- (void)insertLineBreak:(id)sender;
- (void)insertContainerBreak:(id)sender;

Text Tables (Section updated since WWDC)

The Cocoa text system now supports tables. The basic object involved is a new class, NSTextBlock, which represents a block of text that is to be laid out in a sub-region of the text container. The most important subclass is NSTextTableBlock, which represents a block of text that appears as a cell in a table. The table itself is represented by a separate class, NSTextTable, which is referenced by all of its NSTextTableBlocks and which controls their sizing and positioning.

Text blocks appear as attributes on paragraphs, as part of the paragraph style. An NSParagraphStyle now may have an array of text blocks, representing the nested blocks containing the paragraph, in order from outermost to innermost. For example, if block1 contains four paragraphs, the middle two of which are also in the inner block2, then the text blocks array for the first and fourth paragraphs will be (block1), while the text blocks array for the second and third paragraphs will be (block1, block2).

The methods implementing this are
- (NSArray *)textBlocks
on NSParagraphStyle, and
- (void)setTextBlocks:(NSArray *)array
on NSMutableParagraphStyle.

In addition, there are convenience methods on NSAttributedString for determining the range covered by a block or a table (or (NSNotFound, 0) if the given location is not in the specified block or table):
- (NSRange)rangeOfTextBlock:(NSTextBlock *)block atIndex:(unsigned)location;
- (NSRange)rangeOfTextTable:(NSTextTable *)table atIndex:(unsigned)location;
During layout, the typesetter proposes a large rect within which a particular block should fit. For the outermost block, this will be determined by the text container; for inner blocks, it will be determined by the containing block. The block object then decides what subrect of this it should actually take up. There are actually two rects that are determined: first, the layout rect, within which text in the block is to be laid out; second, the bounds rect, which also contains space for padding, borders, border decoration, margins, and so forth. The layout rect is determined immediately before the first glyph in a particular block is laid out, because it is needed for all subsequent layout of text in the block. It will often be quite tall, because at this point the height of the text laid out in the block has not yet been determined. The bounds rect is determined immediately after the last glyph in the block has been laid out, and it is based on the actual used rect for the text within the block.

The methods on NSTextBlock that are called by the typesetter are
- (NSRect)rectForLayoutAtPoint:(NSPoint)startingPoint inRect:(NSRect)rect
textContainer:(NSTextContainer *)textContainer characterRange:(NSRange)charRange;
- (NSRect)boundsRectForContentRect:(NSRect)contentRect inRect:(NSRect)rect
textContainer:(NSTextContainer *)textContainer characterRange:(NSRange)charRange;
An NSTextTableBlock will call upon its NSTextTable to perform these calculations, using its methods
- (NSRect)rectForBlock:(NSTextTableBlock *)block layoutAtPoint:(NSPoint)startingPoint inRect:(NSRect)rect
textContainer:(NSTextContainer *)textContainer characterRange:(NSRange)charRange;
- (NSRect)boundsRectForBlock:(NSTextTableBlock *)block contentRect:(NSRect)contentRect inRect:(NSRect)rect
textContainer:(NSTextContainer *)textContainer characterRange:(NSRange)charRange;
The typesetter stores the results of these methods in the layout manager. The new NSLayoutManager methods are:
- (void)setLayoutRect:(NSRect)rect forTextBlock:(NSTextBlock *)block glyphRange:(NSRange)glyphRange;
- (void)setBoundsRect:(NSRect)rect forTextBlock:(NSTextBlock *)block glyphRange:(NSRange)glyphRange;
- (NSRect)layoutRectForTextBlock:(NSTextBlock *)block glyphRange:(NSRange)glyphRange;
- (NSRect)boundsRectForTextBlock:(NSTextBlock *)block glyphRange:(NSRange)glyphRange;
- (NSRect)layoutRectForTextBlock:(NSTextBlock *)block atIndex:(unsigned)glyphIndex effectiveRange:(NSRangePointer)effectiveGlyphRange;
- (NSRect)boundsRectForTextBlock:(NSTextBlock *)block atIndex:(unsigned)glyphIndex effectiveRange:(NSRangePointer)effectiveGlyphRange;
- (NSRect)lineFragmentRectForGlyphAtIndex:(unsigned)glyphIndex effectiveRange:(NSRangePointer)effectiveGlyphRange
withoutAdditionalLayout:(BOOL)flag;
- (NSRect)lineFragmentUsedRectForGlyphAtIndex:(unsigned)glyphIndex effectiveRange:(NSRangePointer)effectiveGlyphRange
withoutAdditionalLayout:(BOOL)flag;
- (NSTextContainer *)textContainerForGlyphAtIndex:(unsigned)glyphIndex effectiveRange:(NSRangePointer)effectiveGlyphRange
withoutAdditionalLayout:(BOOL)flag;
The first two methods are used by the typesetter to store the information obtained from text blocks. The others are used during layout when it is necessary to determine what space has been taken up by previously laid blocks. The last three are variants of existing methods, that have the option of not causing additional layout; when they are called during layout, they must be called in such a way as not to force further layout so as to avoid an infinite recursion. For the same reason, the ...RectForTextBlock methods cause glyph generation but not layout; if no rect has been set, they return NSZeroRect. The layout rect should be set immediately before the first glyph in the block is laid out; the bounds rect should be set immediately after the last glyph in the block has been laid out. Under some circumstances, the bounds rect may be adjusted subsequently, as additional blocks in the same table are laid out.

At display time, the text is drawn as usual, but the text block is called upon to draw any background or border decoration while glyph backgrounds are being drawn, using the following method:
- (void)drawBackgroundWithFrame:(NSRect)frameRect inView:(NSView *)controlView
characterRange:(NSRange)charRange layoutManager:(NSLayoutManager *)layoutManager;
and again NSTextTableBlocks call upon their NSTextTable for this using its method:
- (void)drawBackgroundForBlock:(NSTextTableBlock *)block withFrame:(NSRect)frameRect
inView:(NSView *)controlView characterRange:(NSRange)charRange
layoutManager:(NSLayoutManager *)layoutManager;
The sizing and positioning model for text blocks involves a number of dimensions for each block, each of which may either have an absolute value in points or else be expressed as a percentage of the containing block. These dimensions include width, height, minimum and maximum width and height, and margin, border, and padding widths for each of the four sides. The default in each case will be 0, meaning no margin/border/padding and the natural width and height. Certain portions of the block will have their own colors, the default being nil for no color.
- (void)setValue:(float)val type:(NSTextBlockValueType)type forDimension:(NSTextBlockDimension)dimension;
- (float)valueForDimension:(NSTextBlockDimension)dimension;
- (NSTextBlockValueType)valueTypeForDimension:(NSTextBlockDimension)dimension;
- (void)setWidth:(float)val type:(NSTextBlockValueType)type forLayer:(NSTextBlockLayer)layer edge:(NSRectEdge)edge;
- (void)setWidth:(float)val type:(NSTextBlockValueType)type forLayer:(NSTextBlockLayer)layer; // Convenience method
- (float)widthForLayer:(NSTextBlockLayer)layer edge:(NSRectEdge)edge;
- (NSTextBlockValueType)widthValueTypeForLayer:(NSTextBlockLayer)layer edge:(NSRectEdge)edge;
- (void)setBackgroundColor:(NSColor *)color;
- (NSColor *)backgroundColor;
- (void)setBorderColor:(NSColor *)color forEdge:(NSRectEdge)edge;
- (void)setBorderColor:(NSColor *)color; // Convenience method sets all edges at once
- (NSColor *)borderColorForEdge:(NSRectEdge)edge;
NSTextTableBlock and NSTextTable instances have additional methods specific to table cells and to tables. For NSTextTableBlock:
- (id)initWithTable:(NSTextTable *)table startingRow:(int)row rowSpan:(int)rowSpan
startingColumn:(int)col columnSpan:(int)colSpan; /* Designated initializer */
- (NSTextTable *)table;
- (int)startingRow;
- (int)rowSpan;
- (int)startingColumn;
- (int)columnSpan;
For NSTextTable:
- (unsigned)numberOfColumns;
- (void)setNumberOfColumns:(unsigned)numCols;
- (NSTextTableLayoutAlgorithm)layoutAlgorithm;
- (void)setLayoutAlgorithm:(NSTextTableLayoutAlgorithm)algorithm;
- (BOOL)collapsesBorders;
- (void)setCollapsesBorders:(BOOL)flag;
- (BOOL)hidesEmptyCells;
- (void)setHidesEmptyCells:(BOOL)flag;

Text Lists

The Cocoa text system now supports lists. The basic object involved is a new class, NSTextList, which represents a section of text that forms a single list. The visible elements of the list, including list markers, will still appear in the text as they do for lists created by hand today. The list object, however, will allow the list to be recognized as such by the text system, so that markers and spacing can be automatically created for the user, and so that lists can be properly noted when saving in various rich text formats.

Text lists appear as attributes on paragraphs, as part of the paragraph style. An NSParagraphStyle now may have an array of text lists, representing the nested lists containing the paragraph, in order from outermost to innermost. For example, if list1 contains four paragraphs, the middle two of which are also in the inner list2, then the text lists array for the first and fourth paragraphs will be (list1), while the text lists array for the second and third paragraphs will be (list1, list2).

The methods implementing this are
- (NSArray *)textLists
on NSParagraphStyle, and
- (void)setTextLists:(NSArray *)array
on NSMutableParagraphStyle.

In addition, there are convenience methods on NSAttributedString for determining the range covered by a list and the ordinal position within a list of a particular item:
- (NSRange)rangeOfTextList:(NSTextList *)list atIndex:(unsigned)location;
- (int)itemNumberInTextList:(NSTextList *)list atIndex:(unsigned)location;
The NSTextList object itself describes the format of lists markers:
- (id)initWithMarkerFormat:(NSString *)format options:(unsigned)mask;
- (NSString *)markerFormat;
- (unsigned)listOptions;
- (NSString *)markerForItemNumber:(int)itemNum;
Here "marker" refers to the computed value for a specific ordinal position in the list, and "marker format" refers to a generic string used to specify the format for all markers in the list. The marker format is interpreted as a constant string, except for a numbering specifier, which will take the form "{<keyword>}". The currently supported values for <keyword> include decimal, lower-roman, upper-roman, lower-alpha, and upper-alpha. Thus "({decimal})" would be the format for a list number (1), (2), (3)... The only option value currently defined is NSTextListPrependEnclosingMarker, signaling that a nested list should include the marker for its enclosing super-list before its own marker.


Text Panels (Section updated since WWDC)

NSTextView now defines several action methods that are intended to bring forward panels to allow the user to manipulate paragraph spacing, links, lists, and tables in the text. TextEdit now has a menu item corresponding to each of these action methods:
- (void)orderFrontSpacingPanel:(id)sender;
- (void)orderFrontLinkPanel:(id)sender;
- (void)orderFrontListPanel:(id)sender;
- (void)orderFrontTablePanel:(id)sender;
There are now several means provided for users to add and manipulate links in text. Links can be created, manipulated, or removed using the link panel; they can also be dragged and dropped or copied and pasted to or from other applications. In addition, if a link is already present in the text, then control-clicking on the link will bring up a contextual menu with several options related to the link.

There are also several means to manipulate lists. There is a popup in the ruler that allows the selection of most basic list styles, and gives access to the list panel, which allows more general configuration of the content of list markers. The positioning of list markers and text is controlled by tab stops, which can be manipulated as usual, and the attributes of marker text may also be manipulated like those of any other text.

There are also several means to manipulate tables. The table panel can be used to add or remove rows and columns, to nest lists, and to join or split cells, as well as to manipulate border colors and sizes and background colors. Cell sizes can be manipulated directly by dragging on cell borders.


Text Import/Export (Section updated since WWDC)

The Cocoa text system can now import the following formats: plain text, RTF, RTFD, SimpleText, doc format, WordML, HTML, and WebArchive. It can now export all of these except for SimpleText. Instead of adding new methods for each new format, NSAttributedString and NSMutableAttributedString now have general-purpose methods that allow the format to be specified as a parameter. The existing methods remain as conveniences.

The new methods on NSAttributedString are:
- (id)initWithURL:(NSURL *)url options:(NSDictionary *)options documentAttributes:(NSDictionary **)dict error:(NSError **)error;
- (id)initWithData:(NSData *)data options:(NSDictionary *)options documentAttributes:(NSDictionary **)dict error:(NSError **)error;
- (NSData *)dataFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict error:(NSError **)error;
- (NSFileWrapper *)fileWrapperFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict error:(NSError **)error;
The first two methods are similar to the existing methods, but with the addition of an error parameter. The last two methods likewise add an error parameter, but in addition they require that the document type be specified in the document attributes dictionary. The last method returns a directory file wrapper if appropriate for the document type, otherwise a regular file wrapper.

The new methods on NSMutableAttributedString are:
- (BOOL)readFromURL:(NSURL *)url options:(NSDictionary *)opts documentAttributes:(NSDictionary **)dict error:(NSError **)error;
- (BOOL)readFromData:(NSData *)data options:(NSDictionary *)opts documentAttributes:(NSDictionary **)dict error:(NSError **)error;
which again are similar to existing methods but with the addition of an error parameter.

There are a number of new constants used in text import/export. There is a new document type
NSString *NSWebArchiveTextDocumentType;
for the WebKit WebArchive type; the actual format used is the data representation of a WebArchive.

Constants have been defined for all of the existing document attributes and options keys; the values are still specified for existing keys for backward compatibility with previous system versions. (To use them in Panther systems, use the actual string value of the identifier, where provided in the comments.) In addition, some new keys have been added (see below) and some have slightly different usages. NSCharacterEncodingDocumentAttribute is now used for both import and export of plain text documents. NSCocoaVersionDocumentAttribute still returns an NSNumber, but now it may have a floating-point value; for Tiger and later, this value will be the NSAppKitVersionNumber for the AppKit version that created the file. Previous values should be interpreted as follows: values less than 100 are pre-Mac OS X; 100 is Mac OS X 10.0 and 10.1; 102 is Mac OS X 10.2 and 10.3.

Various document attributes (author, subject, etc) can now be read and written in rich text files. Keywords for this have been defined in AppKit/NSAttributedString.h:
NSString *NSTitleDocumentAttribute;
NSString *NSCompanyDocumentAttribute;
NSString *NSSubjectDocumentAttribute;
NSString *NSAuthorDocumentAttribute;
NSString *NSKeywordsDocumentAttribute;
NSString *NSCommentDocumentAttribute;
NSString *NSEditorDocumentAttribute;
NSString *NSCreationTimeDocumentAttribute;
NSString *NSCopyrightDocumentAttribute;
TextEdit's "Document Properties" panel demonstrates the use of some of these keywords.


HTML Import/Export (Section updated since WWDC)

The Cocoa text system now imports and exports HTML. HTML import has been completely revised to use WebKit in all cases, and to make use of the new text table and list support. For Tiger, the @"UseWebKit" document option will no longer be relevant, and the NSTimeoutDocumentOption, NSWebPreferencesDocumentOption, and NSWebResourceLoadDelegateDocumentOption will always be available.

There is now an additional document option,
NSString *NSTextSizeMultiplierDocumentOption;
which specifies a scale factor for font sizes, corresponding to WebView's textSizeMultiplier, the same value that is exposed in Safari through the "Make Text Bigger/Smaller" menu and toolbar items.

The NSCharacterEncodingDocumentAttribute is now available for use in HTML export. If it is not specified, the default encoding for HTML export will be UTF-8. Characters not directly representable in the specified encoding will be represented by character references--usually numeric character references, but character entity references may be used in certain special cases. (At present some encodings may not be fully supported.)

The type of HTML generated is controlled by a new document attribute,
NSString *NSExcludedElementsDocumentAttribute;
This should be an NSArray containing strings (case-insensitive) that are names of HTML elements, plus several additional values: DOCTYPE (representing a doctype declaration) and XML (representing an XML declaration), and certain Apple-specific values described below. The elements contained in this list are those that will not be used in HTML generation; the text system will make use of any other HTML elements as it sees fit. By default, if no list is specified, the excluded elements will be those that are deprecated in HTML 4--namely APPLET, BASEFONT, CENTER, DIR, FONT, ISINDEX, MENU, S, STRIKE, and U--plus XML. When XML is on the excluded list, HTML forms will be used where there is a distinction; when it is removed from the list, XHTML forms will be used.

Clients specifying a list will have considerable control over the HTML generated, for example:

- Remove U from this list, and U will be used for underlined text (and the doctype will no longer be strict).
- Remove XML from this list, and an XML declaration will be generated, and XHTML forms will be used.
- Add STYLE to this list, and styles will be inlined rather than placed in the head.
- Remove FONT from this list, and FONT tags will be generated.
- Add B, I, SUB, and SUPER to this list, and CSS will be used for all styling.
- Conversely, add SPAN to this list, and no CSS at all will be used.
- Add A to this list, and all links will be suppressed.
- Add IMG and OBJECT to this list, and all attachments will be stripped.
- Add DOCTYPE to this list, and no doctype will be generated.
- Add META to this list, and all meta tags will be suppressed.
- Add HEAD to this list, and all head information will be suppressed (and styles will be inlined).
- Add BODY to this list, and the body tag will be omitted (but the body will still be generated).
- Add DOCTYPE, HTML, HEAD, and BODY to this list, and the result will be a bare HTML snippet (with styles inlined).
- Add DOCTYPE and every HTML tag except B and I, and the result will be a bare HTML snippet using only B and I.

The doctype generated depends on the list of elements. If XML is on the excluded list, then an HTML doctype will be used; otherwise an XHTML will be used. If any of the HTML 4 deprecated elements is not on the list, or if P is on the list, then a transitional doctype will be used; otherwise a strict doctype will be used. However, in all cases a doctype will be used only if the generated HTML actually conforms to it (for example, all of the relevant doctypes require a TITLE).

There are two Apple-specific values that may be added to the excluded elements array: Apple-converted-space and Apple-converted-tab. If these are absent from the array, then HTML export will add SPAN elements with these class names as a mechanism to preserve certain types of whitespace (tabs and multiple spaces) that otherwise have no faithful representation in HTML. If these are present in the array, then the generated HTML will not contain these somewhat unwieldy SPAN elements. There is a also a third recognized value, Apple-interchange-newline, but its use is not currently enabled. In the case of TextEdit, the presence of these values is controlled by the "Preserve white space" checkbox in the HTML portion of the open and save preferences pane.

The format of the HTML generated is also controlled by a new document attribute,
NSString *NSPrefixSpacesDocumentAttribute;
This should be an NSNumber containing an integer (default 0) representing the number of spaces per level by which certain nested HTML elements (notably elements involved in representing lists and tables) should be indented from the start of the line. This automatic indentation makes the resulting HTML more readable.


RTF

Handling of \expnd was fixed to treat the argument as quarter-points rather than half-points. (Note that in practice this bug didn't have any impact as we always follow \expnd with \expndtw, which correctly specifies the value out in twips.)

RTF files can now handle additional fractional sizes. (RTF spec allows only half-point precision.)

"No kerning" is now preserved in RTF files; it was not being written out correctly.


Command-Line Tool (Section added since WWDC)

There is now a tool, /usr/bin/textutil, that allows command-line access to the import/export facilities of the Cocoa text system. It can be used for inspecting properties of files, for altering attributes, and for conversion between any of the types of files that the text system supports. Examples might include: determining the format and metadata attributes of a file; adding or changing metadata such as author or title; converting between HTML and RTF; extracting the plain text from rich text documents; or converting plain text from one encoding to another. For more information, see "man 1 textutil".


NSFontPanel modes

We added the following modes to NSFontPanel to allow disabling font effects using validModesForFontPanel. However these are actually not yet implemented:
NSFontPanelUnderlineEffectModeMask      = 1<<8,
NSFontPanelStrikethroughEffectModeMask = 1<<9,
NSFontPanelTextColorEffectModeMask = 1<<10,
NSFontPanelDocumentColorEffectModeMask = 1<<11,
NSFontPanelShadowEffectModeMask = 1<<12,
NSFontPanelAllEffectsModeMask = 0XFFF00

Font Effects Color Changes (Section added since WWDC)

In Panther, the font effects portion of the font panel used a private mechanism to handle color changes (foreground color, underline color, strikethrough color, and document background color). In Tiger, it uses the standard changeAttributes:/convertAttributes: mechanism for foreground color, underline color, and strikethrough color, and the standard changeDocumentBackgroundColor: method for document background color. This makes it easier for custom views to respond to these color changes. The previous private mechanism will still be supported, for those views that may have implemented it based on the Panther implementation, but changeAttributes:/convertAttributes: is recommended for future use.


NSLayoutManager Screen Font Changes (Section added since WWDC)

In Panther, a layout manager would not use screen fonts if any text view associated with it was scaled or rotated, regardless of the setting of usesScreenFonts. In Tiger, a layout manager will use screen fonts if usesScreenFonts is set, regardless of the state of text views associated with it. The previous behavior remains in effect for applications linked on Panther or previous. Applications linked on Tiger or beyond will have to manually call setUsesScreenFonts: on their layout managers if they wish the use of screen fonts to change.


NSLayoutManager Glyph Generator

NSLayoutManager now has methods for directly setting and getting its glyph generator:
- (NSGlyphGenerator *)glyphGenerator;
- (void)setGlyphGenerator:(NSGlyphGenerator *)glyphGenerator;

NSSpellChecker Language Argument (Section added since WWDC)

Documentation has stated that the language argument to
- (NSRange)checkSpellingOfString:(NSString *)stringToCheck startingAt:(int)startingOffset
language:(NSString *)language wrap:(BOOL)wrapFlag inSpellDocumentWithTag:(int)tag wordCount:(int *)wordCount;
should be the empty string to specify that the user's currently selected spellchecker should be used. Actually, nil should be used instead; using the empty string would give inconsistent results on pre-Tiger system versions. For Tiger, the empty string will be handled as nil, but nil is still preferred. The same holds for the similar methods
- (int)countWordsInString:(NSString *)stringToCount language:(NSString *)language;
- (NSArray *)completionsForPartialWordRange:(NSRange)range inString:(NSString *)string
language:(NSString *)language inSpellDocumentWithTag:(int)tag;

NSFileWrapper Changes (Section added since WWDC)

- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)atomicFlag updateFilenames:(BOOL)updateFilenamesFlag;
This method was changed to be more efficient when overwriting existing directories. The change in behavior is that it tries to avoid creating new copies of any files which haven't changed.


NSToolbar (Section updated since WWDC)

NSToolbarItem menuFormRepresentation are now only validated when the toolbar is in label only mode. This is a performance win since these menuFormRepresentations are only used when the toolbar is in label only mode.

A bug was discovered in Panther that affected layout of flexibly sized "view" items when in "Icon & Text" mode. In this case, the view portion of a toolbar item was much smaller than the space which was available to it. In essence, these items looked like they had too much empty space on the right and left side. They looked much farther away from adjacent items than desired (besides the fact that the view portion was smaller than it should be). In Panther, the view portion should never have excess spacing on the right and left unless the extent of the toolbar item and its label is larger than the item's maximum specified view size.

NSToolbar now indicates the selection status of items even if they are in the overflow menu. Prior to Tiger, items in the overflow menu did not indicate selection status. Selected items are indicated using the standard menu check mark.

NSToolbarItem's with valid actions and nil targets now work much better. Normally the receiver of a nil-targeted action is found by traversing the responder chain to find a responder that implements the action method. The traversal starts with the 'firstResponder' in the keyWindow. For applications linked on Tiger or later, toolbar item's now start their traversal with the firstResponder of toolbar item's window even if it is not key.

To understand why the old behavior is a problem, imagine the following: You have a nil-targeted "save" item whose action is saveContentsOfWindow:. There are 2 windows "A" and "B" are open with A being the key window. Each window implements saveContentsOfWindow:. Prior to Tiger, validation of window B's "save" item, would always go through the A's validation code. Further, if you cmd-clicked on B's "save" item (ie. perform the click without changing the key window), the action would be executed by A.

NSToolbar validates its toolbar items once before they are shown so that it is animating with up-to-date state.

A NSToolbarItem's menuFormRepresentation that is a submenu can now have an target/action associated with the top-level item. Such items behave like pulldown menu's whose top level item is clickable like a button. If the user mouses up before the pulldown is shown, the top-level item's action is sent. The pulldown menu is shown after a short delay, or if the user moves the mouse. The action methods 'sender' argument will be the top-level menu item. This new behavior is enabled for applications linked on Tiger and later. .

The resizing behavior of NSToolbarFlexibleSpaceItemIdentifier items has changed slightly. The purpose of a flexible spacer is two-fold. First it is used by users that want to right align certain items. Second, it is use to create visual separation. Flexible spacer items used to share an equal amount of the available space with other resizable items. Unfortunately, this steals valuable space from the items with actual content. To address this, flexible spacers no longer resize with an equal weighting.

NSToolbar's layout of resizable items has been improved. For applications linked on Panther or earlier, there is a bug in the layout of resizable items in icon and label mode. Often items that should have been the same size were different in size. In particular, if two items have the same minSize, when they are both wider than their label, yet smaller then their maxSize, they should be the same size. This bug has been fixed for applications linked on or after Tiger.

Prior to Tiger, changing the label of a resizable item could sometimes cause the item to resize on the screen when it was unnecessary to resize. This bug has been fixed for applications linked on or after Tiger.

Prior to Tiger, changing the label of a resizable item could sometimes cause the item to resize on the screen when it was unnecessary to resize. This bug has been fixed for applications linked on or after Tiger.

Layout changes - NSToolbar's margins have changed slightly.

Margins in aqua windows: The inset of the first and last item have changed, but the sum of those two values is the same as in Panther. However, the inter item spacing has changed by 1 pixel per item. This results in a bit more space for the layout algorithm, resulting in slightly larger flexibly sized items.

Margins in metal windows: The layout has the same changes that the aqua windows have, plus a small vertical margin change. Metal windows are now a vertically shorter, with margins that now match Safari and Finder. As a result, metal windows with toolbars will be a few pixels shorter for the same content size.

Drawing the baseline separator: For applications that want their toolbar to look more like the Finder and Safari, NSToolbar allows you to hide the baseline it draws between itself and the main window contents. Developers can also use this API to hide the base line so that they can draw their own custom looking separator. To turn on/off baseline drawing, call the following methods before you attach your toolbar to its window:
- (void)setShowsBaselineSeparator:(BOOL)flag;
- (BOOL)showsBaselineSeparator;

NSToolbarItem

Autovalidation

NSToolbar relies on window update to drive its auto-validation mechanism to enable/disable NSToolbarItems. Unfortunately, window update may not happen at the exact moment a developer needs to update their UI. Also, window update happens very frequently, and can cause performance problems for validators that need to do a lot of work. Therefore, we now provide a way to turn off the default auto validation on a per-item basis.
/* By default NSToolbar automatically invokes its items validate method on a regular basis.
To be in complete control of when the -validate method is invoked, you can disable automatic validation
on a per-item basis. In particular, if your validation code is slow, you may want to do this for performance reasons.
*/
- (void)setAutovalidates:(BOOL)autovalidates;
- (BOOL)autovalidates;
Avoiding Overflow

Some applications would like to suggest that certain toolbar items always be visible, never falling into the overflow menu. For instance, in Safari, the URL field is very important, and should generally always be visible (and not in the overflow menu). Users may find it very useful to define this sort of thing as well. For instance, In Xcode, I might want to suggest that the "build styles" popup always be visible. To accomplish this, we added the following API.
enum {
// The default visibility priority value. By default, all items have this priority
NSToolbarItemVisibilityPriorityStandard = 0,
    // A good value to use for items which should be first to fall into the overflow menu
NSToolbarItemVisibilityPriorityLow = -1000,
    // A good value to use for items you want to stay visible, allowing users to still have highest priority
NSToolbarItemVisibilityPriorityHigh = 1000,
    // Value assigned to an item the user wants to "keep visible". You should only use values less than this
NSToolbarItemVisibilityPriorityUser = 2000
};
/* When a toolbar does not have enough space to fit all its items, it must push some into the overflow menu.
Items with the highest visibility priority level are chosen last for the overflow menu. The default
visibilityPriority value is NSToolbarItemVisibilityPriorityStandard. To suggest that an item always
remain visible, give it a value greater than NSToolbarItemVisibilityPriorityStandard, but less than
NSToolbarItemVisibilityPriorityUser. In configurable toolbars, users can control the setting of any item,
and the value is rememeber by NSToolbar along with its other autosaved information. You should allow
user setting to have the highest priority.
*/
- (void)setVisibilityPriority:(int)visibilityPriority;
- (int)visibilityPriority;
A cautionary note: The toolbar is never meant to be the only way of doing a particular function. This API should not be used as a crutch. In fact, since users can override your suggestion of what should be visible, you are still never guaranteed that an item will always be visible.


NSStatusItem

You can now get and set a separate double click action on an NSStatusItem. This action is called for any even with a clickCount of greater than 1.
- (SEL)doubleAction;
- (void)setDoubleAction:(SEL)aSelector;

NSMenu

A new method has been added to NSMenu that returns the menu bar height for the main menu. This method supersedes +[NSMenuView menuBarHeight]. The method returns the menu bar height if the menu is the application's current main menu and 0 otherwise.
- (float)menuBarHeight;

Dock Menu

In Tiger, we added support for alternate menu items in dock menus. We also added support to invoke the NSMenu delegate methods if a menu delegate is set.


NSSegmentedCell/NSSegmentedControl

A new method has been added to NSSegmentedCell and NSSegmentedControl that selects the cell given the tag. It will search for the item with the tag and then call -[NSSegmentedCell setSelectedSegment]. The function will return YES if the segment is found even if the cell isn't selectable because it's disabled. The control method calls the cell method.
- (BOOL)selectSegmentWithTag:(int)tag;

NSPopUpButtonCell/NSPopUpButton

A new method has been added to NSPopUpButtonCell and NSPopUpButton that selects an item given the tag. It will search for the item with the tag and then call -[NSPopUpButtonCell selectItemAtIndex]. The method will return YES if the item is found. If the item isn't found, the function returns NO and does nothing. The control method calls the cell method.
- (BOOL)selectItemWithTag:(int)tag;

NSFormCell

Placeholder string methods in NSTextFieldCell are also available in NSFormCell.
- (void)setPlaceholderString:(NSString *)string;
- (NSString *)placeholderString;
- (void)setPlaceholderAttributedString:(NSAttributedString *)string;
- (NSAttributedString *)placeholderAttributedString;

NSButtonCell (Section updated since WWDC)

You can now set the background colour of a borderless button. This does not affect the color of bordered buttons such as push buttons.
- (void)setBackgroundColor:(NSColor *)color;
- (NSColor *)backgroundColor;
The drawing of the button background, image, and text has been broken out into 3 separate public methods that give a subclass an override point.
- (void)drawImage:(NSImage *)image withFrame:(NSRect)frame inView:(NSView *)controlView;
- (NSRect)drawTitle:(NSAttributedString *)title withFrame:(NSRect)frame inView:(NSView *)controlView;
- (void)drawBezelWithFrame:(NSRect)frame inView:(NSView *)controlView;
These methods are called if the button has the appropriate setting. -drawBezelWithFrame:inView: is called if the button is bordered. -drawImage:withFrame:inView: and -drawTitle:withFrame:inView: will be called if the button has an image and title respectively. -drawTitle:withFrame:inView: will return the actual rectangle used to render the text (usually centered in the frame passed in.)

There are several new bezel styles available:
    NSSmallSquareBezelStyle = 10
NSTexturedRoundedBezelStyle = 11
The first is a simple square bezel that appears in the accounts preference pane in System Preferences (the '+' and '-' buttons). NSTexturedRoundedBezelStyle is used to create a button similar to the Finder's action (gear) button. It is similar in appearance to a single item segmented control.

Both have regular, pressed, and disabled appearances. The controlSize setting does not apply. NSSmallSquareBezelStyle can scale to any size and NSTexturedRoundedBezelStyle has only a single size and a fixed height.
    NSRoundRectBezelStyle = 12
This is a button with semicircular end caps. It has a gray shading from light to dark. It has regular, pressed, and disabled appearances. You should set the font to be Lucida Grande 12 point.
    NSRecessedBezelStyle = 13
This is a button with semicircular end caps. It appears to have a recessed appearance. It has regular, pressed, selected, and disabled appearances. You should set the font to be Lucida Grande 12 point. This button works when you set -setShowsBorderOnlyWhileMouseInside to YES.
    NSRoundedDisclosureBezelStyle = 14
This button is a square button that is the same as the disclosure button in the save panel. It has regular and selected states. It should not have text or an image set.


NSSearchFieldCell

A flag is available that will cause the search field cell's action to be sent without delay instead of grouping the changes together until the user pauses typing.
- (BOOL)sendsSearchStringImmediately;
- (void)setSendsSearchStringImmediately:(BOOL)flag;

NSCell

The method -setControlView: is now available in NSCell and its subclasses. The implementation in NSCell does nothing. The implementation in NSActionCell saves the view.

You can now set the cell (usually an NSTextFieldCell) to allow or disallow undo. By default, the field is set to allow undo. The undo stack is cleared when the cell begins editing.
- (void)setAllowsUndo:(BOOL)allowsUndo;
- (BOOL)allowsUndo;
You can set the line break mode of a cell for text drawing. This will allow truncation of text in the cell without needing to subclass. Calling -[NSCell setWraps:] will set the mode to NSLineBreakByWordWrapping and NSLineBreakByClipping for YES and NO respectively. -setWraps will return YES if the mode is NSLineBreakByWordWrapping or NSLineBreakByCharWrapping.
- (void)setLineBreakMode:(NSLineBreakMode)mode
- (NSLineBreakMode)lineBreakMode

NSBox (Section added since WWDC)

In Tiger, we added an optimization to draw the background of an NSBox opaquely, if the NSBox is a top level view in the window and fits other criteria. Since we have discovered some applications whose nib layout involves views which overlap the NSBox rather than being completely contained, we have enabled this optimization only for applications built on Tiger or later.


NSComboBox

Calling -[NSComboBox setUsesDataSource:] now always sets indexOfSelectedItem to -1. Prior to Tiger it didn't do this for setUsesDataSource:NO.

copyWithZone: no longer loses font information in the table. Previously, copying a combo box cell would always result in a table using the default font size, even if the combo box cell was using something different like the smallSystemFont.

NSComboBox's popup menu now only shows the scroll bar if it is necessary.

NSComboBox popup window positioning has changed a bit. If the popup can not fit below the text field, NSComboBox now places the combo box above the text field only if there is more space above the combo box than there is below. In the past, NSComboBox didn't pay attention to which side had more space. Further, if there isn't enough room on either side, NSComboBox now shrinks the window to fit on screen.

NSComboBox has fixed a bug in -indexOfSelectedItems which caused the index to be incorrect when used from the NSComboBox action in certain situations.

NSComboBoxCell can now be used inside of an NSMatrix. To function properly, the matrix should use NSTrackModeMatrix for its -mode.


NSBrowser (Section updated since WWDC)

Calling setTitle:ofColumn: during column creation now works. Prior to Tiger, calling it was not possible to set the title of the column being created using this API from either of the delegate "creation" methods: -browser:numberOfRowsInColumn: or -browser:createRowsForColumn:inMatrix:.

The titleFrame passed into drawTitleOfColumn:inRect: is now 2 pixels bigger on each side. In the past, NSBrowser used to inset value returned by titleFrameOfColumn: before calling drawTitleOfColumn:inRect:. This was the wrong thing to do, and also led to certain descenders (like "g", and "j") being clipped at the bottom. Note that the baseline of the text will not move. The new behavior applies unless you are using a subclass of NSBrowser linked before Tiger. For applications linked on or after Tiger, NSBrowserCell now defaults to using NSLineBreakByTruncatingTail for its line break mode. Set -[NSCell setLineBreakMode:] for more information on line break modes.


NSMatrix

When dirtying a cells contents, NSMatrix now pays attention to the cells -focusRingType. In the past, NSMatrix always assumed a -focusRingType of NSFocusRingTypeExterior, causing it to use [self setKeyboardFocusRingNeedsDisplayInRect:cellFrame] always, instead of just for cells that support focus rings.

NSComboBoxCell can now be used inside of an NSMatrix. To function properly, the matrix should use NSTrackModeMatrix for its -mode.


NSOutlineView

-(BOOL)isItemExpanded:(id)item; now returns NO if 'item' is not found.

NSOutlineView now returns valid results from rowForItem:, and levelForItem: when called with children of an item in progress of expanding. While a particular child item is being loaded, these methods will return valid results. Previously they returned -1 while an item was in the process of being loaded by a parent (for instance during an item expand). There is no reason this particular type of information should not be valid during load time, so it is now available.

NSOutlineView's data loading is much lazier now. Data is now requested from the data source on demand. In general, the data source will not be queried for an items data unless that item is visible, or an API is invoked which requires that data to be present.

The following methods, indicated as deprecated since before 10.0, have been removed: -(void)setAutoResizesOutlineColumn: (BOOL)resize; and - (BOOL)autoResizesOutlineColumn; If you are using these methods, switch to using: -(void)setAutoresizesOutlineColumn: (BOOL)resize; and - (BOOL)autoresizesOutlineColumn;

NSOutlineView's outline column now respects the minWidth specified in IB. In the past, NSOutlineView forced the minWidth to the a value large enough to always show the expansion cells, even if that was smaller than the minWidth you specified. This change takes effect for apps linked on or after Tiger.

Drag and Drop

For applications built on Tiger or later, NSOutlineView now allows row dragging to begin with a click in any column. Previously, dragging out was limited to the outline column. Applications built on Tiger and later, can keep the old behavior by simply overriding -(BOOL)canDragRowsWithIndexes:(NSIndexSet *)rowIndexes atPoint:(NSPoint)aPoint; to return YES only when 'aPoint' is in the outline column.

For more drag and drop changes, see NSTableView release notes.

ToolTips

For many reasons, it is difficult to add cell-level toolTips to an NSOutlineView using existing NSView API. To solve this, we are adding new API which will let your delegate supply the tooltip for a cell (column/row intersection) on demand. Note that there are no tooltip tracking tags associated with these tooltips. To take advantage of this new feature, you need to implement the delegate method:
/* When the user pauses over a cell, the value returned from this method will be displayed in a tooltip.
'point' represents the current mouse location in view coordinates. If you don't want a tooltip
at that location, return nil. On entry, 'rect' represents the proposed active
area of the tooltip. By default, rect is computed as [cell drawingRectForBounds:cellFrame].
To control the default active area, you can modify the 'rect' parameter.
*/
- (NSString *)outlineView:(NSOutlineView *)ov toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect
tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation;

NSTableView

Misc

For applications linked on or after Tiger, NSTableView invalidates the table and header view only when the frame size or column sizes has changed. In the past, calls to -tile unnecessarily invalidated the entire table and header view.

For applications linked on or after Tiger, NSTableView's (and NSOutlineView) default data cell now defaults to using NSLineBreakByTruncatingTail for its line break mode. Set -[NSCell setLineBreakMode:] for more information on line break modes.

Deselecting the edited row now ends editing. Due to a bug, in Panther and earlier systems, deselecting sometimes ended editing and sometimes did not. This has been fixed.

Double clicking now only starts editing if the click was in the cellFrame area. Prior to Tiger, double clicking anywhere in the column/row rect started editing. In particular, this had an odd effect in NSOutlineView. So now, double clicking on the left side of the outline disclosure button no longer starts cell editing.

Menu validation has changed slightly. deselectAll: is now disabled if there is no selection in the table. selectAll: is now disabled if the table disallows multiple selections.

Changing the table background color, and grid color now cause a redisplay if needed.

NSTableView is now much more aggressive about making sure the dataCell and headerCell's controlView ivar is set. For example, in Panther and earlier systems, calling [self controlView] would normally return nil for a cell in a table. For applications linked on or after Tiger, this and other related problems have been fixed. In general, NSTableView tries to make sure calling -controlView will return a reference to itself for the cells it uses.

For applications linked on or after Tiger, removing a column from an NSTableView now results in the column's tableView reference to be set to nil.

The attributes of table column's default data cells have changed. In its -init method NSTableColumn now creates a default NSTextFieldCell data cell with the following changed attributes: setDrawsBackground=YES; font=systemFont with size systemFontSize (Lucida Grande, 13). Prior to Tiger, NSTableColumn used to set the default properties to setDrawsBackground=NO, and font=(Lucida Grande, 12). The difference used to present many problems. For instance, if you added columns to your table in code, they would have different font sizes than those from the archived nib. Further, if you chose to display the popular alternating row colors, those data cells whose setDrawsBackground was YES would obscure the blue on every other line. Finally, in most tables it is unnecessary for the cells to draw the background because the table is already drawing the background. The new attributes are only set for applications linked on Tiger and later, and only in NSTableColumn's init method. So, if you have archived nibs with the wrong font size, you should fix them by hand in IB or in code.

Also, in IB it was possible to create new columns that had different properties than the default columns you get when dragging a table from the palette.

Deprecated API

Wherever possible, NSTableView now uses NSIndexSet in its APIs. The following methods have been deprecated:
- (NSImage *)dragImageForRows:(NSArray *)dragRows event:(NSEvent *)dragEvent dragImageOffset:(NSPointPointer)dragImageOffset;
- (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray *)rows toPasteboard:(NSPasteboard *)pboard;
... and replaced with NSIndexSet based versions:
- (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns
event:(NSEvent *)dragEvent offset:(NSPointPointer)dragImageOffset;
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard;
Accessibility Fixes

Selecting a table row using accesibility APIs now properly consults NSTableView's shouldSelectRow: delegate API.

NSTableView now sets up more NSCell state before calling accessibilityPerformAction: when the action is NSAccessibilityPressAction. In particular, the cells controlView is set to the table, and the table's clickedRow and clickedColumn reflect the row and column of the cell receiving the NSAccessibilityPressAction. Finally, the selectedCell of the table will return the cell handling the NSAccessibilityPressAction. The idea is that NSAccessibilityPressAction should be able to be handled the same as if the user had clicked on the cell with the mouse.

Drag and Drop (Section updated since WWDC)

NSTableView has added API that lets you control where row dragging operations can begin. By default NSTableView allows drags to begin anywhere with a few exceptions: Clicks on a buttons, popup buttons, sliders and other similar controls will track the mouse instead of beginning a row drag. To customize this behavior, override the following method:
/* The return value indicates whether the table can attempt to initiate a row drag at 'mouseDownPoint'.
Return NO to disallow initiating a row drag at the given location.
*/
- (BOOL)canDragRowsWithIndexes:(NSIndexSet *)rowIndexes atPoint:(NSPoint)mouseDownPoint;
NSTable/OutlineViews that are drag destinations now revalidate drags (validateDrop: API) if the modifier keys change.

You can now customize NSTableView's (and NSOutlineView) implementation of -draggingSourceOperationMaskForLocal without subclassing. NSTableView implements the dragging source method -draggingSourceOperationMaskForLocal: for you. By default it disallows dragging to destinations outside its application while allowing any type of drag within its application. There are two options for customizing this behavior. Developers can always subclass and override this method when decisions need to be dynamic. However, In Tiger, we have added an easier way to customize the behavior. Simply use the following new method:
/* Configures the default value returned from -draggingSourceOperationMaskForLocal:.
An isLocal value of YES indicates that 'mask' applies when the destination object is in the same application.
An isLocal value of NO indicates that 'mask' applies when the destination object in an application outside
the receiver's application. NSTableView will archive the values you set.
*/
- (void)setDraggingSourceOperationMask:(unsigned int)mask forLocal:(BOOL)isLocal;
Drag and Drop - File Promise Dragging

NSTableView supports file promised drags via the new pasteboard type NSFilesPromisePboardType. To support file promise drags, clients simply add this type to the pasteboard in tableView:writeRowsWithIndexes:toPasteboard:. When a destination accepts the promise drag, it asks NSTableView to supply the files via -namesOfPromisedFilesDroppedAtDestination:. NSTableView passes the responsibility of the actual file creation to data source that specified the promise drag. It will be sent the following new data source method:
/* Returns an array of filenames for the created files (filenames only, not full paths).  The URL represents
the drop location. For more information on file promise dragging, see documentation on the
NSDraggingSource protocol and -namesOfPromisedFilesDroppedAtDestination:.
*/
- (NSArray *)tableView:(NSTableView *)tableView namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
forDraggedRowsWithIndexes:(NSIndexSet *)indexSet;
NSOutlineView has its own version:
- (NSArray *)outlineView:(NSOutlineView *)outlineView
namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
forDraggedItems:(NSArray *)items;
ToolTips

For many reasons, it is difficult to add cell-level toolTips to an NSOutlineView using existing NSView API. To solve this, we are adding new API which will let your delegate supply the tooltip for a cell (column/row intersection) on demand. Note that there are no tooltip tracking tags associated with these tooltips. To take advantage of this new feature, you need to implement the delegate method:
/* When the user pauses over a cell, the value returned from this method will be displayed in a tooltip.
'point' represents the current mouse location in view coordinates. If you don't want a tooltip at that location, return nil.
On entry, 'rect' represents the proposed active area of the tooltip. By default, rect is computed as
[cell drawingRectForBounds:cellFrame]. To control the default active area, you can modify the 'rect' parameter.
*/
- (NSString *)tableView:(NSTableView *)tv toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect
tableColumn:(NSTableColumn *)tc row:(int)row mouseLocation:(NSPoint)mouseLocation;

Variable Row Heights

Until now, every row in a table view was required to be the same height as every other row. This is appropriate for the vast majority of table view's uses. However, in some cases, it is limiting. Sometimes it can be beneficial to specify the height of a row based on the data that row needs to display. In Tiger, we will allow table view rows to be specified per row if necessary with the following API:
/* If the delegate implements -tableView:heightOfRow:, this method immediately re-tiles the table view using row heights it provides.
*/
- (void)noteHeightOfRowsWithIndexesChanged:(NSIndexSet *)indexSet;

@interface NSObject (NSTableViewDelegate)
/* Optional - Variable Row Heights
Implement this method to support a table with varying row heights. The height returned by this method should not include
intercell spacing and must be >0. Performance Considerations: For large tables in particular, you should make sure that this
method is efficient. NSTableView may cache the values this method returns. So if you would like to change a row's height make
sure to invalidate the row height by calling -noteHeightOfRowsWithIndexesChanged:. NSTableView automatically invalidates its
entire row height cache in -reloadData, and -noteNumberOfRowsChanged.
*/
- (float)tableView:(NSTableView *)tableView heightOfRow:(int)row;
@end
@interface NSObject (NSOutlineViewDelegate)
/* Optional - Variable Row Heights
Implement this method to support an outline view with varying row heights. The height returned by this method should not include
intercell spacing and must be >0. Performance Considerations: For large tables in particular, you should make sure that this
method is efficient. NSTableView may cache the values this method returns. So if you would like to change a row's height make
sure to invalidate the row height by calling -noteHeightOfRowsWithIndexesChanged:. NSTableView automatically invalidates its
entire row height cache in -reloadData, and -noteNumberOfRowsChanged.
*/
- (float)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item;
@end
Some notes about variable row height table view's:
- The lineScroll of the table's scroll view is controlled by -rowHeight.
- The height of "filler" rows is controlled by -rowHeight. Ie. The distance between grid lines, and alternating row colors past the last table row.


Table Column Resizing - Better algorithm / New APIs

NSTableView currently supports two column autoresizing modes: "resize all" and "resize last".

We are changing behavior of the current "resize all" mode. Its behavior is simplistic and not useful. Currently, as the table resizes, it sets the width of each resizable column to (availableWidth / numResizableColumns), contraining to each min and max. This distribution algorithm doesn't take into account the current relative sizes of each column. This behavior will be replaced with an algorithm that uniformly distributes space while taking into account initial values. The current "resize last" resizes the last resizable column until it reaches its min or max width. This behavior will be retained.

In addition to the existing modes, we will be adding a few more column autoresizing modes to NSTableView. In particular, we are adding a mode which allow developers to turn off column autoresizing. This will be useful for those that want to implement their own column autoresizing algorithm.

Note: "autoresizing" refers to column resizing in response to a frame change. Think live resize. "User resizing" refers to resizing by dragging from the right edge of a column header.

Also note, NSTableView's -sizeToFit, and -sizeLastColumnToFit behavior has been fixed as part of this work to use the new styles and algorithms. During a live resizing operation, these methods use the specific resizing style you have choosen via -setColumnAutoresizingStyle:. When called outside a live resize operation, -sizeToFit resizes columns to fit the visible width using NSTableViewUniformColumnAutoresizingStyle, while -sizeLastColumnToFit resizes columns using NSTableViewLastColumnOnlyAutoresizingStyle.
/* The column auto resizing style controls resizing in response to a table view frame change.
Compatability Note: This method replaces -setAutoresizesAllColumnsToFit:.
*/
typedef enum {
// Turn of column autoresizing
NSTableViewNoColumnAutoresizing = 0,
    // Autoresize all columns by distributing equal shares of space simultaeously
NSTableViewUniformColumnAutoresizingStyle,
    // Autoresize each table column one at a time.
// Proceed to the next column when the current column can no longer be autoresized (when it reaches maximum/minimum size).
NSTableViewSequentialColumnAutoresizingStyle, // Start with the last autoresizable column, proceed to the first.
NSTableViewReverseSequentialColumnAutoresizingStyle, // Start with the first autoresizable column, proceed to the last.
    // Autoresize only one table column one at a time.
// When that table column can no longer be resized, stop autoresizing.
// Normally you should use one of the Sequential autoresizing modes instead.
NSTableViewLastColumnOnlyAutoresizingStyle,
NSTableViewFirstColumnOnlyAutoresizingStyle
} NSTableViewColumnAutoresizingStyle;
- (void)setColumnAutoresizingStyle:(NSTableViewColumnAutoresizingStyle)style;
- (NSTableViewColumnAutoresizingStyle)columnAutoresizingStyle;

/* Deprecated in Mac OS 10.4.  You should use setColumnAutoresizingStyle: instead.
To preserve compatibility, if flag is YES, This method calls setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle.
If flag is NO, this method calls setColumnAutoresizingStyle:NSTableViewLastColumnOnlyAutoresizingStyle.
*/
- (void)setAutoresizesAllColumnsToFit:(BOOL)flag;
- (BOOL)autoresizesAllColumnsToFit;

NSTableColumn's resizing characteristics now provide finer controls. We have added API which allows you to declare that the user can resize a column, but that it should not resize during live resize (autoresizing).
/* The resizing mask controls the resizability of a table column.  Compatability Note: This method replaces setResizable.
*/
- (void)setResizingMask:(unsigned)resizingMask;
- (unsigned)resizingMask;
enum {
NSTableColumnNoResizing = 0, // Disallow any kind of resizing.
NSTableColumnAutoresizingMask = ( 1 << 0 ), // This column can be resized as the table is resized.
NSTableColumnUserResizingMask = ( 1 << 1 ), // The user can resize this column manually.
};
/* Deprecated in Mac OS 10.4.  If flag is YES, calls
setResizingMask:(NSTableColumnUserResizingMask | NSTableColumnAutoresizingMask).
If flag is NO, calls setResizingMask:(NSTableColumnNoResizing).
*/
- (void)setResizable:(BOOL)flag;
- (BOOL)isResizable;


Bindings and Binding Options (Section updated since WWDC)

The names of all the bindings and options used in AppKit are now declared through explicit constants in NSKeyValueBinding.h.

To debug problems with key-value coding and key-value observing related to bindings, you can now set a user default NSBindingDebugLogLevel to get more useful logs about the problems (instead of undefined key exceptions, for example). This may be useful when you try to find a misconfigured binding in a large nib file with many bindings. Set the default value to 1 to turn on logging, and to 0 to turn it off.

A new method for returning information about the current bindings of an object has been added to NSObject:
- (NSDictionary *)infoForBinding:(NSString *)binding;
It returns a dictionary with information about an existing binding or nil if it is not bound. See NSKeyValueBinding.h for more details.

Several new bindings are available:
- NSTextView now has an attributedString binding (available if multiple fonts are allowed or not).
- NSView and subclasses now have a toolTip binding.
- NSWindow now has displayPatternTitle bindings to specify the window title through a pattern string with multiple values (and work the same way as the displayPatternValue bindings on text fields).
- NSBox now has a title and displayPatternTitle bindings (that work like the bindings on NSWindow).
- NSSearchField now has a predicate binding (and if bound exposes additional predicate2, predicate3, etc. bindings) - you specify a display name and a prediate format string through the binding's options and typically bind them to the filterPreciate of an array controller.
- NSTableView now has doubleClickArgument/doubleClickTarget bindings that work the same way as the argument/target bindings of buttons - the bindings are used to trigger a method invocation on a double-click in the table view.
- Selection widgets (NSPopUpButton/NSPopUpButtonCell and NSMatrix) now offer a contentObjects binding in addition to the content and contentValues bindings (the contentObjects binding becomes available only if content is bound). This allows you to bind the content of the widget to an array (content binding), the displayed values to a dependent array (contentValues binding - which needs to use a key path that is an extension to the one of the content binding), and the "represented" objects to be handled through the selectedObject/selectedObjects bindings to another depdendent array (contentObjects - which also needs to use a key path that is an extension to the one of the content binding). For example, if you have an array with dictionaries (that can be bound to a controller through the "selection.dictionaries" key) which each have values for a key "displayName" and a key "representedObject", you can bind content of a pop-up button to "selection.dictionaries", contentValues to "selection.dictionaries.displayName" and contentObjects to "selection.dictionaries.representedObject" - the selectedObject will then operate on the "representedObject" values, while the pop-up displays the "displayName" values in the user interface. Of course, if you do not use the contentObjects binding, the represented objects are still the values in the array to which content is bound.

An option for the value bindings on table columns has been added:
NSCreatesSortDescriptorBindingOption
This option can be used to suppress the creation of sort descriptors on a column (binding) basis.

An option to force bindings to handle errors with alert panels instead of sheets has been added to varioius bindings:
NSAlwaysPresentsApplicationModalAlertsBindingOption
A variety of bugs have been fixed, some of the fixes caused a slight change in the behavior of bindings/controllers:
- For bindings with immediate validation turned on, the user interface now correctly reflects values immediately when coerced in the validateValue:forKey: method.
- Bindings on table columns with pop-up data cells and content, contentValues and selectedObject bindings now generate sort descriptors for the selected objects based on the display key (as determined from the contentValues binding).
- Unless explicitly specified, table view content, selectionIndexes and sortDescriptor bindings are automatically generated, derived from the common cases of bindings of table columns. These automatic table view bindings are now created by invoking the public -bind:toObject:withKeyPath:options: on the table view, so that subclasses can intercept the calls if needed.
- In the Panther implementation, when removing objects in the background from the array to which an NSArrayController content was bound (not removing through the controller), an index-out-of-bounds exception was sometimes raised. This situation has been fixed for Tiger.
- In Panther, copying the arrangedObjects array of an NSArrayController copied the items in the array as well. This situation has been fixed for Tiger, now the content objects are not copied any more.
- In Panther, collection values (NSArrays and NSDictionaries) read from an NSUserDefaultsController were returned as immutable collections, making it impossible to add or remove from them directly. The workaround was to use a value transformer to make mutable copies of the collections on read. For Tiger, NSUserDefaultsController now returns collections as mutable instances, which makes the use of a special value transformer obsolete (but you still usually need to turn on the NSHandlesContentAsCompoundValueBindingOption option if you bind the content of an NSArrayController or NSObjectController to a value provided through an NSUserDefaultsController).
- Interface Builder now correctly calls -exposedBindings to populate the Bindings inspector.


NSEditor, bindings error presentation as sheets (Section added since WWDC)

So that support for bindings in Cocoa's own views can present error alerts as sheets when appropriate, a new method has been added to key-value binding's NSObject (NSEditor) category:
- (void)commitEditingWithDelegate:(id)delegate didCommitSelector:(SEL)didCommitSelector contextInfo:(void *)contextInfo;
Given that the receiver has been registered with -objectDidBeginEditing: as the editor of some object, and not yet deregistered by a subsequent invocation of -objectDidEndEditing:, attempt to commit the result of the editing. When committing has either succeeded or failed, send the selected message to the specified object with the context info as the last parameter. The method selected by didCommitSelector must have the same signature as:
- (void)editor:(id)editor didCommit:(BOOL)didCommit contextInfo:(void *)contextInfo;
If an error occurs while attempting to commit, because key-value coding validation fails for example, an implementation of this method should typically send the NSView in which editing is being done a -presentError:modalForWindow:delegate:didRecoverSelector:contextInfo: message, specifying the view's containing window.

Various Cocoa classes use this method whenever possible, instead of the existing -commitEditing method, to enable proper use of sheets when presenting errors. The existing -commitEditing method is not deprecated however, because there are situations in which the success or failure of commitment must be known immediately. Implementations of -commitEditing should typically use -presentError: if an error occurs.

The bindings and controller default implementation of the NSEditor methods will use the new NSResponder-based error presentation API, so that error handling can be customized by individual applications.

Also, due to the sheet based error handling, controller action methods like add:, remove:, fetch:, etc. will now execute their operations deferred, so when the methods return, the operation is not actually performed (it will be later, but programmatic invocations of the action methods cannot rely on the operation to have completed).

CoreData's NSManagedObjectContext does implement both the NSEditor and the NSEditorRegistration methods, like NSController does.


NSObjectController

NSObjectController (and thus the subclasses NSArrayController and NSTreeController) has the following new API to specify a managed object context and a description for how to fetch objects from it (by entity name and fetch predicate). The entity name (if specified) also determines how new objects are created (instead of using a class name).
- (NSManagedObjectContext *)managedObjectContext;
- (void)setManagedObjectContext:(NSManagedObjectContext *)managedObjectContext;
- (NSString *)entityName;
- (void)setEntityName:(NSString *)entityName;
- (NSPredicate *)fetchPredicate;
- (void)setFetchPredicate:(NSPredicate *)predicate;
- (void)fetch:(id)sender;

NSTreeController

Tiger introduces a new controller class in Cocoa, NSTreeController, for managing hierarchical data structures. Much of the API of this class parallels that of the array controller. This class, in conjunction with the new NSIndexPath class in Foundation, enables displaying a tree of model objects in a NSOutlineView or NSBrowser.

Much of the API in this class is either inherited from NSController and NSObjectController or is designed to be similar to the API of the NSArrayController. Many of the concepts are the same except that objects are addressed by NSIndexPath instead of int.

Content of the tree controller can be set to the root of the tree of model objects. Clients can then progressively traverse parts of the tree as necessary by getting model objects based on an NSIndexPath, which represents their depth-first location in the tree. This removes the need for us to pull the entire tree into memory up front. On the down side, this prevents us from offering selectObject: functionality since we don't want to pay the penalty of doing a depth first search for an object in an unordered tree.

The NSTreeController traverses its content tree by using key value coding to find the children of a model object. There are methods for setting the key to use to traverse the tree, as well as 2 other keys used as performance improvement opportunities. If a model object in the tree can report the number of children it has, we can use the countKey to find the number of children instead of getting the entire child array just to message it count. Likewise, we can stop traversing a sub-tree once we encounter an object that tells us it is a leaf node in the tree by return YES for the key set as the leafKey.

It should be noted that because of the nature of selections in unordered trees, implementation of the preservesSelection feature maybe pretty expensive for large, sparse selections combined with broad trees.


NSArrayController (Section added since WWDC)

NSArrayController now supports filtering with NSPredicates:
- (void)setFilterPredicate:(NSPredicate *)filterPredicate;
- (NSPredicate *)filterPredicate;
- (void)setClearsFilterPredicateOnInsertion:(BOOL)flag;
- (BOOL)clearsFilterPredicateOnInsertion;
The filter predicate can be bound to the (enumerated) predicate bindings of NSSearchFields (use the NSDisplayNameBindingOption and NSPredicateFormatBindingOption options to configure the bindings) to automatically set up filtering in the user interface.

NSArrayController also has a new mode to handle multiple selections:
- (void)setAlwaysUsesMultipleValuesMarker:(BOOL)flag;
- (BOOL)alwaysUsesMultipleValuesMarker;
By default, the array controller will look at values of selected objects on a key-by-key basis and indicate selections of multiple different values through the NSMultipleValuesMarker, but provide the common value if all selected objects have the same value. If alwaysUsesMultipleValuesMarker is set to YES, it will use the NSMultipleValuesMarker for all selections with two or more objects (you will typically also set NSAllowsEditingMultipleValuesSelectionBindingOption option of the various value bindings for controls bound to the array controller's selection to NO). This new flag is very useful in applications with very large arrays of objects that don't want to allow editing multiple selected objects at all and also results in strong performance enhancements in certain situations with large arrays.


NSUserDefaultsController (Section added since WWDC)

NSUserDefaultsController has a new method to indicate whether there are unapplied changes:
- (BOOL)hasUnappliedChanges;
For example, you can bind the enabled state of buttons to this method.


NSGraphicsContext

The NSGraphicsContext class now has API to specify the compositing operation setting: compositingOperation and setCompositingOperation:. As with other graphics context state, the setting is saved/restore via saveGraphicsState and restoreGraphicsState methods. The setting has no effect with existing rendering API that take compositing operation as an argument such as -[NSImage compositeToPoint:operation:].

The NSGraphicsContextDestinationAttributeName now allows an instance of NSBitmapImageRep as its value. Currently only non-planar NSBitmapImageRep instances are supported. A convenience factory method graphicsContextWithBitmapImageRep: is also added.

As an example of usage, the following code will resample the bitmap in srcRep to be width x height, with the result going to outputRep:
    NSBitmapImageRep *outputRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:width pixelsHigh:height bitsPerSample:8 samplesPerPixel:4
hasAlpha:YES isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace
bytesPerRow:0 bitsPerPixel:0];
    NSGraphicsContext *ctxt = [NSGraphicsContext graphicsContextWithBitmapImageRep:bitmapImageRep];
    [NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:ctxt];
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
[srcRep drawInRect:NSMakeRect(0, 0, width, height)];
[NSGraphicsContext restoreGraphicsState];

A factory method graphicsContextWithGraphicsPort:flipped: is added. You can instantiate NSGraphicsContext from any arbitrary CGContextRef using this method. The initialFlippedState argument determines the initial flippedness setting returned from the -isFlipped method.

The class now has the flipped setting accessible via the isFlipped method. The recommended way to determine the current flipped state of rendering coordinate system is now [[NSGraphicsContext currentContext] isFlipped] instead of [[NSView focusView] isFlipped]. Note, as with NSView's counterpart, this setting does not necessarily reflect the current coordinate system.


NSAffineTransform (Section added since WWDC)

The NSAffineTransform class implementation moved from AppKit to Foundation. -transformBezierPath:, -set, and -concat methods are now part of a category implemented in AppKit.


NSBitmapImageRep (Section updated since WWDC)

You can now get and set custom gamma values for PNG files using the NSImageGamma property. If set in the file, the property is set in the bitmap image rep on reading and is used when saving the bitmap as PNG data.

NSBitmapImageRep now supports alternate arrangements of alpha values and formats. The following enum and methods have been added:
typedef enum {
NSAlphaFirstBitmapFormat = 1 << 0, // 0 means is alpha last (RGBA, CMYKA, etc.)
NSAlphaNonpremultipliedBitmapFormat = 1 << 1, // 0 means is premultiplied
NSFloatingPointSamplesBitmapFormat = 1 << 2     // 0 means integer
} NSBitmapFormat;
- (id)initWithBitmapDataPlanes:(unsigned char **)planes
pixelsWide:(int)width pixelsHigh:(int)height bitsPerSample:(int)bps
samplesPerPixel:(int)spp hasAlpha:(BOOL)alpha isPlanar:(BOOL)isPlanar
colorSpaceName:(NSString *)colorSpaceName
bitmapFormat:(NSBitmapFormat)bitmapFormat bytesPerRow:(int)rBytes bitsPerPixel:(int)pBits;
- (NSBitmapFormat)bitmapFormat;
Returns new bitmap image rep using the specified format. Invalid formats will cause the method to return nil. For example, setting a floating point format requires bits/sample to be 32 (though we may support double or so-called half floating point values in the future.). The bitmap format may also be set to non-zero when loading in images from files. For example, PNG files are now loaded in with a format of NSAlphaNonpremultipliedBitmapFormat.

Return format image is in either from loading file or via -initWithBitmapDataPlanes:... or -initWithFocusedViewRect... If created using the older APIs that don't take a bitmap format, the format defaults to 0 (alpha last, premultiplied, integer)

By default, the new formats will be returned for cases where the source of the bitmap data is in the new format (floating point, non-premutliplied, etc.). Pre-Tiger compiled apps will still get the old premultiplied RGBA format. You can also enable or disable it explicity by setting the NSOldBitmapFormatOnly default to YES or NO. -initWithFocusedViewRect: will continue to return bitmap format 0 images.

You can now specify NSJPEG2000FileType as the output format to write JPEG 2000 files.

You can now specify progressive JPEG saving via the bitmap property NSImageProgressive to output progressive JPEG images.

When reading or writing a JPEG file, you can include an NSDictionary as the NSImageEXIFData bitmap image property.

Methods are now available that set/get the color info for a pixel in an NSBitmapImageRep. If the image rep is part of an NSImage and the rep is cached by the NSImage then changing the pixel values may not show up when drawing the image. If you do place the rep inside an image and intend to modify it, use [image setCacheMode:NSImageCacheNever]. For -getPixel:atX:y: and -setPixel:atX:y: you must supply an array to match the rep's samples per pixel and the range of values are based on the rep's bits per sample (e.g. if bps is 4, then values returned are from 0 to 15). Setting values out of range are not defined. If the bitmap has floating point samples, the actual values returned are floats. Similarly, pixel order and premultiplication are assumed to match the rep's bitmap format.
- (void)setColor:(NSColor *)color atX:(int)x y:(int)y;
- (NSColor *)colorAtX:(int)x y:(int)y;
- (void)getPixel:(unsigned int[]) atX:(int)x y:(int)y;
- (void)setPixel:(unsigned int[]) atX:(int)x y:(int)y;
If you do not pass in explicit planes and bytesPerRow values when creating an NSBitmapImageRep, the buffer pointer returned may no longer be the first byte of a malloc'ed block and the bytesPerRow may be padded with extra bytes for performance. If you traverse the bytes, do not assume that bytesPerRow = width * bitsPerSample / 8 otherwise the image may appear skewed.

If you modify the bits of an NSBitmapImageRep directly, you may find cases where the image does not draw the updated image because we now allow Quartz to cache the pixels (possibly in video memory). If you request the bitmap data pointer via -[NSBitmapImageRep getBitmapDataPlanes:] or -[NSBitmapImageRep bitmapData] then the cached information will be cleared and subsequent draws will draw the updated image. If your application just requests the bitmap data pointer once and then continuously modifys it, the image may not update.


NSImage and NSCachedImageRep caching behavior

The behavior of NSCachedImageRep has changed to improve performance when Quartz Extreme is enabled. These changes may affect your code. If the image rep is created via -[NSCachedImageRep initWithSize:depth:separate:alpha:] then after drawing, the image will be be copied from the offscreen window where it is stored and the window may be released. Do not rely on the window or the rectangle to be valid outside of NSImage -lockFocus/unlockFocus calls. If you need the window, calling -[NSCachedImageRep window] will return a valid new window and rectangle that can be used until the next time -[NSCachedImageRep draw] call is made. If the NSCachedImageRep is created via -[NSCachedImageRep: initWithWindow:rect:], after drawing, the contents of the window will be copied and may not reflect the current window contents when drawn later. If you want the imageRep to reflect the current window contents, calling -[NSCachedImageRep window] will release any cached information.


NSImageView now supports cut:, copy:, paste:, and delete: (Section added since WWDC)

For Tiger, NSImageView has acquired action methods that enable it to automatically support cut, copy, and paste behavior. This can pose a compatibility problem for some applications, however, as an NSImageView that is not the firstResponder, but is in the responder chain by virtue of being an ancestor of the firstResponder view, may consume -cut:, -copy:, -paste:, and -delete: action messages that would previously have been passed up the responder chain to a different intended recipient.

By default, NSImageView will provide the new behavior for applications built on Tiger, while suppressing it for compatibility for applications built on Panther and earlier. To allow applications to override this decision and disable or enable the new functionality on a per-instance basis, we have added a new "allowsCutCopyPaste" attribute, and the following corresponding accessor API, to NSImageView:
@interface NSImageView
- (BOOL)allowsCutCopyPaste;
- (void)setAllowsCutCopyPaste:(BOOL)flag;
@end

NSImageCell Animation Playback

Occasionally, the frames in an animaged GIF image will specify a playback duration of zero. On Panther, an animation-enabled NSImageCell plays such an animation back with no delay between frames (i.e. as fast as possible). On Tiger, NSImageCell clamps the frame duration to a minimum of 1/30 of a second, matching the way that Safari and Internet Explorer handle such GIFs.


NSImageCell NSScaleToFit and Copying

On Panther and earlier, an NSImageCell set to NSScaleToFit mode would always make a copy of its assigned image, even when the NSImageCell was sized such that the image would be displayed at exactly its original size. On Tiger, NSImageCell only makes this copy when the image is being displayed at some size other than its exact original size.


NSWorkspace Custom Icon Setting API

NSWorkspace has a new -setIcon:forFile:options: method that creates an icon from a given image and assigns it as the custom icon of a given file or folder:
- (BOOL)setIcon:(NSImage *)image forFile:(NSString *)fullPath options:(unsigned)options;
The "image" parameter specifies an arbitrary image, with or without transparency information (alpha), that will be automatically rescaled to generate the icon's representations. "fullPath" must specify an existing file or folder to which the user has write permissions. The "options" parameter is a bitwise combination of the following flags declared in NSWorkspace.h (it may be zero):
typedef unsigned int NSWorkspaceIconCreationOptions;
enum {
NSExcludeQuickDrawElementsIconCreationOption = 1 << 1,
NSExclude10_4ElementsIconCreationOption = 1 << 2
};
The option flags provide control over the kinds of representations the custom icon will contain.

The "QuickDraw" format allows for icon representations up to 128x128 pixels, and is supported on Mac OS X 10.0 through 10.4.

Mac OS X 10.4 supplements this with a new class of icon representation, designed to support higher resolutions with better storage efficiency. Finder on Mac OS X 10.4 does not yet make use of icons in this new format, but support for generating them is being provided in the -setIcon:forFile:options: API for forward compatibility. This new icon representation is safely ignored by Finder on Mac OS X 10.3, but its presence will prevent display of the file's custom icon on pre-10.3 systems, even if representations in the "QuickDraw" format are also present. Due to this compatibility issue, and to avoid needlessly consuming additional storage, it is recommended that applications that use the -setIcon:forFile:options: API suppress generation of this new representation, pending its use by a future release of the system.

NSExclude10_4ElementsIconCreationOption suppresses generation of representations in the new compressed high-resolution format. NSExcludeQuickDrawElementsIconCreationOption suppresses generation of the QuickDraw-format representations that are understood and used by Mac OS X 10.0 through 10.4. When neither flag is specified, the behavior will be to generate representations in both formats, resulting in a file or folder whose custom icon will be displayable on Mac OS X 10.3 and 10.4.


NSWorkspace (Section added since WWDC)

If the method fullPathForApplication: failed to find an application using the standard LaunchServices database, it would fall back to doing a world-search in the standard places where applications many be found. In practice though the LaunchServices database does include those places, so a brute force search through these just makes the method call considerably slower.

For apps linked on Tiger, this method will no longer do this secondary search.

The method selectFile:inFileViewerRootedAtPath: now consults a user default named "NSFileViewer", and if present, uses it as the bundle ID of the application to use as the file viewer to select the file in. If this default is not set, or there is no corresponding registered application, then Finder is used as normal. selectFile:inFileViewerRootedAtPath: is the method applications should use for "Reveal in Finder" functionality.

Since 10.0, the method getInfoForFile:application:type has been behaving differently than documented. The documentation claimed that the returned type is actually not the file type but one of a small set of predetermined identifiers indicating the kind of file; it turns out this method actually returned the type of the document. In Tiger, we continue with this preexisting behavior; the documentation will be fixed. The other documentation claim, that this method will return NO if the file doesn't exist, is true only when application info is asked for (argument to application: is non-NULL). So this method will return NO only if app info is asked for, and either the file doesn't exist, or LaunchServices does not have an application association for the document.

The NSWorkspaceDidUnmountNotification notification could be sent even for unsuccessful unmount attempts. It is now sent only if the volume is actually unmounted.


NSColor (Section added since WWDC)

Calibrated NSColors (those created with colorWithCalibratedRed:.., colorWithCalibratedHue:.., or colorWithCalibratedWhite:...) now use Quartz generic color spaces, rather than the "display" (aka "device") color spaces. For debugging purposes this behavior can be disabled with the NSUseGenericColorSpaceForCalibrated default. This is a debugging default and will be removed in a future update.

In applications, the proper way to get the device color space behavior is to create colors with colorWithDeviceRed:..., colorWithDeviceWhite:..., etc.

In many cases, as appropriate, the underlying color for NSColors representing colors used in the user interface (for instance, methods such as -[NSColor alternateSelectedControlColor]) has been changed to device color space.


NSColorSpace (Section added since WWDC)

A new class, NSColorSpace, along with new API in NSColor, enables creating NSColor instances which refer to custom color spaces, including those created with ColorSync profiles.

You can create NSColorSpace instances from CMProfileRef instances or NSDatas containing ICC profile data:
- (id)initWithICCProfileData:(NSData *)iccData;
- (id)initWithColorSyncProfile:(void * /* CMProfileRef */)prof;
- (NSData *)ICCProfileData;
- (void * /* CMProfileRef */)colorSyncProfile;
You can query the characteristics of color spaces with:
- (int)numberOfColorComponents;       // Does not include alpha
- (NSColorSpaceModel)colorSpaceModel;
- (NSString *)localizedName; // Will return nil if no localized name
NSColorSpace is intended to be toll-free bridge to CGColorSpaceRef. But this has not been implemented in Tiger.

Note that the existing "color space" concept, where a small number of predefined color spaces are identified by their names, should not be confused with this new NSColorSpace class. Different types of NSColors have traditionally been distinguished by their "color space" (represented by colorSpaceName). This color space determines the primitive accessors for the color, and in general NSColor instances with different colorspaces do not compare equal; NSColor instances in the same colorspace compare equal if all their attributes are equal.

Existing color space names supported by NSColor are NSCalibratedWhiteColorSpace, NSCalibratedRGBColorSpace, NSDeviceWhiteColorSpace, NSDeviceRGBColorSpace , NSDeviceCMYKColorSpace, NSNamedColorSpace, and NSPatternColorSpace. NSColorSpace provides class methods to return instances which correspond to the applicable predefined color space names:
+ (NSColorSpace *)genericRGBColorSpace;   // NSColorSpace corresponding to Cocoa colorspace name NSCalibratedRGBColorSpace
+ (NSColorSpace *)genericGrayColorSpace; // NSColorSpace corresponding to Cocoa colorspace name NSCalibratedWhiteColorSpace
+ (NSColorSpace *)genericCMYKColorSpace;
+ (NSColorSpace *)deviceRGBColorSpace; // NSColorSpace corresponding to Cocoa colorspace name NSDeviceRGBColorSpace
+ (NSColorSpace *)deviceGrayColorSpace; // NSColorSpace corresponding to Cocoa colorspace name NSDeviceWhiteColorSpace
+ (NSColorSpace *)deviceCMYKColorSpace; // NSColorSpace corresponding to Cocoa colorspace name NSDeviceCMYKColorSpace
In order to support custom NSColorSpaces, we enable creating NSColors with the color space name "NSCustomColorSpace". Such colors are created with:
/* Create colors with arbitrary color space. The number of components in the provided array should match
the number dictated by the specified color space, plus one for alpha (1.0 for opaque colors);
otherwise an exception will be raised. If the color space is one which cannot be used with NSColors, nil is returned.
*/
+ (NSColor *)colorWithColorSpace:(NSColorSpace *)space components:(const float *)components count:(int)numberOfComponents;
and the attributes of such colors are accessed with:
/* For colors with custom color space; get the color space and individual floating point components, including alpha.
Note that all these methods will work for other NSColors which have floating point components.
They will raise exceptions otherwise, like other existing color space-specific methods.
*/
- (NSColorSpace *)colorSpace;
- (int)numberOfComponents;
- (void)getComponents:(float *)components;
One additional API added to NSColor allows converting colors between color spaces:
/* colorUsingColorSpace: will convert existing color to a new color space and create a new color,
which will likely have different component values but look the same. It will return the same color
if the color space is already the same as the one specified. Will return nil if conversion is not possible.
*/
- (NSColor *)colorUsingColorSpace:(NSColorSpace *)space;
colorUsingColorSpace: and the three methods above work not only on NSCustomColorSpace colors, but also on other floating component-based colors. Note that colorUsingColorSpace: does not guarantee to return a NSCustomColorSpace color; for instance, if -[NSColor colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]] is sent to a NSCalibratedRGBColorSpace color, the same color might very well be returned. Of course such a color will respond with NSColorSpace genericRGBColorSpace] for its colorSpace, so the result is not that unexpected.

The existing method colorUsingColorSpaceName: is still the sure way to get a color with a certain colorSpaceName for another color (or nil if the conversion is not possible).

When colors with custom color spaces are archived into old (non-keyed) archives, the assumption is that such archives still need to be read on pre-Tiger systems, so the colors are written in a pre-Tiger compatible fashion, by being converted to one of the named colorspaces, without the NSColorSpace instance. it's possible to override this behavior with the NSWriteCustomColorSpacesToOldArchives default; setting this to YES means that custom color spaces will be written to non-keyed archives, making them incompatible with pre-Tiger systems.


NSColorWell (Section added since WWDC)

Removing a NSColorWell from its window now deactivates the color well.


NSColorList (Section added since WWDC)

Non-editable color lists (identified by isEditable returning NO) now raise exceptions when an attempt is made to modify them. This is as documented.


Accessibility Changes

The following method was added:
- (BOOL)accessibilitySetOverrideValue:(id)value forAttribute:(NSString *)attribute;
It allows you to override the value of an attribute or add a new attribute to a particular UI Element – i.e. an instance of NSObject conforming to the the NSAccessibility protocol. Previously, the only way to accomplish this was to use a custom a subclass for that UI Element and override the appropriate NSAccessibility protocol methods – e.g. accessibilityAttributeValue.

• This method only works on objects whose class already implements the NSAccessibility protocol.

• The return value indicates if the attempt to override was successful.

• If the specified attribute is already supported by the object, the value you specified wins - i.e. f