/* * 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.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using Asm65; using CommonWPF; namespace SourceGen.WpfGui { /// /// Edit a LocalVariableTable. /// public partial class EditLocalVariableTable : Window, INotifyPropertyChanged { /// /// Output. Will be null if the table was deleted, or if cancel was hit while /// creating a new table. /// public LocalVariableTable NewTable { get; private set; } /// /// Output. If the table was moved, the new offset will be different from the old. /// public int NewOffset { get; private set; } // Item for the symbol list view ItemsSource. public class FormattedSymbol { public string Label { get; private set; } public string Value { get; private set; } public string Type { get; private set; } public string Width { get; private set; } public string Comment { get; private set; } public FormattedSymbol(string label, string value, string type, string width, string comment) { Label = label; Value = value; Type = type; Width = width; Comment = comment; } } public ObservableCollection Variables { get; private set; } = new ObservableCollection(); /// /// Clear-previous flag. /// public bool ClearPrevious { get { return mWorkTable.ClearPrevious; } set { mWorkTable.ClearPrevious = value; OnPropertyChanged(); } } /// /// True if this is not a new table. (Using "not" because that's the sense we /// need in XAML.) /// public bool IsNotNewTable { get { return mIsNotNewTable; } set { mIsNotNewTable = value; OnPropertyChanged(); } } private bool mIsNotNewTable; /// /// Table header text string, formatted at load time. /// public string TableHeaderText { get { return mTableHeaderText; } set { mTableHeaderText = value; OnPropertyChanged(); } } private string mTableHeaderText; /// /// Working set. Used internally to hold state. /// private LocalVariableTable mWorkTable; /// /// Project reference. /// private DisasmProject mProject; /// /// Format object to use when formatting addresses and constants. /// private Formatter mFormatter; /// /// Symbol table for uniqueness check. /// private SymbolTable mSymbolTable; /// /// Table offset, for move ops. /// private int mOffset; // INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } /// /// Constructor. lvt will be null when creating a new entry. /// public EditLocalVariableTable(Window owner, DisasmProject project, Formatter formatter, LocalVariableTable lvt, int offset) { InitializeComponent(); Owner = owner; DataContext = this; mProject = project; mFormatter = formatter; mSymbolTable = project.SymbolTable; mOffset = NewOffset = offset; if (lvt != null) { mWorkTable = new LocalVariableTable(lvt); mIsNotNewTable = true; } else { mWorkTable = new LocalVariableTable(); } LoadVariables(); } public void Window_Loaded(object sender, RoutedEventArgs e) { string fmt = (string)FindResource("str_TableHeaderFmt"); TableHeaderText = string.Format(fmt, mFormatter.FormatOffset24(mOffset)); UpdateControls(); } /// /// Loads entries from the work table into the items source. /// private void LoadVariables() { Variables.Clear(); for (int i = 0; i < mWorkTable.Count; i++) { DefSymbol defSym = mWorkTable[i]; string typeStr; if (defSym.SymbolType == Symbol.Type.Constant) { typeStr = Res.Strings.ABBREV_CONSTANT; } else { typeStr = Res.Strings.ABBREV_ADDRESS; } FormattedSymbol fsym = new FormattedSymbol( defSym.Label, mFormatter.FormatValueInBase(defSym.Value, defSym.DataDescriptor.NumBase), typeStr, defSym.DataDescriptor.Length.ToString(), defSym.Comment); Variables.Add(fsym); } } private void UpdateControls() { // Enable or disable the edit/remove buttons based on how many items are selected. // (We're currently configured for single-select, so this is really just a != 0 test.) int symSelCount = symbolsListView.SelectedItems.Count; removeSymbolButton.IsEnabled = (symSelCount == 1); editSymbolButton.IsEnabled = (symSelCount == 1); } private void OkButton_Click(object sender, RoutedEventArgs e) { NewTable = mWorkTable; DialogResult = true; } private void DeleteTableButton_Click(object sender, RoutedEventArgs e) { MessageBoxResult result = MessageBox.Show((string)FindResource("str_ConfirmDelete"), (string)FindResource("str_ConfirmDeleteCaption"), MessageBoxButton.YesNo, MessageBoxImage.Question); if (result == MessageBoxResult.Yes) { NewTable = null; DialogResult = true; } } private void MoveTableButton_Click(object sender, RoutedEventArgs e) { EditLvTableLocation dlg = new EditLvTableLocation(this, mProject, mOffset, NewOffset); if (dlg.ShowDialog() == true) { NewOffset = dlg.NewOffset; } } private void SymbolsListView_SelectionChanged(object sender, SelectionChangedEventArgs e) { UpdateControls(); } private void SymbolsListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) { ListViewItem lvi = symbolsListView.GetClickedItem(e); if (lvi == null) { return; } FormattedSymbol item = (FormattedSymbol)lvi.Content; DefSymbol defSym = mWorkTable.GetByLabel(item.Label); DoEditSymbol(defSym); } private void NewSymbolButton_Click(object sender, RoutedEventArgs e) { EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, mWorkTable.GetSortedByLabel(), null, mSymbolTable, true, false); dlg.ShowDialog(); if (dlg.DialogResult == true) { Debug.WriteLine("ADD: " + dlg.NewSym); mWorkTable.AddOrReplace(dlg.NewSym); // Reload the contents. This loses the selection, but that shouldn't be an // issue when adding new symbols. To do this incrementally we'd need to add // the symbol at the correct sorted position. LoadVariables(); UpdateControls(); } } private void EditSymbolButton_Click(object sender, EventArgs e) { // Single-select list view, button dimmed when no selection. Debug.Assert(symbolsListView.SelectedItems.Count == 1); FormattedSymbol item = (FormattedSymbol)symbolsListView.SelectedItems[0]; DefSymbol defSym = mWorkTable.GetByLabel(item.Label); DoEditSymbol(defSym); } private void DoEditSymbol(DefSymbol defSym) { EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, mWorkTable.GetSortedByLabel(), defSym, mSymbolTable, true, false); dlg.ShowDialog(); if (dlg.DialogResult == true) { // Label might have changed, so remove old before adding new. mWorkTable.RemoveByLabel(defSym.Label); mWorkTable.AddOrReplace(dlg.NewSym); LoadVariables(); UpdateControls(); } } private void RemoveSymbolButton_Click(object sender, RoutedEventArgs e) { // Single-select list view, button dimmed when no selection. Debug.Assert(symbolsListView.SelectedItems.Count == 1); int selectionIndex = symbolsListView.SelectedIndex; FormattedSymbol item = (FormattedSymbol)symbolsListView.SelectedItems[0]; mWorkTable.RemoveByLabel(item.Label); LoadVariables(); UpdateControls(); // Restore selection to the item that used to come after the one we just deleted, // so you can hit "Remove" repeatedly to delete multiple items. int newCount = symbolsListView.Items.Count; if (selectionIndex >= newCount) { selectionIndex = newCount - 1; } if (selectionIndex >= 0) { symbolsListView.SelectedIndex = selectionIndex; removeSymbolButton.Focus(); } } } }