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 Be.Windows.Forms.Design;
using System.Text;
namespace Be.Windows.Forms{
#region HexCasing enumeration
/// <summary>
/// Specifies the case of hex characters in the HexBox control
/// </summary>
public enum HexCasing
{
/// <summary>
/// Converts all characters to uppercase.
/// </summary>
Upper = 0,
/// <summary>
/// Converts all characters to lowercase.
/// </summary>
Lower = 1
}
#endregion
#region BytePositionInfo structure
/// <summary>
/// Represents a position in the HexBox control
/// </summary>
struct BytePositionInfo
{
public BytePositionInfo(long index, int characterPosition)
{
_index = index;
_characterPosition = characterPosition;
}
public int CharacterPosition
{
get { return _characterPosition; }
} int _characterPosition;
public long Index
{
get { return _index; }
} long _index;
}
#endregion
/// <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
{
#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;
#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");
_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); /// <--- This line is added
}
}
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;
switch(keyData)
{
case Keys.Left:
case Keys.Up:
case Keys.Right:
case Keys.Down:
case Keys.PageUp:
case Keys.PageDown:
case Keys.Left | Keys.Shift:
case Keys.Up | Keys.Shift:
case Keys.Right | Keys.Shift:
case Keys.Down | Keys.Shift:
case Keys.Tab:
case Keys.Back:
case Keys.Delete:
case Keys.Home:
case Keys.End:
case Keys.ShiftKey | Keys.Shift:
case Keys.C | Keys.Control:
case Keys.X | Keys.Control:
case Keys.V | Keys.Control:
if(RaiseKeyDown(keyData))
return true;
break;
}
switch(keyData)
{
case Keys.Left: // move left
return PreProcessWmKeyDown_Left(ref m);
case Keys.Up: // move up
return PreProcessWmKeyDown_Up(ref m);
case Keys.Right: // move right
return PreProcessWmKeyDown_Right(ref m);
case Keys.Down: // move down
return PreProcessWmKeyDown_Down(ref m);
case Keys.PageUp: // move pageup
return PreProcessWmKeyDown_PageUp(ref m);
case Keys.PageDown: // move pagedown
return PreProcessWmKeyDown_PageDown(ref m);
case Keys.Left | Keys.Shift: // move left with selection
return PreProcessWmKeyDown_ShiftLeft(ref m);
case Keys.Up | Keys.Shift: // move up with selection
return PreProcessWmKeyDown_ShiftUp(ref m);
case Keys.Right | Keys.Shift: // move right with selection
return PreProcessWmKeyDown_ShiftRight(ref m);
case Keys.Down | Keys.Shift: // move down with selection
return PreProcessWmKeyDown_ShiftDown(ref m);
case Keys.Tab: // switch focus to string view
return PreProcessWmKeyDown_Tab(ref m);
case Keys.Back: // back
return PreProcessWmKeyDown_Back(ref m);
case Keys.Delete: // delete
return PreProcessWmKeyDown_Delete(ref m);
case Keys.Home: // move to home
return PreProcessWmKeyDown_Home(ref m);
case Keys.End: // move to end
return PreProcessWmKeyDown_End(ref m);
case Keys.ShiftKey | Keys.Shift: // begin selection process
return PreProcessWmKeyDown_ShiftShiftKey(ref m);
case Keys.C | Keys.Control: // copy
return PreProcessWmKeyDown_ControlC(ref m);
case Keys.X | Keys.Control: // cut
return PreProcessWmKeyDown_ControlX(ref m);
case Keys.V | Keys.Control: // paste
return PreProcessWmKeyDown_ControlV(ref m);
default:
_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)
{
return PerformPosMoveRight();
}
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;
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;
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);
if(isInsertMode)
_hexBox._byteProvider.InsertBytes(pos, new byte[]{newcb});
else
_hexBox._byteProvider.WriteByte(pos, newcb);
PerformPosMoveRight();
_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
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;
if(cp > 0)
{
cp--;
}
else
{
pos = Math.Max(0, pos-1);
cp++;
}
_hexBox.SetPosition(pos, cp);
if(pos < _hexBox._startByte)
{
_hexBox.PerformScrollLineUp();
}
_hexBox.UpdateCaret();
_hexBox.Invalidate();
}
_hexBox.ScrollByteIntoView();
return true;
}
protected virtual bool PerformPosMoveRight()
{
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) )
{
if(cp > 0)
{
pos = Math.Min(_hexBox._byteProvider.Length, pos+1);
cp = 0;
}
else
{
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();
if(isInsertMode)
_hexBox._byteProvider.InsertBytes(pos, new byte[]{(byte)c});
else
_hexBox._byteProvider.WriteByte(pos, (byte)c);
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 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 width and height of a single char
/// </summary>
SizeF _charSize;
/// <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 borders left shift
/// </summary>
int _recBorderLeft = SystemInformation.Border3DSize.Width;
/// <summary>
/// Contains the borders right shift
/// </summary>
int _recBorderRight = SystemInformation.Border3DSize.Width;
/// <summary>
/// Contains the borders 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;
#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 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 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;
#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);
BackColor = Color.White;
Font = new Font("Courier New", 9F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
_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.Enabled = true;
}
else
{
_vScrollBar.Enabled = 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>
/// 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)
{
InternalSelect(start, length);
ScrollByteIntoView();
}
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");
// define the caret width depending on InsertActive mode
int caretWidth = (this.InsertActive) ? 1 : (int)_charSize.Width;
int caretHeight = (int)_charSize.Height;
NativeMethods.CreateCaret(Handle, IntPtr.Zero, caretWidth, caretHeight);
UpdateCaret();
NativeMethods.ShowCaret(Handle);
_caretVisible = true;
}
void UpdateCaret()
{
if(_byteProvider == null || _keyInterpreter == null )
return;
System.Diagnostics.Debug.WriteLine("UpdateCaret()", "HexBox");
long byteIndex =_bytePos - _startByte;
PointF p = _keyInterpreter.GetCaretPointF(byteIndex);
p.X += _byteCharacterPos*_charSize.Width;
NativeMethods.SetCaretPos((int)p.X, (int)p.Y);
}
void DestroyCaret()
{
if(!_caretVisible)
return;
System.Diagnostics.Debug.WriteLine("DestroyCaret()", "HexBox");
NativeMethods.DestroyCaret();
_caretVisible = false;
}
void SetCaretPosition(Point p)
{
System.Diagnostics.Debug.WriteLine("SetCaretPosition()", "HexBox");
if(_byteProvider == null || _keyInterpreter == null)
return;
long pos = _bytePos;
int cp = _byteCharacterPos;
if(_recHex.Contains(p))
{
BytePositionInfo bpi = GetHexBytePositionInfo(p);
pos = bpi.Index;
cp = bpi.CharacterPosition;
SetPosition(pos, cp);
ActivateKeyInterpreter();
UpdateCaret();
Invalidate();
}
else if(_recStringView.Contains(p))
{
BytePositionInfo bpi = GetStringBytePositionInfo(p);
pos = bpi.Index;
cp = bpi.CharacterPosition;
SetPosition(pos, cp);
ActivateStringKeyInterpreter();
UpdateCaret();
Invalidate();
}
}
BytePositionInfo GetHexBytePositionInfo(Point p)
{
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;
int hPos = (iX / 3 + 1);
bytePos = Math.Min(_byteProvider.Length,
_startByte + (_iHexMaxHBytes * (iY+1) - _iHexMaxHBytes) + hPos - 1);
byteCharaterPos = (iX % 3);
if(byteCharaterPos > 1)
byteCharaterPos = 1;
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 x = ((float)(p.X - _recStringView.X) / _charSize.Width);
float y = ((float)(p.Y - _recStringView.Y) / _charSize.Height);
int iX = (int)x;
int iY = (int)y;
int hPos = iX+1;
bytePos = Math.Min(_byteProvider.Length,
_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
#region Find methods
/// <summary>
/// Searches the current ByteProvider
/// </summary>
/// <param name="bytes">the array of bytes to find</param>
/// <param name="startIndex">the start index</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(byte[] bytes, long startIndex)
{
int match = 0;
int bytesLength = bytes.Length;
_abortFind = false;
for(long pos = startIndex; pos < _byteProvider.Length; pos++)
{
if(_abortFind)
return -2;
if(pos % 1000 == 0) // for performance reasons: DoEvents only 1 times per 1000 loops
Application.DoEvents();
if(_byteProvider.ReadByte(pos) != bytes[match])
{
pos -= match;
match = 0;
_findingPos = pos;
continue;
}
match++;
if(match == bytesLength)
{
long bytePos = pos-bytesLength+1;
Select(bytePos, bytesLength);
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 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;
if(_selectionLength > 0)
_byteProvider.DeleteBytes(_bytePos, _selectionLength);
byte[] buffer = null;
IDataObject da = Clipboard.GetDataObject();
if(da.GetDataPresent("BinaryData"))
{
System.IO.MemoryStream ms = (System.IO.MemoryStream)da.GetData("BinaryData");
buffer = new byte[ms.Length];
ms.Read(buffer, 0, buffer.Length);
}
else if(da.GetDataPresent(typeof(string)))
{
string sBuffer = (string)da.GetData(typeof(string));
buffer = System.Text.Encoding.ASCII.GetBytes(sBuffer);
}
else
{
return;
}
_byteProvider.InsertBytes(_bytePos, buffer);
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;
if(_byteProvider == null || !_byteProvider.SupportsInsertBytes())
return false;
if(!_byteProvider.SupportsDeleteBytes() && _selectionLength > 0)
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;
}
}
}
/// <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;
// 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();
if(_lineInfoVisible)
PaintLineInfo(e.Graphics, _startByte, _endByte);
if(!_stringViewVisible)
{
PaintHex(e.Graphics, _startByte, _endByte);
}
else
{
PaintHexAndStringView(e.Graphics, _startByte, _endByte);
if(_shadowSelectionVisible)
PaintCurrentBytesSign(e.Graphics);
}
}
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.LineInfoForeColor != Color.Empty) ? this.LineInfoForeColor : this.ForeColor;
Brush brush = new SolidBrush(lineInfoColor);
int maxLine = GetGridBytePoint(endByte-startByte).Y+1;
for(int i = 0; i < maxLine; i++)
{
long firstLineByte = startByte + (_iHexMaxHBytes)*i;
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', 8-info.Length) + info;
}
else
{
formattedInfo = new string('~', 8);
}
g.DrawString(formattedInfo, Font, brush, new PointF(_recLineInfo.X, bytePointF.Y), _stringFormat);
}
}
void PaintHex(Graphics g, long startByte, long endByte)
{
Brush brush = new SolidBrush(GetDefaultForeColor());
Brush selBrush = new SolidBrush(_selectionForeColor);
Brush selBrushBack = new SolidBrush(_selectionBackColor);
int counter = -1;
long intern_endByte = Math.Min(_byteProvider.Length-1, endByte+_iHexMaxHBytes);
bool isKeyInterpreterActive = _keyInterpreter == null || _keyInterpreter.GetType() == typeof(KeyInterpreter);
for(long i = startByte; i < intern_endByte+1; i++)
{
counter++;
Point gridPoint = GetGridBytePoint(counter);
byte b = _byteProvider.ReadByte(i);
bool isSelectedByte = i >= _bytePos && i <= (_bytePos + _selectionLength-1) && _selectionLength != 0;
if(isSelectedByte && isKeyInterpreterActive)
{
PaintHexStringSelected(g, b, selBrush, selBrushBack, gridPoint);
}
else
{
PaintHexString(g, b, brush, gridPoint);
}
}
}
void PaintHexString(Graphics g, byte b, Brush brush, Point gridPoint)
{
PointF bytePointF = GetBytePointF(gridPoint);
string sB = ConvertByteToHex(b);
g.DrawString(sB.Substring(0,1), Font, brush, bytePointF, _stringFormat);
bytePointF.X += _charSize.Width;
g.DrawString(sB.Substring(1,1), Font, brush, bytePointF, _stringFormat);
}
void PaintHexStringSelected(Graphics g, byte b, Brush brush, Brush brushBack, Point gridPoint)
{
string sB = b.ToString(_hexStringFormat, System.Threading.Thread.CurrentThread.CurrentCulture);
if(sB.Length == 1)
sB = "0" + sB;
PointF bytePointF = GetBytePointF(gridPoint);
bool isLastLineChar = (gridPoint.X+1 == _iHexMaxHBytes);
float bcWidth = (isLastLineChar) ? _charSize.Width*2 : _charSize.Width*3;
g.FillRectangle(brushBack, bytePointF.X, bytePointF.Y, bcWidth, _charSize.Height);
g.DrawString(sB.Substring(0,1), Font, brush, bytePointF, _stringFormat);
bytePointF.X += _charSize.Width;
g.DrawString(sB.Substring(1,1), Font, brush, bytePointF, _stringFormat);
}
void PaintHexAndStringView(Graphics g, long startByte, long endByte)
{
Brush brush = new SolidBrush(GetDefaultForeColor());
Brush selBrush = new SolidBrush(_selectionForeColor);
Brush selBrushBack = new SolidBrush(_selectionBackColor);
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);
for(long i = startByte; i < intern_endByte+1; i++)
{
counter++;
Point gridPoint = GetGridBytePoint(counter);
PointF byteStringPointF = GetByteStringPointF(gridPoint);
byte b = _byteProvider.ReadByte(i);
bool isSelectedByte = i >= _bytePos && i <= (_bytePos + _selectionLength-1) && _selectionLength != 0;
if(isSelectedByte && isKeyInterpreterActive)
{
PaintHexStringSelected(g, b, selBrush, selBrushBack, gridPoint);
}
else
{
PaintHexString(g, b, brush, gridPoint);
}
string s;
if(b > 0x1F && !(b > 0x7E && b < 0xA0) )
{
s = ((char)b).ToString();
}
else
{
s = ".";
}
if(isSelectedByte && isStringKeyInterpreterActive)
{
g.FillRectangle(selBrushBack, byteStringPointF.X, byteStringPointF.Y, _charSize.Width, _charSize.Height);
g.DrawString(s, Font, selBrush, byteStringPointF, _stringFormat);
}
else
{
g.DrawString(s, Font, brush, byteStringPointF, _stringFormat);
}
}
}
void PaintCurrentBytesSign(Graphics g)
{
if(_keyInterpreter != null && Focused && _bytePos != -1 && Enabled)
{
if(_keyInterpreter.GetType() == typeof(KeyInterpreter))
{
if(_selectionLength == 0)
{
Point gp = GetGridBytePoint(_bytePos - _startByte);
PointF pf = GetByteStringPointF(gp);
Size s = new Size((int)_charSize.Width, (int)_charSize.Height);
Rectangle r = new Rectangle((int)pf.X, (int)pf.Y, s.Width, s.Height);
if(r.IntersectsWith(_recStringView))
{
r.Intersect(_recStringView);
PaintCurrentByteSign(g, r);
}
}
else
{
int lineWidth = (int)(_recStringView.Width-_charSize.Width);
Point startSelGridPoint = GetGridBytePoint(_bytePos-_startByte);
PointF startSelPointF = GetByteStringPointF(startSelGridPoint);
Point endSelGridPoint = GetGridBytePoint(_bytePos-_startByte+_selectionLength-1);
PointF endSelPointF = GetByteStringPointF(endSelGridPoint);
int multiLine = endSelGridPoint.Y - startSelGridPoint.Y;
if(multiLine == 0)
{
Rectangle singleLine = new Rectangle(
(int)startSelPointF.X,
(int)startSelPointF.Y,
(int)(endSelPointF.X-startSelPointF.X+_charSize.Width),
(int)_charSize.Height);
if(singleLine.IntersectsWith(_recStringView))
{
singleLine.Intersect(_recStringView);
PaintCurrentByteSign(g, singleLine);
}
}
else
{
Rectangle firstLine = new Rectangle(
(int)startSelPointF.X,
(int)startSelPointF.Y,
(int)(_recStringView.X+lineWidth-startSelPointF.X+_charSize.Width),
(int)_charSize.Height);
if(firstLine.IntersectsWith(_recStringView))
{
firstLine.Intersect(_recStringView);
PaintCurrentByteSign(g, firstLine);
}
if(multiLine > 1)
{
Rectangle betweenLines = new Rectangle(
_recStringView.X,
(int)(startSelPointF.Y+_charSize.Height),
(int)(_recStringView.Width),
(int)(_charSize.Height*(multiLine-1)));
if(betweenLines.IntersectsWith(_recStringView))
{
betweenLines.Intersect(_recStringView);
PaintCurrentByteSign(g, betweenLines);
}
}
Rectangle lastLine = new Rectangle(
_recStringView.X,
(int)endSelPointF.Y,
(int)(endSelPointF.X-_recStringView.X+_charSize.Width),
(int)_charSize.Height);
if(lastLine.IntersectsWith(_recStringView))
{
lastLine.Intersect(_recStringView);
PaintCurrentByteSign(g, lastLine);
}
}
}
}
else
{
if(_selectionLength == 0)
{
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);
}
else
{
int lineWidth = (int)(_recHex.Width-_charSize.Width*5);
Point startSelGridPoint = GetGridBytePoint(_bytePos-_startByte);
PointF startSelPointF = GetBytePointF(startSelGridPoint);
Point endSelGridPoint = GetGridBytePoint(_bytePos-_startByte+_selectionLength-1);
PointF endSelPointF = GetBytePointF(endSelGridPoint);
int multiLine = endSelGridPoint.Y - startSelGridPoint.Y;
if(multiLine == 0)
{
Rectangle singleLine = new Rectangle(
(int)startSelPointF.X,
(int)startSelPointF.Y,
(int)(endSelPointF.X-startSelPointF.X+_charSize.Width*2),
(int)_charSize.Height);
if(singleLine.IntersectsWith(_recHex))
{
singleLine.Intersect(_recHex);
PaintCurrentByteSign(g, singleLine);
}
}
else
{
Rectangle firstLine = new Rectangle(
(int)startSelPointF.X,
(int)startSelPointF.Y,
(int)(_recHex.X+lineWidth-startSelPointF.X+_charSize.Width*2),
(int)_charSize.Height);
if(firstLine.IntersectsWith(_recHex))
{
firstLine.Intersect(_recHex);
PaintCurrentByteSign(g, firstLine);
}
if(multiLine > 1)
{
Rectangle betweenLines = new Rectangle(
_recHex.X,
(int)(startSelPointF.Y+_charSize.Height),
(int)(lineWidth+_charSize.Width*2),
(int)(_charSize.Height*(multiLine-1)));
if(betweenLines.IntersectsWith(_recHex))
{
betweenLines.Intersect(_recHex);
PaintCurrentByteSign(g, betweenLines);
}
}
Rectangle lastLine = new Rectangle(
_recHex.X,
(int)endSelPointF.Y,
(int)(endSelPointF.X-_recHex.X+_charSize.Width*2),
(int)_charSize.Height);
if(lastLine.IntersectsWith(_recHex))
{
lastLine.Intersect(_recHex);
PaintCurrentByteSign(g, lastLine);
}
}
}
}
}
}
void PaintCurrentByteSign(Graphics g, Rectangle rec)
{
// stack overflowexception on big files - workaround
if(rec.Top < 0 || rec.Left < 0 || rec.Width <= 0 || rec.Height <= 0)
return;
Bitmap myBitmap = new Bitmap(rec.Width, rec.Height);
Graphics bitmapGraphics = Graphics.FromImage(myBitmap);
SolidBrush greenBrush = new SolidBrush(_shadowSelectionColor);
bitmapGraphics.FillRectangle(greenBrush, 0,
0, rec.Width, rec.Height);
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.GammaCorrected;
g.DrawImage(myBitmap, rec.Left, rec.Top);
}
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
#region Positioning methods
void UpdateRectanglePositioning()
{
// calc char size
SizeF charSize = this.CreateGraphics().MeasureString("A", Font, 100, _stringFormat);
_charSize = new SizeF((float)Math.Ceiling(charSize.Width), (float)Math.Ceiling(charSize.Height));
// 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;
}
int marginLeft = 4;
// calc line info bounds
if(_lineInfoVisible)
{
_recLineInfo = new Rectangle(_recContent.X+marginLeft,
_recContent.Y,
(int)(_charSize.Width*10),
_recContent.Height);
}
else
{
_recLineInfo = Rectangle.Empty;
_recLineInfo.X = marginLeft;
}
// calc hex bounds and grid
_recHex = new Rectangle(_recLineInfo.X + _recLineInfo.Width,
_recLineInfo.Y,
_recContent.Width - _recLineInfo.Width,
_recContent.Height);
if(UseFixedBytesPerLine)
{
SetHorizontalByteCount(_bytesPerLine);
_recHex.Width = (int)Math.Floor(((double)_iHexMaxHBytes)*_charSize.Width*3+(2*_charSize.Width));
}
else
{
int hmax = (int)Math.Floor((double)_recHex.Width/(double)_charSize.Width);
if(hmax > 1)
SetHorizontalByteCount((int)Math.Floor((double)hmax/3));
else
SetHorizontalByteCount(hmax);
}
if(_stringViewVisible)
{
_recStringView = new Rectangle(_recHex.X + _recHex.Width,
_recHex.Y,
(int)(_charSize.Width*_iHexMaxHBytes),
_recHex.Height);
}
else
{
_recStringView = Rectangle.Empty;
}
int vmax = (int)Math.Floor((double)_recHex.Height/(double)_charSize.Height);
SetVerticalByteCount(vmax);
_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 GetByteStringPointF(Point gp)
{
float x = (_charSize.Width) * gp.X + _recStringView.X;
float y = (gp.Y+1)*_charSize.Height-_charSize.Height+_recStringView.Y;
return new PointF(x,y);
}
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>
[Editor(typeof(HexFontEditor), typeof(System.Drawing.Design.UITypeEditor))]
public override Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
}
}
/// <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
/// <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>
/// UsedFixedBytesPerLine property must set to true
/// </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;
OnByteProviderChanged(EventArgs.Empty);
UpdateRectanglePositioning();
Invalidate();
}
} int _bytesPerLine = 16;
/// <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
{
if(_byteProvider == value)
return;
if(value == null)
ActivateEmptyKeyInterpreter();
else
ActivateKeyInterpreter();
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
{
SetPosition(0, 0);
SetSelectionLength(0);
if(_caretVisible && Focused)
UpdateCaret();
else
CreateCaret();
}
CheckCurrentLineChanged();
CheckCurrentPositionInLineChanged();
_scrollVpos = 0;
UpdateVisibilityBytes();
UpdateRectanglePositioning();
Invalidate();
}
} IByteProvider _byteProvider;
/// <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;
/// <summary>
/// Gets or sets the hex boxs border style.
/// </summary>
[DefaultValue(typeof(BorderStyle), "Fixed3D"), Category("Hex"), Description("Gets or sets the hex boxs 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 line info color. When this property is null, then ForeColor property is used.
/// </summary>
[DefaultValue(typeof(Color), "Empty"), Category("Hex"), Description("Gets or sets the line info color. When this property is null, then ForeColor property is used.")]
public Color LineInfoForeColor
{
get { return _lineInfoForeColor; }
set { _lineInfoForeColor = value; Invalidate(); }
} Color _lineInfoForeColor = Color.Empty;
/// <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>
/// 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);
}
}
#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)
{
if(_byteCharacterPos != byteCharacterPos)
{
_byteCharacterPos = byteCharacterPos;
}
if(bytePos != _bytePos)
{
_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 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 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();
SetCaretPosition(new Point(e.X, e.Y));
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)
{
int linesToScroll = -(e.Delta * SystemInformation.MouseWheelScrollLines / 120);
this.PerformScrollLines(linesToScroll);
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);
DestroyCaret();
}
void _byteProvider_LengthChanged(object sender, EventArgs e)
{
UpdateScrollSize();
}
#endregion
}
}
|