mirror of
https://github.com/nickshanks/ResKnife.git
synced 2024-06-28 00:29:33 +00:00
Hex Editor bug fixes, and copy & paste of resources added
This commit is contained in:
parent
df5dfb0829
commit
9747396b40
|
@ -1,7 +1,7 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import "ResKnifeResourceProtocol.h"
|
||||
|
||||
@interface Resource : NSObject <ResKnifeResourceProtocol>
|
||||
@interface Resource : NSObject <NSCoding, ResKnifeResourceProtocol>
|
||||
{
|
||||
@private
|
||||
// flags
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#import "ResourceDocument.h"
|
||||
#import "ResourceDataSource.h"
|
||||
|
||||
NSString *RKResourcePboardType = @"RKResourcePboardType";
|
||||
|
||||
@implementation Resource
|
||||
|
||||
- (id)init
|
||||
|
@ -252,11 +254,37 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* encoding */
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)decoder
|
||||
{
|
||||
self = [super init];
|
||||
if( self )
|
||||
{
|
||||
dirty = YES;
|
||||
name = [[decoder decodeObject] retain];
|
||||
type = [[decoder decodeObject] retain];
|
||||
resID = [[decoder decodeObject] retain];
|
||||
attributes = [[decoder decodeObject] retain];
|
||||
data = [[decoder decodeDataObject] retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)encoder
|
||||
{
|
||||
[encoder encodeObject:name];
|
||||
[encoder encodeObject:type];
|
||||
[encoder encodeObject:resID];
|
||||
[encoder encodeObject:attributes];
|
||||
[encoder encodeDataObject:data];
|
||||
}
|
||||
|
||||
/* description */
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"\nName: %@\nType: %@ ID: %@\nModified: %@", name, type, resID, dirty? @"YES":@"NO"];
|
||||
return [NSString stringWithFormat:@"\nName: %@\nType: %@ ID: %@\nModified: %@", name, type, resID, dirty? @"YES":@"NO"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
- (void)setResources:(NSMutableArray *)newResources;
|
||||
- (void)addResource:(Resource *)resource;
|
||||
- (void)removeResource:(Resource *)resource;
|
||||
- (NSNumber *)uniqueIDForType:(NSString *)type;
|
||||
|
||||
// accessors
|
||||
- (Resource *)resourceOfType:(NSString *)type andID:(NSNumber *)resID;
|
||||
|
|
|
@ -72,6 +72,13 @@ NSString *DataSourceDidRemoveResourceNotification = @"DataSourceDidRemoveResourc
|
|||
[outlineView reloadItem:[notification object]];
|
||||
}
|
||||
|
||||
- (NSNumber *)uniqueIDForType:(NSString *)type
|
||||
{
|
||||
short resID = 128;
|
||||
while( [self resourceOfType:type andID:[NSNumber numberWithShort:resID]] != nil ) resID++;
|
||||
return [NSNumber numberWithShort:resID];
|
||||
}
|
||||
|
||||
/* Data source protocol implementation */
|
||||
|
||||
- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
|
||||
|
|
|
@ -29,8 +29,12 @@
|
|||
- (IBAction)playSound:(id)sender;
|
||||
- (void)sound:(NSSound *)sound didFinishPlaying:(BOOL)finished;
|
||||
|
||||
- (IBAction)copy:(id)sender;
|
||||
- (IBAction)paste:(id)sender;
|
||||
- (void)pasteResources:(NSArray *)pastedResources;
|
||||
- (void)overwritePasteSheetDidDismiss:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
|
||||
- (IBAction)clear:(id)sender;
|
||||
- (void)deleteResourcesSheetDidDismiss:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
|
||||
- (void)deleteResourcesSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
|
||||
- (void)deleteSelectedResources;
|
||||
|
||||
- (void)resourceNameWillChange:(NSNotification *)notification;
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
NSString *DocumentInfoWillChangeNotification = @"DocumentInfoWillChangeNotification";
|
||||
NSString *DocumentInfoDidChangeNotification = @"DocumentInfoDidChangeNotification";
|
||||
|
||||
extern NSString *RKResourcePboardType;
|
||||
|
||||
@implementation ResourceDocument
|
||||
|
||||
- (id)init
|
||||
|
@ -323,7 +325,6 @@ static NSString *RKShowInfoItemIdentifier = @"com.nickshanks.resknife.toolbar.sh
|
|||
{
|
||||
NSBundle *hexEditor = [NSBundle bundleWithPath:[[[NSBundle mainBundle] builtInPlugInsPath] stringByAppendingPathComponent:@"Hexadecimal Editor.plugin"]];
|
||||
// bug: I alloc a plug instance here, but have no idea where I should dealloc it, perhaps the plug ought to call [self autorelease] when it's last window is closed?
|
||||
|
||||
NSWindowController *plugController = [(id <ResKnifePluginProtocol>)[[hexEditor principalClass] alloc] initWithResource:resource];
|
||||
}
|
||||
|
||||
|
@ -386,18 +387,86 @@ static NSString *RKShowInfoItemIdentifier = @"com.nickshanks.resknife.toolbar.sh
|
|||
/* EDIT OPERATIONS */
|
||||
#pragma mark -
|
||||
|
||||
- (IBAction)cut:(id)sender
|
||||
{
|
||||
[self copy:sender];
|
||||
[self clear:sender];
|
||||
}
|
||||
|
||||
- (IBAction)copy:(id)sender
|
||||
{
|
||||
#pragma unused( sender )
|
||||
NSArray *selectedItems = [outlineView selectedItems];
|
||||
NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard];
|
||||
[pb declareTypes:[NSArray arrayWithObject:RKResourcePboardType] owner:self];
|
||||
[pb setData:[NSArchiver archivedDataWithRootObject:selectedItems] forType:RKResourcePboardType];
|
||||
}
|
||||
|
||||
- (IBAction)paste:(id)sender
|
||||
{
|
||||
#pragma unused( sender )
|
||||
NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSGeneralPboard];
|
||||
if( [pb availableTypeFromArray:[NSArray arrayWithObject:RKResourcePboardType]] )
|
||||
[self pasteResources:[NSUnarchiver unarchiveObjectWithData:[pb dataForType:RKResourcePboardType]]];
|
||||
}
|
||||
|
||||
- (void)pasteResources:(NSArray *)pastedResources
|
||||
{
|
||||
Resource *resource;
|
||||
NSEnumerator *enumerator = [pastedResources objectEnumerator];
|
||||
while( resource = (Resource *) [enumerator nextObject] )
|
||||
{
|
||||
// check resource type/ID is available
|
||||
if( [dataSource resourceOfType:[resource type] andID:[resource resID]] == nil )
|
||||
{
|
||||
// resource slot is available, paste this one in
|
||||
[dataSource addResource:resource];
|
||||
}
|
||||
else
|
||||
{
|
||||
// resource slot is ocupied, ask user what to do
|
||||
NSMutableArray *remainingResources = [[NSMutableArray alloc] initWithCapacity:1];
|
||||
[remainingResources addObject:resource];
|
||||
[remainingResources addObjectsFromArray:[enumerator allObjects]];
|
||||
NSBeginAlertSheet( @"Paste Error", @"Unique ID", @"Skip", @"Overwrite", mainWindow, self, NULL, @selector(overwritePasteSheetDidDismiss:returnCode:contextInfo:), remainingResources, @"There already exists a resource of type %@ with ID %@. Do you wish to assign the pasted resource a unique ID, overwrite the existing resource, or skip pasting of this resource?", [resource type], [resource resID] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)overwritePasteSheetDidDismiss:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
|
||||
{
|
||||
NSMutableArray *remainingResources = [NSMutableArray arrayWithArray:[(NSArray *)contextInfo autorelease]];
|
||||
Resource *resource = [remainingResources objectAtIndex:0];
|
||||
if( returnCode == NSAlertDefaultReturn ) // unique ID
|
||||
{
|
||||
Resource *newResource = [Resource resourceOfType:[resource type] andID:[dataSource uniqueIDForType:[resource type]] withName:[resource name] andAttributes:[resource attributes] data:[resource data]];
|
||||
[dataSource addResource:newResource];
|
||||
}
|
||||
else if( NSAlertOtherReturn ) // overwrite
|
||||
{
|
||||
[dataSource removeResource:[dataSource resourceOfType:[resource type] andID:[resource resID]]];
|
||||
[dataSource addResource:resource];
|
||||
}
|
||||
// else if( NSAlertAlternateReturn ) // skip
|
||||
|
||||
// remove top resource and continue paste
|
||||
[remainingResources removeObjectAtIndex:0];
|
||||
[self pasteResources:remainingResources];
|
||||
}
|
||||
|
||||
- (IBAction)clear:(id)sender
|
||||
{
|
||||
#pragma unused( sender )
|
||||
if( [prefs boolForKey:@"DeleteResourceWarning"] )
|
||||
{
|
||||
NSBeginCriticalAlertSheet( @"Delete Resource", @"Delete", @"Cancel", nil, [self mainWindow], self, nil, @selector(deleteResourcesSheetDidDismiss:returnCode:contextInfo:), nil, @"Please confirm you wish to delete the selected resources." );
|
||||
NSBeginCriticalAlertSheet( @"Delete Resource", @"Delete", @"Cancel", nil, [self mainWindow], self, @selector(deleteResourcesSheetDidEnd:returnCode:contextInfo:), NULL, nil, @"Please confirm you wish to delete the selected resources." );
|
||||
}
|
||||
else [self deleteSelectedResources];
|
||||
}
|
||||
|
||||
- (void)deleteResourcesSheetDidDismiss:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
|
||||
- (void)deleteResourcesSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
|
||||
{
|
||||
#pragma unused( contextInfo )
|
||||
#pragma unused( contextInfo )
|
||||
if( returnCode == NSOKButton )
|
||||
[self deleteSelectedResources];
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
SUPERCLASS = NSObject;
|
||||
},
|
||||
{
|
||||
CLASS = HexEditorDelegate;
|
||||
CLASS = HexWindowController;
|
||||
LANGUAGE = ObjC;
|
||||
OUTLETS = {pasteSubmenu = NSMenu; };
|
||||
SUPERCLASS = NSObject;
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
<?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>226 310 356 240 0 0 1152 848 </string>
|
||||
<string>285 171 356 240 0 0 1024 746 </string>
|
||||
<key>IBEditorPositions</key>
|
||||
<dict>
|
||||
<key>5</key>
|
||||
<string>226 555 72 66 0 0 1152 848 </string>
|
||||
<string>196 478 174 114 0 0 1024 746 </string>
|
||||
</dict>
|
||||
<key>IBFramework Version</key>
|
||||
<string>263.2</string>
|
||||
<string>286.0</string>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>5</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>5Q125</string>
|
||||
<string>6C115</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
Binary file not shown.
|
@ -11,7 +11,6 @@
|
|||
IBOutlet NSTextView *hex;
|
||||
IBOutlet NSTextView *offset;
|
||||
IBOutlet NSTextField *message;
|
||||
IBOutlet NSMenu *pasteSubmenu;
|
||||
|
||||
BOOL editedLow;
|
||||
}
|
||||
|
|
|
@ -11,17 +11,6 @@
|
|||
if( !self ) return nil;
|
||||
|
||||
editedLow = NO;
|
||||
|
||||
// swap paste: menu item for my own paste submenu
|
||||
{
|
||||
NSMenu *editMenu = [[[NSApp mainMenu] itemAtIndex:2] submenu];
|
||||
NSMenuItem *pasteItem = [editMenu itemAtIndex:[editMenu indexOfItemWithTarget:nil andAction:@selector(paste:)]];
|
||||
[NSBundle loadNibNamed:@"PasteMenu" owner:self];
|
||||
[pasteItem setEnabled:YES];
|
||||
[pasteItem setKeyEquivalent:@""];
|
||||
[pasteItem setKeyEquivalentModifierMask:0];
|
||||
[editMenu setSubmenu:pasteSubmenu forItem:pasteItem];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -29,11 +18,11 @@
|
|||
|
||||
- (NSString *)offsetRepresentation:(NSData *)data;
|
||||
{
|
||||
int row, dataLength = [data length], bytesPerRow = [controller bytesPerRow];
|
||||
int dataLength = [data length], bytesPerRow = [controller bytesPerRow];
|
||||
int rows = (dataLength / bytesPerRow) + ((dataLength % bytesPerRow)? 1:0);
|
||||
NSMutableString *representation = [NSMutableString string];
|
||||
|
||||
for( row = 0; row < rows; row++ )
|
||||
for( int row = 0; row < rows; row++ )
|
||||
[representation appendFormat:@"%08lX:", row * bytesPerRow];
|
||||
|
||||
return representation;
|
||||
|
@ -41,16 +30,16 @@
|
|||
|
||||
- (NSString *)hexRepresentation:(NSData *)data;
|
||||
{
|
||||
int row, addr, currentByte = 0, dataLength = [data length], bytesPerRow = [controller bytesPerRow];
|
||||
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];
|
||||
|
||||
// calculate bytes
|
||||
for( row = 0; row < rows; row++ )
|
||||
for( int row = 0; row < rows; row++ )
|
||||
{
|
||||
for( addr = 0; addr < bytesPerRow; addr++ )
|
||||
for( int addr = 0; addr < bytesPerRow; addr++ )
|
||||
{
|
||||
if( currentByte < dataLength )
|
||||
{
|
||||
|
@ -88,16 +77,16 @@
|
|||
|
||||
- (NSString *)asciiRepresentation:(NSData *)data;
|
||||
{
|
||||
int row, addr, currentByte = 0, dataLength = [data length], bytesPerRow = [controller bytesPerRow];
|
||||
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];
|
||||
|
||||
// calculate bytes
|
||||
for( row = 0; row < rows; row++ )
|
||||
for( int row = 0; row < rows; row++ )
|
||||
{
|
||||
for( addr = 0; addr < bytesPerRow; addr++ )
|
||||
for( int addr = 0; addr < bytesPerRow; addr++ )
|
||||
{
|
||||
if( currentByte < dataLength )
|
||||
{
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
@interface HexTextView : NSTextView
|
||||
{
|
||||
}
|
||||
- (void)editData:(NSMutableData *)data replaceBytesInRange:(NSRange)range withData:(NSData *)newData;
|
||||
- (void)pasteAsASCII:(id)sender;
|
||||
- (void)pasteAsHex:(id)sender;
|
||||
- (void)pasteAsUnicode:(id)sender;
|
||||
- (void)editData:(NSData *)data replaceBytesInRange:(NSRange)range withData:(NSData *)newData;
|
||||
@end
|
||||
|
||||
@interface NSTextView (HexTextView)
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
#import "HexTextView.h"
|
||||
|
||||
@implementation HexTextView
|
||||
@class _NSUndoStack;
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
[[[NSCursor alloc] initWithImage:[NSImage imageNamed:@"Show Info"] hotSpot:NSMakePoint(0,0)] set];
|
||||
return self;
|
||||
}
|
||||
@implementation HexTextView
|
||||
|
||||
- (void)drawRect:(NSRect)rect
|
||||
{
|
||||
|
@ -52,8 +47,9 @@
|
|||
newRange.location += 3;
|
||||
}
|
||||
|
||||
// select return character if selecting ascii - no longer necessary as there's a one-to-one for ascii
|
||||
/* else if( self == (id) [[self delegate] ascii] )
|
||||
// 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)
|
||||
|
@ -70,19 +66,114 @@
|
|||
else newRange.length += 1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
// call the superclass to update the selection
|
||||
[super setSelectedRange:newRange affinity:affinity stillSelecting:NO];
|
||||
}
|
||||
|
||||
/* NSText overrides */
|
||||
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)item
|
||||
{
|
||||
// NSMenuItem *pasteItem = [[item menu] itemAtIndex:[[item menu] indexOfItemWithTarget:nil andAction:@selector(paste:)]];
|
||||
|
||||
// paste submenu
|
||||
if( [item action] == @selector(paste:) )
|
||||
{
|
||||
NSMenu *editMenu = [[item menu] supermenu];
|
||||
[[editMenu itemAtIndex:[editMenu indexOfItemWithSubmenu:[item menu]]] setEnabled:[super validateMenuItem:item]];
|
||||
}
|
||||
else return [super validateMenuItem:item];
|
||||
}
|
||||
|
||||
- (void)paste:(id)sender
|
||||
{
|
||||
NSLog( @"paste:" );
|
||||
// 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)?
|
||||
[super paste: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]] )
|
||||
[self editData:[[[self window] windowController] data] replaceBytesInRange:byteSelection withData:[pb dataForType:NSStringPboardType]];
|
||||
}
|
||||
|
||||
- (void)pasteAsASCII:(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]] )
|
||||
[self editData:[[[self window] windowController] data] replaceBytesInRange:byteSelection withData:[pb dataForType:NSStringPboardType]];
|
||||
}
|
||||
|
||||
- (void)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]]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pasteAsUnicode:(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]] )
|
||||
{
|
||||
NSData *unicodeData = [[pb stringForType:NSStringPboardType] dataUsingEncoding:NSUnicodeStringEncoding];
|
||||
[self editData:[[[self window] windowController] data] replaceBytesInRange:byteSelection withData:unicodeData];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clear:(id)sender
|
||||
|
@ -104,7 +195,6 @@
|
|||
NSRange selection = [self rangeForUserTextChange], byteSelection;
|
||||
NSMutableData *data = [[[[self window] windowController] data] mutableCopy];
|
||||
NSData *replaceData = [NSData dataWithBytes:[string cString] length:[string cStringLength]];
|
||||
NSLog( @"insertText:" );
|
||||
|
||||
// get selection range
|
||||
if( self == (id) [[self delegate] hex] )
|
||||
|
@ -120,8 +210,7 @@
|
|||
if( self == (id) [[self delegate] hex] )
|
||||
{
|
||||
// bug: iteration through each character in string is broken, paste not yet mapped to this function
|
||||
int i;
|
||||
for( i= 0; i < [string cStringLength]; i++ )
|
||||
for( int i = 0; i < [string cStringLength]; i++ )
|
||||
{
|
||||
char typedChar = [string characterAtIndex:i];
|
||||
if( typedChar >= 0x30 && typedChar <= 0x39 ) typedChar -= 0x30; // 0 to 9
|
||||
|
@ -170,7 +259,6 @@
|
|||
{
|
||||
NSRange selection = [self rangeForUserTextChange], byteSelection;
|
||||
NSMutableData *data = [[[[self window] windowController] data] mutableCopy];
|
||||
NSLog( @"deleteBackward:" );
|
||||
|
||||
// get selection range
|
||||
if( self == (id) [[self delegate] hex] )
|
||||
|
@ -205,7 +293,6 @@
|
|||
{
|
||||
NSRange selection = [self rangeForUserTextChange], byteSelection;
|
||||
NSMutableData *data = [[[[self window] windowController] data] mutableCopy];
|
||||
NSLog( @"deleteForward:" );
|
||||
|
||||
// get selection range
|
||||
if( self == (id) [[self delegate] hex] )
|
||||
|
@ -251,22 +338,37 @@
|
|||
[self transpose:sender];
|
||||
}
|
||||
|
||||
- (void)editData:(NSMutableData *)data replaceBytesInRange:(NSRange)range withData:(NSData *)newData
|
||||
- (void)editData:(NSData *)data replaceBytesInRange:(NSRange)range withData:(NSData *)newBytes
|
||||
{
|
||||
// record an undo
|
||||
NSRange newRange = NSMakeRange( range.location, [newData length] );
|
||||
NSData *oldData = [[data subdataWithRange:range] retain]; // bug: memory leak, need to release somewhere
|
||||
[[[[self window] undoManager] prepareWithInvocationTarget:self] editData:data replaceBytesInRange:newRange withData:oldData];
|
||||
[[[self window] undoManager] setActionName:NSLocalizedString(@"Typing", nil)];
|
||||
|
||||
NSLog( @"Edit Called: replaceBytesInRange: %@ withData: %@", NSStringFromRange(range), [[[NSString alloc] initWithData:newData encoding:NSMacOSRomanStringEncoding] autorelease] );
|
||||
NSLog( @"Edit Saved: replaceBytesInRange: %@ withData: %@", NSStringFromRange(newRange), [[[NSString alloc] initWithData:oldData encoding:NSMacOSRomanStringEncoding] autorelease] );
|
||||
// save data we're about to replace so we can restore it in an undo
|
||||
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?)
|
||||
|
||||
// replace bytes (updates views implicitly)
|
||||
[data replaceBytesInRange:range withBytes:[newData bytes] length:[newData length]];
|
||||
[[(HexWindowController *)[[self window] windowController] resource] setData:data];
|
||||
[self setSelectedRange:NSMakeRange(range.location, [newData length])];
|
||||
// manipulate undo stack to concatenate multiple undos
|
||||
BOOL closeUndoGroup = NO;
|
||||
_NSUndoStack *undoStack = nil;
|
||||
if( ![[[self window] undoManager] isUndoing] )
|
||||
undoStack = [[[self window] undoManager] _undoStack];
|
||||
|
||||
if( undoStack && [undoStack count] > 0 && [[[self window] undoManager] groupingLevel] == 0 )
|
||||
{
|
||||
[undoStack popUndoObject]; // pop endUndoGrouping item
|
||||
closeUndoGroup = YES;
|
||||
}
|
||||
|
||||
// replace bytes, save new data, updates views & selection and mark doc as edited
|
||||
[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];
|
||||
|
||||
// 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 )
|
||||
[[[self window] undoManager] endUndoGrouping];
|
||||
// NSLog( @"%@", undoStack );
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -278,4 +380,4 @@
|
|||
isa = [HexTextView class];
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
|
@ -12,6 +12,7 @@
|
|||
IBOutlet NSTextView *hex;
|
||||
IBOutlet NSTextView *offset;
|
||||
IBOutlet NSTextField *message;
|
||||
IBOutlet NSMenu *pasteSubmenu;
|
||||
|
||||
NSUndoManager *undoManager;
|
||||
id <ResKnifeResourceProtocol> resource;
|
||||
|
|
|
@ -66,6 +66,31 @@
|
|||
// finally, show the window
|
||||
[self showWindow:self];
|
||||
}
|
||||
|
||||
- (void)windowDidBecomeKey:(NSNotification *)notification
|
||||
{
|
||||
// swap paste: menu item for my own paste submenu
|
||||
NSMenu *editMenu = [[[NSApp mainMenu] itemAtIndex:2] submenu];
|
||||
NSMenuItem *pasteItem = [editMenu itemAtIndex:[editMenu indexOfItemWithTarget:nil andAction:@selector(paste:)]];
|
||||
[NSBundle loadNibNamed:@"PasteMenu" owner:self];
|
||||
[pasteItem setEnabled:YES];
|
||||
[pasteItem setKeyEquivalent:@"\0"]; // clear key equiv. (yes, really!)
|
||||
[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 *pasteItem = [editMenu itemAtIndex:[editMenu indexOfItemWithSubmenu:pasteSubmenu]];
|
||||
[editMenu setSubmenu:nil forItem:pasteItem];
|
||||
[pasteItem setTarget:nil];
|
||||
[pasteItem setAction:@selector(paste:)];
|
||||
[pasteItem setKeyEquivalent:@"v"];
|
||||
[pasteItem setKeyEquivalentModifierMask:NSCommandKeyMask];
|
||||
}
|
||||
|
||||
/*
|
||||
- (BOOL)windowShouldClose:(NSWindow *)sender
|
||||
{
|
||||
|
@ -185,6 +210,11 @@
|
|||
return bytesPerRow;
|
||||
}
|
||||
|
||||
- (NSMenu *)pasteSubmenu
|
||||
{
|
||||
return pasteSubmenu;
|
||||
}
|
||||
|
||||
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)sender
|
||||
{
|
||||
return undoManager;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
- (void)readTemplate:(id <ResKnifeResourceProtocol>)tmpl;
|
||||
- (void)parseData;
|
||||
- (void)createUI;
|
||||
- (void)enumerateElements:(NSMutableArray *)elements;
|
||||
- (void)resourceDataDidChange:(NSNotification *)notification;
|
||||
- (void)refreshData:(NSData *)data;
|
||||
|
||||
|
|
1
Prefix Files/C99 Prefix.h
Normal file
1
Prefix Files/C99 Prefix.h
Normal file
|
@ -0,0 +1 @@
|
|||
#undef __STDC_VERSION__
|
|
@ -64,9 +64,10 @@
|
|||
F535443C022674B401A80001,
|
||||
);
|
||||
buildSettings = {
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_CFLAGS = "-std=c99";
|
||||
OTHER_LDFLAGS = "";
|
||||
OTHER_REZFLAGS = "";
|
||||
PREFIX_HEADER = "\"Prefix Files/C99 Prefix.h\"";
|
||||
PRODUCT_NAME = "Template Editor";
|
||||
SECTORDER_FLAGS = "";
|
||||
WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
|
||||
|
@ -877,9 +878,10 @@
|
|||
F57CEE130189C95101A8010B,
|
||||
);
|
||||
buildSettings = {
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_CFLAGS = "-std=c99";
|
||||
OTHER_LDFLAGS = "-bundle -undefined error";
|
||||
OTHER_REZFLAGS = "";
|
||||
PREFIX_HEADER = "\"Prefix Files/C99 Prefix.h\"";
|
||||
PRODUCT_NAME = "Hexadecimal Editor";
|
||||
SECTORDER_FLAGS = "";
|
||||
WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
|
||||
|
@ -1319,11 +1321,14 @@
|
|||
F5B588180156D30301000001,
|
||||
);
|
||||
buildSettings = {
|
||||
OTHER_CFLAGS = "";
|
||||
OPTIMIZATION_CFLAGS = "-O0";
|
||||
OTHER_CFLAGS = "-std=c99";
|
||||
OTHER_LDFLAGS = "";
|
||||
OTHER_REZFLAGS = "";
|
||||
PREBINDING = NO;
|
||||
PREFIX_HEADER = "\"Prefix Files/C99 Prefix.h\"";
|
||||
PRODUCT_NAME = "ResKnife Cocoa";
|
||||
PROFILING_CODE = YES;
|
||||
SECTORDER_FLAGS = "";
|
||||
WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
|
||||
WRAPPER_EXTENSION = app;
|
||||
|
@ -2232,6 +2237,7 @@
|
|||
children = (
|
||||
F5B5887D0156D6D901000001,
|
||||
F5B5887E0156D6D901000001,
|
||||
FDBB1C8E038062C0015E48C3,
|
||||
);
|
||||
isa = PBXGroup;
|
||||
path = "Prefix Files";
|
||||
|
@ -3572,9 +3578,10 @@
|
|||
F5DF1C0E0254C6BA01A80001,
|
||||
);
|
||||
buildSettings = {
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_CFLAGS = "-std=c99";
|
||||
OTHER_LDFLAGS = "";
|
||||
OTHER_REZFLAGS = "";
|
||||
PREFIX_HEADER = "\"Prefix Files/C99 Prefix.h\"";
|
||||
PRODUCT_NAME = NovaTools;
|
||||
SECTORDER_FLAGS = "";
|
||||
WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
|
||||
|
@ -3976,6 +3983,21 @@
|
|||
path = TemplateInitalisation.cpp;
|
||||
refType = 4;
|
||||
};
|
||||
//F50
|
||||
//F51
|
||||
//F52
|
||||
//F53
|
||||
//F54
|
||||
//FD0
|
||||
//FD1
|
||||
//FD2
|
||||
//FD3
|
||||
//FD4
|
||||
FDBB1C8E038062C0015E48C3 = {
|
||||
isa = PBXFileReference;
|
||||
path = "C99 Prefix.h";
|
||||
refType = 4;
|
||||
};
|
||||
};
|
||||
rootObject = F5B5880F0156D2A601000001;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user