ResKnife/Hex Editor/Classes/Events.cpp

998 lines
34 KiB
C++
Raw Blame History

#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 );
}