mirror of
https://github.com/nickshanks/ResKnife.git
synced 2025-01-06 23:30:09 +00:00
998 lines
34 KiB
C++
998 lines
34 KiB
C++
#include "Events.h"
|
||
#include "HexWindow.h"
|
||
#include "Utility.h"
|
||
|
||
extern globals g;
|
||
extern prefs p;
|
||
|
||
/******************/
|
||
/* EVENT HANDLING */
|
||
/******************/
|
||
|
||
/*** CARBON WINDOW EVENT HANDLER ***/
|
||
pascal OSStatus CarbonWindowEventHandler( EventHandlerCallRef handler, EventRef event, void *userData )
|
||
{
|
||
#pragma unused( handler )
|
||
OSStatus error = eventNotHandledErr;
|
||
Plug_PlugInRef plugRef = (Plug_PlugInRef) userData;
|
||
WindowRef window = GetUserFocusWindow();
|
||
|
||
// get event type
|
||
UInt32 eventClass = GetEventClass( event );
|
||
UInt32 eventKind = GetEventKind( event );
|
||
|
||
// get event parameters
|
||
if( eventClass == kEventClassWindow )
|
||
GetEventParameter( event, kEventParamDirectObject, typeWindowRef, null, sizeof(WindowRef), null, &window );
|
||
if( !window ) return error;
|
||
Plug_WindowRef plugWindow = Host_GetPlugWindowFromWindowRef( window );
|
||
if( !plugWindow ) return error;
|
||
HexWindowPtr hexWindow = (HexWindowPtr) Host_GetWindowRefCon( plugWindow );
|
||
if( !hexWindow ) return error;
|
||
|
||
// get window rect
|
||
Rect windowBounds;
|
||
GetWindowPortBounds( window, &windowBounds );
|
||
|
||
// handle event
|
||
static EventHandlerRef resizeEventHandlerRef = null;
|
||
switch( eventClass )
|
||
{
|
||
case kEventClassWindow:
|
||
switch( eventKind )
|
||
{
|
||
case kEventWindowClose:
|
||
delete hexWindow;
|
||
break;
|
||
|
||
case kEventWindowActivated:
|
||
case kEventWindowDeactivated:
|
||
if( hexWindow->activeWindow && hexWindow->insertionPointVisable )
|
||
BlinkInsertionPoint( null, window ); // this has to be done before the window is marked as deactivated
|
||
// hexWindow->activeWindow = !hexWindow->activeWindow; // bug: OS X is sending the event twice (naughty Apple!), so i shall do this more correctly as follows
|
||
if( eventKind == kEventWindowActivated )
|
||
hexWindow->activeWindow = true;
|
||
else hexWindow->activeWindow = false;
|
||
InvalidateWindowRect( window, &windowBounds );
|
||
break;
|
||
|
||
case kEventWindowBoundsChanging:
|
||
error = hexWindow->BoundsChanging( event );
|
||
break;
|
||
|
||
case kEventWindowBoundsChanged:
|
||
error = hexWindow->BoundsChanged( event );
|
||
break;
|
||
|
||
case kEventWindowDrawContent:
|
||
error = hexWindow->DrawContent( event );
|
||
break;
|
||
|
||
case kEventWindowHandleContentClick:
|
||
{ // get mouse
|
||
Point mouse;
|
||
error = GetEventParameter( event, kEventParamMouseLocation, typeQDPoint, null, sizeof(Point), null, &mouse );
|
||
if( !error )
|
||
{
|
||
MakeLocal( window, mouse, &mouse );
|
||
|
||
// get modifier keys
|
||
UInt32 modifiers = null;
|
||
error = GetEventParameter( event, kEventParamKeyModifiers, typeUInt32, null, sizeof(UInt32), null, &modifiers );
|
||
if( error ) break;
|
||
|
||
// identify click is in edit boxes & act accordingly
|
||
Boolean clickHex = false; // clicked in hex rect?
|
||
Boolean clickAscii = false; // clicked in ascii rect? - neither means not editing
|
||
Plug_WindowRef plugWindow = Host_GetPlugWindowFromWindowRef( window );
|
||
HexWindowPtr hexWindow = (HexWindowPtr) Host_GetWindowRefCon( plugWindow );
|
||
if( PtInRect( mouse, &hexWindow->hexRect ) ) { clickHex = true; hexWindow->editingHex = true; }
|
||
if( PtInRect( mouse, &hexWindow->asciiRect ) ) { clickAscii = true; hexWindow->editingHex = false; }
|
||
if( clickHex || clickAscii ) error = HandleEditClick( window, event, mouse, (EventModifiers) LoWord(modifiers) );
|
||
else error = eventNotHandledErr;
|
||
}
|
||
else error = eventNotHandledErr;
|
||
} break;
|
||
}
|
||
break;
|
||
|
||
case kEventClassKeyboard:
|
||
switch( eventKind )
|
||
{
|
||
case kEventRawKeyDown:
|
||
case kEventRawKeyRepeat:
|
||
{ signed char charCode; // key character pressed
|
||
UInt32 modifiers = null;
|
||
error = GetEventParameter( event, kEventParamKeyMacCharCodes, typeChar, null, sizeof(char), null, &charCode );
|
||
if( error ) break;
|
||
error = GetEventParameter( event, kEventParamKeyModifiers, typeUInt32, null, sizeof(UInt32), null, &modifiers );
|
||
if( error ) break;
|
||
HandleKeyDown( window, charCode, (EventModifiers) LoWord(modifiers) );
|
||
} break;
|
||
}
|
||
|
||
// calculate new scrollbar values & redraw window
|
||
hexWindow->UpdateHexInfo();
|
||
InvalidateWindowRect( window, &windowBounds );
|
||
break;
|
||
}
|
||
return error;
|
||
}
|
||
|
||
/*** CARBON HUMAN INTERFACE EVENT HANDLER ***/
|
||
pascal OSStatus CarbonHIEventHandler( EventHandlerCallRef handler, EventRef event, void *userData )
|
||
{
|
||
#pragma unused( handler, userData )
|
||
OSStatus error = eventNotHandledErr;
|
||
WindowRef window = GetUserFocusWindow(); // overridden below for window class events
|
||
if( !window ) return error;
|
||
|
||
// get event type
|
||
UInt32 eventClass = GetEventClass( event );
|
||
UInt32 eventKind = GetEventKind( event );
|
||
|
||
// get event parameters
|
||
Plug_WindowRef plugWindow = Host_GetPlugWindowFromWindowRef( window );
|
||
if( !plugWindow ) return error;
|
||
HexWindowPtr hexWindow = (HexWindowPtr) Host_GetWindowRefCon( plugWindow );
|
||
if( !hexWindow ) return error;
|
||
|
||
// handle event
|
||
switch( eventClass )
|
||
{
|
||
case kEventClassMenu:
|
||
switch( eventKind )
|
||
{
|
||
case kEventMenuEnableItems:
|
||
{ Plug_ResourceRef resource = Host_GetTargetResource( plugWindow );
|
||
Boolean canPlaySound = false;
|
||
if( Host_GetResourceType( resource ) == soundListRsrc )
|
||
canPlaySound = true;
|
||
|
||
// get host to set menus so we can modify them
|
||
Host_UpdateMenus( resource );
|
||
|
||
// edit menu
|
||
EnableCommand( null, kHICommandUndo, false );
|
||
EnableCommand( null, kHICommandRedo, false );
|
||
EnableCommand( null, kHICommandCut, hexWindow->selStart != hexWindow->selEnd );
|
||
EnableCommand( null, kHICommandCopy, hexWindow->selStart != hexWindow->selEnd );
|
||
EnableCommand( null, kHICommandPaste, true ); // bug
|
||
EnableCommand( null, kHICommandClear, hexWindow->selStart != hexWindow->selEnd );
|
||
error = noErr;
|
||
} break;
|
||
}
|
||
break;
|
||
|
||
case kEventClassCommand:
|
||
HICommand command;
|
||
Plug_ResourceRef resource = Host_GetTargetResource( plugWindow );
|
||
error = GetEventParameter( event, kEventParamDirectObject, typeHICommand, null, sizeof(HICommand), null, &command );
|
||
if( error ) return eventNotHandledErr;
|
||
switch( eventKind )
|
||
{
|
||
case kEventCommandProcess:
|
||
switch( command.commandID )
|
||
{
|
||
/* case kHICommandOK:
|
||
case kHICommandCancel:
|
||
case kHICommandQuit:
|
||
case kHICommandUndo:
|
||
case kHICommandRedo:
|
||
case kHICommandCut:
|
||
SendEventToWindow( copyEvent, window );
|
||
SendEventToWindow( clearEvent, window );
|
||
error = noErr;
|
||
break;
|
||
*/
|
||
case kHICommandCopy:
|
||
{ // copy should be disabled if there isn't a selection, but just in case<73>
|
||
if( hexWindow->selStart == hexWindow->selEnd ) return error;
|
||
|
||
// lock the data
|
||
Size size;
|
||
ScrapRef scrap;
|
||
ClearCurrentScrap();
|
||
error = GetCurrentScrap( &scrap );
|
||
if( error ) return error;
|
||
SInt8 state = HGetState( hexWindow->data );
|
||
HLock( hexWindow->data );
|
||
if( hexWindow->editingHex )
|
||
{
|
||
// copy data with hex formatting
|
||
size = 3*(hexWindow->selEnd - hexWindow->selStart) -1;
|
||
Ptr hex = NewPtrClear( size );
|
||
Ptr ascii = NewPtrClear( hexWindow->selEnd - hexWindow->selStart );
|
||
BlockMoveData( *hexWindow->data + hexWindow->selStart, ascii, hexWindow->selEnd - hexWindow->selStart );
|
||
AsciiToHex( ascii, hex, hexWindow->selEnd - hexWindow->selStart );
|
||
error = PutScrapFlavor( scrap, kScrapFlavorTypeText, kScrapFlavorMaskNone, size, hex );
|
||
DisposePtr( hex );
|
||
DisposePtr( ascii );
|
||
}
|
||
else
|
||
{
|
||
// copy raw data as byte stream
|
||
size = hexWindow->selEnd - hexWindow->selStart;
|
||
Ptr ascii = NewPtrClear( size );
|
||
BlockMoveData( *hexWindow->data + hexWindow->selStart, ascii, size );
|
||
error = PutScrapFlavor( scrap, kScrapFlavorTypeText, kScrapFlavorMaskNone, size, ascii );
|
||
DisposePtr( ascii );
|
||
}
|
||
HSetState( hexWindow->data, state );
|
||
error = noErr;
|
||
} break;
|
||
|
||
case kHICommandPaste:
|
||
{ Size size;
|
||
ScrapRef scrap;
|
||
error = GetCurrentScrap( &scrap );
|
||
if( error ) return error;
|
||
error = GetScrapFlavorSize( scrap, kScrapFlavorTypeText, &size );
|
||
if( error ) return error;
|
||
if( size > 0 )
|
||
{
|
||
Ptr bytes = NewPtr( size );
|
||
error = GetScrapFlavorData( scrap, kScrapFlavorTypeText, &size, bytes );
|
||
if( !error )
|
||
{
|
||
hexWindow->InsertBytes( null, hexWindow->selStart - hexWindow->selEnd, hexWindow->selEnd ); // remove this when using the above
|
||
hexWindow->InsertBytes( bytes, size, hexWindow->selStart );
|
||
hexWindow->selStart = hexWindow->selEnd += size;
|
||
}
|
||
DisposePtr( bytes );
|
||
Host_SetResourceDirty( resource, true );
|
||
}
|
||
error = noErr;
|
||
} break;
|
||
|
||
case kHICommandClear:
|
||
hexWindow->InsertBytes( nil, hexWindow->selStart - hexWindow->selEnd, hexWindow->selEnd );
|
||
hexWindow->selEnd = hexWindow->selStart;
|
||
Host_SetResourceDirty( resource, true );
|
||
error = noErr;
|
||
break;
|
||
|
||
case kHICommandSelectAll:
|
||
hexWindow->selStart = 0;
|
||
hexWindow->selEnd = GetHandleSize( hexWindow->data );
|
||
error = noErr;
|
||
break;
|
||
|
||
/* case kHICommandHide:
|
||
case kHICommandPreferences:
|
||
case kHICommandZoomWindow:
|
||
case kHICommandMinimizeWindow:
|
||
case kHICommandArrangeInFront:
|
||
case kHICommandAbout:
|
||
*/ default:
|
||
error = eventNotHandledErr;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case kEventCommandUpdateStatus:
|
||
error = eventNotHandledErr;
|
||
break;
|
||
|
||
default:
|
||
error = eventNotHandledErr;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
|
||
// most calls need window updating, so do it here
|
||
Rect windowBounds;
|
||
hexWindow->UpdateHexInfo();
|
||
GetWindowPortBounds( window, &windowBounds );
|
||
InvalidateWindowRect( window, &windowBounds );
|
||
return error;
|
||
}
|
||
|
||
#if !TARGET_API_MAC_CARBON
|
||
|
||
/*** CLASSIC WINDOW EVENT HANDLER ***/
|
||
pascal OSStatus ClassicWindowEventHandler( EventRecord *event, UInt32 eventKind, void *userData )
|
||
{
|
||
#pragma unused( event, userData )
|
||
OSStatus error = eventNotHandledErr;
|
||
/* Plug_PlugInRef plugRef = (Plug_PlugInRef) userData;
|
||
WindowRef window;
|
||
GetEventParameter( event, kEventParamDirectObject, typeWindowRef, null, sizeof(WindowRef), null, &window );
|
||
*/
|
||
switch( eventKind )
|
||
{
|
||
case kEventWindowDrawContent:
|
||
error = DrawWindow( window );
|
||
break;
|
||
|
||
case kEventWindowHandleContentClick:
|
||
SysBeep(0);
|
||
break;
|
||
}
|
||
return error;
|
||
}
|
||
|
||
#endif
|
||
|
||
/*****************/
|
||
/* WINDOW EVENTS */
|
||
/*****************/
|
||
|
||
pascal OSStatus ConstrainWindowResize( EventHandlerCallRef handler, EventRef event, void *userData )
|
||
{
|
||
#pragma unused( handler )
|
||
Rect windowBounds;
|
||
HexWindowPtr hexWindow = (HexWindowPtr) userData;
|
||
OSStatus 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;
|
||
|
||
// resize controls & update hex info
|
||
hexWindow->BoundsChanged( event );
|
||
return noErr;
|
||
}
|
||
|
||
/***************/
|
||
/* USER EVENTS */
|
||
/***************/
|
||
|
||
/*** HANDLE CLICK IN HEX/ASCII REGION ***/
|
||
OSStatus HandleEditClick( WindowRef window, EventRef event, Point mouse, EventModifiers modifiers )
|
||
{
|
||
OSStatus error = eventNotHandledErr;
|
||
|
||
// get hex info
|
||
Plug_WindowRef plugWindow = Host_GetPlugWindowFromWindowRef( window );
|
||
HexWindowPtr hexWindow = (HexWindowPtr) Host_GetWindowRefCon( plugWindow );
|
||
|
||
// a mouse down event has been recieved and is within hexRect or asciiRect
|
||
UInt16 clickLine; // from zero, the line in which the click occoured
|
||
UInt16 clickChar; // 0 to 16, the char after the clickloc, or at line end
|
||
|
||
// get line clicked on
|
||
clickLine = (mouse.v - kHeaderHeight - kTextMargin)/kHexLineHeight + hexWindow->topline;
|
||
if( clickLine > hexWindow->lastline ) // beyond last line
|
||
{
|
||
hexWindow->selStart = hexWindow->selEnd = GetHandleSize( hexWindow->data );
|
||
return noErr;
|
||
}
|
||
|
||
// get char clicked on -- duplicated in dragging routine
|
||
if( hexWindow->editingHex ) clickChar = (mouse.h - kHexCharWidth *10) / (kHexCharWidth*3);
|
||
else clickChar = (mouse.h - kHexCharWidth *60) / kHexCharWidth;
|
||
|
||
// check for drags
|
||
Point globalMouse;
|
||
MakeGlobal( window, mouse, &globalMouse );
|
||
Boolean dragging = WaitMouseMoved( globalMouse );
|
||
unsigned long selectFromChar = (clickLine *16) + clickChar;
|
||
unsigned long selectToChar = selectFromChar;
|
||
|
||
// define selection region
|
||
RgnHandle hexRgn = NewRgn(), asciiRgn = NewRgn(), selectedRgn = NewRgn();
|
||
FindSelectedRegions( window, hexRgn, asciiRgn );
|
||
UnionRgn( hexRgn, asciiRgn, selectedRgn );
|
||
|
||
// drag current selection
|
||
if( PtInRgn( mouse, selectedRgn ) && dragging )
|
||
{
|
||
OSErr error = noErr;
|
||
DragReference theDragRef;
|
||
ItemReference theItemRef = 1;
|
||
FlavorFlags theFlags = nil;
|
||
|
||
short resCounter = 0;
|
||
Size dataSize = hexWindow->selEnd - hexWindow->selStart;
|
||
RgnHandle dragRgn = NewRgn(), subtractRgn = NewRgn();
|
||
Handle dataToDrag = NewHandleClear( dataSize * (hexWindow->editingHex? 3:1) - (hexWindow->editingHex? 1:0) );
|
||
|
||
NewDrag( &theDragRef );
|
||
if( MemError() ) return eventNotHandledErr;
|
||
|
||
// get region of dragged items
|
||
if( hexWindow->editingHex ) dragRgn = hexRgn;
|
||
else dragRgn = asciiRgn;
|
||
CopyRgn( dragRgn, subtractRgn ); // duplicate region
|
||
InsetRgn( subtractRgn, 2, 2 ); // inset it by 2 pixels
|
||
DiffRgn( dragRgn, subtractRgn, dragRgn ); // subtract subRgn from dragRgn
|
||
|
||
// get the drag data
|
||
EventRecord eventRec;
|
||
Boolean eventValid = ConvertEventRefToEventRecord( event, &eventRec );
|
||
if( !eventValid ) eventValid = true; // bug: for some reason the event converter is not returning valid events, but the drag still works
|
||
if( dataToDrag && eventValid )
|
||
{
|
||
// I can't tell what the previous state of editingHex was, so cannot restore it.
|
||
// as such, i will redraw the window with the selection flipped if necessary
|
||
hexWindow->DrawContent();
|
||
|
||
SInt8 dataState = HGetState( hexWindow->data );
|
||
SInt8 dragState = HGetState( dataToDrag );
|
||
HLock( hexWindow->data );
|
||
HLock( dataToDrag );
|
||
if( hexWindow->editingHex ) AsciiToHex( *hexWindow->data + hexWindow->selStart, *dataToDrag, dataSize );
|
||
else BlockMoveData( *hexWindow->data + hexWindow->selStart, *dataToDrag, dataSize );
|
||
HSetState( hexWindow->data, dataState );
|
||
HSetState( dataToDrag, dragState );
|
||
|
||
// do the drag
|
||
SetPoint( &globalMouse, 0, 0 );
|
||
MakeGlobal( window, globalMouse, &globalMouse );
|
||
OffsetRgn( dragRgn, globalMouse.h, globalMouse.v );
|
||
error = AddDragItemFlavor( theDragRef, theItemRef, kScrapFlavorTypeText, *dataToDrag, GetHandleSize(dataToDrag), theFlags );
|
||
error = TrackDrag( theDragRef, &eventRec, dragRgn );
|
||
|
||
// when dragging from the ACSII pane, drag will contain <20>ascii<69>, when dragging from the HEX pane, drag will contain <20>61 73 63 69 69<36>
|
||
}
|
||
|
||
// clear up
|
||
if( dataToDrag ) DisposeHandle( dataToDrag );
|
||
if( theDragRef ) DisposeDrag( theDragRef );
|
||
if( subtractRgn ) DisposeRgn( subtractRgn );
|
||
if( dragRgn ) DisposeRgn( dragRgn );
|
||
}
|
||
|
||
// dragging new selection
|
||
else if( dragging )
|
||
{
|
||
// remove insetion point if visable
|
||
if( hexWindow->activeWindow && hexWindow->insertionPointVisable )
|
||
BlinkInsertionPoint( null, window );
|
||
|
||
Point localMouse;
|
||
while( WaitMouseUp() )
|
||
{
|
||
// get local mouse co-ords
|
||
GetMouse( &mouse );
|
||
MakeLocal( window, mouse, &localMouse );
|
||
|
||
// get line clicked on
|
||
clickLine = (localMouse.v - kHeaderHeight - kTextMargin)/kHexLineHeight + hexWindow->topline;
|
||
|
||
// get char clicked on
|
||
if( hexWindow->editingHex ) clickChar = (localMouse.h - kHexCharWidth *10) / (kHexCharWidth*3);
|
||
else clickChar = (localMouse.h - kHexCharWidth *60) / kHexCharWidth;
|
||
|
||
// update selection according to mouse position and scroll to maintain visibility
|
||
selectToChar = (clickLine *16) + clickChar;
|
||
if( selectToChar < 0 ) selectToChar = 0;
|
||
if( selectToChar > GetHandleSize(hexWindow->data) -1 ) selectToChar = GetHandleSize(hexWindow->data) -1;
|
||
if( selectFromChar < selectToChar )
|
||
{
|
||
hexWindow->selStart = selectFromChar;
|
||
hexWindow->selEnd = selectToChar +1;
|
||
// hexScrollToLine( theWindow, clickLine, kBottomOfWindow );
|
||
}
|
||
else if( selectFromChar > selectToChar )
|
||
{
|
||
hexWindow->selStart = selectToChar;
|
||
hexWindow->selEnd = selectFromChar;
|
||
// hexScrollToLine( theWindow, clickLine, kTopOfWindow );
|
||
}
|
||
else
|
||
{
|
||
hexWindow->selStart = selectFromChar;
|
||
hexWindow->selEnd = selectToChar +1;
|
||
}
|
||
|
||
// draw window
|
||
hexWindow->DrawContent();
|
||
}
|
||
}
|
||
|
||
// change cursor position
|
||
else
|
||
{
|
||
// remove insertion point if visable
|
||
if( hexWindow->activeWindow && hexWindow->insertionPointVisable )
|
||
BlinkInsertionPoint( null, window );
|
||
|
||
if( modifiers & shiftKey ) // extend selection
|
||
{
|
||
if( selectFromChar < hexWindow->selStart ) hexWindow->selStart = selectFromChar;
|
||
else hexWindow->selEnd = selectFromChar +1;
|
||
}
|
||
else // normal click
|
||
{
|
||
if( mouse.v < GetHandleSize(hexWindow->data) ) hexWindow->selStart = selectFromChar;
|
||
else hexWindow->selStart = GetHandleSize(hexWindow->data);
|
||
hexWindow->selEnd = selectToChar;
|
||
}
|
||
|
||
if( hexWindow->selStart < 0 ) hexWindow->selStart = 0;
|
||
if( hexWindow->selEnd > GetHandleSize(hexWindow->data) ) hexWindow->selEnd = GetHandleSize(hexWindow->data);
|
||
|
||
// invalidate window incase there was a selection
|
||
Rect windowBounds;
|
||
GetWindowPortBounds( window, &windowBounds );
|
||
InvalidateWindowRect( window, &windowBounds );
|
||
}
|
||
|
||
DisposeRgn( hexRgn );
|
||
DisposeRgn( asciiRgn );
|
||
DisposeRgn( selectedRgn );
|
||
return noErr;
|
||
}
|
||
|
||
/*** HANDLE EDIT DRAG ***/
|
||
OSStatus HandleEditDrag( WindowRef window, EventRef event, Point mouse, EventModifiers modifiers )
|
||
{
|
||
#pragma unused( window, event, mouse, modifiers )
|
||
return eventNotHandledErr;
|
||
}
|
||
|
||
/*** HANDLE KEY DOWN ***/
|
||
OSStatus HandleKeyDown( WindowRef window, unsigned char charCode, EventModifiers modifiers )
|
||
{
|
||
OSStatus error = eventNotHandledErr;
|
||
|
||
// get hex info
|
||
Plug_WindowRef plugWindow = Host_GetPlugWindowFromWindowRef( window );
|
||
Plug_ResourceRef resource = Host_GetTargetResource( plugWindow );
|
||
HexWindowPtr hexWindow = (HexWindowPtr) Host_GetWindowRefCon( plugWindow );
|
||
|
||
// handle arrow keys
|
||
Boolean resEdited = false;
|
||
switch( charCode )
|
||
{
|
||
case kTabCharCode:
|
||
case kEnterCharCode:
|
||
case kReturnCharCode:
|
||
hexWindow->editingHex = !hexWindow->editingHex;
|
||
break;
|
||
|
||
case kBackspaceCharCode: // delete
|
||
if( hexWindow->selStart == hexWindow->selEnd ) {
|
||
if( hexWindow->selStart != 0 ) {
|
||
hexWindow->InsertBytes( nil, -1, hexWindow->selEnd ); // delete prev char
|
||
hexWindow->selStart = hexWindow->selEnd -= 1; } }
|
||
else {
|
||
hexWindow->InsertBytes( nil, hexWindow->selStart - hexWindow->selEnd, hexWindow->selEnd ); // remove selection
|
||
hexWindow->selEnd = hexWindow->selStart; }
|
||
|
||
Host_SetResourceDirty( resource, true );
|
||
break;
|
||
|
||
case kDeleteCharCode: // forward delete
|
||
if( hexWindow->selStart == hexWindow->selEnd )
|
||
{
|
||
if( hexWindow->selStart != GetHandleSize( hexWindow->data ) )
|
||
hexWindow->InsertBytes( nil, -1, hexWindow->selEnd +1 ); // delete next char
|
||
}
|
||
else
|
||
{
|
||
hexWindow->InsertBytes( nil, hexWindow->selStart - hexWindow->selEnd, hexWindow->selEnd ); // remove selection
|
||
hexWindow->selEnd = hexWindow->selStart;
|
||
}
|
||
Host_SetResourceDirty( resource, true );
|
||
break;
|
||
|
||
case kLeftArrowCharCode:
|
||
case kRightArrowCharCode:
|
||
case kUpArrowCharCode:
|
||
case kDownArrowCharCode:
|
||
error = HandleArrowKeyDown( window, charCode, modifiers );
|
||
break;
|
||
|
||
default:
|
||
if( hexWindow->editingHex ) // editing in hexadecimal
|
||
{
|
||
Boolean deletePrev = false; // delete prev typing to add new one
|
||
if( hexWindow->editedHigh ) // edited high bits already
|
||
{
|
||
// shift typed char into high bits and add new low char
|
||
if( charCode >= 0x30 && charCode <= 0x39 ) charCode -= 0x30; // 0 to 9
|
||
else if( charCode >= 0x61 && charCode <= 0x66 ) charCode -= 0x57; // a to f
|
||
else if( charCode >= 0x93 && charCode <= 0x98 ) charCode -= 0x8A; // A to F
|
||
else break;
|
||
hexWindow->hexChar <<= 4; // store high bit
|
||
hexWindow->hexChar += charCode & 0x0F; // add low bit
|
||
hexWindow->selStart += 1;
|
||
hexWindow->selEnd = hexWindow->selStart;
|
||
hexWindow->editedHigh = false;
|
||
deletePrev = true;
|
||
}
|
||
else // editing low bits
|
||
{
|
||
// put typed char into low bits
|
||
if( charCode >= 0x30 && charCode <= 0x39 ) charCode -= 0x30; // 0 to 9
|
||
else if( charCode >= 0x61 && charCode <= 0x66 ) charCode -= 0x57; // a to f
|
||
else if( charCode >= 0x93 && charCode <= 0x98 ) charCode -= 0x8A; // A to F
|
||
else break;
|
||
hexWindow->hexChar = charCode & 0x0F;
|
||
hexWindow->editedHigh = true;
|
||
}
|
||
hexWindow->InsertBytes( nil, hexWindow->selStart - hexWindow->selEnd, hexWindow->selEnd ); // remove selection
|
||
hexWindow->selEnd = hexWindow->selStart;
|
||
if( deletePrev )
|
||
{
|
||
hexWindow->InsertBytes( nil, -1, hexWindow->selStart ); // remove previous hex char
|
||
hexWindow->InsertBytes( &hexWindow->hexChar, 1, hexWindow->selStart -1 ); // insert typed char (bug fix hack)
|
||
}
|
||
else hexWindow->InsertBytes( &hexWindow->hexChar, 1, hexWindow->selStart ); // insert typed char
|
||
}
|
||
else // editing in ascii
|
||
{
|
||
hexWindow->InsertBytes( nil, hexWindow->selStart - hexWindow->selEnd, hexWindow->selEnd ); // remove selection
|
||
hexWindow->selEnd = hexWindow->selStart;
|
||
hexWindow->InsertBytes( &charCode, 1, hexWindow->selStart ); // insert typed char
|
||
hexWindow->selStart += 1;
|
||
hexWindow->selEnd = hexWindow->selStart;
|
||
}
|
||
Host_SetResourceDirty( resource, true );
|
||
break;
|
||
}
|
||
|
||
// check selection is within resource
|
||
if( hexWindow->selStart > hexWindow->selEnd ) hexWindow->selStart = hexWindow->selEnd;
|
||
if( hexWindow->selStart > GetHandleSize(hexWindow->data) ) hexWindow->selStart = GetHandleSize(hexWindow->data);
|
||
if( hexWindow->selEnd > GetHandleSize(hexWindow->data) ) hexWindow->selEnd = GetHandleSize(hexWindow->data);
|
||
|
||
// modify key pressed so scroller interperets it correctly
|
||
if( modifiers & controlKey ) switch( charCode )
|
||
{
|
||
case kUpArrowCharCode:
|
||
charCode = kDownArrowCharCode;
|
||
break;
|
||
|
||
case kDownArrowCharCode:
|
||
charCode = kUpArrowCharCode;
|
||
break;
|
||
|
||
case kLeftArrowCharCode:
|
||
charCode = kRightArrowCharCode;
|
||
break;
|
||
|
||
case kRightArrowCharCode:
|
||
charCode = kLeftArrowCharCode;
|
||
break;
|
||
}
|
||
|
||
// get scrollbar
|
||
#if TARGET_API_MAC_CARBON
|
||
ControlRef scrollbar;
|
||
ControlID id = { kScrollbarSignature, 0 };
|
||
GetControlByID( window, &id, &scrollbar );
|
||
#else
|
||
ControlRef scrollbar = hexWindow->scrollbar;
|
||
#endif
|
||
|
||
// scroll to selection
|
||
switch( charCode )
|
||
{
|
||
case kUpArrowCharCode:
|
||
if( hexWindow->selStart /16 < hexWindow->topline +1 )
|
||
SetControlValue( scrollbar, hexWindow->selStart /16 -1 );
|
||
break;
|
||
|
||
case kDownArrowCharCode:
|
||
if( hexWindow->selEnd /16 > hexWindow->bottomline -1 )
|
||
SetControlValue( scrollbar, hexWindow->selEnd /16 - (hexWindow->bottomline - hexWindow->topline) +1 );
|
||
break;
|
||
|
||
case kBackspaceCharCode:
|
||
case kLeftArrowCharCode:
|
||
if( hexWindow->selStart /16 < hexWindow->topline +1 )
|
||
SetControlValue( scrollbar, hexWindow->selStart /16 -1 );
|
||
break;
|
||
|
||
case kRightArrowCharCode:
|
||
default:
|
||
if( hexWindow->selEnd /16 > hexWindow->bottomline -1 )
|
||
SetControlValue( scrollbar, hexWindow->selEnd /16 - (hexWindow->bottomline - hexWindow->topline) +1 );
|
||
break;
|
||
}
|
||
return error;
|
||
}
|
||
|
||
/*** HANDLE ARROW KEY DOWN ***/
|
||
OSStatus HandleArrowKeyDown( WindowRef window, unsigned char charCode, EventModifiers modifiers )
|
||
{
|
||
OSStatus error = noErr;
|
||
|
||
// get hex info
|
||
Plug_WindowRef plugWindow = Host_GetPlugWindowFromWindowRef( window );
|
||
Plug_ResourceRef resource = Host_GetTargetResource( plugWindow );
|
||
HexWindowPtr hexWindow = (HexWindowPtr) Host_GetWindowRefCon( plugWindow );
|
||
|
||
if( modifiers & optionKey ) switch( charCode ) // move selection
|
||
{
|
||
case kLeftArrowCharCode:
|
||
hexWindow->selStart -= 1;
|
||
hexWindow->selEnd -= 1;
|
||
break;
|
||
case kRightArrowCharCode:
|
||
hexWindow->selStart += 1;
|
||
hexWindow->selEnd += 1;
|
||
break;
|
||
case kUpArrowCharCode:
|
||
hexWindow->selStart -= 16;
|
||
hexWindow->selEnd -= 16;
|
||
break;
|
||
case kDownArrowCharCode:
|
||
hexWindow->selStart += 16;
|
||
hexWindow->selEnd += 16;
|
||
break;
|
||
}
|
||
|
||
else if( modifiers & shiftKey ) switch( charCode ) // extend selection
|
||
{
|
||
case kLeftArrowCharCode:
|
||
hexWindow->selStart -= 1;
|
||
break;
|
||
case kRightArrowCharCode:
|
||
hexWindow->selEnd += 1;
|
||
break;
|
||
case kUpArrowCharCode:
|
||
hexWindow->selStart -= 16;
|
||
break;
|
||
case kDownArrowCharCode:
|
||
hexWindow->selEnd += 16;
|
||
break;
|
||
}
|
||
|
||
else if( modifiers & controlKey ) switch( charCode ) // reduce selection
|
||
{
|
||
case kLeftArrowCharCode:
|
||
hexWindow->selEnd -= 1;
|
||
break;
|
||
case kRightArrowCharCode:
|
||
hexWindow->selStart += 1;
|
||
break;
|
||
case kUpArrowCharCode:
|
||
hexWindow->selEnd -= 16;
|
||
break;
|
||
case kDownArrowCharCode:
|
||
hexWindow->selStart += 16;
|
||
break;
|
||
}
|
||
|
||
else switch( charCode ) // move cursor
|
||
{
|
||
case kLeftArrowCharCode:
|
||
if( hexWindow->selStart == hexWindow->selEnd )
|
||
{
|
||
if( hexWindow->selStart >= 1 ) hexWindow->selStart -= 1;
|
||
if( hexWindow->selEnd >= 1 ) hexWindow->selEnd -= 1;
|
||
}
|
||
else hexWindow->selEnd = hexWindow->selStart;
|
||
hexWindow->editedHigh = false;
|
||
break;
|
||
|
||
case kRightArrowCharCode:
|
||
if( hexWindow->selStart == hexWindow->selEnd )
|
||
{
|
||
hexWindow->selStart += 1;
|
||
hexWindow->selEnd += 1;
|
||
}
|
||
else hexWindow->selStart = hexWindow->selEnd;
|
||
hexWindow->editedHigh = false;
|
||
break;
|
||
|
||
case kUpArrowCharCode:
|
||
if( hexWindow->selStart == hexWindow->selEnd )
|
||
{
|
||
if( hexWindow->selStart >= 16 ) hexWindow->selStart -= 16;
|
||
else hexWindow->selStart = 0;
|
||
if( hexWindow->selEnd >= 16 ) hexWindow->selEnd -= 16;
|
||
else hexWindow->selEnd = 0;
|
||
}
|
||
else hexWindow->selEnd = hexWindow->selStart;
|
||
hexWindow->editedHigh = false;
|
||
break;
|
||
|
||
case kDownArrowCharCode:
|
||
if( hexWindow->selStart == hexWindow->selEnd ) {
|
||
hexWindow->selStart += 16;
|
||
hexWindow->selEnd += 16; }
|
||
else hexWindow->selStart = hexWindow->selEnd;
|
||
hexWindow->editedHigh = false;
|
||
break;
|
||
}
|
||
|
||
return error;
|
||
}
|
||
|
||
/*** FIND SELECTED REGIONS ***/
|
||
OSStatus FindSelectedRegions( WindowRef window, RgnHandle hexRgn, RgnHandle asciiRgn )
|
||
{
|
||
// get hex info structure
|
||
Plug_WindowRef plugWindow = Host_GetPlugWindowFromWindowRef( window );
|
||
HexWindowPtr hexWindow = (HexWindowPtr) Host_GetWindowRefCon( plugWindow );
|
||
|
||
// get window bounds
|
||
Rect windowBounds, subtractRect;
|
||
RgnHandle subtractRgn = NewRgn();
|
||
GetWindowPortBounds( window, &windowBounds );
|
||
|
||
// start and end points of selection relative to first visable char
|
||
Boolean clipTop, clipBottom;
|
||
signed long startLine = hexWindow->selStart/16 - hexWindow->topline;
|
||
signed long endLine = (hexWindow->selEnd - hexWindow->selEnd %16)/16 - hexWindow->topline +1;
|
||
|
||
// find hex region
|
||
Rect hexSelRect;
|
||
SetRect( &hexSelRect, kHexCharWidth *11 -1, startLine * kHexLineHeight, kHexCharWidth *58 +1, endLine * kHexLineHeight );
|
||
OffsetRect( &hexSelRect, 0, kHeaderHeight + kTextMargin + 2 );
|
||
clipTop = hexSelRect.top < kHeaderHeight + kTextMargin +2;
|
||
clipBottom = hexSelRect.bottom > windowBounds.bottom - kTextMargin -2;
|
||
if( clipTop ) hexSelRect.top = kHeaderHeight + kTextMargin +2;
|
||
if( clipBottom ) hexSelRect.bottom = windowBounds.bottom - kTextMargin -2;
|
||
RectRgn( hexRgn, &hexSelRect );
|
||
|
||
if( !clipTop )
|
||
{
|
||
SetRect( &subtractRect, hexSelRect.left, hexSelRect.top, hexSelRect.left + (hexWindow->selStart % 16) * (kHexCharWidth *3), hexSelRect.top + kHexLineHeight );
|
||
RectRgn( subtractRgn, &subtractRect );
|
||
DiffRgn( hexRgn, subtractRgn, hexRgn );
|
||
}
|
||
|
||
if( !clipBottom )
|
||
{
|
||
SetRect( &subtractRect, hexSelRect.right - (16 - hexWindow->selEnd % 16) * (kHexCharWidth *3), hexSelRect.bottom - kHexLineHeight, hexSelRect.right, hexSelRect.bottom );
|
||
RectRgn( subtractRgn, &subtractRect );
|
||
DiffRgn( hexRgn, subtractRgn, hexRgn );
|
||
}
|
||
|
||
// find ascii region
|
||
Rect asciiSelRect;
|
||
SetRect( &asciiSelRect, kHexCharWidth *60 -1, startLine * kHexLineHeight, kHexCharWidth *76 +1, endLine * kHexLineHeight );
|
||
OffsetRect( &asciiSelRect, 0, kHeaderHeight + kTextMargin + 2 );
|
||
if( clipTop ) asciiSelRect.top = kHeaderHeight + kTextMargin +2;
|
||
if( clipBottom ) asciiSelRect.bottom = windowBounds.bottom - kTextMargin -2;
|
||
RectRgn( asciiRgn, &asciiSelRect );
|
||
|
||
if( !clipTop )
|
||
{
|
||
SetRect( &subtractRect, asciiSelRect.left, asciiSelRect.top, asciiSelRect.left + (hexWindow->selStart % 16) * kHexCharWidth, asciiSelRect.top + kHexLineHeight );
|
||
RectRgn( subtractRgn, &subtractRect );
|
||
DiffRgn( asciiRgn, subtractRgn, asciiRgn );
|
||
}
|
||
|
||
if( !clipBottom )
|
||
{
|
||
SetRect( &subtractRect, asciiSelRect.right - (16 - hexWindow->selEnd % 16) * kHexCharWidth, asciiSelRect.bottom - kHexLineHeight, asciiSelRect.right, asciiSelRect.bottom );
|
||
RectRgn( subtractRgn, &subtractRect );
|
||
DiffRgn( asciiRgn, subtractRgn, asciiRgn );
|
||
}
|
||
|
||
// clear up
|
||
DisposeRgn( subtractRgn );
|
||
return noErr;
|
||
}
|
||
|
||
/*** UPDATE SELECTION ***/
|
||
OSStatus UpdateSelection( WindowRef window, Boolean editingHex )
|
||
{
|
||
// get controls
|
||
ControlRef root, hex, ascii;
|
||
GetRootControl( window, &root );
|
||
GetIndexedSubControl( root, 3, &hex );
|
||
GetIndexedSubControl( root, 4, &ascii );
|
||
|
||
// reflect selection
|
||
Size actualSize;
|
||
ControlEditTextSelectionRec selection;
|
||
GetControlData( editingHex? hex:ascii, kControlEditTextPart, kControlEditTextSelectionTag, sizeof(ControlEditTextSelectionRec), &selection, &actualSize );
|
||
if( editingHex )
|
||
{
|
||
selection.selStart /= 3;
|
||
selection.selEnd = (selection.selEnd +1) /3;
|
||
}
|
||
else
|
||
{
|
||
selection.selStart *= 3;
|
||
selection.selEnd = (selection.selEnd *3) -1;
|
||
}
|
||
SetControlData( editingHex? ascii:hex, kControlEditTextPart, kControlEditTextSelectionTag, actualSize, &selection );
|
||
return noErr;
|
||
}
|
||
|
||
/********************/
|
||
/* CONTROL HANDLING */
|
||
/********************/
|
||
|
||
/*** TRACK SCROLL BAR ***/
|
||
pascal void TrackScrollbar( ControlRef control, short part )
|
||
{
|
||
WindowRef window = GetControlOwner(control);
|
||
Plug_WindowRef plugWindow = Host_GetPlugWindowFromWindowRef( window );
|
||
HexWindowPtr hexWindow = (HexWindowPtr) Host_GetWindowRefCon( plugWindow );
|
||
|
||
// decide how many lines to scroll ( and in which direction )
|
||
short startValue, delta, endValue, max;
|
||
if( !part ) return;
|
||
switch( part )
|
||
{
|
||
case kControlUpButtonPart: // up (20)
|
||
delta = -1;
|
||
break;
|
||
|
||
case kControlDownButtonPart: // down (21)
|
||
delta = 1;
|
||
break;
|
||
|
||
case kControlPageUpPart: // page up (22)
|
||
delta = hexWindow->topline - hexWindow->bottomline +1;
|
||
break;
|
||
|
||
case kControlPageDownPart: // page down (23)
|
||
delta = hexWindow->bottomline - hexWindow->topline -1;
|
||
break;
|
||
|
||
default:
|
||
delta = 0;
|
||
break;
|
||
}
|
||
|
||
// calculate and correct control value
|
||
max = GetControlMaximum( control );
|
||
startValue = GetControlValue( control );
|
||
endValue = startValue + delta;
|
||
if( endValue < 0 ) endValue = 0;
|
||
if( endValue > max ) endValue = max;
|
||
SetControlValue( control, endValue );
|
||
hexWindow->UpdateHexInfo();
|
||
|
||
// update the window
|
||
// InvalidateWindowRect( window, &windowBounds ); // doesn't update live scroll immediatly anyway, so may as well not call it
|
||
hexWindow->DrawContent();
|
||
}
|
||
|
||
/******************/
|
||
/* TIMER ROUTINES */
|
||
/******************/
|
||
|
||
/*** BLINK INSERTION POINT ***/
|
||
pascal void BlinkInsertionPoint( EventLoopTimerRef inTimer, void *inUserData )
|
||
{
|
||
#pragma unused( inTimer ) // inTimer can be null (if routine was not called from a timer)
|
||
|
||
// get window's info structure
|
||
WindowRef window = (WindowRef) inUserData;
|
||
Plug_WindowRef plugWindow = Host_GetPlugWindowFromWindowRef( window );
|
||
HexWindowPtr hexWindow = (HexWindowPtr) Host_GetWindowRefCon( plugWindow );
|
||
|
||
// if not focus window, don't flash cursor
|
||
if( !hexWindow->activeWindow ) return;
|
||
|
||
// if there's a selection, don't flash cursor
|
||
if( hexWindow->selStart != hexWindow->selEnd ) return;
|
||
|
||
// if insertion point is not within visable region, don't flash cursor
|
||
if( hexWindow->selStart < hexWindow->topline * 16 ) return;
|
||
if( hexWindow->selEnd >= (hexWindow->bottomline +1) * 16 ) return;
|
||
|
||
GrafPtr oldPort;
|
||
GetPort( &oldPort );
|
||
SetPortWindowPort( window );
|
||
Rect blinkRect;
|
||
|
||
long theStart = hexWindow->selStart - hexWindow->topline * 16;
|
||
long theEnd = hexWindow->selEnd - hexWindow->topline * 16;
|
||
if( hexWindow->editingHex ) SetRect( &blinkRect, kHexCharWidth *11 + (theStart %16) * (kHexCharWidth *3) -1, (theStart /16) * kHexLineHeight + kTextMargin +2,
|
||
kHexCharWidth *11 + (theStart %16) * (kHexCharWidth *3) +1, (theStart /16) * kHexLineHeight + kTextMargin +2 + kHexLineHeight );
|
||
else SetRect( &blinkRect, kHexCharWidth *60 + (theStart %16) * kHexCharWidth -1, (theStart /16) * kHexLineHeight + kTextMargin +2,
|
||
kHexCharWidth *60 + (theStart %16) * kHexCharWidth +1, (theStart /16) * kHexLineHeight + kTextMargin +2 + kHexLineHeight );
|
||
OffsetRect( &blinkRect, 0, kHeaderHeight );
|
||
InvertRect( &blinkRect );
|
||
hexWindow->insertionPointVisable = !hexWindow->insertionPointVisable;
|
||
|
||
SetPort( oldPort );
|
||
} |