2019-05-02 22:45:40 +00:00
|
|
|
|
/*
|
|
|
|
|
* 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;
|
2019-05-11 17:16:54 +00:00
|
|
|
|
using System.Collections;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
using System.Collections.Generic;
|
2019-05-11 17:16:54 +00:00
|
|
|
|
using System.Collections.Specialized;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
using System.Diagnostics;
|
2019-05-11 17:16:54 +00:00
|
|
|
|
using System.ComponentModel;
|
2019-07-14 22:39:27 +00:00
|
|
|
|
using System.Windows.Media;
|
2019-12-03 22:34:45 +00:00
|
|
|
|
using System.Windows.Media.Imaging;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-07-20 20:28:10 +00:00
|
|
|
|
namespace SourceGen {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// <summary>
|
2019-05-11 17:16:54 +00:00
|
|
|
|
/// List of items formatted for display.
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// </summary>
|
2019-05-11 17:16:54 +00:00
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This is intended to be useful as an ItemSource for a WPF ListView. We need to implement
|
|
|
|
|
/// plain IList to cause ListView to perform data virtualization, and the property/collection
|
|
|
|
|
/// changed events so the view will pick up our changes.
|
|
|
|
|
///
|
|
|
|
|
/// The ItemsControl.ItemsSource property wants an IEnumerable (which IList implements).
|
|
|
|
|
/// According to various articles, if the object implements IList, and the UI element
|
2019-05-28 01:46:09 +00:00
|
|
|
|
/// is providing *UI* virtualization, you will also get *data* virtualization. This behavior
|
2019-05-11 17:16:54 +00:00
|
|
|
|
/// doesn't seem to be documented anywhere, but the consensus is that it's expected to work.
|
|
|
|
|
///
|
2019-05-28 01:46:09 +00:00
|
|
|
|
/// Implementing generic IList<> doesn't seem necessary for XAML, but may be useful
|
|
|
|
|
/// for other consumers of the data.
|
|
|
|
|
///
|
|
|
|
|
/// The list is initially filled with null references, with FormattedParts instances
|
|
|
|
|
/// generated on demand. This is done by requesting individual items from the
|
2019-05-28 21:23:17 +00:00
|
|
|
|
/// LineListGen object.
|
|
|
|
|
///
|
|
|
|
|
/// NOTE: it may or may not be possible to implement this trivially with an
|
2019-06-10 01:09:00 +00:00
|
|
|
|
/// ObservableCollection. At an earlier iteration it wasn't, and I'd like to keep this
|
2019-05-28 21:23:17 +00:00
|
|
|
|
/// around even if it is now possible, in case the pendulum swings back the other way.
|
2019-07-13 00:04:14 +00:00
|
|
|
|
///
|
|
|
|
|
/// Additional reading on data virtualization:
|
|
|
|
|
/// https://www.codeproject.com/Articles/34405/WPF-Data-Virtualization?msg=5635751
|
|
|
|
|
/// https://web.archive.org/web/20121216034305/http://www.zagstudio.com/blog/498
|
|
|
|
|
/// https://web.archive.org/web/20121107200359/http://www.zagstudio.com/blog/378
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
/// https://github.com/lvaleriu/Virtualization
|
2019-05-11 17:16:54 +00:00
|
|
|
|
/// </remarks>
|
|
|
|
|
public class DisplayList : IList<DisplayList.FormattedParts>, IList,
|
|
|
|
|
INotifyCollectionChanged, INotifyPropertyChanged {
|
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// <summary>
|
2019-05-28 01:46:09 +00:00
|
|
|
|
/// List of formatted parts. DO NOT access this directly outside the event-sending
|
|
|
|
|
/// method wrappers.
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// </summary>
|
2019-05-11 17:16:54 +00:00
|
|
|
|
private List<FormattedParts> mList;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-28 01:46:09 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Data generation object.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This property is set by the LineListGen constructor.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public LineListGen ListGen { get; set; }
|
|
|
|
|
|
2019-06-08 00:25:04 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Set of selected items, by list index.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public DisplayListSelection SelectedIndices { get; private set; }
|
|
|
|
|
|
2019-05-28 01:46:09 +00:00
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// <summary>
|
2019-05-11 17:16:54 +00:00
|
|
|
|
/// Constructs an empty collection, with the default initial capacity.
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// </summary>
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public DisplayList() {
|
|
|
|
|
mList = new List<FormattedParts>();
|
2019-06-08 00:25:04 +00:00
|
|
|
|
SelectedIndices = new DisplayListSelection();
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
#region Property / Collection Changed
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
// See ObservableCollection class, e.g.
|
|
|
|
|
// https://github.com/Microsoft/referencesource/blob/master/System/compmod/system/collections/objectmodel/observablecollection.cs
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
private const string CountString = "Count";
|
|
|
|
|
private const string IndexerName = "Item[]";
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
|
|
|
|
|
PropertyChanged?.Invoke(this, e);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
private void OnPropertyChanged(string propertyName) {
|
|
|
|
|
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) {
|
|
|
|
|
CollectionChanged?.Invoke(this, e);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
private void OnCollectionChanged(NotifyCollectionChangedAction action,
|
|
|
|
|
object item, int index) {
|
|
|
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
private void OnCollectionChanged(NotifyCollectionChangedAction action,
|
|
|
|
|
object item, int index, int oldIndex) {
|
|
|
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index, oldIndex));
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
private void OnCollectionChanged(NotifyCollectionChangedAction action,
|
|
|
|
|
object oldItem, object newItem, int index) {
|
|
|
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
private void OnCollectionReset() {
|
|
|
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
#endregion Property / Collection Changed
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
#region IList / IList<T>
|
|
|
|
|
public int Count => ((IList<FormattedParts>)mList).Count;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public bool IsReadOnly => ((IList<FormattedParts>)mList).IsReadOnly;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public bool IsFixedSize => ((IList)mList).IsFixedSize;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public object SyncRoot => ((IList)mList).SyncRoot;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public bool IsSynchronized => ((IList)mList).IsSynchronized;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public void Add(FormattedParts item) {
|
|
|
|
|
((IList<FormattedParts>)mList).Add(item);
|
|
|
|
|
OnPropertyChanged(CountString);
|
|
|
|
|
OnPropertyChanged(IndexerName);
|
|
|
|
|
OnCollectionReset();
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public int Add(object value) {
|
|
|
|
|
int posn = ((IList)mList).Add(value);
|
|
|
|
|
if (posn >= 0) {
|
|
|
|
|
OnPropertyChanged(CountString);
|
|
|
|
|
OnPropertyChanged(IndexerName);
|
|
|
|
|
OnCollectionChanged(NotifyCollectionChangedAction.Add, value, posn);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
2019-05-11 17:16:54 +00:00
|
|
|
|
return posn;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public void Clear() {
|
|
|
|
|
((IList<FormattedParts>)mList).Clear();
|
|
|
|
|
OnPropertyChanged(CountString);
|
|
|
|
|
OnPropertyChanged(IndexerName);
|
|
|
|
|
OnCollectionReset();
|
2019-06-10 01:09:00 +00:00
|
|
|
|
|
|
|
|
|
// Not strictly necessary, but does free up the memory sooner.
|
|
|
|
|
SelectedIndices = new DisplayListSelection();
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public bool Contains(FormattedParts item) {
|
|
|
|
|
return ((IList<FormattedParts>)mList).Contains(item);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
2019-05-11 17:16:54 +00:00
|
|
|
|
bool IList.Contains(object value) {
|
|
|
|
|
return Contains((FormattedParts)value);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public void CopyTo(FormattedParts[] array, int arrayIndex) {
|
|
|
|
|
((IList<FormattedParts>)mList).CopyTo(array, arrayIndex);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public void CopyTo(Array array, int index) {
|
|
|
|
|
((IList)mList).CopyTo(array, index);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public IEnumerator<FormattedParts> GetEnumerator() {
|
|
|
|
|
// Use the indexer, rather than mList's enumerator, to get on-demand string gen.
|
|
|
|
|
for (int i = 0; i < Count; i++) {
|
|
|
|
|
yield return this[i];
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() {
|
|
|
|
|
return GetEnumerator();
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public int IndexOf(FormattedParts item) {
|
|
|
|
|
return ((IList<FormattedParts>)mList).IndexOf(item);
|
|
|
|
|
}
|
|
|
|
|
int IList.IndexOf(object value) {
|
|
|
|
|
return IndexOf((FormattedParts)value);
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public void Insert(int index, FormattedParts item) {
|
|
|
|
|
((IList<FormattedParts>)mList).Insert(index, item);
|
|
|
|
|
OnPropertyChanged(CountString);
|
|
|
|
|
OnPropertyChanged(IndexerName);
|
|
|
|
|
OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
|
|
|
|
|
}
|
|
|
|
|
void IList.Insert(int index, object value) {
|
|
|
|
|
Insert(index, (FormattedParts)value);
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public void RemoveAt(int index) {
|
|
|
|
|
FormattedParts removed = mList[index];
|
|
|
|
|
((IList<FormattedParts>)mList).RemoveAt(index);
|
|
|
|
|
OnPropertyChanged(CountString);
|
|
|
|
|
OnPropertyChanged(IndexerName);
|
|
|
|
|
OnCollectionChanged(NotifyCollectionChangedAction.Remove, removed, index);
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public bool Remove(FormattedParts item) {
|
|
|
|
|
// NotifyCollectionChangedAction.Remove wants an index. We can find the index
|
|
|
|
|
// of the first matching item and then do a RemoveAt, but this call just isn't
|
|
|
|
|
// all that interesting for us, so it's easier to ignore it.
|
|
|
|
|
//return ((IList<FormattedParts>)mList).Remove(item);
|
|
|
|
|
throw new NotSupportedException();
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
void IList.Remove(object value) {
|
|
|
|
|
//Remove((FormattedParts)value);
|
|
|
|
|
throw new NotSupportedException();
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
object IList.this[int index] {
|
|
|
|
|
// forward to generic impl
|
|
|
|
|
get { return this[index]; }
|
|
|
|
|
set { this[index] = (FormattedParts)value; }
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
// For IList<T>.
|
|
|
|
|
public FormattedParts this[int index] {
|
|
|
|
|
get {
|
|
|
|
|
FormattedParts parts = mList[index];
|
|
|
|
|
if (parts == null) {
|
|
|
|
|
parts = mList[index] = GetEntry(index);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
2019-05-11 17:16:54 +00:00
|
|
|
|
return parts;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
2019-05-11 17:16:54 +00:00
|
|
|
|
set {
|
|
|
|
|
FormattedParts orig = mList[index];
|
|
|
|
|
mList[index] = value;
|
|
|
|
|
OnPropertyChanged(IndexerName);
|
|
|
|
|
OnCollectionChanged(NotifyCollectionChangedAction.Replace, orig, value, index);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
#endregion IList / IList<T>
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Retrieves the Nth element.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private FormattedParts GetEntry(int index) {
|
2019-05-28 01:46:09 +00:00
|
|
|
|
FormattedParts parts = mList[index];
|
|
|
|
|
if (parts == null) {
|
|
|
|
|
parts = mList[index] = ListGen.GetFormattedParts(index);
|
2019-06-08 00:25:04 +00:00
|
|
|
|
parts.ListIndex = index;
|
2019-05-28 01:46:09 +00:00
|
|
|
|
}
|
|
|
|
|
return parts;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 23:30:59 +00:00
|
|
|
|
/// <summary>
|
2019-06-08 00:25:04 +00:00
|
|
|
|
/// Resets the list, filling it with empty elements. Also resets the selected indices.
|
2019-05-30 23:30:59 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="size">New size of the list.</param>
|
2019-05-28 01:46:09 +00:00
|
|
|
|
public void ResetList(int size) {
|
2019-05-30 23:30:59 +00:00
|
|
|
|
// TODO: can we recycle existing elements and just add/trim as needed?
|
2019-05-28 01:46:09 +00:00
|
|
|
|
Clear();
|
|
|
|
|
mList.Capacity = size;
|
|
|
|
|
for (int i = 0; i < size; i++) {
|
2019-05-30 23:30:59 +00:00
|
|
|
|
// add directly to list so we don't send events
|
|
|
|
|
mList.Add(null);
|
2019-05-12 00:36:50 +00:00
|
|
|
|
}
|
2019-05-30 23:30:59 +00:00
|
|
|
|
|
2019-07-07 23:18:46 +00:00
|
|
|
|
SelectedIndices = new DisplayListSelection(size);
|
|
|
|
|
|
2019-05-30 23:30:59 +00:00
|
|
|
|
// send one big notification at the end; "reset" means "forget everything you knew"
|
|
|
|
|
OnPropertyChanged(CountString);
|
|
|
|
|
OnPropertyChanged(IndexerName);
|
|
|
|
|
OnCollectionReset();
|
2019-05-11 17:16:54 +00:00
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-07-07 00:24:42 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// A range of lines has been replaced with a new range of lines. The new set may be
|
|
|
|
|
/// the same size, larger, or smaller than the previous.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="startIndex">Start index of change area.</param>
|
|
|
|
|
/// <param name="oldCount">Number of old lines.</param>
|
|
|
|
|
/// <param name="newCount">Number of new lines. May be zero.</param>
|
|
|
|
|
public void ClearListSegment(int startIndex, int oldCount, int newCount) {
|
|
|
|
|
Debug.WriteLine("ClearListSegment start=" + startIndex + " old=" + oldCount +
|
2019-07-07 23:18:46 +00:00
|
|
|
|
" new=" + newCount + " (mList.Count=" + mList.Count + ")");
|
2019-07-07 00:24:42 +00:00
|
|
|
|
|
|
|
|
|
Debug.Assert(startIndex >= 0 && startIndex < mList.Count);
|
2020-01-21 18:29:58 +00:00
|
|
|
|
Debug.Assert(oldCount >= 0 && startIndex + oldCount <= mList.Count);
|
2019-07-07 00:24:42 +00:00
|
|
|
|
Debug.Assert(newCount >= 0);
|
|
|
|
|
|
|
|
|
|
// Remove the old elements to clear them.
|
2020-01-21 18:29:58 +00:00
|
|
|
|
if (oldCount != 0) {
|
|
|
|
|
mList.RemoveRange(startIndex, oldCount);
|
|
|
|
|
}
|
2019-07-07 00:24:42 +00:00
|
|
|
|
// Replace with the appropriate number of null entries.
|
|
|
|
|
for (int i = 0; i < newCount; i++) {
|
|
|
|
|
mList.Insert(startIndex, null);
|
|
|
|
|
}
|
2019-12-28 19:44:26 +00:00
|
|
|
|
// TODO(someday): can we null out existing entries, and just insert/remove when
|
|
|
|
|
// counts differ?
|
2019-07-07 23:18:46 +00:00
|
|
|
|
|
|
|
|
|
if (oldCount != newCount) {
|
|
|
|
|
SelectedIndices = new DisplayListSelection(mList.Count);
|
2019-07-14 21:41:46 +00:00
|
|
|
|
RecalculateListIndices();
|
2019-07-07 23:18:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-07 00:24:42 +00:00
|
|
|
|
OnPropertyChanged(CountString);
|
|
|
|
|
OnPropertyChanged(IndexerName);
|
2019-12-28 19:44:26 +00:00
|
|
|
|
// TODO: this causes the ListView to format the entire listing, despite
|
2019-11-13 01:24:41 +00:00
|
|
|
|
// being virtual. So we're regenerating the entire list after something trivial,
|
2019-12-28 19:44:26 +00:00
|
|
|
|
// like renaming a label, which hampers performance. Need to figure this out.
|
2019-07-07 00:24:42 +00:00
|
|
|
|
OnCollectionReset();
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-14 21:41:46 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Recalculates the list index fields after lines are added or removed.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void RecalculateListIndices() {
|
|
|
|
|
Debug.WriteLine("Recalculating list indices");
|
|
|
|
|
for (int i = 0; i < mList.Count; i++) {
|
|
|
|
|
if (mList[i] != null) {
|
|
|
|
|
mList[i].ListIndex = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-16 16:29:54 +00:00
|
|
|
|
/// <summary>
|
ORG rework, part 6
Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM. Regions are specified
with the "NA" address value. The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).
Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence. The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data. So we keep the label and add a
warning to the Messages list when we see a reference.
Moved NON_ADDR constants to Address class. AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.
Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.
Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map. It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.
Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
2021-10-01 01:07:21 +00:00
|
|
|
|
/// List elements. Instances are immutable except for ListIndex.
|
2019-06-16 16:29:54 +00:00
|
|
|
|
/// </summary>
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public class FormattedParts {
|
|
|
|
|
public string Offset { get; private set; }
|
|
|
|
|
public string Addr { get; private set; }
|
|
|
|
|
public string Bytes { get; private set; }
|
|
|
|
|
public string Flags { get; private set; }
|
|
|
|
|
public string Attr { get; private set; }
|
|
|
|
|
public string Label { get; private set; }
|
|
|
|
|
public string Opcode { get; private set; }
|
|
|
|
|
public string Operand { get; private set; }
|
|
|
|
|
public string Comment { get; private set; }
|
2019-05-23 20:38:41 +00:00
|
|
|
|
public bool IsLongComment { get; private set; }
|
2019-12-03 22:34:45 +00:00
|
|
|
|
|
2019-07-14 22:39:27 +00:00
|
|
|
|
public bool HasBackgroundColor { get; private set; }
|
|
|
|
|
public Brush BackgroundBrush { get; private set; }
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-12-03 22:34:45 +00:00
|
|
|
|
public bool IsVisualizationSet { get; private set; }
|
|
|
|
|
public Visualization[] VisualizationSet { get; private set; }
|
|
|
|
|
|
ORG rework, part 6
Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM. Regions are specified
with the "NA" address value. The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).
Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence. The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data. So we keep the label and add a
warning to the Messages list when we see a reference.
Moved NON_ADDR constants to Address class. AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.
Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.
Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map. It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.
Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
2021-10-01 01:07:21 +00:00
|
|
|
|
// Set to true if we want to highlight the address and label fields. This is
|
|
|
|
|
// examined by a data trigger in CodeListItemStyle.xaml.
|
2019-06-16 16:29:54 +00:00
|
|
|
|
public bool HasAddrLabelHighlight { get; private set; }
|
|
|
|
|
|
2021-11-17 19:18:23 +00:00
|
|
|
|
// Set to true if we want to highlight the operand field. This is
|
|
|
|
|
// examined by a data trigger in CodeListItemStyle.xaml.
|
|
|
|
|
public bool HasOperandHighlight { get; private set; }
|
|
|
|
|
|
2019-09-02 22:18:55 +00:00
|
|
|
|
// Set to true if the Flags field has been modified.
|
ORG rework, part 6
Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM. Regions are specified
with the "NA" address value. The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).
Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence. The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data. So we keep the label and add a
warning to the Messages list when we see a reference.
Moved NON_ADDR constants to Address class. AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.
Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.
Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map. It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.
Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
2021-10-01 01:07:21 +00:00
|
|
|
|
public bool HasModifiedFlags {
|
|
|
|
|
get { return (mPartFlags & PartFlags.HasModifiedFlags) != 0; }
|
|
|
|
|
}
|
|
|
|
|
// Set to true if the address here is actually non-addressable.
|
|
|
|
|
public bool IsNonAddressable {
|
|
|
|
|
get { return (mPartFlags & PartFlags.IsNonAddressable) != 0; }
|
|
|
|
|
}
|
2019-09-02 22:18:55 +00:00
|
|
|
|
|
ORG rework, part 6
Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM. Regions are specified
with the "NA" address value. The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).
Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence. The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data. So we keep the label and add a
warning to the Messages list when we see a reference.
Moved NON_ADDR constants to Address class. AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.
Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.
Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map. It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.
Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
2021-10-01 01:07:21 +00:00
|
|
|
|
// List index, filled in on demand. If the list is regenerated we want to
|
|
|
|
|
// renumber elements without having to recreate them, so this field is mutable.
|
2019-06-08 00:25:04 +00:00
|
|
|
|
public int ListIndex { get; set; } = -1;
|
|
|
|
|
|
ORG rework, part 6
Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM. Regions are specified
with the "NA" address value. The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).
Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence. The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data. So we keep the label and add a
warning to the Messages list when we see a reference.
Moved NON_ADDR constants to Address class. AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.
Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.
Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map. It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.
Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
2021-10-01 01:07:21 +00:00
|
|
|
|
[Flags]
|
|
|
|
|
public enum PartFlags {
|
|
|
|
|
None = 0,
|
|
|
|
|
HasModifiedFlags = 1, // Flags field is non-default
|
|
|
|
|
IsNonAddressable = 1 << 1, // this is a non-addressable region
|
|
|
|
|
}
|
|
|
|
|
private PartFlags mPartFlags;
|
|
|
|
|
|
|
|
|
|
|
Improve save & restore of top line
Whenever the display list gets regenerated, we need to restore the
code list view scroll position to the previous location in the file.
This gets tricky when multiple lines are appearing or disappearing.
We were saving the file offset of the line, but that works poorly
when there's a multi-line comment associated with that offset,
because we end up scrolling to the top of the comment whenever any
part of the comment is at the top of the screen.
We now track the file offset and the number of lines we were from
the top of that offset's content. This works well unless we remove
a lot of lines. If the adjusted line index would put us into a
different file offset, we punt and just scroll to the top of the item.
Also, fix a crasher in Edit Note.
Also, fix behavior when the list shrinks while a line near the end
of the file is selected.
Also, change a few instances of "Color.FromArgb(0,0,0,0)" to use a
common constant.
2019-07-17 20:47:43 +00:00
|
|
|
|
private static Color NoColor = CommonWPF.Helper.ZeroColor;
|
2019-07-14 22:39:27 +00:00
|
|
|
|
|
|
|
|
|
|
2019-05-23 20:38:41 +00:00
|
|
|
|
// Private constructor -- create instances with factory methods.
|
2019-10-06 21:38:49 +00:00
|
|
|
|
private FormattedParts() {
|
|
|
|
|
Offset = Addr = Bytes = Flags = Attr = Label = Opcode = Operand = Comment =
|
|
|
|
|
string.Empty;
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-06-16 16:29:54 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Clones the specified object.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static FormattedParts Clone(FormattedParts orig) {
|
|
|
|
|
FormattedParts newParts = FormattedParts.Create(orig.Offset, orig.Addr,
|
|
|
|
|
orig.Bytes, orig.Flags, orig.Attr, orig.Label, orig.Opcode, orig.Operand,
|
ORG rework, part 6
Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM. Regions are specified
with the "NA" address value. The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).
Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence. The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data. So we keep the label and add a
warning to the Messages list when we see a reference.
Moved NON_ADDR constants to Address class. AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.
Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.
Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map. It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.
Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
2021-10-01 01:07:21 +00:00
|
|
|
|
orig.Comment, orig.mPartFlags);
|
2019-06-16 16:29:54 +00:00
|
|
|
|
|
|
|
|
|
newParts.IsLongComment = orig.IsLongComment;
|
|
|
|
|
newParts.HasAddrLabelHighlight = orig.HasAddrLabelHighlight;
|
|
|
|
|
|
|
|
|
|
newParts.ListIndex = orig.ListIndex;
|
|
|
|
|
return newParts;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-11 17:16:54 +00:00
|
|
|
|
public static FormattedParts Create(string offset, string addr, string bytes,
|
|
|
|
|
string flags, string attr, string label, string opcode, string operand,
|
ORG rework, part 6
Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM. Regions are specified
with the "NA" address value. The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).
Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence. The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data. So we keep the label and add a
warning to the Messages list when we see a reference.
Moved NON_ADDR constants to Address class. AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.
Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.
Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map. It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.
Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
2021-10-01 01:07:21 +00:00
|
|
|
|
string comment, PartFlags pflags) {
|
2019-05-11 17:16:54 +00:00
|
|
|
|
FormattedParts parts = new FormattedParts();
|
|
|
|
|
parts.Offset = offset;
|
|
|
|
|
parts.Addr = addr;
|
|
|
|
|
parts.Bytes = bytes;
|
|
|
|
|
parts.Flags = flags;
|
|
|
|
|
parts.Attr = attr;
|
|
|
|
|
parts.Label = label;
|
|
|
|
|
parts.Opcode = opcode;
|
|
|
|
|
parts.Operand = operand;
|
|
|
|
|
parts.Comment = comment;
|
2019-05-23 20:38:41 +00:00
|
|
|
|
parts.IsLongComment = false;
|
ORG rework, part 6
Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM. Regions are specified
with the "NA" address value. The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).
Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence. The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data. So we keep the label and add a
warning to the Messages list when we see a reference.
Moved NON_ADDR constants to Address class. AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.
Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.
Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map. It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.
Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
2021-10-01 01:07:21 +00:00
|
|
|
|
parts.mPartFlags = pflags;
|
2019-05-12 00:36:50 +00:00
|
|
|
|
|
|
|
|
|
return parts;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-28 01:46:09 +00:00
|
|
|
|
public static FormattedParts CreateBlankLine() {
|
|
|
|
|
FormattedParts parts = new FormattedParts();
|
|
|
|
|
return parts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static FormattedParts CreateLongComment(string comment) {
|
|
|
|
|
FormattedParts parts = new FormattedParts();
|
|
|
|
|
parts.Comment = comment;
|
2019-06-04 20:06:24 +00:00
|
|
|
|
parts.IsLongComment = true;
|
2019-05-28 01:46:09 +00:00
|
|
|
|
return parts;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-14 22:39:27 +00:00
|
|
|
|
public static FormattedParts CreateNote(string comment, Color color) {
|
|
|
|
|
FormattedParts parts = new FormattedParts();
|
|
|
|
|
parts.Comment = comment;
|
|
|
|
|
parts.IsLongComment = true;
|
|
|
|
|
if (color != NoColor) {
|
|
|
|
|
parts.HasBackgroundColor = true;
|
|
|
|
|
parts.BackgroundBrush = new SolidColorBrush(color);
|
2020-03-30 23:04:37 +00:00
|
|
|
|
parts.BackgroundBrush.Freeze(); // export runs on non-UI thread
|
2019-07-14 22:39:27 +00:00
|
|
|
|
}
|
|
|
|
|
return parts;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-05 03:41:19 +00:00
|
|
|
|
public static FormattedParts CreateDirective(string opstr, string operandStr) {
|
2019-05-28 01:46:09 +00:00
|
|
|
|
FormattedParts parts = new FormattedParts();
|
|
|
|
|
parts.Opcode = opstr;
|
2021-10-05 03:41:19 +00:00
|
|
|
|
parts.Operand = operandStr;
|
|
|
|
|
return parts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static FormattedParts CreatePreLabelDirective(string addrStr, string labelStr) {
|
|
|
|
|
FormattedParts parts = new FormattedParts();
|
|
|
|
|
parts.Addr = addrStr;
|
|
|
|
|
parts.Label = labelStr;
|
2019-05-28 01:46:09 +00:00
|
|
|
|
return parts;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-27 00:06:43 +00:00
|
|
|
|
public static FormattedParts CreateFullDirective(string label, string opstr,
|
2021-10-05 03:41:19 +00:00
|
|
|
|
string operandStr, string comment) {
|
2019-05-28 01:46:09 +00:00
|
|
|
|
FormattedParts parts = new FormattedParts();
|
|
|
|
|
parts.Label = label;
|
|
|
|
|
parts.Opcode = opstr;
|
2021-10-05 03:41:19 +00:00
|
|
|
|
parts.Operand = operandStr;
|
2019-05-28 01:46:09 +00:00
|
|
|
|
parts.Comment = comment;
|
|
|
|
|
return parts;
|
|
|
|
|
}
|
2019-06-16 16:29:54 +00:00
|
|
|
|
|
2019-12-03 00:38:32 +00:00
|
|
|
|
public static FormattedParts CreateVisualizationSet(VisualizationSet visSet) {
|
2019-12-01 01:52:33 +00:00
|
|
|
|
FormattedParts parts = new FormattedParts();
|
2019-12-03 00:38:32 +00:00
|
|
|
|
if (visSet.Count == 0) {
|
2019-12-03 22:34:45 +00:00
|
|
|
|
// should not happen
|
|
|
|
|
parts.Comment = "!EMPTY VSET!";
|
|
|
|
|
parts.IsLongComment = true;
|
2019-12-03 00:38:32 +00:00
|
|
|
|
} else {
|
|
|
|
|
string fmt;
|
|
|
|
|
if (visSet.Count == 1) {
|
|
|
|
|
fmt = Res.Strings.VIS_SET_SINGLE_FMT;
|
|
|
|
|
} else {
|
|
|
|
|
fmt = Res.Strings.VIS_SET_MULTIPLE_FMT;
|
|
|
|
|
}
|
2019-12-03 22:34:45 +00:00
|
|
|
|
parts.Comment = string.Format(fmt, "Bitmap", visSet[0].Tag, visSet.Count - 1);
|
|
|
|
|
parts.VisualizationSet = visSet.ToArray();
|
|
|
|
|
parts.IsVisualizationSet = true;
|
2019-12-03 00:38:32 +00:00
|
|
|
|
}
|
2019-12-01 01:52:33 +00:00
|
|
|
|
return parts;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-17 19:18:23 +00:00
|
|
|
|
public static FormattedParts AddSelectionAddrHighlight(FormattedParts orig) {
|
2019-06-16 16:29:54 +00:00
|
|
|
|
FormattedParts newParts = Clone(orig);
|
|
|
|
|
newParts.HasAddrLabelHighlight = true;
|
|
|
|
|
return newParts;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-17 19:18:23 +00:00
|
|
|
|
public static FormattedParts RemoveSelectionAddrHighlight(FormattedParts orig) {
|
2019-06-16 16:29:54 +00:00
|
|
|
|
FormattedParts newParts = Clone(orig);
|
|
|
|
|
newParts.HasAddrLabelHighlight = false;
|
|
|
|
|
return newParts;
|
|
|
|
|
}
|
Improve save & restore of top line
Whenever the display list gets regenerated, we need to restore the
code list view scroll position to the previous location in the file.
This gets tricky when multiple lines are appearing or disappearing.
We were saving the file offset of the line, but that works poorly
when there's a multi-line comment associated with that offset,
because we end up scrolling to the top of the comment whenever any
part of the comment is at the top of the screen.
We now track the file offset and the number of lines we were from
the top of that offset's content. This works well unless we remove
a lot of lines. If the adjusted line index would put us into a
different file offset, we punt and just scroll to the top of the item.
Also, fix a crasher in Edit Note.
Also, fix behavior when the list shrinks while a line near the end
of the file is selected.
Also, change a few instances of "Color.FromArgb(0,0,0,0)" to use a
common constant.
2019-07-17 20:47:43 +00:00
|
|
|
|
|
2021-11-17 19:18:23 +00:00
|
|
|
|
public static FormattedParts AddSelectionOperHighlight(FormattedParts orig) {
|
|
|
|
|
FormattedParts newParts = Clone(orig);
|
|
|
|
|
newParts.HasOperandHighlight = true;
|
|
|
|
|
return newParts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static FormattedParts RemoveSelectionOperHighlight(FormattedParts orig) {
|
|
|
|
|
FormattedParts newParts = Clone(orig);
|
|
|
|
|
newParts.HasOperandHighlight = false;
|
|
|
|
|
return newParts;
|
|
|
|
|
}
|
|
|
|
|
|
Improve save & restore of top line
Whenever the display list gets regenerated, we need to restore the
code list view scroll position to the previous location in the file.
This gets tricky when multiple lines are appearing or disappearing.
We were saving the file offset of the line, but that works poorly
when there's a multi-line comment associated with that offset,
because we end up scrolling to the top of the comment whenever any
part of the comment is at the top of the screen.
We now track the file offset and the number of lines we were from
the top of that offset's content. This works well unless we remove
a lot of lines. If the adjusted line index would put us into a
different file offset, we punt and just scroll to the top of the item.
Also, fix a crasher in Edit Note.
Also, fix behavior when the list shrinks while a line near the end
of the file is selected.
Also, change a few instances of "Color.FromArgb(0,0,0,0)" to use a
common constant.
2019-07-17 20:47:43 +00:00
|
|
|
|
public override string ToString() {
|
|
|
|
|
return "[Parts: index=" + ListIndex + " off=" + Offset + "]";
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|