mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-14 14:24:10 +02:00
2443 lines
101 KiB
Beef
2443 lines
101 KiB
Beef
using System;
|
|
using System.Collections;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Globalization;
|
|
using Beefy.widgets;
|
|
using Beefy.theme;
|
|
using Beefy.theme.dark;
|
|
using Beefy.gfx;
|
|
using Beefy.geom;
|
|
using Beefy.events;
|
|
using Beefy.utils;
|
|
using Beefy;
|
|
using System.Diagnostics;
|
|
using System.Security.Cryptography;
|
|
|
|
namespace IDE.ui
|
|
{
|
|
public interface IBinaryDataContentProvider
|
|
{
|
|
bool IsReadOnly { get; }
|
|
uint8[] ReadBinaryData(int offset, int32 size);
|
|
void WriteBinaryData(uint8[] data, int offset);
|
|
}
|
|
|
|
class TestBinaryContentDataProvider : IBinaryDataContentProvider
|
|
{
|
|
public bool IsReadOnly { get { return true; } }
|
|
|
|
public uint8[] ReadBinaryData(int offset, int32 size)
|
|
{
|
|
uint8[] result = new uint8[size];
|
|
for (int32 i=0; i<size; ++i)
|
|
result[i] = (uint8)(((int32)offset + i) & 255);
|
|
return result;
|
|
}
|
|
public void WriteBinaryData(uint8[] data, int offset)
|
|
{
|
|
}
|
|
}
|
|
|
|
public class BinaryDataContent
|
|
{
|
|
public class LockedRange : IDisposable
|
|
{
|
|
bool mDisposed = false;
|
|
BinaryDataContent mContent;
|
|
public int mBaseOffset;
|
|
public uint8[] mData ~ delete _;
|
|
public bool mModified;
|
|
|
|
public this(BinaryDataContent content, int baseOffset, uint8[] data)
|
|
{
|
|
mContent = content;
|
|
mBaseOffset = baseOffset;
|
|
mData = data;
|
|
mModified = false;
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
Dispose(true);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
}
|
|
|
|
public void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
if (!mDisposed)
|
|
{
|
|
mContent.UnlockRange(this);
|
|
mDisposed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class Snapshot
|
|
{
|
|
public int mBaseOffset;
|
|
public uint8[] mData ~ delete _;
|
|
|
|
public this()
|
|
{
|
|
}
|
|
|
|
public this(LockedRange lockRange)
|
|
{
|
|
mBaseOffset = lockRange.mBaseOffset;
|
|
mData = new uint8[lockRange.mData.Count];
|
|
Array.Copy(lockRange.mData, 0, mData, 0, mData.Count);
|
|
}
|
|
}
|
|
|
|
public class BinaryChangeAction : UndoAction
|
|
{
|
|
BinaryDataWidget mWidget;
|
|
int mOffset;
|
|
uint8 mOldValue;
|
|
uint8 mNewValue;
|
|
uint8 mSubChar;
|
|
bool mInStrView;
|
|
|
|
public this(BinaryDataWidget widget, int offset, uint8 oldValue, uint8 newValue, uint8 subChar, bool inStrView)
|
|
{
|
|
mWidget = widget;
|
|
mOffset = offset;
|
|
mOldValue = oldValue;
|
|
mNewValue = newValue;
|
|
mSubChar = subChar;
|
|
mInStrView = inStrView;
|
|
}
|
|
|
|
public override bool Undo()
|
|
{
|
|
return mWidget.ApplyUndoAction(mOffset, mNewValue, mOldValue, mSubChar, mInStrView, false);
|
|
}
|
|
public override bool Redo()
|
|
{
|
|
return mWidget.ApplyUndoAction(mOffset, mOldValue, mNewValue, mSubChar, mInStrView, true);
|
|
}
|
|
}
|
|
|
|
public class SnapshotDiff
|
|
{
|
|
|
|
public int mBaseOffset;
|
|
public bool[] mChanged ~ delete _;
|
|
public Dictionary<int, bool> mExtraChanged ~ delete _;
|
|
|
|
public this()
|
|
{
|
|
}
|
|
|
|
public this(Snapshot oldShot, Snapshot newShot)
|
|
{
|
|
int oldShotLen = oldShot.mData != null ? (int)oldShot.mData.Count : 0;
|
|
int newShotLen = newShot.mData != null ? (int)newShot.mData.Count : 0;
|
|
|
|
int rangeMin = Math.Max(oldShot.mBaseOffset, newShot.mBaseOffset);
|
|
int rangeMax = Math.Min(oldShot.mBaseOffset+oldShotLen, newShot.mBaseOffset+newShotLen);
|
|
|
|
if (rangeMin < rangeMax)
|
|
{
|
|
int changeLen = rangeMax - rangeMin;
|
|
|
|
mBaseOffset = rangeMin;
|
|
mChanged = new bool[(int32)changeLen];
|
|
|
|
for (int i=0; i<changeLen; ++i)
|
|
mChanged[(int32)i] = oldShot.mData[(int32)(i + mBaseOffset - oldShot.mBaseOffset)] != newShot.mData[(int32)(i + mBaseOffset - newShot.mBaseOffset)];
|
|
}
|
|
}
|
|
|
|
public bool Changed(int offset)
|
|
{
|
|
bool changed = false;
|
|
if (mChanged != null)
|
|
{
|
|
if ((offset >= mBaseOffset) && (offset < (mBaseOffset + (int)mChanged.Count)))
|
|
changed = mChanged[(int32)(offset - mBaseOffset)];
|
|
}
|
|
if (!changed && mExtraChanged != null)
|
|
mExtraChanged.TryGetValue(offset, out changed);
|
|
return changed;
|
|
}
|
|
|
|
public void SetChanged(int offset)
|
|
{
|
|
if (mChanged != null)
|
|
{
|
|
if ((offset >= mBaseOffset) && (offset < (mBaseOffset + (int)mChanged.Count)))
|
|
{
|
|
mChanged[(int32)(offset - mBaseOffset)] = true;
|
|
return;
|
|
}
|
|
}
|
|
if (mExtraChanged == null)
|
|
mExtraChanged = new Dictionary<int,bool>();
|
|
mExtraChanged[offset] = true;
|
|
}
|
|
}
|
|
|
|
class Page
|
|
{
|
|
public int mPageKey;
|
|
public int mBaseOffset;
|
|
public uint8[] mPageData ~ delete _;
|
|
public int32 mAge;
|
|
public int32 mLockCount;
|
|
}
|
|
|
|
IBinaryDataContentProvider mProvider;
|
|
Dictionary<int, Page> mPageDict = new Dictionary<int, Page>() ~ { for (let page in _.Values) delete page; delete _; };
|
|
readonly int mPageSize;
|
|
public int mCacheRevision;
|
|
const int32 mMaxCachePages = 50;
|
|
|
|
public this(int pageSize, IBinaryDataContentProvider provider)
|
|
{
|
|
mPageSize = pageSize;
|
|
mProvider = provider;
|
|
mCacheRevision = 1;
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
|
|
}
|
|
|
|
public LockedRange LockRange(int offset, int size)
|
|
{
|
|
//System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
|
|
//sw.Start();
|
|
|
|
uint8[] data = new uint8[(int32)size];
|
|
|
|
int remainingSize = size;
|
|
int writeOfs = 0;
|
|
int readOfs = (int)offset % mPageSize;
|
|
int curPage = (int)offset / mPageSize;
|
|
|
|
while (remainingSize > 0UL)
|
|
{
|
|
Page p = GetPage(curPage);
|
|
++p.mLockCount;
|
|
|
|
int readSize = Math.Min((int)p.mPageData.Count - readOfs, remainingSize);
|
|
Array.Copy(p.mPageData, (int32)readOfs, data, (int32)writeOfs, (int32)readSize);
|
|
|
|
remainingSize -= readSize;
|
|
writeOfs += readSize;
|
|
readOfs = 0;
|
|
++curPage;
|
|
}
|
|
|
|
UpdatePageCache();
|
|
|
|
//sw.Stop();
|
|
//double elapsedMs = sw.Elapsed.TotalMilliseconds;
|
|
//if (elapsedMs > 0.001)
|
|
// Console.WriteLine("STOPWATCH(LockRange): {0}ms", elapsedMs);
|
|
|
|
return new LockedRange(this, offset, data);
|
|
}
|
|
|
|
public void UnlockRange(LockedRange range)
|
|
{
|
|
int remainingSize = (int)range.mData.Count;
|
|
int readOfs = 0;
|
|
int writeOfs = range.mBaseOffset % mPageSize;
|
|
int curPage = range.mBaseOffset / mPageSize;
|
|
|
|
while (remainingSize > 0UL)
|
|
{
|
|
Page p = GetPage(curPage);
|
|
//System.Diagnostics.Trace.Assert(p.mLockCount > 0);
|
|
|
|
int writeSize = Math.Min((int)p.mPageData.Count - writeOfs, remainingSize);
|
|
if (range.mModified && !mProvider.IsReadOnly)
|
|
{
|
|
Array.Copy(range.mData, (int32)readOfs, p.mPageData, (int32)writeOfs, (int32)writeSize);
|
|
CommitPage(p);
|
|
}
|
|
|
|
--p.mLockCount;
|
|
|
|
remainingSize -= writeSize;
|
|
readOfs += writeSize;
|
|
writeOfs = 0;
|
|
++curPage;
|
|
}
|
|
|
|
if (range.mModified)
|
|
{
|
|
IDEApp.sApp.mWatchPanel.MarkWatchesDirty(false);
|
|
}
|
|
}
|
|
|
|
Page GetPage(int pageNum)
|
|
{
|
|
Page p;
|
|
if (!mPageDict.TryGetValue(pageNum, out p))
|
|
{
|
|
p = new Page();
|
|
p.mPageKey = pageNum;
|
|
p.mBaseOffset = pageNum * mPageSize;
|
|
p.mPageData = mProvider.ReadBinaryData((int)p.mBaseOffset, (int32)mPageSize);
|
|
p.mLockCount = 0;
|
|
|
|
mPageDict[p.mPageKey] = p;
|
|
}
|
|
|
|
p.mAge = 0;
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
void CommitPage(Page p)
|
|
{
|
|
mProvider.WriteBinaryData(p.mPageData, (int)p.mBaseOffset);
|
|
}
|
|
|
|
void UpdatePageCache()
|
|
{
|
|
for (var p in mPageDict)
|
|
{
|
|
if (p.value.mLockCount == 0)
|
|
p.value.mAge++;
|
|
}
|
|
|
|
if (mPageDict.Count > mMaxCachePages)
|
|
{
|
|
List<Page> orderedPages = scope List<Page>();
|
|
for (var page in mPageDict.Values)
|
|
orderedPages.Add(page);
|
|
orderedPages.Sort(scope (lhs, rhs) => lhs.mAge <=> rhs.mAge);
|
|
|
|
for (int i = mMaxCachePages; i < orderedPages.Count; ++i)
|
|
{
|
|
var page = orderedPages[i];
|
|
if (page.mLockCount > 0)
|
|
continue;
|
|
mPageDict.Remove(page.mPageKey);
|
|
delete page;
|
|
}
|
|
|
|
/*List<Page> pages = mPageDict.Values.OrderBy(p => p.mAge).ToList();
|
|
for (int i=mMaxCachePages; i<mPageDict.Values.Count; ++i)
|
|
{
|
|
if (pages[i].mLockCount > 0)
|
|
continue;
|
|
|
|
mPageDict.Remove(pages[i].mPageKey);
|
|
}*/
|
|
}
|
|
}
|
|
|
|
public void ClearCache()
|
|
{
|
|
for (var p in mPageDict)
|
|
{
|
|
delete p.value;
|
|
//System.Diagnostics.Trace.Assert(p.Value.mLockCount == 0); //CDH cache clearing not currently threadsafe w/ respect to locked ranges
|
|
}
|
|
|
|
mPageDict.Clear();
|
|
++mCacheRevision;
|
|
}
|
|
}
|
|
|
|
#pragma warning disable 0067
|
|
|
|
public class BinaryDataWidget : DockedWidget
|
|
{
|
|
public enum AutoResizeType
|
|
{
|
|
Manual,
|
|
Auto,
|
|
Auto_Mul8,
|
|
}
|
|
|
|
public DarkButton mGotoButton;
|
|
public AutoResizeType mAutoResizeType = .Auto_Mul8;
|
|
public int mBytesPerDisplayLine;
|
|
public int mCurPosition;
|
|
double mCurPositionDisplayOffset; //0.0-1.0
|
|
double mShowPositionDisplayOffset; //0.0-1.0
|
|
Font mFont;
|
|
bool mCacheDirty;
|
|
bool mTrackedExprsDirty;
|
|
|
|
IBinaryDataContentProvider mProvider;
|
|
BinaryDataContent mContent ~ delete _;
|
|
|
|
public InfiniteScrollbar mInfiniteScrollbar;
|
|
public Insets mScrollbarInsets = new Insets() ~ delete _;
|
|
|
|
int mColumnDisplayStart;
|
|
int mColumnDisplayStride;
|
|
public int mColumnHeaderHeight;
|
|
int mStrViewDisplayStartOffset;
|
|
int mStrViewDisplayStride;
|
|
int mRegLabelDisplayStart;
|
|
|
|
//EditWidget mByteEditWidget;
|
|
Selection mCurHoverSelection = null ~ delete _;
|
|
//Selection mCurEditSelection = null;
|
|
Cursor mCurKeyCursor ~ delete _;
|
|
|
|
Cursor mSelection ~ delete _;
|
|
int32 mCurKeyCursorBlinkTicks = 0;
|
|
Cursor mDraggingSelectionStart = null;
|
|
//string mCurKeyCursorText = null;
|
|
|
|
BinaryDataContent.Snapshot mLastSnapshot = new BinaryDataContent.Snapshot() ~ delete _;
|
|
BinaryDataContent.SnapshotDiff mLastSnapshotDiff = new BinaryDataContent.SnapshotDiff() ~ delete _;
|
|
int mLastSnapshotCacheRevision = 0;
|
|
|
|
UndoManager mUndoManager = new UndoManager() ~ delete _;
|
|
|
|
List<TrackedExpression> mTrackedExprs = new List<TrackedExpression>() ~ DeleteContainerAndItems!(_);
|
|
public HashSet<String> mPinnedTrackedExprs = new HashSet<String>() ~ DeleteContainerAndItems!(_);
|
|
public delegate void TrackedExpressionsUpdatedDelegate(List<TrackedExpression> trackedExpressions);
|
|
public Event<TrackedExpressionsUpdatedDelegate> TrackedExpressionsUpdated ~ _.Dispose();
|
|
int mTrackedExprRangeStart;
|
|
int mTrackedExprRangeSize;
|
|
int mTrackedExprUpdateCnt;
|
|
float mDelayedClearTrackedExprsTimer;
|
|
|
|
public delegate void KeyCursorUpdatedDelegate();
|
|
public Event<KeyCursorUpdatedDelegate> OnKeyCursorUpdated ~ _.Dispose();
|
|
|
|
public delegate void ActiveWidthChangedDelegate();
|
|
public Event<ActiveWidthChangedDelegate> OnActiveWidthChanged ~ _.Dispose();
|
|
|
|
public Event<Action> mOnGotoAddress ~ _.Dispose();
|
|
|
|
public class Cursor
|
|
{
|
|
public int mSelStart;
|
|
public int mSelLength; //CDH TODO length ignored for now
|
|
public int32 mSubChar;
|
|
public bool mInStrView;
|
|
|
|
public this()
|
|
{
|
|
mSelStart = mSelLength = 0;
|
|
mSubChar = 0;
|
|
mInStrView = false;
|
|
}
|
|
|
|
public this(int selStart, int selLength, int subChar, bool inStrView)
|
|
{
|
|
mSelStart = selStart;
|
|
mSelLength = selLength;
|
|
mSubChar = (.)subChar;
|
|
mInStrView = inStrView;
|
|
}
|
|
|
|
public this(Selection fromSelection)
|
|
{
|
|
mSelStart = fromSelection.mSelStart;
|
|
mSelLength = fromSelection.mSelLength;
|
|
mSubChar = fromSelection.mSubChar;
|
|
mInStrView = fromSelection.mInStrView;
|
|
}
|
|
}
|
|
public class Selection
|
|
{
|
|
public Rect mBinRect;
|
|
public Rect mStrRect;
|
|
public int mSelStart;
|
|
public int mSelLength;
|
|
public int mBytePosX;
|
|
public int mBytePosY;
|
|
public int32 mSubChar;
|
|
public bool mInStrView;
|
|
|
|
public this()
|
|
{
|
|
mBinRect = Rect();
|
|
mStrRect = Rect();
|
|
mSelStart = mSelLength = 0;
|
|
mBytePosX = mBytePosY = 0;
|
|
mSubChar = 0;
|
|
mInStrView = false;
|
|
}
|
|
public this(Rect binRect, Rect strRect, int selStart, int selLength, int bytePosX, int bytePosY, int32 subChar, bool inStrView)
|
|
{
|
|
mBinRect = binRect;
|
|
mStrRect = strRect;
|
|
mSelStart = selStart;
|
|
mSelLength = selLength;
|
|
mSubChar = subChar;
|
|
mInStrView = inStrView;
|
|
}
|
|
}
|
|
|
|
public class TrackedExpression
|
|
{
|
|
public BinaryDataWidget mBinaryDataWidget;
|
|
public String mExpr ~ delete _;
|
|
public int mMemStart;
|
|
public int mSize;
|
|
public int mCurValue;
|
|
public String mError ~ delete _;
|
|
public String mDisplayName ~ delete _;
|
|
public uint32 mDisplayColor;
|
|
public bool mIsReg;
|
|
public bool mIsVisible;
|
|
public Rect? mDrawRect;
|
|
|
|
public this(BinaryDataWidget binaryDataWidget, StringView expr, int memStart, int size, StringView displayName, uint32 displayColor, bool isReg)
|
|
{
|
|
mBinaryDataWidget = binaryDataWidget;
|
|
mExpr = new String(expr);
|
|
mMemStart = memStart;
|
|
mSize = size;
|
|
mDisplayName = new String(displayName);
|
|
mDisplayColor = displayColor;
|
|
mIsReg = isReg;
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
|
|
}
|
|
|
|
public void Update(int visibleMemoryRangeStart, int visibleMemoryRangeLen)
|
|
{
|
|
if (mMemStart != 0UL)
|
|
{
|
|
mCurValue = mMemStart;
|
|
}
|
|
else
|
|
{
|
|
DeleteAndNullify!(mError);
|
|
|
|
String evalResultStr = scope String();
|
|
IDEApp.sApp.mDebugger.Evaluate(mExpr, evalResultStr, -1);
|
|
if (evalResultStr.StartsWith("!", StringComparison.Ordinal))
|
|
{
|
|
mError = new String(evalResultStr);
|
|
}
|
|
else
|
|
{
|
|
mError = null;
|
|
|
|
var lines = scope List<StringView>(evalResultStr.Split('\n'));
|
|
if (lines.Count > 0)
|
|
{
|
|
String valStr = scope String(lines[0]);
|
|
if (valStr.Contains(' '))
|
|
{
|
|
var tempSplit = scope List<StringView>(valStr.Split(' '));
|
|
valStr.Set(tempSplit[0]);
|
|
}
|
|
|
|
if (!EvalAddressExpression(valStr, out mCurValue))
|
|
mError = new String("!Integer expression required");
|
|
}
|
|
else
|
|
mError = new String("!Invalid expression format");
|
|
}
|
|
}
|
|
|
|
int rangeMin = Math.Max(visibleMemoryRangeStart, mCurValue);
|
|
int rangeMax = Math.Min(visibleMemoryRangeStart + visibleMemoryRangeLen, mCurValue + mSize);
|
|
mIsVisible = (rangeMax >= rangeMin) || (mBinaryDataWidget.mPinnedTrackedExprs.Contains(mDisplayName)) || (mDisplayName.StartsWith("$"));
|
|
}
|
|
|
|
//CDH TODO duplicate from memory panel. Need some shared utilities here?
|
|
bool EvalAddressExpression(String expr, out int position)
|
|
{
|
|
position = 0;
|
|
StringView parseStr = expr;
|
|
bool parsed = false;
|
|
|
|
NumberStyles numberStyle = .Number;
|
|
if (parseStr.StartsWith("0x", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
parseStr = StringView(parseStr, 2);
|
|
numberStyle = .HexNumber;
|
|
}
|
|
|
|
switch (uint64.Parse(parseStr, numberStyle))
|
|
{
|
|
case .Ok(let val):
|
|
position = (.)val;
|
|
parsed = true;
|
|
default:
|
|
}
|
|
|
|
return parsed;
|
|
}
|
|
|
|
}
|
|
|
|
public this(IBinaryDataContentProvider provider)
|
|
{
|
|
mGotoButton = new DarkButton();
|
|
mGotoButton.Label = "Goto...";
|
|
AddWidget(mGotoButton);
|
|
mGotoButton.mOnMouseClick.Add(new (evt) =>
|
|
{
|
|
mOnGotoAddress();
|
|
});
|
|
|
|
mProvider = provider;
|
|
if (mProvider == null)
|
|
mProvider = new TestBinaryContentDataProvider();
|
|
mContent = new BinaryDataContent(64, mProvider); //CDH TODO really low page size for now so we can test boundary conditions
|
|
|
|
mClipGfx = true;
|
|
mClipMouse = true;
|
|
|
|
mBytesPerDisplayLine = 16;
|
|
mCurPosition = 0x10000000;
|
|
mCurPositionDisplayOffset = 0.0f;
|
|
|
|
mColumnDisplayStart = 150;
|
|
mColumnDisplayStride = 25;
|
|
mColumnHeaderHeight = 23;
|
|
mStrViewDisplayStartOffset = 10;
|
|
mStrViewDisplayStride = 8;
|
|
mRegLabelDisplayStart = 110;
|
|
|
|
mFont = IDEApp.sApp.mCodeFont;//DarkTheme.sDarkTheme.mSmallFont;
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
}
|
|
|
|
public void InitScrollbar()
|
|
{
|
|
mInfiniteScrollbar = ThemeFactory.mDefault.CreateInfiniteScrollbar();
|
|
mInfiniteScrollbar.Init();
|
|
mInfiniteScrollbar.mOnInfiniteScrollEvent.Add(new => ApplyScrollDelta);
|
|
AddWidgetAtIndex(0, mInfiniteScrollbar);
|
|
}
|
|
|
|
public virtual void UpdateScrollbar()
|
|
{
|
|
if (mInfiniteScrollbar != null)
|
|
{
|
|
mInfiniteScrollbar.Resize(mWidth - mScrollbarInsets.mRight - mInfiniteScrollbar.mBaseSize, mScrollbarInsets.mTop, mInfiniteScrollbar.mBaseSize,
|
|
mHeight - mScrollbarInsets.mTop - mScrollbarInsets.mBottom);
|
|
|
|
float lineSpacing = GetLineSpacing();
|
|
int lineCount = Math.Max(1, (int)(mHeight / lineSpacing) - 3);
|
|
mInfiniteScrollbar.mScrollDeltaLevelAmount = (double)lineCount;
|
|
|
|
mInfiniteScrollbar.ScrollDeltaLevel(0);
|
|
mInfiniteScrollbar.ResizeContent();
|
|
}
|
|
|
|
//UpdateScrollbarData();
|
|
//ScrollPositionChanged();
|
|
}
|
|
|
|
public float GetActiveWidth()
|
|
{
|
|
return GS!(mColumnDisplayStart) + mBytesPerDisplayLine*GS!(mColumnDisplayStride) + GS!(mStrViewDisplayStartOffset) + mBytesPerDisplayLine*GS!(mStrViewDisplayStride) + GS!(10);
|
|
}
|
|
|
|
float GetLineSpacing()
|
|
{
|
|
return mFont.GetLineSpacing();// + 4.0f;
|
|
}
|
|
|
|
void SetColumns(int columnCount)
|
|
{
|
|
mBytesPerDisplayLine = columnCount;
|
|
if ((mBytesPerDisplayLine & 15) == 0)
|
|
mCurPosition &= ~15;
|
|
else if ((mBytesPerDisplayLine & 7) == 0)
|
|
mCurPosition &= ~7;
|
|
|
|
if (OnActiveWidthChanged.HasListeners)
|
|
OnActiveWidthChanged();
|
|
}
|
|
|
|
public void ShowMenu(float x, float y)
|
|
{
|
|
Menu menu = new Menu();
|
|
|
|
var menuItem = menu.AddItem("Column width");
|
|
var columnChoice = menuItem.AddItem("Auto");
|
|
if (mAutoResizeType == .Auto)
|
|
columnChoice.mIconImage = DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Check);
|
|
columnChoice.mOnMenuItemSelected.Add(new (evt) =>
|
|
{
|
|
mAutoResizeType = .Auto;
|
|
RehupSize();
|
|
});
|
|
|
|
columnChoice = menuItem.AddItem("Auto multiples of 8");
|
|
if (mAutoResizeType == .Auto_Mul8)
|
|
columnChoice.mIconImage = DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Check);
|
|
columnChoice.mOnMenuItemSelected.Add(new (evt) =>
|
|
{
|
|
mAutoResizeType = .Auto_Mul8;
|
|
RehupSize();
|
|
});
|
|
|
|
for (int32 c in scope int32[] ( 4, 8, 16, 32, 64 ))
|
|
{
|
|
columnChoice = menuItem.AddItem(StackStringFormat!("{0} bytes", c));
|
|
if ((mAutoResizeType == .Manual) && (c == (int32)mBytesPerDisplayLine))
|
|
columnChoice.mIconImage = DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Check);
|
|
columnChoice.mOnMenuItemSelected.Add(new (evt) =>
|
|
{
|
|
mAutoResizeType = .Manual;
|
|
SetColumns(c);
|
|
});
|
|
}
|
|
//menu.AddItem(); // separatator
|
|
|
|
//var menuItem = menu.AddItem("Fwee!");
|
|
//menuItem.mMenuItemSelectedHandler = (evt) => { Console.WriteLine("BLARGO MENU!"); };
|
|
|
|
/*menu.AddItem("Item 2");
|
|
menu.AddItem();
|
|
menu.AddItem("Item 3");*/
|
|
|
|
MenuWidget menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(menu);
|
|
|
|
menuWidget.Init(this, x, y);
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
CheckClearData();
|
|
++mCurKeyCursorBlinkTicks;
|
|
|
|
if (mHasFocus)
|
|
MarkDirty();
|
|
|
|
if (mDelayedClearTrackedExprsTimer > 0.0f)
|
|
{
|
|
mDelayedClearTrackedExprsTimer -= 0.01f;
|
|
if (mDelayedClearTrackedExprsTimer <= 0.0f)
|
|
{
|
|
ClearTrackedExprs(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void DrawMulticolorOutlineBox(Graphics g, uint32[] colors, float stride, float x, float y, float width, float height, float thickness = 1, float phase = 0)
|
|
{
|
|
if (colors.Count == 0)
|
|
return;
|
|
|
|
List<Rect>[] colorRects = new List<Rect>[colors.Count];
|
|
for (int32 i=0; i<colorRects.Count; ++i)
|
|
colorRects[i] = new List<Rect>();
|
|
|
|
float usePhase = phase;
|
|
usePhase = Math.Max(usePhase, 0.0f);
|
|
int32 curColorIndex = (int32)usePhase;
|
|
usePhase -= (float)curColorIndex;
|
|
curColorIndex %= (int32)colors.Count;
|
|
|
|
float curX = x;
|
|
float curY = y;
|
|
|
|
for (int32 iPart=0; iPart<4; ++iPart)
|
|
{
|
|
float remainingLen = ((iPart & 1) != 0) ? width : height;
|
|
|
|
while (remainingLen > 0)
|
|
{
|
|
float spanWidth = stride - (stride * usePhase);
|
|
int32 colorInc = 1;
|
|
if (spanWidth > remainingLen)
|
|
{
|
|
usePhase = 1.0f - ((spanWidth - remainingLen) / stride);
|
|
spanWidth = remainingLen;
|
|
colorInc = 0;
|
|
}
|
|
else
|
|
{
|
|
usePhase = 0;
|
|
}
|
|
|
|
switch (iPart)
|
|
{
|
|
case 0:
|
|
{
|
|
colorRects[curColorIndex].Add(Rect(curX, curY, thickness, spanWidth));
|
|
curY += spanWidth;
|
|
}
|
|
break;
|
|
case 1:
|
|
{
|
|
colorRects[curColorIndex].Add(Rect(curX, curY, spanWidth, thickness));
|
|
curX += spanWidth;
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
colorRects[curColorIndex].Add(Rect(curX, curY - spanWidth + thickness, thickness, spanWidth));
|
|
curY -= spanWidth;
|
|
}
|
|
break;
|
|
case 3:
|
|
{
|
|
colorRects[curColorIndex].Add(Rect(curX - spanWidth + thickness, curY, spanWidth, thickness));
|
|
curX -= spanWidth;
|
|
}
|
|
break;
|
|
}
|
|
|
|
curColorIndex = (curColorIndex + colorInc) % (int32)colors.Count;
|
|
remainingLen -= spanWidth;
|
|
}
|
|
}
|
|
|
|
for (int32 iColor=0; iColor<colorRects.Count; ++iColor)
|
|
{
|
|
var rects = colorRects[iColor];
|
|
|
|
using (g.PushColor(colors[iColor]))
|
|
{
|
|
for (var r in rects)
|
|
g.FillRect(r.mX, r.mY, r.mWidth, r.mHeight);
|
|
}
|
|
}
|
|
}
|
|
|
|
public BinaryDataContent.LockedRange GetLockedRangeAtCursor(int32 size)
|
|
{
|
|
if (mCurKeyCursor == null)
|
|
return null;
|
|
|
|
return mContent.LockRange(mCurKeyCursor.mSelStart, (int)size);
|
|
}
|
|
|
|
class CoverageEntry
|
|
{
|
|
public List<TrackedExpression> mRegCoverage;
|
|
public List<TrackedExpression> mVarCoverage;
|
|
public bool mIsSelected;
|
|
public bool mIsShared;
|
|
}
|
|
|
|
struct TrackedEntry
|
|
{
|
|
public float mStartY;
|
|
public float mGoalY;
|
|
public int mLineIdx;
|
|
public int mRegRankForLine;
|
|
public int mColIdx;
|
|
|
|
public this(float startY, float goalY, int lineIdx, int regRankForLine, int colIdx)
|
|
{
|
|
mStartY = startY;
|
|
mGoalY = goalY;
|
|
mLineIdx = lineIdx;
|
|
mRegRankForLine = regRankForLine;
|
|
mColIdx = colIdx;
|
|
}
|
|
}
|
|
|
|
public override void Draw(Graphics g)
|
|
{
|
|
using (g.PushClip(0, 0, mWidth - GS!(18), mHeight - GS!(18)))
|
|
DoDraw(g);
|
|
}
|
|
|
|
public void DoDraw(Graphics g)
|
|
{
|
|
base.Draw(g);
|
|
|
|
for (var te in mTrackedExprs)
|
|
te.mDrawRect = null;
|
|
|
|
int barThickness = (int)GS!(1.5f);
|
|
|
|
/*
|
|
//g.DrawString(string.Format("CacheRev: {0}", mContent.mCacheRevision), 150, 0);
|
|
if (mInfiniteScrollbar != null)
|
|
{
|
|
g.DrawString(string.Format("Thumb: {0}, Accel: {1}, Vel: {2}, DO: {3}", mInfiniteScrollbar.mScrollThumbFrac, mInfiniteScrollbar.mScrollAccel, mInfiniteScrollbar.mScrollVelocity, mCurPositionDisplayOffset), 150, 30);
|
|
}
|
|
*/
|
|
// column header
|
|
using (g.PushClip(0, 0, mWidth, GS!(mColumnHeaderHeight)))
|
|
{
|
|
using (g.PushColor(0xFFFFFFFF))
|
|
{
|
|
g.SetFont(mFont);
|
|
float strViewColumnStart = GS!(mColumnDisplayStart) + mBytesPerDisplayLine*GS!(mColumnDisplayStride) + GS!(mStrViewDisplayStartOffset);
|
|
|
|
for (int i=0; i<mBytesPerDisplayLine; ++i)
|
|
{
|
|
g.DrawString(StackStringFormat!("{0:X1}", i), GS!(mColumnDisplayStart) + i*GS!(mColumnDisplayStride) + GS!(mColumnDisplayStride)*0.125f - ((i > 0xF) ? GS!(4) : 0), GS!(3), FontAlign.Left);
|
|
g.DrawString(StackStringFormat!("{0:X1}", i & 0xF), strViewColumnStart + i*GS!(mStrViewDisplayStride), GS!(3), FontAlign.Left);
|
|
}
|
|
}
|
|
}
|
|
|
|
// binary content
|
|
|
|
float lineSpacing = GetLineSpacing();
|
|
int lineCount = (int)(mHeight / lineSpacing) + 3;
|
|
int minAddr = mCurPosition;
|
|
int maxAddr = mCurPosition + lineCount*mBytesPerDisplayLine;
|
|
BumpAllocator bumpAlloc = scope BumpAllocator();
|
|
|
|
using (g.PushClip(0, GS!(mColumnHeaderHeight) + GS!(2), mWidth, mHeight - GS!(mColumnHeaderHeight) - GS!(4)))
|
|
{
|
|
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.EditBox), 0, GS!(mColumnHeaderHeight), mWidth, mHeight - GS!(mColumnHeaderHeight));
|
|
|
|
if (!gApp.mDebugger.mIsRunning)
|
|
return;
|
|
|
|
float displayAdj = (float)(-mShowPositionDisplayOffset * lineSpacing);
|
|
using (g.PushTranslate(0, displayAdj))
|
|
{
|
|
using (g.PushColor(0xFFFFFFFF))
|
|
{
|
|
//ulong lineStart = mCurPosition / mBytesPerDisplayLine;
|
|
int lockSize = lineCount * mBytesPerDisplayLine;
|
|
#unwarn
|
|
float strViewColumnStart = GS!(mColumnDisplayStart) + mBytesPerDisplayLine*GS!(mColumnDisplayStride) + GS!(mStrViewDisplayStartOffset);
|
|
#unwarn
|
|
Selection curKeySelection = (mCurKeyCursor != null) ? GetSelectionForCursor(mCurKeyCursor, false) : null;
|
|
defer { delete curKeySelection; }
|
|
|
|
//ulong lineEnd = Math.Min((ulong)long.MaxValue, lineStart + (ulong)(mHeight / lineSpacing) + 3);
|
|
|
|
var lockRange = mContent.LockRange(mCurPosition, lockSize);
|
|
defer delete lockRange;
|
|
|
|
TrackedBlock:
|
|
{
|
|
if (mLastSnapshotCacheRevision != mContent.mCacheRevision)
|
|
{
|
|
defer delete mLastSnapshotDiff;
|
|
defer delete mLastSnapshot;
|
|
var curSnapshot = new BinaryDataContent.Snapshot(lockRange);
|
|
mLastSnapshotDiff = new BinaryDataContent.SnapshotDiff(mLastSnapshot, curSnapshot);
|
|
mLastSnapshot = curSnapshot;
|
|
mLastSnapshotCacheRevision = mContent.mCacheRevision;
|
|
}
|
|
|
|
//TrackedE
|
|
|
|
//var trackedCoverage = scope Dictionary<int, CoverageEntry>();
|
|
CoverageEntry[] trackedCoverage = new:bumpAlloc CoverageEntry[maxAddr - minAddr];
|
|
|
|
bool GetTrackedCoverage(int addr, out CoverageEntry value)
|
|
{
|
|
if ((addr < minAddr) || (addr >= maxAddr))
|
|
{
|
|
value = default;
|
|
return false;
|
|
}
|
|
value = trackedCoverage[addr - minAddr];
|
|
return value != null;
|
|
}
|
|
|
|
CoverageEntry DuplicateCoverageEntry(CoverageEntry prevEntry)
|
|
{
|
|
var entry = new:bumpAlloc CoverageEntry();
|
|
if (prevEntry.mRegCoverage != null)
|
|
{
|
|
entry.mRegCoverage = new:bumpAlloc List<TrackedExpression>();
|
|
for (var val in prevEntry.mRegCoverage)
|
|
entry.mRegCoverage.Add(val);
|
|
}
|
|
if (prevEntry.mVarCoverage != null)
|
|
{
|
|
entry.mVarCoverage = new:bumpAlloc List<TrackedExpression>();
|
|
for (var val in prevEntry.mVarCoverage)
|
|
entry.mVarCoverage.Add(val);
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
//var trackedRegCoverage = new Dictionary<ulong, List<TrackedExpression>>();
|
|
//var trackedVarCoverage = new Dictionary<ulong, List<TrackedExpression>>();
|
|
//var trackedRegRanks = scope Dictionary<TrackedExpression, int32>();
|
|
//var trackedVarRanks = scope Dictionary<TrackedExpression, int32>();
|
|
for (var te in mTrackedExprs)
|
|
{
|
|
if (te.mError != null)
|
|
continue;
|
|
if (!te.mIsVisible)
|
|
continue;
|
|
|
|
if (te.mDisplayName.StartsWith("$"))
|
|
continue;
|
|
|
|
TrackedEntryBlock: do
|
|
{
|
|
int useSize = te.mIsReg ? 1 : te.mSize;
|
|
int lowAddr = Math.Max(te.mCurValue, minAddr);
|
|
int hiAddr = Math.Min(te.mCurValue + useSize, maxAddr);
|
|
if (lowAddr == hiAddr)
|
|
break TrackedEntryBlock;
|
|
|
|
CoverageEntry entry = new:bumpAlloc CoverageEntry();
|
|
entry.mIsShared = true;
|
|
for (int ofs = lowAddr; ofs < hiAddr; ++ofs)
|
|
{
|
|
int idx = ofs - minAddr;
|
|
|
|
var curCoverage = trackedCoverage[idx];
|
|
if (curCoverage == null)
|
|
{
|
|
curCoverage = entry;
|
|
}
|
|
else if (curCoverage.mIsShared)
|
|
{
|
|
curCoverage = DuplicateCoverageEntry(curCoverage);
|
|
}
|
|
trackedCoverage[idx] = curCoverage;
|
|
|
|
List<TrackedExpression> offsetCoverage = te.mIsReg ? curCoverage.mRegCoverage : curCoverage.mVarCoverage;
|
|
if (offsetCoverage == null)
|
|
{
|
|
offsetCoverage = new:bumpAlloc List<TrackedExpression>();
|
|
if (te.mIsReg)
|
|
curCoverage.mRegCoverage = offsetCoverage;
|
|
else
|
|
curCoverage.mVarCoverage = offsetCoverage;
|
|
}
|
|
|
|
offsetCoverage.Add(te);
|
|
}
|
|
}
|
|
}
|
|
if (mSelection != null)
|
|
{
|
|
int lowAddr = Math.Max(mSelection.mSelStart, minAddr);
|
|
int hiAddr = Math.Min(mSelection.mSelStart + mSelection.mSelLength, maxAddr);
|
|
for (int ofs = lowAddr; ofs < hiAddr; ++ofs)
|
|
{
|
|
CoverageEntry entry = trackedCoverage[ofs - minAddr];
|
|
if (entry == null)
|
|
entry = new:bumpAlloc CoverageEntry();
|
|
else if (entry.mIsShared)
|
|
entry = DuplicateCoverageEntry(entry);
|
|
entry.mIsSelected = true;
|
|
trackedCoverage[ofs - minAddr] = entry;
|
|
}
|
|
}
|
|
|
|
var trackedRegYDict = scope Dictionary<TrackedExpression, TrackedEntry>(); // start Y, goal Y, lineIdx, regRankForLine, colIdx
|
|
bool[] trackedRegYShortLines = scope bool[lineCount]; // in case we have a reg arrow at the first byte of the row
|
|
var trackedRegsByLine = scope List<TrackedExpression>[lineCount];
|
|
|
|
for (int iPass=0; iPass<2; ++iPass)
|
|
{
|
|
float curTrackedRegY = 0.0f;
|
|
|
|
for (int lineIdx = 0; lineIdx < lineCount; lineIdx++)
|
|
{
|
|
int baseOffset = lineIdx * mBytesPerDisplayLine;
|
|
|
|
curTrackedRegY = Math.Max(curTrackedRegY, GS!(mColumnHeaderHeight) + (lineIdx * lineSpacing));
|
|
|
|
if (iPass == 0)
|
|
{
|
|
//g.SetFont(DarkTheme.sDarkTheme.mSmallFont);
|
|
g.SetFont(IDEApp.sApp.mTinyCodeFont);
|
|
var str = scope String();
|
|
str.AppendF("{0:X16}", mCurPosition + baseOffset);
|
|
str.Insert(8, '\'');
|
|
int spaceCount = 0;
|
|
for (int i < 7)
|
|
{
|
|
if (str[i] == '0')
|
|
spaceCount++;
|
|
}
|
|
str.Remove(0, spaceCount);
|
|
|
|
g.DrawString(str, GS!(4), GS!(mColumnHeaderHeight) + (lineIdx * lineSpacing), FontAlign.Left);
|
|
|
|
g.SetFont(mFont);
|
|
|
|
if (curKeySelection != null)
|
|
{
|
|
int relStart = (int)curKeySelection.mSelStart - (int)mCurPosition;
|
|
if ((relStart - (relStart % mBytesPerDisplayLine)) == baseOffset)
|
|
{
|
|
using (g.PushColor(0x40808000))
|
|
{
|
|
g.FillRect(GS!(mColumnDisplayStart), curKeySelection.mBinRect.mY - displayAdj, (strViewColumnStart + mBytesPerDisplayLine*GS!(mStrViewDisplayStride)) - mColumnDisplayStart, curKeySelection.mBinRect.mHeight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i=0; i<mBytesPerDisplayLine; ++i)
|
|
{
|
|
if (iPass == 1)
|
|
{
|
|
// binary view
|
|
|
|
IDisposable colorScope = null;
|
|
uint8 val = lockRange.mData[baseOffset + i];
|
|
if (mLastSnapshotDiff.Changed(mCurPosition + baseOffset + i))
|
|
{
|
|
colorScope = g.PushColor(0xff60C0D0);
|
|
}
|
|
|
|
var str = scope String();
|
|
str.AppendF("{0:X2}", val);
|
|
g.DrawString(str, GS!(mColumnDisplayStart) + i*GS!(mColumnDisplayStride), GS!(mColumnHeaderHeight) + (lineIdx * lineSpacing), FontAlign.Left);
|
|
if (colorScope != null)
|
|
{
|
|
colorScope.Dispose();
|
|
colorScope = null;
|
|
}
|
|
|
|
// string view
|
|
|
|
char8 valChar = (char8)(val);
|
|
if (valChar.IsControl || (valChar == '\u{2028}') || (valChar == '\u{2029}')) // unicode line/paragraph separators, also unprintable
|
|
{
|
|
valChar = '.';
|
|
colorScope = g.PushColor(0xffffa000);
|
|
}
|
|
|
|
str.Clear();
|
|
str.Append(valChar);
|
|
g.DrawString(str, strViewColumnStart + i*GS!(mStrViewDisplayStride), GS!(mColumnHeaderHeight) + (lineIdx * lineSpacing), FontAlign.Left);
|
|
if (colorScope != null)
|
|
{
|
|
colorScope.Dispose();
|
|
colorScope = null;
|
|
}
|
|
|
|
if (mCurHoverSelection != null)
|
|
{
|
|
if ((mCurPosition + baseOffset + i) == mCurHoverSelection.mSelStart)
|
|
{
|
|
Rect primaryRect = mCurHoverSelection.mInStrView ? mCurHoverSelection.mStrRect : mCurHoverSelection.mBinRect;
|
|
Rect secondaryRect = mCurHoverSelection.mInStrView ? mCurHoverSelection.mBinRect : mCurHoverSelection.mStrRect;
|
|
int cursorBarXAdj = mCurHoverSelection.mInStrView ? 0 : 3;
|
|
|
|
using (g.PushColor(0xffffffff))
|
|
{
|
|
g.FillRect(primaryRect.mX + cursorBarXAdj + GS!(8)*mCurHoverSelection.mSubChar + GS!(1), primaryRect.mY - displayAdj, GS!(2), primaryRect.mHeight);
|
|
}
|
|
using (g.PushColor(0xffc0c0c0))
|
|
{
|
|
g.OutlineRect(secondaryRect.mX, secondaryRect.mY - displayAdj, secondaryRect.mWidth, secondaryRect.mHeight, GS!(1));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool hasFocus = mHasFocus;
|
|
if (!hasFocus)
|
|
{
|
|
if (mWidgetWindow.mHasFocus)
|
|
{
|
|
if (mWidgetWindow.mFocusWidget is MemoryRepListView)
|
|
hasFocus = true;
|
|
else if (mWidgetWindow.mFocusWidget is MemoryPanel)
|
|
hasFocus = true;
|
|
}
|
|
}
|
|
|
|
if ((curKeySelection != null) && (hasFocus))
|
|
{
|
|
if ((mCurPosition + baseOffset + i) == curKeySelection.mSelStart)
|
|
{
|
|
Rect primaryRect = curKeySelection.mInStrView ? curKeySelection.mStrRect : curKeySelection.mBinRect;
|
|
Rect secondaryRect = curKeySelection.mInStrView ? curKeySelection.mBinRect : curKeySelection.mStrRect;
|
|
|
|
using (g.PushColor(0x80A0A0A0))
|
|
{
|
|
g.FillRect(secondaryRect.mX, secondaryRect.mY - displayAdj, secondaryRect.mWidth, secondaryRect.mHeight);
|
|
}
|
|
|
|
float brightness = (float)Math.Cos(Math.Max(0.0f, mCurKeyCursorBlinkTicks - 20) / 9.0f);
|
|
brightness = Math.Max(0, Math.Min(1.0f, brightness * 2.0f + 1.6f));
|
|
uint32 cursorBarColor = 0xffffff + ((uint32)(brightness * 128) << 24);
|
|
int cursorBarXAdj = curKeySelection.mInStrView ? 0 : GS!(3);
|
|
using (g.PushColor(cursorBarColor))
|
|
{
|
|
g.FillRect(primaryRect.mX + cursorBarXAdj + GS!(8)*mCurKeyCursor.mSubChar, primaryRect.mY - displayAdj, GS!(2), primaryRect.mHeight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iPass == 0)
|
|
{
|
|
CoverageEntry coverageEntry = null;
|
|
if (GetTrackedCoverage(mCurPosition + baseOffset + i, out coverageEntry))
|
|
{
|
|
List<TrackedExpression> coverage = coverageEntry.mRegCoverage;
|
|
if (coverage != null)
|
|
{
|
|
List<TrackedExpression> usedRanks = scope List<TrackedExpression>();
|
|
for (TrackedExpression c in coverage)
|
|
{
|
|
if (c != null)
|
|
usedRanks.Add(c);
|
|
}
|
|
|
|
Cursor tempCursor = scope Cursor(mCurPosition + baseOffset + i, 1, 0, false);
|
|
Selection tempSel = GetSelectionForCursor(tempCursor, false);
|
|
if (tempSel != null)
|
|
{
|
|
defer delete tempSel;
|
|
|
|
//int cursorBarXAdj = 3;
|
|
Rect primaryRect = tempSel.mBinRect;
|
|
float imgX = primaryRect.mX - GS!(10);
|
|
float imgY = primaryRect.mY - displayAdj - GS!(1);
|
|
|
|
//System.Diagnostics.Trace.Assert(usedRanks.Count > 0);
|
|
if (usedRanks.Count == 1)
|
|
{
|
|
using (g.PushColor(usedRanks[0].mDisplayColor))
|
|
{
|
|
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.MemoryArrowSingle), imgX, imgY);
|
|
}
|
|
}
|
|
else if (usedRanks.Count == 2)
|
|
{
|
|
using (g.PushColor(usedRanks[0].mDisplayColor))
|
|
{
|
|
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.MemoryArrowDoubleTop), imgX, imgY);
|
|
}
|
|
using (g.PushColor(usedRanks[1].mDisplayColor))
|
|
{
|
|
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.MemoryArrowDoubleBottom), imgX, imgY);
|
|
}
|
|
}
|
|
else if (usedRanks.Count == 3)
|
|
{
|
|
using (g.PushColor(usedRanks[0].mDisplayColor))
|
|
{
|
|
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.MemoryArrowTripleTop), imgX, imgY);
|
|
}
|
|
using (g.PushColor(usedRanks[1].mDisplayColor))
|
|
{
|
|
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.MemoryArrowTripleMiddle), imgX, imgY);
|
|
}
|
|
using (g.PushColor(usedRanks[2].mDisplayColor))
|
|
{
|
|
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.MemoryArrowTripleBottom), imgX, imgY);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.MemoryArrowRainbow), imgX, imgY);
|
|
}
|
|
}
|
|
}
|
|
|
|
coverage = coverageEntry.mVarCoverage;
|
|
if (coverage != null)
|
|
{
|
|
List<TrackedExpression> usedRanks = scope List<TrackedExpression>();
|
|
for (TrackedExpression c in coverage)
|
|
{
|
|
if (c != null)
|
|
usedRanks.Add(c);
|
|
}
|
|
|
|
bool neighborCoverageFunc(int offsetAdj)
|
|
{
|
|
CoverageEntry neighborEntry = null;
|
|
bool showBorder = true;
|
|
if (GetTrackedCoverage((((int)mCurPosition + baseOffset + i) + offsetAdj), out neighborEntry))
|
|
{
|
|
List<TrackedExpression> neighborCoverage = neighborEntry.mVarCoverage;
|
|
if (neighborCoverage != null)
|
|
{
|
|
showBorder = false;
|
|
|
|
List<TrackedExpression> testRanks = scope List<TrackedExpression>();
|
|
for (TrackedExpression c in neighborCoverage)
|
|
{
|
|
if (c != null)
|
|
testRanks.Add(c);
|
|
}
|
|
if (testRanks.Count != usedRanks.Count)
|
|
{
|
|
showBorder = true;
|
|
}
|
|
else
|
|
{
|
|
for (int iRank=0; iRank<testRanks.Count; ++iRank)
|
|
{
|
|
if (testRanks[iRank] != usedRanks[iRank])
|
|
{
|
|
showBorder = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return showBorder;
|
|
}
|
|
|
|
bool leftBorder = neighborCoverageFunc(-1);
|
|
bool rightBorder = neighborCoverageFunc(1);
|
|
|
|
Cursor tempCursor = scope Cursor(mCurPosition + baseOffset + i, 1, 0, false);
|
|
Selection tempSel = GetSelectionForCursor(tempCursor, false);
|
|
if (tempSel != null)
|
|
{
|
|
defer delete tempSel;
|
|
//int cursorBarXAdj = 3;
|
|
Rect primaryRect = tempSel.mBinRect;
|
|
float rectX = primaryRect.mX;
|
|
float rectY = primaryRect.mY - displayAdj;
|
|
float rectW = primaryRect.mWidth + (rightBorder ? 0 : 1);
|
|
float rectH = primaryRect.mHeight - 1;
|
|
|
|
if ((i == 0) || leftBorder)
|
|
{
|
|
rectX += 4;
|
|
rectW -= 4;
|
|
}
|
|
if ((i == mBytesPerDisplayLine - 1) || rightBorder)
|
|
{
|
|
rectW -= 4;
|
|
}
|
|
|
|
Debug.Assert(usedRanks.Count > 0);
|
|
using (g.PushColor(usedRanks[0].mDisplayColor))
|
|
{
|
|
g.FillRect(rectX, rectY, rectW, 1);
|
|
g.FillRect(rectX, rectY + rectH, rectW, 1);
|
|
|
|
if (leftBorder)
|
|
g.FillRect(rectX, rectY + 1, 1, rectH - 1);
|
|
if (rightBorder)
|
|
g.FillRect(rectX + rectW, rectY, 1, rectH + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (coverageEntry.mIsSelected)
|
|
{
|
|
bool neighborCoverageFunc(int offsetAdj)
|
|
{
|
|
CoverageEntry neighborEntry = null;
|
|
bool showBorder = true;
|
|
if (GetTrackedCoverage(((int)mCurPosition + baseOffset + i) + (int)offsetAdj, out neighborEntry))
|
|
{
|
|
showBorder = !neighborEntry.mIsSelected;
|
|
}
|
|
return showBorder;
|
|
}
|
|
|
|
bool leftBorder = neighborCoverageFunc(-1);
|
|
bool rightBorder = neighborCoverageFunc(1);
|
|
|
|
Cursor tempCursor = scope Cursor(mCurPosition + baseOffset + i, 1, 0, false);
|
|
Selection tempSel = GetSelectionForCursor(tempCursor, false);
|
|
if (tempSel != null)
|
|
{
|
|
defer delete tempSel;
|
|
//int cursorBarXAdj = 3;
|
|
Rect primaryRect = tempSel.mBinRect;
|
|
float rectX = primaryRect.mX;
|
|
float rectY = primaryRect.mY - displayAdj;
|
|
float rectW = primaryRect.mWidth + (rightBorder ? 0 : 1);
|
|
float rectH = primaryRect.mHeight - 1;
|
|
|
|
rectY += 1;
|
|
rectH -= 1;
|
|
if (leftBorder)
|
|
{
|
|
rectX += 1;
|
|
rectW -= 1;
|
|
}
|
|
|
|
if ((i == 0) || leftBorder)
|
|
{
|
|
rectX += 4;
|
|
rectW -= 4;
|
|
}
|
|
if ((i == mBytesPerDisplayLine - 1) || rightBorder)
|
|
{
|
|
rectW -= 4;
|
|
}
|
|
|
|
using (g.PushColor(0xFF2f5c88)) //CDH TODO: same as DarkEditWidgetContent.mHiliteColor, link symbolically?
|
|
{
|
|
g.FillRect(rectX, rectY, rectW, rectH);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iPass == 0)
|
|
{
|
|
for (var te in mTrackedExprs)
|
|
{
|
|
if (te.mError != null)
|
|
continue;
|
|
if (!te.mIsVisible)
|
|
continue;
|
|
if (!te.mIsReg)
|
|
continue;
|
|
|
|
if ((mCurPosition + baseOffset + i) == te.mCurValue)
|
|
{
|
|
Cursor tempCursor = scope Cursor(te.mCurValue, 1, 0, false);
|
|
Selection tempSel = GetSelectionForCursor(tempCursor, false);
|
|
if (tempSel != null)
|
|
{
|
|
defer delete tempSel;
|
|
var useFont = IDEApp.sApp.mTinyCodeFont;
|
|
//float strWidth = useFont.GetWidth(te.mExpr);
|
|
|
|
//int cursorBarXAdj = 3;
|
|
//Rect primaryRect = tempSel.mBinRect;
|
|
//float barX = primaryRect.mX + cursorBarXAdj;
|
|
//float barY = primaryRect.mY - displayAdj;
|
|
|
|
if (trackedRegsByLine[lineIdx] == null)
|
|
trackedRegsByLine[lineIdx] = scope:TrackedBlock List<TrackedExpression>();
|
|
|
|
int regRankForLine = trackedRegsByLine[lineIdx].Count;
|
|
trackedRegsByLine[lineIdx].Add(te);
|
|
|
|
trackedRegYDict[te] = TrackedEntry(curTrackedRegY, GS!(mColumnHeaderHeight) + (lineIdx * lineSpacing), lineIdx, regRankForLine, i);
|
|
curTrackedRegY += useFont.GetLineSpacing() + 4;
|
|
|
|
if (i == 0)
|
|
trackedRegYShortLines[lineIdx] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for (var kvp in trackedRegYDict)
|
|
{
|
|
var te = kvp.key;
|
|
float barX = GS!(mRegLabelDisplayStart);
|
|
float barY = kvp.value.mStartY;
|
|
float barGoalY = kvp.value.mGoalY;
|
|
int lineIdx = kvp.value.mLineIdx;
|
|
int regRankForLine = kvp.value.mRegRankForLine;
|
|
int colIdx = kvp.value.mColIdx;
|
|
var useFont = IDEApp.sApp.mTinyCodeFont;
|
|
float strWidth = useFont.GetWidth(te.mExpr);
|
|
bool shortLine = trackedRegYShortLines[lineIdx];
|
|
var useLineSpacing = useFont.GetLineSpacing();
|
|
|
|
te.mDrawRect = Rect(barX, barY, strWidth + GS!(2), useLineSpacing + GS!(2));
|
|
|
|
if (te.mDisplayName.StartsWith("$"))
|
|
continue;
|
|
|
|
using (g.PushColor(te.mDisplayColor))
|
|
{
|
|
var regsOnThisLine = trackedRegsByLine[lineIdx].Count;
|
|
float barYAdj = barY + useLineSpacing*0.5f + GS!(1);
|
|
float barGoalYAdj = barGoalY + useLineSpacing*0.5f + GS!(1);
|
|
if (regsOnThisLine == 2)
|
|
barGoalYAdj -= regsOnThisLine - regRankForLine*5;
|
|
else if (regsOnThisLine == 3)
|
|
barGoalYAdj -= regsOnThisLine - regRankForLine*3;
|
|
else
|
|
barGoalYAdj -= regsOnThisLine/2 - regRankForLine;
|
|
|
|
int desiredLineWidth = shortLine ? GS!(5) : GS!(9);
|
|
if (Math.Abs(barYAdj - barGoalYAdj) < 0.01f)
|
|
{
|
|
g.FillRect(barX + strWidth, barGoalYAdj, GS!(mColumnDisplayStart) - (barX + strWidth) - GS!(7), barThickness);
|
|
}
|
|
else
|
|
{
|
|
int firstPartWidth = GS!(2) + regRankForLine;
|
|
g.FillRect(barX + strWidth, barYAdj, firstPartWidth, barThickness);
|
|
g.FillRect(barX + strWidth + firstPartWidth, barGoalYAdj + barThickness, barThickness, barYAdj - barGoalYAdj);
|
|
g.FillRect(barX + strWidth + firstPartWidth, barGoalYAdj, desiredLineWidth - firstPartWidth, barThickness);
|
|
}
|
|
|
|
for (int i=1; i<colIdx; ++i)
|
|
{
|
|
g.FillRect(GS!(mColumnDisplayStart) + i*GS!(mColumnDisplayStride)-GS!(6), barGoalYAdj, barThickness*2, barThickness);
|
|
}
|
|
|
|
g.FillRect(barX, barY + GS!(2), strWidth, useLineSpacing - 1);
|
|
}
|
|
|
|
g.SetFont(useFont);
|
|
using (g.PushColor(0xffffffff))
|
|
{
|
|
g.DrawString(te.mDisplayName, barX + GS!(2), barY + GS!(1), FontAlign.Left);
|
|
}
|
|
g.SetFont(mFont);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Selection GetSelectionForBytePosition(int ulx, int uly, int32 align, int32 length, int32 subChar, bool inStrView)
|
|
{
|
|
float lineSpacing = GetLineSpacing();
|
|
float strViewColumnStart = GS!(mColumnDisplayStart) + mBytesPerDisplayLine*GS!(mColumnDisplayStride) + GS!(mStrViewDisplayStartOffset);
|
|
float displayAdj = (float)(-mShowPositionDisplayOffset) * lineSpacing;
|
|
int lineCount = (int)(mHeight / lineSpacing) + 3;
|
|
|
|
if ((ulx > mBytesPerDisplayLine) || (uly > lineCount))
|
|
return null;
|
|
|
|
var useUlx = ulx;
|
|
if (align > 1)
|
|
useUlx &= ~((int)align - 1);
|
|
|
|
int selStart = (uly * mBytesPerDisplayLine) + useUlx + mCurPosition;
|
|
int selLength = (int)length;
|
|
|
|
Rect br = Rect();
|
|
br.mX = GS!(mColumnDisplayStart) + useUlx*GS!(mColumnDisplayStride) - GS!(5);
|
|
br.mY = GS!(mColumnHeaderHeight) + (uly * lineSpacing) + displayAdj;
|
|
br.mWidth = GS!(mColumnDisplayStride) - 1;
|
|
br.mHeight = lineSpacing;
|
|
|
|
Rect sr = Rect();
|
|
sr.mX = strViewColumnStart + useUlx*mStrViewDisplayStride;
|
|
sr.mY = GS!(mColumnHeaderHeight) + (uly * lineSpacing) + displayAdj;
|
|
sr.mWidth = mStrViewDisplayStride;
|
|
sr.mHeight = lineSpacing;
|
|
|
|
let selection = new Selection(br, sr, selStart, selLength, useUlx, uly, subChar, inStrView);
|
|
return selection;
|
|
}
|
|
|
|
Selection GetSelectionForDisplayPosition(float x, float y, int32 align, int32 length)
|
|
{
|
|
float lineSpacing = GetLineSpacing();
|
|
float strViewColumnStart = GS!(mColumnDisplayStart) + mBytesPerDisplayLine*GS!(mColumnDisplayStride) + GS!(mStrViewDisplayStartOffset);
|
|
float displayAdj = (float)(-mShowPositionDisplayOffset) * lineSpacing;
|
|
|
|
float curX = x;
|
|
float curY = y;
|
|
|
|
curY -= displayAdj;
|
|
curY -= GS!(mColumnHeaderHeight);
|
|
curY /= lineSpacing;
|
|
|
|
int lineCount = (int)(mHeight / lineSpacing) + 3;
|
|
if ((curY < 0.0f) || (curY >= (float)lineCount))
|
|
return null;
|
|
|
|
bool inStrView = false;
|
|
bool checkSubChar = true;
|
|
float testX = curX;
|
|
curX += 4; // offset by half a char8acter
|
|
testX -= GS!(mColumnDisplayStart);
|
|
testX /= (float)GS!(mColumnDisplayStride);
|
|
if (testX < 0.0f)
|
|
return null;
|
|
if (testX >= (float)mBytesPerDisplayLine)
|
|
{
|
|
// check string view
|
|
testX = curX;
|
|
testX -= strViewColumnStart;
|
|
testX /= GS!(mStrViewDisplayStride);
|
|
if ((testX < 0.0f) || (testX >= (float)mBytesPerDisplayLine))
|
|
return null;
|
|
|
|
inStrView = true;
|
|
checkSubChar = false;
|
|
}
|
|
|
|
float origX = curX;
|
|
curX = testX;
|
|
|
|
int ulx = (int)curX;
|
|
int uly = (int)curY;
|
|
|
|
var result = GetSelectionForBytePosition(ulx, uly, align, length, 0, inStrView);
|
|
|
|
if (checkSubChar && (result != null))
|
|
{
|
|
const int32 subCharsPerElement = 2; //CDH add support for multibyte
|
|
float subCharFrac = (origX - result.mBinRect.mX) / result.mBinRect.mWidth;
|
|
if (subCharFrac >= 1.0f && ulx < (mBytesPerDisplayLine - 1))
|
|
{
|
|
++ulx;
|
|
delete result;
|
|
result = GetSelectionForBytePosition(ulx, uly, align, length, 0, inStrView);
|
|
subCharFrac = (origX - result.mBinRect.mX) / result.mBinRect.mWidth;
|
|
}
|
|
result.mSubChar = Math.Min(Math.Max(0, (int32)(subCharFrac * subCharsPerElement)), subCharsPerElement - 1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Selection GetSelectionForCursor(Cursor cursor, bool ensureVisible)
|
|
{
|
|
//CDH TODO cursor length ignored for now; assume only single byte
|
|
Selection result = null;
|
|
if (cursor.mSelStart >= mCurPosition)
|
|
{
|
|
int ulx = (cursor.mSelStart - mCurPosition) % mBytesPerDisplayLine;
|
|
int uly = (cursor.mSelStart - mCurPosition) / mBytesPerDisplayLine;
|
|
result = GetSelectionForBytePosition(ulx, uly, 1, 1, cursor.mSubChar, cursor.mInStrView);
|
|
}
|
|
if ((result == null) && ensureVisible)
|
|
{
|
|
float lineSpacing = GetLineSpacing();
|
|
int lineCount = Math.Max(1, (int)(mHeight / lineSpacing) - 2);
|
|
|
|
// try and adjust current position if we're out of range
|
|
|
|
int maxPosition = mCurPosition + (lineCount * mBytesPerDisplayLine);
|
|
if (cursor.mSelStart < mCurPosition)
|
|
{
|
|
int lineDelta = ((mCurPosition - cursor.mSelStart) / mBytesPerDisplayLine) + 2;
|
|
mCurPosition -= lineDelta * mBytesPerDisplayLine;
|
|
DirtyTrackedExprs();
|
|
}
|
|
else if (cursor.mSelStart >= maxPosition)
|
|
{
|
|
int lineDelta = ((cursor.mSelStart - maxPosition) / mBytesPerDisplayLine) + 1;
|
|
mCurPosition += lineDelta * mBytesPerDisplayLine;
|
|
DirtyTrackedExprs();
|
|
}
|
|
|
|
result = GetSelectionForCursor(cursor, false);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public override void Resize(float x, float y, float width, float height)
|
|
{
|
|
//System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
|
|
//sw.Start();
|
|
|
|
base.Resize(x, y, width, height);
|
|
|
|
mGotoButton.Resize(GS!(6), GS!(2), GS!(132), GS!(20));
|
|
|
|
ClearTrackedExprs(false);
|
|
UpdateScrollbar();
|
|
|
|
if (mAutoResizeType != .Manual)
|
|
{
|
|
int numColumns = (int)((mWidth - GS!(mColumnDisplayStart) - GS!(mStrViewDisplayStartOffset) - GS!(22)) / (GS!(mColumnDisplayStride) + GS!(mStrViewDisplayStride)));
|
|
if (mAutoResizeType == .Auto_Mul8)
|
|
{
|
|
numColumns &= ~7;
|
|
if (numColumns == 0)
|
|
numColumns = 8;
|
|
}
|
|
if (numColumns < 4)
|
|
numColumns = 4;
|
|
SetColumns(numColumns);
|
|
}
|
|
|
|
//sw.Stop();
|
|
//Console.WriteLine("STOPWATCH(Update): {0}ms", sw.Elapsed.TotalMilliseconds);
|
|
}
|
|
|
|
public override void MouseMove(float x, float y)
|
|
{
|
|
base.MouseMove(x, y);
|
|
|
|
//delete mCurHoverSelection;
|
|
//mCurHoverSelection = GetSelectionForDisplayPosition(x, y, 1, 1);
|
|
|
|
if (mDraggingSelectionStart != null)
|
|
{
|
|
let curSel = GetSelectionForDisplayPosition(x, y, 1, 1);
|
|
if (curSel != null)
|
|
{
|
|
DeleteAndNullify!(mCurKeyCursor);
|
|
DeleteAndNullify!(mSelection);
|
|
mCurKeyCursor = new Cursor(curSel);
|
|
mSelection = new Cursor(curSel);
|
|
|
|
int leftSel = Math.Min(mCurKeyCursor.mSelStart, mDraggingSelectionStart.mSelStart);
|
|
int rightSel = Math.Max(mCurKeyCursor.mSelStart + mCurKeyCursor.mSelLength, mDraggingSelectionStart.mSelStart + mDraggingSelectionStart.mSelLength);
|
|
|
|
mSelection.mSelStart = leftSel;
|
|
mSelection.mSelLength = rightSel - leftSel;
|
|
|
|
delete curSel;
|
|
KeyCursorUpdated();
|
|
}
|
|
}
|
|
}
|
|
|
|
//public override void MouseDown(float x, float y)
|
|
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
|
|
{
|
|
base.MouseDown(x, y, btn, btnCount);
|
|
DeleteAndNullify!(mDraggingSelectionStart);
|
|
|
|
if (btn == 0)
|
|
{
|
|
//if (mByteEditWidget != null)
|
|
//{
|
|
// OnSelEditLostFocus(mByteEditWidget);
|
|
//}
|
|
DeleteAndNullify!(mSelection);
|
|
|
|
var selection = GetSelectionForDisplayPosition(x, y, 1, 1);
|
|
|
|
if (selection == null)
|
|
{
|
|
for (var te in mTrackedExprs)
|
|
{
|
|
if ((var rect = te.mDrawRect) && (rect.Contains(x, y)))
|
|
{
|
|
SelectRange(te.mCurValue, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (selection != null)
|
|
{
|
|
delete mCurKeyCursor;
|
|
mCurKeyCursor = new Cursor(selection);
|
|
mCurKeyCursorBlinkTicks = 0;
|
|
if (mCurKeyCursor.mInStrView)
|
|
mCurKeyCursor.mSubChar = 0;
|
|
KeyCursorUpdated();
|
|
|
|
SetFocus();
|
|
mDraggingSelectionStart = new Cursor(selection);
|
|
|
|
//TODO:
|
|
delete selection;
|
|
|
|
/*
|
|
mCurEditSelection = selection;
|
|
|
|
EditWidget ew = new DarkEditWidget();
|
|
mByteEditWidget = ew;
|
|
(ew.Content as DarkEditWidgetContent).SetFont(IDEApp.sApp.mCodeFont, true, true);
|
|
using (var lockRange = mContent.LockRange(selection.mSelStart, selection.mSelLength))
|
|
{
|
|
ew.SetText(string.Format("{0:X2}", lockRange.mData[0]));
|
|
}
|
|
ew.Content.SelectAll();
|
|
ew.Resize(selection.mBinRect.mX, selection.mBinRect.mY, selection.mBinRect.mWidth, selection.mBinRect.mHeight);
|
|
AddWidget(ew);
|
|
|
|
ew.mLostFocusHandler += OnSelEditLostFocus;
|
|
ew.mSubmitHandler += OnSelEditSubmit;
|
|
ew.mCancelHandler += OnSelEditCancel;
|
|
ew.SetFocus();
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
SetFocus();
|
|
}
|
|
}
|
|
else if (btn == 1)
|
|
{
|
|
ShowMenu(x, y);
|
|
}
|
|
}
|
|
|
|
public override void MouseUp(float x, float y, int32 btn)
|
|
{
|
|
base.MouseUp(x, y, btn);
|
|
DeleteAndNullify!(mDraggingSelectionStart);
|
|
}
|
|
|
|
public bool ApplyUndoAction(int offset, uint8 fromVal, uint8 toVal, uint8 subChar, bool inStrView, bool advanceAfterAction)
|
|
{
|
|
bool result = false;
|
|
|
|
delete mCurKeyCursor;
|
|
mCurKeyCursor = new Cursor(offset, 1, subChar, inStrView);
|
|
|
|
var lockRange = mContent.LockRange(mCurKeyCursor.mSelStart, mCurKeyCursor.mSelLength);
|
|
defer delete lockRange;
|
|
|
|
{
|
|
if (lockRange.mData[0] == fromVal)
|
|
{
|
|
lockRange.mData[0] = toVal;
|
|
lockRange.mModified = true;
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
Selection sel = GetSelectionForCursor(mCurKeyCursor, true);
|
|
if (sel != null)
|
|
{
|
|
defer delete sel;
|
|
|
|
mCurKeyCursorBlinkTicks = 0;
|
|
DeleteAndNullify!(mCurHoverSelection);
|
|
}
|
|
|
|
if (advanceAfterAction)
|
|
{
|
|
const int32 subCharsPerElement = 2; //CDH add support for multibyte
|
|
++mCurKeyCursor.mSubChar;
|
|
mCurKeyCursor.mSubChar %= subCharsPerElement;
|
|
if (mCurKeyCursor.mSubChar == 0)
|
|
{
|
|
AdvanceCursor(mCurKeyCursor);
|
|
}
|
|
}
|
|
|
|
KeyCursorUpdated();
|
|
|
|
return result;
|
|
}
|
|
|
|
void AdvanceCursor(Cursor cursor)
|
|
{
|
|
float lineSpacing = GetLineSpacing();
|
|
int lineCount = Math.Max(1, (int)(mHeight / lineSpacing) - 2);
|
|
|
|
int desiredPosition = cursor.mSelStart;
|
|
|
|
++desiredPosition; //CDH TODO generalize to general signed advance amount w/ ensure-visible support
|
|
|
|
bool clearTrackedExprs = false;
|
|
while (desiredPosition >= (mCurPosition + mBytesPerDisplayLine*lineCount))
|
|
{
|
|
mCurPosition += mBytesPerDisplayLine;
|
|
clearTrackedExprs = true;
|
|
}
|
|
if (clearTrackedExprs)
|
|
DirtyTrackedExprs();
|
|
|
|
cursor.mSelStart = desiredPosition;
|
|
}
|
|
|
|
private void KeyCursorUpdated()
|
|
{
|
|
DirtyTrackedExprs();
|
|
if (OnKeyCursorUpdated.HasListeners)
|
|
OnKeyCursorUpdated();
|
|
}
|
|
|
|
public override void KeyChar(char32 theChar)
|
|
{
|
|
base.KeyChar(theChar);
|
|
|
|
char8 ctrlChar = Utils.CtrlKeyChar(theChar);
|
|
if (ctrlChar == 'Z')
|
|
{
|
|
mUndoManager.Undo();
|
|
}
|
|
else if (ctrlChar == 'Y')
|
|
{
|
|
mUndoManager.Redo();
|
|
}
|
|
else if ((mCurKeyCursor != null) && !theChar.IsControl)
|
|
{
|
|
bool accepted = false;
|
|
|
|
if (mCurKeyCursor.mInStrView)
|
|
{
|
|
var lockRange = mContent.LockRange(mCurKeyCursor.mSelStart, mCurKeyCursor.mSelLength);
|
|
defer delete lockRange;
|
|
|
|
uint8 oldValue = lockRange.mData[0];
|
|
lockRange.mData[0] = (uint8)theChar;//(byte)Convert.ToUInt16(theChar); //CDH TODO add unicode strview support
|
|
lockRange.mModified = true;
|
|
accepted = true;
|
|
mCurKeyCursor.mSubChar = 0;
|
|
|
|
mLastSnapshotDiff.SetChanged(mCurKeyCursor.mSelStart);
|
|
mUndoManager.Add(new BinaryDataContent.BinaryChangeAction(this, mCurKeyCursor.mSelStart, oldValue, lockRange.mData[0], 0, true), false);
|
|
}
|
|
else
|
|
{
|
|
DeleteAndNullify!(mSelection);
|
|
|
|
var lockRange = mContent.LockRange(mCurKeyCursor.mSelStart, mCurKeyCursor.mSelLength);
|
|
defer delete lockRange;
|
|
|
|
char32 c = theChar;
|
|
int32 t;
|
|
if ((c >= '0') && (c <= '9'))
|
|
t = (int32)(c - '0');
|
|
else if ((c >= 'a') && (c <= 'f'))
|
|
t = (int32)(c - 'a') + 10;
|
|
else if ((c >= 'A') && (c <= 'F'))
|
|
t = (int32)(c - 'A') + 10;
|
|
else
|
|
t = -1;
|
|
|
|
if (t >= 0)
|
|
{
|
|
const int32 subCharsPerElement = 2; //CDH add support for multibyte
|
|
int32 shiftBits = ((subCharsPerElement-1) - mCurKeyCursor.mSubChar) * 4;
|
|
uint8 mask = (uint8)(0xF << shiftBits);
|
|
uint8 val = lockRange.mData[0];
|
|
uint8 origVal = val;
|
|
val &= (uint8)~mask;
|
|
val |= (uint8)((t << shiftBits) & mask);
|
|
if (val != origVal)
|
|
{
|
|
uint8 oldValue = lockRange.mData[0];
|
|
lockRange.mData[0] = val;
|
|
lockRange.mModified = true;
|
|
|
|
mLastSnapshotDiff.SetChanged(mCurKeyCursor.mSelStart);
|
|
mUndoManager.Add(new BinaryDataContent.BinaryChangeAction(this, mCurKeyCursor.mSelStart, oldValue, val, (uint8)mCurKeyCursor.mSubChar, false), false);
|
|
}
|
|
accepted = true;
|
|
|
|
++mCurKeyCursor.mSubChar;
|
|
mCurKeyCursor.mSubChar %= subCharsPerElement;
|
|
}
|
|
}
|
|
|
|
if (accepted)
|
|
{
|
|
if (mCurKeyCursor.mSubChar == 0)
|
|
{
|
|
AdvanceCursor(mCurKeyCursor);
|
|
}
|
|
|
|
mCurKeyCursorBlinkTicks = 0;
|
|
DeleteAndNullify!(mCurHoverSelection);//GetSelectionForCursor(mCurKeyCursor, false);
|
|
|
|
KeyCursorUpdated();
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void KeyDown(KeyCode keyCode, bool isRepeat)
|
|
{
|
|
base.KeyDown(keyCode, isRepeat);
|
|
|
|
bool isCtrl = mWidgetWindow.IsKeyDown(KeyCode.Control);
|
|
const int32 subCharsPerElement = 2; //CDH add support for multibyte
|
|
|
|
switch(keyCode)
|
|
{
|
|
case KeyCode.Up: fallthrough;
|
|
case KeyCode.Down: fallthrough;
|
|
case KeyCode.Left: fallthrough;
|
|
case KeyCode.Right: fallthrough;
|
|
case KeyCode.PageUp: fallthrough;
|
|
case KeyCode.PageDown: fallthrough;
|
|
case KeyCode.Home: fallthrough;
|
|
case KeyCode.End:
|
|
{
|
|
DeleteAndNullify!(mSelection);
|
|
|
|
if (mCurKeyCursor == null)
|
|
mCurKeyCursor = new Cursor(mCurPosition, 1, 0, false);
|
|
|
|
if (mCurKeyCursor != null)
|
|
{
|
|
Selection sel = GetSelectionForCursor(mCurKeyCursor, true);
|
|
if (sel != null)
|
|
{
|
|
defer delete sel;
|
|
|
|
float lineSpacing = GetLineSpacing();
|
|
int lineCount = Math.Max(1, (int)(mHeight / lineSpacing) - 2);
|
|
|
|
int desiredPosition = mCurKeyCursor.mSelStart;
|
|
switch(keyCode)
|
|
{
|
|
case KeyCode.Up: desiredPosition -= mBytesPerDisplayLine; break;
|
|
case KeyCode.Down: desiredPosition += mBytesPerDisplayLine; break;
|
|
case KeyCode.Left:
|
|
{
|
|
if (sel.mInStrView)
|
|
{
|
|
desiredPosition -= 1;
|
|
mCurKeyCursor.mSubChar = 0;
|
|
}
|
|
else if (isCtrl)
|
|
{
|
|
if (mCurKeyCursor.mSubChar == 0)
|
|
desiredPosition -= 1;
|
|
else
|
|
mCurKeyCursor.mSubChar = 0;
|
|
}
|
|
else
|
|
{
|
|
--mCurKeyCursor.mSubChar;
|
|
if (mCurKeyCursor.mSubChar < 0)
|
|
{
|
|
desiredPosition -= 1;
|
|
mCurKeyCursor.mSubChar = subCharsPerElement - 1;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case KeyCode.Right:
|
|
{
|
|
if (sel.mInStrView)
|
|
{
|
|
desiredPosition += 1;
|
|
mCurKeyCursor.mSubChar = 0;
|
|
}
|
|
else if (isCtrl)
|
|
{
|
|
desiredPosition += 1;
|
|
mCurKeyCursor.mSubChar = 0;
|
|
}
|
|
else
|
|
{
|
|
++mCurKeyCursor.mSubChar;
|
|
if (mCurKeyCursor.mSubChar >= subCharsPerElement)
|
|
{
|
|
desiredPosition += 1;
|
|
mCurKeyCursor.mSubChar = 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case KeyCode.PageUp: desiredPosition -= mBytesPerDisplayLine * lineCount; break;
|
|
case KeyCode.PageDown: desiredPosition += mBytesPerDisplayLine * lineCount; break;
|
|
case KeyCode.Home:
|
|
{
|
|
desiredPosition -= mCurPosition;
|
|
desiredPosition -= desiredPosition % mBytesPerDisplayLine;
|
|
desiredPosition += mCurPosition;
|
|
if (desiredPosition != mCurKeyCursor.mSelStart)
|
|
mCurKeyCursor.mSubChar = 0;
|
|
|
|
}
|
|
break;
|
|
case KeyCode.End:
|
|
{
|
|
desiredPosition -= mCurPosition;
|
|
desiredPosition -= desiredPosition % mBytesPerDisplayLine;
|
|
desiredPosition += mCurPosition + (mBytesPerDisplayLine - 1);
|
|
if (desiredPosition != mCurKeyCursor.mSelStart)
|
|
mCurKeyCursor.mSubChar = 0;
|
|
}
|
|
break;
|
|
default:
|
|
}
|
|
|
|
bool clearTrackedExprs = false;
|
|
while (desiredPosition < mCurPosition)
|
|
{
|
|
mCurPosition -= mBytesPerDisplayLine;
|
|
clearTrackedExprs = true;
|
|
}
|
|
while (desiredPosition >= (mCurPosition + mBytesPerDisplayLine*lineCount))
|
|
{
|
|
mCurPosition += mBytesPerDisplayLine;
|
|
clearTrackedExprs = true;
|
|
}
|
|
if (clearTrackedExprs)
|
|
DirtyTrackedExprs();
|
|
|
|
mCurKeyCursor.mSelStart = desiredPosition;
|
|
mCurKeyCursorBlinkTicks = 0;
|
|
|
|
DeleteAndNullify!(mCurHoverSelection);//GetSelectionForCursor(mCurKeyCursor);
|
|
|
|
KeyCursorUpdated();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
void OnSelEditLostFocus(Widget widget)
|
|
{
|
|
EditWidget editWidget = (EditWidget)widget;
|
|
if (editWidget == mByteEditWidget)
|
|
{
|
|
mCurEditSelection = null;
|
|
|
|
editWidget.mLostFocusHandler -= OnSelEditLostFocus;
|
|
editWidget.mSubmitHandler -= OnSelEditSubmit;
|
|
editWidget.mCancelHandler -= OnSelEditCancel;
|
|
|
|
editWidget.RemoveSelf();
|
|
mByteEditWidget = null;
|
|
|
|
if (mWidgetWindow.mFocusWidget == null)
|
|
SetFocus();
|
|
}
|
|
}
|
|
void OnSelEditSubmit(EditEvent theEvent)
|
|
{
|
|
if (mCurEditSelection != null)
|
|
{
|
|
string text = mByteEditWidget.Text;
|
|
if (text.Length == 2)
|
|
{
|
|
int val = 0;
|
|
for (int i=0; i<2; ++i)
|
|
{
|
|
char8 c = text[i];
|
|
int t;
|
|
if ((c >= '0') && (c <= '9'))
|
|
t = c - '0';
|
|
else if ((c >= 'a') && (c <= 'f'))
|
|
t = (c - 'a') + 10;
|
|
else if ((c >= 'A') && (c <= 'F'))
|
|
t = (c - 'A') + 10;
|
|
else
|
|
{
|
|
val = -1;
|
|
break;
|
|
}
|
|
|
|
val <<= 4;
|
|
val += t;
|
|
}
|
|
if (val >= 0)
|
|
{
|
|
using (var lockRange = mContent.LockRange(mCurEditSelection.mSelStart, mCurEditSelection.mSelLength))
|
|
{
|
|
lockRange.mData[0] = (byte)val;
|
|
lockRange.mModified = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
OnSelEditLostFocus((EditWidget)theEvent.mSender);
|
|
}
|
|
void OnSelEditCancel(EditEvent theEvent)
|
|
{
|
|
OnSelEditLostFocus((EditWidget)theEvent.mSender);
|
|
}
|
|
*/
|
|
|
|
public override void MouseWheel(float x, float y, float delta)
|
|
{
|
|
base.MouseWheel(x, y, delta);
|
|
if (mInfiniteScrollbar != null)
|
|
mInfiniteScrollbar.MouseWheel(x, y, delta);
|
|
}
|
|
|
|
public void ResetPosition(int position)
|
|
{
|
|
if (position == mCurPosition)
|
|
return; // no change
|
|
|
|
mCurPosition = Math.Min(position, (int)int64.MaxValue);
|
|
mCurPositionDisplayOffset = 0;
|
|
mShowPositionDisplayOffset = 0;
|
|
|
|
ClearTrackedExprs(false);
|
|
}
|
|
|
|
public void DirtyTrackedExprs()
|
|
{
|
|
mDelayedClearTrackedExprsTimer = 0.03125f;
|
|
}
|
|
public void ClearTrackedExprs(bool force)
|
|
{
|
|
float lineSpacing = GetLineSpacing();
|
|
int lineCount = (int)(mHeight / lineSpacing) + 3;
|
|
int lockSize = lineCount * mBytesPerDisplayLine;
|
|
|
|
mDelayedClearTrackedExprsTimer = 0.0f;
|
|
|
|
//if (!force && ((mCurPosition == mTrackedExprRangeStart) && (lockSize == mTrackedExprRangeSize))
|
|
if (!force)
|
|
{
|
|
if (mTrackedExprUpdateCnt == mUpdateCnt)
|
|
return;
|
|
if ((mCurPosition == mTrackedExprRangeStart) && (lockSize == mTrackedExprRangeSize))
|
|
return;
|
|
}
|
|
|
|
Stopwatch sw = scope Stopwatch();
|
|
sw.Start();
|
|
|
|
ClearAndDeleteItems(mTrackedExprs);
|
|
|
|
if (!gApp.mDebugger.IsPaused())
|
|
{
|
|
TrackedExpressionsUpdated(mTrackedExprs);
|
|
return;
|
|
}
|
|
|
|
// slow part 1 (~6ms in mintest)
|
|
String autoExprs = scope String();
|
|
//IDEApp.sApp.mDebugger.GetAutoExpressions((uint64)mCurPosition, (uint64)lockSize, autoExprs);
|
|
IDEApp.sApp.mDebugger.GetAutoExpressions((uint64)0, (uint64)0, autoExprs);
|
|
|
|
if (!autoExprs.StartsWith("!"))
|
|
{
|
|
//var hashAlgo = System.Security.Cryptography.MD5.Create();
|
|
|
|
//string[] autoSplit = autoExprs.Split('\n');
|
|
for (var autoStr in autoExprs.Split('\n'))
|
|
{
|
|
if (autoStr.Length == 0)
|
|
continue;
|
|
|
|
var tabSplit = autoStr.Split('\t');
|
|
|
|
int trackedMemStart = 0;
|
|
int trackedMemSize = 0;
|
|
|
|
String trackedExpr = scope String(tabSplit.GetNext());
|
|
StringView sizeStr = tabSplit.GetNext();
|
|
StringView addrStr = tabSplit.GetNext().GetValueOrDefault();
|
|
|
|
if (int.Parse(sizeStr) case .Ok(out trackedMemSize))
|
|
{
|
|
}
|
|
|
|
|
|
uint32 displayColor = 0;
|
|
StringView displayName = scope String();
|
|
bool isReg = false;
|
|
if (trackedExpr.StartsWith("$"))
|
|
{
|
|
displayName = StringView(trackedExpr, 1);
|
|
isReg = true;
|
|
|
|
switch(displayName)
|
|
{
|
|
case "esp", "rsp": displayColor = Color.FromHSV(0.0f, 1.0f, 1.0f, 0x80);
|
|
case "ebp", "rbp": displayColor = Color.FromHSV(0.1f, 1.0f, 1.0f, 0x80);
|
|
case "ecx", "rcx": displayColor = Color.FromHSV(0.15f, 1.0f, 1.0f, 0x80);
|
|
case "edx", "rdx": displayColor = Color.FromHSV(0.25f, 1.0f, 1.0f, 0x80);
|
|
case "esi", "rsi": displayColor = Color.FromHSV(0.45f, 1.0f, 1.0f, 0x80);
|
|
case "edi", "rdi": displayColor = Color.FromHSV(0.575f, 1.0f, 1.0f, 0x80);
|
|
case "ebx", "rbx": displayColor = Color.FromHSV(0.8f, 1.0f, 1.0f, 0x80);
|
|
case "eip", "rip": displayColor = Color.FromHSV(0.85f, 1.0f, 1.0f, 0x80);
|
|
case "eax", "rax": displayColor = Color.FromHSV(0.9f, 1.0f, 1.0f, 0x80);
|
|
|
|
case "r8": displayColor = Color.FromHSV(0.0f, 0.5f, 1.0f, 0x80);
|
|
case "r9": displayColor = Color.FromHSV(0.1f, 0.05f, 1.0f, 0x80);
|
|
case "r10": displayColor = Color.FromHSV(0.15f, 0.5f, 1.0f, 0x80);
|
|
case "r11": displayColor = Color.FromHSV(0.25f, 0.5f, 1.0f, 0x80);
|
|
case "r12": displayColor = Color.FromHSV(0.45f, 0.5f, 1.0f, 0x80);
|
|
case "r13": displayColor = Color.FromHSV(0.575f, 0.5f, 1.0f, 0x80);
|
|
case "r14": displayColor = Color.FromHSV(0.8f, 0.5f, 1.0f, 0x80);
|
|
case "r15": displayColor = Color.FromHSV(0.85f, 0.5f, 1.0f, 0x80);
|
|
}
|
|
}
|
|
else if (trackedExpr.StartsWith("&"))
|
|
{
|
|
//displayColor = 0x8000c000;
|
|
displayName = StringView(trackedExpr, 1);
|
|
|
|
if (int.Parse(addrStr) case .Ok(out trackedMemStart))
|
|
{
|
|
}
|
|
|
|
if (displayName == "$StackFrame")
|
|
{
|
|
if (mCurPosition == 0)
|
|
{
|
|
mCurPosition = trackedMemStart - mBytesPerDisplayLine*2;
|
|
if ((mBytesPerDisplayLine & 15) == 0)
|
|
mCurPosition &= ~15;
|
|
else if ((mBytesPerDisplayLine & 7) == 0)
|
|
mCurPosition &= ~7;
|
|
}
|
|
}
|
|
}
|
|
else if (trackedExpr.StartsWith("?"))
|
|
{
|
|
//displayColor = 0x8000c000;
|
|
displayName = StringView(trackedExpr, 1);
|
|
trackedExpr.Clear();
|
|
trackedExpr.Append("&");
|
|
trackedExpr.Append(displayName);
|
|
}
|
|
|
|
if (!displayName.IsEmpty)
|
|
{
|
|
let digest = MD5.Hash(Span<uint8>((uint8*)displayName.Ptr, displayName.Length));
|
|
|
|
if (displayColor == 0)
|
|
{
|
|
float h = (float)digest.mHash[0] / 255.0f;
|
|
float s = 1.0f;//0.75f + ((float)digest[1] / 1024.0f);
|
|
float v = 1.0f;//0.5f + ((float)digest[2] / 512.0f);
|
|
displayColor = Color.FromHSV(h, s, v, 0x80);
|
|
}
|
|
|
|
let te = new TrackedExpression(this, trackedExpr, trackedMemStart, trackedMemSize, displayName, displayColor, isReg);
|
|
mTrackedExprs.Add(te);
|
|
}
|
|
}
|
|
}
|
|
|
|
// slow part 2 (~3ms in mintest)
|
|
for (var te in mTrackedExprs)
|
|
te.Update(mCurPosition, lockSize);
|
|
|
|
sw.Stop();
|
|
|
|
TrackedExpressionsUpdated(mTrackedExprs);
|
|
|
|
double elapsedMs = sw.Elapsed.TotalMilliseconds;
|
|
if ((elapsedMs > 0.0) || (mTrackedExprs.Count > 0))
|
|
Console.WriteLine("STOPWATCH(ClearTrackedExprs {2}): {0}ms (te count = {1}, MemS = ({3} -> {4}), MemL = ({5} -> {6}))", elapsedMs, mTrackedExprs.Count, mUpdateCnt, mTrackedExprRangeStart, mCurPosition, mTrackedExprRangeSize, lockSize);
|
|
|
|
mTrackedExprRangeStart = mCurPosition;
|
|
mTrackedExprRangeSize = lockSize;
|
|
mTrackedExprUpdateCnt = mUpdateCnt;
|
|
}
|
|
|
|
public void CheckClearData()
|
|
{
|
|
if (mCacheDirty)
|
|
mContent.ClearCache();
|
|
if (mTrackedExprsDirty)
|
|
ClearTrackedExprs(true);
|
|
mCacheDirty = false;
|
|
mTrackedExprsDirty = false;
|
|
}
|
|
|
|
public void ClearData(bool doRefreshTrackedExprs)
|
|
{
|
|
mCacheDirty = true;
|
|
mTrackedExprsDirty |= doRefreshTrackedExprs;
|
|
if (mWidgetWindow != null)
|
|
CheckClearData();
|
|
}
|
|
|
|
public void ApplyScrollDelta(double scrollDelta)
|
|
{
|
|
if (scrollDelta == 0.0)
|
|
return; // no change
|
|
|
|
MarkDirty();
|
|
double useScrollDelta = scrollDelta;
|
|
int position = (int)mCurPosition;
|
|
|
|
//float lineSpacing = GetLineSpacing();
|
|
//ulong lineCount = (ulong)(mHeight / lineSpacing) + 3;
|
|
|
|
//Debug.WriteLine("Scroll: {0}", scrollDelta);
|
|
|
|
/*if (Math.Abs(useScrollDelta) >= 0.5)//(double)lineCount * 0.25)
|
|
{
|
|
mCurPositionDisplayOffset = 0.0;
|
|
useScrollDelta = Math.Round(useScrollDelta);
|
|
}*/
|
|
|
|
mCurPositionDisplayOffset += useScrollDelta;
|
|
if (mCurPositionDisplayOffset >= 1.0)
|
|
{
|
|
int wholeAdj = (int)mCurPositionDisplayOffset;
|
|
mCurPositionDisplayOffset -= wholeAdj;
|
|
position += (int64)(wholeAdj * mBytesPerDisplayLine);
|
|
}
|
|
else if (mCurPositionDisplayOffset < 0.0)
|
|
{
|
|
int64 wholeAdj = (int64)mCurPositionDisplayOffset - 1;
|
|
mCurPositionDisplayOffset -= wholeAdj;
|
|
|
|
if (mCurPositionDisplayOffset >= 1.0)
|
|
{
|
|
int64 wholeAdj2 = (int64)mCurPositionDisplayOffset;
|
|
wholeAdj += wholeAdj2;
|
|
mCurPositionDisplayOffset -= wholeAdj2;
|
|
}
|
|
|
|
position = (int64)Math.Max((int64)mCurPosition + (wholeAdj * (int64)mBytesPerDisplayLine), (int64)0);
|
|
}
|
|
|
|
// Snap to full lines once we're moving at a fast enough rate
|
|
if (Math.Abs(scrollDelta) < 0.25f)
|
|
mShowPositionDisplayOffset = mCurPositionDisplayOffset;
|
|
else
|
|
mShowPositionDisplayOffset = 0;
|
|
|
|
mCurPosition = (int)Math.Min((uint)Math.Max(0, position), (uint)-1);
|
|
|
|
DirtyTrackedExprs();
|
|
}
|
|
|
|
public void EnsureCursorVisible()
|
|
{
|
|
float lineSpacing = GetLineSpacing();
|
|
int lineCount = Math.Max(1, (int)(mHeight / lineSpacing) - 3);
|
|
|
|
int selEnd = mCurKeyCursor.mSelStart + mCurKeyCursor.mSelLength;
|
|
if (mSelection != null)
|
|
selEnd = mSelection.mSelStart + mSelection.mSelLength;
|
|
|
|
if (selEnd >= mCurPosition + lineCount * mBytesPerDisplayLine)
|
|
{
|
|
int adjust = ((selEnd - (mCurPosition + lineCount * mBytesPerDisplayLine) + mBytesPerDisplayLine - 1) / mBytesPerDisplayLine + Math.Min(3, lineCount - 1)) * mBytesPerDisplayLine;
|
|
mCurPosition += adjust;
|
|
}
|
|
|
|
if (mCurKeyCursor.mSelStart <= mCurPosition)
|
|
{
|
|
int adjust = (mCurPosition - mCurKeyCursor.mSelStart + mBytesPerDisplayLine - 1) / mBytesPerDisplayLine * mBytesPerDisplayLine;
|
|
mCurPosition -= adjust;
|
|
mShowPositionDisplayOffset = 0;
|
|
mCurPositionDisplayOffset = 0;
|
|
}
|
|
|
|
KeyCursorUpdated();
|
|
}
|
|
|
|
public void SelectRange(int memStart, int memLen)
|
|
{
|
|
//CDH TODO make this selection support more "real" once it looks good, add drag select etc, not just a "test" feature
|
|
delete mSelection;
|
|
mSelection = new Cursor(memStart, memLen, 0, false);
|
|
|
|
delete mCurKeyCursor;
|
|
mCurKeyCursor = new Cursor(memStart, 1, 0, false);
|
|
|
|
EnsureCursorVisible();
|
|
SetFocus();
|
|
|
|
var sel = GetSelectionForCursor(mSelection, false);
|
|
LocatorAnim.Show(.Always, this, sel.mBinRect.Left, sel.mBinRect.Top + sel.mBinRect.mHeight / 2);
|
|
delete sel;
|
|
}
|
|
|
|
public void DeselectRange()
|
|
{
|
|
DeleteAndNullify!(mSelection);
|
|
}
|
|
|
|
public static void SanityTest()
|
|
{
|
|
var provider = new TestBinaryContentDataProvider();
|
|
var content = new BinaryDataContent(64, provider);
|
|
|
|
using (var range = content.LockRange(0x12345678, 200))
|
|
{
|
|
Console.WriteLine("SANITY TEST! {0} {1}", range.mBaseOffset, range.mData.Count);
|
|
}
|
|
}
|
|
}
|
|
}
|