2017-02-26 13:46:57 -05:00
using System ;
using System.Drawing ;
using System.Windows.Forms ;
using System.Runtime.InteropServices ;
using System.ComponentModel ;
using System.Security.Permissions ;
using System.Windows.Forms.VisualStyles ;
using System.Text ;
using System.Collections.Generic ;
namespace Be.Windows.Forms
{
/// <summary>
/// Represents a hex box control.
/// </summary>
[ToolboxBitmap(typeof(HexBox), "HexBox.bmp")]
public class HexBox : Control
{
#region IKeyInterpreter interface
/// <summary>
/// Defines a user input handler such as for mouse and keyboard input
/// </summary>
interface IKeyInterpreter
{
/// <summary>
/// Activates mouse events
/// </summary>
void Activate ( ) ;
/// <summary>
/// Deactivate mouse events
/// </summary>
void Deactivate ( ) ;
/// <summary>
/// Preprocesses WM_KEYUP window message.
/// </summary>
/// <param name="m">the Message object to process.</param>
/// <returns>True, if the message was processed.</returns>
bool PreProcessWmKeyUp ( ref Message m ) ;
/// <summary>
/// Preprocesses WM_CHAR window message.
/// </summary>
/// <param name="m">the Message object to process.</param>
/// <returns>True, if the message was processed.</returns>
bool PreProcessWmChar ( ref Message m ) ;
/// <summary>
/// Preprocesses WM_KEYDOWN window message.
/// </summary>
/// <param name="m">the Message object to process.</param>
/// <returns>True, if the message was processed.</returns>
bool PreProcessWmKeyDown ( ref Message m ) ;
/// <summary>
/// Gives some information about where to place the caret.
/// </summary>
/// <param name="byteIndex">the index of the byte</param>
/// <returns>the position where the caret is to place.</returns>
PointF GetCaretPointF ( long byteIndex ) ;
}
#endregion
#region EmptyKeyInterpreter class
/// <summary>
/// Represents an empty input handler without any functionality.
/// If is set ByteProvider to null, then this interpreter is used.
/// </summary>
class EmptyKeyInterpreter : IKeyInterpreter
{
HexBox _hexBox ;
public EmptyKeyInterpreter ( HexBox hexBox )
{
_hexBox = hexBox ;
}
#region IKeyInterpreter Members
public void Activate ( ) { }
public void Deactivate ( ) { }
public bool PreProcessWmKeyUp ( ref Message m )
{ return _hexBox . BasePreProcessMessage ( ref m ) ; }
public bool PreProcessWmChar ( ref Message m )
{ return _hexBox . BasePreProcessMessage ( ref m ) ; }
public bool PreProcessWmKeyDown ( ref Message m )
{ return _hexBox . BasePreProcessMessage ( ref m ) ; }
public PointF GetCaretPointF ( long byteIndex )
{ return new PointF ( ) ; }
#endregion
}
#endregion
#region KeyInterpreter class
/// <summary>
/// Handles user input such as mouse and keyboard input during hex view edit
/// </summary>
class KeyInterpreter : IKeyInterpreter
{
/// <summary>
/// Delegate for key-down processing.
/// </summary>
/// <param name="m">the message object contains key data information</param>
/// <returns>True, if the message was processed</returns>
delegate bool MessageDelegate ( ref Message m ) ;
#region Fields
/// <summary>
/// Contains the parent HexBox control
/// </summary>
protected HexBox _hexBox ;
/// <summary>
/// Contains True, if shift key is down
/// </summary>
protected bool _shiftDown ;
/// <summary>
/// Contains True, if mouse is down
/// </summary>
bool _mouseDown ;
/// <summary>
/// Contains the selection start position info
/// </summary>
BytePositionInfo _bpiStart ;
/// <summary>
/// Contains the current mouse selection position info
/// </summary>
BytePositionInfo _bpi ;
/// <summary>
/// Contains all message handlers of key interpreter key down message
/// </summary>
Dictionary < Keys , MessageDelegate > _messageHandlers ;
#endregion
#region Ctors
public KeyInterpreter ( HexBox hexBox )
{
_hexBox = hexBox ;
}
#endregion
#region Activate , Deactive methods
public virtual void Activate ( )
{
_hexBox . MouseDown + = new MouseEventHandler ( BeginMouseSelection ) ;
_hexBox . MouseMove + = new MouseEventHandler ( UpdateMouseSelection ) ;
_hexBox . MouseUp + = new MouseEventHandler ( EndMouseSelection ) ;
}
public virtual void Deactivate ( )
{
_hexBox . MouseDown - = new MouseEventHandler ( BeginMouseSelection ) ;
_hexBox . MouseMove - = new MouseEventHandler ( UpdateMouseSelection ) ;
_hexBox . MouseUp - = new MouseEventHandler ( EndMouseSelection ) ;
}
#endregion
#region Mouse selection methods
void BeginMouseSelection ( object sender , MouseEventArgs e )
{
System . Diagnostics . Debug . WriteLine ( "BeginMouseSelection()" , "KeyInterpreter" ) ;
if ( e . Button ! = MouseButtons . Left )
return ;
_mouseDown = true ;
if ( ! _shiftDown )
{
_bpiStart = new BytePositionInfo ( _hexBox . _bytePos , _hexBox . _byteCharacterPos ) ;
_hexBox . ReleaseSelection ( ) ;
}
else
{
UpdateMouseSelection ( this , e ) ;
}
}
void UpdateMouseSelection ( object sender , MouseEventArgs e )
{
if ( ! _mouseDown )
return ;
_bpi = GetBytePositionInfo ( new Point ( e . X , e . Y ) ) ;
long selEnd = _bpi . Index ;
long realselStart ;
long realselLength ;
if ( selEnd < _bpiStart . Index )
{
realselStart = selEnd ;
realselLength = _bpiStart . Index - selEnd ;
}
else if ( selEnd > _bpiStart . Index )
{
realselStart = _bpiStart . Index ;
realselLength = selEnd - realselStart ;
}
else
{
realselStart = _hexBox . _bytePos ;
realselLength = 0 ;
}
if ( realselStart ! = _hexBox . _bytePos | | realselLength ! = _hexBox . _selectionLength )
{
_hexBox . InternalSelect ( realselStart , realselLength ) ;
_hexBox . ScrollByteIntoView ( _bpi . Index ) ;
}
}
void EndMouseSelection ( object sender , MouseEventArgs e )
{
_mouseDown = false ;
}
#endregion
#region PrePrcessWmKeyDown methods
public virtual bool PreProcessWmKeyDown ( ref Message m )
{
System . Diagnostics . Debug . WriteLine ( "PreProcessWmKeyDown(ref Message m)" , "KeyInterpreter" ) ;
Keys vc = ( Keys ) m . WParam . ToInt32 ( ) ;
Keys keyData = vc | Control . ModifierKeys ;
// detect whether key down event should be raised
var hasMessageHandler = this . MessageHandlers . ContainsKey ( keyData ) ;
if ( hasMessageHandler & & RaiseKeyDown ( keyData ) )
return true ;
MessageDelegate messageHandler = hasMessageHandler
? this . MessageHandlers [ keyData ]
: messageHandler = new MessageDelegate ( PreProcessWmKeyDown_Default ) ;
return messageHandler ( ref m ) ;
}
protected bool PreProcessWmKeyDown_Default ( ref Message m )
{
_hexBox . ScrollByteIntoView ( ) ;
return _hexBox . BasePreProcessMessage ( ref m ) ;
}
protected bool RaiseKeyDown ( Keys keyData )
{
KeyEventArgs e = new KeyEventArgs ( keyData ) ;
_hexBox . OnKeyDown ( e ) ;
return e . Handled ;
}
protected virtual bool PreProcessWmKeyDown_Left ( ref Message m )
{
return PerformPosMoveLeft ( ) ;
}
protected virtual bool PreProcessWmKeyDown_Up ( ref Message m )
{
long pos = _hexBox . _bytePos ;
int cp = _hexBox . _byteCharacterPos ;
if ( ! ( pos = = 0 & & cp = = 0 ) )
{
pos = Math . Max ( - 1 , pos - _hexBox . _iHexMaxHBytes ) ;
if ( pos = = - 1 )
return true ;
_hexBox . SetPosition ( pos ) ;
if ( pos < _hexBox . _startByte )
{
_hexBox . PerformScrollLineUp ( ) ;
}
_hexBox . UpdateCaret ( ) ;
_hexBox . Invalidate ( ) ;
}
_hexBox . ScrollByteIntoView ( ) ;
_hexBox . ReleaseSelection ( ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_Right ( ref Message m )
{
2018-02-24 09:58:50 -05:00
return PerformPosMoveRight ( true ) ;
2017-02-26 13:46:57 -05:00
}
protected virtual bool PreProcessWmKeyDown_Down ( ref Message m )
{
long pos = _hexBox . _bytePos ;
int cp = _hexBox . _byteCharacterPos ;
if ( pos = = _hexBox . _byteProvider . Length & & cp = = 0 )
return true ;
pos = Math . Min ( _hexBox . _byteProvider . Length , pos + _hexBox . _iHexMaxHBytes ) ;
if ( pos = = _hexBox . _byteProvider . Length )
cp = 0 ;
_hexBox . SetPosition ( pos , cp ) ;
if ( pos > _hexBox . _endByte - 1 )
{
_hexBox . PerformScrollLineDown ( ) ;
}
_hexBox . UpdateCaret ( ) ;
_hexBox . ScrollByteIntoView ( ) ;
_hexBox . ReleaseSelection ( ) ;
_hexBox . Invalidate ( ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_PageUp ( ref Message m )
{
long pos = _hexBox . _bytePos ;
int cp = _hexBox . _byteCharacterPos ;
if ( pos = = 0 & & cp = = 0 )
return true ;
pos = Math . Max ( 0 , pos - _hexBox . _iHexMaxBytes ) ;
if ( pos = = 0 )
return true ;
_hexBox . SetPosition ( pos ) ;
if ( pos < _hexBox . _startByte )
{
_hexBox . PerformScrollPageUp ( ) ;
}
_hexBox . ReleaseSelection ( ) ;
_hexBox . UpdateCaret ( ) ;
_hexBox . Invalidate ( ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_PageDown ( ref Message m )
{
long pos = _hexBox . _bytePos ;
int cp = _hexBox . _byteCharacterPos ;
if ( pos = = _hexBox . _byteProvider . Length & & cp = = 0 )
return true ;
pos = Math . Min ( _hexBox . _byteProvider . Length , pos + _hexBox . _iHexMaxBytes ) ;
if ( pos = = _hexBox . _byteProvider . Length )
cp = 0 ;
_hexBox . SetPosition ( pos , cp ) ;
if ( pos > _hexBox . _endByte - 1 )
{
_hexBox . PerformScrollPageDown ( ) ;
}
_hexBox . ReleaseSelection ( ) ;
_hexBox . UpdateCaret ( ) ;
_hexBox . Invalidate ( ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_ShiftLeft ( ref Message m )
{
long pos = _hexBox . _bytePos ;
long sel = _hexBox . _selectionLength ;
if ( pos + sel < 1 )
return true ;
if ( pos + sel < = _bpiStart . Index )
{
if ( pos = = 0 )
return true ;
pos - - ;
sel + + ;
}
else
{
sel = Math . Max ( 0 , sel - 1 ) ;
}
_hexBox . ScrollByteIntoView ( ) ;
_hexBox . InternalSelect ( pos , sel ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_ShiftUp ( ref Message m )
{
long pos = _hexBox . _bytePos ;
long sel = _hexBox . _selectionLength ;
if ( pos - _hexBox . _iHexMaxHBytes < 0 & & pos < = _bpiStart . Index )
return true ;
if ( _bpiStart . Index > = pos + sel )
{
pos = pos - _hexBox . _iHexMaxHBytes ;
sel + = _hexBox . _iHexMaxHBytes ;
_hexBox . InternalSelect ( pos , sel ) ;
_hexBox . ScrollByteIntoView ( ) ;
}
else
{
sel - = _hexBox . _iHexMaxHBytes ;
if ( sel < 0 )
{
pos = _bpiStart . Index + sel ;
sel = - sel ;
_hexBox . InternalSelect ( pos , sel ) ;
_hexBox . ScrollByteIntoView ( ) ;
}
else
{
sel - = _hexBox . _iHexMaxHBytes ;
_hexBox . InternalSelect ( pos , sel ) ;
_hexBox . ScrollByteIntoView ( pos + sel ) ;
}
}
return true ;
}
protected virtual bool PreProcessWmKeyDown_ShiftRight ( ref Message m )
{
long pos = _hexBox . _bytePos ;
long sel = _hexBox . _selectionLength ;
if ( pos + sel > = _hexBox . _byteProvider . Length )
return true ;
if ( _bpiStart . Index < = pos )
{
sel + + ;
_hexBox . InternalSelect ( pos , sel ) ;
_hexBox . ScrollByteIntoView ( pos + sel ) ;
}
else
{
pos + + ;
sel = Math . Max ( 0 , sel - 1 ) ;
_hexBox . InternalSelect ( pos , sel ) ;
_hexBox . ScrollByteIntoView ( ) ;
}
return true ;
}
protected virtual bool PreProcessWmKeyDown_ShiftDown ( ref Message m )
{
long pos = _hexBox . _bytePos ;
long sel = _hexBox . _selectionLength ;
long max = _hexBox . _byteProvider . Length ;
if ( pos + sel + _hexBox . _iHexMaxHBytes > max )
return true ;
if ( _bpiStart . Index < = pos )
{
sel + = _hexBox . _iHexMaxHBytes ;
_hexBox . InternalSelect ( pos , sel ) ;
_hexBox . ScrollByteIntoView ( pos + sel ) ;
}
else
{
sel - = _hexBox . _iHexMaxHBytes ;
if ( sel < 0 )
{
pos = _bpiStart . Index ;
sel = - sel ;
}
else
{
pos + = _hexBox . _iHexMaxHBytes ;
//sel -= _hexBox._iHexMaxHBytes;
}
_hexBox . InternalSelect ( pos , sel ) ;
_hexBox . ScrollByteIntoView ( ) ;
}
return true ;
}
protected virtual bool PreProcessWmKeyDown_Tab ( ref Message m )
{
if ( _hexBox . _stringViewVisible & & _hexBox . _keyInterpreter . GetType ( ) = = typeof ( KeyInterpreter ) )
{
_hexBox . ActivateStringKeyInterpreter ( ) ;
_hexBox . ScrollByteIntoView ( ) ;
_hexBox . ReleaseSelection ( ) ;
_hexBox . UpdateCaret ( ) ;
_hexBox . Invalidate ( ) ;
return true ;
}
if ( _hexBox . Parent = = null ) return true ;
_hexBox . Parent . SelectNextControl ( _hexBox , true , true , true , true ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_ShiftTab ( ref Message m )
{
if ( _hexBox . _keyInterpreter is StringKeyInterpreter )
{
_shiftDown = false ;
_hexBox . ActivateKeyInterpreter ( ) ;
_hexBox . ScrollByteIntoView ( ) ;
_hexBox . ReleaseSelection ( ) ;
_hexBox . UpdateCaret ( ) ;
_hexBox . Invalidate ( ) ;
return true ;
}
if ( _hexBox . Parent = = null ) return true ;
_hexBox . Parent . SelectNextControl ( _hexBox , false , true , true , true ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_Back ( ref Message m )
{
if ( ! _hexBox . _byteProvider . SupportsDeleteBytes ( ) )
return true ;
if ( _hexBox . ReadOnly )
return true ;
long pos = _hexBox . _bytePos ;
long sel = _hexBox . _selectionLength ;
int cp = _hexBox . _byteCharacterPos ;
long startDelete = ( cp = = 0 & & sel = = 0 ) ? pos - 1 : pos ;
if ( startDelete < 0 & & sel < 1 )
return true ;
long bytesToDelete = ( sel > 0 ) ? sel : 1 ;
_hexBox . _byteProvider . DeleteBytes ( Math . Max ( 0 , startDelete ) , bytesToDelete ) ;
_hexBox . UpdateScrollSize ( ) ;
if ( sel = = 0 )
PerformPosMoveLeftByte ( ) ;
_hexBox . ReleaseSelection ( ) ;
_hexBox . Invalidate ( ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_Delete ( ref Message m )
{
if ( ! _hexBox . _byteProvider . SupportsDeleteBytes ( ) )
return true ;
if ( _hexBox . ReadOnly )
return true ;
long pos = _hexBox . _bytePos ;
long sel = _hexBox . _selectionLength ;
if ( pos > = _hexBox . _byteProvider . Length )
return true ;
long bytesToDelete = ( sel > 0 ) ? sel : 1 ;
_hexBox . _byteProvider . DeleteBytes ( pos , bytesToDelete ) ;
_hexBox . UpdateScrollSize ( ) ;
_hexBox . ReleaseSelection ( ) ;
_hexBox . Invalidate ( ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_Home ( ref Message m )
{
long pos = _hexBox . _bytePos ;
int cp = _hexBox . _byteCharacterPos ;
if ( pos < 1 )
return true ;
pos = 0 ;
cp = 0 ;
_hexBox . SetPosition ( pos , cp ) ;
_hexBox . ScrollByteIntoView ( ) ;
_hexBox . UpdateCaret ( ) ;
_hexBox . ReleaseSelection ( ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_End ( ref Message m )
{
long pos = _hexBox . _bytePos ;
int cp = _hexBox . _byteCharacterPos ;
if ( pos > = _hexBox . _byteProvider . Length - 1 )
return true ;
pos = _hexBox . _byteProvider . Length ;
cp = 0 ;
_hexBox . SetPosition ( pos , cp ) ;
_hexBox . ScrollByteIntoView ( ) ;
_hexBox . UpdateCaret ( ) ;
_hexBox . ReleaseSelection ( ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_ShiftShiftKey ( ref Message m )
{
if ( _mouseDown )
return true ;
if ( _shiftDown )
return true ;
_shiftDown = true ;
if ( _hexBox . _selectionLength > 0 )
return true ;
_bpiStart = new BytePositionInfo ( _hexBox . _bytePos , _hexBox . _byteCharacterPos ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_ControlC ( ref Message m )
{
_hexBox . Copy ( ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_ControlX ( ref Message m )
{
_hexBox . Cut ( ) ;
return true ;
}
protected virtual bool PreProcessWmKeyDown_ControlV ( ref Message m )
{
_hexBox . Paste ( ) ;
return true ;
}
#endregion
#region PreProcessWmChar methods
public virtual bool PreProcessWmChar ( ref Message m )
{
if ( Control . ModifierKeys = = Keys . Control )
{
return _hexBox . BasePreProcessMessage ( ref m ) ;
}
bool sw = _hexBox . _byteProvider . SupportsWriteByte ( ) ;
bool si = _hexBox . _byteProvider . SupportsInsertBytes ( ) ;
bool sd = _hexBox . _byteProvider . SupportsDeleteBytes ( ) ;
long pos = _hexBox . _bytePos ;
long sel = _hexBox . _selectionLength ;
int cp = _hexBox . _byteCharacterPos ;
if (
( ! sw & & pos ! = _hexBox . _byteProvider . Length ) | |
( ! si & & pos = = _hexBox . _byteProvider . Length ) )
{
return _hexBox . BasePreProcessMessage ( ref m ) ;
}
char c = ( char ) m . WParam . ToInt32 ( ) ;
if ( Uri . IsHexDigit ( c ) )
{
if ( RaiseKeyPress ( c ) )
return true ;
if ( _hexBox . ReadOnly )
return true ;
bool isInsertMode = ( pos = = _hexBox . _byteProvider . Length ) ;
// do insert when insertActive = true
if ( ! isInsertMode & & si & & _hexBox . InsertActive & & cp = = 0 )
isInsertMode = true ;
if ( sd & & si & & sel > 0 )
{
_hexBox . _byteProvider . DeleteBytes ( pos , sel ) ;
isInsertMode = true ;
cp = 0 ;
_hexBox . SetPosition ( pos , cp ) ;
}
_hexBox . ReleaseSelection ( ) ;
byte currentByte ;
if ( isInsertMode )
currentByte = 0 ;
else
currentByte = _hexBox . _byteProvider . ReadByte ( pos ) ;
string sCb = currentByte . ToString ( "X" , System . Threading . Thread . CurrentThread . CurrentCulture ) ;
if ( sCb . Length = = 1 )
sCb = "0" + sCb ;
string sNewCb = c . ToString ( ) ;
if ( cp = = 0 )
sNewCb + = sCb . Substring ( 1 , 1 ) ;
else
sNewCb = sCb . Substring ( 0 , 1 ) + sNewCb ;
byte newcb = byte . Parse ( sNewCb , System . Globalization . NumberStyles . AllowHexSpecifier , System . Threading . Thread . CurrentThread . CurrentCulture ) ;
2018-03-10 13:41:01 -05:00
if ( isInsertMode ) {
2017-02-26 13:46:57 -05:00
_hexBox . _byteProvider . InsertBytes ( pos , new byte [ ] { newcb } ) ;
2018-03-10 13:41:01 -05:00
} else {
if ( cp = = 1 | | ! _hexBox . ByteEditingMode ) {
_hexBox . _byteProvider . WriteByte ( pos , newcb ) ;
} else {
//First char in byte, do not update the changes right away
_hexBox . _byteProvider . PartialWriteByte ( pos , newcb ) ;
}
}
2017-02-26 13:46:57 -05:00
2018-02-24 09:58:50 -05:00
PerformPosMoveRight ( false ) ;
2017-02-26 13:46:57 -05:00
_hexBox . Invalidate ( ) ;
return true ;
}
else
{
return _hexBox . BasePreProcessMessage ( ref m ) ;
}
}
protected bool RaiseKeyPress ( char keyChar )
{
KeyPressEventArgs e = new KeyPressEventArgs ( keyChar ) ;
_hexBox . OnKeyPress ( e ) ;
return e . Handled ;
}
#endregion
#region PreProcessWmKeyUp methods
public virtual bool PreProcessWmKeyUp ( ref Message m )
{
System . Diagnostics . Debug . WriteLine ( "PreProcessWmKeyUp(ref Message m)" , "KeyInterpreter" ) ;
Keys vc = ( Keys ) m . WParam . ToInt32 ( ) ;
Keys keyData = vc | Control . ModifierKeys ;
switch ( keyData )
{
case Keys . ShiftKey :
case Keys . Insert :
if ( RaiseKeyUp ( keyData ) )
return true ;
break ;
}
switch ( keyData )
{
case Keys . ShiftKey :
_shiftDown = false ;
return true ;
case Keys . Insert :
return PreProcessWmKeyUp_Insert ( ref m ) ;
default :
return _hexBox . BasePreProcessMessage ( ref m ) ;
}
}
protected virtual bool PreProcessWmKeyUp_Insert ( ref Message m )
{
_hexBox . InsertActive = ! _hexBox . InsertActive ;
return true ;
}
protected bool RaiseKeyUp ( Keys keyData )
{
KeyEventArgs e = new KeyEventArgs ( keyData ) ;
_hexBox . OnKeyUp ( e ) ;
return e . Handled ;
}
#endregion
#region Misc
Dictionary < Keys , MessageDelegate > MessageHandlers
{
get
{
if ( _messageHandlers = = null )
{
_messageHandlers = new Dictionary < Keys , MessageDelegate > ( ) ;
_messageHandlers . Add ( Keys . Left , new MessageDelegate ( PreProcessWmKeyDown_Left ) ) ; // move left
_messageHandlers . Add ( Keys . Up , new MessageDelegate ( PreProcessWmKeyDown_Up ) ) ; // move up
_messageHandlers . Add ( Keys . Right , new MessageDelegate ( PreProcessWmKeyDown_Right ) ) ; // move right
_messageHandlers . Add ( Keys . Down , new MessageDelegate ( PreProcessWmKeyDown_Down ) ) ; // move down
_messageHandlers . Add ( Keys . PageUp , new MessageDelegate ( PreProcessWmKeyDown_PageUp ) ) ; // move pageup
_messageHandlers . Add ( Keys . PageDown , new MessageDelegate ( PreProcessWmKeyDown_PageDown ) ) ; // move page down
_messageHandlers . Add ( Keys . Left | Keys . Shift , new MessageDelegate ( PreProcessWmKeyDown_ShiftLeft ) ) ; // move left with selection
_messageHandlers . Add ( Keys . Up | Keys . Shift , new MessageDelegate ( PreProcessWmKeyDown_ShiftUp ) ) ; // move up with selection
_messageHandlers . Add ( Keys . Right | Keys . Shift , new MessageDelegate ( PreProcessWmKeyDown_ShiftRight ) ) ; // move right with selection
_messageHandlers . Add ( Keys . Down | Keys . Shift , new MessageDelegate ( PreProcessWmKeyDown_ShiftDown ) ) ; // move down with selection
_messageHandlers . Add ( Keys . Tab , new MessageDelegate ( PreProcessWmKeyDown_Tab ) ) ; // switch to string view
_messageHandlers . Add ( Keys . Back , new MessageDelegate ( PreProcessWmKeyDown_Back ) ) ; // back
_messageHandlers . Add ( Keys . Delete , new MessageDelegate ( PreProcessWmKeyDown_Delete ) ) ; // delete
_messageHandlers . Add ( Keys . Home , new MessageDelegate ( PreProcessWmKeyDown_Home ) ) ; // move to home
_messageHandlers . Add ( Keys . End , new MessageDelegate ( PreProcessWmKeyDown_End ) ) ; // move to end
_messageHandlers . Add ( Keys . ShiftKey | Keys . Shift , new MessageDelegate ( PreProcessWmKeyDown_ShiftShiftKey ) ) ; // begin selection process
_messageHandlers . Add ( Keys . C | Keys . Control , new MessageDelegate ( PreProcessWmKeyDown_ControlC ) ) ; // copy
_messageHandlers . Add ( Keys . X | Keys . Control , new MessageDelegate ( PreProcessWmKeyDown_ControlX ) ) ; // cut
_messageHandlers . Add ( Keys . V | Keys . Control , new MessageDelegate ( PreProcessWmKeyDown_ControlV ) ) ; // paste
}
return _messageHandlers ;
}
}
protected virtual bool PerformPosMoveLeft ( )
{
long pos = _hexBox . _bytePos ;
long sel = _hexBox . _selectionLength ;
int cp = _hexBox . _byteCharacterPos ;
if ( sel ! = 0 )
{
cp = 0 ;
_hexBox . SetPosition ( pos , cp ) ;
_hexBox . ReleaseSelection ( ) ;
}
else
{
if ( pos = = 0 & & cp = = 0 )
return true ;
2018-02-23 12:45:49 -05:00
if ( _hexBox . EnablePerByteNavigation ) {
2017-02-26 13:46:57 -05:00
pos = Math . Max ( 0 , pos - 1 ) ;
2018-02-23 12:45:49 -05:00
cp = 0 ;
} else {
if ( cp > 0 ) {
cp - - ;
} else {
pos = Math . Max ( 0 , pos - 1 ) ;
cp + + ;
}
2017-02-26 13:46:57 -05:00
}
_hexBox . SetPosition ( pos , cp ) ;
if ( pos < _hexBox . _startByte )
{
_hexBox . PerformScrollLineUp ( ) ;
}
_hexBox . UpdateCaret ( ) ;
_hexBox . Invalidate ( ) ;
}
_hexBox . ScrollByteIntoView ( ) ;
return true ;
}
2018-02-24 09:58:50 -05:00
protected virtual bool PerformPosMoveRight ( bool allowNibbleSkip )
2017-02-26 13:46:57 -05:00
{
long pos = _hexBox . _bytePos ;
int cp = _hexBox . _byteCharacterPos ;
long sel = _hexBox . _selectionLength ;
if ( sel ! = 0 )
{
pos + = sel ;
cp = 0 ;
_hexBox . SetPosition ( pos , cp ) ;
_hexBox . ReleaseSelection ( ) ;
}
else
{
if ( ! ( pos = = _hexBox . _byteProvider . Length & & cp = = 0 ) )
{
2018-02-24 09:58:50 -05:00
if ( cp > 0 | | ( allowNibbleSkip & & _hexBox . EnablePerByteNavigation ) ) {
2017-02-26 13:46:57 -05:00
pos = Math . Min ( _hexBox . _byteProvider . Length , pos + 1 ) ;
cp = 0 ;
2018-02-23 12:45:49 -05:00
} else {
2017-02-26 13:46:57 -05:00
cp + + ;
}
_hexBox . SetPosition ( pos , cp ) ;
if ( pos > _hexBox . _endByte - 1 )
{
_hexBox . PerformScrollLineDown ( ) ;
}
_hexBox . UpdateCaret ( ) ;
_hexBox . Invalidate ( ) ;
}
}
_hexBox . ScrollByteIntoView ( ) ;
return true ;
}
protected virtual bool PerformPosMoveLeftByte ( )
{
long pos = _hexBox . _bytePos ;
int cp = _hexBox . _byteCharacterPos ;
if ( pos = = 0 )
return true ;
pos = Math . Max ( 0 , pos - 1 ) ;
cp = 0 ;
_hexBox . SetPosition ( pos , cp ) ;
if ( pos < _hexBox . _startByte )
{
_hexBox . PerformScrollLineUp ( ) ;
}
_hexBox . UpdateCaret ( ) ;
_hexBox . ScrollByteIntoView ( ) ;
_hexBox . Invalidate ( ) ;
return true ;
}
protected virtual bool PerformPosMoveRightByte ( )
{
long pos = _hexBox . _bytePos ;
int cp = _hexBox . _byteCharacterPos ;
if ( pos = = _hexBox . _byteProvider . Length )
return true ;
pos = Math . Min ( _hexBox . _byteProvider . Length , pos + 1 ) ;
cp = 0 ;
_hexBox . SetPosition ( pos , cp ) ;
if ( pos > _hexBox . _endByte - 1 )
{
_hexBox . PerformScrollLineDown ( ) ;
}
_hexBox . UpdateCaret ( ) ;
_hexBox . ScrollByteIntoView ( ) ;
_hexBox . Invalidate ( ) ;
return true ;
}
public virtual PointF GetCaretPointF ( long byteIndex )
{
System . Diagnostics . Debug . WriteLine ( "GetCaretPointF()" , "KeyInterpreter" ) ;
return _hexBox . GetBytePointF ( byteIndex ) ;
}
protected virtual BytePositionInfo GetBytePositionInfo ( Point p )
{
return _hexBox . GetHexBytePositionInfo ( p ) ;
}
#endregion
}
#endregion
#region StringKeyInterpreter class
/// <summary>
/// Handles user input such as mouse and keyboard input during string view edit
/// </summary>
class StringKeyInterpreter : KeyInterpreter
{
#region Ctors
public StringKeyInterpreter ( HexBox hexBox )
: base ( hexBox )
{
_hexBox . _byteCharacterPos = 0 ;
}
#endregion
#region PreProcessWmKeyDown methods
public override bool PreProcessWmKeyDown ( ref Message m )
{
Keys vc = ( Keys ) m . WParam . ToInt32 ( ) ;
Keys keyData = vc | Control . ModifierKeys ;
switch ( keyData )
{
case Keys . Tab | Keys . Shift :
case Keys . Tab :
if ( RaiseKeyDown ( keyData ) )
return true ;
break ;
}
switch ( keyData )
{
case Keys . Tab | Keys . Shift :
return PreProcessWmKeyDown_ShiftTab ( ref m ) ;
case Keys . Tab :
return PreProcessWmKeyDown_Tab ( ref m ) ;
default :
return base . PreProcessWmKeyDown ( ref m ) ;
}
}
protected override bool PreProcessWmKeyDown_Left ( ref Message m )
{
return PerformPosMoveLeftByte ( ) ;
}
protected override bool PreProcessWmKeyDown_Right ( ref Message m )
{
return PerformPosMoveRightByte ( ) ;
}
#endregion
#region PreProcessWmChar methods
public override bool PreProcessWmChar ( ref Message m )
{
if ( Control . ModifierKeys = = Keys . Control )
{
return _hexBox . BasePreProcessMessage ( ref m ) ;
}
bool sw = _hexBox . _byteProvider . SupportsWriteByte ( ) ;
bool si = _hexBox . _byteProvider . SupportsInsertBytes ( ) ;
bool sd = _hexBox . _byteProvider . SupportsDeleteBytes ( ) ;
long pos = _hexBox . _bytePos ;
long sel = _hexBox . _selectionLength ;
int cp = _hexBox . _byteCharacterPos ;
if (
( ! sw & & pos ! = _hexBox . _byteProvider . Length ) | |
( ! si & & pos = = _hexBox . _byteProvider . Length ) )
{
return _hexBox . BasePreProcessMessage ( ref m ) ;
}
char c = ( char ) m . WParam . ToInt32 ( ) ;
if ( RaiseKeyPress ( c ) )
return true ;
if ( _hexBox . ReadOnly )
return true ;
bool isInsertMode = ( pos = = _hexBox . _byteProvider . Length ) ;
// do insert when insertActive = true
if ( ! isInsertMode & & si & & _hexBox . InsertActive )
isInsertMode = true ;
if ( sd & & si & & sel > 0 )
{
_hexBox . _byteProvider . DeleteBytes ( pos , sel ) ;
isInsertMode = true ;
cp = 0 ;
_hexBox . SetPosition ( pos , cp ) ;
}
_hexBox . ReleaseSelection ( ) ;
byte b = _hexBox . ByteCharConverter . ToByte ( c ) ;
if ( isInsertMode )
_hexBox . _byteProvider . InsertBytes ( pos , new byte [ ] { b } ) ;
else
_hexBox . _byteProvider . WriteByte ( pos , b ) ;
PerformPosMoveRightByte ( ) ;
_hexBox . Invalidate ( ) ;
return true ;
}
#endregion
#region Misc
public override PointF GetCaretPointF ( long byteIndex )
{
System . Diagnostics . Debug . WriteLine ( "GetCaretPointF()" , "StringKeyInterpreter" ) ;
Point gp = _hexBox . GetGridBytePoint ( byteIndex ) ;
return _hexBox . GetByteStringPointF ( gp ) ;
}
protected override BytePositionInfo GetBytePositionInfo ( Point p )
{
return _hexBox . GetStringBytePositionInfo ( p ) ;
}
#endregion
}
#endregion
#region Fields
/// <summary>
/// Contains the hole content bounds of all text
/// </summary>
Rectangle _recContent ;
/// <summary>
/// Contains the line info bounds
/// </summary>
Rectangle _recLineInfo ;
/// <summary>
/// Contains the column info header rectangle bounds
/// </summary>
Rectangle _recColumnInfo ;
/// <summary>
/// Contains the hex data bounds
/// </summary>
Rectangle _recHex ;
/// <summary>
/// Contains the string view bounds
/// </summary>
Rectangle _recStringView ;
/// <summary>
/// Contains string format information for text drawing
/// </summary>
StringFormat _stringFormat ;
/// <summary>
/// Contains the maximum of visible horizontal bytes
/// </summary>
int _iHexMaxHBytes ;
/// <summary>
/// Contains the maximum of visible vertical bytes
/// </summary>
int _iHexMaxVBytes ;
/// <summary>
/// Contains the maximum of visible bytes.
/// </summary>
int _iHexMaxBytes ;
/// <summary>
/// Contains the scroll bars minimum value
/// </summary>
long _scrollVmin ;
/// <summary>
/// Contains the scroll bars maximum value
/// </summary>
long _scrollVmax ;
/// <summary>
/// Contains the scroll bars current position
/// </summary>
long _scrollVpos ;
/// <summary>
/// Contains a vertical scroll
/// </summary>
VScrollBar _vScrollBar ;
/// <summary>
/// Contains a timer for thumbtrack scrolling
/// </summary>
Timer _thumbTrackTimer = new Timer ( ) ;
/// <summary>
/// Contains the thumbtrack scrolling position
/// </summary>
long _thumbTrackPosition ;
/// <summary>
/// Contains the thumptrack delay for scrolling in milliseconds.
/// </summary>
const int THUMPTRACKDELAY = 50 ;
/// <summary>
/// Contains the Enviroment.TickCount of the last refresh
/// </summary>
int _lastThumbtrack ;
/// <summary>
/// Contains the border<65> s left shift
/// </summary>
int _recBorderLeft = SystemInformation . Border3DSize . Width ;
/// <summary>
/// Contains the border<65> s right shift
/// </summary>
int _recBorderRight = SystemInformation . Border3DSize . Width ;
/// <summary>
/// Contains the border<65> s top shift
/// </summary>
int _recBorderTop = SystemInformation . Border3DSize . Height ;
/// <summary>
/// Contains the border bottom shift
/// </summary>
int _recBorderBottom = SystemInformation . Border3DSize . Height ;
/// <summary>
/// Contains the index of the first visible byte
/// </summary>
long _startByte ;
/// <summary>
/// Contains the index of the last visible byte
/// </summary>
long _endByte ;
/// <summary>
/// Contains the current byte position
/// </summary>
long _bytePos = - 1 ;
/// <summary>
/// Contains the current char position in one byte
/// </summary>
/// <example>
/// "1A"
/// "1" = char position of 0
/// "A" = char position of 1
/// </example>
int _byteCharacterPos ;
/// <summary>
/// Contains string format information for hex values
/// </summary>
string _hexStringFormat = "X" ;
/// <summary>
/// Contains the current key interpreter
/// </summary>
IKeyInterpreter _keyInterpreter ;
/// <summary>
/// Contains an empty key interpreter without functionality
/// </summary>
EmptyKeyInterpreter _eki ;
/// <summary>
/// Contains the default key interpreter
/// </summary>
KeyInterpreter _ki ;
/// <summary>
/// Contains the string key interpreter
/// </summary>
StringKeyInterpreter _ski ;
/// <summary>
/// Contains True if caret is visible
/// </summary>
bool _caretVisible ;
/// <summary>
/// Contains true, if the find (Find method) should be aborted.
/// </summary>
bool _abortFind ;
/// <summary>
/// Contains a value of the current finding position.
/// </summary>
long _findingPos ;
/// <summary>
/// Contains a state value about Insert or Write mode. When this value is true and the ByteProvider SupportsInsert is true bytes are inserted instead of overridden.
/// </summary>
bool _insertActive ;
PointF _caretPos ;
#endregion
#region Events
/// <summary>
/// Occurs, when the value of InsertActive property has changed.
/// </summary>
[Description("Occurs, when the value of InsertActive property has changed.")]
public event EventHandler InsertActiveChanged ;
/// <summary>
/// Occurs, when the value of ReadOnly property has changed.
/// </summary>
[Description("Occurs, when the value of ReadOnly property has changed.")]
public event EventHandler ReadOnlyChanged ;
/// <summary>
/// Occurs, when the value of ByteProvider property has changed.
/// </summary>
[Description("Occurs, when the value of ByteProvider property has changed.")]
public event EventHandler ByteProviderChanged ;
/// <summary>
/// Occurs, when the value of SelectionStart property has changed.
/// </summary>
[Description("Occurs, when the value of SelectionStart property has changed.")]
public event EventHandler SelectionStartChanged ;
/// <summary>
/// Occurs, when the value of SelectionLength property has changed.
/// </summary>
[Description("Occurs, when the value of SelectionLength property has changed.")]
public event EventHandler SelectionLengthChanged ;
/// <summary>
/// Occurs, when the value of LineInfoVisible property has changed.
/// </summary>
[Description("Occurs, when the value of LineInfoVisible property has changed.")]
public event EventHandler LineInfoVisibleChanged ;
/// <summary>
/// Occurs, when the value of ColumnInfoVisibleChanged property has changed.
/// </summary>
[Description("Occurs, when the value of ColumnInfoVisibleChanged property has changed.")]
public event EventHandler ColumnInfoVisibleChanged ;
/// <summary>
/// Occurs, when the value of GroupSeparatorVisibleChanged property has changed.
/// </summary>
[Description("Occurs, when the value of GroupSeparatorVisibleChanged property has changed.")]
public event EventHandler GroupSeparatorVisibleChanged ;
/// <summary>
/// Occurs, when the value of StringViewVisible property has changed.
/// </summary>
[Description("Occurs, when the value of StringViewVisible property has changed.")]
public event EventHandler StringViewVisibleChanged ;
/// <summary>
/// Occurs, when the value of BorderStyle property has changed.
/// </summary>
[Description("Occurs, when the value of BorderStyle property has changed.")]
public event EventHandler BorderStyleChanged ;
/// <summary>
/// Occurs, when the value of ColumnWidth property has changed.
/// </summary>
[Description("Occurs, when the value of GroupSize property has changed.")]
public event EventHandler GroupSizeChanged ;
/// <summary>
/// Occurs, when the value of BytesPerLine property has changed.
/// </summary>
[Description("Occurs, when the value of BytesPerLine property has changed.")]
public event EventHandler BytesPerLineChanged ;
/// <summary>
/// Occurs, when the value of UseFixedBytesPerLine property has changed.
/// </summary>
[Description("Occurs, when the value of UseFixedBytesPerLine property has changed.")]
public event EventHandler UseFixedBytesPerLineChanged ;
/// <summary>
/// Occurs, when the value of VScrollBarVisible property has changed.
/// </summary>
[Description("Occurs, when the value of VScrollBarVisible property has changed.")]
public event EventHandler VScrollBarVisibleChanged ;
/// <summary>
/// Occurs, when the value of HexCasing property has changed.
/// </summary>
[Description("Occurs, when the value of HexCasing property has changed.")]
public event EventHandler HexCasingChanged ;
/// <summary>
/// Occurs, when the value of HorizontalByteCount property has changed.
/// </summary>
[Description("Occurs, when the value of HorizontalByteCount property has changed.")]
public event EventHandler HorizontalByteCountChanged ;
/// <summary>
/// Occurs, when the value of VerticalByteCount property has changed.
/// </summary>
[Description("Occurs, when the value of VerticalByteCount property has changed.")]
public event EventHandler VerticalByteCountChanged ;
/// <summary>
/// Occurs, when the value of CurrentLine property has changed.
/// </summary>
[Description("Occurs, when the value of CurrentLine property has changed.")]
public event EventHandler CurrentLineChanged ;
/// <summary>
/// Occurs, when the value of CurrentPositionInLine property has changed.
/// </summary>
[Description("Occurs, when the value of CurrentPositionInLine property has changed.")]
public event EventHandler CurrentPositionInLineChanged ;
/// <summary>
/// Occurs, when Copy method was invoked and ClipBoardData changed.
/// </summary>
[Description("Occurs, when Copy method was invoked and ClipBoardData changed.")]
public event EventHandler Copied ;
/// <summary>
/// Occurs, when CopyHex method was invoked and ClipBoardData changed.
/// </summary>
[Description("Occurs, when CopyHex method was invoked and ClipBoardData changed.")]
public event EventHandler CopiedHex ;
/// <summary>
/// Occurs, when the CharSize property has changed
/// </summary>
[Description("Occurs, when the CharSize property has changed")]
public event EventHandler CharSizeChanged ;
/// <summary>
/// Occurs, when the RequiredWidth property changes
/// </summary>
[Description("Occurs, when the RequiredWidth property changes")]
public event EventHandler RequiredWidthChanged ;
#endregion
#region Ctors
/// <summary>
/// Initializes a new instance of a HexBox class.
/// </summary>
public HexBox ( )
{
this . _vScrollBar = new VScrollBar ( ) ;
this . _vScrollBar . Scroll + = new ScrollEventHandler ( _vScrollBar_Scroll ) ;
this . _builtInContextMenu = new BuiltInContextMenu ( this ) ;
BackColor = Color . White ;
Font = SystemFonts . MessageBoxFont ;
_stringFormat = new StringFormat ( StringFormat . GenericTypographic ) ;
_stringFormat . FormatFlags = StringFormatFlags . MeasureTrailingSpaces ;
ActivateEmptyKeyInterpreter ( ) ;
SetStyle ( ControlStyles . UserPaint , true ) ;
SetStyle ( ControlStyles . DoubleBuffer , true ) ;
SetStyle ( ControlStyles . AllPaintingInWmPaint , true ) ;
SetStyle ( ControlStyles . ResizeRedraw , true ) ;
_thumbTrackTimer . Interval = 50 ;
_thumbTrackTimer . Tick + = new EventHandler ( PerformScrollThumbTrack ) ;
}
#endregion
#region Scroll methods
void _vScrollBar_Scroll ( object sender , ScrollEventArgs e )
{
switch ( e . Type )
{
case ScrollEventType . Last :
break ;
case ScrollEventType . EndScroll :
break ;
case ScrollEventType . SmallIncrement :
PerformScrollLineDown ( ) ;
break ;
case ScrollEventType . SmallDecrement :
PerformScrollLineUp ( ) ;
break ;
case ScrollEventType . LargeIncrement :
PerformScrollPageDown ( ) ;
break ;
case ScrollEventType . LargeDecrement :
PerformScrollPageUp ( ) ;
break ;
case ScrollEventType . ThumbPosition :
long lPos = FromScrollPos ( e . NewValue ) ;
PerformScrollThumpPosition ( lPos ) ;
break ;
case ScrollEventType . ThumbTrack :
// to avoid performance problems use a refresh delay implemented with a timer
if ( _thumbTrackTimer . Enabled ) // stop old timer
_thumbTrackTimer . Enabled = false ;
// perform scroll immediately only if last refresh is very old
int currentThumbTrack = System . Environment . TickCount ;
if ( currentThumbTrack - _lastThumbtrack > THUMPTRACKDELAY )
{
PerformScrollThumbTrack ( null , null ) ;
_lastThumbtrack = currentThumbTrack ;
break ;
}
// start thumbtrack timer
_thumbTrackPosition = FromScrollPos ( e . NewValue ) ;
_thumbTrackTimer . Enabled = true ;
break ;
case ScrollEventType . First :
break ;
default :
break ;
}
e . NewValue = ToScrollPos ( _scrollVpos ) ;
}
/// <summary>
/// Performs the thumbtrack scrolling after an delay.
/// </summary>
void PerformScrollThumbTrack ( object sender , EventArgs e )
{
_thumbTrackTimer . Enabled = false ;
PerformScrollThumpPosition ( _thumbTrackPosition ) ;
_lastThumbtrack = Environment . TickCount ;
}
void UpdateScrollSize ( )
{
System . Diagnostics . Debug . WriteLine ( "UpdateScrollSize()" , "HexBox" ) ;
// calc scroll bar info
if ( VScrollBarVisible & & _byteProvider ! = null & & _byteProvider . Length > 0 & & _iHexMaxHBytes ! = 0 )
{
long scrollmax = ( long ) Math . Ceiling ( ( double ) ( _byteProvider . Length + 1 ) / ( double ) _iHexMaxHBytes - ( double ) _iHexMaxVBytes ) ;
scrollmax = Math . Max ( 0 , scrollmax ) ;
long scrollpos = _startByte / _iHexMaxHBytes ;
if ( scrollmax < _scrollVmax )
{
/* Data size has been decreased. */
if ( _scrollVpos = = _scrollVmax )
/* Scroll one line up if we at bottom. */
PerformScrollLineUp ( ) ;
}
if ( scrollmax = = _scrollVmax & & scrollpos = = _scrollVpos )
return ;
_scrollVmin = 0 ;
_scrollVmax = scrollmax ;
_scrollVpos = Math . Min ( scrollpos , scrollmax ) ;
UpdateVScroll ( ) ;
}
else if ( VScrollBarVisible )
{
// disable scroll bar
_scrollVmin = 0 ;
_scrollVmax = 0 ;
_scrollVpos = 0 ;
UpdateVScroll ( ) ;
}
}
void UpdateVScroll ( )
{
System . Diagnostics . Debug . WriteLine ( "UpdateVScroll()" , "HexBox" ) ;
int max = ToScrollMax ( _scrollVmax ) ;
if ( max > 0 )
{
_vScrollBar . Minimum = 0 ;
_vScrollBar . Maximum = max ;
_vScrollBar . Value = ToScrollPos ( _scrollVpos ) ;
_vScrollBar . Visible = true ;
}
else
{
_vScrollBar . Visible = false ;
}
}
int ToScrollPos ( long value )
{
int max = 65535 ;
if ( _scrollVmax < max )
return ( int ) value ;
else
{
double valperc = ( double ) value / ( double ) _scrollVmax * ( double ) 100 ;
int res = ( int ) Math . Floor ( ( double ) max / ( double ) 100 * valperc ) ;
res = ( int ) Math . Max ( _scrollVmin , res ) ;
res = ( int ) Math . Min ( _scrollVmax , res ) ;
return res ;
}
}
long FromScrollPos ( int value )
{
int max = 65535 ;
if ( _scrollVmax < max )
{
return ( long ) value ;
}
else
{
double valperc = ( double ) value / ( double ) max * ( double ) 100 ;
long res = ( int ) Math . Floor ( ( double ) _scrollVmax / ( double ) 100 * valperc ) ;
return res ;
}
}
int ToScrollMax ( long value )
{
long max = 65535 ;
if ( value > max )
return ( int ) max ;
else
return ( int ) value ;
}
void PerformScrollToLine ( long pos )
{
if ( pos < _scrollVmin | | pos > _scrollVmax | | pos = = _scrollVpos )
return ;
_scrollVpos = pos ;
UpdateVScroll ( ) ;
UpdateVisibilityBytes ( ) ;
UpdateCaret ( ) ;
Invalidate ( ) ;
}
void PerformScrollLines ( int lines )
{
long pos ;
if ( lines > 0 )
{
pos = Math . Min ( _scrollVmax , _scrollVpos + lines ) ;
}
else if ( lines < 0 )
{
pos = Math . Max ( _scrollVmin , _scrollVpos + lines ) ;
}
else
{
return ;
}
PerformScrollToLine ( pos ) ;
}
void PerformScrollLineDown ( )
{
this . PerformScrollLines ( 1 ) ;
}
void PerformScrollLineUp ( )
{
this . PerformScrollLines ( - 1 ) ;
}
void PerformScrollPageDown ( )
{
this . PerformScrollLines ( _iHexMaxVBytes ) ;
}
void PerformScrollPageUp ( )
{
this . PerformScrollLines ( - _iHexMaxVBytes ) ;
}
void PerformScrollThumpPosition ( long pos )
{
// Bug fix: Scroll to end, do not scroll to end
int difference = ( _scrollVmax > 65535 ) ? 10 : 9 ;
if ( ToScrollPos ( pos ) = = ToScrollMax ( _scrollVmax ) - difference )
pos = _scrollVmax ;
// End Bug fix
PerformScrollToLine ( pos ) ;
}
/// <summary>
/// Scrolls the selection start byte into view
/// </summary>
public void ScrollByteIntoView ( )
{
System . Diagnostics . Debug . WriteLine ( "ScrollByteIntoView()" , "HexBox" ) ;
ScrollByteIntoView ( _bytePos ) ;
}
/// <summary>
/// Scrolls the specific byte into view
/// </summary>
/// <param name="index">the index of the byte</param>
public void ScrollByteIntoView ( long index )
{
System . Diagnostics . Debug . WriteLine ( "ScrollByteIntoView(long index)" , "HexBox" ) ;
if ( _byteProvider = = null | | _keyInterpreter = = null )
return ;
if ( index < _startByte )
{
long line = ( long ) Math . Floor ( ( double ) index / ( double ) _iHexMaxHBytes ) ;
PerformScrollThumpPosition ( line ) ;
}
else if ( index > _endByte )
{
long line = ( long ) Math . Floor ( ( double ) index / ( double ) _iHexMaxHBytes ) ;
line - = _iHexMaxVBytes - 1 ;
PerformScrollThumpPosition ( line ) ;
}
}
#endregion
#region Selection methods
void ReleaseSelection ( )
{
System . Diagnostics . Debug . WriteLine ( "ReleaseSelection()" , "HexBox" ) ;
if ( _selectionLength = = 0 )
return ;
_selectionLength = 0 ;
OnSelectionLengthChanged ( EventArgs . Empty ) ;
if ( ! _caretVisible )
CreateCaret ( ) ;
else
UpdateCaret ( ) ;
Invalidate ( ) ;
}
/// <summary>
/// Returns true if Select method could be invoked.
/// </summary>
public bool CanSelectAll ( )
{
if ( ! this . Enabled )
return false ;
if ( _byteProvider = = null )
return false ;
return true ;
}
/// <summary>
/// Selects all bytes.
/// </summary>
public void SelectAll ( )
{
if ( this . ByteProvider = = null )
return ;
this . Select ( 0 , this . ByteProvider . Length ) ;
}
/// <summary>
/// Selects the hex box.
/// </summary>
/// <param name="start">the start index of the selection</param>
/// <param name="length">the length of the selection</param>
public void Select ( long start , long length )
{
if ( this . ByteProvider = = null )
return ;
if ( ! this . Enabled )
return ;
InternalSelect ( start , length ) ;
ScrollByteIntoView ( ) ;
2017-12-26 15:59:58 -05:00
ActivateKeyInterpreter ( ) ;
2017-02-26 13:46:57 -05:00
}
void InternalSelect ( long start , long length )
{
long pos = start ;
long sel = length ;
int cp = 0 ;
if ( sel > 0 & & _caretVisible )
DestroyCaret ( ) ;
else if ( sel = = 0 & & ! _caretVisible )
CreateCaret ( ) ;
SetPosition ( pos , cp ) ;
SetSelectionLength ( sel ) ;
UpdateCaret ( ) ;
Invalidate ( ) ;
}
#endregion
#region Key interpreter methods
void ActivateEmptyKeyInterpreter ( )
{
if ( _eki = = null )
_eki = new EmptyKeyInterpreter ( this ) ;
if ( _eki = = _keyInterpreter )
return ;
if ( _keyInterpreter ! = null )
_keyInterpreter . Deactivate ( ) ;
_keyInterpreter = _eki ;
_keyInterpreter . Activate ( ) ;
}
void ActivateKeyInterpreter ( )
{
if ( _ki = = null )
_ki = new KeyInterpreter ( this ) ;
if ( _ki = = _keyInterpreter )
return ;
if ( _keyInterpreter ! = null )
_keyInterpreter . Deactivate ( ) ;
_keyInterpreter = _ki ;
_keyInterpreter . Activate ( ) ;
}
void ActivateStringKeyInterpreter ( )
{
if ( _ski = = null )
_ski = new StringKeyInterpreter ( this ) ;
if ( _ski = = _keyInterpreter )
return ;
if ( _keyInterpreter ! = null )
_keyInterpreter . Deactivate ( ) ;
_keyInterpreter = _ski ;
_keyInterpreter . Activate ( ) ;
}
#endregion
#region Caret methods
void CreateCaret ( )
{
if ( _byteProvider = = null | | _keyInterpreter = = null | | _caretVisible | | ! this . Focused )
return ;
System . Diagnostics . Debug . WriteLine ( "CreateCaret()" , "HexBox" ) ;
UpdateCaret ( ) ;
_caretVisible = true ;
}
void UpdateCaret ( )
{
if ( _byteProvider = = null | | _keyInterpreter = = null )
return ;
System . Diagnostics . Debug . WriteLine ( "UpdateCaret()" , "HexBox" ) ;
long byteIndex = _bytePos - _startByte ;
2017-02-26 18:58:48 -05:00
2017-02-26 13:46:57 -05:00
PointF p = _keyInterpreter . GetCaretPointF ( byteIndex ) ;
p . X + = _byteCharacterPos * _charSize . Width ;
2018-02-23 11:59:14 -05:00
if ( HighDensityMode ) {
p . Y + = _highDensityModeOffset ;
}
2017-02-26 13:46:57 -05:00
_caretPos = p ;
}
void DestroyCaret ( )
{
2017-03-02 20:33:25 -05:00
//Never hide caret
return ;
}
2017-02-26 13:46:57 -05:00
2017-03-02 20:33:25 -05:00
BytePositionInfo ? GetBytePositionInfo ( Point p )
{
if ( _recHex . Contains ( p ) ) {
return GetHexBytePositionInfo ( p ) ;
} else if ( _recStringView . Contains ( p ) | | p . X > _recStringView . Right ) {
return GetStringBytePositionInfo ( p ) ;
}
return null ;
2017-02-26 13:46:57 -05:00
}
void SetCaretPosition ( Point p )
{
System . Diagnostics . Debug . WriteLine ( "SetCaretPosition()" , "HexBox" ) ;
if ( _byteProvider = = null | | _keyInterpreter = = null )
return ;
long pos = _bytePos ;
int cp = _byteCharacterPos ;
2017-03-02 20:33:25 -05:00
CreateCaret ( ) ;
2017-02-26 13:46:57 -05:00
if ( _recHex . Contains ( p ) )
{
BytePositionInfo bpi = GetHexBytePositionInfo ( p ) ;
pos = bpi . Index ;
2018-03-10 13:41:01 -05:00
cp = ByteEditingMode ? 0 : bpi . CharacterPosition ;
2017-02-26 13:46:57 -05:00
SetPosition ( pos , cp ) ;
ActivateKeyInterpreter ( ) ;
UpdateCaret ( ) ;
Invalidate ( ) ;
}
2017-02-26 18:58:48 -05:00
else if ( _recStringView . Contains ( p ) | | p . X > _recStringView . Right )
2017-02-26 13:46:57 -05:00
{
BytePositionInfo bpi = GetStringBytePositionInfo ( p ) ;
pos = bpi . Index ;
cp = bpi . CharacterPosition ;
SetPosition ( pos , cp ) ;
ActivateStringKeyInterpreter ( ) ;
UpdateCaret ( ) ;
Invalidate ( ) ;
}
}
2017-12-26 18:29:47 -05:00
internal BytePositionInfo GetHexBytePositionInfo ( Point p )
2017-02-26 13:46:57 -05:00
{
System . Diagnostics . Debug . WriteLine ( "GetHexBytePositionInfo()" , "HexBox" ) ;
long bytePos ;
int byteCharaterPos ;
float x = ( ( float ) ( p . X - _recHex . X ) / _charSize . Width ) ;
float y = ( ( float ) ( p . Y - _recHex . Y ) / _charSize . Height ) ;
int iX = ( int ) x ;
int iY = ( int ) y ;
2018-03-10 13:41:01 -05:00
//Offset by half a character to make the selection more intuitive (e.g half the white space belongs to the left byte, the other to the right)
float pos = ( x + 0.5f ) / 3 + 1 ;
int hPos = ( int ) pos ;
2017-02-26 13:46:57 -05:00
bytePos = Math . Min ( _byteProvider . Length ,
_startByte + ( _iHexMaxHBytes * ( iY + 1 ) - _iHexMaxHBytes ) + hPos - 1 ) ;
2018-03-10 13:41:01 -05:00
if ( pos - Math . Floor ( pos ) > 0.5f ) {
2017-02-26 13:46:57 -05:00
byteCharaterPos = 1 ;
2018-03-10 13:41:01 -05:00
} else {
byteCharaterPos = 0 ;
}
2017-02-26 13:46:57 -05:00
if ( bytePos = = _byteProvider . Length )
byteCharaterPos = 0 ;
if ( bytePos < 0 )
return new BytePositionInfo ( 0 , 0 ) ;
return new BytePositionInfo ( bytePos , byteCharaterPos ) ;
}
BytePositionInfo GetStringBytePositionInfo ( Point p )
{
System . Diagnostics . Debug . WriteLine ( "GetStringBytePositionInfo()" , "HexBox" ) ;
long bytePos ;
int byteCharacterPos ;
float y = ( ( float ) ( p . Y - _recStringView . Y ) / _charSize . Height ) ;
int iY = ( int ) y ;
2017-02-26 18:58:48 -05:00
int hPos = 0 ;
2017-02-26 13:46:57 -05:00
2017-02-26 18:58:48 -05:00
if ( _xPosList . ContainsKey ( iY ) ) {
List < float > posList = _xPosList [ iY ] ;
for ( int i = 0 ; i < posList . Count - 1 ; i + + ) {
if ( posList [ i ] < = p . X & & posList [ i + 1 ] > = p . X ) {
hPos = i + 1 ;
break ;
}
}
if ( hPos = = 0 ) {
hPos = posList . Count ;
}
}
2017-02-26 13:46:57 -05:00
2017-02-26 18:58:48 -05:00
bytePos = Math . Min ( _byteProvider . Length ,
2017-02-26 13:46:57 -05:00
_startByte + ( _iHexMaxHBytes * ( iY + 1 ) - _iHexMaxHBytes ) + hPos - 1 ) ;
byteCharacterPos = 0 ;
if ( bytePos < 0 )
return new BytePositionInfo ( 0 , 0 ) ;
return new BytePositionInfo ( bytePos , byteCharacterPos ) ;
}
#endregion
#region PreProcessMessage methods
/// <summary>
/// Preprocesses windows messages.
/// </summary>
/// <param name="m">the message to process.</param>
/// <returns>true, if the message was processed</returns>
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true), SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
public override bool PreProcessMessage ( ref Message m )
{
switch ( m . Msg )
{
case NativeMethods . WM_KEYDOWN :
return _keyInterpreter . PreProcessWmKeyDown ( ref m ) ;
case NativeMethods . WM_CHAR :
return _keyInterpreter . PreProcessWmChar ( ref m ) ;
case NativeMethods . WM_KEYUP :
return _keyInterpreter . PreProcessWmKeyUp ( ref m ) ;
default :
return base . PreProcessMessage ( ref m ) ;
}
}
bool BasePreProcessMessage ( ref Message m )
{
return base . PreProcessMessage ( ref m ) ;
}
#endregion
public enum eSearchDirection
{
Incremental ,
Next ,
Previous
}
#region Find methods
/// <summary>
/// Searches the current ByteProvider
/// </summary>
/// <param name="options">contains all find options</param>
/// <returns>the SelectionStart property value if find was successfull or
/// -1 if there is no match
/// -2 if Find was aborted.</returns>
public long Find ( FindOptions options , eSearchDirection direction )
{
long startIndex = 0 ;
switch ( direction ) {
case eSearchDirection . Previous : startIndex = this . SelectionStart - 1 ; break ;
default :
case eSearchDirection . Incremental : startIndex = this . SelectionStart ; break ;
case eSearchDirection . Next : startIndex = this . SelectionStart + 1 ; break ;
}
2017-02-26 19:52:48 -05:00
options . UpdateFindBuffer ( this . ByteCharConverter ) ;
2017-02-26 13:46:57 -05:00
byte [ ] buffer1 = null ;
byte [ ] buffer2 = null ;
if ( options . Type = = FindType . Text & & options . MatchCase ) {
if ( options . FindBuffer = = null | | options . FindBuffer . Length = = 0 )
throw new ArgumentException ( "FindBuffer can not be null when Type: Text and MatchCase: false" ) ;
buffer1 = options . FindBuffer ;
} else if ( options . Type = = FindType . Text & & ! options . MatchCase ) {
if ( options . FindBufferLowerCase = = null | | options . FindBufferLowerCase . Length = = 0 )
throw new ArgumentException ( "FindBufferLowerCase can not be null when Type is Text and MatchCase is true" ) ;
if ( options . FindBufferUpperCase = = null | | options . FindBufferUpperCase . Length = = 0 )
throw new ArgumentException ( "FindBufferUpperCase can not be null when Type is Text and MatchCase is true" ) ;
if ( options . FindBufferLowerCase . Length ! = options . FindBufferUpperCase . Length )
throw new ArgumentException ( "FindBufferUpperCase and FindBufferUpperCase must have the same size when Type is Text and MatchCase is true" ) ;
buffer1 = options . FindBufferLowerCase ;
buffer2 = options . FindBufferUpperCase ;
} else if ( options . Type = = FindType . Hex ) {
if ( options . Hex = = null | | options . Hex . Length = = 0 )
throw new ArgumentException ( "Hex can not be null when Type is Hex" ) ;
buffer1 = options . Hex ;
}
_abortFind = false ;
long result = - 1 ;
if ( direction = = eSearchDirection . Previous ) {
result = FindLoop ( buffer1 , buffer2 , startIndex , 0 , options . HasWildcard ) ;
if ( result = = - 1 & & options . WrapSearch ) {
result = FindLoop ( buffer1 , buffer2 , _byteProvider . Length - 1 , Math . Max ( 0 , startIndex - buffer1 . Length ) , options . HasWildcard ) ;
}
} else {
result = FindLoop ( buffer1 , buffer2 , startIndex , _byteProvider . Length , options . HasWildcard ) ;
if ( result = = - 1 & & options . WrapSearch ) {
result = FindLoop ( buffer1 , buffer2 , 0 , Math . Min ( _byteProvider . Length , startIndex + buffer1 . Length ) , options . HasWildcard ) ;
}
}
return result ;
}
private long FindLoop ( byte [ ] buffer1 , byte [ ] buffer2 , long start , long end , bool hasWildcard )
{
int matchStartIndex = start > end ? buffer1 . Length - 1 : 0 ;
int match = matchStartIndex ;
int buffer1Length = buffer1 . Length ;
for ( long pos = start ; pos ! = end ; pos = start > end ? pos - 1 : pos + 1 ) {
if ( _abortFind )
return - 2 ;
//if (pos % 1000 == 0) // for performance reasons: DoEvents only 1 times per 1000 loops
//Application.DoEvents();
byte compareByte = _byteProvider . ReadByte ( pos ) ;
bool wildcardMatch = false ;
if ( match = = buffer1Length - 1 & & hasWildcard ) {
if ( ( buffer1 [ match ] > > 4 ) = = ( compareByte > > 4 ) ) {
wildcardMatch = true ;
}
}
bool buffer1Match = compareByte = = buffer1 [ match ] ;
bool hasBuffer2 = buffer2 ! = null ;
bool buffer2Match = hasBuffer2 ? compareByte = = buffer2 [ match ] : false ;
bool isMatch = buffer1Match | | buffer2Match | | wildcardMatch ;
if ( ! isMatch ) {
pos = pos + ( matchStartIndex - match ) ;
match = matchStartIndex ;
_findingPos = pos ;
continue ;
}
match = ( start > end ? match - 1 : match + 1 ) ;
if ( match = = buffer1Length | | match = = - 1 ) {
long bytePos = start > end ? pos : ( pos - buffer1Length + 1 ) ;
Select ( bytePos , buffer1Length ) ;
ScrollByteIntoView ( _bytePos + _selectionLength ) ;
ScrollByteIntoView ( _bytePos ) ;
return bytePos ;
}
}
return - 1 ;
}
/// <summary>
/// Aborts a working Find method.
/// </summary>
public void AbortFind ( )
{
_abortFind = true ;
}
/// <summary>
/// Gets a value that indicates the current position during Find method execution.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public long CurrentFindingPosition
{
get
{
return _findingPos ;
}
}
#endregion
#region Copy , Cut and Paste methods
byte [ ] GetCopyData ( )
{
if ( ! CanCopy ( ) ) return new byte [ 0 ] ;
// put bytes into buffer
byte [ ] buffer = new byte [ _selectionLength ] ;
int id = - 1 ;
for ( long i = _bytePos ; i < _bytePos + _selectionLength ; i + + )
{
id + + ;
buffer [ id ] = _byteProvider . ReadByte ( i ) ;
}
return buffer ;
}
/// <summary>
/// Copies the current selection in the hex box to the Clipboard.
/// </summary>
public void Copy ( )
{
if ( ! CanCopy ( ) ) return ;
// put bytes into buffer
byte [ ] buffer = GetCopyData ( ) ;
DataObject da = new DataObject ( ) ;
// set string buffer clipbard data
string hex = BitConverter . ToString ( buffer ) . Replace ( "-" , "" ) ;
StringBuilder sb = new StringBuilder ( hex . Length * 2 ) ;
for ( int i = 0 ; i < hex . Length ; i + = BytesPerLine * 2 ) {
sb . AppendLine ( hex . Substring ( i , i + BytesPerLine * 2 > hex . Length ? hex . Length - i : BytesPerLine * 2 ) ) ;
}
da . SetData ( typeof ( string ) , sb . ToString ( ) ) ;
//string sBuffer = System.Text.Encoding.ASCII.GetString(buffer, 0, buffer.Length);
//da.SetData(typeof(string), sBuffer);
//set memorystream (BinaryData) clipboard data
System . IO . MemoryStream ms = new System . IO . MemoryStream ( buffer , 0 , buffer . Length , false , true ) ;
da . SetData ( "BinaryData" , ms ) ;
Clipboard . SetDataObject ( da , true ) ;
UpdateCaret ( ) ;
ScrollByteIntoView ( ) ;
Invalidate ( ) ;
OnCopied ( EventArgs . Empty ) ;
}
/// <summary>
/// Return true if Copy method could be invoked.
/// </summary>
public bool CanCopy ( )
{
if ( _selectionLength < 1 | | _byteProvider = = null )
return false ;
return true ;
}
/// <summary>
/// Moves the current selection in the hex box to the Clipboard.
/// </summary>
public void Cut ( )
{
if ( ! CanCut ( ) ) return ;
Copy ( ) ;
_byteProvider . DeleteBytes ( _bytePos , _selectionLength ) ;
_byteCharacterPos = 0 ;
UpdateCaret ( ) ;
ScrollByteIntoView ( ) ;
ReleaseSelection ( ) ;
Invalidate ( ) ;
Refresh ( ) ;
}
/// <summary>
/// Return true if Cut method could be invoked.
/// </summary>
public bool CanCut ( )
{
if ( ReadOnly | | ! this . Enabled )
return false ;
if ( _byteProvider = = null )
return false ;
if ( _selectionLength < 1 | | ! _byteProvider . SupportsDeleteBytes ( ) )
return false ;
return true ;
}
/// <summary>
/// Replaces the current selection in the hex box with the contents of the Clipboard.
/// </summary>
public void Paste ( )
{
if ( ! CanPaste ( ) ) return ;
byte [ ] buffer = null ;
IDataObject da = Clipboard . GetDataObject ( ) ;
2018-02-24 14:23:54 -05:00
if ( da . GetDataPresent ( "BinaryData" ) ) {
2017-02-26 13:46:57 -05:00
System . IO . MemoryStream ms = ( System . IO . MemoryStream ) da . GetData ( "BinaryData" ) ;
buffer = new byte [ ms . Length ] ;
ms . Read ( buffer , 0 , buffer . Length ) ;
2018-02-24 14:23:54 -05:00
} else if ( da . GetDataPresent ( typeof ( string ) ) ) {
2017-02-26 13:46:57 -05:00
string sBuffer = ( string ) da . GetData ( typeof ( string ) ) ;
buffer = System . Text . Encoding . ASCII . GetBytes ( sBuffer ) ;
2018-02-24 14:23:54 -05:00
} else {
2017-02-26 13:46:57 -05:00
return ;
}
2018-02-24 14:23:54 -05:00
_byteProvider . WriteBytes ( _bytePos , buffer ) ;
2017-02-26 13:46:57 -05:00
SetPosition ( _bytePos + buffer . Length , 0 ) ;
ReleaseSelection ( ) ;
ScrollByteIntoView ( ) ;
UpdateCaret ( ) ;
Invalidate ( ) ;
}
/// <summary>
/// Return true if Paste method could be invoked.
/// </summary>
public bool CanPaste ( )
{
if ( ReadOnly | | ! this . Enabled ) return false ;
IDataObject da = Clipboard . GetDataObject ( ) ;
if ( da . GetDataPresent ( "BinaryData" ) )
return true ;
else if ( da . GetDataPresent ( typeof ( string ) ) )
return true ;
else
return false ;
}
/// <summary>
/// Return true if PasteHex method could be invoked.
/// </summary>
public bool CanPasteHex ( )
{
if ( ! CanPaste ( ) ) return false ;
byte [ ] buffer = null ;
IDataObject da = Clipboard . GetDataObject ( ) ;
if ( da . GetDataPresent ( typeof ( string ) ) )
{
string hexString = ( string ) da . GetData ( typeof ( string ) ) ;
buffer = ConvertHexToBytes ( hexString ) ;
return ( buffer ! = null ) ;
}
return false ;
}
/// <summary>
/// Replaces the current selection in the hex box with the hex string data of the Clipboard.
/// </summary>
public void PasteHex ( )
{
if ( ! CanPaste ( ) ) return ;
byte [ ] buffer = null ;
IDataObject da = Clipboard . GetDataObject ( ) ;
if ( da . GetDataPresent ( typeof ( string ) ) )
{
string hexString = ( string ) da . GetData ( typeof ( string ) ) ;
buffer = ConvertHexToBytes ( hexString ) ;
if ( buffer = = null )
return ;
}
else
{
return ;
}
if ( _selectionLength > 0 )
_byteProvider . DeleteBytes ( _bytePos , _selectionLength ) ;
_byteProvider . InsertBytes ( _bytePos , buffer ) ;
SetPosition ( _bytePos + buffer . Length , 0 ) ;
ReleaseSelection ( ) ;
ScrollByteIntoView ( ) ;
UpdateCaret ( ) ;
Invalidate ( ) ;
}
/// <summary>
/// Copies the current selection in the hex box to the Clipboard in hex format.
/// </summary>
public void CopyHex ( )
{
if ( ! CanCopy ( ) ) return ;
// put bytes into buffer
byte [ ] buffer = GetCopyData ( ) ;
DataObject da = new DataObject ( ) ;
// set string buffer clipbard data
string hexString = ConvertBytesToHex ( buffer ) ; ;
da . SetData ( typeof ( string ) , hexString ) ;
//set memorystream (BinaryData) clipboard data
System . IO . MemoryStream ms = new System . IO . MemoryStream ( buffer , 0 , buffer . Length , false , true ) ;
da . SetData ( "BinaryData" , ms ) ;
Clipboard . SetDataObject ( da , true ) ;
UpdateCaret ( ) ;
ScrollByteIntoView ( ) ;
Invalidate ( ) ;
OnCopiedHex ( EventArgs . Empty ) ;
}
#endregion
#region Paint methods
/// <summary>
/// Paints the background.
/// </summary>
/// <param name="e">A PaintEventArgs that contains the event data.</param>
protected override void OnPaintBackground ( PaintEventArgs e )
{
switch ( _borderStyle )
{
case BorderStyle . Fixed3D :
{
if ( TextBoxRenderer . IsSupported )
{
VisualStyleElement state = VisualStyleElement . TextBox . TextEdit . Normal ;
Color backColor = this . BackColor ;
if ( this . Enabled )
{
if ( this . ReadOnly )
state = VisualStyleElement . TextBox . TextEdit . ReadOnly ;
else if ( this . Focused )
state = VisualStyleElement . TextBox . TextEdit . Focused ;
}
else
{
state = VisualStyleElement . TextBox . TextEdit . Disabled ;
backColor = this . BackColorDisabled ;
}
VisualStyleRenderer vsr = new VisualStyleRenderer ( state ) ;
vsr . DrawBackground ( e . Graphics , this . ClientRectangle ) ;
Rectangle rectContent = vsr . GetBackgroundContentRectangle ( e . Graphics , this . ClientRectangle ) ;
e . Graphics . FillRectangle ( new SolidBrush ( backColor ) , rectContent ) ;
}
else
{
// draw background
e . Graphics . FillRectangle ( new SolidBrush ( BackColor ) , ClientRectangle ) ;
// draw default border
ControlPaint . DrawBorder3D ( e . Graphics , ClientRectangle , Border3DStyle . Sunken ) ;
}
break ;
}
case BorderStyle . FixedSingle :
{
// draw background
e . Graphics . FillRectangle ( new SolidBrush ( BackColor ) , ClientRectangle ) ;
// draw fixed single border
ControlPaint . DrawBorder ( e . Graphics , ClientRectangle , Color . Black , ButtonBorderStyle . Solid ) ;
break ;
}
default :
{
// draw background
e . Graphics . FillRectangle ( new SolidBrush ( BackColor ) , ClientRectangle ) ;
break ;
}
}
}
/// <summary>
/// Paints the hex box.
/// </summary>
/// <param name="e">A PaintEventArgs that contains the event data.</param>
protected override void OnPaint ( PaintEventArgs e )
{
base . OnPaint ( e ) ;
if ( _byteProvider = = null )
return ;
System . Diagnostics . Debug . WriteLine ( "OnPaint " + DateTime . Now . ToString ( ) , "HexBox" ) ;
// draw only in the content rectangle, so exclude the border and the scrollbar.
Region r = new Region ( ClientRectangle ) ;
r . Exclude ( _recContent ) ;
e . Graphics . ExcludeClip ( r ) ;
UpdateVisibilityBytes ( ) ;
2017-02-26 18:58:48 -05:00
if ( _caretVisible & & this . Focused & & _keyInterpreter . GetType ( ) ! = typeof ( StringKeyInterpreter ) ) {
2017-02-26 13:46:57 -05:00
int caretWidth = ( this . InsertActive ) ? 1 : ( int ) _charSize . Width ;
int caretHeight = ( int ) _charSize . Height ;
e . Graphics . FillRectangle ( Brushes . Yellow , _caretPos . X - 1 , _caretPos . Y , caretWidth , caretHeight ) ;
}
if ( _lineInfoVisible )
PaintLineInfo ( e . Graphics , _startByte , _endByte ) ;
2018-01-14 14:03:43 -05:00
PaintHexAndStringView ( e . Graphics , _startByte , _endByte ) ;
2018-02-24 14:41:11 -05:00
if ( _shadowSelectionVisible & & _stringViewVisible )
2018-01-14 14:03:43 -05:00
PaintCurrentBytesSign ( e . Graphics ) ;
2017-02-26 13:46:57 -05:00
if ( _columnInfoVisible )
PaintHeaderRow ( e . Graphics ) ;
if ( _groupSeparatorVisible )
PaintColumnSeparator ( e . Graphics ) ;
2017-12-28 21:53:50 -05:00
if ( _caretVisible & & this . Focused & & _keyInterpreter . GetType ( ) ! = typeof ( StringKeyInterpreter ) ) {
2018-01-14 14:03:43 -05:00
float caretWidth = ( this . InsertActive ) ? 1 : _charSize . Width ;
float caretHeight = _charSize . Height ;
2017-12-28 21:53:50 -05:00
e . Graphics . DrawRectangle ( Pens . Gray , _caretPos . X - 1 , _caretPos . Y , caretWidth , caretHeight ) ;
}
2017-02-26 13:46:57 -05:00
}
void PaintLineInfo ( Graphics g , long startByte , long endByte )
{
// Ensure endByte isn't > length of array.
endByte = Math . Min ( _byteProvider . Length - 1 , endByte ) ;
Color lineInfoColor = ( this . InfoForeColor ! = Color . Empty ) ? this . InfoForeColor : this . ForeColor ;
using ( Brush backBrush = new SolidBrush ( this . InfoBackColor ) ) {
using ( Brush brush = new SolidBrush ( lineInfoColor ) ) {
int maxLine = GetGridBytePoint ( endByte - startByte ) . Y + 1 ;
g . FillRectangle ( backBrush , _recLineInfo . X - 4 , _recLineInfo . Y , _recLineInfo . Width , _recLineInfo . Height ) ;
for ( int i = 0 ; i < maxLine ; i + + ) {
long firstLineByte = ( startByte + ( _iHexMaxHBytes ) * i ) + _lineInfoOffset ;
PointF bytePointF = GetBytePointF ( new Point ( 0 , 0 + i ) ) ;
string info = firstLineByte . ToString ( _hexStringFormat , System . Threading . Thread . CurrentThread . CurrentCulture ) ;
int nulls = 8 - info . Length ;
string formattedInfo ;
if ( nulls > - 1 ) {
formattedInfo = new string ( '0' , LineInfoCharCount - info . Length ) + info ;
} else {
formattedInfo = new string ( '~' , LineInfoCharCount ) ;
}
g . DrawString ( formattedInfo , Font , brush , new PointF ( _recLineInfo . X , bytePointF . Y ) , _stringFormat ) ;
}
}
}
}
void PaintHeaderRow ( Graphics g )
{
using ( Brush brush = new SolidBrush ( this . InfoForeColor ) ) {
using ( Brush backBrush = new SolidBrush ( this . InfoBackColor ) ) {
g . FillRectangle ( backBrush , 0 , 0 , this . ClientRectangle . Width , _recLineInfo . Y ) ;
for ( int col = 0 ; col < _iHexMaxHBytes ; col + + ) {
PaintColumnInfo ( g , ( byte ) col , brush , col ) ;
}
}
}
}
void PaintColumnSeparator ( Graphics g )
{
for ( int col = GroupSize ; col < _iHexMaxHBytes ; col + = GroupSize )
{
var pen = new Pen ( new SolidBrush ( this . InfoForeColor ) , 1 ) ;
PointF headerPointF = GetColumnInfoPointF ( col ) ;
headerPointF . X - = _charSize . Width / 2 ;
g . DrawLine ( pen , headerPointF , new PointF ( headerPointF . X , headerPointF . Y + _recColumnInfo . Height + _recHex . Height ) ) ;
if ( StringViewVisible )
{
PointF byteStringPointF = GetByteStringPointF ( new Point ( col , 0 ) ) ;
headerPointF . X - = 2 ;
g . DrawLine ( pen , new PointF ( byteStringPointF . X , byteStringPointF . Y ) , new PointF ( byteStringPointF . X , byteStringPointF . Y + _recHex . Height ) ) ;
}
}
}
2018-01-14 14:03:43 -05:00
void PaintHexString ( Graphics g , string hexString , Brush brush , Point gridPoint )
2017-02-26 13:46:57 -05:00
{
PointF bytePointF = GetBytePointF ( gridPoint ) ;
2018-01-14 14:03:43 -05:00
g . DrawString ( hexString , Font , brush , bytePointF , _stringFormat ) ;
2017-02-26 13:46:57 -05:00
}
void PaintColumnInfo ( Graphics g , byte b , Brush brush , int col )
{
PointF headerPointF = GetColumnInfoPointF ( col ) ;
string sB = ConvertByteToHex ( b ) ;
g . DrawString ( sB . Substring ( 0 , 1 ) , Font , brush , headerPointF , _stringFormat ) ;
headerPointF . X + = _charSize . Width ;
g . DrawString ( sB . Substring ( 1 , 1 ) , Font , brush , headerPointF , _stringFormat ) ;
}
2018-01-14 14:03:43 -05:00
void PaintHexStringSelected ( Graphics g , string hexString , Brush brush , Brush brushBack , Point gridPoint )
2017-02-26 13:46:57 -05:00
{
PointF bytePointF = GetBytePointF ( gridPoint ) ;
2018-01-14 14:03:43 -05:00
float width = hexString . Length * _charSize . Width ;
float xPos = bytePointF . X - _charSize . Width / 2 ;
2017-12-28 21:53:50 -05:00
2018-02-23 11:59:14 -05:00
if ( HighDensityMode ) {
g . FillRectangle ( brushBack , xPos , bytePointF . Y + _highDensityModeOffset , width , _charSize . Height ) ;
} else {
g . FillRectangle ( brushBack , xPos , bytePointF . Y , width , _charSize . Height ) ;
}
2018-01-14 14:03:43 -05:00
if ( _selectionLength = = 0 & & _caretPos . Y = = bytePointF . Y & & _caretPos . X > = bytePointF . X & & _caretPos . X < = bytePointF . X + width ) {
2017-12-28 21:53:50 -05:00
if ( _caretVisible & & this . Focused & & _keyInterpreter . GetType ( ) ! = typeof ( StringKeyInterpreter ) ) {
//Redraw caret over background color
2018-01-14 14:03:43 -05:00
float caretWidth = ( this . InsertActive ) ? 1 : _charSize . Width ;
float caretHeight = ( int ) _charSize . Height ;
2017-12-28 21:53:50 -05:00
g . FillRectangle ( Brushes . Yellow , _caretPos . X - 1 , _caretPos . Y , caretWidth , caretHeight ) ;
}
}
2018-01-14 14:03:43 -05:00
g . DrawString ( hexString , Font , brush , bytePointF , _stringFormat ) ;
2017-02-26 13:46:57 -05:00
}
2017-02-26 18:58:48 -05:00
Dictionary < int , float > _lineWidthCache = new Dictionary < int , float > ( ) ;
2017-02-26 13:46:57 -05:00
void PaintHexAndStringView ( Graphics g , long startByte , long endByte )
{
int counter = - 1 ;
long intern_endByte = Math . Min ( _byteProvider . Length - 1 , endByte + _iHexMaxHBytes ) ;
bool isKeyInterpreterActive = _keyInterpreter = = null | | _keyInterpreter . GetType ( ) = = typeof ( KeyInterpreter ) ;
bool isStringKeyInterpreterActive = _keyInterpreter ! = null & & _keyInterpreter . GetType ( ) = = typeof ( StringKeyInterpreter ) ;
2017-02-26 18:58:48 -05:00
int yPrevious = - 1 ;
float xOffset = 0 ;
2018-01-14 14:03:43 -05:00
Color defaultForeColor = this . ForeColor ;
_lineWidthCache . Clear ( ) ;
for ( int i = 0 ; i < _xPosCache . Length ; i + + ) {
_xPosCache [ i ] = - 1 ;
}
_xPosList . Clear ( ) ;
2017-02-26 18:58:48 -05:00
float xPrevious = 0 ;
Point ? caretPoint = null ;
if ( _caretVisible & & this . Focused & & _keyInterpreter . GetType ( ) = = typeof ( StringKeyInterpreter ) ) {
long byteIndex = _bytePos - _startByte ;
caretPoint = GetGridBytePoint ( byteIndex ) ;
}
int skipCount = 0 ;
2017-03-01 20:52:15 -05:00
if ( this . ByteColorProvider ! = null ) {
this . ByteColorProvider . Prepare ( _startByte , intern_endByte ) ;
}
2018-01-14 14:03:43 -05:00
bool forceDraw = false ;
float prevXOffset = 0 ;
2017-12-28 21:53:50 -05:00
bool prevSelected = false ;
2018-01-14 14:03:43 -05:00
Color prevByteColor = Color . Transparent ;
2017-12-28 21:53:50 -05:00
Color prevBgColor = Color . Transparent ;
2018-01-14 14:03:43 -05:00
Color prevDrawnColor = Color . Transparent ;
Point gridPoint = GetGridBytePoint ( 0 ) ;
List < byte > bytesToDisplay = new List < byte > ( ) ;
string stringToDisplay = string . Empty ;
float caretWidth = 0 ;
Action < Color , Color > outputHex = ( Color byteColor , Color bgColor ) = > {
StringBuilder sb = new StringBuilder ( ) ;
foreach ( byte b in bytesToDisplay ) {
sb . Append ( b . ToString ( "X2" ) ) ;
sb . Append ( " " ) ;
}
2017-12-28 21:53:50 -05:00
2018-01-14 14:03:43 -05:00
PointF bytePointF = GetBytePointF ( gridPoint ) ;
PointF byteStringPointF = GetByteStringPointF ( gridPoint , false ) ;
float xPos = byteStringPointF . X + prevXOffset ;
2017-02-26 18:58:48 -05:00
2018-01-14 14:03:43 -05:00
//String view caret
Action drawCaret = ( ) = > {
2018-02-23 11:59:14 -05:00
float yPos = bytePointF . Y + ( HighDensityMode ? _highDensityModeOffset : 0 ) ;
if ( _selectionLength = = 0 & & _caretPos . Y = = yPos & & _caretPos . X > = xPos ) {
2018-01-14 14:03:43 -05:00
if ( _caretVisible & & this . Focused & & _keyInterpreter . GetType ( ) = = typeof ( StringKeyInterpreter ) ) {
g . FillRectangle ( Brushes . Yellow , _caretPos . X , _caretPos . Y , this . InsertActive ? 1 : caretWidth , _charSize . Height ) ;
g . DrawRectangle ( Pens . Gray , _caretPos . X , _caretPos . Y , this . InsertActive ? 1 : caretWidth , _charSize . Height ) ;
}
2017-02-26 18:58:48 -05:00
}
2018-01-14 14:03:43 -05:00
} ;
using ( Brush fgBrush = new SolidBrush ( byteColor ) ) {
if ( bgColor ! = Color . Transparent ) {
using ( Brush bgBrush = new SolidBrush ( bgColor ) ) {
PaintHexStringSelected ( g , sb . ToString ( ) , fgBrush , bgBrush , gridPoint ) ;
//Draw string view
SizeF stringSize = g . MeasureString ( stringToDisplay , Font , 1000 , _stringFormat ) ;
2018-03-25 17:52:07 -04:00
float yPos = bytePointF . Y + ( HighDensityMode ? _highDensityModeOffset : 0 ) ;
g . FillRectangle ( bgBrush , xPos , yPos , stringSize . Width , _charSize . Height ) ;
2018-01-14 14:03:43 -05:00
drawCaret ( ) ;
g . DrawString ( stringToDisplay , Font , fgBrush , new PointF ( xPos , bytePointF . Y ) , _stringFormat ) ;
}
} else {
PaintHexString ( g , sb . ToString ( ) , fgBrush , gridPoint ) ;
2017-02-26 18:58:48 -05:00
2018-01-14 14:03:43 -05:00
//Draw string view
drawCaret ( ) ;
g . DrawString ( stringToDisplay , Font , fgBrush , new PointF ( xPos , bytePointF . Y ) , _stringFormat ) ;
}
}
2017-02-26 13:46:57 -05:00
2018-01-14 14:03:43 -05:00
prevDrawnColor = bgColor ;
bytesToDisplay = new List < byte > ( ) ;
stringToDisplay = string . Empty ;
} ;
2018-02-24 14:56:22 -05:00
for ( long i = startByte ; i < intern_endByte + 1 ; i + + ) {
Color byteColor = defaultForeColor ;
Color bgColor = Color . Transparent ;
bool isSelectedByte = i > = _bytePos & & i < = ( _bytePos + _selectionLength - 1 ) & & _selectionLength ! = 0 ;
if ( this . ByteColorProvider ! = null ) {
byteColor = this . ByteColorProvider . GetByteColor ( _startByte , i , out bgColor ) ;
}
2017-03-01 20:52:15 -05:00
2018-02-24 14:56:22 -05:00
counter + + ;
2017-03-01 20:52:15 -05:00
2018-02-24 14:56:22 -05:00
Point currentPoint = GetGridBytePoint ( counter ) ;
2017-03-01 20:52:15 -05:00
2018-02-24 14:56:22 -05:00
bool lineChanged = false ;
if ( yPrevious ! = currentPoint . Y ) {
if ( _xPosList . ContainsKey ( yPrevious ) ) {
_xPosList [ yPrevious ] . Add ( xPrevious ) ;
2018-01-14 14:03:43 -05:00
}
2018-02-24 14:56:22 -05:00
_xPosList [ currentPoint . Y ] = new List < float > ( ) ;
_lineWidthCache [ yPrevious ] = _recStringView . Width + xOffset ;
xOffset = 0 ;
yPrevious = currentPoint . Y ;
lineChanged = true ;
}
2017-03-01 20:52:15 -05:00
2018-02-24 14:56:22 -05:00
if ( forceDraw | | lineChanged | | byteColor ! = prevByteColor | | bgColor ! = prevBgColor | | prevSelected ! = isSelectedByte ) {
outputHex ( this . ByteColorProvider ! = null ? prevByteColor : ( prevSelected ? _selectionForeColor : defaultForeColor ) , prevSelected ? _selectionBackColor : prevBgColor ) ;
gridPoint = GetGridBytePoint ( counter ) ;
prevXOffset = xOffset ;
forceDraw = false ;
}
2017-03-01 20:52:15 -05:00
2018-02-24 14:56:22 -05:00
byte b = _byteProvider . ReadByte ( i ) ;
bytesToDisplay . Add ( b ) ;
2017-03-01 20:52:15 -05:00
2018-02-24 14:56:22 -05:00
prevSelected = isSelectedByte ;
prevBgColor = bgColor ;
prevByteColor = byteColor ;
2018-01-14 14:03:43 -05:00
2018-02-24 14:56:22 -05:00
if ( ! _stringViewVisible ) {
continue ;
}
2018-01-14 14:03:43 -05:00
2018-02-24 14:56:22 -05:00
string s ;
if ( skipCount > 0 ) {
skipCount - - ;
s = "" ;
} else {
long len = _byteProvider . Length ;
UInt64 tblValue = ( UInt64 ) b ;
for ( int j = 1 ; j < 8 ; j + + ) {
if ( len > i + j ) {
tblValue + = ( UInt64 ) _byteProvider . ReadByte ( i + j ) < < ( 8 * j ) ;
2017-02-26 13:46:57 -05:00
}
2018-01-14 14:03:43 -05:00
}
2018-02-24 14:56:22 -05:00
int keyLength ;
s = ByteCharConverter . ToChar ( tblValue , out keyLength ) ;
skipCount = keyLength - 1 ;
}
2018-01-14 14:03:43 -05:00
2018-02-24 14:56:22 -05:00
float width ;
if ( ! _measureCache . TryGetValue ( s , out width ) ) {
width = g . MeasureString ( s , Font , 1000 , _stringFormat ) . Width ;
_measureCache [ s ] = width ;
}
2018-01-14 14:03:43 -05:00
2018-02-24 14:56:22 -05:00
PointF byteStringPointF = GetByteStringPointF ( currentPoint , false ) ;
float xPos = byteStringPointF . X + xOffset ;
if ( currentPoint . Y < 150 ) {
_xPosCache [ currentPoint . Y * 64 + currentPoint . X ] = xPos ;
}
_xPosList [ currentPoint . Y ] . Add ( xPos ) ;
2018-01-14 14:03:43 -05:00
2018-02-24 14:56:22 -05:00
if ( currentPoint = = caretPoint ) {
caretWidth = width ;
}
2018-01-14 14:03:43 -05:00
2018-02-24 14:56:22 -05:00
stringToDisplay + = s ;
2018-01-14 14:03:43 -05:00
2018-02-24 14:56:22 -05:00
if ( s . Length > 1 | | s . Length > 0 & & s [ 0 ] > 127 | | s [ 0 ] < 32 ) {
//Force draw if we hit a non-ascii character (to avoid minor positioning issues)
forceDraw = true ;
2017-02-26 13:46:57 -05:00
}
2018-02-24 14:56:22 -05:00
xOffset + = width - _charSize . Width ;
xPrevious = xPos + width ;
2017-02-26 13:46:57 -05:00
}
2018-02-24 14:56:22 -05:00
outputHex ( this . ByteColorProvider ! = null ? prevByteColor : ( prevSelected ? _selectionForeColor : defaultForeColor ) , prevSelected ? _selectionBackColor : prevBgColor ) ;
2017-02-26 13:46:57 -05:00
}
2017-02-26 18:58:48 -05:00
float GetLineWidth ( int y )
{
float width ;
if ( _lineWidthCache . TryGetValue ( y , out width ) ) {
return width ;
} else {
return _recStringView . Width ;
}
}
2017-02-26 13:46:57 -05:00
void PaintCurrentBytesSign ( Graphics g )
{
2018-01-14 14:03:43 -05:00
if ( _selectionLength = = 0 & & _keyInterpreter ! = null & & _bytePos ! = - 1 & & Enabled ) {
2017-02-26 18:58:48 -05:00
if ( _keyInterpreter . GetType ( ) = = typeof ( KeyInterpreter ) ) {
2018-01-14 14:03:43 -05:00
Point gp = GetGridBytePoint ( _bytePos - _startByte ) ;
PointF pf = GetByteStringPointF ( gp ) ;
Point gp2 = GetGridBytePoint ( _bytePos - _startByte + 1 ) ;
PointF pf2 = GetByteStringPointF ( gp2 ) ;
Size s ;
if ( gp . X > gp2 . X ) {
s = new Size ( ( int ) ( GetLineWidth ( gp . Y ) - ( pf . X - _recStringView . X ) ) , ( int ) _charSize . Height ) ;
2017-02-26 18:58:48 -05:00
} else {
2018-01-14 14:03:43 -05:00
s = new Size ( ( int ) ( pf2 . X - pf . X ) , ( int ) _charSize . Height ) ;
2017-02-26 13:46:57 -05:00
}
2018-01-14 14:03:43 -05:00
Rectangle r = new Rectangle ( ( int ) pf . X , ( int ) pf . Y , s . Width , s . Height ) ;
PaintCurrentByteSign ( g , r ) ;
2017-02-26 18:58:48 -05:00
} else {
2018-01-14 14:03:43 -05:00
Point gp = GetGridBytePoint ( _bytePos - _startByte ) ;
PointF pf = GetBytePointF ( gp ) ;
Size s = new Size ( ( int ) ( _charSize . Width * 2 ) , ( int ) _charSize . Height ) ;
Rectangle r = new Rectangle ( ( int ) pf . X , ( int ) pf . Y , s . Width , s . Height ) ;
PaintCurrentByteSign ( g , r ) ;
2017-02-26 13:46:57 -05:00
}
}
}
2017-02-26 18:58:48 -05:00
Dictionary < Point , Bitmap > _shadowSelectionCache = new Dictionary < Point , Bitmap > ( ) ;
2017-02-26 13:46:57 -05:00
void PaintCurrentByteSign ( Graphics g , Rectangle rec )
{
// stack overflowexception on big files - workaround
2017-02-26 18:58:48 -05:00
if ( rec . Top < 0 | | rec . Left < 0 | | rec . Width < = 0 | | rec . Height < = 0 ) {
2017-02-26 13:46:57 -05:00
return ;
2017-02-26 18:58:48 -05:00
}
2017-02-26 13:46:57 -05:00
2017-02-26 18:58:48 -05:00
Point dimensions = new Point ( rec . Width , rec . Height ) ;
Bitmap bitmap ;
if ( ! _shadowSelectionCache . TryGetValue ( dimensions , out bitmap ) ) {
bitmap = new Bitmap ( rec . Width , rec . Height ) ;
using ( Graphics bitmapGraphics = Graphics . FromImage ( bitmap ) ) {
using ( SolidBrush shadowSelectionBrush = new SolidBrush ( _shadowSelectionColor ) ) {
bitmapGraphics . FillRectangle ( shadowSelectionBrush , 0 , 0 , rec . Width , rec . Height ) ;
_shadowSelectionCache [ dimensions ] = bitmap ;
}
}
}
2017-02-26 13:46:57 -05:00
g . CompositingQuality = System . Drawing . Drawing2D . CompositingQuality . GammaCorrected ;
2017-02-26 18:58:48 -05:00
g . DrawImage ( bitmap , rec . Left , rec . Top ) ;
2017-02-26 13:46:57 -05:00
}
Color GetDefaultForeColor ( )
{
if ( Enabled )
return ForeColor ;
else
return Color . Gray ;
}
void UpdateVisibilityBytes ( )
{
if ( _byteProvider = = null | | _byteProvider . Length = = 0 )
return ;
_startByte = ( _scrollVpos + 1 ) * _iHexMaxHBytes - _iHexMaxHBytes ;
_endByte = ( long ) Math . Min ( _byteProvider . Length - 1 , _startByte + _iHexMaxBytes ) ;
}
#endregion
public int LineInfoCharCount
{
get
{
if ( this . ByteProvider = = null | | this . ByteProvider . Length > 0x10000000 ) {
return 8 ;
} else if ( this . ByteProvider . Length > 0x1000000 ) {
return 7 ;
} else if ( this . ByteProvider . Length > 0x100000 ) {
return 6 ;
} else if ( this . ByteProvider . Length > 0x10000 ) {
return 5 ;
} else {
return 4 ;
}
}
}
#region Positioning methods
void UpdateRectanglePositioning ( )
{
// calc char size
SizeF charSize ;
using ( var graphics = this . CreateGraphics ( ) )
{
charSize = this . CreateGraphics ( ) . MeasureString ( "A" , Font , 100 , _stringFormat ) ;
}
2018-02-23 11:59:14 -05:00
CharSize = new SizeF ( ( float ) charSize . Width , ( float ) Math . Ceiling ( charSize . Height * 100 / ( this . HighDensityMode ? 133 : 100 ) ) ) ;
_highDensityModeOffset = ( ( float ) Math . Ceiling ( charSize . Height ) - CharSize . Height ) / 2 ;
2017-02-26 13:46:57 -05:00
int requiredWidth = 0 ;
// calc content bounds
_recContent = ClientRectangle ;
_recContent . X + = _recBorderLeft ;
_recContent . Y + = _recBorderTop ;
_recContent . Width - = _recBorderRight + _recBorderLeft ;
_recContent . Height - = _recBorderBottom + _recBorderTop ;
if ( _vScrollBarVisible )
{
_recContent . Width - = _vScrollBar . Width ;
_vScrollBar . Left = _recContent . X + _recContent . Width ;
_vScrollBar . Top = _recContent . Y ;
_vScrollBar . Height = _recContent . Height ;
requiredWidth + = _vScrollBar . Width ;
}
int marginLeft = 4 ;
// calc line info bounds
if ( _lineInfoVisible )
{
_recLineInfo = new Rectangle ( _recContent . X + marginLeft ,
_recContent . Y ,
( int ) ( _charSize . Width * ( LineInfoCharCount + 1 ) ) ,
_recContent . Height ) ;
requiredWidth + = _recLineInfo . Width + marginLeft ;
}
else
{
_recLineInfo = Rectangle . Empty ;
_recLineInfo . X = marginLeft ;
requiredWidth + = marginLeft ;
}
// calc line info bounds
_recColumnInfo = new Rectangle ( _recLineInfo . X + _recLineInfo . Width , _recContent . Y , _recContent . Width - _recLineInfo . Width , ( int ) charSize . Height + 4 ) ;
if ( _columnInfoVisible )
{
_recLineInfo . Y + = ( int ) charSize . Height + 4 ;
_recLineInfo . Height - = ( int ) charSize . Height + 4 ;
}
else
{
_recColumnInfo . Height = 0 ;
}
// calc hex bounds and grid
_recHex = new Rectangle ( _recLineInfo . X + _recLineInfo . Width ,
_recLineInfo . Y ,
_recContent . Width - _recLineInfo . Width ,
_recContent . Height - _recColumnInfo . Height ) ;
if ( UseFixedBytesPerLine )
{
SetHorizontalByteCount ( _bytesPerLine ) ;
_recHex . Width = ( int ) Math . Floor ( ( ( double ) _iHexMaxHBytes ) * _charSize . Width * 3 + ( 2 * _charSize . Width ) ) ;
requiredWidth + = _recHex . Width ;
}
else
{
int hmax = ( int ) Math . Floor ( ( double ) _recHex . Width / ( double ) _charSize . Width ) ;
if ( _stringViewVisible )
{
hmax - = 2 ;
if ( hmax > 1 )
SetHorizontalByteCount ( ( int ) Math . Floor ( ( double ) hmax / 4 ) ) ;
else
SetHorizontalByteCount ( 1 ) ;
}
else
{
if ( hmax > 1 )
SetHorizontalByteCount ( ( int ) Math . Floor ( ( double ) hmax / 3 ) ) ;
else
SetHorizontalByteCount ( 1 ) ;
}
_recHex . Width = ( int ) Math . Floor ( ( ( double ) _iHexMaxHBytes ) * _charSize . Width * 3 + ( 2 * _charSize . Width ) ) ;
requiredWidth + = _recHex . Width ;
}
if ( _stringViewVisible )
{
_recStringView = new Rectangle ( _recHex . X + _recHex . Width ,
_recHex . Y ,
( int ) ( _charSize . Width * _iHexMaxHBytes ) ,
_recHex . Height ) ;
2017-10-06 18:26:02 -04:00
requiredWidth + = _recStringView . Width + 40 ;
2017-02-26 13:46:57 -05:00
}
else
{
_recStringView = Rectangle . Empty ;
}
RequiredWidth = requiredWidth ;
int vmax = ( int ) Math . Floor ( ( double ) _recHex . Height / ( double ) _charSize . Height ) ;
2018-02-24 14:56:22 -05:00
SetVerticalByteCount ( vmax > 0 ? vmax : 1 ) ;
2017-02-26 13:46:57 -05:00
_iHexMaxBytes = _iHexMaxHBytes * _iHexMaxVBytes ;
UpdateScrollSize ( ) ;
}
PointF GetBytePointF ( long byteIndex )
{
Point gp = GetGridBytePoint ( byteIndex ) ;
return GetBytePointF ( gp ) ;
}
PointF GetBytePointF ( Point gp )
{
float x = ( 3 * _charSize . Width ) * gp . X + _recHex . X ;
float y = ( gp . Y + 1 ) * _charSize . Height - _charSize . Height + _recHex . Y ;
return new PointF ( x , y ) ;
}
PointF GetColumnInfoPointF ( int col )
{
Point gp = GetGridBytePoint ( col ) ;
float x = ( 3 * _charSize . Width ) * gp . X + _recColumnInfo . X ;
float y = _recColumnInfo . Y ;
return new PointF ( x , y ) ;
}
2018-01-14 14:03:43 -05:00
float [ ] _xPosCache = new float [ 64 * 150 ] ;
2017-02-26 18:58:48 -05:00
Dictionary < int , List < float > > _xPosList = new Dictionary < int , List < float > > ( ) ;
2018-01-14 14:03:43 -05:00
PointF GetByteStringPointF ( Point gp , bool useCache = true )
2017-02-26 13:46:57 -05:00
{
float x = ( _charSize . Width ) * gp . X + _recStringView . X ;
float y = ( gp . Y + 1 ) * _charSize . Height - _charSize . Height + _recStringView . Y ;
2018-01-14 14:03:43 -05:00
float cachedXPos = gp . Y > 0 & & gp . Y < 150 ? _xPosCache [ gp . Y * 64 + gp . X ] : - 1 ;
if ( useCache & & cachedXPos > = 0 ) {
return new PointF ( cachedXPos , y ) ;
2017-02-26 18:58:48 -05:00
} else {
return new PointF ( x , y ) ;
}
2017-02-26 13:46:57 -05:00
}
Point GetGridBytePoint ( long byteIndex )
{
int row = ( int ) Math . Floor ( ( double ) byteIndex / ( double ) _iHexMaxHBytes ) ;
int column = ( int ) ( byteIndex + _iHexMaxHBytes - _iHexMaxHBytes * ( row + 1 ) ) ;
Point res = new Point ( column , row ) ;
return res ;
}
#endregion
#region Overridden properties
/// <summary>
/// Gets or sets the background color for the control.
/// </summary>
[DefaultValue(typeof(Color), "White")]
public override Color BackColor
{
get
{
return base . BackColor ;
}
set
{
base . BackColor = value ;
}
}
/// <summary>
/// The font used to display text in the hexbox.
/// </summary>
public override Font Font
{
get
{
return base . Font ;
}
set
{
2017-03-02 20:33:25 -05:00
if ( value = = null )
return ;
2017-02-26 13:46:57 -05:00
base . Font = value ;
2017-03-02 20:33:25 -05:00
this . UpdateRectanglePositioning ( ) ;
this . UpdateCaret ( ) ;
this . Invalidate ( ) ;
2018-01-14 14:03:43 -05:00
_measureCache = new Dictionary < string , float > ( ) ;
2017-02-26 13:46:57 -05:00
}
}
2018-01-14 14:03:43 -05:00
Dictionary < string , float > _measureCache = new Dictionary < string , float > ( ) ;
2017-02-26 13:46:57 -05:00
/// <summary>
/// Not used.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)]
public override string Text
{
get
{
return base . Text ;
}
set
{
base . Text = value ;
}
}
/// <summary>
/// Not used.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)]
public override RightToLeft RightToLeft
{
get
{
return base . RightToLeft ;
}
set
{
base . RightToLeft = value ;
}
}
#endregion
#region Properties
2018-03-10 13:41:01 -05:00
public bool ByteEditingMode { get ; set ; } = false ;
2017-02-26 13:46:57 -05:00
/// <summary>
/// Gets or sets the background color for the disabled control.
/// </summary>
[Category("Appearance"), DefaultValue(typeof(Color), "WhiteSmoke")]
public Color BackColorDisabled
{
get
{
return _backColorDisabled ;
}
set
{
_backColorDisabled = value ;
}
} Color _backColorDisabled = Color . FromName ( "WhiteSmoke" ) ;
/// <summary>
/// Gets or sets if the count of bytes in one line is fix.
/// </summary>
/// <remarks>
/// When set to True, BytesPerLine property determine the maximum count of bytes in one line.
/// </remarks>
[DefaultValue(false), Category("Hex"), Description("Gets or sets if the count of bytes in one line is fix.")]
public bool ReadOnly
{
get { return _readOnly ; }
set
{
if ( _readOnly = = value )
return ;
_readOnly = value ;
OnReadOnlyChanged ( EventArgs . Empty ) ;
Invalidate ( ) ;
}
} bool _readOnly ;
/// <summary>
/// Gets or sets the maximum count of bytes in one line.
/// </summary>
/// <remarks>
/// UseFixedBytesPerLine property no longer has to be set to true for this to work
/// </remarks>
[DefaultValue(16), Category("Hex"), Description("Gets or sets the maximum count of bytes in one line.")]
public int BytesPerLine
{
get { return _bytesPerLine ; }
set
{
if ( _bytesPerLine = = value )
return ;
_bytesPerLine = value ;
OnBytesPerLineChanged ( EventArgs . Empty ) ;
UpdateRectanglePositioning ( ) ;
Invalidate ( ) ;
}
} int _bytesPerLine = 16 ;
/// <summary>
/// Gets or sets the number of bytes in a group. Used to show the group separator line (if GroupSeparatorVisible is true)
/// </summary>
/// <remarks>
/// GroupSeparatorVisible property must set to true
/// </remarks>
[DefaultValue(4), Category("Hex"), Description("Gets or sets the byte-count between group separators (if visible).")]
public int GroupSize
{
get { return _groupSize ; }
set
{
if ( _groupSize = = value )
return ;
_groupSize = value ;
OnGroupSizeChanged ( EventArgs . Empty ) ;
UpdateRectanglePositioning ( ) ;
Invalidate ( ) ;
}
} int _groupSize = 4 ;
/// <summary>
/// Gets or sets if the count of bytes in one line is fix.
/// </summary>
/// <remarks>
/// When set to True, BytesPerLine property determine the maximum count of bytes in one line.
/// </remarks>
[DefaultValue(false), Category("Hex"), Description("Gets or sets if the count of bytes in one line is fix.")]
public bool UseFixedBytesPerLine
{
get { return _useFixedBytesPerLine ; }
set
{
if ( _useFixedBytesPerLine = = value )
return ;
_useFixedBytesPerLine = value ;
OnUseFixedBytesPerLineChanged ( EventArgs . Empty ) ;
UpdateRectanglePositioning ( ) ;
Invalidate ( ) ;
}
} bool _useFixedBytesPerLine ;
/// <summary>
/// Gets or sets the visibility of a vertical scroll bar.
/// </summary>
[DefaultValue(false), Category("Hex"), Description("Gets or sets the visibility of a vertical scroll bar.")]
public bool VScrollBarVisible
{
get { return this . _vScrollBarVisible ; }
set
{
if ( _vScrollBarVisible = = value )
return ;
_vScrollBarVisible = value ;
if ( _vScrollBarVisible )
Controls . Add ( _vScrollBar ) ;
else
Controls . Remove ( _vScrollBar ) ;
UpdateRectanglePositioning ( ) ;
UpdateScrollSize ( ) ;
OnVScrollBarVisibleChanged ( EventArgs . Empty ) ;
}
} bool _vScrollBarVisible ;
/// <summary>
/// Gets or sets the ByteProvider.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IByteProvider ByteProvider
{
get { return _byteProvider ; }
set
{
bool sameLength = value ! = null & & _byteProvider ! = null & & _byteProvider . Length = = value . Length ;
if ( _byteProvider = = value )
return ;
2017-09-02 00:42:36 -04:00
if ( _keyInterpreter = = null ) {
if ( value = = null )
ActivateEmptyKeyInterpreter ( ) ;
else
ActivateKeyInterpreter ( ) ;
}
2017-02-26 13:46:57 -05:00
if ( _byteProvider ! = null )
_byteProvider . LengthChanged - = new EventHandler ( _byteProvider_LengthChanged ) ;
_byteProvider = value ;
if ( _byteProvider ! = null )
_byteProvider . LengthChanged + = new EventHandler ( _byteProvider_LengthChanged ) ;
OnByteProviderChanged ( EventArgs . Empty ) ;
if ( value = = null ) // do not raise events if value is null
{
_bytePos = - 1 ;
_byteCharacterPos = 0 ;
_selectionLength = 0 ;
DestroyCaret ( ) ;
}
else
{
if ( ! sameLength ) {
SetPosition ( 0 , 0 ) ;
SetSelectionLength ( 0 ) ;
}
if ( _caretVisible & & Focused )
UpdateCaret ( ) ;
else
CreateCaret ( ) ;
}
CheckCurrentLineChanged ( ) ;
CheckCurrentPositionInLineChanged ( ) ;
if ( ! sameLength ) {
_scrollVpos = 0 ;
}
UpdateVisibilityBytes ( ) ;
UpdateRectanglePositioning ( ) ;
Invalidate ( ) ;
}
}
IByteProvider _byteProvider ;
2017-03-04 23:15:50 -05:00
IByteColorProvider _byteColorProvider ;
2017-02-26 13:46:57 -05:00
2017-03-04 23:15:50 -05:00
public IByteColorProvider ByteColorProvider
{
get { return _byteColorProvider ; }
set { _byteColorProvider = value ; Invalidate ( ) ; }
}
2017-02-26 13:46:57 -05:00
/// <summary>
/// Gets or sets the visibility of the group separator.
/// </summary>
[DefaultValue(false), Category("Hex"), Description("Gets or sets the visibility of a separator vertical line.")]
public bool GroupSeparatorVisible
{
get { return _groupSeparatorVisible ; }
set
{
if ( _groupSeparatorVisible = = value )
return ;
_groupSeparatorVisible = value ;
OnGroupSeparatorVisibleChanged ( EventArgs . Empty ) ;
UpdateRectanglePositioning ( ) ;
Invalidate ( ) ;
}
} bool _groupSeparatorVisible = false ;
/// <summary>
/// Gets or sets the visibility of the column info
/// </summary>
[DefaultValue(false), Category("Hex"), Description("Gets or sets the visibility of header row.")]
public bool ColumnInfoVisible
{
get { return _columnInfoVisible ; }
set
{
if ( _columnInfoVisible = = value )
return ;
_columnInfoVisible = value ;
OnColumnInfoVisibleChanged ( EventArgs . Empty ) ;
UpdateRectanglePositioning ( ) ;
Invalidate ( ) ;
}
} bool _columnInfoVisible = false ;
/// <summary>
/// Gets or sets the visibility of a line info.
/// </summary>
[DefaultValue(false), Category("Hex"), Description("Gets or sets the visibility of a line info.")]
public bool LineInfoVisible
{
get { return _lineInfoVisible ; }
set
{
if ( _lineInfoVisible = = value )
return ;
_lineInfoVisible = value ;
OnLineInfoVisibleChanged ( EventArgs . Empty ) ;
UpdateRectanglePositioning ( ) ;
Invalidate ( ) ;
}
} bool _lineInfoVisible = false ;
/// <summary>
/// Gets or sets the offset of a line info.
/// </summary>
[DefaultValue((long)0), Category("Hex"), Description("Gets or sets the offset of the line info.")]
public long LineInfoOffset
{
get { return _lineInfoOffset ; }
set
{
if ( _lineInfoOffset = = value )
return ;
_lineInfoOffset = value ;
Invalidate ( ) ;
}
} long _lineInfoOffset = 0 ;
/// <summary>
/// Gets or sets the hex box<6F> s border style.
/// </summary>
[DefaultValue(typeof(BorderStyle), "Fixed3D"), Category("Hex"), Description("Gets or sets the hex box<6F> s border style.")]
public BorderStyle BorderStyle
{
get { return _borderStyle ; }
set
{
if ( _borderStyle = = value )
return ;
_borderStyle = value ;
switch ( _borderStyle )
{
case BorderStyle . None :
_recBorderLeft = _recBorderTop = _recBorderRight = _recBorderBottom = 0 ;
break ;
case BorderStyle . Fixed3D :
_recBorderLeft = _recBorderRight = SystemInformation . Border3DSize . Width ;
_recBorderTop = _recBorderBottom = SystemInformation . Border3DSize . Height ;
break ;
case BorderStyle . FixedSingle :
_recBorderLeft = _recBorderTop = _recBorderRight = _recBorderBottom = 1 ;
break ;
}
UpdateRectanglePositioning ( ) ;
OnBorderStyleChanged ( EventArgs . Empty ) ;
}
} BorderStyle _borderStyle = BorderStyle . Fixed3D ;
/// <summary>
/// Gets or sets the visibility of the string view.
/// </summary>
[DefaultValue(false), Category("Hex"), Description("Gets or sets the visibility of the string view.")]
public bool StringViewVisible
{
get { return _stringViewVisible ; }
set
{
if ( _stringViewVisible = = value )
return ;
_stringViewVisible = value ;
OnStringViewVisibleChanged ( EventArgs . Empty ) ;
UpdateRectanglePositioning ( ) ;
Invalidate ( ) ;
}
} bool _stringViewVisible ;
/// <summary>
/// Gets or sets whether the HexBox control displays the hex characters in upper or lower case.
/// </summary>
[DefaultValue(typeof(HexCasing), "Upper"), Category("Hex"), Description("Gets or sets whether the HexBox control displays the hex characters in upper or lower case.")]
public HexCasing HexCasing
{
get
{
if ( _hexStringFormat = = "X" )
return HexCasing . Upper ;
else
return HexCasing . Lower ;
}
set
{
string format ;
if ( value = = HexCasing . Upper )
format = "X" ;
else
format = "x" ;
if ( _hexStringFormat = = format )
return ;
_hexStringFormat = format ;
OnHexCasingChanged ( EventArgs . Empty ) ;
Invalidate ( ) ;
}
}
/// <summary>
/// Gets and sets the starting point of the bytes selected in the hex box.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public long SelectionStart
{
get { return _bytePos ; }
set
{
SetPosition ( value , 0 ) ;
ScrollByteIntoView ( ) ;
Invalidate ( ) ;
}
}
/// <summary>
/// Gets and sets the number of bytes selected in the hex box.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public long SelectionLength
{
get { return _selectionLength ; }
set
{
SetSelectionLength ( value ) ;
ScrollByteIntoView ( ) ;
Invalidate ( ) ;
}
} long _selectionLength ;
/// <summary>
/// Gets or sets the info color used for column info and line info. When this property is null, then ForeColor property is used.
/// </summary>
[DefaultValue(typeof(Color), "Gray"), Category("Hex"), Description("Gets or sets the line info color. When this property is null, then ForeColor property is used.")]
public Color InfoForeColor
{
get { return _infoForeColor ; }
set { _infoForeColor = value ; Invalidate ( ) ; }
} Color _infoForeColor = Color . Gray ;
/// <summary>
/// Gets or sets the info color used for column info and line info. When this property is null, then ForeColor property is used.
/// </summary>
[DefaultValue(typeof(Color), "Gray"), Category("Hex"), Description("Gets or sets the line info color. When this property is null, then ForeColor property is used.")]
public Color InfoBackColor
{
get { return _infoBackColor ; }
set { _infoBackColor = value ; Invalidate ( ) ; }
}
Color _infoBackColor = Color . DarkGray ;
/// <summary>
/// Gets or sets the background color for the selected bytes.
/// </summary>
[DefaultValue(typeof(Color), "Blue"), Category("Hex"), Description("Gets or sets the background color for the selected bytes.")]
public Color SelectionBackColor
{
get { return _selectionBackColor ; }
set { _selectionBackColor = value ; Invalidate ( ) ; }
} Color _selectionBackColor = Color . Blue ;
/// <summary>
/// Gets or sets the foreground color for the selected bytes.
/// </summary>
[DefaultValue(typeof(Color), "White"), Category("Hex"), Description("Gets or sets the foreground color for the selected bytes.")]
public Color SelectionForeColor
{
get { return _selectionForeColor ; }
set { _selectionForeColor = value ; Invalidate ( ) ; }
} Color _selectionForeColor = Color . White ;
/// <summary>
/// Gets or sets the visibility of a shadow selection.
/// </summary>
[DefaultValue(true), Category("Hex"), Description("Gets or sets the visibility of a shadow selection.")]
public bool ShadowSelectionVisible
{
get { return _shadowSelectionVisible ; }
set
{
if ( _shadowSelectionVisible = = value )
return ;
_shadowSelectionVisible = value ;
Invalidate ( ) ;
}
} bool _shadowSelectionVisible = true ;
/// <summary>
/// Gets or sets the color of the shadow selection.
/// </summary>
/// <remarks>
/// A alpha component must be given!
/// Default alpha = 100
/// </remarks>
[Category("Hex"), Description("Gets or sets the color of the shadow selection.")]
public Color ShadowSelectionColor
{
get { return _shadowSelectionColor ; }
set { _shadowSelectionColor = value ; Invalidate ( ) ; }
} Color _shadowSelectionColor = Color . FromArgb ( 100 , 60 , 188 , 255 ) ;
/// <summary>
/// Contains the size of a single character in pixel
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public SizeF CharSize
{
get { return _charSize ; }
private set
{
if ( _charSize = = value )
return ;
_charSize = value ;
if ( CharSizeChanged ! = null )
CharSizeChanged ( this , EventArgs . Empty ) ;
}
} SizeF _charSize ;
2018-02-23 10:18:25 -05:00
public bool HighDensityMode
{
get { return _highDensityMode ; }
set
{
if ( _highDensityMode = = value )
return ;
_highDensityMode = value ;
this . UpdateCaret ( ) ;
this . Invalidate ( ) ;
this . UpdateRectanglePositioning ( ) ;
}
} bool _highDensityMode ;
2018-02-23 11:59:14 -05:00
float _highDensityModeOffset = 0f ;
2018-02-23 10:18:25 -05:00
2018-02-23 12:45:49 -05:00
public bool EnablePerByteNavigation { get ; set ; }
2018-02-23 11:59:14 -05:00
/// <summary>
/// Gets the width required for the content
/// </summary>
[DefaultValue(0), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2017-02-26 13:46:57 -05:00
public int RequiredWidth
{
get { return _requiredWidth ; }
private set
{
if ( _requiredWidth = = value )
return ;
_requiredWidth = value ;
if ( RequiredWidthChanged ! = null )
RequiredWidthChanged ( this , EventArgs . Empty ) ;
}
} int _requiredWidth ;
/// <summary>
/// Gets the number bytes drawn horizontally.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int HorizontalByteCount
{
get { return _iHexMaxHBytes ; }
}
/// <summary>
/// Gets the number bytes drawn vertically.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int VerticalByteCount
{
get { return _iHexMaxVBytes ; }
}
/// <summary>
/// Gets the current line
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public long CurrentLine
{
get { return _currentLine ; }
} long _currentLine ;
/// <summary>
/// Gets the current position in the current line
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public long CurrentPositionInLine
{
get { return _currentPositionInLine ; }
} int _currentPositionInLine ;
/// <summary>
/// Gets the a value if insertion mode is active or not.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool InsertActive
{
get { return _insertActive ; }
set
{
if ( _insertActive = = value )
return ;
_insertActive = value ;
// recreate caret
DestroyCaret ( ) ;
CreateCaret ( ) ;
// raise change event
OnInsertActiveChanged ( EventArgs . Empty ) ;
}
}
/// <summary>
/// Gets or sets the built-in context menu.
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public BuiltInContextMenu BuiltInContextMenu
{
get { return _builtInContextMenu ; }
} BuiltInContextMenu _builtInContextMenu ;
/// <summary>
/// Gets or sets the converter that will translate between byte and character values.
/// </summary>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IByteCharConverter ByteCharConverter
{
get
{
if ( _byteCharConverter = = null )
_byteCharConverter = new DefaultByteCharConverter ( ) ;
return _byteCharConverter ;
}
set
{
2017-02-26 19:18:01 -05:00
if ( value ! = _byteCharConverter )
2017-02-26 13:46:57 -05:00
{
_byteCharConverter = value ;
Invalidate ( ) ;
}
}
} IByteCharConverter _byteCharConverter ;
#endregion
#region Misc
/// <summary>
/// Converts a byte array to a hex string. For example: {10,11} = "0A 0B"
/// </summary>
/// <param name="data">the byte array</param>
/// <returns>the hex string</returns>
string ConvertBytesToHex ( byte [ ] data )
{
StringBuilder sb = new StringBuilder ( ) ;
foreach ( byte b in data )
{
string hex = ConvertByteToHex ( b ) ;
sb . Append ( hex ) ;
sb . Append ( " " ) ;
}
if ( sb . Length > 0 )
sb . Remove ( sb . Length - 1 , 1 ) ;
string result = sb . ToString ( ) ;
return result ;
}
/// <summary>
/// Converts the byte to a hex string. For example: "10" = "0A";
/// </summary>
/// <param name="b">the byte to format</param>
/// <returns>the hex string</returns>
string ConvertByteToHex ( byte b )
{
string sB = b . ToString ( _hexStringFormat , System . Threading . Thread . CurrentThread . CurrentCulture ) ;
if ( sB . Length = = 1 )
sB = "0" + sB ;
return sB ;
}
/// <summary>
/// Converts the hex string to an byte array. The hex string must be separated by a space char ' '. If there is any invalid hex information in the string the result will be null.
/// </summary>
/// <param name="hex">the hex string separated by ' '. For example: "0A 0B 0C"</param>
/// <returns>the byte array. null if hex is invalid or empty</returns>
byte [ ] ConvertHexToBytes ( string hex )
{
if ( string . IsNullOrEmpty ( hex ) )
return null ;
hex = hex . Trim ( ) ;
var hexArray = hex . Split ( ' ' ) ;
var byteArray = new byte [ hexArray . Length ] ;
for ( int i = 0 ; i < hexArray . Length ; i + + )
{
var hexValue = hexArray [ i ] ;
byte b ;
var isByte = ConvertHexToByte ( hexValue , out b ) ;
if ( ! isByte )
return null ;
byteArray [ i ] = b ;
}
return byteArray ;
}
bool ConvertHexToByte ( string hex , out byte b )
{
bool isByte = byte . TryParse ( hex , System . Globalization . NumberStyles . HexNumber , System . Threading . Thread . CurrentThread . CurrentCulture , out b ) ;
return isByte ;
}
void SetPosition ( long bytePos )
{
SetPosition ( bytePos , _byteCharacterPos ) ;
}
void SetPosition ( long bytePos , int byteCharacterPos )
{
2018-03-05 21:51:21 -05:00
if ( bytePos > = this . _byteProvider . Length ) {
bytePos = this . _byteProvider . Length - 1 ;
2018-02-24 14:23:54 -05:00
}
2017-02-26 13:46:57 -05:00
if ( _byteCharacterPos ! = byteCharacterPos )
{
_byteCharacterPos = byteCharacterPos ;
}
if ( bytePos ! = _bytePos )
{
2018-03-10 13:41:01 -05:00
_byteProvider . CommitWriteByte ( ) ;
2017-02-26 13:46:57 -05:00
_bytePos = bytePos ;
CheckCurrentLineChanged ( ) ;
CheckCurrentPositionInLineChanged ( ) ;
OnSelectionStartChanged ( EventArgs . Empty ) ;
}
}
void SetSelectionLength ( long selectionLength )
{
if ( selectionLength ! = _selectionLength )
{
_selectionLength = selectionLength ;
OnSelectionLengthChanged ( EventArgs . Empty ) ;
}
}
void SetHorizontalByteCount ( int value )
{
if ( _iHexMaxHBytes = = value )
return ;
_iHexMaxHBytes = value ;
OnHorizontalByteCountChanged ( EventArgs . Empty ) ;
}
void SetVerticalByteCount ( int value )
{
if ( _iHexMaxVBytes = = value )
return ;
_iHexMaxVBytes = value ;
OnVerticalByteCountChanged ( EventArgs . Empty ) ;
}
void CheckCurrentLineChanged ( )
{
long currentLine = ( long ) Math . Floor ( ( double ) _bytePos / ( double ) _iHexMaxHBytes ) + 1 ;
if ( _byteProvider = = null & & _currentLine ! = 0 )
{
_currentLine = 0 ;
OnCurrentLineChanged ( EventArgs . Empty ) ;
}
else if ( currentLine ! = _currentLine )
{
_currentLine = currentLine ;
OnCurrentLineChanged ( EventArgs . Empty ) ;
}
}
void CheckCurrentPositionInLineChanged ( )
{
Point gb = GetGridBytePoint ( _bytePos ) ;
int currentPositionInLine = gb . X + 1 ;
if ( _byteProvider = = null & & _currentPositionInLine ! = 0 )
{
_currentPositionInLine = 0 ;
OnCurrentPositionInLineChanged ( EventArgs . Empty ) ;
}
else if ( currentPositionInLine ! = _currentPositionInLine )
{
_currentPositionInLine = currentPositionInLine ;
OnCurrentPositionInLineChanged ( EventArgs . Empty ) ;
}
}
/// <summary>
/// Raises the InsertActiveChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnInsertActiveChanged ( EventArgs e )
{
if ( InsertActiveChanged ! = null )
InsertActiveChanged ( this , e ) ;
}
/// <summary>
/// Raises the ReadOnlyChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnReadOnlyChanged ( EventArgs e )
{
if ( ReadOnlyChanged ! = null )
ReadOnlyChanged ( this , e ) ;
}
/// <summary>
/// Raises the ByteProviderChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnByteProviderChanged ( EventArgs e )
{
if ( ByteProviderChanged ! = null )
ByteProviderChanged ( this , e ) ;
}
/// <summary>
/// Raises the SelectionStartChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnSelectionStartChanged ( EventArgs e )
{
if ( SelectionStartChanged ! = null )
SelectionStartChanged ( this , e ) ;
}
/// <summary>
/// Raises the SelectionLengthChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnSelectionLengthChanged ( EventArgs e )
{
if ( SelectionLengthChanged ! = null )
SelectionLengthChanged ( this , e ) ;
}
/// <summary>
/// Raises the LineInfoVisibleChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnLineInfoVisibleChanged ( EventArgs e )
{
if ( LineInfoVisibleChanged ! = null )
LineInfoVisibleChanged ( this , e ) ;
}
/// <summary>
/// Raises the OnColumnInfoVisibleChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnColumnInfoVisibleChanged ( EventArgs e )
{
if ( ColumnInfoVisibleChanged ! = null )
ColumnInfoVisibleChanged ( this , e ) ;
}
/// <summary>
/// Raises the ColumnSeparatorVisibleChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnGroupSeparatorVisibleChanged ( EventArgs e )
{
if ( GroupSeparatorVisibleChanged ! = null )
GroupSeparatorVisibleChanged ( this , e ) ;
}
/// <summary>
/// Raises the StringViewVisibleChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnStringViewVisibleChanged ( EventArgs e )
{
if ( StringViewVisibleChanged ! = null )
StringViewVisibleChanged ( this , e ) ;
}
/// <summary>
/// Raises the BorderStyleChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnBorderStyleChanged ( EventArgs e )
{
if ( BorderStyleChanged ! = null )
BorderStyleChanged ( this , e ) ;
}
/// <summary>
/// Raises the UseFixedBytesPerLineChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnUseFixedBytesPerLineChanged ( EventArgs e )
{
if ( UseFixedBytesPerLineChanged ! = null )
UseFixedBytesPerLineChanged ( this , e ) ;
}
/// <summary>
/// Raises the GroupSizeChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnGroupSizeChanged ( EventArgs e )
{
if ( GroupSizeChanged ! = null )
GroupSizeChanged ( this , e ) ;
}
/// <summary>
/// Raises the BytesPerLineChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnBytesPerLineChanged ( EventArgs e )
{
if ( BytesPerLineChanged ! = null )
BytesPerLineChanged ( this , e ) ;
}
/// <summary>
/// Raises the VScrollBarVisibleChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnVScrollBarVisibleChanged ( EventArgs e )
{
if ( VScrollBarVisibleChanged ! = null )
VScrollBarVisibleChanged ( this , e ) ;
}
/// <summary>
/// Raises the HexCasingChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnHexCasingChanged ( EventArgs e )
{
if ( HexCasingChanged ! = null )
HexCasingChanged ( this , e ) ;
}
/// <summary>
/// Raises the HorizontalByteCountChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnHorizontalByteCountChanged ( EventArgs e )
{
if ( HorizontalByteCountChanged ! = null )
HorizontalByteCountChanged ( this , e ) ;
}
/// <summary>
/// Raises the VerticalByteCountChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnVerticalByteCountChanged ( EventArgs e )
{
if ( VerticalByteCountChanged ! = null )
VerticalByteCountChanged ( this , e ) ;
}
/// <summary>
/// Raises the CurrentLineChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnCurrentLineChanged ( EventArgs e )
{
if ( CurrentLineChanged ! = null )
CurrentLineChanged ( this , e ) ;
}
/// <summary>
/// Raises the CurrentPositionInLineChanged event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnCurrentPositionInLineChanged ( EventArgs e )
{
if ( CurrentPositionInLineChanged ! = null )
CurrentPositionInLineChanged ( this , e ) ;
}
/// <summary>
/// Raises the Copied event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnCopied ( EventArgs e )
{
if ( Copied ! = null )
Copied ( this , e ) ;
}
/// <summary>
/// Raises the CopiedHex event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected virtual void OnCopiedHex ( EventArgs e )
{
if ( CopiedHex ! = null )
CopiedHex ( this , e ) ;
}
/// <summary>
/// Raises the MouseDown event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected override void OnMouseDown ( MouseEventArgs e )
{
System . Diagnostics . Debug . WriteLine ( "OnMouseDown()" , "HexBox" ) ;
if ( ! Focused )
Focus ( ) ;
2017-03-02 20:33:25 -05:00
BytePositionInfo ? bpi = GetBytePositionInfo ( new Point ( e . X , e . Y ) ) ;
bool insideSelection = false ;
if ( bpi . HasValue ) {
if ( _bytePos < = bpi . Value . Index & & _bytePos + _selectionLength > = bpi . Value . Index ) {
//Clicked inside selection
insideSelection = true ;
}
}
if ( ! insideSelection | | e . Button = = MouseButtons . Left ) {
if ( e . Button ! = MouseButtons . Left ) {
_selectionLength = 0 ;
}
2017-02-26 13:46:57 -05:00
SetCaretPosition ( new Point ( e . X , e . Y ) ) ;
2017-03-02 20:33:25 -05:00
}
2017-02-26 13:46:57 -05:00
base . OnMouseDown ( e ) ;
}
/// <summary>
/// Raises the MouseWhell event
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected override void OnMouseWheel ( MouseEventArgs e )
{
2018-03-26 18:16:19 -04:00
if ( _byteProvider ! = null & & ! this . ReadOnly & & Control . ModifierKeys = = Keys . Shift ) {
//Allow the user to change the value of the byte under the cursor by hold Alt & using the mouse wheel
BytePositionInfo ? byteUnderCursor = GetBytePositionInfo ( e . Location ) ;
if ( byteUnderCursor . HasValue ) {
_byteProvider . WriteByte ( byteUnderCursor . Value . Index , ( byte ) ( _byteProvider . ReadByte ( byteUnderCursor . Value . Index ) + ( e . Delta > 0 ? 1 : - 1 ) ) ) ;
}
} else {
int linesToScroll = - ( e . Delta * SystemInformation . MouseWheelScrollLines / 120 ) ;
this . PerformScrollLines ( linesToScroll ) ;
}
2017-02-26 13:46:57 -05:00
base . OnMouseWheel ( e ) ;
}
/// <summary>
/// Raises the Resize event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected override void OnResize ( EventArgs e )
{
base . OnResize ( e ) ;
UpdateRectanglePositioning ( ) ;
}
/// <summary>
/// Raises the GotFocus event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected override void OnGotFocus ( EventArgs e )
{
System . Diagnostics . Debug . WriteLine ( "OnGotFocus()" , "HexBox" ) ;
base . OnGotFocus ( e ) ;
CreateCaret ( ) ;
}
/// <summary>
/// Raises the LostFocus event.
/// </summary>
/// <param name="e">An EventArgs that contains the event data.</param>
protected override void OnLostFocus ( EventArgs e )
{
System . Diagnostics . Debug . WriteLine ( "OnLostFocus()" , "HexBox" ) ;
base . OnLostFocus ( e ) ;
2018-03-10 13:41:01 -05:00
if ( _byteProvider ! = null ) {
_byteProvider . CommitWriteByte ( ) ;
}
2017-02-26 13:46:57 -05:00
DestroyCaret ( ) ;
}
void _byteProvider_LengthChanged ( object sender , EventArgs e )
{
UpdateScrollSize ( ) ;
}
#endregion
#region Scaling Support for High DPI resolution screens
/// <summary>
/// For high resolution screen support
/// </summary>
/// <param name="factor">the factor</param>
/// <param name="specified">bounds</param>
protected override void ScaleControl ( SizeF factor , BoundsSpecified specified )
{
base . ScaleControl ( factor , specified ) ;
this . BeginInvoke ( new MethodInvoker ( ( ) = >
{
this . UpdateRectanglePositioning ( ) ;
if ( _caretVisible )
{
DestroyCaret ( ) ;
CreateCaret ( ) ;
}
this . Invalidate ( ) ;
} ) ) ;
}
#endregion
}
}