PDA

View Full Version : NSBrowser Question




Soulstorm
Feb 17, 2008, 05:26 AM
I have a problem which probably is a logical one. I have a tree representing nodes of a simple XML document. Actually, what it represents is pretty irrelevant to my problem.

I want to be able to represent these data (which is plain text) into an NSBrowser. Although I fail to see how can I traverse the tree and represent those nodes. I have chosen the passive implementation of the tree, since it's easier. This is what my delegate does:

@interface GeneralHandler : NSObject {
NSXMLDocument *doc;

NSXMLNode *currentNode;
}

@end


@implementation GeneralHandler

-(void)awakeFromNib
{
NSLog([[self class]description]);
NSString *xmlContents = [NSString stringWithContentsOfFile:[[NSString stringWithString:@"~/Desktop/info.xml"]stringByStandardizingPath]];
doc = [[NSXMLDocument alloc]initWithXMLString:xmlContents options:NSXMLDocumentTidyXML error:nil];
NSLog([[doc rootElement]name]);
currentNode = [doc rootElement];
}

- (int)browser:(NSBrowser *)sender numberOfRowsInColumn:(int)column {
if (column == 0) {
return 1;
}

return [currentNode countSiblings];


}


- (void)browser:(NSBrowser *)sender willDisplayCell:(id)cell atRow:(int)row column:(int)column {
// Find our parent FSNodeInfo and access the child at this particular row

[cell setStringValue:[currentNode name]];


currentNode = [currentNode childAtIndex:row];

}

@end

Note that countSiblings is a function created by me that count's a node's siblings. Anyway , how am I going to do this? I need just a method, not necessarily code. Any help would be appreciated.



kainjow
Feb 17, 2008, 09:08 AM
You might want to check out the example at /Developer/Examples/AppKit/SimpleBrowser

Soulstorm
Feb 17, 2008, 12:56 PM
Nah. Used the active delegation method instead. It may require more lines, but it is a far more decent approach. Thanks, anyway.

kainjow
Feb 17, 2008, 01:23 PM
Well then you'll need to figure out a way to convert a row and column value into a corresponding node from your XML. Or try using XPath.

Edit: ok it shouldn't be too hard. NSBrowser works by having a selection in each column. So to get the XMLNode at any point, first get column 0's selected row (R1), then get child R1 of your root element (C1), then get column 1's selected row (R2), then get child R2 of C1, rinse and repeat.

Soulstorm
Feb 17, 2008, 02:10 PM
Well then you'll need to figure out a way to convert a row and column value into a corresponding node from your XML. Or try using XPath.

Edit: ok it shouldn't be too hard. NSBrowser works by having a selection in each column. So to get the XMLNode at any point, first get column 0's selected row (R1), then get child R1 of your root element (C1), then get column 1's selected row (R2), then get child R2 of C1, rinse and repeat.

Thank you for your answer. I didn't use XPath, but it works for now. I made a simple wrapper and I used this for displaying.

@interface ElementNode : NSBrowserCell {
NSXMLNode *xmlNode;
ElementNode *parent;
NSMutableArray *subNodes;
}


- (id)initWithNode:(NSXMLNode *)aNode withParent:(ElementNode *)theParent;
- (id)initwithNode:(NSXMLNode *)aNode withParent:(ElementNode *)theParent andSubNodes:(NSArray *)otherSubNodes;
- (id)initwithNode:(NSXMLNode *)aNode withParent:(ElementNode *)theParent andXMLSubNodes:(NSArray *)NSXMLNodes;

- (ElementNode *)parent;
- (NSMutableArray *)subNodes;
- (NSXMLNode *)xmlNode;

- (void)addSubNodeFromXMLNode:(NSXMLNode *)anXMLNode;

@end

@implementation ElementNode

- (id)initWithNode:(NSXMLNode *)aNode withParent:(ElementNode *)theParent
{
self = [super init];
if (self != nil) {
xmlNode = [aNode retain];
if (theParent != nil)
parent = theParent;
else
parent = nil;
subNodes = [[NSMutableArray alloc]init];
}
return self;

}

- (id)initwithNode:(NSXMLNode *)aNode withParent:(ElementNode *)theParent andSubNodes:(NSArray *)otherSubNodes
{
self = [super init];
if (self != nil) {
xmlNode = [aNode retain];
if (theParent != nil)
parent = theParent;
else
parent = nil;
subNodes = [[NSMutableArray alloc]initWithArray:otherSubNodes];
}
return self;
}

- (id)initwithNode:(NSXMLNode *)aNode withParent:(ElementNode *)theParent andXMLSubNodes:(NSArray *)NSXMLNodes
{
self = [super init];
if (self != nil) {
xmlNode = [aNode retain];
if (theParent != nil)
parent = theParent;
else
parent = nil;
int i;
subNodes = [[NSMutableArray alloc]init];
for (i=0; i < [NSXMLNodes count]; i++) {
ElementNode *elementNode = [[ElementNode alloc]initWithNode:[NSXMLNodes objectAtIndex:i] withParent:self];
[subNodes addObject:elementNode];

}
}
return self;
}

- (void)addSubNodeFromXMLNode:(NSXMLNode *)anXMLNode
{
if (subNodes == nil)
subNodes = [[NSMutableArray alloc]init];
ElementNode *elementNode = [[[elementNode alloc]initWithNode:anXMLNode withParent:self]autorelease];
[subNodes addObject:elementNode];

}


- (ElementNode *)parent
{
return parent;
}

- (NSMutableArray *)subNodes
{
return subNodes;
}

- (NSXMLNode *)xmlNode
{
return xmlNode;
}

- (void) dealloc
{
[xmlNode release];
[subNodes release];
[super dealloc];
}

@end

then in the main program:
@interface GeneralHandler : NSObject {
NSXMLDocument *doc;

NSXMLNode *currentNode;

IBOutlet NSBrowser *xmlBrowser;

ElementNode *rootElementNode;
}


- (IBAction)DO:(id)sender;
@end

@implementation GeneralHandler

-(void)awakeFromNib
{
[xmlBrowser setCellClass:[ElementNode class]];

NSLog([[self class]description]);
NSString *xmlContents = [NSString stringWithContentsOfFile:[[NSString stringWithString:@"~/Desktop/info.xml"]stringByStandardizingPath]];
doc = [[NSXMLDocument alloc]initWithXMLString:xmlContents options:NSXMLDocumentTidyXML error:nil];
NSLog([[doc rootElement]name]);
currentNode = [doc rootElement];

rootElementNode = [[ElementNode alloc]initwithNode:[doc rootElement] withParent:nil andXMLSubNodes:[[doc rootElement]children]];

}

- (void)browser:(NSBrowser *)sender createRowsForColumn:(unsigned)column inMatrix:(NSMatrix *)matrix
{

if (column == 0) {
ElementNode *node = rootElementNode;
[node setStringValue:[[node xmlNode]name]];
[matrix addRowWithCells:[NSArray arrayWithObject:node]];
}
else {
NSXMLNode *selectedNode = [[sender selectedCellInColumn:column-1]xmlNode];
NSArray *xmlNodesToBeAdded = [selectedNode children];
int i;
for (i=0; i<[xmlNodesToBeAdded count]; i++) {
ElementNode *elementNode = [[ElementNode alloc]initWithNode:[xmlNodesToBeAdded objectAtIndex:i] withParent:nil];

NS_DURING
[elementNode setStringValue:[[elementNode xmlNode]name]];
NS_HANDLER
NSLog(@"exception!!!");
[elementNode setStringValue:[[elementNode xmlNode]objectValue]];
NS_ENDHANDLER


[matrix addRowWithCells:[NSArray arrayWithObject:elementNode]];
}

}
}


- (IBAction)DO:(id)sender
{
NSXMLNode *node = [[[doc rootElement]childAtIndex:0]childAtIndex:0];
NSLog([[node childAtIndex:0]objectValue]);
}

@end

I made a subclass of NSBrowserCell in order to make it easy for my class to be displayed. I admit that code should be soooo much better... For example, it could handle the names of the cells automatically, and it should avoid checking the state of a node inside the NS_DURING block.

However, it works for now, and I will adopt tat logic to proceed with the project. I will alter it completely, but it seems like a good start.