Implementing right-click context menu in Cocoa as a function call
I was porting some Windows code to Cocoa that used a synchronous call to TrackMenuPopup (TPM_RETURNCMD | TPM_NONOTIFY) to display a context menu and immediately return the menu item selected. I didn't want the menu to post a notification because that would require a more complicated architecture that could port between Mac and Windows. I posted the question titled "Is there an equivalent technique in Cocoa for the synchronous TrackPopupMenu in Windows?" to stackoverflow.com.
I couldn't decouple the NSMenu notification to an NSView so I came up with a solution using a dummy NSView. This way I could implement a popup menu as a function call that returns the selected value. Apparently popUpContextMenu is synchronous:
I ultimately came up with the following solution:
@interface DVFBaseView : NSView
{
NSMenuItem* nsMenuItem;
}
– (void) OnMenuSelection:(id)sender;
– (NSMenuItem*)MenuItem;
@end
@implementation DVFBaseView
– (NSMenuItem*)MenuItem
{
return nsMenuItem;
}
– (void)OnMenuSelection:(id)sender
{
nsMenuItem = sender;
}
@end
// Calling Code:
void HandleRButtonDown (NSPoint pt)
{
NSRect graphicsRect; // contains an origin, width, height
graphicsRect = NSMakeRect(200, 200, 50, 100);
//—————————–
// Create Menu and Dummy View
//—————————–
nsMenu = [[[NSMenu alloc] initWithTitle:@"Contextual Menu"] autorelease];
nsView = [[[DVFBaseView alloc] initWithFrame:graphicsRect] autorelease];
NSMenuItem* item = [nsMenu addItemWithTitle:@"Menu Item# 1" action:@selector(OnMenuSelection:) keyEquivalent:@""];
[item setTag:ID_FIRST];
item = [nsMenu addItemWithTitle:@"Menu Item #2" action:@selector(OnMenuSelection:) keyEquivalent:@""];
[item setTag:ID_SECOND];
//———————————————————————————————
// Providing a valid windowNumber is key in getting the Menu to display in the proper location
//———————————————————————————————
int windowNumber = [(NSWindow*)myWindow windowNumber];
NSRect frame = [(NSWindow*)myWindow frame];
NSPoint wp = {pt.x, frame.size.height – pt.y}; // Origin in lower left
NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined
location:wp
modifierFlags:NSApplicationDefined
timestamp: (NSTimeInterval) 0
windowNumber: windowNumber
context: [NSGraphicsContext currentContext]
subtype:0
data1: 0

0; data2: 0];
[NSMenu popUpContextMenu:nsMenu withEvent:event forView:nsView];
NSMenuItem* MenuItem = [nsView MenuItem];
switch ([MenuItem tag])
{
case ID_FIRST: HandleFirstCommand(); break;
case ID_SECOND: HandleSecondCommand(); break;
}
}