enum { NSNotFound = 0x7fffffff };

Odd notes about developing in Cocoa and Objective-C

Tuesday, August 28, 2007

NSImageView and image filenames

One of my pet peeves with the cocoa class NSImageView is that there is no way to directly get the path of image files you drop on this view.

Below is my cure for this. It overrides two methods to do its work.

First it overrides -performDragOperation to grab the filename from the draggingPasteboard, squirreling it away in an ivar for later access.

This may be sufficient for many uses, especially if you can easily access the view from you code. In my last use of this code, I was using bindings, so I wanted the filename to be available to me when -observeValueForKeyPath was called with a keyPath of "selection.image". I didn't have any reference to the NSImageView at that point, so I have the code set the image's name to be the path string. That makes it easy enough to access the image's name later when needed.

Note: this code sets the image's name in the -setImage method because trying to set the name of the view's image inside -performDragOperation results in setting the name for the old image (the one being replaced), rather than the new one.


@interface MyImageView : NSImageView
{
NSString *mImagename;
}
@end

@implementation MyImageView

- (void)setImage:(NSImage *)image
{
[image setName:[[mImagename lastPathComponent] stringByDeletingPathExtension]];
[super setImage:image];
}

- (BOOL)performDragOperation:(id )sender
{
BOOL dragSucceeded = [super performDragOperation:sender];
if (dragSucceeded) {
NSString *filenamesXML = [[sender draggingPasteboard] stringForType:NSFilenamesPboardType];
if (filenamesXML) {
NSArray *filenames = [NSPropertyListSerialization
propertyListFromData:[filenamesXML dataUsingEncoding:NSUTF8StringEncoding]
mutabilityOption:NSPropertyListImmutable
format:nil
errorDescription:nil];
if ([filenames count] >= 1) {
mImagename = [filenames objectAtIndex:0];
} else {
mImagename = nil;
}
}
}
return dragSucceeded;
}

@end

Tuesday, August 21, 2007

Picking up PageSetup Dialog Changes

The standard Page Setup dialog (OK, "sheet") can be customized by implementing -runPageLayout in your NSApplication or NSDocument subclass. It's all nicely documented in Apple's fine Developer Docs.

But what if the standard dialog is fine for your needs but you still want to run some code when the user changes the page setup?

All you need to do is implement -setPrintInfo in your NSDocument subclass. It'll get called with the new printInfo. Something like this:


- (void) setPrintInfo:(NSPrintInfo*)info
{
[super setPrintInfo:info];
[self myCodeThatDoesSomethingWithTheNewPrintInfo];
}

Wednesday, April 18, 2007

Automatically reopen a document on launch

A handy technique during development is to have an application automatically open the last document it used each time it's launched.


It's just that much faster to get back running the app after making a change.


Add this method to the app delegate:


- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSArray *urls = [[NSDocumentController sharedDocumentController] recentDocumentURLs];
if ([urls count] > 0) {
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:[urls objectAtIndex:0]
display:YES];
}
}

Tuesday, March 6, 2007

if blocks

I've never understood why some programmers continue to write C code in the form:


if (someCondition)
doSomething();
else
doSomethingElse();

as opposed to


if (someCondition) {
doSomething();
} else {
doSomethingElse();
}

Beyond saving a couple of keystrokes, the first example has absolutely no advantage over the second. While the second is much more bullet proof.

When working on code written by others that cling to the first form, I continue to run across code like this:


if (someCondition)
doSomething();
doMore();
doOtherthings();

Where the intention is that doMore() is intended to be executed only when someCondition is true.

When you look at the history of the code it almost always starts out as


if (someCondition)
doSomething();
doOtherthings();

and then later the doMore() is added.

Please, please, please take the extra split second it takes to type in the safe form of the if from day one.

Saturday, February 17, 2007

Timing operations

I'll sometimes want to measure the time a certain operation takes and compare it with another operation or to compare it with another implementation of the same operation. Here's the fragment of Cocoa code I use to do that:


NSDate *startTime = [NSDate date];

{ ... } // the operation that I'm timing

NSTimeInterval elapsedTime = -[startTime timeIntervalSinceNow];

NSLog(@"That took %f seconds", elapsedTime);

This code grabs the current time using NSDate, does something, calculates the time spent doing the operation, and then logs the result.

Wednesday, February 14, 2007

Optional breakpoints

I sometimes find myself wanting to break into the debugger only after a series of steps that would otherwise trigger the breakpoint. I find the following code snippet useful in some of these situations.


BOOL optionKeyIsDown = (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0);
if (optionKeyIsDown) {
NSLog(@"voila");
}

You set a breakpoint on the NSLog() which it's only triggered when you have the option key down.

It's easy to use another modifier key if you like by using another key mask constant instead of NSAlternateKeyMask.

The downside of course is that it requires a little piece of live code that needs to be cleaned up before ship time.

This same approach can useful for other things as well. For example, you could trigger a data structure verification routine. Maybe that routine is very time consuming and you don't want it interfering with normal execution, but by simply holding down the option key (or may multiple modifier keys) you can trigger some special code.

Sunday, February 11, 2007

A simple ~/.gdbinit file

I have a one line .gdbinit file containing:

 fb -[NSException raise] 

This is executed each time gdb (the Xcode debugger) is started. With this the code will break into the debugger whenever an exception is raised by a Cocoa program.

fb is "forward break" which unlike plain b, or "break", will set a breakpoint on code that hasn't been loaded yet.