i would like to place a context menu onto a NSTableView
. this part is done. what i would like to do is to show different menu entries based on the content of the right clicked cell, and do NOT show the context menu for specific columns.
this is:
column 0, and 1 no context menu
all other cells should have the context menu like this:
first entry: "delete " samerow.column1.value
second entry: "save " samecolumn.headertext
-EDIT-
the one on the right is how the context menu is supposed to look like for any given cell.
Typically, a ContextMenu is displayed when the user clicks the right mouse button on a control or area of the form that the ContextMenu is bound to. You can use this method to manually display the shortcut menu at a specific location and bind it with a specific control.
A context menu (also know as a contextual menu, shortcut menu or pop-up menu) is the menu that appears when you right-click and offers a set of choices that are available for, or in context of, whatever it was you clicked. The available choices are usually actions specifically related to the selected object.
A context menu is a pop-up menu that provides shortcuts for actions the software developer anticipates the user might want to take. In a Windows environment, the context menu is accessed with a right mouse click.
In iOS and iPadOS, a context menu can display a preview of the current content near the list of commands. People can choose a command in the menu or — in some cases — they can tap the preview to open it or drag it to another area. Prefer a graphical preview that clarifies the target of a context menu's commands.
Theres a delegate for that! - No need to subclass
In IB if you drag an NSTableView
onto your window/view you'll notice that theres a menu
outlet for the table.
So a very easy way to implement the contextual menu is to connect that outlet to a stub menu and connect the delegate outlet of the menu to an object which implements the NSMenuDelegate
protocol method - (void)menuNeedsUpdate:(NSMenu *)menu
Normally the delegate of the menu is the same object which provides the datasource/delegates to the table but it might also be the view controller which owns the table too.
Have a look at the docs for more info on this
Theres a bundle of clever stuff you can do in the protocol but a very simple implementation might be like below
#pragma mark tableview menu delegates
- (void)menuNeedsUpdate:(NSMenu *)menu
{
NSInteger clickedrow = [mytable clickedRow];
NSInteger clickedcol = [mytable clickedColumn];
if (clickedrow > -1 && clickedcol > -1) {
//construct a menu based on column and row
NSMenu *newmenu = [self constructMenuForRow:clickedrow andColumn:clickedcol];
//strip all the existing stuff
[menu removeAllItems];
//then repopulate with the menu that you just created
NSArray *itemarr = [NSArray arrayWithArray:[newmenu itemArray]];
for(NSMenuItem *item in itemarr)
{
[newmenu removeItem:[item retain]];
[menu addItem:item];
[item release];
}
}
}
And then a method to construct the menu.
-(NSMenu *)constructMenuForRow:(int)row andColumn:(int)col
{
NSMenu *contextMenu = [[[NSMenu alloc] initWithTitle:@"Context"] autorelease];
NSString *title1 = [NSString stringWithFormat:@"Delete %@",[self titleForRow:row]];
NSMenuItem *item1 = [[[NSMenuItem alloc] initWithTitle:title1 action:@selector(deleteObject:) keyEquivalent:@""] autorelease];
[contextMenu addItem:item1];
//
NSString *title2 = [NSString stringWithFormat:@"Save %@",[self titleForColumn:col]];
NSMenuItem *item2 = [[[NSMenuItem alloc] initWithTitle:title1 action:@selector(saveObject:) keyEquivalent:@""] autorelease];
[contextMenu addItem:item2];
return contextMenu;
}
How you choose to implement titleForRow:
and titleForColumn:
is up to you.
Note that NSMenuItem
provides the property representedObject
to allow you to bind an arbitrary object to the menu item and hence send information into your method (e.g deleteObject:
)
EDIT
Watch out - implementing - (void)menuNeedsUpdate:(NSMenu *)menu
in your NSDocument
subclass will stop the Autosave/Versions menu that appears in the title bar appearing in 10.8.
It still works in in 10.7 so go figure. In any case the menu delegate will need to be something other than your NSDocument
subclass.
Edit: The better way to do this than the below method is using delegate as shown in the accepted answer.
You can subclass your UITableView and implement menuForEvent:
method:
-(NSMenu *)menuForEvent:(NSEvent *)event{
if (event.type==NSRightMouseDown) {
if (self.selectedColumn == 0 || self.selectedColumn ==1) {
return nil;
}else {
//create NSMenu programmatically or get a IBOutlet from one created in IB
NSMenu *menu=[[NSMenu alloc] initWithTitle:@"Custom"];
//code to set the menu items
//Instead of the following line get the value from your datasource array/dictionary
//I used this as I don't know how you have implemented your datasource, but this will also work
NSString *deleteValue = [[self preparedCellAtColumn:1 row:self.selectedRow] title];
NSString *deleteString = [NSString stringWithFormat:@"Delete %@",deleteValue];
NSMenuItem *deleteItem = [[NSMenuItem alloc] initWithTitle:deleteString action:@selector(deleteAction:) keyEquivalent:@""];
[menu addItem:deleteItem];
//save item
//similarly
[menu addItem:saveItem];
return menu;
}
}
return nil;
}
That should do it. I haven't tried out the code though. But this should give you an idea.
I also tried the solution posted by Warren Burton and it works fine. But in my case I had to add the following to the menu items:
[item1 setTarget:self];
[item2 setTarget:self];
Setting no target explicitly causes the context menu to remain disabled.
Cheers!
Alex
PS: I would have posted this as a comment but I do not have enough reputation to do that :(
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With