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;
|
|
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
2019-06-08 00:25:04 +00:00
|
|
|
|
using System.Windows.Controls;
|
2019-06-08 22:48:44 +00:00
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
using CommonUtil;
|
|
|
|
|
|
2019-07-20 20:28:10 +00:00
|
|
|
|
namespace SourceGen {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// <summary>
|
2019-06-09 00:13:11 +00:00
|
|
|
|
/// Tracks the items selected in the DisplayList, using forwarded SelectionChanged events.
|
|
|
|
|
/// When enumerated, provides an ordered list of selected indices.
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// </summary>
|
2019-06-09 00:13:11 +00:00
|
|
|
|
/// <remarks>
|
|
|
|
|
/// In WPF you can't get indices, only items, so we have to store the item index in the
|
|
|
|
|
/// item itself.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public class DisplayListSelection : IEnumerable<int> {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
private BitArray mSelection;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Retrieves the total number of boolean values in the set. This is NOT the
|
|
|
|
|
/// number of selected items.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int Length { get { return mSelection.Length; } }
|
|
|
|
|
|
2019-06-10 22:46:35 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Retrieves the number of values that are set.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int Count { get; private set; }
|
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sets or gets the Nth element. True means the line is selected.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool this[int key] {
|
|
|
|
|
get {
|
|
|
|
|
return mSelection[key];
|
|
|
|
|
}
|
|
|
|
|
set {
|
2019-06-10 22:46:35 +00:00
|
|
|
|
// If an entry has changed, update the count of set items.
|
|
|
|
|
if (mSelection[key] != value) {
|
|
|
|
|
Count += value ? 1 : -1;
|
|
|
|
|
mSelection[key] = value;
|
|
|
|
|
}
|
|
|
|
|
Debug.Assert(Count >= 0 && Count <= Length);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-09 00:13:11 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructs an empty list.
|
|
|
|
|
/// </summary>
|
2019-06-08 00:25:04 +00:00
|
|
|
|
public DisplayListSelection() {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
mSelection = new BitArray(0);
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-09 00:13:11 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructs a list of the specified length.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="length">Number of elements.</param>
|
2019-06-08 00:25:04 +00:00
|
|
|
|
public DisplayListSelection(int length) {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
mSelection = new BitArray(length);
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-09 00:13:11 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns an enumeration of selected indices, in ascending order.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public IEnumerator<int> GetEnumerator() {
|
|
|
|
|
for (int i = 0; i < mSelection.Length; i++) {
|
|
|
|
|
if (mSelection[i]) {
|
|
|
|
|
yield return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() {
|
|
|
|
|
return GetEnumerator();
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sets the length of the selection array.
|
|
|
|
|
///
|
|
|
|
|
/// If the new length is longer, the new elements are initialized to false. If the
|
2019-06-09 00:13:11 +00:00
|
|
|
|
/// new length is shorter, the excess elements are discarded.
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="length">New length.</param>
|
2019-06-09 00:13:11 +00:00
|
|
|
|
//public void SetLength(int length) {
|
|
|
|
|
// mSelection.Length = length;
|
|
|
|
|
//}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-06-08 00:25:04 +00:00
|
|
|
|
/// Handles selection change.
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// </summary>
|
2019-06-08 00:25:04 +00:00
|
|
|
|
/// <param name="e">Argument from SelectionChanged event.</param>
|
|
|
|
|
public void SelectionChanged(SelectionChangedEventArgs e) {
|
2019-07-18 00:42:57 +00:00
|
|
|
|
Debug.WriteLine("SelectionChanged event: Add=" + e.AddedItems.Count +
|
|
|
|
|
" Rem=" + e.RemovedItems.Count);
|
2019-06-08 00:25:04 +00:00
|
|
|
|
foreach (DisplayList.FormattedParts parts in e.AddedItems) {
|
|
|
|
|
Debug.Assert(parts.ListIndex >= 0 && parts.ListIndex < mSelection.Length);
|
2019-06-10 22:46:35 +00:00
|
|
|
|
this[parts.ListIndex] = true;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
2019-06-08 00:25:04 +00:00
|
|
|
|
foreach (DisplayList.FormattedParts parts in e.RemovedItems) {
|
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
|
|
|
|
Debug.Assert(parts.ListIndex >= 0);
|
|
|
|
|
if (parts.ListIndex < mSelection.Length) {
|
|
|
|
|
this[parts.ListIndex] = false;
|
|
|
|
|
} else {
|
|
|
|
|
Debug.WriteLine("Attempted to remove selected item off end of list: " + parts);
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-08 22:48:44 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the index of the first selected item, or -1 if nothing is selected.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int GetFirstSelectedIndex() {
|
|
|
|
|
int idx;
|
|
|
|
|
for (idx = 0; idx < mSelection.Length; idx++) {
|
|
|
|
|
if (mSelection[idx]) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (idx == mSelection.Length) {
|
|
|
|
|
idx = -1;
|
|
|
|
|
}
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the index of the last selected item, or -1 if nothing is selected.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int GetLastSelectedIndex() {
|
|
|
|
|
int idx;
|
|
|
|
|
for (idx = mSelection.Length - 1; idx >= 0; idx--) {
|
|
|
|
|
if (mSelection[idx]) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-10 22:46:35 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns true if all items are selected.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsAllSelected() {
|
|
|
|
|
return Count == Length;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Confirms that the selection count matches the number of set bits. Pass
|
|
|
|
|
/// in {ListView}.SelectedIndices.Count.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="expected">Expected number of selected entries.</param>
|
|
|
|
|
/// <returns>True if count matches.</returns>
|
|
|
|
|
public bool DebugValidateSelectionCount(int expected) {
|
2019-06-10 22:46:35 +00:00
|
|
|
|
if (Count != expected) {
|
|
|
|
|
Debug.WriteLine("SelectionCount expected " + expected + ", count=" + Count);
|
|
|
|
|
}
|
|
|
|
|
int computed = 0;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
foreach (bool bit in mSelection) {
|
|
|
|
|
if (bit) {
|
2019-06-10 22:46:35 +00:00
|
|
|
|
computed++;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-06-10 22:46:35 +00:00
|
|
|
|
if (Count != computed) {
|
|
|
|
|
Debug.WriteLine("SelectionCount internal error: computed=" + computed +
|
|
|
|
|
", count=" + Count);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
2019-06-10 22:46:35 +00:00
|
|
|
|
return (Count == expected);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void DebugDump() {
|
|
|
|
|
RangeSet rangeSet = new RangeSet();
|
|
|
|
|
for (int i = 0; i < mSelection.Length; i++) {
|
|
|
|
|
if (mSelection[i]) {
|
|
|
|
|
rangeSet.Add(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-06-10 22:46:35 +00:00
|
|
|
|
Debug.WriteLine("DisplayListSelection ranges:");
|
2019-05-02 22:45:40 +00:00
|
|
|
|
IEnumerator<RangeSet.Range> iter = rangeSet.RangeListIterator;
|
|
|
|
|
while (iter.MoveNext()) {
|
|
|
|
|
RangeSet.Range range = iter.Current;
|
|
|
|
|
Debug.WriteLine(" [" + range.Low.ToString() + "," + range.High.ToString() + "]");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|