views:

848

answers:

3

I'm using NSOutlineView without NSTreeController and have implemented my own datasource. What is the best way to select an item? NSOutlineView support already expandItem: and collapseItem:. And I'm missing a handy method like `selectItem:. How can I do it programatically ?

Thank you.

+4  A: 

Remember to look in superclasses when you can't find something. In this case, one of the methods you need comes from NSTableView, which is NSOutlineView's immediate superclass.

The solution is to get the row index for the item, and if it isn't -1 (item not visible/not found), create an index set with it and pass that index set to the selectRowIndexes:byExtendingSelection: method.

Peter Hosey
Hi Peter, thank you for the answer. I already know the method selectRowIndexes:byExtendingSelection:. The problem is that NSOutlineView is working with NSIndexPath and not NSIndexSet.
cocoafan
I don't see a single instance of NSIndexPath in the NSOutlineView documentation. Perhaps you're thinking of NSTreeController, which you're not using. Moreover, an outline view *is* a table view, which means all the table view functionality should work just fine in the outline view.
Peter Hosey
Yes I see. To bad that there is no built-in solution which not depends on my datasource. I have to write extra code in my datasource, right?
cocoafan
No. Those three steps are all you have to do to select a specific item. (Or items, for that matter.)
Peter Hosey
I think I need at least the code in my datasource to expand a node if rowForItem: fails.
cocoafan
cocoafan: You mentioned `expandItem:` in your question. That's a method of the NSOutlineView. I'm not sure whether you can pass it a deeply-nested item, but even if you can't, you can use NSOutlineView's `parentForItem:` to build a stack of the desired item's ancestors, then walk that stack and expand each item in turn.
Peter Hosey
Peter, thank you for your advise. You were very helpful.
cocoafan
+1  A: 

No, there isn't a selectItem: method, but there is an rowForItem: method. If you combine that with Peter's advice about using selectRowIndexes:byExtendingSelection: above, you should have all the information you need.

If you really wanted to have a method to select an item, which I would recommend calling setSelectedItem: for consistency's sake, you could write something like this in a category on NSOutlineView

- (void)setSelectedItem:(id)item {
    NSInteger itemIndex = [self rowForItem:item];
    if (itemIndex < 0) {
        // You need to decide what happens if the item doesn't exist
        return;
    }

    [self selectRowIndexes:[NSIndexSet indexSetWithIndex:itemIndex] byExtendingSelection:NO];
}

I have no idea if this code actually works; I just dashed it off to illustrate the concept.

Alex
Thanks to both of you for your advise.
cocoafan
+2  A: 

Here is how I finally ended up. Suggestions and corrections are always welcome.

@implementation NSOutlineView (Additions)

- (void)expandParentsOfItem:(id)item {
    while (item != nil) {
        id parent = [self parentForItem: item];
        if (![self isExpandable: parent])
            break;
        if (![self isItemExpanded: parent])
            [self expandItem: parent];
        item = parent;
    }
}

- (void)selectItem:(id)item {
    NSInteger itemIndex = [self rowForItem:item];
    if (itemIndex < 0) {
        [self expandParentsOfItem: item];
        itemIndex = [self rowForItem:item];
        if (itemIndex < 0)
            return;
    }

    [self selectRowIndexes: [NSIndexSet indexSetWithIndex: itemIndex] byExtendingSelection: NO];
}
@end
cocoafan