diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index c9ef7cb..d6be468 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -1559,7 +1559,7 @@ namespace SourceGen { } break; case CodeListColumn.Opcode: - HandleDoubleClickOnOpcode(line); + JumpToOperandTarget(line, false); break; case CodeListColumn.Operand: if (CanEditOperand()) { @@ -1581,11 +1581,17 @@ namespace SourceGen { } } - private void HandleDoubleClickOnOpcode(LineListGen.Line line) { + /// + /// Jumps to the line referenced by the operand of the selected line. + /// + /// Selected line. + /// If set, don't actually do the goto. + /// True if a jump is available for this line. + private bool JumpToOperandTarget(LineListGen.Line line, bool testOnly) { if (line.FileOffset < 0) { // Double-click on project symbol EQUs and the file header comment are handled // elsewhere. - return; + return false; } Anattrib attr = mProject.GetAnattrib(line.FileOffset); @@ -1595,20 +1601,29 @@ namespace SourceGen { // (Resolve it as a numeric reference.) if (attr.OperandOffset >= 0) { // Yup, find the line for that offset and jump to it. - GoToLocation(new NavStack.Location(attr.OperandOffset, 0, false), - GoToMode.JumpToCodeData, true); + if (!testOnly) { + GoToLocation(new NavStack.Location(attr.OperandOffset, 0, false), + GoToMode.JumpToCodeData, true); + } + return true; } else if (dfd != null && dfd.HasSymbol) { // Operand has a symbol, do a symbol lookup. if (dfd.SymbolRef.IsVariable) { - GoToVarDefinition(line.FileOffset, dfd.SymbolRef, true); + if (!testOnly) { + GoToVarDefinition(line.FileOffset, dfd.SymbolRef, true); + } + return true; } else { if (mProject.SymbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) { if (sym.SymbolSource == Symbol.Source.User || sym.SymbolSource == Symbol.Source.Auto) { int labelOffset = mProject.FindLabelOffsetByName(dfd.SymbolRef.Label); if (labelOffset >= 0) { - GoToLocation(new NavStack.Location(labelOffset, 0, false), - GoToMode.JumpToCodeData, true); + if (!testOnly) { + GoToLocation(new NavStack.Location(labelOffset, 0, false), + GoToMode.JumpToCodeData, true); + } + return true; } } else if (sym.SymbolSource == Symbol.Source.Platform || sym.SymbolSource == Symbol.Source.Project) { @@ -1616,9 +1631,11 @@ namespace SourceGen { for (int i = 0; i < mProject.ActiveDefSymbolList.Count; i++) { if (mProject.ActiveDefSymbolList[i] == sym) { int offset = LineListGen.DefSymOffsetFromIndex(i); - GoToLocation(new NavStack.Location(offset, 0, false), - GoToMode.JumpToCodeData, true); - break; + if (!testOnly) { + GoToLocation(new NavStack.Location(offset, 0, false), + GoToMode.JumpToCodeData, true); + } + return true; } } } else { @@ -1635,10 +1652,15 @@ namespace SourceGen { // previous clause, but Address entries would not have been.) int operandOffset = DataAnalysis.GetDataOperandOffset(mProject, line.FileOffset); if (operandOffset >= 0) { - GoToLocation(new NavStack.Location(operandOffset, 0, false), - GoToMode.JumpToCodeData, true); + if (!testOnly) { + GoToLocation(new NavStack.Location(operandOffset, 0, false), + GoToMode.JumpToCodeData, true); + } + return true; } } + + return false; } public bool CanDeleteMlc() { @@ -2704,6 +2726,10 @@ namespace SourceGen { // Update the selection. mMainWin.CodeListView_SelectRange(topLineIndex, lastLineIndex - topLineIndex); + // Set the focus to the first selected item. The focus is used by the keyboard + // handler to decide what the up/down arrows select next. + mMainWin.CodeListView_SetSelectionFocus(); + if (doPush) { // Update the back stack and associated controls. mNavStack.Push(prevLoc); @@ -2783,6 +2809,27 @@ namespace SourceGen { } } + public bool CanJumpToOperand() { + if (SelectionAnalysis.mNumItemsSelected != 1) { + return false; + } + LineListGen.Line.Type lineType = SelectionAnalysis.mLineType; + if (lineType != LineListGen.Line.Type.Code && + lineType != LineListGen.Line.Type.Data) { + return false; + } + + int selIndex = mMainWin.CodeListView_GetFirstSelectedIndex(); + Debug.Assert(selIndex >= 0); + return JumpToOperandTarget(CodeLineList[selIndex], true); + } + + public void JumpToOperand() { + int selIndex = mMainWin.CodeListView_GetFirstSelectedIndex(); + Debug.Assert(selIndex >= 0); + JumpToOperandTarget(CodeLineList[selIndex], false); + } + /// /// Calculates the currently-selected location. /// diff --git a/SourceGen/RuntimeData/Help/mainwin.html b/SourceGen/RuntimeData/Help/mainwin.html index 4f53bbf..e7646bc 100644 --- a/SourceGen/RuntimeData/Help/mainwin.html +++ b/SourceGen/RuntimeData/Help/mainwin.html @@ -381,6 +381,10 @@ address, like "FEED", the label takes precedence. To jump to the address write "$FEED" instead. If you enter a non-unique label, the selection will jump to the nearest instance.

+

If an instruction or data line has an operand that references an address +in the file, you can navigate to the operand's location with +Navigate > Jump to Operand.

+

When you edit something, lines throughout the listing can change. This is different from a source code editor, where editing a line just changes that line. To allow you to watch the effects changes have, the undo/redo diff --git a/SourceGen/Tests/GenTest.cs b/SourceGen/Tests/GenTest.cs index 9dc1378..d2fe0d7 100644 --- a/SourceGen/Tests/GenTest.cs +++ b/SourceGen/Tests/GenTest.cs @@ -20,6 +20,7 @@ using System.Diagnostics; using System.IO; using System.Text.RegularExpressions; using System.Windows.Media; + using Asm65; using CommonUtil; using SourceGen.AsmGen; diff --git a/SourceGen/WpfGui/MainWindow.xaml b/SourceGen/WpfGui/MainWindow.xaml index fead078..6f9b1c5 100644 --- a/SourceGen/WpfGui/MainWindow.xaml +++ b/SourceGen/WpfGui/MainWindow.xaml @@ -143,6 +143,11 @@ limitations under the License. + + + Ctrl+J + + Alt+Left @@ -274,6 +279,8 @@ limitations under the License. CanExecute="CanHintAsDataStart" Executed="HintAsDataStartCmd_Executed"/> + + diff --git a/SourceGen/WpfGui/MainWindow.xaml.cs b/SourceGen/WpfGui/MainWindow.xaml.cs index 3586546..3259a11 100644 --- a/SourceGen/WpfGui/MainWindow.xaml.cs +++ b/SourceGen/WpfGui/MainWindow.xaml.cs @@ -881,6 +881,10 @@ namespace SourceGen.WpfGui { mIsSplitterBeingDragged = false; } + public void CodeListView_SetSelectionFocus() { + ItemContainerGenerator_StatusChanged(null, null); + } + ///

/// Returns the index of the line that's currently at the top of the control. /// @@ -1089,9 +1093,12 @@ namespace SourceGen.WpfGui { (counts.mCodeHints != 0 || counts.mDataHints != 0 || counts.mInlineDataHints != 0); } + private void CanJumpToOperand(object sender, CanExecuteRoutedEventArgs e) { + e.CanExecute = IsProjectOpen() && mMainCtrl.CanJumpToOperand(); + } + private void CanSaveProject(object sender, CanExecuteRoutedEventArgs e) { - e.CanExecute = mMainCtrl != null && mMainCtrl.IsProjectOpen && - !mMainCtrl.IsProjectReadOnly; + e.CanExecute = IsProjectOpen() && !mMainCtrl.IsProjectReadOnly; } private void CanToggleSingleByteFormat(object sender, CanExecuteRoutedEventArgs e) { @@ -1258,6 +1265,10 @@ namespace SourceGen.WpfGui { mMainCtrl.MarkAsType(CodeAnalysis.TypeHint.InlineData, false); } + private void JumpToOperandCmd_Executed(object sender, ExecutedRoutedEventArgs e) { + mMainCtrl.JumpToOperand(); + } + private void NavigateBackwardCmd_Executed(object sender, ExecutedRoutedEventArgs e) { mMainCtrl.NavigateBackward(); }