Mesen-X/GUI.NET/Debugger/HexBox/HexBox.cs

4245 lines
114 KiB
C#
Raw Permalink Blame History

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;
using System.Text.RegularExpressions;
using System.Drawing.Imaging;
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)
{
return PerformPosMoveRight(true);
}
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();
_hexBox.Invalidate();
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();
_hexBox.Invalidate();
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 {
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);
}
}
PerformPosMoveRight(false);
_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;
if(_hexBox.EnablePerByteNavigation) {
pos = Math.Max(0, pos - 1);
cp = 0;
} else {
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(bool allowNibbleSkip)
{
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 || (allowNibbleSkip && _hexBox.EnablePerByteNavigation)) {
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();
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();
ActivateKeyInterpreter();
}
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)
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;
PointF p = _keyInterpreter.GetCaretPointF(byteIndex);
p.X += _byteCharacterPos * _charSize.Width;
if(HighDensityMode) {
p.Y += _highDensityModeOffset;
}
_caretPos = p;
}
void DestroyCaret()
{
//Never hide caret
return;
}
internal BytePositionInfo? GetRestrictedHexBytePositionInfo(Point p)
{
if(_recHex.Contains(p) && p.X < _recHex.Left + (HorizontalByteCount * 3 - 1) * _charSize.Width) {
//Only return the position if the mouse is over a hex digit
return GetHexBytePositionInfo(p);
}
return null;
}
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;
}
void SetCaretPosition(Point p)
{
System.Diagnostics.Debug.WriteLine("SetCaretPosition()", "HexBox");
if (_byteProvider == null || _keyInterpreter == null)
return;
long pos = _bytePos;
int cp = _byteCharacterPos;
CreateCaret();
if (_recHex.Contains(p))
{
BytePositionInfo bpi = GetHexBytePositionInfo(p);
pos = bpi.Index;
cp = ByteEditingMode ? 0 : bpi.CharacterPosition;
SetPosition(pos, cp);
ActivateKeyInterpreter();
UpdateCaret();
Invalidate();
}
else if (_recStringView.Contains(p) || p.X > _recStringView.Right)
{
BytePositionInfo bpi = GetStringBytePositionInfo(p);
pos = bpi.Index;
cp = bpi.CharacterPosition;
SetPosition(pos, cp);
ActivateStringKeyInterpreter();
UpdateCaret();
Invalidate();
}
}
internal 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 iY = (int)y;
//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;
bytePos = Math.Min(_byteProvider.Length,
_startByte + (_iHexMaxHBytes * (iY + 1) - _iHexMaxHBytes) + hPos - 1);
if(pos - Math.Floor(pos) > 0.5f) {
byteCharaterPos = 1;
} else {
byteCharaterPos = 0;
}
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;
int hPos = 0;
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;
}
}
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
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;
}
options.UpdateFindBuffer(this.ByteCharConverter);
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;
}
private Regex _nonHexRegex = new Regex("[^A-Fa-f0-9]", RegexOptions.Compiled);
/// <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();
if(da != null && 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 != null && da.GetDataPresent(typeof(string))) {
string sBuffer = (string)da.GetData(typeof(string));
if(_keyInterpreter.GetType() == typeof(StringKeyInterpreter)) {
//When pasting in text view, assume any string is text
buffer = System.Text.Encoding.ASCII.GetBytes(sBuffer);
} else {
//When pasting in the hex view, assume any string is hex
string hexString = _nonHexRegex.Replace(sBuffer, "");
if(hexString.Length % 2 != 0) {
hexString = "0" + hexString;
}
buffer = new byte[hexString.Length / 2];
for(int i = 0; i < hexString.Length; i += 2) {
buffer[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
}
}
} else {
return;
}
_byteProvider.WriteBytes(_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;
IDataObject da = Clipboard.GetDataObject();
if(da == null) {
return false;
}
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 == null) {
return false;
}
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 != null && 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);
e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.Default;
UpdateVisibilityBytes();
if(_caretVisible && _keyInterpreter.GetType() != typeof(StringKeyInterpreter)) {
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);
}
PaintHexAndStringView(e.Graphics, _startByte, _endByte);
if (_shadowSelectionVisible && _stringViewVisible)
PaintCurrentBytesSign(e.Graphics);
if(_lineInfoVisible)
PaintLineInfo(e.Graphics, _startByte, _endByte);
if(_columnInfoVisible)
PaintHeaderRow(e.Graphics);
if(_groupSeparatorVisible)
PaintColumnSeparator(e.Graphics);
if(_caretVisible && _keyInterpreter.GetType() != typeof(StringKeyInterpreter)) {
float caretWidth = (this.InsertActive) ? 1 : _charSize.Width;
float caretHeight = _charSize.Height;
e.Graphics.DrawRectangle(Pens.Gray, _caretPos.X - 1, _caretPos.Y, caretWidth, caretHeight);
}
}
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);
Point gp = GetGridBytePoint(_bytePos - _startByte);
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);
}
if(gp.Y == i && _highlightCurrentRowColumn && !(_keyInterpreter is StringKeyInterpreter)) {
using(SolidBrush highlightBrush = new SolidBrush(Color.FromArgb(15, 0, 0, 0))) {
g.FillRectangle(highlightBrush, _recHex.X, bytePointF.Y, _recHex.Width - (int)(_charSize.Width*2.5), _charSize.Height);
}
g.FillRectangle(Brushes.White, _recLineInfo.X - 4, bytePointF.Y, _recLineInfo.Width, _charSize.Height);
}
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);
if(_highlightCurrentRowColumn && !(_keyInterpreter is StringKeyInterpreter)) {
Point gp = GetGridBytePoint(_bytePos - _startByte);
PointF bytePointF = GetBytePointF(gp);
float columnLeft = _recColumnInfo.Left + _charSize.Width * gp.X * 3 - _charSize.Width / 2;
using(SolidBrush highlightBrush = new SolidBrush(Color.FromArgb(15, 0, 0, 0))) {
g.FillRectangle(highlightBrush, columnLeft, _recHex.Y, _charSize.Width * 3, bytePointF.Y - _recHex.Y);
g.FillRectangle(highlightBrush, columnLeft, bytePointF.Y + _charSize.Height, _charSize.Width * 3, _recHex.Height - (bytePointF.Y - _recHex.Y) - _charSize.Height);
}
g.FillRectangle(Brushes.White, columnLeft, 0, _charSize.Width * 3, _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));
}
}
}
void PaintHexString(Graphics g, string hexString, Brush brush, Point gridPoint)
{
PointF bytePointF = GetBytePointF(gridPoint);
g.DrawString(hexString, Font, brush, bytePointF, _stringFormat);
}
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);
}
void PaintHexStringSelected(Graphics g, string hexString, Brush brush, Brush brushBack, Point gridPoint, Color borderColor)
{
PointF bytePointF = GetBytePointF(gridPoint);
float width = hexString.Length * _charSize.Width;
float xPos = bytePointF.X - _charSize.Width / 2;
float offset = HighDensityMode ? _highDensityModeOffset : 0;
g.FillRectangle(brushBack, xPos, bytePointF.Y + offset, width, _charSize.Height);
if(!borderColor.IsEmpty) {
using(Pen p = new Pen(borderColor, 2)) {
g.DrawRectangle(p, xPos, bytePointF.Y + offset, width - 1, _charSize.Height - 1);
}
}
if(_selectionLength == 0 && _caretPos.Y == bytePointF.Y && _caretPos.X >= bytePointF.X && _caretPos.X <= bytePointF.X + width) {
if(_caretVisible && _keyInterpreter.GetType() != typeof(StringKeyInterpreter)) {
//Redraw caret over background color
float caretWidth = (this.InsertActive) ? 1 : _charSize.Width;
float caretHeight = (int)_charSize.Height;
g.FillRectangle(Brushes.Yellow, _caretPos.X - 1, _caretPos.Y, caretWidth, caretHeight);
}
}
g.DrawString(hexString, Font, brush, bytePointF, _stringFormat);
}
Dictionary<int, float> _lineWidthCache = new Dictionary<int, float>();
void PaintHexAndStringView(Graphics g, long startByte, long endByte)
{
int counter = -1;
long intern_endByte = Math.Min(_byteProvider.Length - 1, endByte + _iHexMaxHBytes);
int yPrevious = -1;
float xOffset = 0;
Color defaultForeColor = this.ForeColor;
_lineWidthCache.Clear();
for(int i = 0; i < _xPosCache.Length; i++) {
_xPosCache[i] = -1;
}
_xPosList.Clear();
float xPrevious = 0;
Point? caretPoint = null;
if(_caretVisible && _keyInterpreter.GetType() == typeof(StringKeyInterpreter)) {
long byteIndex = _bytePos - _startByte;
caretPoint = GetGridBytePoint(byteIndex);
}
int skipCount = 0;
if(this.ByteColorProvider != null) {
this.ByteColorProvider.Prepare(_startByte, intern_endByte);
}
bool forceDraw = false;
float prevXOffset = 0;
bool prevSelected = false;
ByteColors defaultColors = new ByteColors() {
ForeColor = defaultForeColor,
BackColor = Color.Transparent,
BorderColor = Color.Transparent
};
ByteColors prevColors = new ByteColors() {
ForeColor = defaultForeColor,
BackColor = Color.Transparent,
BorderColor = Color.Transparent
};
Point gridPoint = GetGridBytePoint(0);
List<byte> bytesToDisplay = new List<byte>();
string stringToDisplay = string.Empty;
float caretWidth = 0;
Action<Color, Color, Color> outputHex = (Color byteColor, Color bgColor, Color borderColor) => {
StringBuilder sb = new StringBuilder();
foreach(byte b in bytesToDisplay) {
sb.Append(b.ToString("X2"));
sb.Append(" ");
}
PointF bytePointF = GetBytePointF(gridPoint);
PointF byteStringPointF = GetByteStringPointF(gridPoint, false);
float xPos = byteStringPointF.X + prevXOffset;
//String view caret
Action drawCaret = () => {
float yPos = bytePointF.Y + (HighDensityMode ? _highDensityModeOffset : 0);
if(_selectionLength == 0 && _caretPos.Y == yPos && _caretPos.X >= xPos) {
if(_caretVisible && _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);
}
}
};
using(Brush fgBrush = new SolidBrush(byteColor)) {
if(bgColor != Color.Transparent || !borderColor.IsEmpty) {
using(Brush bgBrush = new SolidBrush(bgColor)) {
PaintHexStringSelected(g, sb.ToString(), fgBrush, bgBrush, gridPoint, borderColor);
//Draw string view
SizeF stringSize = g.MeasureString(stringToDisplay, Font, 1000, _stringFormat);
float yPos = bytePointF.Y + (HighDensityMode ? _highDensityModeOffset : 0);
g.FillRectangle(bgBrush, xPos, yPos, stringSize.Width, _charSize.Height);
using(Pen p = new Pen(borderColor, 2)) {
g.DrawRectangle(p, xPos, yPos, stringSize.Width - 1, _charSize.Height - 1);
}
drawCaret();
g.DrawString(stringToDisplay, Font, fgBrush, new PointF(xPos, bytePointF.Y), _stringFormat);
}
} else {
PaintHexString(g, sb.ToString(), fgBrush, gridPoint);
//Draw string view
drawCaret();
g.DrawString(stringToDisplay, Font, fgBrush, new PointF(xPos, bytePointF.Y), _stringFormat);
}
}
bytesToDisplay = new List<byte>();
stringToDisplay = string.Empty;
};
for(long i = startByte; i < intern_endByte + 1; i++) {
ByteColors colors;
bool isSelectedByte = i >= _bytePos && i <= (_bytePos + _selectionLength - 1) && _selectionLength != 0;
if(this.ByteColorProvider != null) {
colors = this.ByteColorProvider.GetByteColor(_startByte, i);
} else {
colors = defaultColors;
}
counter++;
Point currentPoint = GetGridBytePoint(counter);
bool lineChanged = false;
if(yPrevious != currentPoint.Y) {
if(_xPosList.ContainsKey(yPrevious)) {
_xPosList[yPrevious].Add(xPrevious);
}
_xPosList[currentPoint.Y] = new List<float>();
_lineWidthCache[yPrevious] = _recStringView.Width + xOffset;
xOffset = 0;
yPrevious = currentPoint.Y;
lineChanged = true;
}
if(forceDraw || lineChanged || colors.ForeColor != prevColors.ForeColor || colors.BackColor != prevColors.BackColor || colors.BorderColor != prevColors.BorderColor || prevSelected != isSelectedByte) {
outputHex(this.ByteColorProvider != null ? prevColors.ForeColor : (prevSelected ? _selectionForeColor : defaultForeColor), prevSelected ? _selectionBackColor : prevColors.BackColor, prevColors.BorderColor);
gridPoint = GetGridBytePoint(counter);
prevXOffset = xOffset;
forceDraw = false;
}
byte b = _byteProvider.ReadByte(i);
bytesToDisplay.Add(b);
prevSelected = isSelectedByte;
prevColors.ForeColor = colors.ForeColor;
prevColors.BackColor = colors.BackColor;
prevColors.BorderColor = colors.BorderColor;
if(!_stringViewVisible) {
continue;
}
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);
}
}
int keyLength;
s = ByteCharConverter.ToChar(tblValue, out keyLength);
skipCount = keyLength - 1;
}
float width;
if(!_measureCache.TryGetValue(s, out width)) {
width = g.MeasureString(s, Font, 1000, _stringFormat).Width;
_measureCache[s] = width;
}
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);
if(currentPoint == caretPoint) {
caretWidth = width;
}
stringToDisplay += s;
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;
}
xOffset += width - _charSize.Width;
xPrevious = xPos + width;
}
outputHex(this.ByteColorProvider != null ? prevColors.ForeColor : (prevSelected ? _selectionForeColor : defaultForeColor), prevSelected ? _selectionBackColor : prevColors.BackColor, prevColors.BorderColor);
}
float GetLineWidth(int y)
{
float width;
if(_lineWidthCache.TryGetValue(y, out width)) {
return width;
} else {
return _recStringView.Width;
}
}
void PaintCurrentBytesSign(Graphics g)
{
if(_selectionLength == 0 && _keyInterpreter != null && _bytePos != -1 && Enabled) {
if(_keyInterpreter.GetType() == typeof(KeyInterpreter)) {
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);
} else {
s = new Size((int)(pf2.X - pf.X), (int)_charSize.Height);
}
Rectangle r = new Rectangle((int)pf.X, (int)pf.Y, s.Width, s.Height);
PaintCurrentByteSign(g, r);
} else {
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);
}
}
}
Dictionary<Point, Bitmap> _shadowSelectionCache = new Dictionary<Point, Bitmap>();
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;
}
Point dimensions = new Point(rec.Width, rec.Height);
Bitmap bitmap;
if(!_shadowSelectionCache.TryGetValue(dimensions, out bitmap)) {
bitmap = new Bitmap(rec.Width, rec.Height, PixelFormat.Format32bppPArgb);
using(Graphics bitmapGraphics = Graphics.FromImage(bitmap)) {
using(SolidBrush shadowSelectionBrush = new SolidBrush(_shadowSelectionColor)) {
bitmapGraphics.FillRectangle(shadowSelectionBrush, 0, 0, rec.Width, rec.Height);
_shadowSelectionCache[dimensions] = bitmap;
}
}
}
g.DrawImage(bitmap, 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
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);
}
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;
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);
requiredWidth += _recStringView.Width + 40;
}
else
{
_recStringView = Rectangle.Empty;
}
RequiredWidth = requiredWidth;
int vmax = (int)Math.Floor((double)_recHex.Height / (double)_charSize.Height);
SetVerticalByteCount(vmax > 0 ? vmax : 1);
_iHexMaxBytes = _iHexMaxHBytes * _iHexMaxVBytes;
UpdateScrollSize();
}
public Point GetBytePosition(long byteIndex)
{
if(byteIndex < _startByte) {
return Point.Empty;
}
Point gp = GetGridBytePoint(byteIndex - _startByte);
PointF pos = GetBytePointF(gp);
return this.PointToScreen(new Point((int)pos.X, (int)pos.Y));
}
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);
}
float[] _xPosCache = new float[64 * 150];
Dictionary<int, List<float>> _xPosList = new Dictionary<int, List<float>>();
PointF GetByteStringPointF(Point gp, bool useCache = true)
{
float x = (_charSize.Width) * gp.X + _recStringView.X;
float y = (gp.Y + 1) * _charSize.Height - _charSize.Height + _recStringView.Y;
float cachedXPos = gp.Y > 0 && gp.Y < 150 ? _xPosCache[gp.Y * 64 + gp.X] : -1;
if(useCache && cachedXPos >= 0) {
return new PointF(cachedXPos, y);
} else {
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>
public override Font Font
{
get
{
return base.Font;
}
set
{
if(value == null)
return;
base.Font = value;
this.UpdateRectanglePositioning();
this.UpdateCaret();
this.Invalidate();
_measureCache = new Dictionary<string, float>();
}
}
Dictionary<string, float> _measureCache = new Dictionary<string, float>();
/// <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
public bool ByteEditingMode { get; set; } = false;
/// <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;
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
{
if(!sameLength) {
SetPosition(0, 0);
SetSelectionLength(0);
}
}
CheckCurrentLineChanged();
CheckCurrentPositionInLineChanged();
if(!sameLength) {
_scrollVpos = 0;
}
UpdateVisibilityBytes();
UpdateRectanglePositioning();
if(_caretVisible)
UpdateCaret();
else
CreateCaret();
Invalidate();
}
}
IByteProvider _byteProvider;
IByteColorProvider _byteColorProvider;
public IByteColorProvider ByteColorProvider
{
get { return _byteColorProvider; }
set { _byteColorProvider = value; Invalidate(); }
}
/// <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;
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool HighlightCurrentRowColumn
{
get { return _highlightCurrentRowColumn; }
set
{
_highlightCurrentRowColumn = value;
Invalidate();
}
}
bool _highlightCurrentRowColumn;
/// <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;
public bool HighDensityMode
{
get { return _highDensityMode; }
set
{
if (_highDensityMode == value)
return;
_highDensityMode = value;
this.UpdateCaret();
this.Invalidate();
this.UpdateRectanglePositioning();
}
} bool _highDensityMode;
float _highDensityModeOffset = 0f;
public bool EnablePerByteNavigation { get; set; }
/// <summary>
/// Gets the width required for the content
/// </summary>
[DefaultValue(0), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
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
{
if (value != _byteCharConverter)
{
_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)
{
if(bytePos >= this._byteProvider.Length) {
bytePos = this._byteProvider.Length - 1;
}
if (_byteCharacterPos != byteCharacterPos)
{
_byteCharacterPos = byteCharacterPos;
}
if (bytePos != _bytePos)
{
_byteProvider.CommitWriteByte();
_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();
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;
}
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)
{
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);
}
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);
if(_byteProvider != null) {
_byteProvider.CommitWriteByte();
}
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
}
}