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; namespace Be.Windows.Forms { /// /// Represents a hex box control. /// [ToolboxBitmap(typeof(HexBox), "HexBox.bmp")] public class HexBox : Control { #region IKeyInterpreter interface /// /// Defines a user input handler such as for mouse and keyboard input /// interface IKeyInterpreter { /// /// Activates mouse events /// void Activate(); /// /// Deactivate mouse events /// void Deactivate(); /// /// Preprocesses WM_KEYUP window message. /// /// the Message object to process. /// True, if the message was processed. bool PreProcessWmKeyUp(ref Message m); /// /// Preprocesses WM_CHAR window message. /// /// the Message object to process. /// True, if the message was processed. bool PreProcessWmChar(ref Message m); /// /// Preprocesses WM_KEYDOWN window message. /// /// the Message object to process. /// True, if the message was processed. bool PreProcessWmKeyDown(ref Message m); /// /// Gives some information about where to place the caret. /// /// the index of the byte /// the position where the caret is to place. PointF GetCaretPointF(long byteIndex); } #endregion #region EmptyKeyInterpreter class /// /// Represents an empty input handler without any functionality. /// If is set ByteProvider to null, then this interpreter is used. /// 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 /// /// Handles user input such as mouse and keyboard input during hex view edit /// class KeyInterpreter : IKeyInterpreter { /// /// Delegate for key-down processing. /// /// the message object contains key data information /// True, if the message was processed delegate bool MessageDelegate(ref Message m); #region Fields /// /// Contains the parent HexBox control /// protected HexBox _hexBox; /// /// Contains True, if shift key is down /// protected bool _shiftDown; /// /// Contains True, if mouse is down /// bool _mouseDown; /// /// Contains the selection start position info /// BytePositionInfo _bpiStart; /// /// Contains the current mouse selection position info /// BytePositionInfo _bpi; /// /// Contains all message handlers of key interpreter key down message /// Dictionary _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 MessageHandlers { get { if (_messageHandlers == null) { _messageHandlers = new Dictionary(); _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 /// /// Handles user input such as mouse and keyboard input during string view edit /// 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 /// /// Contains the hole content bounds of all text /// Rectangle _recContent; /// /// Contains the line info bounds /// Rectangle _recLineInfo; /// /// Contains the column info header rectangle bounds /// Rectangle _recColumnInfo; /// /// Contains the hex data bounds /// Rectangle _recHex; /// /// Contains the string view bounds /// Rectangle _recStringView; /// /// Contains string format information for text drawing /// StringFormat _stringFormat; /// /// Contains the maximum of visible horizontal bytes /// int _iHexMaxHBytes; /// /// Contains the maximum of visible vertical bytes /// int _iHexMaxVBytes; /// /// Contains the maximum of visible bytes. /// int _iHexMaxBytes; /// /// Contains the scroll bars minimum value /// long _scrollVmin; /// /// Contains the scroll bars maximum value /// long _scrollVmax; /// /// Contains the scroll bars current position /// long _scrollVpos; /// /// Contains a vertical scroll /// VScrollBar _vScrollBar; /// /// Contains a timer for thumbtrack scrolling /// Timer _thumbTrackTimer = new Timer(); /// /// Contains the thumbtrack scrolling position /// long _thumbTrackPosition; /// /// Contains the thumptrack delay for scrolling in milliseconds. /// const int THUMPTRACKDELAY = 50; /// /// Contains the Enviroment.TickCount of the last refresh /// int _lastThumbtrack; /// /// Contains the border´s left shift /// int _recBorderLeft = SystemInformation.Border3DSize.Width; /// /// Contains the border´s right shift /// int _recBorderRight = SystemInformation.Border3DSize.Width; /// /// Contains the border´s top shift /// int _recBorderTop = SystemInformation.Border3DSize.Height; /// /// Contains the border bottom shift /// int _recBorderBottom = SystemInformation.Border3DSize.Height; /// /// Contains the index of the first visible byte /// long _startByte; /// /// Contains the index of the last visible byte /// long _endByte; /// /// Contains the current byte position /// long _bytePos = -1; /// /// Contains the current char position in one byte /// /// /// "1A" /// "1" = char position of 0 /// "A" = char position of 1 /// int _byteCharacterPos; /// /// Contains string format information for hex values /// string _hexStringFormat = "X"; /// /// Contains the current key interpreter /// IKeyInterpreter _keyInterpreter; /// /// Contains an empty key interpreter without functionality /// EmptyKeyInterpreter _eki; /// /// Contains the default key interpreter /// KeyInterpreter _ki; /// /// Contains the string key interpreter /// StringKeyInterpreter _ski; /// /// Contains True if caret is visible /// bool _caretVisible; /// /// Contains true, if the find (Find method) should be aborted. /// bool _abortFind; /// /// Contains a value of the current finding position. /// long _findingPos; /// /// 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. /// bool _insertActive; PointF _caretPos; #endregion #region Events /// /// Occurs, when the value of InsertActive property has changed. /// [Description("Occurs, when the value of InsertActive property has changed.")] public event EventHandler InsertActiveChanged; /// /// Occurs, when the value of ReadOnly property has changed. /// [Description("Occurs, when the value of ReadOnly property has changed.")] public event EventHandler ReadOnlyChanged; /// /// Occurs, when the value of ByteProvider property has changed. /// [Description("Occurs, when the value of ByteProvider property has changed.")] public event EventHandler ByteProviderChanged; /// /// Occurs, when the value of SelectionStart property has changed. /// [Description("Occurs, when the value of SelectionStart property has changed.")] public event EventHandler SelectionStartChanged; /// /// Occurs, when the value of SelectionLength property has changed. /// [Description("Occurs, when the value of SelectionLength property has changed.")] public event EventHandler SelectionLengthChanged; /// /// Occurs, when the value of LineInfoVisible property has changed. /// [Description("Occurs, when the value of LineInfoVisible property has changed.")] public event EventHandler LineInfoVisibleChanged; /// /// Occurs, when the value of ColumnInfoVisibleChanged property has changed. /// [Description("Occurs, when the value of ColumnInfoVisibleChanged property has changed.")] public event EventHandler ColumnInfoVisibleChanged; /// /// Occurs, when the value of GroupSeparatorVisibleChanged property has changed. /// [Description("Occurs, when the value of GroupSeparatorVisibleChanged property has changed.")] public event EventHandler GroupSeparatorVisibleChanged; /// /// Occurs, when the value of StringViewVisible property has changed. /// [Description("Occurs, when the value of StringViewVisible property has changed.")] public event EventHandler StringViewVisibleChanged; /// /// Occurs, when the value of BorderStyle property has changed. /// [Description("Occurs, when the value of BorderStyle property has changed.")] public event EventHandler BorderStyleChanged; /// /// Occurs, when the value of ColumnWidth property has changed. /// [Description("Occurs, when the value of GroupSize property has changed.")] public event EventHandler GroupSizeChanged; /// /// Occurs, when the value of BytesPerLine property has changed. /// [Description("Occurs, when the value of BytesPerLine property has changed.")] public event EventHandler BytesPerLineChanged; /// /// Occurs, when the value of UseFixedBytesPerLine property has changed. /// [Description("Occurs, when the value of UseFixedBytesPerLine property has changed.")] public event EventHandler UseFixedBytesPerLineChanged; /// /// Occurs, when the value of VScrollBarVisible property has changed. /// [Description("Occurs, when the value of VScrollBarVisible property has changed.")] public event EventHandler VScrollBarVisibleChanged; /// /// Occurs, when the value of HexCasing property has changed. /// [Description("Occurs, when the value of HexCasing property has changed.")] public event EventHandler HexCasingChanged; /// /// Occurs, when the value of HorizontalByteCount property has changed. /// [Description("Occurs, when the value of HorizontalByteCount property has changed.")] public event EventHandler HorizontalByteCountChanged; /// /// Occurs, when the value of VerticalByteCount property has changed. /// [Description("Occurs, when the value of VerticalByteCount property has changed.")] public event EventHandler VerticalByteCountChanged; /// /// Occurs, when the value of CurrentLine property has changed. /// [Description("Occurs, when the value of CurrentLine property has changed.")] public event EventHandler CurrentLineChanged; /// /// Occurs, when the value of CurrentPositionInLine property has changed. /// [Description("Occurs, when the value of CurrentPositionInLine property has changed.")] public event EventHandler CurrentPositionInLineChanged; /// /// Occurs, when Copy method was invoked and ClipBoardData changed. /// [Description("Occurs, when Copy method was invoked and ClipBoardData changed.")] public event EventHandler Copied; /// /// Occurs, when CopyHex method was invoked and ClipBoardData changed. /// [Description("Occurs, when CopyHex method was invoked and ClipBoardData changed.")] public event EventHandler CopiedHex; /// /// Occurs, when the CharSize property has changed /// [Description("Occurs, when the CharSize property has changed")] public event EventHandler CharSizeChanged; /// /// Occurs, when the RequiredWidth property changes /// [Description("Occurs, when the RequiredWidth property changes")] public event EventHandler RequiredWidthChanged; #endregion #region Ctors /// /// Initializes a new instance of a HexBox class. /// 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); } /// /// Performs the thumbtrack scrolling after an delay. /// 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); } /// /// Scrolls the selection start byte into view /// public void ScrollByteIntoView() { System.Diagnostics.Debug.WriteLine("ScrollByteIntoView()", "HexBox"); ScrollByteIntoView(_bytePos); } /// /// Scrolls the specific byte into view /// /// the index of the byte 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(); } /// /// Returns true if Select method could be invoked. /// public bool CanSelectAll() { if (!this.Enabled) return false; if (_byteProvider == null) return false; return true; } /// /// Selects all bytes. /// public void SelectAll() { if (this.ByteProvider == null) return; this.Select(0, this.ByteProvider.Length); } /// /// Selects the hex box. /// /// the start index of the selection /// the length of the selection 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 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 /// /// Preprocesses windows messages. /// /// the message to process. /// true, if the message was processed [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 /// /// Searches the current ByteProvider /// /// contains all find options /// the SelectionStart property value if find was successfull or /// -1 if there is no match /// -2 if Find was aborted. 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; } /// /// Aborts a working Find method. /// public void AbortFind() { _abortFind = true; } /// /// Gets a value that indicates the current position during Find method execution. /// [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; } /// /// Copies the current selection in the hex box to the Clipboard. /// 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); } /// /// Return true if Copy method could be invoked. /// public bool CanCopy() { if (_selectionLength < 1 || _byteProvider == null) return false; return true; } /// /// Moves the current selection in the hex box to the Clipboard. /// public void Cut() { if (!CanCut()) return; Copy(); _byteProvider.DeleteBytes(_bytePos, _selectionLength); _byteCharacterPos = 0; UpdateCaret(); ScrollByteIntoView(); ReleaseSelection(); Invalidate(); Refresh(); } /// /// Return true if Cut method could be invoked. /// 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); /// /// Replaces the current selection in the hex box with the contents of the Clipboard. /// 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(); } /// /// Return true if Paste method could be invoked. /// 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; } /// /// Return true if PasteHex method could be invoked. /// 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; } /// /// Replaces the current selection in the hex box with the hex string data of the Clipboard. /// 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(); } /// /// Copies the current selection in the hex box to the Clipboard in hex format. /// 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 /// /// Paints the background. /// /// A PaintEventArgs that contains the event data. 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; } } } /// /// Paints the hex box. /// /// A PaintEventArgs that contains the event data. 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 _lineWidthCache = new Dictionary(); 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 bytesToDisplay = new List(); string stringToDisplay = string.Empty; float caretWidth = 0; Action 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(); 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(); _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 _shadowSelectionCache = new Dictionary(); 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); 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> _xPosList = new Dictionary>(); 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 /// /// Gets or sets the background color for the control. /// [DefaultValue(typeof(Color), "White")] public override Color BackColor { get { return base.BackColor; } set { base.BackColor = value; } } /// /// The font used to display text in the hexbox. /// 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(); } } Dictionary _measureCache = new Dictionary(); /// /// Not used. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] public override string Text { get { return base.Text; } set { base.Text = value; } } /// /// Not used. /// [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; /// /// Gets or sets the background color for the disabled control. /// [Category("Appearance"), DefaultValue(typeof(Color), "WhiteSmoke")] public Color BackColorDisabled { get { return _backColorDisabled; } set { _backColorDisabled = value; } } Color _backColorDisabled = Color.FromName("WhiteSmoke"); /// /// Gets or sets if the count of bytes in one line is fix. /// /// /// When set to True, BytesPerLine property determine the maximum count of bytes in one line. /// [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; /// /// Gets or sets the maximum count of bytes in one line. /// /// /// UseFixedBytesPerLine property no longer has to be set to true for this to work /// [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; /// /// Gets or sets the number of bytes in a group. Used to show the group separator line (if GroupSeparatorVisible is true) /// /// /// GroupSeparatorVisible property must set to true /// [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; /// /// Gets or sets if the count of bytes in one line is fix. /// /// /// When set to True, BytesPerLine property determine the maximum count of bytes in one line. /// [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; /// /// Gets or sets the visibility of a vertical scroll bar. /// [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; /// /// Gets or sets the ByteProvider. /// [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(); } } /// /// Gets or sets the visibility of the group separator. /// [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; /// /// Gets or sets the visibility of the column info /// [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; /// /// Gets or sets the visibility of a line info. /// [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; /// /// Gets or sets the offset of a line info. /// [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; /// /// Gets or sets the hex box´s border style. /// [DefaultValue(typeof(BorderStyle), "Fixed3D"), Category("Hex"), Description("Gets or sets the hex box´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; /// /// Gets or sets the visibility of the string view. /// [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; /// /// Gets or sets whether the HexBox control displays the hex characters in upper or lower case. /// [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(); } } /// /// Gets and sets the starting point of the bytes selected in the hex box. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public long SelectionStart { get { return _bytePos; } set { SetPosition(value, 0); ScrollByteIntoView(); Invalidate(); } } /// /// Gets and sets the number of bytes selected in the hex box. /// [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; /// /// Gets or sets the info color used for column info and line info. When this property is null, then ForeColor property is used. /// [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; /// /// Gets or sets the info color used for column info and line info. When this property is null, then ForeColor property is used. /// [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; /// /// Gets or sets the background color for the selected bytes. /// [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; /// /// Gets or sets the foreground color for the selected bytes. /// [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; /// /// Gets or sets the visibility of a shadow selection. /// [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; /// /// Gets or sets the color of the shadow selection. /// /// /// A alpha component must be given! /// Default alpha = 100 /// [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); /// /// Contains the size of a single character in pixel /// [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; } /// /// Gets the width required for the content /// [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; /// /// Gets the number bytes drawn horizontally. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int HorizontalByteCount { get { return _iHexMaxHBytes; } } /// /// Gets the number bytes drawn vertically. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int VerticalByteCount { get { return _iHexMaxVBytes; } } /// /// Gets the current line /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public long CurrentLine { get { return _currentLine; } } long _currentLine; /// /// Gets the current position in the current line /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public long CurrentPositionInLine { get { return _currentPositionInLine; } } int _currentPositionInLine; /// /// Gets the a value if insertion mode is active or not. /// [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); } } /// /// Gets or sets the built-in context menu. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public BuiltInContextMenu BuiltInContextMenu { get { return _builtInContextMenu; } } BuiltInContextMenu _builtInContextMenu; /// /// Gets or sets the converter that will translate between byte and character values. /// [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 /// /// Converts a byte array to a hex string. For example: {10,11} = "0A 0B" /// /// the byte array /// the hex string 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; } /// /// Converts the byte to a hex string. For example: "10" = "0A"; /// /// the byte to format /// the hex string string ConvertByteToHex(byte b) { string sB = b.ToString(_hexStringFormat, System.Threading.Thread.CurrentThread.CurrentCulture); if (sB.Length == 1) sB = "0" + sB; return sB; } /// /// 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. /// /// the hex string separated by ' '. For example: "0A 0B 0C" /// the byte array. null if hex is invalid or empty 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); } } /// /// Raises the InsertActiveChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnInsertActiveChanged(EventArgs e) { if (InsertActiveChanged != null) InsertActiveChanged(this, e); } /// /// Raises the ReadOnlyChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnReadOnlyChanged(EventArgs e) { if (ReadOnlyChanged != null) ReadOnlyChanged(this, e); } /// /// Raises the ByteProviderChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnByteProviderChanged(EventArgs e) { if (ByteProviderChanged != null) ByteProviderChanged(this, e); } /// /// Raises the SelectionStartChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnSelectionStartChanged(EventArgs e) { if (SelectionStartChanged != null) SelectionStartChanged(this, e); } /// /// Raises the SelectionLengthChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnSelectionLengthChanged(EventArgs e) { if (SelectionLengthChanged != null) SelectionLengthChanged(this, e); } /// /// Raises the LineInfoVisibleChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnLineInfoVisibleChanged(EventArgs e) { if (LineInfoVisibleChanged != null) LineInfoVisibleChanged(this, e); } /// /// Raises the OnColumnInfoVisibleChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnColumnInfoVisibleChanged(EventArgs e) { if (ColumnInfoVisibleChanged != null) ColumnInfoVisibleChanged(this, e); } /// /// Raises the ColumnSeparatorVisibleChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnGroupSeparatorVisibleChanged(EventArgs e) { if (GroupSeparatorVisibleChanged != null) GroupSeparatorVisibleChanged(this, e); } /// /// Raises the StringViewVisibleChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnStringViewVisibleChanged(EventArgs e) { if (StringViewVisibleChanged != null) StringViewVisibleChanged(this, e); } /// /// Raises the BorderStyleChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnBorderStyleChanged(EventArgs e) { if (BorderStyleChanged != null) BorderStyleChanged(this, e); } /// /// Raises the UseFixedBytesPerLineChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnUseFixedBytesPerLineChanged(EventArgs e) { if (UseFixedBytesPerLineChanged != null) UseFixedBytesPerLineChanged(this, e); } /// /// Raises the GroupSizeChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnGroupSizeChanged(EventArgs e) { if (GroupSizeChanged != null) GroupSizeChanged(this, e); } /// /// Raises the BytesPerLineChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnBytesPerLineChanged(EventArgs e) { if (BytesPerLineChanged != null) BytesPerLineChanged(this, e); } /// /// Raises the VScrollBarVisibleChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnVScrollBarVisibleChanged(EventArgs e) { if (VScrollBarVisibleChanged != null) VScrollBarVisibleChanged(this, e); } /// /// Raises the HexCasingChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnHexCasingChanged(EventArgs e) { if (HexCasingChanged != null) HexCasingChanged(this, e); } /// /// Raises the HorizontalByteCountChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnHorizontalByteCountChanged(EventArgs e) { if (HorizontalByteCountChanged != null) HorizontalByteCountChanged(this, e); } /// /// Raises the VerticalByteCountChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnVerticalByteCountChanged(EventArgs e) { if (VerticalByteCountChanged != null) VerticalByteCountChanged(this, e); } /// /// Raises the CurrentLineChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnCurrentLineChanged(EventArgs e) { if (CurrentLineChanged != null) CurrentLineChanged(this, e); } /// /// Raises the CurrentPositionInLineChanged event. /// /// An EventArgs that contains the event data. protected virtual void OnCurrentPositionInLineChanged(EventArgs e) { if (CurrentPositionInLineChanged != null) CurrentPositionInLineChanged(this, e); } /// /// Raises the Copied event. /// /// An EventArgs that contains the event data. protected virtual void OnCopied(EventArgs e) { if (Copied != null) Copied(this, e); } /// /// Raises the CopiedHex event. /// /// An EventArgs that contains the event data. protected virtual void OnCopiedHex(EventArgs e) { if (CopiedHex != null) CopiedHex(this, e); } /// /// Raises the MouseDown event. /// /// An EventArgs that contains the event data. 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); } /// /// Raises the MouseWhell event /// /// An EventArgs that contains the event data. 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); } /// /// Raises the Resize event. /// /// An EventArgs that contains the event data. protected override void OnResize(EventArgs e) { base.OnResize(e); UpdateRectanglePositioning(); } /// /// Raises the GotFocus event. /// /// An EventArgs that contains the event data. protected override void OnGotFocus(EventArgs e) { System.Diagnostics.Debug.WriteLine("OnGotFocus()", "HexBox"); base.OnGotFocus(e); CreateCaret(); } /// /// Raises the LostFocus event. /// /// An EventArgs that contains the event data. 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 /// /// For high resolution screen support /// /// the factor /// bounds 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 } }