From 7f491cbb39b6615dbd01c495df0bd5e24686fdbc Mon Sep 17 00:00:00 2001
From: Nicholas Shanks
Date: Thu, 31 Jul 2008 20:27:55 +0000
Subject: [PATCH] Changes accumulated on my G5 while repository was
inaccessible.
---
.../Categories/NSOutlineView-SelectedItems.m | 2 +-
Cocoa/Categories/NSString-FSSpec.h | 8 +
Cocoa/Categories/NSString-FSSpec.m | 39 +-
Cocoa/Classes/ApplicationDelegate.h | 117 +-
Cocoa/Classes/ApplicationDelegate.m | 218 ++-
Cocoa/Classes/CreateResourceSheetController.m | 81 +-
Cocoa/Classes/InfoWindowController.h | 4 +-
Cocoa/Classes/InfoWindowController.m | 90 +-
Cocoa/Classes/NameFormatter.m | 3 +
Cocoa/Classes/OpenFileDataSource.h | 19 -
Cocoa/Classes/OpenFileDataSource.m | 113 --
Cocoa/Classes/OpenPanelDelegate.h | 43 +
Cocoa/Classes/OpenPanelDelegate.m | 131 ++
Cocoa/Classes/OutlineViewDelegate.h | 19 +-
Cocoa/Classes/OutlineViewDelegate.m | 263 ++--
Cocoa/Classes/PasteboardDocument.m | 54 +-
Cocoa/Classes/PrefsWindowController.h | 7 -
Cocoa/Classes/PrefsWindowController.m | 26 +-
Cocoa/Classes/RKDocumentController.m | 35 +-
Cocoa/Classes/RKEditorRegistry.h | 11 +-
Cocoa/Classes/RKEditorRegistry.m | 227 +--
Cocoa/Classes/RKSupportResourceRegistry.h | 7 +-
Cocoa/Classes/RKSupportResourceRegistry.m | 56 +-
Cocoa/Classes/Resource.h | 17 +-
Cocoa/Classes/Resource.m | 122 +-
Cocoa/Classes/ResourceDataSource.h | 56 +-
Cocoa/Classes/ResourceDataSource.m | 188 +--
Cocoa/Classes/ResourceDocument.h | 31 +-
Cocoa/Classes/ResourceDocument.m | 1300 ++++++++++-------
Cocoa/Classes/ResourceNameCell.m | 30 +-
Cocoa/Classes/SizeFormatter.m | 2 +-
Cocoa/English.lproj/AboutPanel.nib/info.nib | 12 +-
.../English.lproj/AboutPanel.nib/objects.nib | Bin 2228 -> 4905 bytes
.../English.lproj/Application.nib/classes.nib | 30 +-
Cocoa/English.lproj/Application.nib/info.nib | 15 +-
.../Application.nib/keyedobjects.nib | Bin 0 -> 26686 bytes
.../English.lproj/Application.nib/objects.nib | Bin 9377 -> 11162 bytes
.../CreateResourceSheet.nib/info.nib | 8 +-
.../CreateResourceSheet.nib/keyedobjects.nib | Bin 0 -> 9039 bytes
Cocoa/English.lproj/Credits.rtf | 30 +
Cocoa/English.lproj/InfoPlist.strings | Bin 236 -> 508 bytes
.../English.lproj/InfoWindow.nib/classes.nib | 3 +-
Cocoa/English.lproj/InfoWindow.nib/info.nib | 14 +-
.../InfoWindow.nib/keyedobjects.nib | Bin 0 -> 10492 bytes
.../English.lproj/InfoWindow.nib/objects.nib | Bin 3985 -> 4034 bytes
Cocoa/English.lproj/Localizable.strings | Bin 3338 -> 4484 bytes
.../ResourceDocument.nib/classes.nib | 8 +-
.../ResourceDocument.nib/info.nib | 10 +-
.../ResourceDocument.nib/objects.nib | Bin 2848 -> 2719 bytes
.../Categories/NSData-HexRepresentation.h | 11 +
.../Categories/NSData-HexRepresentation.m | 95 ++
.../English.lproj/HexWindow.nib/classes.nib | 21 +-
.../English.lproj/HexWindow.nib/info.nib | 28 +-
.../HexWindow.nib/keyedobjects.nib | Bin 0 -> 12717 bytes
.../English.lproj/HexWindow.nib/objects.nib | Bin 5909 -> 0 bytes
.../Plug-Ins/Hex Editor/FindSheetController.m | 2 +-
Cocoa/Plug-Ins/Hex Editor/HexEditorDelegate.h | 29 +-
Cocoa/Plug-Ins/Hex Editor/HexEditorDelegate.m | 219 +--
Cocoa/Plug-Ins/Hex Editor/HexTextView.h | 16 +-
Cocoa/Plug-Ins/Hex Editor/HexTextView.m | 527 +++----
.../Plug-Ins/Hex Editor/HexWindowController.h | 40 +-
.../Plug-Ins/Hex Editor/HexWindowController.m | 262 ++--
Cocoa/Plug-Ins/Hex Editor/Info.plist | 27 +-
Cocoa/Resources/Apply3.png | Bin 0 -> 3341 bytes
Cocoa/Resources/Button_certified.png | Bin 0 -> 1653 bytes
Cocoa/Resources/Button_reload.png | Bin 0 -> 1420 bytes
Cocoa/Resources/Create48.png | Bin 0 -> 4750 bytes
Cocoa/Resources/Delete.png | Bin 0 -> 2367 bytes
Cocoa/Resources/Delete.svg | 93 ++
Cocoa/Resources/Delete3.png | Bin 0 -> 3590 bytes
Cocoa/Resources/Go3.png | Bin 0 -> 3276 bytes
Cocoa/Resources/Help.png | Bin 0 -> 2622 bytes
Cocoa/Resources/Help3.png | Bin 0 -> 3870 bytes
Cocoa/Resources/ResKnife-old.icns | Bin 0 -> 61179 bytes
Cocoa/Resources/ResKnife.icns | Bin 61179 -> 39176 bytes
.../Resources/Resource Type Mappings.strings | 43 +
Cocoa/Resources/Resource file-old.icns | Bin 0 -> 58820 bytes
Cocoa/Resources/Resource file.icns | Bin 58820 -> 6918 bytes
Cocoa/Resources/Search3.png | Bin 0 -> 2906 bytes
Cocoa/Resources/add.png | Bin 0 -> 3579 bytes
Cocoa/Resources/defaults.plist | 2 +-
Cocoa/Resources/delete-1.png | Bin 0 -> 3788 bytes
Cocoa/cy.lproj/Application.nib/classes.nib | 88 ++
Cocoa/cy.lproj/Application.nib/info.nib | 27 +
.../cy.lproj/Application.nib/keyedobjects.nib | Bin 0 -> 26672 bytes
Cocoa/cy.lproj/Application.nib/objects.nib | Bin 0 -> 11347 bytes
Cocoa/cy.lproj/InfoPlist.strings | 6 +
Cocoa/cy.lproj/Localizable.strings | Bin 0 -> 4416 bytes
Cocoa/gd.lproj/Application.nib/classes.nib | 88 ++
Cocoa/gd.lproj/Application.nib/info.nib | 27 +
.../gd.lproj/Application.nib/keyedobjects.nib | Bin 0 -> 26900 bytes
Cocoa/gd.lproj/Application.nib/objects.nib | Bin 0 -> 11342 bytes
Cocoa/gd.lproj/InfoPlist.strings | 6 +
Cocoa/gd.lproj/Localizable.strings | Bin 0 -> 5126 bytes
Cocoa/ja.lproj/Application.nib/classes.nib | 50 +
Cocoa/ja.lproj/Application.nib/info.nib | 23 +
.../ja.lproj/Application.nib/keyedobjects.nib | Bin 0 -> 25589 bytes
Cocoa/main.m | 4 +
ICONEditor/ICONWindowController.m | 2 +-
.../{TEMPLATE EDITOR.txt => Read Me.txt} | 32 +-
NuTemplateEditor/Resources/DisplayTMPL.png | Bin 0 -> 274 bytes
NuTemplateEditor/TMPLs.rsrc | Bin 0 -> 15074 bytes
NuTemplateEditor/Templates.rsrc | Bin 0 -> 22260 bytes
ResKnife.xcodeproj/project.pbxproj | 292 ++--
104 files changed, 3328 insertions(+), 2181 deletions(-)
delete mode 100644 Cocoa/Classes/OpenFileDataSource.h
delete mode 100644 Cocoa/Classes/OpenFileDataSource.m
create mode 100644 Cocoa/Classes/OpenPanelDelegate.h
create mode 100644 Cocoa/Classes/OpenPanelDelegate.m
create mode 100644 Cocoa/English.lproj/Application.nib/keyedobjects.nib
create mode 100644 Cocoa/English.lproj/CreateResourceSheet.nib/keyedobjects.nib
create mode 100644 Cocoa/English.lproj/Credits.rtf
create mode 100644 Cocoa/English.lproj/InfoWindow.nib/keyedobjects.nib
create mode 100644 Cocoa/Plug-Ins/Hex Editor/Categories/NSData-HexRepresentation.h
create mode 100644 Cocoa/Plug-Ins/Hex Editor/Categories/NSData-HexRepresentation.m
create mode 100644 Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/keyedobjects.nib
delete mode 100644 Cocoa/Plug-Ins/Hex Editor/English.lproj/HexWindow.nib/objects.nib
create mode 100644 Cocoa/Resources/Apply3.png
create mode 100644 Cocoa/Resources/Button_certified.png
create mode 100644 Cocoa/Resources/Button_reload.png
create mode 100644 Cocoa/Resources/Create48.png
create mode 100644 Cocoa/Resources/Delete.png
create mode 100644 Cocoa/Resources/Delete.svg
create mode 100644 Cocoa/Resources/Delete3.png
create mode 100644 Cocoa/Resources/Go3.png
create mode 100644 Cocoa/Resources/Help.png
create mode 100644 Cocoa/Resources/Help3.png
create mode 100644 Cocoa/Resources/ResKnife-old.icns
create mode 100644 Cocoa/Resources/Resource Type Mappings.strings
create mode 100644 Cocoa/Resources/Resource file-old.icns
create mode 100644 Cocoa/Resources/Search3.png
create mode 100644 Cocoa/Resources/add.png
create mode 100644 Cocoa/Resources/delete-1.png
create mode 100644 Cocoa/cy.lproj/Application.nib/classes.nib
create mode 100644 Cocoa/cy.lproj/Application.nib/info.nib
create mode 100644 Cocoa/cy.lproj/Application.nib/keyedobjects.nib
create mode 100644 Cocoa/cy.lproj/Application.nib/objects.nib
create mode 100644 Cocoa/cy.lproj/InfoPlist.strings
create mode 100644 Cocoa/cy.lproj/Localizable.strings
create mode 100644 Cocoa/gd.lproj/Application.nib/classes.nib
create mode 100644 Cocoa/gd.lproj/Application.nib/info.nib
create mode 100644 Cocoa/gd.lproj/Application.nib/keyedobjects.nib
create mode 100644 Cocoa/gd.lproj/Application.nib/objects.nib
create mode 100644 Cocoa/gd.lproj/InfoPlist.strings
create mode 100644 Cocoa/gd.lproj/Localizable.strings
create mode 100644 Cocoa/ja.lproj/Application.nib/classes.nib
create mode 100644 Cocoa/ja.lproj/Application.nib/info.nib
create mode 100644 Cocoa/ja.lproj/Application.nib/keyedobjects.nib
rename NuTemplateEditor/{TEMPLATE EDITOR.txt => Read Me.txt} (75%)
create mode 100644 NuTemplateEditor/Resources/DisplayTMPL.png
create mode 100644 NuTemplateEditor/TMPLs.rsrc
create mode 100644 NuTemplateEditor/Templates.rsrc
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 05ca5a12e8557894664b330d5956ec314ea486c9..f6d72e60c4fa03ac73a1f27468e8ce86d2eb610b 100644
GIT binary patch
literal 4905
zcmb_gU2Gf25#ALkT0e9wDVAfoj!Pr~Y%7gQDXx(UK}A`q93?Srm`YsK2!hk`WM6aQ
zvB#4o0e$e%%X{hDfg;FQB^n_$YFf5Zp*7IHxadPlAOQQ&_Mt`LqOAZLp8Bf&3jtUWqC*Dz&1#pl$t7Ou8DybMO?}!yAS7IU@
zLz}HP&z@8BrZl4|wowAdo
zp9re59Ag}l#hxbHCKOHkmJne8%BHHP=(*Q5k+^XzT~)I|exGGoj4=@;(?qe@3A5i!
zsk2I#4L~et5>7ajFTEhz&%b=>fW5`$naos{WD+~c4E(3SD*)rO`vsR-
zg;!XG*|{8ajUoa$XH^8`Sd2`?LML=hhjS_+5%6?}5=$9VnX_R=SoVDH%GlyYB_P^n
zXRnM6R&H>coxnu1{Q
zPuwGlr2Vm>73b4BChc#^$h5^){oeW1+u;;cY(zps204x7?=%}(|1(7Xdg&nc=*TPH
z9^H$@V2{3z{iv}^_&2drWhOOpli4LkSb=rU?R8Hxy65Uk?V!A$}Bw@<7MAP}e{l@$Okc{Q7$O%z44SUS)IQUyUvds`({*r_tYsi$XI6w*6ueB#wUNQ
zRXyPbK<%I)h|-Y8TG4#$?gwazMB+Vwc7jM>D|8UXF3zgY*a
zC<#2%*+TpLajk(*c@N=6juP+h2YT1s@U9m`X9>-JRC@@YFxS@B{y;q@K3G~>`drub
z8{p4_UI%?0^fZXhs~zF{_5$zb;^N|GnBQ0f_*X$kKm(v|(00xb-~Pk?`Z4djpttF{
zX$^mZ&L3jDAGDM6kMK$UIl@Cv>wkqPolgGD_Qw+JVT>k9$AdU+F4}3%HZB=@w`M
z7lZC?&%1{!hQ)q=)xBq5_U_qKTIMWn@ICHrl>XhjC@Relp(N*2MPPPz6qQ~0y`=7=
zjJoiS`0OF5m6dbYYUldC1o8>LGG^?X_$^&g-3#y=0~XI(>?69UQ}322bXmXa{MEa(
zi*_pV)h@KGfGLf8^It3uA6T4vOmKg8Df=9)h-!d0LKeS*y-xpMsC>R$n~qNC=_NzG
zaM6tX=6Yl#8XbP=_}3zFRlcZeQYMnPD5c)d3`NE@EpnEmG7;27ig8Iv4(*_j?RKx`
ztP*Y%vZc*Jwp^%H>5B;N=-&4dzpkRLh0(
zy;4V^gt`2fU06mRrh4>nCUSZqg%3A{8ofGh7w$n{*Z7P+Z+hAat9Zfe!Unw`whOnZ
zAF&Hn>c4^1`{mD(jDD@RH=c;AGpFZGO~HqTR0wBUY1
z!e<;_+aFIyeNf5TTCrIUHi{hYO6i9
z>b}}Cul@5tt=wC?y|=c}SG(6=`|rV8ZBOZ0D&+1Id!|lI=&~+N8mPq=bmM*7{vSU@
BpMC%U
literal 2228
zcmb7F|7#pY6rZ_on&jGEVq4pya7aO-HC)qLBp|)?BsFclmTqZ(5RuK^T)TF2d+u$T
z1_Zm8+4)siE=#ipE@;yl>-9?%BNjxoHa!jsK?D&ir35Pe1^(3f-tOI{jlbv~J2SWQ
z=Dqj%ye|{!GAD{GS27JIk6PhBIZboU*Jq{SeTPThV~Y8PY|0k7$NAG)DfmKcClJYu
zl}vp!_}E+Fp)?-dkd@vVGvyJDNv5F|PE!A5b6w%|<+dJS3r^K87Y-$|(h0SY)5njo
z(V`}s%%Xo+_&Qb1!=tA0QNkgMerVB)7X96#k@B^(gz)pzM8NP=RyraVn1<`fag~h=
z&>720xa{S;D=QskrJ`QQF@sbb+ao-VDp5H;0h*@QgN7kb5Y9nou&j7d#0yG}v9soo
z%Cy{8&O5SFMlYC#uJz73Rj=Gh2&vle9JI)97X9FPLU44dj*UMaQ57XkC@4mT+L|&<
z(_ZI14#pHy)eHE)+xHz;gFvdPmLv2bDU&qdjiB3ndb$Jf94x4L*4acl1ioylc-wjO
z?Ol76cVghpHh3RCB8SKrQHV-%L?-)*0b>Ey43yFQ7ETpXqcu{C3EKu+0}xx=nd4z4
zokmxIgp97~z*;TF!-4IXG*L3yr~o(ON}X#1c(0
zM7=7yKtM(SHFUrh4!^jT#oNGN!e=qjV(|hJIRDuyA@iVp-En4=fVyO=Z+C8&;8(ou
zm;Vd?FV+EW#2x|cSA5`$SIO0{z@;zIAwT59qw-0HX|(MU`WJ6T88#tgtvqKYa23r;
z5-cFYhp7X3xG5_QsTwQDqpWmfM3;@+F(@=4;ug7`N)eJuJ%MV~8V0`iasZOdgC!`A
zpwb}d`k0}x-iiyjAvV5u7>I7jpJGu{6&aIg82A?ATZm{_RaJ0V)M$(07n%s{12KzE
zu19_+*FWLOT7CyIdU3)~Po6T9Kg=ilQ>mS=^zTS!Rppef$)%)pN-mr(^(6;2EqN5B
zN=fjO8E06o?|%fNs~L=VI~Yq(1_St(6eu?hqhXQze%1AygR0}7dSMJ#C-9`HMcf~l
zbsAood>`yWJp7i1`M26&Zu#F(y7jGZ*16-Ads+-3H${tl(wdz+K`lE#I5Zd32D#$2
z)4aoM=l9n@_Z9CssK)-+f@<6dTA>=>gv!&%85m8dNrlMmETqCCs4KicsaOas!}NZc
zoOjPTwx6k(ub8o|mk40XrWC}Qe&Xyjz^i3^c@+AlqWlW*q3dSGjFN72R~G0ahu?I6
z!rJkhoIW0G2?4r^AW)c1J_E*?VUeFN`HVLCCTQ292HoJ>#nHD4-QxlPwVehJ=ko(l
zt?_qrdP~cIuioK*{7co$%n6|VM9&%}Khnv)#^%gT1nfgSu+^evi-3(__7fMwa@V;R
zgoj75qpO|plsUN4W(Y40JcctI@PERlJc9DD5hYgmuNno)KeEVOAUw1Hr>ESxs$2Ek
z#j4w=x=U4ex#8ZZ;*|yYc(}+4`ww9Tg=QT-ix&$fBX#aK!x92}z?kLFZKKT;14*C8
z)_!YtRsu
zk4rA$_HN-Wi#YFuRQhg_z=&{xqBsww)hSwUY_5Bo>ML97%Ny!T&(!A<^&1=O
li#_#fcm2V$_4`}BZCDTIrxU~bGP
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 0000000000000000000000000000000000000000..97761a838ef0aab813739fcbddc4c9a149664a5a
GIT binary patch
literal 26686
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
YYrwvM!wg^moHhpo?qv`fMh0900J)DCegFUf
literal 0
HcmV?d00001
diff --git a/Cocoa/English.lproj/Application.nib/objects.nib b/Cocoa/English.lproj/Application.nib/objects.nib
index 5cd86bad5ef9e2f758d93cd86d77fe1539f2641d..5525d74e278b6566986ef90ebbf42e27890dcee1 100644
GIT binary patch
literal 11162
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
Lw%u5%ZTtTL9l?N=
literal 9377
zcmb7K3vg7|c|I3C(CUehksl>w?LdTS?EzyPthz=*pdcQLq6O??d*apZm9%EHSGl{(
z!>S2KzI!LrHseMk&RB{ZQ|denL1BVDaUGnXme#bVLrd$rrln;QS{j#z6f(4=G|=xq
zckkU@2}wj6?YaBEU+48-6;~wp^y!gA($qt}yyz#E)?`^$bs*TbdUNLsdN{czlnil}
zv8=K{(1Eb@phMDBP2D!?x4bH^euA0W)DX
zMR)6wU^0}{fkMpiYb$#WkQ3GIrrxESdOWNr9)9>?`;7rMhq)GfJ^f}O$G{23*c;Q+
zsL%v;b7vGHC&{y_6RFN#nAIGnus14$tk+&+)~my19+!1sgem7f9|*cY^2N5Zrr)U{
zRqLV=y&%3?k3Fr6WPXL>+MMjs&B-1VM#S;9$Jpg>j>RT<>(P$nB3|{mRJ1VV+83rW
ziDFQR7*L_=P5=%}@A{}fsi}B61*)pXMm#IYf^n7e`W8&Gr0x&syC^h@thPp>>r#o7p*jK=+I^sZ1Umh{ti!&EBt=~Ylf?^Eo%qRAdlz!tr?FGfX7R<4~{
z#DbiNV4oh2c16Q}SBC2>iMeO0YIkB)r^JL3{&o89Py}*%Lov^b+w-2tg`sxs?mojz
z`jdvgH5$`N?{t&LQF(WcNzL**WWCeWl()x1d;CEo6^|%K`VfS>rVqm04t=K%HzpQK
z$5d&+YC1!mj_1{BF~x>zw2C5^Q8ikG;#B6vr_zz5oxQnFkJI8srD4oWUqSS6CalMk
zlk#yee?=;wgn=3Ms>M?yCRcnlJ3M*eSk++g%4h%HSw4^!#
zDQSrr38(sZ&Fo@vFtpPlPRvYPPQ?CZwf<8GgEO;~Iu)TJZca}=gM;UtJkCJn)kRf1
z_C0@4Pp10ZjJHp(U#-yXI07VH2CDemZgNz%L|z%B1-&iaWwPbC(izY6~rMyO_Y$q4XP$)6PsI%T~oT~(ff!VyY+4-m)$dSR4EP|?RG|uR6->+
zGBa2G)&xy0$^edL{w~AB)^i&N^Yi9-R2K}JMl44AoX4-UzA%+mOf_|sbLzAVTXZaj
zQ~ET)d>lb6q98V^AXd06ZXg?{QkCPWR;OgY%c(5<&6(NmD`dk;>Hlpb<==t}%Orc<
z*pPqTa4fN(Xl^c
z53bEh_>)N#MMN>~O2FJJeF~{Jc1?2dU@=kB33C%nQqwTtBxJi`^!lgZa-Ago4tdR{
zXgt~*eNlH=XukJOmyEy+M~a+mXZ_w{7_rWfImv^`w%p}hvdEY$(@ZC|U%{451~ykr
zjMbs=jyter?rOTN(h8_fjizy%Gm7g06>v4q7f#Ug&lRWw;ELDjvA#(<50A*#MXQ33UvLeY5ka>B|dRn{!h=CfNxQAV5`7o%--$HwJ*
z3*F6Ij998Sj!S!4Q&(44Q&Tt$nI(N86GxrJix-2kHcPoq$MM^2!7?#lUGBGFIc$yU
zu?T^lw5&?plGM6I-)q&Cot~P}4~Rwwiup}8P==G1RosLhQqr!JKm#uKt%&lxp^=4F
z$ur9z3qMy1!u#-d^A_94QY_4(EW$$Uv&_V29M3ugX*Xg89eQ1}t>jp#Rpgja8r*}8w3mWi)1zD@#}pUQ6@8a!
zSV*kiHwW%ocC$&ja42n=BMZNMCq^Erf^lSEF%0~UbF#Ipifl-?MfF|3Wm!Hn8Q?z_
z@CQ{e2#hCCp&t5-IKs|U7X*zF+Cd55xR=@@h`F=y6Ns}PyFh0;DLH!w(<1|ODe#pP
zcqCc+um?oH2)Ya9|kovL7q|;g2I8~uPBp8oLs9g71EMk
z{JzIDG}bCqC88P|8>0vm>8GHlPZ0bB~sojWrxx)x|sf>Oig3Wnf59LbFwMO⋙2t+By=ggj?J0^5fWXOc#AmQ@0d+Duk77-_>oV{k}%yQU&g~g*XyLgm(i%0)a4WZ`jYmT{g(JZL&
zbo;u^Xkj(MUQ|Jg?NM=RT-5kPM?l=`6LMUv^oi#J
z;-hi#P=g3;6h}tIda}&gFP?7|?~IDA0WsGn=KDnBN^y2v+{lQHt)e|3PLGSRQE??B
zZe>L0^Wup{v9v)%Iz?u^*cK2Aed66w(YjwO@QE|yqS7bMjf<-paWf-WMqJ2Tg(Nlz-Z(PAiN*QJ4V2i3nb%lTdbZ)PyJ2xns=kQQ6X&JBfJpuR}plM|&Ekt}#|Zy`OR
zytEL4;#yP)LFP{vLXd*>qy>{22&6f8fPewxfO*+jRmcgi&J{u+{TkT@y_JO+RM8D!
zW-UWTTk%ll9Z^LMDD!g;t(2UiWsl&g-w~r~%8A)=K#G^)#}@B9NN;x8!Uu?F?)~_Z
zFZdC)ff6M98cI(h871^WBZ&=1YhxfdF!CANX6FThfoNy;6ow&=E4X2D_Sfnan1ga)
zwL`}`((tIRPdBkm_0laWluVh}5E|)5&DrH+&k;dQ2rr-F-FjQRmF^H865HRa!_{Ug
zPK~sUp*?v2frzD(eGVd{5I=wx~%$7$}DCv~$o8po>Y!Qa`BsMPo1
zC5(ETlD7aSJes$Ori5rb(7TE)6(=BWLbqkBBddzTdFJ0skkUTtOnH)O2v%aEujYXk
z5UAX_kwdr#uT-Qs5ameg6)23xm`63AK&2`e9_$H%)ll3%o6-}B_E22UiCsm=>M*^Z
za~lsHN(&h=+Ry!+Nx~YRWsN_T_99tT;Jukm_5uz
z#bjW&oP@fjPzkhtC!wwrswN-RoJ-dYLe`_Tgf{{%=N5xn$PrKNS)wV&`Z*@9XFg@k#qL5WBaii9iK`c#(>-_@!7C%#c2a)vE+LGH6SeF-
zhKCfm3m|GAdf=r5t|GA8BlJ+q2{o5cPvP~O*Uj_rD-=GV`h7^d+b#6)=LnxI57)bi
z<#C#5RF)im_q|+UmZYL8h*DeJCH3?b5mj{`_4nOs^l+yXu0Vo=(-t1XNt4$7@<{Fk
zNhO!V6qX|3YWt+^w1Olyr*^9yBFhA6w0Ts~)giZic*rADAhB9M;8ea^K2%p-b*Mcb
zJ|G|F>FPaVE}nGC@<=`;lKUo-sCS21Ivcley5-DjiZ*ig$C7I}$A3p&ZIjpA<&8dk
z|25A348umwu1U^ra5gAA?gn5RNHHwt?4K}z`4WaToc)*FugQz+K#Rlq4Zw;xJBn{V
ze@dR^>~CdJKt93Q_a#85p#Q
z*)K6X&9!0-OE^0xZ*le!o?*{_!ixX-B0gT_?7DoKvwxA9&jHoS*%gTh?U!YoeJl@i
z#wElbk!R4=kn=fv14NH}`2%gIvkkJM!jw`FuAlfv*3<@JrYvH`d8>oSgw_EoVQFAODVg
zi?ctF?{g;QBF?@A#5&Hdg8Zk8BwQPjY?HjuA&(*pz}?N+p91#_G%4gN&fb%k_sX@L
z{gM2TYqKTd{3eJxIJ+fBAD7RS;LBs2{RG3OIQs`#k6Rg@(TTY%uWXiZ?nm-8jv$bD
zr2&X%kt(?qw`x2;%-IEboU<><6`Xw)6gXIZ2>2&B`*VClz=ts$K(OUUoc)dLd_kT-
zj6gA)vttr@9g|>r1%P`wJBa~@!k=TtFBfq3ZCQycBTxw5
zf5`PgJXLY_Jq)OTpULx_ot1Mr858 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 0000000000000000000000000000000000000000..630827ce5299405a7a5cb1b777a26fdc1076c970
GIT binary patch
literal 9039
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
literal 236
zcmaKl%?g4*6h`+x#l@wNT0{iVMxh@H7Y?*)bJeReIL^q-7}y*2mL);7h}Pfv&WQpz
z>k_57Qw29NR%m5G*Kky|4tpuAgO)}MLF9SqE*VrMDZ`Fn3w}=aq(n{r<})#_jk~-(
z%o(AY35>%P6yy+=U+A9P6!}G6%>x#3JfE#06D8LYEijI%wc$&UvBS`z$_XnhB&E6^
LjcB-=O8
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 0000000000000000000000000000000000000000..d3496eab163fbae3f6c2e183c18eae3aa55830d5
GIT binary patch
literal 10492
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