1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-25 05:29:31 +00:00

Add Edit Address

Pretty simple dialog, but it took a while to figure out the best
way to deal with input validation.  Works from the various menus as
well as double-clicking on .ORG and address column entries.

Also, moved some stuff around to places that made more sense.
This commit is contained in:
Andy McFadden 2019-06-16 16:34:47 -07:00
parent d3230ef0d6
commit 61ecb28d60
11 changed files with 518 additions and 63 deletions

View File

@ -18,13 +18,13 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Web.Script.Serialization;
using System.Windows;
using Asm65;
using CommonUtil;
using SourceGenWPF.ProjWin;
using System.Web.Script.Serialization;
using System.Text;
namespace SourceGenWPF {
/// <summary>
@ -57,7 +57,7 @@ namespace SourceGenWPF {
/// <summary>
/// Data backing the code list.
/// </summary>
public LineListGen CodeListGen { get; private set; }
public LineListGen CodeLineList { get; private set; }
#endregion Project state
@ -73,6 +73,11 @@ namespace SourceGenWPF {
private List<string> mRecentProjectPaths = new List<string>(MAX_RECENT_PROJECTS);
public const int MAX_RECENT_PROJECTS = 6;
/// <summary>
/// Analyzed selection state, updated whenever the selection changes.
/// </summary>
public SelectionState SelectionAnalysis { get; set; }
/// <summary>
/// Activity log generated by the code and data analyzers. Displayed in window.
/// </summary>
@ -139,6 +144,10 @@ namespace SourceGenWPF {
/// </summary>
private bool mUseMainAppDomainForPlugins = false;
private enum ColumnIndex {
Offset = 0, Address, Bytes, Flags, Attributes, Label, Opcode, Operand, Comment
}
#region Init and settings
@ -303,7 +312,7 @@ namespace SourceGenWPF {
#endif
// Finally, update the display list generator with all the fancy settings.
if (CodeListGen != null) {
if (CodeLineList != null) {
// Regenerate the display list with the latest formatter config and
// pseudo-op definition. (These are set as part of the refresh.)
UndoableChange uc =
@ -398,7 +407,7 @@ namespace SourceGenWPF {
dlg.ShowDialog();
}
CodeListGen = new LineListGen(mProject, mMainWin.CodeDisplayList,
CodeLineList = new LineListGen(mProject, mMainWin.CodeDisplayList,
mOutputFormatter, mPseudoOpNames);
RefreshProject(UndoableChange.ReanalysisScope.CodeAndData);
@ -481,9 +490,12 @@ namespace SourceGenWPF {
mReanalysisTimer.StartTask("Save selection");
int topItemIndex = mMainWin.CodeListView_GetTopIndex();
LineListGen.SavedSelection savedSel = LineListGen.SavedSelection.Generate(
CodeListGen, mMainWin.CodeDisplayList.SelectedIndices,
CodeListGen[topItemIndex].FileOffset);
CodeLineList, mMainWin.CodeDisplayList.SelectedIndices,
CodeLineList[topItemIndex].FileOffset);
//savedSel.DebugDump();
// Clear this so we don't try to fiddle with it later.
mTargetHighlightIndex = -1;
mReanalysisTimer.EndTask("Save selection");
mReanalysisTimer.StartTask("Apply changes");
@ -503,7 +515,7 @@ namespace SourceGenWPF {
}
mReanalysisTimer.EndTask(refreshTaskStr);
DisplayListSelection newSel = savedSel.Restore(CodeListGen, out topItemIndex);
DisplayListSelection newSel = savedSel.Restore(CodeLineList, out topItemIndex);
//newSel.DebugDump();
// Restore the selection. The selection-changed event will cause updates to the
@ -556,8 +568,8 @@ namespace SourceGenWPF {
Debug.WriteLine("CpuDef has changed, resetting formatter (now " +
mProject.CpuDef + ")");
mOutputFormatter = new Formatter(mFormatterConfig);
CodeListGen.SetFormatter(mOutputFormatter);
CodeListGen.SetPseudoOpNames(mPseudoOpNames);
CodeLineList.SetFormatter(mOutputFormatter);
CodeLineList.SetPseudoOpNames(mPseudoOpNames);
mOutputFormatterCpuDef = mProject.CpuDef;
}
@ -605,7 +617,7 @@ namespace SourceGenWPF {
IEnumerator<RangeSet.Range> iter = offsetSet.RangeListIterator;
while (iter.MoveNext()) {
RangeSet.Range range = iter.Current;
CodeListGen.GenerateRange(range.Low, range.High);
CodeLineList.GenerateRange(range.Low, range.High);
}
}
@ -633,7 +645,7 @@ namespace SourceGenWPF {
}
mReanalysisTimer.StartTask("Generate DisplayList");
CodeListGen.GenerateAll();
CodeLineList.GenerateAll();
mReanalysisTimer.EndTask("Generate DisplayList");
}
@ -955,6 +967,185 @@ namespace SourceGenWPF {
public void HandleCodeListDoubleClick(int row, int col) {
Debug.WriteLine("DCLICK: row=" + row + " col=" + col);
// Clicking on some types of lines, such as ORG directives, results in
// specific behavior regardless of which column you click in. We're just
// checking the clicked-on line to decide what action to take. If it doesn't
// make sense to do for a multi-line selection, the action will have been
// disabled.
LineListGen.Line line = CodeLineList[row];
switch (line.LineType) {
case LineListGen.Line.Type.EquDirective:
// Currently only does something for project symbols; platform symbols
// do nothing.
#if false
if (editProjectSymbolToolStripMenuItem.Enabled) {
EditProjectSymbol_Click(sender, e);
}
#endif
break;
case LineListGen.Line.Type.OrgDirective:
if (CanEditAddress()) {
EditAddress();
}
break;
case LineListGen.Line.Type.RegWidthDirective:
#if false
if (overrideStatusFlagsToolStripMenuItem.Enabled) {
EditStatusFlags_Click(sender, e);
}
#endif
break;
case LineListGen.Line.Type.LongComment:
#if false
if (editLongCommentToolStripMenuItem.Enabled) {
EditLongComment_Click(sender, e);
}
#endif
break;
case LineListGen.Line.Type.Note:
#if false
if (editNoteToolStripMenuItem.Enabled) {
EditNote_Click(sender, e);
}
#endif
break;
case LineListGen.Line.Type.Code:
case LineListGen.Line.Type.Data:
// For code and data, we have to break it down by column.
switch ((ColumnIndex)col) {
case ColumnIndex.Offset:
// does nothing
break;
case ColumnIndex.Address:
// edit address
if (CanEditAddress()) {
EditAddress();
}
break;
case ColumnIndex.Bytes:
#if false
if (showHexDumpToolStripMenuItem.Enabled) {
ShowHexDump_Click(sender, e);
}
#endif
break;
case ColumnIndex.Flags:
#if false
if (overrideStatusFlagsToolStripMenuItem.Enabled) {
EditStatusFlags_Click(sender, e);
}
#endif
break;
case ColumnIndex.Attributes:
// does nothing
break;
case ColumnIndex.Label:
#if false
if (editLabelToolStripMenuItem.Enabled) {
EditLabel_Click(sender, e);
}
#endif
break;
case ColumnIndex.Opcode:
// File offset should always be valid, since we excluded the EQU
// statements and header comment earlier.
if (line.FileOffset >= 0) {
Anattrib attr = mProject.GetAnattrib(line.FileOffset);
FormatDescriptor dfd = attr.DataDescriptor;
// Does this have an operand with an in-file target offset?
// (Resolve it as a numeric reference.)
if (attr.OperandOffset >= 0) {
// Yup, find the line for that offset and jump to it.
GoToOffset(attr.OperandOffset, false, true);
} else if (dfd != null && dfd.HasSymbol) {
// Operand has a symbol, do a symbol lookup.
int labelOffset = mProject.FindLabelOffsetByName(
dfd.SymbolRef.Label);
if (labelOffset >= 0) {
GoToOffset(labelOffset, false, true);
}
} else if (attr.IsDataStart || attr.IsInlineDataStart) {
// If it's an Address or Symbol, we can try to resolve
// the value. (Symbols should have been resolved by the
// previous clause, but Address entries would not have been.)
int operandOffset = DataAnalysis.GetDataOperandOffset(
mProject, line.FileOffset);
if (operandOffset >= 0) {
GoToOffset(operandOffset, false, true);
}
}
}
break;
case ColumnIndex.Operand:
#if false
if (editOperandToolStripMenuItem.Enabled) {
EditInstrDataOperand_Click(sender, e);
}
#endif
break;
case ColumnIndex.Comment:
#if false
if (editCommentToolStripMenuItem.Enabled) {
EditComment_Click(sender, e);
}
#endif
break;
}
break;
default:
Debug.WriteLine("Double-click: unhandled line type " + line.LineType);
break;
}
}
public bool CanEditAddress() {
if (SelectionAnalysis.mNumItemsSelected != 1) {
return false;
}
EntityCounts counts = SelectionAnalysis.mEntityCounts;
// Line must be code, data, or an ORG directive.
return (counts.mDataLines > 0 || counts.mCodeLines > 0) ||
(SelectionAnalysis.mLineType == LineListGen.Line.Type.OrgDirective);
}
public void EditAddress() {
int selIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
int offset = CodeLineList[selIndex].FileOffset;
Anattrib attr = mProject.GetAnattrib(offset);
EditAddress dlg = new EditAddress(attr.Address, mProject.CpuDef.MaxAddressValue);
dlg.Owner = mMainWin;
bool? ok = dlg.ShowDialog();
if (ok != true) {
return;
}
if (offset == 0 && dlg.Address < 0) {
// Not allowed. The AddressMap will just put it back, which confuses
// the undo operation.
Debug.WriteLine("EditAddress: not allowed to remove address at offset +000000");
} else if (attr.Address != dlg.Address) {
Debug.WriteLine("EditAddress: changing addr at offset +" + offset.ToString("x6") +
" to " + dlg.Address);
AddressMap addrMap = mProject.AddrMap;
// Get the previous address map entry for this exact offset, if one
// exists. This may be different from the value used as the default
// (attr.Address), which is the address assigned to the offset, in
// the case where no previous mapping existed.
int prevAddress = addrMap.Get(offset);
UndoableChange uc = UndoableChange.CreateAddressChange(offset,
prevAddress, dlg.Address);
ChangeSet cs = new ChangeSet(uc);
ApplyUndoableChanges(cs);
} else {
Debug.WriteLine("EditAddress: no change");
}
}
/// <summary>
@ -966,7 +1157,7 @@ namespace SourceGenWPF {
public void GoToOffset(int gotoOffset, bool jumpToNote, bool doPush) {
int curSelIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
int topLineIndex = CodeListGen.FindLineIndexByOffset(gotoOffset);
int topLineIndex = CodeLineList.FindLineIndexByOffset(gotoOffset);
if (topLineIndex < 0) {
Debug.Assert(false, "failed goto offset +" + gotoOffset.ToString("x6"));
return;
@ -974,13 +1165,13 @@ namespace SourceGenWPF {
int lastLineIndex;
if (jumpToNote) {
// Select all note lines, disregard the rest.
while (CodeListGen[topLineIndex].LineType != LineListGen.Line.Type.Note) {
while (CodeLineList[topLineIndex].LineType != LineListGen.Line.Type.Note) {
topLineIndex++;
Debug.Assert(CodeListGen[topLineIndex].FileOffset == gotoOffset);
Debug.Assert(CodeLineList[topLineIndex].FileOffset == gotoOffset);
}
lastLineIndex = topLineIndex + 1;
while (lastLineIndex < CodeListGen.Count &&
CodeListGen[lastLineIndex].LineType == LineListGen.Line.Type.Note) {
while (lastLineIndex < CodeLineList.Count &&
CodeLineList[lastLineIndex].LineType == LineListGen.Line.Type.Note) {
lastLineIndex++;
}
} else if (gotoOffset < 0) {
@ -988,8 +1179,8 @@ namespace SourceGenWPF {
lastLineIndex = topLineIndex + 1;
} else {
// Advance to the code or data line.
while (CodeListGen[topLineIndex].LineType != LineListGen.Line.Type.Code &&
CodeListGen[topLineIndex].LineType != LineListGen.Line.Type.Data) {
while (CodeLineList[topLineIndex].LineType != LineListGen.Line.Type.Code &&
CodeLineList[topLineIndex].LineType != LineListGen.Line.Type.Data) {
topLineIndex++;
}
lastLineIndex = topLineIndex + 1;
@ -1006,7 +1197,7 @@ namespace SourceGenWPF {
if (doPush) {
if (curSelIndex >= 0) {
// Update the back stack and associated controls.
mNavStack.Push(CodeListGen[curSelIndex].FileOffset, gotoOffset);
mNavStack.Push(CodeLineList[curSelIndex].FileOffset, gotoOffset);
#if false
UpdateMenuItemsAndTitle();
#endif
@ -1052,8 +1243,8 @@ namespace SourceGenWPF {
GoToOffset(fwdOff, false, false);
}
public void SelectionChanged(out SelectionState newState) {
newState = UpdateSelectionState();
public void SelectionChanged() {
SelectionAnalysis = UpdateSelectionState();
UpdateReferencesPanel();
UpdateInfoPanel();
@ -1069,21 +1260,22 @@ namespace SourceGenWPF {
// 0 if no lines are selected
// 1 if a single *item* is selected (regardless of number of lines)
// >1 if more than one item is selected (exact value not specified)
public int mNumSelected;
public int mNumItemsSelected;
// Single selection: the type of line selected.
// Single selection: the type of line selected. (Multi-sel: Unclassified)
public LineListGen.Line.Type mLineType;
// Single selection: is line an instruction with an operand.
// Single selection: is line an instruction with an operand. (Multi-sel: False)
public bool mIsInstructionWithOperand;
// Single selection: is line an EQU directive for a project symbol.
// Single selection: is line an EQU directive for a project symbol. (Multi-sel: False)
public bool mIsProjectSymbolEqu;
// Some totals.
public EntityCounts mEntityCounts;
public SelectionState() {
mLineType = LineListGen.Line.Type.Unclassified;
mEntityCounts = new EntityCounts();
}
}
@ -1106,9 +1298,9 @@ namespace SourceGenWPF {
state.mEntityCounts = new EntityCounts();
} else if (IsSingleItemSelected()) {
int firstIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
state.mNumSelected = 1;
state.mNumItemsSelected = 1;
state.mEntityCounts = GatherEntityCounts(firstIndex);
LineListGen.Line line = CodeListGen[firstIndex];
LineListGen.Line line = CodeLineList[firstIndex];
state.mLineType = line.LineType;
state.mIsInstructionWithOperand = (line.LineType == LineListGen.Line.Type.Code &&
@ -1120,7 +1312,7 @@ namespace SourceGenWPF {
state.mIsProjectSymbolEqu = (defSym.SymbolSource == Symbol.Source.Project);
}
} else {
state.mNumSelected = 2;
state.mNumItemsSelected = 2;
state.mEntityCounts = GatherEntityCounts(-1);
}
@ -1168,7 +1360,7 @@ namespace SourceGenWPF {
// not selected, ignore
continue;
}
LineListGen.Line line = CodeListGen[i];
LineListGen.Line line = CodeLineList[i];
switch (line.LineType) {
case LineListGen.Line.Type.Code:
codeLines++;
@ -1248,8 +1440,8 @@ namespace SourceGenWPF {
}
// Just check the first and last entries to see if they're the same.
LineListGen.Line firstItem = CodeListGen[firstIndex];
LineListGen.Line lastItem = CodeListGen[lastIndex];
LineListGen.Line firstItem = CodeLineList[firstIndex];
LineListGen.Line lastItem = CodeLineList[lastIndex];
if (firstItem.FileOffset == lastItem.FileOffset &&
firstItem.LineType == lastItem.LineType) {
return true;
@ -1279,7 +1471,7 @@ namespace SourceGenWPF {
return -1;
}
int selIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
LineListGen.Line line = CodeListGen[selIndex];
LineListGen.Line line = CodeLineList[selIndex];
if (!line.IsCodeOrData) {
return -1;
}
@ -1288,13 +1480,13 @@ namespace SourceGenWPF {
// Does this have an operand with an in-file target offset?
Anattrib attr = mProject.GetAnattrib(line.FileOffset);
if (attr.OperandOffset >= 0) {
return CodeListGen.FindCodeDataIndexByOffset(attr.OperandOffset);
return CodeLineList.FindCodeDataIndexByOffset(attr.OperandOffset);
} else if (attr.IsDataStart || attr.IsInlineDataStart) {
// If it's an Address or Symbol, we can try to resolve
// the value.
int operandOffset = DataAnalysis.GetDataOperandOffset(mProject, line.FileOffset);
if (operandOffset >= 0) {
return CodeListGen.FindCodeDataIndexByOffset(operandOffset);
return CodeLineList.FindCodeDataIndexByOffset(operandOffset);
}
}
return -1;
@ -1359,7 +1551,7 @@ namespace SourceGenWPF {
if (firstByteOnly) {
sel = new RangeSet();
foreach (int index in mMainWin.CodeDisplayList.SelectedIndices) {
int offset = CodeListGen[index].FileOffset;
int offset = CodeLineList[index].FileOffset;
if (offset >= 0) {
// Not interested in the header stuff for hinting.
sel.Add(offset);
@ -1408,7 +1600,7 @@ namespace SourceGenWPF {
RangeSet rs = new RangeSet();
foreach (int index in mMainWin.CodeDisplayList.SelectedIndices) {
int offset = CodeListGen[index].FileOffset;
int offset = CodeLineList[index].FileOffset;
// Mark every byte of an instruction or multi-byte data item --
// everything that is represented by the line the user selected.
@ -1436,6 +1628,7 @@ namespace SourceGenWPF {
#endregion Main window UI event handlers
#region References panel
/// <summary>
@ -1453,7 +1646,7 @@ namespace SourceGenWPF {
return;
}
int lineIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
LineListGen.Line.Type type = CodeListGen[lineIndex].LineType;
LineListGen.Line.Type type = CodeLineList[lineIndex].LineType;
if (type != LineListGen.Line.Type.Code &&
type != LineListGen.Line.Type.Data &&
type != LineListGen.Line.Type.EquDirective) {
@ -1462,7 +1655,7 @@ namespace SourceGenWPF {
}
// Find the appropriate xref set.
int offset = CodeListGen[lineIndex].FileOffset;
int offset = CodeLineList[lineIndex].FileOffset;
XrefSet xrefs;
if (offset < 0) {
int index = LineListGen.DefSymIndexFromOffset(offset);
@ -1582,7 +1775,7 @@ namespace SourceGenWPF {
return;
}
int lineIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
LineListGen.Line line = CodeListGen[lineIndex];
LineListGen.Line line = CodeLineList[lineIndex];
StringBuilder sb = new StringBuilder(250);
// TODO(someday): this should be made easier to localize

View File

@ -1,5 +1,6 @@
<!--
Copyright 2019 faddenSoft
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@ -153,13 +154,18 @@ See also https://github.com/fadden/DisasmUiTest
</Style>
<!-- Highlighting for individual cells. -->
<LinearGradientBrush x:Key="HighlightedCellFill" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFD9Fff4" Offset="0"/>
<GradientStop Color="#FF9Bfbdd" Offset="1"/>
</LinearGradientBrush>
<DataTemplate x:Key="addrHighlightTemplate">
<TextBlock>
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=HasAddrLabelHighlight}" Value="True">
<Setter Property="TextBlock.Background" Value="{StaticResource ListItemSelectedFill}"/>
<Setter Property="TextBlock.Background" Value="{StaticResource HighlightedCellFill}"/>
</DataTrigger>
</Style.Triggers>
</Style>
@ -175,7 +181,7 @@ See also https://github.com/fadden/DisasmUiTest
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=HasAddrLabelHighlight}" Value="True">
<Setter Property="TextBlock.Background" Value="{StaticResource ListItemSelectedFill}"/>
<Setter Property="TextBlock.Background" Value="{StaticResource HighlightedCellFill}"/>
</DataTrigger>
</Style.Triggers>
</Style>

View File

@ -1,4 +1,20 @@
<Window x:Class="SourceGenWPF.ProjWin.DataFileLoadIssue"
<!--
Copyright 2019 faddenSoft
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<Window x:Class="SourceGenWPF.ProjWin.DataFileLoadIssue"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

View File

@ -1,12 +1,29 @@
<Window x:Class="SourceGenWPF.ProjWin.DiscardChanges"
<!--
Copyright 2019 faddenSoft
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<Window x:Class="SourceGenWPF.ProjWin.DiscardChanges"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SourceGenWPF.ProjWin"
mc:Ignorable="d"
Title="Discard Changes?" ShowInTaskbar="False"
Width="398" Height="125" ResizeMode="NoResize" WindowStartupLocation="CenterOwner">
Title="Discard Changes?"
SizeToContent="WidthAndHeight" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner">
<StackPanel Margin="8">
<TextBlock Text="You have unsaved changes that will be lost if you continue."/>
<TextBlock Margin="0,8,0,0" Text="How do you wish to proceed?"/>

View File

@ -0,0 +1,55 @@
<!--
Copyright 2019 faddenSoft
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<Window x:Class="SourceGenWPF.ProjWin.EditAddress"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SourceGenWPF.ProjWin"
mc:Ignorable="d"
Title="Set Address"
SizeToContent="WidthAndHeight" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
Loaded="Window_Loaded">
<StackPanel Margin="8">
<TextBlock Text="Enter 16-bit or 24-bit address in hexadecimal, e.g. $1000 or 00/be00."/>
<TextBlock Text="Leave the field blank to remove the address override."/>
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<TextBlock Text="Address:"/>
<TextBox Name="addrTextBox" Width="100" Margin="4,0,0,0"
FontFamily="{StaticResource GeneralMonoFont}"
Text="{Binding Path=AddressText, UpdateSourceTrigger=PropertyChanged}"
IsInactiveSelectionHighlightEnabled="True"
TextChanged="TextBox_TextChanged">
<TextBox.Resources>
<!-- default non-focus highlight color is nearly invisible -->
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}"
Color="LightBlue"/>
</TextBox.Resources>
</TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,8,0,0">
<Button Name="okButton" Content="OK" IsDefault="True"
Width="70" Click="OkButton_Click"/>
<Button Name="cancelButton" Content="Cancel" IsCancel="True"
Width="70" Margin="4,0,0,0"/>
</StackPanel>
</StackPanel>
</Window>

View File

@ -0,0 +1,121 @@
/*
* Copyright 2019 faddenSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
namespace SourceGenWPF.ProjWin {
/// <summary>
/// Edit Address dialog.
/// </summary>
public partial class EditAddress : Window {
/// <summary>
/// Address typed by user. Only valid after the dialog returns OK. Will be set to -1
/// if the user is attempting to delete the address.
/// </summary>
public int Address { get; private set; }
/// <summary>
/// Maximum allowed address value.
/// </summary>
private int mMaxAddressValue;
/// <summary>
/// Bound two-way property.
/// </summary>
public string AddressText { get; set; }
public EditAddress(int initialAddr, int maxAddressValue) {
// Set the property before initializing the window -- we don't have a property
// change notifier.
Address = -2;
mMaxAddressValue = maxAddressValue;
AddressText = Asm65.Address.AddressToString(initialAddr, false);
this.DataContext = this;
InitializeComponent();
}
private void OkButton_Click(object sender, RoutedEventArgs e) {
if (AddressText.Length == 0) {
Address = -1;
} else {
Asm65.Address.ParseAddress(AddressText, mMaxAddressValue, out int addr);
Address = addr;
}
DialogResult = true;
}
/// <summary>
/// Handles a TextChanged event on the address text box.
/// </summary>
/// <remarks>
/// Must have UpdateSourceTrigger=PropertyChanged set for this to work. The default
/// for TextBox is LostFocus.
/// </remarks>
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) {
if (IsLoaded) {
UpdateOkEnabled();
}
}
private void UpdateOkEnabled() {
string text = AddressText;
okButton.IsEnabled = (text.Length == 0) ||
Asm65.Address.ParseAddress(text, mMaxAddressValue, out int unused);
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
addrTextBox.SelectAll();
addrTextBox.Focus();
}
}
// I briefly played with the validation rules. However, they're primarily designed for
// form entry, which means they fire when focus leaves the text box. [note: not sure if
// this would change with UpdateSourceTrigger=PropertyChanged] I want the OK button to be
// kept constantly up to date as the user types, so this didn't really work. It's also a
// lot bigger and uglier than just handling an event.
//
// It's also sort of awkward to pass parameters, like MaxAddressValue, into the
// validation rule.
// https://social.technet.microsoft.com/wiki/contents/articles/31422.wpf-passing-a-data-bound-value-to-a-validation-rule.aspx
//
// Speaking of awkward, updating the OK button enable/disable through validation
// is possible via MultiDataTrigger.
//public class AddressValidationRule : ValidationRule {
// public int MaxAddress { get; set; }
// public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
// string text = value.ToString();
// Debug.WriteLine("VALIDATE " + text);
// if ((text.Length == 0) ||
// Asm65.Address.ParseAddress(text, MaxAddress, out int unused)) {
// return new ValidationResult(true, null);
// } else {
// return new ValidationResult(false, "Invalid address");
// }
// }
//}
}

View File

@ -1,5 +1,6 @@
<!--
Copyright 2019 faddenSoft
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@ -48,6 +49,7 @@ limitations under the License.
</RoutedUICommand.InputGestures>
</RoutedUICommand>
<RoutedUICommand x:Key="CloseCmd" Text="Close"/>
<RoutedUICommand x:Key="EditAddressCmd" Text="Set Address..."/>
<RoutedUICommand x:Key="HintAsCodeEntryPointCmd" Text="Hint As Code Entry Point"/>
<RoutedUICommand x:Key="HintAsDataStartCmd" Text="Hint As Data Start"/>
<RoutedUICommand x:Key="HintAsInlineDataCmd" Text="Hint As Inline Data"/>
@ -92,6 +94,8 @@ limitations under the License.
CanExecute="IsProjectOpen" Executed="AssembleCmd_Executed"/>
<CommandBinding Command="{StaticResource CloseCmd}"
CanExecute="IsProjectOpen" Executed="CloseCmd_Executed"/>
<CommandBinding Command="{StaticResource EditAddressCmd}"
CanExecute="CanEditAddress" Executed="EditAddressCmd_Executed"/>
<CommandBinding Command="Help"
Executed="HelpCmd_Executed"/>
<CommandBinding Command="{StaticResource HintAsCodeEntryPointCmd}"
@ -152,7 +156,7 @@ limitations under the License.
<MenuItem Header="Settings..."/>
</MenuItem>
<MenuItem Name="ActionsMenu" Header="_Actions">
<MenuItem Header="Set Address..."/>
<MenuItem Command="{StaticResource EditAddressCmd}"/>
<MenuItem Header="Override Status Flags..."/>
<MenuItem Header="Edit Label..."/>
<MenuItem Header="Edit Operand..."/>

View File

@ -56,11 +56,6 @@ namespace SourceGenWPF.ProjWin {
/// </summary>
private MainController mMainCtrl;
/// <summary>
/// Analyzed selection state.
/// </summary>
private MainController.SelectionState mSelectionState;
// Handle to protected ListView.SetSelectedItems() method
private MethodInfo listViewSetSelectedItems;
@ -80,8 +75,6 @@ namespace SourceGenWPF.ProjWin {
mMainCtrl = new MainController(this);
mSelectionState = new MainController.SelectionState();
AddMultiKeyGestures();
//GridView gv = (GridView)codeListView.View;
@ -258,9 +251,8 @@ namespace SourceGenWPF.ProjWin {
// Update the selected-item bitmap.
CodeDisplayList.SelectedIndices.SelectionChanged(e);
// Notify MainController that the selection has changed. This hands back an updated
// selection summary, which is used for "can execute" methods.
mMainCtrl.SelectionChanged(out mSelectionState);
// Notify MainController that the selection has changed.
mMainCtrl.SelectionChanged();
Debug.Assert(CodeDisplayList.SelectedIndices.DebugValidateSelectionCount(
codeListView.SelectedItems.Count));
@ -474,12 +466,20 @@ namespace SourceGenWPF.ProjWin {
e.CanExecute = mMainCtrl != null && mMainCtrl.IsProjectOpen();
}
private void CanEditAddress(object sender, CanExecuteRoutedEventArgs e) {
if (mMainCtrl == null || !mMainCtrl.IsProjectOpen()) {
e.CanExecute = false;
return;
}
e.CanExecute = mMainCtrl.CanEditAddress();
}
private void CanHintAsCodeEntryPoint(object sender, CanExecuteRoutedEventArgs e) {
if (mMainCtrl == null || !mMainCtrl.IsProjectOpen()) {
e.CanExecute = false;
return;
}
MainController.EntityCounts counts = mSelectionState.mEntityCounts;
MainController.EntityCounts counts = mMainCtrl.SelectionAnalysis.mEntityCounts;
e.CanExecute = (counts.mDataLines > 0 || counts.mCodeLines > 0) &&
(counts.mDataHints != 0 || counts.mInlineDataHints != 0 || counts.mNoHints != 0);
}
@ -488,7 +488,7 @@ namespace SourceGenWPF.ProjWin {
e.CanExecute = false;
return;
}
MainController.EntityCounts counts = mSelectionState.mEntityCounts;
MainController.EntityCounts counts = mMainCtrl.SelectionAnalysis.mEntityCounts;
e.CanExecute = (counts.mDataLines > 0 || counts.mCodeLines > 0) &&
(counts.mCodeHints != 0 || counts.mInlineDataHints != 0 || counts.mNoHints != 0);
}
@ -497,7 +497,7 @@ namespace SourceGenWPF.ProjWin {
e.CanExecute = false;
return;
}
MainController.EntityCounts counts = mSelectionState.mEntityCounts;
MainController.EntityCounts counts = mMainCtrl.SelectionAnalysis.mEntityCounts;
e.CanExecute = (counts.mDataLines > 0 || counts.mCodeLines > 0) &&
(counts.mCodeHints != 0 || counts.mDataHints != 0 || counts.mNoHints != 0);
}
@ -506,7 +506,7 @@ namespace SourceGenWPF.ProjWin {
e.CanExecute = false;
return;
}
MainController.EntityCounts counts = mSelectionState.mEntityCounts;
MainController.EntityCounts counts = mMainCtrl.SelectionAnalysis.mEntityCounts;
e.CanExecute = (counts.mDataLines > 0 || counts.mCodeLines > 0) &&
(counts.mCodeHints != 0 || counts.mDataHints != 0 || counts.mInlineDataHints != 0);
}
@ -541,6 +541,10 @@ namespace SourceGenWPF.ProjWin {
}
}
private void EditAddressCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
mMainCtrl.EditAddress();
}
private void HelpCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
mMainCtrl.ShowHelp();
}

View File

@ -1,4 +1,20 @@
<Window x:Class="SourceGenWPF.ProjWin.ProjectLoadIssues"
<!--
Copyright 2019 faddenSoft
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<Window x:Class="SourceGenWPF.ProjWin.ProjectLoadIssues"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

View File

@ -1,4 +1,20 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
<!--
Copyright 2019 faddenSoft
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SourceGenWPF.Res">

View File

@ -81,6 +81,9 @@
<Compile Include="ProjWin\DiscardChanges.xaml.cs">
<DependentUpon>DiscardChanges.xaml</DependentUpon>
</Compile>
<Compile Include="ProjWin\EditAddress.xaml.cs">
<DependentUpon>EditAddress.xaml</DependentUpon>
</Compile>
<Compile Include="ProjWin\ProjectLoadIssue.xaml.cs">
<DependentUpon>ProjectLoadIssue.xaml</DependentUpon>
</Compile>
@ -161,6 +164,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="ProjWin\EditAddress.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="ProjWin\MainWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>