Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

simX

macrumors 6502a
Original poster
May 28, 2002
765
4
Bay Area, CA
I'm trying to provide a contextual menu to a table view that I have in my standard Cocoa application. Ideally, this will allow those with two-button mice or those with lightning-quick reflexes to quickly access some options based on the item selected in the list.

However, I'm having an annoying problem: when you right-click directly on an item in the NSTableView, the item under the mouse isn't automatically selected! Because how my actions are created, they rely on the selected item in the NSTableView, so right-clicking in the table view and then selecting an action will probably not (in most cases) result in the action being applied to the desired item. This negates the usefulness of the contextual menus to a great extent.

What's the easiest way to modify the mouse event so that a control-click on this NSTableView will first select the item and THEN bring up the contextual menu? (I'm even interested in "workarounds" where you would get the mouse position at the time of the control-click, and then my program would calculate which item should be selected, select it, and then bring up the menu.)
 

HexMonkey

Administrator emeritus
Feb 5, 2004
2,240
504
New Zealand
The way I do it is to subclass NSTableView and change the table selection in the menuForEvent method. Here's the code I use (note that it requires Panther since it uses -[NSTableView selectedRowIndexes], if you need to support Jaguar use -[NSTableView selectedRowEnumerator] instead):

Code:
-(NSMenu*)menuForEvent:(NSEvent*)event
{
	//Find which row is under the cursor
	[[self window] makeFirstResponder:self];
	NSPoint menuPoint = [self convertPoint:[event locationInWindow] fromView:nil];
	int row = [self rowAtPoint:menuPoint];
	
	/* Update the table selection before showing menu
	Preserves the selection if the row under the mouse is selected (to allow for
	multiple items to be selected), otherwise selects the row under the mouse */
	BOOL currentRowIsSelected = [[self selectedRowIndexes] containsIndex:row];
	if (!currentRowIsSelected)
		[self selectRow:row byExtendingSelection:NO];
	
	if ([self numberOfSelectedRows] <=0)
	{
        //No rows are selected, so the table should be displayed with all items disabled
		NSMenu* tableViewMenu = [[self menu] copy];
		int i;
		for (i=0;i<[tableViewMenu numberOfItems];i++)
			[[tableViewMenu itemAtIndex:i] setEnabled:NO];
		return [tableViewMenu autorelease];
	}
	else
		return [self menu];
}
 

simX

macrumors 6502a
Original poster
May 28, 2002
765
4
Bay Area, CA
Yes! Woo!

OMG. You rock! That worked perfectly! You solved it exactly how I would have done it... not interrupting the selection if it's already selected, and selecting it if it isn't.

Just a quick question, though -- does the menuForEvent: method handle anything else? That is, if I change the NSTableView method so that it uses your code instead, will it affect anything else that you might do with a table view? I assume, given the name of the method, that this just handles the display of a contextual menu, so all you're basically doing is adding some code so that the selection will happen first before the menu comes up. Am I right?
 

caveman_uk

Guest
Feb 17, 2003
2,390
1
Hitchin, Herts, UK
HexMonkey said:
Here's the code I use (note that it requires Panther since it uses -[NSTableView selectedRowIndexes], if you need to support Jaguar use -[NSTableView selectedRowEnumerator] instead):
I guess you could use selectedRowEnumerator for both as although it's now deprecated it still works.
 

HexMonkey

Administrator emeritus
Feb 5, 2004
2,240
504
New Zealand
simX said:
Just a quick question, though -- does the menuForEvent: method handle anything else? That is, if I change the NSTableView method so that it uses your code instead, will it affect anything else that you might do with a table view? I assume, given the name of the method, that this just handles the display of a contextual menu, so all you're basically doing is adding some code so that the selection will happen first before the menu comes up. Am I right?

Yes, you're correct. From the documentation:

Overridden by subclasses to return a context-sensitive pop-up menu for the mouse-down event theEvent....NSView’s implementation returns the receiver’s normal menu.

So by default it would return [self menu] and do nothing else.

caveman_uk said:
I guess you could use selectedRowEnumerator for both as although it's now deprecated it still works.

Yeah, I still use it extensively in a lot of my older code. I don't expect Apple will remove it for a long time (if ever) since that would break a lot of applications.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.