+4  A: 

(If you want to change the layout of the menu, similar to how the Airport menu shows more info when you option click it, then keep reading. If you want to do something entirely different, then this answer may not be as relevant as you'd like.)

The key is -[NSMenuItem setAlternate:]. For an example, let's say we're going to build an NSMenu that has a Do something... action in it. You'd code that up as something like:

NSMenu * m = [[NSMenu alloc] init];

NSMenuItem * doSomethingPrompt = [m addItemWithTitle:@"Do something..." action:@selector(doSomethingPrompt:) keyEquivalent:@"d"];
[doSomethingPrompt setTarget:self];
[doSomethingPrompt setKeyEquivalentModifierMask:NSShiftKeyMask];

NSMenuItem * doSomething = [m addItemWithTitle:@"Do something" action:@selector(doSomething:) keyEquivalent:@"d"];
[doSomething setTarget:self];
[doSomething setKeyEquivalentModifierMask:(NSShiftKeyMask | NSAlternateKeyMask)];
[doSomething setAlternate:YES];

//do something with m

Now, you'd think that that would create a menu with two items in it: "Do something..." and "Do something", and you'd be partly right. Because we set the second menu item to be an alternate, and because both menu items have the same key equivalent (but different modifier masks), then only the first one (ie, the one that is by default setAlternate:NO) will show. Then when you have the menu open, if you press the modifier mask that represents the second one (ie, the option key), then the menu item will transform in real time from the first menu item to the second.

This, for example, is how the Apple menu works. If you click once on it, you'll see a few options with ellipses after them, such as "Restart..." and "Shutdown...". The HIG specifies that if there's an ellipsis, it means that the system will prompt the user for confirmation before executing the action. However, if you press the option key (with the menu still open), you'll notice they change to "Restart" and "Shutdown". The ellipses go away, which means that if you select them while the option key is pressed down, they will execute immediately without prompting the user for confirmation.

The same general functionality holds true for the menus in status items. You can have the expanded information be "alternate" items to the regular info that only shows up with the option key is pressed. Once you understand the basic principle, it's actually quite easy to implement without a whole lot of trickery.

Dave DeLong
Extremely useful information, though not what I was going for. I'll definitely find a use for it though. I'm really after the way the menu injects the networks it finds while it is open. Thanks
Tegeril
+5  A: 

Menu mouse tracking is done in a special run loop mode (NSEventTrackingRunLoopMode). In order to modify the menu, you need to dispatch a message so that it will be processed in the event tracking mode. The easiest way to do this is to use this method of NSRunLoop:

[[NSRunLoop currentRunLoop] performSelector:@selector(updateTheMenu:) target:self argument:yourMenu order:0 modes:[NSArray arrayWithObject:NSEventTrackingRunLoopMode]]

You can also specify the mode as NSRunLoopCommonModes and the message will be sent during any of the common run loop modes, including NSEventTrackingRunLoopMode.

Your update method would then do something like this:

- (void)updateTheMenu:(NSMenu*)menu
{
    [menu addItemWithTitle:@"Foobar" action:NULL keyEquivalent:@""];
    [menu update];
}
Rob Keniger
Gonna give this a shot tonight, thanks!
Tegeril
This doesn't seem to be working. I make my request for data via NSTask, wait for a notification, upon receiving that notification, I populate a data object that is accessible to the entire class, and call your NSRunLoop line which calls an updateTheMenu method. The menu does not update live however, I have to click out of it and then reopen it before the updated information shows up.
Tegeril
If you use `NSRunLoopCommonModes` instead of `NSEventTrackingRunLoopMode` does it work then?
Rob Keniger
When I did that, updateTheMenu was not called at all, which really confused me.
Tegeril