From fa386a0d03b4c7a795a8b4a0fca749060c629125 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Sat, 8 Jun 2019 17:13:11 -0700 Subject: [PATCH] Finish wiring up code hint commands --- SourceGenWPF/DisplayListSelection.cs | 43 ++++++++--- SourceGenWPF/LineListGen.cs | 12 +-- SourceGenWPF/MainController.cs | 97 +++++++++++++++++++++---- SourceGenWPF/ProjWin/MainWindow.xaml.cs | 9 +++ 4 files changed, 129 insertions(+), 32 deletions(-) diff --git a/SourceGenWPF/DisplayListSelection.cs b/SourceGenWPF/DisplayListSelection.cs index 8aa0981..c2f1164 100644 --- a/SourceGenWPF/DisplayListSelection.cs +++ b/SourceGenWPF/DisplayListSelection.cs @@ -23,12 +23,14 @@ using CommonUtil; namespace SourceGenWPF { /// - /// Tracks the items selected in the DisplayList. - /// - /// Forward the SelectionChanged event. In WPF you can't get indices, only items, so we - /// have to store the item index in the item itself. + /// Tracks the items selected in the DisplayList, using forwarded SelectionChanged events. + /// When enumerated, provides an ordered list of selected indices. /// - public class DisplayListSelection { + /// + /// In WPF you can't get indices, only items, so we have to store the item index in the + /// item itself. + /// + public class DisplayListSelection : IEnumerable { private BitArray mSelection; /// @@ -49,26 +51,45 @@ namespace SourceGenWPF { } } + /// + /// Constructs an empty list. + /// public DisplayListSelection() { mSelection = new BitArray(0); } + /// + /// Constructs a list of the specified length. + /// + /// Number of elements. public DisplayListSelection(int length) { mSelection = new BitArray(length); } + /// + /// Returns an enumeration of selected indices, in ascending order. + /// + public IEnumerator GetEnumerator() { + for (int i = 0; i < mSelection.Length; i++) { + if (mSelection[i]) { + yield return i; + } + } + } + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + /// /// Sets the length of the selection array. /// /// If the new length is longer, the new elements are initialized to false. If the - /// new length is shorter, the excess elements are discarded. (This matches the behavior - /// of a virtual ListView selection set.) + /// new length is shorter, the excess elements are discarded. /// /// New length. - public void SetLength(int length) { - //Debug.WriteLine("VirtualListViewSelection length now " + length); - mSelection.Length = length; - } + //public void SetLength(int length) { + // mSelection.Length = length; + //} /// /// Handles selection change. diff --git a/SourceGenWPF/LineListGen.cs b/SourceGenWPF/LineListGen.cs index b3182aa..368c9ab 100644 --- a/SourceGenWPF/LineListGen.cs +++ b/SourceGenWPF/LineListGen.cs @@ -388,13 +388,13 @@ namespace SourceGenWPF { } /// - /// Changes the Formatter object. Clears the display list, instigating a full re-render. + /// Changes the Formatter object. Clears the line list, instigating a full re-render. /// /// Formatter object. public void SetFormatter(Formatter formatter) { mFormatter = formatter; mLineList.Clear(); - // TODO: update display list + mDisplayList.Clear(); // We probably just changed settings, so update this as well. mShowCycleCounts = AppSettings.Global.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, @@ -402,14 +402,14 @@ namespace SourceGenWPF { } /// - /// Changes the pseudo-op name object. Clears the display list, instigating a + /// Changes the pseudo-op name object. Clears the line list, instigating a /// full re-render. /// /// Pseudo-op names. public void SetPseudoOpNames(PseudoOp.PseudoOpNames opNames) { mPseudoOpNames = opNames; mLineList.Clear(); - // TODO: update display list + mDisplayList.Clear(); } /// @@ -654,7 +654,7 @@ namespace SourceGenWPF { // Out with the old, in with the new. mLineList.RemoveRange(startIndex, endIndex - startIndex + 1); mLineList.InsertRange(startIndex, newLines); - // TODO: update display list + Debug.Assert(false); // TODO: update display list Debug.Assert(ValidateLineList(), "Display list failed validation"); } @@ -720,7 +720,7 @@ namespace SourceGenWPF { } Debug.WriteLine("Removing " + endIndex + " header lines"); mLineList.RemoveRange(0, endIndex); - // TODO: update display list + Debug.Assert(false); // TODO: update display list } /// diff --git a/SourceGenWPF/MainController.cs b/SourceGenWPF/MainController.cs index c8d569c..3988c11 100644 --- a/SourceGenWPF/MainController.cs +++ b/SourceGenWPF/MainController.cs @@ -51,15 +51,6 @@ namespace SourceGenWPF { /// an empty symbol table. /// private SymbolTableSubset mSymbolSubset; - - /// - /// Current code list view selection. The length will match the DisplayList Count. - /// - /// WinForms was bad -- a simple foreach through SelectedIndices on a 500K-line data set - /// took about 2.5 seconds on a fast Win10 x64 machine. In WPF you get a list of - /// selected objects, which is fine unless what you really want is the line number. - /// - private VirtualListViewSelection mCodeViewSelection = new VirtualListViewSelection(); #endif /// @@ -493,7 +484,7 @@ namespace SourceGenWPF { #endif int topOffset = CodeListGen[topItem].FileOffset; LineListGen.SavedSelection savedSel = LineListGen.SavedSelection.Generate( - CodeListGen, null /*mCodeViewSelection*/, topOffset); + CodeListGen, mMainWin.CodeDisplayList.SelectedIndices, topOffset); //savedSel.DebugDump(); mReanalysisTimer.EndTask("Save selection"); @@ -1051,18 +1042,19 @@ namespace SourceGenWPF { /// /// is selected. public SelectionState UpdateSelectionState() { - int firstIndex = mMainWin.GetFirstSelectedIndex(); - Debug.WriteLine("SelectionChanged, firstIndex=" + firstIndex); + int selCount = mMainWin.GetSelectionCount(); + Debug.WriteLine("SelectionChanged, selCount=" + selCount); SelectionState state = new SelectionState(); // Use IsSingleItemSelected(), rather than just checking sel.Count, because we // want the user to be able to e.g. EditData on a multi-line string even if all // lines in the string are selected. - if (firstIndex == -1) { + if (selCount < 0) { // nothing selected, leave everything set to false / 0 state.mEntityCounts = new EntityCounts(); } else if (IsSingleItemSelected()) { + int firstIndex = mMainWin.GetFirstSelectedIndex(); state.mNumSelected = 1; state.mEntityCounts = GatherEntityCounts(firstIndex); LineListGen.Line line = CodeListGen[firstIndex]; @@ -1148,7 +1140,8 @@ namespace SourceGenWPF { } // A single line can span multiple offsets, each of which could have a - // different hint. + // different hint. Note the code/data hints are only applied to the first + // byte of each selected line, so we're not quite in sync with that. for (int offset = line.FileOffset; offset < line.FileOffset + line.OffsetSpan; offset++) { switch (mProject.TypeHints[offset]) { @@ -1171,7 +1164,7 @@ namespace SourceGenWPF { } } - //Debug.WriteLine("GatherEntityCounts (len=" + mCodeViewSelection.Length + ") took " + + //Debug.WriteLine("GatherEntityCounts (len=" + CodeListGen.Count + ") took " + // (DateTime.Now - startWhen).TotalMilliseconds + " ms"); return new EntityCounts() { @@ -1214,6 +1207,80 @@ namespace SourceGenWPF { return false; } + public void MarkAsType(CodeAnalysis.TypeHint hint, bool firstByteOnly) { + RangeSet sel; + + if (firstByteOnly) { + sel = new RangeSet(); + foreach (int index in mMainWin.CodeDisplayList.SelectedIndices) { + int offset = CodeListGen[index].FileOffset; + if (offset >= 0) { + // Not interested in the header stuff for hinting. + sel.Add(offset); + } + } + } else { + sel = OffsetSetFromSelected(); + } + + TypedRangeSet newSet = new TypedRangeSet(); + TypedRangeSet undoSet = new TypedRangeSet(); + + foreach (int offset in sel) { + if (offset < 0) { + // header comment + continue; + } + CodeAnalysis.TypeHint oldType = mProject.TypeHints[offset]; + if (oldType == hint) { + // no change, don't add to set + continue; + } + undoSet.Add(offset, (int)oldType); + newSet.Add(offset, (int)hint); + } + if (newSet.Count == 0) { + Debug.WriteLine("No changes found (" + hint + ", " + sel.Count + " offsets)"); + return; + } + + UndoableChange uc = UndoableChange.CreateTypeHintChange(undoSet, newSet); + ChangeSet cs = new ChangeSet(uc); + + ApplyUndoableChanges(cs); + } + + /// + /// Converts the set of selected items into a set of offsets. If a line + /// spans multiple offsets (e.g. a 3-byte instruction), offsets for every + /// byte are included. + /// + /// Boundaries such as labels and address changes are ignored. + /// + /// RangeSet with all offsets. + private RangeSet OffsetSetFromSelected() { + RangeSet rs = new RangeSet(); + + foreach (int index in mMainWin.CodeDisplayList.SelectedIndices) { + int offset = CodeListGen[index].FileOffset; + + // Mark every byte of an instruction or multi-byte data item -- + // everything that is represented by the line the user selected. + int len; + if (offset >= 0) { + len = mProject.GetAnattrib(offset).Length; + } else { + // header area + len = 1; + } + Debug.Assert(len > 0); + for (int i = offset; i < offset + len; i++) { + rs.Add(i); + } + } + return rs; + } + #endregion Main window UI event handlers } } diff --git a/SourceGenWPF/ProjWin/MainWindow.xaml.cs b/SourceGenWPF/ProjWin/MainWindow.xaml.cs index 74ce10d..4a1816d 100644 --- a/SourceGenWPF/ProjWin/MainWindow.xaml.cs +++ b/SourceGenWPF/ProjWin/MainWindow.xaml.cs @@ -210,6 +210,8 @@ namespace SourceGenWPF.ProjWin { } private void CodeListView_SelectionChanged(object sender, SelectionChangedEventArgs e) { + //DateTime startWhen = DateTime.Now; + // Update the selected-item bitmap. CodeDisplayList.SelectedIndices.SelectionChanged(e); @@ -218,6 +220,9 @@ namespace SourceGenWPF.ProjWin { Debug.Assert(CodeDisplayList.SelectedIndices.DebugValidateSelectionCount( codeListView.SelectedItems.Count)); + + //Debug.WriteLine("SelectionChanged took " + + // (DateTime.Now - startWhen).TotalMilliseconds + " ms"); } /// @@ -341,18 +346,22 @@ namespace SourceGenWPF.ProjWin { private void HintAsCodeEntryPoint_Executed(object sender, ExecutedRoutedEventArgs e) { Debug.WriteLine("hint as code entry point"); + mMainCtrl.MarkAsType(CodeAnalysis.TypeHint.Code, true); } private void HintAsDataStart_Executed(object sender, ExecutedRoutedEventArgs e) { Debug.WriteLine("hint as data start"); + mMainCtrl.MarkAsType(CodeAnalysis.TypeHint.Data, true); } private void HintAsInlineData_Executed(object sender, ExecutedRoutedEventArgs e) { Debug.WriteLine("hint as inline data"); + mMainCtrl.MarkAsType(CodeAnalysis.TypeHint.InlineData, false); } private void RemoveHints_Executed(object sender, ExecutedRoutedEventArgs e) { Debug.WriteLine("remove hints"); + mMainCtrl.MarkAsType(CodeAnalysis.TypeHint.NoHint, false); } private void SelectAllCmd_Executed(object sender, ExecutedRoutedEventArgs e) {