2023-07-29 23:37:10 +05:30

1065 lines
22 KiB
C#

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.Drawing;
using DevExpress.XtraEditors.ViewInfo;
namespace Server.Helper.HexEditor;
public class HexEditor : BaseControl
{
public enum CaseStyle
{
LowerCase,
UpperCase
}
private object _caretLock = new object();
private object _hexTableLock = new object();
private IKeyMouseEventHandler _handler;
private EditView _editView;
private ByteCollection _hexTable;
private string _lineCountCaps = "X";
private int _nrCharsLineCount = 4;
private Caret _caret;
private Rectangle _recContent;
private Rectangle _recLineCount;
private StringFormat _stringFormat;
private int _firstByte;
private int _lastByte;
private int _maxBytesH;
private int _maxBytesV;
private int _maxBytes;
private int _maxVisibleBytesV;
private DevExpress.XtraEditors.VScrollBar _vScrollBar;
private int _vScrollBarWidth = 20;
private int _vScrollPos;
private int _vScrollMax;
private int _vScrollMin;
private int _vScrollSmall;
private int _vScrollLarge;
private SizeF _charSize;
private bool _isVScrollHidden = true;
private int _bytesPerLine = 8;
private int _entityMargin = 10;
private BorderStyle _borderStyle = BorderStyle.Fixed3D;
private Color _borderColor = Color.Empty;
private Color _selectionBackColor = Color.Blue;
private Color _selectionForeColor = Color.White;
private CaseStyle _lineCountCaseStyle = CaseStyle.UpperCase;
private CaseStyle _hexViewCaseStyle = CaseStyle.UpperCase;
private bool _isVScrollVisible;
private bool _dragging;
public override Font Font
{
set
{
base.Font = value;
UpdateRectanglePositioning();
Invalidate();
}
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public byte[] HexTable
{
get
{
lock (_hexTableLock)
{
return _hexTable.ToArray();
}
}
set
{
lock (_hexTableLock)
{
if (value == _hexTable.ToArray())
{
return;
}
_hexTable = new ByteCollection(value);
}
UpdateRectanglePositioning();
Invalidate();
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public SizeF CharSize
{
get
{
return _charSize;
}
private set
{
if (!(_charSize == value))
{
_charSize = value;
if (this.CharSizeChanged != null)
{
this.CharSizeChanged(this, EventArgs.Empty);
}
}
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int MaxBytesV => _maxBytesV;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int FirstVisibleByte => _firstByte;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int LastVisibleByte => _lastByte;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool VScrollBarHidden
{
get
{
return _isVScrollHidden;
}
set
{
if (_isVScrollHidden != value)
{
_isVScrollHidden = value;
if (!_isVScrollHidden)
{
base.Controls.Add(_vScrollBar);
}
else
{
base.Controls.Remove(_vScrollBar);
}
UpdateRectanglePositioning();
Invalidate();
}
}
}
[DefaultValue(8)]
[Category("Hex")]
[Description("Property that specifies the number of bytes to display per line.")]
public int BytesPerLine
{
get
{
return _bytesPerLine;
}
set
{
if (_bytesPerLine != value)
{
_bytesPerLine = value;
UpdateRectanglePositioning();
Invalidate();
}
}
}
[DefaultValue(10)]
[Category("Hex")]
[Description("Property that specifies the margin between each of the entitys in the control.")]
public int EntityMargin
{
get
{
return _entityMargin;
}
set
{
if (_entityMargin != value)
{
_entityMargin = value;
UpdateRectanglePositioning();
Invalidate();
}
}
}
[DefaultValue(BorderStyle.Fixed3D)]
[Category("Appearance")]
[Description("Indicates where the control should have a border.")]
public new BorderStyle BorderStyle
{
get
{
return _borderStyle;
}
set
{
if (_borderStyle != value)
{
if (value != BorderStyle.FixedSingle)
{
_borderColor = Color.Empty;
}
_borderStyle = value;
UpdateRectanglePositioning();
Invalidate();
}
}
}
[DefaultValue(typeof(Color), "Empty")]
[Category("Appearance")]
[Description("Indicates the color to be used when displaying a FixedSingle border.")]
public Color BorderColor
{
get
{
return _borderColor;
}
set
{
if (BorderStyle == BorderStyle.FixedSingle && !(_borderColor == value))
{
_borderColor = value;
Invalidate();
}
}
}
[DefaultValue(typeof(Color), "Blue")]
[Category("Hex")]
[Description("Property for the background color of the selected text areas.")]
public Color SelectionBackColor
{
get
{
return _selectionBackColor;
}
set
{
if (!(_selectionBackColor == value))
{
_selectionBackColor = value;
}
}
}
[DefaultValue(typeof(Color), "White")]
[Category("Hex")]
[Description("Property for the foreground color of the selected text areas.")]
public Color SelectionForeColor
{
get
{
return _selectionForeColor;
}
set
{
if (!(_selectionForeColor == value))
{
_selectionForeColor = value;
}
}
}
[DefaultValue(CaseStyle.UpperCase)]
[Category("Hex")]
[Description("Property for the case type to use on the line counter.")]
public CaseStyle LineCountCaseStyle
{
get
{
return _lineCountCaseStyle;
}
set
{
if (_lineCountCaseStyle != value)
{
_lineCountCaseStyle = value;
if (_lineCountCaseStyle == CaseStyle.LowerCase)
{
_lineCountCaps = "x";
}
else
{
_lineCountCaps = "X";
}
Invalidate();
}
}
}
[DefaultValue(CaseStyle.UpperCase)]
[Category("Hex")]
[Description("Property for the case type to use for the hexadecimal values view.")]
public CaseStyle HexViewCaseStyle
{
get
{
return _hexViewCaseStyle;
}
set
{
if (_hexViewCaseStyle != value)
{
_hexViewCaseStyle = value;
if (_hexViewCaseStyle == CaseStyle.LowerCase)
{
_editView.SetLowerCase();
}
else
{
_editView.SetUpperCase();
}
Invalidate();
}
}
}
[DefaultValue(false)]
[Category("Hex")]
[Description("Property for the visibility of the vertical scrollbar.")]
public bool VScrollBarVisisble
{
get
{
return _isVScrollVisible;
}
set
{
if (_isVScrollVisible != value)
{
_isVScrollVisible = value;
UpdateRectanglePositioning();
Invalidate();
}
}
}
public int CaretPosX
{
get
{
lock (_caretLock)
{
return _caret.Location.X;
}
}
}
public int CaretPosY
{
get
{
lock (_caretLock)
{
return _caret.Location.Y;
}
}
}
public int SelectionStart
{
get
{
lock (_caretLock)
{
return _caret.SelectionStart;
}
}
}
public int SelectionLength
{
get
{
lock (_caretLock)
{
return _caret.SelectionLength;
}
}
}
public int CaretIndex
{
get
{
lock (_caretLock)
{
return _caret.CurrentIndex;
}
}
}
public bool CaretFocused
{
get
{
lock (_caretLock)
{
return _caret.Focused;
}
}
}
public int HexTableLength
{
get
{
lock (_hexTableLock)
{
return _hexTable.Length;
}
}
}
protected override BaseControlPainter Painter => new BaseControlPainter();
protected override BaseControlViewInfo ViewInfo => new BaseControlViewInfo(this);
[Description("Event that is triggered whenever the hextable has been changed.")]
public event EventHandler HexTableChanged;
[Description("Event that is triggered whenever the SelectionStart value is changed.")]
public event EventHandler SelectionStartChanged;
[Description("Event that is triggered whenever the SelectionLength value is changed.")]
public event EventHandler SelectionLengthChanged;
[Description("Event that is triggered whenever the size of the char is changed.")]
public event EventHandler CharSizeChanged;
protected void OnVScrollBarScroll(object sender, ScrollEventArgs e)
{
switch (e.Type)
{
case ScrollEventType.SmallIncrement:
ScrollLineDown(1);
break;
case ScrollEventType.SmallDecrement:
ScrollLineUp(1);
break;
case ScrollEventType.LargeIncrement:
ScrollLineDown(_vScrollLarge);
break;
case ScrollEventType.LargeDecrement:
ScrollLineUp(_vScrollLarge);
break;
case ScrollEventType.ThumbTrack:
ScrollThumbTrack(e.NewValue - e.OldValue);
break;
}
Invalidate();
}
protected void CaretSelectionStartChanged(object sender, EventArgs e)
{
if (this.SelectionStartChanged != null)
{
this.SelectionStartChanged(this, e);
}
}
protected void CaretSelectionLengthChanged(object sender, EventArgs e)
{
if (this.SelectionLengthChanged != null)
{
this.SelectionLengthChanged(this, e);
}
}
protected override void OnMarginChanged(EventArgs e)
{
base.OnMarginChanged(e);
UpdateRectanglePositioning();
Invalidate();
}
protected override void OnGotFocus(EventArgs e)
{
if (_handler != null)
{
_handler.OnGotFocus(e);
}
UpdateRectanglePositioning();
Invalidate();
base.OnGotFocus(e);
}
protected override void OnLostFocus(EventArgs e)
{
_dragging = false;
DestroyCaret();
base.OnLostFocus(e);
}
protected override bool IsInputKey(Keys keyData)
{
if ((uint)(keyData - 37) <= 3u || (uint)(keyData - 65573) <= 3u)
{
return true;
}
return base.IsInputKey(keyData);
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
if (_handler != null)
{
_handler.OnKeyPress(e);
}
base.OnKeyPress(e);
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Next)
{
if (!_isVScrollHidden)
{
ScrollLineDown(_vScrollLarge);
Invalidate();
}
}
else if (e.KeyCode == Keys.Prior)
{
if (!_isVScrollHidden)
{
ScrollLineUp(_vScrollLarge);
Invalidate();
}
}
else if (_handler != null)
{
_handler.OnKeyDown(e);
}
base.OnKeyDown(e);
}
protected override void OnKeyUp(KeyEventArgs e)
{
if (_handler != null)
{
_handler.OnKeyUp(e);
}
base.OnKeyUp(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (Focused)
{
if (_handler != null)
{
_handler.OnMouseDown(e);
}
if (e.Button == MouseButtons.Left)
{
_dragging = true;
Invalidate();
}
}
else
{
Focus();
}
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (Focused && _dragging)
{
if (_handler != null)
{
_handler.OnMouseDragged(e);
}
Invalidate();
}
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
_dragging = false;
if (Focused && _handler != null)
{
_handler.OnMouseUp(e);
}
base.OnMouseUp(e);
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
if (Focused && _handler != null)
{
_handler.OnMouseDoubleClick(e);
}
base.OnMouseDoubleClick(e);
}
public void SetCaretStart(int index, Point location)
{
location = ScrollToCaret(index, location);
lock (_caretLock)
{
_caret.SetStartIndex(index);
_caret.SetCaretLocation(location);
}
Invalidate();
}
public void SetCaretEnd(int index, Point location)
{
location = ScrollToCaret(index, location);
lock (_caretLock)
{
_caret.SetEndIndex(index);
_caret.SetCaretLocation(location);
}
Invalidate();
}
public bool IsSelected(int byteIndex)
{
lock (_caretLock)
{
return _caret.IsSelected(byteIndex);
}
}
public void DestroyCaret()
{
lock (_caretLock)
{
_caret.Destroy();
}
}
public void RemoveSelectedBytes()
{
int selectionStart = SelectionStart;
int selectionLength = SelectionLength;
if (selectionLength > 0)
{
lock (_hexTableLock)
{
_hexTable.RemoveRange(selectionStart, selectionLength);
}
UpdateRectanglePositioning();
Invalidate();
if (this.HexTableChanged != null)
{
this.HexTableChanged(this, EventArgs.Empty);
}
}
}
public void RemoveByteAt(int index)
{
lock (_hexTableLock)
{
_hexTable.RemoveAt(index);
}
UpdateRectanglePositioning();
Invalidate();
if (this.HexTableChanged != null)
{
this.HexTableChanged(this, EventArgs.Empty);
}
}
public void AppendByte(byte item)
{
lock (_hexTableLock)
{
_hexTable.Add(item);
}
UpdateRectanglePositioning();
Invalidate();
if (this.HexTableChanged != null)
{
this.HexTableChanged(this, EventArgs.Empty);
}
}
public void InsertByte(int index, byte item)
{
lock (_hexTableLock)
{
_hexTable.Insert(index, item);
}
UpdateRectanglePositioning();
Invalidate();
if (this.HexTableChanged != null)
{
this.HexTableChanged(this, EventArgs.Empty);
}
}
public char GetByteAsChar(int index)
{
lock (_hexTableLock)
{
return _hexTable.GetCharAt(index);
}
}
public byte GetByte(int index)
{
lock (_hexTableLock)
{
return _hexTable.GetAt(index);
}
}
public void SetByte(int index, byte item)
{
lock (_hexTableLock)
{
_hexTable.SetAt(index, item);
}
Invalidate();
if (this.HexTableChanged != null)
{
this.HexTableChanged(this, EventArgs.Empty);
}
}
public void ScrollLineUp(int lines)
{
if (_firstByte <= 0)
{
return;
}
lines = ((lines > _vScrollPos) ? _vScrollPos : lines);
_vScrollPos -= _vScrollSmall * lines;
UpdateVisibleByteIndex();
UpdateScrollValues();
if (CaretFocused)
{
Point caretLocation = new Point(CaretPosX, CaretPosY);
caretLocation.Y += _recLineCount.Height * lines;
lock (_caretLock)
{
_caret.SetCaretLocation(caretLocation);
}
}
}
public void ScrollLineDown(int lines)
{
if (_vScrollPos > _vScrollMax - _vScrollLarge)
{
return;
}
lines = ((lines + _vScrollPos > _vScrollMax - _vScrollLarge) ? (_vScrollMax - _vScrollLarge - _vScrollPos + 1) : lines);
_vScrollPos += _vScrollSmall * lines;
UpdateVisibleByteIndex();
UpdateScrollValues();
if (!CaretFocused)
{
return;
}
Point caretLocation = new Point(CaretPosX, CaretPosY);
caretLocation.Y -= _recLineCount.Height * lines;
lock (_caretLock)
{
_caret.SetCaretLocation(caretLocation);
if (caretLocation.Y < _recContent.Y)
{
_caret.Hide(base.Handle);
}
}
}
public void ScrollThumbTrack(int lines)
{
if (lines != 0)
{
if (lines < 0)
{
ScrollLineUp(-1 * lines);
}
else
{
ScrollLineDown(lines);
}
}
}
public Point ScrollToCaret(int caretIndex, Point position)
{
if (position.Y < 0)
{
_vScrollPos -= Math.Abs((position.Y - _recContent.Y) / _recLineCount.Height) * _vScrollSmall;
UpdateVisibleByteIndex();
UpdateScrollValues();
if (CaretFocused)
{
position.Y = _recContent.Y;
}
}
else if (position.Y > _maxVisibleBytesV * _recLineCount.Height)
{
_vScrollPos += (position.Y / _recLineCount.Height - (_maxVisibleBytesV - 1)) * _vScrollSmall;
if (_vScrollPos > _vScrollMax - (_vScrollLarge - 1))
{
_vScrollPos = _vScrollMax - (_vScrollLarge - 1);
}
UpdateVisibleByteIndex();
UpdateScrollValues();
if (CaretFocused)
{
position.Y = (_maxVisibleBytesV - 1) * _recLineCount.Height + _recContent.Y;
}
}
return position;
}
private void UpdateRectanglePositioning()
{
if (base.ClientRectangle.Width != 0)
{
SizeF sizeF;
using (Graphics graphics = CreateGraphics())
{
sizeF = graphics.MeasureString("D", Font, 100, _stringFormat);
}
CharSize = new SizeF((float)Math.Ceiling(sizeF.Width), (float)Math.Ceiling(sizeF.Height));
_recContent = base.ClientRectangle;
_recContent.X += base.Margin.Left;
_recContent.Y += base.Margin.Top;
_recContent.Width -= base.Margin.Right;
_recContent.Height -= base.Margin.Bottom;
if (BorderStyle == BorderStyle.Fixed3D)
{
_recContent.X += 2;
_recContent.Y += 2;
_recContent.Width--;
_recContent.Height--;
}
else if (BorderStyle == BorderStyle.FixedSingle)
{
_recContent.X++;
_recContent.Y++;
_recContent.Width--;
_recContent.Height--;
}
if (!VScrollBarHidden)
{
_recContent.Width -= _vScrollBarWidth;
_vScrollBar.Left = _recContent.X + _recContent.Width - base.Margin.Left;
_vScrollBar.Top = _recContent.Y - base.Margin.Top;
_vScrollBar.Width = _vScrollBarWidth;
_vScrollBar.Height = _recContent.Height;
}
_recLineCount = new Rectangle(_recContent.X, _recContent.Y, (int)(_charSize.Width * 4f), (int)_charSize.Height - 2);
_editView.Update(_recLineCount.X + _recLineCount.Width + _entityMargin / 2, _recContent);
_maxBytesH = _bytesPerLine;
_maxBytesV = (int)Math.Ceiling((float)_recContent.Height / (float)_recLineCount.Height);
_maxBytes = _maxBytesH * _maxBytesV;
_maxVisibleBytesV = (int)Math.Floor((float)_recContent.Height / (float)_recLineCount.Height);
UpdateScrollBarSize();
}
}
private void UpdateVisibleByteIndex()
{
if (_hexTable.Length == 0)
{
_firstByte = 0;
_lastByte = 0;
}
else
{
_firstByte = _vScrollPos * _maxBytesH;
_lastByte = Math.Min(HexTableLength, _firstByte + _maxBytes);
}
}
private void UpdateScrollValues()
{
if (!_isVScrollHidden)
{
_vScrollBar.Minimum = _vScrollMin;
_vScrollBar.Maximum = _vScrollMax;
_vScrollBar.Value = _vScrollPos;
_vScrollBar.SmallChange = _vScrollSmall;
_vScrollBar.LargeChange = _vScrollLarge;
_vScrollBar.Visible = true;
}
else
{
_vScrollBar.Visible = false;
}
}
private void UpdateScrollBarSize()
{
if (VScrollBarVisisble && _maxVisibleBytesV > 0 && _maxBytesH > 0)
{
int maxVisibleBytesV = _maxVisibleBytesV;
int num = 1;
int vScrollMin = 0;
int num2 = HexTableLength / _maxBytesH;
int num3 = _firstByte / _maxBytesH;
if (maxVisibleBytesV != _vScrollLarge || num != _vScrollSmall)
{
_vScrollLarge = maxVisibleBytesV;
_vScrollSmall = num;
}
if (num2 >= maxVisibleBytesV)
{
if (num2 != _vScrollMax || num3 != _vScrollPos)
{
_vScrollMin = vScrollMin;
_vScrollMax = num2;
_vScrollPos = num3;
}
VScrollBarHidden = false;
UpdateScrollValues();
}
else
{
VScrollBarHidden = true;
}
}
else
{
VScrollBarHidden = true;
}
}
public HexEditor()
: this(new ByteCollection())
{
}
public HexEditor(ByteCollection collection)
{
_stringFormat = new StringFormat(StringFormat.GenericTypographic);
_stringFormat.Alignment = StringAlignment.Center;
_stringFormat.LineAlignment = StringAlignment.Center;
_hexTable = collection;
_vScrollBar = new DevExpress.XtraEditors.VScrollBar();
_vScrollBar.Scroll += OnVScrollBarScroll;
SetStyle(ControlStyles.ResizeRedraw, value: true);
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, value: true);
SetStyle(ControlStyles.Selectable, value: true);
_caret = new Caret(this);
_caret.SelectionStartChanged += CaretSelectionStartChanged;
_caret.SelectionLengthChanged += CaretSelectionLengthChanged;
_editView = new EditView(this);
_handler = _editView;
Cursor = Cursors.IBeam;
}
private RectangleF GetLineCountBound(int index)
{
return new RectangleF(_recLineCount.X, _recLineCount.Y + _recLineCount.Height * index, _recLineCount.Width, _recLineCount.Height);
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
if (BorderStyle == BorderStyle.Fixed3D)
{
SolidBrush brush = new SolidBrush(BackColor);
Rectangle clientRectangle = base.ClientRectangle;
pevent.Graphics.FillRectangle(brush, clientRectangle);
ControlPaint.DrawBorder3D(pevent.Graphics, base.ClientRectangle, Border3DStyle.Sunken);
}
else if (BorderStyle == BorderStyle.FixedSingle)
{
SolidBrush brush2 = new SolidBrush(BackColor);
Rectangle clientRectangle2 = base.ClientRectangle;
pevent.Graphics.FillRectangle(brush2, clientRectangle2);
ControlPaint.DrawBorder(pevent.Graphics, base.ClientRectangle, BorderColor, ButtonBorderStyle.Solid);
}
else
{
base.OnPaintBackground(pevent);
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Region region = new Region(base.ClientRectangle);
region.Exclude(_recContent);
e.Graphics.ExcludeClip(region);
UpdateVisibleByteIndex();
PaintLineCount(e.Graphics, _firstByte, _lastByte);
_editView.Paint(e.Graphics, _firstByte, _lastByte);
}
private void PaintLineCount(Graphics g, int startIndex, int lastIndex)
{
SolidBrush brush = new SolidBrush(ForeColor);
for (int i = 0; i * _maxBytesH + startIndex <= lastIndex; i++)
{
RectangleF lineCountBound = GetLineCountBound(i);
string text = (startIndex + i * _maxBytesH).ToString(_lineCountCaps);
int num = _nrCharsLineCount - text.Length;
string s = ((num <= -1) ? new string('~', _nrCharsLineCount) : (new string('0', num) + text));
g.DrawString(s, Font, brush, lineCountBound, _stringFormat);
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
UpdateRectanglePositioning();
Invalidate();
}
}