ResKnife/Hex Editor/Classes/HexWindow.cpp

409 lines
13 KiB
C++

#include "HexWindow.h"
#include "Events.h"
#include "stdio.h"
// #include "strings.h"
extern globals g;
extern prefs p;
/*** CREATOR ***/
HexWindow::HexWindow( WindowRef newWindow )
{
// set contents to zero
memset( this, 0, sizeof(HexWindow) );
// initalise the bits that need initalising
window = newWindow;
#if USE_NIBS
// load stuff from nib file
#elif TARGET_API_MAC_CARBON
// create root control
ControlRef root;
CreateRootControl( window, &root );
// create header
header = null;
left = null;
right = null;
// create scroll bar
Rect windowBounds;
ControlActionUPP liveTrackingProc = NewControlActionUPP( TrackScrollbar );
GetWindowPortBounds( window, &windowBounds );
windowBounds.top += kHeaderHeight -1;
windowBounds.bottom -= kScrollBarWidth -2;
windowBounds.left = windowBounds.right - kScrollBarWidth +1;
windowBounds.right += 1;
CreateScrollBarControl( window, &windowBounds, 0, 0, 0, 0, true, liveTrackingProc, &scrollbar );
ControlID id = { kScrollbarSignature, 0 };
SetControlID( scrollbar, &id );
#else
// only create a scroll bar
scrollbar = GetNewControl( kSystem7ScrollBarControl, window );
#endif
}
/*** DESTRUCTOR ***/
HexWindow::~HexWindow( void )
{
// Host_ReleaseResourceData( hexInfo->data ); // decreases refCount
RemoveEventLoopTimer( timer );
DisposeControl( scrollbar );
}
/**********/
/* EVENTS */
/**********/
/*** BOUNDS CHANGING ***/
OSStatus HexWindow::BoundsChanging( EventRef event )
{
OSStatus error = noErr;
#if TARGET_API_MAC_CARBON
// check that window is not just being dragged
UInt32 attributes;
error = GetEventParameter( event, kEventParamAttributes, typeUInt32, null, sizeof(UInt32), null, &attributes );
if( error || attributes & kWindowBoundsChangeUserDrag ) return eventNotHandledErr;
// get new bounds
Rect windowBounds;
error = GetEventParameter( event, kEventParamCurrentBounds, typeQDRectangle, null, sizeof(Rect), null, &windowBounds );
if( error ) return eventNotHandledErr;
// constrain window width
UInt8 modulo = (windowBounds.right - windowBounds.left - 13*kHexCharWidth - kScrollBarWidth) % (4*kDataBlockWidth);
if( modulo < (2*kDataBlockWidth) ) windowBounds.right -= modulo;
else windowBounds.right += (4*kDataBlockWidth) - modulo;
// constrain window height
modulo = (windowBounds.bottom - windowBounds.top - kHeaderHeight - 2*kTextMargin -4) % kHexLineHeight;
if( modulo < (kHexLineHeight/2) ) windowBounds.bottom -= modulo;
else windowBounds.bottom += kHexLineHeight - modulo;
// update window rect to constrained version
if( (windowBounds.bottom - windowBounds.top) < kMinimumWindowHeight ) windowBounds.bottom = windowBounds.top + kMinimumWindowHeight;
if( (windowBounds.right - windowBounds.left) < kMinimumWindowWidth ) windowBounds.right = windowBounds.left + kMinimumWindowWidth;
error = SetEventParameter( event, kEventParamCurrentBounds, typeQDRectangle, sizeof(Rect), &windowBounds );
if( error ) error = eventNotHandledErr;
if( g.systemVersion < kMacOSX ) return noErr;
error = BoundsChanged( event );
#endif
return error;
}
/*** BOUNDS CHANGED ***/
OSStatus HexWindow::BoundsChanged( EventRef event )
{
Rect windowBounds;
OSStatus error = GetEventParameter( event, kEventParamCurrentBounds, typeQDRectangle, null, sizeof(Rect), null, &windowBounds );
if( error ) return eventNotHandledErr;
#if TARGET_API_MAC_CARBON
// resize header
SizeControl( header, (windowBounds.right - windowBounds.left) +2, kHeaderHeight +1 );
SizeControl( left, (windowBounds.right - windowBounds.left) /2 -4, kHeaderHeight -4 );
SizeControl( right, (windowBounds.right - windowBounds.left) /2 -4, kHeaderHeight -4 );
MoveControl( right, (windowBounds.right - windowBounds.left) /2, 2 );
#endif
// resize scrollbar
MoveControl( scrollbar, (windowBounds.right - windowBounds.left) - kScrollBarWidth +1, kHeaderHeight -1 );
SizeControl( scrollbar, kScrollBarWidth, (windowBounds.bottom - windowBounds.top) - kHeaderHeight - kScrollBarWidth +3 );
// calculate new scrollbar values & redraw window
UpdateHexInfo();
InvalidateWindowRect( window, &windowBounds );
EventRef updateEvent;
error = CreateEvent( null, kEventClassWindow, kEventWindowUpdate, GetCurrentEventTime(), 0, &updateEvent );
if( error == noErr )
{
SetEventParameter( updateEvent, kEventParamDirectObject, typeWindowRef, sizeof(WindowRef), &window );
SendEventToWindow( updateEvent, window );
ReleaseEvent( updateEvent );
}
return noErr;
}
/*** CONTENT CLICK ***/
OSStatus HexWindow::ContentClick( EventRef event )
{
#pragma unused( event )
return eventNotHandledErr;
}
/*** DRAW CONTENT ***/
OSStatus HexWindow::DrawContent( EventRef event )
{
#pragma unused( event ) // could be null if I called this directly, therefore I ignore the param
// set window
GrafPtr oldPort;
GetPort( &oldPort );
SetPortWindowPort( window );
// initalise rects
RGBColour colour;
Rect windowBounds, drawingBounds;
GetWindowPortBounds( window, &windowBounds );
SetRect( &drawingBounds, windowBounds.left, windowBounds.top + kHeaderHeight, windowBounds.right - kScrollBarWidth +1, windowBounds.bottom );
SetRect( &hexRect, kHexCharWidth *11 -3, kTextMargin + kHeaderHeight, kHexCharWidth *58 +3, windowBounds.bottom - kTextMargin );
SetRect( &asciiRect, kHexCharWidth *60 -3, kTextMargin + kHeaderHeight, kHexCharWidth *76 +3, windowBounds.bottom - kTextMargin );
#if Use_GWorlds
CGrafPtr origPort;
GDHandle origDev;
GWorldPtr theGWorld;
PixMapHandle thePixMap;
// create GWorld
GetGWorld( &origPort, &origDev );
// ( storage, bit depth, rect, colour table, GDevice, option flags ) // bit depth 0 = screen depth
NewGWorld( &theGWorld, p.GWorldDepth, &drawingBounds, null, null, 0 );
if( !theGWorld )
{
Host_DebugError( "\pNewGWorld() failed", 0 );
return eventNotHandledErr;
}
SetGWorld( theGWorld, null );
thePixMap = GetGWorldPixMap( theGWorld );
// lock the pixelmap in place
Boolean pixelsLocked = LockPixels( thePixMap );
if( !pixelsLocked )
{
UpdateGWorld( &theGWorld, 0, &drawingBounds, null, null, 0 );
pixelsLocked = LockPixels( thePixMap );
if( !pixelsLocked ) // if I still can't lock the damn pixels, give up! (don't even attempt writing to a moving target :)
{
SetGWorld( origPort, origDev );
DisposeGWorld( theGWorld );
Host_DebugError( "\pUpdateGWorld() failed", 0 );
return eventNotHandledErr;
}
}
#endif
// set font
short font;
GetFNum( "\pCourier", &font );
TextFont( font );
TextFace( 0 );
TextSize( 12 );
PenNormal();
// clear the background
RGBBackColor( &g.bgColour );
EraseRect( &drawingBounds );
// set text colour
if( activeWindow )
colour = g.black;
else colour = g.textColour;
RGBForeColor( &colour );
// draw box around hex
RGBBackColor( &g.white );
EraseRect( &hexRect );
FrameRect( &hexRect );
if( activeWindow && p.GWorldDepth > 1 )
{
RGBForeColor( &g.white );
MoveTo( hexRect.left, hexRect.bottom );
LineTo( hexRect.right, hexRect.bottom );
LineTo( hexRect.right, hexRect.top );
RGBForeColor( &g.bevelColour );
MoveTo( hexRect.right, hexRect.top -1 );
LineTo( hexRect.left -1, hexRect.top -1 );
LineTo( hexRect.left -1, hexRect.bottom );
}
// draw box around ascii
RGBForeColor( &colour );
RGBBackColor( &g.white );
EraseRect( &asciiRect );
FrameRect( &asciiRect );
if( activeWindow && p.GWorldDepth > 1 )
{
RGBForeColor( &g.white );
MoveTo( asciiRect.left, asciiRect.bottom );
LineTo( asciiRect.right, asciiRect.bottom );
LineTo( asciiRect.right, asciiRect.top );
RGBForeColor( &g.bevelColour );
MoveTo( asciiRect.right, asciiRect.top -1 );
LineTo( asciiRect.left -1, asciiRect.top -1 );
LineTo( asciiRect.left -1, asciiRect.bottom );
}
// hilight where selected text will be
if( selStart != selEnd )
{
// get regions of selected text
RgnHandle hexRgn = NewRgn(), asciiRgn = NewRgn(), insetRgn = NewRgn();
FindSelectedRegions( window, hexRgn, asciiRgn );
// set foreground colour to hilight colour
RGBColour hilightColour;
GetPortHilightColour( GetWindowPort(window), &hilightColour );
RGBForeColor( &hilightColour );
// draw what needs to be drawn
PenSize( 2, 2 );
if( editingHex ) PaintRgn( hexRgn );
else FrameRgn( hexRgn );
if( !editingHex ) PaintRgn( asciiRgn );
else FrameRgn( asciiRgn );
PenNormal();
DisposeRgn( hexRgn );
DisposeRgn( asciiRgn );
DisposeRgn( insetRgn );
}
// reset text colour
if( activeWindow )
colour = g.black;
else colour = g.textColour;
RGBForeColor( &colour );
// get useful data
Plug_WindowRef plugWindow = Host_GetPlugWindowFromWindowRef( window );
Plug_ResourceRef resource = Host_GetTargetResource( plugWindow );
// get resource length & lock data handle
UInt32 length = Host_GetResourceSize( resource ); // which of these two would be faster?
// UInt32 length = GetHandleSize( data ); // the host callback returns a value from a struct, GetHandleSize probably does the same
HLock( data );
if( Host_GetResourceSize( resource ) != GetHandleSize( data ) )
{
SetGWorld( origPort, origDev );
Host_DebugError( "\presource size != handle size", Host_GetResourceSize( resource ) ); // would be nice if I could pass both values! (any one know how to convert a number to > 1? - i.e. use "12.34" to pass 12 and 34)
SetGWorld( theGWorld, null );
}
// init variables
char buffer[16*4 +11];
long addr; // offset of byte in current res chunk
UInt32 line, // current line number
currentByte = topline *16; // offset of byte in resource
short hexPos, asciiPos;
unsigned char ascii, hex1, hex2; // HexEdit uses 'register short' not 'unsigned char' ?!?!
// start line count at scroll value
for( line = topline; line <= (lastline < bottomline? lastline:bottomline); line++ )
{
hexPos = 10;
asciiPos = 59;
sprintf( buffer, "%08lX: ", currentByte );
// draw bytes
for( addr = 0; addr < 16; addr++ )
{
if( currentByte < length )
{
BlockMoveData( *data + currentByte, &ascii, 1 );
hex1 = ascii;
hex2 = ascii;
hex1 >>= 4;
hex1 &= 0x0F;
hex2 &= 0x0F;
hex1 += (hex1 < 10)? 0x30 : 0x37;
hex2 += (hex2 < 10)? 0x30 : 0x37;
buffer[hexPos++] = hex1;
buffer[hexPos++] = hex2;
buffer[hexPos++] = 0x20;
if( ascii >= p.lowChar && ascii < p.highChar )
buffer[asciiPos++] = ascii;
else buffer[asciiPos++] = 0x2E; // full stop
}
else
{
// without this the ascii would be printed right next to the hex on the last line
buffer[hexPos++] = 0x20;
buffer[hexPos++] = 0x20;
buffer[hexPos++] = 0x20;
buffer[asciiPos++] = 0x20;
}
// fill hex/ascii gap with a space to make it print
buffer[58] = 0x20;
// advance current byte
currentByte++;
}
MoveTo( kHexCharWidth, kHeaderHeight + kTextMargin + kHexLineHeight*(line - topline +1) );
DrawText( buffer, 0, 75 ); // buffer, first byte, byte count
}
// unlock handle and record window heights
HUnlock( data );
#if Use_GWorlds
SetGWorld( origPort, origDev );
RGBBackColor( &g.white );
RGBForeColor( &g.black );
CopyBits( GetPortBitMapForCopyBits(theGWorld), GetPortBitMapForCopyBits(GetWindowPort(window)), &drawingBounds /*source rect*/, &drawingBounds /*dest rect*/, srcCopy, null );
UnlockPixels( thePixMap ); // line redundant as GWorld is disposed of next!
DisposeGWorld( theGWorld );
#endif
// restore old port
SetPort( oldPort );
return eventNotHandledErr;
}
/****************/
/* MAINTAINANCE */
/****************/
/*** UPDATE HEX INFO ***/
OSStatus HexWindow::UpdateHexInfo( void )
{
// hex and ascii rects
Rect windowBounds;
GetWindowPortBounds( window, &windowBounds );
SetRect( &hexRect, kHexCharWidth *11 -3, kTextMargin + kHeaderHeight, kHexCharWidth *58 +3, windowBounds.bottom - kTextMargin );
SetRect( &asciiRect, kHexCharWidth *60 -3, kTextMargin + kHeaderHeight, kHexCharWidth *76 +3, windowBounds.bottom - kTextMargin );
// scrollbar globals
firstline = 0;
topline = GetControlValue( scrollbar );
bottomline = ((windowBounds.bottom - windowBounds.top - kHeaderHeight - 2*kTextMargin -4) / kHexLineHeight) + topline -1;
lastline = (GetHandleSize( data ) /16) - (GetHandleSize( data ) %16? 0:1);
// scrollbar values
SInt32 number = lastline - (bottomline - topline);
if( number <= 0 ) SetControlValue( scrollbar, 0 );
SetControlMaximum( scrollbar, (number > 0)? number:0 );
SetControlViewSize( scrollbar, bottomline - topline );
return noErr;
}
/*** INSERT BYTES ***/
Boolean HexWindow::InsertBytes( void *newData, signed long length, unsigned long offset )
{
signed long size = (signed long) GetHandleSize( data );
if( size + length <= 0 ) length = -size; // don't dispose of resource, just set it to zero length
SInt8 state = HGetState( data );
HLock( data );
if( length > 0 ) // writing things
{
SetHandleSize( data, size + length );
if( MemError() ) return false;
BlockMoveData( *data + offset, *data + offset + length, size - offset );
BlockMoveData( newData, *data + offset, length );
}
else if( length < 0 ) // deleting things
{
BlockMoveData( *data + offset, *data + offset + length, size - offset );
SetHandleSize( data, size + length );
if( MemError() ) return false;
}
HSetState( data, state );
return true;
}