diff --git a/Cocoa/Categories/NSOutlineView-SelectedItems.m b/Cocoa/Categories/NSOutlineView-SelectedItems.m index 00c3b84..d768516 100644 --- a/Cocoa/Categories/NSOutlineView-SelectedItems.m +++ b/Cocoa/Categories/NSOutlineView-SelectedItems.m @@ -10,7 +10,7 @@ else return [self itemAtRow:[self selectedRow]]; } -- (NSArray *)selectedItems; +- (NSArray *)selectedItems { NSNumber *row; NSMutableArray *items = [NSMutableArray array]; diff --git a/Cocoa/Categories/NSString-FSSpec.h b/Cocoa/Categories/NSString-FSSpec.h index 1d9bf7f..8255ebe 100644 --- a/Cocoa/Categories/NSString-FSSpec.h +++ b/Cocoa/Categories/NSString-FSSpec.h @@ -6,3 +6,11 @@ - (FSSpec *)createFSSpec; @end + + +@interface NSString (ResKnifeBooleanExtensions) + +- (BOOL)boolValue; ++ (NSString *)stringWithBool:(BOOL)boolean; + +@end \ No newline at end of file diff --git a/Cocoa/Categories/NSString-FSSpec.m b/Cocoa/Categories/NSString-FSSpec.m index 5bcb098..267ac96 100644 --- a/Cocoa/Categories/NSString-FSSpec.m +++ b/Cocoa/Categories/NSString-FSSpec.m @@ -4,25 +4,46 @@ - (FSRef *)createFSRef { - FSRef *fsRef = NULL; - OSStatus error = FSPathMakeRef( [self fileSystemRepresentation], fsRef, NULL ); - if( error == noErr ) + // caller is responsible for disposing of the FSRef (method is a 'create' method) + FSRef *fsRef = (FSRef *) NewPtrClear(sizeof(FSRef)); + OSStatus error = FSPathMakeRef([self fileSystemRepresentation], fsRef, NULL); + if(error == noErr) return fsRef; return NULL; } - (FSSpec *)createFSSpec { - FSRef *fsRef = NULL; - FSSpec *fsSpec = NULL; - OSStatus error = FSPathMakeRef( [self fileSystemRepresentation], fsRef, NULL ); - if( error == noErr ) + // caller is responsible for disposing of the FSSpec (method is a 'create' method) + FSRef *fsRef = (FSRef *) NewPtrClear(sizeof(FSRef)); + FSSpec *fsSpec = (FSSpec *) NewPtrClear(sizeof(FSSpec)); + OSStatus error = FSPathMakeRef([self fileSystemRepresentation], fsRef, NULL); + if(error == noErr) { - error = FSGetCatalogInfo( fsRef, kFSCatInfoNone, NULL, NULL, fsSpec, NULL ); - if( error == noErr ) + error = FSGetCatalogInfo(fsRef, kFSCatInfoNone, NULL, NULL, fsSpec, NULL); + if(error == noErr) + { + DisposePtr((Ptr) fsRef); return fsSpec; + } } + DisposePtr((Ptr) fsRef); return NULL; } @end + +@implementation NSString (ResKnifeBooleanExtensions) + +- (BOOL)boolValue +{ + return ![self isEqualToString:@"NO"]; +// return [self isEqualToString:@"YES"]; +} + ++ (NSString *)stringWithBool:(BOOL)boolean +{ + return boolean? @"YES" : @"NO"; +} + +@end diff --git a/Cocoa/Classes/ApplicationDelegate.h b/Cocoa/Classes/ApplicationDelegate.h index ef9bd40..4bd0144 100644 --- a/Cocoa/Classes/ApplicationDelegate.h +++ b/Cocoa/Classes/ApplicationDelegate.h @@ -1,75 +1,100 @@ #import -/*! @header ApplicationDelegate.h - * @discussion This class is the delegate object for NSApp. - */ +/*! +@header ApplicationDelegate.h +@abstract This class is the delegate object for NSApp. +*/ -/*! @class ApplicationDelegate - * @discussion This class is the delegate object for NSApp. - */ +/*! +@class ApplicationDelegate +@abstract This class is the delegate object for NSApp. +*/ +@class OpenPanelDelegate; @interface ApplicationDelegate : NSObject { -/*! @var openAuxView Accessory view for NSOpenPanels. */ - IBOutlet NSView *openAuxView; -/*! @var forkTableView Table view inside openAuxView. */ - IBOutlet NSTableView *forkTableView; -/*! @var icons A dictionary within which to cache icons. Keys are four-character NSStrings representing ResTypes. */ - NSMutableDictionary *icons; +/*! @var openPanelDelegate Delegate for NSOpenPanels. */ + IBOutlet OpenPanelDelegate *openPanelDelegate; +/*! @var icons A dictionary within which to cache icons. Keys are four-character NSStrings representing ResTypes. */ + NSMutableDictionary *_icons; } -/*! @function showAbout: - * @discussion Displays the about box located in AboutPanel.nib. - */ +/*! +@method showAbout: +@abstract Displays the about box located in AboutPanel.nib. +*/ - (IBAction)showAbout:(id)sender; -/*! @function visitWebsite: - * @discussion Takes the user to http://web.nickshanks.com/resknife/. - */ +/*! +@method visitWebsite: +@abstract Takes the user to http://web.nickshanks.com/resknife/. +*/ - (IBAction)visitWebsite:(id)sender; -/*! @function visitSourceforge: - * @discussion Takes the user to http://resknife.sourceforge.net/. - */ +/*! +@method visitSourceforge: +@abstract Takes the user to http://resknife.sourceforge.net/. +*/ - (IBAction)visitSourceforge:(id)sender; -/*! @function emailDeveloper: - * @discussion Launches email client and inserts resknife@nickshanks.com into To field. - */ +/*! +@method emailDeveloper: +@abstract Launches email client and inserts resknife@nickshanks.com into To field. +*/ - (IBAction)emailDeveloper:(id)sender; -/*! @function showInfo: - * @discussion Displays the Info panel stored in InfoWindow.nib - */ +/*! +@method showInfo: +@abstract Displays the Info panel stored in InfoWindow.nib +*/ - (IBAction)showInfo:(id)sender; -/*! @function showPasteboard: - * @discussion Displays the pasteboard document, a singleton instance of class PasteboardDocument - */ +/*! +@method showPasteboard: +@abstract Displays the pasteboard document, a singleton instance of class PasteboardDocument +*/ - (IBAction)showPasteboard:(id)sender; -/*! @function showPrefs: - * @discussion Displays the preferences panel stored in PrefsWindow.nib - */ +/*! +@method showPrefs: +@abstract Displays the preferences panel stored in PrefsWindow.nib +*/ - (IBAction)showPrefs:(id)sender; -/*! @function initUserDefaults - * @discussion Initalises any unset user preferences to default values as read in from defaults.plist. - */ +/*! +@method initUserDefaults +@abstract Initalises any unset user preferences to default values as read in from defaults.plist. +*/ - (void)initUserDefaults; -/*! @function openAuxView - * @discussion Accessor method for the openAuxView instance variable. - */ -- (NSView *)openAuxView; +/* accessors */ -/*! @function icons - * @discussion Accessor method for the icons instance variable. - */ +/*! +@method openPanelDelegate +@abstract Accessor method for the openPanelDelegate instance variable. +*/ +- (OpenPanelDelegate *)openPanelDelegate; + +/*! +@@method iconForResourceType: +@abstract Returns the icon to be used throughout the UI for any given resource type. +*/ +- (NSImage *)iconForResourceType:(NSString *)resourceType; + +/*! +@@method _icons +@abstract Private accessor method for the _icons instance variable. +*/ +- (NSMutableDictionary *)_icons; + +/*! +@method icons +@abstract Accessor method for the _icons instance variable. Returns an immutable dictionary. +*/ - (NSDictionary *)icons; -@end - -@interface NSSavePanel (PackageBrowser) +/* utility methods */ + +- (NSArray *)forksForFile:(FSRef *)fileRef; @end \ No newline at end of file diff --git a/Cocoa/Classes/ApplicationDelegate.m b/Cocoa/Classes/ApplicationDelegate.m index 148af01..2b232af 100644 --- a/Cocoa/Classes/ApplicationDelegate.m +++ b/Cocoa/Classes/ApplicationDelegate.m @@ -1,4 +1,5 @@ #import "ApplicationDelegate.h" +#import "OpenPanelDelegate.h" #import "RKDocumentController.h" #import "InfoWindowController.h" #import "PasteboardWindowController.h" @@ -6,8 +7,9 @@ #import "CreateResourceSheetController.h" #import "ResourceDocument.h" #import "ResourceDataSource.h" +#import "RKEditorRegistry.h" -#import "ResknifePluginProtocol.h" +#import "ResKnifePluginProtocol.h" #import "RKSupportResourceRegistry.h" @@ -23,49 +25,103 @@ - (void)applicationWillFinishLaunching:(NSNotification *)notification { // instanciate my own subclass of NSDocumentController so I can override the open dialog - RKDocumentController *docController = [[RKDocumentController alloc] init]; - - [RKSupportResourceRegistry scanForSupportResources: [NSDocumentController sharedDocumentController]]; + [[RKDocumentController alloc] init]; + [RKSupportResourceRegistry scanForSupportResources]; } +/*! +@method awakeFromNib +@updated 2003-10-24 NGS: moved icon caching into method called by timer (to speed up app launch time) +*/ + - (void)awakeFromNib { - NSTableColumn *tableColumn; - NSButtonCell *buttonCell; - // Part of my EvilPlanª to find out how many people use ResKnife and how often! int launchCount = [[NSUserDefaults standardUserDefaults] integerForKey:@"LaunchCount"]; [[NSUserDefaults standardUserDefaults] setInteger:launchCount + 1 forKey:@"LaunchCount"]; - // save a number of icons - icons = [[NSMutableDictionary alloc] init]; - [icons setObject:[[NSWorkspace sharedWorkspace] iconForFileType:@"TEXT"] forKey:@"TEXT"]; - [icons setObject:[[NSWorkspace sharedWorkspace] iconForFileType:@"PICT"] forKey:@"PICT"]; - [icons setObject:[[NSWorkspace sharedWorkspace] iconForFileType:@"icns"] forKey:@"icns"]; - - // set up open dialog's aux table view - tableColumn = [forkTableView tableColumnWithIdentifier:@"parse"]; - buttonCell = [[[NSButtonCell alloc] initTextCell:@""] autorelease]; - [buttonCell setEditable:YES]; - [buttonCell setButtonType:NSSwitchButton]; - [tableColumn setDataCell:buttonCell]; + // initalise an empty icon cache and create timer used to pre-cache a number of common icons + _icons = [[NSMutableDictionary alloc] init]; + [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(precacheIcons:) userInfo:nil repeats:NO]; + // set default preferences [self initUserDefaults]; } - (void)dealloc { - [icons release]; + [_icons release]; [super dealloc]; } +/*! +@method precacheIcons: +@author Nicholas Shanks +@created 2003-10-24 +@abstract Pre-caches the icons for a number of common resource types. +@description Icon pre-caching now uses the more sophisticated iconForResourceType: instead of obtaining the images directly from the file system (otherwise pre-cached icons would not be overridable by plug-ins). In addition it has been moved from the awakeFromNib: method into one called by a timer. This method should not be called until after the editor registry has been built. +*/ + +- (void)precacheIcons:(NSTimer *)timer +{ + // pre-cache a number of common icons (ignores return value, relies on iconForResourceType: to do the actual caching) + [self iconForResourceType:@" "]; + [self iconForResourceType:@"CODE"]; + [self iconForResourceType:@"icns"]; + [self iconForResourceType:@"PICT"]; + [self iconForResourceType:@"plst"]; + [self iconForResourceType:@"snd "]; + [self iconForResourceType:@"TEXT"]; +} + +- (NSArray *)forksForFile:(FSRef *)fileRef +{ + if(!fileRef) return nil; + + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo = kFSCatInfoNodeFlags; + CatPositionRec forkIterator = { 0 }; + NSMutableArray *forks = [NSMutableArray array]; + + // check we have a file, not a folder + OSErr error = FSGetCatalogInfo(fileRef, whichInfo, &catalogInfo, NULL, NULL, NULL); + if(!error && !(catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) + { + // iterate over file and populate forks array + while(error == noErr) + { + HFSUniStr255 forkName; + SInt64 forkSize; + UInt64 forkPhysicalSize; // used if opening selected fork fails to find empty forks + + error = FSIterateForks(fileRef, &forkIterator, &forkName, &forkSize, &forkPhysicalSize); + if(!error) + { + NSString *fName = [NSString stringWithCharacters:forkName.unicode length:forkName.length]; + NSNumber *fSize = [NSNumber numberWithLongLong:forkSize]; + NSNumber *fAlloc = [NSNumber numberWithUnsignedLongLong:forkPhysicalSize]; + [forks addObject:[NSDictionary dictionaryWithObjectsAndKeys:fName, @"forkname", fSize, @"forksize", fAlloc, @"forkallocation", nil]]; + } + else if(error != errFSNoMoreItems) + { + NSLog(@"FSIterateForks() error: %d", error); + } + } + } + else if(error) + { + NSLog(@"FSGetCatalogInfo() error: %d", error); + } + return forks; +} + - (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender { -#pragma unused( sender ) +#pragma unused(sender) NSString *launchAction = [[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchAction"]; - if( [launchAction isEqualToString:@"OpenUntitledFile"] ) + if([launchAction isEqualToString:@"OpenUntitledFile"]) return YES; - else if( [launchAction isEqualToString:@"DisplayOpenPanel"] ) + else if([launchAction isEqualToString:@"DisplayOpenPanel"]) { [[NSDocumentController sharedDocumentController] openDocument:sender]; return NO; @@ -75,7 +131,7 @@ - (BOOL)application:(NSApplication *)application openFile:(NSString *)file { -#pragma unused( application ) +#pragma unused(application) // bug: check if application was an external editor (e.g. Iconographer) and update existing open file instead [[NSDocumentController sharedDocumentController] openDocumentWithContentsOfFile:file display:YES]; return YES; @@ -83,14 +139,20 @@ - (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag { -#pragma unused( sender ) +#pragma unused(sender) return !flag; } - (IBAction)showAbout:(id)sender { - [NSApp orderFrontStandardAboutPanel:sender]; - // get about box code from http://cocoadevcentral.com/tutorials/showpage.php?show=00000041.php + // could do with a better about box +/* NSWindowController *wc = [[NSWindowController alloc] initWithWindowNibName:@"AboutPanel"]; + if([(NSTextView *)[[wc window] initialFirstResponder] readRTFDFromFile:[[NSBundle mainBundle] pathForResource:@"Credits" ofType:@"rtf"]]) + { + [[wc window] center]; + [[wc window] orderFront:nil]; + } + else*/ [NSApp orderFrontStandardAboutPanel:sender]; } - (IBAction)visitWebsite:(id)sender @@ -145,12 +207,12 @@ // enumerate over all the keys in the dictionary overDefaults = [[defaultsPlist allKeys] objectEnumerator]; - while( eachDefault = [overDefaults nextObject] ) + while(eachDefault = [overDefaults nextObject]) { // for each key in the dictionary // check if there is a value already registered for it // and if there isn't, then register the value that was in the file - if( ![defaults stringForKey:eachDefault] ) + if(![defaults stringForKey:eachDefault]) { [defaults setObject:[defaultsPlist objectForKey:eachDefault] forKey:eachDefault]; } @@ -160,26 +222,94 @@ [defaults synchronize]; } -- (NSView *)openAuxView +- (OpenPanelDelegate *)openPanelDelegate { - return openAuxView; + return openPanelDelegate; +} + +/*! +@method iconForResourceType: +@author Nicholas Shanks +@created 2003-10-24 +@abstract Manages the cache of icons used for representing resource types. +@description This method loads icons for each resource type from a variety of places and caches them for faster access. Your plug-in may be asked to return an icon for any resource type it declares it can edit. To implement this, your plug should respond to the iconForResourceType: selector with the same method signature as this method. The icons can be in any format recognised by NSImage. Alternativly, just leave your icons in "Your.plugin/Contents/Resources/Resource Type Icons/" (or any equivalent localised directory) with a name like "TYPE.tiff" and ResKnife will retreive them automatically. +@pending I don't like the name I chose here for the resource type icons directory. Can anyone think of something better? +*/ + +- (NSImage *)iconForResourceType:(NSString *)resourceType +{ + // check if we have image in cache already + NSImage *icon = nil; + if([resourceType isEqualToString:@""]) + resourceType = nil; + + if(resourceType); + icon = [[self _icons] valueForKey:resourceType]; + if(!icon) + { + NSString *iconPath = nil; + + // try to load icon from the default editor for that type + Class editor = [[RKEditorRegistry defaultRegistry] editorForType:resourceType]; + if(editor && resourceType) + { + // ask politly for icon + if([editor respondsToSelector:@selector(iconForResourceType:)]) + icon = [editor iconForResourceType:resourceType]; + + // try getting it myself + if(!icon) + { + iconPath = [[NSBundle bundleForClass:editor] pathForResource:resourceType ofType:nil inDirectory:@"Resource Type Icons"]; + if(iconPath) + icon = [[[NSImage alloc] initWithContentsOfFile:iconPath] autorelease]; + } + } + + // try to load icon from the ResKnife app bundle itself + if(!icon && resourceType) + { + iconPath = [[NSBundle mainBundle] pathForResource:resourceType ofType:nil inDirectory:@"Resource Type Icons"]; + if(iconPath) + icon = [[[NSImage alloc] initWithContentsOfFile:iconPath] autorelease]; + } + + // try to retreive from file system (after first mangling the type through our converter strings file) + if(!icon && resourceType) + { + NSString *fileType = [[NSBundle mainBundle] localizedStringForKey:resourceType value:@"" table:@"Resource Type Mappings"]; + NSRange range = [fileType rangeOfString:@"."]; + if(range.location == NSNotFound) + icon = [[NSWorkspace sharedWorkspace] iconForFileType:fileType]; + else // a '.' character in a file type means ResKnife should look for a bundle icon with fileType as the bundle's identifier + { + NSString *bundlePath = [[NSBundle bundleWithIdentifier:fileType] bundlePath]; + if(bundlePath) + icon = [[NSWorkspace sharedWorkspace] iconForFile:bundlePath]; + } + } + + // if we still don't have an icon, try to get the generic one - this is what icon represented forks get +// if(!icon) icon = [NSImage imageNamed:@"NSMysteryDocument"]; + if(!icon) icon = [[NSWorkspace sharedWorkspace] iconForFileType:@"' '"]; + + // save the newly retreived icon in the cache + if(icon && resourceType) + [[self _icons] setObject:icon forKey:resourceType]; + } + + // return the cached icon, or nil if none was found + return icon; +} + +- (NSMutableDictionary *)_icons +{ + return _icons; } - (NSDictionary *)icons { - return icons; + return [NSDictionary dictionaryWithDictionary:[self _icons]]; } -@end - -@implementation NSSavePanel (PackageBrowser) - -/* Don't tell anyone I did this... */ - -/*- (void)setTreatsFilePackagesAsDirectories:(BOOL)flag -{ -#pragma unused( flag ) - _spFlags.treatsFilePackagesAsDirectories = YES; -}*/ - @end \ No newline at end of file diff --git a/Cocoa/Classes/CreateResourceSheetController.m b/Cocoa/Classes/CreateResourceSheetController.m index ed7470d..71ff787 100644 --- a/Cocoa/Classes/CreateResourceSheetController.m +++ b/Cocoa/Classes/CreateResourceSheetController.m @@ -5,62 +5,55 @@ @implementation CreateResourceSheetController -/* ----------------------------------------------------------------------------- - controlTextDidChange: - Someone changed the control ID edit field. Check whether this is - a unique ID and appropriately enable the "create" button. - - Check "notification" against being nil, which is how we call it when - we need to explicitly update the enabled state of the "create" button. - - - REVISIONS: - 2003-08-01 UK Commented, changed to use data source's resourceOfType - instead of directly messing with the resource list's - enumerator Removed ID > 0 check -- negative IDs are - allowed as well. - -------------------------------------------------------------------------- */ +/*! +@method controlTextDidChange: +@abstract Handles updating of the 'Create' button when valid values are present in the sheet's fields. +@updated 2003-08-01 UK: Changed to use data source's resourceOfType instead of directly messing with the resource list's enumerator. +@updated 2003-08-01 UK: Removed ID > 0 check -- negative IDs are allowed as well.
Note from Nick: IIRC this was there as a workaround for another bug which prohibited negative IDs from being used. Not sure if that got fixed though :) +@description Someone changed the control ID edit field. Check whether this is a unique ID and appropriately enable the "create" button.

Check "notification" against being nil, which is how we call it when we need to explicitly update the enabled state of the "create" button. +*/ --(void) controlTextDidChange: (NSNotification*)notification +- (void)controlTextDidChange:(NSNotification *)notification { BOOL enableButton = NO; NSString *type = [typeView stringValue]; NSNumber *resID = [NSNumber numberWithInt:[resIDView intValue]]; - if( [type length] == 4 ) + if([type length] == 4) { // I could use +[Resource getResourceOfType:andID:inDocument:] != nil, but that would be much slower Resource *resource = [[document dataSource] resourceOfType:type andID:resID]; - if( resource == nil ) // No resource with that type and ID yet? + if(resource == nil) // No resource with that type and ID yet? enableButton = YES; } [createButton setEnabled:enableButton]; } +/*! +@method showCreateResourceSheet: +@abstract Shows the sheet allowing the user to define the properties of a new resource. +@updated 2003-08-01 UK: Made it "fake" a popup selection so type field and popup match. +@updated 2003-08-01 UK: Made it suggest an unused resource ID. +*/ -/* ----------------------------------------------------------------------------- - showCreateResourceSheet: - Show our sheet and set it up before that. - - REVISIONS: - 2003-08-01 UK Commented, made it "fake" a popup selection so - type field and popup match. Made it suggest an unused - resource ID. - -------------------------------------------------------------------------- */ - --(void) showCreateResourceSheet: (ResourceDocument*)sheetDoc +- (void)showCreateResourceSheet:(ResourceDocument *)sheetDoc { // bug: didEndSelector could be better employed than using the button's targets from interface builder document = sheetDoc; [NSApp beginSheet:[self window] modalForWindow:[document mainWindow] modalDelegate:self didEndSelector:NULL contextInfo:nil]; - [resIDView setObjectValue: [[document dataSource] uniqueIDForType: [typeView stringValue]]]; - [self typePopupSelection: typePopup]; // Puts current popup value in text field and updates state of "create" button. + [resIDView setObjectValue:[[document dataSource] uniqueIDForType:[typeView stringValue]]]; + + // put current popup value in text field and updates state of "create" button. + [self typePopupSelection:typePopup]; } - (IBAction)hideCreateResourceSheet:(id)sender { - if( sender == createButton ) + if(sender == createButton) { + // bug should be using cell's tag rather than position (so cells can be moved around if needed) + // attributes ^= [[attributesMatrix cellAtRow:0 column:0] intValue]? [[attributesMatrix cellAtRow:0 column:0] tag]:0; + unsigned short attributes = 0; attributes ^= [[attributesMatrix cellAtRow:0 column:0] intValue]? resPreload:0; attributes ^= [[attributesMatrix cellAtRow:1 column:0] intValue]? resPurgeable:0; @@ -68,28 +61,26 @@ attributes ^= [[attributesMatrix cellAtRow:0 column:1] intValue]? resSysHeap:0; attributes ^= [[attributesMatrix cellAtRow:1 column:1] intValue]? resProtected:0; + Resource *resource = [Resource resourceOfType:[typeView stringValue] andID:[NSNumber numberWithShort:(short) [resIDView intValue]] withName:[nameView stringValue] andAttributes:[NSNumber numberWithUnsignedShort:attributes]]; + [resource setDocumentName:[document displayName]]; [[document undoManager] beginUndoGrouping]; - [[document dataSource] addResource:[Resource resourceOfType:[typeView stringValue] andID:[NSNumber numberWithShort:(short) [resIDView intValue]] withName:[nameView stringValue] andAttributes:[NSNumber numberWithUnsignedShort:attributes]]]; - if( [[nameView stringValue] length] == 0 ) + [[document dataSource] addResource:resource]; + if([[nameView stringValue] length] == 0) [[document undoManager] setActionName:NSLocalizedString(@"Create Resource", nil)]; - else [[document undoManager] setActionName:[NSString stringWithFormat:NSLocalizedString(@"Create Resource Ò%@Ó", nil), [nameView stringValue]]]; + else [[document undoManager] setActionName:[NSString stringWithFormat:NSLocalizedString(@"Create Resource '%@'", nil), [nameView stringValue]]]; [[document undoManager] endUndoGrouping]; } [[self window] orderOut:nil]; [NSApp endSheet:[self window]]; } +/*! +@method typePopupSelection: +@abstract Updates the edit text field when the type pop-up selection is changed. +@updated 2003-08-01 UK: Commented, made it update state of "create" button. +*/ -/* ----------------------------------------------------------------------------- - typePopupSelection: - Someone chose an item from our "res type" popup menu. Update our - edit field to show that. - - REVISIONS: - 2003-08-01 UK Commented, made it update state of "create" button.. - -------------------------------------------------------------------------- */ - --(IBAction) typePopupSelection:(id)sender +- (IBAction)typePopupSelection:(id)sender { [typeView setStringValue:[typePopup titleOfSelectedItem]]; [typeView selectText:sender]; diff --git a/Cocoa/Classes/InfoWindowController.h b/Cocoa/Classes/InfoWindowController.h index 772cf99..4b3863f 100644 --- a/Cocoa/Classes/InfoWindowController.h +++ b/Cocoa/Classes/InfoWindowController.h @@ -35,7 +35,7 @@ enum Attributes - (void)updateInfoWindow; - (void)setMainWindow:(NSWindow *)mainWindow; - (IBAction)attributesChanged:(id)sender; -- (IBAction)nameChanged: (id)sender; +- (IBAction)nameDidChange:(id)sender; - (void)resourceAttributesDidChange:(NSNotification *)notification; - (void)documentInfoDidChange:(NSNotification *)notification; @@ -46,7 +46,7 @@ enum Attributes @interface NSWindowController (InfoWindowAdditions) /*! @function resource - @discussion Your plug-in should override this method to return the resource it's editing. Default implementation returns nil. + @discussion Your plug-in should override this method to return the primary resource it's editing. Default implementation returns nil. */ - (Resource *)resource; diff --git a/Cocoa/Classes/InfoWindowController.m b/Cocoa/Classes/InfoWindowController.m index b6560e2..f1374ff 100644 --- a/Cocoa/Classes/InfoWindowController.m +++ b/Cocoa/Classes/InfoWindowController.m @@ -2,18 +2,12 @@ #import // Actually I only need CarbonCore.framework, but and don't work, so I don't know what else to do #import "ResourceDocument.h" #import "Resource.h" +#import "ApplicationDelegate.h" #import "NSOutlineView-SelectedItems.h" #import "MoreFilesX.h" @implementation InfoWindowController -- (id)init -{ - self = [self initWithWindowNibName:@"InfoWindow"]; - if( self ) [self setWindowFrameAutosaveName:@"InfoWindow"]; - return self; -} - - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; @@ -40,59 +34,87 @@ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(documentInfoDidChange:) name:DocumentInfoDidChangeNotification object:nil]; } --(void) updateInfoWindow +/*! +@method updateInfoWindow +@updated 2003-11-06 NGS: Fixed creator/type handling. +@updated 2003-10-26 NGS: Now asks app delegate for icon instead of NSWorkspace. +@updated 2003-10-26 NGS: Improved document name & icon display. +*/ + +- (void)updateInfoWindow { [nameView setEditable:(selectedResource != nil)]; [nameView setDrawsBackground:(selectedResource != nil)]; - if( selectedResource ) + if(selectedResource) { - [[self window] setTitle:@"Resource Info"]; - [placeholderView setContentView:resourceView]; + // set UI values + [[self window] setTitle:NSLocalizedString(@"Resource Info",nil)]; [nameView setStringValue:[selectedResource name]]; - [iconView setImage:[[NSWorkspace sharedWorkspace] iconForFileType:[selectedResource type]]]; + [iconView setImage:[(ApplicationDelegate *)[NSApp delegate] iconForResourceType:[selectedResource type]]]; [[attributesMatrix cellAtRow:changedBox column:0] setState:[[selectedResource attributes] shortValue] & resChanged]; [[attributesMatrix cellAtRow:preloadBox column:0] setState:[[selectedResource attributes] shortValue] & resPreload]; [[attributesMatrix cellAtRow:protectedBox column:0] setState:[[selectedResource attributes] shortValue] & resProtected]; [[attributesMatrix cellAtRow:lockedBox column:0] setState:[[selectedResource attributes] shortValue] & resLocked]; [[attributesMatrix cellAtRow:purgableBox column:0] setState:[[selectedResource attributes] shortValue] & resPurgeable]; [[attributesMatrix cellAtRow:systemHeapBox column:0] setState:[[selectedResource attributes] shortValue] & resSysHeap]; + + // swap box + [placeholderView setContentView:resourceView]; } - else if( currentDocument != nil ) + else if(currentDocument != nil) { // get sizes of forks as they are on disk UInt64 dataLogicalSize = 0, rsrcLogicalSize = 0; - FSRef *fileRef = (FSRef *) NewPtrClear( sizeof(FSRef) ); - if( fileRef && [currentDocument fileName] ) + FSRef *fileRef = (FSRef *) NewPtrClear(sizeof(FSRef)); + if(fileRef && [currentDocument fileName]) { - OSStatus error = FSPathMakeRef( [[currentDocument fileName] cString], fileRef, nil ); - if( !error ) FSGetForkSizes( fileRef, &dataLogicalSize, &rsrcLogicalSize ); + OSStatus error = FSPathMakeRef([[currentDocument fileName] fileSystemRepresentation], fileRef, nil); + if(!error) FSGetForkSizes(fileRef, &dataLogicalSize, &rsrcLogicalSize); } - if( fileRef ) DisposePtr( (Ptr) fileRef ); + if(fileRef) DisposePtr((Ptr) fileRef); // set info window elements to correct values - [[self window] setTitle:@"Document Info"]; - [iconView setImage:[NSImage imageNamed:@"Resource file"]]; - [nameView setStringValue:[currentDocument fileName]? [[currentDocument fileName] lastPathComponent]:[currentDocument displayName]]; - [[filePropertyForm cellAtIndex:0] setStringValue:[currentDocument creator]]; - [[filePropertyForm cellAtIndex:1] setStringValue:[currentDocument type]]; + [[self window] setTitle:NSLocalizedString(@"Document Info",nil)]; + if([currentDocument fileName]) // document has been saved + { + [iconView setImage:[[NSWorkspace sharedWorkspace] iconForFile:[currentDocument fileName]]]; + [nameView setStringValue:[[currentDocument fileName] lastPathComponent]]; + } + else // new, untitled document + { + [iconView setImage:[NSImage imageNamed:@"Resource file"]]; + [nameView setStringValue:[currentDocument displayName]]; + } + [currentDocument creator]; + [[NSString alloc] initWithData:[currentDocument creator] encoding:NSMacOSRomanStringEncoding]; + [[filePropertyForm cellAtIndex:0] setStringValue:[[[NSString alloc] initWithData:[currentDocument creator] encoding:NSMacOSRomanStringEncoding] autorelease]]; + [[filePropertyForm cellAtIndex:1] setStringValue:[[[NSString alloc] initWithData:[currentDocument type] encoding:NSMacOSRomanStringEncoding] autorelease]]; // [[filePropertyForm cellAtIndex:2] setObjectValue:[NSNumber numberWithUnsignedLongLong:dataLogicalSize]]; // [[filePropertyForm cellAtIndex:3] setObjectValue:[NSNumber numberWithUnsignedLongLong:rsrcLogicalSize]]; [[filePropertyForm cellAtIndex:2] setStringValue:[[NSNumber numberWithUnsignedLongLong:dataLogicalSize] description]]; [[filePropertyForm cellAtIndex:3] setStringValue:[[NSNumber numberWithUnsignedLongLong:rsrcLogicalSize] description]]; + + // swap box [placeholderView setContentView:documentView]; } + else + { + [iconView setImage:nil]; + [nameView setStringValue:nil]; + [placeholderView setContentView:nil]; + } } - (void)setMainWindow:(NSWindow *)mainWindow { NSWindowController *controller = [mainWindow windowController]; - if( [[controller document] isKindOfClass:[ResourceDocument class]] ) + if([[controller document] isKindOfClass:[ResourceDocument class]]) currentDocument = [controller document]; else currentDocument = nil; - if( currentDocument ) + if(currentDocument) selectedResource = [[currentDocument outlineView] selectedItem]; else selectedResource = [controller resource]; [self updateInfoWindow]; @@ -105,15 +127,15 @@ - (void)selectedResourceChanged:(NSNotification *)notification { - if( ![[nameView stringValue] isEqualToString: [selectedResource name]] ) - [self nameChanged:nameView]; - selectedResource = [[notification object] selectedItem]; + if(![[nameView stringValue] isEqualToString:[selectedResource name]]) + [self nameDidChange:nameView]; + selectedResource = (Resource *) [[notification object] selectedItem]; [self updateInfoWindow]; } - (void)documentInfoDidChange:(NSNotification *)notification { -#pragma unused( notification ) +#pragma unused(notification) [self updateInfoWindow]; } @@ -124,23 +146,21 @@ [selectedResource setAttributes:[NSNumber numberWithShort:number]]; } --(IBAction) nameChanged: (id)sender +- (IBAction)nameDidChange:(id)sender { - [selectedResource setName: [nameView stringValue]]; + [selectedResource setName:[nameView stringValue]]; } - (void)resourceAttributesDidChange:(NSNotification *)notification; { - if( ![[nameView stringValue] isEqualToString: [selectedResource name]] ) - [self nameChanged:nameView]; [self updateInfoWindow]; } + (id)sharedInfoWindowController { static InfoWindowController *sharedInfoWindowController = nil; - if( !sharedInfoWindowController ) - sharedInfoWindowController = [[InfoWindowController allocWithZone:[self zone]] init]; + if(!sharedInfoWindowController) + sharedInfoWindowController = [[InfoWindowController allocWithZone:[self zone]] initWithWindowNibName:@"InfoWindow"]; return sharedInfoWindowController; } diff --git a/Cocoa/Classes/NameFormatter.m b/Cocoa/Classes/NameFormatter.m index 6a39ada..e235aa8 100644 --- a/Cocoa/Classes/NameFormatter.m +++ b/Cocoa/Classes/NameFormatter.m @@ -2,6 +2,9 @@ #import "NSOutlineView-SelectedItems.h" #import "Resource.h" +/* NameFormatter has been deprecated (it never did what I wanted anyway :-) */ +/* functionality is now in -outlineView:willDisplayCell:forTableColumn:item: */ + @implementation NameFormatter - (NSString *)stringForObjectValue:(id)obj diff --git a/Cocoa/Classes/OpenFileDataSource.h b/Cocoa/Classes/OpenFileDataSource.h deleted file mode 100644 index 2b91bef..0000000 --- a/Cocoa/Classes/OpenFileDataSource.h +++ /dev/null @@ -1,19 +0,0 @@ -/* OpenFileDataSource */ - -#import - -@interface OpenFileDataSource : NSObject -{ - IBOutlet NSTableView *forkTableView; -} -@end - -@interface OpenPanelDelegate : NSObject -{ - id originalDelegate; -} -@end - -@interface NSSavePanel (ResKnife) -- (NSBrowser *)browser; -@end \ No newline at end of file diff --git a/Cocoa/Classes/OpenFileDataSource.m b/Cocoa/Classes/OpenFileDataSource.m deleted file mode 100644 index 0233a02..0000000 --- a/Cocoa/Classes/OpenFileDataSource.m +++ /dev/null @@ -1,113 +0,0 @@ -#import "OpenFileDataSource.h" -#import -#import - -struct directoryinfo { - unsigned long length; - u_int32_t dirid; -}; - -struct dunnowhat -{ - unsigned long length; - u_int32_t data1; - u_int32_t data2; - u_int32_t data3; - u_int32_t data4; - u_int32_t data5; - u_int32_t data6; -}; - -@implementation OpenFileDataSource - -//get action method and target of browser, intercept (or re-route and call it myself) - -/* NSTableView data source protocol implementation */ -- (int)numberOfRowsInTableView:(NSTableView *)tableView -{ - //NSBrowser *browser = [(NSOpenPanel *)[tableView window] browser]; - //if( [[browser selectedCells] count] == 1 ) - { - // only one file is selected, parse it for forks -/* const char *path = [[browser path] cString]; - struct attrlist attributes; - struct directoryinfo fileinfo; - - NSLog( @"%s", path ); -// memset( &attributes, 0, sizeof(struct attrlist) ); - bzero( &attributes, sizeof(struct attrlist) ); - attributes.bitmapcount = ATTR_BIT_MAP_COUNT; -// attributes.fileattr = ATTR_FILE_FORKCOUNT; - attributes.commonattr = ATTR_CMN_OBJID; - int result = getattrlist( path, &attributes, &fileinfo, sizeof(struct directoryinfo), 0 ); - NSLog( @"%d", result ); - if( result != 0 ) return 0; - NSLog( @"%d", fileinfo.length ); - NSLog( @"%d", fileinfo.dirid ); -*/ - /*struct attrlist alist; - struct directoryinfo dirinfo; - char *path = [[browser path] cString]; - bzero(&alist, sizeof(alist)); - alist.bitmapcount = 5; - alist.commonattr = ATTR_CMN_OBJID; - int result = getattrlist(path, &alist, &dirinfo, sizeof(dirinfo), 0); - printf("result: %d; directory id: %lu; %s\n", result, dirinfo.dirid, path); - - return 3;*/ - } - - // multiple/no selected files, return nothing - /*else*/ return 0; -} - -- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row -{ - return nil; -} - -- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row -{ - ; -} - -/* - NSLog( [browser path] ); - CatPositionRec forkIterator; - forkIterator.initialize = 0; - FSIterateForks( FSRef *ref, &forkIterator, NULL, NULL, NULL ); -*/ - -@end - -@implementation OpenPanelDelegate - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector -{ -/* NSMethodSignature *sig; - NS_DURING - sig = [super methodSignatureForSelector:selector]; - NS_HANDLER - sig = [originalDelegate methodSignatureForSelector:selector]; - NS_ENDHANDLER - return sig; */ - return [originalDelegate methodSignatureForSelector:selector]; -} - -- (void)forwardInvocation:(NSInvocation *)invocation -{ - if( [originalDelegate respondsToSelector:[invocation selector]] ) - [invocation invokeWithTarget:originalDelegate]; - else [self doesNotRecognizeSelector:[invocation selector]]; -} - -@end - -@implementation NSSavePanel (ResKnife) - -- (NSBrowser *)browser -{ - return nil; //return _browser; -} - -@end \ No newline at end of file diff --git a/Cocoa/Classes/OpenPanelDelegate.h b/Cocoa/Classes/OpenPanelDelegate.h new file mode 100644 index 0000000..a6035a6 --- /dev/null +++ b/Cocoa/Classes/OpenPanelDelegate.h @@ -0,0 +1,43 @@ +#import + +@interface OpenPanelDelegate : NSObject +{ +/*! @var openPanelAccessoryView Accessory view for NSOpenPanels. */ + IBOutlet NSView *openPanelAccessoryView; +/*! @var forkTableView Table view inside openPanelAccessoryView. */ + IBOutlet NSTableView *forkTableView; +/*! @var addForkButton Button for adding forks to a file. */ + IBOutlet NSButton *addForkButton; +/*! @var removeForkButton Button for removing forks from a file. */ + IBOutlet NSButton *removeForkButton; + +/*! @var forks Array of forks representing the currently selected file. */ + NSMutableArray *forks; +/*! @var readOpenPanelForFork Flag indicating whether ResKnife should ask for a fork to parse in a secondary dialog (false) or obtain it from the selected item in the open dialog (true). */ + BOOL readOpenPanelForFork; +} + +/* actions from aux view controls */ + +- (IBAction)addFork:(id)sender; +- (IBAction)removeFork:(id)sender; + +/* accessors */ + +/*! +@method openPanelAccessoryView +@abstract Accessor method for the openPanelAccessoryView instance variable. +*/ +- (NSView *)openPanelAccessoryView; + +/*! +@method forkTableView +@abstract Accessor method for the forkTableView instance variable. +*/ +- (NSTableView *)forkTableView; + +- (NSArray *)forks; +- (void)setReadOpenPanelForFork:(BOOL)flag; +- (BOOL)readOpenPanelForFork; + +@end \ No newline at end of file diff --git a/Cocoa/Classes/OpenPanelDelegate.m b/Cocoa/Classes/OpenPanelDelegate.m new file mode 100644 index 0000000..2b65e5f --- /dev/null +++ b/Cocoa/Classes/OpenPanelDelegate.m @@ -0,0 +1,131 @@ +#import "OpenPanelDelegate.h" +#import "ApplicationDelegate.h" +#import "SizeFormatter.h" +#import "../Categories/NSString-FSSpec.h" + +@implementation OpenPanelDelegate + +- (id)init +{ + self = [super init]; + if(self) + { + forks = [[NSMutableArray alloc] init]; + readOpenPanelForFork = NO; + } + return self; +} + +- (void)awakeFromNib +{ + // remove this when functionality actually works + [addForkButton setEnabled:NO]; + [removeForkButton setEnabled:NO]; +} + +- (void)dealloc +{ + [forks release]; + [super dealloc]; +} + +// open panel delegate method +- (void)panelSelectionDidChange:(id)sender +{ + [forks setArray:[(ApplicationDelegate *)[NSApp delegate] forksForFile:[[sender filename] createFSRef]]]; + [forkTableView reloadData]; +} + +- (BOOL)readOpenPanelForFork +{ + return readOpenPanelForFork; +} + +- (void)setReadOpenPanelForFork:(BOOL)flag +{ + readOpenPanelForFork = flag; +} + +// table view data source methods +- (int)numberOfRowsInTableView:(NSTableView *)tableView +{ + return [forks count]; +} + +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row +{ + // return object in array + if(row < [forks count]) + { + if([[tableColumn identifier] isEqualToString:@"forkname"]) + { + NSString *forkName = nil; + HFSUniStr255 *resourceForkName = (HFSUniStr255 *) NewPtrClear(sizeof(HFSUniStr255)); + OSErr error = FSGetResourceForkName(resourceForkName); + forkName = [(NSDictionary *)[forks objectAtIndex:row] objectForKey:[tableColumn identifier]]; + + // return custom names for data and resource forks + if([forkName isEqualToString:@""]) + forkName = NSLocalizedString(@"Data Fork", nil); + else if(!error && [forkName isEqualToString:[NSString stringWithCharacters:resourceForkName->unicode length:resourceForkName->length]]) + forkName = NSLocalizedString(@"Resource Fork", nil); + + DisposePtr((Ptr) resourceForkName); + return forkName; + } + + // return default value otherwise + return [(NSDictionary *)[forks objectAtIndex:row] objectForKey:[tableColumn identifier]]; + } + else return nil; +} + +- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row +{ + if([[tableColumn identifier] isEqualToString:@"forkname"]) + { + // update forks array + // create fork with new name + } +} + +- (IBAction)addFork:(id)sender +{ + // add placeholder to forks array + [forks addObject:[NSDictionary dictionaryWithObjectsAndKeys:NSLocalizedString(@"UNTITLED_FORK", nil), @"forkname", [NSNumber numberWithInt:0], @"forksize", [NSNumber numberWithInt:0], @"forkallocation", nil]]; + [forkTableView noteNumberOfRowsChanged]; + [forkTableView reloadData]; + + // start editing placeholder + [forkTableView selectRow:[forks count]-1 byExtendingSelection:NO]; + [forkTableView editColumn:0 row:[forks count]-1 withEvent:nil select:YES]; +} + +- (IBAction)removeFork:(id)sender +{ + // display warning + // delete fork + + // update table view + [forks removeObjectAtIndex:[forkTableView selectedRow]+1]; + [forkTableView noteNumberOfRowsChanged]; + [forkTableView reloadData]; +} + +- (NSArray *)forks +{ + // returns an immutable array + return [NSArray arrayWithArray:forks]; +} + +- (NSView *)openPanelAccessoryView +{ + return openPanelAccessoryView; +} + +- (NSTableView *)forkTableView +{ + return forkTableView; +} + +@end \ No newline at end of file diff --git a/Cocoa/Classes/OutlineViewDelegate.h b/Cocoa/Classes/OutlineViewDelegate.h index e652e34..6765e93 100644 --- a/Cocoa/Classes/OutlineViewDelegate.h +++ b/Cocoa/Classes/OutlineViewDelegate.h @@ -1,5 +1,4 @@ #import -#import "NameFormatter.h" #import "SizeFormatter.h" #import "AttributesFormatter.h" @@ -7,20 +6,16 @@ @interface OutlineViewDelegate : NSObject { - IBOutlet NSWindow *window; - IBOutlet NameFormatter *nameFormatter; - IBOutlet SizeFormatter *sizeFormatter; - IBOutlet AttributesFormatter *attributesFormatter; + IBOutlet NSWindow *window; + IBOutlet NSOutlineView *outlineView; + IBOutlet SizeFormatter *sizeFormatter; + IBOutlet AttributesFormatter *attributesFormatter; } -int compareResourcesAscending( Resource *r1, Resource *r2, void *context ); -int compareResourcesDescending( Resource *r1, Resource *r2, void *context ); +int compareResourcesAscending(Resource *r1, Resource *r2, void *context); +int compareResourcesDescending(Resource *r1, Resource *r2, void *context); @end -@interface NSOutlineView (OutlineSortView) -- (void)swapForOutlineSortView; -@end - -@interface OutlineSortView : NSOutlineView +@interface RKOutlineView : NSOutlineView @end \ No newline at end of file diff --git a/Cocoa/Classes/OutlineViewDelegate.m b/Cocoa/Classes/OutlineViewDelegate.m index 5a86d23..efae584 100644 --- a/Cocoa/Classes/OutlineViewDelegate.m +++ b/Cocoa/Classes/OutlineViewDelegate.m @@ -1,11 +1,42 @@ #import "OutlineViewDelegate.h" #import "Resource.h" +#import "ResourceDocument.h" #import "ResourceDataSource.h" #import "ResourceNameCell.h" #import "ApplicationDelegate.h" @implementation OutlineViewDelegate +- (id)init +{ + self = [super init]; + if(!self) return nil; + if(NSAppKitVersionNumber >= 700.0) // darwin 7.0 == Mac OS 10.3, needed for -setPlaceholderString: + { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatePlaceholder:) name:ResourceNameDidChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatePlaceholder:) name:ResourceTypeDidChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatePlaceholder:) name:ResourceIDDidChangeNotification object:nil]; + } + return self; +} + +- (void)updatePlaceholder:(NSNotification *)notification +{ + Resource *resource = [notification object]; + ResourceNameCell *cell = (ResourceNameCell *) [[outlineView tableColumnWithIdentifier:@"name"] dataCellForRow:[outlineView rowForItem:resource]]; + if([[resource name] isEqualToString:@""]) + { + if([[resource resID] shortValue] == -16455) + [cell setPlaceholderString:NSLocalizedString(@"Custom Icon", nil)]; + else [cell setPlaceholderString:NSLocalizedString(@"Untitled Resource", nil)]; + } +} + +/*! +@method tableView:didClickTableColumn: +@pending not needed in 10.3+, use existing sort functionality +*/ + - (void)tableView:(NSTableView*)tableView didClickTableColumn:(NSTableColumn *)tableColumn { NSArray *newResources; @@ -14,111 +45,156 @@ // sort the array NSImage *indicator = [tableView indicatorImageInTableColumn:tableColumn]; NSImage *upArrow = [NSTableView _defaultTableHeaderSortImage]; - if( indicator == upArrow ) - { - newResources = [oldResources sortedArrayUsingFunction:compareResourcesAscending context:(void*)[tableColumn identifier]]; - } - else - { - newResources = [oldResources sortedArrayUsingFunction:compareResourcesDescending context:(void*)[tableColumn identifier]]; - } + if(indicator == upArrow) + newResources = [oldResources sortedArrayUsingFunction:compareResourcesAscending context:[tableColumn identifier]]; + else newResources = [oldResources sortedArrayUsingFunction:compareResourcesDescending context:[tableColumn identifier]]; // swap new array for old one [(ResourceDataSource *)[tableView dataSource] setResources:[NSMutableArray arrayWithArray:newResources]]; [tableView reloadData]; } -int compareResourcesAscending( Resource *r1, Resource *r2, void *context ) +/*! +@function compareResourcesAscending +@updated 2003-10-25 NGS: now uses KVC methods to obtain the strings to compare +*/ + +int compareResourcesAscending(Resource *r1, Resource *r2, void *context) { NSString *key = (NSString *)context; - SEL sel = NSSelectorFromString(key); - - if( [key isEqualToString:@"name"] || [key isEqualToString:@"type"] ) - { - // compare two NSStrings (case-insensitive) - return [(NSString *)[r1 performSelector:sel] caseInsensitiveCompare: (NSString *)[r2 performSelector:sel]]; - } - else - { - // compare two NSNumbers (or any other class) - return [(NSNumber *)[r1 performSelector:sel] compare: (NSNumber *)[r2 performSelector:sel]]; - } + // compare two NSStrings (case-insensitive) + if([key isEqualToString:@"name"] || [key isEqualToString:@"type"]) + return [(NSString *)[r1 valueForKey:key] caseInsensitiveCompare:(NSString *)[r2 valueForKey:key]]; + // compare two NSNumbers (or any other class) + else return [(NSNumber *)[r1 valueForKey:key] compare:(NSNumber *)[r2 valueForKey:key]]; } -int compareResourcesDescending( Resource *r1, Resource *r2, void *context ) +/*! +@function compareResourcesDescending +@updated 2003-10-25 NGS: now uses KVC methods to obtain the strings to compare +*/ + +int compareResourcesDescending(Resource *r1, Resource *r2, void *context) { NSString *key = (NSString *)context; - SEL sel = NSSelectorFromString(key); - - if( [key isEqualToString:@"name"] || [key isEqualToString:@"type"] ) - { - // compare two NSStrings (case-insensitive) - return -1 * [(NSString *)[r1 performSelector:sel] caseInsensitiveCompare: (NSString *)[r2 performSelector:sel]]; - } - else - { - // compare two NSNumbers (or any other class) - return -1 * [(NSNumber *)[r1 performSelector:sel] compare: (NSNumber *)[r2 performSelector:sel]]; - } + // compare two NSStrings (case-insensitive) + if([key isEqualToString:@"name"] || [key isEqualToString:@"type"]) + return -1 * [(NSString *)[r1 valueForKey:key] caseInsensitiveCompare: (NSString *)[r2 valueForKey:key]]; + // compare two NSNumbers (or any other class) + else return -1 * [(NSNumber *)[r1 valueForKey:key] compare: (NSNumber *)[r2 valueForKey:key]]; } - (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item { - return YES; + if([[tableColumn identifier] isEqualToString:@"size"] || [[tableColumn identifier] isEqualToString:@"attributes"]) + return NO; + else return YES; } +/*! +@method outlineView:willDisplayCell:forTableColumn:item: +@updated 2003-10-25 NGS: Moved functionality of NameFormatter into this method, removed NameFormatter class. +@updated 2003-10-24 NGS: Swapped row colours so first row is white (as per 10.3), conditionalised drawing line background colours to system versions < 10.3, since in 10.3 it is handled by the nib file. +@updated 2003-10-24 NGS: Added iconForResourceType method to app delegate instead of interrogating the cache here. +@pending remove setting of the cell formatter when that capability is in interface builder +*/ + - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item { - int row = [outlineView rowForItem:item]; + Resource *resource = (Resource *)item; NSString *identifier = [tableColumn identifier]; - if( [identifier isEqualToString:@"name"] ) [cell setFormatter:nameFormatter]; - else if( [identifier isEqualToString:@"size"] ) [cell setFormatter:sizeFormatter]; - else if( [identifier isEqualToString:@"attributes"] ) [cell setFormatter:attributesFormatter]; + + // set formatters for cells (remove when IB can set a formatter for an entire table column) + if([identifier isEqualToString:@"size"]) [cell setFormatter:sizeFormatter]; + else if([identifier isEqualToString:@"attributes"]) [cell setFormatter:attributesFormatter]; // set resource icon - if( [identifier isEqualToString:@"name"] ) + if([identifier isEqualToString:@"name"]) { -// [(ResourceNameCell *)cell setImage:[NSImage imageNamed:@"Resource file"]]; -// [(ResourceNameCell *)cell setImage:[[NSWorkspace sharedWorkspace] iconForFileType:[(Resource *)item type]]]; - [(ResourceNameCell *)cell setImage:[[(ApplicationDelegate *)[NSApp delegate] icons] valueForKey:[(Resource *)item type]]]; + if(![resource representedFork]) + [(ResourceNameCell *)cell setImage:[(ApplicationDelegate *)[NSApp delegate] iconForResourceType:[resource type]]]; + else [(ResourceNameCell *)cell setImage:[(ApplicationDelegate *)[NSApp delegate] iconForResourceType:nil]]; + + if([[resource name] isEqualToString:@""]) + { + if([cell respondsToSelector:@selector(setPlaceholderString:)]) // 10.3+ + { + // 10.3+ uses placeholder strings + if([[resource resID] shortValue] == -16455) // don't bother checking type since there are too many icon types + [cell setPlaceholderString:NSLocalizedString(@"Custom Icon", nil)]; + else if([[resource type] isEqualToString:@"carb"] && [[resource resID] shortValue] == 0) + [cell setPlaceholderString:NSLocalizedString(@"Carbon Identifier", nil)]; + else if([[resource type] isEqualToString:@"pnot"] && [[resource resID] shortValue] == 0) + [cell setPlaceholderString:NSLocalizedString(@"File Preview", nil)]; + else if([[resource type] isEqualToString:@"STR "] && [[resource resID] shortValue] == -16396) + [cell setPlaceholderString:NSLocalizedString(@"Creator Information", nil)]; + else if([[resource type] isEqualToString:@"vers"] && [[resource resID] shortValue] == 1) + [cell setPlaceholderString:NSLocalizedString(@"File Version", nil)]; + else if([[resource type] isEqualToString:@"vers"] && [[resource resID] shortValue] == 2) + [cell setPlaceholderString:NSLocalizedString(@"Package Version", nil)]; + else [cell setPlaceholderString:NSLocalizedString(@"Untitled Resource", nil)]; + } + else + { + // pre-10.3, set text colour to grey and set title accordingly + if([[resource resID] shortValue] == -16455) + [cell setTitle:NSLocalizedString(@"Custom Icon", nil)]; + else if([[resource type] isEqualToString:@"carb"] && [[resource resID] shortValue] == 0) + [cell setTitle:NSLocalizedString(@"Carbon Identifier", nil)]; + else if([[resource type] isEqualToString:@"pnot"] && [[resource resID] shortValue] == 0) + [cell setTitle:NSLocalizedString(@"File Preview", nil)]; + else if([[resource type] isEqualToString:@"STR "] && [[resource resID] shortValue] == -16396) + [cell setTitle:NSLocalizedString(@"Creator Information", nil)]; + else if([[resource type] isEqualToString:@"vers"] && [[resource resID] shortValue] == 1) + [cell setTitle:NSLocalizedString(@"File Version", nil)]; + else if([[resource type] isEqualToString:@"vers"] && [[resource resID] shortValue] == 2) + [cell setTitle:NSLocalizedString(@"Package Version", nil)]; + else [cell setTitle:NSLocalizedString(@"Untitled Resource", nil)]; + +// if([[outlineView selectedItems] containsObject:resource]) +// [cell setTextColor:[NSColor whiteColor]]; +// else [cell setTextColor:[NSColor grayColor]]; + } + } } - if( row % 2 ) + // draw alternating blue/white backgrounds (if pre-10.3) + if(NSAppKitVersionNumber < 700.0) { - [cell setDrawsBackground:NO]; - [cell setBackgroundColor:[NSColor whiteColor]]; - } - else - { - [cell setDrawsBackground:YES]; - [cell setBackgroundColor:[NSColor colorWithCalibratedRed:0.93 green:0.95 blue:1.0 alpha:1.0]]; + int row = [outlineView rowForItem:item]; + if(row % 2) [cell setBackgroundColor:[NSColor colorWithCalibratedRed:0.93 green:0.95 blue:1.0 alpha:1.0]]; + else [cell setBackgroundColor:[NSColor whiteColor]]; + [cell setDrawsBackground:YES]; } } @end -@implementation NSOutlineView (OutlineSortView) +@implementation RKOutlineView -- (void)swapForOutlineSortView +/*! +@method draggingSourceOperationMaskForLocal: +*/ +- (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)local { - isa = [OutlineSortView class]; + if(local) return NSDragOperationEvery; + else return NSDragOperationCopy; } -@end - -@implementation OutlineSortView - - (void)keyDown:(NSEvent *)event { - if( [self selectedRow] != -1 && [[event characters] isEqualToString:[NSString stringWithCString:"\r"]] ) - [self editColumn:0 row:[self selectedRow] withEvent:nil select:YES]; + int selectedRow = [self selectedRow]; + if(selectedRow != -1 && [[event characters] isEqualToString:[NSString stringWithCString:"\r"]]) + [self editColumn:0 row:selectedRow withEvent:nil select:YES]; + else if(selectedRow != -1 && [[event characters] isEqualToString:[NSString stringWithCString:"\x7F"]]) + [(ResourceDocument *)[[[self window] windowController] document] deleteSelectedResources]; else [super keyDown:event]; } - (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)selector { // pressed return, end editing - if( selector == @selector(insertNewline:) ) + if(selector == @selector(insertNewline:)) { [[self window] makeFirstResponder:self]; [self abortEditing]; @@ -126,15 +202,15 @@ int compareResourcesDescending( Resource *r1, Resource *r2, void *context ) } // pressed tab, move to next editable field - else if( selector == @selector(insertTab:) ) + else if(selector == @selector(insertTab:)) { int newColumn = ([self editedColumn] +1) % [self numberOfColumns]; NSString *newColIdentifier = [[[self tableColumns] objectAtIndex:newColumn] identifier]; - if( [newColIdentifier isEqualToString:@"size"] || [newColIdentifier isEqualToString:@"attributes"] ) + if([newColIdentifier isEqualToString:@"size"] || [newColIdentifier isEqualToString:@"attributes"]) { newColumn = (newColumn +1) % [self numberOfColumns]; newColIdentifier = [[[self tableColumns] objectAtIndex:newColumn] identifier]; - if( [newColIdentifier isEqualToString:@"size"] || [newColIdentifier isEqualToString:@"attributes"] ) + if([newColIdentifier isEqualToString:@"size"] || [newColIdentifier isEqualToString:@"attributes"]) newColumn = (newColumn +1) % [self numberOfColumns]; } @@ -143,15 +219,15 @@ int compareResourcesDescending( Resource *r1, Resource *r2, void *context ) } // pressed shift-tab, move to previous editable field - else if( selector == @selector(insertBacktab:) ) + else if(selector == @selector(insertBacktab:)) { int newColumn = ([self editedColumn] + [self numberOfColumns] -1) % [self numberOfColumns]; NSString *newColIdentifier = [[[self tableColumns] objectAtIndex:newColumn] identifier]; - if( [newColIdentifier isEqualToString:@"size"] || [newColIdentifier isEqualToString:@"attributes"] ) + if([newColIdentifier isEqualToString:@"size"] || [newColIdentifier isEqualToString:@"attributes"]) { newColumn = (newColumn + [self numberOfColumns] -1) % [self numberOfColumns]; newColIdentifier = [[[self tableColumns] objectAtIndex:newColumn] identifier]; - if( [newColIdentifier isEqualToString:@"size"] || [newColIdentifier isEqualToString:@"attributes"] ) + if([newColIdentifier isEqualToString:@"size"] || [newColIdentifier isEqualToString:@"attributes"]) newColumn = (newColumn + [self numberOfColumns] -1) % [self numberOfColumns]; } @@ -162,42 +238,43 @@ int compareResourcesDescending( Resource *r1, Resource *r2, void *context ) return NO; } +/*! +@method _sendDelegateDidClickColumn: +@pending not needed in 10.3+, use existing sort functionality +*/ + //- (void)_sendDelegateDidMouseDownInHeader:(int)columnIndex - (void)_sendDelegateDidClickColumn:(int)columnIndex { - NSTableColumn *tableColumn = [[self tableColumns] objectAtIndex:columnIndex]; - NSImage *indicator = [self indicatorImageInTableColumn:tableColumn]; - NSImage *upArrow = [NSTableView _defaultTableHeaderSortImage]; - NSImage *downArrow = [NSTableView _defaultTableHeaderReverseSortImage]; - if( indicator ) +// if(NSAppKitVersionNumber < 700.0) { - // column already selected - if( indicator == upArrow ) - [self setIndicatorImage:downArrow inTableColumn:tableColumn]; - else [self setIndicatorImage:upArrow inTableColumn:tableColumn]; - } - else - { - // new column selected - if( [self highlightedTableColumn] != nil ) + NSTableColumn *tableColumn = [[self tableColumns] objectAtIndex:columnIndex]; + NSImage *indicator = [self indicatorImageInTableColumn:tableColumn]; + NSImage *upArrow = [NSTableView _defaultTableHeaderSortImage]; + NSImage *downArrow = [NSTableView _defaultTableHeaderReverseSortImage]; + if(indicator) + { + // column already selected + if(indicator == upArrow) + [self setIndicatorImage:downArrow inTableColumn:tableColumn]; + else [self setIndicatorImage:upArrow inTableColumn:tableColumn]; + } + else // new column selected { // if there is an existing selection, clear it's image - [self setIndicatorImage:nil inTableColumn:[self highlightedTableColumn]]; - } - - if( [[tableColumn identifier] isEqualToString:@"name"] || [[tableColumn identifier] isEqualToString:@"type"] ) - { + if([self highlightedTableColumn] != nil) + [self setIndicatorImage:nil inTableColumn:[self highlightedTableColumn]]; + // sort name and type columns ascending by default - [self setIndicatorImage:upArrow inTableColumn:tableColumn]; - } - else - { + if([[tableColumn identifier] isEqualToString:@"name"] || [[tableColumn identifier] isEqualToString:@"type"]) + [self setIndicatorImage:upArrow inTableColumn:tableColumn]; // sort all other columns descending by default - [self setIndicatorImage:downArrow inTableColumn:tableColumn]; + else [self setIndicatorImage:downArrow inTableColumn:tableColumn]; + [self setHighlightedTableColumn:tableColumn]; } - [self setHighlightedTableColumn:tableColumn]; + [[self delegate] tableView:self didClickTableColumn:tableColumn]; } - [[self delegate] tableView:self didClickTableColumn:tableColumn]; +// else [super _sendDelegateDidClickColumn:columnIndex]; } @end \ No newline at end of file diff --git a/Cocoa/Classes/PasteboardDocument.m b/Cocoa/Classes/PasteboardDocument.m index f275c16..0071fef 100644 --- a/Cocoa/Classes/PasteboardDocument.m +++ b/Cocoa/Classes/PasteboardDocument.m @@ -1,6 +1,8 @@ #import "PasteboardDocument.h" #import "Resource.h" +extern NSString *RKResourcePboardType; + @implementation PasteboardDocument - (id)init @@ -13,40 +15,44 @@ return self; } --(void) readPasteboard:(NSString *)pbName +-(void)readPasteboard:(NSString *)pbName { - NSPasteboard *pb = [NSPasteboard pasteboardWithName:pbName]; - NSArray *types = [pb types]; - NSEnumerator *enumerator = [types objectEnumerator]; - NSString *currentType; + // this method is mostly a duplicate of -[ResourceDocument paste:] but takes a pasteboard name for an argument + NSPasteboard *pb = [NSPasteboard pasteboardWithName:pbName]; + NSEnumerator *enumerator = [[pb types] objectEnumerator]; + NSString *pbType; + // clear current pasteboard representation + [self selectAll:nil]; + [self clear:nil]; + + // set the window's title to represent the pasteboard being shown (at some point I anticipate having several of these) + [[self window] setTitle:pbName]; + + // disable undos during loading [[self undoManager] disableUndoRegistration]; - while( currentType = [enumerator nextObject] ) + + // get all types off the pasteboard + while( pbType = [enumerator nextObject] ) { - // create the resource & add it to the array - NSString *name = pbName; - NSString *type; - NSNumber *resID; - NSNumber *attributes; - NSData *data; - Resource *resource; - NS_DURING - type = [currentType substringToIndex:3]; - NS_HANDLER - type = currentType; - NS_ENDHANDLER - resID = [NSNumber numberWithShort:0]; - attributes = [NSNumber numberWithShort:0]; - data = [pb dataForType:type]; - resource = [Resource resourceOfType:type andID:resID withName:name andAttributes:attributes data:data]; - [resources addObject:resource]; // array retains resource + // 'paste' any resources into pbdoc's data source + if( [pbType isEqualToString:RKResourcePboardType] ) + [self pasteResources:[NSUnarchiver unarchiveObjectWithData:[pb dataForType:RKResourcePboardType]]]; + else + { + // create the faux resource & add it to the array + Resource *resource = [Resource resourceOfType:nil andID:nil withName:pbType andAttributes:nil data:[pb dataForType:pbType]]; + [resources addObject:resource]; // array retains resource + } } + + // re-enable undos [[self undoManager] enableUndoRegistration]; [outlineView reloadData]; } --(void) windowDidBecomeKey: (NSNotification*)notification +-(void)windowDidBecomeKey:(NSNotification *)notification { // This mess sponsored by Uli Kusterer ;-) generalChangeCount = [[NSPasteboard generalPasteboard] changeCount]; diff --git a/Cocoa/Classes/PrefsWindowController.h b/Cocoa/Classes/PrefsWindowController.h index e116df8..6c98d28 100644 --- a/Cocoa/Classes/PrefsWindowController.h +++ b/Cocoa/Classes/PrefsWindowController.h @@ -31,10 +31,3 @@ enum LaunchAction + (id)sharedPrefsWindowController; @end - -@interface NSString (BooleanSupport) - -- (BOOL)boolValue; -+ (NSString *)stringWithBool:(BOOL)boolean; - -@end \ No newline at end of file diff --git a/Cocoa/Classes/PrefsWindowController.m b/Cocoa/Classes/PrefsWindowController.m index c749013..c70295f 100644 --- a/Cocoa/Classes/PrefsWindowController.m +++ b/Cocoa/Classes/PrefsWindowController.m @@ -4,9 +4,7 @@ - (id)init { - self = [self initWithWindowNibName:@"PrefsWindow"]; - if( self ) [self setWindowFrameAutosaveName:@"ResKnife Preferences"]; - return self; + return [self initWithWindowNibName:@"PrefsWindow"]; } - (void)dealloc @@ -19,6 +17,7 @@ { // represent current prefs in window state [self updatePrefs:nil]; + [[self window] center]; // listen out for pref changes from elsewhere [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatePrefs:) name:NSUserDefaultsDidChangeNotification object:nil]; @@ -63,8 +62,8 @@ [defaults setBool:autosave forKey:@"Autosave"]; [defaults setInteger:autosaveInterval forKey:@"AutosaveInterval"]; [defaults setBool:deleteResourceWarning forKey:@"DeleteResourceWarning"]; - if( createNewDocument ) [defaults setObject:@"OpenUntitledFile" forKey:@"LaunchAction"]; - else if( displayOpenPanel ) [defaults setObject:@"DisplayOpenPanel" forKey:@"LaunchAction"]; + if(createNewDocument) [defaults setObject:@"OpenUntitledFile" forKey:@"LaunchAction"]; + else if(displayOpenPanel) [defaults setObject:@"DisplayOpenPanel" forKey:@"LaunchAction"]; else [defaults setObject:@"None" forKey:@"LaunchAction"]; [defaults synchronize]; } @@ -101,26 +100,9 @@ + (id)sharedPrefsWindowController { static PrefsWindowController *sharedPrefsWindowController = nil; - if( !sharedPrefsWindowController ) - { sharedPrefsWindowController = [[PrefsWindowController allocWithZone:[self zone]] init]; - } return sharedPrefsWindowController; } @end - -@implementation NSString (BooleanSupport) - -- (BOOL)boolValue -{ - return [self isEqualToString:@"YES"]; -} - -+ (NSString *)stringWithBool:(BOOL)boolean -{ - return boolean? @"YES" : @"NO"; -} - -@end diff --git a/Cocoa/Classes/RKDocumentController.m b/Cocoa/Classes/RKDocumentController.m index 96bcb17..f25eb67 100644 --- a/Cocoa/Classes/RKDocumentController.m +++ b/Cocoa/Classes/RKDocumentController.m @@ -1,29 +1,26 @@ #import "RKDocumentController.h" #import "ApplicationDelegate.h" -#import "OpenFileDataSource.h" +#import "OpenPanelDelegate.h" @implementation RKDocumentController -// because I swap the isa pointer I can't add instance variables, so use statics instead (there will only ever be one RKDocumentController) -static id oldDelegate = nil; - -- (id)init -{ - self = [super init]; - if( self ) - { - // for some reason calling -[super init] causes a new instance of self to be returned (which is not of my subclass) so to get my overridden methods called again, I have to do this... - isa = [RKDocumentController class]; - oldDelegate = [[NSOpenPanel openPanel] delegate]; - [[NSOpenPanel openPanel] setDelegate:[[[OpenPanelDelegate alloc] init] autorelease]]; - } - return self; -} - - (int)runModalOpenPanel:(NSOpenPanel *)openPanel forTypes:(NSArray *)extensions { - [openPanel setAccessoryView:[(ApplicationDelegate *)[NSApp delegate] openAuxView]]; - return [super runModalOpenPanel:openPanel forTypes:extensions]; + // set-up open panel (this happens every time, but no harm done) + ApplicationDelegate *appDelegate = [NSApp delegate]; + OpenPanelDelegate *openPanelDelegate = [appDelegate openPanelDelegate]; + NSView *openPanelAccessoryView = [openPanelDelegate openPanelAccessoryView]; + [openPanel setDelegate:openPanelDelegate]; + [openPanel setAccessoryView:openPanelAccessoryView]; + [openPanel setAllowsOtherFileTypes:YES]; + [openPanel setTreatsFilePackagesAsDirectories:YES]; + [openPanelAccessoryView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + // run panel + int button = [super runModalOpenPanel:openPanel forTypes:extensions]; + if(button == NSOKButton) + [openPanelDelegate setReadOpenPanelForFork:YES]; + return button; } @end diff --git a/Cocoa/Classes/RKEditorRegistry.h b/Cocoa/Classes/RKEditorRegistry.h index e0e0f4b..42144db 100644 --- a/Cocoa/Classes/RKEditorRegistry.h +++ b/Cocoa/Classes/RKEditorRegistry.h @@ -32,13 +32,12 @@ @interface RKEditorRegistry : NSObject { - NSMutableDictionary* typeRegistry; // Private. Use editorForType: to access this. +@private + NSMutableDictionary *typeRegistry; } -+(RKEditorRegistry*) mainRegistry; // There's usually only one object, and this returns or creates it. - --(IBAction) scanForPlugins: (id)sender; // Called automatically by mainRegistry. --(Class) editorForType: (NSString*)typeStr; // You probably want to call this. - ++ (RKEditorRegistry *)defaultRegistry; // There's usually only one object, and this returns or creates it. +- (IBAction)scanForPlugins:(id)sender; // Called automatically by mainRegistry. +- (Class)editorForType:(NSString *)typeStr; // You probably want to call this. @end diff --git a/Cocoa/Classes/RKEditorRegistry.m b/Cocoa/Classes/RKEditorRegistry.m index d9fc994..52e2e75 100644 --- a/Cocoa/Classes/RKEditorRegistry.m +++ b/Cocoa/Classes/RKEditorRegistry.m @@ -22,130 +22,159 @@ -------------------------------------------------------------------------- */ #import "RKEditorRegistry.h" +#import "RKSupportResourceRegistry.h" +#import "NSString-FSSpec.h" // for ResKnifeBoolExtensions (in wrong file) - -/* ----------------------------------------------------------------------------- - Globals: - -------------------------------------------------------------------------- */ - -RKEditorRegistry* gRKEditorRegistryMainRegistry = nil; // Access through +mainRegistry! - +/*! +@class RKEditorRegistry +@author Uli Kusterer +@created 2003-07-31 +@description This is a registry where all our resource-editor plugins are looked + up and entered in a list, so you can ask for the editor for a specific + resource type and it is returned immediately. This registry reads the + types a plugin handles from their info.plist. This is better than + encoding the type in the plugin file name, as file names are not + guaranteed to be on a case-sensitive file system on Mac, and this also + allows an editor to register for several resource types. +*/ @implementation RKEditorRegistry -/* ----------------------------------------------------------------------------- - mainRegistry: - Returns the main plugin registry of this application, instantiating - it first if there is none yet. As soon as this is instantiated, the - plugins are loaded. - - REVISIONS: - 2003-07-31 UK Created. - -------------------------------------------------------------------------- */ - -+(RKEditorRegistry*) mainRegistry +/*! +@method +defaultRegistry +@author Uli Kusterer +@created 2003-07-31 +@updated 2003-10-28 NGS: Changed method name from +mainRegistry (so it more closly matchs +defaultCenter) and moved global var inside method, making it a static. +@description Returns the default plugin registry of this application, instantiating it first if there is none yet. As soon as this is instantiated, the plugins are loaded. +*/ ++ (RKEditorRegistry *)defaultRegistry { - if( !gRKEditorRegistryMainRegistry ) + static RKEditorRegistry *defaultRegistry = nil; + if(!defaultRegistry) { - gRKEditorRegistryMainRegistry = [[RKEditorRegistry alloc] init]; - [gRKEditorRegistryMainRegistry scanForPlugins: gRKEditorRegistryMainRegistry]; + defaultRegistry = [[RKEditorRegistry alloc] init]; + [defaultRegistry scanForPlugins:nil]; } - return gRKEditorRegistryMainRegistry; + return defaultRegistry; } - -/* ----------------------------------------------------------------------------- - awakeFromNib: - Makes sure that if an instance of this is instantiated from a NIB file, - it automatically loads the plugins. - - REVISIONS: - 2003-07-31 UK Created. - -------------------------------------------------------------------------- */ - --(void) awakeFromNib +/*! +@method awakeFromNib +@abstract Makes sure that if an instance of this is instantiated from a nib file, it automatically loads the plugins. +@author Uli Kusterer +@created 2003-07-31 +*/ +- (void)awakeFromNib { - [self scanForPlugins: self]; + [self scanForPlugins:nil]; } +/*! +@method scanForPlugins: +@abstract (Re)loads our list of plugins. You can use this as an action for a menu item, if you want. +@author Uli Kusterer +@created 2003-07-31 +@updated 2003-10-28 NGS: Updated to look for more sophisticated RKSupportedTypes key in addition to (the now deprecated) RKEditedTypes. +@pending Use NSSearchPathForDirectoriesInDomains() or equivalent to get folder paths instead of hard coding them. +@pending Currently, Cocoa classes can't be unloaded, which means we're + not leaking the NSBundles we load here. However, if this one + day shouldn't hold true anymore, we'll want to retain these + bundles in our dictionary and do the principalClass thingie + in editorForType: instead, which allows us to get rid of the + class and its bundle when reloading the plugin list by simply + relying on NSMutableDictionary to release it. -/* ----------------------------------------------------------------------------- - scanForPlugins: - (Re)loads our list of plugins. You can use this as an action for a menu - item, if you want. - - This scans the application's internal Plugins folder, - ~/Library/Application Support/ResKnife/Plugins/ and - /Library/Application Support/ResKnife/Plugins/ for plugins that have - the extension ".plugin" and implement initWithResource: (which means - this won't get into the way if you want to support other kinds of - plugins). - - It builds a registry of Class objects in an NSMutableDictionary, where - the key is the resource type. If a plugin supports several resource - types (as indicated by the RKEditedTypes key in its Info.plist), it - will be registered for these types as well. If several plugins register - for the same type, the last one loaded wins. - - To instantiate an object from a plugin, see the method below. - - TODO: - Currently, Cocoa classes can't be unloaded, which means we're not - leaking the NSBundles we load here. However, if this one day shouldn't - hold true anymore, we'll want to retain these bundles in our dictionary - and do the principalClass thingie in editorForType: instead, which - allows us to get rid of the class and its bundle when reloading the - plugin list by simply relying on NSMutableDictionary to release it. - - REVISIONS: - 2003-07-31 UK Created. - -------------------------------------------------------------------------- */ - --(IBAction) scanForPlugins: (id)sender +@description This scans the application's internal Plugins folder, + ~/Library/Application Support/ResKnife/Plugins/ and + /Library/Application Support/ResKnife/Plugins/ for + plugins that have the extension ".plugin" and implement + initWithResource: (which means this won't get into + the way if you want to support other kinds of plugins).

+ +

It builds a registry of Class objects in an NSMutableDictionary, + where the key is the resource type. If a plugin supports several + resource types (as indicated by the RKEditedTypes key in it's + Info.plist), it will be registered for these types as well. + If several plugins register for the same type, the last one + loaded wins.

+ +

To instantiate an object from a plugin, see editorForType: +*/ +- (IBAction)scanForPlugins:(id)sender { - // TODO: Instead of hard-coding sysPath we should use some FindFolder-like API! - Class pluginClass; NSString *appSupport = @"Library/Application Support/ResKnife/Plugins/"; NSString *appPath = [[NSBundle mainBundle] builtInPlugInsPath]; NSString *userPath = [NSHomeDirectory() stringByAppendingPathComponent:appSupport]; NSString *sysPath = [@"/" stringByAppendingPathComponent:appSupport]; - NSArray *paths = [NSArray arrayWithObjects:appPath, userPath, sysPath, nil]; - NSEnumerator *pathEnum = [paths objectEnumerator]; +// NSArray *paths = NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory, NSAllDomainsMask, YES); + NSEnumerator *pathEnumerator = [[NSArray arrayWithObjects:appPath, userPath, sysPath, nil] objectEnumerator]; NSString *path; - if( typeRegistry ) - [typeRegistry release]; + // release any existing registry to clear old values + if(typeRegistry) [typeRegistry release]; typeRegistry = [[NSMutableDictionary alloc] init]; - while( path = [pathEnum nextObject] ) + // scan all paths + while(path = [pathEnumerator nextObject]) { - NSEnumerator *e = [[[NSFileManager defaultManager] directoryContentsAtPath:path] objectEnumerator]; - NSString *name; + NSEnumerator *fileEnumerator = [[[NSFileManager defaultManager] directoryContentsAtPath:path] objectEnumerator]; + NSString *pluginName; - while( name = [e nextObject] ) + // enumerate all files in this directory + while(pluginName = [fileEnumerator nextObject]) { - name = [path stringByAppendingPathComponent:name]; - NSLog(@"Examining %@", name); - if( [[name pathExtension] isEqualToString:@"plugin"] ) + NSString *pluginPath = [path stringByAppendingPathComponent:pluginName]; +// NSLog(@"Examining %@", pluginPath); + + // verify file is a plugin + if([[pluginName pathExtension] isEqualToString:@"plugin"]) { - NSBundle *plugin = [NSBundle bundleWithPath: name]; - - if( pluginClass = [plugin principalClass] ) + NSBundle *plugin = [NSBundle bundleWithPath:pluginPath]; + Class pluginClass = [plugin principalClass]; + if(plugin && pluginClass) { - if( [pluginClass instancesRespondToSelector:@selector(initWithResource:)] ) +// NSLog(@"Principal class %@ %@ to ResKnifePluginProtocol", NSStringFromClass(pluginClass), [pluginClass conformsToProtocol:@protocol(ResKnifePluginProtocol)]? @"conforms":@"does not conform"); + + // check principal class implements ResKnifePluginProtocol + if([pluginClass conformsToProtocol:@protocol(ResKnifePluginProtocol)]) { - NSString *resType; - NSArray *types = [[plugin infoDictionary] objectForKey:@"RKEditedTypes"]; - NSEnumerator *enny; - - if( types == nil ) - types = [NSArray arrayWithObject: [[plugin infoDictionary] objectForKey:@"RKEditedType"]]; - - for( enny = [types objectEnumerator]; resType = [enny nextObject]; ) + NSArray *supportedTypes = [[plugin infoDictionary] objectForKey:@"RKSupportedTypes"]; + if(supportedTypes) { - [typeRegistry setObject:pluginClass forKey:resType]; - NSLog(@"Registered for type %@.",resType); + NSDictionary *typeEntry; + NSEnumerator *typesEnumerator = [supportedTypes objectEnumerator]; + + // enumerate entries + while(typeEntry = [typesEnumerator nextObject]) + { + // get values for type entry + NSString *name = [typeEntry objectForKey:@"RKTypeName"]; + NSString *role = [typeEntry objectForKey:@"RKTypeRole"]; + BOOL isDefault = [(NSString *)[typeEntry objectForKey:@"IsResKnifeDefaultForType"] boolValue]; + + // register them + [typeRegistry setObject:pluginClass forKey:name]; // bug: very primative, doesn't use extra data +// NSLog(@"Plug-in class %@ registered as %@%@ for type %@.", NSStringFromClass(pluginClass), isDefault? @"default ":@"", role, name); + } } + else + { + // try the old way of looking up types + NSString *resType; + NSEnumerator *enny; + supportedTypes = [[plugin infoDictionary] objectForKey:@"RKEditedTypes"]; + if(supportedTypes == nil) + supportedTypes = [NSArray arrayWithObject: [[plugin infoDictionary] objectForKey:@"RKEditedType"]]; + + for(enny = [supportedTypes objectEnumerator]; resType = [enny nextObject];) + { + [typeRegistry setObject:pluginClass forKey:resType]; +// NSLog(@"Registered for type %@.",resType); + } + } + + // load any support resources in the plug-in + [RKSupportResourceRegistry scanForSupportResourcesInFolder:[[plugin resourcePath] stringByAppendingPathComponent:@"Support Resources"]]; } } } @@ -169,13 +198,11 @@ RKEditorRegistry* gRKEditorRegistryMainRegistry = nil; // Access through +mainR 2003-07-31 UK Created. -------------------------------------------------------------------------- */ --(Class) editorForType: (NSString*)typeStr +- (Class)editorForType:(NSString *)typeStr { - Class theClass = [typeRegistry objectForKey: typeStr]; - if( !theClass ) - return Nil; - - return theClass; + Class theClass = [typeRegistry objectForKey: typeStr]; + if(!theClass) return Nil; + else return theClass; } @end diff --git a/Cocoa/Classes/RKSupportResourceRegistry.h b/Cocoa/Classes/RKSupportResourceRegistry.h index d02231d..58ba0e2 100644 --- a/Cocoa/Classes/RKSupportResourceRegistry.h +++ b/Cocoa/Classes/RKSupportResourceRegistry.h @@ -11,10 +11,7 @@ @interface RKSupportResourceRegistry : NSObject { - } - -+(void) scanForSupportResources: (NSDocumentController*)c; - - ++ (void)scanForSupportResources; ++ (void)scanForSupportResourcesInFolder:(NSString *)path; @end diff --git a/Cocoa/Classes/RKSupportResourceRegistry.m b/Cocoa/Classes/RKSupportResourceRegistry.m index 4ec704a..a8b367a 100644 --- a/Cocoa/Classes/RKSupportResourceRegistry.m +++ b/Cocoa/Classes/RKSupportResourceRegistry.m @@ -1,44 +1,32 @@ -// -// RKSupportResourceRegistry.m -// ResKnife -// -// Created by Uli Kusterer on Mon Aug 18 2003. -// Copyright (c) 2003 __MyCompanyName__. All rights reserved. -// - #import "RKSupportResourceRegistry.h" - +#import "NGSCategories.h" @implementation RKSupportResourceRegistry -+(void) scanForSupportResources: (NSDocumentController*)c ++ (void)scanForSupportResources { // TODO: Instead of hard-coding sysPath we should use some FindFolder-like API! - NSString *appSupport = @"Library/Application Support/ResKnife/Support Resources/"; - NSString *appPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Support Resources"]; - NSString *userPath = [NSHomeDirectory() stringByAppendingPathComponent:appSupport]; - NSString *sysPath = [@"/" stringByAppendingPathComponent:appSupport]; - NSArray *paths = [NSArray arrayWithObjects:appPath, userPath, sysPath, nil]; - NSEnumerator *pathEnum = [paths objectEnumerator]; - NSString *path; - - while( path = [pathEnum nextObject] ) +#if MAC_OS_X_VERSION_10_4 <= MAC_OS_X_VERSION_MAX_ALLOWED + NSArray *dirsArray = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSAllDomainsMask, YES); + dirsArray = [dirsArray arrayByMakingObjectsPerformSelector:@selector(stringByAppendingPathComponent:) withObject:@"ResKnife/Support Resources"]; +#endif + [RKSupportResourceRegistry scanForSupportResourcesInFolder:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Support Resources"]]; + [RKSupportResourceRegistry scanForSupportResourcesInFolder:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/ResKnife/Support Resources"]]; + [RKSupportResourceRegistry scanForSupportResourcesInFolder:@"/Library/Application Support/ResKnife/Support Resources"]; + [RKSupportResourceRegistry scanForSupportResourcesInFolder:@"/Network/Library/Application Support/ResKnife/Support Resources"]; +} + ++ (void)scanForSupportResourcesInFolder:(NSString *)path +{ +// NSLog(@"Looking for resources in %@", path); + NSString *name; + NSEnumerator *enumerator = [[[NSFileManager defaultManager] directoryContentsAtPath:path] objectEnumerator]; + while(name = [enumerator nextObject]) { - NSEnumerator *e = [[[NSFileManager defaultManager] directoryContentsAtPath:path] objectEnumerator]; - NSString *name; - - NSLog(@"Looking for resources in %@", path); - - while( name = [e nextObject] ) - { - name = [path stringByAppendingPathComponent:name]; - NSLog(@"Examining %@", name); - if( [[name pathExtension] isEqualToString:@"rsrc"] ) - { - [c openDocumentWithContentsOfFile:name display:YES]; - //[[[[[c openDocumentWithContentsOfFile:name display:YES] windowControllers] objectAtIndex:0] window] orderOut: self]; - } - } +// NSLog(@"Examining %@", name); + if([[name pathExtension] isEqualToString:@"rsrc"]) + // FIXME: this method was deprecate in 10.4 in favour of - (id)openDocumentWithContentsOfURL:(NSURL *)absoluteURL display:(BOOL)displayDocument error:(NSError **)outError; + [[NSDocumentController sharedDocumentController] openDocumentWithContentsOfFile:[path stringByAppendingPathComponent:name] display:YES]; } } diff --git a/Cocoa/Classes/Resource.h b/Cocoa/Classes/Resource.h index df7f9f7..596e242 100644 --- a/Cocoa/Classes/Resource.h +++ b/Cocoa/Classes/Resource.h @@ -1,5 +1,12 @@ #import -#import "ResKnifeResourceProtocol.h" +#import "../Plug-Ins/ResKnifeResourceProtocol.h" + +/*! +@class Resource +@author Nicholas Shanks +@abstract Encapsulates a single resource and all associated meta-data. +@description The Resource class fully complies with key-value coding, with the keys @"name", @"type", @"resID", @"attributes", @"data", @"dirty" and @"representedFork" available. +*/ @interface Resource : NSObject { @@ -17,13 +24,16 @@ // the actual data NSData *data; - NSDocument *document; // Our owner. + // the document name for display to the user; updating this is the responsibility of the document itself + NSString *_docName; } // accessor methods not part of the protocol +- (void)_setName:(NSString *)newName; - (void)setDirty:(BOOL)newValue; - (NSString *)representedFork; - (void)setRepresentedFork:(NSString *)forkName; +- (void)setDocumentName:(NSString *)docName; // init methods - (id)initWithType:(NSString *)typeValue andID:(NSNumber *)resIDValue; @@ -35,7 +45,4 @@ + (id)resourceOfType:(NSString *)typeValue andID:(NSNumber *)resIDValue withName:(NSString *)nameValue andAttributes:(NSNumber *)attributesValue; + (id)resourceOfType:(NSString *)typeValue andID:(NSNumber *)resIDValue withName:(NSString *)nameValue andAttributes:(NSNumber *)attributesValue data:(NSData *)dataValue; --(void) setDocument: (NSDocument*)doc; --(NSDocument*) document; // Resource protocol guarantees this. - @end diff --git a/Cocoa/Classes/Resource.m b/Cocoa/Classes/Resource.m index 9541328..7c41543 100644 --- a/Cocoa/Classes/Resource.m +++ b/Cocoa/Classes/Resource.m @@ -2,7 +2,7 @@ #import "ResourceDocument.h" #import "ResourceDataSource.h" -NSString *RKResourcePboardType = @"RKResourcePboardType"; +NSString *RKResourcePboardType = @"RKResourcePboardType"; @implementation Resource @@ -58,17 +58,17 @@ NSString *RKResourcePboardType = @"RKResourcePboardType"; return [resource autorelease]; } -+ (Resource *)getResourceOfType:(NSString *)typeValue andID:(NSNumber *)resIDValue inDocument:(NSDocument *)document ++ (Resource *)getResourceOfType:(NSString *)typeValue andID:(NSNumber *)resIDValue inDocument:(NSDocument *)searchDoc { NSDocument *doc; NSEnumerator *enumerator = [[[NSDocumentController sharedDocumentController] documents] objectEnumerator]; - while( doc = [enumerator nextObject] ) + while(doc = [enumerator nextObject]) { - if( document == nil || document == doc ) + if(searchDoc == nil || searchDoc == doc) { // parse document for correct resource Resource *resource = [[(ResourceDocument *)doc dataSource] resourceOfType:typeValue andID:resIDValue]; - if( resource ) return resource; + if(resource) return resource; } } return nil; @@ -76,47 +76,65 @@ NSString *RKResourcePboardType = @"RKResourcePboardType"; /* ResKnifeResourceProtocol implementation */ -+ (NSArray *)allResourcesOfType:(NSString *)typeValue inDocument:(NSDocument *)document ++ (NSArray *)allResourcesOfType:(NSString *)typeValue inDocument:(NSDocument *)searchDoc { NSMutableArray *array = [NSMutableArray array]; NSDocument *doc; NSEnumerator *enumerator = [[[NSDocumentController sharedDocumentController] documents] objectEnumerator]; - while( doc = [enumerator nextObject] ) + while(doc = [enumerator nextObject]) { // parse document for resources - if( document == nil || document == doc ) + if(searchDoc == nil || searchDoc == doc) [array addObjectsFromArray:[[(ResourceDocument *)doc dataSource] allResourcesOfType:typeValue]]; } return [NSArray arrayWithArray:array]; } -+ (Resource *)resourceOfType:(NSString *)typeValue withName:(NSString *)nameValue inDocument:(NSDocument *)document ++ (Resource *)resourceOfType:(NSString *)typeValue withName:(NSString *)nameValue inDocument:(NSDocument *)searchDoc { NSDocument *doc; NSEnumerator *enumerator = [[[NSDocumentController sharedDocumentController] documents] objectEnumerator]; - while( doc = [enumerator nextObject] ) + while(doc = [enumerator nextObject]) { - if( document == nil || document == doc ) + if(searchDoc == nil || searchDoc == doc) { // parse document for correct resource Resource *resource = [[(ResourceDocument *)doc dataSource] resourceOfType:typeValue withName:nameValue]; - if( resource ) return resource; + if(resource) return resource; } } return nil; } -+ (id)resourceOfType:(NSString *)typeValue andID:(NSNumber *)resIDValue inDocument:(NSDocument *)document ++ (Resource *)resourceOfType:(NSString *)typeValue andID:(NSNumber *)resIDValue inDocument:(NSDocument *)searchDoc { NSDocument *doc; NSEnumerator *enumerator = [[[NSDocumentController sharedDocumentController] documents] objectEnumerator]; - while( doc = [enumerator nextObject] ) + while(doc = [enumerator nextObject]) { - if( document == nil || document == doc ) + if(searchDoc == nil || searchDoc == doc) { // parse document for correct resource Resource *resource = [[(ResourceDocument *)doc dataSource] resourceOfType:typeValue andID:resIDValue]; - if( resource ) return resource; + if(resource) return resource; + } + } + return nil; +} + +// should probably be in resource document, not resource, but it fits in with the above methods quite well ++ (NSDocument *)documentForResource:(Resource *)resource +{ + NSDocument *doc; + NSEnumerator *enumerator = [[[NSDocumentController sharedDocumentController] documents] objectEnumerator]; + while(doc = [enumerator nextObject]) + { + Resource *res; + NSEnumerator *enumerator2 = [[(ResourceDocument *)doc resources] objectEnumerator]; + while(res = [enumerator2 nextObject]) + { + if([res isEqual:resource]) + return doc; } } return nil; @@ -130,16 +148,18 @@ NSString *RKResourcePboardType = @"RKResourcePboardType"; [resID release]; [attributes release]; [data release]; + [_docName release]; [super dealloc]; } - (id)copyWithZone:(NSZone *)zone { Resource *copy = [[Resource alloc] initWithType:type andID:resID withName:name andAttributes:attributes data:[data copy]]; + [copy setDocumentName:_docName]; return copy; } -/* Accessors */ +/* accessors */ - (void)touch { @@ -157,19 +177,16 @@ NSString *RKResourcePboardType = @"RKResourcePboardType"; [[NSNotificationCenter defaultCenter] postNotificationName:ResourceDidChangeNotification object:self]; } - --(void) setDocument: (NSDocument*)doc +- (NSDocument *)document { - document = doc; // It owns us, so don't retain, or we could get a closed loop! + return [Resource documentForResource:self]; } - --(NSDocument*) document +- (void)setDocumentName:(NSString *)docName { - return document; + _docName = [docName copy]; } - - (NSString *)representedFork { return representedFork; @@ -185,31 +202,37 @@ NSString *RKResourcePboardType = @"RKResourcePboardType"; return name; } +// shouldn't need this - it's used by forks to give them alternate names - should use name formatter replacement instead +- (void)_setName:(NSString *)newName +{ + id old = name; + name = [newName copy]; + [old release]; +} + - (void)setName:(NSString *)newName { - if( ![name isEqualToString:newName] ) + if(![name isEqualToString:newName]) { [[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillChangeNotification object:self]; [[NSNotificationCenter defaultCenter] postNotificationName:ResourceNameWillChangeNotification object:self]; - [name autorelease]; + id old = name; name = [newName copy]; + [old release]; - [[NSNotificationCenter defaultCenter] postNotificationName:ResourceNameDidChangeNotification object:self]; + // bug: this line is causing crashes! +// [[NSNotificationCenter defaultCenter] postNotificationName:ResourceNameDidChangeNotification object:self]; [self setDirty:YES]; } } - --(NSString*) nameForEditorWindow +- (NSString *)defaultWindowTitle { - if( [name length] > 0 ) - return [NSString stringWithFormat: @"%@: '%@' ID=%@ \"%@\"", [document displayName], type, resID, name]; - else - return [NSString stringWithFormat: @"%@: '%@' ID=%@", [document displayName], type, resID]; + if([name length] > 0) return [NSString stringWithFormat: NSLocalizedString(@"%@: %@ %@ '%@'", @"default window title format with resource name"), _docName, type, resID, name]; + else return [NSString stringWithFormat: NSLocalizedString(@"%@: %@ %@", @"default window title format without resource name"), _docName, type, resID]; } - - (NSString *)type { return type; @@ -217,15 +240,17 @@ NSString *RKResourcePboardType = @"RKResourcePboardType"; - (void)setType:(NSString *)newType { - if( ![type isEqualToString:newType] ) + if(![type isEqualToString:newType]) { [[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillChangeNotification object:self]; [[NSNotificationCenter defaultCenter] postNotificationName:ResourceTypeWillChangeNotification object:self]; - [type autorelease]; + id old = type; type = [newType copy]; + [old release]; - [[NSNotificationCenter defaultCenter] postNotificationName:ResourceTypeDidChangeNotification object:self]; + // bug: this line is causing crashes! +// [[NSNotificationCenter defaultCenter] postNotificationName:ResourceTypeDidChangeNotification object:self]; [self setDirty:YES]; } } @@ -237,15 +262,17 @@ NSString *RKResourcePboardType = @"RKResourcePboardType"; - (void)setResID:(NSNumber *)newResID { - if( ![resID isEqualToNumber:newResID] ) + if(![resID isEqualToNumber:newResID]) { [[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillChangeNotification object:self]; [[NSNotificationCenter defaultCenter] postNotificationName:ResourceIDWillChangeNotification object:self]; - [resID autorelease]; + id old = resID; resID = [newResID copy]; + [old release]; - [[NSNotificationCenter defaultCenter] postNotificationName:ResourceIDDidChangeNotification object:self]; + // bug: this line is causing crashes! +// [[NSNotificationCenter defaultCenter] postNotificationName:ResourceIDDidChangeNotification object:self]; [self setDirty:YES]; } } @@ -257,15 +284,17 @@ NSString *RKResourcePboardType = @"RKResourcePboardType"; - (void)setAttributes:(NSNumber *)newAttributes { - if( ![attributes isEqualToNumber:newAttributes] ) + if(![attributes isEqualToNumber:newAttributes]) { [[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillChangeNotification object:self]; [[NSNotificationCenter defaultCenter] postNotificationName:ResourceAttributesWillChangeNotification object:self]; - [attributes autorelease]; + id old = attributes; attributes = [newAttributes copy]; + [old release]; - [[NSNotificationCenter defaultCenter] postNotificationName:ResourceAttributesDidChangeNotification object:self]; + // bug: this line is causing crashes! +// [[NSNotificationCenter defaultCenter] postNotificationName:ResourceAttributesDidChangeNotification object:self]; [self setDirty:YES]; } } @@ -282,14 +311,15 @@ NSString *RKResourcePboardType = @"RKResourcePboardType"; - (void)setData:(NSData *)newData { - if( ![data isEqualToData:newData] ) + if(![data isEqualToData:newData]) { [[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillChangeNotification object:self]; [[NSNotificationCenter defaultCenter] postNotificationName:ResourceDataWillChangeNotification object:self]; // note: this function retains, rather than copies, the supplied data - [data autorelease]; + id old = data; data = [newData retain]; + [old release]; [[NSNotificationCenter defaultCenter] postNotificationName:ResourceDataDidChangeNotification object:self]; [self setDirty:YES]; @@ -301,7 +331,7 @@ NSString *RKResourcePboardType = @"RKResourcePboardType"; - (id)initWithCoder:(NSCoder *)decoder { self = [super init]; - if( self ) + if(self) { dirty = YES; name = [[decoder decodeObject] retain]; diff --git a/Cocoa/Classes/ResourceDataSource.h b/Cocoa/Classes/ResourceDataSource.h index 37c3abe..193eebc 100644 --- a/Cocoa/Classes/ResourceDataSource.h +++ b/Cocoa/Classes/ResourceDataSource.h @@ -2,6 +2,11 @@ @class ResourceDocument, Resource; +/*! +@class ResourceDataSource +@pending This class needs to be made KVC compliant. +*/ + @interface ResourceDataSource : NSObject { IBOutlet NSOutlineView *outlineView; @@ -11,18 +16,59 @@ NSMutableArray *resources; } +/*! +@method window +*/ - (NSWindow *)window; + +/*! +@method resources +*/ - (NSArray *)resources; + +/*! +@method setResources: +*/ - (void)setResources:(NSMutableArray *)newResources; + +/*! +@method addResource: +*/ - (void)addResource:(Resource *)resource; + +/*! +@method removeResource: +*/ - (void)removeResource:(Resource *)resource; + +/*! +@method uniqueIDForType: +*/ - (NSNumber *)uniqueIDForType:(NSString *)type; -// accessors --(Resource*) resourceOfType: (NSString*)type andID: (NSNumber*)resID; --(Resource*) resourceOfType: (NSString*)type withName: (NSString*)name; --(NSArray*) allResourcesOfType: (NSString*)type; --(NSArray*) allResourceIDsOfType: (NSString*)type; +/*! +@method defaultIDForType: +*/ +- (NSNumber *)defaultIDForType:(NSString *)type; +/*! +@method resourceOfType:andID: +*/ +- (Resource *)resourceOfType:(NSString *)type andID:(NSNumber *)resID; + +/*! +@method resourceOfType:withName: +*/ +- (Resource *)resourceOfType:(NSString *)type withName:(NSString *)name; + +/*! +@method allResourcesOfType: +*/ +- (NSArray *)allResourcesOfType:(NSString *)type; + +/*! +@method allResourceIDsOfType: +*/ +- (NSArray *)allResourceIDsOfType:(NSString *)type; @end diff --git a/Cocoa/Classes/ResourceDataSource.m b/Cocoa/Classes/ResourceDataSource.m index 6f3b918..3d127bf 100644 --- a/Cocoa/Classes/ResourceDataSource.m +++ b/Cocoa/Classes/ResourceDataSource.m @@ -1,37 +1,14 @@ -/* ============================================================================= - PROJECT: ResKnife - FILE: ResourceDataSource.m - - PURPOSE: - Dedicated data source for our resource list. Shares its list of - resources with ResourceDocument. I have no idea why Nick did this - split, though... - - AUTHORS: Nick Shanks, nick(at)nickshanks.com, (c) ~2001. - M. Uli Kusterer, witness(at)zathras.de, (c) 2003. - - REVISIONS: - 2003-07-31 UK Added storing document pointer in resource, commented. - ========================================================================== */ - -/* ----------------------------------------------------------------------------- - Headers: - -------------------------------------------------------------------------- */ - #import "ResourceDataSource.h" #import "ResourceDocument.h" #import "Resource.h" #import +NSString *DataSourceWillAddResourceNotification = @"DataSourceWillAddResource"; +NSString *DataSourceDidAddResourceNotification = @"DataSourceDidAddResource"; +NSString *DataSourceWillRemoveResourceNotification = @"DataSourceWillRemoveResource"; +NSString *DataSourceDidRemoveResourceNotification = @"DataSourceDidRemoveResource"; -/* ----------------------------------------------------------------------------- - Notification names: - -------------------------------------------------------------------------- */ - -NSString *DataSourceWillAddResourceNotification = @"DataSourceWillAddResourceNotification"; -NSString *DataSourceDidAddResourceNotification = @"DataSourceDidAddResourceNotification"; -NSString *DataSourceWillRemoveResourceNotification = @"DataSourceWillRemoveResourceNotification"; -NSString *DataSourceDidRemoveResourceNotification = @"DataSourceDidRemoveResourceNotification"; +extern NSString *RKResourcePboardType; @implementation ResourceDataSource @@ -60,8 +37,9 @@ NSString *DataSourceDidRemoveResourceNotification = @"DataSourceDidRemoveResourc - (void)setResources:(NSMutableArray *)newResources { - [resources autorelease]; + id old = resources; resources = [newResources retain]; + [old release]; [outlineView reloadData]; } @@ -71,8 +49,7 @@ NSString *DataSourceDidRemoveResourceNotification = @"DataSourceDidRemoveResourc [[NSNotificationCenter defaultCenter] postNotificationName:DataSourceWillAddResourceNotification object:dictionary]; // it seems very inefficient to reload the entire data source when just adding/removing one item - // for large resource files, the data source gets reloaded hundereds of times upon load - [resource setDocument:document]; + // for large resource files, the data source gets reloaded hundreds of times upon load [resources addObject:resource]; [outlineView reloadData]; @@ -93,67 +70,57 @@ NSString *DataSourceDidRemoveResourceNotification = @"DataSourceDidRemoveResourc [[document undoManager] registerUndoWithTarget:self selector:@selector(addResource:) object:resource]; // NB: I hope the undo manager retains the resource, because it just got deleted :) - undo action name set by calling function } - -/* ----------------------------------------------------------------------------- - resourceDidChange: - Notification from the Resource class that we're registered to. It's - sent whenever the resource is "touch"ed. - - REVISIONS: - 2003-08-01 UK Commented, made this "touch" the document as well. - -------------------------------------------------------------------------- */ - --(void) resourceDidChange: (NSNotification*)notification +- (void)resourceDidChange:(NSNotification *)notification { // reload the data for the changed resource [outlineView reloadItem:[notification object]]; - [document updateChangeCount: NSChangeDone]; // TODO: We shouldn't need to do this, Undo should take care of this, according to the docs, but somehow the document always forgets its changes. } /* Data source protocol implementation */ - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item { - #pragma unused( outlineView, item ) + #pragma unused(outlineView, item) return [resources objectAtIndex:index]; } - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item { - #pragma unused( outlineView, item ) + #pragma unused(outlineView, item) return NO; } - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { - #pragma unused( outlineView, item ) + #pragma unused(outlineView, item) return [resources count]; } - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { - #pragma unused( outlineView ) + #pragma unused(outlineView) return [item valueForKey:[tableColumn identifier]]; } - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { - #pragma unused( outlineView ) + #pragma unused(outlineView) NSString *identifier = [tableColumn identifier]; - if( [identifier isEqualToString:@"resID"] ) - [item takeValue:[NSNumber numberWithInt:[object intValue]] forKey:identifier]; - else [item takeValue:object forKey:identifier]; + if([identifier isEqualToString:@"resID"]) + [item setValue:[NSNumber numberWithInt:[object intValue]] forKey:identifier]; + else [item setValue:object forKey:identifier]; } +#pragma mark - /* ACCESSORS */ - (Resource *)resourceOfType:(NSString *)type andID:(NSNumber *)resID { Resource *resource; NSEnumerator *enumerator = [resources objectEnumerator]; - while( resource = [enumerator nextObject] ) + while(resource = [enumerator nextObject]) { - if( resID && [[resource resID] isEqualToNumber:resID] && type && [[resource type] isEqualToString:type] ) + if(resID && [[resource resID] isEqualToNumber:resID] && type && [[resource type] isEqualToString:type]) return resource; } return nil; @@ -163,9 +130,9 @@ NSString *DataSourceDidRemoveResourceNotification = @"DataSourceDidRemoveResourc { Resource *resource; NSEnumerator *enumerator = [resources objectEnumerator]; - while( resource = [enumerator nextObject] ) + while(resource = [enumerator nextObject]) { - if( [[resource name] isEqualToString:name] && [[resource type] isEqualToString:type] ) + if([[resource name] isEqualToString:name] && [[resource type] isEqualToString:type]) return resource; } return nil; @@ -173,65 +140,108 @@ NSString *DataSourceDidRemoveResourceNotification = @"DataSourceDidRemoveResourc - (NSArray *)allResourcesOfType:(NSString *)type { - Resource *resource; - NSMutableArray *array = [NSMutableArray array]; - NSEnumerator *enumerator = [resources objectEnumerator]; - while( resource = [enumerator nextObject] ) + Resource *resource; + NSMutableArray *array = [NSMutableArray array]; + NSEnumerator *enumerator = [resources objectEnumerator]; + while(resource = [enumerator nextObject]) { - if( [[resource type] isEqualToString:type] ) + if([[resource type] isEqualToString:type]) [array addObject:resource]; } return [NSArray arrayWithArray:array]; } +/*! +@method allResourceIDsOfType: +@discussion Returns an NSArray full of NSNumber* objects containing the IDs of all resources of specified type. Used by uniqueIDForType:. +@updated 2003-08-01 UK Created based on allResourcesOfType: +*/ -/* ----------------------------------------------------------------------------- - allResourceIDsOfType: - Returns an NSArray full of NSNumber* objects containing the IDs of all - resources of specified type. Used by uniqueIDForType:. - - REVISIONS: - 2003-08-01 UK Created based on allResourcesOfType:. - -------------------------------------------------------------------------- */ - --(NSArray*) allResourceIDsOfType: (NSString*)type +- (NSArray*)allResourceIDsOfType:(NSString *)type { + if(!type || [type isEqualToString:@""]) + return [NSArray array]; + Resource *resource; NSMutableArray *array = [NSMutableArray array]; NSEnumerator *enumerator = [resources objectEnumerator]; - while( resource = [enumerator nextObject] ) + while(resource = [enumerator nextObject]) { - if( [[resource type] isEqualToString:type] ) + if([[resource type] isEqualToString:type]) [array addObject:[resource resID]]; } return [NSArray arrayWithArray:array]; } +/*! +@method uniqueIDForType: +@discussion Tries to return an unused resource ID for a new resource of specified type. If all IDs are used up (can't really happen, because the resource manager can't take more than 2727 resources per file without crashing, but just in theory...), this will return 128 no matter whether it's used or not. +@updated 2003-08-01 UK: Created. +@updated 2003-10-21 NGS: Changed to obtain initial ID from -[resource defaultIDForType:], so we can vary it on a pre-resource-type basis (like Resourcerer can) +*/ -/* ----------------------------------------------------------------------------- - uniqueIDForType: - Tries to return an unused resource ID for a new resource of specified - type. If all IDs are used up (can't really happen, because the resource - manager can't take more than 2727 resources per file without crashing, - but just in theory...), this will return 128 no matter whether it's - used or not. - - REVISIONS: - 2003-08-01 UK Created. - -------------------------------------------------------------------------- */ - --(NSNumber*) uniqueIDForType: (NSString*)type +- (NSNumber *)uniqueIDForType:(NSString *)type { - short theID = 128; - NSArray *array = [self allResourceIDsOfType: type]; + short theID = [[self defaultIDForType:type] shortValue]; + NSArray *array = [self allResourceIDsOfType:type]; - if( [array count] <= USHRT_MAX ) + if([array count] <= USHRT_MAX) { - while( [array containsObject: [NSNumber numberWithShort:theID]] ) + while([array containsObject:[NSNumber numberWithShort:theID]]) theID++; } return [NSNumber numberWithShort: theID]; } +/*! +@method defaultIDForType: +@pending Method should look for resources specifying what the initial ID is for this resource type (e.g. 'vers' resources start at 0) +*/ + +- (NSNumber *)defaultIDForType:(NSString *)type +{ + short defaultID = 128; + return [NSNumber numberWithShort:defaultID]; +} + +#pragma mark - + +/*! +@method outlineView:writeItems:toPasteboard: +@abstract Called at the start of a drag event. +*/ +- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pb +{ + [pb declareTypes:[NSArray arrayWithObject:RKResourcePboardType] owner:self]; + [pb setData:[NSArchiver archivedDataWithRootObject:items] forType:RKResourcePboardType]; + return YES; +} + +/*! +@method outlineView:validateDrop:proposedItem:proposedChildIndex: +@abstract Called when the user is hovering with a drop over our outline view. +*/ +- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id )info proposedItem:(id)item proposedChildIndex:(int)childIndex +{ + if([info draggingSource] != outlineView) + { + [outlineView setDropItem:nil dropChildIndex:NSOutlineViewDropOnItemIndex]; + return NSDragOperationCopy; + } + else return NSDragOperationNone; +} + +/*! +@method outlineView:acceptDrop:item:childIndex: +@abstract Called when the user drops something on our outline view. +*/ +- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id )info item:(id)targetItem childIndex:(int)childIndex +{ + NSPasteboard *pb = [info draggingPasteboard]; + if([pb availableTypeFromArray:[NSArray arrayWithObject:RKResourcePboardType]]) + [document pasteResources:[NSUnarchiver unarchiveObjectWithData:[pb dataForType:RKResourcePboardType]]]; + return YES; +} + @end diff --git a/Cocoa/Classes/ResourceDocument.h b/Cocoa/Classes/ResourceDocument.h index 0dcceb4..b3dc791 100644 --- a/Cocoa/Classes/ResourceDocument.h +++ b/Cocoa/Classes/ResourceDocument.h @@ -12,10 +12,20 @@ NSMutableDictionary *toolbarItems; NSMutableArray *resources; HFSUniStr255 *fork; // name of fork to save to, usually empty string (data fork) or 'RESOURCE_FORK' as returned from FSGetResourceForkName() - NSString *creator; - NSString *type; + NSData *creator; + NSData *type; + BOOL _createFork; // file had no existing resource map when opened } +- (BOOL)readFork:(NSString *)forkName asStreamFromFile:(FSRef *)fileRef; +- (BOOL)readResourceMap:(SInt16)fileRefNum; +- (BOOL)writeResourceMap:(SInt16)fileRefNum; +- (BOOL)writeForkStreamsToFile:(NSString *)fileName; + +- (IBAction)exportResources:(id)sender; +- (void)exportResource:(Resource *)resource; +- (void)exportPanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo; + - (void)setupToolbar:(NSWindowController *)windowController; - (IBAction)showCreateResourceSheet:(id)sender; @@ -29,9 +39,6 @@ - (IBAction)playSound:(id)sender; - (void)sound:(NSSound *)sound didFinishPlaying:(BOOL)finished; --(IBAction) exportResourceToFile: (id)sender; --(IBAction) exportResourceToImageFile: (id)sender; - - (IBAction)copy:(id)sender; - (IBAction)paste:(id)sender; - (void)pasteResources:(NSArray *)pastedResources; @@ -45,21 +52,17 @@ - (void)resourceTypeWillChange:(NSNotification *)notification; - (void)resourceAttributesWillChange:(NSNotification *)notification; -- (BOOL)readFork:(NSString *)forkName asStreamFromFile:(NSString *)fileName; -- (BOOL)readResourceMap:(SInt16)fileRefNum; -- (BOOL)writeResourceMap:(SInt16)fileRefNum; - - (NSWindow *)mainWindow; - (ResourceDataSource *)dataSource; - (NSOutlineView *)outlineView; - (NSArray *)resources; // return the array as non-mutable -- (NSString *)creator; -- (NSString *)type; +- (NSData *)creator; +- (NSData *)type; - (IBAction)creatorChanged:(id)sender; - (IBAction)typeChanged:(id)sender; -- (void)setCreator:(NSString *)oldCreator; -- (void)setType:(NSString *)oldType; -- (void)setCreator:(NSString *)newCreator andType:(NSString *)newType; +- (BOOL)setCreator:(NSData *)newCreator; +- (BOOL)setType:(NSData *)newType; +- (BOOL)setCreator:(NSData *)newCreator andType:(NSData *)newType; @end diff --git a/Cocoa/Classes/ResourceDocument.m b/Cocoa/Classes/ResourceDocument.m index ac8f8d2..c4d405d 100644 --- a/Cocoa/Classes/ResourceDocument.m +++ b/Cocoa/Classes/ResourceDocument.m @@ -1,46 +1,23 @@ -/* ============================================================================= - PROJECT: ResKnife - FILE: ResourceDocument.m - - PURPOSE: - This is a ResKnife document (I still write Resurrection occasionally, - sorry Nick...), it handles loading, display etc. of resources. - - It uses a separate object as the data source for its outline view, - though, ResourceDataSource. I'd better ask Nick why. - - AUTHORS: Nick Shanks, nick(at)nickshanks.com, (c) ~2001. - M. Uli Kusterer, witness(at)zathras.de, (c) 2003. - - REVISIONS: - 2003-07-31 UK Added support for plugin registry, commented. - ========================================================================== */ - -/* ----------------------------------------------------------------------------- - Headers: - -------------------------------------------------------------------------- */ - #import "ResourceDocument.h" #import "ResourceDataSource.h" #import "ResourceNameCell.h" #import "Resource.h" +#import "ApplicationDelegate.h" +#import "OpenPanelDelegate.h" +#import "OutlineViewDelegate.h" +#import "InfoWindowController.h" #import "PrefsWindowController.h" #import "CreateResourceSheetController.h" -#import "OutlineViewDelegate.h" -#import "NSOutlineView-SelectedItems.h" +#import "NGSCategories.h" +#import "../Categories/NSString-FSSpec.h" +#import "../Categories/NSOutlineView-SelectedItems.h" -#import "ResKnifePluginProtocol.h" +#import "../Plug-Ins/ResKnifePluginProtocol.h" #import "RKEditorRegistry.h" -/* ----------------------------------------------------------------------------- - Notification names: - -------------------------------------------------------------------------- */ - NSString *DocumentInfoWillChangeNotification = @"DocumentInfoWillChangeNotification"; NSString *DocumentInfoDidChangeNotification = @"DocumentInfoDidChangeNotification"; - - extern NSString *RKResourcePboardType; @implementation ResourceDocument @@ -48,20 +25,19 @@ extern NSString *RKResourcePboardType; - (id)init { self = [super init]; - if( !self ) - return nil; + if(!self) return nil; toolbarItems = [[NSMutableDictionary alloc] init]; resources = [[NSMutableArray alloc] init]; fork = nil; - creator = [@"ResK" retain]; - type = [@"rsrc" retain]; + creator = [[@"ResK" dataUsingEncoding:NSMacOSRomanStringEncoding] retain]; // should I be calling -setCreator & -setType here instead? + type = [[@"rsrc" dataUsingEncoding:NSMacOSRomanStringEncoding] retain]; return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; - if( fork ) DisposePtr( (Ptr) fork ); + if(fork) DisposePtr((Ptr) fork); [resources release]; [toolbarItems release]; [type release]; @@ -69,8 +45,569 @@ extern NSString *RKResourcePboardType; [super dealloc]; } -/* WINDOW DELEGATION */ #pragma mark - +#pragma mark File Management + +/*! +@method readFromFile:ofType: +@abstract Open the specified file and read its resources. +@description Open the specified file and read its resources. This first tries to load the resources from the res fork, and failing that tries the data fork. +@author Nicholas Shanks +@updated 2003-11-08 NGS: Now handles opening user-selected forks. +*/ + +-(BOOL)readFromFile:(NSString *)fileName ofType:(NSString *)fileKind +{ + BOOL succeeded = NO; + OSStatus error = noErr; + FSRef *fileRef = (FSRef *) NewPtrClear(sizeof(FSRef)); + SInt16 fileRefNum = 0; + OpenPanelDelegate *openPanelDelegate = [(ApplicationDelegate *)[NSApp delegate] openPanelDelegate]; + + // bug: need to handle error better here + error = FSPathMakeRef((const UInt8 *)[fileName fileSystemRepresentation], fileRef, nil); + if(error) return NO; + + // find out which fork to parse + if(NSAppKitVersionNumber < 700.0 || ![openPanelDelegate readOpenPanelForFork]) + { + // display second dialog to ask user to select a fork, pre-10.3 or if open command did not come via the open dialog + + // bug: unimplemented - always tells app to try resource fork first + fork = (HFSUniStr255 *) NewPtrClear(sizeof(HFSUniStr255)); + error = FSGetResourceForkName(fork); + if(error) return NO; + } + else + { + // get selected fork from open panel, 10.3+ + int row = [[openPanelDelegate forkTableView] selectedRow]; + NSString *selectedFork = [(NSDictionary *)[[openPanelDelegate forks] objectAtIndex:row] valueForKey:@"forkname"]; + fork = (HFSUniStr255 *) NewPtrClear(sizeof(HFSUniStr255)); + fork->length = ([selectedFork length] < 255)? [selectedFork length]:255; + if(fork->length > 0) + [selectedFork getCharacters:fork->unicode range:NSMakeRange(0,fork->length)]; + else fork->unicode[0] = 0; + + // clear so next document doesn't get confused + [openPanelDelegate setReadOpenPanelForFork:NO]; + } + + NSArray *forks = [(ApplicationDelegate *)[NSApp delegate] forksForFile:fileRef]; + + // attempt to open fork user selected as a resource map + SetResLoad(false); // don't load "preload" resources + error = FSOpenResourceFile(fileRef, fork->length, (UniChar *) &fork->unicode, fsRdPerm, &fileRefNum); + if(error || !fileRefNum) + { + // if opening the user-selected fork fails, try to open resource fork instead + error = FSGetResourceForkName(fork); + if(error) return NO; +/* HFSUniStr255 *rfork; + error = FSGetResourceForkName(rfork); + if(error) return NO; + + bool checkFork = true; + if(FSCreateStringFromHFSUniStr) // 10.4 only + { + if(CFStringCompare(FSCreateStringFromHFSUniStr(NULL, fork), FSCreateStringFromHFSUniStr(NULL, rfork), 0) == NSOrderedSame) + checkFork = false; // skip checking resource fork if it's the one the user chose + else fork = rfork; + } + if(checkFork) +*/ error = FSOpenResourceFile(fileRef, fork->length, (UniChar *) &fork->unicode, fsRdPerm, &fileRefNum); + if(error || !fileRefNum) + { + // if opening the resource fork fails, try to open data fork instead + error = FSGetDataForkName(fork); + if(error) return NO; + error = FSOpenResourceFile(fileRef, fork->length, (UniChar *) &fork->unicode, fsRdPerm, &fileRefNum); + if(error || !fileRefNum) + { + // bug: should check fork the user selected is empty before trying data fork + NSNumber *fAlloc = [[forks firstObjectReturningValue:[NSString stringWithCharacters:fork->unicode length:fork->length] forKey:@"forkname"] valueForKey:@"forkallocation"]; + if([fAlloc unsignedLongLongValue] > 0) + { + // data fork is not empty, check resource fork + error = FSGetResourceForkName(fork); + if(error) return NO; + fAlloc = [[forks firstObjectReturningValue:[NSString stringWithCharacters:fork->unicode length:fork->length] forKey:@"forkname"] valueForKey:@"forkallocation"]; + if([fAlloc unsignedLongLongValue] > 0) + { + // resource fork is not empty either, give up (ask user for a fork?) + NSLog(@"Could not find existing map nor create a new map in either the data or resource forks! Aborting."); + return NO; + } + } + + // note that map needs initalising on first save + _createFork = YES; + } + } + } + SetResLoad(true); // restore resource loading as soon as is possible + + if(!_createFork) + { + // disable undos during resource creation and setting of the creator and type + [[self undoManager] disableUndoRegistration]; + + // then read resources from the selected fork + succeeded = [self readResourceMap:fileRefNum]; + + // get creator and type + FSCatalogInfo info; + error = FSGetCatalogInfo(fileRef, kFSCatInfoFinderInfo, &info, nil, nil, nil); + if(!error) + { + [self setType:[NSData dataWithBytes:&((FileInfo *)info.finderInfo)->fileType length:4]]; + [self setCreator:[NSData dataWithBytes:&((FileInfo *)info.finderInfo)->fileCreator length:4]]; + } + + // restore undos + [[self undoManager] enableUndoRegistration]; + } + else succeeded = YES; + + // now read all other forks as streams + NSString *forkName; + NSEnumerator *forkEnumerator = [forks objectEnumerator]; + NSString *selectedFork = [NSString stringWithCharacters:fork->unicode length:fork->length]; + while(forkName = [[forkEnumerator nextObject] valueForKey:@"forkname"]) + { + // check current fork is not the fork we're going to parse + if(![forkName isEqualToString:selectedFork]) + [self readFork:forkName asStreamFromFile:fileRef]; + } + + // tidy up loose ends + if(fileRefNum) FSClose(fileRefNum); + DisposePtr((Ptr) fileRef); + return succeeded; +} + +/*! +@method readFork:asStreamFromFile: +@author Nicholas Shanks +@updated 2003-11-08 NGS: Now handles opening user-selected forks. +@description Note: there is a 2 GB limit to the size of forks that can be read in due to FSReaadFork() taking a 32-bit buffer length value. +*/ + +- (BOOL)readFork:(NSString *)forkName asStreamFromFile:(FSRef *)fileRef +{ + if(!fileRef) return NO; + + /* NTFS Note: When running SFM (Services for Macintosh) a Windows NT-based system (including 2000 & XP) serving NTFS-formatted drives stores Mac resource forks in a stream named "AFP_Resource". The finder info/attributes are stored in a stream called "AFP_AfpInfo". The default data fork stream is called "$DATA" and any of these can be accessed thus: "c:\filename.txt:forkname". Finder comments are stored in a stream called "Comments". + As a result, ResKnife prohibits creation of forks with the following names: "" (empty string, Mac data fork name), + "$DATA" (NTFS data fork name), + "AFP_Resource", "AFP_AfpInfo" and "Comments". + It is perfectly legal in ResKnife to read in forks of these names when accessing a shared NTFS drive via SMB. The server does not need to be running SFM since the file requests will appear to be coming from a PC. If the files are accessed via AFP on a server running SFM, SFM will automatically convert the files (and truncate the name to 31 chars). */ + + + // translate NSString into HFSUniStr255 -- in 10.4 this can be done with FSGetHFSUniStrFromString + HFSUniStr255 uniForkName = { 0 }; + uniForkName.length = ([forkName length] < 255)? [forkName length]:255; + if(uniForkName.length > 0) + [forkName getCharacters:uniForkName.unicode range:NSMakeRange(0, uniForkName.length)]; + else uniForkName.unicode[0] = 0; + + // get fork length and create empty buffer, bug: only sizeof(size_t) bytes long + ByteCount forkLength = (ByteCount) [[[[(ApplicationDelegate *)[NSApp delegate] forksForFile:fileRef] firstObjectReturningValue:forkName forKey:@"forkname"] valueForKey:@"forksize"] unsignedLongValue]; + void *buffer = malloc(forkLength); + if(!buffer) return NO; + + // read fork contents into buffer, bug: assumes no errors + SInt16 forkRefNum; + FSOpenFork(fileRef, uniForkName.length, uniForkName.unicode, fsRdPerm, &forkRefNum); + FSReadFork(forkRefNum, fsFromStart, 0, forkLength, buffer, &forkLength); + FSCloseFork(forkRefNum); + + // create data + NSData *data = [NSData dataWithBytesNoCopy:buffer length:forkLength freeWhenDone:YES]; + if(!data) return NO; + + // create resource + Resource *resource = [Resource resourceOfType:@"" andID:0 withName:forkName andAttributes:0 data:data]; + if(!resource) return NO; + + // customise fork name for default data & resource forks - bug: this should really be in resource data source!! + HFSUniStr255 resourceForkName; + OSErr error = FSGetResourceForkName(&resourceForkName); + if(!error && [[resource name] isEqualToString:@""]) // bug: should use FSGetDataForkName() + [resource _setName:NSLocalizedString(@"Data Fork", nil)]; + else if(!error && [[resource name] isEqualToString:[NSString stringWithCharacters:resourceForkName.unicode length:resourceForkName.length]]) + [resource _setName:NSLocalizedString(@"Resource Fork", nil)]; + + [resource setRepresentedFork:forkName]; + [resources addObject:resource]; + return YES; +} + +-(BOOL)readResourceMap:(SInt16)fileRefNum +{ + OSStatus error = noErr; + SInt16 oldResFile = CurResFile(); + UseResFile(fileRefNum); + + for(unsigned short i = 1; i <= Count1Types(); i++) + { + ResType resType; + Get1IndType(&resType, i); + ResType swappedType = EndianS32_NtoB(resType); // Swapped type for use as string (types are treated as numbers by the resource manager and swapped on Intel). + unsigned short n = Count1Resources(resType); + for(unsigned short j = 1; j <= n; j++) + { + Handle resourceHandle = Get1IndResource(resType, j); + error = ResError(); + if(error != noErr) + { + NSLog(@"Error %d reading resource map...", error); + UseResFile(oldResFile); + return NO; + } + + Str255 nameStr; + short resIDShort; + GetResInfo(resourceHandle, &resIDShort, &resType, nameStr); + long sizeLong = GetResourceSizeOnDisk(resourceHandle); + short attrsShort = GetResAttrs(resourceHandle); + HLockHi(resourceHandle); + + // cool: "The advantage of obtaining a methodÕs implementation and calling it as a function is that you can invoke the implementation multiple times within a loop, or similar C construct, without the overhead of Objective-C messaging." + + // create the resource & add it to the array + NSString *name = [[NSString alloc] initWithBytes:&nameStr[1] length:nameStr[0] encoding:NSMacOSRomanStringEncoding]; + NSString *resType = [[NSString alloc] initWithBytes:(char *) &swappedType length:4 encoding:NSMacOSRomanStringEncoding]; + NSNumber *resID = [NSNumber numberWithShort:resIDShort]; + NSNumber *attributes = [NSNumber numberWithShort:attrsShort]; + NSData *data = [NSData dataWithBytes:*resourceHandle length:sizeLong]; + Resource *resource = [Resource resourceOfType:resType andID:resID withName:name andAttributes:attributes data:data]; + [resource setDocumentName:[self displayName]]; + [resources addObject:resource]; // array retains resource + [name release]; + [resType release]; + + HUnlock(resourceHandle); + ReleaseResource(resourceHandle); + } + } + + // save resource map and clean up + UseResFile(oldResFile); + return YES; +} + +/*! +@pending Uli's changed this routine - see what I had and unify the two +@pending Doesn't write correct type/creator info - always ResKnife's! +*/ + +- (BOOL)writeToFile:(NSString *)fileName ofType:(NSString *)type +{ + OSStatus error = noErr; + SInt16 fileRefNum = 0; + FSRef *parentRef = (FSRef *) NewPtrClear(sizeof(FSRef)); + FSRef *fileRef = (FSRef *) NewPtrClear(sizeof(FSRef)); + + // create and open file for writing + // bug: doesn't set the cat info to the same as the old file + unichar *uniname = (unichar *) NewPtrClear(sizeof(unichar) *256); + [[fileName lastPathComponent] getCharacters:uniname]; + error = FSPathMakeRef((const UInt8 *)[[fileName stringByDeletingLastPathComponent] UTF8String], parentRef, nil); + if(fork) + error = FSCreateResourceFile(parentRef, [[fileName lastPathComponent] length], (UniChar *) uniname, kFSCatInfoNone, NULL, fork->length, (UniChar *) &fork->unicode, fileRef, NULL); + else error = FSCreateResourceFile(parentRef, [[fileName lastPathComponent] length], (UniChar *) uniname, kFSCatInfoNone, NULL, 0, NULL, fileRef, NULL); + + // write any data streams to file + BOOL succeeded = [self writeForkStreamsToFile:fileName]; +// FSRef *fileRef = [fileName createFSRef]; + +/* error = FSPathMakeRef((const UInt8 *)[fileName UTF8String], fileRef, nil); + if(_createFork) + { + error = FSCreateResourceFork(fileRef, fork->length, (UniChar *) &fork->unicode, 0); + _createFork = NO; + } +*/ + if(!error) + { + // set creator & type + // bug: due to a bug in AppKit, the temporary file that we are writing to (in /var/tmp, managed by NSDocument) does not get it's creator code copied over to the new document (it sets the new document's to nil). this timer sets the creator code after we have returned to the main loop and the buggy Apple code has been bypassed. + [NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(setTypeCreatorAfterSave:) userInfo:nil repeats:NO]; + + // open fork as resource map + if(fork) + error = FSOpenResourceFile(fileRef, fork->length, (UniChar *) &fork->unicode, fsWrPerm, &fileRefNum); + else error = FSOpenResourceFile(fileRef, 0, NULL, fsWrPerm, &fileRefNum); + } +// else NSLog(@"error creating resource fork. (error=%d, spec=%d, ref=%d, parent=%d)", error, fileSpec, fileRef, parentRef); + else NSLog(@"error creating resource fork. (error=%d, ref=%d)", error, fileRef); + + // write resource array to file + if(fileRefNum && !error) + succeeded = [self writeResourceMap:fileRefNum]; + + // tidy up loose ends + if(fileRefNum) FSClose(fileRefNum); + DisposePtr((Ptr) fileRef); + + // update info window + [[InfoWindowController sharedInfoWindowController] updateInfoWindow]; + + return succeeded; +} + +- (BOOL)writeForkStreamsToFile:(NSString *)fileName +{ + // try and get an FSRef + OSStatus error; + FSRef *fileRef = [fileName createFSRef], *parentRef = nil; + if(!fileRef) + { + parentRef = (FSRef *) NewPtrClear(sizeof(FSRef)); + fileRef = (FSRef *) NewPtrClear(sizeof(FSRef)); + unichar *uniname = (unichar *) NewPtrClear(sizeof(unichar) *256); + [[fileName lastPathComponent] getCharacters:uniname]; + error = FSPathMakeRef((const UInt8 *)[[fileName stringByDeletingLastPathComponent] UTF8String], parentRef, nil); + if(error) return NO; + error = FSCreateFileUnicode(parentRef, 0, NULL, kFSCatInfoNone, NULL, fileRef, NULL); + if(error || !fileRef) return NO; + } + + Resource *resource; + NSEnumerator *enumerator = [resources objectEnumerator]; + while(resource = [enumerator nextObject]) + { + // if the resource object represents an actual resource, skip it + if([resource representedFork] == nil) continue; + unichar *uniname = (unichar *) NewPtrClear(sizeof(unichar) *256); + [[resource representedFork] getCharacters:uniname]; + SInt16 forkRefNum = 0; + error = FSOpenFork(fileRef, [[resource representedFork] length], (UniChar *) uniname, fsWrPerm, &forkRefNum); + if(!error && forkRefNum) + error = FSWriteFork(forkRefNum, fsFromStart, 0, [[resource data] length], [[resource data] bytes], NULL); + if(forkRefNum) FSClose(forkRefNum); + } + DisposePtr((Ptr) fileRef); + return YES; +} + +/*! +@method writeResourceMap: +@abstract Writes all resources (except the ones representing other forks of the file) to the specified resource file. +*/ + +- (BOOL)writeResourceMap:(SInt16)fileRefNum +{ + // make the resource file current + OSStatus error = noErr; + SInt16 oldResFile = CurResFile(); + UseResFile(fileRefNum); + + // loop over all our resources + Resource *resource; + NSEnumerator *enumerator = [resources objectEnumerator]; + while(resource = [enumerator nextObject]) + { + Str255 nameStr; + char resType[5]; // includes null char for getCString: + short resIDShort; + short attrsShort; + Handle resourceHandle; + + // if the resource represents another fork in the file, skip it + if([resource representedFork] != nil) continue; + + resIDShort = [[resource resID] shortValue]; + attrsShort = [[resource attributes] shortValue]; + resourceHandle = NewHandleClear([[resource data] length]); + + // convert unicode name to pascal string + nameStr[0] = [[resource name] lengthOfBytesUsingEncoding:NSMacOSRomanStringEncoding]; + BlockMoveData([[resource name] cStringUsingEncoding:NSMacOSRomanStringEncoding], &nameStr[1], nameStr[0]); + + // convert type string to ResType + [[resource type] getCString:resType maxLength:4]; + + // convert NSData to resource handle + HLockHi(resourceHandle); + [[resource data] getBytes:*resourceHandle]; + HUnlock(resourceHandle); + + // now that everything's converted, tell the resource manager we want to create this resource + AddResource(resourceHandle, *(ResType *)resType, resIDShort, nameStr); + if(ResError() == addResFailed) + { + NSLog(@"*Saving failed*; could not add resource ID %@ of type %@ to file.", [resource resID], [resource type]); + DisposeHandle(resourceHandle); + error = addResFailed; + } + else + { +// NSLog(@"Added resource ID %@ of type %@ to file.", [resource resID], [resource type]); + SetResAttrs(resourceHandle, attrsShort); + ChangedResource(resourceHandle); + // the resourceHandle memory is disposed of when calling CloseResFile() for the file to which the resource has been added + } + } + + // update the file on disk + UpdateResFile(fileRefNum); + + // restore original resource file + UseResFile(oldResFile); + return error? NO:YES; +} + +- (void)setTypeCreatorAfterSave:(id)userInfo +{ + FInfo finderInfo; + FSRef *fileRef = (FSRef *) NewPtrClear(sizeof(FSRef)); + FSSpec *fileSpec = (FSSpec *) NewPtrClear(sizeof(FSSpec)); + OSStatus error = FSPathMakeRef((const UInt8 *)[[self fileName] UTF8String], fileRef, nil); + if(!error) + { + error = FSGetCatalogInfo(fileRef, kFSCatInfoNone, NULL, NULL, fileSpec, NULL); + if(!error) + { + error = FSpGetFInfo(fileSpec, &finderInfo); + if(!error) + { + [[self type] getBytes:&finderInfo.fdType length:4]; + [[self creator] getBytes:&finderInfo.fdCreator length:4]; +// NSLog(@"setting finder info to type: %X; creator: %X", finderInfo.fdType, finderInfo.fdCreator); + error = FSpSetFInfo(fileSpec, &finderInfo); + FSpGetFInfo(fileSpec, &finderInfo); +// NSLog(@"finder info got set to type: %X; creator: %X", finderInfo.fdType, finderInfo.fdCreator); + } + else NSLog(@"error getting Finder info. (error=%d, spec=%d, ref=%d)", error, fileSpec, fileRef); + } + else NSLog(@"error converting fsref to fsspec. (error=%d, spec=%d, ref=%d)", error, fileSpec, fileRef); + } + else NSLog(@"error making fsref from file path. (error=%d, ref=%d, path=%@)", error, fileRef, [self fileName]); +} + +#pragma mark - +#pragma mark Export to File + +/*! +@method exportResources: +@author Nicholas Shanks +@created 24 October 2003 +@pending note that this method will cause a cascade of sheets to be displayed for each resource being exported! v.bad needs fixing +*/ + +- (IBAction)exportResources:(id)sender +{ + if ([outlineView numberOfSelectedRows] > 1) + { + NSOpenPanel *panel = [NSOpenPanel openPanel]; + [panel setAllowsMultipleSelection:NO]; + [panel setCanChooseDirectories:YES]; + [panel setCanChooseFiles:NO]; + [panel beginSheetForDirectory:nil file:nil modalForWindow:mainWindow modalDelegate:self didEndSelector:@selector(folderChoosePanelDidEnd:returnCode:contextInfo:) contextInfo:nil]; + } + else + { + [self exportResource:[outlineView selectedItem]]; + } +} + +/*! +@method exportResource: +@author Uli Kusterer +@updated 2003-10-24 NGS: moved IBAction target to exportResources: above, renamed this method +*/ + +#warning Note to Uli: how about changing the selector that the plug should implement to -(BOOL)dataForFileExport:(NSData **)fileData ofType:(NSString **)fileType. This is basically a concatenation of the two methods you came up with, but can allow the host app to specify a preferred file type (e.g. EPS) to a plug (say the PICT plug) and if the plug can't return data in that format, that's OK, it just returns the fileType of the associated data anyway. I would also recommend adding a plug method called something like availableTypesForFileExport: which returns a dictionary of file extensions and human-readable names (names should be overridden by system default names for that extension if present) that the plug can export data into, useful for say populating a pop-up menu in the export dialog. + +- (void)exportResource:(Resource *)resource +{ + Class editorClass = [[RKEditorRegistry defaultRegistry] editorForType:[resource type]]; + NSData *exportData = [resource data]; + NSString *extension = [[[resource type] lowercaseString] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + // basic overrides for file name extensions (assume no plug-ins installed) + NSString *newExtension; + NSDictionary *adjustments = [NSDictionary dictionaryWithObjectsAndKeys: @"ttf", @"sfnt", nil]; + if(newExtension = [adjustments valueForKey:extension]) + extension = newExtension; + + // ask for data + if([editorClass respondsToSelector:@selector(dataForFileExport:)]) + exportData = [editorClass dataForFileExport:resource]; + + // ask for file extension + if([editorClass respondsToSelector:@selector(filenameExtensionForFileExport:)]) + extension = [editorClass filenameExtensionForFileExport:resource]; + + NSSavePanel *panel = [NSSavePanel savePanel]; + NSString *filename = [resource name] ? [resource name] : NSLocalizedString(@"Untitled Resource",nil); + filename = [filename stringByAppendingFormat:@".%@", extension]; + [panel beginSheetForDirectory:nil file:filename modalForWindow:mainWindow modalDelegate:self didEndSelector:@selector(exportPanelDidEnd:returnCode:contextInfo:) contextInfo:[exportData retain]]; +} + +- (void)exportPanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + NSData *data = (NSData *) contextInfo; + [data autorelease]; + + if(returnCode == NSOKButton) + [data writeToFile:[sheet filename] atomically:YES]; +} + +- (void)folderChoosePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + if(returnCode == NSOKButton) + { + NSUInteger i = 1; + Resource *resource; + NSString *path, *filename, *extension; + NSDictionary *adjustments = [NSDictionary dictionaryWithObjectsAndKeys: @"ttf", @"sfnt", @"png", @"PNGf", nil]; + NSEnumerator *enumerator = [[outlineView selectedItems] objectEnumerator]; + while(resource = [enumerator nextObject]) + { + Class editorClass = [[RKEditorRegistry defaultRegistry] editorForType:[resource type]]; + NSData *exportData = [resource data]; + extension = [[[resource type] lowercaseString] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + // basic overrides for file name extensions (assume no plug-ins installed) + if([adjustments objectForKey:[resource type]]) + extension = [adjustments objectForKey:[resource type]]; + + // ask for data + if([editorClass respondsToSelector:@selector(dataForFileExport:)]) + exportData = [editorClass dataForFileExport:resource]; + + // ask for file extension + if([editorClass respondsToSelector:@selector(filenameExtensionForFileExport:)]) + extension = [editorClass filenameExtensionForFileExport:resource]; + + filename = [resource name]; + if (!filename || [filename isEqualToString:@""]) + { + filename = [NSString stringWithFormat:NSLocalizedString(@"Untitled '%@' Resource %d",nil), [resource type], i++]; + filename = [filename stringByAppendingPathExtension:extension]; + } + else + { + NSUInteger j = 1; + NSString *tempname = [filename stringByAppendingPathExtension:extension]; + while ([[NSFileManager defaultManager] fileExistsAtPath:tempname]) + { + tempname = [filename stringByAppendingFormat:@" (%d)", j++]; + tempname = [tempname stringByAppendingPathExtension:extension]; + } + filename = tempname; + } + path = [[sheet filename] stringByAppendingPathComponent:filename]; + [exportData writeToFile:path atomically:YES]; + } + } +} + +#pragma mark - +#pragma mark Window Management - (NSString *)windowNibName { @@ -94,13 +631,15 @@ extern NSString *RKResourcePboardType; ResourceNameCell *resourceNameCell = [[[ResourceNameCell alloc] init] autorelease]; [resourceNameCell setEditable:YES]; [[outlineView tableColumnWithIdentifier:@"name"] setDataCell:resourceNameCell]; +// NSLog(@"Changed data cell"); } - // set outline view's inter-cell psacing to zero to avoid getting gaps between blue bits + // set outline view's inter-cell spacing to zero to avoid getting gaps between blue bits [outlineView setIntercellSpacing:NSMakeSize(0,0)]; - [outlineView swapForOutlineSortView]; [outlineView setTarget:self]; [outlineView setDoubleAction:@selector(openResources:)]; + [outlineView setVerticalMotionCanBeginDrag:YES]; + [outlineView registerForDraggedTypes:[NSArray arrayWithObjects:RKResourcePboardType, NSStringPboardType, NSFilenamesPboardType, nil]]; // register for resource will change notifications (for undo management) [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceNameWillChange:) name:ResourceNameWillChangeNotification object:nil]; @@ -120,7 +659,7 @@ extern NSString *RKResourcePboardType; - (void)printOperationDidRun:(NSPrintOperation *)printOperation success:(BOOL)success contextInfo:(void *)contextInfo { - if( !success ) NSLog( @"Printing Failed!" ); + if(!success) NSLog(@"Printing Failed!"); } - (BOOL)keepBackupFile @@ -134,35 +673,31 @@ extern NSString *RKResourcePboardType; Resource *resource = (Resource *) [outlineView selectedItem]; // file menu - if( [item action] == @selector(saveDocument:) ) return [self isDocumentEdited]; + if([item action] == @selector(saveDocument:)) return [self isDocumentEdited]; // edit menu - else if( [item action] == @selector(clear:) ) return selectedRows > 0; - else if( [item action] == @selector(selectAll:) ) return [outlineView numberOfRows] > 0; - else if( [item action] == @selector(deselectAll:) ) return selectedRows > 0; + else if([item action] == @selector(clear:)) return selectedRows > 0; + else if([item action] == @selector(selectAll:)) return [outlineView numberOfRows] > 0; + else if([item action] == @selector(deselectAll:)) return selectedRows > 0; // resource menu - else if( [item action] == @selector(openResources:) ) return selectedRows > 0; - else if( [item action] == @selector(openResourcesInTemplate:) ) return selectedRows > 0; - else if( [item action] == @selector(openResourcesWithOtherTemplate:) ) return selectedRows > 0; - else if( [item action] == @selector(openResourcesAsHex:) ) return selectedRows > 0; - else if( [item action] == @selector(exportResourceToImageFile:) ) + else if([item action] == @selector(openResources:)) return selectedRows > 0; + else if([item action] == @selector(openResourcesInTemplate:)) return selectedRows > 0; + else if([item action] == @selector(openResourcesWithOtherTemplate:)) return selectedRows > 0; + else if([item action] == @selector(openResourcesAsHex:)) return selectedRows > 0; + else if([item action] == @selector(exportResourceToImageFile:)) { - Class edClass; - - if( selectedRows < 1 ) - return NO; - - edClass = [[RKEditorRegistry mainRegistry] editorForType: [resource type]]; - return [edClass respondsToSelector:@selector(imageForImageFileExport:)]; + if(selectedRows < 1) return NO; + Class editorClass = [[RKEditorRegistry defaultRegistry] editorForType:[resource type]]; + return [editorClass respondsToSelector:@selector(imageForImageFileExport:)]; } - else if( [item action] == @selector(playSound:) ) return selectedRows == 1 && [[resource type] isEqualToString:@"snd "]; - else if( [item action] == @selector(revertResourceToSaved:) ) return selectedRows == 1 && [resource isDirty]; + else if([item action] == @selector(playSound:)) return selectedRows == 1 && [[resource type] isEqualToString:@"snd "]; + else if([item action] == @selector(revertResourceToSaved:)) return selectedRows == 1 && [resource isDirty]; else return [super validateMenuItem:item]; } -/* TOOLBAR MANAGMENT */ #pragma mark - +#pragma mark Toolbar Management static NSString *RKToolbarIdentifier = @"com.nickshanks.resknife.toolbar"; static NSString *RKCreateItemIdentifier = @"com.nickshanks.resknife.toolbar.create"; @@ -171,7 +706,7 @@ static NSString *RKEditItemIdentifier = @"com.nickshanks.resknife.toolbar.edit" static NSString *RKEditHexItemIdentifier = @"com.nickshanks.resknife.toolbar.edithex"; static NSString *RKSaveItemIdentifier = @"com.nickshanks.resknife.toolbar.save"; static NSString *RKShowInfoItemIdentifier = @"com.nickshanks.resknife.toolbar.showinfo"; -static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.export"; +static NSString *RKExportItemIdentifier = @"com.nickshanks.resknife.toolbar.export"; - (void)setupToolbar:(NSWindowController *)windowController { @@ -200,12 +735,15 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex [item setAction:@selector(clear:)]; [toolbarItems setObject:item forKey:RKDeleteItemIdentifier]; + NSImage *image; item = [[NSToolbarItem alloc] initWithItemIdentifier:RKEditItemIdentifier]; [item autorelease]; [item setLabel:NSLocalizedString(@"Edit", nil)]; [item setPaletteLabel:NSLocalizedString(@"Edit", nil)]; [item setToolTip:NSLocalizedString(@"Edit Resource In Default Editor", nil)]; - [item setImage:[NSImage imageNamed:@"Edit"]]; + if(image = [[NSWorkspace sharedWorkspace] iconForFileType:@"rtf"]) + [item setImage:image]; + else [item setImage:[NSImage imageNamed:@"Edit"]]; [item setTarget:self]; [item setAction:@selector(openResources:)]; [toolbarItems setObject:item forKey:RKEditItemIdentifier]; @@ -215,7 +753,9 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex [item setLabel:NSLocalizedString(@"Edit Hex", nil)]; [item setPaletteLabel:NSLocalizedString(@"Edit Hex", nil)]; [item setToolTip:NSLocalizedString(@"Edit Resource As Hexadecimal", nil)]; - [item setImage:[NSImage imageNamed:@"Edit Hex"]]; + if(image = [[NSWorkspace sharedWorkspace] iconForFileType:@"txt"]) + [item setImage:image]; + else [item setImage:[NSImage imageNamed:@"Edit Hex"]]; [item setTarget:self]; [item setAction:@selector(openResourcesAsHex:)]; [toolbarItems setObject:item forKey:RKEditHexItemIdentifier]; @@ -235,7 +775,9 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex [item setLabel:NSLocalizedString(@"Show Info", nil)]; [item setPaletteLabel:NSLocalizedString(@"Show Info", nil)]; [item setToolTip:NSLocalizedString(@"Show Resource Information Window", nil)]; - [item setImage:[NSImage imageNamed:@"Show Info"]]; + if(image = [NSImage imageNamed:@"NSGetInfoToolbar"]) + [item setImage:image]; + else [item setImage:[NSImage imageNamed:@"Show Info"]]; [item setTarget:[NSApp delegate]]; [item setAction:@selector(showInfo:)]; [toolbarItems setObject:item forKey:RKShowInfoItemIdentifier]; @@ -247,10 +789,10 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex [item setToolTip:NSLocalizedString(@"Export the resource's data to a file", nil)]; [item setImage:[NSImage imageNamed:@"Export"]]; [item setTarget:self]; - [item setAction:@selector(exportResourceToFile:)]; + [item setAction:@selector(exportResources:)]; [toolbarItems setObject:item forKey:RKExportItemIdentifier]; - if( [windowController window] == mainWindow ) + if([windowController window] == mainWindow) { NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier:RKToolbarIdentifier] autorelease]; @@ -263,7 +805,7 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex // attach toolbar to window [toolbar setDelegate:self]; [mainWindow setToolbar:toolbar]; - } + } } - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag @@ -287,19 +829,19 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex int selectedRows = [outlineView numberOfSelectedRows]; NSString *identifier = [item itemIdentifier]; - if( [identifier isEqualToString:RKCreateItemIdentifier] ) valid = YES; - else if( [identifier isEqualToString:RKDeleteItemIdentifier] ) valid = selectedRows > 0; - else if( [identifier isEqualToString:RKEditItemIdentifier] ) valid = selectedRows > 0; - else if( [identifier isEqualToString:RKEditHexItemIdentifier] ) valid = selectedRows > 0; - else if( [identifier isEqualToString:RKExportItemIdentifier] ) valid = selectedRows > 0; - else if( [identifier isEqualToString:RKSaveItemIdentifier] ) valid = [self isDocumentEdited]; - else if( [identifier isEqualToString:NSToolbarPrintItemIdentifier] ) valid = YES; + if([identifier isEqualToString:RKCreateItemIdentifier]) valid = YES; + else if([identifier isEqualToString:RKDeleteItemIdentifier]) valid = selectedRows > 0; + else if([identifier isEqualToString:RKEditItemIdentifier]) valid = selectedRows > 0; + else if([identifier isEqualToString:RKEditHexItemIdentifier]) valid = selectedRows > 0; + else if([identifier isEqualToString:RKExportItemIdentifier]) valid = selectedRows > 0; + else if([identifier isEqualToString:RKSaveItemIdentifier]) valid = [self isDocumentEdited]; + else if([identifier isEqualToString:NSToolbarPrintItemIdentifier]) valid = YES; return valid; } -/* DOCUMENT MANAGEMENT */ #pragma mark - +#pragma mark Document Management - (IBAction)showCreateResourceSheet:(id)sender { @@ -308,19 +850,23 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex [sheetController showCreateResourceSheet:self]; } -- (IBAction)showSelectTemplateSheet:(id)sender +/*- (IBAction)showSelectTemplateSheet:(id)sender { // bug: ResourceDocument allocs a sheet controller, but it's never disposed of // SelectTemplateSheetController *sheetController = [[CreateResourceSheetController alloc] initWithWindowNibName:@"SelectTemplateSheet"]; // [sheetController showSelectTemplateSheet:self]; -} +}*/ - (IBAction)openResources:(id)sender { + // ignore double-clicks in table header + if(sender == outlineView && [outlineView clickedRow] == -1) + return; + Resource *resource; NSArray *selected = [outlineView selectedItems]; NSEnumerator *enumerator = [selected objectEnumerator]; - while( resource = [enumerator nextObject] ) + while(resource = [enumerator nextObject]) [self openResourceUsingEditor:resource]; } @@ -330,7 +876,7 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex Resource *resource; NSArray *selected = [outlineView selectedItems]; NSEnumerator *enumerator = [selected objectEnumerator]; - while( resource = [enumerator nextObject] ) + while(resource = [enumerator nextObject]) [self openResource:resource usingTemplate:[resource type]]; } @@ -339,7 +885,7 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex Resource *resource; NSArray *selected = [outlineView selectedItems]; NSEnumerator *enumerator = [selected objectEnumerator]; - while( resource = [enumerator nextObject] ) + while(resource = [enumerator nextObject]) [self openResourceAsHex:resource]; } @@ -356,17 +902,20 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex 2003-07-31 UK Changed to use plugin registry instead of file name. -------------------------------------------------------------------------- */ --(void) openResourceUsingEditor: (Resource*)resource +/* Method name should be changed to: -(void)openResource:(Resource *)resource usingEditor:(Class)overrideEditor */ + +- (void)openResourceUsingEditor:(Resource *)resource { - Class editorClass = [[RKEditorRegistry mainRegistry] editorForType: [resource type]]; + Class editorClass = [[RKEditorRegistry defaultRegistry] editorForType:[resource type]]; // open the resources, passing in the template to use - if( editorClass ) + if(editorClass) { // bug: I alloc a plug instance here, but have no idea where I should dealloc it, perhaps the plug ought to call [self autorelease] when it's last window is closed? - // update: doug says window controllers automatically release themselves when their window is closed. + // update: doug says window controllers automatically release themselves when their window is closed. All default plugs have a window controller as their principal class, but 3rd party ones might not + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceDataDidChange:) name:ResourceDataDidChangeNotification object:resource]; id plug = [(id )[editorClass alloc] initWithResource:resource]; - if( plug ) return; + if(plug) return; } // if no editor exists, or the editor is broken, open using template @@ -386,69 +935,105 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex 2003-07-31 UK Changed to use plugin registry instead of file name. -------------------------------------------------------------------------- */ --(void) openResource: (Resource*)resource usingTemplate: (NSString*)templateName +- (void)openResource:(Resource *)resource usingTemplate:(NSString *)templateName { // opens resource in template using TMPL resource with name templateName - Class editorClass = [[RKEditorRegistry mainRegistry] editorForType: @"Template Editor"]; + Class editorClass = [[RKEditorRegistry defaultRegistry] editorForType:@"Template Editor"]; // TODO: this checks EVERY DOCUMENT for template resources (might not be desired) // TODO: it doesn't, however, check the application's resource map for a matching template! Resource *tmpl = [Resource resourceOfType:@"TMPL" withName:[resource type] inDocument:nil]; // open the resources, passing in the template to use - if( tmpl && editorClass ) + if(tmpl && editorClass) { - // bug: I alloc a plug instance here, but have no idea where I should dealloc it, perhaps the plug ought to call [self autorelease] when it's last window is closed? - // update: doug says window controllers automatically release themselves when their window is closed. - NSWindowController *plugController = [(id )[editorClass alloc] initWithResources:resource, tmpl, nil]; - if( plugController ) return; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceDataDidChange:) name:ResourceDataDidChangeNotification object:resource]; + id plug = [(id )[editorClass alloc] initWithResources:resource, tmpl, nil]; + if(plug) return; } // if no template exists, or template editor is broken, open as hex [self openResourceAsHex:resource]; } +/*! +@method openResourceAsHex: +@author Nicholas Shanks +@created 2001 +@updated 2003-07-31 UK: Changed to use plugin registry instead of file name. +@description Open a hex editor for the specified Resource instance. This looks up the hexadecimal editor in the plugin registry and then instantiates an editor object, handing it the resource. +@param resource Resource to edit +*/ -/* ----------------------------------------------------------------------------- - openResourceAsHex: - Open a hex editor for the specified Resource instance. This looks - up the hexadecimal editor in the plugin registry and then instantiates an - editor object, handing it the resource. - - REVISIONS: - 2003-07-31 UK Changed to use plugin registry instead of file name. - -------------------------------------------------------------------------- */ - --(void) openResourceAsHex: (Resource*)resource +- (void)openResourceAsHex:(Resource *)resource { - Class editorClass = [[RKEditorRegistry mainRegistry] editorForType: @"Hexadecimal Editor"]; + Class editorClass = [[RKEditorRegistry defaultRegistry] editorForType: @"Hexadecimal Editor"]; // bug: I alloc a plug instance here, but have no idea where I should dealloc it, perhaps the plug ought to call [self autorelease] when it's last window is closed? // update: doug says window controllers automatically release themselves when their window is closed. + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceDataDidChange:) name:ResourceDataDidChangeNotification object:resource]; NSWindowController *plugController = [(id )[editorClass alloc] initWithResource:resource]; +#pragma unused(plugController) } +/*! +@method playSound: +@abstract Plays the selected carbon 'snd ' resource. +@author Nicholas Shanks +@created 2001 +@updated 2003-10-22 NGS: Moved playing into seperate thread to avoid locking up main thread. +@pending should really be moved to a 'snd ' editor, but first we'd need to extend the plugin protocol to call the class so it can add such menu items. Of course, we could just make the 'snd ' editor have a button in its window that plays the sound. +@description This method is called from a menu item which is validated against there being only one selected resource (of type 'snd '), so shouldn't have to deal with playing multiple sounds, though this may of course change in future. +@param sender ignored +*/ -// TODO: These two should really be moved to a 'snd ' editor, but first we'd -// need to extend the plugin protocol to call the class so it can add -// such menu items. Of course, we could just make the 'snd ' editor -// have a button in its window that plays the sound. - (IBAction)playSound:(id)sender { // bug: can only cope with one selected item NSData *data = [(Resource *)[outlineView itemAtRow:[outlineView selectedRow]] data]; - if( data && [data length] != 0 ) + if(data && [data length] != 0) { - // bug: plays sound synchronously in main thread! - SndListPtr sndPtr = (SndListPtr) [data bytes]; - SndPlay( nil, &sndPtr, false ); + [NSThread detachNewThreadSelector:@selector(playSoundThreadController:) toTarget:self withObject:data]; } else NSBeep(); } +/*! +@method playSoundThreadController: +@abstract Plays a carbon 'snd ' resource. +@author Nicholas Shanks +@created 2003-10-22 +@pending should really be moved to a 'snd ' editor, but first we'd need to extend the plugin protocol to call the class so it can add such menu items. Of course, we could just make the 'snd ' editor have a button in its window that plays the sound. +@description This method was added to prevent having to use AsynchSoundHelper to play them asynchronously in the main thread and all the associated idle checking, which since we have no event loop, would have to have been called from a timer. I'm not sure if the autorelease pool is necessary, as no cocoa objects are created, but an NSData is passed in and messages sent to it, and NSBeep() might need one. +@param data An NSData object containing the snd resource data to be played. +*/ + +- (void)playSoundThreadController:(NSData *)data +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + if(data && [data length] != 0) + { + // plays sound synchronously, thread exits when sound is done playing + SndListPtr sndPtr = (SndListPtr) [data bytes]; + SndPlay(nil, &sndPtr, false); + } + else NSBeep(); + [pool release]; +} + +/*! +@method sound:didFinishPlaying: +@abstract Called frequently when playing a sound via NSSound. Unused, here for reference and possible future use. +@author Nicholas Shanks +@pending should really be moved to a 'snd ' editor, but first we'd need to extend the plugin protocol to call the class so it can add such menu items. Of course, we could just make the 'snd ' editor have a button in its window that plays the sound. +@param sound The NSSound that is playing. +@param finished Flag to indicate if it has just finished and that we should clean up. +*/ + - (void)sound:(NSSound *)sound didFinishPlaying:(BOOL)finished { - if( finished ) [sound release]; - NSLog( @"sound released" ); + // unused because I can't get NSSound to play snd resources, so I use Carbon's SndPlay(), above + if(finished) [sound release]; + NSLog(@"sound released"); } - (void)resourceNameWillChange:(NSNotification *)notification @@ -464,9 +1049,9 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex // this saves the current resource's ID number so we can undo the change Resource *resource = (Resource *) [notification object]; [[self undoManager] registerUndoWithTarget:resource selector:@selector(setResID:) object:[[[resource resID] copy] autorelease]]; - if( [[resource name] length] == 0 ) + if([[resource name] length] == 0) [[self undoManager] setActionName:NSLocalizedString(@"ID Change", nil)]; - else [[self undoManager] setActionName:[NSString stringWithFormat:NSLocalizedString(@"ID Change for Ò%@Ó", nil), [resource name]]]; + else [[self undoManager] setActionName:[NSString stringWithFormat:NSLocalizedString(@"ID Change for '%@'", nil), [resource name]]]; } - (void)resourceTypeWillChange:(NSNotification *)notification @@ -474,9 +1059,9 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex // this saves the current resource's type so we can undo the change Resource *resource = (Resource *) [notification object]; [[self undoManager] registerUndoWithTarget:resource selector:@selector(setType:) object:[[[resource type] copy] autorelease]]; - if( [[resource name] length] == 0 ) + if([[resource name] length] == 0) [[self undoManager] setActionName:NSLocalizedString(@"Type Change", nil)]; - else [[self undoManager] setActionName:[NSString stringWithFormat:NSLocalizedString(@"Type Change for Ò%@Ó", nil), [resource name]]]; + else [[self undoManager] setActionName:[NSString stringWithFormat:NSLocalizedString(@"Type Change for '%@'", nil), [resource name]]]; } - (void)resourceAttributesWillChange:(NSNotification *)notification @@ -484,13 +1069,18 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex // this saves the current state of the resource's attributes so we can undo the change Resource *resource = (Resource *) [notification object]; [[self undoManager] registerUndoWithTarget:resource selector:@selector(setAttributes:) object:[[[resource attributes] copy] autorelease]]; - if( [[resource name] length] == 0 ) + if([[resource name] length] == 0) [[self undoManager] setActionName:NSLocalizedString(@"Attributes Change", nil)]; - else [[self undoManager] setActionName:[NSString stringWithFormat:NSLocalizedString(@"Attributes Change for Ò%@Ó", nil), [resource name]]]; + else [[self undoManager] setActionName:[NSString stringWithFormat:NSLocalizedString(@"Attributes Change for '%@'", nil), [resource name]]]; +} + +- (void)resourceDataDidChange:(NSNotification *)notification +{ + [self updateChangeCount:NSChangeDone]; } -/* EDIT OPERATIONS */ #pragma mark - +#pragma mark Edit Operations - (IBAction)cut:(id)sender { @@ -500,7 +1090,7 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex - (IBAction)copy:(id)sender { - #pragma unused( sender ) + #pragma unused(sender) NSArray *selectedItems = [outlineView selectedItems]; NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard]; [pb declareTypes:[NSArray arrayWithObject:RKResourcePboardType] owner:self]; @@ -509,9 +1099,9 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex - (IBAction)paste:(id)sender { - #pragma unused( sender ) + #pragma unused(sender) NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard]; - if( [pb availableTypeFromArray:[NSArray arrayWithObject:RKResourcePboardType]] ) + if([pb availableTypeFromArray:[NSArray arrayWithObject:RKResourcePboardType]]) [self pasteResources:[NSUnarchiver unarchiveObjectWithData:[pb dataForType:RKResourcePboardType]]]; } @@ -519,10 +1109,10 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex { Resource *resource; NSEnumerator *enumerator = [pastedResources objectEnumerator]; - while( resource = (Resource *) [enumerator nextObject] ) + while(resource = (Resource *) [enumerator nextObject]) { // check resource type/ID is available - if( [dataSource resourceOfType:[resource type] andID:[resource resID]] == nil ) + if([dataSource resourceOfType:[resource type] andID:[resource resID]] == nil) { // resource slot is available, paste this one in [dataSource addResource:resource]; @@ -533,7 +1123,7 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex NSMutableArray *remainingResources = [[NSMutableArray alloc] initWithCapacity:1]; [remainingResources addObject:resource]; [remainingResources addObjectsFromArray:[enumerator allObjects]]; - NSBeginAlertSheet( @"Paste Error", @"Unique ID", @"Skip", @"Overwrite", mainWindow, self, NULL, @selector(overwritePasteSheetDidDismiss:returnCode:contextInfo:), remainingResources, @"There already exists a resource of type %@ with ID %@. Do you wish to assign the pasted resource a unique ID, overwrite the existing resource, or skip pasting of this resource?", [resource type], [resource resID] ); + NSBeginAlertSheet(@"Paste Error", @"Unique ID", @"Skip", @"Overwrite", mainWindow, self, NULL, @selector(overwritePasteSheetDidDismiss:returnCode:contextInfo:), remainingResources, @"There already exists a resource of type %@ with ID %@. Do you wish to assign the pasted resource a unique ID, overwrite the existing resource, or skip pasting of this resource?", [resource type], [resource resID]); } } } @@ -542,17 +1132,17 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex { NSMutableArray *remainingResources = [NSMutableArray arrayWithArray:[(NSArray *)contextInfo autorelease]]; Resource *resource = [remainingResources objectAtIndex:0]; - if( returnCode == NSAlertDefaultReturn ) // unique ID + if(returnCode == NSAlertDefaultReturn) // unique ID { Resource *newResource = [Resource resourceOfType:[resource type] andID:[dataSource uniqueIDForType:[resource type]] withName:[resource name] andAttributes:[resource attributes] data:[resource data]]; [dataSource addResource:newResource]; } - else if( NSAlertOtherReturn ) // overwrite + else if(NSAlertOtherReturn) // overwrite { [dataSource removeResource:[dataSource resourceOfType:[resource type] andID:[resource resID]]]; [dataSource addResource:resource]; } -// else if( NSAlertAlternateReturn ) // skip +// else if(NSAlertAlternateReturn) // skip // remove top resource and continue paste [remainingResources removeObjectAtIndex:0]; @@ -561,18 +1151,18 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex - (IBAction)clear:(id)sender { - #pragma unused( sender ) - if( [prefs boolForKey:@"DeleteResourceWarning"] ) + #pragma unused(sender) + if([prefs boolForKey:@"DeleteResourceWarning"]) { - NSBeginCriticalAlertSheet( @"Delete Resource", @"Delete", @"Cancel", nil, [self mainWindow], self, @selector(deleteResourcesSheetDidEnd:returnCode:contextInfo:), NULL, nil, @"Please confirm you wish to delete the selected resources." ); + NSBeginCriticalAlertSheet(@"Delete Resource", @"Delete", @"Cancel", nil, [self mainWindow], self, @selector(deleteResourcesSheetDidEnd:returnCode:contextInfo:), NULL, nil, @"Please confirm you wish to delete the selected resources."); } else [self deleteSelectedResources]; } - (void)deleteResourcesSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo { - #pragma unused( contextInfo ) - if( returnCode == NSOKButton ) + #pragma unused(contextInfo) + if(returnCode == NSOKButton) [self deleteSelectedResources]; } @@ -585,325 +1175,25 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex // enumerate through array and delete resources [[self undoManager] beginUndoGrouping]; enumerator = [selectedItems reverseObjectEnumerator]; // reverse so an undo will replace items in original order - while( resource = [enumerator nextObject] ) + while(resource = [enumerator nextObject]) { [dataSource removeResource:resource]; - if( [[resource name] length] == 0 ) + if([[resource name] length] == 0) [[self undoManager] setActionName:NSLocalizedString(@"Delete Resource", nil)]; - else [[self undoManager] setActionName:[NSString stringWithFormat:NSLocalizedString(@"Delete Resource Ò%@Ó", nil), [resource name]]]; + else [[self undoManager] setActionName:[NSString stringWithFormat:NSLocalizedString(@"Delete Resource '%@'", nil), [resource name]]]; } [[self undoManager] endUndoGrouping]; // generalise undo name if more than one was deleted - if( [outlineView numberOfSelectedRows] > 1 ) + if([outlineView numberOfSelectedRows] > 1) [[self undoManager] setActionName:NSLocalizedString(@"Delete Resources", nil)]; // deselect resources (otherwise other resources move into selected rows!) [outlineView deselectAll:self]; } -/* FILE HANDLING */ -#pragma mark - - -/*- (BOOL)prepareSavePanel:(NSSavePanel *)savePanel -{ - [savePanel setTreatsFilePackagesAsDirectories:YES]; - return YES; -}*/ - - -/* ----------------------------------------------------------------------------- - readFromFile:ofType: - Open the specified file and read its resources. This first tries to - load the resources from the res fork, and failing that tries the data - fork. - - REVISIONS: - 2003-08-01 UK Commented. - -------------------------------------------------------------------------- */ - --(BOOL) readFromFile: (NSString*)fileName ofType: (NSString*)fileKind -{ - BOOL succeeded = NO; - OSStatus error = noErr; - HFSUniStr255 *resourceForkName = (HFSUniStr255 *) NewPtrClear( sizeof(HFSUniStr255) ); // This may be saved away in the instance variable "fork" to keep track of which fork our resources are in. - FSRef *fileRef = (FSRef *) NewPtrClear( sizeof(FSRef) ); - SInt16 fileRefNum = 0; - FSCatalogInfo info = { 0 }; - - // open fork with resources in it - error = FSPathMakeRef( [fileName cString], fileRef, nil ); - error = FSGetResourceForkName( resourceForkName ); - SetResLoad( false ); // don't load "preload" resources - - [type release]; - [creator release]; - - error = FSGetCatalogInfo( fileRef, kFSCatInfoFinderInfo, &info, nil, nil, nil ); - type = [[NSString stringWithCString: &((FileInfo*)info.finderInfo)->fileType length:4] retain]; - creator = [[NSString stringWithCString: &((FileInfo*)info.finderInfo)->fileCreator length:4] retain]; - - // Try res fork first: - error = FSOpenResourceFile( fileRef, resourceForkName->length, (UniChar *) &resourceForkName->unicode, fsRdPerm, &fileRefNum); - if( error ) // try to open data fork instead - { - NSLog( @"Opening Resource fork failed, trying data fork..." ); - error = FSOpenResourceFile( fileRef, 0, nil, fsRdPerm, &fileRefNum); - } - else - { - fork = resourceForkName; - [self readFork:@"" asStreamFromFile:fileName]; // bug: only reads data fork for now, need to scan file for other forks too - } - SetResLoad( true ); // restore resource loading as soon as is possible - - // read the resources (without spawning thousands of undos for resource creation) - [[self undoManager] disableUndoRegistration]; - if( fileRefNum && error == noErr ) - succeeded = [self readResourceMap:fileRefNum]; - else if( !fileRefNum ) - { - // supposed to read data fork as byte stream here - NSLog( @"Opening data fork failed too! (fileRef)" ); - } - else NSLog( @"Opening data fork failed too! (error)" ); - [[self undoManager] enableUndoRegistration]; - - // tidy up loose ends - if( !fork ) DisposePtr( (Ptr) resourceForkName ); // only delete if we're not saving it to "fork" instance var. - if( fileRefNum ) FSClose( fileRefNum ); - DisposePtr( (Ptr) fileRef ); - - return succeeded; -} - -- (BOOL)readFork:(NSString *)forkName asStreamFromFile:(NSString *)fileName -{ - NSData *data = [NSData dataWithContentsOfFile:fileName]; - Resource *resource = [Resource resourceOfType:@"" andID:0 withName:NSLocalizedString(@"Data Fork", nil) andAttributes:0 data:data]; - if( data && resource ) - { - /* NTFS Note: When running SFM (Services for Macintosh) a Windows NT-based system (including 2000 & XP) serving NTFS-formatted drives stores Mac resource forks in a stream named "AFP_Resource". The finder info/attributes are stored in a stream called "Afp_AfpInfo". The default data fork stream is called "$DATA" and any of these can be accessed thus: "c:\filename.txt:forkname". - As a result, ResKnife prohibits creation of forks with the following names: "" (empty string, Mac data fork name), - "$DATA" (NTFS data fork name), - "AFP_Resource" and "Afp_AfpInfo". - It is perfectly legal in ResKnife to read in forks of these names when accessing a shared NTFS drive from a server running SFM. */ - - [resource setRepresentedFork:forkName]; - [resource setDocument:self]; - [resources insertObject:resource atIndex:0]; - return YES; - } - else return NO; -} - --(BOOL) readResourceMap: (SInt16)fileRefNum -{ - OSStatus error = noErr; - unsigned short n; - unsigned short i; - SInt16 oldResFile = CurResFile(); - UseResFile( fileRefNum ); - - for( i = 1; i <= Count1Types(); i++ ) - { - ResType resType; - unsigned short j; - - Get1IndType( &resType, i ); - n = Count1Resources( resType ); - for( j = 1; j <= n; j++ ) - { - Str255 nameStr; - long sizeLong; - short resIDShort; - short attrsShort; - Handle resourceHandle; - NSString *name; - NSString *type; - NSNumber *resID; - NSNumber *attributes; - NSData *data; - Resource *resource; - - resourceHandle = Get1IndResource( resType, j ); - error = ResError(); - if( error != noErr ) - { - NSLog( @"Error reading resource map..." ); - UseResFile( oldResFile ); - return NO; - } - - GetResInfo( resourceHandle, &resIDShort, &resType, nameStr ); - sizeLong = GetResourceSizeOnDisk( resourceHandle ); - attrsShort = GetResAttrs( resourceHandle ); - HLockHi( resourceHandle ); - - // create the resource & add it to the array - name = [NSString stringWithCString:&nameStr[1] length:nameStr[0]]; - type = [NSString stringWithCString:(char *) &resType length:4]; - resID = [NSNumber numberWithShort:resIDShort]; - attributes = [NSNumber numberWithShort:attrsShort]; - data = [NSData dataWithBytes:*resourceHandle length:sizeLong]; - resource = [Resource resourceOfType:type andID:resID withName:name andAttributes:attributes data:data]; - [resource setDocument:self]; - [resources addObject:resource]; // array retains resource - - HUnlock( resourceHandle ); - ReleaseResource( resourceHandle ); - } - } - - // save resource map and clean up - UseResFile( oldResFile ); - return YES; -} - -- (BOOL)writeToFile:(NSString *)fileName ofType:(NSString *)type -{ - BOOL succeeded = NO; - OSStatus error = noErr; - FSRef *parentRef = (FSRef *) NewPtrClear( sizeof(FSRef) ); - FSRef *fileRef = (FSRef *) NewPtrClear( sizeof(FSRef) ); - FSSpec *fileSpec = (FSSpec *) NewPtrClear( sizeof(FSSpec) ); - SInt16 fileRefNum = 0; - - // create and open file for writing - error = FSPathMakeRef( [[fileName stringByDeletingLastPathComponent] cString], parentRef, nil ); - if( fork && ![type isEqualToString: @"Resource File (Data Fork)"] ) - { - unichar *uniname = (unichar *) NewPtrClear( sizeof(unichar) *256 ); - [[fileName lastPathComponent] getCharacters:uniname]; - error = FSCreateResourceFile( parentRef, [[fileName lastPathComponent] length], (UniChar *) uniname, kFSCatInfoNone, nil, fork->length, (UniChar *) &fork->unicode, fileRef, fileSpec ); - if( !error ) - { - // Set type & creator code: - FInfo macMetadata; - error = FSpGetFInfo( fileSpec, &macMetadata ); - macMetadata.fdType = 'rsrc'; - macMetadata.fdCreator = 'ResK'; - if( !error ) - FSpSetFInfo( fileSpec, &macMetadata ); - - // Open fork for fetching resources: - error = FSOpenResourceFile( fileRef, fork->length, (UniChar *) &fork->unicode, fsWrPerm, &fileRefNum); - - /* at some point make use of: - - FSCreateResourceFork( const FSRef * ref, - UniCharCount forkNameLength, - const UniChar * forkName, // can be NULL - UInt32 flags); - - Creates the named fork and initalises as a resource fork - - Mac OS 10.2 or later */ - } - } - else - { - unichar *uniname = (unichar *) NewPtrClear( sizeof(unichar) *256 ); - [[fileName lastPathComponent] getCharacters:uniname]; - error = FSCreateResourceFile( parentRef, [[fileName lastPathComponent] length], (UniChar *) uniname, kFSCatInfoNone, nil, 0, nil, fileRef, fileSpec ); - if( !error ) - { - // Set type & creator: - FInfo macMetadata; - error = FSpGetFInfo( fileSpec, &macMetadata ); - macMetadata.fdType = 'rsrc'; - macMetadata.fdCreator = 'ResK'; - if( !error ) - error = FSpSetFInfo( fileSpec, &macMetadata ); - - // Open resource map for fetching resources: - error = FSOpenResourceFile( fileRef, 0, nil, fsWrPerm, &fileRefNum); - } - } - - // write resource array to file - if( fileRefNum && !error ) - succeeded = [self writeResourceMap:fileRefNum]; - - // tidy up loose ends - if( fileRefNum ) FSClose( fileRefNum ); - DisposePtr( (Ptr) fileRef ); - return succeeded; -} - - -/* ----------------------------------------------------------------------------- - writeResourceMap: - Writes all resources (except the ones representing other forks of the - file) to the specified resource file. - - REVISIONS: - 2003-08-01 UK Swiss national holiday, and I'm stuck in Germany... - Commented, changed to use enumerator instead of - objectAtIndex. - -------------------------------------------------------------------------- */ - --(BOOL) writeResourceMap: (SInt16)fileRefNum -{ - OSStatus error = noErr; - NSEnumerator* enny; - Resource *resource; - - // Make the resource file current: - SInt16 oldResFile = CurResFile(); - UseResFile( fileRefNum ); - - // Loop over all our resources: - for( enny = [resources objectEnumerator]; resource = [enny nextObject]; ) - { - Str255 nameStr; - ResType resType; - short resIDShort; - short attrsShort; - Handle resourceHandle; - - // Resource represents another fork in the file? Skip it. - if( [resource representedFork] != nil ) continue; - - resIDShort = [[resource resID] shortValue]; - attrsShort = [[resource attributes] shortValue]; - resourceHandle = NewHandleClear( [[resource data] length] ); - - // Unicode name -> P-String: - nameStr[0] = [[resource name] cStringLength]; - BlockMoveData( [[resource name] cString], &nameStr[1], nameStr[0] ); - - // Type string to ResType: - [[resource type] getCString:(char *) &resType maxLength:4]; - - // NSData to resource data Handle: - HLockHi( resourceHandle ); - [[resource data] getBytes:*resourceHandle]; - HUnlock( resourceHandle ); - - // Now that everything's converted, write it to our file: - AddResource( resourceHandle, resType, resIDShort, nameStr ); - if( ResError() == addResFailed ) - { - NSLog( @"*Saving failed*; could not add resource ID %@ of type %@ to file.", [resource resID], [resource type] ); - error = addResFailed; - } - else - { - NSLog( @"Added resource %@, \"%@\", of type %@ to file.", [resource resID], [resource name], [resource type] ); - SetResAttrs( resourceHandle, attrsShort ); - ChangedResource( resourceHandle ); - UpdateResFile( fileRefNum ); - } - } - - // Save resource map and clean up: - UseResFile( oldResFile ); - return error? NO:YES; -} - -/* ACCESSORS */ #pragma mark - +#pragma mark Accessors - (NSWindow *)mainWindow { @@ -925,132 +1215,86 @@ static NSString *RKExportItemIdentifier = @"com.ulikusterer.resknife.toolbar.ex return resources; } -- (NSString *)creator +- (NSData *)creator { return creator; } -- (NSString *)type +- (NSData *)type { return type; } - (IBAction)creatorChanged:(id)sender { - [self setCreator:[sender stringValue]]; + unsigned long newCreator = 0x00; // creator is nil by default + NSData *creatorData = [[sender stringValue] dataUsingEncoding:NSMacOSRomanStringEncoding]; +// NSLog(@"creatorChanged: [sender stringValue] = '%@'; creatorData = '%@'", [sender stringValue], creatorData); + if(creatorData && [creatorData length] > 0) + { + newCreator = ' '; // pad with spaces if not nil + [creatorData getBytes:&newCreator length:([creatorData length] < 4? [creatorData length]:4)]; + } + [self setCreator:[NSData dataWithBytes:&newCreator length:4]]; +// NSLog(@"Creator changed to '%@'", [[[NSString alloc] initWithBytes:&newCreator length:4 encoding:NSMacOSRomanStringEncoding] autorelease]); } - (IBAction)typeChanged:(id)sender { - [self setType:[sender stringValue]]; + unsigned long newType = 0x00; + NSData *typeData = [[sender stringValue] dataUsingEncoding:NSMacOSRomanStringEncoding]; +// NSLog(@"typeChanged: [sender stringValue] = '%@'; typeData = '%@'", [sender stringValue], typeData); + if(typeData && [typeData length] > 0) + { + newType = ' '; + [typeData getBytes:&newType length:([typeData length] < 4? [typeData length]:4)]; + } + [self setType:[NSData dataWithBytes:&newType length:4]]; +// NSLog(@"Type changed to '%@'", [[[NSString alloc] initWithBytes:&newType length:4 encoding:NSMacOSRomanStringEncoding] autorelease]); } -- (void)setCreator:(NSString *)newCreator +- (BOOL)setCreator:(NSData *)newCreator { - if( ![newCreator isEqualToString:creator] ) + if(![newCreator isEqualToData:creator]) { id old = creator; - [[NSNotificationCenter defaultCenter] postNotificationName:DocumentInfoWillChangeNotification object:[NSDictionary dictionaryWithObjectsAndKeys:self, @"NSDocument", newCreator, @"NSString creator", nil]]; + [[NSNotificationCenter defaultCenter] postNotificationName:DocumentInfoWillChangeNotification object:[NSDictionary dictionaryWithObjectsAndKeys:self, @"NSDocument", newCreator, @"creator", nil]]; [[self undoManager] registerUndoWithTarget:self selector:@selector(setCreator:) object:creator]; - [[self undoManager] setActionName:NSLocalizedString( @"Change Creator Code", nil)]; + [[self undoManager] setActionName:NSLocalizedString(@"Change Creator Code", nil)]; creator = [newCreator copy]; [old release]; - [[NSNotificationCenter defaultCenter] postNotificationName:DocumentInfoDidChangeNotification object:[NSDictionary dictionaryWithObjectsAndKeys:self, @"NSDocument", newCreator, @"NSString creator", nil]]; + [[NSNotificationCenter defaultCenter] postNotificationName:DocumentInfoDidChangeNotification object:[NSDictionary dictionaryWithObjectsAndKeys:self, @"NSDocument", creator, @"creator", nil]]; + return YES; } + else return NO; } -- (void)setType:(NSString *)newType +- (BOOL)setType:(NSData *)newType { - if( ![newType isEqualToString:type] ) + if(![newType isEqualToData:type]) { id old = type; - [[NSNotificationCenter defaultCenter] postNotificationName:DocumentInfoWillChangeNotification object:[NSDictionary dictionaryWithObjectsAndKeys:self, @"NSDocument", newType, @"NSString type", nil]]; + [[NSNotificationCenter defaultCenter] postNotificationName:DocumentInfoWillChangeNotification object:[NSDictionary dictionaryWithObjectsAndKeys:self, @"NSDocument", newType, @"type", nil]]; [[self undoManager] registerUndoWithTarget:self selector:@selector(setType:) object:type]; - [[self undoManager] setActionName:NSLocalizedString( @"Change File Type", nil)]; + [[self undoManager] setActionName:NSLocalizedString(@"Change File Type", nil)]; type = [newType copy]; [old release]; - [[NSNotificationCenter defaultCenter] postNotificationName:DocumentInfoDidChangeNotification object:[NSDictionary dictionaryWithObjectsAndKeys:self, @"NSDocument", newType, @"NSString type", nil]]; + [[NSNotificationCenter defaultCenter] postNotificationName:DocumentInfoDidChangeNotification object:[NSDictionary dictionaryWithObjectsAndKeys:self, @"NSDocument", type, @"type", nil]]; + return YES; } + else return NO; } -- (void)setCreator:(NSString *)newCreator andType:(NSString *)newType +- (BOOL)setCreator:(NSData *)newCreator andType:(NSData *)newType { - BOOL changeAction = ![newCreator isEqualToString:creator] && ![newType isEqualToString:type]; + BOOL creatorChanged, typeChanged; [[self undoManager] beginUndoGrouping]; - [self setCreator:newCreator]; - [self setType:newType]; + creatorChanged = [self setCreator:newCreator]; + typeChanged = [self setType:newType]; [[self undoManager] endUndoGrouping]; - if( changeAction ) - [[self undoManager] setActionName:NSLocalizedString( @"Change Creator & Type", nil)]; -} - - --(void) exportDataPanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo -{ - NSData* data = contextInfo; - [data autorelease]; - - if( returnCode == NSOKButton ) - [data writeToFile:[sheet filename] atomically: YES]; -} - - --(void) exportImagePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo -{ - NSImage* img = contextInfo; - [img autorelease]; - - if( returnCode == NSOKButton ) - { - NSData* data = [img TIFFRepresentation]; - [data writeToFile:[sheet filename] atomically: YES]; - } -} - - --(IBAction) exportResourceToFile: (id)sender -{ - Resource* resource = (Resource*) [outlineView selectedItem]; - NSData* theData; - Class edClass = [[RKEditorRegistry mainRegistry] editorForType: [resource type]]; - NSString* extension = [resource type]; - NSSavePanel* panel; - NSString* fName; - - if( [edClass respondsToSelector:@selector(dataForFileExport:)] ) - theData = [edClass dataForFileExport: resource]; - else - theData = [resource data]; - - if( [edClass respondsToSelector:@selector(extensionForFileExport:)] ) - extension = [edClass extensionForFileExport]; - - panel = [NSSavePanel savePanel]; - fName = [[resource name] stringByAppendingFormat: @".%@", extension]; - - [panel beginSheetForDirectory:nil file:fName modalForWindow:mainWindow modalDelegate:self - didEndSelector:@selector(exportDataPanelDidEnd:returnCode:contextInfo:) contextInfo:[theData retain]]; -} - - --(IBAction) exportResourceToImageFile: (id)sender -{ - Resource* resource = (Resource*) [outlineView selectedItem]; - NSImage* theData; - Class edClass = [[RKEditorRegistry mainRegistry] editorForType: [resource type]]; - NSString* extension = @"tiff"; - NSSavePanel* panel; - NSString* fName; - - if( ![edClass respondsToSelector:@selector(imageForImageFileExport:)] ) - return; - - theData = [edClass imageForImageFileExport: resource]; - panel = [NSSavePanel savePanel]; - fName = [[resource name] stringByAppendingFormat: @".%@", extension]; - - [panel beginSheetForDirectory:nil file:fName modalForWindow:mainWindow modalDelegate:self - didEndSelector:@selector(exportImagePanelDidEnd:returnCode:contextInfo:) contextInfo:[theData retain]]; + if(creatorChanged && typeChanged) + [[self undoManager] setActionName:NSLocalizedString(@"Change Creator & Type", nil)]; + return (creatorChanged || typeChanged); } @end diff --git a/Cocoa/Classes/ResourceNameCell.m b/Cocoa/Classes/ResourceNameCell.m index 375fd3a..44a58bf 100644 --- a/Cocoa/Classes/ResourceNameCell.m +++ b/Cocoa/Classes/ResourceNameCell.m @@ -5,10 +5,9 @@ - (id)init { self = [super init]; - if( self ) - { - drawImage = YES; - } + if(!self) return nil; + [self setWraps:NO]; + drawImage = YES; return self; } @@ -42,7 +41,7 @@ - (void)setImage:(NSImage *)newImage { - if( image != newImage ) + if(image != newImage) { // save image and set to 16x16 pixels id old = image; @@ -55,7 +54,7 @@ - (NSRect)imageFrameForCellFrame:(NSRect)cellFrame { - if( image != nil && drawImage == YES ) + if(image != nil && drawImage == YES) { // center image vertically in frame, offset right by three NSRect imageFrame; @@ -70,11 +69,11 @@ - (void)editWithFrame:(NSRect)cellFrame inView:(NSView *)controlView editor:(NSText *)textObject delegate:(id)delegateObject event:(NSEvent *)theEvent { - if( drawImage == YES ) + if(drawImage == YES) { // split cell frame into two, pass the text part to the superclass NSRect textFrame, imageFrame; - NSDivideRect( cellFrame, &imageFrame, &textFrame, 3 + [image size].width, NSMinXEdge ); + NSDivideRect(cellFrame, &imageFrame, &textFrame, 3 + [image size].width, NSMinXEdge); [super editWithFrame:textFrame inView:controlView editor:textObject delegate:delegateObject event:theEvent]; } else @@ -85,11 +84,11 @@ - (void)selectWithFrame:(NSRect)cellFrame inView:(NSView *)controlView editor:(NSText *)textObject delegate:(id)delegateObject start:(int)selStart length:(int)selLength { - if( drawImage == YES ) + if(drawImage == YES) { // split cell frame into two, pass the text part to the superclass NSRect textFrame, imageFrame; - NSDivideRect( cellFrame, &imageFrame, &textFrame, 3 + [image size].width, NSMinXEdge); + NSDivideRect(cellFrame, &imageFrame, &textFrame, 3 + [image size].width, NSMinXEdge); [super selectWithFrame:textFrame inView:controlView editor:textObject delegate:delegateObject start:selStart length:selLength]; } else @@ -100,15 +99,14 @@ - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { - if( image != nil && drawImage == YES ) + if(image != nil && drawImage == YES) { NSRect imageFrame; NSSize imageSize = [image size]; // get image frame - NSDivideRect( cellFrame, &imageFrame, &cellFrame, 3 + imageSize.width, NSMinXEdge ); -// NSLog( "drawing name cell with bg: %@ colour: %@", [self drawsBackground]? @"YES":@"NO", [self backgroundColor] ); - if( [self drawsBackground] && ![self isHighlighted] /* ![self cellAttribute:NSCellHighlighted] */ ) + NSDivideRect(cellFrame, &imageFrame, &cellFrame, 3 + imageSize.width, NSMinXEdge); + if([self drawsBackground] && ![self isHighlighted] /* ![self cellAttribute:NSCellHighlighted] */) { [[self backgroundColor] set]; NSRectFill(imageFrame); @@ -117,7 +115,7 @@ imageFrame.size = imageSize; // center vertically - if( [controlView isFlipped] ) + if([controlView isFlipped]) imageFrame.origin.y += ceil((cellFrame.size.height + imageFrame.size.height) / 2); else imageFrame.origin.y += ceil((cellFrame.size.height - imageFrame.size.height) / 2); @@ -132,7 +130,7 @@ - (NSSize)cellSize { NSSize cellSize = [super cellSize]; - if( drawImage == YES ) + if(drawImage == YES) cellSize.width += (image? [image size].width:0) + 3; return cellSize; } diff --git a/Cocoa/Classes/SizeFormatter.m b/Cocoa/Classes/SizeFormatter.m index 2cddebe..e71eb61 100644 --- a/Cocoa/Classes/SizeFormatter.m +++ b/Cocoa/Classes/SizeFormatter.m @@ -15,7 +15,7 @@ float value = [obj floatValue]; int power = 0; - while( value >= 1024 && power <= 30 ) + while( value >= 1024 /*&& power <= 30*/ ) { power += 10; // 10 == KB, 20 == MB, 30+ == GB value /= 1024; diff --git a/Cocoa/English.lproj/AboutPanel.nib/info.nib b/Cocoa/English.lproj/AboutPanel.nib/info.nib index 57f78f4..cd6112a 100644 --- a/Cocoa/English.lproj/AboutPanel.nib/info.nib +++ b/Cocoa/English.lproj/AboutPanel.nib/info.nib @@ -1,20 +1,20 @@ - - + + IBDocumentLocation - 109 310 356 240 0 0 1280 1002 + 147 310 356 240 0 0 1600 1002 IBFramework Version - 248.0 + 446.1 IBLockedObjects 25 IBOpenObjects - 23 + 21 IBSystem Version - 5P48 + 8L127 diff --git a/Cocoa/English.lproj/AboutPanel.nib/objects.nib b/Cocoa/English.lproj/AboutPanel.nib/objects.nib index 05ca5a1..f6d72e6 100644 Binary files a/Cocoa/English.lproj/AboutPanel.nib/objects.nib and b/Cocoa/English.lproj/AboutPanel.nib/objects.nib differ diff --git a/Cocoa/English.lproj/Application.nib/classes.nib b/Cocoa/English.lproj/Application.nib/classes.nib index 589e2c5..cbfc87a 100644 --- a/Cocoa/English.lproj/Application.nib/classes.nib +++ b/Cocoa/English.lproj/Application.nib/classes.nib @@ -1,10 +1,18 @@ { IBClasses = ( { - ACTIONS = {showInfo = id; showPasteboard = id; showPrefs = id; }; + ACTIONS = { + emailDeveloper = id; + showAbout = id; + showInfo = id; + showPasteboard = id; + showPrefs = id; + visitSourceforge = id; + visitWebsite = id; + }; CLASS = ApplicationDelegate; LANGUAGE = ObjC; - OUTLETS = {forkTableView = NSTableView; openAuxView = NSView; }; + OUTLETS = {openPanelDelegate = OpenPanelDelegate; }; SUPERCLASS = NSObject; }, { @@ -15,12 +23,12 @@ findNext = id; findPrevious = id; findWithSelection = id; - myAction = id; openResources = id; openResourcesAsHex = id; openResourcesInTemplate = id; playSound = id; - revertResourceToSaved = id; + revertResource = id; + saveResource = id; scrollToSelection = id; showAbout = id; showCreateResourceSheet = id; @@ -29,17 +37,27 @@ showInfo = id; showPrefs = id; showSelectTemplateSheet = id; + useIconView = id; + useListView = id; }; CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + {CLASS = NSOutlineView; LANGUAGE = ObjC; SUPERCLASS = NSTableView; }, { - CLASS = OpenFileDataSource; + ACTIONS = {addFork = id; removeFork = id; }; + CLASS = OpenPanelDelegate; LANGUAGE = ObjC; - OUTLETS = {forkTableView = NSTableView; }; + OUTLETS = { + addForkButton = NSButton; + forkTableView = NSTableView; + openPanelAccessoryView = NSView; + removeForkButton = NSButton; + }; SUPERCLASS = NSObject; }, + {CLASS = OutlineSortView; LANGUAGE = ObjC; SUPERCLASS = NSOutlineView; }, { ACTIONS = { clear = id; diff --git a/Cocoa/English.lproj/Application.nib/info.nib b/Cocoa/English.lproj/Application.nib/info.nib index e7da567..ffbb676 100644 --- a/Cocoa/English.lproj/Application.nib/info.nib +++ b/Cocoa/English.lproj/Application.nib/info.nib @@ -3,22 +3,25 @@ IBDocumentLocation - 264 129 383 248 0 0 1024 746 + 75 727 356 240 0 0 1600 1002 IBEditorPositions 246 - 344 386 340 222 0 0 1024 746 + 569 580 520 175 0 0 1600 1002 29 - 22 682 347 44 0 0 1024 746 + 66 658 366 44 0 0 1600 1002 IBFramework Version - 326.0 + 446.1 + IBLockedObjects + + IBOldestOS + 1 IBOpenObjects - 246 29 IBSystem Version - 7A202 + 8L127 diff --git a/Cocoa/English.lproj/Application.nib/keyedobjects.nib b/Cocoa/English.lproj/Application.nib/keyedobjects.nib new file mode 100644 index 0000000..97761a8 Binary files /dev/null and b/Cocoa/English.lproj/Application.nib/keyedobjects.nib differ diff --git a/Cocoa/English.lproj/Application.nib/objects.nib b/Cocoa/English.lproj/Application.nib/objects.nib index 5cd86ba..5525d74 100644 Binary files a/Cocoa/English.lproj/Application.nib/objects.nib and b/Cocoa/English.lproj/Application.nib/objects.nib differ diff --git a/Cocoa/English.lproj/CreateResourceSheet.nib/info.nib b/Cocoa/English.lproj/CreateResourceSheet.nib/info.nib index 1733bed..439dda9 100644 --- a/Cocoa/English.lproj/CreateResourceSheet.nib/info.nib +++ b/Cocoa/English.lproj/CreateResourceSheet.nib/info.nib @@ -6,7 +6,13 @@ 58 46 387 357 0 0 832 602 IBFramework Version 326.0 + IBOldestOS + 3 + IBOpenObjects + + 20 + IBSystem Version - 7A202 + 7A179 diff --git a/Cocoa/English.lproj/CreateResourceSheet.nib/keyedobjects.nib b/Cocoa/English.lproj/CreateResourceSheet.nib/keyedobjects.nib new file mode 100644 index 0000000..630827c Binary files /dev/null and b/Cocoa/English.lproj/CreateResourceSheet.nib/keyedobjects.nib differ diff --git a/Cocoa/English.lproj/Credits.rtf b/Cocoa/English.lproj/Credits.rtf new file mode 100644 index 0000000..ae0bf93 --- /dev/null +++ b/Cocoa/English.lproj/Credits.rtf @@ -0,0 +1,30 @@ +{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410 +{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;\f1\fswiss\fcharset77 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\b\fs24 \cf0 Lead Developer: +\f1\b0 \ + Nicholas Shanks\ +\ + +\f0\b Developers: +\f1\b0 \ + Uli Kusterer\ +\ + +\f0\b Other Contributors: +\f1\b0 \ + Thomas Castiglione\ + Philippe Devallois\ + Mike Margolis\ + Lane Roathe\ + Jeffrey Seibert\ +\ + +\f0\b Wanted: +\f1\b0 \ + Plug-in editors for resource types.\ + Translations for any language.\ + Application, document & toolbar icons.\ +} \ No newline at end of file diff --git a/Cocoa/English.lproj/InfoPlist.strings b/Cocoa/English.lproj/InfoPlist.strings index 147c5d7..0c3a28f 100644 Binary files a/Cocoa/English.lproj/InfoPlist.strings and b/Cocoa/English.lproj/InfoPlist.strings differ diff --git a/Cocoa/English.lproj/InfoWindow.nib/classes.nib b/Cocoa/English.lproj/InfoWindow.nib/classes.nib index fdd3f5f..50a1c82 100644 --- a/Cocoa/English.lproj/InfoWindow.nib/classes.nib +++ b/Cocoa/English.lproj/InfoWindow.nib/classes.nib @@ -7,7 +7,7 @@ SUPERCLASS = NSObject; }, { - ACTIONS = {attributesChanged = id; nameChanged = id; }; + ACTIONS = {attributesChanged = id; }; CLASS = InfoWindowController; LANGUAGE = ObjC; OUTLETS = { @@ -21,7 +21,6 @@ }; SUPERCLASS = NSWindowController; }, - {CLASS = NSWindowController; LANGUAGE = ObjC; SUPERCLASS = NSResponder; }, {CLASS = SizeFormatter; LANGUAGE = ObjC; SUPERCLASS = NSNumberFormatter; } ); IBVersion = 1; diff --git a/Cocoa/English.lproj/InfoWindow.nib/info.nib b/Cocoa/English.lproj/InfoWindow.nib/info.nib index 03152d3..c641bdf 100644 --- a/Cocoa/English.lproj/InfoWindow.nib/info.nib +++ b/Cocoa/English.lproj/InfoWindow.nib/info.nib @@ -3,16 +3,12 @@ IBDocumentLocation - 142 473 384 240 0 0 1024 746 + 267 434 384 240 0 0 1600 1002 IBFramework Version - 326.0 - IBOpenObjects - - 35 - 5 - 50 - + 439.0 + IBOldestOS + 0 IBSystem Version - 7A202 + 8I127 diff --git a/Cocoa/English.lproj/InfoWindow.nib/keyedobjects.nib b/Cocoa/English.lproj/InfoWindow.nib/keyedobjects.nib new file mode 100644 index 0000000..d3496ea Binary files /dev/null and b/Cocoa/English.lproj/InfoWindow.nib/keyedobjects.nib differ diff --git a/Cocoa/English.lproj/InfoWindow.nib/objects.nib b/Cocoa/English.lproj/InfoWindow.nib/objects.nib index 674ae98..2288b5e 100644 Binary files a/Cocoa/English.lproj/InfoWindow.nib/objects.nib and b/Cocoa/English.lproj/InfoWindow.nib/objects.nib differ diff --git a/Cocoa/English.lproj/Localizable.strings b/Cocoa/English.lproj/Localizable.strings index b844bf1..167aaf7 100644 Binary files a/Cocoa/English.lproj/Localizable.strings and b/Cocoa/English.lproj/Localizable.strings differ diff --git a/Cocoa/English.lproj/ResourceDocument.nib/classes.nib b/Cocoa/English.lproj/ResourceDocument.nib/classes.nib index 25bf16d..6252961 100644 --- a/Cocoa/English.lproj/ResourceDocument.nib/classes.nib +++ b/Cocoa/English.lproj/ResourceDocument.nib/classes.nib @@ -2,23 +2,17 @@ IBClasses = ( {CLASS = AttributesFormatter; LANGUAGE = ObjC; SUPERCLASS = NSFormatter; }, {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, - { - CLASS = NameFormatter; - LANGUAGE = ObjC; - OUTLETS = {outlineView = NSOutlineView; }; - SUPERCLASS = NSFormatter; - }, { CLASS = OutlineViewDelegate; LANGUAGE = ObjC; OUTLETS = { attributesFormatter = NSFormatter; - nameFormatter = NSFormatter; sizeFormatter = NSNumberFormatter; window = NSWindow; }; SUPERCLASS = NSObject; }, + {CLASS = RKOutlineView; LANGUAGE = ObjC; SUPERCLASS = NSOutlineView; }, { CLASS = ResourceDataSource; LANGUAGE = ObjC; diff --git a/Cocoa/English.lproj/ResourceDocument.nib/info.nib b/Cocoa/English.lproj/ResourceDocument.nib/info.nib index bccdc20..aacca19 100644 --- a/Cocoa/English.lproj/ResourceDocument.nib/info.nib +++ b/Cocoa/English.lproj/ResourceDocument.nib/info.nib @@ -3,11 +3,15 @@ IBDocumentLocation - 71 141 530 549 0 0 1600 1002 + 579 557 418 241 0 0 1600 1002 IBFramework Version - 286.0 + 349.0 + IBLockedObjects + + 146 + IBSystem Version - 6F21 + 7H63 IBUserGuides CreateResourceSheet diff --git a/Cocoa/English.lproj/ResourceDocument.nib/objects.nib b/Cocoa/English.lproj/ResourceDocument.nib/objects.nib index b543e90..20d7ac0 100644 Binary files a/Cocoa/English.lproj/ResourceDocument.nib/objects.nib and b/Cocoa/English.lproj/ResourceDocument.nib/objects.nib differ diff --git a/Cocoa/Plug-Ins/Hex Editor/Categories/NSData-HexRepresentation.h b/Cocoa/Plug-Ins/Hex Editor/Categories/NSData-HexRepresentation.h new file mode 100644 index 0000000..5c39092 --- /dev/null +++ b/Cocoa/Plug-Ins/Hex Editor/Categories/NSData-HexRepresentation.h @@ -0,0 +1,11 @@ +#import + +@interface NSData (RKHexRepresentationExtensions) +- (NSString *)hexRepresentation; +- (NSString *)asciiRepresentation; +- (NSString *)nonLossyAsciiRepresentation; +@end + +@interface NSString (RKHexConversionExtensions) +- (NSData *)dataFromHex; +@end \ No newline at end of file diff --git a/Cocoa/Plug-Ins/Hex Editor/Categories/NSData-HexRepresentation.m b/Cocoa/Plug-Ins/Hex Editor/Categories/NSData-HexRepresentation.m new file mode 100644 index 0000000..449b87f --- /dev/null +++ b/Cocoa/Plug-Ins/Hex Editor/Categories/NSData-HexRepresentation.m @@ -0,0 +1,95 @@ +#import "NSData-HexRepresentation.h" + +@implementation NSData (RKHexRepresentationExtensions) + +- (NSString *)hexRepresentation +{ + int currentByte = 0, dataLength = [self length]; + char buffer[dataLength*3 -1], hex1, hex2; + char *bytes = (char *) [self bytes]; + + // return empty string if no data + if(dataLength == 0) return [NSString string]; + + // calculate bytes + for(currentByte = 0; currentByte < dataLength; currentByte++) + { + hex1 = bytes[currentByte]; + hex2 = bytes[currentByte]; + hex1 >>= 4; + hex1 &= 0x0F; + hex2 &= 0x0F; + hex1 += (hex1 < 10)? 0x30 : 0x37; + hex2 += (hex2 < 10)? 0x30 : 0x37; + + buffer[currentByte*3] = hex1; + buffer[currentByte*3 +1] = hex2; + buffer[currentByte*3 +2] = 0x20; + } + + return [NSString stringWithCString:buffer length:(dataLength*3 -1)]; +} + +- (NSString *)asciiRepresentation +{ + int currentByte = 0, dataLength = [self length]; + char buffer[dataLength]; + char *bytes = (char *) [self bytes]; + + // calculate bytes + for(currentByte = 0; currentByte < dataLength; currentByte++) + { + if(bytes[currentByte] >= 0x20 && bytes[currentByte] < 0x7F) + buffer[currentByte] = bytes[currentByte]; + else buffer[currentByte] = 0x2E; // full stop + } + + return [NSString stringWithCString:buffer length:dataLength]; +} + +- (NSString *)nonLossyAsciiRepresentation +{ + int currentByte = 0, dataLength = [self length]; + char buffer[dataLength]; + char *bytes = (char *) [self bytes]; + + // calculate bytes + for(currentByte = 0; currentByte < dataLength; currentByte++) + { + if(bytes[currentByte] > 0x20) // doesn't check for < 0x7F + buffer[currentByte] = bytes[currentByte]; +// else if(bytes[currentByte] == 0x20) +// buffer[currentByte] = 0xCA; // nbsp to stop maligned wraps - doesn't work :( + else buffer[currentByte] = 0x2E; // full stop + } + + return [NSString stringWithCString:buffer length:dataLength]; +} + +@end + +@implementation NSString (RKHexConversionExtensions) + +- (NSData *)dataFromHex +{ + unsigned long actualBytesEncoded = 0; + unsigned long maxBytesEncoded = floor([self cStringLength] / 2.0); + const char *bytes = [self cString]; + char *buffer = (char *) malloc(maxBytesEncoded); + signed char hex1, hex2; + int i; + + for(i = 0; i < maxBytesEncoded * 2;) + { + hex1 = bytes[i]; + hex2 = bytes[i+1]; + hex1 -= (hex1 < 'A')? 0x30 : ((hex1 < 'a')? 0x37 : 0x57); // 0-9 < A-Z < a-z + hex2 -= (hex2 < 'A')? 0x30 : ((hex2 < 'a')? 0x37 : 0x57); + if(hex1 & 0xF0 || hex2 & 0xF0) { i++; continue; } // invalid character found, move forward one byte and try again + buffer[actualBytesEncoded++] = (hex1 << 4) + hex2; + i += 2; + } + return [NSData dataWithBytesNoCopy:buffer length:actualBytesEncoded freeWhenDone:YES]; +} + +@end \ No newline at end of file diff --git a/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/classes.nib b/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/classes.nib index e40a0f2..9c91d31 100644 --- a/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/classes.nib +++ b/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/classes.nib @@ -1,6 +1,19 @@ { IBClasses = ( - {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + {CLASS = AsciiTextView; LANGUAGE = ObjC; SUPERCLASS = HexEditorTextView; }, + { + ACTIONS = { + copyASCII = id; + copyHex = id; + pasteAsASCII = id; + pasteAsHex = id; + pasteAsUnicode = id; + pasteFromHex = id; + }; + CLASS = FirstResponder; + LANGUAGE = ObjC; + SUPERCLASS = NSObject; + }, { CLASS = HexEditorDelegate; LANGUAGE = ObjC; @@ -13,18 +26,20 @@ }; SUPERCLASS = NSObject; }, + {CLASS = HexEditorTextView; LANGUAGE = ObjC; SUPERCLASS = NSTextView; }, + {CLASS = HexTextView; LANGUAGE = ObjC; SUPERCLASS = HexEditorTextView; }, { ACTIONS = {showFind = id; }; CLASS = HexWindowController; LANGUAGE = ObjC; OUTLETS = { ascii = NSTextView; - asciiScroll = NSScrollView; + copySubmenu = NSMenu; hex = NSTextView; hexDelegate = HexEditorDelegate; - hexScroll = NSScrollView; message = NSTextField; offset = NSTextView; + pasteSubmenu = NSMenu; }; SUPERCLASS = NSWindowController; } diff --git a/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/info.nib b/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/info.nib index b13ed8d..65439ea 100644 --- a/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/info.nib +++ b/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/info.nib @@ -3,14 +3,34 @@ IBDocumentLocation - 137 230 413 304 0 0 1024 746 + 76 233 356 240 0 0 1600 1002 + IBEditorPositions + + 24 + 239 475 202 137 0 0 1600 1002 + 8 + 30 493 203 99 0 0 1600 1002 + IBFramework Version - 286.0 + 446.1 + IBLockedObjects + + 45 + 47 + 48 + 42 + 44 + 46 + 43 + + IBOldestOS + 2 IBOpenObjects - 6 + 24 + 36 IBSystem Version - 6F21 + 8L127 diff --git a/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/keyedobjects.nib b/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/keyedobjects.nib new file mode 100644 index 0000000..343ebbf Binary files /dev/null and b/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/keyedobjects.nib differ diff --git a/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/objects.nib b/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/objects.nib deleted file mode 100644 index ef83cd0..0000000 Binary files a/Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/objects.nib and /dev/null differ diff --git a/Cocoa/Plug-Ins/Hex Editor/FindSheetController.m b/Cocoa/Plug-Ins/Hex Editor/FindSheetController.m index d448e7a..253960e 100644 --- a/Cocoa/Plug-Ins/Hex Editor/FindSheetController.m +++ b/Cocoa/Plug-Ins/Hex Editor/FindSheetController.m @@ -29,7 +29,7 @@ // enable/disable boxes [searchSelectionOnlyBox setEnabled:([(NSTextView *)[[sender window] firstResponder] rangeForUserTextChange].length != 0)]; - // set inital vales + // set inital values if( ![searchSelectionOnlyBox isEnabled] ) [searchSelectionOnlyBox setIntValue:0]; // show sheet diff --git a/Cocoa/Plug-Ins/Hex Editor/HexEditorDelegate.h b/Cocoa/Plug-Ins/Hex Editor/HexEditorDelegate.h index 18b4dda..e6270ca 100644 --- a/Cocoa/Plug-Ins/Hex Editor/HexEditorDelegate.h +++ b/Cocoa/Plug-Ins/Hex Editor/HexEditorDelegate.h @@ -2,33 +2,34 @@ #import "ResKnifeResourceProtocol.h" -@class HexWindowController; +@class HexWindowController, HexTextView, AsciiTextView; @interface HexEditorDelegate : NSObject { - IBOutlet HexWindowController *controller; - IBOutlet NSTextView *ascii; - IBOutlet NSTextView *hex; + IBOutlet HexWindowController *controller; IBOutlet NSTextView *offset; - IBOutlet NSTextField *message; + IBOutlet HexTextView *hex; + IBOutlet AsciiTextView *ascii; + IBOutlet NSTextField *message; - BOOL editedLow; + BOOL editedLow; + NSRange rangeForUserTextChange; } +/* REMOVE THESE WHEN I.B. IS FIXED */ +- (void)setHex:(id)newView; +- (void)setAscii:(id)newView; +/* END REMOVE MARKER */ + +- (void)viewDidScroll:(NSNotification *)notification; - (NSString *)offsetRepresentation:(NSData *)data; -- (NSString *)hexRepresentation:(NSData *)data; -- (NSString *)asciiRepresentation:(NSData *)data; -- (NSString *)hexToAscii:(NSData *)data; - -- (NSRange)byteRangeFromHexRange:(NSRange)hexRange; -- (NSRange)hexRangeFromByteRange:(NSRange)byteRange; -- (NSRange)byteRangeFromAsciiRange:(NSRange)asciiRange; -- (NSRange)asciiRangeFromByteRange:(NSRange)byteRange; +- (HexWindowController *)controller; - (NSTextView *)hex; - (NSTextView *)ascii; - (BOOL)editedLow; - (void)setEditedLow:(BOOL)flag; +- (NSRange)rangeForUserTextChange; @end \ No newline at end of file diff --git a/Cocoa/Plug-Ins/Hex Editor/HexEditorDelegate.m b/Cocoa/Plug-Ins/Hex Editor/HexEditorDelegate.m index 7931cc8..12cc40a 100644 --- a/Cocoa/Plug-Ins/Hex Editor/HexEditorDelegate.m +++ b/Cocoa/Plug-Ins/Hex Editor/HexEditorDelegate.m @@ -1,22 +1,43 @@ #import "HexEditorDelegate.h" #import "HexWindowController.h" - -/* Ideas, method names, and occasionally code stolen from HexEditor by Raphael Sebbe http://raphaelsebbe.multimania.com/ */ +#import "HexTextView.h" @implementation HexEditorDelegate - (id)init { self = [super init]; - if( !self ) return nil; + if(!self) return nil; editedLow = NO; return self; } +- (void)awakeFromNib +{ + // - MOVED TO HEX WINDOW CONTROLLER DUE TO BUG IN IB MEANING OFFSET, HEX AND ASCII AREN'T SET AT THIS TIME + + // notify me when a view scrolls so I can update the other two +// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[offset enclosingScrollView] contentView]]; +// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[hex enclosingScrollView] contentView]]; +// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[ascii enclosingScrollView] contentView]]; +} + +/* REMOVE THESE WHEN I.B. IS FIXED */ +- (void)setHex:(id)newView +{ + hex = newView; +} + +- (void)setAscii:(id)newView +{ + ascii = newView; +} +/* END REMOVE MARKER */ + /* data re-representation methods */ -- (NSString *)offsetRepresentation:(NSData *)data; +- (NSString *)offsetRepresentation:(NSData *)data { int dataLength = [data length], bytesPerRow = [controller bytesPerRow]; int rows = (dataLength / bytesPerRow) + ((dataLength % bytesPerRow)? 1:0); @@ -29,122 +50,30 @@ return representation; } -- (NSString *)hexRepresentation:(NSData *)data; -{ - int currentByte = 0, dataLength = [data length], bytesPerRow = [controller bytesPerRow]; - int rows = (dataLength / bytesPerRow) + ((dataLength % bytesPerRow)? 1:0); - char buffer[bytesPerRow*3 +1], hex1, hex2; - char *bytes = (char *) [data bytes]; - NSMutableString *representation = [NSMutableString string]; - int row; - - // calculate bytes - for( row = 0; row < rows; row++ ) - { - int addr; - - for( addr = 0; addr < bytesPerRow; addr++ ) - { - if( currentByte < dataLength ) - { - hex1 = bytes[currentByte]; - hex2 = bytes[currentByte]; - hex1 >>= 4; - hex1 &= 0x0F; - hex2 &= 0x0F; - hex1 += (hex1 < 10)? 0x30 : 0x37; - hex2 += (hex2 < 10)? 0x30 : 0x37; - - buffer[addr*3] = hex1; - buffer[addr*3 +1] = hex2; - buffer[addr*3 +2] = 0x20; - - // advance current byte - currentByte++; - } - else - { - buffer[addr*3] = 0x00; - break; - } - } - - // clear last byte on line - buffer[bytesPerRow*3] = 0x00; - - // append buffer to representation - [representation appendString:[NSString stringWithCString:buffer]]; - } - - return representation; -} - -- (NSString *)asciiRepresentation:(NSData *)data; -{ - int currentByte = 0, dataLength = [data length], bytesPerRow = [controller bytesPerRow]; - int rows = (dataLength / bytesPerRow) + ((dataLength % bytesPerRow)? 1:0); - char buffer[bytesPerRow +1]; - char *bytes = (char *) [data bytes]; - NSMutableString *representation = [NSMutableString string]; - int row; - - // calculate bytes - for( row = 0; row < rows; row++ ) - { - int addr; - - for( addr = 0; addr < bytesPerRow; addr++ ) - { - if( currentByte < dataLength ) - { - if( bytes[currentByte] > 0x20 && bytes[currentByte] < 0x7F ) - buffer[addr] = bytes[currentByte]; - else if( bytes[currentByte] == 0x20 ) - buffer[addr] = 0xCA; // nbsp to stop maligned wraps - else buffer[addr] = 0x2E; // full stop - - // advance current byte - currentByte++; - } - else - { - buffer[addr] = 0x00; - break; - } - } - - // clear last byte on line - buffer[bytesPerRow] = 0x00; - - // append buffer to representation - [representation appendString:[NSString stringWithCString:buffer]]; - } - - return representation; -} - -- (NSString *)hexToAscii:(NSData *)data -{ - NSString *result; - unsigned long bytesEncoded = ([data length] + 1) / 3; - char *buffer = (char *) malloc( bytesEncoded ), hex1, hex2; - int i; - - for( i = 0; i < bytesEncoded; i++ ) - { - hex1 = ((char *)[data bytes])[3*i]; - hex2 = ((char *)[data bytes])[3*i+1]; - hex1 -= (hex1 < 'A')? 0x30 : 0x37; - hex2 -= (hex2 < 'A')? 0x30 : 0x37; - buffer[i] = (hex1 << 4) + hex2; - } - result = [NSString stringWithCString:buffer length:bytesEncoded]; - free( buffer ); - return result; -} - /* delegation methods */ +- (void)viewDidScroll:(NSNotification *)notification +{ + // get object refs for increased speed + NSClipView *object = (NSClipView *) [notification object]; + NSClipView *offsetClip = [[offset enclosingScrollView] contentView]; + NSClipView *hexClip = [[hex enclosingScrollView] contentView]; + NSClipView *asciiClip = [[ascii enclosingScrollView] contentView]; + + // remove observer to stop myself from receiving bounds changed notifications (n.b. -setPostsBoundsChangedNotifications: only suspends them temporarilly - you get a unified bounds changed notification upon enabling it again and is designed for live resizing and such, so of no use here) + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSViewBoundsDidChangeNotification object:nil]; + + // when a view scrolls, update the other two + if( object != offsetClip ) [offsetClip setBoundsOrigin:[object bounds].origin]; + if( object != hexClip ) [hexClip setBoundsOrigin:[object bounds].origin]; + if( object != asciiClip ) [asciiClip setBoundsOrigin:[object bounds].origin]; + + // restore notifications + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:offsetClip]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:hexClip]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:asciiClip]; +} + - (NSRange)textView:(NSTextView *)textView willChangeSelectionFromCharacterRange:(NSRange)oldSelectedCharRange toCharacterRange:(NSRange)newSelectedCharRange { NSRange hexRange, asciiRange, byteRange = NSMakeRange(0,0); @@ -156,16 +85,14 @@ if( textView == hex ) // we're selecting hexadecimal { - byteRange = [self byteRangeFromHexRange:newSelectedCharRange]; - asciiRange = [self asciiRangeFromByteRange:byteRange]; + byteRange = [HexWindowController byteRangeFromHexRange:newSelectedCharRange]; + asciiRange = [HexWindowController asciiRangeFromByteRange:byteRange]; [ascii setSelectedRange:asciiRange]; } else if( textView == ascii ) // we're selecting ASCII { - byteRange = [self byteRangeFromAsciiRange:newSelectedCharRange]; - hexRange = [self hexRangeFromByteRange:byteRange]; - if( hexRange.length > 0 ) - hexRange.length -= 1; + byteRange = [HexWindowController byteRangeFromAsciiRange:newSelectedCharRange]; + hexRange = [HexWindowController hexRangeFromByteRange:byteRange]; [hex setSelectedRange:hexRange]; } @@ -178,36 +105,9 @@ return newSelectedCharRange; } -/* range conversion methods */ - -- (NSRange)byteRangeFromHexRange:(NSRange)hexRange; +- (HexWindowController *)controller { - // valid for all window widths - NSRange byteRange = NSMakeRange(0,0); - byteRange.location = (hexRange.location / 3); - byteRange.length = (hexRange.length / 3) + ((hexRange.length % 3)? 1:0); - return byteRange; -} - -- (NSRange)hexRangeFromByteRange:(NSRange)byteRange; -{ - // valid for all window widths - NSRange hexRange = NSMakeRange(0,0); - hexRange.location = (byteRange.location * 3); - hexRange.length = (byteRange.length * 3); - return hexRange; -} - -- (NSRange)byteRangeFromAsciiRange:(NSRange)asciiRange; -{ - // one-to-one mapping - return asciiRange; -} - -- (NSRange)asciiRangeFromByteRange:(NSRange)byteRange; -{ - // one-to-one mapping - return byteRange; + return controller; } - (NSTextView *)hex @@ -230,4 +130,17 @@ editedLow = flag; } +- (NSRange)rangeForUserTextChange +{ + // if editing hex, convert hex selection to byte selection + if( [[controller window] firstResponder] == hex ) + rangeForUserTextChange = [HexWindowController byteRangeFromHexRange:[hex rangeForUserTextChange]]; + + // if editing ascii, convert ascii selection to byte selection + else if( [[controller window] firstResponder] == ascii ) + rangeForUserTextChange = [HexWindowController byteRangeFromAsciiRange:[ascii rangeForUserTextChange]]; + + return rangeForUserTextChange; +} + @end \ No newline at end of file diff --git a/Cocoa/Plug-Ins/Hex Editor/HexTextView.h b/Cocoa/Plug-Ins/Hex Editor/HexTextView.h index 4542d95..d0196e9 100644 --- a/Cocoa/Plug-Ins/Hex Editor/HexTextView.h +++ b/Cocoa/Plug-Ins/Hex Editor/HexTextView.h @@ -2,16 +2,18 @@ #import "HexEditorDelegate.h" #import "HexWindowController.h" -@interface HexTextView : NSTextView -{ -} -- (IBAction)clear:(id)sender; +@interface HexEditorTextView : NSTextView +- (IBAction)copyASCII:(id)sender; +- (IBAction)copyHex:(id)sender; - (IBAction)pasteAsASCII:(id)sender; - (IBAction)pasteAsHex:(id)sender; - (IBAction)pasteAsUnicode:(id)sender; +- (IBAction)clear:(id)sender; - (void)editData:(NSData *)data replaceBytesInRange:(NSRange)range withData:(NSData *)newData; @end -@interface NSTextView (HexTextView) -- (void)swapForHexTextView; -@end \ No newline at end of file +@interface HexTextView : HexEditorTextView +@end + +@interface AsciiTextView : HexEditorTextView +@end diff --git a/Cocoa/Plug-Ins/Hex Editor/HexTextView.m b/Cocoa/Plug-Ins/Hex Editor/HexTextView.m index b9fa792..42c5078 100644 --- a/Cocoa/Plug-Ins/Hex Editor/HexTextView.m +++ b/Cocoa/Plug-Ins/Hex Editor/HexTextView.m @@ -1,88 +1,10 @@ #import "HexTextView.h" +#import "NSData-HexRepresentation.h" -@implementation HexTextView - -- (void)drawRect:(NSRect)rect -{ - [super drawRect:rect]; -/* if( [[self window] isKeyWindow] && [[self window] firstResponder] == self ) - { - NSSetFocusRingStyle( NSFocusRingOnly ); - [self setKeyboardFocusRingNeedsDisplayInRect:rect]; - }*/ - -/* [super drawRect:rect]; - if( [[self window] isKeyWindow] ) - { - NSResponder *responder = [[self window] firstResponder]; - if( [responder isKindOfClass:[NSView class]] && [(NSView *)responder isDescendantOf:self]) - { - NSSetFocusRingStyle( NSFocusRingOnly ); - NSRectFill( rect ); - } - } - [self setKeyboardFocusRingNeedsDisplayInRect:rect];*/ -} - -- (void)setSelectedRange:(NSRange)charRange affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)flag -{ - NSRange newRange = charRange; - - // select whole bytes at a time (only if selecting in hex!) - if( self == (id) [[self delegate] hex] ) - { - // move selection offset to beginning of byte - newRange.location -= (charRange.location % 3); - newRange.length += (charRange.location % 3); - - // set selection length to whole number of bytes - if( charRange.length != 0 ) - newRange.length -= (newRange.length % 3) -2; - else newRange.length = 0; - - // move insertion point to next byte if needs be - if( newRange.location == charRange.location -1 && newRange.length == 0 ) - newRange.location += 3; - } - - // select return character if selecting ascii -#if( 0 ) // no longer necessary as there's a one-to-one for ascii, and the thing wraps properly instead :-) - else if( self == (id) [[self delegate] ascii] ) - { - // if ascii selection goes up to sixteenth byte on last line, select return character too - if( (charRange.length + charRange.location) % 17 == 16) - { - // if selection is zero bytes long, move insertion point to character after return - if( charRange.length == 0 ) - { - // if moving back from first byte of line to previous line, skip return char - NSRange selected = [self selectedRange]; - if( (selected.length + selected.location) % 17 == 0 ) - newRange.location -= 1; - else newRange.location += 1; - } - else newRange.length += 1; - } - } -#endif - - // call the superclass to update the selection - [super setSelectedRange:newRange affinity:affinity stillSelecting:NO]; -} +@implementation HexEditorTextView /* NSText overrides */ -- (BOOL)validateMenuItem:(NSMenuItem *)item -{ - // paste submenu - if( [item action] == @selector(paste:) ) - { - NSMenu *editMenu = [[item menu] supermenu]; - [[editMenu itemAtIndex:[editMenu indexOfItemWithSubmenu:[item menu]]] setEnabled:[super validateMenuItem:item]]; - } - return [super validateMenuItem:item]; -} - - (IBAction)cut:(id)sender { [self copy:sender]; @@ -91,117 +13,91 @@ - (IBAction)copy:(id)sender { - NSRange selection = [self rangeForUserTextChange], byteSelection; + NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange]; NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard]; - // get selection range - if( self == (id) [[self delegate] hex] ) - byteSelection = [[self delegate] byteRangeFromHexRange:selection]; - else if( self == (id) [[self delegate] ascii] ) - byteSelection = [[self delegate] byteRangeFromAsciiRange:selection]; - else - { - NSLog( @"Pasting text into illegal object: %@", self ); - return; - } - [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self]; - [pb setData:[[[[self window] windowController] data] subdataWithRange:byteSelection] forType:NSStringPboardType]; + [pb setData:[[(HexWindowController *)[[self window] windowController] data] subdataWithRange:selection] forType:NSStringPboardType]; +} + +- (IBAction)copyASCII:(id)sender +{ + +} + +- (IBAction)copyHex:(id)sender +{ + } - (IBAction)paste:(id)sender { - // be 'smart' - determine if the pasted text is in hex format, such as "5F 3E 04 8E" or ascii. - // what about unicode? should I paste "00 63 00 64" as "63 64" ("Paste As ASCII" submenu item)? - - NSRange selection = [self rangeForUserTextChange], byteSelection; + NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange]; NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard]; - // get selection range - if( self == (id) [[self delegate] hex] ) - byteSelection = [[self delegate] byteRangeFromHexRange:selection]; - else if( self == (id) [[self delegate] ascii] ) - byteSelection = [[self delegate] byteRangeFromAsciiRange:selection]; - else - { - NSLog( @"Pasting text into illegal object: %@", self ); - return; - } - - if( [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] ) - [self editData:[[[self window] windowController] data] replaceBytesInRange:byteSelection withData:[pb dataForType:NSStringPboardType]]; + // pastes data as it is on the clipboard + if([pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]) + [self editData:[[[self window] windowController] data] replaceBytesInRange:selection withData:[pb dataForType:NSStringPboardType]]; } - (IBAction)pasteAsASCII:(id)sender { - NSRange selection = [self rangeForUserTextChange], byteSelection; + NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange]; NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard]; - // get selection range - if( self == (id) [[self delegate] hex] ) - byteSelection = [[self delegate] byteRangeFromHexRange:selection]; - else if( self == (id) [[self delegate] ascii] ) - byteSelection = [[self delegate] byteRangeFromAsciiRange:selection]; - else + // converts whatever string encoding is on the clipboard to the default C string encoding, then pastes + if([pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]) { - NSLog( @"Pasting text into illegal object: %@", self ); - return; - } - - if( [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] ) - [self editData:[[[self window] windowController] data] replaceBytesInRange:byteSelection withData:[pb dataForType:NSStringPboardType]]; -} - -- (IBAction)pasteAsHex:(id)sender -{ - NSRange selection = [self rangeForUserTextChange], byteSelection; - NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard]; - - // get selection range - if( self == (id) [[self delegate] hex] ) - byteSelection = [[self delegate] byteRangeFromHexRange:selection]; - else if( self == (id) [[self delegate] ascii] ) - byteSelection = [[self delegate] byteRangeFromAsciiRange:selection]; - else - { - NSLog( @"Pasting text into illegal object: %@", self ); - return; - } - - if( [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] ) - { - NSString *hexString = [[self delegate] hexRepresentation:[pb dataForType:NSStringPboardType]]; - [self editData:[[[self window] windowController] data] replaceBytesInRange:byteSelection withData:[NSData dataWithBytes:[hexString cString] length:[hexString cStringLength]]]; + NSData *asciiData = [[pb stringForType:NSStringPboardType] dataUsingEncoding:[NSString defaultCStringEncoding] allowLossyConversion:YES]; + [self editData:[[[self window] windowController] data] replaceBytesInRange:selection withData:asciiData]; } } - (IBAction)pasteAsUnicode:(id)sender { - NSRange selection = [self rangeForUserTextChange], byteSelection; + NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange]; NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard]; - // get selection range - if( self == (id) [[self delegate] hex] ) - byteSelection = [[self delegate] byteRangeFromHexRange:selection]; - else if( self == (id) [[self delegate] ascii] ) - byteSelection = [[self delegate] byteRangeFromAsciiRange:selection]; - else + // converts whatever string encoding is on the clipboard to Unicode, strips off the byte order mark, then pastes + if([pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]) { - NSLog( @"Pasting text into illegal object: %@", self ); - return; + NSMutableData *unicodeData = [[[pb stringForType:NSStringPboardType] dataUsingEncoding:NSUnicodeStringEncoding] mutableCopy]; + if(*((unsigned short *)[unicodeData mutableBytes]) == 0xFEFF || *((unsigned short *)[unicodeData mutableBytes]) == 0xFFFE) + [unicodeData replaceBytesInRange:NSMakeRange(0,2) withBytes:NULL length:0]; + [self editData:[[[self window] windowController] data] replaceBytesInRange:selection withData:unicodeData]; } +} + +- (IBAction)pasteAsHex:(id)sender +{ + NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange]; + NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard]; - if( [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] ) + // converts whatever data is on the clipboard to a hex representation of that data, then pastes + if([pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]) { - NSData *unicodeData = [[NSString stringWithUTF8String:[[pb dataForType:NSStringPboardType] bytes]] dataUsingEncoding:NSUnicodeStringEncoding]; - [self editData:[[[self window] windowController] data] replaceBytesInRange:byteSelection withData:unicodeData]; + NSData *hexData = [[[pb dataForType:NSStringPboardType] hexRepresentation] dataUsingEncoding:[NSString defaultCStringEncoding] allowLossyConversion:YES]; + [self editData:[[[self window] windowController] data] replaceBytesInRange:selection withData:hexData]; + } +} + +- (IBAction)pasteFromHex:(id)sender +{ + NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange]; + NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard]; + + // converts hex data present on the clipboard to the bytes they represent, then pastes + if([pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]) + { + NSData *binaryData = [[pb stringForType:NSStringPboardType] dataFromHex]; + [self editData:[[[self window] windowController] data] replaceBytesInRange:selection withData:binaryData]; } } - (IBAction)clear:(id)sender { NSRange selection = [self rangeForUserTextChange]; - if( selection.length > 0 ) + if(selection.length > 0) [self delete:sender]; } @@ -212,20 +108,13 @@ /* Dragging routines */ -- (unsigned int)_insertionGlyphIndexForDrag:(id )sender -{ - unsigned int charIndex = [super _insertionGlyphIndexForDrag:sender]; - if( self == [[self delegate] hex] ) - charIndex -= charIndex % 3; - return charIndex; -} - - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal { -/* if( isLocal ) return NSDragOperationEvery; + return NSDragOperationNone; +/* if(isLocal) return NSDragOperationEvery; else return NSDragOperationCopy; -*/ return NSDragOperationCopy | NSDragOperationMove | NSDragOperationGeneric; -} + return NSDragOperationCopy | NSDragOperationMove | NSDragOperationGeneric; +*/} static NSRange draggedRange; @@ -236,7 +125,7 @@ static NSRange draggedRange; - (void)draggedImage:(NSImage *)image endedAt:(NSPoint)point operation:(NSDragOperation)operation { -/* if( operation == NSDragOperationMove ) + if(operation == NSDragOperationMove) { NSRange selection = [self rangeForUserTextChange]; [self editData:[[[self window] windowController] data] replaceBytesInRange:draggedRange withData:[NSData data]]; @@ -244,16 +133,16 @@ static NSRange draggedRange; // set the new selection/insertion point selection.location -= draggedRange.length; selection.length = draggedRange.length; - if( selection.location > draggedRange.location ) + if(selection.location > draggedRange.location) selection.location -= draggedRange.length; [self setSelectedRange:selection]; - }*/ + } } - (NSDragOperation)draggingUpdated:(id )sender { [super draggingUpdated:sender]; // ignore return value - if( [sender draggingSource] == [[self delegate] hex] || [sender draggingSource] == [[self delegate] ascii] ) + if([sender draggingSource] == [[self delegate] hex] || [sender draggingSource] == [[self delegate] ascii]) return NSDragOperationMove; else return NSDragOperationCopy; } @@ -263,18 +152,18 @@ static NSRange draggedRange; // get the insertion point location NSPasteboard *pb = [sender draggingPasteboard]; NSData *pastedData = [pb dataForType:NSStringPboardType]; - int charIndex = [self _insertionGlyphIndexForDrag:sender]; + unsigned int charIndex = (unsigned int) [self _insertionGlyphIndexForDrag:sender]; NSRange selection; // convert hex string to data - if( [sender draggingSource] == [[self delegate] hex] ) - pastedData = [[[self delegate] hexToAscii:pastedData] dataUsingEncoding:NSASCIIStringEncoding]; + if([sender draggingSource] == [[self delegate] hex]) + pastedData = [[[[NSString alloc] initWithData:pastedData encoding:[NSString defaultCStringEncoding]] autorelease] dataFromHex]; - if( [sender draggingSource] == [[self delegate] hex] || [sender draggingSource] == [[self delegate] ascii] ) -// if( operation == NSDragOperationMove ) + if([sender draggingSource] == [[self delegate] hex] || [sender draggingSource] == [[self delegate] ascii]) +// if(operation == NSDragOperationMove) { NSRange deleteRange = draggedRange; - if( self == [[self delegate] hex] ) + if(self == (id) [[self delegate] hex]) { deleteRange.location /= 3; deleteRange.length += 1; @@ -285,12 +174,12 @@ static NSRange draggedRange; [self editData:[[[self window] windowController] data] replaceBytesInRange:deleteRange withData:[NSData data]]; // compensate for already removing the dragged data - if( charIndex > draggedRange.location ) + if(charIndex > draggedRange.location) charIndex -= draggedRange.length; } // insert data at insertion point - if( self == [[self delegate] hex] ) charIndex /= 3; + if(self == (id) [[self delegate] hex]) charIndex /= 3; [self editData:[[[self window] windowController] data] replaceBytesInRange:NSMakeRange(charIndex,0) withData:pastedData]; // set the new selection/insertion point @@ -311,39 +200,35 @@ static NSRange draggedRange; - (void)insertText:(NSString *)string { - NSRange selection = [self rangeForUserTextChange], byteSelection; + NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange]; NSMutableData *data = [[[[self window] windowController] data] mutableCopy]; NSData *replaceData = [NSData dataWithBytes:[string cString] length:[string cStringLength]]; - // get selection range - if( self == (id) [[self delegate] hex] ) - byteSelection = [[self delegate] byteRangeFromHexRange:selection]; - else if( self == (id) [[self delegate] ascii] ) - byteSelection = [[self delegate] byteRangeFromAsciiRange:selection]; - else - { - NSLog( @"Inserting text into illegal object: %@", self ); - return; - } - - if( self == (id) [[self delegate] hex] ) + if(self == (id) [[self delegate] hex]) { int i; // bug: iteration through each character in string is broken, paste not yet mapped to this function - for( i = 0; i < [string cStringLength]; i++ ) + for(i = 0; i < [string cStringLength]; i++) { char typedChar = [string characterAtIndex:i]; - if( typedChar >= 0x30 && typedChar <= 0x39 ) typedChar -= 0x30; // 0 to 9 - else if( typedChar >= 0x41 && typedChar <= 0x46 ) typedChar -= 0x37; // A to F - else if( typedChar >= 0x61 && typedChar <= 0x66 ) typedChar -= 0x57; // a to f + if(typedChar >= 0x30 && typedChar <= 0x39) typedChar -= 0x30; // 0 to 9 + else if(typedChar >= 0x41 && typedChar <= 0x46) typedChar -= 0x37; // A to F + else if(typedChar >= 0x61 && typedChar <= 0x66) typedChar -= 0x57; // a to f else return; - if( [[self delegate] editedLow] ) // edited low bits already + if(![[self delegate] editedLow]) // editing low bits + { + // put typed char into low bits + typedChar &= 0x0F; + replaceData = [NSData dataWithBytes:&typedChar length:1]; + [[self delegate] setEditedLow:YES]; + } + else // edited low bits already { // select & retrieve old byte so it gets replaced char prevByte; - byteSelection = NSMakeRange(byteSelection.location -1, 1); - [data getBytes:&prevByte range:byteSelection]; + selection = NSMakeRange(selection.location -1, 1); + [data getBytes:&prevByte range:selection]; // shift typed char into high bits and add new low char prevByte <<= 4; // store high bit @@ -351,89 +236,61 @@ static NSRange draggedRange; replaceData = [NSData dataWithBytes:&prevByte length:1]; [[self delegate] setEditedLow:NO]; } - else // editing low bits - { - // put typed char into low bits - typedChar &= 0x0F; - replaceData = [NSData dataWithBytes:&typedChar length:1]; - [[self delegate] setEditedLow:YES]; - } } } // replace bytes (updates views implicitly, records an undo) - [self editData:data replaceBytesInRange:byteSelection withData:replaceData]; + [self editData:data replaceBytesInRange:selection withData:replaceData]; [data release]; - // set the new selection/insertion point - byteSelection.location++; - byteSelection.length = 0; - if( self == (id) [[self delegate] hex] ) - selection = [[self delegate] hexRangeFromByteRange:byteSelection]; - else if( self == (id) [[self delegate] ascii] ) - selection = [[self delegate] asciiRangeFromByteRange:byteSelection]; + // set the new selection (insertion point) + selection.location++; + selection.length = 0; + if(self == (id) [[self delegate] hex]) selection = [HexWindowController hexRangeFromByteRange:selection]; + if(self == (id) [[self delegate] ascii]) selection = [HexWindowController asciiRangeFromByteRange:selection]; [self setSelectedRange:selection]; } - (IBAction)deleteBackward:(id)sender { - NSRange selection = [self rangeForUserTextChange], byteSelection; + NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange]; NSMutableData *data = [[[[self window] windowController] data] mutableCopy]; - // get selection range - if( self == (id) [[self delegate] hex] ) - byteSelection = [[self delegate] byteRangeFromHexRange:selection]; - else if( self == (id) [[self delegate] ascii] ) - byteSelection = [[self delegate] byteRangeFromAsciiRange:selection]; - else - { - NSLog( @"Inserting text into illegal object: %@", self ); - return; - } - // adjust selection if is insertion point - if( byteSelection.length == 0 && selection.location > 0 ) + if(selection.length == 0 && selection.location > 0) { - byteSelection.location -= 1; - byteSelection.length = 1; + selection.location -= 1; + selection.length = 1; } // replace bytes (updates views implicitly) - [self editData:data replaceBytesInRange:byteSelection withData:[NSData data]]; + [self editData:data replaceBytesInRange:selection withData:[NSData data]]; [data release]; - // set the new selection/insertion point - if( selection.length == 0 ) + // set the new selection (insertion point) + if(selection.length == 0 && selection.location > 0) selection.location -= 1; else selection.length = 0; + if(self == (id) [[self delegate] hex]) selection = [HexWindowController hexRangeFromByteRange:selection]; + if(self == (id) [[self delegate] ascii]) selection = [HexWindowController asciiRangeFromByteRange:selection]; [self setSelectedRange:selection]; } - (IBAction)deleteForward:(id)sender { - NSRange selection = [self rangeForUserTextChange], byteSelection; + NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange]; NSMutableData *data = [[[[self window] windowController] data] mutableCopy]; - // get selection range - if( self == (id) [[self delegate] hex] ) - byteSelection = [[self delegate] byteRangeFromHexRange:selection]; - else if( self == (id) [[self delegate] ascii] ) - byteSelection = [[self delegate] byteRangeFromAsciiRange:selection]; - else - { - NSLog( @"Inserting text into illegal object: %@", self ); - return; - } - // adjust selection if is insertion point - if( byteSelection.length == 0 && selection.location < [[self string] length] -1 ) - byteSelection.length = 1; + if(selection.length == 0 && [self rangeForUserTextChange].location < [[self string] length] -1) + selection.length = 1; // replace bytes (updates views implicitly) - [self editData:data replaceBytesInRange:byteSelection withData:[NSData data]]; + [self editData:data replaceBytesInRange:selection withData:[NSData data]]; [data release]; // set the new selection/insertion point + selection = [self rangeForUserTextChange]; selection.length = 0; [self setSelectedRange:selection]; } @@ -458,20 +315,25 @@ static NSRange draggedRange; [self transpose:sender]; } +- (void)setSelectedRange:(NSRange)charRange affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag +{ + [super setSelectedRange:charRange affinity:affinity stillSelecting:NO]; +} + - (void)editData:(NSData *)data replaceBytesInRange:(NSRange)range withData:(NSData *)newBytes { // save data we're about to replace so we can restore it in an undo - NSRange newRange = NSMakeRange( range.location, [newBytes length] ); + NSRange newRange = NSMakeRange(range.location, [newBytes length]); NSMutableData *newData = [NSMutableData dataWithData:data]; NSMutableData *oldBytes = [[data subdataWithRange:range] retain]; // bug: memory leak, need to release somewhere (call -[NSInvocation retainArguments] instead?) // manipulate undo stack to concatenate multiple undos BOOL closeUndoGroup = NO; id undoStack = nil; // object of class _NSUndoStack - if( ![[[self window] undoManager] isUndoing] ) - undoStack = [[[self window] undoManager] _undoStack]; + if(![[[self window] undoManager] isUndoing]) + undoStack = (id) [[[self window] undoManager] _undoStack]; - if( undoStack && (int)[undoStack count] > 0 && [[[self window] undoManager] groupingLevel] == 0 ) + if(undoStack && (int)[undoStack count] > 0 && [[[self window] undoManager] groupingLevel] == 0) { [undoStack popUndoObject]; // pop endUndoGrouping item closeUndoGroup = YES; @@ -481,23 +343,162 @@ static NSRange draggedRange; [newData replaceBytesInRange:range withBytes:[newBytes bytes] length:[newBytes length]]; [[(HexWindowController *)[[self window] windowController] resource] setData:newData]; [self setSelectedRange:NSMakeRange(range.location + [newBytes length], 0)]; -// [[self window] setDocumentEdited:YES]; // moved to window controller's -resourceDataDidChange: notification method // record undo with new data object [[[[self window] undoManager] prepareWithInvocationTarget:self] editData:newData replaceBytesInRange:newRange withData:oldBytes]; [[[self window] undoManager] setActionName:NSLocalizedString(@"Typing", nil)]; - if( closeUndoGroup ) + if(closeUndoGroup) [[[self window] undoManager] endUndoGrouping]; -// NSLog( @"%@", undoStack ); +} + +@end + +@implementation HexTextView + +/*! +@method selectionRangeForProposedRange:granularity: +@abstract Adjusts the selection for insertion point and byte-selection +@author Nicholas Shanks +@created 2003-11-10 +*/ + +- (NSRange)selectionRangeForProposedRange:(NSRange)proposedCharRange granularity:(NSSelectionGranularity)granularity +{ + NSRange newRange = proposedCharRange; + + if(newRange.length == 0) + { + // set insertion point location + if(newRange.location % 3 == 1) newRange.location--; + if(newRange.location % 3 == 2) newRange.location++; + } + else + { + // select whole bytes at a time - bug: this doesn't quite work when selecting forwards one byte with the mouse + granularity = NSSelectByWord; + newRange.location -= (proposedCharRange.location % 3); + newRange.length += (proposedCharRange.location % 3); + + // set selection length to whole number of bytes + if(newRange.length > 0) + { + if(newRange.length % 3 == 0) newRange.length--; + if(newRange.length % 3 == 1) newRange.length++; + } + } + + return [super selectionRangeForProposedRange:newRange granularity:granularity]; +} +// also, lots of subclasser pasteboard support better to use than overriding copy: and paste:? see NSTextView.h + +- (void)setSelectedRange:(NSRange)newRange affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag +{ + if(newRange.length == 0 && stillSelectingFlag == NO) + { + // moving insertion point + if(newRange.location % 3 == 1) newRange.location += 2; + if(newRange.location % 3 == 2) newRange.location -= 2; + } + else if(stillSelectingFlag == NO && [[self window] firstResponder] == self) + { + NSRange oldRange = [self rangeForUserTextChange]; + + // selecting forwards + if(oldRange.location == newRange.location && oldRange.length != newRange.length) +// if(affinity == NSSelectionAffinityDownstream) + { + // selecting first byte + if(newRange.location % 3 == 0 && newRange.length == 1 && oldRange.length < newRange.length) + newRange.length += 1; + // deselecting first byte + if(newRange.location % 3 == 0 && newRange.length == 1 && oldRange.length > newRange.length) + newRange.length = 0; + // extending selection + else if(newRange.location % 3 == 0 && newRange.length % 3 == 0) newRange.length += 2; + + // reducing selection + else if(newRange.location % 3 == 0 && newRange.length % 3 == 1) newRange.length -= 2; + } + + // reducing forwards selection spanning multiple rows past start point + else if(newRange.location + newRange.length == oldRange.location && newRange.length % 3 == 1 && oldRange.length > 0) + { + // example: 00 00 00 00 00 00 FF FF => 00 00 00|FF FF FF 00 00 + // FF FF FF|00 00 00 00 00 => 00 00 00 00 00 00 00 00 + + newRange.location += 1; + newRange.length -= 2; + } + + // inverse of above + else if(oldRange.location + oldRange.length == newRange.location && newRange.length % 3 == 0 && oldRange.length > 0) + { + // example: 00 00 00|FF FF FF 00 00 => 00 00 00 00 00 00 FF FF + // 00 00 00 00 00 00 00 00 => FF FF FF|00 00 00 00 00 + + newRange.location += 1; + newRange.length -= 1; + } + + // reducing backwards selection spanning multiple rows past start point + else if(oldRange.location + oldRange.length == newRange.location && newRange.length % 3 == 1 && oldRange.length > 0) + { + // example: 00 00 00 00 00 00|FF FF => 00 00 00 00 00 00 00 00 + // FF FF FF 00 00 00 00 00 => 00 00 00 FF FF FF|00 00 + + newRange.location += 1; + newRange.length -= 2; + } + + // inverse of above + else if(newRange.location + newRange.length == oldRange.location && newRange.length % 3 == 0 && oldRange.length > 0) + { + // example: 00 00 00 00 00 00 00 00 => 00 00 00 00 00 00|FF FF + // 00 00 00 FF FF FF|00 00 => FF FF FF 00 00 00 00 00 + + newRange.location += 1; + newRange.length -= 2; + } + + // selecting backwards + else if(oldRange.location != newRange.location && oldRange.length != newRange.length) +// else if(affinity == NSSelectionAffinityDownstream) + { + // selecting first byte + if(newRange.location % 3 == 2 && newRange.length == 1) { newRange.location -= 2; + newRange.length += 1; } + // deselecting first byte + else if(newRange.location % 3 == 1 && newRange.length == 1) { newRange.location += 2; + newRange.length = 0; } + // extending selection + else if(newRange.location % 3 == 2 && newRange.length % 3 == 0) { newRange.location -= 2; + newRange.length += 2; } + // reducing selection + else if(newRange.location % 3 == 1 && newRange.length % 3 == 1) { newRange.location += 2; + newRange.length -= 2; } + } + } + + [super setSelectedRange:newRange affinity:affinity stillSelecting:stillSelectingFlag]; +} + +/*! +@method selectionRangeForProposedRange:granularity: +@abstract Puts insertion pointer between bytes during drag operation +@author Nicholas Shanks +@updated 2003-11-10 NGS: Changed algorithm. +*/ + +- (unsigned int)_insertionGlyphIndexForDrag:(id )sender +{ + unsigned int glyphIndex = (unsigned int) [super _insertionGlyphIndexForDrag:sender]; + if(glyphIndex % 3 == 1) glyphIndex--; + if(glyphIndex % 3 == 2) glyphIndex++; + return glyphIndex; } @end -@implementation NSTextView (HexTextView) +@implementation AsciiTextView -- (void)swapForHexTextView -{ - isa = [HexTextView class]; -} - -@end \ No newline at end of file +@end diff --git a/Cocoa/Plug-Ins/Hex Editor/HexWindowController.h b/Cocoa/Plug-Ins/Hex Editor/HexWindowController.h index 7097100..215bea9 100644 --- a/Cocoa/Plug-Ins/Hex Editor/HexWindowController.h +++ b/Cocoa/Plug-Ins/Hex Editor/HexWindowController.h @@ -8,22 +8,31 @@ #define kWindowStepWidthPerChar 28 #define kWindowStepCharsPerStep 1 +/*! +@class HexWindowController +@author Nicholas Shanks +@pending Add a category to NSString to convert from hex-formatted strings to NSData objects. +*/ + +/* Based on HexEdit by Bill Bumgardner, Lane Roath & myself: http://hexedit.sourceforge.net/ */ +/* Some ideas, method names, and occasionally code stolen from HexEditor by Raphael Sebbe: http://raphaelsebbe.multimania.com/ */ + @interface HexWindowController : NSWindowController { IBOutlet HexEditorDelegate *hexDelegate; - IBOutlet NSScrollView *asciiScroll; - IBOutlet NSScrollView *hexScroll; - IBOutlet NSTextView *ascii; - IBOutlet NSTextView *hex; - IBOutlet NSTextView *offset; - IBOutlet NSTextField *message; + IBOutlet NSTextView *offset; // these four should be phased out whenever possible + IBOutlet HexTextView *hex; // these four should be phased out whenever possible + IBOutlet AsciiTextView *ascii; // these four should be phased out whenever possible + IBOutlet NSTextField *message; // these four should be phased out whenever possible + IBOutlet NSMenu *copySubmenu; IBOutlet NSMenu *pasteSubmenu; - NSUndoManager *undoManager; id resource; id backup; - BOOL liveEdit; - int bytesPerRow; + + BOOL liveEdit; + int bytesPerRow; + NSUndoManager *undoManager; } // conform to the ResKnifePluginProtocol with the inclusion of these methods @@ -34,11 +43,10 @@ // save sheet methods - (void)saveSheetDidClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo; -- (void)saveResource; -- (void)revertResource; +- (IBAction)saveResource:(id)sender; +- (IBAction)revertResource:(id)sender; // normal methods -- (void)viewDidScroll:(NSNotification *)notification; - (void)resourceNameDidChange:(NSNotification *)notification; - (void)resourceDataDidChange:(NSNotification *)notification; - (void)resourceWasSaved:(NSNotification *)notification; @@ -48,5 +56,13 @@ - (id)resource; - (NSData *)data; - (int)bytesPerRow; +- (NSMenu *)copySubmenu; +- (NSMenu *)pasteSubmenu; + +// bug: these should be functions not class member methods ++ (NSRange)byteRangeFromHexRange:(NSRange)hexRange; ++ (NSRange)hexRangeFromByteRange:(NSRange)byteRange; ++ (NSRange)byteRangeFromAsciiRange:(NSRange)asciiRange; ++ (NSRange)asciiRangeFromByteRange:(NSRange)byteRange; @end diff --git a/Cocoa/Plug-Ins/Hex Editor/HexWindowController.m b/Cocoa/Plug-Ins/Hex Editor/HexWindowController.m index e25ce7c..7c950cb 100644 --- a/Cocoa/Plug-Ins/Hex Editor/HexWindowController.m +++ b/Cocoa/Plug-Ins/Hex Editor/HexWindowController.m @@ -1,17 +1,18 @@ #import "HexWindowController.h" #import "HexTextView.h" #import "FindSheetController.h" +#import "NSData-HexRepresentation.h" /* -OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource ) +OSStatus Plug_InitInstance(Plug_PlugInRef plug, Plug_ResourceRef resource) { // init function called by carbon apps - if( NSApplicationLoad() ) + if(NSApplicationLoad()) { id newResource = [NSClassFromString(@"Resource") resourceOfType:[NSString stringWithCString:length:4] andID:[NSNumber numberWithInt:] withName:[NSString stringWithCString:length:] andAttributes:[NSNumber numberWithUnsignedShort:] data:[NSData dataWithBytes:length:]]; - if( !newResource ) return paramErr; + if(!newResource) return paramErr; id windowController = [[HexWindowController alloc] initWithResource:newResource]; - if( !windowController ) return paramErr; + if(!windowController) return paramErr; else return noErr; } else return paramErr; @@ -23,35 +24,33 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource ) - (id)initWithResource:(id)newResource { self = [self initWithWindowNibName:@"HexWindow"]; - if( !self ) return self; + if(!self) return nil; // one instance of your principal class will be created for every resource the user wants to edit (similar to Windows apps) undoManager = [[NSUndoManager alloc] init]; liveEdit = NO; - if( liveEdit ) + if(liveEdit) { - resource = [newResource retain]; - backup = [newResource copy]; + resource = [newResource retain]; // resource to work on and monitor for external changes + backup = [newResource copy]; // for reverting only } else { - resource = [newResource copy]; - backup = [newResource retain]; + resource = [newResource copy]; // resource to work on + backup = [newResource retain]; // actual resource to change when saving data and monitor for external changes } bytesPerRow = 16; - // load the window from the nib file and set it's title - [self window]; // implicitly loads nib - [[self window] setTitle:[resource nameForEditorWindow]]; - + // load the window from the nib file + [self window]; return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; - [(id)resource autorelease]; [undoManager release]; + [(id)resource release]; [super dealloc]; } @@ -59,68 +58,86 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource ) { [super windowDidLoad]; - // swap text views to instances of my class instead - [offset swapForHexTextView]; - [hex swapForHexTextView]; - [ascii swapForHexTextView]; - - // turn off the background for the offset column (IB is broken when it comes to this) - [offset setDrawsBackground:NO]; - [[offset enclosingScrollView] setDrawsBackground:NO]; - - // set up tab, shift-tab and enter behaviour - [hex setFieldEditor:YES]; - [ascii setFieldEditor:YES]; + { + // set up tab, shift-tab and enter behaviour (cannot set these in IB at the moment) + [hex setFieldEditor:YES]; + [ascii setFieldEditor:YES]; + [offset setDrawsBackground:NO]; + [[offset enclosingScrollView] setDrawsBackground:NO]; + + // IB fonts get ignored for some reason + NSFont *courier = [[NSFontManager sharedFontManager] fontWithFamily:@"Courier" traits:0 weight:5 size:12.0]; + [hex setFont:courier]; + [ascii setFont:courier]; + [offset setFont:courier]; + + // from HexEditorDelegate, here until bug is fixed + [[NSNotificationCenter defaultCenter] addObserver:hexDelegate selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[offset enclosingScrollView] contentView]]; + [[NSNotificationCenter defaultCenter] addObserver:hexDelegate selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[hex enclosingScrollView] contentView]]; + [[NSNotificationCenter defaultCenter] addObserver:hexDelegate selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[ascii enclosingScrollView] contentView]]; + } // insert the resources' data into the text fields [self refreshData:[resource data]]; - [[self window] setResizeIncrements:NSMakeSize(kWindowStepWidthPerChar * kWindowStepCharsPerStep, 1)]; + [[self window] setResizeIncrements:NSMakeSize(kWindowStepWidthPerChar * kWindowStepCharsPerStep * [[self window] userSpaceScaleFactor], 1)]; // min 346, step 224, norm 570, step 224, max 794 - // we don't want this notification until we have a window! (Only register for notifications on the resource we're editing) + // here because we don't want these notifications until we have a window! (Only register for notifications on the resource we're editing) [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceNameDidChange:) name:ResourceNameDidChangeNotification object:resource]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceDataDidChange:) name:ResourceDataDidChangeNotification object:resource]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceWasSaved:) name:ResourceWasSavedNotification object:resource]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceWasSaved:) name:ResourceWasSavedNotification object:backup]; + if(liveEdit) [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceWasSaved:) name:ResourceDataDidChangeNotification object:resource]; + else [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceWasSaved:) name:ResourceDataDidChangeNotification object:backup]; - // put other notifications here too, just for togetherness - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[offset enclosingScrollView] contentView]]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[hex enclosingScrollView] contentView]]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[ascii enclosingScrollView] contentView]]; - - // finally, show the window + // finally, set the window title & show the window + [[self window] setTitle:[resource defaultWindowTitle]]; [self showWindow:self]; } - (void)windowDidResize:(NSNotification *)notification { - int width = [[notification object] frame].size.width; + float width = [[(NSWindow *)[notification object] contentView] frame].size.width; int oldBytesPerRow = bytesPerRow; bytesPerRow = (((width - (kWindowStepWidthPerChar * kWindowStepCharsPerStep) - 122) / (kWindowStepWidthPerChar * kWindowStepCharsPerStep)) + 1) * kWindowStepCharsPerStep; - if( bytesPerRow != oldBytesPerRow ) + if(bytesPerRow != oldBytesPerRow) [offset setString:[hexDelegate offsetRepresentation:[resource data]]]; - [hexScroll setFrameSize:NSMakeSize( (bytesPerRow * 21) + 5, [hexScroll frame].size.height)]; - [asciiScroll setFrameOrigin:NSMakePoint( (bytesPerRow * 21) + 95, 20)]; - [asciiScroll setFrameSize:NSMakeSize( (bytesPerRow * 7) + 28, [asciiScroll frame].size.height)]; + [[hex enclosingScrollView] setFrameSize:NSMakeSize((bytesPerRow * 21) + 5, [[hex enclosingScrollView] frame].size.height)]; + [[ascii enclosingScrollView] setFrameOrigin:NSMakePoint((bytesPerRow * 21) + 95, 20)]; + [[ascii enclosingScrollView] setFrameSize:NSMakeSize((bytesPerRow * 7) + 28, [[ascii enclosingScrollView] frame].size.height)]; } - (void)windowDidBecomeKey:(NSNotification *)notification { - // swap paste: menu item for my own paste submenu NSMenu *editMenu = [[[NSApp mainMenu] itemAtIndex:2] submenu]; + NSMenuItem *copyItem = [editMenu itemAtIndex:[editMenu indexOfItemWithTarget:nil andAction:@selector(copy:)]]; NSMenuItem *pasteItem = [editMenu itemAtIndex:[editMenu indexOfItemWithTarget:nil andAction:@selector(paste:)]]; - [NSBundle loadNibNamed:@"PasteMenu" owner:self]; + + // swap copy: menu item for my own copy submenu + [copyItem setEnabled:YES]; + [copyItem setKeyEquivalent:@"\0"]; // clear key equiv. + [copyItem setKeyEquivalentModifierMask:0]; + [editMenu setSubmenu:copySubmenu forItem:copyItem]; + + // swap paste: menu item for my own paste submenu [pasteItem setEnabled:YES]; - [pasteItem setKeyEquivalent:@"\0"]; // clear key equiv. (yes, really!) + [pasteItem setKeyEquivalent:@"\0"]; [pasteItem setKeyEquivalentModifierMask:0]; [editMenu setSubmenu:pasteSubmenu forItem:pasteItem]; } - (void)windowDidResignKey:(NSNotification *)notification { - // swap my submenu for plain paste menu item NSMenu *editMenu = [[[NSApp mainMenu] itemAtIndex:2] submenu]; + NSMenuItem *copyItem = [editMenu itemAtIndex:[editMenu indexOfItemWithSubmenu:copySubmenu]]; NSMenuItem *pasteItem = [editMenu itemAtIndex:[editMenu indexOfItemWithSubmenu:pasteSubmenu]]; + + // swap my submenu for plain copy menu item + [editMenu setSubmenu:nil forItem:copyItem]; + [copyItem setTarget:nil]; + [copyItem setAction:@selector(copy:)]; + [copyItem setKeyEquivalent:@"c"]; + [copyItem setKeyEquivalentModifierMask:NSCommandKeyMask]; + + // swap my submenu for plain paste menu item [editMenu setSubmenu:nil forItem:pasteItem]; [pasteItem setTarget:nil]; [pasteItem setAction:@selector(paste:)]; @@ -130,9 +147,9 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource ) - (BOOL)windowShouldClose:(id)sender { - if( [[self window] isDocumentEdited] ) + if([[self window] isDocumentEdited]) { - NSBeginAlertSheet( @"Do you want to save the changes you made to this resource?", @"Save", @"DonÕt Save", @"Cancel", sender, self, @selector(saveSheetDidClose:returnCode:contextInfo:), nil, nil, @"Your changes will be lost if you don't save them." ); + NSBeginAlertSheet(@"Do you want to keep the changes you made to this resource?", @"Keep", @"DonÕt Keep", @"Cancel", sender, self, @selector(saveSheetDidClose:returnCode:contextInfo:), nil, nil, @"Your changes cannot be saved later if you don't keep them."); return NO; } else return YES; @@ -140,15 +157,14 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource ) - (void)saveSheetDidClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo { - switch( returnCode ) + switch(returnCode) { - case NSAlertDefaultReturn: // save - [self saveResource]; + case NSAlertDefaultReturn: // keep + [self saveResource:nil]; [[self window] close]; break; - case NSAlertAlternateReturn: // don't save - [self revertResource]; + case NSAlertAlternateReturn: // don't keep [[self window] close]; break; @@ -157,83 +173,33 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource ) } } -- (void)saveResource +- (void)saveResource:(id)sender { - if( liveEdit ) - { - [[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillBeSavedNotification object:resource]; - [backup setData:[resource data]]; - [[NSNotificationCenter defaultCenter] postNotificationName:ResourceWasSavedNotification object:resource]; - } - else - { - [[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillBeSavedNotification object:backup]; - [backup setData:[resource data]]; - [[NSNotificationCenter defaultCenter] postNotificationName:ResourceWasSavedNotification object:backup]; - } + [backup setData:[[resource data] copy]]; } -- (void)revertResource +- (void)revertResource:(id)sender { - [resource setData:[backup data]]; + [resource setData:[[backup data] copy]]; } -- (IBAction)showFind:(id)sender +- (void)showFind:(id)sender { // bug: HexWindowController allocs a sheet controller, but it's never disposed of FindSheetController *sheetController = [[FindSheetController alloc] initWithWindowNibName:@"FindSheet"]; [sheetController showFindSheet:self]; } -- (void)viewDidScroll:(NSNotification *)notification -{ - // get object refs for increased speed - NSClipView *object = (NSClipView *) [notification object]; - NSClipView *offsetClip = [[offset enclosingScrollView] contentView]; - NSClipView *hexClip = [[hex enclosingScrollView] contentView]; - NSClipView *asciiClip = [[ascii enclosingScrollView] contentView]; - - // due to a bug in -[NSView setPostsBoundsChangedNotifications:] (it basically doesn't work), I am having to work around it by removing myself from the notification center and restoring things later on! - // update, Apple say this isn't their bug. Yeah, right! - [[NSNotificationCenter defaultCenter] removeObserver:self name:NSViewBoundsDidChangeNotification object:nil]; - - // when a view scrolls, update the other two - if( object != offsetClip ) - { -// [offsetClip setPostsBoundsChangedNotifications:NO]; - [offsetClip setBoundsOrigin:[object bounds].origin]; -// [offsetClip setPostsBoundsChangedNotifications:YES]; - } - - if( object != hexClip ) - { -// [hexClip setPostsBoundsChangedNotifications:NO]; - [hexClip setBoundsOrigin:[object bounds].origin]; -// [hexClip setPostsBoundsChangedNotifications:YES]; - } - - if( object != asciiClip ) - { -// [asciiClip setPostsBoundsChangedNotifications:NO]; - [asciiClip setBoundsOrigin:[object bounds].origin]; -// [asciiClip setPostsBoundsChangedNotifications:YES]; - } - - // restore notifications - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:offsetClip]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:hexClip]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:asciiClip]; -} - - (void)resourceNameDidChange:(NSNotification *)notification { - [[self window] setTitle:[(id )[notification object] nameForEditorWindow]]; + [[self window] setTitle:[(id )[notification object] defaultWindowTitle]]; } - (void)resourceDataDidChange:(NSNotification *)notification { // ensure it's our resource which got changed (should always be true, we don't register for other resource notifications) - if( [notification object] == (id)resource ) + // bug: if liveEdit is false and another editor changes backup, if we are dirty we need to ask the user whether to accept the changes from the other editor and discard our changes, or vice versa. + if([notification object] == (id)resource) { [self refreshData:[resource data]]; [self setDocumentEdited:YES]; @@ -242,35 +208,21 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource ) - (void)resourceWasSaved:(NSNotification *)notification { - NSLog( @"%@; %@; %@", [notification object], resource, backup ); - if( [notification object] == (id)resource ) + id object = [notification object]; + if(liveEdit) { - // if resource gets saved, liveEdit is true and this resource is saving - [backup setData:[resource data]]; - [self setDocumentEdited:NO]; + // haven't worked out what to do here yet } - else if( [notification object] == (id)backup && !liveEdit ) + else { - // backup will get saved by this resource if liveEdit is false, rather than the 'resource' variable - // but really the data to preserve is in the resource variable - [backup setData:[resource data]]; -// [self refreshData:[resource data]]; - [self setDocumentEdited:NO]; - } - else if( [notification object] == (id)backup ) - { - // backup will get saved by another editor too if liveEdit is false - [resource setData:[backup data]]; -// [self refreshData:[resource data]]; + // this should refresh the view automatically + [resource setData:[[object data] copy]]; [self setDocumentEdited:NO]; } } - (void)refreshData:(NSData *)data; { - NSDictionary *dictionary; - NSMutableParagraphStyle *paragraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - // save selections NSRange hexSelection = [hex selectedRange]; NSRange asciiSelection = [ascii selectedRange]; @@ -281,13 +233,16 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource ) [ascii setDelegate:nil]; // prepare attributes dictionary + NSMutableParagraphStyle *paragraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; [paragraph setLineBreakMode:NSLineBreakByCharWrapping]; - dictionary = [NSDictionary dictionaryWithObject:paragraph forKey:NSParagraphStyleAttributeName]; + NSDictionary *dictionary = [NSDictionary dictionaryWithObject:paragraph forKey:NSParagraphStyleAttributeName]; // do stuff with data - [offset setString:[hexDelegate offsetRepresentation:data]]; - [hex setString:[hexDelegate hexRepresentation:data]]; - [ascii setString:[hexDelegate asciiRepresentation:data]]; + [offset setString:[hexDelegate offsetRepresentation:data]]; + if([data length] > 0) + [hex setString:[[data hexRepresentation] stringByAppendingString:@" "]]; + else [hex setString:[data hexRepresentation]]; + [ascii setString:[data asciiRepresentation]]; // apply attributes [[offset textStorage] addAttributes:dictionary range:NSMakeRange(0, [[offset textStorage] length])]; @@ -318,6 +273,11 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource ) return bytesPerRow; } +- (NSMenu *)copySubmenu +{ + return copySubmenu; +} + - (NSMenu *)pasteSubmenu { return pasteSubmenu; @@ -328,4 +288,36 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource ) return undoManager; } +/* range conversion methods */ + ++ (NSRange)byteRangeFromHexRange:(NSRange)hexRange +{ + // valid for all window widths + NSRange byteRange = NSMakeRange(0,0); + byteRange.location = (hexRange.location / 3); + byteRange.length = (hexRange.length / 3) + ((hexRange.length % 3)? 1:0); + return byteRange; +} + ++ (NSRange)hexRangeFromByteRange:(NSRange)byteRange +{ + // valid for all window widths + NSRange hexRange = NSMakeRange(0,0); + hexRange.location = (byteRange.location * 3); + hexRange.length = (byteRange.length * 3) - ((byteRange.length > 0)? 1:0); + return hexRange; +} + ++ (NSRange)byteRangeFromAsciiRange:(NSRange)asciiRange +{ + // one-to-one mapping + return asciiRange; +} + ++ (NSRange)asciiRangeFromByteRange:(NSRange)byteRange +{ + // one-to-one mapping + return byteRange; +} + @end \ No newline at end of file diff --git a/Cocoa/Plug-Ins/Hex Editor/Info.plist b/Cocoa/Plug-Ins/Hex Editor/Info.plist index ee1d95f..bb083d9 100644 --- a/Cocoa/Plug-Ins/Hex Editor/Info.plist +++ b/Cocoa/Plug-Ins/Hex Editor/Info.plist @@ -1,34 +1,33 @@ - + CFBundleDevelopmentRegion English CFBundleExecutable Hexadecimal Editor - CFBundleGetInfoString - - CFBundleIconFile - CFBundleIdentifier com.nickshanks.resknife.hexadecimal CFBundleInfoDictionaryVersion 6.0 - CFBundleName - CFBundlePackageType BNDL - CFBundleShortVersionString - CFBundleSignature ResK CFBundleVersion - 0.0.1d1 - NSMainNibFile - HexWindow + 3 NSPrincipalClass HexWindowController - RKEditedType - Hexadecimal Editor + RKSupportedTypes + + + IsResKnifeDefaultForType + YES + RKTypeName + Hexadecimal Editor + RKTypeRole + Editor + + diff --git a/Cocoa/Resources/Apply3.png b/Cocoa/Resources/Apply3.png new file mode 100644 index 0000000..91c4503 Binary files /dev/null and b/Cocoa/Resources/Apply3.png differ diff --git a/Cocoa/Resources/Button_certified.png b/Cocoa/Resources/Button_certified.png new file mode 100644 index 0000000..db18b52 Binary files /dev/null and b/Cocoa/Resources/Button_certified.png differ diff --git a/Cocoa/Resources/Button_reload.png b/Cocoa/Resources/Button_reload.png new file mode 100644 index 0000000..3fa8db7 Binary files /dev/null and b/Cocoa/Resources/Button_reload.png differ diff --git a/Cocoa/Resources/Create48.png b/Cocoa/Resources/Create48.png new file mode 100644 index 0000000..7f86b48 Binary files /dev/null and b/Cocoa/Resources/Create48.png differ diff --git a/Cocoa/Resources/Delete.png b/Cocoa/Resources/Delete.png new file mode 100644 index 0000000..ba6ca80 Binary files /dev/null and b/Cocoa/Resources/Delete.png differ diff --git a/Cocoa/Resources/Delete.svg b/Cocoa/Resources/Delete.svg new file mode 100644 index 0000000..ca71dbf --- /dev/null +++ b/Cocoa/Resources/Delete.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Delete + 2007-02-20 + + + Andreas Nilsson + Nicholas Shanks + + + http://tango-project.org/ + + + delete + remove + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cocoa/Resources/Delete3.png b/Cocoa/Resources/Delete3.png new file mode 100644 index 0000000..4e5e790 Binary files /dev/null and b/Cocoa/Resources/Delete3.png differ diff --git a/Cocoa/Resources/Go3.png b/Cocoa/Resources/Go3.png new file mode 100644 index 0000000..c7827e4 Binary files /dev/null and b/Cocoa/Resources/Go3.png differ diff --git a/Cocoa/Resources/Help.png b/Cocoa/Resources/Help.png new file mode 100644 index 0000000..07abdcd Binary files /dev/null and b/Cocoa/Resources/Help.png differ diff --git a/Cocoa/Resources/Help3.png b/Cocoa/Resources/Help3.png new file mode 100644 index 0000000..4d7bb00 Binary files /dev/null and b/Cocoa/Resources/Help3.png differ diff --git a/Cocoa/Resources/ResKnife-old.icns b/Cocoa/Resources/ResKnife-old.icns new file mode 100644 index 0000000..da57f85 Binary files /dev/null and b/Cocoa/Resources/ResKnife-old.icns differ diff --git a/Cocoa/Resources/ResKnife.icns b/Cocoa/Resources/ResKnife.icns index da57f85..8a6833a 100644 Binary files a/Cocoa/Resources/ResKnife.icns and b/Cocoa/Resources/ResKnife.icns differ diff --git a/Cocoa/Resources/Resource Type Mappings.strings b/Cocoa/Resources/Resource Type Mappings.strings new file mode 100644 index 0000000..1662cca --- /dev/null +++ b/Cocoa/Resources/Resource Type Mappings.strings @@ -0,0 +1,43 @@ +/* Resource type to file type mapping table */ + +/* useful for mangling a resource type to yield an icon for a different file extension */ +/* also could be used in future for binding a resource to an external editor (the default editor for the corrisponding file type) */ +/* acceptable values are: + - file name extensions + - MacOS file types (four characters in single quotes) + - bundle identifiers +*/ + +"cfrg" = "shlb"; +"SIZE" = "shlb"; + +"CODE" = "s"; +"MWBB" = "'MMPF'"; +"MWKB" = "'MMPF'"; + +"STR " = "text"; +"STR#" = "text"; + +"plst" = "plist"; +"snd " = "'sfil'"; +"url " = "webloc"; + +"hfdr" = "com.apple.finder"; + +"cicn" = "icns"; +"SICN" = "icns"; +"icl8" = "icns"; +"icl4" = "icns"; +"ICON" = "icns"; +"ICN#" = "icns"; +"ics8" = "icns"; +"ics4" = "icns"; +"ics#" = "icns"; +"icm8" = "icns"; +"icm4" = "icns"; +"icm#" = "icns"; + +"PNG " = "png"; + +"NFNT" = "'tfil'"; +"sfnt" = "ttf"; diff --git a/Cocoa/Resources/Resource file-old.icns b/Cocoa/Resources/Resource file-old.icns new file mode 100644 index 0000000..6c50128 Binary files /dev/null and b/Cocoa/Resources/Resource file-old.icns differ diff --git a/Cocoa/Resources/Resource file.icns b/Cocoa/Resources/Resource file.icns index 6c50128..0cce04e 100644 Binary files a/Cocoa/Resources/Resource file.icns and b/Cocoa/Resources/Resource file.icns differ diff --git a/Cocoa/Resources/Search3.png b/Cocoa/Resources/Search3.png new file mode 100644 index 0000000..8265a00 Binary files /dev/null and b/Cocoa/Resources/Search3.png differ diff --git a/Cocoa/Resources/add.png b/Cocoa/Resources/add.png new file mode 100644 index 0000000..8bd5f6e Binary files /dev/null and b/Cocoa/Resources/add.png differ diff --git a/Cocoa/Resources/defaults.plist b/Cocoa/Resources/defaults.plist index 23c3a91..dd1da53 100644 --- a/Cocoa/Resources/defaults.plist +++ b/Cocoa/Resources/defaults.plist @@ -1,6 +1,6 @@ { PreserveBackups = YES; - Autosave = YES; + Autosave = NO; AutosaveInterval = 5; DeleteResourceWarning = YES; diff --git a/Cocoa/Resources/delete-1.png b/Cocoa/Resources/delete-1.png new file mode 100644 index 0000000..6602a50 Binary files /dev/null and b/Cocoa/Resources/delete-1.png differ diff --git a/Cocoa/cy.lproj/Application.nib/classes.nib b/Cocoa/cy.lproj/Application.nib/classes.nib new file mode 100644 index 0000000..cbfc87a --- /dev/null +++ b/Cocoa/cy.lproj/Application.nib/classes.nib @@ -0,0 +1,88 @@ +{ + IBClasses = ( + { + ACTIONS = { + emailDeveloper = id; + showAbout = id; + showInfo = id; + showPasteboard = id; + showPrefs = id; + visitSourceforge = id; + visitWebsite = id; + }; + CLASS = ApplicationDelegate; + LANGUAGE = ObjC; + OUTLETS = {openPanelDelegate = OpenPanelDelegate; }; + SUPERCLASS = NSObject; + }, + { + ACTIONS = { + deselectAll = id; + exportResourceToFile = id; + exportResourceToImageFile = id; + findNext = id; + findPrevious = id; + findWithSelection = id; + openResources = id; + openResourcesAsHex = id; + openResourcesInTemplate = id; + playSound = id; + revertResource = id; + saveResource = id; + scrollToSelection = id; + showAbout = id; + showCreateResourceSheet = id; + showExportToDFSheet = id; + showFind = id; + showInfo = id; + showPrefs = id; + showSelectTemplateSheet = id; + useIconView = id; + useListView = id; + }; + CLASS = FirstResponder; + LANGUAGE = ObjC; + SUPERCLASS = NSObject; + }, + {CLASS = NSOutlineView; LANGUAGE = ObjC; SUPERCLASS = NSTableView; }, + { + ACTIONS = {addFork = id; removeFork = id; }; + CLASS = OpenPanelDelegate; + LANGUAGE = ObjC; + OUTLETS = { + addForkButton = NSButton; + forkTableView = NSTableView; + openPanelAccessoryView = NSView; + removeForkButton = NSButton; + }; + SUPERCLASS = NSObject; + }, + {CLASS = OutlineSortView; LANGUAGE = ObjC; SUPERCLASS = NSOutlineView; }, + { + ACTIONS = { + clear = id; + copy = id; + creatorChanged = id; + exportResourceToFile = id; + exportResourceToImageFile = id; + openResources = id; + openResourcesAsHex = id; + openResourcesInTemplate = id; + paste = id; + playSound = id; + showCreateResourceSheet = id; + showSelectTemplateSheet = id; + typeChanged = id; + }; + CLASS = ResourceDocument; + LANGUAGE = ObjC; + OUTLETS = { + dataSource = ResourceDataSource; + mainWindow = NSWindow; + outlineView = NSOutlineView; + }; + SUPERCLASS = NSDocument; + } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/Cocoa/cy.lproj/Application.nib/info.nib b/Cocoa/cy.lproj/Application.nib/info.nib new file mode 100644 index 0000000..4512ef0 --- /dev/null +++ b/Cocoa/cy.lproj/Application.nib/info.nib @@ -0,0 +1,27 @@ + + + + + IBDocumentLocation + 75 727 356 240 0 0 1600 1002 + IBEditorPositions + + 246 + 569 580 520 175 0 0 1600 1002 + 29 + 66 658 413 44 0 0 1600 1002 + + IBFramework Version + 446.1 + IBLockedObjects + + IBOldestOS + 1 + IBOpenObjects + + 29 + + IBSystem Version + 8P135 + + diff --git a/Cocoa/cy.lproj/Application.nib/keyedobjects.nib b/Cocoa/cy.lproj/Application.nib/keyedobjects.nib new file mode 100644 index 0000000..54c9612 Binary files /dev/null and b/Cocoa/cy.lproj/Application.nib/keyedobjects.nib differ diff --git a/Cocoa/cy.lproj/Application.nib/objects.nib b/Cocoa/cy.lproj/Application.nib/objects.nib new file mode 100644 index 0000000..82a0594 Binary files /dev/null and b/Cocoa/cy.lproj/Application.nib/objects.nib differ diff --git a/Cocoa/cy.lproj/InfoPlist.strings b/Cocoa/cy.lproj/InfoPlist.strings new file mode 100644 index 0000000..2b3f34c --- /dev/null +++ b/Cocoa/cy.lproj/InfoPlist.strings @@ -0,0 +1,6 @@ +/* Localized versions of Info.plist keys */ + +CFBundleName = "ResKnife"; +CFBundleShortVersionString = "0.6 beta 4"; +CFBundleGetInfoString = "ResKnife 0.6 b4, Hawlfraint © Nicolas Siancs, 2001-7."; +NSHumanReadableCopyright = "© Nicolas Siancs, 2001-7."; diff --git a/Cocoa/cy.lproj/Localizable.strings b/Cocoa/cy.lproj/Localizable.strings new file mode 100644 index 0000000..0f2d44c Binary files /dev/null and b/Cocoa/cy.lproj/Localizable.strings differ diff --git a/Cocoa/gd.lproj/Application.nib/classes.nib b/Cocoa/gd.lproj/Application.nib/classes.nib new file mode 100644 index 0000000..cbfc87a --- /dev/null +++ b/Cocoa/gd.lproj/Application.nib/classes.nib @@ -0,0 +1,88 @@ +{ + IBClasses = ( + { + ACTIONS = { + emailDeveloper = id; + showAbout = id; + showInfo = id; + showPasteboard = id; + showPrefs = id; + visitSourceforge = id; + visitWebsite = id; + }; + CLASS = ApplicationDelegate; + LANGUAGE = ObjC; + OUTLETS = {openPanelDelegate = OpenPanelDelegate; }; + SUPERCLASS = NSObject; + }, + { + ACTIONS = { + deselectAll = id; + exportResourceToFile = id; + exportResourceToImageFile = id; + findNext = id; + findPrevious = id; + findWithSelection = id; + openResources = id; + openResourcesAsHex = id; + openResourcesInTemplate = id; + playSound = id; + revertResource = id; + saveResource = id; + scrollToSelection = id; + showAbout = id; + showCreateResourceSheet = id; + showExportToDFSheet = id; + showFind = id; + showInfo = id; + showPrefs = id; + showSelectTemplateSheet = id; + useIconView = id; + useListView = id; + }; + CLASS = FirstResponder; + LANGUAGE = ObjC; + SUPERCLASS = NSObject; + }, + {CLASS = NSOutlineView; LANGUAGE = ObjC; SUPERCLASS = NSTableView; }, + { + ACTIONS = {addFork = id; removeFork = id; }; + CLASS = OpenPanelDelegate; + LANGUAGE = ObjC; + OUTLETS = { + addForkButton = NSButton; + forkTableView = NSTableView; + openPanelAccessoryView = NSView; + removeForkButton = NSButton; + }; + SUPERCLASS = NSObject; + }, + {CLASS = OutlineSortView; LANGUAGE = ObjC; SUPERCLASS = NSOutlineView; }, + { + ACTIONS = { + clear = id; + copy = id; + creatorChanged = id; + exportResourceToFile = id; + exportResourceToImageFile = id; + openResources = id; + openResourcesAsHex = id; + openResourcesInTemplate = id; + paste = id; + playSound = id; + showCreateResourceSheet = id; + showSelectTemplateSheet = id; + typeChanged = id; + }; + CLASS = ResourceDocument; + LANGUAGE = ObjC; + OUTLETS = { + dataSource = ResourceDataSource; + mainWindow = NSWindow; + outlineView = NSOutlineView; + }; + SUPERCLASS = NSDocument; + } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/Cocoa/gd.lproj/Application.nib/info.nib b/Cocoa/gd.lproj/Application.nib/info.nib new file mode 100644 index 0000000..36622a0 --- /dev/null +++ b/Cocoa/gd.lproj/Application.nib/info.nib @@ -0,0 +1,27 @@ + + + + + IBDocumentLocation + 75 727 356 240 0 0 1600 1002 + IBEditorPositions + + 246 + 567 580 520 175 0 0 1600 1002 + 29 + 408 624 463 44 0 0 1600 1002 + + IBFramework Version + 446.1 + IBLockedObjects + + IBOldestOS + 1 + IBOpenObjects + + 29 + + IBSystem Version + 8L127 + + diff --git a/Cocoa/gd.lproj/Application.nib/keyedobjects.nib b/Cocoa/gd.lproj/Application.nib/keyedobjects.nib new file mode 100644 index 0000000..1118338 Binary files /dev/null and b/Cocoa/gd.lproj/Application.nib/keyedobjects.nib differ diff --git a/Cocoa/gd.lproj/Application.nib/objects.nib b/Cocoa/gd.lproj/Application.nib/objects.nib new file mode 100644 index 0000000..c42d2e2 Binary files /dev/null and b/Cocoa/gd.lproj/Application.nib/objects.nib differ diff --git a/Cocoa/gd.lproj/InfoPlist.strings b/Cocoa/gd.lproj/InfoPlist.strings new file mode 100644 index 0000000..6873859 --- /dev/null +++ b/Cocoa/gd.lproj/InfoPlist.strings @@ -0,0 +1,6 @@ +/* Localized versions of Info.plist keys */ + +CFBundleName = "ResKnife"; +CFBundleShortVersionString = "0.6 beta 4"; +CFBundleGetInfoString = "ResKnife 0.6 b4, Dlighe-sgrìobhaidh © Nicolas Sauncs, 2001-7."; +NSHumanReadableCopyright = "© Nicolas Sauncs, 2001-7."; diff --git a/Cocoa/gd.lproj/Localizable.strings b/Cocoa/gd.lproj/Localizable.strings new file mode 100644 index 0000000..c0d52fc Binary files /dev/null and b/Cocoa/gd.lproj/Localizable.strings differ diff --git a/Cocoa/ja.lproj/Application.nib/classes.nib b/Cocoa/ja.lproj/Application.nib/classes.nib new file mode 100644 index 0000000..1c978c4 --- /dev/null +++ b/Cocoa/ja.lproj/Application.nib/classes.nib @@ -0,0 +1,50 @@ +{ + IBClasses = ( + { + ACTIONS = { + emailDeveloper = id; + showAbout = id; + showInfo = id; + showPasteboard = id; + showPrefs = id; + visitSourceforge = id; + visitWebsite = id; + }; + CLASS = ApplicationDelegate; + LANGUAGE = ObjC; + OUTLETS = {openPanelDelegate = OpenPanelDelegate; }; + SUPERCLASS = NSObject; + }, + { + ACTIONS = { + deselectAll = id; + openResources = id; + openResourcesAsHex = id; + openResourcesInTemplate = id; + playSound = id; + revertResource = id; + saveResource = id; + showCreateResourceSheet = id; + showSelectTemplateSheet = id; + useIconView = id; + useListView = id; + }; + CLASS = FirstResponder; + LANGUAGE = ObjC; + SUPERCLASS = NSObject; + }, + { + ACTIONS = {addFork = id; removeFork = id; }; + CLASS = OpenPanelDelegate; + LANGUAGE = ObjC; + OUTLETS = { + addForkButton = NSButton; + forkTableView = NSTableView; + openPanelAccessoryView = NSView; + removeForkButton = NSButton; + }; + SUPERCLASS = NSObject; + } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/Cocoa/ja.lproj/Application.nib/info.nib b/Cocoa/ja.lproj/Application.nib/info.nib new file mode 100644 index 0000000..844d92b --- /dev/null +++ b/Cocoa/ja.lproj/Application.nib/info.nib @@ -0,0 +1,23 @@ + + + + + IBDocumentLocation + 454 731 356 240 0 0 1600 1002 + IBEditorPositions + + 252 + 580 400 520 175 0 0 1600 1002 + 29 + 465 651 429 44 0 0 1600 1002 + + IBFramework Version + 446.1 + IBOpenObjects + + 29 + + IBSystem Version + 8L127 + + diff --git a/Cocoa/ja.lproj/Application.nib/keyedobjects.nib b/Cocoa/ja.lproj/Application.nib/keyedobjects.nib new file mode 100644 index 0000000..23840a9 Binary files /dev/null and b/Cocoa/ja.lproj/Application.nib/keyedobjects.nib differ diff --git a/Cocoa/main.m b/Cocoa/main.m index 2a4380a..f16a24f 100644 --- a/Cocoa/main.m +++ b/Cocoa/main.m @@ -1,5 +1,9 @@ #import +/*! +@function main +*/ + int main(int argc, const char *argv[]) { return NSApplicationMain(argc, argv); diff --git a/ICONEditor/ICONWindowController.m b/ICONEditor/ICONWindowController.m index 39c2adc..f099bf0 100644 --- a/ICONEditor/ICONWindowController.m +++ b/ICONEditor/ICONWindowController.m @@ -82,7 +82,7 @@ resImage = [[NSImage alloc] init]; resData = [[resource data] retain]; - planes[0] = (char*) [resData bytes]; + planes[0] = (unsigned char*) [resData bytes]; if( [resType isEqualToString: @"ICN#"] ) { diff --git a/NuTemplateEditor/TEMPLATE EDITOR.txt b/NuTemplateEditor/Read Me.txt similarity index 75% rename from NuTemplateEditor/TEMPLATE EDITOR.txt rename to NuTemplateEditor/Read Me.txt index a7ea2a4..baf5537 100644 --- a/NuTemplateEditor/TEMPLATE EDITOR.txt +++ b/NuTemplateEditor/Read Me.txt @@ -1,40 +1,37 @@ ABOUT THE CODE -------------- - WHAT HAPPENS WHEN A TEMPLATE EDITOR IS OPENED FOR A RESOURCE The template editor uses a dedicated class for each field type (or family of field types). -These classes are all subclasses of the common base class NuTemplateElement. For things like -lists, there is a subclass NuTemplateGroupElement from which you can instead subclass to +These classes are all subclasses of the common base class Element. For things like +lists, there is a subclass GroupElement from which you can instead subclass to have some of the work done for you. -When opening a resource, the template editor first creates a NuTemplateStream for the template -resource and calls readOneObject: on it until it runs out of data, instantiating a hierarchy -of NuTemplateElement subclasses for based on the template. +When opening a resource, the template editor first creates a TemplateStream for the template +resource and calls readOneObject:on it until it runs out of data, instantiating a hierarchy +of Element subclasses for based on the template. -After that, it creates a NuTemplateStream for the resource and loops over the template object -hierarchy, creating a copy of each item, and then calling readDataFrom:containingArray: on -the copy, which in turn reads data from the NuTemplateStream and stores it in its instance -variables. For this to work, subclasses of NuTemplateElement *must* implement the NSCopying +After that, it creates a TemplateStream for the resource and loops over the template object +hierarchy, creating a copy of each item, and then calling readDataFrom:containingArray:on +the copy, which in turn reads data from the TemplateStream and stores it in its instance +variables. For this to work, subclasses of Element *must* implement the NSCopying protocol. This results in an object hierarchy that describes the entire resource using template fields containing the data, which can now be edited, displayed in lists, etc. - WHAT HAPPENS WHEN THE TEMPLATE EDITOR IS CLOSED -As soon as the template editor is closed, another NuTemplateStream is created for the +As soon as the template editor is closed, another TemplateStream is created for the resource and passed to all template fields (the copies with the resource data, not the pre-parsed template hierarchy), via writeDataTo:, which is where they should write their -data to the stream. Before that, sizeOnDisk is called on each NuTemplateElement to +data to the stream. Before that, sizeOnDisk is called on each Element to calculate the new size of the resource before the actual process of writing them out. -This size must include the size of all of their sub-items, and writeDataTo: must call +This size must include the size of all of their sub-items, and writeDataTo:must call these sub items if they are to be written to disk. - -SPECIAL CASE: LISTS +SPECIAL CASE:LISTS When the editor encounters an LSTB element, the LSTB element is called upon to parse its "sub-elements" (the items that make up one list item). The LSTB element reads all elements @@ -65,8 +62,7 @@ Analogous behaviour is employed for other kinds of lists, except that additional features in LSTEs are activated to allow writing terminating zeroes to the file, and that the equivalent to the "LSTB" item may also update a counter field. - - REVISIONS: + 2006-02-05 NS Rewrote plugin. 2003-08-13 UK Finished chapter on lists, added revision history. 2003-08-08 UK Created. diff --git a/NuTemplateEditor/Resources/DisplayTMPL.png b/NuTemplateEditor/Resources/DisplayTMPL.png new file mode 100644 index 0000000..695d4c0 Binary files /dev/null and b/NuTemplateEditor/Resources/DisplayTMPL.png differ diff --git a/NuTemplateEditor/TMPLs.rsrc b/NuTemplateEditor/TMPLs.rsrc new file mode 100644 index 0000000..0454a63 Binary files /dev/null and b/NuTemplateEditor/TMPLs.rsrc differ diff --git a/NuTemplateEditor/Templates.rsrc b/NuTemplateEditor/Templates.rsrc new file mode 100644 index 0000000..5b696e3 Binary files /dev/null and b/NuTemplateEditor/Templates.rsrc differ diff --git a/ResKnife.xcodeproj/project.pbxproj b/ResKnife.xcodeproj/project.pbxproj index b00074e..2306ed6 100644 --- a/ResKnife.xcodeproj/project.pbxproj +++ b/ResKnife.xcodeproj/project.pbxproj @@ -7,6 +7,17 @@ objects = { /* Begin PBXBuildFile section */ + E17752350E424BAB00F737F8 /* NSData-HexRepresentation.m in Sources */ = {isa = PBXBuildFile; fileRef = E17752330E424BAB00F737F8 /* NSData-HexRepresentation.m */; }; + E177526F0E424DCB00F737F8 /* OpenPanelDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E177526D0E424DCB00F737F8 /* OpenPanelDelegate.m */; }; + E17752700E424DCB00F737F8 /* OpenPanelDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = E177526E0E424DCB00F737F8 /* OpenPanelDelegate.h */; }; + E17752760E424ED400F737F8 /* Notifications.m in Sources */ = {isa = PBXBuildFile; fileRef = F5C9ECCE027F474A01A8010C /* Notifications.m */; }; + E17753350E424F0C00F737F8 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5B5884A0156D40B01000001 /* Carbon.framework */; }; + E177538B0E4251C600F737F8 /* DisplayTMPL.png in Resources */ = {isa = PBXBuildFile; fileRef = E17753880E4251C600F737F8 /* DisplayTMPL.png */; }; + E177538E0E4251D000F737F8 /* Templates.rsrc in Copy Support Resources */ = {isa = PBXBuildFile; fileRef = E17753890E4251C600F737F8 /* Templates.rsrc */; }; + E177539D0E42521800F737F8 /* Font Templates.rsrc in Copy Support Resources */ = {isa = PBXBuildFile; fileRef = E1B2A73B0E41218F00A72928 /* Font Templates.rsrc */; }; + E17753B90E42528100F737F8 /* NovaTools.plugin in CopyFiles */ = {isa = PBXBuildFile; fileRef = E1B2A4940E41103800A72928 /* NovaTools.plugin */; }; + E17753CD0E42528400F737F8 /* Font Editor.plugin in CopyFiles */ = {isa = PBXBuildFile; fileRef = E1B2A7220E41213300A72928 /* Font Editor.plugin */; }; + E17753E40E42534300F737F8 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5B5884B0156D40B01000001 /* Cocoa.framework */; }; E1B2A3EA0E41103700A72928 /* ResKnifeResourceProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = F5CDEBAB01FC893201A80001 /* ResKnifeResourceProtocol.h */; }; E1B2A3EB0E41103700A72928 /* ResKnifePluginProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = F5502C4001C579FF01C57124 /* ResKnifePluginProtocol.h */; }; E1B2A3EC0E41103700A72928 /* ApplicationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = F5B5881D0156D40B01000001 /* ApplicationDelegate.h */; }; @@ -27,7 +38,6 @@ E1B2A3FB0E41103700A72928 /* PasteboardDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = F5F1071603CCC61E01A8010A /* PasteboardDocument.h */; }; E1B2A3FC0E41103700A72928 /* PasteboardWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = F5F1071A03CCFAAC01A8010A /* PasteboardWindowController.h */; }; E1B2A3FD0E41103700A72928 /* RKDocumentController.h in Headers */ = {isa = PBXBuildFile; fileRef = F59481AD03D0776C01A8010A /* RKDocumentController.h */; }; - E1B2A3FE0E41103700A72928 /* OpenFileDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = F59481B103D077DC01A8010A /* OpenFileDataSource.h */; }; E1B2A3FF0E41103700A72928 /* RKEditorRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D35755C04DAEB6200B8225B /* RKEditorRegistry.h */; }; E1B2A4000E41103700A72928 /* RKSupportResourceRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D53A9FD04F171DC006651FA /* RKSupportResourceRegistry.h */; }; E1B2A4020E41103700A72928 /* AboutPanel.nib in Resources */ = {isa = PBXBuildFile; fileRef = F5B588360156D40B01000001 /* AboutPanel.nib */; }; @@ -70,18 +80,11 @@ E1B2A42D0E41103700A72928 /* PasteboardDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = F5F1071703CCC61E01A8010A /* PasteboardDocument.m */; }; E1B2A42E0E41103700A72928 /* PasteboardWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5F1071B03CCFAAC01A8010A /* PasteboardWindowController.m */; }; E1B2A42F0E41103700A72928 /* RKDocumentController.m in Sources */ = {isa = PBXBuildFile; fileRef = F59481AE03D0776C01A8010A /* RKDocumentController.m */; }; - E1B2A4300E41103700A72928 /* OpenFileDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = F59481B203D077DC01A8010A /* OpenFileDataSource.m */; }; E1B2A4310E41103700A72928 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D35755A04DAEB4300B8225B /* main.m */; }; E1B2A4320E41103700A72928 /* RKEditorRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D35755D04DAEB6200B8225B /* RKEditorRegistry.m */; }; E1B2A4330E41103700A72928 /* RKSupportResourceRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D53A9FE04F171DC006651FA /* RKSupportResourceRegistry.m */; }; E1B2A4350E41103700A72928 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5B5884B0156D40B01000001 /* Cocoa.framework */; }; E1B2A4360E41103700A72928 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5B5884A0156D40B01000001 /* Carbon.framework */; }; - E1B2A4400E41103800A72928 /* ResKnifeResourceProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = F5CDEBAB01FC893201A80001 /* ResKnifeResourceProtocol.h */; }; - E1B2A4410E41103800A72928 /* ResKnifePluginProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = F5502C4001C579FF01C57124 /* ResKnifePluginProtocol.h */; }; - E1B2A4420E41103800A72928 /* HexEditorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = F5EF83A0020C08E601A80001 /* HexEditorDelegate.h */; }; - E1B2A4430E41103800A72928 /* HexTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = F5EF83A2020C08E601A80001 /* HexTextView.h */; }; - E1B2A4440E41103800A72928 /* HexWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = F5EF83A7020C08E601A80001 /* HexWindowController.h */; }; - E1B2A4450E41103800A72928 /* FindSheetController.h in Headers */ = {isa = PBXBuildFile; fileRef = F54E6220021B6A0801A80001 /* FindSheetController.h */; }; E1B2A4470E41103800A72928 /* HexWindow.nib in Resources */ = {isa = PBXBuildFile; fileRef = F5EF83C7020C20D701A80001 /* HexWindow.nib */; }; E1B2A4480E41103800A72928 /* FindSheet.nib in Resources */ = {isa = PBXBuildFile; fileRef = F54E6222021B6A0801A80001 /* FindSheet.nib */; }; E1B2A4490E41103800A72928 /* PasteMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = F5606FDD02ACF2F701A8010C /* PasteMenu.nib */; }; @@ -98,19 +101,6 @@ E1B2A4600E41103800A72928 /* Element.m in Sources */ = {isa = PBXBuildFile; fileRef = F535444E0226B5F501A80001 /* Element.m */; }; E1B2A4610E41103800A72928 /* Notifications.m in Sources */ = {isa = PBXBuildFile; fileRef = F5C9ECCE027F474A01A8010C /* Notifications.m */; }; E1B2A4630E41103800A72928 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5B5884B0156D40B01000001 /* Cocoa.framework */; }; - E1B2A46C0E41103800A72928 /* HostCallbacks.h in Headers */ = {isa = PBXBuildFile; fileRef = F5B588920156D6D901000001 /* HostCallbacks.h */; }; - E1B2A46D0E41103800A72928 /* BoomWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = F58F6B7E025BC3A501A8010C /* BoomWindowController.h */; }; - E1B2A46E0E41103800A72928 /* NovaWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = F58F6B82025BC73001A8010C /* NovaWindowController.h */; }; - E1B2A46F0E41103800A72928 /* CharWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = F58F6B86025BC76D01A8010C /* CharWindowController.h */; }; - E1B2A4700E41103800A72928 /* ColrWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = F58F6B8A025BC7C001A8010C /* ColrWindowController.h */; }; - E1B2A4710E41103800A72928 /* CronWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = F58F6B8E025BCF5901A8010C /* CronWindowController.h */; }; - E1B2A4720E41103800A72928 /* DescWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = F58F6B93025BD97701A8010C /* DescWindowController.h */; }; - E1B2A4730E41103800A72928 /* ResKnifeResourceProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = F5CDEBAB01FC893201A80001 /* ResKnifeResourceProtocol.h */; }; - E1B2A4740E41103800A72928 /* ResKnifePluginProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = F5502C4001C579FF01C57124 /* ResKnifePluginProtocol.h */; }; - E1B2A4750E41103800A72928 /* DescSplitViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = F58A18410278355D01A8010C /* DescSplitViewDelegate.h */; }; - E1B2A4760E41103800A72928 /* DataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = F543AFDB027B2A5001A8010C /* DataSource.h */; }; - E1B2A4770E41103800A72928 /* Structs.h in Headers */ = {isa = PBXBuildFile; fileRef = F5C9ECD8027F562201A8010C /* Structs.h */; }; - E1B2A4780E41103800A72928 /* NSNumber-Range.h in Headers */ = {isa = PBXBuildFile; fileRef = F59D5DE8032106D201A8010C /* NSNumber-Range.h */; }; E1B2A47A0E41103800A72928 /* NovaTools.nib in Resources */ = {isa = PBXBuildFile; fileRef = F58F6BA9025BDBA701A8010C /* NovaTools.nib */; }; E1B2A47B0E41103800A72928 /* boom.nib in Resources */ = {isa = PBXBuildFile; fileRef = F5EDC612025BFB7301A8010C /* boom.nib */; }; E1B2A47C0E41103800A72928 /* char.nib in Resources */ = {isa = PBXBuildFile; fileRef = F5EDC615025BFB7C01A8010C /* char.nib */; }; @@ -244,28 +234,10 @@ E1B2A53F0E41103900A72928 /* Templar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F50DFE67036C258301A8010A /* Templar.cpp */; }; E1B2A5400E41103900A72928 /* TemplateWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F50DFE6D036C258301A8010A /* TemplateWindow.cpp */; }; E1B2A5430E41103900A72928 /* Templar.rsrc in Rez */ = {isa = PBXBuildFile; fileRef = F50DFE6C036C258301A8010A /* Templar.rsrc */; }; - E1B2A54B0E41103A00A72928 /* ResKnifePluginProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = F5502C4001C579FF01C57124 /* ResKnifePluginProtocol.h */; }; - E1B2A54C0E41103A00A72928 /* ResKnifeResourceProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = F5CDEBAB01FC893201A80001 /* ResKnifeResourceProtocol.h */; }; - E1B2A54D0E41103A00A72928 /* ICONWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D3B99B904DC16A30056861E /* ICONWindowController.h */; }; E1B2A54F0E41103A00A72928 /* ICONWindow.nib in Resources */ = {isa = PBXBuildFile; fileRef = 3D3B99BC04DC16FC0056861E /* ICONWindow.nib */; }; E1B2A5510E41103A00A72928 /* Notifications.m in Sources */ = {isa = PBXBuildFile; fileRef = F5C9ECCE027F474A01A8010C /* Notifications.m */; }; E1B2A5520E41103A00A72928 /* ICONWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D3B99B804DC16A30056861E /* ICONWindowController.m */; }; E1B2A5540E41103A00A72928 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5B5884B0156D40B01000001 /* Cocoa.framework */; }; - E1B2A55D0E41103A00A72928 /* NuTemplateWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D0B38B204DEF41E005AED5E /* NuTemplateWindowController.h */; }; - E1B2A55E0E41103A00A72928 /* ResKnifePluginProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = F5502C4001C579FF01C57124 /* ResKnifePluginProtocol.h */; }; - E1B2A55F0E41103A00A72928 /* ResKnifeResourceProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = F5CDEBAB01FC893201A80001 /* ResKnifeResourceProtocol.h */; }; - E1B2A5600E41103A00A72928 /* NuTemplateElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D0933A604DEFEE600DD74B1 /* NuTemplateElement.h */; }; - E1B2A5610E41103A00A72928 /* NuTemplateGroupElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D0933BA04DF10D700DD74B1 /* NuTemplateGroupElement.h */; }; - E1B2A5620E41103A00A72928 /* NuTemplateStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D0933BE04DF151C00DD74B1 /* NuTemplateStream.h */; }; - E1B2A5630E41103A00A72928 /* NuTemplateLSTBElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D0933C404DF1C0800DD74B1 /* NuTemplateLSTBElement.h */; }; - E1B2A5640E41103A00A72928 /* NuTemplateTNAMElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D0933E304DF2FF700DD74B1 /* NuTemplateTNAMElement.h */; }; - E1B2A5650E41103A00A72928 /* NuTemplatePSTRElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D0933E704DF317D00DD74B1 /* NuTemplatePSTRElement.h */; }; - E1B2A5660E41103A00A72928 /* NuTemplateDWRDElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D0933EF04DF381900DD74B1 /* NuTemplateDWRDElement.h */; }; - E1B2A5670E41103A00A72928 /* NuTemplateLSTEElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D0933F604DFE80500DD74B1 /* NuTemplateLSTEElement.h */; }; - E1B2A5680E41103A00A72928 /* NuTemplateDLNGElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D4737B404E872DB00AF65FE /* NuTemplateDLNGElement.h */; }; - E1B2A5690E41103A00A72928 /* NuTemplateDBYTElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D4737B804E873F300AF65FE /* NuTemplateDBYTElement.h */; }; - E1B2A56A0E41103A00A72928 /* NuTemplateOCNTElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D50046F04EF040900F3B64D /* NuTemplateOCNTElement.h */; }; - E1B2A56B0E41103A00A72928 /* NuTemplateLSTCElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D50047404EF122000F3B64D /* NuTemplateLSTCElement.h */; }; E1B2A56D0E41103A00A72928 /* NuTemplateWindow.nib in Resources */ = {isa = PBXBuildFile; fileRef = 3D0B38B504DEF465005AED5E /* NuTemplateWindow.nib */; }; E1B2A56F0E41103A00A72928 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3D2EE5E204E5C56F00515930 /* Localizable.strings */; }; E1B2A5710E41103A00A72928 /* NuTemplateWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D0B38B304DEF41E005AED5E /* NuTemplateWindowController.m */; }; @@ -289,18 +261,13 @@ E1B2A73F0E41218F00A72928 /* FontDocument.nib in Resources */ = {isa = PBXBuildFile; fileRef = E1B2A7350E41218F00A72928 /* FontDocument.nib */; }; E1B2A7400E41218F00A72928 /* FontWindow.nib in Resources */ = {isa = PBXBuildFile; fileRef = E1B2A7370E41218F00A72928 /* FontWindow.nib */; }; E1B2A7410E41219000A72928 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E1B2A7390E41218F00A72928 /* Localizable.strings */; }; - E1B2A7420E41219000A72928 /* Font Templates.rsrc in Resources */ = {isa = PBXBuildFile; fileRef = E1B2A73B0E41218F00A72928 /* Font Templates.rsrc */; }; E1B2A7430E41219000A72928 /* FontWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = E1B2A73D0E41218F00A72928 /* FontWindowController.m */; }; E1B2A7760E41230E00A72928 /* NGSCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = E1B2A7750E41230E00A72928 /* NGSCategories.m */; }; E1B2A7770E41230E00A72928 /* NGSCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = E1B2A7740E41230E00A72928 /* NGSCategories.h */; }; E1B2A7780E41230E00A72928 /* NGSCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = E1B2A7750E41230E00A72928 /* NGSCategories.m */; }; - E1B2A7790E41230E00A72928 /* NGSCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = E1B2A7740E41230E00A72928 /* NGSCategories.h */; }; E1B2A77A0E41230E00A72928 /* NGSCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = E1B2A7750E41230E00A72928 /* NGSCategories.m */; }; - E1B2A77B0E41230E00A72928 /* NGSCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = E1B2A7740E41230E00A72928 /* NGSCategories.h */; }; E1B2A77C0E41230E00A72928 /* NGSCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = E1B2A7750E41230E00A72928 /* NGSCategories.m */; }; - E1B2A77D0E41230E00A72928 /* NGSCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = E1B2A7740E41230E00A72928 /* NGSCategories.h */; }; E1B2A77E0E41230E00A72928 /* NGSCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = E1B2A7750E41230E00A72928 /* NGSCategories.m */; }; - E1B2A77F0E41230E00A72928 /* NGSCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = E1B2A7740E41230E00A72928 /* NGSCategories.h */; }; E1B2A7800E41230E00A72928 /* NGSCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = E1B2A7750E41230E00A72928 /* NGSCategories.m */; }; /* End PBXBuildFile section */ @@ -324,6 +291,20 @@ /* End PBXBuildRule section */ /* Begin PBXContainerItemProxy section */ + E17753B10E42526500F737F8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F5B5880F0156D2A601000001 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E1B2A7210E41213300A72928 /* Font Editor Cocoa */; + remoteInfo = "Font Editor Cocoa"; + }; + E17753B30E42526F00F737F8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F5B5880F0156D2A601000001 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E1B2A46A0E41103800A72928 /* NovaTools */; + remoteInfo = NovaTools; + }; E1B2A5870E41103A00A72928 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F5B5880F0156D2A601000001 /* Project object */; @@ -341,6 +322,28 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + E17753850E4250CA00F737F8 /* Copy Support Resources */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "Support Resources"; + dstSubfolderSpec = 7; + files = ( + E177538E0E4251D000F737F8 /* Templates.rsrc in Copy Support Resources */, + ); + name = "Copy Support Resources"; + runOnlyForDeploymentPostprocessing = 0; + }; + E177539C0E42520800F737F8 /* Copy Support Resources */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "Support Resources"; + dstSubfolderSpec = 7; + files = ( + E177539D0E42521800F737F8 /* Font Templates.rsrc in Copy Support Resources */, + ); + name = "Copy Support Resources"; + runOnlyForDeploymentPostprocessing = 0; + }; E1B2A4170E41103700A72928 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -349,6 +352,8 @@ files = ( E1B2A5930E41103A00A72928 /* Hexadecimal Editor.plugin in CopyFiles */, E1B2A5920E41103A00A72928 /* Template Editor.plugin in CopyFiles */, + E17753CD0E42528400F737F8 /* Font Editor.plugin in CopyFiles */, + E17753B90E42528100F737F8 /* NovaTools.plugin in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -372,12 +377,12 @@ 3D0933F404DFD7CF00DD74B1 /* TODO.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TODO.txt; sourceTree = SOURCE_ROOT; }; 3D0933F604DFE80500DD74B1 /* NuTemplateLSTEElement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NuTemplateLSTEElement.h; sourceTree = ""; }; 3D0933F704DFE80500DD74B1 /* NuTemplateLSTEElement.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NuTemplateLSTEElement.m; sourceTree = ""; }; - 3D0ABFB904E152CA00C85300 /* TEMPLATE EDITOR.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "TEMPLATE EDITOR.txt"; path = "NuTemplateEditor/TEMPLATE EDITOR.txt"; sourceTree = SOURCE_ROOT; }; + 3D0ABFB904E152CA00C85300 /* Read Me.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Read Me.txt"; sourceTree = ""; }; 3D0ABFBC04E172F700C85300 /* README.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = README.txt; sourceTree = SOURCE_ROOT; }; 3D0B38B204DEF41E005AED5E /* NuTemplateWindowController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NuTemplateWindowController.h; sourceTree = ""; }; 3D0B38B304DEF41E005AED5E /* NuTemplateWindowController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NuTemplateWindowController.m; sourceTree = ""; }; 3D0B38B604DEF465005AED5E /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/NuTemplateWindow.nib; sourceTree = ""; }; - 3D2EE5E204E5C56F00515930 /* Localizable.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; + 3D2EE5E204E5C56F00515930 /* Localizable.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = Localizable.strings; path = ../Localizable.strings; sourceTree = ""; }; 3D35755A04DAEB4300B8225B /* main.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 3D35755C04DAEB6200B8225B /* RKEditorRegistry.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = RKEditorRegistry.h; sourceTree = ""; }; 3D35755D04DAEB6200B8225B /* RKEditorRegistry.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = RKEditorRegistry.m; sourceTree = ""; }; @@ -395,6 +400,13 @@ 3D50047404EF122000F3B64D /* NuTemplateLSTCElement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NuTemplateLSTCElement.h; sourceTree = ""; }; 3D53A9FD04F171DC006651FA /* RKSupportResourceRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKSupportResourceRegistry.h; sourceTree = ""; }; 3D53A9FE04F171DC006651FA /* RKSupportResourceRegistry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKSupportResourceRegistry.m; sourceTree = ""; }; + E17752320E424BAB00F737F8 /* NSData-HexRepresentation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData-HexRepresentation.h"; sourceTree = ""; }; + E17752330E424BAB00F737F8 /* NSData-HexRepresentation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData-HexRepresentation.m"; sourceTree = ""; }; + E177526D0E424DCB00F737F8 /* OpenPanelDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OpenPanelDelegate.m; sourceTree = ""; }; + E177526E0E424DCB00F737F8 /* OpenPanelDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenPanelDelegate.h; sourceTree = ""; }; + E17753880E4251C600F737F8 /* DisplayTMPL.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = DisplayTMPL.png; sourceTree = ""; }; + E17753890E4251C600F737F8 /* Templates.rsrc */ = {isa = PBXFileReference; lastKnownFileType = archive.rsrc; name = Templates.rsrc; path = ../Templates.rsrc; sourceTree = ""; }; + E177538A0E4251C600F737F8 /* TMPLs.rsrc */ = {isa = PBXFileReference; lastKnownFileType = archive.rsrc; name = TMPLs.rsrc; path = ../TMPLs.rsrc; sourceTree = ""; }; E1B2A43B0E41103700A72928 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../Plug-Ins/Hex Editor/Info.plist"; sourceTree = ""; }; E1B2A43C0E41103800A72928 /* ResKnife Cocoa.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ResKnife Cocoa.app"; sourceTree = BUILT_PRODUCTS_DIR; }; E1B2A4560E41103800A72928 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -415,7 +427,7 @@ E1B2A5470E41103900A72928 /* Info-Uli_s_Template_Editor__Upgraded_.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-Uli_s_Template_Editor__Upgraded_.plist"; sourceTree = ""; }; E1B2A5480E41103A00A72928 /* Ulis Template Editor.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Ulis Template Editor.bundle"; sourceTree = BUILT_PRODUCTS_DIR; }; E1B2A55A0E41103A00A72928 /* Bitmap Editor.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Bitmap Editor.plugin"; sourceTree = BUILT_PRODUCTS_DIR; }; - E1B2A5850E41103A00A72928 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E1B2A5850E41103A00A72928 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../Info.plist; sourceTree = ""; }; E1B2A5860E41103A00A72928 /* Template Editor.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Template Editor.plugin"; sourceTree = BUILT_PRODUCTS_DIR; }; E1B2A7220E41213300A72928 /* Font Editor.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Font Editor.plugin"; sourceTree = BUILT_PRODUCTS_DIR; }; E1B2A7360E41218F00A72928 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/FontDocument.nib; sourceTree = ""; }; @@ -460,8 +472,6 @@ F543AFDB027B2A5001A8010C /* DataSource.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DataSource.h; sourceTree = ""; }; F543AFDC027B2A5001A8010C /* DataSource.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = DataSource.m; sourceTree = ""; }; F543AFF0027C716E01A8010C /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = "English.lproj/Resource Types.strings"; sourceTree = ""; }; - F54625C6029174F601A8010C /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = /System/Library/Frameworks/CoreServices.framework; sourceTree = ""; }; - F54626490291750201A8010C /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = ""; }; F54E6220021B6A0801A80001 /* FindSheetController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = FindSheetController.h; sourceTree = ""; }; F54E6221021B6A0801A80001 /* FindSheetController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = FindSheetController.m; sourceTree = ""; }; F54E6223021B6A0801A80001 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/FindSheet.nib; sourceTree = ""; }; @@ -494,8 +504,6 @@ F58F6BAA025BDBA701A8010C /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/NovaTools.nib; sourceTree = ""; }; F59481AD03D0776C01A8010A /* RKDocumentController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = RKDocumentController.h; sourceTree = ""; }; F59481AE03D0776C01A8010A /* RKDocumentController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = RKDocumentController.m; sourceTree = ""; }; - F59481B103D077DC01A8010A /* OpenFileDataSource.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = OpenFileDataSource.h; sourceTree = ""; }; - F59481B203D077DC01A8010A /* OpenFileDataSource.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = OpenFileDataSource.m; sourceTree = ""; }; F59D5DE40320DFF601A8010C /* NSString-FSSpec.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "NSString-FSSpec.h"; sourceTree = ""; }; F59D5DE50320DFF601A8010C /* NSString-FSSpec.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = "NSString-FSSpec.m"; sourceTree = ""; }; F59D5DE8032106D201A8010C /* NSNumber-Range.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "NSNumber-Range.h"; sourceTree = ""; }; @@ -527,15 +535,13 @@ F5B5883B0156D40B01000001 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/InfoWindow.nib; sourceTree = ""; }; F5B5883F0156D40B01000001 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/PrefsWindow.nib; sourceTree = ""; }; F5B588410156D40B01000001 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/ResourceDocument.nib; sourceTree = ""; }; - F5B588430156D40B01000001 /* English */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + F5B588430156D40B01000001 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; F5B588450156D40B01000001 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = English; path = English.lproj/Localizable.strings; sourceTree = ""; }; F5B588460156D40B01000001 /* ResKnife.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = ResKnife.icns; sourceTree = ""; }; F5B588470156D40B01000001 /* Resource file.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = "Resource file.icns"; sourceTree = ""; }; F5B588480156D40B01000001 /* Icon file.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = "Icon file.icns"; sourceTree = ""; }; F5B5884A0156D40B01000001 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; F5B5884B0156D40B01000001 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; - F5B5884C0156D40B01000001 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; - F5B5884D0156D40B01000001 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; F5B5887D0156D6D901000001 /* Carbon Prefix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "Carbon Prefix.h"; sourceTree = ""; }; F5B5887E0156D6D901000001 /* Classic Prefix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "Classic Prefix.h"; sourceTree = ""; }; F5B588800156D6D901000001 /* Transfer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Transfer.h; sourceTree = ""; }; @@ -744,6 +750,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + E17753350E424F0C00F737F8 /* Carbon.framework in Frameworks */, + E17753E40E42534300F737F8 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -778,7 +786,7 @@ 3D0B38A504DEF41E005AED5E /* Template Editor */ = { isa = PBXGroup; children = ( - 3D0ABFB904E152CA00C85300 /* TEMPLATE EDITOR.txt */, + 3D0ABFB904E152CA00C85300 /* Read Me.txt */, 3D0B38B204DEF41E005AED5E /* NuTemplateWindowController.h */, 3D0B38B304DEF41E005AED5E /* NuTemplateWindowController.m */, 3D0933A604DEFEE600DD74B1 /* NuTemplateElement.h */, @@ -788,9 +796,7 @@ 3D0933BE04DF151C00DD74B1 /* NuTemplateStream.h */, 3D0933BF04DF151C00DD74B1 /* NuTemplateStream.m */, 3D0933EB04DF319F00DD74B1 /* Field Types */, - E1B2A5850E41103A00A72928 /* Info.plist */, - 3D2EE5E204E5C56F00515930 /* Localizable.strings */, - 3D0B38B504DEF465005AED5E /* NuTemplateWindow.nib */, + E17753870E4251C600F737F8 /* Resources */, ); name = "Template Editor"; path = NuTemplateEditor; @@ -808,14 +814,36 @@ path = "Template Editor"; sourceTree = ""; }; + E17752310E424BAB00F737F8 /* Categories */ = { + isa = PBXGroup; + children = ( + E17752320E424BAB00F737F8 /* NSData-HexRepresentation.h */, + E17752330E424BAB00F737F8 /* NSData-HexRepresentation.m */, + ); + path = Categories; + sourceTree = ""; + }; + E17753870E4251C600F737F8 /* Resources */ = { + isa = PBXGroup; + children = ( + E1B2A5850E41103A00A72928 /* Info.plist */, + 3D2EE5E204E5C56F00515930 /* Localizable.strings */, + E17753880E4251C600F737F8 /* DisplayTMPL.png */, + E17753890E4251C600F737F8 /* Templates.rsrc */, + E177538A0E4251C600F737F8 /* TMPLs.rsrc */, + 3D0B38B504DEF465005AED5E /* NuTemplateWindow.nib */, + ); + path = Resources; + sourceTree = ""; + }; E1B2A7340E41218F00A72928 /* Font Editor */ = { isa = PBXGroup; children = ( E1B2A73C0E41218F00A72928 /* FontWindowController.h */, E1B2A73D0E41218F00A72928 /* FontWindowController.m */, - E1B2A73B0E41218F00A72928 /* Font Templates.rsrc */, E1B2A73E0E41218F00A72928 /* Info.plist */, E1B2A7390E41218F00A72928 /* Localizable.strings */, + E1B2A73B0E41218F00A72928 /* Font Templates.rsrc */, E1B2A7350E41218F00A72928 /* FontDocument.nib */, E1B2A7370E41218F00A72928 /* FontWindow.nib */, ); @@ -902,9 +930,10 @@ F5502C4001C579FF01C57124 /* ResKnifePluginProtocol.h */, F5CDEBAB01FC893201A80001 /* ResKnifeResourceProtocol.h */, F5EF839F020C08E601A80001 /* Hex Editor */, - E1B2A7340E41218F00A72928 /* Font Editor */, - 3D3B99B704DC167D0056861E /* Bitmap Editor */, 3D0B38A504DEF41E005AED5E /* Template Editor */, + 3D3B99B704DC167D0056861E /* Bitmap Editor */, + E1B2A7340E41218F00A72928 /* Font Editor */, + F5DF1C0F0254C78801A80001 /* NovaTools */, F5354435022673C101A80001 /* Template Editor (Abandoned) */, ); path = "Plug-Ins"; @@ -929,7 +958,6 @@ F5B5881A0156D40B01000001 /* Cocoa */, F5B5887F0156D6D901000001 /* Carbon */, F5EA10690254A7B401A80001 /* External */, - F5DF1C0F0254C78801A80001 /* NovaTools */, F5B588490156D40B01000001 /* Frameworks */, F5B588110156D30301000001 /* Products */, E1B2A4C30E41103800A72928 /* Info-ResKnife_Carbon__Upgraded_.plist */, @@ -987,8 +1015,8 @@ F5B588260156D40B01000001 /* InfoWindowController.m */, F5B588270156D40B01000001 /* NameFormatter.h */, F5B588280156D40B01000001 /* NameFormatter.m */, - F59481B103D077DC01A8010A /* OpenFileDataSource.h */, - F59481B203D077DC01A8010A /* OpenFileDataSource.m */, + E177526E0E424DCB00F737F8 /* OpenPanelDelegate.h */, + E177526D0E424DCB00F737F8 /* OpenPanelDelegate.m */, F5B588290156D40B01000001 /* OutlineViewDelegate.h */, F5B5882A0156D40B01000001 /* OutlineViewDelegate.m */, F5F1071603CCC61E01A8010A /* PasteboardDocument.h */, @@ -1050,11 +1078,7 @@ isa = PBXGroup; children = ( F5B5884A0156D40B01000001 /* Carbon.framework */, - F54625C6029174F601A8010C /* CoreServices.framework */, - F54626490291750201A8010C /* ApplicationServices.framework */, F5B5884B0156D40B01000001 /* Cocoa.framework */, - F5B5884C0156D40B01000001 /* AppKit.framework */, - F5B5884D0156D40B01000001 /* Foundation.framework */, ); name = Frameworks; sourceTree = ""; @@ -1251,7 +1275,8 @@ F58A183F0278353501A8010C /* Aux Support */, F51FB38E0256057F01A80001 /* Resources */, ); - path = NovaTools; + name = NovaTools; + path = ../../NovaTools; sourceTree = ""; }; F5EA10690254A7B401A80001 /* External */ = { @@ -1276,6 +1301,7 @@ F5EF83A7020C08E601A80001 /* HexWindowController.h */, F5EF83A8020C08E601A80001 /* HexWindowController.m */, E1B2A4560E41103800A72928 /* Info.plist */, + E17752310E424BAB00F737F8 /* Categories */, F5EF83C7020C20D701A80001 /* HexWindow.nib */, F5606FDD02ACF2F701A8010C /* PasteMenu.nib */, F54E6222021B6A0801A80001 /* FindSheet.nib */, @@ -1310,24 +1336,10 @@ E1B2A3FB0E41103700A72928 /* PasteboardDocument.h in Headers */, E1B2A3FC0E41103700A72928 /* PasteboardWindowController.h in Headers */, E1B2A3FD0E41103700A72928 /* RKDocumentController.h in Headers */, - E1B2A3FE0E41103700A72928 /* OpenFileDataSource.h in Headers */, E1B2A3FF0E41103700A72928 /* RKEditorRegistry.h in Headers */, E1B2A4000E41103700A72928 /* RKSupportResourceRegistry.h in Headers */, E1B2A7770E41230E00A72928 /* NGSCategories.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - E1B2A43F0E41103800A72928 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - E1B2A4400E41103800A72928 /* ResKnifeResourceProtocol.h in Headers */, - E1B2A4410E41103800A72928 /* ResKnifePluginProtocol.h in Headers */, - E1B2A4420E41103800A72928 /* HexEditorDelegate.h in Headers */, - E1B2A4430E41103800A72928 /* HexTextView.h in Headers */, - E1B2A4440E41103800A72928 /* HexWindowController.h in Headers */, - E1B2A4450E41103800A72928 /* FindSheetController.h in Headers */, - E1B2A7790E41230E00A72928 /* NGSCategories.h in Headers */, + E17752700E424DCB00F737F8 /* OpenPanelDelegate.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1340,27 +1352,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - E1B2A46B0E41103800A72928 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - E1B2A46C0E41103800A72928 /* HostCallbacks.h in Headers */, - E1B2A46D0E41103800A72928 /* BoomWindowController.h in Headers */, - E1B2A46E0E41103800A72928 /* NovaWindowController.h in Headers */, - E1B2A46F0E41103800A72928 /* CharWindowController.h in Headers */, - E1B2A4700E41103800A72928 /* ColrWindowController.h in Headers */, - E1B2A4710E41103800A72928 /* CronWindowController.h in Headers */, - E1B2A4720E41103800A72928 /* DescWindowController.h in Headers */, - E1B2A4730E41103800A72928 /* ResKnifeResourceProtocol.h in Headers */, - E1B2A4740E41103800A72928 /* ResKnifePluginProtocol.h in Headers */, - E1B2A4750E41103800A72928 /* DescSplitViewDelegate.h in Headers */, - E1B2A4760E41103800A72928 /* DataSource.h in Headers */, - E1B2A4770E41103800A72928 /* Structs.h in Headers */, - E1B2A4780E41103800A72928 /* NSNumber-Range.h in Headers */, - E1B2A77F0E41230E00A72928 /* NGSCategories.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; E1B2A4960E41103800A72928 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1460,40 +1451,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - E1B2A54A0E41103A00A72928 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - E1B2A54B0E41103A00A72928 /* ResKnifePluginProtocol.h in Headers */, - E1B2A54C0E41103A00A72928 /* ResKnifeResourceProtocol.h in Headers */, - E1B2A54D0E41103A00A72928 /* ICONWindowController.h in Headers */, - E1B2A77D0E41230E00A72928 /* NGSCategories.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - E1B2A55C0E41103A00A72928 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - E1B2A55D0E41103A00A72928 /* NuTemplateWindowController.h in Headers */, - E1B2A55E0E41103A00A72928 /* ResKnifePluginProtocol.h in Headers */, - E1B2A55F0E41103A00A72928 /* ResKnifeResourceProtocol.h in Headers */, - E1B2A5600E41103A00A72928 /* NuTemplateElement.h in Headers */, - E1B2A5610E41103A00A72928 /* NuTemplateGroupElement.h in Headers */, - E1B2A5620E41103A00A72928 /* NuTemplateStream.h in Headers */, - E1B2A5630E41103A00A72928 /* NuTemplateLSTBElement.h in Headers */, - E1B2A5640E41103A00A72928 /* NuTemplateTNAMElement.h in Headers */, - E1B2A5650E41103A00A72928 /* NuTemplatePSTRElement.h in Headers */, - E1B2A5660E41103A00A72928 /* NuTemplateDWRDElement.h in Headers */, - E1B2A5670E41103A00A72928 /* NuTemplateLSTEElement.h in Headers */, - E1B2A5680E41103A00A72928 /* NuTemplateDLNGElement.h in Headers */, - E1B2A5690E41103A00A72928 /* NuTemplateDBYTElement.h in Headers */, - E1B2A56A0E41103A00A72928 /* NuTemplateOCNTElement.h in Headers */, - E1B2A56B0E41103A00A72928 /* NuTemplateLSTCElement.h in Headers */, - E1B2A77B0E41230E00A72928 /* NGSCategories.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -1513,6 +1470,8 @@ dependencies = ( E1B2A5880E41103A00A72928 /* PBXTargetDependency */, E1B2A5900E41103A00A72928 /* PBXTargetDependency */, + E17753B20E42526500F737F8 /* PBXTargetDependency */, + E17753B40E42526F00F737F8 /* PBXTargetDependency */, ); name = "ResKnife Cocoa"; productInstallPath = "$(USER_APPS_DIR)"; @@ -1524,7 +1483,6 @@ isa = PBXNativeTarget; buildConfigurationList = E1B2A4530E41103800A72928 /* Build configuration list for PBXNativeTarget "Hex Editor Cocoa" */; buildPhases = ( - E1B2A43F0E41103800A72928 /* Headers */, E1B2A4460E41103800A72928 /* Resources */, E1B2A44A0E41103800A72928 /* Sources */, E1B2A4500E41103800A72928 /* Frameworks */, @@ -1563,11 +1521,9 @@ isa = PBXNativeTarget; buildConfigurationList = E1B2A4900E41103800A72928 /* Build configuration list for PBXNativeTarget "NovaTools" */; buildPhases = ( - E1B2A46B0E41103800A72928 /* Headers */, E1B2A4790E41103800A72928 /* Resources */, E1B2A4810E41103800A72928 /* Sources */, E1B2A48C0E41103800A72928 /* Frameworks */, - E1B2A48F0E41103800A72928 /* Rez */, ); buildRules = ( ); @@ -1701,11 +1657,9 @@ isa = PBXNativeTarget; buildConfigurationList = E1B2A5560E41103A00A72928 /* Build configuration list for PBXNativeTarget "Bitmap Editor Cocoa" */; buildPhases = ( - E1B2A54A0E41103A00A72928 /* Headers */, E1B2A54E0E41103A00A72928 /* Resources */, E1B2A5500E41103A00A72928 /* Sources */, E1B2A5530E41103A00A72928 /* Frameworks */, - E1B2A5550E41103A00A72928 /* Rez */, ); buildRules = ( ); @@ -1721,11 +1675,10 @@ isa = PBXNativeTarget; buildConfigurationList = E1B2A5820E41103A00A72928 /* Build configuration list for PBXNativeTarget "Template Editor Cocoa" */; buildPhases = ( - E1B2A55C0E41103A00A72928 /* Headers */, E1B2A56C0E41103A00A72928 /* Resources */, + E17753850E4250CA00F737F8 /* Copy Support Resources */, E1B2A5700E41103A00A72928 /* Sources */, E1B2A57F0E41103A00A72928 /* Frameworks */, - E1B2A5810E41103A00A72928 /* Rez */, ); buildRules = ( E1B2A5980E41103D00A72928 /* PBXBuildRule */, @@ -1744,6 +1697,7 @@ buildConfigurationList = E1B2A7260E41213400A72928 /* Build configuration list for PBXNativeTarget "Font Editor Cocoa" */; buildPhases = ( E1B2A71E0E41213300A72928 /* Resources */, + E177539C0E42520800F737F8 /* Copy Support Resources */, E1B2A71F0E41213300A72928 /* Sources */, E1B2A7200E41213300A72928 /* Frameworks */, ); @@ -1898,6 +1852,7 @@ files = ( E1B2A56D0E41103A00A72928 /* NuTemplateWindow.nib in Resources */, E1B2A56F0E41103A00A72928 /* Localizable.strings in Resources */, + E177538B0E4251C600F737F8 /* DisplayTMPL.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1908,7 +1863,6 @@ E1B2A73F0E41218F00A72928 /* FontDocument.nib in Resources */, E1B2A7400E41218F00A72928 /* FontWindow.nib in Resources */, E1B2A7410E41219000A72928 /* Localizable.strings in Resources */, - E1B2A7420E41219000A72928 /* Font Templates.rsrc in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1936,13 +1890,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - E1B2A48F0E41103800A72928 /* Rez */ = { - isa = PBXRezBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; E1B2A4BD0E41103800A72928 /* Rez */ = { isa = PBXRezBuildPhase; buildActionMask = 2147483647; @@ -1989,20 +1936,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - E1B2A5550E41103A00A72928 /* Rez */ = { - isa = PBXRezBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - E1B2A5810E41103A00A72928 /* Rez */ = { - isa = PBXRezBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXRezBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -2029,11 +1962,11 @@ E1B2A42D0E41103700A72928 /* PasteboardDocument.m in Sources */, E1B2A42E0E41103700A72928 /* PasteboardWindowController.m in Sources */, E1B2A42F0E41103700A72928 /* RKDocumentController.m in Sources */, - E1B2A4300E41103700A72928 /* OpenFileDataSource.m in Sources */, E1B2A4310E41103700A72928 /* main.m in Sources */, E1B2A4320E41103700A72928 /* RKEditorRegistry.m in Sources */, E1B2A4330E41103700A72928 /* RKSupportResourceRegistry.m in Sources */, E1B2A7780E41230E00A72928 /* NGSCategories.m in Sources */, + E177526F0E424DCB00F737F8 /* OpenPanelDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2047,6 +1980,7 @@ E1B2A44E0E41103800A72928 /* FindSheetController.m in Sources */, E1B2A44F0E41103800A72928 /* Notifications.m in Sources */, E1B2A77A0E41230E00A72928 /* NGSCategories.m in Sources */, + E17752350E424BAB00F737F8 /* NSData-HexRepresentation.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2204,12 +2138,23 @@ files = ( E1B2A7430E41219000A72928 /* FontWindowController.m in Sources */, E1B2A7760E41230E00A72928 /* NGSCategories.m in Sources */, + E17752760E424ED400F737F8 /* Notifications.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + E17753B20E42526500F737F8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E1B2A7210E41213300A72928 /* Font Editor Cocoa */; + targetProxy = E17753B10E42526500F737F8 /* PBXContainerItemProxy */; + }; + E17753B40E42526F00F737F8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E1B2A46A0E41103800A72928 /* NovaTools */; + targetProxy = E17753B30E42526F00F737F8 /* PBXContainerItemProxy */; + }; E1B2A5880E41103A00A72928 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = E1B2A43E0E41103800A72928 /* Hex Editor Cocoa */; @@ -2229,6 +2174,7 @@ 3D0B38B604DEF465005AED5E /* English */, ); name = NuTemplateWindow.nib; + path = ..; sourceTree = ""; }; 3D3B99BC04DC16FC0056861E /* ICONWindow.nib */ = { @@ -2444,19 +2390,17 @@ 84A45AA60E410BAD008598FD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_C_LANGUAGE_STANDARD = c99; GCC_OPTIMIZATION_LEVEL = 0; GCC_VERSION = com.apple.compilers.llvmgcc42; - MACOSX_DEPLOYMENT_TARGET = 10.1; }; name = Debug; }; 84A45AA70E410BAD008598FD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_C_LANGUAGE_STANDARD = c99; GCC_VERSION = com.apple.compilers.llvmgcc42; - MACOSX_DEPLOYMENT_TARGET = 10.1; }; name = Release; }; @@ -2470,7 +2414,6 @@ GCC_PREFIX_HEADER = "Prefix Files/C99 Prefix.h"; GCC_SYMBOLS_PRIVATE_EXTERN = NO; INFOPLIST_FILE = Cocoa/Info.plist; - OTHER_LDFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = "ResKnife Cocoa"; PROFILING_CODE = YES; @@ -2493,7 +2436,6 @@ GCC_PREFIX_HEADER = "Prefix Files/C99 Prefix.h"; GCC_SYMBOLS_PRIVATE_EXTERN = NO; INFOPLIST_FILE = Cocoa/Info.plist; - OTHER_LDFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = "ResKnife Cocoa"; PROFILING_CODE = YES;