2002-02-02 11:48:54 +00:00
# import "HexWindowController.h"
2002-02-23 03:40:24 +00:00
# import "HexTextView.h"
2002-03-21 04:02:26 +00:00
# import "FindSheetController.h"
2008-07-31 20:27:55 +00:00
# import "NSData-HexRepresentation.h"
2002-02-06 20:57:56 +00:00
2003-04-03 15:38:42 +00:00
/ *
2008-07-31 20:27:55 +00:00
OSStatus Plug_InitInstance ( Plug_PlugInRef plug , Plug_ResourceRef resource )
2003-04-03 15:38:42 +00:00
{
// init function called by carbon apps
2008-07-31 20:27:55 +00:00
if ( NSApplicationLoad ( ) )
2003-04-03 15:38:42 +00:00
{
id newResource = [ NSClassFromString ( @ "Resource" ) resourceOfType : [ NSString stringWithCString : length : 4 ] andID : [ NSNumber numberWithInt : ] withName : [ NSString stringWithCString : length : ] andAttributes : [ NSNumber numberWithUnsignedShort : ] data : [ NSData dataWithBytes : length : ] ] ;
2008-07-31 20:27:55 +00:00
if ( ! newResource ) return paramErr ;
2003-04-03 15:38:42 +00:00
id windowController = [ [ HexWindowController alloc ] initWithResource : newResource ] ;
2008-07-31 20:27:55 +00:00
if ( ! windowController ) return paramErr ;
2003-04-03 15:38:42 +00:00
else return noErr ;
}
else return paramErr ;
}
* /
2002-02-02 11:48:54 +00:00
@ implementation HexWindowController
- ( id ) initWithResource : ( id ) newResource
{
self = [ self initWithWindowNibName : @ "HexWindow" ] ;
2008-07-31 20:27:55 +00:00
if ( ! self ) return nil ;
2002-02-02 11:48:54 +00:00
// one instance of your principal class will be created for every resource the user wants to edit ( similar to Windows apps )
2002-05-31 00:17:53 +00:00
undoManager = [ [ NSUndoManager alloc ] init ] ;
2002-12-31 19:06:40 +00:00
liveEdit = NO ;
2008-07-31 20:27:55 +00:00
if ( liveEdit )
2002-12-31 19:06:40 +00:00
{
2008-07-31 20:27:55 +00:00
resource = [ newResource retain ] ; // resource to work on and monitor for external changes
backup = [ newResource copy ] ; // for reverting only
2002-12-31 19:06:40 +00:00
}
else
{
2008-07-31 20:27:55 +00:00
resource = [ newResource copy ] ; // resource to work on
backup = [ newResource retain ] ; // actual resource to change when saving data and monitor for external changes
2002-12-31 19:06:40 +00:00
}
2002-03-21 04:02:26 +00:00
bytesPerRow = 16 ;
2002-02-02 11:48:54 +00:00
2008-07-31 20:27:55 +00:00
// load the window from the nib file
[ self window ] ;
2002-02-02 11:48:54 +00:00
return self ;
}
2002-02-06 20:57:56 +00:00
- ( void ) dealloc
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : self ] ;
2003-04-03 15:38:42 +00:00
[ undoManager release ] ;
2008-07-31 20:27:55 +00:00
[ ( id ) resource release ] ;
2002-02-06 20:57:56 +00:00
[ super dealloc ] ;
}
2002-02-02 11:48:54 +00:00
- ( void ) windowDidLoad
{
[ super windowDidLoad ] ;
2008-07-31 20:27:55 +00:00
{
// 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 ] ] ;
}
2002-02-23 03:40:24 +00:00
// insert the resources ' data into the text fields
[ self refreshData : [ resource data ] ] ;
2008-07-31 20:27:55 +00:00
[ [ self window ] setResizeIncrements : NSMakeSize ( kWindowStepWidthPerChar * kWindowStepCharsPerStep * [ [ self window ] userSpaceScaleFactor ] , 1 ) ] ;
2002-11-15 15:12:42 +00:00
// min 346 , step 224 , norm 570 , step 224 , max 794
2002-02-12 01:24:53 +00:00
2008-07-31 20:27:55 +00:00
// here because we don ' t want these notifications until we have a window ! ( Only register for notifications on the resource we ' re editing )
2002-04-30 23:44:23 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( resourceNameDidChange : ) name : ResourceNameDidChangeNotification object : resource ] ;
2002-02-14 23:24:53 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( resourceDataDidChange : ) name : ResourceDataDidChangeNotification object : resource ] ;
2008-07-31 20:27:55 +00:00
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 ] ;
2002-02-11 22:03:20 +00:00
2008-07-31 20:27:55 +00:00
// finally , set the window title & show the window
[ [ self window ] setTitle : [ resource defaultWindowTitle ] ] ;
2002-02-02 11:48:54 +00:00
[ self showWindow : self ] ;
2002-02-06 20:57:56 +00:00
}
2002-11-13 03:35:54 +00:00
2002-11-15 15:12:42 +00:00
- ( void ) windowDidResize : ( NSNotification * ) notification
{
2008-07-31 20:27:55 +00:00
float width = [ [ ( NSWindow * ) [ notification object ] contentView ] frame ] . size . width ;
2002-11-15 15:12:42 +00:00
int oldBytesPerRow = bytesPerRow ;
bytesPerRow = ( ( ( width - ( kWindowStepWidthPerChar * kWindowStepCharsPerStep ) - 122 ) / ( kWindowStepWidthPerChar * kWindowStepCharsPerStep ) ) + 1 ) * kWindowStepCharsPerStep ;
2008-07-31 20:27:55 +00:00
if ( bytesPerRow ! = oldBytesPerRow )
2002-11-15 15:12:42 +00:00
[ offset setString : [ hexDelegate offsetRepresentation : [ resource data ] ] ] ;
2008-07-31 20:27:55 +00:00
[ [ 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 ) ] ;
2002-11-15 15:12:42 +00:00
}
2002-11-13 03:35:54 +00:00
- ( void ) windowDidBecomeKey : ( NSNotification * ) notification
{
NSMenu * editMenu = [ [ [ NSApp mainMenu ] itemAtIndex : 2 ] submenu ] ;
2008-07-31 20:27:55 +00:00
NSMenuItem * copyItem = [ editMenu itemAtIndex : [ editMenu indexOfItemWithTarget : nil andAction : @ selector ( copy : ) ] ] ;
2002-11-13 03:35:54 +00:00
NSMenuItem * pasteItem = [ editMenu itemAtIndex : [ editMenu indexOfItemWithTarget : nil andAction : @ selector ( paste : ) ] ] ;
2008-07-31 20:27:55 +00:00
// 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
2002-11-13 03:35:54 +00:00
[ pasteItem setEnabled : YES ] ;
2008-07-31 20:27:55 +00:00
[ pasteItem setKeyEquivalent : @ "\0" ] ;
2002-11-13 03:35:54 +00:00
[ pasteItem setKeyEquivalentModifierMask : 0 ] ;
[ editMenu setSubmenu : pasteSubmenu forItem : pasteItem ] ;
}
- ( void ) windowDidResignKey : ( NSNotification * ) notification
{
NSMenu * editMenu = [ [ [ NSApp mainMenu ] itemAtIndex : 2 ] submenu ] ;
2008-07-31 20:27:55 +00:00
NSMenuItem * copyItem = [ editMenu itemAtIndex : [ editMenu indexOfItemWithSubmenu : copySubmenu ] ] ;
2002-11-13 03:35:54 +00:00
NSMenuItem * pasteItem = [ editMenu itemAtIndex : [ editMenu indexOfItemWithSubmenu : pasteSubmenu ] ] ;
2008-07-31 20:27:55 +00:00
// 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
2002-11-13 03:35:54 +00:00
[ editMenu setSubmenu : nil forItem : pasteItem ] ;
[ pasteItem setTarget : nil ] ;
[ pasteItem setAction : @ selector ( paste : ) ] ;
[ pasteItem setKeyEquivalent : @ "v" ] ;
[ pasteItem setKeyEquivalentModifierMask : NSCommandKeyMask ] ;
}
2002-12-31 19:06:40 +00:00
- ( BOOL ) windowShouldClose : ( id ) sender
{
2008-07-31 20:27:55 +00:00
if ( [ [ self window ] isDocumentEdited ] )
2002-12-31 19:06:40 +00:00
{
2008-07-31 20:27:55 +00:00
NSBeginAlertSheet ( @ "Do you want to keep the changes you made to this resource?" , @ "Keep" , @ "Don<6F> t Keep" , @ "Cancel" , sender , self , @ selector ( saveSheetDidClose : returnCode : contextInfo : ) , nil , nil , @ "Your changes cannot be saved later if you don't keep them." ) ;
2002-12-31 19:06:40 +00:00
return NO ;
}
else return YES ;
}
- ( void ) saveSheetDidClose : ( NSWindow * ) sheet returnCode : ( int ) returnCode contextInfo : ( void * ) contextInfo
{
2008-07-31 20:27:55 +00:00
switch ( returnCode )
2002-12-31 19:06:40 +00:00
{
2008-07-31 20:27:55 +00:00
case NSAlertDefaultReturn : // keep
[ self saveResource : nil ] ;
2002-12-31 19:06:40 +00:00
[ [ self window ] close ] ;
break ;
2008-07-31 20:27:55 +00:00
case NSAlertAlternateReturn : // don ' t keep
2002-12-31 19:06:40 +00:00
[ [ self window ] close ] ;
break ;
case NSAlertOtherReturn : // cancel
break ;
}
}
2008-07-31 20:27:55 +00:00
- ( void ) saveResource : ( id ) sender
2002-12-31 19:06:40 +00:00
{
2008-07-31 20:27:55 +00:00
[ backup setData : [ [ resource data ] copy ] ] ;
2002-12-31 19:06:40 +00:00
}
2008-07-31 20:27:55 +00:00
- ( void ) revertResource : ( id ) sender
2002-05-31 00:17:53 +00:00
{
2008-07-31 20:27:55 +00:00
[ resource setData : [ [ backup data ] copy ] ] ;
2002-12-31 19:06:40 +00:00
}
2002-02-06 20:57:56 +00:00
2008-07-31 20:27:55 +00:00
- ( void ) showFind : ( id ) sender
2002-03-21 04:02:26 +00:00
{
// bug : HexWindowController allocs a sheet controller , but it ' s never disposed of
FindSheetController * sheetController = [ [ FindSheetController alloc ] initWithWindowNibName : @ "FindSheet" ] ;
[ sheetController showFindSheet : self ] ;
}
2002-04-30 23:44:23 +00:00
- ( void ) resourceNameDidChange : ( NSNotification * ) notification
{
2008-07-31 20:27:55 +00:00
[ [ self window ] setTitle : [ ( id < ResKnifeResourceProtocol > ) [ notification object ] defaultWindowTitle ] ] ;
2002-04-30 23:44:23 +00:00
}
2002-02-14 23:24:53 +00:00
- ( void ) resourceDataDidChange : ( NSNotification * ) notification
2002-02-12 01:24:53 +00:00
{
2002-02-14 23:24:53 +00:00
// ensure it ' s our resource which got changed ( should always be true , we don ' t register for other resource notifications )
2008-07-31 20:27:55 +00:00
// 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 )
2002-12-31 19:06:40 +00:00
{
2002-02-23 03:40:24 +00:00
[ self refreshData : [ resource data ] ] ;
2002-12-31 19:06:40 +00:00
[ self setDocumentEdited : YES ] ;
}
}
- ( void ) resourceWasSaved : ( NSNotification * ) notification
{
2008-07-31 20:27:55 +00:00
id < ResKnifeResourceProtocol > object = [ notification object ] ;
if ( liveEdit )
2002-12-31 19:06:40 +00:00
{
2008-07-31 20:27:55 +00:00
// haven ' t worked out what to do here yet
2002-12-31 19:06:40 +00:00
}
2008-07-31 20:27:55 +00:00
else
2002-12-31 19:06:40 +00:00
{
2008-07-31 20:27:55 +00:00
// this should refresh the view automatically
[ resource setData : [ [ object data ] copy ] ] ;
2002-12-31 19:06:40 +00:00
[ self setDocumentEdited : NO ] ;
}
2002-02-12 01:24:53 +00:00
}
2002-02-02 11:48:54 +00:00
- ( void ) refreshData : ( NSData * ) data ;
{
2002-02-23 03:40:24 +00:00
// save selections
NSRange hexSelection = [ hex selectedRange ] ;
NSRange asciiSelection = [ ascii selectedRange ] ;
2002-02-06 20:57:56 +00:00
// clear delegates ( see HexEditorDelegate class for explanation of why )
2002-02-02 11:48:54 +00:00
id oldDelegate = [ hex delegate ] ;
[ hex setDelegate : nil ] ;
[ ascii setDelegate : nil ] ;
2002-03-21 04:02:26 +00:00
// prepare attributes dictionary
2008-07-31 20:27:55 +00:00
NSMutableParagraphStyle * paragraph = [ [ NSParagraphStyle defaultParagraphStyle ] mutableCopy ] ;
2002-03-21 04:02:26 +00:00
[ paragraph setLineBreakMode : NSLineBreakByCharWrapping ] ;
2008-07-31 20:27:55 +00:00
NSDictionary * dictionary = [ NSDictionary dictionaryWithObject : paragraph forKey : NSParagraphStyleAttributeName ] ;
2002-03-21 04:02:26 +00:00
2002-02-02 11:48:54 +00:00
// do stuff with data
2008-07-31 20:27:55 +00:00
[ offset setString : [ hexDelegate offsetRepresentation : data ] ] ;
if ( [ data length ] > 0 )
[ hex setString : [ [ data hexRepresentation ] stringByAppendingString : @ " " ] ] ;
else [ hex setString : [ data hexRepresentation ] ] ;
[ ascii setString : [ data asciiRepresentation ] ] ;
2002-02-02 11:48:54 +00:00
2002-03-21 04:02:26 +00:00
// apply attributes
[ [ offset textStorage ] addAttributes : dictionary range : NSMakeRange ( 0 , [ [ offset textStorage ] length ] ) ] ;
[ [ hex textStorage ] addAttributes : dictionary range : NSMakeRange ( 0 , [ [ hex textStorage ] length ] ) ] ;
[ [ ascii textStorage ] addAttributes : dictionary range : NSMakeRange ( 0 , [ [ ascii textStorage ] length ] ) ] ;
2002-02-23 03:40:24 +00:00
// restore selections ( this is the dumbest way to do it , but it ' ll do for now )
2002-04-27 18:17:47 +00:00
[ hex setSelectedRange : NSIntersectionRange ( hexSelection , [ hex selectedRange ] ) ] ;
[ ascii setSelectedRange : NSIntersectionRange ( asciiSelection , [ ascii selectedRange ] ) ] ;
2002-02-23 03:40:24 +00:00
2002-02-02 11:48:54 +00:00
// restore delegates
[ hex setDelegate : oldDelegate ] ;
[ ascii setDelegate : oldDelegate ] ;
}
2002-02-06 20:57:56 +00:00
- ( id ) resource
{
return resource ;
}
- ( NSData * ) data
{
2002-02-23 03:40:24 +00:00
return [ resource data ] ;
2002-02-06 20:57:56 +00:00
}
2002-03-21 04:02:26 +00:00
- ( int ) bytesPerRow
{
return bytesPerRow ;
}
2008-07-31 20:27:55 +00:00
- ( NSMenu * ) copySubmenu
{
return copySubmenu ;
}
2002-11-13 03:35:54 +00:00
- ( NSMenu * ) pasteSubmenu
{
return pasteSubmenu ;
}
2002-05-09 23:29:22 +00:00
- ( NSUndoManager * ) windowWillReturnUndoManager : ( NSWindow * ) sender
{
2002-05-31 00:17:53 +00:00
return undoManager ;
2002-05-09 23:29:22 +00:00
}
2008-07-31 20:27:55 +00:00
/ * 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 ;
}
2002-04-27 18:17:47 +00:00
@ end