|
@@ -31,7 +31,6 @@ namespace Terminal.Gui;
|
|
/// Control the byte at the caret for editing by setting the <see cref="Address"/> property to an offset in the
|
|
/// Control the byte at the caret for editing by setting the <see cref="Address"/> property to an offset in the
|
|
/// stream.
|
|
/// stream.
|
|
/// </para>
|
|
/// </para>
|
|
-/// <para>Control the first byte shown by setting the <see cref="DisplayStart"/> property to an offset in the stream.</para>
|
|
|
|
/// </remarks>
|
|
/// </remarks>
|
|
public class HexView : View, IDesignable
|
|
public class HexView : View, IDesignable
|
|
{
|
|
{
|
|
@@ -72,10 +71,10 @@ public class HexView : View, IDesignable
|
|
AddCommand (Command.End, () => MoveEnd ());
|
|
AddCommand (Command.End, () => MoveEnd ());
|
|
AddCommand (Command.LeftStart, () => MoveLeftStart ());
|
|
AddCommand (Command.LeftStart, () => MoveLeftStart ());
|
|
AddCommand (Command.RightEnd, () => MoveEndOfLine ());
|
|
AddCommand (Command.RightEnd, () => MoveEndOfLine ());
|
|
- AddCommand (Command.StartOfPage, () => MoveUp (BytesPerLine * ((int)(Address - _displayStart) / BytesPerLine)));
|
|
|
|
|
|
+ AddCommand (Command.StartOfPage, () => MoveUp (BytesPerLine * ((int)(Address - Viewport.Y) / BytesPerLine)));
|
|
AddCommand (
|
|
AddCommand (
|
|
Command.EndOfPage,
|
|
Command.EndOfPage,
|
|
- () => MoveDown (BytesPerLine * (Viewport.Height - 1 - (int)(Address - _displayStart) / BytesPerLine))
|
|
|
|
|
|
+ () => MoveDown (BytesPerLine * (Viewport.Height - 1 - (int)(Address - Viewport.Y) / BytesPerLine))
|
|
);
|
|
);
|
|
AddCommand (Command.DeleteCharLeft, () => true);
|
|
AddCommand (Command.DeleteCharLeft, () => true);
|
|
AddCommand (Command.DeleteCharRight, () => true);
|
|
AddCommand (Command.DeleteCharRight, () => true);
|
|
@@ -104,7 +103,13 @@ public class HexView : View, IDesignable
|
|
KeyBindings.Remove (Key.Space);
|
|
KeyBindings.Remove (Key.Space);
|
|
KeyBindings.Remove (Key.Enter);
|
|
KeyBindings.Remove (Key.Enter);
|
|
|
|
|
|
- SubviewsLaidOut += HexView_LayoutComplete;
|
|
|
|
|
|
+ SubviewsLaidOut += HexViewSubviewsLaidOut;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void HexViewSubviewsLaidOut (object? sender, LayoutEventArgs e)
|
|
|
|
+ {
|
|
|
|
+ SetBytesPerLine ();
|
|
|
|
+ SetContentSize (new (GetLeftSideStartColumn () + BytesPerLine / NUM_BYTES_PER_HEX_COLUMN * HEX_COLUMN_WIDTH, (int)((GetEditedSize ()) / BytesPerLine) + 1));
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>Initializes a <see cref="HexView"/> class.</summary>
|
|
/// <summary>Initializes a <see cref="HexView"/> class.</summary>
|
|
@@ -118,44 +123,69 @@ public class HexView : View, IDesignable
|
|
public bool AllowEdits { get; set; } = true;
|
|
public bool AllowEdits { get; set; } = true;
|
|
|
|
|
|
/// <summary>Gets the current edit position.</summary>
|
|
/// <summary>Gets the current edit position.</summary>
|
|
- public Point Position
|
|
|
|
|
|
+ /// <param name="address"></param>
|
|
|
|
+ public Point GetPosition (long address)
|
|
{
|
|
{
|
|
- get
|
|
|
|
|
|
+ if (_source is null || BytesPerLine == 0)
|
|
{
|
|
{
|
|
- if (_source is null || BytesPerLine == 0)
|
|
|
|
- {
|
|
|
|
- return Point.Empty;
|
|
|
|
- }
|
|
|
|
|
|
+ return Point.Empty;
|
|
|
|
+ }
|
|
|
|
|
|
- var delta = (int)Address;
|
|
|
|
|
|
+ var line = address / BytesPerLine;
|
|
|
|
+ var item = address % BytesPerLine;
|
|
|
|
|
|
- int line = delta / BytesPerLine;
|
|
|
|
- int item = delta % BytesPerLine;
|
|
|
|
|
|
+ return new ((int)item, (int)line);
|
|
|
|
+ }
|
|
|
|
|
|
- return new (item, line);
|
|
|
|
|
|
+ /// <summary>Gets cursor location, given an address.</summary>
|
|
|
|
+ /// <param name="address"></param>
|
|
|
|
+ public Point GetCursor (long address)
|
|
|
|
+ {
|
|
|
|
+ Point position = GetPosition (address);
|
|
|
|
+ position.Offset (-Viewport.X, -Viewport.Y);
|
|
|
|
+ return position;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void ScrollToMakeCursorVisible (Point offsetToNewCursor)
|
|
|
|
+ {
|
|
|
|
+ // Adjust vertical scrolling
|
|
|
|
+ if (offsetToNewCursor.Y < 1) // Header is at Y = 0
|
|
|
|
+ {
|
|
|
|
+ ScrollVertical (offsetToNewCursor.Y);
|
|
|
|
+ }
|
|
|
|
+ else if (offsetToNewCursor.Y >= Viewport.Height)
|
|
|
|
+ {
|
|
|
|
+ ScrollVertical (offsetToNewCursor.Y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
///<inheritdoc/>
|
|
///<inheritdoc/>
|
|
public override Point? PositionCursor ()
|
|
public override Point? PositionCursor ()
|
|
{
|
|
{
|
|
- var delta = (int)(Address - _displayStart);
|
|
|
|
- int line = delta / BytesPerLine;
|
|
|
|
- int item = delta % BytesPerLine;
|
|
|
|
- int block = item / NUM_BYTES_PER_HEX_COLUMN;
|
|
|
|
- int column = item % NUM_BYTES_PER_HEX_COLUMN * 3;
|
|
|
|
|
|
+ Point position = GetCursor (Address);
|
|
|
|
+
|
|
|
|
+ int block = position.X / NUM_BYTES_PER_HEX_COLUMN;
|
|
|
|
+ int column = position.X % NUM_BYTES_PER_HEX_COLUMN * 3;
|
|
|
|
|
|
int x = GetLeftSideStartColumn () + block * HEX_COLUMN_WIDTH + column + (_firstNibble ? 0 : 1);
|
|
int x = GetLeftSideStartColumn () + block * HEX_COLUMN_WIDTH + column + (_firstNibble ? 0 : 1);
|
|
- int y = line;
|
|
|
|
|
|
+ int y = position.Y;
|
|
|
|
|
|
if (!_leftSideHasFocus)
|
|
if (!_leftSideHasFocus)
|
|
{
|
|
{
|
|
- x = GetLeftSideStartColumn () + BytesPerLine / NUM_BYTES_PER_HEX_COLUMN * HEX_COLUMN_WIDTH + item - 1;
|
|
|
|
|
|
+ x = GetLeftSideStartColumn () + BytesPerLine / NUM_BYTES_PER_HEX_COLUMN * HEX_COLUMN_WIDTH + position.X - 1;
|
|
}
|
|
}
|
|
|
|
|
|
- Move (x, y);
|
|
|
|
-
|
|
|
|
- return new (x, y);
|
|
|
|
|
|
+ if (HasFocus
|
|
|
|
+ && x >= 0
|
|
|
|
+ && x < Viewport.Width - AddressWidth + 1
|
|
|
|
+ && y >= 0
|
|
|
|
+ && y < Viewport.Height)
|
|
|
|
+ {
|
|
|
|
+ Move (x, y);
|
|
|
|
+ return new (x, y);
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
}
|
|
}
|
|
|
|
|
|
private SortedDictionary<long, byte> _edits = [];
|
|
private SortedDictionary<long, byte> _edits = [];
|
|
@@ -167,6 +197,51 @@ public class HexView : View, IDesignable
|
|
/// <value>The edits.</value>
|
|
/// <value>The edits.</value>
|
|
public IReadOnlyDictionary<long, byte> Edits => _edits;
|
|
public IReadOnlyDictionary<long, byte> Edits => _edits;
|
|
|
|
|
|
|
|
+ private long GetEditedSize ()
|
|
|
|
+ {
|
|
|
|
+ if (_edits.Count == 0)
|
|
|
|
+ {
|
|
|
|
+ return _source!.Length;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ long maxEditAddress = _edits.Keys.Max ();
|
|
|
|
+
|
|
|
|
+ return Math.Max (_source!.Length, maxEditAddress + 1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Applies and edits made to the <see cref="Stream"/> and resets the contents of the
|
|
|
|
+ /// <see cref="Edits"/> property.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="stream">If provided also applies the changes to the passed <see cref="Stream"/>.</param>
|
|
|
|
+ /// .
|
|
|
|
+ public void ApplyEdits (Stream? stream = null)
|
|
|
|
+ {
|
|
|
|
+ foreach (KeyValuePair<long, byte> kv in _edits)
|
|
|
|
+ {
|
|
|
|
+ _source!.Position = kv.Key;
|
|
|
|
+ _source.WriteByte (kv.Value);
|
|
|
|
+ _source.Flush ();
|
|
|
|
+
|
|
|
|
+ if (stream is { })
|
|
|
|
+ {
|
|
|
|
+ stream.Position = kv.Key;
|
|
|
|
+ stream.WriteByte (kv.Value);
|
|
|
|
+ stream.Flush ();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _edits = new ();
|
|
|
|
+ SetNeedsDraw ();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Discards the edits made to the <see cref="Stream"/> by resetting the contents of the
|
|
|
|
+ /// <see cref="Edits"/> property.
|
|
|
|
+ /// </summary>
|
|
|
|
+ public void DiscardEdits () { _edits = new (); }
|
|
|
|
+
|
|
private Stream? _source;
|
|
private Stream? _source;
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -186,12 +261,9 @@ public class HexView : View, IDesignable
|
|
throw new ArgumentException (@"The source stream must be seekable (CanSeek property)");
|
|
throw new ArgumentException (@"The source stream must be seekable (CanSeek property)");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ DiscardEdits ();
|
|
_source = value;
|
|
_source = value;
|
|
-
|
|
|
|
- if (_displayStart > _source.Length)
|
|
|
|
- {
|
|
|
|
- DisplayStart = 0;
|
|
|
|
- }
|
|
|
|
|
|
+ SetBytesPerLine ();
|
|
|
|
|
|
if (Address > _source.Length)
|
|
if (Address > _source.Length)
|
|
{
|
|
{
|
|
@@ -229,29 +301,15 @@ public class HexView : View, IDesignable
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- //ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual (value, Source!.Length, $"Position");
|
|
|
|
|
|
+ long newAddress = Math.Clamp (value, 0, GetEditedSize ());
|
|
|
|
|
|
- _address = value;
|
|
|
|
- RaisePositionChanged ();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ Point offsetToNewCursor = GetCursor (newAddress);
|
|
|
|
|
|
- private long _displayStart;
|
|
|
|
|
|
+ _address = newAddress;
|
|
|
|
|
|
- // TODO: Use Viewport content scrolling instead
|
|
|
|
- /// <summary>
|
|
|
|
- /// Sets or gets the offset into the <see cref="Stream"/> that will be displayed at the top of the
|
|
|
|
- /// <see cref="HexView"/>.
|
|
|
|
- /// </summary>
|
|
|
|
- /// <value>The display start.</value>
|
|
|
|
- public long DisplayStart
|
|
|
|
- {
|
|
|
|
- get => _displayStart;
|
|
|
|
- set
|
|
|
|
- {
|
|
|
|
- Address = value;
|
|
|
|
-
|
|
|
|
- SetDisplayStart (value);
|
|
|
|
|
|
+ // Ensure the new cursor position is visible
|
|
|
|
+ ScrollToMakeCursorVisible (offsetToNewCursor);
|
|
|
|
+ RaisePositionChanged ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -278,56 +336,6 @@ public class HexView : View, IDesignable
|
|
|
|
|
|
private int GetLeftSideStartColumn () { return AddressWidth == 0 ? 0 : AddressWidth + 1; }
|
|
private int GetLeftSideStartColumn () { return AddressWidth == 0 ? 0 : AddressWidth + 1; }
|
|
|
|
|
|
- internal void SetDisplayStart (long value)
|
|
|
|
- {
|
|
|
|
- if (value > 0 && value >= _source?.Length)
|
|
|
|
- {
|
|
|
|
- _displayStart = _source.Length - 1;
|
|
|
|
- }
|
|
|
|
- else if (value < 0)
|
|
|
|
- {
|
|
|
|
- _displayStart = 0;
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- _displayStart = value;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- SetNeedsDraw ();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// <summary>
|
|
|
|
- /// Applies and edits made to the <see cref="Stream"/> and resets the contents of the
|
|
|
|
- /// <see cref="Edits"/> property.
|
|
|
|
- /// </summary>
|
|
|
|
- /// <param name="stream">If provided also applies the changes to the passed <see cref="Stream"/>.</param>
|
|
|
|
- /// .
|
|
|
|
- public void ApplyEdits (Stream? stream = null)
|
|
|
|
- {
|
|
|
|
- foreach (KeyValuePair<long, byte> kv in _edits)
|
|
|
|
- {
|
|
|
|
- _source!.Position = kv.Key;
|
|
|
|
- _source.WriteByte (kv.Value);
|
|
|
|
- _source.Flush ();
|
|
|
|
-
|
|
|
|
- if (stream is { })
|
|
|
|
- {
|
|
|
|
- stream.Position = kv.Key;
|
|
|
|
- stream.WriteByte (kv.Value);
|
|
|
|
- stream.Flush ();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- _edits = new ();
|
|
|
|
- SetNeedsDraw ();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// <summary>
|
|
|
|
- /// Discards the edits made to the <see cref="Stream"/> by resetting the contents of the
|
|
|
|
- /// <see cref="Edits"/> property.
|
|
|
|
- /// </summary>
|
|
|
|
- public void DiscardEdits () { _edits = new (); }
|
|
|
|
-
|
|
|
|
/// <inheritdoc/>
|
|
/// <inheritdoc/>
|
|
protected override bool OnMouseEvent (MouseEventArgs me)
|
|
protected override bool OnMouseEvent (MouseEventArgs me)
|
|
{
|
|
{
|
|
@@ -351,14 +359,14 @@ public class HexView : View, IDesignable
|
|
|
|
|
|
if (me.Flags == MouseFlags.WheeledDown)
|
|
if (me.Flags == MouseFlags.WheeledDown)
|
|
{
|
|
{
|
|
- DisplayStart = Math.Min (DisplayStart + BytesPerLine, GetEditedSize ());
|
|
|
|
|
|
+ ScrollVertical (1);
|
|
|
|
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
if (me.Flags == MouseFlags.WheeledUp)
|
|
if (me.Flags == MouseFlags.WheeledUp)
|
|
{
|
|
{
|
|
- DisplayStart = Math.Max (DisplayStart - BytesPerLine, 0);
|
|
|
|
|
|
+ ScrollVertical (-1);
|
|
|
|
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
@@ -368,8 +376,8 @@ public class HexView : View, IDesignable
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
- int nblocks = BytesPerLine / NUM_BYTES_PER_HEX_COLUMN;
|
|
|
|
- int blocksSize = nblocks * HEX_COLUMN_WIDTH;
|
|
|
|
|
|
+ int blocks = BytesPerLine / NUM_BYTES_PER_HEX_COLUMN;
|
|
|
|
+ int blocksSize = blocks * HEX_COLUMN_WIDTH;
|
|
int blocksRightOffset = GetLeftSideStartColumn () + blocksSize - 1;
|
|
int blocksRightOffset = GetLeftSideStartColumn () + blocksSize - 1;
|
|
|
|
|
|
if (me.Position.X > blocksRightOffset + BytesPerLine - 1)
|
|
if (me.Position.X > blocksRightOffset + BytesPerLine - 1)
|
|
@@ -378,7 +386,7 @@ public class HexView : View, IDesignable
|
|
}
|
|
}
|
|
|
|
|
|
bool clickIsOnLeftSide = me.Position.X >= blocksRightOffset;
|
|
bool clickIsOnLeftSide = me.Position.X >= blocksRightOffset;
|
|
- long lineStart = me.Position.Y * BytesPerLine + _displayStart;
|
|
|
|
|
|
+ long lineStart = me.Position.Y * BytesPerLine + Viewport.Y * BytesPerLine;
|
|
int x = me.Position.X - GetLeftSideStartColumn () + 1;
|
|
int x = me.Position.X - GetLeftSideStartColumn () + 1;
|
|
int block = x / HEX_COLUMN_WIDTH;
|
|
int block = x / HEX_COLUMN_WIDTH;
|
|
x -= block * 2;
|
|
x -= block * 2;
|
|
@@ -413,10 +421,9 @@ public class HexView : View, IDesignable
|
|
{
|
|
{
|
|
_firstNibble = true;
|
|
_firstNibble = true;
|
|
}
|
|
}
|
|
|
|
+ SetNeedsDraw ();
|
|
}
|
|
}
|
|
|
|
|
|
- SetNeedsDraw ();
|
|
|
|
-
|
|
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -433,35 +440,38 @@ public class HexView : View, IDesignable
|
|
SetAttribute (current);
|
|
SetAttribute (current);
|
|
Move (0, 0);
|
|
Move (0, 0);
|
|
|
|
|
|
|
|
+ long addressOfFirstLine = Viewport.Y * BytesPerLine;
|
|
|
|
+
|
|
int nBlocks = BytesPerLine / NUM_BYTES_PER_HEX_COLUMN;
|
|
int nBlocks = BytesPerLine / NUM_BYTES_PER_HEX_COLUMN;
|
|
var data = new byte [nBlocks * NUM_BYTES_PER_HEX_COLUMN * Viewport.Height];
|
|
var data = new byte [nBlocks * NUM_BYTES_PER_HEX_COLUMN * Viewport.Height];
|
|
- Source.Position = _displayStart;
|
|
|
|
- int n = _source!.Read (data, 0, data.Length);
|
|
|
|
|
|
+ Source.Position = addressOfFirstLine;
|
|
|
|
+ long bytesRead = Source!.Read (data, 0, data.Length);
|
|
|
|
|
|
Attribute selectedAttribute = GetHotNormalColor ();
|
|
Attribute selectedAttribute = GetHotNormalColor ();
|
|
Attribute editedAttribute = new Attribute (GetNormalColor ().Foreground.GetHighlightColor (), GetNormalColor ().Background);
|
|
Attribute editedAttribute = new Attribute (GetNormalColor ().Foreground.GetHighlightColor (), GetNormalColor ().Background);
|
|
Attribute editingAttribute = new Attribute (GetFocusColor ().Background, GetFocusColor ().Foreground);
|
|
Attribute editingAttribute = new Attribute (GetFocusColor ().Background, GetFocusColor ().Foreground);
|
|
|
|
+ Attribute addressAttribute = new Attribute (GetNormalColor ().Foreground.GetHighlightColor (), GetNormalColor ().Background);
|
|
for (var line = 0; line < Viewport.Height; line++)
|
|
for (var line = 0; line < Viewport.Height; line++)
|
|
{
|
|
{
|
|
- Rectangle lineRect = new (0, line, Viewport.Width, 1);
|
|
|
|
|
|
+ Move (0, line);
|
|
|
|
+ long addressOfLine = addressOfFirstLine + line * nBlocks * NUM_BYTES_PER_HEX_COLUMN;
|
|
|
|
|
|
- if (!Viewport.Contains (lineRect))
|
|
|
|
|
|
+ if (addressOfLine <= GetEditedSize ())
|
|
{
|
|
{
|
|
- continue;
|
|
|
|
|
|
+ SetAttribute (addressAttribute);
|
|
}
|
|
}
|
|
-
|
|
|
|
- Move (0, line);
|
|
|
|
- currentAttribute = new Attribute (GetNormalColor ().Foreground.GetHighlightColor (), GetNormalColor ().Background);
|
|
|
|
- SetAttribute (currentAttribute);
|
|
|
|
- var address = $"{_displayStart + line * nBlocks * NUM_BYTES_PER_HEX_COLUMN:x8}";
|
|
|
|
- Driver?.AddStr ($"{address.Substring (8 - AddressWidth)}");
|
|
|
|
-
|
|
|
|
- if (AddressWidth > 0)
|
|
|
|
|
|
+ else
|
|
{
|
|
{
|
|
- Driver?.AddStr (" ");
|
|
|
|
|
|
+ SetAttribute (new Attribute (GetNormalColor ().Background.GetHighlightColor (), addressAttribute.Background));
|
|
}
|
|
}
|
|
|
|
+ var address = $"{addressOfLine:x8}";
|
|
|
|
+ AddStr ($"{address.Substring (8 - AddressWidth)}");
|
|
|
|
|
|
SetAttribute (GetNormalColor ());
|
|
SetAttribute (GetNormalColor ());
|
|
|
|
+ if (AddressWidth > 0)
|
|
|
|
+ {
|
|
|
|
+ AddStr (" ");
|
|
|
|
+ }
|
|
|
|
|
|
for (var block = 0; block < nBlocks; block++)
|
|
for (var block = 0; block < nBlocks; block++)
|
|
{
|
|
{
|
|
@@ -470,7 +480,7 @@ public class HexView : View, IDesignable
|
|
int offset = line * nBlocks * NUM_BYTES_PER_HEX_COLUMN + block * NUM_BYTES_PER_HEX_COLUMN + b;
|
|
int offset = line * nBlocks * NUM_BYTES_PER_HEX_COLUMN + block * NUM_BYTES_PER_HEX_COLUMN + b;
|
|
byte value = GetData (data, offset, out bool edited);
|
|
byte value = GetData (data, offset, out bool edited);
|
|
|
|
|
|
- if (offset + _displayStart == Address)
|
|
|
|
|
|
+ if (offset + addressOfFirstLine == Address)
|
|
{
|
|
{
|
|
// Selected
|
|
// Selected
|
|
SetAttribute (_leftSideHasFocus ? editingAttribute : (edited ? editedAttribute : selectedAttribute));
|
|
SetAttribute (_leftSideHasFocus ? editingAttribute : (edited ? editedAttribute : selectedAttribute));
|
|
@@ -480,12 +490,12 @@ public class HexView : View, IDesignable
|
|
SetAttribute (edited ? editedAttribute : GetNormalColor ());
|
|
SetAttribute (edited ? editedAttribute : GetNormalColor ());
|
|
}
|
|
}
|
|
|
|
|
|
- Driver?.AddStr (offset >= n && !edited ? " " : $"{value:x2}");
|
|
|
|
|
|
+ AddStr (offset >= bytesRead && !edited ? " " : $"{value:x2}");
|
|
SetAttribute (GetNormalColor ());
|
|
SetAttribute (GetNormalColor ());
|
|
- Driver?.AddRune (_spaceCharRune);
|
|
|
|
|
|
+ AddRune (_spaceCharRune);
|
|
}
|
|
}
|
|
|
|
|
|
- Driver?.AddStr (block + 1 == nBlocks ? " " : $"{_columnSeparatorRune} ");
|
|
|
|
|
|
+ AddStr (block + 1 == nBlocks ? " " : $"{_columnSeparatorRune} ");
|
|
}
|
|
}
|
|
|
|
|
|
for (var byteIndex = 0; byteIndex < nBlocks * NUM_BYTES_PER_HEX_COLUMN; byteIndex++)
|
|
for (var byteIndex = 0; byteIndex < nBlocks * NUM_BYTES_PER_HEX_COLUMN; byteIndex++)
|
|
@@ -496,7 +506,7 @@ public class HexView : View, IDesignable
|
|
|
|
|
|
var utf8BytesConsumed = 0;
|
|
var utf8BytesConsumed = 0;
|
|
|
|
|
|
- if (offset >= n && !edited)
|
|
|
|
|
|
+ if (offset >= bytesRead && !edited)
|
|
{
|
|
{
|
|
c = _spaceCharRune;
|
|
c = _spaceCharRune;
|
|
}
|
|
}
|
|
@@ -528,7 +538,7 @@ public class HexView : View, IDesignable
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- if (offset + _displayStart == Address)
|
|
|
|
|
|
+ if (offset + Source.Position == Address)
|
|
{
|
|
{
|
|
// Selected
|
|
// Selected
|
|
SetAttribute (_leftSideHasFocus ? editingAttribute : (edited ? editedAttribute : selectedAttribute));
|
|
SetAttribute (_leftSideHasFocus ? editingAttribute : (edited ? editedAttribute : selectedAttribute));
|
|
@@ -538,26 +548,17 @@ public class HexView : View, IDesignable
|
|
SetAttribute (edited ? editedAttribute : GetNormalColor ());
|
|
SetAttribute (edited ? editedAttribute : GetNormalColor ());
|
|
}
|
|
}
|
|
|
|
|
|
- Driver?.AddRune (c);
|
|
|
|
|
|
+ AddRune (c);
|
|
|
|
|
|
for (var i = 1; i < utf8BytesConsumed; i++)
|
|
for (var i = 1; i < utf8BytesConsumed; i++)
|
|
{
|
|
{
|
|
byteIndex++;
|
|
byteIndex++;
|
|
- Driver?.AddRune (_periodCharRune);
|
|
|
|
|
|
+ AddRune (_periodCharRune);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return true;
|
|
return true;
|
|
-
|
|
|
|
- void SetAttribute (Attribute attribute)
|
|
|
|
- {
|
|
|
|
- if (currentAttribute != attribute)
|
|
|
|
- {
|
|
|
|
- currentAttribute = attribute;
|
|
|
|
- SetAttribute (attribute);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>Raises the <see cref="Edited"/> event.</summary>
|
|
/// <summary>Raises the <see cref="Edited"/> event.</summary>
|
|
@@ -576,24 +577,22 @@ public class HexView : View, IDesignable
|
|
protected virtual void OnEdited (HexViewEditEventArgs e) { }
|
|
protected virtual void OnEdited (HexViewEditEventArgs e) { }
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Call this when <see cref="Position"/> (and <see cref="Address"/>) has changed. Raises the
|
|
|
|
|
|
+ /// Call this when the position (see <see cref="GetPosition"/>) and <see cref="Address"/> have changed. Raises the
|
|
/// <see cref="PositionChanged"/> event.
|
|
/// <see cref="PositionChanged"/> event.
|
|
/// </summary>
|
|
/// </summary>
|
|
protected void RaisePositionChanged ()
|
|
protected void RaisePositionChanged ()
|
|
{
|
|
{
|
|
- SetNeedsDraw ();
|
|
|
|
-
|
|
|
|
- HexViewEventArgs args = new (Address, Position, BytesPerLine);
|
|
|
|
|
|
+ HexViewEventArgs args = new (Address, GetPosition (Address), BytesPerLine);
|
|
OnPositionChanged (args);
|
|
OnPositionChanged (args);
|
|
PositionChanged?.Invoke (this, args);
|
|
PositionChanged?.Invoke (this, args);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Called when <see cref="Position"/> (and <see cref="Address"/>) has changed.
|
|
|
|
|
|
+ /// Called when the position (see <see cref="GetPosition"/>) and <see cref="Address"/> have changed.
|
|
/// </summary>
|
|
/// </summary>
|
|
protected virtual void OnPositionChanged (HexViewEventArgs e) { }
|
|
protected virtual void OnPositionChanged (HexViewEventArgs e) { }
|
|
|
|
|
|
- /// <summary>Raised when <see cref="Position"/> (and <see cref="Address"/>) has changed.</summary>
|
|
|
|
|
|
+ /// <summary>Raised when the position (see <see cref="GetPosition"/>) and <see cref="Address"/> have changed.</summary>
|
|
public event EventHandler<HexViewEventArgs>? PositionChanged;
|
|
public event EventHandler<HexViewEventArgs>? PositionChanged;
|
|
|
|
|
|
/// <inheritdoc/>
|
|
/// <inheritdoc/>
|
|
@@ -642,9 +641,6 @@ public class HexView : View, IDesignable
|
|
b = (byte)_source.ReadByte ();
|
|
b = (byte)_source.ReadByte ();
|
|
}
|
|
}
|
|
|
|
|
|
- // BUGBUG: This makes no sense here.
|
|
|
|
- RedisplayLine (Address);
|
|
|
|
-
|
|
|
|
if (_firstNibble)
|
|
if (_firstNibble)
|
|
{
|
|
{
|
|
_firstNibble = false;
|
|
_firstNibble = false;
|
|
@@ -701,13 +697,13 @@ public class HexView : View, IDesignable
|
|
//
|
|
//
|
|
// This is used to support editing of the buffer on a peer List<>,
|
|
// This is used to support editing of the buffer on a peer List<>,
|
|
// the offset corresponds to an offset relative to DisplayStart, and
|
|
// the offset corresponds to an offset relative to DisplayStart, and
|
|
- // the buffer contains the contents of a screenful of data, so the
|
|
|
|
|
|
+ // the buffer contains the contents of a Viewport of data, so the
|
|
// offset is relative to the buffer.
|
|
// offset is relative to the buffer.
|
|
//
|
|
//
|
|
//
|
|
//
|
|
private byte GetData (byte [] buffer, int offset, out bool edited)
|
|
private byte GetData (byte [] buffer, int offset, out bool edited)
|
|
{
|
|
{
|
|
- long pos = DisplayStart + offset;
|
|
|
|
|
|
+ long pos = Viewport.Y * BytesPerLine + offset;
|
|
|
|
|
|
if (_edits.TryGetValue (pos, out byte v))
|
|
if (_edits.TryGetValue (pos, out byte v))
|
|
{
|
|
{
|
|
@@ -726,7 +722,7 @@ public class HexView : View, IDesignable
|
|
var returnBytes = new byte [count];
|
|
var returnBytes = new byte [count];
|
|
edited = false;
|
|
edited = false;
|
|
|
|
|
|
- long pos = DisplayStart + offset;
|
|
|
|
|
|
+ long pos = Viewport.Y + offset;
|
|
for (long i = pos; i < pos + count; i++)
|
|
for (long i = pos; i < pos + count; i++)
|
|
{
|
|
{
|
|
if (_edits.TryGetValue (i, out byte v))
|
|
if (_edits.TryGetValue (i, out byte v))
|
|
@@ -746,7 +742,7 @@ public class HexView : View, IDesignable
|
|
return returnBytes;
|
|
return returnBytes;
|
|
}
|
|
}
|
|
|
|
|
|
- private void HexView_LayoutComplete (object? sender, LayoutEventArgs e)
|
|
|
|
|
|
+ private void SetBytesPerLine ()
|
|
{
|
|
{
|
|
// Small buffers will just show the position, with the bsize field value (4 bytes)
|
|
// Small buffers will just show the position, with the bsize field value (4 bytes)
|
|
BytesPerLine = NUM_BYTES_PER_HEX_COLUMN;
|
|
BytesPerLine = NUM_BYTES_PER_HEX_COLUMN;
|
|
@@ -761,16 +757,14 @@ public class HexView : View, IDesignable
|
|
|
|
|
|
private bool MoveDown (int bytes)
|
|
private bool MoveDown (int bytes)
|
|
{
|
|
{
|
|
- RedisplayLine (Address);
|
|
|
|
-
|
|
|
|
if (Address + bytes < GetEditedSize ())
|
|
if (Address + bytes < GetEditedSize ())
|
|
{
|
|
{
|
|
// We can move down lines cleanly (without extending stream)
|
|
// We can move down lines cleanly (without extending stream)
|
|
Address += bytes;
|
|
Address += bytes;
|
|
}
|
|
}
|
|
- else if ((bytes == BytesPerLine * Viewport.Height && _source!.Length >= DisplayStart + BytesPerLine * Viewport.Height)
|
|
|
|
|
|
+ else if ((bytes == BytesPerLine * Viewport.Height && _source!.Length >= Viewport.Y * BytesPerLine + BytesPerLine * Viewport.Height)
|
|
|| (bytes <= BytesPerLine * Viewport.Height - BytesPerLine
|
|
|| (bytes <= BytesPerLine * Viewport.Height - BytesPerLine
|
|
- && _source!.Length <= DisplayStart + BytesPerLine * Viewport.Height))
|
|
|
|
|
|
+ && _source!.Length <= Viewport.Y * BytesPerLine + BytesPerLine * Viewport.Height))
|
|
{
|
|
{
|
|
long p = Address;
|
|
long p = Address;
|
|
|
|
|
|
@@ -783,16 +777,6 @@ public class HexView : View, IDesignable
|
|
Address = p;
|
|
Address = p;
|
|
}
|
|
}
|
|
|
|
|
|
- if (Address >= DisplayStart + BytesPerLine * Viewport.Height)
|
|
|
|
- {
|
|
|
|
- SetDisplayStart (DisplayStart + bytes);
|
|
|
|
- SetNeedsDraw ();
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- RedisplayLine (Address);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -800,17 +784,6 @@ public class HexView : View, IDesignable
|
|
{
|
|
{
|
|
// This lets address go past the end of the stream one, enabling adding to the stream.
|
|
// This lets address go past the end of the stream one, enabling adding to the stream.
|
|
Address = GetEditedSize ();
|
|
Address = GetEditedSize ();
|
|
-
|
|
|
|
- if (Address >= DisplayStart + BytesPerLine * Viewport.Height)
|
|
|
|
- {
|
|
|
|
- SetDisplayStart (Address);
|
|
|
|
- SetNeedsDraw ();
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- RedisplayLine (Address);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -818,23 +791,17 @@ public class HexView : View, IDesignable
|
|
{
|
|
{
|
|
// This lets address go past the end of the stream one, enabling adding to the stream.
|
|
// This lets address go past the end of the stream one, enabling adding to the stream.
|
|
Address = Math.Min (Address / BytesPerLine * BytesPerLine + BytesPerLine - 1, GetEditedSize ());
|
|
Address = Math.Min (Address / BytesPerLine * BytesPerLine + BytesPerLine - 1, GetEditedSize ());
|
|
- SetNeedsDraw ();
|
|
|
|
-
|
|
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
private bool MoveHome ()
|
|
private bool MoveHome ()
|
|
{
|
|
{
|
|
- DisplayStart = 0;
|
|
|
|
- SetNeedsDraw ();
|
|
|
|
-
|
|
|
|
|
|
+ Address = 0;
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
private bool MoveLeft ()
|
|
private bool MoveLeft ()
|
|
{
|
|
{
|
|
- RedisplayLine (Address);
|
|
|
|
-
|
|
|
|
if (_leftSideHasFocus)
|
|
if (_leftSideHasFocus)
|
|
{
|
|
{
|
|
if (!_firstNibble)
|
|
if (!_firstNibble)
|
|
@@ -852,16 +819,6 @@ public class HexView : View, IDesignable
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
- if (Address - 1 < DisplayStart)
|
|
|
|
- {
|
|
|
|
- SetDisplayStart (_displayStart - BytesPerLine);
|
|
|
|
- SetNeedsDraw ();
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- RedisplayLine (Address);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
Address--;
|
|
Address--;
|
|
|
|
|
|
return true;
|
|
return true;
|
|
@@ -869,8 +826,6 @@ public class HexView : View, IDesignable
|
|
|
|
|
|
private bool MoveRight ()
|
|
private bool MoveRight ()
|
|
{
|
|
{
|
|
- RedisplayLine (Address);
|
|
|
|
-
|
|
|
|
if (_leftSideHasFocus)
|
|
if (_leftSideHasFocus)
|
|
{
|
|
{
|
|
if (_firstNibble)
|
|
if (_firstNibble)
|
|
@@ -889,74 +844,24 @@ public class HexView : View, IDesignable
|
|
Address++;
|
|
Address++;
|
|
}
|
|
}
|
|
|
|
|
|
- if (Address >= DisplayStart + BytesPerLine * Viewport.Height)
|
|
|
|
- {
|
|
|
|
- SetDisplayStart (DisplayStart + BytesPerLine);
|
|
|
|
- SetNeedsDraw ();
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- RedisplayLine (Address);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
- private long GetEditedSize ()
|
|
|
|
- {
|
|
|
|
- if (_edits.Count == 0)
|
|
|
|
- {
|
|
|
|
- return _source!.Length;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- long maxEditAddress = _edits.Keys.Max ();
|
|
|
|
-
|
|
|
|
- return Math.Max (_source!.Length, maxEditAddress + 1);
|
|
|
|
- }
|
|
|
|
|
|
|
|
private bool MoveLeftStart ()
|
|
private bool MoveLeftStart ()
|
|
{
|
|
{
|
|
Address = Address / BytesPerLine * BytesPerLine;
|
|
Address = Address / BytesPerLine * BytesPerLine;
|
|
- SetNeedsDraw ();
|
|
|
|
|
|
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
private bool MoveUp (int bytes)
|
|
private bool MoveUp (int bytes)
|
|
{
|
|
{
|
|
- RedisplayLine (Address);
|
|
|
|
-
|
|
|
|
- if (Address - bytes > -1)
|
|
|
|
- {
|
|
|
|
- Address -= bytes;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (Address < DisplayStart)
|
|
|
|
- {
|
|
|
|
- SetDisplayStart (DisplayStart - bytes);
|
|
|
|
- SetNeedsDraw ();
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- RedisplayLine (Address);
|
|
|
|
- }
|
|
|
|
|
|
+ Address -= bytes;
|
|
|
|
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
- private void RedisplayLine (long pos)
|
|
|
|
- {
|
|
|
|
- if (BytesPerLine == 0)
|
|
|
|
- {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var delta = (int)(pos - DisplayStart);
|
|
|
|
- int line = delta / BytesPerLine;
|
|
|
|
-
|
|
|
|
- SetNeedsDraw (new (0, line, Viewport.Width, 1));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/// <inheritdoc />
|
|
/// <inheritdoc />
|
|
protected override bool OnAdvancingFocus (NavigationDirection direction, TabBehavior? behavior)
|
|
protected override bool OnAdvancingFocus (NavigationDirection direction, TabBehavior? behavior)
|
|
{
|
|
{
|
|
@@ -969,8 +874,8 @@ public class HexView : View, IDesignable
|
|
|| (direction == NavigationDirection.Backward && !_leftSideHasFocus))
|
|
|| (direction == NavigationDirection.Backward && !_leftSideHasFocus))
|
|
{
|
|
{
|
|
_leftSideHasFocus = !_leftSideHasFocus;
|
|
_leftSideHasFocus = !_leftSideHasFocus;
|
|
- RedisplayLine (Address);
|
|
|
|
_firstNibble = true;
|
|
_firstNibble = true;
|
|
|
|
+ SetNeedsDraw ();
|
|
|
|
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|