1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-05-31 22:41:37 +00:00
6502bench/SourceGen/WpfGui/EditLabel.xaml.cs
Andy McFadden 4d079c8d14 Label rework, part 1
This adds the concept of label annotations.  The primary driver of
the feature is the desire to note that sometimes you know what a
thing is, but sometimes you're just taking an educated guess.
Instead of writing "high_score_maybe", you can now write "high_score?",
which is more compact and consistent.  The annotations are stripped
off when generating source code, making them similar to Notes.

I also created a "Generated" annotation for the labels that are
synthesized by the address table formatter, but don't modify the
label for them, because there's not much need to remind the user
that "T1234" was generated by algorithm.

This also lays some of the groundwork for non-unique labels.
2019-11-08 21:02:15 -08:00

229 lines
8.8 KiB
C#

/*
* 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 {
/// <summary>
/// Edit a label.
/// </summary>
public partial class EditLabel : Window, INotifyPropertyChanged {
/// <summary>
/// Symbol object. When the dialog completes successfully,
/// this will have the new symbol, or null if the user deleted the label.
/// </summary>
public Symbol LabelSym { get; private set; }
/// <summary>
/// Address we are editing the label for.
/// </summary>
private int mAddress;
/// <summary>
/// Reference to DisasmProject's SymbolTable.
/// </summary>
private SymbolTable mSymbolTable;
// Dialog label text color, saved off at dialog load time.
private Brush mDefaultLabelColor;
/// <summary>
/// Set to true when input is valid. Controls whether the OK button is enabled.
/// </summary>
public bool IsValid {
get { return mIsValid; }
set {
mIsValid = value;
OnPropertyChanged();
}
}
private bool mIsValid;
/// <summary>
/// Property backing the text in the text entry box.
/// </summary>
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.AnnotatedLabel;
switch (LabelSym.SymbolType) {
case Symbol.Type.NonUniqueLocalAddr:
Debug.Assert(false); // TODO(xyzzy)
break;
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() {
#if false
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;
}
#endif
bool isBlank = (LabelText.Length == 0);
string trimLabel = Symbol.TrimAndValidateLabel(LabelText, out bool isValid,
out bool isLenValid, out bool isFirstCharValid, out Symbol.LabelAnnotation anno);
if (isBlank || isLenValid) {
maxLengthLabel.Foreground = mDefaultLabelColor;
} else {
maxLengthLabel.Foreground = Brushes.Red;
}
if (isBlank || isFirstCharValid) {
firstLetterLabel.Foreground = mDefaultLabelColor;
} else {
firstLetterLabel.Foreground = Brushes.Red;
}
if (isBlank || isValid) {
// TODO(maybe): if the problem is that the label starts with a number, we
// shouldn't light up this (which is the "valid chars are" label) as well.
validCharsLabel.Foreground = mDefaultLabelColor;
} else {
validCharsLabel.Foreground = Brushes.Red;
}
// 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 (isValid && mSymbolTable.TryGetValue(trimLabel, out Symbol sym) &&
(sym != LabelSym || LabelSym.SymbolSource != Symbol.Source.User)) {
isValid = false;
notDuplicateLabel.Foreground = Brushes.Red;
} else {
notDuplicateLabel.Foreground = mDefaultLabelColor;
}
IsValid = isBlank || isValid;
}
private void OkButton_Click(object sender, RoutedEventArgs e) {
if (string.IsNullOrEmpty(LabelText)) {
LabelSym = null;
} else {
Symbol.Type symbolType;
// TODO(xyzzy): non-local
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;
}
// Parse and strip the annotation.
string trimLabel = Symbol.TrimAndValidateLabel(LabelText, out bool unused1,
out bool unused2, out bool unused3, out Symbol.LabelAnnotation anno);
LabelSym = new Symbol(trimLabel, mAddress, Symbol.Source.User, symbolType, anno);
}
DialogResult = true;
}
}
}