Changes accumulated on my G5 while repository was inaccessible.

This commit is contained in:
Nicholas Shanks 2008-07-31 20:27:55 +00:00
parent d2a16f7a81
commit 7f491cbb39
104 changed files with 3328 additions and 2181 deletions

View File

@ -10,7 +10,7 @@
else return [self itemAtRow:[self selectedRow]];
}
- (NSArray *)selectedItems;
- (NSArray *)selectedItems
{
NSNumber *row;
NSMutableArray *items = [NSMutableArray array];

View File

@ -6,3 +6,11 @@
- (FSSpec *)createFSSpec;
@end
@interface NSString (ResKnifeBooleanExtensions)
- (BOOL)boolValue;
+ (NSString *)stringWithBool:(BOOL)boolean;
@end

View File

@ -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

View File

@ -1,75 +1,100 @@
#import <Cocoa/Cocoa.h>
/*! @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 <tt>NSOpenPanels</tt>. */
IBOutlet NSView *openAuxView;
/*! @var forkTableView Table view inside <tt>openAuxView</tt>. */
IBOutlet NSTableView *forkTableView;
/*! @var icons A dictionary within which to cache icons. Keys are four-character <tt>NSStrings</tt> representing <tt>ResTypes</tt>. */
NSMutableDictionary *icons;
/*! @var openPanelDelegate Delegate for <tt>NSOpenPanels</tt>. */
IBOutlet OpenPanelDelegate *openPanelDelegate;
/*! @var icons A dictionary within which to cache icons. Keys are four-character <tt>NSStrings</tt> representing <tt>ResTypes</tt>. */
NSMutableDictionary *_icons;
}
/*! @function showAbout:
* @discussion Displays the about box located in <b>AboutPanel.nib</b>.
*/
/*!
@method showAbout:
@abstract Displays the about box located in <b>AboutPanel.nib</b>.
*/
- (IBAction)showAbout:(id)sender;
/*! @function visitWebsite:
* @discussion Takes the user to <i>http://web.nickshanks.com/resknife/</i>.
*/
/*!
@method visitWebsite:
@abstract Takes the user to <i>http://web.nickshanks.com/resknife/</i>.
*/
- (IBAction)visitWebsite:(id)sender;
/*! @function visitSourceforge:
* @discussion Takes the user to <i>http://resknife.sourceforge.net/</i>.
*/
/*!
@method visitSourceforge:
@abstract Takes the user to <i>http://resknife.sourceforge.net/</i>.
*/
- (IBAction)visitSourceforge:(id)sender;
/*! @function emailDeveloper:
* @discussion Launches email client and inserts <i>resknife@nickshanks.com</i> into To field.
*/
/*!
@method emailDeveloper:
@abstract Launches email client and inserts <i>resknife@nickshanks.com</i> into To field.
*/
- (IBAction)emailDeveloper:(id)sender;
/*! @function showInfo:
* @discussion Displays the Info panel stored in <b>InfoWindow.nib</b>
*/
/*!
@method showInfo:
@abstract Displays the Info panel stored in <b>InfoWindow.nib</b>
*/
- (IBAction)showInfo:(id)sender;
/*! @function showPasteboard:
* @discussion Displays the pasteboard document, a singleton instance of class <tt>PasteboardDocument</tt>
*/
/*!
@method showPasteboard:
@abstract Displays the pasteboard document, a singleton instance of class <tt>PasteboardDocument</tt>
*/
- (IBAction)showPasteboard:(id)sender;
/*! @function showPrefs:
* @discussion Displays the preferences panel stored in <b>PrefsWindow.nib</b>
*/
/*!
@method showPrefs:
@abstract Displays the preferences panel stored in <b>PrefsWindow.nib</b>
*/
- (IBAction)showPrefs:(id)sender;
/*! @function initUserDefaults
* @discussion Initalises any unset user preferences to default values as read in from <b>defaults.plist</b>.
*/
/*!
@method initUserDefaults
@abstract Initalises any unset user preferences to default values as read in from <b>defaults.plist</b>.
*/
- (void)initUserDefaults;
/*! @function openAuxView
* @discussion Accessor method for the <tt>openAuxView</tt> instance variable.
*/
- (NSView *)openAuxView;
/* accessors */
/*! @function icons
* @discussion Accessor method for the <tt>icons</tt> instance variable.
*/
/*!
@method openPanelDelegate
@abstract Accessor method for the <tt>openPanelDelegate</tt> 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 <tt>_icons</tt> instance variable.
*/
- (NSMutableDictionary *)_icons;
/*!
@method icons
@abstract Accessor method for the <tt>_icons</tt> instance variable. Returns an immutable dictionary.
*/
- (NSDictionary *)icons;
@end
@interface NSSavePanel (PackageBrowser)
/* utility methods */
- (NSArray *)forksForFile:(FSRef *)fileRef;
@end

View File

@ -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

View File

@ -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.<br/><small>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 :)</small>
@description Someone changed the control ID edit field. Check whether this is a unique ID and appropriately enable the "create" button.</p><p>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];

View File

@ -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;

View File

@ -2,18 +2,12 @@
#import <Carbon/Carbon.h> // Actually I only need CarbonCore.framework, but <Carbon/CarbonCore.h> and <CarbonCore/CarbonCore.h> 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;
}

View File

@ -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

View File

@ -1,19 +0,0 @@
/* OpenFileDataSource */
#import <Cocoa/Cocoa.h>
@interface OpenFileDataSource : NSObject
{
IBOutlet NSTableView *forkTableView;
}
@end
@interface OpenPanelDelegate : NSObject
{
id originalDelegate;
}
@end
@interface NSSavePanel (ResKnife)
- (NSBrowser *)browser;
@end

View File

@ -1,113 +0,0 @@
#import "OpenFileDataSource.h"
#import <unistd.h>
#import <sys/attr.h>
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

View File

@ -0,0 +1,43 @@
#import <Cocoa/Cocoa.h>
@interface OpenPanelDelegate : NSObject
{
/*! @var openPanelAccessoryView Accessory view for <tt>NSOpenPanels</tt>. */
IBOutlet NSView *openPanelAccessoryView;
/*! @var forkTableView Table view inside <tt>openPanelAccessoryView</tt>. */
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 <tt>openPanelAccessoryView</tt> instance variable.
*/
- (NSView *)openPanelAccessoryView;
/*!
@method forkTableView
@abstract Accessor method for the <tt>forkTableView</tt> instance variable.
*/
- (NSTableView *)forkTableView;
- (NSArray *)forks;
- (void)setReadOpenPanelForFork:(BOOL)flag;
- (BOOL)readOpenPanelForFork;
@end

View File

@ -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

View File

@ -1,5 +1,4 @@
#import <Cocoa/Cocoa.h>
#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

View File

@ -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

View File

@ -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];

View File

@ -31,10 +31,3 @@ enum LaunchAction
+ (id)sharedPrefsWindowController;
@end
@interface NSString (BooleanSupport)
- (BOOL)boolValue;
+ (NSString *)stringWithBool:(BOOL)boolean;
@end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,
<tt>~/Library/Application Support/ResKnife/Plugins/</tt> and
<tt>/Library/Application Support/ResKnife/Plugins/</tt> for
plugins that have the extension ".plugin" and implement
<tt>initWithResource:</tt> (which means this won't get into
the way if you want to support other kinds of plugins).</p>
<p>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.</p>
<p>To instantiate an object from a plugin, see <tt>editorForType:</tt>
*/
- (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

View File

@ -11,10 +11,7 @@
@interface RKSupportResourceRegistry : NSObject
{
}
+(void) scanForSupportResources: (NSDocumentController*)c;
+ (void)scanForSupportResources;
+ (void)scanForSupportResourcesInFolder:(NSString *)path;
@end

View File

@ -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];
}
}

View File

@ -1,5 +1,12 @@
#import <Foundation/Foundation.h>
#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 <NSCopying, NSCoding, ResKnifeResourceProtocol>
{
@ -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

View File

@ -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];

View File

@ -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

View File

@ -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 <limits.h>
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 <NSDraggingInfo>)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 <NSDraggingInfo>)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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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;

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBDocumentLocation</key>
<string>109 310 356 240 0 0 1280 1002 </string>
<string>147 310 356 240 0 0 1600 1002 </string>
<key>IBFramework Version</key>
<string>248.0</string>
<string>446.1</string>
<key>IBLockedObjects</key>
<array>
<integer>25</integer>
</array>
<key>IBOpenObjects</key>
<array>
<integer>23</integer>
<integer>21</integer>
</array>
<key>IBSystem Version</key>
<string>5P48</string>
<string>8L127</string>
</dict>
</plist>

Binary file not shown.

View File

@ -1,10 +1,18 @@
{
IBClasses = (
{
ACTIONS = {showInfo = id; showPasteboard = id; showPrefs = id; };
ACTIONS = {
emailDeveloper = id;
showAbout = id;
showInfo = id;
showPasteboard = id;
showPrefs = id;
visitSourceforge = id;
visitWebsite = id;
};
CLASS = ApplicationDelegate;
LANGUAGE = ObjC;
OUTLETS = {forkTableView = NSTableView; openAuxView = NSView; };
OUTLETS = {openPanelDelegate = OpenPanelDelegate; };
SUPERCLASS = NSObject;
},
{
@ -15,12 +23,12 @@
findNext = id;
findPrevious = id;
findWithSelection = id;
myAction = id;
openResources = id;
openResourcesAsHex = id;
openResourcesInTemplate = id;
playSound = id;
revertResourceToSaved = id;
revertResource = id;
saveResource = id;
scrollToSelection = id;
showAbout = id;
showCreateResourceSheet = id;
@ -29,17 +37,27 @@
showInfo = id;
showPrefs = id;
showSelectTemplateSheet = id;
useIconView = id;
useListView = id;
};
CLASS = FirstResponder;
LANGUAGE = ObjC;
SUPERCLASS = NSObject;
},
{CLASS = NSOutlineView; LANGUAGE = ObjC; SUPERCLASS = NSTableView; },
{
CLASS = OpenFileDataSource;
ACTIONS = {addFork = id; removeFork = id; };
CLASS = OpenPanelDelegate;
LANGUAGE = ObjC;
OUTLETS = {forkTableView = NSTableView; };
OUTLETS = {
addForkButton = NSButton;
forkTableView = NSTableView;
openPanelAccessoryView = NSView;
removeForkButton = NSButton;
};
SUPERCLASS = NSObject;
},
{CLASS = OutlineSortView; LANGUAGE = ObjC; SUPERCLASS = NSOutlineView; },
{
ACTIONS = {
clear = id;

View File

@ -3,22 +3,25 @@
<plist version="1.0">
<dict>
<key>IBDocumentLocation</key>
<string>264 129 383 248 0 0 1024 746 </string>
<string>75 727 356 240 0 0 1600 1002 </string>
<key>IBEditorPositions</key>
<dict>
<key>246</key>
<string>344 386 340 222 0 0 1024 746 </string>
<string>569 580 520 175 0 0 1600 1002 </string>
<key>29</key>
<string>22 682 347 44 0 0 1024 746 </string>
<string>66 658 366 44 0 0 1600 1002 </string>
</dict>
<key>IBFramework Version</key>
<string>326.0</string>
<string>446.1</string>
<key>IBLockedObjects</key>
<array/>
<key>IBOldestOS</key>
<integer>1</integer>
<key>IBOpenObjects</key>
<array>
<integer>246</integer>
<integer>29</integer>
</array>
<key>IBSystem Version</key>
<string>7A202</string>
<string>8L127</string>
</dict>
</plist>

Binary file not shown.

Binary file not shown.

View File

@ -6,7 +6,13 @@
<string>58 46 387 357 0 0 832 602 </string>
<key>IBFramework Version</key>
<string>326.0</string>
<key>IBOldestOS</key>
<integer>3</integer>
<key>IBOpenObjects</key>
<array>
<integer>20</integer>
</array>
<key>IBSystem Version</key>
<string>7A202</string>
<string>7A179</string>
</dict>
</plist>

Binary file not shown.

View File

@ -0,0 +1,30 @@
{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf410
{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;\f1\fswiss\fcharset77 Helvetica;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
\f0\b\fs24 \cf0 Lead Developer:
\f1\b0 \
Nicholas Shanks\
\
\f0\b Developers:
\f1\b0 \
Uli Kusterer\
\
\f0\b Other Contributors:
\f1\b0 \
Thomas Castiglione\
Philippe Devallois\
Mike Margolis\
Lane Roathe\
Jeffrey Seibert\
\
\f0\b Wanted:
\f1\b0 \
Plug-in editors for resource types.\
Translations for any language.\
Application, document & toolbar icons.\
}

View File

@ -7,7 +7,7 @@
SUPERCLASS = NSObject;
},
{
ACTIONS = {attributesChanged = id; nameChanged = id; };
ACTIONS = {attributesChanged = id; };
CLASS = InfoWindowController;
LANGUAGE = ObjC;
OUTLETS = {
@ -21,7 +21,6 @@
};
SUPERCLASS = NSWindowController;
},
{CLASS = NSWindowController; LANGUAGE = ObjC; SUPERCLASS = NSResponder; },
{CLASS = SizeFormatter; LANGUAGE = ObjC; SUPERCLASS = NSNumberFormatter; }
);
IBVersion = 1;

View File

@ -3,16 +3,12 @@
<plist version="1.0">
<dict>
<key>IBDocumentLocation</key>
<string>142 473 384 240 0 0 1024 746 </string>
<string>267 434 384 240 0 0 1600 1002 </string>
<key>IBFramework Version</key>
<string>326.0</string>
<key>IBOpenObjects</key>
<array>
<integer>35</integer>
<integer>5</integer>
<integer>50</integer>
</array>
<string>439.0</string>
<key>IBOldestOS</key>
<integer>0</integer>
<key>IBSystem Version</key>
<string>7A202</string>
<string>8I127</string>
</dict>
</plist>

Binary file not shown.

Binary file not shown.

View File

@ -2,23 +2,17 @@
IBClasses = (
{CLASS = AttributesFormatter; LANGUAGE = ObjC; SUPERCLASS = NSFormatter; },
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{
CLASS = NameFormatter;
LANGUAGE = ObjC;
OUTLETS = {outlineView = NSOutlineView; };
SUPERCLASS = NSFormatter;
},
{
CLASS = OutlineViewDelegate;
LANGUAGE = ObjC;
OUTLETS = {
attributesFormatter = NSFormatter;
nameFormatter = NSFormatter;
sizeFormatter = NSNumberFormatter;
window = NSWindow;
};
SUPERCLASS = NSObject;
},
{CLASS = RKOutlineView; LANGUAGE = ObjC; SUPERCLASS = NSOutlineView; },
{
CLASS = ResourceDataSource;
LANGUAGE = ObjC;

View File

@ -3,11 +3,15 @@
<plist version="1.0">
<dict>
<key>IBDocumentLocation</key>
<string>71 141 530 549 0 0 1600 1002 </string>
<string>579 557 418 241 0 0 1600 1002 </string>
<key>IBFramework Version</key>
<string>286.0</string>
<string>349.0</string>
<key>IBLockedObjects</key>
<array>
<integer>146</integer>
</array>
<key>IBSystem Version</key>
<string>6F21</string>
<string>7H63</string>
<key>IBUserGuides</key>
<dict>
<key>CreateResourceSheet</key>

View File

@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@interface NSData (RKHexRepresentationExtensions)
- (NSString *)hexRepresentation;
- (NSString *)asciiRepresentation;
- (NSString *)nonLossyAsciiRepresentation;
@end
@interface NSString (RKHexConversionExtensions)
- (NSData *)dataFromHex;
@end

View File

@ -0,0 +1,95 @@
#import "NSData-HexRepresentation.h"
@implementation NSData (RKHexRepresentationExtensions)
- (NSString *)hexRepresentation
{
int currentByte = 0, dataLength = [self length];
char buffer[dataLength*3 -1], hex1, hex2;
char *bytes = (char *) [self bytes];
// return empty string if no data
if(dataLength == 0) return [NSString string];
// calculate bytes
for(currentByte = 0; currentByte < dataLength; currentByte++)
{
hex1 = bytes[currentByte];
hex2 = bytes[currentByte];
hex1 >>= 4;
hex1 &= 0x0F;
hex2 &= 0x0F;
hex1 += (hex1 < 10)? 0x30 : 0x37;
hex2 += (hex2 < 10)? 0x30 : 0x37;
buffer[currentByte*3] = hex1;
buffer[currentByte*3 +1] = hex2;
buffer[currentByte*3 +2] = 0x20;
}
return [NSString stringWithCString:buffer length:(dataLength*3 -1)];
}
- (NSString *)asciiRepresentation
{
int currentByte = 0, dataLength = [self length];
char buffer[dataLength];
char *bytes = (char *) [self bytes];
// calculate bytes
for(currentByte = 0; currentByte < dataLength; currentByte++)
{
if(bytes[currentByte] >= 0x20 && bytes[currentByte] < 0x7F)
buffer[currentByte] = bytes[currentByte];
else buffer[currentByte] = 0x2E; // full stop
}
return [NSString stringWithCString:buffer length:dataLength];
}
- (NSString *)nonLossyAsciiRepresentation
{
int currentByte = 0, dataLength = [self length];
char buffer[dataLength];
char *bytes = (char *) [self bytes];
// calculate bytes
for(currentByte = 0; currentByte < dataLength; currentByte++)
{
if(bytes[currentByte] > 0x20) // doesn't check for < 0x7F
buffer[currentByte] = bytes[currentByte];
// else if(bytes[currentByte] == 0x20)
// buffer[currentByte] = 0xCA; // nbsp to stop maligned wraps - doesn't work :(
else buffer[currentByte] = 0x2E; // full stop
}
return [NSString stringWithCString:buffer length:dataLength];
}
@end
@implementation NSString (RKHexConversionExtensions)
- (NSData *)dataFromHex
{
unsigned long actualBytesEncoded = 0;
unsigned long maxBytesEncoded = floor([self cStringLength] / 2.0);
const char *bytes = [self cString];
char *buffer = (char *) malloc(maxBytesEncoded);
signed char hex1, hex2;
int i;
for(i = 0; i < maxBytesEncoded * 2;)
{
hex1 = bytes[i];
hex2 = bytes[i+1];
hex1 -= (hex1 < 'A')? 0x30 : ((hex1 < 'a')? 0x37 : 0x57); // 0-9 < A-Z < a-z
hex2 -= (hex2 < 'A')? 0x30 : ((hex2 < 'a')? 0x37 : 0x57);
if(hex1 & 0xF0 || hex2 & 0xF0) { i++; continue; } // invalid character found, move forward one byte and try again
buffer[actualBytesEncoded++] = (hex1 << 4) + hex2;
i += 2;
}
return [NSData dataWithBytesNoCopy:buffer length:actualBytesEncoded freeWhenDone:YES];
}
@end

View File

@ -1,6 +1,19 @@
{
IBClasses = (
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
{CLASS = AsciiTextView; LANGUAGE = ObjC; SUPERCLASS = HexEditorTextView; },
{
ACTIONS = {
copyASCII = id;
copyHex = id;
pasteAsASCII = id;
pasteAsHex = id;
pasteAsUnicode = id;
pasteFromHex = id;
};
CLASS = FirstResponder;
LANGUAGE = ObjC;
SUPERCLASS = NSObject;
},
{
CLASS = HexEditorDelegate;
LANGUAGE = ObjC;
@ -13,18 +26,20 @@
};
SUPERCLASS = NSObject;
},
{CLASS = HexEditorTextView; LANGUAGE = ObjC; SUPERCLASS = NSTextView; },
{CLASS = HexTextView; LANGUAGE = ObjC; SUPERCLASS = HexEditorTextView; },
{
ACTIONS = {showFind = id; };
CLASS = HexWindowController;
LANGUAGE = ObjC;
OUTLETS = {
ascii = NSTextView;
asciiScroll = NSScrollView;
copySubmenu = NSMenu;
hex = NSTextView;
hexDelegate = HexEditorDelegate;
hexScroll = NSScrollView;
message = NSTextField;
offset = NSTextView;
pasteSubmenu = NSMenu;
};
SUPERCLASS = NSWindowController;
}

View File

@ -3,14 +3,34 @@
<plist version="1.0">
<dict>
<key>IBDocumentLocation</key>
<string>137 230 413 304 0 0 1024 746 </string>
<string>76 233 356 240 0 0 1600 1002 </string>
<key>IBEditorPositions</key>
<dict>
<key>24</key>
<string>239 475 202 137 0 0 1600 1002 </string>
<key>8</key>
<string>30 493 203 99 0 0 1600 1002 </string>
</dict>
<key>IBFramework Version</key>
<string>286.0</string>
<string>446.1</string>
<key>IBLockedObjects</key>
<array>
<integer>45</integer>
<integer>47</integer>
<integer>48</integer>
<integer>42</integer>
<integer>44</integer>
<integer>46</integer>
<integer>43</integer>
</array>
<key>IBOldestOS</key>
<integer>2</integer>
<key>IBOpenObjects</key>
<array>
<integer>6</integer>
<integer>24</integer>
<integer>36</integer>
</array>
<key>IBSystem Version</key>
<string>6F21</string>
<string>8L127</string>
</dict>
</plist>

View File

@ -29,7 +29,7 @@
// enable/disable boxes
[searchSelectionOnlyBox setEnabled:([(NSTextView *)[[sender window] firstResponder] rangeForUserTextChange].length != 0)];
// set inital vales
// set inital values
if( ![searchSelectionOnlyBox isEnabled] ) [searchSelectionOnlyBox setIntValue:0];
// show sheet

View File

@ -2,33 +2,34 @@
#import "ResKnifeResourceProtocol.h"
@class HexWindowController;
@class HexWindowController, HexTextView, AsciiTextView;
@interface HexEditorDelegate : NSObject
{
IBOutlet HexWindowController *controller;
IBOutlet NSTextView *ascii;
IBOutlet NSTextView *hex;
IBOutlet HexWindowController *controller;
IBOutlet NSTextView *offset;
IBOutlet NSTextField *message;
IBOutlet HexTextView *hex;
IBOutlet AsciiTextView *ascii;
IBOutlet NSTextField *message;
BOOL editedLow;
BOOL editedLow;
NSRange rangeForUserTextChange;
}
/* REMOVE THESE WHEN I.B. IS FIXED */
- (void)setHex:(id)newView;
- (void)setAscii:(id)newView;
/* END REMOVE MARKER */
- (void)viewDidScroll:(NSNotification *)notification;
- (NSString *)offsetRepresentation:(NSData *)data;
- (NSString *)hexRepresentation:(NSData *)data;
- (NSString *)asciiRepresentation:(NSData *)data;
- (NSString *)hexToAscii:(NSData *)data;
- (NSRange)byteRangeFromHexRange:(NSRange)hexRange;
- (NSRange)hexRangeFromByteRange:(NSRange)byteRange;
- (NSRange)byteRangeFromAsciiRange:(NSRange)asciiRange;
- (NSRange)asciiRangeFromByteRange:(NSRange)byteRange;
- (HexWindowController *)controller;
- (NSTextView *)hex;
- (NSTextView *)ascii;
- (BOOL)editedLow;
- (void)setEditedLow:(BOOL)flag;
- (NSRange)rangeForUserTextChange;
@end

View File

@ -1,22 +1,43 @@
#import "HexEditorDelegate.h"
#import "HexWindowController.h"
/* Ideas, method names, and occasionally code stolen from HexEditor by Raphael Sebbe http://raphaelsebbe.multimania.com/ */
#import "HexTextView.h"
@implementation HexEditorDelegate
- (id)init
{
self = [super init];
if( !self ) return nil;
if(!self) return nil;
editedLow = NO;
return self;
}
- (void)awakeFromNib
{
// - MOVED TO HEX WINDOW CONTROLLER DUE TO BUG IN IB MEANING OFFSET, HEX AND ASCII AREN'T SET AT THIS TIME
// notify me when a view scrolls so I can update the other two
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[offset enclosingScrollView] contentView]];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[hex enclosingScrollView] contentView]];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[ascii enclosingScrollView] contentView]];
}
/* REMOVE THESE WHEN I.B. IS FIXED */
- (void)setHex:(id)newView
{
hex = newView;
}
- (void)setAscii:(id)newView
{
ascii = newView;
}
/* END REMOVE MARKER */
/* data re-representation methods */
- (NSString *)offsetRepresentation:(NSData *)data;
- (NSString *)offsetRepresentation:(NSData *)data
{
int dataLength = [data length], bytesPerRow = [controller bytesPerRow];
int rows = (dataLength / bytesPerRow) + ((dataLength % bytesPerRow)? 1:0);
@ -29,122 +50,30 @@
return representation;
}
- (NSString *)hexRepresentation:(NSData *)data;
{
int currentByte = 0, dataLength = [data length], bytesPerRow = [controller bytesPerRow];
int rows = (dataLength / bytesPerRow) + ((dataLength % bytesPerRow)? 1:0);
char buffer[bytesPerRow*3 +1], hex1, hex2;
char *bytes = (char *) [data bytes];
NSMutableString *representation = [NSMutableString string];
int row;
// calculate bytes
for( row = 0; row < rows; row++ )
{
int addr;
for( addr = 0; addr < bytesPerRow; addr++ )
{
if( currentByte < dataLength )
{
hex1 = bytes[currentByte];
hex2 = bytes[currentByte];
hex1 >>= 4;
hex1 &= 0x0F;
hex2 &= 0x0F;
hex1 += (hex1 < 10)? 0x30 : 0x37;
hex2 += (hex2 < 10)? 0x30 : 0x37;
buffer[addr*3] = hex1;
buffer[addr*3 +1] = hex2;
buffer[addr*3 +2] = 0x20;
// advance current byte
currentByte++;
}
else
{
buffer[addr*3] = 0x00;
break;
}
}
// clear last byte on line
buffer[bytesPerRow*3] = 0x00;
// append buffer to representation
[representation appendString:[NSString stringWithCString:buffer]];
}
return representation;
}
- (NSString *)asciiRepresentation:(NSData *)data;
{
int currentByte = 0, dataLength = [data length], bytesPerRow = [controller bytesPerRow];
int rows = (dataLength / bytesPerRow) + ((dataLength % bytesPerRow)? 1:0);
char buffer[bytesPerRow +1];
char *bytes = (char *) [data bytes];
NSMutableString *representation = [NSMutableString string];
int row;
// calculate bytes
for( row = 0; row < rows; row++ )
{
int addr;
for( addr = 0; addr < bytesPerRow; addr++ )
{
if( currentByte < dataLength )
{
if( bytes[currentByte] > 0x20 && bytes[currentByte] < 0x7F )
buffer[addr] = bytes[currentByte];
else if( bytes[currentByte] == 0x20 )
buffer[addr] = 0xCA; // nbsp to stop maligned wraps
else buffer[addr] = 0x2E; // full stop
// advance current byte
currentByte++;
}
else
{
buffer[addr] = 0x00;
break;
}
}
// clear last byte on line
buffer[bytesPerRow] = 0x00;
// append buffer to representation
[representation appendString:[NSString stringWithCString:buffer]];
}
return representation;
}
- (NSString *)hexToAscii:(NSData *)data
{
NSString *result;
unsigned long bytesEncoded = ([data length] + 1) / 3;
char *buffer = (char *) malloc( bytesEncoded ), hex1, hex2;
int i;
for( i = 0; i < bytesEncoded; i++ )
{
hex1 = ((char *)[data bytes])[3*i];
hex2 = ((char *)[data bytes])[3*i+1];
hex1 -= (hex1 < 'A')? 0x30 : 0x37;
hex2 -= (hex2 < 'A')? 0x30 : 0x37;
buffer[i] = (hex1 << 4) + hex2;
}
result = [NSString stringWithCString:buffer length:bytesEncoded];
free( buffer );
return result;
}
/* delegation methods */
- (void)viewDidScroll:(NSNotification *)notification
{
// get object refs for increased speed
NSClipView *object = (NSClipView *) [notification object];
NSClipView *offsetClip = [[offset enclosingScrollView] contentView];
NSClipView *hexClip = [[hex enclosingScrollView] contentView];
NSClipView *asciiClip = [[ascii enclosingScrollView] contentView];
// remove observer to stop myself from receiving bounds changed notifications (n.b. -setPostsBoundsChangedNotifications: only suspends them temporarilly - you get a unified bounds changed notification upon enabling it again and is designed for live resizing and such, so of no use here)
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSViewBoundsDidChangeNotification object:nil];
// when a view scrolls, update the other two
if( object != offsetClip ) [offsetClip setBoundsOrigin:[object bounds].origin];
if( object != hexClip ) [hexClip setBoundsOrigin:[object bounds].origin];
if( object != asciiClip ) [asciiClip setBoundsOrigin:[object bounds].origin];
// restore notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:offsetClip];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:hexClip];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:asciiClip];
}
- (NSRange)textView:(NSTextView *)textView willChangeSelectionFromCharacterRange:(NSRange)oldSelectedCharRange toCharacterRange:(NSRange)newSelectedCharRange
{
NSRange hexRange, asciiRange, byteRange = NSMakeRange(0,0);
@ -156,16 +85,14 @@
if( textView == hex ) // we're selecting hexadecimal
{
byteRange = [self byteRangeFromHexRange:newSelectedCharRange];
asciiRange = [self asciiRangeFromByteRange:byteRange];
byteRange = [HexWindowController byteRangeFromHexRange:newSelectedCharRange];
asciiRange = [HexWindowController asciiRangeFromByteRange:byteRange];
[ascii setSelectedRange:asciiRange];
}
else if( textView == ascii ) // we're selecting ASCII
{
byteRange = [self byteRangeFromAsciiRange:newSelectedCharRange];
hexRange = [self hexRangeFromByteRange:byteRange];
if( hexRange.length > 0 )
hexRange.length -= 1;
byteRange = [HexWindowController byteRangeFromAsciiRange:newSelectedCharRange];
hexRange = [HexWindowController hexRangeFromByteRange:byteRange];
[hex setSelectedRange:hexRange];
}
@ -178,36 +105,9 @@
return newSelectedCharRange;
}
/* range conversion methods */
- (NSRange)byteRangeFromHexRange:(NSRange)hexRange;
- (HexWindowController *)controller
{
// valid for all window widths
NSRange byteRange = NSMakeRange(0,0);
byteRange.location = (hexRange.location / 3);
byteRange.length = (hexRange.length / 3) + ((hexRange.length % 3)? 1:0);
return byteRange;
}
- (NSRange)hexRangeFromByteRange:(NSRange)byteRange;
{
// valid for all window widths
NSRange hexRange = NSMakeRange(0,0);
hexRange.location = (byteRange.location * 3);
hexRange.length = (byteRange.length * 3);
return hexRange;
}
- (NSRange)byteRangeFromAsciiRange:(NSRange)asciiRange;
{
// one-to-one mapping
return asciiRange;
}
- (NSRange)asciiRangeFromByteRange:(NSRange)byteRange;
{
// one-to-one mapping
return byteRange;
return controller;
}
- (NSTextView *)hex
@ -230,4 +130,17 @@
editedLow = flag;
}
- (NSRange)rangeForUserTextChange
{
// if editing hex, convert hex selection to byte selection
if( [[controller window] firstResponder] == hex )
rangeForUserTextChange = [HexWindowController byteRangeFromHexRange:[hex rangeForUserTextChange]];
// if editing ascii, convert ascii selection to byte selection
else if( [[controller window] firstResponder] == ascii )
rangeForUserTextChange = [HexWindowController byteRangeFromAsciiRange:[ascii rangeForUserTextChange]];
return rangeForUserTextChange;
}
@end

View File

@ -2,16 +2,18 @@
#import "HexEditorDelegate.h"
#import "HexWindowController.h"
@interface HexTextView : NSTextView
{
}
- (IBAction)clear:(id)sender;
@interface HexEditorTextView : NSTextView
- (IBAction)copyASCII:(id)sender;
- (IBAction)copyHex:(id)sender;
- (IBAction)pasteAsASCII:(id)sender;
- (IBAction)pasteAsHex:(id)sender;
- (IBAction)pasteAsUnicode:(id)sender;
- (IBAction)clear:(id)sender;
- (void)editData:(NSData *)data replaceBytesInRange:(NSRange)range withData:(NSData *)newData;
@end
@interface NSTextView (HexTextView)
- (void)swapForHexTextView;
@end
@interface HexTextView : HexEditorTextView
@end
@interface AsciiTextView : HexEditorTextView
@end

View File

@ -1,88 +1,10 @@
#import "HexTextView.h"
#import "NSData-HexRepresentation.h"
@implementation HexTextView
- (void)drawRect:(NSRect)rect
{
[super drawRect:rect];
/* if( [[self window] isKeyWindow] && [[self window] firstResponder] == self )
{
NSSetFocusRingStyle( NSFocusRingOnly );
[self setKeyboardFocusRingNeedsDisplayInRect:rect];
}*/
/* [super drawRect:rect];
if( [[self window] isKeyWindow] )
{
NSResponder *responder = [[self window] firstResponder];
if( [responder isKindOfClass:[NSView class]] && [(NSView *)responder isDescendantOf:self])
{
NSSetFocusRingStyle( NSFocusRingOnly );
NSRectFill( rect );
}
}
[self setKeyboardFocusRingNeedsDisplayInRect:rect];*/
}
- (void)setSelectedRange:(NSRange)charRange affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)flag
{
NSRange newRange = charRange;
// select whole bytes at a time (only if selecting in hex!)
if( self == (id) [[self delegate] hex] )
{
// move selection offset to beginning of byte
newRange.location -= (charRange.location % 3);
newRange.length += (charRange.location % 3);
// set selection length to whole number of bytes
if( charRange.length != 0 )
newRange.length -= (newRange.length % 3) -2;
else newRange.length = 0;
// move insertion point to next byte if needs be
if( newRange.location == charRange.location -1 && newRange.length == 0 )
newRange.location += 3;
}
// select return character if selecting ascii
#if( 0 ) // no longer necessary as there's a one-to-one for ascii, and the thing wraps properly instead :-)
else if( self == (id) [[self delegate] ascii] )
{
// if ascii selection goes up to sixteenth byte on last line, select return character too
if( (charRange.length + charRange.location) % 17 == 16)
{
// if selection is zero bytes long, move insertion point to character after return
if( charRange.length == 0 )
{
// if moving back from first byte of line to previous line, skip return char
NSRange selected = [self selectedRange];
if( (selected.length + selected.location) % 17 == 0 )
newRange.location -= 1;
else newRange.location += 1;
}
else newRange.length += 1;
}
}
#endif
// call the superclass to update the selection
[super setSelectedRange:newRange affinity:affinity stillSelecting:NO];
}
@implementation HexEditorTextView
/* NSText overrides */
- (BOOL)validateMenuItem:(NSMenuItem *)item
{
// paste submenu
if( [item action] == @selector(paste:) )
{
NSMenu *editMenu = [[item menu] supermenu];
[[editMenu itemAtIndex:[editMenu indexOfItemWithSubmenu:[item menu]]] setEnabled:[super validateMenuItem:item]];
}
return [super validateMenuItem:item];
}
- (IBAction)cut:(id)sender
{
[self copy:sender];
@ -91,117 +13,91 @@
- (IBAction)copy:(id)sender
{
NSRange selection = [self rangeForUserTextChange], byteSelection;
NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange];
NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard];
// get selection range
if( self == (id) [[self delegate] hex] )
byteSelection = [[self delegate] byteRangeFromHexRange:selection];
else if( self == (id) [[self delegate] ascii] )
byteSelection = [[self delegate] byteRangeFromAsciiRange:selection];
else
{
NSLog( @"Pasting text into illegal object: %@", self );
return;
}
[pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self];
[pb setData:[[[[self window] windowController] data] subdataWithRange:byteSelection] forType:NSStringPboardType];
[pb setData:[[(HexWindowController *)[[self window] windowController] data] subdataWithRange:selection] forType:NSStringPboardType];
}
- (IBAction)copyASCII:(id)sender
{
}
- (IBAction)copyHex:(id)sender
{
}
- (IBAction)paste:(id)sender
{
// be 'smart' - determine if the pasted text is in hex format, such as "5F 3E 04 8E" or ascii.
// what about unicode? should I paste "00 63 00 64" as "63 64" ("Paste As ASCII" submenu item)?
NSRange selection = [self rangeForUserTextChange], byteSelection;
NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange];
NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard];
// get selection range
if( self == (id) [[self delegate] hex] )
byteSelection = [[self delegate] byteRangeFromHexRange:selection];
else if( self == (id) [[self delegate] ascii] )
byteSelection = [[self delegate] byteRangeFromAsciiRange:selection];
else
{
NSLog( @"Pasting text into illegal object: %@", self );
return;
}
if( [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] )
[self editData:[[[self window] windowController] data] replaceBytesInRange:byteSelection withData:[pb dataForType:NSStringPboardType]];
// pastes data as it is on the clipboard
if([pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]])
[self editData:[[[self window] windowController] data] replaceBytesInRange:selection withData:[pb dataForType:NSStringPboardType]];
}
- (IBAction)pasteAsASCII:(id)sender
{
NSRange selection = [self rangeForUserTextChange], byteSelection;
NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange];
NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard];
// get selection range
if( self == (id) [[self delegate] hex] )
byteSelection = [[self delegate] byteRangeFromHexRange:selection];
else if( self == (id) [[self delegate] ascii] )
byteSelection = [[self delegate] byteRangeFromAsciiRange:selection];
else
// converts whatever string encoding is on the clipboard to the default C string encoding, then pastes
if([pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]])
{
NSLog( @"Pasting text into illegal object: %@", self );
return;
}
if( [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] )
[self editData:[[[self window] windowController] data] replaceBytesInRange:byteSelection withData:[pb dataForType:NSStringPboardType]];
}
- (IBAction)pasteAsHex:(id)sender
{
NSRange selection = [self rangeForUserTextChange], byteSelection;
NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard];
// get selection range
if( self == (id) [[self delegate] hex] )
byteSelection = [[self delegate] byteRangeFromHexRange:selection];
else if( self == (id) [[self delegate] ascii] )
byteSelection = [[self delegate] byteRangeFromAsciiRange:selection];
else
{
NSLog( @"Pasting text into illegal object: %@", self );
return;
}
if( [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] )
{
NSString *hexString = [[self delegate] hexRepresentation:[pb dataForType:NSStringPboardType]];
[self editData:[[[self window] windowController] data] replaceBytesInRange:byteSelection withData:[NSData dataWithBytes:[hexString cString] length:[hexString cStringLength]]];
NSData *asciiData = [[pb stringForType:NSStringPboardType] dataUsingEncoding:[NSString defaultCStringEncoding] allowLossyConversion:YES];
[self editData:[[[self window] windowController] data] replaceBytesInRange:selection withData:asciiData];
}
}
- (IBAction)pasteAsUnicode:(id)sender
{
NSRange selection = [self rangeForUserTextChange], byteSelection;
NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange];
NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard];
// get selection range
if( self == (id) [[self delegate] hex] )
byteSelection = [[self delegate] byteRangeFromHexRange:selection];
else if( self == (id) [[self delegate] ascii] )
byteSelection = [[self delegate] byteRangeFromAsciiRange:selection];
else
// converts whatever string encoding is on the clipboard to Unicode, strips off the byte order mark, then pastes
if([pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]])
{
NSLog( @"Pasting text into illegal object: %@", self );
return;
NSMutableData *unicodeData = [[[pb stringForType:NSStringPboardType] dataUsingEncoding:NSUnicodeStringEncoding] mutableCopy];
if(*((unsigned short *)[unicodeData mutableBytes]) == 0xFEFF || *((unsigned short *)[unicodeData mutableBytes]) == 0xFFFE)
[unicodeData replaceBytesInRange:NSMakeRange(0,2) withBytes:NULL length:0];
[self editData:[[[self window] windowController] data] replaceBytesInRange:selection withData:unicodeData];
}
}
- (IBAction)pasteAsHex:(id)sender
{
NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange];
NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard];
if( [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] )
// converts whatever data is on the clipboard to a hex representation of that data, then pastes
if([pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]])
{
NSData *unicodeData = [[NSString stringWithUTF8String:[[pb dataForType:NSStringPboardType] bytes]] dataUsingEncoding:NSUnicodeStringEncoding];
[self editData:[[[self window] windowController] data] replaceBytesInRange:byteSelection withData:unicodeData];
NSData *hexData = [[[pb dataForType:NSStringPboardType] hexRepresentation] dataUsingEncoding:[NSString defaultCStringEncoding] allowLossyConversion:YES];
[self editData:[[[self window] windowController] data] replaceBytesInRange:selection withData:hexData];
}
}
- (IBAction)pasteFromHex:(id)sender
{
NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange];
NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard];
// converts hex data present on the clipboard to the bytes they represent, then pastes
if([pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]])
{
NSData *binaryData = [[pb stringForType:NSStringPboardType] dataFromHex];
[self editData:[[[self window] windowController] data] replaceBytesInRange:selection withData:binaryData];
}
}
- (IBAction)clear:(id)sender
{
NSRange selection = [self rangeForUserTextChange];
if( selection.length > 0 )
if(selection.length > 0)
[self delete:sender];
}
@ -212,20 +108,13 @@
/* Dragging routines */
- (unsigned int)_insertionGlyphIndexForDrag:(id <NSDraggingInfo>)sender
{
unsigned int charIndex = [super _insertionGlyphIndexForDrag:sender];
if( self == [[self delegate] hex] )
charIndex -= charIndex % 3;
return charIndex;
}
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
{
/* if( isLocal ) return NSDragOperationEvery;
return NSDragOperationNone;
/* if(isLocal) return NSDragOperationEvery;
else return NSDragOperationCopy;
*/ return NSDragOperationCopy | NSDragOperationMove | NSDragOperationGeneric;
}
return NSDragOperationCopy | NSDragOperationMove | NSDragOperationGeneric;
*/}
static NSRange draggedRange;
@ -236,7 +125,7 @@ static NSRange draggedRange;
- (void)draggedImage:(NSImage *)image endedAt:(NSPoint)point operation:(NSDragOperation)operation
{
/* if( operation == NSDragOperationMove )
if(operation == NSDragOperationMove)
{
NSRange selection = [self rangeForUserTextChange];
[self editData:[[[self window] windowController] data] replaceBytesInRange:draggedRange withData:[NSData data]];
@ -244,16 +133,16 @@ static NSRange draggedRange;
// set the new selection/insertion point
selection.location -= draggedRange.length;
selection.length = draggedRange.length;
if( selection.location > draggedRange.location )
if(selection.location > draggedRange.location)
selection.location -= draggedRange.length;
[self setSelectedRange:selection];
}*/
}
}
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
{
[super draggingUpdated:sender]; // ignore return value
if( [sender draggingSource] == [[self delegate] hex] || [sender draggingSource] == [[self delegate] ascii] )
if([sender draggingSource] == [[self delegate] hex] || [sender draggingSource] == [[self delegate] ascii])
return NSDragOperationMove;
else return NSDragOperationCopy;
}
@ -263,18 +152,18 @@ static NSRange draggedRange;
// get the insertion point location
NSPasteboard *pb = [sender draggingPasteboard];
NSData *pastedData = [pb dataForType:NSStringPboardType];
int charIndex = [self _insertionGlyphIndexForDrag:sender];
unsigned int charIndex = (unsigned int) [self _insertionGlyphIndexForDrag:sender];
NSRange selection;
// convert hex string to data
if( [sender draggingSource] == [[self delegate] hex] )
pastedData = [[[self delegate] hexToAscii:pastedData] dataUsingEncoding:NSASCIIStringEncoding];
if([sender draggingSource] == [[self delegate] hex])
pastedData = [[[[NSString alloc] initWithData:pastedData encoding:[NSString defaultCStringEncoding]] autorelease] dataFromHex];
if( [sender draggingSource] == [[self delegate] hex] || [sender draggingSource] == [[self delegate] ascii] )
// if( operation == NSDragOperationMove )
if([sender draggingSource] == [[self delegate] hex] || [sender draggingSource] == [[self delegate] ascii])
// if(operation == NSDragOperationMove)
{
NSRange deleteRange = draggedRange;
if( self == [[self delegate] hex] )
if(self == (id) [[self delegate] hex])
{
deleteRange.location /= 3;
deleteRange.length += 1;
@ -285,12 +174,12 @@ static NSRange draggedRange;
[self editData:[[[self window] windowController] data] replaceBytesInRange:deleteRange withData:[NSData data]];
// compensate for already removing the dragged data
if( charIndex > draggedRange.location )
if(charIndex > draggedRange.location)
charIndex -= draggedRange.length;
}
// insert data at insertion point
if( self == [[self delegate] hex] ) charIndex /= 3;
if(self == (id) [[self delegate] hex]) charIndex /= 3;
[self editData:[[[self window] windowController] data] replaceBytesInRange:NSMakeRange(charIndex,0) withData:pastedData];
// set the new selection/insertion point
@ -311,39 +200,35 @@ static NSRange draggedRange;
- (void)insertText:(NSString *)string
{
NSRange selection = [self rangeForUserTextChange], byteSelection;
NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange];
NSMutableData *data = [[[[self window] windowController] data] mutableCopy];
NSData *replaceData = [NSData dataWithBytes:[string cString] length:[string cStringLength]];
// get selection range
if( self == (id) [[self delegate] hex] )
byteSelection = [[self delegate] byteRangeFromHexRange:selection];
else if( self == (id) [[self delegate] ascii] )
byteSelection = [[self delegate] byteRangeFromAsciiRange:selection];
else
{
NSLog( @"Inserting text into illegal object: %@", self );
return;
}
if( self == (id) [[self delegate] hex] )
if(self == (id) [[self delegate] hex])
{
int i;
// bug: iteration through each character in string is broken, paste not yet mapped to this function
for( i = 0; i < [string cStringLength]; i++ )
for(i = 0; i < [string cStringLength]; i++)
{
char typedChar = [string characterAtIndex:i];
if( typedChar >= 0x30 && typedChar <= 0x39 ) typedChar -= 0x30; // 0 to 9
else if( typedChar >= 0x41 && typedChar <= 0x46 ) typedChar -= 0x37; // A to F
else if( typedChar >= 0x61 && typedChar <= 0x66 ) typedChar -= 0x57; // a to f
if(typedChar >= 0x30 && typedChar <= 0x39) typedChar -= 0x30; // 0 to 9
else if(typedChar >= 0x41 && typedChar <= 0x46) typedChar -= 0x37; // A to F
else if(typedChar >= 0x61 && typedChar <= 0x66) typedChar -= 0x57; // a to f
else return;
if( [[self delegate] editedLow] ) // edited low bits already
if(![[self delegate] editedLow]) // editing low bits
{
// put typed char into low bits
typedChar &= 0x0F;
replaceData = [NSData dataWithBytes:&typedChar length:1];
[[self delegate] setEditedLow:YES];
}
else // edited low bits already
{
// select & retrieve old byte so it gets replaced
char prevByte;
byteSelection = NSMakeRange(byteSelection.location -1, 1);
[data getBytes:&prevByte range:byteSelection];
selection = NSMakeRange(selection.location -1, 1);
[data getBytes:&prevByte range:selection];
// shift typed char into high bits and add new low char
prevByte <<= 4; // store high bit
@ -351,89 +236,61 @@ static NSRange draggedRange;
replaceData = [NSData dataWithBytes:&prevByte length:1];
[[self delegate] setEditedLow:NO];
}
else // editing low bits
{
// put typed char into low bits
typedChar &= 0x0F;
replaceData = [NSData dataWithBytes:&typedChar length:1];
[[self delegate] setEditedLow:YES];
}
}
}
// replace bytes (updates views implicitly, records an undo)
[self editData:data replaceBytesInRange:byteSelection withData:replaceData];
[self editData:data replaceBytesInRange:selection withData:replaceData];
[data release];
// set the new selection/insertion point
byteSelection.location++;
byteSelection.length = 0;
if( self == (id) [[self delegate] hex] )
selection = [[self delegate] hexRangeFromByteRange:byteSelection];
else if( self == (id) [[self delegate] ascii] )
selection = [[self delegate] asciiRangeFromByteRange:byteSelection];
// set the new selection (insertion point)
selection.location++;
selection.length = 0;
if(self == (id) [[self delegate] hex]) selection = [HexWindowController hexRangeFromByteRange:selection];
if(self == (id) [[self delegate] ascii]) selection = [HexWindowController asciiRangeFromByteRange:selection];
[self setSelectedRange:selection];
}
- (IBAction)deleteBackward:(id)sender
{
NSRange selection = [self rangeForUserTextChange], byteSelection;
NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange];
NSMutableData *data = [[[[self window] windowController] data] mutableCopy];
// get selection range
if( self == (id) [[self delegate] hex] )
byteSelection = [[self delegate] byteRangeFromHexRange:selection];
else if( self == (id) [[self delegate] ascii] )
byteSelection = [[self delegate] byteRangeFromAsciiRange:selection];
else
{
NSLog( @"Inserting text into illegal object: %@", self );
return;
}
// adjust selection if is insertion point
if( byteSelection.length == 0 && selection.location > 0 )
if(selection.length == 0 && selection.location > 0)
{
byteSelection.location -= 1;
byteSelection.length = 1;
selection.location -= 1;
selection.length = 1;
}
// replace bytes (updates views implicitly)
[self editData:data replaceBytesInRange:byteSelection withData:[NSData data]];
[self editData:data replaceBytesInRange:selection withData:[NSData data]];
[data release];
// set the new selection/insertion point
if( selection.length == 0 )
// set the new selection (insertion point)
if(selection.length == 0 && selection.location > 0)
selection.location -= 1;
else selection.length = 0;
if(self == (id) [[self delegate] hex]) selection = [HexWindowController hexRangeFromByteRange:selection];
if(self == (id) [[self delegate] ascii]) selection = [HexWindowController asciiRangeFromByteRange:selection];
[self setSelectedRange:selection];
}
- (IBAction)deleteForward:(id)sender
{
NSRange selection = [self rangeForUserTextChange], byteSelection;
NSRange selection = [(HexEditorDelegate *)[self delegate] rangeForUserTextChange];
NSMutableData *data = [[[[self window] windowController] data] mutableCopy];
// get selection range
if( self == (id) [[self delegate] hex] )
byteSelection = [[self delegate] byteRangeFromHexRange:selection];
else if( self == (id) [[self delegate] ascii] )
byteSelection = [[self delegate] byteRangeFromAsciiRange:selection];
else
{
NSLog( @"Inserting text into illegal object: %@", self );
return;
}
// adjust selection if is insertion point
if( byteSelection.length == 0 && selection.location < [[self string] length] -1 )
byteSelection.length = 1;
if(selection.length == 0 && [self rangeForUserTextChange].location < [[self string] length] -1)
selection.length = 1;
// replace bytes (updates views implicitly)
[self editData:data replaceBytesInRange:byteSelection withData:[NSData data]];
[self editData:data replaceBytesInRange:selection withData:[NSData data]];
[data release];
// set the new selection/insertion point
selection = [self rangeForUserTextChange];
selection.length = 0;
[self setSelectedRange:selection];
}
@ -458,20 +315,25 @@ static NSRange draggedRange;
[self transpose:sender];
}
- (void)setSelectedRange:(NSRange)charRange affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag
{
[super setSelectedRange:charRange affinity:affinity stillSelecting:NO];
}
- (void)editData:(NSData *)data replaceBytesInRange:(NSRange)range withData:(NSData *)newBytes
{
// save data we're about to replace so we can restore it in an undo
NSRange newRange = NSMakeRange( range.location, [newBytes length] );
NSRange newRange = NSMakeRange(range.location, [newBytes length]);
NSMutableData *newData = [NSMutableData dataWithData:data];
NSMutableData *oldBytes = [[data subdataWithRange:range] retain]; // bug: memory leak, need to release somewhere (call -[NSInvocation retainArguments] instead?)
// manipulate undo stack to concatenate multiple undos
BOOL closeUndoGroup = NO;
id undoStack = nil; // object of class _NSUndoStack
if( ![[[self window] undoManager] isUndoing] )
undoStack = [[[self window] undoManager] _undoStack];
if(![[[self window] undoManager] isUndoing])
undoStack = (id) [[[self window] undoManager] _undoStack];
if( undoStack && (int)[undoStack count] > 0 && [[[self window] undoManager] groupingLevel] == 0 )
if(undoStack && (int)[undoStack count] > 0 && [[[self window] undoManager] groupingLevel] == 0)
{
[undoStack popUndoObject]; // pop endUndoGrouping item
closeUndoGroup = YES;
@ -481,23 +343,162 @@ static NSRange draggedRange;
[newData replaceBytesInRange:range withBytes:[newBytes bytes] length:[newBytes length]];
[[(HexWindowController *)[[self window] windowController] resource] setData:newData];
[self setSelectedRange:NSMakeRange(range.location + [newBytes length], 0)];
// [[self window] setDocumentEdited:YES]; // moved to window controller's -resourceDataDidChange: notification method
// record undo with new data object
[[[[self window] undoManager] prepareWithInvocationTarget:self] editData:newData replaceBytesInRange:newRange withData:oldBytes];
[[[self window] undoManager] setActionName:NSLocalizedString(@"Typing", nil)];
if( closeUndoGroup )
if(closeUndoGroup)
[[[self window] undoManager] endUndoGrouping];
// NSLog( @"%@", undoStack );
}
@end
@implementation HexTextView
/*!
@method selectionRangeForProposedRange:granularity:
@abstract Adjusts the selection for insertion point and byte-selection
@author Nicholas Shanks
@created 2003-11-10
*/
- (NSRange)selectionRangeForProposedRange:(NSRange)proposedCharRange granularity:(NSSelectionGranularity)granularity
{
NSRange newRange = proposedCharRange;
if(newRange.length == 0)
{
// set insertion point location
if(newRange.location % 3 == 1) newRange.location--;
if(newRange.location % 3 == 2) newRange.location++;
}
else
{
// select whole bytes at a time - bug: this doesn't quite work when selecting forwards one byte with the mouse
granularity = NSSelectByWord;
newRange.location -= (proposedCharRange.location % 3);
newRange.length += (proposedCharRange.location % 3);
// set selection length to whole number of bytes
if(newRange.length > 0)
{
if(newRange.length % 3 == 0) newRange.length--;
if(newRange.length % 3 == 1) newRange.length++;
}
}
return [super selectionRangeForProposedRange:newRange granularity:granularity];
}
// also, lots of subclasser pasteboard support better to use than overriding copy: and paste:? see NSTextView.h
- (void)setSelectedRange:(NSRange)newRange affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag
{
if(newRange.length == 0 && stillSelectingFlag == NO)
{
// moving insertion point
if(newRange.location % 3 == 1) newRange.location += 2;
if(newRange.location % 3 == 2) newRange.location -= 2;
}
else if(stillSelectingFlag == NO && [[self window] firstResponder] == self)
{
NSRange oldRange = [self rangeForUserTextChange];
// selecting forwards
if(oldRange.location == newRange.location && oldRange.length != newRange.length)
// if(affinity == NSSelectionAffinityDownstream)
{
// selecting first byte
if(newRange.location % 3 == 0 && newRange.length == 1 && oldRange.length < newRange.length)
newRange.length += 1;
// deselecting first byte
if(newRange.location % 3 == 0 && newRange.length == 1 && oldRange.length > newRange.length)
newRange.length = 0;
// extending selection
else if(newRange.location % 3 == 0 && newRange.length % 3 == 0) newRange.length += 2;
// reducing selection
else if(newRange.location % 3 == 0 && newRange.length % 3 == 1) newRange.length -= 2;
}
// reducing forwards selection spanning multiple rows past start point
else if(newRange.location + newRange.length == oldRange.location && newRange.length % 3 == 1 && oldRange.length > 0)
{
// example: 00 00 00 00 00 00 FF FF => 00 00 00|FF FF FF 00 00
// FF FF FF|00 00 00 00 00 => 00 00 00 00 00 00 00 00
newRange.location += 1;
newRange.length -= 2;
}
// inverse of above
else if(oldRange.location + oldRange.length == newRange.location && newRange.length % 3 == 0 && oldRange.length > 0)
{
// example: 00 00 00|FF FF FF 00 00 => 00 00 00 00 00 00 FF FF
// 00 00 00 00 00 00 00 00 => FF FF FF|00 00 00 00 00
newRange.location += 1;
newRange.length -= 1;
}
// reducing backwards selection spanning multiple rows past start point
else if(oldRange.location + oldRange.length == newRange.location && newRange.length % 3 == 1 && oldRange.length > 0)
{
// example: 00 00 00 00 00 00|FF FF => 00 00 00 00 00 00 00 00
// FF FF FF 00 00 00 00 00 => 00 00 00 FF FF FF|00 00
newRange.location += 1;
newRange.length -= 2;
}
// inverse of above
else if(newRange.location + newRange.length == oldRange.location && newRange.length % 3 == 0 && oldRange.length > 0)
{
// example: 00 00 00 00 00 00 00 00 => 00 00 00 00 00 00|FF FF
// 00 00 00 FF FF FF|00 00 => FF FF FF 00 00 00 00 00
newRange.location += 1;
newRange.length -= 2;
}
// selecting backwards
else if(oldRange.location != newRange.location && oldRange.length != newRange.length)
// else if(affinity == NSSelectionAffinityDownstream)
{
// selecting first byte
if(newRange.location % 3 == 2 && newRange.length == 1) { newRange.location -= 2;
newRange.length += 1; }
// deselecting first byte
else if(newRange.location % 3 == 1 && newRange.length == 1) { newRange.location += 2;
newRange.length = 0; }
// extending selection
else if(newRange.location % 3 == 2 && newRange.length % 3 == 0) { newRange.location -= 2;
newRange.length += 2; }
// reducing selection
else if(newRange.location % 3 == 1 && newRange.length % 3 == 1) { newRange.location += 2;
newRange.length -= 2; }
}
}
[super setSelectedRange:newRange affinity:affinity stillSelecting:stillSelectingFlag];
}
/*!
@method selectionRangeForProposedRange:granularity:
@abstract Puts insertion pointer between bytes during drag operation
@author Nicholas Shanks
@updated 2003-11-10 NGS: Changed algorithm.
*/
- (unsigned int)_insertionGlyphIndexForDrag:(id <NSDraggingInfo>)sender
{
unsigned int glyphIndex = (unsigned int) [super _insertionGlyphIndexForDrag:sender];
if(glyphIndex % 3 == 1) glyphIndex--;
if(glyphIndex % 3 == 2) glyphIndex++;
return glyphIndex;
}
@end
@implementation NSTextView (HexTextView)
@implementation AsciiTextView
- (void)swapForHexTextView
{
isa = [HexTextView class];
}
@end
@end

View File

@ -8,22 +8,31 @@
#define kWindowStepWidthPerChar 28
#define kWindowStepCharsPerStep 1
/*!
@class HexWindowController
@author Nicholas Shanks
@pending Add a category to NSString to convert from hex-formatted strings to NSData objects.
*/
/* Based on HexEdit by Bill Bumgardner, Lane Roath & myself: http://hexedit.sourceforge.net/ */
/* Some ideas, method names, and occasionally code stolen from HexEditor by Raphael Sebbe: http://raphaelsebbe.multimania.com/ */
@interface HexWindowController : NSWindowController <ResKnifePluginProtocol>
{
IBOutlet HexEditorDelegate *hexDelegate;
IBOutlet NSScrollView *asciiScroll;
IBOutlet NSScrollView *hexScroll;
IBOutlet NSTextView *ascii;
IBOutlet NSTextView *hex;
IBOutlet NSTextView *offset;
IBOutlet NSTextField *message;
IBOutlet NSTextView *offset; // these four should be phased out whenever possible
IBOutlet HexTextView *hex; // these four should be phased out whenever possible
IBOutlet AsciiTextView *ascii; // these four should be phased out whenever possible
IBOutlet NSTextField *message; // these four should be phased out whenever possible
IBOutlet NSMenu *copySubmenu;
IBOutlet NSMenu *pasteSubmenu;
NSUndoManager *undoManager;
id <ResKnifeResourceProtocol> resource;
id <ResKnifeResourceProtocol> backup;
BOOL liveEdit;
int bytesPerRow;
BOOL liveEdit;
int bytesPerRow;
NSUndoManager *undoManager;
}
// conform to the ResKnifePluginProtocol with the inclusion of these methods
@ -34,11 +43,10 @@
// save sheet methods
- (void)saveSheetDidClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
- (void)saveResource;
- (void)revertResource;
- (IBAction)saveResource:(id)sender;
- (IBAction)revertResource:(id)sender;
// normal methods
- (void)viewDidScroll:(NSNotification *)notification;
- (void)resourceNameDidChange:(NSNotification *)notification;
- (void)resourceDataDidChange:(NSNotification *)notification;
- (void)resourceWasSaved:(NSNotification *)notification;
@ -48,5 +56,13 @@
- (id)resource;
- (NSData *)data;
- (int)bytesPerRow;
- (NSMenu *)copySubmenu;
- (NSMenu *)pasteSubmenu;
// bug: these should be functions not class member methods
+ (NSRange)byteRangeFromHexRange:(NSRange)hexRange;
+ (NSRange)hexRangeFromByteRange:(NSRange)byteRange;
+ (NSRange)byteRangeFromAsciiRange:(NSRange)asciiRange;
+ (NSRange)asciiRangeFromByteRange:(NSRange)byteRange;
@end

View File

@ -1,17 +1,18 @@
#import "HexWindowController.h"
#import "HexTextView.h"
#import "FindSheetController.h"
#import "NSData-HexRepresentation.h"
/*
OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource )
OSStatus Plug_InitInstance(Plug_PlugInRef plug, Plug_ResourceRef resource)
{
// init function called by carbon apps
if( NSApplicationLoad() )
if(NSApplicationLoad())
{
id newResource = [NSClassFromString(@"Resource") resourceOfType:[NSString stringWithCString:length:4] andID:[NSNumber numberWithInt:] withName:[NSString stringWithCString:length:] andAttributes:[NSNumber numberWithUnsignedShort:] data:[NSData dataWithBytes:length:]];
if( !newResource ) return paramErr;
if(!newResource) return paramErr;
id windowController = [[HexWindowController alloc] initWithResource:newResource];
if( !windowController ) return paramErr;
if(!windowController) return paramErr;
else return noErr;
}
else return paramErr;
@ -23,35 +24,33 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource )
- (id)initWithResource:(id)newResource
{
self = [self initWithWindowNibName:@"HexWindow"];
if( !self ) return self;
if(!self) return nil;
// one instance of your principal class will be created for every resource the user wants to edit (similar to Windows apps)
undoManager = [[NSUndoManager alloc] init];
liveEdit = NO;
if( liveEdit )
if(liveEdit)
{
resource = [newResource retain];
backup = [newResource copy];
resource = [newResource retain]; // resource to work on and monitor for external changes
backup = [newResource copy]; // for reverting only
}
else
{
resource = [newResource copy];
backup = [newResource retain];
resource = [newResource copy]; // resource to work on
backup = [newResource retain]; // actual resource to change when saving data and monitor for external changes
}
bytesPerRow = 16;
// load the window from the nib file and set it's title
[self window]; // implicitly loads nib
[[self window] setTitle:[resource nameForEditorWindow]];
// load the window from the nib file
[self window];
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[(id)resource autorelease];
[undoManager release];
[(id)resource release];
[super dealloc];
}
@ -59,68 +58,86 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource )
{
[super windowDidLoad];
// swap text views to instances of my class instead
[offset swapForHexTextView];
[hex swapForHexTextView];
[ascii swapForHexTextView];
// turn off the background for the offset column (IB is broken when it comes to this)
[offset setDrawsBackground:NO];
[[offset enclosingScrollView] setDrawsBackground:NO];
// set up tab, shift-tab and enter behaviour
[hex setFieldEditor:YES];
[ascii setFieldEditor:YES];
{
// set up tab, shift-tab and enter behaviour (cannot set these in IB at the moment)
[hex setFieldEditor:YES];
[ascii setFieldEditor:YES];
[offset setDrawsBackground:NO];
[[offset enclosingScrollView] setDrawsBackground:NO];
// IB fonts get ignored for some reason
NSFont *courier = [[NSFontManager sharedFontManager] fontWithFamily:@"Courier" traits:0 weight:5 size:12.0];
[hex setFont:courier];
[ascii setFont:courier];
[offset setFont:courier];
// from HexEditorDelegate, here until bug is fixed
[[NSNotificationCenter defaultCenter] addObserver:hexDelegate selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[offset enclosingScrollView] contentView]];
[[NSNotificationCenter defaultCenter] addObserver:hexDelegate selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[hex enclosingScrollView] contentView]];
[[NSNotificationCenter defaultCenter] addObserver:hexDelegate selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[ascii enclosingScrollView] contentView]];
}
// insert the resources' data into the text fields
[self refreshData:[resource data]];
[[self window] setResizeIncrements:NSMakeSize(kWindowStepWidthPerChar * kWindowStepCharsPerStep, 1)];
[[self window] setResizeIncrements:NSMakeSize(kWindowStepWidthPerChar * kWindowStepCharsPerStep * [[self window] userSpaceScaleFactor], 1)];
// min 346, step 224, norm 570, step 224, max 794
// we don't want this notification until we have a window! (Only register for notifications on the resource we're editing)
// here because we don't want these notifications until we have a window! (Only register for notifications on the resource we're editing)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceNameDidChange:) name:ResourceNameDidChangeNotification object:resource];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceDataDidChange:) name:ResourceDataDidChangeNotification object:resource];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceWasSaved:) name:ResourceWasSavedNotification object:resource];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceWasSaved:) name:ResourceWasSavedNotification object:backup];
if(liveEdit) [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceWasSaved:) name:ResourceDataDidChangeNotification object:resource];
else [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceWasSaved:) name:ResourceDataDidChangeNotification object:backup];
// put other notifications here too, just for togetherness
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[offset enclosingScrollView] contentView]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[hex enclosingScrollView] contentView]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:[[ascii enclosingScrollView] contentView]];
// finally, show the window
// finally, set the window title & show the window
[[self window] setTitle:[resource defaultWindowTitle]];
[self showWindow:self];
}
- (void)windowDidResize:(NSNotification *)notification
{
int width = [[notification object] frame].size.width;
float width = [[(NSWindow *)[notification object] contentView] frame].size.width;
int oldBytesPerRow = bytesPerRow;
bytesPerRow = (((width - (kWindowStepWidthPerChar * kWindowStepCharsPerStep) - 122) / (kWindowStepWidthPerChar * kWindowStepCharsPerStep)) + 1) * kWindowStepCharsPerStep;
if( bytesPerRow != oldBytesPerRow )
if(bytesPerRow != oldBytesPerRow)
[offset setString:[hexDelegate offsetRepresentation:[resource data]]];
[hexScroll setFrameSize:NSMakeSize( (bytesPerRow * 21) + 5, [hexScroll frame].size.height)];
[asciiScroll setFrameOrigin:NSMakePoint( (bytesPerRow * 21) + 95, 20)];
[asciiScroll setFrameSize:NSMakeSize( (bytesPerRow * 7) + 28, [asciiScroll frame].size.height)];
[[hex enclosingScrollView] setFrameSize:NSMakeSize((bytesPerRow * 21) + 5, [[hex enclosingScrollView] frame].size.height)];
[[ascii enclosingScrollView] setFrameOrigin:NSMakePoint((bytesPerRow * 21) + 95, 20)];
[[ascii enclosingScrollView] setFrameSize:NSMakeSize((bytesPerRow * 7) + 28, [[ascii enclosingScrollView] frame].size.height)];
}
- (void)windowDidBecomeKey:(NSNotification *)notification
{
// swap paste: menu item for my own paste submenu
NSMenu *editMenu = [[[NSApp mainMenu] itemAtIndex:2] submenu];
NSMenuItem *copyItem = [editMenu itemAtIndex:[editMenu indexOfItemWithTarget:nil andAction:@selector(copy:)]];
NSMenuItem *pasteItem = [editMenu itemAtIndex:[editMenu indexOfItemWithTarget:nil andAction:@selector(paste:)]];
[NSBundle loadNibNamed:@"PasteMenu" owner:self];
// swap copy: menu item for my own copy submenu
[copyItem setEnabled:YES];
[copyItem setKeyEquivalent:@"\0"]; // clear key equiv.
[copyItem setKeyEquivalentModifierMask:0];
[editMenu setSubmenu:copySubmenu forItem:copyItem];
// swap paste: menu item for my own paste submenu
[pasteItem setEnabled:YES];
[pasteItem setKeyEquivalent:@"\0"]; // clear key equiv. (yes, really!)
[pasteItem setKeyEquivalent:@"\0"];
[pasteItem setKeyEquivalentModifierMask:0];
[editMenu setSubmenu:pasteSubmenu forItem:pasteItem];
}
- (void)windowDidResignKey:(NSNotification *)notification
{
// swap my submenu for plain paste menu item
NSMenu *editMenu = [[[NSApp mainMenu] itemAtIndex:2] submenu];
NSMenuItem *copyItem = [editMenu itemAtIndex:[editMenu indexOfItemWithSubmenu:copySubmenu]];
NSMenuItem *pasteItem = [editMenu itemAtIndex:[editMenu indexOfItemWithSubmenu:pasteSubmenu]];
// swap my submenu for plain copy menu item
[editMenu setSubmenu:nil forItem:copyItem];
[copyItem setTarget:nil];
[copyItem setAction:@selector(copy:)];
[copyItem setKeyEquivalent:@"c"];
[copyItem setKeyEquivalentModifierMask:NSCommandKeyMask];
// swap my submenu for plain paste menu item
[editMenu setSubmenu:nil forItem:pasteItem];
[pasteItem setTarget:nil];
[pasteItem setAction:@selector(paste:)];
@ -130,9 +147,9 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource )
- (BOOL)windowShouldClose:(id)sender
{
if( [[self window] isDocumentEdited] )
if([[self window] isDocumentEdited])
{
NSBeginAlertSheet( @"Do you want to save the changes you made to this resource?", @"Save", @"DonÕt Save", @"Cancel", sender, self, @selector(saveSheetDidClose:returnCode:contextInfo:), nil, nil, @"Your changes will be lost if you don't save them." );
NSBeginAlertSheet(@"Do you want to keep the changes you made to this resource?", @"Keep", @"DonÕt Keep", @"Cancel", sender, self, @selector(saveSheetDidClose:returnCode:contextInfo:), nil, nil, @"Your changes cannot be saved later if you don't keep them.");
return NO;
}
else return YES;
@ -140,15 +157,14 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource )
- (void)saveSheetDidClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
switch( returnCode )
switch(returnCode)
{
case NSAlertDefaultReturn: // save
[self saveResource];
case NSAlertDefaultReturn: // keep
[self saveResource:nil];
[[self window] close];
break;
case NSAlertAlternateReturn: // don't save
[self revertResource];
case NSAlertAlternateReturn: // don't keep
[[self window] close];
break;
@ -157,83 +173,33 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource )
}
}
- (void)saveResource
- (void)saveResource:(id)sender
{
if( liveEdit )
{
[[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillBeSavedNotification object:resource];
[backup setData:[resource data]];
[[NSNotificationCenter defaultCenter] postNotificationName:ResourceWasSavedNotification object:resource];
}
else
{
[[NSNotificationCenter defaultCenter] postNotificationName:ResourceWillBeSavedNotification object:backup];
[backup setData:[resource data]];
[[NSNotificationCenter defaultCenter] postNotificationName:ResourceWasSavedNotification object:backup];
}
[backup setData:[[resource data] copy]];
}
- (void)revertResource
- (void)revertResource:(id)sender
{
[resource setData:[backup data]];
[resource setData:[[backup data] copy]];
}
- (IBAction)showFind:(id)sender
- (void)showFind:(id)sender
{
// bug: HexWindowController allocs a sheet controller, but it's never disposed of
FindSheetController *sheetController = [[FindSheetController alloc] initWithWindowNibName:@"FindSheet"];
[sheetController showFindSheet:self];
}
- (void)viewDidScroll:(NSNotification *)notification
{
// get object refs for increased speed
NSClipView *object = (NSClipView *) [notification object];
NSClipView *offsetClip = [[offset enclosingScrollView] contentView];
NSClipView *hexClip = [[hex enclosingScrollView] contentView];
NSClipView *asciiClip = [[ascii enclosingScrollView] contentView];
// due to a bug in -[NSView setPostsBoundsChangedNotifications:] (it basically doesn't work), I am having to work around it by removing myself from the notification center and restoring things later on!
// update, Apple say this isn't their bug. Yeah, right!
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSViewBoundsDidChangeNotification object:nil];
// when a view scrolls, update the other two
if( object != offsetClip )
{
// [offsetClip setPostsBoundsChangedNotifications:NO];
[offsetClip setBoundsOrigin:[object bounds].origin];
// [offsetClip setPostsBoundsChangedNotifications:YES];
}
if( object != hexClip )
{
// [hexClip setPostsBoundsChangedNotifications:NO];
[hexClip setBoundsOrigin:[object bounds].origin];
// [hexClip setPostsBoundsChangedNotifications:YES];
}
if( object != asciiClip )
{
// [asciiClip setPostsBoundsChangedNotifications:NO];
[asciiClip setBoundsOrigin:[object bounds].origin];
// [asciiClip setPostsBoundsChangedNotifications:YES];
}
// restore notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:offsetClip];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:hexClip];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidScroll:) name:NSViewBoundsDidChangeNotification object:asciiClip];
}
- (void)resourceNameDidChange:(NSNotification *)notification
{
[[self window] setTitle:[(id <ResKnifeResourceProtocol>)[notification object] nameForEditorWindow]];
[[self window] setTitle:[(id <ResKnifeResourceProtocol>)[notification object] defaultWindowTitle]];
}
- (void)resourceDataDidChange:(NSNotification *)notification
{
// ensure it's our resource which got changed (should always be true, we don't register for other resource notifications)
if( [notification object] == (id)resource )
// bug: if liveEdit is false and another editor changes backup, if we are dirty we need to ask the user whether to accept the changes from the other editor and discard our changes, or vice versa.
if([notification object] == (id)resource)
{
[self refreshData:[resource data]];
[self setDocumentEdited:YES];
@ -242,35 +208,21 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource )
- (void)resourceWasSaved:(NSNotification *)notification
{
NSLog( @"%@; %@; %@", [notification object], resource, backup );
if( [notification object] == (id)resource )
id <ResKnifeResourceProtocol> object = [notification object];
if(liveEdit)
{
// if resource gets saved, liveEdit is true and this resource is saving
[backup setData:[resource data]];
[self setDocumentEdited:NO];
// haven't worked out what to do here yet
}
else if( [notification object] == (id)backup && !liveEdit )
else
{
// backup will get saved by this resource if liveEdit is false, rather than the 'resource' variable
// but really the data to preserve is in the resource variable
[backup setData:[resource data]];
// [self refreshData:[resource data]];
[self setDocumentEdited:NO];
}
else if( [notification object] == (id)backup )
{
// backup will get saved by another editor too if liveEdit is false
[resource setData:[backup data]];
// [self refreshData:[resource data]];
// this should refresh the view automatically
[resource setData:[[object data] copy]];
[self setDocumentEdited:NO];
}
}
- (void)refreshData:(NSData *)data;
{
NSDictionary *dictionary;
NSMutableParagraphStyle *paragraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
// save selections
NSRange hexSelection = [hex selectedRange];
NSRange asciiSelection = [ascii selectedRange];
@ -281,13 +233,16 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource )
[ascii setDelegate:nil];
// prepare attributes dictionary
NSMutableParagraphStyle *paragraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[paragraph setLineBreakMode:NSLineBreakByCharWrapping];
dictionary = [NSDictionary dictionaryWithObject:paragraph forKey:NSParagraphStyleAttributeName];
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:paragraph forKey:NSParagraphStyleAttributeName];
// do stuff with data
[offset setString:[hexDelegate offsetRepresentation:data]];
[hex setString:[hexDelegate hexRepresentation:data]];
[ascii setString:[hexDelegate asciiRepresentation:data]];
[offset setString:[hexDelegate offsetRepresentation:data]];
if([data length] > 0)
[hex setString:[[data hexRepresentation] stringByAppendingString:@" "]];
else [hex setString:[data hexRepresentation]];
[ascii setString:[data asciiRepresentation]];
// apply attributes
[[offset textStorage] addAttributes:dictionary range:NSMakeRange(0, [[offset textStorage] length])];
@ -318,6 +273,11 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource )
return bytesPerRow;
}
- (NSMenu *)copySubmenu
{
return copySubmenu;
}
- (NSMenu *)pasteSubmenu
{
return pasteSubmenu;
@ -328,4 +288,36 @@ OSStatus Plug_InitInstance( Plug_PlugInRef plug, Plug_ResourceRef resource )
return undoManager;
}
/* range conversion methods */
+ (NSRange)byteRangeFromHexRange:(NSRange)hexRange
{
// valid for all window widths
NSRange byteRange = NSMakeRange(0,0);
byteRange.location = (hexRange.location / 3);
byteRange.length = (hexRange.length / 3) + ((hexRange.length % 3)? 1:0);
return byteRange;
}
+ (NSRange)hexRangeFromByteRange:(NSRange)byteRange
{
// valid for all window widths
NSRange hexRange = NSMakeRange(0,0);
hexRange.location = (byteRange.location * 3);
hexRange.length = (byteRange.length * 3) - ((byteRange.length > 0)? 1:0);
return hexRange;
}
+ (NSRange)byteRangeFromAsciiRange:(NSRange)asciiRange
{
// one-to-one mapping
return asciiRange;
}
+ (NSRange)asciiRangeFromByteRange:(NSRange)byteRange
{
// one-to-one mapping
return byteRange;
}
@end

View File

@ -1,34 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>Hexadecimal Editor</string>
<key>CFBundleGetInfoString</key>
<string></string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.nickshanks.resknife.hexadecimal</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string></string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string></string>
<key>CFBundleSignature</key>
<string>ResK</string>
<key>CFBundleVersion</key>
<string>0.0.1d1</string>
<key>NSMainNibFile</key>
<string>HexWindow</string>
<string>3</string>
<key>NSPrincipalClass</key>
<string>HexWindowController</string>
<key>RKEditedType</key>
<string>Hexadecimal Editor</string>
<key>RKSupportedTypes</key>
<array>
<dict>
<key>IsResKnifeDefaultForType</key>
<string>YES</string>
<key>RKTypeName</key>
<string>Hexadecimal Editor</string>
<key>RKTypeRole</key>
<string>Editor</string>
</dict>
</array>
</dict>
</plist>

BIN
Cocoa/Resources/Apply3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
Cocoa/Resources/Delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,93 @@
<?xml version="1.0" ?>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://web.resource.org/cc/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="960px"
height="960px">
<defs>
<linearGradient id="linearGradient1">
<stop style="stop-color:#ef2929" offset="0" />
<stop style="stop-color:#cc0000" offset="1" />
</linearGradient>
<linearGradient id="linearGradient2">
<stop offset="0" style="stop-color:black;stop-opacity:1;" />
<stop offset="1" style="stop-color:black;stop-opacity:0;" />
</linearGradient>
<radialGradient id="radialGradient1"
xlink:href="#linearGradient2"
gradientTransform="matrix(1,0,0,0.500000,1.635742e-14,20)"
gradientUnits="userSpaceOnUse"
r="17.142857"
fy="40"
fx="23.857143"
cy="40"
cx="23.857143" />
<radialGradient id="radialGradient2"
xlink:href="#linearGradient1"
gradientTransform="matrix(-1.262871,2.796553e-2,-3.606716e-2,-1.629656,47.36662,36.49787)"
gradientUnits="userSpaceOnUse"
cx="20.935379"
cy="12.592707"
fx="20.935379"
fy="12.592707"
r="19.967958" />
</defs>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Delete</dc:title>
<dc:date>2007-02-20</dc:date>
<dc:creator>
<rdf:Bag>
<rdf:li><cc:Agent><dc:title>Andreas Nilsson</dc:title></cc:Agent></rdf:li>
<rdf:li><cc:Agent><dc:title>Nicholas Shanks</dc:title></cc:Agent></rdf:li>
</rdf:Bag>
</dc:creator>
<dc:source>http://tango-project.org/</dc:source>
<dc:subject>
<rdf:Bag>
<rdf:li>delete</rdf:li>
<rdf:li>remove</rdf:li>
</rdf:Bag>
</dc:subject>
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</cc:Work>
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
<cc:permits rdf:resource="http://web.resource.org/cc/Reproduction" />
<cc:permits rdf:resource="http://web.resource.org/cc/Distribution" />
<cc:permits rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
<cc:requires rdf:resource="http://web.resource.org/cc/Notice" />
<cc:requires rdf:resource="http://web.resource.org/cc/Attribution" />
<cc:requires rdf:resource="http://web.resource.org/cc/ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g transform="scale(20,20)">
<path style="opacity:0.52688169;color:black;fill:url(#radialGradient1);stroke:none;"
transform="matrix(1.225,0,0,0.408334,-5.221224,25.17622)"
d="M 41 40 A 17.142857 8.5714283 0 1 1 6.7142868,40 A 17.142857 8.5714283 0 1 1 41 40 z" />
<path style="fill:url(#radialGradient2);stroke:#a40000;"
d="M 24.035816,3.5720836 C 13.289574,3.5720836 4.5678570,12.294146 4.5678580,23.040834 C 4.5678580,33.787521 13.289575,42.509583 24.035816,42.509584 C 34.782058,42.509584 43.503776,33.787520 43.503775,23.040834 C 43.503775,12.294147 34.782058,3.5720836 24.035816,3.5720836 z M 24.004517,8.4783336 C 32.049892,8.4783336 38.589837,14.990984 38.589837,23.009584 C 38.589837,25.981868 37.657973,28.737374 36.117218,31.040834 L 15.960683,10.915834 C 18.272680,9.3813936 21.022553,8.4783336 24.004517,8.4783336 z M 12.267404,14.447084 L 32.580435,34.728334 C 30.166684,36.490827 27.221538,37.540834 24.004517,37.540834 C 15.959141,37.540834 9.4191980,31.028184 9.4191980,23.009584 C 9.4191980,19.803270 10.497961,16.851741 12.267404,14.447084 z " />
<path style="opacity:0.55376345;fill:none;stroke:white;"
transform="matrix(1.007576,0,0,1.019157,-4.568194,-4.726048)"
d="M 46.714287 27.214285 A 18.357143 18.142857 0 1 1 10,27.214285 A 18.357143 18.142857 0 1 1 46.714287 27.214285 z" />
<path style="opacity:0.47849461;fill:none;stroke:white;"
d="M 15.075203,11.366727 L 35.646632,31.938156" />
<!--path style="opacity:0.30645159;fill:white;stroke:none;"
d="M 4.4323460,19.795298 C 4.4561550,21.985774 9.8371077,20.461965 9.8609167,21.652441 C 9.8132977,19.842917 11.837108,15.961965 12.289489,15.152441 L 20.932346,23.509584 C 20.884728,21.771489 27.122823,23.390537 27.003776,21.509584 L 16.718061,10.866727 C 18.241871,10.081013 21.837109,8.7952976 24.075204,8.9381546 C 32.313299,9.1524406 38.051394,16.795298 38.075204,22.009584 L 37.503775,27.009584 C 37.384727,28.866727 43.337108,26.795298 43.146632,28.295298 C 43.551394,27.152441 44.027584,24.795298 43.932346,22.081013 C 43.884727,12.438155 35.765679,3.3667266 24.003775,3.1524406 C 15.420441,3.2833936 8.4978220,8.1822026 6.1287740,14.661370 C 5.9933010,14.694830 4.6585360,16.842917 4.4323460,19.795298 z " /-->
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
Cocoa/Resources/Delete3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
Cocoa/Resources/Go3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
Cocoa/Resources/Help.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
Cocoa/Resources/Help3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,43 @@
/* Resource type to file type mapping table */
/* useful for mangling a resource type to yield an icon for a different file extension */
/* also could be used in future for binding a resource to an external editor (the default editor for the corrisponding file type) */
/* acceptable values are:
- file name extensions
- MacOS file types (four characters in single quotes)
- bundle identifiers
*/
"cfrg" = "shlb";
"SIZE" = "shlb";
"CODE" = "s";
"MWBB" = "'MMPF'";
"MWKB" = "'MMPF'";
"STR " = "text";
"STR#" = "text";
"plst" = "plist";
"snd " = "'sfil'";
"url " = "webloc";
"hfdr" = "com.apple.finder";
"cicn" = "icns";
"SICN" = "icns";
"icl8" = "icns";
"icl4" = "icns";
"ICON" = "icns";
"ICN#" = "icns";
"ics8" = "icns";
"ics4" = "icns";
"ics#" = "icns";
"icm8" = "icns";
"icm4" = "icns";
"icm#" = "icns";
"PNG " = "png";
"NFNT" = "'tfil'";
"sfnt" = "ttf";

Binary file not shown.

Binary file not shown.

BIN
Cocoa/Resources/Search3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
Cocoa/Resources/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -1,6 +1,6 @@
{
PreserveBackups = YES;
Autosave = YES;
Autosave = NO;
AutosaveInterval = 5;
DeleteResourceWarning = YES;

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,88 @@
{
IBClasses = (
{
ACTIONS = {
emailDeveloper = id;
showAbout = id;
showInfo = id;
showPasteboard = id;
showPrefs = id;
visitSourceforge = id;
visitWebsite = id;
};
CLASS = ApplicationDelegate;
LANGUAGE = ObjC;
OUTLETS = {openPanelDelegate = OpenPanelDelegate; };
SUPERCLASS = NSObject;
},
{
ACTIONS = {
deselectAll = id;
exportResourceToFile = id;
exportResourceToImageFile = id;
findNext = id;
findPrevious = id;
findWithSelection = id;
openResources = id;
openResourcesAsHex = id;
openResourcesInTemplate = id;
playSound = id;
revertResource = id;
saveResource = id;
scrollToSelection = id;
showAbout = id;
showCreateResourceSheet = id;
showExportToDFSheet = id;
showFind = id;
showInfo = id;
showPrefs = id;
showSelectTemplateSheet = id;
useIconView = id;
useListView = id;
};
CLASS = FirstResponder;
LANGUAGE = ObjC;
SUPERCLASS = NSObject;
},
{CLASS = NSOutlineView; LANGUAGE = ObjC; SUPERCLASS = NSTableView; },
{
ACTIONS = {addFork = id; removeFork = id; };
CLASS = OpenPanelDelegate;
LANGUAGE = ObjC;
OUTLETS = {
addForkButton = NSButton;
forkTableView = NSTableView;
openPanelAccessoryView = NSView;
removeForkButton = NSButton;
};
SUPERCLASS = NSObject;
},
{CLASS = OutlineSortView; LANGUAGE = ObjC; SUPERCLASS = NSOutlineView; },
{
ACTIONS = {
clear = id;
copy = id;
creatorChanged = id;
exportResourceToFile = id;
exportResourceToImageFile = id;
openResources = id;
openResourcesAsHex = id;
openResourcesInTemplate = id;
paste = id;
playSound = id;
showCreateResourceSheet = id;
showSelectTemplateSheet = id;
typeChanged = id;
};
CLASS = ResourceDocument;
LANGUAGE = ObjC;
OUTLETS = {
dataSource = ResourceDataSource;
mainWindow = NSWindow;
outlineView = NSOutlineView;
};
SUPERCLASS = NSDocument;
}
);
IBVersion = 1;
}

27
Cocoa/cy.lproj/Application.nib/info.nib generated Normal file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBDocumentLocation</key>
<string>75 727 356 240 0 0 1600 1002 </string>
<key>IBEditorPositions</key>
<dict>
<key>246</key>
<string>569 580 520 175 0 0 1600 1002 </string>
<key>29</key>
<string>66 658 413 44 0 0 1600 1002 </string>
</dict>
<key>IBFramework Version</key>
<string>446.1</string>
<key>IBLockedObjects</key>
<array/>
<key>IBOldestOS</key>
<integer>1</integer>
<key>IBOpenObjects</key>
<array>
<integer>29</integer>
</array>
<key>IBSystem Version</key>
<string>8P135</string>
</dict>
</plist>

Binary file not shown.

BIN
Cocoa/cy.lproj/Application.nib/objects.nib generated Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
/* Localized versions of Info.plist keys */
CFBundleName = "ResKnife";
CFBundleShortVersionString = "0.6 beta 4";
CFBundleGetInfoString = "ResKnife 0.6 b4, Hawlfraint © Nicolas Siancs, 2001-7.";
NSHumanReadableCopyright = "© Nicolas Siancs, 2001-7.";

Binary file not shown.

View File

@ -0,0 +1,88 @@
{
IBClasses = (
{
ACTIONS = {
emailDeveloper = id;
showAbout = id;
showInfo = id;
showPasteboard = id;
showPrefs = id;
visitSourceforge = id;
visitWebsite = id;
};
CLASS = ApplicationDelegate;
LANGUAGE = ObjC;
OUTLETS = {openPanelDelegate = OpenPanelDelegate; };
SUPERCLASS = NSObject;
},
{
ACTIONS = {
deselectAll = id;
exportResourceToFile = id;
exportResourceToImageFile = id;
findNext = id;
findPrevious = id;
findWithSelection = id;
openResources = id;
openResourcesAsHex = id;
openResourcesInTemplate = id;
playSound = id;
revertResource = id;
saveResource = id;
scrollToSelection = id;
showAbout = id;
showCreateResourceSheet = id;
showExportToDFSheet = id;
showFind = id;
showInfo = id;
showPrefs = id;
showSelectTemplateSheet = id;
useIconView = id;
useListView = id;
};
CLASS = FirstResponder;
LANGUAGE = ObjC;
SUPERCLASS = NSObject;
},
{CLASS = NSOutlineView; LANGUAGE = ObjC; SUPERCLASS = NSTableView; },
{
ACTIONS = {addFork = id; removeFork = id; };
CLASS = OpenPanelDelegate;
LANGUAGE = ObjC;
OUTLETS = {
addForkButton = NSButton;
forkTableView = NSTableView;
openPanelAccessoryView = NSView;
removeForkButton = NSButton;
};
SUPERCLASS = NSObject;
},
{CLASS = OutlineSortView; LANGUAGE = ObjC; SUPERCLASS = NSOutlineView; },
{
ACTIONS = {
clear = id;
copy = id;
creatorChanged = id;
exportResourceToFile = id;
exportResourceToImageFile = id;
openResources = id;
openResourcesAsHex = id;
openResourcesInTemplate = id;
paste = id;
playSound = id;
showCreateResourceSheet = id;
showSelectTemplateSheet = id;
typeChanged = id;
};
CLASS = ResourceDocument;
LANGUAGE = ObjC;
OUTLETS = {
dataSource = ResourceDataSource;
mainWindow = NSWindow;
outlineView = NSOutlineView;
};
SUPERCLASS = NSDocument;
}
);
IBVersion = 1;
}

27
Cocoa/gd.lproj/Application.nib/info.nib generated Normal file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBDocumentLocation</key>
<string>75 727 356 240 0 0 1600 1002 </string>
<key>IBEditorPositions</key>
<dict>
<key>246</key>
<string>567 580 520 175 0 0 1600 1002 </string>
<key>29</key>
<string>408 624 463 44 0 0 1600 1002 </string>
</dict>
<key>IBFramework Version</key>
<string>446.1</string>
<key>IBLockedObjects</key>
<array/>
<key>IBOldestOS</key>
<integer>1</integer>
<key>IBOpenObjects</key>
<array>
<integer>29</integer>
</array>
<key>IBSystem Version</key>
<string>8L127</string>
</dict>
</plist>

Binary file not shown.

BIN
Cocoa/gd.lproj/Application.nib/objects.nib generated Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
/* Localized versions of Info.plist keys */
CFBundleName = "ResKnife";
CFBundleShortVersionString = "0.6 beta 4";
CFBundleGetInfoString = "ResKnife 0.6 b4, Dlighe-sgrìobhaidh © Nicolas Sauncs, 2001-7.";
NSHumanReadableCopyright = "© Nicolas Sauncs, 2001-7.";

Binary file not shown.

View File

@ -0,0 +1,50 @@
{
IBClasses = (
{
ACTIONS = {
emailDeveloper = id;
showAbout = id;
showInfo = id;
showPasteboard = id;
showPrefs = id;
visitSourceforge = id;
visitWebsite = id;
};
CLASS = ApplicationDelegate;
LANGUAGE = ObjC;
OUTLETS = {openPanelDelegate = OpenPanelDelegate; };
SUPERCLASS = NSObject;
},
{
ACTIONS = {
deselectAll = id;
openResources = id;
openResourcesAsHex = id;
openResourcesInTemplate = id;
playSound = id;
revertResource = id;
saveResource = id;
showCreateResourceSheet = id;
showSelectTemplateSheet = id;
useIconView = id;
useListView = id;
};
CLASS = FirstResponder;
LANGUAGE = ObjC;
SUPERCLASS = NSObject;
},
{
ACTIONS = {addFork = id; removeFork = id; };
CLASS = OpenPanelDelegate;
LANGUAGE = ObjC;
OUTLETS = {
addForkButton = NSButton;
forkTableView = NSTableView;
openPanelAccessoryView = NSView;
removeForkButton = NSButton;
};
SUPERCLASS = NSObject;
}
);
IBVersion = 1;
}

23
Cocoa/ja.lproj/Application.nib/info.nib generated Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBDocumentLocation</key>
<string>454 731 356 240 0 0 1600 1002 </string>
<key>IBEditorPositions</key>
<dict>
<key>252</key>
<string>580 400 520 175 0 0 1600 1002 </string>
<key>29</key>
<string>465 651 429 44 0 0 1600 1002 </string>
</dict>
<key>IBFramework Version</key>
<string>446.1</string>
<key>IBOpenObjects</key>
<array>
<integer>29</integer>
</array>
<key>IBSystem Version</key>
<string>8L127</string>
</dict>
</plist>

Binary file not shown.

View File

@ -1,5 +1,9 @@
#import <Cocoa/Cocoa.h>
/*!
@function main
*/
int main(int argc, const char *argv[])
{
return NSApplicationMain(argc, argv);

View File

@ -82,7 +82,7 @@
resImage = [[NSImage alloc] init];
resData = [[resource data] retain];
planes[0] = (char*) [resData bytes];
planes[0] = (unsigned char*) [resData bytes];
if( [resType isEqualToString: @"ICN#"] )
{

View File

@ -1,40 +1,37 @@
ABOUT THE CODE
--------------
WHAT HAPPENS WHEN A TEMPLATE EDITOR IS OPENED FOR A RESOURCE
The template editor uses a dedicated class for each field type (or family of field types).
These classes are all subclasses of the common base class NuTemplateElement. For things like
lists, there is a subclass NuTemplateGroupElement from which you can instead subclass to
These classes are all subclasses of the common base class Element. For things like
lists, there is a subclass GroupElement from which you can instead subclass to
have some of the work done for you.
When opening a resource, the template editor first creates a NuTemplateStream for the template
resource and calls readOneObject: on it until it runs out of data, instantiating a hierarchy
of NuTemplateElement subclasses for based on the template.
When opening a resource, the template editor first creates a TemplateStream for the template
resource and calls readOneObject:on it until it runs out of data, instantiating a hierarchy
of Element subclasses for based on the template.
After that, it creates a NuTemplateStream for the resource and loops over the template object
hierarchy, creating a copy of each item, and then calling readDataFrom:containingArray: on
the copy, which in turn reads data from the NuTemplateStream and stores it in its instance
variables. For this to work, subclasses of NuTemplateElement *must* implement the NSCopying
After that, it creates a TemplateStream for the resource and loops over the template object
hierarchy, creating a copy of each item, and then calling readDataFrom:containingArray:on
the copy, which in turn reads data from the TemplateStream and stores it in its instance
variables. For this to work, subclasses of Element *must* implement the NSCopying
protocol.
This results in an object hierarchy that describes the entire resource using template fields
containing the data, which can now be edited, displayed in lists, etc.
WHAT HAPPENS WHEN THE TEMPLATE EDITOR IS CLOSED
As soon as the template editor is closed, another NuTemplateStream is created for the
As soon as the template editor is closed, another TemplateStream is created for the
resource and passed to all template fields (the copies with the resource data, not the
pre-parsed template hierarchy), via writeDataTo:, which is where they should write their
data to the stream. Before that, sizeOnDisk is called on each NuTemplateElement to
data to the stream. Before that, sizeOnDisk is called on each Element to
calculate the new size of the resource before the actual process of writing them out.
This size must include the size of all of their sub-items, and writeDataTo: must call
This size must include the size of all of their sub-items, and writeDataTo:must call
these sub items if they are to be written to disk.
SPECIAL CASE: LISTS
SPECIAL CASE:LISTS
When the editor encounters an LSTB element, the LSTB element is called upon to parse its
"sub-elements" (the items that make up one list item). The LSTB element reads all elements
@ -65,8 +62,7 @@ Analogous behaviour is employed for other kinds of lists, except that additional
features in LSTEs are activated to allow writing terminating zeroes to the file, and
that the equivalent to the "LSTB" item may also update a counter field.
REVISIONS:
2006-02-05 NS Rewrote plugin.
2003-08-13 UK Finished chapter on lists, added revision history.
2003-08-08 UK Created.

Some files were not shown because too many files have changed in this diff Show More