Custom NSPopupButtonCell in NSTableView

685 views Asked by At

I am trying to create a custom popup menu in a tableview. As I understand it I can should be able to do so by calling the [NSPopupButtonCell setView:myView] method passing in the custom view (which is just a NSView with an NSOutlineView in it).

So I have created a NSPopupButtonCell subclass and during initialisation I call setView and pass in the custom outline view..

EDIT

In IB I have set the table columns cell to a Popup Button Cell and set the class to my custom LookupPopupButtonCell.

I still don't get my custom view displaying but my custom classes initialisation methods appear to be getting called.

I have since replaced this approach with using a NSTableViewDelegate method dataCellForTableColumn. Now the popup shows my custom tableView.

Still no joy in getting the NSOutlineViewDelegate methods called though.

EDIT OK I have managed to get things working using a NSPopupButton on a view. delegate works find and table view displays things fine. Seems that using NSPopupButtonCell the delegate methods never get called.

@implementation LookupPopupButtonCell

- (id)init
{
    LOG(@"init called");
    self = [super init];
    if (self) {
        [self initialise];
    }
    return self;
}
- (void)initialise
{
    LOG(@"initialise called");
    
    [self setFont:[NSFont fontWithName:@"System Regular" size:11]];
    [self setBordered:NO];
    [self setBezeled:NO];
    
    // Set the Task Lookup Popup Menu
    NSMenu *newLookupMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Custom"];
    
    NSMenuItem *newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Lookup" action:nil keyEquivalent:@""];
    [newItem setEnabled:YES];
    
    TaskLookupViewController *viewController = [[TaskLookupViewController alloc] initWithNibName:@"TaskLookupViewController" bundle:nil];
    
    [newItem setView:[viewController view]];
    
    [newLookupMenu addItem:newItem];
    [newItem release];
    
    [self setMenu:newLookupMenu];
}
@end

@implementation TaskLookupViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Initialization code here.
        [self initialise];
    }
    
    return self;
}
- (void)awakeFromNib{
    LOG(@"awakeFromNib called...");
    [self viewDidLoad];
}

- (void)viewDidLoad {
    FLOG(@"viewDidLoad called for %@", self);
    /*
    FLOG(@" _outlineView is %@", _outlineView);
    FLOG(@" _outlineView is %@", [_outlineView identifier]);
    FLOG(@" _outlineView delegate is %@", [_outlineView delegate]);
    FLOG(@" _outlineView dataSource is %@", [_outlineView dataSource]);
     */
    [_outlineView setDataSource:self];
    [_outlineView setDelegate:self];
    [_outlineView reloadData];
    [_outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
    [_outlineView setNeedsDisplay];
    [_outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:2] byExtendingSelection:NO];
    /*
    FLOG(@" _outlineView delegate is %@", [_outlineView delegate]);
    FLOG(@" _outlineView dataSource is %@", [_outlineView dataSource]);
    */
    //NSTableColumn *tableColumn = [[_outlineView tableColumns] objectAtIndex:0];
    
    //LOG(@" setting bindings");
    
    //[tableColumn bind: @"value" toObject: _treeController withKeyPath: @"arrangedObjects.displayName" options: nil];

}
- (void)initialise {
    LOG(@"initialise called");
    
    _topLevelItems = [[NSArray arrayWithObjects:@"Project", @"Tasks and Deliverables", @"Finance", nil] retain];
    
    _childrenDictionary = [NSMutableDictionary new];
    
    [_childrenDictionary setObject:[NSArray arrayWithObjects:@"Scope", nil] forKey:@"Project"];
    
    //[_childrenDictionary setObject:[NSArray arrayWithObjects:@"Issues", @"Risks", nil] forKey:@"Quality"];

    [_childrenDictionary setObject:[NSArray arrayWithObjects:@"WBS", @"Functions", nil] forKey:@"Tasks and Deliverables"];
    
    [_childrenDictionary setObject:[NSArray arrayWithObjects:@"Expenses", @"Ongoing Costs", @"Timesheets", nil] forKey:@"Finance"];
    
    //[_childrenDictionary setObject:[NSArray arrayWithObjects:@"Applications", @"Interfaces", nil] forKey:@"IT Systems"];
    
    //[_childrenDictionary setObject:[NSArray arrayWithObjects:@"People", @"Setup", nil] forKey:@"Administration"];
    
}

- (NSArray *)_childrenForItem:(id)item {
    LOG(@"_childrenForItem called");
    NSArray *children;
    if (item == nil) {
        children = _topLevelItems;
    } else {
        children = [_childrenDictionary objectForKey:item];
    }
    //FLOG(@" children are %@", children);
    
    return children;
}
@end

@implementation TaskLookupViewController (NSOutlineViewDataSource)

- (void)outlineViewSelectionDidChange:(NSNotification *)notification {
    LOG(@"outlineViewSelectionDidChange: called");
    if ([_outlineView selectedRow] != -1) {
        NSObject *item = [_outlineView itemAtRow:[_outlineView selectedRow]];
        if ([_outlineView parentForItem:item] != nil) {
            // Only change things for non-root items (root items can be selected, but are ignored)
            FLOG(@" selected item is %@", item);
        }
    }
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
    FLOG(@"outlineView:child:ofItem: called for item %@", item);
    return [[self _childrenForItem:item] objectAtIndex:index];
}

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
    LOG(@"outlineView:isItemExpandable: called");
    if ([outlineView parentForItem:item] == nil) {
        return YES;
    } else {
        return YES;
    }
}

- (NSInteger) outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
    LOG(@"outlineView:numberOfChildrenOfItem: called");
    FLOG(@" children count is %d", [[self _childrenForItem:item] count]);
    return [[self _childrenForItem:item] count];
}
@end

@implementation TaskLookupViewController (NSOutlineViewDelegate)

- (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
    LOG(@"outlineView:isGroupItem: called");
    return [_topLevelItems containsObject:item];
}

- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
    LOG(@"willDisplayCell called");
    [cell setTitle:@"Cell Title"];
}

- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
    LOG(@"outlineView:viewForTableColumn called");
    // We just return a regular text view.

    if ([_topLevelItems containsObject:item]) {
        NSTextField *result = [outlineView makeViewWithIdentifier:@"HeaderTextField" owner:self];
        // Uppercase the string value, but don't set anything else. NSOutlineView automatically applies attributes as necessary
        NSString *value = [item uppercaseString];
        [result setStringValue:value];
        return result;
    } else  {

        NSTextField *result = [outlineView makeViewWithIdentifier:@"ItemTextField" owner:self];
        [result setStringValue:value];
        return result;
    }
}

- (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item;
{
    LOG(@"outlineView:shouldExpandItem: called");
        return YES;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
{
    LOG(@"outlineView:shouldSelectItem: called");
    return YES;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldCollapseItem:(id)item;
{
    LOG(@"outlineView:shouldCollapseItem: called");
    return NO;
}
@end

@implementation TaskLookupViewController (NSTableViewDelegate)
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    LOG(@"tableView:dataCellForTableColumn:row: called");

    NSString *identifier = [tableColumn identifier];
    
    if ([identifier isEqualToString:@"task"]) {
        //LOG(@" task column setting the cell");

        LookupPopupButtonCell *cellView = [[LookupPopupButtonCell alloc] init];
        return cellView;
    }
    
    NSTextFieldCell *cellView = [tableView makeViewWithIdentifier:@"hoursCell" owner:self];
    return cellView;
    
}
@end
1

There are 1 answers

0
Duncan Groenewald On

It seems you must use a VIEW based tableView to get the delegate messages. Blah, now to figure out how to bind one of them to Core Data, hopefully its not too hard !

Is there any way to reuse the same menu for each row ? I guess as long as the dataSource is not recreated each time its probably not too bad, still there could be lots of rows in this hierarchy!

Outline view in tableView popup menu