From 12874d32baf74fa748756c1a7bd569adc45538bb Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Sun, 14 Jul 2019 13:50:15 -0700 Subject: [PATCH] Implement three shortcut commands Added: Toggle Single-BYte Format (Ctrl+B) Format As Word (Ctrl+W) Delete Note/Long Comment (Del) --- SourceGenWPF/MainController.cs | 281 ++++++++++++++++++++----- SourceGenWPF/Res/Strings.xaml | 3 + SourceGenWPF/Res/Strings.xaml.cs | 6 + SourceGenWPF/WpfGui/MainWindow.xaml | 27 ++- SourceGenWPF/WpfGui/MainWindow.xaml.cs | 24 +++ 5 files changed, 290 insertions(+), 51 deletions(-) diff --git a/SourceGenWPF/MainController.cs b/SourceGenWPF/MainController.cs index 404f16b..2f9cac4 100644 --- a/SourceGenWPF/MainController.cs +++ b/SourceGenWPF/MainController.cs @@ -1438,6 +1438,42 @@ namespace SourceGenWPF { } } + public bool CanDeleteMlc() { + if (SelectionAnalysis.mNumItemsSelected != 1) { + return false; + } + Debug.WriteLine("LINE TYPE " + SelectionAnalysis.mLineType); + return (SelectionAnalysis.mLineType == LineListGen.Line.Type.LongComment || + SelectionAnalysis.mLineType == LineListGen.Line.Type.Note); + } + + // Delete multi-line comment (Note or LongComment) + public void DeleteMlc() { + int selIndex = mMainWin.CodeListView_GetFirstSelectedIndex(); + LineListGen.Line line = CodeLineList[selIndex]; + int offset = line.FileOffset; + + UndoableChange uc; + if (line.LineType == LineListGen.Line.Type.Note) { + if (!mProject.Notes.TryGetValue(offset, out MultiLineComment oldNote)) { + Debug.Assert(false); + return; + } + uc = UndoableChange.CreateNoteChange(offset, oldNote, null); + } else if (line.LineType == LineListGen.Line.Type.LongComment) { + if (!mProject.LongComments.TryGetValue(offset, out MultiLineComment oldComment)) { + Debug.Assert(false); + return; + } + uc = UndoableChange.CreateLongCommentChange(offset, oldComment, null); + } else { + Debug.Assert(false); + return; + } + ChangeSet cs = new ChangeSet(uc); + ApplyUndoableChanges(cs); + } + public bool CanEditAddress() { if (SelectionAnalysis.mNumItemsSelected != 1) { return false; @@ -1917,6 +1953,100 @@ namespace SourceGenWPF { mMainWin.CodeListView_Focus(); } + public bool CanFormatAsWord() { + EntityCounts counts = SelectionAnalysis.mEntityCounts; + // This is insufficient -- we need to know how many bytes are selected, and + // whether they're already formatted as multi-byte items. Too expensive to + // deal with here, so we'll need to show failure dialogs instead (ugh). + return (counts.mDataLines > 0 && counts.mCodeLines == 0); + } + + public void FormatAsWord() { + TypedRangeSet trs = GroupedOffsetSetFromSelected(); + if (trs.Count == 0) { + Debug.Assert(false, "nothing to edit"); // shouldn't happen + return; + } + + // If the user has only selected a single byte, we want to add the following byte + // to the selection, then proceed as usual. We can't simply modify the ListView + // selection because the following item might be an auto-detected string or fill, + // and we'd be adding multiple bytes. We have to be careful when grabbing the byte + // in case there's a region-split at that point (e.g. user label or .ORG). + // + // We could expand this to allow multiple regions, each of which is a single byte, + // but we'd need to deal with the case where the user selects two adjacent bytes that + // cross a region boundary. + if (trs.RangeCount == 1) { + // Exactly one range entry. Check its size. + IEnumerator checkIter = trs.RangeListIterator; + checkIter.MoveNext(); + TypedRangeSet.TypedRange rng = checkIter.Current; + if (rng.Low == rng.High && rng.Low < mProject.FileDataLength - 1) { + // Single byte selected. Check to see if it's okay to grab the next byte. + Anattrib thisAttr = mProject.GetAnattrib(rng.Low); + Debug.Assert(thisAttr.DataDescriptor.Length == 1); + + int nextOffset = rng.Low + 1; + Anattrib nextAttr = mProject.GetAnattrib(nextOffset); + // This must match what GroupedOffsetSetFromSelected() does. + if (!mProject.UserLabels.ContainsKey(nextOffset) && + !mProject.HasCommentOrNote(nextOffset) && + thisAttr.Address == nextAttr.Address - 1) { + // Good to go. + Debug.WriteLine("Grabbing second byte from +" + nextOffset.ToString("x6")); + trs.Add(nextOffset, rng.Type); + } + } + } + + // Confirm that every selected byte is a single-byte data item (either set by + // the user or as part of the uncategorized data scan). + foreach (TypedRangeSet.Tuple tup in trs) { + FormatDescriptor dfd = mProject.GetAnattrib(tup.Value).DataDescriptor; + if (dfd != null && dfd.Length != 1) { + Debug.WriteLine("Can't format as word: offset +" + tup.Value.ToString("x6") + + " has len=" + dfd.Length + " (must be 1)"); + MessageBox.Show(Res.Strings.INVALID_FORMAT_WORD_SEL_NON1, + Res.Strings.INVALID_FORMAT_WORD_SEL_CAPTION, + MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + } + + // Confirm that, in each region, an even number of bytes are selected. + IEnumerator rngIter = trs.RangeListIterator; + while (rngIter.MoveNext()) { + TypedRangeSet.TypedRange rng = rngIter.Current; + int rangeLen = rng.High - rng.Low + 1; + if ((rangeLen & 0x01) != 0) { + string msg = string.Format(Res.Strings.INVALID_FORMAT_WORD_SEL_UNEVEN_FMT, + trs.RangeCount); + MessageBox.Show(msg, + Res.Strings.INVALID_FORMAT_WORD_SEL_CAPTION, + MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + } + + // Selection is good, generate changes. + SortedList newFmts = new SortedList(); + rngIter.Reset(); + FormatDescriptor newDfd = FormatDescriptor.Create(2, FormatDescriptor.Type.NumericLE, + FormatDescriptor.SubType.None); + while (rngIter.MoveNext()) { + TypedRangeSet.TypedRange rng = rngIter.Current; + for (int i = rng.Low; i <= rng.High; i += 2) { + newFmts.Add(i, newDfd); + } + } + + ChangeSet cs = mProject.GenerateFormatMergeSet(newFmts); + if (cs.Count != 0) { + ApplyUndoableChanges(cs); + } + } + public bool CanFormatSplitAddress() { EntityCounts counts = SelectionAnalysis.mEntityCounts; // Must be at least one line of data, and no code. Note this is lines, not bytes, @@ -2408,54 +2538,6 @@ namespace SourceGenWPF { } } - public bool CanUndo() { - return (mProject != null && mProject.CanUndo); - } - - /// - /// Handles Edit - Undo. - /// - public void UndoChanges() { - if (!mProject.CanUndo) { - Debug.Assert(false, "Nothing to undo"); - return; - } - ChangeSet cs = mProject.PopUndoSet(); - ApplyChanges(cs, true); -#if false - UpdateMenuItemsAndTitle(); - - // If the debug dialog is visible, update it. - if (mShowUndoRedoHistoryDialog != null) { - mShowUndoRedoHistoryDialog.BodyText = mProject.DebugGetUndoRedoHistory(); - } -#endif - } - - public bool CanRedo() { - return (mProject != null && mProject.CanRedo); - } - - /// - /// Handles Edit - Redo. - /// - public void RedoChanges() { - if (!mProject.CanRedo) { - Debug.Assert(false, "Nothing to redo"); - return; - } - ChangeSet cs = mProject.PopRedoSet(); - ApplyChanges(cs, false); -#if false - UpdateMenuItemsAndTitle(); - - // If the debug dialog is visible, update it. - if (mShowUndoRedoHistoryDialog != null) { - mShowUndoRedoHistoryDialog.BodyText = mProject.DebugGetUndoRedoHistory(); - } -#endif - } - /// /// Handles the four Actions - edit hint commands. /// @@ -2559,6 +2641,61 @@ namespace SourceGenWPF { ApplyUndoableChanges(new ChangeSet(uc)); } + public bool CanToggleSingleByteFormat() { + EntityCounts counts = SelectionAnalysis.mEntityCounts; + return (counts.mDataLines > 0 && counts.mCodeLines == 0); + } + + public void ToggleSingleByteFormat() { + TypedRangeSet trs = GroupedOffsetSetFromSelected(); + if (trs.Count == 0) { + Debug.Assert(false, "nothing to edit"); // shouldn't happen + return; + } + + // Check the format descriptor of the first selected offset. + int firstOffset = -1; + foreach (TypedRangeSet.Tuple tup in trs) { + firstOffset = tup.Value; + break; + } + Debug.Assert(mProject.GetAnattrib(firstOffset).IsDataStart); + bool toDefault = false; + if (mProject.OperandFormats.TryGetValue(firstOffset, out FormatDescriptor curDfd)) { + if (curDfd.FormatType == FormatDescriptor.Type.NumericLE && + curDfd.FormatSubType == FormatDescriptor.SubType.None && + curDfd.Length == 1) { + // Currently single-byte, toggle to default. + toDefault = true; + } + } + + // Iterate through the selected regions. + SortedList newFmts = new SortedList(); + IEnumerator rngIter = trs.RangeListIterator; + while (rngIter.MoveNext()) { + TypedRangeSet.TypedRange rng = rngIter.Current; + if (toDefault) { + // Create a single REMOVE descriptor that covers the full span. + FormatDescriptor newDfd = FormatDescriptor.Create(rng.High - rng.Low + 1, + FormatDescriptor.Type.REMOVE, FormatDescriptor.SubType.None); + newFmts.Add(rng.Low, newDfd); + } else { + // Add individual single-byte format descriptors for everything. + FormatDescriptor newDfd = FormatDescriptor.Create(1, + FormatDescriptor.Type.NumericLE, FormatDescriptor.SubType.None); + for (int i = rng.Low; i <= rng.High; i++) { + newFmts.Add(i, newDfd); + } + } + } + + ChangeSet cs = mProject.GenerateFormatMergeSet(newFmts); + if (cs.Count != 0) { + ApplyUndoableChanges(cs); + } + } + /// /// Converts the ListView's selected items into a set of offsets. If a line /// spans multiple offsets (e.g. a 3-byte instruction), offsets for every @@ -2741,6 +2878,54 @@ namespace SourceGenWPF { } } + public bool CanUndo() { + return (mProject != null && mProject.CanUndo); + } + + /// + /// Handles Edit - Undo. + /// + public void UndoChanges() { + if (!mProject.CanUndo) { + Debug.Assert(false, "Nothing to undo"); + return; + } + ChangeSet cs = mProject.PopUndoSet(); + ApplyChanges(cs, true); +#if false + UpdateMenuItemsAndTitle(); + + // If the debug dialog is visible, update it. + if (mShowUndoRedoHistoryDialog != null) { + mShowUndoRedoHistoryDialog.BodyText = mProject.DebugGetUndoRedoHistory(); + } +#endif + } + + public bool CanRedo() { + return (mProject != null && mProject.CanRedo); + } + + /// + /// Handles Edit - Redo. + /// + public void RedoChanges() { + if (!mProject.CanRedo) { + Debug.Assert(false, "Nothing to redo"); + return; + } + ChangeSet cs = mProject.PopRedoSet(); + ApplyChanges(cs, false); +#if false + UpdateMenuItemsAndTitle(); + + // If the debug dialog is visible, update it. + if (mShowUndoRedoHistoryDialog != null) { + mShowUndoRedoHistoryDialog.BodyText = mProject.DebugGetUndoRedoHistory(); + } +#endif + } + #endregion References panel #region Notes panel diff --git a/SourceGenWPF/Res/Strings.xaml b/SourceGenWPF/Res/Strings.xaml index 303b8af..c8056e5 100644 --- a/SourceGenWPF/Res/Strings.xaml +++ b/SourceGenWPF/Res/Strings.xaml @@ -75,6 +75,9 @@ limitations under the License. Default settings: Symbol files: (unknown address) + Invalid Selection + Unable to format as word: selection must be an even number of bytes that have not previously been formatted as multi-byte values. Use Toggle Data Scan (Ctrl+D) to turn off auto-detection of strings and memory fill. + Unable to format as word: each selected region must have an even number of bytes ({0} region(s) are selected). no files available The file doesn't exist. File is empty diff --git a/SourceGenWPF/Res/Strings.xaml.cs b/SourceGenWPF/Res/Strings.xaml.cs index 096bd04..aa9ac90 100644 --- a/SourceGenWPF/Res/Strings.xaml.cs +++ b/SourceGenWPF/Res/Strings.xaml.cs @@ -131,6 +131,12 @@ namespace SourceGenWPF.Res { (string)Application.Current.FindResource("str_InitialSymbolFiles"); public static string INVALID_ADDRESS = (string)Application.Current.FindResource("str_InvalidAddress"); + public static string INVALID_FORMAT_WORD_SEL_CAPTION = + (string)Application.Current.FindResource("str_InvalidFormatWordSelCaption"); + public static string INVALID_FORMAT_WORD_SEL_NON1 = + (string)Application.Current.FindResource("str_InvalidFormatWordSelNon1"); + public static string INVALID_FORMAT_WORD_SEL_UNEVEN_FMT = + (string)Application.Current.FindResource("str_InvalidFormatWordSelUnevenFmt"); public static string NO_FILES_AVAILABLE = (string)Application.Current.FindResource("str_NoFilesAvailable"); public static string OPEN_DATA_DOESNT_EXIST = diff --git a/SourceGenWPF/WpfGui/MainWindow.xaml b/SourceGenWPF/WpfGui/MainWindow.xaml index f5b41f2..270744c 100644 --- a/SourceGenWPF/WpfGui/MainWindow.xaml +++ b/SourceGenWPF/WpfGui/MainWindow.xaml @@ -55,6 +55,11 @@ limitations under the License. + + + Del + + @@ -92,6 +97,11 @@ limitations under the License. F3 + + + Ctrl+W + + @@ -135,6 +145,11 @@ limitations under the License. Ctrl+D + + + Ctrl+B + + Ctrl+Z @@ -157,6 +172,8 @@ limitations under the License. CanExecute="IsProjectOpen" Executed="CloseCmd_Executed"/> + + + @@ -281,9 +302,9 @@ limitations under the License. - - - + + + diff --git a/SourceGenWPF/WpfGui/MainWindow.xaml.cs b/SourceGenWPF/WpfGui/MainWindow.xaml.cs index 2d07ec8..290418a 100644 --- a/SourceGenWPF/WpfGui/MainWindow.xaml.cs +++ b/SourceGenWPF/WpfGui/MainWindow.xaml.cs @@ -745,6 +745,10 @@ namespace SourceGenWPF.WpfGui { e.CanExecute = IsProjectOpen(); } + private void CanDeleteMlc(object sender, CanExecuteRoutedEventArgs e) { + e.CanExecute = IsProjectOpen() && mMainCtrl.CanDeleteMlc(); + } + private void CanEditAddress(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = IsProjectOpen() && mMainCtrl.CanEditAddress(); } @@ -777,6 +781,10 @@ namespace SourceGenWPF.WpfGui { e.CanExecute = IsProjectOpen() && mMainCtrl.CanEditStatusFlags(); } + private void CanFormatAsWord(object sender, CanExecuteRoutedEventArgs e) { + e.CanExecute = IsProjectOpen() && mMainCtrl.CanFormatAsWord(); + } + private void CanFormatSplitAddress(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = IsProjectOpen() && mMainCtrl.CanFormatSplitAddress(); } @@ -818,6 +826,10 @@ namespace SourceGenWPF.WpfGui { (counts.mCodeHints != 0 || counts.mDataHints != 0 || counts.mInlineDataHints != 0); } + private void CanToggleSingleByteFormat(object sender, CanExecuteRoutedEventArgs e) { + e.CanExecute = IsProjectOpen() && mMainCtrl.CanToggleSingleByteFormat(); + } + private void CanNavigateBackward(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = IsProjectOpen() && mMainCtrl.CanNavigateBackward(); } @@ -860,6 +872,10 @@ namespace SourceGenWPF.WpfGui { mMainCtrl.RunSourceGenerationTests(); } + private void DeleteMlcCmd_Executed(object sender, ExecutedRoutedEventArgs e) { + mMainCtrl.DeleteMlc(); + } + private void EditAddressCmd_Executed(object sender, ExecutedRoutedEventArgs e) { mMainCtrl.EditAddress(); } @@ -912,6 +928,10 @@ namespace SourceGenWPF.WpfGui { mMainCtrl.FindNext(); } + private void FormatAsWordCmd_Executed(object sender, ExecutedRoutedEventArgs e) { + mMainCtrl.FormatAsWord(); + } + private void FormatSplitAddressCmd_Executed(object sender, ExecutedRoutedEventArgs e) { mMainCtrl.FormatSplitAddress(); } @@ -1023,6 +1043,10 @@ namespace SourceGenWPF.WpfGui { mMainCtrl.ToggleDataScan(); } + private void ToggleSingleByteFormatCmd_Executed(object sender, ExecutedRoutedEventArgs e) { + mMainCtrl.ToggleSingleByteFormat(); + } + private void UndoCmd_Executed(object sender, ExecutedRoutedEventArgs e) { mMainCtrl.UndoChanges(); }