NSTreeController Development: Walking the Tree

Since I started developing for Mac OS X on brand new shinny Leopard, I was able to take advantage of the new API calls on NSTreeController to walk the tree, access the real objects and not the proxy objects and to insert items into the controller easier than folks have in the past.  That is what I've gathered from reading about NSTreeController on the Internet.

The method arrangedObjects is very useful, see here: – arrangedObjects Notice that it returns a proxy root tree node containing the receiver's sorted content object. Those are some very important words and hard to explain, so instead of trying to explain, I''ll show.

NSArray *treeNodes = [[self arrangedObjects] childNodes];

So, arrangedObjects returns a proxy root tree node, so just pass that into the childNodes of NSTreeController, another useful 10.5 method, and you get all the children of the treeNode. Since I used arrangedObject, I get the top of the tree so that I can with it as I please, mostly.

Anyway here are some category classes that allow you to walk the tree using any controller selector as a callback.  This example is not perfect, it  is a work in progress, but useful none the less.

The code below is defined as a catagory of NSTreeController, in a garbage collected environment, so there is no memory management.

//
// NSTreeController-Extensions.h
//
// Created by Joe Michels on 3/8/08.
// Copyright 2008 Software Ops LLC. All rights reserved.
//

#import Cocoa/Cocoa.h


@interface NSTreeController (NSTreeController_Extensions)

- (
void)visitAllNodesStartingWithNode: (NSArray* )treeNodes useSelector: (SEL)inSelector
                                                                 withObject: (
id)inObject;

@end


//
// NSTreeController-Extensions.m
// TeamPlayer
//
// Created by Joe Michels on 3/8/08.
// Copyright 2008 Software Ops LLC. All rights reserved.
//

#import "NSTreeController-Extensions.h"
#import
"ProjectsController.h"


@implementation NSTreeController (NSTreeController_Extensions)


- (
void)visitAllNodesStartingWithNode: (NSArray* )treeNodes
                                                                         useSelector: (
SEL)inSelector
                                                                         withObject: (
id)inObject
{
      
// This is a recursive decent routine. It will walk the tree visiting each node

      
for (id item in treeNodes)
      {
            
NSLog( @"Visiting tree node: %@", [[item representedObject] valueForKeyPath:@"name"] );

            [
self performSelector:inSelector withObject:item withObject:inObject];

            
//check for a decendent
            
NSArray *childNodes = [item childNodes];
            
if ([childNodes count] > 0)
                                                                             
// we are recursing
                  [
self visitAllNodesStartingWithNode:childNodes useSelector:inSelector withObject:inObject];
      }
}
@end


Let's make a call using the above code.

- (NSArray* )itemsForScripting
{
      
NSLog(@"Getting projects for scripting, returned in array.");
      
      
NSArray *treeNodes = [[self arrangedObjects] childNodes];
      
NSMutableArray *itemsArray = [[NSMutableArray alloc] initWithCapacity: 2]; << 2??, whatever!

      [
self visitAllNodesStartingWithNode:treeNodes
            
useSelector:@selector(getOrderedListOfProjects:withArray: )
            
withObject:itemsArray];
      
      
return itemsArray;
}


You can pass nil for the object as in this code snipet:

NSArray *treeNodes = [[self arrangedObjects] childNodes];
[
self visitAllNodesStartingWithNode:treeNodes
      
useSelector:@selector(findAllMatchesInNodes:withObject: )
      
withObject:nil];


|