What is up.
RSS icon Email icon Home icon
  • Implementing right-click context menu in Cocoa as a function call

    Posted on October 23rd, 2008 Alan No comments

    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:

    // Dummy View class used to receive Menu Events

    @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
                &#016
    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;
        } 
    }

    Leave a reply