ResKnife/NuTemplateEditor/NuTemplateWindowController.m

257 lines
8.1 KiB
Objective-C

/* =============================================================================
PROJECT: ResKnife
FILE: NuTemplateWindowController.h
PURPOSE: This is the main class of our template editor. Every
resource editor's main class implements the
ResKnifePluginProtocol. Every editor should implement
initWithResource:. Only implement initWithResources: if you feel
like writing a template editor.
Note that your plugin is responsible for committing suicide
after its window has been closed. If you subclass it from
NSWindowController, the controller will take care of that
for you, according to a guy named Doug.
AUTHORS: M. Uli Kusterer, witness(at)zathras.de, (c) 2003.
REVISIONS:
2003-07-31 UK Created.
========================================================================== */
/* -----------------------------------------------------------------------------
Headers:
-------------------------------------------------------------------------- */
#import "NuTemplateWindowController.h"
#import "NuTemplateElement.h"
#import "NuTemplateLSTBElement.h"
#import "NuTemplateLSTEElement.h"
#import "NuTemplateTNAMElement.h"
#import "NuTemplatePSTRElement.h"
#import "NuTemplateDWRDElement.h"
#import "NuTemplateStream.h"
@implementation NuTemplateWindowController
/* -----------------------------------------------------------------------------
initWithResource:
This is it! This is the constructor. Create your window here and
do whatever else makes you happy. A new instance is created for each
resource. Note that you are responsible for keeping track of your
resource.
-------------------------------------------------------------------------- */
- (id)initWithResource:(id)newResource
{
return [self initWithResources:newResource, nil];
}
- (id)initWithResources:(id)newResource, ...
{
id currentResource;
va_list resourceList;
va_start( resourceList, newResource );
self = [self initWithWindowNibName:@"NuTemplateWindow"];
if( !self )
{
va_end( resourceList );
return self;
}
resource = [newResource retain];
templateStructure = [[NSMutableArray alloc] init];
resourceStructure = [[NSMutableArray alloc] init];
currentResource = va_arg( resourceList, id );
[self readTemplate:currentResource]; // reads (but doesn't retain) the template for this resource (TMPL resource with name equal to the passed resource's type)
while( currentResource = va_arg( resourceList, id ) )
NSLog( @"too many params passed to -initWithResources: %@", [currentResource description] );
va_end( resourceList );
// load the window from the nib
[self window];
return self;
}
/* -----------------------------------------------------------------------------
* DESTRUCTOR
-------------------------------------------------------------------------- */
-(void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[(id)resource autorelease];
[templateStructure release];
[resourceStructure release];
[super dealloc];
}
/* -----------------------------------------------------------------------------
windowDidLoad:
Our window is there, stuff the image in it.
-------------------------------------------------------------------------- */
-(void) windowDidLoad
{
[super windowDidLoad];
// set the window's title
[[self window] setTitle:[resource nameForEditorWindow]];
[self reloadResData];
// we don't want this notification until we have a window! (Only register for notifications on the resource we're editing)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceDataDidChange:) name:ResourceDataDidChangeNotification object:resource];
// finally, show the window
[self showWindow:self];
[displayList reloadData];
}
/* -----------------------------------------------------------------------------
resourceDataDidChange:
Notification that someone changed our resource's data and we should
update our display.
-------------------------------------------------------------------------- */
-(void) resourceDataDidChange: (NSNotification*)notification
{
// ensure it's our resource which got changed (should always be true, we don't register for notifications on other resource objects)
if( [notification object] == (id)resource )
[self reloadResData];
}
-(void) reloadResData
{
char* theData = (char*) [[resource data] bytes];
unsigned long bytesToGo = [[resource data] length];
NuTemplateStream* stream = [NuTemplateStream streamWithBytes: theData length: bytesToGo];
NSEnumerator* enny = [templateStructure objectEnumerator];
NuTemplateElement* currElement;
[resourceStructure removeAllObjects]; // Get rid of old parsed resource.
// Loop over template and read each field:
while( currElement = [enny nextObject] )
{
currElement = [currElement copy]; // Copy the template object.
[resourceStructure addObject: currElement]; // Add it to our parsed resource data list. Do this right away so the element can append other items should it desire to.
[currElement readDataFrom: stream containingArray: resourceStructure]; // Fill it with resource data.
}
[dataList reloadData]; // Make sure our outline view displays the new data.
}
-(void) writeResData
{
unsigned int theSize = 0;
NSEnumerator* enny = [resourceStructure objectEnumerator];
NuTemplateElement* obj;
while( obj = [enny nextObject] )
theSize += [obj sizeOnDisk];
NSMutableData* newData = [NSMutableData dataWithLength: theSize];
NuTemplateStream* stream = [NuTemplateStream streamWithBytes: [newData bytes] length: theSize];
enny = [resourceStructure objectEnumerator];
while( obj = [enny nextObject] )
[obj writeDataTo: stream];
[resource setData: newData];
}
-(void) readTemplate: (id <ResKnifeResourceProtocol>)tmplRes
{
char* theData = (char*) [[tmplRes data] bytes];
unsigned long bytesToGo = [[tmplRes data] length];
NuTemplateStream* stream = [NuTemplateStream streamWithBytes: theData length: bytesToGo];
NSMutableDictionary* fieldReg = [NuTemplateStream fieldRegistry];
// Registry empty? Add field types we support:
if( [fieldReg count] == 0 )
{
[fieldReg setObject: [NuTemplateLSTBElement class] forKey: @"LSTB"];
[fieldReg setObject: [NuTemplateLSTEElement class] forKey: @"LSTE"];
[fieldReg setObject: [NuTemplateTNAMElement class] forKey: @"TNAM"];
[fieldReg setObject: [NuTemplatePSTRElement class] forKey: @"PSTR"];
[fieldReg setObject: [NuTemplatePSTRElement class] forKey: @"P100"];
[fieldReg setObject: [NuTemplatePSTRElement class] forKey: @"P020"];
[fieldReg setObject: [NuTemplatePSTRElement class] forKey: @"P040"];
[fieldReg setObject: [NuTemplateDWRDElement class] forKey: @"DWRD"];
}
// Read new fields from the template and add them to our list:
while( [stream bytesToGo] > 0 )
{
NuTemplateElement* obj = [stream readOneElement];
[templateStructure addObject: obj];
}
[displayList reloadData];
}
-(id) outlineView:(NSOutlineView*)outlineView child:(int)index ofItem:(id)item
{
if( (item == nil) && (outlineView == displayList) )
return [templateStructure objectAtIndex:index];
else if( (item == nil) && (outlineView == dataList) )
return [resourceStructure objectAtIndex:index];
else
return [item subElementAtIndex: index];
}
-(BOOL) outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
return ([item subElementCount] > 0);
}
-(int) outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
if( (item == nil) && (outlineView == displayList) )
return [templateStructure count];
else if( (item == nil) && (outlineView == dataList) )
return [resourceStructure count];
else
return [item subElementCount];
}
-(id) outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
return [item valueForKey:[tableColumn identifier]];
}
-(void) outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
[item takeValue:object forKey: [tableColumn identifier]];
}
-(BOOL) windowShouldClose: (id)sender // Window delegate.
{
[self writeResData]; // Save resource.
return YES;
}
@end