Getting an NSOutlineView Item's parent

  • I'm writing a program which contains an outline view. The data source
    manages a bunch of dictionaries, arrays, and other objects nested
    inside each other. My problem is that when the data source method
    outlineView:objectValueForTableColumn:byItem: is called, I need to be
    able to get a pointer to the item's parent so I can find its key in
    the case that it is in a dictionary, or its index if its in an array.
    Is there any simple way of doing this, or am I going to have to make
    a big work around? Any help would be appreciated. Thanks.

    Isaiah
  • On Oct 17, 2006, at 4:07 PM, The Beerbowers wrote:

    > I'm writing a program which contains an outline view. The data
    > source manages a bunch of dictionaries, arrays, and other objects
    > nested inside each other. My problem is that when the data source
    > method outlineView:objectValueForTableColumn:byItem: is called, I
    > need to be able to get a pointer to the item's parent so I can find
    > its key in the case that it is in a dictionary, or its index if its
    > in an array. Is there any simple way of doing this, or am I going
    > to have to make a big work around? Any help would be appreciated.
    > Thanks.

    Create a simple NSObject subclass and use that instead of arrays,
    dictionaries, etc. The subclass can have a weak reference to its parent.

        - Scott
  • You normally retrieve the parent item from your own data model. The
    "item" input to objectValueForTableColumn isn't retained by
    NSOutlineView so it might be invalid if you don't manage it yourself.
    I think you are "abusing and not using" the NSOutlineView.

    However, to compute the parent item, you can start at the row previous
    to the item, and decrement the row count, checking if the previous
    item is expandable and if it is expanded. If that is the case, then it
    is the parent. (This idea comes from my way of saving the ouline view
    state and not actually used to retrieve the parent item.)

    for( int row = [outlineView rowForItem:theItem] - 1; row >= 0; --row ) {
    id item = [outlineView itemAtRow:idx];
    if( item == nil ) {
      return gRootItem; // the root parent
    }
    if( [outlineView isExpandable:item] == YES && [outlineView
    isItemExpanded:item] == YES) {
            // item is the parent for theItem
    } else {
      // item is sibling for theItem
    }
  • Greetings,

    Scott meant create a NSObject subclass that would suffice for your
    needs. If you really do need both array and dictionary functionality,
    and whatever else, you could make a class that contained one of each.
    When you would allocate one of these special objects you could tell it
    which of the contained objects to allocate. How ever you do it, the
    point is you would have a weak reference to the objects parent.

    Perrog's method could be extremely slow and I wouldn't use it. There
    may be a case where the parent is very far "up" there and thus the
    computation would be slow and CPU intensive. Also, I suspect that
    isExpandable calls the data source method -
    (BOOL)outlineView:(NSOutlineView*)outline isItemExpandable:(id)item so
    this is another potential slow spot. It would be better to have a
    direct link of some kind to the parent.

    I have found that using a CFTree, or other like class, is perfect for
    NSOutlineView data sources. I made my own but if you don't want to
    search for JKPTree, it's a wrapper on CFTree.

    I have found that trees are much cooler than it may seem and more
    useful. When I made my tree class I thought that I'd only use it for
    the project I was currently working on but I've used it since.

    If you are still stuck I suggest that you tell us why you need so many
    different classes to represent the contents of an outline view. It is
    not a very common way of doing it, at least not in my experience. With
    our combined consciousness we should be able to figure this out.

    Cheers, Alan

    PS. Don't forget to tell us how you fix this. ;)

    --
    // Quotes from yours truly -------------------------
    "You don't forget, you just don't remember."
    "Maturity resides in the mind."
    "Silence is the Universe's greatest gift."
    "When the World realizes that religion really is unimportant, then it
    shall evolve."
  • On Oct 18, 2006, at 4:13 AM, The Beerbowers wrote:

    >>> My problem is that when the data source
    >>> method outlineView:objectValueForTableColumn:byItem: is called, I
    >>> need to be able to get a pointer to the item's parent
    >>
    >> Create a simple NSObject subclass and use that instead of arrays,
    >> dictionaries, etc. The subclass can have a weak reference to its
    >> parent.
    >
    > I'm not sure if I understand what your saying here. Maybe I didn't
    > explain my problem very well either? Just in case here's an example:
    >
    > The data source's method outlineView:outlineView
    > objectValueForTableColumn:tableColumn byItem:item is called. From
    > this method I need to get a pointer to 'item's parent in the
    > NSOutlineView. That is my entire problem. (Sorry for being so
    > cryptic before.)

    You're trying to solve this via the NSOutlineView side of things, but
    that's not very Cocoa-like. It's generally much cleaner for the items
    *inside the view* to know who their parent is.

    It's sounds like you're just using Foundation classes in the view at
    the moment (dictionaries, arrays, strings), but it would probably be
    easier to make a custom class which contains the dictionaries and
    arrays.

    In that case, your NSOutlineView would be populated with instances of
    a class called Group:

    @interface Group : NSObject {
      NSString *title;
      NSMutableSet *children;
      Group *parent;
    }
    @end

    And the setter for parent would look like this:

    - (void)setParent:(id)newParent
    {
      // weak reference
      parent = newParent;
    }

    Then in objectValueForTableColumn:tableColumnbyItem:item you say:

    id parent = [item parent];

    The children in the NSMutableSet (or maybe NSMutableArray) could be
    whatever class you want. I don't know the design of your app so this
    is just a starting point.

        - Scott
  • 2006/10/18, Scott Stevenson <scott...>:

    > It's sounds like you're just using Foundation classes in the view at
    > the moment (dictionaries, arrays, strings), but it would probably be
    > easier to make a custom class which contains the dictionaries and
    > arrays.

    To write a generetic data source for objects like dictionaries,
    arrays, etc. you can also address your data with a string (or key
    path), and let the string represent the item.

    For example, the following items represents an "address" to an object.

    "rootObject.MyKey" -> [rootObject objectForKey:@"MyKey"]
    "rootObject.MyKey.2" -> [[rootObject objectForKey:@"MyKey"] objectAtIndex:2]

    You will have to take care of the item string retain and release
    yourself, since NSOutlineView doesn't do that; when creating them, add
    it to a "garbage array".

    You will have to write your own code for evaluating "rootObject.MyKey"
    as the statement [rootObject objectForKey:@"MyKey"], but it turns out
    to be trival code.
  • On Oct 19, 2006, at 3:21 PM, Perrog wrote:

    > For example, the following items represents an "address" to an object.
    >
    > "rootObject.MyKey" -> [rootObject objectForKey:@"MyKey"]
    > "rootObject.MyKey.2" -> [[rootObject objectForKey:@"MyKey"]
    > objectAtIndex:2]

    Is that second one a KVC keypath? You can't use indexes that way.

    Did you mean something like an NSIndexPath?

        - Scott
  • That is a keypath-like string used by my data source . The basic idea
    I wanted to share is that all rows in the table or outline view
    contains a NSString, instead of a CFTreeRef.
previous month october 2006 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
Go to today