mirror of
https://github.com/nickshanks/ResKnife.git
synced 2024-08-29 22:28:57 +00:00
372 lines
12 KiB
Objective-C
372 lines
12 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 "NuTemplateDLNGElement.h"
|
|
#import "NuTemplateDBYTElement.h"
|
|
#import "NuTemplateOCNTElement.h"
|
|
#import "NuTemplateLSTCElement.h"
|
|
#import "NuTemplateStream.h"
|
|
|
|
#import "NSOutlineView-SelectedItems.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;
|
|
}
|
|
|
|
createFieldItem = nil;
|
|
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] autorelease]; // Copy the template object.
|
|
|
|
if( [[currElement type] isEqualToString: @"OCNT"] )
|
|
NSLog( @"Instantiated: %@", currElement );
|
|
|
|
[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 setContaining: resourceStructure];
|
|
[currElement readDataFrom: stream]; // 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: [NuTemplateLSTBElement class] forKey: @"LSTZ"];
|
|
[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"];
|
|
[fieldReg setObject: [NuTemplateDLNGElement class] forKey: @"DLNG"];
|
|
[fieldReg setObject: [NuTemplateDBYTElement class] forKey: @"DBYT"];
|
|
[fieldReg setObject: [NuTemplateDBYTElement class] forKey: @"CHAR"];
|
|
[fieldReg setObject: [NuTemplateOCNTElement class] forKey: @"OCNT"];
|
|
[fieldReg setObject: [NuTemplateOCNTElement class] forKey: @"ZCNT"];
|
|
[fieldReg setObject: [NuTemplateOCNTElement class] forKey: @"LCNT"];
|
|
[fieldReg setObject: [NuTemplateOCNTElement class] forKey: @"LZCT"];
|
|
[fieldReg setObject: [NuTemplateLSTCElement class] forKey: @"LSTC"];
|
|
}
|
|
|
|
// 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
|
|
{
|
|
NS_DURING
|
|
[item takeValue:object forKey: [tableColumn identifier]];
|
|
NS_HANDLER
|
|
|
|
NS_ENDHANDLER
|
|
}
|
|
|
|
|
|
-(IBAction) cut: (id)sender;
|
|
{
|
|
NuTemplateElement *selItem = (NuTemplateElement*) [dataList selectedItem];
|
|
|
|
[selItem cut: sender]; // Let selected item do its magic.
|
|
|
|
[dataList reloadData]; // Update our display.
|
|
}
|
|
|
|
-(IBAction) copy: (id)sender;
|
|
{
|
|
NuTemplateElement *selItem = (NuTemplateElement*) [dataList selectedItem];
|
|
|
|
[selItem copy: sender]; // Let selected item do its magic.
|
|
|
|
[dataList reloadData]; // Update our display.
|
|
}
|
|
|
|
-(IBAction) paste: (id)sender;
|
|
{
|
|
NuTemplateElement *selItem = (NuTemplateElement*) [dataList selectedItem];
|
|
|
|
[selItem paste: sender]; // Let selected item do its magic.
|
|
|
|
[dataList reloadData]; // Update our display.
|
|
}
|
|
|
|
-(IBAction) clear: (id)sender;
|
|
{
|
|
NuTemplateElement *selItem = (NuTemplateElement*) [dataList selectedItem];
|
|
|
|
[selItem clear: sender]; // Let selected item do its magic.
|
|
|
|
[dataList reloadData]; // Update our display.
|
|
}
|
|
|
|
/* showCreateResourceSheet: we mis-use this menu item for creating new template fields.
|
|
This works by selecting an item that serves as a template (another LSTB), or knows
|
|
how to create an item (LSTE) and passing the message on to it. */
|
|
|
|
-(IBAction) showCreateResourceSheet: (id)sender;
|
|
{
|
|
NuTemplateElement *selItem = (NuTemplateElement*) [dataList selectedItem];
|
|
|
|
[selItem showCreateResourceSheet: sender]; // Let selected item do its magic.
|
|
|
|
[dataList reloadData]; // Update our display.
|
|
}
|
|
|
|
|
|
-(BOOL) validateMenuItem: (NSMenuItem*)item
|
|
{
|
|
NuTemplateElement *selElement = (NuTemplateElement*) [dataList selectedItem];
|
|
|
|
if( [item action] == @selector(showCreateResourceSheet:) )
|
|
{
|
|
createFieldItem = item;
|
|
[item setTitle: NSLocalizedString(@"Create List Entry",@"")];
|
|
|
|
return( selElement != nil && [selElement respondsToSelector: @selector(showCreateResourceSheet:)] );
|
|
}
|
|
else if( [item action] == @selector(cut:) )
|
|
return( selElement != nil && [selElement respondsToSelector: @selector(cut:)] );
|
|
else if( [item action] == @selector(copy:) )
|
|
return( selElement != nil && [selElement respondsToSelector: @selector(copy:)] );
|
|
else if( [item action] == @selector(paste:) && selElement != nil
|
|
&& [selElement respondsToSelector: @selector(validateMenuItem:)] )
|
|
return( [selElement validateMenuItem: item] );
|
|
else if( [item action] == @selector(clear:) )
|
|
return( selElement != nil && [selElement respondsToSelector: @selector(clear:)] );
|
|
else if( [item action] == @selector(saveDocument:) )
|
|
return YES;
|
|
else return NO;
|
|
}
|
|
|
|
|
|
-(void) windowDidResignKey: (NSNotification*)notification
|
|
{
|
|
if( createFieldItem )
|
|
{
|
|
[createFieldItem setTitle: NSLocalizedString(@"Create New Resource...",@"")];
|
|
createFieldItem = nil;
|
|
}
|
|
}
|
|
|
|
|
|
-(IBAction) saveDocument: (id)sender
|
|
{
|
|
[self writeResData];
|
|
}
|
|
|
|
|
|
-(BOOL) windowShouldClose: (id)sender // Window delegate.
|
|
{
|
|
[self writeResData]; // Save resource.
|
|
|
|
return YES;
|
|
}
|
|
|
|
@end
|