/* * 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.Runtime.CompilerServices; using System.Windows; using System.Windows.Media; namespace SourceGen.WpfGui { /// /// Edit a label. /// public partial class EditLabel : Window, INotifyPropertyChanged { /// /// Symbol object. When the dialog completes successfully, /// this will have the new symbol, or null if the user deleted the label. /// public Symbol LabelSym { get; private set; } /// /// Address we are editing the label for. /// private int mAddress; /// /// Reference to DisasmProject's SymbolTable. /// private SymbolTable mSymbolTable; // Dialog label text color, saved off at dialog load time. private Brush mDefaultLabelColor; /// /// Set to true when input is valid. Controls whether the OK button is enabled. /// public bool IsValid { get { return mIsValid; } set { mIsValid = value; OnPropertyChanged(); } } private bool mIsValid; /// /// Property backing the text in the text entry box. /// public string LabelText { get { return mLabelText; } set { mLabelText = value; LabelTextBox_TextChanged(); OnPropertyChanged(); } } string mLabelText; // INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public EditLabel(Window owner, Symbol origSym, int address, SymbolTable symbolTable) { InitializeComponent(); Owner = owner; DataContext = this; LabelSym = origSym; mAddress = address; mSymbolTable = symbolTable; } private void Window_Loaded(object sender, RoutedEventArgs e) { mDefaultLabelColor = maxLengthLabel.Foreground; if (LabelSym == null) { LabelText = string.Empty; radioButtonLocal.IsChecked = true; } else { LabelText = LabelSym.Label; switch (LabelSym.SymbolType) { case Symbol.Type.LocalOrGlobalAddr: radioButtonLocal.IsChecked = true; break; case Symbol.Type.GlobalAddr: radioButtonGlobal.IsChecked = true; break; case Symbol.Type.GlobalAddrExport: radioButtonExport.IsChecked = true; break; default: Debug.Assert(false); // WTF radioButtonLocal.IsChecked = true; break; } } } private void Window_ContentRendered(object sender, EventArgs e) { labelTextBox.SelectAll(); labelTextBox.Focus(); } private void LabelTextBox_TextChanged() { string str = LabelText; bool valid = true; if (str.Length == 1 || str.Length > Asm65.Label.MAX_LABEL_LEN) { valid = false; maxLengthLabel.Foreground = Brushes.Red; } else { maxLengthLabel.Foreground = mDefaultLabelColor; } // Regex never matches on strings of length 0 or 1, but we don't want // to complain about that since we're already doing that above. // TODO(maybe): Ideally this wouldn't light up if the only problem was a // non-alpha first character, since the next test will call that out. if (str.Length > 1) { if (!Asm65.Label.ValidateLabel(str)) { valid = false; validCharsLabel.Foreground = Brushes.Red; } else { validCharsLabel.Foreground = mDefaultLabelColor; } } else { validCharsLabel.Foreground = mDefaultLabelColor; } if (str.Length > 0 && !((str[0] >= 'A' && str[0] <= 'Z') || (str[0] >= 'a' && str[0] <= 'z') || str[0] == '_')) { // This should have been caught by the regex. We just want to set the // color on the "first character must be" instruction text. Debug.Assert(!valid); firstLetterLabel.Foreground = Brushes.Red; } else { firstLetterLabel.Foreground = mDefaultLabelColor; } // Refuse to continue if the label already exists. The only exception is if // it's the same symbol, and it's user-defined. (If they're trying to edit an // auto label, we want to force them to change the name.) // // NOTE: if label matching is case-insensitive, we want to allow a situation // where a label is being renamed from "FOO" to "Foo". We should be able to // test for object equality on the Symbol to determine if we're renaming a // symbol to itself. if (valid && mSymbolTable.TryGetValue(str, out Symbol sym) && (sym != LabelSym || LabelSym.SymbolSource != Symbol.Source.User)) { valid = false; notDuplicateLabel.Foreground = Brushes.Red; } else { notDuplicateLabel.Foreground = mDefaultLabelColor; } IsValid = valid; } private void OkButton_Click(object sender, RoutedEventArgs e) { if (string.IsNullOrEmpty(LabelText)) { LabelSym = null; } else { Symbol.Type symbolType; if (radioButtonLocal.IsChecked == true) { symbolType = Symbol.Type.LocalOrGlobalAddr; } else if (radioButtonGlobal.IsChecked == true) { symbolType = Symbol.Type.GlobalAddr; } else if (radioButtonExport.IsChecked == true) { symbolType = Symbol.Type.GlobalAddrExport; } else { Debug.Assert(false); // WTF symbolType = Symbol.Type.LocalOrGlobalAddr; } LabelSym = new Symbol(LabelText, mAddress, Symbol.Source.User, symbolType); } DialogResult = true; } } }