1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-09-11 20:54:37 +00:00
6502bench/SourceGen/WpfGui/EditLabel.xaml.cs
Andy McFadden 5dd7576529 Label rework, part 2
Continue development of non-unique labels.  The actual labels are
still unique, because we append a uniquifier tag, which gets added
and removed behind the scenes.  We're currently using the six-digit
hex file offset because this is only used for internal address
symbols.

The label editor and most of the formatters have been updated.  We
can't yet assemble code that includes non-unique labels, but older
stuff hasn't been broken.

This removes the "disable label localization" property, since that's
fundamentally incompatible with what we're doing, and adds a non-
unique label prefix setting so you can put '@' or ':' in front of
your should-be-local labels.

Also, fixed a field name typo.
2019-11-12 17:44:51 -08:00

304 lines
12 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;
using Asm65;
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>
/// Unique tag, for non-unique label creation. (Currently using offset.)
/// </summary>
private int mUniqueTag;
/// <summary>
/// Address we are editing the label for.
/// </summary>
private int mAddress;
/// <summary>
/// Reference to DisasmProject's SymbolTable.
/// </summary>
private SymbolTable mSymbolTable;
/// <summary>
/// Label formatter.
/// </summary>
private Formatter mFormatter;
// Dialog label text color, saved off at dialog load time.
private Brush mDefaultLabelColor;
/// <summary>
/// Recursion guard.
/// </summary>
private bool mInUpdateControls;
public string NonUniqueButtonLabel { get; private set; }
/// <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; OnPropertyChanged(); UpdateControls(); }
}
string mLabelText;
// Radio buttons.
public bool mIsNonUniqueChecked, mIsNonUniqueEnabled;
public bool IsNonUniqueChecked {
get { return mIsNonUniqueChecked; }
set { mIsNonUniqueChecked = value; OnPropertyChanged(); UpdateControls(); }
}
public bool IsNonUniqueEnabled {
get { return mIsNonUniqueEnabled; }
set { mIsNonUniqueEnabled = value; OnPropertyChanged(); }
}
public bool mIsLocalChecked, mIsLocalEnabled;
public bool IsLocalChecked {
get { return mIsLocalChecked; }
set { mIsLocalChecked = value; OnPropertyChanged(); UpdateControls(); }
}
public bool IsLocalEnabled {
get { return mIsLocalEnabled; }
set { mIsLocalEnabled = value; OnPropertyChanged(); }
}
public bool mIsGlobalChecked, mIsGlobalEnabled;
public bool IsGlobalChecked {
get { return mIsGlobalChecked; }
set { mIsGlobalChecked = value; OnPropertyChanged(); UpdateControls(); }
}
public bool IsGlobalEnabled {
get { return mIsGlobalEnabled; }
set { mIsGlobalEnabled = value; OnPropertyChanged(); }
}
public bool mIsExportedChecked, mIsExportedEnabled;
public bool IsExportedChecked {
get { return mIsExportedChecked; }
set { mIsExportedChecked = value; OnPropertyChanged(); UpdateControls(); }
}
public bool IsExportedEnabled {
get { return mIsExportedEnabled; }
set { mIsExportedEnabled = value; OnPropertyChanged(); }
}
// 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, int uniqueTag,
SymbolTable symbolTable, Formatter formatter) {
InitializeComponent();
Owner = owner;
DataContext = this;
LabelSym = origSym;
mAddress = address;
mUniqueTag = uniqueTag;
mSymbolTable = symbolTable;
mFormatter = formatter;
string fmt = (string)FindResource("str_NonUniqueLocalFmt");
NonUniqueButtonLabel = string.Format(fmt, mFormatter.NonUniqueLabelPrefix);
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
mDefaultLabelColor = maxLengthLabel.Foreground;
IsNonUniqueEnabled = IsLocalEnabled = IsGlobalEnabled = IsExportedEnabled = true;
if (LabelSym == null) {
LabelText = string.Empty;
IsGlobalChecked = true;
} else {
LabelText = LabelSym.GenerateDisplayLabel(mFormatter);
switch (LabelSym.SymbolType) {
case Symbol.Type.NonUniqueLocalAddr:
IsNonUniqueChecked = true;
break;
case Symbol.Type.LocalOrGlobalAddr:
IsLocalChecked = true;
break;
case Symbol.Type.GlobalAddr:
IsGlobalChecked = true;
break;
case Symbol.Type.GlobalAddrExport:
IsExportedChecked = true;
break;
default:
Debug.Assert(false); // WTF
IsGlobalChecked = true;
break;
}
}
UpdateControls();
}
private void Window_ContentRendered(object sender, EventArgs e) {
labelTextBox.SelectAll();
labelTextBox.Focus();
}
private void UpdateControls() {
if (mInUpdateControls) {
return;
}
mInUpdateControls = true;
LabelTextChanged();
mInUpdateControls = false;
}
private bool mHadNonUniquePrefix = false;
private void LabelTextChanged() {
bool isBlank = (LabelText.Length == 0);
// Strip leading non-unique prefix and the trailing annotation.
string trimLabel = Symbol.TrimAndValidateLabel(LabelText,
mFormatter.NonUniqueLabelPrefix, out bool isValid, out bool isLenValid,
out bool isFirstCharValid, out bool hasNonUniquePrefix,
out Symbol.LabelAnnotation anno);
// If they type '@'/':'/'.' at the start of the label, switch the radio button.
// Alternatively, if they choose a different radio button, remove the prefix.
// We only want to do this on the first event so we don't wedge the control.
if (hasNonUniquePrefix && !mHadNonUniquePrefix && !IsNonUniqueChecked) {
IsNonUniqueChecked = true;
} else if (hasNonUniquePrefix && mHadNonUniquePrefix && !IsNonUniqueChecked) {
LabelText = LabelText.Substring(1);
hasNonUniquePrefix = false;
}
mHadNonUniquePrefix = hasNonUniquePrefix;
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;
}
#if false
if (hasNonUniqueTag) {
IsNonUniqueChecked = true;
IsLocalEnabled = IsGlobalEnabled = IsExportedEnabled = false;
} else {
IsNonUniqueEnabled = IsLocalEnabled = IsGlobalEnabled = IsExportedEnabled = true;
}
#endif
// Refuse to continue if the label already exists and this isn't a non-unique label.
// 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 (!IsNonUniqueChecked && isValid &&
mSymbolTable.TryGetValue(trimLabel, out Symbol sym) &&
(sym != LabelSym || LabelSym.SymbolSource != Symbol.Source.User)) {
isValid = false;
notDuplicateLabel.Foreground = Brushes.Red;
} else if (IsNonUniqueChecked) {
notDuplicateLabel.Foreground = Brushes.Gray;
} 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;
if (IsNonUniqueChecked) {
symbolType = Symbol.Type.NonUniqueLocalAddr;
} else if (IsLocalChecked == true) {
symbolType = Symbol.Type.LocalOrGlobalAddr;
} else if (IsGlobalChecked == true) {
symbolType = Symbol.Type.GlobalAddr;
} else if (IsExportedChecked == true) {
symbolType = Symbol.Type.GlobalAddrExport;
} else {
Debug.Assert(false); // WTF
symbolType = Symbol.Type.GlobalAddr;
}
// Parse and strip the annotation and optional non-unique tag.
string trimLabel = Symbol.TrimAndValidateLabel(LabelText,
mFormatter.NonUniqueLabelPrefix, out bool unused1, out bool unused2,
out bool unused3, out bool hasNonUniquePrefix,
out Symbol.LabelAnnotation anno);
if (IsNonUniqueChecked) {
LabelSym = new Symbol(trimLabel, mAddress, anno, mUniqueTag);
} else {
Debug.Assert(!hasNonUniquePrefix);
LabelSym = new Symbol(trimLabel, mAddress, Symbol.Source.User, symbolType,
anno);
}
}
DialogResult = true;
}
}
}